X-Git-Url: http://git.annexia.org/?a=blobdiff_plain;f=lib%2Fdiskimage.mli;h=5d2a420561898fba39205ad79936e865ffdd842b;hb=f5a7417793b3f0780b0dd49798ea27981f046d98;hp=113c99b63c6b555778f8a4cac17ffdf7aa62be19;hpb=3844c0e3ba0eb0e07b7ecabe7794def7dd622c2b;p=virt-df.git diff --git a/lib/diskimage.mli b/lib/diskimage.mli index 113c99b..5d2a420 100644 --- a/lib/diskimage.mli +++ b/lib/diskimage.mli @@ -18,93 +18,104 @@ *) (** - {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 Examples} + +{[ + let machine = open_machine "host" ["hda", "/dev/hda"] in + let machine = scan_machine machine in + (* do what you want with the scan results ... *) + close_machine machine +]} *) +(** {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]). @@ -112,9 +123,62 @@ class offset_device : string -> int64 -> int64 -> device -> 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. *) @@ -125,7 +189,7 @@ type machine = { 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. *) @@ -139,6 +203,7 @@ and disk_content = and partitions = { parts_plugin_id : parts_plugin_id; (** Partitioning scheme. *) + parts_dev : device; (** Partitions (whole) device. *) parts : partition list; (** Partitions. *) } and partition = { @@ -158,22 +223,24 @@ and partition_content = 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 = { @@ -191,7 +258,9 @@ val name_of_filesystem : fs_plugin_id -> string 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] @@ -208,19 +277,62 @@ val open_machine : string -> (string * string) list -> machine 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. *) + (** {2 Debugging} *) val debug : bool ref