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