Clarify licensing for Debian.
[virt-df.git] / lib / diskimage_fat.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 library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2 of the License, or (at your option) any later version,
9    with the OCaml linking exception described in ../COPYING.LIB.
10
11    This library is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Lesser General Public License for more details.
15
16    You should have received a copy of the GNU Lesser General Public
17    License along with this library; if not, write to the Free Software
18    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
19
20    Support for FAT32.
21 *)
22
23 open Unix
24 open Printf
25
26 open Diskimage_impl
27 open Int63.Operators
28
29 let id = "fat"
30
31 (* Private data that we'll save about the FAT filesystem. *)
32 type fat = {
33   fat_dev : device;                     (* Device. *)
34   fat_blocksize : int63;                (* Cluster size. *)
35   fat_sectorsize : int63;               (* Sector size. *)
36   fat_sectors_per_cluster : int63;      (* Sectors per cluster. *)
37   fat_number_of_sectors : int63;        (* Total number of sectors in dev. *)
38   fat_reserved_sectors : int63;         (* Offset of the first FAT. *)
39   fat_sectors_per_fat : int63;          (* Size of each FAT. *)
40   fat_root_directory_cluster : int63;   (* Cluster offset of root dir. *)
41 }
42
43 (* Private data functions. *)
44 let attach_private_data, get_private_data =
45   private_data_functions (fun {fs_cb = {fs_cb_uq = u}} -> u)
46
47 (* FAT entries. *)
48 type fat_entry =
49   | Free                                (* 0x0 *)
50   | Reserved1                           (* 0x1 *)
51   | Used of int63                       (* Used, points to next cluster *)
52   | Reserved of int63                   (* 0x_FFFFFF0 - 0x_FFFFFF6 *)
53   | Bad                                 (* Bad / reserved 0x_FFFFFF7 *)
54   | Last of int63                       (* Last cluster in file *)
55
56 let rec probe dev =
57   let fs = probe_superblock dev in
58   fs
59
60 and probe_superblock dev =
61     (* Load the boot sector / superblock. *)
62   let bits = dev#read_bitstring ~^0 ~^512 in
63
64   (* Standard stuff in a boot sector. *)
65   bitmatch bits with
66   | { _ : 24;                           (* Jump to boot up code. *)
67       oem_name : 64 : string;           (* OEM name. *)
68       bytes_per_sector : 16 : littleendian;
69       sectors_per_cluster : 8 : littleendian;
70       reserved_sectors : 16 : littleendian;
71       2 : 8;                            (* Number of FATs. *)
72       0 : 16;                           (* Root dir entries, 0 for FAT32 *)
73       0 : 16;                           (* Old number of sectors - unused. *)
74       _ : 8;                            (* Media type, probably 'f8' = HDD *)
75       _ : 16;                           (* Sectors per FAT, not for FAT32 *)
76       _ : 16;                           (* Sectors per track. *)
77       _ : 16;                           (* Heads. *)
78       _ : 32;                           (* Hidden sectors. *)
79       number_of_sectors : 32 : littleendian;
80       sectors_per_fat : 32 : littleendian;
81       _ : 16;                           (* FAT32 flags *)
82       _ : 16;                           (* FAT32 version *)
83       root_directory_cluster : 32 : littleendian;
84       _ : 16;                           (* FS information sector *)
85       _ : 16;                           (* Backup of boot sector *)
86       _ : 12*8 : bitstring;             (* Reserved *)
87       _ : 8;                            (* Physical drive number *)
88       _ : 8;                            (* Reserved *)
89       _ : 8;                            (* Extended boot signature *)
90       serial : 32 : littleendian;       (* Serial number. *)
91       volume_label : 88 : string;       (* Volume label. *)
92       "FAT32   " : 64 : string;         (* FAT32 identifier. *)
93       _ : 420*8 : bitstring;            (* Boot code. *)
94       0x55AA : 16 } ->                  (* End of bootsector magic. *)
95
96       let blocksize = bytes_per_sector * sectors_per_cluster in
97
98       (* FAT32 forbids cluster size > 32K. *)
99       if blocksize > 32768 then (
100         prerr_endline "FAT: cluster size > 32K";
101         raise Not_found
102       );
103
104       if !debug then
105         eprintf "%s: found FAT32 filesystem with clustersize = %d, label %s\n"
106           dev#name blocksize volume_label;
107
108       let blocksize = Int63.of_int blocksize in
109       let sectorsize = Int63.of_int bytes_per_sector in
110       let sectors_per_cluster = Int63.of_int sectors_per_cluster in
111       let number_of_sectors = Int63.of_int32 number_of_sectors in
112       let reserved_sectors = Int63.of_int reserved_sectors in
113       let sectors_per_fat = Int63.of_int32 sectors_per_fat in
114       let root_directory_cluster = Int63.of_int32 root_directory_cluster in
115
116       (* The blocksize of the filesystem is likely to be quite different
117        * from that of the underlying device, so create an overlay device
118        * with the natural filesystem blocksize.
119        *)
120       let fs_dev = new blocksize_overlay blocksize dev in
121
122       let fat = {
123         fat_dev = fs_dev;
124         fat_blocksize = blocksize;
125         fat_sectorsize = sectorsize;
126         fat_sectors_per_cluster = sectors_per_cluster;
127         fat_number_of_sectors = number_of_sectors;
128         fat_reserved_sectors = reserved_sectors;
129         fat_sectors_per_fat = sectors_per_fat;
130         fat_root_directory_cluster = root_directory_cluster
131       } in
132
133       (* Parse the first FAT to get free space. *)
134       let blocks_used, blocks_avail = parse_fat_freespace fat in
135
136       (* Calculate reserved space. *)
137       let blocks_reserved =
138         let sectors_reserved = reserved_sectors +^ ~^2 *^ sectors_per_fat in
139         sectors_reserved *^ sectors_per_cluster in
140
141       (* Create a filesystem structure. *)
142       let fs = {
143         fs_cb = callbacks ();
144         fs_dev = fs_dev;
145         fs_blocksize = blocksize;
146         fs_blocks_total = number_of_sectors *^ sectorsize /^ blocksize;
147         fs_is_swap = false;
148         fs_blocks_reserved = blocks_reserved;
149         fs_blocks_avail = blocks_avail;
150         fs_blocks_used = blocks_used;
151         fs_inodes_total = ~^0;
152         fs_inodes_reserved = ~^0;
153         fs_inodes_avail = ~^0;
154         fs_inodes_used = ~^0;
155       } in
156
157       attach_private_data fs fat;
158       fs
159
160   | { _ } -> raise Not_found            (* Not FAT32 *)
161
162 (* Find amount of free space by reading the FAT. *)
163 and parse_fat_freespace fat =
164   let used = ref ~^0 and free = ref ~^0 in
165   iter_fat_entries fat (
166     fun cn ->
167       function
168       | Free -> free := !free +^ ~^1
169       | _ -> used := !used +^ ~^1
170   );
171   (!used, !free)
172
173 (* Iterate over the FAT.  We ignore the first two FAT entries. *)
174 and iter_fat_entries { fat_dev = dev; fat_sectorsize = sectorsize;
175                        fat_reserved_sectors = start;
176                        fat_sectors_per_fat = size } f =
177   let cn = ref ~^0 in
178
179   for i = 0 to (Int63.to_int size)-1 do
180     let sector =
181       dev#read_bitstring ((start +^ Int63.of_int i) *^ sectorsize) sectorsize in
182     let rec loop bits =
183       bitmatch bits with
184       | { e : 28 : littleendian; _ : 4; rest : -1 : bitstring } ->
185           let e =
186             match e with
187             | 0 -> Free
188             | 1 -> Reserved1
189             | (0xffffff0|0xffffff1|0xffffff2|0xffffff3|
190                    0xffffff4|0xffffff5|0xffffff6) as r ->
191                 Reserved (Int63.of_int r)
192             | 0xffffff7 -> Bad
193             | (0xffffff8|0xffffff9|0xffffffa|0xffffffb|
194                    0xffffffc|0xffffffd|0xffffffe|
195                        0xfffffff) as r -> Last (Int63.of_int r)
196             | _ -> Used (Int63.of_int e) in
197
198           if !cn >= ~^2 then f !cn e;
199           cn := !cn +^ ~^1;
200           loop rest
201
202         | { _ } ->
203             if !debug then
204               eprintf "iter_fat_entries: unknown FAT entry, ignored\n%!";
205             ()
206     in
207     loop sector
208   done
209
210 and offset_is_free _ _ = false
211
212 and callbacks =
213   let i = ref 0 in
214   fun () -> {
215     fs_cb_uq = (incr i; !i);
216     fs_cb_name = id;
217     fs_cb_printable_name = "DOS/Windows";
218     fs_cb_offset_is_free = offset_is_free;
219   }
220
221 (* Register the plugin. *)
222 let () = register_plugin ~filesystem:probe id