Parses the basic NTFS structures and prints them out.
authorrjones@intel.home.annexia.org <rjones@intel.home.annexia.org>
Fri, 9 May 2008 15:02:35 +0000 (16:02 +0100)
committerrjones@intel.home.annexia.org <rjones@intel.home.annexia.org>
Fri, 9 May 2008 15:02:35 +0000 (16:02 +0100)
lib/.depend
lib/Makefile.in
lib/diskimage.ml
lib/diskimage_ntfs.ml [new file with mode: 0644]
lib/diskimage_ntfs.mli [new file with mode: 0644]

index a121753..5582e9a 100644 (file)
@@ -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 
index 771968c..11a759a 100644 (file)
@@ -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 \
index c4ab516..bdc3d47 100644 (file)
@@ -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 (file)
index 0000000..6c1aafb
--- /dev/null
@@ -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 (file)
index 0000000..52995b3
--- /dev/null
@@ -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