From 2bb5edc3044cad96ebf732706d5f608ba25e7768 Mon Sep 17 00:00:00 2001 From: "rjones@intel.home.annexia.org" Date: Tue, 13 May 2008 16:56:52 +0100 Subject: [PATCH] FAT32 used/available space parsing done. --- lib/diskimage_fat.ml | 176 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 174 insertions(+), 2 deletions(-) diff --git a/lib/diskimage_fat.ml b/lib/diskimage_fat.ml index 1fbeac4..d428b95 100644 --- a/lib/diskimage_fat.ml +++ b/lib/diskimage_fat.ml @@ -23,15 +23,187 @@ open Unix 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 -- 1.8.3.1