]}
*)
-(**
- {2 Machine/device model}
-
- The "machine/device model" that we currently understand looks
- like this:
-
-{v
-machines
- |
- \--- host partitions / disk image files
- ||
- guest block devices
- |
- +--> guest partitions (eg. using MBR)
- | |
- \-(1)->+--- filesystems (eg. ext3)
- |
- \--- PVs for LVM
- |||
- VGs and LVs
-v}
-
- (1) Filesystems and PVs may also appear directly on guest
- block devices.
-
- Partition schemes (eg. MBR) and filesystems register themselves
- with this main module and they are queried first to get an idea
- of the physical devices, partitions and filesystems potentially
- available to the guest.
-
- Volume management schemes (eg. LVM2) register themselves here
- and are called later with "spare" physical devices and partitions
- to see if they contain LVM data. If this results in additional
- logical volumes then these are checked for filesystems.
-
- Swap space is considered to be a dumb filesystem for the purposes
- of this discussion.
-*)
+(** {2 Device class and specialized subclasses} *)
class virtual device :
object
method virtual name : string
(** Return some printable name for the device. *)
- method virtual size : int64
+
+ method virtual size : Int63.t
(** Return the size of the device in bytes.
Note: For some types of devices, the device may have
"holes", alignment requirements, etc. so this method doesn't
imply that every byte from [0..size-1] is readable. *)
- method close : unit -> unit
- (** Close the device. This must be called to fully free up
- any resources used by the device. *)
- method virtual read : int64 -> int -> string
- (** [read offset len] reads len bytes starting at offset. *)
- method read_bitstring : int64 -> int -> Bitmatch.bitstring
+ method read : Int63.t -> Int63.t -> string
+ (** [read offset len] reads len bytes starting at offset.
+
+ Note: A default implementation is provided for [read],
+ but it is fairly inefficient because it uses {!map_block} to
+ map each block in the request. *)
+ method read_bitstring : Int63.t -> Int63.t -> Bitmatch.bitstring
(** [read_bitstring] is the same as [read] but returns
a pa_bitmatch-style bitstring. *)
+
+ method virtual blocksize : Int63.t
+ (** [blocksize] returns the natural block size of the device. *)
+ method virtual map_block : Int63.t -> (device * Int63.t) list
+ (** [map_block] describes how a block in this device is
+ mapped down to any underlying device(s).
+
+ Returns [[]] (empty list) if there is no underlying
+ device for this block. Otherwise returns a list of
+ [(device, byte-offset)] locations where this block is mapped.
+
+ Normally the returned list has length 1, but in cases
+ such as mirroring you can have the same block mapped
+ to several underlying devices. *)
+
+ method virtual contiguous : Int63.t -> Int63.t
+ (** [contiguous offset] returns the number of contiguous
+ {i bytes} starting at byte [offset] on this device,
+ before (eg.) end of device or some discontinuity in
+ the mapping table occurs.
+
+ This method and {!map_block} allow callers to determine how
+ high level blocks map to low level disk images efficiently
+ (complex striping and interleaving patterns can make the
+ process much less efficient however). *)
end
(**
A virtual (or physical!) device, encapsulating any translation
that has to be done to access the device. eg. For partitions
there is a simple offset, but for LVM you may need complicated
table lookups.
-
+
Note this very rare use of OOP in OCaml!
*)
-class block_device : string ->
+class block_device : string -> Int63.t ->
object
method name : string
- method size : int64
+ method size : Int63.t
+ method read : Int63.t -> Int63.t -> string
+ method read_bitstring : Int63.t -> Int63.t -> Bitmatch.bitstring
+ method blocksize : Int63.t
+ method map_block : Int63.t -> (device * Int63.t) list
+ method contiguous : Int63.t -> Int63.t
method close : unit -> unit
- method read : int64 -> int -> string
- method read_bitstring : int64 -> int -> Bitmatch.bitstring
+ (** Close the device, freeing up the file descriptor. *)
end
- (** A concrete device which just direct-maps a file or /dev device. *)
+ (** A concrete device which just direct-maps a file or /dev device.
+
+ Create the device with [new block_device filename blocksize]
+ where [filename] is the path to the file or device and
+ [blocksize] is the blocksize of the device. *)
-class offset_device : string -> int64 -> int64 -> device ->
+class offset_device : string -> Int63.t -> Int63.t -> Int63.t -> device ->
object
method name : string
- method size : int64
- method close : unit -> unit
- method read : int64 -> int -> string
- method read_bitstring : int64 -> int -> Bitmatch.bitstring
+ method size : Int63.t
+ method read : Int63.t -> Int63.t -> string
+ method read_bitstring : Int63.t -> Int63.t -> Bitmatch.bitstring
+ method blocksize : Int63.t
+ method map_block : Int63.t -> (device * Int63.t) list
+ method contiguous : Int63.t -> Int63.t
end
(** A concrete device which maps a linear part of an underlying device.
- [new offset_device name start size dev] creates a new
+ [new offset_device name start size blocksize dev] creates a new
device which maps bytes from [start] to [start+size-1]
of the underlying device [dev] (ie. in this device they
appear as bytes [0] to [size-1]).
Useful for things like partitions.
*)
+class blocksize_overlay : Int63.t -> device ->
+ object
+ method name : string
+ method size : Int63.t
+ method read : Int63.t -> Int63.t -> string
+ method read_bitstring : Int63.t -> Int63.t -> Bitmatch.bitstring
+ method blocksize : Int63.t
+ method contiguous : Int63.t -> Int63.t
+ method map_block : Int63.t -> (device * Int63.t) list
+ end
+ (** Change the blocksize of an existing device. *)
+
val null_device : device
(** The null device. Any attempt to read generates an error. *)
+(**
+ {2 Structures used to describe machines, disks, partitions and filesystems}
+
+ {3 Machine/device model}
+
+ The "machine/device model" that we currently understand looks
+ like this:
+
+{v
+machines
+ |
+ \--- host partitions / disk image files
+ ||
+ guest block devices
+ |
+ +--> guest partitions (eg. using MBR)
+ | |
+ \-(1)->+--- filesystems (eg. ext3)
+ |
+ \--- PVs for LVM
+ |||
+ VGs and LVs
+v}
+
+ (1) Filesystems and PVs may also appear directly on guest
+ block devices.
+
+ Partition schemes (eg. MBR) and filesystems register themselves
+ with this main module and they are queried first to get an idea
+ of the physical devices, partitions and filesystems potentially
+ available to the guest.
+
+ Volume management schemes (eg. LVM2) register themselves here
+ and are called later with "spare" physical devices and partitions
+ to see if they contain LVM data. If this results in additional
+ logical volumes then these are checked for filesystems.
+
+ Swap space is considered to be a dumb filesystem for the purposes
+ of this discussion.
+*)
+
type machine = {
m_name : string; (** Machine name. *)
m_disks : disk list; (** Machine disks. *)
and disk = {
d_name : string; (** Device name (eg "hda") *)
- d_dev : device; (** Disk device. *)
+ d_dev : block_device; (** Disk device. *)
d_content : disk_content; (** What's on it. *)
}
(** A single physical disk image. *)
and partitions = {
parts_plugin_id : parts_plugin_id; (** Partitioning scheme. *)
+ parts_dev : device; (** Partitions (whole) device. *)
parts : partition list; (** Partitions. *)
}
and partition = {
and filesystem = {
fs_plugin_id : fs_plugin_id; (** Filesystem type. *)
- fs_block_size : int64; (** Block size (bytes). *)
- fs_blocks_total : int64; (** Total blocks. *)
+ fs_dev : device; (** Device containing the filesystem. *)
+ fs_blocksize : Int63.t; (** Block size (bytes). *)
+ fs_blocks_total : Int63.t; (** Total blocks. *)
fs_is_swap : bool; (** If swap, following not valid. *)
- fs_blocks_reserved : int64; (** Blocks reserved for super-user. *)
- fs_blocks_avail : int64; (** Blocks free (available). *)
- fs_blocks_used : int64; (** Blocks in use. *)
- fs_inodes_total : int64; (** Total inodes. *)
- fs_inodes_reserved : int64; (** Inodes reserved for super-user. *)
- fs_inodes_avail : int64; (** Inodes free (available). *)
- fs_inodes_used : int64; (** Inodes in use. *)
+ fs_blocks_reserved : Int63.t; (** Blocks reserved for super-user. *)
+ fs_blocks_avail : Int63.t; (** Blocks free (available). *)
+ fs_blocks_used : Int63.t; (** Blocks in use. *)
+ fs_inodes_total : Int63.t; (** Total inodes. *)
+ fs_inodes_reserved : Int63.t; (** Inodes reserved for super-user. *)
+ fs_inodes_avail : Int63.t; (** Inodes free (available). *)
+ fs_inodes_used : Int63.t; (** Inodes in use. *)
}
- (** A filesystem. *)
+ (** A filesystem, with superblock contents. *)
and pv = {
lvm_plugin_id : lvm_plugin_id; (** The LVM plug-in which detected
this. *)
+ pv_dev : device; (** Device covering whole PV. *)
pv_uuid : string; (** UUID. *)
}
and lv = {
val name_of_lvm : lvm_plugin_id -> string
(** Convert plug-in IDs to printable strings. *)
-(** {2 Scanning functions} *)
+(** {2 Functions} *)
+
+(** {3 Create 'machine'} *)
val open_machine : string -> (string * string) list -> machine
(** [open_machine m_name devs]
val close_machine : machine -> unit
(** This is a convenience function which calls the [dev#close]
- method on any open devices owned by the machine. This just
+ method on any open {!block_device}s owned by the machine. This just
has the effect of closing any file descriptors which are
opened by these devices.
*)
+(** {3 Scanning for filesystems} *)
+
val scan_machine : machine -> machine
(** This does a complete scan of all devices owned by a machine,
identifying all partitions, filesystems, physical and logical
volumes that are known to this library.
+ This scans down to the level of the filesystem superblocks.
+
Returns an updated {!machine} structure with the scan results.
*)
+(** {3 Create ownership tables} *)
+
+type ownership
+
+val create_ownership : machine -> ownership
+ (** This creates the ownership tables (mapping disk blocks to the
+ ultimate filesystem, etc., which owns each one).
+ *)
+
(** {2 Debugging} *)
val debug : bool ref