1 (** Diskimage library for reading disk images. *)
2 (* (C) Copyright 2007-2008 Richard W.M. Jones, Red Hat Inc.
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version,
9 with the OCaml linking exception described in ../COPYING.LIB.
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with this library; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
25 let machine = open_machine "host" ["hda", "/dev/hda"] in
26 let machine = scan_machine machine in
27 (* do what you want with the scan results ... *)
32 (** {2 Device class and specialized subclasses} *)
34 class virtual device :
36 method virtual name : string
37 (** Return some printable name for the device. *)
39 method virtual size : Int63.t
40 (** Return the size of the device in bytes.
42 Note: For some types of devices, the device may have
43 "holes", alignment requirements, etc. so this method doesn't
44 imply that every byte from [0..size-1] is readable. *)
45 method read : Int63.t -> Int63.t -> string
46 (** [read offset len] reads len bytes starting at offset.
48 Note: A default implementation is provided for [read],
49 but it is fairly inefficient because it uses {!map_block} to
50 map each block in the request. *)
51 method read_bitstring : Int63.t -> Int63.t -> Bitmatch.bitstring
52 (** [read_bitstring] is the same as [read] but returns
53 a pa_bitmatch-style bitstring. *)
55 method virtual blocksize : Int63.t
56 (** [blocksize] returns the natural block size of the device. *)
57 method virtual map_block : Int63.t -> (device * Int63.t) list
58 (** [map_block] describes how a block in this device is
59 mapped down to any underlying device(s).
61 Returns [[]] (empty list) if there is no underlying
62 device for this block. Otherwise returns a list of
63 [(device, byte-offset)] locations where this block is mapped.
65 Normally the returned list has length 1, but in cases
66 such as mirroring you can have the same block mapped
67 to several underlying devices. *)
69 method virtual contiguous : Int63.t -> Int63.t
70 (** [contiguous offset] returns the number of contiguous
71 {i bytes} starting at byte [offset] on this device,
72 before (eg.) end of device or some discontinuity in
73 the mapping table occurs.
75 This method and {!map_block} allow callers to determine how
76 high level blocks map to low level disk images efficiently
77 (complex striping and interleaving patterns can make the
78 process much less efficient however). *)
81 A virtual (or physical!) device, encapsulating any translation
82 that has to be done to access the device. eg. For partitions
83 there is a simple offset, but for LVM you may need complicated
86 Note this very rare use of OOP in OCaml!
89 class block_device : string -> Int63.t ->
93 method read : Int63.t -> Int63.t -> string
94 method read_bitstring : Int63.t -> Int63.t -> Bitmatch.bitstring
95 method blocksize : Int63.t
96 method map_block : Int63.t -> (device * Int63.t) list
97 method contiguous : Int63.t -> Int63.t
98 method close : unit -> unit
99 (** Close the device, freeing up the file descriptor. *)
101 (** A concrete device which just direct-maps a file or /dev device.
103 Create the device with [new block_device filename blocksize]
104 where [filename] is the path to the file or device and
105 [blocksize] is the blocksize of the device. *)
107 class offset_device : string -> Int63.t -> Int63.t -> Int63.t -> device ->
110 method size : Int63.t
111 method read : Int63.t -> Int63.t -> string
112 method read_bitstring : Int63.t -> Int63.t -> Bitmatch.bitstring
113 method blocksize : Int63.t
114 method map_block : Int63.t -> (device * Int63.t) list
115 method contiguous : Int63.t -> Int63.t
117 (** A concrete device which maps a linear part of an underlying device.
119 [new offset_device name start size blocksize dev] creates a new
120 device which maps bytes from [start] to [start+size-1]
121 of the underlying device [dev] (ie. in this device they
122 appear as bytes [0] to [size-1]).
124 Useful for things like partitions.
127 class blocksize_overlay : Int63.t -> device ->
130 method size : Int63.t
131 method read : Int63.t -> Int63.t -> string
132 method read_bitstring : Int63.t -> Int63.t -> Bitmatch.bitstring
133 method blocksize : Int63.t
134 method contiguous : Int63.t -> Int63.t
135 method map_block : Int63.t -> (device * Int63.t) list
137 (** Change the blocksize of an existing device. *)
139 val null_device : device
140 (** The null device. Any attempt to read generates an error. *)
143 {2 Structures used to describe machines, disks, partitions and filesystems}
145 {3 Machine/device model}
147 The "machine/device model" that we currently understand looks
153 \--- host partitions / disk image files
157 +--> guest partitions (eg. using MBR)
159 \-(1)->+--- filesystems (eg. ext3)
166 (1) Filesystems and PVs may also appear directly on guest
169 Partition schemes (eg. MBR) and filesystems register themselves
170 with this main module and they are queried first to get an idea
171 of the physical devices, partitions and filesystems potentially
172 available to the guest.
174 Volume management schemes (eg. LVM2) register themselves here
175 and are called later with "spare" physical devices and partitions
176 to see if they contain LVM data. If this results in additional
177 logical volumes then these are checked for filesystems.
179 Swap space is considered to be a dumb filesystem for the purposes
184 m_name : string; (** Machine name. *)
185 m_disks : disk list; (** Machine disks. *)
187 (lv * filesystem) list; (** Machine LV filesystems. *)
189 (** A 'machine' is just a convenient holder for collections of disks. *)
192 d_name : string; (** Device name (eg "hda") *)
193 d_dev : block_device; (** Disk device. *)
194 d_content : disk_content; (** What's on it. *)
196 (** A single physical disk image. *)
199 [ `Filesystem of filesystem (** Contains a direct filesystem. *)
200 | `Partitions of partitions (** Contains partitions. *)
201 | `PhysicalVolume of pv (** Contains an LVM PV. *)
202 | `Unknown (** Not probed or unknown. *)
206 parts_cb : partitioner_callbacks; (** Partitioning scheme. *)
207 parts_dev : device; (** Partitions (whole) device. *)
208 parts : partition list; (** Partitions. *)
211 part_status : partition_status; (** Bootable, etc. *)
212 part_type : int; (** Partition filesystem type. *)
213 part_dev : device; (** Partition device. *)
214 part_content : partition_content; (** What's on it. *)
216 (** Partitions as found on a disk image. *)
218 and partition_status = Bootable | Nonbootable | Malformed | NullEntry
219 and partition_content =
220 [ `Filesystem of filesystem (** Filesystem. *)
221 | `PhysicalVolume of pv (** Contains an LVM PV. *)
222 | `Unknown (** Not probed or unknown. *)
226 fs_cb : filesystem_callbacks; (** Filesystem type. *)
227 fs_dev : device; (** Device containing the filesystem. *)
228 fs_blocksize : Int63.t; (** Block size (bytes). *)
229 fs_blocks_total : Int63.t; (** Total blocks. *)
230 fs_is_swap : bool; (** If swap, following not valid. *)
231 fs_blocks_reserved : Int63.t; (** Blocks reserved for super-user. *)
232 fs_blocks_avail : Int63.t; (** Blocks free (available). *)
233 fs_blocks_used : Int63.t; (** Blocks in use. *)
234 fs_inodes_total : Int63.t; (** Total inodes. *)
235 fs_inodes_reserved : Int63.t; (** Inodes reserved for super-user. *)
236 fs_inodes_avail : Int63.t; (** Inodes free (available). *)
237 fs_inodes_used : Int63.t; (** Inodes in use. *)
239 (** A filesystem, with superblock contents. *)
242 pv_cb : lvm_callbacks; (** The LVM plug-in which detected
244 pv_dev : device; (** Device covering whole PV. *)
245 pv_uuid : string; (** UUID. *)
248 lv_dev : device; (** Logical volume device. *)
250 (** Physical and logical volumes as used by LVM plug-ins. *)
252 and partitioner_callbacks
253 and filesystem_callbacks
258 val name_of_filesystem : filesystem -> string
259 (** [name_of_filesystem fs] returns a printable name for
263 (** {3 Create 'machine'} *)
265 val open_machine : string -> (string * string) list -> machine
266 (** [open_machine m_name devs]
267 creates a {!machine} containing the devices listed.
269 [devs] is a list of pairs of [(name, path)] elements
270 where [name] is something like ["hda"] and [path]
271 is a path to a disk image or [/dev] device.
273 This function does not do any scanning, so all disk
274 contents are just set to [`Unknown] and there are no
275 LV filesystems in the returned structure.
278 val open_machine_from_devices : string -> (string * block_device) list ->
280 (** This is the same as {!open_machine} except that instead
281 of passing a path you should pass a {!block_device} object. *)
283 val close_machine : machine -> unit
284 (** This is a convenience function which calls the [dev#close]
285 method on any open {!block_device}s owned by the machine. This just
286 has the effect of closing any file descriptors which are
287 opened by these devices.
290 (** {3 Scanning for filesystems} *)
292 val scan_machine : machine -> machine
293 (** This does a complete scan of all devices owned by a machine,
294 identifying all partitions, filesystems, physical and logical
295 volumes that are known to this library.
297 This scans down to the level of the filesystem superblocks.
299 Returns an updated {!machine} structure with the scan results.
302 (** {3 Create ownership tables} *)
306 val create_ownership : machine -> ownership
307 (** This creates the ownership tables (mapping disk blocks to the
308 ultimate filesystem, etc., which owns each one).
312 [ `Filesystem of filesystem
313 | `Partitions of partitions
314 | `PhysicalVolume of pv ]
316 val get_owners_lookup : machine -> ownership -> block_device ->
317 (Int63.t -> (owner * Int63.t) list)
318 (** [get_owners_lookup machine disk] returns a specialized
319 function for looking up owners (filesystems, etc.)
320 which reside on block device [disk].
322 [disk] must be a block device of the machine.
324 The specialized lookup function that is returned
325 can be called as [lookup offset] to look up the
326 owners of byte offset [offset].
328 Returns a list of [(owner, owner_offset)] where [owner]
329 is the filesystem, etc., and [owner_offset] is the byte
330 offset relative to the owner.
332 It is common for there to be multiple owners: for example
333 in the case where a filesystem is created on a partition,
334 both the filesystem ([`Filesystem fs]) and
335 partition scheme ([`Partitions parts]) will be returned.
337 The specialized function is efficient. {!create_ownership}
338 creates a tree structure which allows ownership to be determined
339 in just a few steps. *)
341 val offset_is_free : (owner * Int63.t) list -> bool
342 (** [offset_is_free owners] tests if the offset is free (unused).
344 If an offset is free, then it may be discarded without
345 changing the semantics of the disk image. In normal cases
346 this extends to the end of the current block, but blocksize
347 will differ for each owner, so what this really means is
348 tricky in practice. *)
353 (** If set to true, functions emit debugging information to stderr. *)