daemon: debug segv correct use of dereferencing NULL.
[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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 "guestfs_protocol.h"
28 #include "daemon.h"
29 #include "c-ctype.h"
30 #include "actions.h"
31
32 /* Confirmed this is true up to ext4 from the Linux sources. */
33 #define EXT2_LABEL_MAX 16
34
35 #define MAX_ARGS 64
36
37 /* Choose which tools like mke2fs to use.  For RHEL 5 (only) there
38  * is a special set of tools which support ext2/3/4.  eg. On RHEL 5,
39  * mke2fs only supports ext2/3, but mke4fs supports ext2/3/4.
40  *
41  * We specify e4fsprogs in the package list to ensure it is loaded
42  * if it exists.
43  */
44 int
45 e2prog (char *name)
46 {
47   char *p = strstr (name, "e2");
48   if (!p) return 0;
49   p++;
50
51   *p = '4';
52   if (prog_exists (name))
53     return 0;
54
55   *p = '2';
56   if (prog_exists (name))
57     return 0;
58
59   reply_with_error ("cannot find required program %s", name);
60   return -1;
61 }
62
63 char **
64 do_tune2fs_l (const char *device)
65 {
66   int r;
67   char *out, *err;
68   char *p, *pend, *colon;
69   char **ret = NULL;
70   int size = 0, alloc = 0;
71
72   char prog[] = "tune2fs";
73   if (e2prog (prog) == -1)
74     return NULL;
75
76   r = command (&out, &err, prog, "-l", device, NULL);
77   if (r == -1) {
78     reply_with_error ("%s", err);
79     free (err);
80     free (out);
81     return NULL;
82   }
83   free (err);
84
85   p = out;
86
87   /* Discard the first line if it contains "tune2fs ...". */
88   if (STRPREFIX (p, "tune2fs ") || STRPREFIX (p, "tune4fs ")) {
89     p = strchr (p, '\n');
90     if (p) p++;
91     else {
92       reply_with_error ("truncated output");
93       free (out);
94       return NULL;
95     }
96   }
97
98   /* Read the lines and split into "key: value". */
99   while (*p) {
100     pend = strchrnul (p, '\n');
101     if (*pend == '\n') {
102       *pend = '\0';
103       pend++;
104     }
105
106     if (!*p) continue;
107
108     colon = strchr (p, ':');
109     if (colon) {
110       *colon = '\0';
111
112       do { colon++; } while (*colon && c_isspace (*colon));
113
114       if (add_string (&ret, &size, &alloc, p) == -1) {
115         free (out);
116         return NULL;
117       }
118       if (STREQ (colon, "<none>") ||
119           STREQ (colon, "<not available>") ||
120           STREQ (colon, "(none)")) {
121         if (add_string (&ret, &size, &alloc, "") == -1) {
122           free (out);
123           return NULL;
124         }
125       } else {
126         if (add_string (&ret, &size, &alloc, colon) == -1) {
127           free (out);
128           return NULL;
129         }
130       }
131     }
132     else {
133       if (add_string (&ret, &size, &alloc, p) == -1) {
134         free (out);
135         return NULL;
136       }
137       if (add_string (&ret, &size, &alloc, "") == -1) {
138         free (out);
139         return NULL;
140       }
141     }
142
143     p = pend;
144   }
145
146   free (out);
147
148   if (add_string (&ret, &size, &alloc, NULL) == -1)
149     return NULL;
150
151   return ret;
152 }
153
154 int
155 do_set_e2label (const char *device, const char *label)
156 {
157   int r;
158   char *err;
159
160   char prog[] = "e2label";
161   if (e2prog (prog) == -1)
162     return -1;
163
164   if (strlen (label) > EXT2_LABEL_MAX) {
165     reply_with_error ("%s: ext2 labels are limited to %d bytes",
166                       label, EXT2_LABEL_MAX);
167     return -1;
168   }
169
170   r = command (NULL, &err, prog, device, label, NULL);
171   if (r == -1) {
172     reply_with_error ("%s", err);
173     free (err);
174     return -1;
175   }
176
177   free (err);
178   return 0;
179 }
180
181 char *
182 do_get_e2label (const char *device)
183 {
184   return do_vfs_label (device);
185 }
186
187 int
188 do_set_e2uuid (const char *device, const char *uuid)
189 {
190   int r;
191   char *err;
192
193   char prog[] = "tune2fs";
194   if (e2prog (prog) == -1)
195     return -1;
196
197   r = command (NULL, &err, prog, "-U", uuid, device, NULL);
198   if (r == -1) {
199     reply_with_error ("%s", err);
200     free (err);
201     return -1;
202   }
203
204   free (err);
205   return 0;
206 }
207
208 char *
209 do_get_e2uuid (const char *device)
210 {
211   return do_vfs_uuid (device);
212 }
213
214 int
215 do_resize2fs (const char *device)
216 {
217   char *err;
218   int r;
219
220   char prog[] = "resize2fs";
221   if (e2prog (prog) == -1)
222     return -1;
223
224   r = command (NULL, &err, prog, device, NULL);
225   if (r == -1) {
226     reply_with_error ("%s", err);
227     free (err);
228     return -1;
229   }
230
231   free (err);
232   return 0;
233 }
234
235 int
236 do_resize2fs_size (const char *device, int64_t size)
237 {
238   char *err;
239   int r;
240
241   char prog[] = "resize2fs";
242   if (e2prog (prog) == -1)
243     return -1;
244
245   /* resize2fs itself may impose additional limits.  Since we are
246    * going to use the 'K' suffix however we can only work with whole
247    * kilobytes.
248    */
249   if (size & 1023) {
250     reply_with_error ("%" PRIi64 ": size must be a round number of kilobytes",
251                       size);
252     return -1;
253   }
254   size /= 1024;
255
256   char buf[32];
257   snprintf (buf, sizeof buf, "%" PRIi64 "K", size);
258
259   r = command (NULL, &err, prog, device, buf, NULL);
260   if (r == -1) {
261     reply_with_error ("%s", err);
262     free (err);
263     return -1;
264   }
265
266   free (err);
267   return 0;
268 }
269
270 int
271 do_resize2fs_M (const char *device)
272 {
273   char *err;
274   int r;
275
276   char prog[] = "resize2fs";
277   if (e2prog (prog) == -1)
278     return -1;
279
280   r = command (NULL, &err, prog, "-M" , device, NULL);
281   if (r == -1) {
282     reply_with_error ("%s", err);
283     free (err);
284     return -1;
285   }
286
287   free (err);
288   return 0;
289 }
290
291 int
292 do_e2fsck_f (const char *device)
293 {
294   char *err;
295   int r;
296
297   char prog[] = "e2fsck";
298   if (e2prog (prog) == -1)
299     return -1;
300
301   /* 0 = no errors, 1 = errors corrected.
302    *
303    * >= 4 means uncorrected or other errors.
304    *
305    * 2, 3 means errors were corrected and we require a reboot.  This is
306    * a difficult corner case.
307    */
308   r = commandr (NULL, &err, prog, "-p", "-f", device, NULL);
309   if (r == -1 || r >= 2) {
310     reply_with_error ("%s", err);
311     free (err);
312     return -1;
313   }
314
315   free (err);
316   return 0;
317 }
318
319 int
320 do_mke2journal (int blocksize, const char *device)
321 {
322   char *err;
323   int r;
324
325   char prog[] = "mke2fs";
326   if (e2prog (prog) == -1)
327     return -1;
328
329   char blocksize_s[32];
330   snprintf (blocksize_s, sizeof blocksize_s, "%d", blocksize);
331
332   r = command (NULL, &err,
333                prog, "-O", "journal_dev", "-b", blocksize_s,
334                device, NULL);
335   if (r == -1) {
336     reply_with_error ("%s", err);
337     free (err);
338     return -1;
339   }
340
341   free (err);
342   return 0;
343 }
344
345 int
346 do_mke2journal_L (int blocksize, const char *label, const char *device)
347 {
348   char *err;
349   int r;
350
351   char prog[] = "mke2fs";
352   if (e2prog (prog) == -1)
353     return -1;
354
355   if (strlen (label) > EXT2_LABEL_MAX) {
356     reply_with_error ("%s: ext2 labels are limited to %d bytes",
357                       label, EXT2_LABEL_MAX);
358     return -1;
359   }
360
361   char blocksize_s[32];
362   snprintf (blocksize_s, sizeof blocksize_s, "%d", blocksize);
363
364   r = command (NULL, &err,
365                prog, "-O", "journal_dev", "-b", blocksize_s,
366                "-L", label,
367                device, NULL);
368   if (r == -1) {
369     reply_with_error ("%s", err);
370     free (err);
371     return -1;
372   }
373
374   free (err);
375   return 0;
376 }
377
378 int
379 do_mke2journal_U (int blocksize, const char *uuid, const char *device)
380 {
381   char *err;
382   int r;
383
384   char prog[] = "mke2fs";
385   if (e2prog (prog) == -1)
386     return -1;
387
388   char blocksize_s[32];
389   snprintf (blocksize_s, sizeof blocksize_s, "%d", blocksize);
390
391   r = command (NULL, &err,
392                prog, "-O", "journal_dev", "-b", blocksize_s,
393                "-U", uuid,
394                device, NULL);
395   if (r == -1) {
396     reply_with_error ("%s", err);
397     free (err);
398     return -1;
399   }
400
401   free (err);
402   return 0;
403 }
404
405 int
406 do_mke2fs_J (const char *fstype, int blocksize, const char *device,
407              const char *journal)
408 {
409   char *err;
410   int r;
411
412   char prog[] = "mke2fs";
413   if (e2prog (prog) == -1)
414     return -1;
415
416   char blocksize_s[32];
417   snprintf (blocksize_s, sizeof blocksize_s, "%d", blocksize);
418
419   int len = strlen (journal);
420   char jdev[len+32];
421   snprintf (jdev, len+32, "device=%s", journal);
422
423   r = command (NULL, &err,
424                prog, "-t", fstype, "-J", jdev, "-b", blocksize_s,
425                device, NULL);
426   if (r == -1) {
427     reply_with_error ("%s", err);
428     free (err);
429     return -1;
430   }
431
432   free (err);
433   return 0;
434 }
435
436 int
437 do_mke2fs_JL (const char *fstype, int blocksize, const char *device,
438               const char *label)
439 {
440   char *err;
441   int r;
442
443   char prog[] = "mke2fs";
444   if (e2prog (prog) == -1)
445     return -1;
446
447   if (strlen (label) > EXT2_LABEL_MAX) {
448     reply_with_error ("%s: ext2 labels are limited to %d bytes",
449                       label, EXT2_LABEL_MAX);
450     return -1;
451   }
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 }
503
504 /* Takes optional arguments, consult optargs_bitmask. */
505 int
506 do_tune2fs (const char *device, /* only required parameter */
507             int force,
508             int maxmountcount,
509             int mountcount,
510             const char *errorbehavior,
511             int64_t group,
512             int intervalbetweenchecks,
513             int reservedblockspercentage,
514             const char *lastmounteddirectory,
515             int64_t reservedblockscount,
516             int64_t user)
517 {
518   const char *argv[MAX_ARGS];
519   size_t i = 0;
520   int r;
521   char *err;
522   char prog[] = "tune2fs";
523   char maxmountcount_s[64];
524   char mountcount_s[64];
525   char group_s[64];
526   char intervalbetweenchecks_s[64];
527   char reservedblockspercentage_s[64];
528   char reservedblockscount_s[64];
529   char user_s[64];
530
531   if (e2prog (prog) == -1)
532     return -1;
533
534   ADD_ARG (argv, i, prog);
535
536   if (optargs_bitmask & GUESTFS_TUNE2FS_FORCE_BITMASK) {
537     if (force)
538       ADD_ARG (argv, i, "-f");
539   }
540
541   if (optargs_bitmask & GUESTFS_TUNE2FS_MAXMOUNTCOUNT_BITMASK) {
542     if (maxmountcount < 0) {
543       reply_with_error ("maxmountcount cannot be negative");
544       return -1;
545     }
546     ADD_ARG (argv, i, "-c");
547     snprintf (maxmountcount_s, sizeof maxmountcount_s, "%d", maxmountcount);
548     ADD_ARG (argv, i, maxmountcount_s);
549   }
550
551   if (optargs_bitmask & GUESTFS_TUNE2FS_MOUNTCOUNT_BITMASK) {
552     if (mountcount < 0) {
553       reply_with_error ("mountcount cannot be negative");
554       return -1;
555     }
556     ADD_ARG (argv, i, "-C");
557     snprintf (mountcount_s, sizeof mountcount_s, "%d", mountcount);
558     ADD_ARG (argv, i, mountcount_s);
559   }
560
561   if (optargs_bitmask & GUESTFS_TUNE2FS_ERRORBEHAVIOR_BITMASK) {
562     if (STRNEQ (errorbehavior, "continue") &&
563         STRNEQ (errorbehavior, "remount-ro") &&
564         STRNEQ (errorbehavior, "panic")) {
565       reply_with_error ("invalid errorbehavior parameter: %s", errorbehavior);
566       return -1;
567     }
568     ADD_ARG (argv, i, "-e");
569     ADD_ARG (argv, i, errorbehavior);
570   }
571
572   if (optargs_bitmask & GUESTFS_TUNE2FS_GROUP_BITMASK) {
573     if (group < 0) {
574       reply_with_error ("group cannot be negative");
575       return -1;
576     }
577     ADD_ARG (argv, i, "-g");
578     snprintf (group_s, sizeof group_s, "%" PRIi64, group);
579     ADD_ARG (argv, i, group_s);
580   }
581
582   if (optargs_bitmask & GUESTFS_TUNE2FS_INTERVALBETWEENCHECKS_BITMASK) {
583     if (intervalbetweenchecks < 0) {
584       reply_with_error ("intervalbetweenchecks cannot be negative");
585       return -1;
586     }
587     ADD_ARG (argv, i, "-i");
588     if (intervalbetweenchecks > 0) {
589       /* -i <NN>s is not documented in the man page, but has been
590        * supported in tune2fs for several years.
591        */
592       snprintf (intervalbetweenchecks_s, sizeof intervalbetweenchecks_s,
593                 "%ds", intervalbetweenchecks);
594       ADD_ARG (argv, i, intervalbetweenchecks_s);
595     }
596     else
597       ADD_ARG (argv, i, "0");
598   }
599
600   if (optargs_bitmask & GUESTFS_TUNE2FS_RESERVEDBLOCKSPERCENTAGE_BITMASK) {
601     if (reservedblockspercentage < 0) {
602       reply_with_error ("reservedblockspercentage cannot be negative");
603       return -1;
604     }
605     ADD_ARG (argv, i, "-m");
606     snprintf (reservedblockspercentage_s, sizeof reservedblockspercentage_s,
607               "%d", reservedblockspercentage);
608     ADD_ARG (argv, i, reservedblockspercentage_s);
609   }
610
611   if (optargs_bitmask & GUESTFS_TUNE2FS_LASTMOUNTEDDIRECTORY_BITMASK) {
612     ADD_ARG (argv, i, "-M");
613     ADD_ARG (argv, i, lastmounteddirectory);
614   }
615
616   if (optargs_bitmask & GUESTFS_TUNE2FS_RESERVEDBLOCKSCOUNT_BITMASK) {
617     if (reservedblockscount < 0) {
618       reply_with_error ("reservedblockscount cannot be negative");
619       return -1;
620     }
621     ADD_ARG (argv, i, "-r");
622     snprintf (reservedblockscount_s, sizeof reservedblockscount_s,
623               "%" PRIi64, reservedblockscount);
624     ADD_ARG (argv, i, reservedblockscount_s);
625   }
626
627   if (optargs_bitmask & GUESTFS_TUNE2FS_USER_BITMASK) {
628     if (user < 0) {
629       reply_with_error ("user cannot be negative");
630       return -1;
631     }
632     ADD_ARG (argv, i, "-u");
633     snprintf (user_s, sizeof user_s, "%" PRIi64, user);
634     ADD_ARG (argv, i, user_s);
635   }
636
637   ADD_ARG (argv, i, device);
638   ADD_ARG (argv, i, NULL);
639
640   r = commandv (NULL, &err, argv);
641   if (r == -1) {
642     reply_with_error ("%s: %s: %s", prog, device, err);
643     free (err);
644     return -1;
645   }
646
647   free (err);
648   return 0;
649 }