(* '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 EXT2/EXT3 filesystems. *) open Unix open Printf open Diskimage_impl open Int63.Operators let ( +* ) = Int32.add let ( -* ) = Int32.sub let ( ** ) = Int32.mul let ( /* ) = Int32.div (* The fields in an ext2/3 superblock. *) type ext2_sb = { s_inodes_count : int32; s_blocks_count : int32; s_r_blocks_count : int32; s_free_blocks_count : int32; s_free_inodes_count : int32; s_first_data_block : int32; s_log_block_size : int32; s_log_frag_size : int32; s_blocks_per_group : int32; s_frags_per_group : int32; s_inodes_per_group : int32; s_mtime : int32; s_wtime : int32; s_mnt_count : int; s_max_mnt_count : int; s_state : int; s_errors : int; s_minor_rev_level : int; s_lastcheck : int32; s_checkinterval : int32; s_creator_os : int32; s_rev_level : int32; s_def_resuid : int; s_def_resgid : int; s_first_ino : int32; s_inode_size : int; s_block_group_nr : int; s_feature_compat : int32; s_feature_incompat : int32; s_feature_ro_compat : int32; s_uuid : string; s_volume_name : string; s_last_mounted : string; s_algorithm_usage_bitmap : int32; s_prealloc_blocks : int; s_prealloc_dir_blocks : int; s_reserved_gdt_blocks : int; s_journal_uuid : string; s_journal_inum : int32; s_journal_dev : int32; s_last_orphan : int32; s_hash_seed0 : int32; s_hash_seed1 : int32; s_hash_seed2 : int32; s_hash_seed3 : int32; s_def_hash_version : int; s_reserved_char_pad : int; s_reserved_word_pad : int; s_default_mount_opts : int32; s_first_meta_bg : int32; (* Computed fields, don't appear in superblock: *) block_size : int63; groups_count : int63; } (* Private data functions. *) let attach_private_data, get_private_data = private_data_functions (fun {fs_cb = {fs_cb_uq = u}} -> u) let id = "ext2" let superblock_offset = ~^1024 let superblock_len = ~^1024 (* Parse ext2/3 superblock and return a big structure containing * all the fields. Also there are some extra fields which don't * appear in the superblock itself, but are computed from other fields. * * If the device doesn't contain a superblock, raises Not_found. *) let parse_ext2_sb dev = (* Load the superblock. *) let bits = dev#read_bitstring superblock_offset superblock_len in (* The structure is straight from /usr/include/linux/ext3_fs.h *) bitmatch bits with | { s_inodes_count : 32 : littleendian; (* Inodes count *) s_blocks_count : 32 : littleendian; (* Blocks count *) s_r_blocks_count : 32 : littleendian; (* Reserved blocks count *) s_free_blocks_count : 32 : littleendian; (* Free blocks count *) s_free_inodes_count : 32 : littleendian; (* Free inodes count *) s_first_data_block : 32 : littleendian; (* First Data Block *) s_log_block_size : 32 : littleendian; (* Block size *) s_log_frag_size : 32 : littleendian; (* Fragment size *) s_blocks_per_group : 32 : littleendian; (* # Blocks per group *) s_frags_per_group : 32 : littleendian; (* # Fragments per group *) s_inodes_per_group : 32 : littleendian; (* # Inodes per group *) s_mtime : 32 : littleendian; (* Mount time *) s_wtime : 32 : littleendian; (* Write time *) s_mnt_count : 16 : littleendian; (* Mount count *) s_max_mnt_count : 16 : littleendian; (* Maximal mount count *) 0xef53 : 16 : littleendian; (* Magic signature *) s_state : 16 : littleendian; (* File system state *) s_errors : 16 : littleendian; (* Behaviour when detecting errors *) s_minor_rev_level : 16 : littleendian; (* minor revision level *) s_lastcheck : 32 : littleendian; (* time of last check *) s_checkinterval : 32 : littleendian; (* max. time between checks *) s_creator_os : 32 : littleendian; (* OS *) s_rev_level : 32 : littleendian; (* Revision level *) s_def_resuid : 16 : littleendian; (* Default uid for reserved blocks *) s_def_resgid : 16 : littleendian; (* Default gid for reserved blocks *) s_first_ino : 32 : littleendian; (* First non-reserved inode *) s_inode_size : 16 : littleendian; (* size of inode structure *) s_block_group_nr : 16 : littleendian; (* block group # of this superblock *) s_feature_compat : 32 : littleendian; (* compatible feature set *) s_feature_incompat : 32 : littleendian; (* incompatible feature set *) s_feature_ro_compat : 32 : littleendian; (* readonly-compatible feature set *) s_uuid : 128 : string; (* 128-bit uuid for volume *) s_volume_name : 128 : string; (* volume name *) s_last_mounted : 512 : string; (* directory where last mounted *) s_algorithm_usage_bitmap : 32 : littleendian; (* For compression *) s_prealloc_blocks : 8; (* Nr of blocks to try to preallocate*) s_prealloc_dir_blocks : 8; (* Nr to preallocate for dirs *) s_reserved_gdt_blocks : 16 : littleendian;(* Per group desc for online growth *) s_journal_uuid : 128 : string; (* uuid of journal superblock *) s_journal_inum : 32 : littleendian; (* inode number of journal file *) s_journal_dev : 32 : littleendian; (* device number of journal file *) s_last_orphan : 32 : littleendian; (* start of list of inodes to delete *) s_hash_seed0 : 32 : littleendian; (* HTREE hash seed *) s_hash_seed1 : 32 : littleendian; s_hash_seed2 : 32 : littleendian; s_hash_seed3 : 32 : littleendian; s_def_hash_version : 8; (* Default hash version to use *) s_reserved_char_pad : 8; s_reserved_word_pad : 16 : littleendian; s_default_mount_opts : 32 : littleendian; s_first_meta_bg : 32 : littleendian } -> (* First metablock block group *) (* Work out the block size in bytes. *) let block_size = ~^1024 <^< Int32.to_int s_log_block_size in (* Number of groups. *) let groups_count = Int63.of_int32 ( (s_blocks_count -* s_first_data_block -* 1l) /* s_blocks_per_group +* 1l ) in { s_inodes_count = s_inodes_count; s_blocks_count = s_blocks_count; s_r_blocks_count = s_r_blocks_count; s_free_blocks_count = s_free_blocks_count; s_free_inodes_count = s_free_inodes_count; s_first_data_block = s_first_data_block; s_log_block_size = s_log_block_size; s_log_frag_size = s_log_frag_size; s_blocks_per_group = s_blocks_per_group; s_frags_per_group = s_frags_per_group; s_inodes_per_group = s_inodes_per_group; s_mtime = s_mtime; s_wtime = s_wtime; s_mnt_count = s_mnt_count; s_max_mnt_count = s_max_mnt_count; s_state = s_state; s_errors = s_errors; s_minor_rev_level = s_minor_rev_level; s_lastcheck = s_lastcheck; s_checkinterval = s_checkinterval; s_creator_os = s_creator_os; s_rev_level = s_rev_level; s_def_resuid = s_def_resuid; s_def_resgid = s_def_resgid; s_first_ino = s_first_ino; s_inode_size = s_inode_size; s_block_group_nr = s_block_group_nr; s_feature_compat = s_feature_compat; s_feature_incompat = s_feature_incompat; s_feature_ro_compat = s_feature_ro_compat; s_uuid = s_uuid; s_volume_name = s_volume_name; s_last_mounted = s_last_mounted; s_algorithm_usage_bitmap = s_algorithm_usage_bitmap; s_prealloc_blocks = s_prealloc_blocks; s_prealloc_dir_blocks = s_prealloc_dir_blocks; s_reserved_gdt_blocks = s_reserved_gdt_blocks; s_journal_uuid = s_journal_uuid; s_journal_inum = s_journal_inum; s_journal_dev = s_journal_dev; s_last_orphan = s_last_orphan; s_hash_seed0 = s_hash_seed0; s_hash_seed1 = s_hash_seed1; s_hash_seed2 = s_hash_seed2; s_hash_seed3 = s_hash_seed3; s_def_hash_version = s_def_hash_version; s_reserved_char_pad = s_reserved_char_pad; s_reserved_word_pad = s_reserved_word_pad; s_default_mount_opts = s_default_mount_opts; s_first_meta_bg = s_first_meta_bg; block_size = block_size; groups_count = groups_count } | { _ } -> raise Not_found (* Not an EXT2/3 superblock. *) let rec probe dev = let sb = parse_ext2_sb dev in (* May raise Not_found. *) (* (* Number of group descriptors per block. *) let s_inodes_per_block = s_blocksize / let s_desc_per_block = block_size / s_inodes_per_block in let db_count = (s_groups_count +^ s_desc_per_block -^ 1L) /^ s_desc_per_block *) (* Calculate the block overhead (used by superblocks, inodes, etc.) * See fs/ext2/super.c. *) let overhead = Int63.of_int32 sb.s_first_data_block in let overhead = (* XXX *) overhead 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 sb.block_size dev in let fs = { fs_cb = callbacks (); fs_dev = fs_dev; fs_blocksize = sb.block_size; fs_blocks_total = Int63.of_int32 sb.s_blocks_count -^ overhead; fs_is_swap = false; fs_blocks_reserved = Int63.of_int32 sb.s_r_blocks_count; fs_blocks_avail = Int63.of_int32 sb.s_free_blocks_count; fs_blocks_used = Int63.of_int32 sb.s_blocks_count -^ overhead -^ Int63.of_int32 sb.s_free_blocks_count; fs_inodes_total = Int63.of_int32 sb.s_inodes_count; fs_inodes_reserved = ~^0; (* XXX? *) fs_inodes_avail = Int63.of_int32 sb.s_free_inodes_count; fs_inodes_used = Int63.of_int32 sb.s_inodes_count (*-^ 0*) -^ Int63.of_int32 sb.s_free_inodes_count; } in (* Attach the original superblock as private data so we can * get to it quickly in other callbacks. *) attach_private_data fs sb; fs 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 = "Linux ext2/3"; fs_cb_offset_is_free = offset_is_free; } (* Register the plugin. *) let () = register_plugin ~filesystem:probe id