Clarify licensing for Debian.
[virt-df.git] / lib / diskimage_ext2.ml
1 (* 'df' command for virtual domains.
2    (C) Copyright 2007 Richard W.M. Jones, Red Hat Inc.
3    http://libvirt.org/
4
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.
10
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.
15
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
19
20    Support for EXT2/EXT3 filesystems.
21 *)
22
23 open Unix
24 open Printf
25
26 open Diskimage_impl
27
28 open Int63.Operators
29
30 let ( +* ) = Int32.add
31 let ( -* ) = Int32.sub
32 let ( ** ) = Int32.mul
33 let ( /* ) = Int32.div
34
35 (* The fields in an ext2/3 superblock. *)
36 type ext2_sb = {
37   s_inodes_count : int32;
38   s_blocks_count : int32;
39   s_r_blocks_count : int32;
40   s_free_blocks_count : int32;
41   s_free_inodes_count : int32;
42   s_first_data_block : int32;
43   s_log_block_size : int32;
44   s_log_frag_size : int32;
45   s_blocks_per_group : int32;
46   s_frags_per_group : int32;
47   s_inodes_per_group : int32;
48   s_mtime : int32;
49   s_wtime : int32;
50   s_mnt_count : int;
51   s_max_mnt_count : int;
52   s_state : int;
53   s_errors : int;
54   s_minor_rev_level : int;
55   s_lastcheck : int32;
56   s_checkinterval : int32;
57   s_creator_os : int32;
58   s_rev_level : int32;
59   s_def_resuid : int;
60   s_def_resgid : int;
61   s_first_ino : int32;
62   s_inode_size : int;
63   s_block_group_nr : int;
64   s_feature_compat : int32;
65   s_feature_incompat : int32;
66   s_feature_ro_compat : int32;
67   s_uuid : string;
68   s_volume_name : string;
69   s_last_mounted : string;
70   s_algorithm_usage_bitmap : int32;
71   s_prealloc_blocks : int;
72   s_prealloc_dir_blocks : int;
73   s_reserved_gdt_blocks : int;
74   s_journal_uuid : string;
75   s_journal_inum : int32;
76   s_journal_dev : int32;
77   s_last_orphan : int32;
78   s_hash_seed0 : int32;
79   s_hash_seed1 : int32;
80   s_hash_seed2 : int32;
81   s_hash_seed3 : int32;
82   s_def_hash_version : int;
83   s_reserved_char_pad : int;
84   s_reserved_word_pad : int;
85   s_default_mount_opts : int32;
86   s_first_meta_bg : int32;
87
88   (* Computed fields, don't appear in superblock: *)
89   block_size : int63;
90   groups_count : int63;
91 }
92
93 (* Private data functions. *)
94 let attach_private_data, get_private_data =
95   private_data_functions (fun {fs_cb = {fs_cb_uq = u}} -> u)
96
97 let id = "ext2"
98 let superblock_offset = ~^1024
99 let superblock_len = ~^1024
100
101 (* Parse ext2/3 superblock and return a big structure containing
102  * all the fields.  Also there are some extra fields which don't
103  * appear in the superblock itself, but are computed from other fields.
104  *
105  * If the device doesn't contain a superblock, raises Not_found.
106  *)
107 let parse_ext2_sb dev =
108   (* Load the superblock. *)
109   let bits = dev#read_bitstring superblock_offset superblock_len in
110
111   (* The structure is straight from /usr/include/linux/ext3_fs.h *)
112   bitmatch bits with
113   | { s_inodes_count : 32 : littleendian;       (* Inodes count *)
114       s_blocks_count : 32 : littleendian;       (* Blocks count *)
115       s_r_blocks_count : 32 : littleendian;     (* Reserved blocks count *)
116       s_free_blocks_count : 32 : littleendian;  (* Free blocks count *)
117       s_free_inodes_count : 32 : littleendian;  (* Free inodes count *)
118       s_first_data_block : 32 : littleendian;   (* First Data Block *)
119       s_log_block_size : 32 : littleendian;     (* Block size *)
120       s_log_frag_size : 32 : littleendian;      (* Fragment size *)
121       s_blocks_per_group : 32 : littleendian;   (* # Blocks per group *)
122       s_frags_per_group : 32 : littleendian;    (* # Fragments per group *)
123       s_inodes_per_group : 32 : littleendian;   (* # Inodes per group *)
124       s_mtime : 32 : littleendian;              (* Mount time *)
125       s_wtime : 32 : littleendian;              (* Write time *)
126       s_mnt_count : 16 : littleendian;          (* Mount count *)
127       s_max_mnt_count : 16 : littleendian;      (* Maximal mount count *)
128       0xef53 : 16 : littleendian;               (* Magic signature *)
129       s_state : 16 : littleendian;              (* File system state *)
130       s_errors : 16 : littleendian;             (* Behaviour when detecting errors *)
131       s_minor_rev_level : 16 : littleendian;    (* minor revision level *)
132       s_lastcheck : 32 : littleendian;          (* time of last check *)
133       s_checkinterval : 32 : littleendian;      (* max. time between checks *)
134       s_creator_os : 32 : littleendian;         (* OS *)
135       s_rev_level : 32 : littleendian;          (* Revision level *)
136       s_def_resuid : 16 : littleendian;         (* Default uid for reserved blocks *)
137       s_def_resgid : 16 : littleendian;         (* Default gid for reserved blocks *)
138       s_first_ino : 32 : littleendian;          (* First non-reserved inode *)
139       s_inode_size : 16 : littleendian;         (* size of inode structure *)
140       s_block_group_nr : 16 : littleendian;     (* block group # of this superblock *)
141       s_feature_compat : 32 : littleendian;     (* compatible feature set *)
142       s_feature_incompat : 32 : littleendian;   (* incompatible feature set *)
143       s_feature_ro_compat : 32 : littleendian;  (* readonly-compatible feature set *)
144       s_uuid : 128 : string;                    (* 128-bit uuid for volume *)
145       s_volume_name : 128 : string;             (* volume name *)
146       s_last_mounted : 512 : string;            (* directory where last mounted *)
147       s_algorithm_usage_bitmap : 32 : littleendian; (* For compression *)
148       s_prealloc_blocks : 8;                    (* Nr of blocks to try to preallocate*)
149       s_prealloc_dir_blocks : 8;                (* Nr to preallocate for dirs *)
150       s_reserved_gdt_blocks : 16 : littleendian;(* Per group desc for online growth *)
151       s_journal_uuid : 128 : string;            (* uuid of journal superblock *)
152       s_journal_inum : 32 : littleendian;       (* inode number of journal file *)
153       s_journal_dev : 32 : littleendian;        (* device number of journal file *)
154       s_last_orphan : 32 : littleendian;        (* start of list of inodes to delete *)
155       s_hash_seed0 : 32 : littleendian;         (* HTREE hash seed *)
156       s_hash_seed1 : 32 : littleendian;
157       s_hash_seed2 : 32 : littleendian;
158       s_hash_seed3 : 32 : littleendian;
159       s_def_hash_version : 8;                   (* Default hash version to use *)
160       s_reserved_char_pad : 8;
161       s_reserved_word_pad : 16 : littleendian;
162       s_default_mount_opts : 32 : littleendian;
163       s_first_meta_bg : 32 : littleendian } ->  (* First metablock block group *)
164
165       (* Work out the block size in bytes. *)
166       let block_size = ~^1024 <^< Int32.to_int s_log_block_size in
167
168       (* Number of groups. *)
169       let groups_count =
170         Int63.of_int32 (
171           (s_blocks_count -* s_first_data_block -* 1l)
172           /* s_blocks_per_group +* 1l
173         ) in
174
175       { s_inodes_count = s_inodes_count;
176         s_blocks_count = s_blocks_count;
177         s_r_blocks_count = s_r_blocks_count;
178         s_free_blocks_count = s_free_blocks_count;
179         s_free_inodes_count = s_free_inodes_count;
180         s_first_data_block = s_first_data_block;
181         s_log_block_size = s_log_block_size;
182         s_log_frag_size = s_log_frag_size;
183         s_blocks_per_group = s_blocks_per_group;
184         s_frags_per_group = s_frags_per_group;
185         s_inodes_per_group = s_inodes_per_group;
186         s_mtime = s_mtime;
187         s_wtime = s_wtime;
188         s_mnt_count = s_mnt_count;
189         s_max_mnt_count = s_max_mnt_count;
190         s_state = s_state;
191         s_errors = s_errors;
192         s_minor_rev_level = s_minor_rev_level;
193         s_lastcheck = s_lastcheck;
194         s_checkinterval = s_checkinterval;
195         s_creator_os = s_creator_os;
196         s_rev_level = s_rev_level;
197         s_def_resuid = s_def_resuid;
198         s_def_resgid = s_def_resgid;
199         s_first_ino = s_first_ino;
200         s_inode_size = s_inode_size;
201         s_block_group_nr = s_block_group_nr;
202         s_feature_compat = s_feature_compat;
203         s_feature_incompat = s_feature_incompat;
204         s_feature_ro_compat = s_feature_ro_compat;
205         s_uuid = s_uuid;
206         s_volume_name = s_volume_name;
207         s_last_mounted = s_last_mounted;
208         s_algorithm_usage_bitmap = s_algorithm_usage_bitmap;
209         s_prealloc_blocks = s_prealloc_blocks;
210         s_prealloc_dir_blocks = s_prealloc_dir_blocks;
211         s_reserved_gdt_blocks = s_reserved_gdt_blocks;
212         s_journal_uuid = s_journal_uuid;
213         s_journal_inum = s_journal_inum;
214         s_journal_dev = s_journal_dev;
215         s_last_orphan = s_last_orphan;
216         s_hash_seed0 = s_hash_seed0;
217         s_hash_seed1 = s_hash_seed1;
218         s_hash_seed2 = s_hash_seed2;
219         s_hash_seed3 = s_hash_seed3;
220         s_def_hash_version = s_def_hash_version;
221         s_reserved_char_pad = s_reserved_char_pad;
222         s_reserved_word_pad = s_reserved_word_pad;
223         s_default_mount_opts = s_default_mount_opts;
224         s_first_meta_bg = s_first_meta_bg;
225         block_size = block_size;
226         groups_count = groups_count }
227
228   | { _ } ->
229       raise Not_found                   (* Not an EXT2/3 superblock. *)
230
231 let rec probe dev =
232   let sb = parse_ext2_sb dev in         (* May raise Not_found. *)
233
234 (*
235       (* Number of group descriptors per block. *)
236       let s_inodes_per_block = s_blocksize / 
237         let s_desc_per_block = block_size / s_inodes_per_block in
238         let db_count =
239           (s_groups_count +^ s_desc_per_block -^ 1L)
240           /^ s_desc_per_block
241 *)
242
243    (* Calculate the block overhead (used by superblocks, inodes, etc.)
244     * See fs/ext2/super.c.
245     *)
246    let overhead = Int63.of_int32 sb.s_first_data_block in
247    let overhead = (* XXX *) overhead in
248
249    (* The blocksize of the filesystem is likely to be quite different
250     * from that of the underlying device, so create an overlay device
251     * with the natural filesystem blocksize.
252     *)
253    let fs_dev = new blocksize_overlay sb.block_size dev in
254
255    let fs = {
256      fs_cb = callbacks ();
257      fs_dev = fs_dev;
258
259      fs_blocksize = sb.block_size;
260      fs_blocks_total = Int63.of_int32 sb.s_blocks_count -^ overhead;
261
262      fs_is_swap = false;
263
264      fs_blocks_reserved = Int63.of_int32 sb.s_r_blocks_count;
265      fs_blocks_avail = Int63.of_int32 sb.s_free_blocks_count;
266      fs_blocks_used =
267        Int63.of_int32 sb.s_blocks_count -^ overhead
268        -^ Int63.of_int32 sb.s_free_blocks_count;
269      fs_inodes_total = Int63.of_int32 sb.s_inodes_count;
270      fs_inodes_reserved = ~^0;  (* XXX? *)
271      fs_inodes_avail = Int63.of_int32 sb.s_free_inodes_count;
272      fs_inodes_used = Int63.of_int32 sb.s_inodes_count
273        (*-^ 0*)
274        -^ Int63.of_int32 sb.s_free_inodes_count;
275    } in
276
277    (* Attach the original superblock as private data so we can
278     * get to it quickly in other callbacks.
279     *)
280    attach_private_data fs sb;
281    fs
282
283 and offset_is_free _ _ = false
284
285 and callbacks =
286   let i = ref 0 in
287   fun () -> {
288     fs_cb_uq = (incr i; !i);
289     fs_cb_name = id;
290     fs_cb_printable_name = "Linux ext2/3";
291     fs_cb_offset_is_free = offset_is_free;
292   }
293
294 (* Register the plugin. *)
295 let () = register_plugin ~filesystem:probe id