Added #contiguous method to make block mapping more efficient, and
[virt-df.git] / lib / diskimage.mli
1 (** Diskimage library for reading disk images. *)
2 (* (C) Copyright 2007-2008 Richard W.M. Jones, Red Hat Inc.
3    http://libvirt.org/
4
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2 of the License, or
8    (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  *)
19
20 (**
21    {2 Examples}
22
23 {[
24   let machine = open_machine "host" ["hda", "/dev/hda"] in
25   let machine = scan_machine machine in
26   (* do what you want with the scan results ... *)
27   close_machine machine
28 ]}
29 *)
30
31 (** {2 Device class and specialized subclasses} *)
32
33 class virtual device :
34   object
35     method virtual name : string
36       (** Return some printable name for the device. *)
37
38     method virtual size : Int63.t
39       (** Return the size of the device in bytes.
40
41           Note: For some types of devices, the device may have
42           "holes", alignment requirements, etc. so this method doesn't
43           imply that every byte from [0..size-1] is readable. *)
44     method read : Int63.t -> Int63.t -> string
45       (** [read offset len] reads len bytes starting at offset.
46
47           Note: A default implementation is provided for [read],
48           but it is fairly inefficient because it uses {!map_block} to
49           map each block in the request. *)
50     method read_bitstring : Int63.t -> Int63.t -> Bitmatch.bitstring
51       (** [read_bitstring] is the same as [read] but returns
52           a pa_bitmatch-style bitstring. *)
53
54     method virtual blocksize : Int63.t
55       (** [blocksize] returns the natural block size of the device. *)
56     method virtual map_block : Int63.t -> (device * Int63.t) list
57       (** [map_block] describes how a block in this device is
58           mapped down to any underlying device(s).
59
60           Returns [[]] (empty list) if there is no underlying
61           device for this block.  Otherwise returns a list of
62           [(device, byte-offset)] locations where this block is mapped.
63
64           Normally the returned list has length 1, but in cases
65           such as mirroring you can have the same block mapped
66           to several underlying devices. *)
67
68     method virtual contiguous : Int63.t -> Int63.t
69       (** [contiguous offset] returns the number of contiguous
70           {i bytes} starting at byte [offset] on this device,
71           before (eg.) end of device or some discontinuity in
72           the mapping table occurs.
73
74           This method and {!map_block} allow callers to determine how
75           high level blocks map to low level disk images efficiently
76           (complex striping and interleaving patterns can make the
77           process much less efficient however). *)
78   end
79   (**
80      A virtual (or physical!) device, encapsulating any translation
81      that has to be done to access the device.  eg. For partitions
82      there is a simple offset, but for LVM you may need complicated
83      table lookups.
84      
85      Note this very rare use of OOP in OCaml!
86   *)
87
88 class block_device : string -> Int63.t ->
89   object
90     method name : string
91     method size : Int63.t
92     method read : Int63.t -> Int63.t -> string
93     method read_bitstring : Int63.t -> Int63.t -> Bitmatch.bitstring
94     method blocksize : Int63.t
95     method map_block : Int63.t -> (device * Int63.t) list
96     method contiguous : Int63.t -> Int63.t
97     method close : unit -> unit
98       (** Close the device, freeing up the file descriptor. *)
99   end
100     (** A concrete device which just direct-maps a file or /dev device.
101
102         Create the device with [new block_device filename blocksize]
103         where [filename] is the path to the file or device and
104         [blocksize] is the blocksize of the device. *)
105
106 class offset_device : string -> Int63.t -> Int63.t -> Int63.t -> device ->
107   object
108     method name : string
109     method size : Int63.t
110     method read : Int63.t -> Int63.t -> string
111     method read_bitstring : Int63.t -> Int63.t -> Bitmatch.bitstring
112     method blocksize : Int63.t
113     method map_block : Int63.t -> (device * Int63.t) list
114     method contiguous : Int63.t -> Int63.t
115   end
116     (** A concrete device which maps a linear part of an underlying device.
117
118         [new offset_device name start size blocksize dev] creates a new
119         device which maps bytes from [start] to [start+size-1]
120         of the underlying device [dev] (ie. in this device they
121         appear as bytes [0] to [size-1]).
122
123         Useful for things like partitions.
124     *)
125
126 class blocksize_overlay : Int63.t -> device ->
127   object
128     method name : string
129     method size : Int63.t
130     method read : Int63.t -> Int63.t -> string
131     method read_bitstring : Int63.t -> Int63.t -> Bitmatch.bitstring
132     method blocksize : Int63.t
133     method contiguous : Int63.t -> Int63.t
134     method map_block : Int63.t -> (device * Int63.t) list
135   end
136     (** Change the blocksize of an existing device. *)
137
138 val null_device : device
139     (** The null device.  Any attempt to read generates an error. *)
140
141 (**
142    {2 Structures used to describe machines, disks, partitions and filesystems}
143
144    {3 Machine/device model}
145
146    The "machine/device model" that we currently understand looks
147    like this:
148
149 {v
150 machines
151   |
152   \--- host partitions / disk image files
153          ||
154        guest block devices
155          |
156          +--> guest partitions (eg. using MBR)
157          |      |
158          \-(1)->+--- filesystems (eg. ext3)
159                 |
160                 \--- PVs for LVM
161                        |||
162                      VGs and LVs
163 v}
164
165    (1) Filesystems and PVs may also appear directly on guest
166    block devices.
167
168    Partition schemes (eg. MBR) and filesystems register themselves
169    with this main module and they are queried first to get an idea
170    of the physical devices, partitions and filesystems potentially
171    available to the guest.
172
173    Volume management schemes (eg. LVM2) register themselves here
174    and are called later with "spare" physical devices and partitions
175    to see if they contain LVM data.  If this results in additional
176    logical volumes then these are checked for filesystems.
177
178    Swap space is considered to be a dumb filesystem for the purposes
179    of this discussion.
180 *)
181
182 type machine = {
183   m_name : string;                      (** Machine name. *)
184   m_disks : disk list;                  (** Machine disks. *)
185   m_lv_filesystems :
186     (lv * filesystem) list;             (** Machine LV filesystems. *)
187 }
188     (** A 'machine' is just a convenient holder for collections of disks. *)
189
190 and disk = {
191   d_name : string;                      (** Device name (eg "hda") *)
192   d_dev : block_device;                 (** Disk device. *)
193   d_content : disk_content;             (** What's on it. *)
194 }
195     (** A single physical disk image. *)
196
197 and disk_content =
198     [ `Filesystem of filesystem         (** Contains a direct filesystem. *)
199     | `Partitions of partitions         (** Contains partitions. *)
200     | `PhysicalVolume of pv             (** Contains an LVM PV. *)
201     | `Unknown                          (** Not probed or unknown. *)
202     ]
203
204 and partitions = {
205   parts_plugin_id : parts_plugin_id;    (** Partitioning scheme. *)
206   parts : partition list;               (** Partitions. *)
207 }
208 and partition = {
209   part_status : partition_status;       (** Bootable, etc. *)
210   part_type : int;                      (** Partition filesystem type. *)
211   part_dev : device;                    (** Partition device. *)
212   part_content : partition_content;     (** What's on it. *)
213 }
214     (** Partitions as found on a disk image. *)
215
216 and partition_status = Bootable | Nonbootable | Malformed | NullEntry
217 and partition_content =
218     [ `Filesystem of filesystem         (** Filesystem. *)
219     | `PhysicalVolume of pv             (** Contains an LVM PV. *)
220     | `Unknown                          (** Not probed or unknown. *)
221     ]
222
223 and filesystem = {
224   fs_plugin_id : fs_plugin_id;          (** Filesystem type. *)
225   fs_dev : device;                      (** Device containing the filesystem. *)
226   fs_blocksize : Int63.t;               (** Block size (bytes). *)
227   fs_blocks_total : Int63.t;            (** Total blocks. *)
228   fs_is_swap : bool;                    (** If swap, following not valid. *)
229   fs_blocks_reserved : Int63.t;         (** Blocks reserved for super-user. *)
230   fs_blocks_avail : Int63.t;            (** Blocks free (available). *)
231   fs_blocks_used : Int63.t;             (** Blocks in use. *)
232   fs_inodes_total : Int63.t;            (** Total inodes. *)
233   fs_inodes_reserved : Int63.t;         (** Inodes reserved for super-user. *)
234   fs_inodes_avail : Int63.t;            (** Inodes free (available). *)
235   fs_inodes_used : Int63.t;             (** Inodes in use. *)
236 }
237     (** A filesystem, with superblock contents. *)
238
239 and pv = {
240   lvm_plugin_id : lvm_plugin_id;        (** The LVM plug-in which detected
241                                             this. *)
242   pv_uuid : string;                     (** UUID. *)
243 }
244 and lv = {
245   lv_dev : device;                      (** Logical volume device. *)
246 }
247     (** Physical and logical volumes as used by LVM plug-ins. *)
248
249 and parts_plugin_id
250 and fs_plugin_id
251 and lvm_plugin_id
252   (** Opaque IDs used to refer to the plug-ins. *)
253
254 val name_of_parts : parts_plugin_id -> string
255 val name_of_filesystem : fs_plugin_id -> string
256 val name_of_lvm : lvm_plugin_id -> string
257   (** Convert plug-in IDs to printable strings. *)
258
259 (** {2 Scanning functions} *)
260
261 val open_machine : string -> (string * string) list -> machine
262   (** [open_machine m_name devs]
263       creates a {!machine} containing the devices listed.
264
265       [devs] is a list of pairs of [(name, path)] elements
266       where [name] is something like ["hda"] and [path]
267       is a path to a disk image or [/dev] device.
268
269       This function does not do any scanning, so all disk
270       contents are just set to [`Unknown] and there are no
271       LV filesystems in the returned structure.
272   *)
273
274 val close_machine : machine -> unit
275   (** This is a convenience function which calls the [dev#close]
276       method on any open {!block_device}s owned by the machine.  This just
277       has the effect of closing any file descriptors which are
278       opened by these devices.
279   *)
280
281 val scan_machine : machine -> machine
282   (** This does a complete scan of all devices owned by a machine,
283       identifying all partitions, filesystems, physical and logical
284       volumes that are known to this library.
285
286       This scans down to the level of the filesystem superblocks.
287
288       Returns an updated {!machine} structure with the scan results.
289   *)
290
291 (** {2 Debugging} *)
292
293 val debug : bool ref
294   (** If set to true, functions emit debugging information to stderr. *)