New API: Implement pwrite system call (partial fix for RHBZ#592883).
[libguestfs.git] / daemon / parted.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 <stdint.h>
24 #include <inttypes.h>
25 #include <string.h>
26 #include <unistd.h>
27
28 #include "daemon.h"
29 #include "actions.h"
30
31 /* Notes:
32  *
33  * Parted 1.9 sends error messages to stdout, hence use of the
34  * COMMAND_FLAG_FOLD_STDOUT_ON_STDERR flag.
35  *
36  * parted occasionally fails to do ioctl(BLKRRPART) on the device,
37  * apparently because of some internal race in the code.  We attempt
38  * to detect and recover from this error if we can.
39  */
40 static int
41 recover_blkrrpart (const char *device, const char *err)
42 {
43   int r;
44
45   if (!strstr (err,
46                "Error informing the kernel about modifications to partition"))
47     return -1;
48
49   r = command (NULL, NULL, "blockdev", "--rereadpt", device, NULL);
50   if (r == -1)
51     return -1;
52
53   udev_settle ();
54
55   return 0;
56 }
57
58 #define RUN_PARTED(error,device,...)                                    \
59   do {                                                                  \
60     int r;                                                              \
61     char *err;                                                          \
62                                                                         \
63     r = commandf (NULL, &err, COMMAND_FLAG_FOLD_STDOUT_ON_STDERR,       \
64                   "parted", "-s", "--", (device), __VA_ARGS__);   \
65     if (r == -1) {                                                      \
66       if (recover_blkrrpart ((device), err) == -1) {                    \
67         reply_with_error ("%s: parted: %s: %s", __func__, (device), err); \
68         free (err);                                                     \
69         error;                                                          \
70       }                                                                 \
71     }                                                                   \
72                                                                         \
73     free (err);                                                         \
74   } while (0)
75
76 static const char *
77 check_parttype (const char *parttype)
78 {
79   /* Check and translate parttype. */
80   if (STREQ (parttype, "aix") ||
81       STREQ (parttype, "amiga") ||
82       STREQ (parttype, "bsd") ||
83       STREQ (parttype, "dasd") ||
84       STREQ (parttype, "dvh") ||
85       STREQ (parttype, "gpt") ||
86       STREQ (parttype, "mac") ||
87       STREQ (parttype, "msdos") ||
88       STREQ (parttype, "pc98") ||
89       STREQ (parttype, "sun"))
90     return parttype;
91   else if (STREQ (parttype, "rdb"))
92     return "amiga";
93   else if (STREQ (parttype, "efi"))
94     return "gpt";
95   else if (STREQ (parttype, "mbr"))
96     return "msdos";
97   else
98     return NULL;
99 }
100
101 int
102 do_part_init (const char *device, const char *parttype)
103 {
104   parttype = check_parttype (parttype);
105   if (!parttype) {
106     reply_with_error ("unknown partition type: common choices are \"gpt\" and \"msdos\"");
107     return -1;
108   }
109
110   RUN_PARTED (return -1, device, "mklabel", parttype, NULL);
111
112   udev_settle ();
113
114   return 0;
115 }
116
117 int
118 do_part_add (const char *device, const char *prlogex,
119              int64_t startsect, int64_t endsect)
120 {
121   char startstr[32];
122   char endstr[32];
123
124   /* Check and translate prlogex. */
125   if (STREQ (prlogex, "primary") ||
126       STREQ (prlogex, "logical") ||
127       STREQ (prlogex, "extended"))
128     ;
129   else if (STREQ (prlogex, "p"))
130     prlogex = "primary";
131   else if (STREQ (prlogex, "l"))
132     prlogex = "logical";
133   else if (STREQ (prlogex, "e"))
134     prlogex = "extended";
135   else {
136     reply_with_error ("unknown partition type: %s: this should be \"primary\", \"logical\" or \"extended\"", prlogex);
137     return -1;
138   }
139
140   if (startsect < 0) {
141     reply_with_error ("startsect cannot be negative");
142     return -1;
143   }
144   /* but endsect can be negative */
145
146   snprintf (startstr, sizeof startstr, "%" PRIi64 "s", startsect);
147   snprintf (endstr, sizeof endstr, "%" PRIi64 "s", endsect);
148
149   /* XXX Bug: If the partition table type (which we don't know in this
150    * function) is GPT, then this parted command sets the _partition
151    * name_ to prlogex, eg. "primary".  I would essentially describe
152    * this as a bug in the parted mkpart command.
153    */
154   RUN_PARTED (return -1, device, "mkpart", prlogex, startstr, endstr, NULL);
155
156   udev_settle ();
157
158   return 0;
159 }
160
161 int
162 do_part_del (const char *device, int partnum)
163 {
164   char partnum_str[16];
165   snprintf (partnum_str, sizeof partnum_str, "%d", partnum);
166
167   RUN_PARTED (return -1, device, "rm", partnum_str, NULL);
168
169   udev_settle ();
170   return 0;
171 }
172
173 int
174 do_part_disk (const char *device, const char *parttype)
175 {
176   const char *startstr;
177   const char *endstr;
178
179   parttype = check_parttype (parttype);
180   if (!parttype) {
181     reply_with_error ("unknown partition type: common choices are \"gpt\" and \"msdos\"");
182     return -1;
183   }
184
185   /* Voooooodooooooooo (thanks Jim Meyering for working this out). */
186   if (STREQ (parttype, "msdos")) {
187     startstr = "1s";
188     endstr = "-1s";
189   } else if (STREQ (parttype, "gpt")) {
190     startstr = "34s";
191     endstr = "-34s";
192   } else {
193     /* untested */
194     startstr = "1s";
195     endstr = "-1s";
196   }
197
198   RUN_PARTED (return -1,
199               device,
200               "mklabel", parttype,
201               /* See comment about about the parted mkpart command. */
202               "mkpart", STREQ (parttype, "gpt") ? "p1" : "primary",
203               startstr, endstr, NULL);
204
205   udev_settle ();
206
207   return 0;
208 }
209
210 int
211 do_part_set_bootable (const char *device, int partnum, int bootable)
212 {
213   char partstr[16];
214
215   snprintf (partstr, sizeof partstr, "%d", partnum);
216
217   RUN_PARTED (return -1,
218               device, "set", partstr, "boot", bootable ? "on" : "off", NULL);
219
220   udev_settle ();
221
222   return 0;
223 }
224
225 int
226 do_part_set_name (const char *device, int partnum, const char *name)
227 {
228   char partstr[16];
229
230   snprintf (partstr, sizeof partstr, "%d", partnum);
231
232   RUN_PARTED (return -1, device, "name", partstr, name, NULL);
233
234   udev_settle ();
235
236   return 0;
237 }
238
239 /* Return the nth field from a string of ':'/';'-delimited strings.
240  * Useful for parsing the return value from print_partition_table
241  * function below.
242  */
243 static char *
244 get_table_field (const char *line, int n)
245 {
246   const char *p = line;
247
248   while (*p && n > 0) {
249     p += strcspn (p, ":;") + 1;
250     n--;
251   }
252
253   if (n > 0) {
254     reply_with_error ("not enough fields in output of parted print command: %s",
255                       line);
256     return NULL;
257   }
258
259   size_t len = strcspn (p, ":;");
260   char *q = strndup (p, len);
261   if (q == NULL) {
262     reply_with_perror ("strndup");
263     return NULL;
264   }
265
266   return q;
267 }
268
269 static char **
270 print_partition_table (const char *device)
271 {
272   char *out, *err;
273   int r;
274   char **lines;
275
276   r = command (&out, &err, "parted", "-m", "--", device,
277                "unit", "b",
278                "print", NULL);
279   if (r == -1) {
280     reply_with_error ("parted print: %s: %s", device,
281                       /* Hack for parted 1.x which sends errors to stdout. */
282                       *err ? err : out);
283     free (out);
284     free (err);
285     return NULL;
286   }
287   free (err);
288
289   lines = split_lines (out);
290   free (out);
291
292   if (!lines)
293     return NULL;
294
295   if (lines[0] == NULL || STRNEQ (lines[0], "BYT;")) {
296     reply_with_error ("unknown signature, expected \"BYT;\" as first line of the output: %s",
297                       lines[0] ? lines[0] : "(signature was null)");
298     free_strings (lines);
299     return NULL;
300   }
301
302   if (lines[1] == NULL) {
303     reply_with_error ("parted didn't return a line describing the device");
304     free_strings (lines);
305     return NULL;
306   }
307
308   return lines;
309 }
310
311 char *
312 do_part_get_parttype (const char *device)
313 {
314   char **lines = print_partition_table (device);
315   if (!lines)
316     return NULL;
317
318   /* lines[1] is something like:
319    * "/dev/sda:1953525168s:scsi:512:512:msdos:ATA Hitachi HDT72101;"
320    */
321   char *r = get_table_field (lines[1], 5);
322   if (r == NULL) {
323     free_strings (lines);
324     return NULL;
325   }
326
327   free_strings (lines);
328
329   return r;
330 }
331
332 guestfs_int_partition_list *
333 do_part_list (const char *device)
334 {
335   char **lines;
336   size_t row, nr_rows, i;
337   guestfs_int_partition_list *r;
338
339   lines = print_partition_table (device);
340   if (!lines)
341     return NULL;
342
343   /* lines[0] is "BYT;", lines[1] is the device line which we ignore,
344    * lines[2..] are the partitions themselves.  Count how many.
345    */
346   nr_rows = 0;
347   for (row = 2; lines[row] != NULL; ++row)
348     ++nr_rows;
349
350   r = malloc (sizeof *r);
351   if (r == NULL) {
352     reply_with_perror ("malloc");
353     goto error1;
354   }
355   r->guestfs_int_partition_list_len = nr_rows;
356   r->guestfs_int_partition_list_val =
357     malloc (nr_rows * sizeof (guestfs_int_partition));
358   if (r->guestfs_int_partition_list_val == NULL) {
359     reply_with_perror ("malloc");
360     goto error2;
361   }
362
363   /* Now parse the lines. */
364   for (i = 0, row = 2; lines[row] != NULL; ++i, ++row) {
365     if (sscanf (lines[row], "%d:%" SCNi64 "B:%" SCNi64 "B:%" SCNi64 "B",
366                 &r->guestfs_int_partition_list_val[i].part_num,
367                 &r->guestfs_int_partition_list_val[i].part_start,
368                 &r->guestfs_int_partition_list_val[i].part_end,
369                 &r->guestfs_int_partition_list_val[i].part_size) != 4) {
370       reply_with_error ("could not parse row from output of parted print command: %s", lines[row]);
371       goto error3;
372     }
373   }
374
375   free_strings (lines);
376   return r;
377
378  error3:
379   free (r->guestfs_int_partition_list_val);
380  error2:
381   free (r);
382  error1:
383   free_strings (lines);
384   return NULL;
385 }
386
387 int
388 do_part_get_bootable (const char *device, int partnum)
389 {
390   char **lines = print_partition_table (device);
391   if (!lines)
392     return -1;
393
394   /* We want lines[1+partnum]. */
395   if (count_strings (lines) < 1+partnum) {
396     reply_with_error ("partition number out of range: %d", partnum);
397     free_strings (lines);
398     return -1;
399   }
400
401   char *boot = get_table_field (lines[1+partnum], 6);
402   if (boot == NULL) {
403     free_strings (lines);
404     return -1;
405   }
406
407   int r = STREQ (boot, "boot");
408
409   free (boot);
410   free_strings (lines);
411
412   return r;
413 }
414
415 /* Currently we use sfdisk for getting and setting the ID byte.  In
416  * future, extend parted to provide this functionality.  As a result
417  * of using sfdisk, this won't work for non-MBR-style partitions, but
418  * that limitation is noted in the documentation and we can extend it
419  * later without breaking the ABI.
420  */
421 int
422 do_part_get_mbr_id (const char *device, int partnum)
423 {
424   char partnum_str[16];
425   snprintf (partnum_str, sizeof partnum_str, "%d", partnum);
426
427   char *out, *err;
428   int r;
429
430   r = command (&out, &err, "sfdisk", "--print-id", device, partnum_str, NULL);
431   if (r == -1) {
432     reply_with_error ("sfdisk --print-id: %s", err);
433     free (out);
434     free (err);
435     return -1;
436   }
437
438   free (err);
439
440   /* It's printed in hex ... */
441   int id;
442   if (sscanf (out, "%x", &id) != 1) {
443     reply_with_error ("sfdisk --print-id: cannot parse output: %s", out);
444     free (out);
445     return -1;
446   }
447
448   free (out);
449   return id;
450 }
451
452 int
453 do_part_set_mbr_id (const char *device, int partnum, int idbyte)
454 {
455   char partnum_str[16];
456   snprintf (partnum_str, sizeof partnum_str, "%d", partnum);
457
458   char idbyte_str[16];
459   snprintf (idbyte_str, sizeof partnum_str, "%x", idbyte); /* NB: hex */
460
461   char *err;
462   int r;
463
464   r = command (NULL, &err, "sfdisk",
465                "--change-id", device, partnum_str, idbyte_str, NULL);
466   if (r == -1) {
467     reply_with_error ("sfdisk --change-id: %s", err);
468     free (err);
469     return -1;
470   }
471
472   free (err);
473   return 0;
474 }