1 (* 'df' command for virtual domains.
2 (C) Copyright 2007 Richard W.M. Jones, Red Hat Inc.
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.
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.
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
31 (* Private data that we'll save about the FAT filesystem. *)
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. *)
43 (* Private data functions. *)
44 let attach_private_data, get_private_data =
45 private_data_functions (fun {fs_cb = {fs_cb_uq = u}} -> u)
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 *)
57 let fs = probe_superblock dev in
60 and probe_superblock dev =
61 (* Load the boot sector / superblock. *)
62 let bits = dev#read_bitstring ~^0 ~^512 in
64 (* Standard stuff in a boot sector. *)
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. *)
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 *)
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. *)
96 let blocksize = bytes_per_sector * sectors_per_cluster in
98 (* FAT32 forbids cluster size > 32K. *)
99 if blocksize > 32768 then (
100 prerr_endline "FAT: cluster size > 32K";
105 eprintf "%s: found FAT32 filesystem with clustersize = %d, label %s\n"
106 dev#name blocksize volume_label;
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
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.
120 let fs_dev = new blocksize_overlay blocksize dev in
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
133 (* Parse the first FAT to get free space. *)
134 let blocks_used, blocks_avail = parse_fat_freespace fat in
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
141 (* Create a filesystem structure. *)
143 fs_cb = callbacks ();
145 fs_blocksize = blocksize;
146 fs_blocks_total = number_of_sectors *^ sectorsize /^ blocksize;
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;
157 attach_private_data fs fat;
160 | { _ } -> raise Not_found (* Not FAT32 *)
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 (
168 | Free -> free := !free +^ ~^1
169 | _ -> used := !used +^ ~^1
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 =
179 for i = 0 to (Int63.to_int size)-1 do
181 dev#read_bitstring ((start +^ Int63.of_int i) *^ sectorsize) sectorsize in
184 | { e : 28 : littleendian; _ : 4; rest : -1 : bitstring } ->
189 | (0xffffff0|0xffffff1|0xffffff2|0xffffff3|
190 0xffffff4|0xffffff5|0xffffff6) as r ->
191 Reserved (Int63.of_int r)
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
198 if !cn >= ~^2 then f !cn e;
204 eprintf "iter_fat_entries: unknown FAT entry, ignored\n%!";
210 and offset_is_free _ _ = false
215 fs_cb_uq = (incr i; !i);
217 fs_cb_printable_name = "DOS/Windows";
218 fs_cb_offset_is_free = offset_is_free;
221 (* Register the plugin. *)
222 let () = register_plugin ~filesystem:probe id