resize: Add --align-first auto|never|always option.
[libguestfs.git] / resize / utils.ml
1 (* virt-resize
2  * Copyright (C) 2010-2011 Red Hat Inc.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  *)
18
19 open Printf
20
21 module G = Guestfs
22
23 let ( +^ ) = Int64.add
24 let ( -^ ) = Int64.sub
25 let ( *^ ) = Int64.mul
26 let ( /^ ) = Int64.div
27 let ( &^ ) = Int64.logand
28 let ( ~^ ) = Int64.lognot
29
30 let int_of_le32 str =
31   assert (String.length str = 4);
32   let c0 = Char.code (String.unsafe_get str 0) in
33   let c1 = Char.code (String.unsafe_get str 1) in
34   let c2 = Char.code (String.unsafe_get str 2) in
35   let c3 = Char.code (String.unsafe_get str 3) in
36   Int64.of_int c0 +^
37     (Int64.shift_left (Int64.of_int c1) 8) +^
38     (Int64.shift_left (Int64.of_int c2) 16) +^
39     (Int64.shift_left (Int64.of_int c3) 24)
40
41 let le32_of_int i =
42   let c0 = i &^ 0xffL in
43   let c1 = Int64.shift_right (i &^ 0xff00L) 8 in
44   let c2 = Int64.shift_right (i &^ 0xff0000L) 16 in
45   let c3 = Int64.shift_right (i &^ 0xff000000L) 24 in
46   let s = String.create 4 in
47   String.unsafe_set s 0 (Char.unsafe_chr (Int64.to_int c0));
48   String.unsafe_set s 1 (Char.unsafe_chr (Int64.to_int c1));
49   String.unsafe_set s 2 (Char.unsafe_chr (Int64.to_int c2));
50   String.unsafe_set s 3 (Char.unsafe_chr (Int64.to_int c3));
51   s
52
53 let output_spaces chan n = for i = 0 to n-1 do output_char chan ' ' done
54
55 let wrap ?(chan = stdout) ?(hanging = 0) str =
56   let rec _wrap col str =
57     let n = String.length str in
58     let i = try String.index str ' ' with Not_found -> n in
59     let col =
60       if col+i >= 72 then (
61         output_char chan '\n';
62         output_spaces chan hanging;
63         i+hanging+1
64       ) else col+i+1 in
65     output_string chan (String.sub str 0 i);
66     if i < n then (
67       output_char chan ' ';
68       _wrap col (String.sub str (i+1) (n-(i+1)))
69     )
70   in
71   _wrap 0 str
72
73 let error fs =
74   let display str =
75     wrap ~chan:stderr ("virt-resize: error: " ^ str);
76     prerr_newline ();
77     prerr_newline ();
78     wrap ~chan:stderr
79       "If reporting bugs, run virt-resize with the '-d' option and include the complete output.";
80     prerr_newline ();
81     exit 1
82   in
83   ksprintf display fs
84
85 (* The reverse of device name translation, see
86  * BLOCK DEVICE NAMING in guestfs(3).
87  *)
88 let canonicalize dev =
89   if String.length dev >= 8 &&
90     dev.[0] = '/' && dev.[1] = 'd' && dev.[2] = 'e' && dev.[3] = 'v' &&
91     dev.[4] = '/' && (dev.[5] = 'h' || dev.[5] = 'v') && dev.[6] = 'd' then (
92       let dev = String.copy dev in
93       dev.[5] <- 's';
94       dev
95     )
96   else
97     dev
98
99 let feature_available (g : Guestfs.guestfs) names =
100   try g#available names; true
101   with G.Error _ -> false
102
103 (* Parse the size field from --resize and --resize-force options. *)
104 let parse_size =
105   let const_re = Pcre.regexp "^([.\\d]+)([bKMG])$"
106   and plus_const_re = Pcre.regexp "^\\+([.\\d]+)([bKMG])$"
107   and minus_const_re = Pcre.regexp "^-([.\\d]+)([bKMG])$"
108   and percent_re = Pcre.regexp "^([.\\d]+)%$"
109   and plus_percent_re = Pcre.regexp "^\\+([.\\d]+)%$"
110   and minus_percent_re = Pcre.regexp "^-([.\\d]+)%$"
111   in
112   fun oldsize field ->
113     let subs = ref None in
114     let matches rex =
115       try subs := Some (Pcre.exec ~rex field); true
116       with Not_found -> false
117     in
118     let sub i =
119       match !subs with None -> assert false
120       | Some subs -> Pcre.get_substring subs i
121     in
122     let size_scaled f = function
123       | "b" -> Int64.of_float f
124       | "K" -> Int64.of_float (f *. 1024.)
125       | "M" -> Int64.of_float (f *. 1024. *. 1024.)
126       | "G" -> Int64.of_float (f *. 1024. *. 1024. *. 1024.)
127       | _ -> assert false
128     in
129
130     if matches const_re then (
131       size_scaled (float_of_string (sub 1)) (sub 2)
132     )
133     else if matches plus_const_re then (
134       let incr = size_scaled (float_of_string (sub 1)) (sub 2) in
135       oldsize +^ incr
136     )
137     else if matches minus_const_re then (
138       let incr = size_scaled (float_of_string (sub 1)) (sub 2) in
139       oldsize -^ incr
140     )
141     else if matches percent_re then (
142       let percent = Int64.of_float (10. *. float_of_string (sub 1)) in
143       oldsize *^ percent /^ 1000L
144     )
145     else if matches plus_percent_re then (
146       let percent = Int64.of_float (10. *. float_of_string (sub 1)) in
147       oldsize +^ oldsize *^ percent /^ 1000L
148     )
149     else if matches minus_percent_re then (
150       let percent = Int64.of_float (10. *. float_of_string (sub 1)) in
151       oldsize -^ oldsize *^ percent /^ 1000L
152     )
153     else
154       error "virt-resize: %s: cannot parse size field" field
155
156 let human_size i =
157   let sign, i = if i < 0L then "-", Int64.neg i else "", i in
158
159   if i < 1024L then
160     sprintf "%s%Ld" sign i
161   else (
162     let f = Int64.to_float i /. 1024. in
163     let i = i /^ 1024L in
164     if i < 1024L then
165       sprintf "%s%.1fK" sign f
166     else (
167       let f = Int64.to_float i /. 1024. in
168       let i = i /^ 1024L in
169       if i < 1024L then
170         sprintf "%s%.1fM" sign f
171       else (
172         let f = Int64.to_float i /. 1024. in
173         (*let i = i /^ 1024L in*)
174         sprintf "%s%.1fG" sign f
175       )
176     )
177   )