1 (* 'df' command for virtual domains.
2 (C) Copyright 2007 Richard W.M. Jones, Red Hat Inc.
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 (* Private data functions. *)
29 let attach_private_data, get_private_data =
30 private_data_functions (fun {fs_cb = {fs_cb_uq = u}} -> u)
35 (* Load the boot sector. *)
36 let bits = dev#read_bitstring ~^0 ~^512 in
38 (* Most of this data comes from ntfsprogs' layout.h header file. *)
40 | { _ : 24; (* Jump to boot up code. *)
41 "NTFS " : 64 : string; (* NTFS OEM ID (magic). *)
42 bytes_per_sector : 16 : littleendian;
43 sectors_per_cluster : 8 : littleendian;
44 _ : 16; (* Reserved sectors - unused. *)
45 _ : 8; (* FATs - unused. *)
46 _ : 16; (* Root entries - unused. *)
47 _ : 16; (* Sectors - unused. *)
48 _ : 8; (* Media type, probably 'f8' = HDD *)
49 _ : 16; (* Sectors per FAT - unused. *)
50 _ : 16; (* Sectors per track. *)
52 _ : 32; (* Hidden sectors. *)
53 _ : 32; (* Large sectors. *)
54 _ : 8; (* Physical drive, 0 = FDD, 0x80 = HDD*)
55 _ : 8; (* Current head. *)
56 _ : 8; (* Extended boot signature. *)
57 _ : 8; (* Reserved. *)
58 number_of_sectors : 64 : littleendian;
59 mft_lcn : 64 : littleendian; (* MFT location in clusters. *)
60 mftmirr_lcn : 64 : littleendian; (* MFT mirror location. *)
61 clusters_per_mft_record : 8;
63 clusters_per_index_record : 8;
65 volume_serial_number : 64 : littleendian;
66 checksum : 32 : littleendian; (* Boot sector checksum. *)
67 code : 8 * 426 : bitstring; (* Boot code. *)
68 0x55AA : 16 } -> (* End of bootsector magic. *)
70 let blocksize = bytes_per_sector * sectors_per_cluster in
73 eprintf "%s: NTFS boot sector with blocksize = %d, serial = %Lx\n%!"
74 dev#name blocksize volume_serial_number;
76 let blocksize = Int63.of_int blocksize in
77 let mft_lcn = Int63.of_int64 mft_lcn *^ blocksize in
78 let mft_size = Int63.of_int clusters_per_mft_record *^ blocksize in
80 (* Read the whole of the MFT. *)
81 let bits = dev#read_bitstring mft_lcn mft_size in
82 (* ... and turn the MFT into records. *)
84 if Bitmatch.bitstring_length bits > 0 then (
86 | { "FILE" : 32 : string;
87 (* Assume 3 USAs starting at offset 0x30. XXX? *)
88 0x30 : 16 : littleendian;
89 0x03 : 16 : littleendian;
91 _ : 16; (* sequence_number *)
92 _ : 16; (* link_count *)
93 _ : 16; (* attrs_offset *)
94 _ : 16; (* MFT_RECORD_FLAGS *)
95 bytes_in_use : 32 : littleendian;
96 record_size : 32 : littleendian;
97 _ : 64; (* base_mft_record *)
98 _ : 16; (* next_attr_instance *)
99 _ : 16; (* reserved *)
100 _ : 32; (* mft_record_number *)
101 _ : 64; (* USN, 3 * USAs -- see above. *)
103 (* The attributes. Subtract header size (0x30 bytes)
104 * and space for the USN/USAs (8 bytes).
106 attrs : (Int32.to_int record_size - 0x30 - 8)*8 : bitstring;
108 (* Subsequent MFT records: *)
109 rest : -1 : bitstring } ->
112 eprintf "got an MFT record, now parsing attributes ...\n%!";
114 (* Parse the MFT record attributes. *)
115 let rec loop2 attrs =
117 | { 0xFFFFFFFF_l : 32 : littleendian } -> (* AT_END *)
119 eprintf "found AT_END, end of attributes\n%!";
122 | { attr_type : 32 : littleendian;
123 attr_size : 32 : littleendian;
124 0 : 8; (* means attribute is resident *)
125 pad : 24*8 - 8 - 64 : bitstring; (* actually meaningful *)
126 attr : (Int32.to_int attr_size - 24) * 8 : bitstring;
127 rest : -1 : bitstring } ->
129 (match attr_type with
130 | 0x30_l -> (* AT_FILE_NAME *)
132 | { _ : 64; (* parent directory ref *)
133 _ : 64; (* creation time *)
134 _ : 64; (* last change time *)
135 _ : 64; (* last MFT change time *)
136 _ : 64; (* last access time *)
137 allocated_size : 64 : littleendian;
138 data_size : 64 : littleendian;
143 name : name_len*16 : string } ->
145 let name = ucs2_to_utf8 name name_len in
147 eprintf "filename: %s (size: %Ld bytes)\n"
152 eprintf "cannot parse AT_FILE_NAME\n%!";
156 eprintf "unknown resident attribute %lx\n%!"
162 | { attr_type : 32 : littleendian;
163 attr_size : 32 : littleendian;
164 1 : 8; (* non-resident attribute *)
165 pad : (Int32.to_int attr_size - 9) * 8 : bitstring;
166 rest : -1 : bitstring } ->
168 eprintf "cannot parse non-resident attr %lx\n%!"
174 eprintf "corrupt MFT attribute entry\n%!"
178 loop rest (* loop rest of MFT records *)
180 (* Just assume that the end of the list of MFT records
181 * is marked by all zeroes. This seems to be the
182 * case, but not sure if it is generally true.
185 | { 0x00000000_l : 32 } ->
188 let mft_records = loop bits in
196 | { _ } -> raise Not_found (* Not an NTFS boot sector. *)
199 (* Poor man's little-endian UCS-2 to UTF-8 conversion.
200 * XXX Should use Camomile.
202 and ucs2_to_utf8 name len =
203 (* Calculate length of final string. *)
204 let outlen = ref 0 in
206 for i = 0 to len-1 do
209 let c0 = Char.code name.[j'] and c1 = Char.code name.[j'+1] in
210 let c = c0 + c1 * 256 in
211 if c < 128 then incr outlen
212 else if c < 0x800 then outlen := !outlen + 2
213 else outlen := !outlen + 3
215 let outstr = String.create !outlen in
217 for i = 0 to len-1 do
220 let c0 = Char.code name.[j'] and c1 = Char.code name.[j'+1] in
221 let c = c0 + c1 * 256 in
223 outstr.[!outlen] <- Char.chr c;
225 ) else if c < 0x800 then (
226 outstr.[!outlen] <- Char.chr (0b11000000 lor (c lsr 6));
227 outstr.[!outlen+1] <- Char.chr (0b10000000 lor (c land 0b00111111));
228 outlen := !outlen + 2
230 outstr.[!outlen] <- Char.chr (0b11100000 lor (c lsr 12));
231 outstr.[!outlen+1] <- Char.chr (0b10000000 lor ((c lsr 6) lor 0b00111111));
232 outstr.[!outlen+2] <- Char.chr (0b10000000 lor (c land 0b00111111));
233 outlen := !outlen + 3
238 and offset_is_free _ _ = false
243 fs_cb_uq = (incr i; !i);
245 fs_cb_printable_name = "Windows NTFS";
246 fs_cb_offset_is_free = offset_is_free;
249 (* Register the plugin. *)
250 let () = register_plugin ~filesystem:probe id