6c1aafbba497dadd2d7d9d8d6f446b48dcd9b0e6
[virt-df.git] / lib / diskimage_ntfs.ml
1 (* 'df' command for virtual domains.
2    (C) Copyright 2007 Richard W.M. Jones, Red Hat Inc.
3    http://libvirt.org/
4
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.
9
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.
14
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.
18
19    Support for NTFS.
20 *)
21
22 open Unix
23 open Printf
24
25 open Diskimage_impl
26 open Int63.Operators
27
28 (* Private data functions. *)
29 let attach_private_data, get_private_data =
30   private_data_functions (fun {fs_cb = {fs_cb_uq = u}} -> u)
31
32 let id = "ntfs"
33
34 let rec probe dev =
35   (* Load the boot sector. *)
36   let bits = dev#read_bitstring ~^0 ~^512 in
37
38   (* Most of this data comes from ntfsprogs' layout.h header file. *)
39   bitmatch bits with
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. *)
51       _ : 16;                           (* Heads. *)
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;
62       _ : 24;
63       clusters_per_index_record : 8;
64       _ : 24;
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. *)
69
70       let blocksize = bytes_per_sector * sectors_per_cluster in
71
72       if !debug then
73         eprintf "%s: NTFS boot sector with blocksize = %d, serial = %Lx\n%!"
74           dev#name blocksize volume_serial_number;
75
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
79
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. *)
83       let rec loop bits =
84         if Bitmatch.bitstring_length bits > 0 then (
85           bitmatch bits with
86           | { "FILE" : 32 : string;
87               (* Assume 3 USAs starting at offset 0x30. XXX? *)
88               0x30 : 16 : littleendian;
89               0x03 : 16 : littleendian;
90               _ : 64;                   (* lsn *)
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. *)
102
103               (* The attributes.  Subtract header size (0x30 bytes)
104                * and space for the USN/USAs (8 bytes).
105                *)
106               attrs : (Int32.to_int record_size - 0x30 - 8)*8 : bitstring;
107
108               (* Subsequent MFT records: *)
109               rest : -1 : bitstring } ->
110
111               if !debug then
112                 eprintf "got an MFT record, now parsing attributes ...\n%!";
113
114               (* Parse the MFT record attributes. *)
115               let rec loop2 attrs =
116                 bitmatch attrs with
117                 | { 0xFFFFFFFF_l : 32 : littleendian } -> (* AT_END *)
118                     if !debug then
119                       eprintf "found AT_END, end of attributes\n%!";
120                     ()
121
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 } ->
128
129                     (match attr_type with
130                      | 0x30_l ->        (* AT_FILE_NAME *)
131                          (bitmatch attr with
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;
139                               _ : 32;
140                               _ : 32;
141                               name_len : 8;
142                               name_type_flags : 8;
143                               name : name_len*16 : string } ->
144
145                               let name = ucs2_to_utf8 name name_len in
146                               if !debug then
147                                 eprintf "filename: %s (size: %Ld bytes)\n"
148                                   name data_size
149
150                           | { _ } ->
151                               if !debug then
152                                 eprintf "cannot parse AT_FILE_NAME\n%!";
153                          );
154                      | _ ->
155                          if !debug then
156                            eprintf "unknown resident attribute %lx\n%!"
157                              attr_type
158                     );
159
160                     loop2 rest
161
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 } ->
167                     if !debug then
168                       eprintf "cannot parse non-resident attr %lx\n%!"
169                         attr_type;
170                     loop2 rest
171
172                 | { _ } ->
173                     if !debug then
174                       eprintf "corrupt MFT attribute entry\n%!"
175               in
176               loop2 attrs;
177
178               loop rest                 (* loop rest of MFT records *)
179
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.
183            * XXX?
184            *)
185           | { 0x00000000_l : 32 } ->
186               ()
187         ) in
188       let mft_records = loop bits in
189
190       
191
192
193
194       raise Not_found;
195
196   | { _ } -> raise Not_found            (* Not an NTFS boot sector. *)
197
198
199 (* Poor man's little-endian UCS-2 to UTF-8 conversion.
200  * XXX Should use Camomile.
201  *)
202 and ucs2_to_utf8 name len =
203   (* Calculate length of final string. *)
204   let outlen = ref 0 in
205   let j = ref 0 in
206   for i = 0 to len-1 do
207     let j' = !j in
208     j := j' + 2;
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
214   done;
215   let outstr = String.create !outlen in
216   j := 0; outlen := 0;
217   for i = 0 to len-1 do
218     let j' = !j in
219     j := j' + 2;
220     let c0 = Char.code name.[j'] and c1 = Char.code name.[j'+1] in
221     let c = c0 + c1 * 256 in
222     if c < 128 then (
223       outstr.[!outlen] <- Char.chr c;
224       incr outlen
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
229     ) else (
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
234     )
235   done;
236   outstr
237
238 and offset_is_free _ _ = false
239
240 and callbacks =
241   let i = ref 0 in
242   fun () -> {
243     fs_cb_uq = (incr i; !i);
244     fs_cb_name = id;
245     fs_cb_printable_name = "Windows NTFS";
246     fs_cb_offset_is_free = offset_is_free;
247   }
248
249 (* Register the plugin. *)
250 let () = register_plugin ~filesystem:probe id