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