New partition APIs: part_del, part_get_bootable, part_get/set_mbr_id
[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 (prog_exists (name))
47     return 0;
48
49   *p = '2';
50   if (prog_exists (name))
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[] = "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[] = "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[] = "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[] = "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[] = "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[] = "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[] = "e2fsck";
310   if (e2prog (prog) == -1)
311     return -1;
312
313   /* 0 = no errors, 1 = errors corrected.
314    *
315    * >= 4 means uncorrected or other errors.
316    *
317    * 2, 3 means errors were corrected and we require a reboot.  This is
318    * a difficult corner case.
319    */
320   r = commandr (NULL, &err, prog, "-p", "-f", device, NULL);
321   if (r == -1 || r >= 2) {
322     reply_with_error ("%s", err);
323     free (err);
324     return -1;
325   }
326
327   free (err);
328   return 0;
329 }
330
331 int
332 do_mke2journal (int blocksize, const char *device)
333 {
334   char *err;
335   int r;
336
337   char prog[] = "mke2fs";
338   if (e2prog (prog) == -1)
339     return -1;
340
341   char blocksize_s[32];
342   snprintf (blocksize_s, sizeof blocksize_s, "%d", blocksize);
343
344   r = command (NULL, &err,
345                prog, "-O", "journal_dev", "-b", blocksize_s,
346                device, NULL);
347   if (r == -1) {
348     reply_with_error ("%s", err);
349     free (err);
350     return -1;
351   }
352
353   free (err);
354   return 0;
355 }
356
357 int
358 do_mke2journal_L (int blocksize, const char *label, const char *device)
359 {
360   char *err;
361   int r;
362
363   char prog[] = "mke2fs";
364   if (e2prog (prog) == -1)
365     return -1;
366
367   char blocksize_s[32];
368   snprintf (blocksize_s, sizeof blocksize_s, "%d", blocksize);
369
370   r = command (NULL, &err,
371                prog, "-O", "journal_dev", "-b", blocksize_s,
372                "-L", label,
373                device, NULL);
374   if (r == -1) {
375     reply_with_error ("%s", err);
376     free (err);
377     return -1;
378   }
379
380   free (err);
381   return 0;
382 }
383
384 int
385 do_mke2journal_U (int blocksize, const char *uuid, const char *device)
386 {
387   char *err;
388   int r;
389
390   char prog[] = "mke2fs";
391   if (e2prog (prog) == -1)
392     return -1;
393
394   char blocksize_s[32];
395   snprintf (blocksize_s, sizeof blocksize_s, "%d", blocksize);
396
397   r = command (NULL, &err,
398                prog, "-O", "journal_dev", "-b", blocksize_s,
399                "-U", uuid,
400                device, NULL);
401   if (r == -1) {
402     reply_with_error ("%s", err);
403     free (err);
404     return -1;
405   }
406
407   free (err);
408   return 0;
409 }
410
411 int
412 do_mke2fs_J (const char *fstype, int blocksize, const char *device,
413              const char *journal)
414 {
415   char *err;
416   int r;
417
418   char prog[] = "mke2fs";
419   if (e2prog (prog) == -1)
420     return -1;
421
422   char blocksize_s[32];
423   snprintf (blocksize_s, sizeof blocksize_s, "%d", blocksize);
424
425   int len = strlen (journal);
426   char jdev[len+32];
427   snprintf (jdev, len+32, "device=%s", journal);
428
429   r = command (NULL, &err,
430                prog, "-t", fstype, "-J", jdev, "-b", blocksize_s,
431                device, NULL);
432   if (r == -1) {
433     reply_with_error ("%s", err);
434     free (err);
435     return -1;
436   }
437
438   free (err);
439   return 0;
440 }
441
442 int
443 do_mke2fs_JL (const char *fstype, int blocksize, const char *device,
444               const char *label)
445 {
446   char *err;
447   int r;
448
449   char prog[] = "mke2fs";
450   if (e2prog (prog) == -1)
451     return -1;
452
453   char blocksize_s[32];
454   snprintf (blocksize_s, sizeof blocksize_s, "%d", blocksize);
455
456   int len = strlen (label);
457   char jdev[len+32];
458   snprintf (jdev, len+32, "device=LABEL=%s", label);
459
460   r = command (NULL, &err,
461                prog, "-t", fstype, "-J", jdev, "-b", blocksize_s,
462                device, NULL);
463   if (r == -1) {
464     reply_with_error ("%s", err);
465     free (err);
466     return -1;
467   }
468
469   free (err);
470   return 0;
471 }
472
473 int
474 do_mke2fs_JU (const char *fstype, int blocksize, const char *device,
475               const char *uuid)
476 {
477   char *err;
478   int r;
479
480   char prog[] = "mke2fs";
481   if (e2prog (prog) == -1)
482     return -1;
483
484   char blocksize_s[32];
485   snprintf (blocksize_s, sizeof blocksize_s, "%d", blocksize);
486
487   int len = strlen (uuid);
488   char jdev[len+32];
489   snprintf (jdev, len+32, "device=UUID=%s", uuid);
490
491   r = command (NULL, &err,
492                prog, "-t", fstype, "-J", jdev, "-b", blocksize_s,
493                device, NULL);
494   if (r == -1) {
495     reply_with_error ("%s", err);
496     free (err);
497     return -1;
498   }
499
500   free (err);
501   return 0;
502 }