diff options
Diffstat (limited to 'daemon/parted.c')
-rw-r--r-- | daemon/parted.c | 357 |
1 files changed, 357 insertions, 0 deletions
diff --git a/daemon/parted.c b/daemon/parted.c new file mode 100644 index 00000000..e0183fb4 --- /dev/null +++ b/daemon/parted.c @@ -0,0 +1,357 @@ +/* libguestfs - the guestfsd daemon + * Copyright (C) 2009 Red Hat Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <inttypes.h> +#include <string.h> +#include <unistd.h> + +#include "daemon.h" +#include "actions.h" + +/* Notes: + * + * Parted 1.9 sends error messages to stdout, hence use of the + * COMMAND_FLAG_FOLD_STDOUT_ON_STDERR flag. + * + * parted occasionally fails to do ioctl(BLKRRPART) on the device, + * apparently because of some internal race in the code. We attempt + * to detect and recover from this error if we can. + */ +static int +recover_blkrrpart (const char *device, const char *err) +{ + int r; + + if (!strstr (err, + "Error informing the kernel about modifications to partition")) + return -1; + + r = command (NULL, NULL, "/sbin/blockdev", "--rereadpt", device, NULL); + if (r == -1) + return -1; + + udev_settle (); + + return 0; +} + +#define RUN_PARTED(device,...) \ + do { \ + int r; \ + char *err; \ + \ + r = commandf (NULL, &err, COMMAND_FLAG_FOLD_STDOUT_ON_STDERR, \ + "/sbin/parted", "-s", "--", (device), __VA_ARGS__); \ + if (r == -1) { \ + if (recover_blkrrpart ((device), err) == -1) { \ + reply_with_error ("%s: parted: %s: %s", __func__, (device), err); \ + free (err); \ + return -1; \ + } \ + } \ + \ + free (err); \ + } while (0) + +static const char * +check_parttype (const char *parttype) +{ + /* Check and translate parttype. */ + if (strcmp (parttype, "aix") == 0 || + strcmp (parttype, "amiga") == 0 || + strcmp (parttype, "bsd") == 0 || + strcmp (parttype, "dasd") == 0 || + strcmp (parttype, "dvh") == 0 || + strcmp (parttype, "gpt") == 0 || + strcmp (parttype, "mac") == 0 || + strcmp (parttype, "msdos") == 0 || + strcmp (parttype, "pc98") == 0 || + strcmp (parttype, "sun") == 0) + return parttype; + else if (strcmp (parttype, "rdb") == 0) + return "amiga"; + else if (strcmp (parttype, "efi") == 0) + return "gpt"; + else if (strcmp (parttype, "mbr") == 0) + return "msdos"; + else + return NULL; +} + +int +do_part_init (const char *device, const char *parttype) +{ + parttype = check_parttype (parttype); + if (!parttype) { + reply_with_error ("part-init: unknown partition type: common choices are \"gpt\" and \"msdos\""); + return -1; + } + + RUN_PARTED (device, "mklabel", parttype, NULL); + + udev_settle (); + + return 0; +} + +int +do_part_add (const char *device, const char *prlogex, + int64_t startsect, int64_t endsect) +{ + char startstr[32]; + char endstr[32]; + + /* Check and translate prlogex. */ + if (strcmp (prlogex, "primary") == 0 || + strcmp (prlogex, "logical") == 0 || + strcmp (prlogex, "extended") == 0) + ; + else if (strcmp (prlogex, "p") == 0) + prlogex = "primary"; + else if (strcmp (prlogex, "l") == 0) + prlogex = "logical"; + else if (strcmp (prlogex, "e") == 0) + prlogex = "extended"; + else { + reply_with_error ("part-add: unknown partition type: %s: this should be \"primary\", \"logical\" or \"extended\"", prlogex); + return -1; + } + + if (startsect < 0) { + reply_with_error ("part-add: startsect cannot be negative"); + return -1; + } + /* but endsect can be negative */ + + snprintf (startstr, sizeof startstr, "%" PRIi64 "s", startsect); + snprintf (endstr, sizeof endstr, "%" PRIi64 "s", endsect); + + /* XXX Bug: If the partition table type (which we don't know in this + * function) is GPT, then this parted command sets the _partition + * name_ to prlogex, eg. "primary". I would essentially describe + * this as a bug in the parted mkpart command. + */ + RUN_PARTED (device, "mkpart", prlogex, startstr, endstr, NULL); + + udev_settle (); + + return 0; +} + +int +do_part_disk (const char *device, const char *parttype) +{ + const char *startstr; + const char *endstr; + + parttype = check_parttype (parttype); + if (!parttype) { + reply_with_error ("part-disk: unknown partition type: common choices are \"gpt\" and \"msdos\""); + return -1; + } + + /* Voooooodooooooooo (thanks Jim Meyering for working this out). */ + if (strcmp (parttype, "msdos") == 0) { + startstr = "1s"; + endstr = "-1s"; + } else if (strcmp (parttype, "gpt") == 0) { + startstr = "34s"; + endstr = "-34s"; + } else { + /* untested */ + startstr = "1s"; + endstr = "-1s"; + } + + RUN_PARTED (device, + "mklabel", parttype, + /* See comment about about the parted mkpart command. */ + "mkpart", strcmp (parttype, "gpt") == 0 ? "p1" : "primary", + startstr, endstr, NULL); + + udev_settle (); + + return 0; +} + +int +do_part_set_bootable (const char *device, int partnum, int bootable) +{ + char partstr[16]; + + snprintf (partstr, sizeof partstr, "%d", partnum); + + RUN_PARTED (device, "set", partstr, "boot", bootable ? "on" : "off", NULL); + + udev_settle (); + + return 0; +} + +int +do_part_set_name (const char *device, int partnum, const char *name) +{ + char partstr[16]; + + snprintf (partstr, sizeof partstr, "%d", partnum); + + RUN_PARTED (device, "name", partstr, name, NULL); + + udev_settle (); + + return 0; +} + +static char ** +print_partition_table (const char *device) +{ + char *out, *err; + int r; + char **lines; + + r = command (&out, &err, "/sbin/parted", "-m", "--", device, + "unit", "b", + "print", NULL); + if (r == -1) { + reply_with_error ("parted print: %s: %s", device, + /* Hack for parted 1.x which sends errors to stdout. */ + *err ? err : out); + free (out); + free (err); + return NULL; + } + free (err); + + lines = split_lines (out); + free (out); + + if (!lines) + return NULL; + + if (lines[0] == NULL || strcmp (lines[0], "BYT;") != 0) { + reply_with_error ("parted print: unknown signature, expected \"BYT;\" as first line of the output: %s", + lines[0] ? lines[0] : "(signature was null)"); + free_strings (lines); + return NULL; + } + + if (lines[1] == NULL) { + reply_with_error ("parted print: parted didn't return a line describing the device"); + free_strings (lines); + return NULL; + } + + return lines; +} + +char * +do_part_get_parttype (const char *device) +{ + char **lines; + char *r; + + lines = print_partition_table (device); + if (!lines) + return NULL; + + /* lines[1] is something like: + * "/dev/sda:1953525168s:scsi:512:512:msdos:ATA Hitachi HDT72101;" + */ + if (strtok (lines[1], ":") == NULL /* device */ + || strtok (NULL, ":") == NULL /* size */ + || strtok (NULL, ":") == NULL /* transport */ + || strtok (NULL, ":") == NULL /* sector size */ + || strtok (NULL, ":") == NULL /* physical sector size */ + || (r = strtok (NULL, ":")) == NULL /* return value */ + ) { + reply_with_error ("part_get_parttype: too few fields in output from parted print command: %s", lines[1]); + free_strings (lines); + return NULL; + } + + r = strdup (r); + if (!r) { + reply_with_perror ("strdup"); + free_strings (lines); + return NULL; + } + + free_strings (lines); + + return r; +} + +guestfs_int_partition_list * +do_part_list (const char *device) +{ + char **lines; + size_t row, nr_rows, i; + guestfs_int_partition_list *r; + + lines = print_partition_table (device); + if (!lines) + return NULL; + + /* lines[0] is "BYT;", lines[1] is the device line which we ignore, + * lines[2..] are the partitions themselves. Count how many. + */ + nr_rows = 0; + for (row = 2; lines[row] != NULL; ++row) + ++nr_rows; + + r = malloc (sizeof *r); + if (r == NULL) { + reply_with_perror ("malloc"); + goto error1; + } + r->guestfs_int_partition_list_len = nr_rows; + r->guestfs_int_partition_list_val = + malloc (nr_rows * sizeof (guestfs_int_partition)); + if (r->guestfs_int_partition_list_val == NULL) { + reply_with_perror ("malloc"); + goto error2; + } + + /* Now parse the lines. */ + for (i = 0, row = 2; lines[row] != NULL; ++i, ++row) { + if (sscanf (lines[row], "%d:%" SCNi64 "B:%" SCNi64 "B:%" SCNi64 "B", + &r->guestfs_int_partition_list_val[i].part_num, + &r->guestfs_int_partition_list_val[i].part_start, + &r->guestfs_int_partition_list_val[i].part_end, + &r->guestfs_int_partition_list_val[i].part_size) != 4) { + reply_with_error ("part_list: could not parse row from output of parted print command: %s", lines[row]); + goto error3; + } + } + + free_strings (lines); + return r; + + error3: + free (r->guestfs_int_partition_list_val); + error2: + free (r); + error1: + free_strings (lines); + return NULL; +} |