X-Git-Url: http://git.annexia.org/?a=blobdiff_plain;f=lib%2Fdiskimage_fat.ml;h=123d1da5ec66f8ad1e30c1153ff00760824c9c5b;hb=HEAD;hp=1fbeac487bf7059b36d12697ff234a54e346c630;hpb=116b1583a8e27e587e382fb53dbfce3d21ad9b45;p=virt-df.git diff --git a/lib/diskimage_fat.ml b/lib/diskimage_fat.ml index 1fbeac4..123d1da 100644 --- a/lib/diskimage_fat.ml +++ b/lib/diskimage_fat.ml @@ -2,19 +2,20 @@ (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 library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version, + with the OCaml linking exception described in ../COPYING.LIB. - This program is distributed in the hope that it will be useful, + This library 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. + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser 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. + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Support for FAT32. *) @@ -23,15 +24,188 @@ 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 *) + _ : 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