From 554b96246db7b4db7972a0aa50eb33d4d6039ab6 Mon Sep 17 00:00:00 2001 From: "rjones@intel.home.annexia.org" Date: Fri, 9 May 2008 16:02:35 +0100 Subject: [PATCH] Parses the basic NTFS structures and prints them out. --- lib/.depend | 14 ++- lib/Makefile.in | 2 + lib/diskimage.ml | 2 + lib/diskimage_ntfs.ml | 250 +++++++++++++++++++++++++++++++++++++++++++++++++ lib/diskimage_ntfs.mli | 22 +++++ 5 files changed, 286 insertions(+), 4 deletions(-) create mode 100644 lib/diskimage_ntfs.ml create mode 100644 lib/diskimage_ntfs.mli diff --git a/lib/.depend b/lib/.depend index a121753..5582e9a 100644 --- a/lib/.depend +++ b/lib/.depend @@ -6,6 +6,8 @@ diskimage_ext2.cmo: int63.cmi diskimage_impl.cmi \ /usr/lib64/ocaml/bitmatch/bitmatch.cmi diskimage_ext2.cmi diskimage_ext2.cmx: int63.cmx diskimage_impl.cmx \ /usr/lib64/ocaml/bitmatch/bitmatch.cmi diskimage_ext2.cmi +diskimage_fat.cmo: diskimage_impl.cmi diskimage_fat.cmi +diskimage_fat.cmx: diskimage_impl.cmx diskimage_fat.cmi diskimage_impl.cmo: int63.cmi diskimage_impl.cmi diskimage_impl.cmx: int63.cmx diskimage_impl.cmi diskimage_linux_swap.cmo: int63.cmi diskimage_impl.cmi \ @@ -30,12 +32,16 @@ diskimage_mbr.cmo: int63.cmi diskimage_impl.cmi \ /usr/lib64/ocaml/bitmatch/bitmatch.cmi diskimage_mbr.cmi diskimage_mbr.cmx: int63.cmx diskimage_impl.cmx \ /usr/lib64/ocaml/bitmatch/bitmatch.cmi diskimage_mbr.cmi -diskimage.cmo: diskimage_mbr.cmi diskimage_lvm2.cmi \ +diskimage.cmo: diskimage_ntfs.cmi diskimage_mbr.cmi diskimage_lvm2.cmi \ diskimage_linux_swsuspend.cmi diskimage_linux_swap.cmi diskimage_impl.cmi \ - diskimage_ext2.cmi diskimage.cmi -diskimage.cmx: diskimage_mbr.cmx diskimage_lvm2.cmx \ + diskimage_fat.cmi diskimage_ext2.cmi diskimage.cmi +diskimage.cmx: diskimage_ntfs.cmx diskimage_mbr.cmx diskimage_lvm2.cmx \ diskimage_linux_swsuspend.cmx diskimage_linux_swap.cmx diskimage_impl.cmx \ - diskimage_ext2.cmx diskimage.cmi + diskimage_fat.cmx diskimage_ext2.cmx diskimage.cmi +diskimage_ntfs.cmo: int63.cmi diskimage_impl.cmi \ + /usr/lib64/ocaml/bitmatch/bitmatch.cmi diskimage_ntfs.cmi +diskimage_ntfs.cmx: int63.cmx diskimage_impl.cmx \ + /usr/lib64/ocaml/bitmatch/bitmatch.cmi diskimage_ntfs.cmi int63.cmo: int63.cmi int63.cmx: int63.cmi int63_on_32.cmo: int63_on_32.cmi diff --git a/lib/Makefile.in b/lib/Makefile.in index 771968c..11a759a 100644 --- a/lib/Makefile.in +++ b/lib/Makefile.in @@ -41,6 +41,8 @@ OBJS := int63.cmo \ OBJS += diskimage_ext2.cmo \ diskimage_linux_swap.cmo \ diskimage_linux_swsuspend.cmo \ + diskimage_fat.cmo \ + diskimage_ntfs.cmo \ diskimage_lvm2_metadata.cmo \ diskimage_lvm2_parser.cmo \ diskimage_lvm2_lexer.cmo \ diff --git a/lib/diskimage.ml b/lib/diskimage.ml index c4ab516..bdc3d47 100644 --- a/lib/diskimage.ml +++ b/lib/diskimage.ml @@ -25,5 +25,7 @@ include Diskimage_impl let _ = Diskimage_ext2.id let _ = Diskimage_linux_swap.id let _ = Diskimage_linux_swsuspend.id +let _ = Diskimage_fat.id +let _ = Diskimage_ntfs.id let _ = Diskimage_lvm2.id let _ = Diskimage_mbr.id diff --git a/lib/diskimage_ntfs.ml b/lib/diskimage_ntfs.ml new file mode 100644 index 0000000..6c1aafb --- /dev/null +++ b/lib/diskimage_ntfs.ml @@ -0,0 +1,250 @@ +(* 'df' command for virtual domains. + (C) Copyright 2007 Richard W.M. Jones, Red Hat Inc. + http://libvirt.org/ + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + Support for NTFS. +*) + +open Unix +open Printf + +open Diskimage_impl +open Int63.Operators + +(* Private data functions. *) +let attach_private_data, get_private_data = + private_data_functions (fun {fs_cb = {fs_cb_uq = u}} -> u) + +let id = "ntfs" + +let rec probe dev = + (* Load the boot sector. *) + let bits = dev#read_bitstring ~^0 ~^512 in + + (* Most of this data comes from ntfsprogs' layout.h header file. *) + bitmatch bits with + | { _ : 24; (* Jump to boot up code. *) + "NTFS " : 64 : string; (* NTFS OEM ID (magic). *) + bytes_per_sector : 16 : littleendian; + sectors_per_cluster : 8 : littleendian; + _ : 16; (* Reserved sectors - unused. *) + _ : 8; (* FATs - unused. *) + _ : 16; (* Root entries - unused. *) + _ : 16; (* Sectors - unused. *) + _ : 8; (* Media type, probably 'f8' = HDD *) + _ : 16; (* Sectors per FAT - unused. *) + _ : 16; (* Sectors per track. *) + _ : 16; (* Heads. *) + _ : 32; (* Hidden sectors. *) + _ : 32; (* Large sectors. *) + _ : 8; (* Physical drive, 0 = FDD, 0x80 = HDD*) + _ : 8; (* Current head. *) + _ : 8; (* Extended boot signature. *) + _ : 8; (* Reserved. *) + number_of_sectors : 64 : littleendian; + mft_lcn : 64 : littleendian; (* MFT location in clusters. *) + mftmirr_lcn : 64 : littleendian; (* MFT mirror location. *) + clusters_per_mft_record : 8; + _ : 24; + clusters_per_index_record : 8; + _ : 24; + volume_serial_number : 64 : littleendian; + checksum : 32 : littleendian; (* Boot sector checksum. *) + code : 8 * 426 : bitstring; (* Boot code. *) + 0x55AA : 16 } -> (* End of bootsector magic. *) + + let blocksize = bytes_per_sector * sectors_per_cluster in + + if !debug then + eprintf "%s: NTFS boot sector with blocksize = %d, serial = %Lx\n%!" + dev#name blocksize volume_serial_number; + + let blocksize = Int63.of_int blocksize in + let mft_lcn = Int63.of_int64 mft_lcn *^ blocksize in + let mft_size = Int63.of_int clusters_per_mft_record *^ blocksize in + + (* Read the whole of the MFT. *) + let bits = dev#read_bitstring mft_lcn mft_size in + (* ... and turn the MFT into records. *) + let rec loop bits = + if Bitmatch.bitstring_length bits > 0 then ( + bitmatch bits with + | { "FILE" : 32 : string; + (* Assume 3 USAs starting at offset 0x30. XXX? *) + 0x30 : 16 : littleendian; + 0x03 : 16 : littleendian; + _ : 64; (* lsn *) + _ : 16; (* sequence_number *) + _ : 16; (* link_count *) + _ : 16; (* attrs_offset *) + _ : 16; (* MFT_RECORD_FLAGS *) + bytes_in_use : 32 : littleendian; + record_size : 32 : littleendian; + _ : 64; (* base_mft_record *) + _ : 16; (* next_attr_instance *) + _ : 16; (* reserved *) + _ : 32; (* mft_record_number *) + _ : 64; (* USN, 3 * USAs -- see above. *) + + (* The attributes. Subtract header size (0x30 bytes) + * and space for the USN/USAs (8 bytes). + *) + attrs : (Int32.to_int record_size - 0x30 - 8)*8 : bitstring; + + (* Subsequent MFT records: *) + rest : -1 : bitstring } -> + + if !debug then + eprintf "got an MFT record, now parsing attributes ...\n%!"; + + (* Parse the MFT record attributes. *) + let rec loop2 attrs = + bitmatch attrs with + | { 0xFFFFFFFF_l : 32 : littleendian } -> (* AT_END *) + if !debug then + eprintf "found AT_END, end of attributes\n%!"; + () + + | { attr_type : 32 : littleendian; + attr_size : 32 : littleendian; + 0 : 8; (* means attribute is resident *) + pad : 24*8 - 8 - 64 : bitstring; (* actually meaningful *) + attr : (Int32.to_int attr_size - 24) * 8 : bitstring; + rest : -1 : bitstring } -> + + (match attr_type with + | 0x30_l -> (* AT_FILE_NAME *) + (bitmatch attr with + | { _ : 64; (* parent directory ref *) + _ : 64; (* creation time *) + _ : 64; (* last change time *) + _ : 64; (* last MFT change time *) + _ : 64; (* last access time *) + allocated_size : 64 : littleendian; + data_size : 64 : littleendian; + _ : 32; + _ : 32; + name_len : 8; + name_type_flags : 8; + 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 + + | { _ } -> + if !debug then + eprintf "cannot parse AT_FILE_NAME\n%!"; + ); + | _ -> + if !debug then + eprintf "unknown resident attribute %lx\n%!" + attr_type + ); + + loop2 rest + + | { attr_type : 32 : littleendian; + attr_size : 32 : littleendian; + 1 : 8; (* non-resident attribute *) + pad : (Int32.to_int attr_size - 9) * 8 : bitstring; + rest : -1 : bitstring } -> + if !debug then + eprintf "cannot parse non-resident attr %lx\n%!" + attr_type; + loop2 rest + + | { _ } -> + if !debug then + eprintf "corrupt MFT attribute entry\n%!" + in + loop2 attrs; + + loop 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 } -> + () + ) in + let mft_records = loop bits in + + + + + + raise Not_found; + + | { _ } -> raise Not_found (* Not an NTFS boot sector. *) + + +(* Poor man's little-endian UCS-2 to UTF-8 conversion. + * XXX Should use Camomile. + *) +and ucs2_to_utf8 name len = + (* Calculate length of final string. *) + let outlen = ref 0 in + let j = ref 0 in + for i = 0 to len-1 do + let j' = !j in + j := j' + 2; + let c0 = Char.code name.[j'] and c1 = Char.code name.[j'+1] in + let c = c0 + c1 * 256 in + if c < 128 then incr outlen + else if c < 0x800 then outlen := !outlen + 2 + else outlen := !outlen + 3 + done; + let outstr = String.create !outlen in + j := 0; outlen := 0; + for i = 0 to len-1 do + let j' = !j in + j := j' + 2; + let c0 = Char.code name.[j'] and c1 = Char.code name.[j'+1] in + let c = c0 + c1 * 256 in + if c < 128 then ( + outstr.[!outlen] <- Char.chr c; + incr outlen + ) else if c < 0x800 then ( + outstr.[!outlen] <- Char.chr (0b11000000 lor (c lsr 6)); + outstr.[!outlen+1] <- Char.chr (0b10000000 lor (c land 0b00111111)); + outlen := !outlen + 2 + ) else ( + outstr.[!outlen] <- Char.chr (0b11100000 lor (c lsr 12)); + outstr.[!outlen+1] <- Char.chr (0b10000000 lor ((c lsr 6) lor 0b00111111)); + outstr.[!outlen+2] <- Char.chr (0b10000000 lor (c land 0b00111111)); + outlen := !outlen + 3 + ) + done; + outstr + +and offset_is_free _ _ = false + +and callbacks = + let i = ref 0 in + fun () -> { + fs_cb_uq = (incr i; !i); + fs_cb_name = id; + fs_cb_printable_name = "Windows NTFS"; + fs_cb_offset_is_free = offset_is_free; + } + +(* Register the plugin. *) +let () = register_plugin ~filesystem:probe id diff --git a/lib/diskimage_ntfs.mli b/lib/diskimage_ntfs.mli new file mode 100644 index 0000000..52995b3 --- /dev/null +++ b/lib/diskimage_ntfs.mli @@ -0,0 +1,22 @@ +(* 'df' command for virtual domains. + (C) Copyright 2007-2008 Richard W.M. Jones, Red Hat Inc. + http://libvirt.org/ + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + *) + +(**/**) + +val id : string -- 1.8.3.1