/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 \
/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
--- /dev/null
+(* '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