(* 'df' command for virtual domains. (C) Copyright 2007 Richard W.M. Jones, Red Hat Inc. http://libvirt.org/ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Support for FAT32. *) 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) (* 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 = 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 *) _ : 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. *) _ : 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 *^ sectorsize /^ 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 (Int63.to_int 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 (Int63.of_int r) | 0xffffff7 -> Bad | (0xffffff8|0xffffff9|0xffffffa|0xffffffb| 0xffffffc|0xffffffd|0xffffffe| 0xfffffff) as r -> Last (Int63.of_int r) | _ -> Used (Int63.of_int 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 and callbacks = let i = ref 0 in fun () -> { fs_cb_uq = (incr i; !i); fs_cb_name = id; fs_cb_printable_name = "DOS/Windows"; fs_cb_offset_is_free = offset_is_free; } (* Register the plugin. *) let () = register_plugin ~filesystem:probe id