(* (C) Copyright 2007-2008 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
+*)
(**
{2 Examples}
]}
*)
-(**
- {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.
-class offset_device : string -> int64 -> int64 -> 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 -> 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_cb : partitioner_callbacks; (** 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_cb : filesystem_callbacks; (** Filesystem type. *)
+ 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
+ pv_cb : lvm_callbacks; (** The LVM plug-in which detected
this. *)
+ pv_dev : device; (** Device covering whole PV. *)
pv_uuid : string; (** UUID. *)
}
and lv = {
}
(** Physical and logical volumes as used by LVM plug-ins. *)
-and parts_plugin_id
-and fs_plugin_id
-and lvm_plugin_id
- (** Opaque IDs used to refer to the plug-ins. *)
+and partitioner_callbacks
+and filesystem_callbacks
+and lvm_callbacks
+
+(** {2 Functions} *)
-val name_of_parts : parts_plugin_id -> string
-val name_of_filesystem : fs_plugin_id -> string
-val name_of_lvm : lvm_plugin_id -> string
- (** Convert plug-in IDs to printable strings. *)
+val name_of_filesystem : filesystem -> string
+ (** [name_of_filesystem fs] returns a printable name for
+ the filesystem.
+ *)
-(** {2 Scanning functions} *)
+(** {3 Create 'machine'} *)
val open_machine : string -> (string * string) list -> machine
(** [open_machine m_name devs]
LV filesystems in the returned structure.
*)
+val open_machine_from_devices : string -> (string * block_device) list ->
+ machine
+ (** This is the same as {!open_machine} except that instead
+ of passing a path you should pass a {!block_device} object. *)
+
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).
+ *)
+
+type owner =
+ [ `Filesystem of filesystem
+ | `Partitions of partitions
+ | `PhysicalVolume of pv ]
+
+val get_owners_lookup : machine -> ownership -> block_device ->
+ (Int63.t -> (owner * Int63.t) list)
+ (** [get_owners_lookup machine disk] returns a specialized
+ function for looking up owners (filesystems, etc.)
+ which reside on block device [disk].
+
+ [disk] must be a block device of the machine.
+
+ The specialized lookup function that is returned
+ can be called as [lookup offset] to look up the
+ owners of byte offset [offset].
+
+ Returns a list of [(owner, owner_offset)] where [owner]
+ is the filesystem, etc., and [owner_offset] is the byte
+ offset relative to the owner.
+
+ It is common for there to be multiple owners: for example
+ in the case where a filesystem is created on a partition,
+ both the filesystem ([`Filesystem fs]) and
+ partition scheme ([`Partitions parts]) will be returned.
+
+ The specialized function is efficient. {!create_ownership}
+ creates a tree structure which allows ownership to be determined
+ in just a few steps. *)
+
+val offset_is_free : (owner * Int63.t) list -> bool
+ (** [offset_is_free owners] tests if the offset is free (unused).
+
+ If an offset is free, then it may be discarded without
+ changing the semantics of the disk image. In normal cases
+ this extends to the end of the current block, but blocksize
+ will differ for each owner, so what this really means is
+ tricky in practice. *)
+
(** {2 Debugging} *)
val debug : bool ref