open Printf
open Diskimage_impl
+open Int63.Operators
+
+let id = "fat"
+
+(* Private data that we'll save about the FAT filesystem. *)
+type fat = {
+ fat_dev : device; (* Device. *)
+ fat_blocksize : int63; (* Cluster size. *)
+ fat_sectorsize : int63; (* Sector size. *)
+ fat_sectors_per_cluster : int63; (* Sectors per cluster. *)
+ fat_number_of_sectors : int63; (* Total number of sectors in dev. *)
+ fat_reserved_sectors : int63; (* Offset of the first FAT. *)
+ fat_sectors_per_fat : int63; (* Size of each FAT. *)
+ fat_root_directory_cluster : int63; (* Cluster offset of root dir. *)
+}
(* Private data functions. *)
let attach_private_data, get_private_data =
private_data_functions (fun {fs_cb = {fs_cb_uq = u}} -> u)
-let id = "fat"
+(* FAT entries. *)
+type fat_entry =
+ | Free (* 0x0 *)
+ | Reserved1 (* 0x1 *)
+ | Used of int63 (* Used, points to next cluster *)
+ | Reserved of int63 (* 0x_FFFFFF0 - 0x_FFFFFF6 *)
+ | Bad (* Bad / reserved 0x_FFFFFF7 *)
+ | Last of int63 (* Last cluster in file *)
let rec probe dev =
- raise Not_found (* XXX *)
+ let fs = probe_superblock dev in
+ fs
+
+and probe_superblock dev =
+ (* Load the boot sector / superblock. *)
+ let bits = dev#read_bitstring ~^0 ~^512 in
+
+ (* Standard stuff in a boot sector. *)
+ bitmatch bits with
+ | { _ : 24; (* Jump to boot up code. *)
+ oem_name : 64 : string; (* OEM name. *)
+ bytes_per_sector : 16 : littleendian;
+ sectors_per_cluster : 8 : littleendian;
+ reserved_sectors : 16 : littleendian;
+ 2 : 8; (* Number of FATs. *)
+ 0 : 16; (* Root dir entries, 0 for FAT32 *)
+ 0 : 16; (* Old number of sectors - unused. *)
+ _ : 8; (* Media type, probably 'f8' = HDD *)
+ _ : 16; (* Sectors per FAT, not for FAT32 *)
+ _ : 16; (* Sectors per track. *)
+ _ : 16; (* Heads. *)
+ _ : 32; (* Hidden sectors. *)
+ number_of_sectors : 32 : littleendian;
+ sectors_per_fat : 32 : littleendian;
+ _ : 16; (* FAT32 flags *)
+ _ : 16; (* FAT32 version *)
+ root_directory_cluster : 32 : littleendian;
+ _ : 16; (* FS information sector *)
+ _ : 16; (* Backup of boot sector *)
+ pad : 12*8 : bitstring; (* Reserved *)
+ _ : 8; (* Physical drive number *)
+ _ : 8; (* Reserved *)
+ _ : 8; (* Extended boot signature *)
+ serial : 32 : littleendian; (* Serial number. *)
+ volume_label : 88 : string; (* Volume label. *)
+ "FAT32 " : 64 : string; (* FAT32 identifier. *)
+ code : 420*8 : bitstring; (* Boot code. *)
+ 0x55AA : 16 } -> (* End of bootsector magic. *)
+
+ let blocksize = bytes_per_sector * sectors_per_cluster in
+
+ (* FAT32 forbids cluster size > 32K. *)
+ if blocksize > 32768 then (
+ prerr_endline "FAT: cluster size > 32K";
+ raise Not_found
+ );
+
+ if !debug then
+ eprintf "%s: found FAT32 filesystem with clustersize = %d, label %s\n"
+ dev#name blocksize volume_label;
+
+ let blocksize = Int63.of_int blocksize in
+ let sectorsize = Int63.of_int bytes_per_sector in
+ let sectors_per_cluster = Int63.of_int sectors_per_cluster in
+ let number_of_sectors = Int63.of_int32 number_of_sectors in
+ let reserved_sectors = Int63.of_int reserved_sectors in
+ let sectors_per_fat = Int63.of_int32 sectors_per_fat in
+ let root_directory_cluster = Int63.of_int32 root_directory_cluster in
+
+ (* The blocksize of the filesystem is likely to be quite different
+ * from that of the underlying device, so create an overlay device
+ * with the natural filesystem blocksize.
+ *)
+ let fs_dev = new blocksize_overlay blocksize dev in
+
+ let fat = {
+ fat_dev = fs_dev;
+ fat_blocksize = blocksize;
+ fat_sectorsize = sectorsize;
+ fat_sectors_per_cluster = sectors_per_cluster;
+ fat_number_of_sectors = number_of_sectors;
+ fat_reserved_sectors = reserved_sectors;
+ fat_sectors_per_fat = sectors_per_fat;
+ fat_root_directory_cluster = root_directory_cluster
+ } in
+
+ (* Parse the first FAT to get free space. *)
+ let blocks_used, blocks_avail = parse_fat_freespace fat in
+
+ (* Calculate reserved space. *)
+ let blocks_reserved =
+ let sectors_reserved = reserved_sectors +^ ~^2 *^ sectors_per_fat in
+ sectors_reserved *^ sectors_per_cluster in
+
+ (* Create a filesystem structure. *)
+ let fs = {
+ fs_cb = callbacks ();
+ fs_dev = fs_dev;
+ fs_blocksize = blocksize;
+ fs_blocks_total = number_of_sectors *^ bytes_per_sector /^ blocksize;
+ fs_is_swap = false;
+ fs_blocks_reserved = blocks_reserved;
+ fs_blocks_avail = blocks_avail;
+ fs_blocks_used = blocks_used;
+ fs_inodes_total = ~^0;
+ fs_inodes_reserved = ~^0;
+ fs_inodes_avail = ~^0;
+ fs_inodes_used = ~^0;
+ } in
+
+ attach_private_data fs fat;
+ fs
+
+ | { _ } -> raise Not_found (* Not FAT32 *)
+
+(* Find amount of free space by reading the FAT. *)
+and parse_fat_freespace fat =
+ let used = ref ~^0 and free = ref ~^0 in
+ iter_fat_entries fat (
+ fun cn ->
+ function
+ | Free -> free := !free +^ ~^1
+ | _ -> used := !used +^ ~^1
+ );
+ (!used, !free)
+
+(* Iterate over the FAT. We ignore the first two FAT entries. *)
+and iter_fat_entries { fat_dev = dev; fat_sectorsize = sectorsize;
+ fat_reserved_sectors = start;
+ fat_sectors_per_fat = size } f =
+ let cn = ref ~^0 in
+
+ for i = 0 to size-1 do
+ let sector =
+ dev#read_bitstring ((start +^ Int63.of_int i) *^ sectorsize) sectorsize in
+ let rec loop bits =
+ bitmatch bits with
+ | { e : 28 : littleendian; _ : 4; rest : -1 : bitstring } ->
+ let e =
+ match e with
+ | 0 -> Free
+ | 1 -> Reserved1
+ | (0xffffff0|0xffffff1|0xffffff2|0xffffff3|
+ 0xffffff4|0xffffff5|0xffffff6) as r -> Reserved r
+ | 0xffffff7 -> Bad
+ | (0xffffff8|0xffffff9|0xffffffa|0xffffffb|
+ 0xffffffc|0xffffffd|0xffffffe|
+ 0xfffffff) as r -> Last r
+ | _ -> Used e in
+
+ if !cn >= ~^2 then f !cn e;
+ cn := !cn +^ ~^1;
+ loop rest
+
+ | { _ } ->
+ if !debug then
+ eprintf "iter_fat_entries: unknown FAT entry, ignored\n%!";
+ ()
+ in
+ loop sector
+ done
and offset_is_free _ _ = false