(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 EXT2/EXT3 filesystems.
*)
open Unix
open Printf
-open Diskimage_utils
+open Diskimage_impl
-let superblock_offset = 1024L
+open Int63.Operators
-let probe_ext2 dev =
+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 1024 in
+ 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_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 *)
- _ : 6080 : bitstring } -> (* Padding to the end of the block *)
+ 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
- (* Work out the block size in bytes. *)
- let s_log_block_size = Int32.to_int s_log_block_size in
- let block_size = 1024L in
- let block_size = Int64.shift_left block_size 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
- (* Number of groups. *)
- let s_groups_count =
- Int64.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. *)
(* Calculate the block overhead (used by superblocks, inodes, etc.)
* See fs/ext2/super.c.
*)
- let overhead = Int64.of_int32 s_first_data_block in
+ let overhead = Int63.of_int32 sb.s_first_data_block in
let overhead = (* XXX *) overhead in
- {
- fs_name = "Linux ext2/3";
- fs_block_size = block_size;
- fs_blocks_total = Int64.of_int32 s_blocks_count -^ overhead;
+ (* 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 = Int64.of_int32 s_r_blocks_count;
- fs_blocks_avail = Int64.of_int32 s_free_blocks_count;
+
+ 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 =
- Int64.of_int32 s_blocks_count -^ overhead
- -^ Int64.of_int32 s_free_blocks_count;
- fs_inodes_total = Int64.of_int32 s_inodes_count;
- fs_inodes_reserved = 0L; (* XXX? *)
- fs_inodes_avail = Int64.of_int32 s_free_inodes_count;
- fs_inodes_used = Int64.of_int32 s_inodes_count
- (*-^ 0L*)
- -^ Int64.of_int32 s_free_inodes_count;
- }
+ 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
- | { _ } ->
- raise Not_found (* Not an EXT2/3 superblock. *)
+ (* 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