New API: fallocate64 (replaces fallocate).
[libguestfs.git] / daemon / ext2.c
1 /* libguestfs - the guestfsd daemon
2  * Copyright (C) 2009 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
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18
19 #include <config.h>
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <inttypes.h>
24 #include <string.h>
25 #include <unistd.h>
26
27 #include "../src/guestfs_protocol.h"
28 #include "daemon.h"
29 #include "c-ctype.h"
30 #include "actions.h"
31
32 /* Choose which tools like mke2fs to use.  For RHEL 5 (only) there
33  * is a special set of tools which support ext2/3/4.  eg. On RHEL 5,
34  * mke2fs only supports ext2/3, but mke4fs supports ext2/3/4.
35  *
36  * We specify e4fsprogs in the package list to ensure it is loaded
37  * if it exists.
38  */
39 static int
40 e2prog (char *name)
41 {
42   char *p = strstr (name, "e2");
43   if (!p) return 0;
44   p++;
45
46   *p = '4';
47   if (prog_exists (name))
48     return 0;
49
50   *p = '2';
51   if (prog_exists (name))
52     return 0;
53
54   reply_with_error ("cannot find required program %s", name);
55   return -1;
56 }
57
58 char **
59 do_tune2fs_l (const char *device)
60 {
61   int r;
62   char *out, *err;
63   char *p, *pend, *colon;
64   char **ret = NULL;
65   int size = 0, alloc = 0;
66
67   char prog[] = "tune2fs";
68   if (e2prog (prog) == -1)
69     return NULL;
70
71   r = command (&out, &err, prog, "-l", device, NULL);
72   if (r == -1) {
73     reply_with_error ("%s", err);
74     free (err);
75     free (out);
76     return NULL;
77   }
78   free (err);
79
80   p = out;
81
82   /* Discard the first line if it contains "tune2fs ...". */
83   if (STRPREFIX (p, "tune2fs ") || STRPREFIX (p, "tune4fs ")) {
84     p = strchr (p, '\n');
85     if (p) p++;
86     else {
87       reply_with_error ("truncated output");
88       free (out);
89       return NULL;
90     }
91   }
92
93   /* Read the lines and split into "key: value". */
94   while (*p) {
95     pend = strchrnul (p, '\n');
96     if (*pend == '\n') {
97       *pend = '\0';
98       pend++;
99     }
100
101     if (!*p) continue;
102
103     colon = strchr (p, ':');
104     if (colon) {
105       *colon = '\0';
106
107       do { colon++; } while (*colon && c_isspace (*colon));
108
109       if (add_string (&ret, &size, &alloc, p) == -1) {
110         free (out);
111         return NULL;
112       }
113       if (STREQ (colon, "<none>") ||
114           STREQ (colon, "<not available>") ||
115           STREQ (colon, "(none)")) {
116         if (add_string (&ret, &size, &alloc, "") == -1) {
117           free (out);
118           return NULL;
119         }
120       } else {
121         if (add_string (&ret, &size, &alloc, colon) == -1) {
122           free (out);
123           return NULL;
124         }
125       }
126     }
127     else {
128       if (add_string (&ret, &size, &alloc, p) == -1) {
129         free (out);
130         return NULL;
131       }
132       if (add_string (&ret, &size, &alloc, "") == -1) {
133         free (out);
134         return NULL;
135       }
136     }
137
138     p = pend;
139   }
140
141   free (out);
142
143   if (add_string (&ret, &size, &alloc, NULL) == -1)
144     return NULL;
145
146   return ret;
147 }
148
149 int
150 do_set_e2label (const char *device, const char *label)
151 {
152   int r;
153   char *err;
154
155   char prog[] = "e2label";
156   if (e2prog (prog) == -1)
157     return -1;
158
159   r = command (NULL, &err, prog, device, label, NULL);
160   if (r == -1) {
161     reply_with_error ("%s", err);
162     free (err);
163     return -1;
164   }
165
166   free (err);
167   return 0;
168 }
169
170 char *
171 do_get_e2label (const char *device)
172 {
173   int r, len;
174   char *out, *err;
175
176   char prog[] = "e2label";
177   if (e2prog (prog) == -1)
178     return NULL;
179
180   r = command (&out, &err, prog, device, NULL);
181   if (r == -1) {
182     reply_with_error ("%s", err);
183     free (out);
184     free (err);
185     return NULL;
186   }
187
188   free (err);
189
190   /* Remove any trailing \n from the label. */
191   len = strlen (out);
192   if (len > 0 && out[len-1] == '\n')
193     out[len-1] = '\0';
194
195   return out;                   /* caller frees */
196 }
197
198 int
199 do_set_e2uuid (const char *device, const char *uuid)
200 {
201   int r;
202   char *err;
203
204   char prog[] = "tune2fs";
205   if (e2prog (prog) == -1)
206     return -1;
207
208   r = command (NULL, &err, prog, "-U", uuid, device, NULL);
209   if (r == -1) {
210     reply_with_error ("%s", err);
211     free (err);
212     return -1;
213   }
214
215   free (err);
216   return 0;
217 }
218
219 char *
220 do_get_e2uuid (const char *device)
221 {
222   int r;
223   char *out, *err, *p, *q;
224
225   /* It's not so straightforward to get the volume UUID.  We have
226    * to use tune2fs -l and then look for a particular string in
227    * the output.
228    */
229   char prog[] = "tune2fs";
230   if (e2prog (prog) == -1)
231     return NULL;
232
233   r = command (&out, &err, prog, "-l", device, NULL);
234   if (r == -1) {
235     reply_with_error ("%s", err);
236     free (out);
237     free (err);
238     return NULL;
239   }
240
241   free (err);
242
243   /* Look for /\nFilesystem UUID:\s+/ in the output. */
244   p = strstr (out, "\nFilesystem UUID:");
245   if (p == NULL) {
246     reply_with_error ("no Filesystem UUID in the output of tune2fs -l");
247     free (out);
248     return NULL;
249   }
250
251   p += 17;
252   while (*p && c_isspace (*p))
253     p++;
254   if (!*p) {
255     reply_with_error ("malformed Filesystem UUID in the output of tune2fs -l");
256     free (out);
257     return NULL;
258   }
259
260   /* Now 'p' hopefully points to the start of the UUID. */
261   q = p;
262   while (*q && (c_isxdigit (*q) || *q == '-'))
263     q++;
264   if (!*q) {
265     reply_with_error ("malformed Filesystem UUID in the output of tune2fs -l");
266     free (out);
267     return NULL;
268   }
269
270   *q = '\0';
271
272   p = strdup (p);
273   if (!p) {
274     reply_with_perror ("strdup");
275     free (out);
276     return NULL;
277   }
278
279   free (out);
280   return p;                     /* caller frees */
281 }
282
283 int
284 do_resize2fs (const char *device)
285 {
286   char *err;
287   int r;
288
289   char prog[] = "resize2fs";
290   if (e2prog (prog) == -1)
291     return -1;
292
293   r = command (NULL, &err, prog, device, NULL);
294   if (r == -1) {
295     reply_with_error ("%s", err);
296     free (err);
297     return -1;
298   }
299
300   free (err);
301   return 0;
302 }
303
304 int
305 do_resize2fs_size (const char *device, int64_t size)
306 {
307   char *err;
308   int r;
309
310   char prog[] = "resize2fs";
311   if (e2prog (prog) == -1)
312     return -1;
313
314   /* resize2fs itself may impose additional limits.  Since we are
315    * going to use the 'K' suffix however we can only work with whole
316    * kilobytes.
317    */
318   if (size & 1023) {
319     reply_with_error ("%" PRIi64 ": size must be a round number of kilobytes",
320                       size);
321     return -1;
322   }
323   size /= 1024;
324
325   char buf[32];
326   snprintf (buf, sizeof buf, "%" PRIi64 "K", size);
327
328   r = command (NULL, &err, prog, device, buf, NULL);
329   if (r == -1) {
330     reply_with_error ("%s", err);
331     free (err);
332     return -1;
333   }
334
335   free (err);
336   return 0;
337 }
338
339 int
340 do_e2fsck_f (const char *device)
341 {
342   char *err;
343   int r;
344
345   char prog[] = "e2fsck";
346   if (e2prog (prog) == -1)
347     return -1;
348
349   /* 0 = no errors, 1 = errors corrected.
350    *
351    * >= 4 means uncorrected or other errors.
352    *
353    * 2, 3 means errors were corrected and we require a reboot.  This is
354    * a difficult corner case.
355    */
356   r = commandr (NULL, &err, prog, "-p", "-f", device, NULL);
357   if (r == -1 || r >= 2) {
358     reply_with_error ("%s", err);
359     free (err);
360     return -1;
361   }
362
363   free (err);
364   return 0;
365 }
366
367 int
368 do_mke2journal (int blocksize, const char *device)
369 {
370   char *err;
371   int r;
372
373   char prog[] = "mke2fs";
374   if (e2prog (prog) == -1)
375     return -1;
376
377   char blocksize_s[32];
378   snprintf (blocksize_s, sizeof blocksize_s, "%d", blocksize);
379
380   r = command (NULL, &err,
381                prog, "-O", "journal_dev", "-b", blocksize_s,
382                device, NULL);
383   if (r == -1) {
384     reply_with_error ("%s", err);
385     free (err);
386     return -1;
387   }
388
389   free (err);
390   return 0;
391 }
392
393 int
394 do_mke2journal_L (int blocksize, const char *label, const char *device)
395 {
396   char *err;
397   int r;
398
399   char prog[] = "mke2fs";
400   if (e2prog (prog) == -1)
401     return -1;
402
403   char blocksize_s[32];
404   snprintf (blocksize_s, sizeof blocksize_s, "%d", blocksize);
405
406   r = command (NULL, &err,
407                prog, "-O", "journal_dev", "-b", blocksize_s,
408                "-L", label,
409                device, NULL);
410   if (r == -1) {
411     reply_with_error ("%s", err);
412     free (err);
413     return -1;
414   }
415
416   free (err);
417   return 0;
418 }
419
420 int
421 do_mke2journal_U (int blocksize, const char *uuid, const char *device)
422 {
423   char *err;
424   int r;
425
426   char prog[] = "mke2fs";
427   if (e2prog (prog) == -1)
428     return -1;
429
430   char blocksize_s[32];
431   snprintf (blocksize_s, sizeof blocksize_s, "%d", blocksize);
432
433   r = command (NULL, &err,
434                prog, "-O", "journal_dev", "-b", blocksize_s,
435                "-U", uuid,
436                device, NULL);
437   if (r == -1) {
438     reply_with_error ("%s", err);
439     free (err);
440     return -1;
441   }
442
443   free (err);
444   return 0;
445 }
446
447 int
448 do_mke2fs_J (const char *fstype, int blocksize, const char *device,
449              const char *journal)
450 {
451   char *err;
452   int r;
453
454   char prog[] = "mke2fs";
455   if (e2prog (prog) == -1)
456     return -1;
457
458   char blocksize_s[32];
459   snprintf (blocksize_s, sizeof blocksize_s, "%d", blocksize);
460
461   int len = strlen (journal);
462   char jdev[len+32];
463   snprintf (jdev, len+32, "device=%s", journal);
464
465   r = command (NULL, &err,
466                prog, "-t", fstype, "-J", jdev, "-b", blocksize_s,
467                device, NULL);
468   if (r == -1) {
469     reply_with_error ("%s", err);
470     free (err);
471     return -1;
472   }
473
474   free (err);
475   return 0;
476 }
477
478 int
479 do_mke2fs_JL (const char *fstype, int blocksize, const char *device,
480               const char *label)
481 {
482   char *err;
483   int r;
484
485   char prog[] = "mke2fs";
486   if (e2prog (prog) == -1)
487     return -1;
488
489   char blocksize_s[32];
490   snprintf (blocksize_s, sizeof blocksize_s, "%d", blocksize);
491
492   int len = strlen (label);
493   char jdev[len+32];
494   snprintf (jdev, len+32, "device=LABEL=%s", label);
495
496   r = command (NULL, &err,
497                prog, "-t", fstype, "-J", jdev, "-b", blocksize_s,
498                device, NULL);
499   if (r == -1) {
500     reply_with_error ("%s", err);
501     free (err);
502     return -1;
503   }
504
505   free (err);
506   return 0;
507 }
508
509 int
510 do_mke2fs_JU (const char *fstype, int blocksize, const char *device,
511               const char *uuid)
512 {
513   char *err;
514   int r;
515
516   char prog[] = "mke2fs";
517   if (e2prog (prog) == -1)
518     return -1;
519
520   char blocksize_s[32];
521   snprintf (blocksize_s, sizeof blocksize_s, "%d", blocksize);
522
523   int len = strlen (uuid);
524   char jdev[len+32];
525   snprintf (jdev, len+32, "device=UUID=%s", uuid);
526
527   r = command (NULL, &err,
528                prog, "-t", fstype, "-J", jdev, "-b", blocksize_s,
529                device, NULL);
530   if (r == -1) {
531     reply_with_error ("%s", err);
532     free (err);
533     return -1;
534   }
535
536   free (err);
537   return 0;
538 }