From: rjones@intel.home.annexia.org Date: Tue, 13 May 2008 09:01:55 +0000 (+0100) Subject: Build internal NTFS structure X-Git-Url: http://git.annexia.org/?a=commitdiff_plain;ds=sidebyside;h=df52c8492235bb3e9fb83336962b631ddfeaaf4f;p=virt-df.git Build internal NTFS structure --- diff --git a/lib/diskimage_ntfs.ml b/lib/diskimage_ntfs.ml index ea077af..d7eb7d6 100644 --- a/lib/diskimage_ntfs.ml +++ b/lib/diskimage_ntfs.ml @@ -25,11 +25,42 @@ open Printf open Diskimage_impl open Int63.Operators +let id = "ntfs" + (* Private data functions. *) let attach_private_data, get_private_data = private_data_functions (fun {fs_cb = {fs_cb_uq = u}} -> u) -let id = "ntfs" +(* Type of the private data, basically all the metadata that we + * read from the NTFS volume. + *) +type ntfs_fs = { + ntfs_blocksize : int63; (* Blocksize (cluster size) *) + ntfs_mft_lcn : int63; (* MFT location (bytes) *) + ntfs_mft_size : int63; (* MFT size (bytes) *) + ntfs_mft_records : ntfs_mft_record list; (* Files in MFT *) +} +and ntfs_mft_record = { + ntfs_filename : ntfs_filename option; (* Filename, if present. *) + ntfs_info : ntfs_info option; (* Standard information, if present. *) + ntfs_data : ntfs_data option; (* $Data stream, if present. *) +} +and ntfs_filename = { + ntfs_name : string; (* Filename (UTF-8 encoded). *) +} +and ntfs_info = { + ntfs_creation_time : int64; + ntfs_last_data_change_time : int64; + ntfs_last_mft_change_time : int64; + ntfs_last_access_time : int64; +} +and ntfs_data = { + ntfs_data_size : int63; (* Actual size of data. *) + ntfs_runlist : ntfs_runentry list; (* Runlist. *) +} +and ntfs_runentry = + (* VCN start,size => LCN / None if sparse hole *) + (int63 * int63) * int63 option let rec probe dev = let fs = probe_superblock dev in @@ -91,6 +122,13 @@ and probe_superblock dev = let mft = parse_mft dev mft_lcn mft_size in + let priv = { + ntfs_blocksize = blocksize; + ntfs_mft_lcn = mft_lcn; + ntfs_mft_size = mft_size; + ntfs_mft_records = mft + } in + raise Not_found (* XXX *) | { _ } -> raise Not_found (* Not an NTFS boot sector. *) @@ -133,27 +171,31 @@ and parse_mft_records bits = if !debug then eprintf "got an MFT record, now parsing attributes ...\n%!"; - let attrs = parse_attrs attrs in + let mft_record = { + ntfs_filename = None; + ntfs_info = None; + ntfs_data = None + } in + let mft_record = parse_attrs attrs mft_record in - parse_mft_records rest (* loop rest of MFT records *) + mft_record :: parse_mft_records rest (* loop rest of MFT records *) (* Just assume that the end of the list of MFT records * is marked by all zeroes. This seems to be the * case, but not sure if it is generally true. * XXX? *) - | { 0x00000000_l : 32 } -> - () + | { 0x00000000_l : 32 } -> [] - | { _ } -> () + | { _ } -> [] -and parse_attrs attrs = +and parse_attrs attrs mft_record = (* Parse the MFT record attributes. *) bitmatch attrs with | { 0xFFFFFFFF_l : 32 : littleendian } -> (* AT_END *) if !debug then eprintf "found AT_END, end of attributes\n%!"; - () + mft_record | { attr_type : 32 : littleendian; attr_size : 32 : littleendian; @@ -162,8 +204,8 @@ and parse_attrs attrs = attr : (Int32.to_int attr_size - 24) * 8 : bitstring; rest : -1 : bitstring } -> - (* XXX let attr = *) parse_resident_attr attr_type attr; - parse_attrs rest + let mft_record = parse_resident_attr attr_type attr mft_record in + parse_attrs rest mft_record | { attr_type : 32 : littleendian; attr_size : 32 : littleendian; @@ -186,12 +228,14 @@ and parse_attrs attrs = rest : -1 : bitstring } -> - (* XXX let attr = *) - parse_nonresident_attr attr_type highest_vcn - allocated_size data_size initialized_size - mapping_pairs; + let data_size = Int63.of_int64 data_size in + + let mft_record = + parse_nonresident_attr attr_type highest_vcn + allocated_size data_size initialized_size + mapping_pairs mft_record in - parse_attrs rest + parse_attrs rest mft_record (* Not matched above, so we don't know how to parse this attribute, but * there is still enough information to skip to the next one. @@ -206,14 +250,15 @@ and parse_attrs attrs = Bitmatch.hexdump_bitstring Pervasives.stderr attrs ); - parse_attrs rest + parse_attrs rest mft_record (* Otherwise unparsable & unskippable attribute entry. *) | { _ } -> if !debug then - eprintf "corrupt MFT attribute entry\n%!" + eprintf "corrupt MFT attribute entry\n%!"; + mft_record -and parse_resident_attr attr_type attr = +and parse_resident_attr attr_type attr mft_record = match attr_type with | 0x10_l -> (* AT_STANDARD_INFORMATION *) (bitmatch attr with @@ -222,13 +267,19 @@ and parse_resident_attr attr_type attr = last_mft_change_time : 64; last_access_time : 64 (* other stuff follows, just ignore it *) } -> - if !debug then - eprintf "creation time: %Lx, last_access_time: %Lx\n" - creation_time last_access_time + + let info = { + ntfs_creation_time = creation_time; + ntfs_last_data_change_time = last_data_change_time; + ntfs_last_mft_change_time = last_mft_change_time; + ntfs_last_access_time = last_access_time + } in + { mft_record with ntfs_info = Some info } | { _ } -> if !debug then - eprintf "cannot parse AT_STANDARD_INFORMATION\n%!" + eprintf "cannot parse AT_STANDARD_INFORMATION\n%!"; + mft_record ); | 0x30_l -> (* AT_FILE_NAME *) @@ -238,8 +289,8 @@ and parse_resident_attr attr_type attr = _ : 64; (* last change time *) _ : 64; (* last MFT change time *) _ : 64; (* last access time *) - allocated_size : 64 : littleendian; - data_size : 64 : littleendian; + _ : 64; (* allocated size *) + _ : 64; (* data size *) _ : 32; _ : 32; name_len : 8; @@ -247,29 +298,27 @@ and parse_resident_attr attr_type attr = name : name_len*16 : string } -> let name = ucs2_to_utf8 name name_len in - if !debug then - eprintf "filename: %s (size: %Ld bytes)\n" name data_size + let filename = { + ntfs_name = name + } in + { mft_record with ntfs_filename = Some filename } | { _ } -> if !debug then - eprintf "cannot parse AT_FILE_NAME\n%!" + eprintf "cannot parse AT_FILE_NAME\n%!"; + mft_record ); | _ -> (* unknown attribute - just ignore *) if !debug then - eprintf "unknown resident attribute %lx\n%!" attr_type + eprintf "unknown resident attribute %lx\n%!" attr_type; + mft_record and parse_nonresident_attr attr_type highest_vcn allocated_size data_size initialized_size - mapping_pairs = + mapping_pairs mft_record = match attr_type with | 0x80_l -> (* AT_DATA, ie. the $Data stream *) - if !debug then ( - eprintf "AT_DATA: size = %Ld bytes, highest_vcn = 0x%Lx\n" - data_size highest_vcn; - Bitmatch.hexdump_bitstring Pervasives.stderr mapping_pairs - ); - let lowest_vcn = ~^0 (* see assumption above *) in let runlist = parse_runlist lowest_vcn ~^0 mapping_pairs in if !debug then ( @@ -286,9 +335,16 @@ and parse_nonresident_attr attr_type highest_vcn ) runlist ); + let data = { + ntfs_data_size = data_size; + ntfs_runlist = runlist + } in + { mft_record with ntfs_data = Some data } + | _ -> if !debug then - eprintf "unknown non-resident attribute %lx\n%!" attr_type + eprintf "unknown non-resident attribute %lx\n%!" attr_type; + mft_record (* mapping_pairs is not straightforward and not documented well. See * ntfsprogs libntfs/runlist.c:ntfs_mapping_pairs_decompress @@ -325,8 +381,6 @@ and parse_runlist vcn lcn bits = let lcn = lcn +^ deltalcn in - eprintf "lcnlen = %d, vcnlen = %d\n" lcnlen vcnlen; - ((vcn, deltavcn), Some lcn) :: parse_runlist (vcn +^ deltavcn) lcn rest