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