>From 74914b6fd255d849c8d5caf482e9c727f0a13afd Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Wed, 4 Nov 2009 23:15:26 +0000 Subject: [PATCH] Generic partition creation interface. The current sfdisk interface works, but is somewhat limited since it can only deal with MBR-style partitions. For disks larger than 2TB, MBR partitions are unsuitable and we need to provide access to other partition table types (eg. GPT). This commit introduces a generic partition creation interface which should be future-proof and extensible. The implementation is based on parted. --- appliance/packagelist.in | 1 + daemon/Makefile.am | 1 + daemon/parted.c | 210 ++++++++++++++++++++ guestfish.pod | 2 +- guestfs.pod | 28 ++-- ocaml/t/guestfs_060_readdir.ml | 2 +- perl/t/060-readdir.t | 2 +- po/POTFILES.in | 1 + recipes/tar2vm.sh | 2 +- regressions/rhbz503169c10.sh | 2 +- regressions/rhbz503169c13.sh | 2 +- .../test-cancellation-upload-daemoncancels.sh | 2 +- regressions/test-remote.sh | 2 +- src/MAX_PROC_NR | 2 +- src/generator.ml | 169 ++++++++++++++--- 15 files changed, 380 insertions(+), 48 deletions(-) create mode 100644 daemon/parted.c diff --git a/appliance/packagelist.in b/appliance/packagelist.in index b3609df..4c75f15 100644 --- a/appliance/packagelist.in +++ b/appliance/packagelist.in @@ -40,6 +40,7 @@ lvm2 module-init-tools net-tools ntfs-3g +parted procps strace xfsprogs diff --git a/daemon/Makefile.am b/daemon/Makefile.am index db311ab..72e1896 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -60,6 +60,7 @@ guestfsd_SOURCES = \ mount.c \ names.c \ ntfs.c \ + parted.c \ pingdaemon.c \ proto.c \ readdir.c \ diff --git a/daemon/parted.c b/daemon/parted.c new file mode 100644 index 0000000..696243c --- /dev/null +++ b/daemon/parted.c @@ -0,0 +1,210 @@ +/* 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 + +#include +#include +#include +#include +#include +#include + +#include "daemon.h" +#include "actions.h" + +/* XXX parted sends error messages to stdout!!! This is an upstream + * parted bug and I have no intention of fixing it here. + */ + +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) +{ + char *err; + int r; + + parttype = check_parttype (parttype); + if (!parttype) { + reply_with_error ("part-init: unknown partition type: common choices are \"gpt\" and \"msdos\""); + return -1; + } + + r = command (NULL, &err, + "/sbin/parted", "-s", "--", device, + "mklabel", parttype, NULL); + if (r == -1) { + reply_with_perror ("part-init: %s: %s", device, err); + free (err); + return -1; + } + + free (err); + + udev_settle (); + + return 0; +} + +int +do_part_add (const char *device, const char *prlogex, + int64_t startsect, int64_t endsect) +{ + char *err; + int r; + 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); + + r = command (NULL, &err, + "/sbin/parted", "-s", "--", device, + "mkpart", prlogex, startstr, endstr, NULL); + if (r == -1) { + reply_with_error ("part-add: %s: %s", device, err); + free (err); + return -1; + } + + free (err); + + udev_settle (); + + return 0; +} + +int +do_part_set_bootable (const char *device, int partnum, int bootable) +{ + char *err; + int r; + char partstr[16]; + + snprintf (partstr, sizeof partstr, "%d", partnum); + + r = command (NULL, &err, + "/sbin/parted", "-s", "--", device, + "set", partstr, "boot", bootable ? "on" : "off", NULL); + if (r == -1) { + reply_with_perror ("part-bootable: %s: %s", device, err); + free (err); + return -1; + } + + free (err); + + udev_settle (); + + return 0; +} + +int +do_part_disk (const char *device, const char *parttype) +{ + char *err; + int r; + 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"; + } + + r = command (NULL, &err, + "/sbin/parted", "-s", "--", device, + "mklabel", parttype, + /* This slightly wrong - in the GPT case it will give + * the partition the name "primary". We have to give + * it some name, so this is as good as any. + */ + "mkpart", "primary", startstr, endstr, + NULL); + if (r == -1) { + reply_with_perror ("part-disk: %s: %s", device, err); + free (err); + return -1; + } + + free (err); + + udev_settle (); + + return 0; +} diff --git a/guestfish.pod b/guestfish.pod index 8ae1800..2508066 100644 --- a/guestfish.pod +++ b/guestfish.pod @@ -63,7 +63,7 @@ Remove C (in reality not such a great idea): #!/usr/bin/guestfish -f alloc /tmp/output.img 10M run - sfdisk /dev/sda 0 0 0 , + part-disk /dev/sda mbr mkfs ext2 /dev/sda1 =head2 Remote control diff --git a/guestfs.pod b/guestfs.pod index 33b84d7..fdac80a 100644 --- a/guestfs.pod +++ b/guestfs.pod @@ -217,29 +217,27 @@ L. =head2 PARTITIONING -To create MBR-style (ie. normal PC) partitions use one of the -C variants. These calls use the external -L command. +In the common case where you want to create a single partition +covering the whole disk, you should use the C +call: -The simplest call is: - - char *lines[] = { ",", NULL }; - guestfs_sfdiskM (g, "/dev/sda", lines); - -This will create a single partition on C called -C covering the whole disk. + const char *parttype = "mbr"; + if (disk_is_larger_than_2TB) + parttype = "gpt"; + guestfs_part_disk (g, "/dev/sda", parttype); In general MBR partitions are both unnecessarily complicated and depend on archaic details, namely the Cylinder-Head-Sector (CHS) -geometry of the disk. C allows you to specify sizes -in megabytes instead of cylinders, which is a small win. +geometry of the disk. C can be used to +create more complex arrangements where the relative sizes are +expressed in megabytes instead of cylinders, which is a small win. C will choose the nearest cylinder to approximate the requested size. There's a lot of crazy stuff to do with IDE and virtio disks having different, incompatible CHS geometries, that you -probably don't want to know about. My advice: make a single partition -to cover the whole disk, then use LVM on top. +probably don't want to know about. -In future we aim to provide access to libparted. +My advice: make a single partition to cover the whole disk, then use +LVM on top. =head2 UPLOADING diff --git a/ocaml/t/guestfs_060_readdir.ml b/ocaml/t/guestfs_060_readdir.ml index 8035a09..f560700 100644 --- a/ocaml/t/guestfs_060_readdir.ml +++ b/ocaml/t/guestfs_060_readdir.ml @@ -28,7 +28,7 @@ let () = Guestfs.add_drive g "test.img"; Guestfs.launch g; - Guestfs.sfdisk g "/dev/sda" 0 0 0 [|","|]; + Guestfs.part_disk g "/dev/sda" "mbr"; Guestfs.mkfs g "ext2" "/dev/sda1"; Guestfs.mount g "/dev/sda1" "/"; Guestfs.mkdir g "/p"; diff --git a/perl/t/060-readdir.t b/perl/t/060-readdir.t index 898b44f..5ed5b7a 100644 --- a/perl/t/060-readdir.t +++ b/perl/t/060-readdir.t @@ -34,7 +34,7 @@ ok (1); $h->launch (); ok (1); -$h->sfdisk ("/dev/sda", 0, 0, 0, [","]); +$h->part_disk ("/dev/sda", "mbr"); ok (1); $h->mkfs ("ext2", "/dev/sda1"); ok (1); diff --git a/po/POTFILES.in b/po/POTFILES.in index d7d12f7..a125f2a 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -35,6 +35,7 @@ daemon/modprobe.c daemon/mount.c daemon/names.c daemon/ntfs.c +daemon/parted.c daemon/pingdaemon.c daemon/proto.c daemon/readdir.c diff --git a/recipes/tar2vm.sh b/recipes/tar2vm.sh index d71a5ef..713e9e3 100755 --- a/recipes/tar2vm.sh +++ b/recipes/tar2vm.sh @@ -3,7 +3,7 @@ guestfish <, with C megabytes."); ("mkfs", (RErr, [String "fstype"; Device "device"]), 42, [], [InitEmpty, Always, TestOutput ( - [["sfdiskM"; "/dev/sda"; ","]; + [["part_disk"; "/dev/sda"; "mbr"]; ["mkfs"; "ext2"; "/dev/sda1"]; ["mount"; "/dev/sda1"; "/"]; ["write_file"; "/new"; "new file contents"; "0"]; @@ -1489,12 +1489,12 @@ use C."); ("umount", (RErr, [String "pathordevice"]), 45, [FishAlias "unmount"], [InitEmpty, Always, TestOutputListOfDevices ( - [["sfdiskM"; "/dev/sda"; ","]; + [["part_disk"; "/dev/sda"; "mbr"]; ["mkfs"; "ext2"; "/dev/sda1"]; ["mount"; "/dev/sda1"; "/"]; ["mounts"]], ["/dev/sda1"]); InitEmpty, Always, TestOutputList ( - [["sfdiskM"; "/dev/sda"; ","]; + [["part_disk"; "/dev/sda"; "mbr"]; ["mkfs"; "ext2"; "/dev/sda1"]; ["mount"; "/dev/sda1"; "/"]; ["umount"; "/"]; @@ -2034,7 +2034,7 @@ to find out what you can do."); ("lvremove", (RErr, [Device "device"]), 77, [], [InitEmpty, Always, TestOutputList ( - [["sfdiskM"; "/dev/sda"; ","]; + [["part_disk"; "/dev/sda"; "mbr"]; ["pvcreate"; "/dev/sda1"]; ["vgcreate"; "VG"; "/dev/sda1"]; ["lvcreate"; "LV1"; "VG"; "50"]; @@ -2042,7 +2042,7 @@ to find out what you can do."); ["lvremove"; "/dev/VG/LV1"]; ["lvs"]], ["/dev/VG/LV2"]); InitEmpty, Always, TestOutputList ( - [["sfdiskM"; "/dev/sda"; ","]; + [["part_disk"; "/dev/sda"; "mbr"]; ["pvcreate"; "/dev/sda1"]; ["vgcreate"; "VG"; "/dev/sda1"]; ["lvcreate"; "LV1"; "VG"; "50"]; @@ -2050,7 +2050,7 @@ to find out what you can do."); ["lvremove"; "/dev/VG"]; ["lvs"]], []); InitEmpty, Always, TestOutputList ( - [["sfdiskM"; "/dev/sda"; ","]; + [["part_disk"; "/dev/sda"; "mbr"]; ["pvcreate"; "/dev/sda1"]; ["vgcreate"; "VG"; "/dev/sda1"]; ["lvcreate"; "LV1"; "VG"; "50"]; @@ -2067,7 +2067,7 @@ the VG name, C."); ("vgremove", (RErr, [String "vgname"]), 78, [], [InitEmpty, Always, TestOutputList ( - [["sfdiskM"; "/dev/sda"; ","]; + [["part_disk"; "/dev/sda"; "mbr"]; ["pvcreate"; "/dev/sda1"]; ["vgcreate"; "VG"; "/dev/sda1"]; ["lvcreate"; "LV1"; "VG"; "50"]; @@ -2075,7 +2075,7 @@ the VG name, C."); ["vgremove"; "VG"]; ["lvs"]], []); InitEmpty, Always, TestOutputList ( - [["sfdiskM"; "/dev/sda"; ","]; + [["part_disk"; "/dev/sda"; "mbr"]; ["pvcreate"; "/dev/sda1"]; ["vgcreate"; "VG"; "/dev/sda1"]; ["lvcreate"; "LV1"; "VG"; "50"]; @@ -2091,7 +2091,7 @@ group (if any)."); ("pvremove", (RErr, [Device "device"]), 79, [], [InitEmpty, Always, TestOutputListOfDevices ( - [["sfdiskM"; "/dev/sda"; ","]; + [["part_disk"; "/dev/sda"; "mbr"]; ["pvcreate"; "/dev/sda1"]; ["vgcreate"; "VG"; "/dev/sda1"]; ["lvcreate"; "LV1"; "VG"; "50"]; @@ -2100,7 +2100,7 @@ group (if any)."); ["pvremove"; "/dev/sda1"]; ["lvs"]], []); InitEmpty, Always, TestOutputListOfDevices ( - [["sfdiskM"; "/dev/sda"; ","]; + [["part_disk"; "/dev/sda"; "mbr"]; ["pvcreate"; "/dev/sda1"]; ["vgcreate"; "VG"; "/dev/sda1"]; ["lvcreate"; "LV1"; "VG"; "50"]; @@ -2109,7 +2109,7 @@ group (if any)."); ["pvremove"; "/dev/sda1"]; ["vgs"]], []); InitEmpty, Always, TestOutputListOfDevices ( - [["sfdiskM"; "/dev/sda"; ","]; + [["part_disk"; "/dev/sda"; "mbr"]; ["pvcreate"; "/dev/sda1"]; ["vgcreate"; "VG"; "/dev/sda1"]; ["lvcreate"; "LV1"; "VG"; "50"]; @@ -2386,7 +2386,7 @@ the human-readable, canonical hex dump of the file."); ("zerofree", (RErr, [Device "device"]), 97, [], [InitNone, Always, TestOutput ( - [["sfdiskM"; "/dev/sda"; ","]; + [["part_disk"; "/dev/sda"; "mbr"]; ["mkfs"; "ext3"; "/dev/sda1"]; ["mount"; "/dev/sda1"; "/"]; ["write_file"; "/new"; "test file"; "0"]; @@ -2484,7 +2484,7 @@ are activated or deactivated."); ("lvresize", (RErr, [Device "device"; Int "mbytes"]), 105, [], [InitNone, Always, TestOutput ( - [["sfdiskM"; "/dev/sda"; ","]; + [["part_disk"; "/dev/sda"; "mbr"]; ["pvcreate"; "/dev/sda1"]; ["vgcreate"; "VG"; "/dev/sda1"]; ["lvcreate"; "LV"; "VG"; "10"]; @@ -2577,11 +2577,11 @@ Sleep for C seconds."); ("ntfs_3g_probe", (RInt "status", [Bool "rw"; Device "device"]), 110, [], [InitNone, Always, TestOutputInt ( - [["sfdiskM"; "/dev/sda"; ","]; + [["part_disk"; "/dev/sda"; "mbr"]; ["mkfs"; "ntfs"; "/dev/sda1"]; ["ntfs_3g_probe"; "true"; "/dev/sda1"]], 0); InitNone, Always, TestOutputInt ( - [["sfdiskM"; "/dev/sda"; ","]; + [["part_disk"; "/dev/sda"; "mbr"]; ["mkfs"; "ext2"; "/dev/sda1"]; ["ntfs_3g_probe"; "true"; "/dev/sda1"]], 12)], "probe NTFS volume", @@ -2859,7 +2859,7 @@ the command C."); ("mkswap", (RErr, [Device "device"]), 130, [], [InitEmpty, Always, TestRun ( - [["sfdiskM"; "/dev/sda"; ","]; + [["part_disk"; "/dev/sda"; "mbr"]; ["mkswap"; "/dev/sda1"]])], "create a swap partition", "\ @@ -2867,7 +2867,7 @@ Create a swap partition on C."); ("mkswap_L", (RErr, [String "label"; Device "device"]), 131, [], [InitEmpty, Always, TestRun ( - [["sfdiskM"; "/dev/sda"; ","]; + [["part_disk"; "/dev/sda"; "mbr"]; ["mkswap_L"; "hello"; "/dev/sda1"]])], "create a swap partition with a label", "\ @@ -2880,7 +2880,7 @@ a limitation of the kernel or swap tools."); ("mkswap_U", (RErr, [String "uuid"; Device "device"]), 132, [], (let uuid = uuidgen () in [InitEmpty, Always, TestRun ( - [["sfdiskM"; "/dev/sda"; ","]; + [["part_disk"; "/dev/sda"; "mbr"]; ["mkswap_U"; uuid; "/dev/sda1"]])]), "create a swap partition with an explicit UUID", "\ @@ -3371,7 +3371,7 @@ This command disables the libguestfs appliance swap on file."); ("swapon_label", (RErr, [String "label"]), 174, [], [InitEmpty, Always, TestRun ( - [["sfdiskM"; "/dev/sdb"; ","]; + [["part_disk"; "/dev/sdb"; "mbr"]; ["mkswap_L"; "swapit"; "/dev/sdb1"]; ["swapon_label"; "swapit"]; ["swapoff_label"; "swapit"]; @@ -3536,7 +3536,7 @@ and C"); ("mkfs_b", (RErr, [String "fstype"; Int "blocksize"; Device "device"]), 187, [], [InitEmpty, Always, TestOutput ( - [["sfdiskM"; "/dev/sda"; ","]; + [["part_disk"; "/dev/sda"; "mbr"]; ["mkfs_b"; "ext2"; "4096"; "/dev/sda1"]; ["mount"; "/dev/sda1"; "/"]; ["write_file"; "/new"; "new file contents"; "0"]; @@ -3893,6 +3893,127 @@ bytes of the file, starting at C, from file C. This may read fewer bytes than requested. For further details see the L system call."); + ("part_init", (RErr, [Device "device"; String "parttype"]), 208, [], + [InitEmpty, Always, TestRun ( + [["part_init"; "/dev/sda"; "gpt"]])], + "create an empty partition table", + "\ +This creates an empty partition table on C of one of the +partition types listed below. Usually C should be +either C or C (for large disks). + +Initially there are no partitions. Following this, you should +call C for each partition required. + +Possible values for C are: + +=over 4 + +=item B | B + +Intel EFI / GPT partition table. + +This is recommended for >= 2 TB partitions that will be accessed +from Linux and Intel-based Mac OS X. It also has limited backwards +compatibility with the C format. + +=item B | B + +The standard PC \"Master Boot Record\" (MBR) format used +by MS-DOS and Windows. This partition type will B work +for device sizes up to 2 TB. For large disks we recommend +using C. + +=back + +Other partition table types that may work but are not +supported include: + +=over 4 + +=item B + +AIX disk labels. + +=item B | B + +Amiga \"Rigid Disk Block\" format. + +=item B + +BSD disk labels. + +=item B + +DASD, used on IBM mainframes. + +=item B + +MIPS/SGI volumes. + +=item B + +Old Mac partition format. Modern Macs use C. + +=item B + +NEC PC-98 format, common in Japan apparently. + +=item B + +Sun disk labels. + +=back"); + + ("part_add", (RErr, [Device "device"; String "prlogex"; Int64 "startsect"; Int64 "endsect"]), 209, [], + [InitEmpty, Always, TestRun ( + [["part_init"; "/dev/sda"; "mbr"]; + ["part_add"; "/dev/sda"; "primary"; "1"; "-1"]])], + "add a partition to the device", + "\ +This command adds a partition to C. If there is no partition +table on the device, call C first. + +The C parameter is the type of partition. Normally you +should pass C

or C here, but MBR partition tables also +support C (or C) and C (or C) partition +types. + +C and C are the start and end of the partition +in I. C may be negative, which means it counts +backwards from the end of the disk (C<-1> is the last sector). + +Creating a partition which covers the whole disk is not so easy. +Use C to do that."); + + ("part_set_bootable", (RErr, [Device "device"; Int "partnum"; Bool "bootable"]), 210, [], + [InitEmpty, Always, TestRun ( + [["part_disk"; "/dev/sda"; "mbr"]; + ["part_set_bootable"; "/dev/sda"; "1"; "true"]])], + "make a partition bootable", + "\ +This sets the bootable flag on partition numbered C on +device C. Note that partitions are numbered from 1. + +The bootable flag is used by some PC BIOSes to determine which +partition to boot from. It is by no means universally recognized, +and in any case if your operating system installed a boot +sector on the device itself, then that takes precedence."); + + ("part_disk", (RErr, [Device "device"; String "parttype"]), 211, [], + [InitEmpty, Always, TestRun ( + [["part_disk"; "/dev/sda"; "mbr"]]); + InitEmpty, Always, TestRun ( + [["part_disk"; "/dev/sda"; "gpt"]])], + "partition whole disk with a single primary partition", + "\ +This command is simply a combination of C +followed by C to create a single primary partition +covering the whole disk. + +C is the partition table type, usually C or C, +but other possible values are described in C."); + ] let all_functions = non_daemon_functions @ daemon_functions @@ -5951,14 +6072,14 @@ and generate_one_test_body name i test_name init test = [["blockdev_setrw"; "/dev/sda"]; ["umount_all"]; ["lvm_remove_all"]; - ["sfdiskM"; "/dev/sda"; ","]] + ["part_disk"; "/dev/sda"; "mbr"]] | InitBasicFS -> pr " /* InitBasicFS for %s: create ext2 on /dev/sda1 */\n" test_name; List.iter (generate_test_command_call test_name) [["blockdev_setrw"; "/dev/sda"]; ["umount_all"]; ["lvm_remove_all"]; - ["sfdiskM"; "/dev/sda"; ","]; + ["part_disk"; "/dev/sda"; "mbr"]; ["mkfs"; "ext2"; "/dev/sda1"]; ["mount"; "/dev/sda1"; "/"]] | InitBasicFSonLVM -> @@ -5968,7 +6089,7 @@ and generate_one_test_body name i test_name init test = [["blockdev_setrw"; "/dev/sda"]; ["umount_all"]; ["lvm_remove_all"]; - ["sfdiskM"; "/dev/sda"; ","]; + ["part_disk"; "/dev/sda"; "mbr"]; ["pvcreate"; "/dev/sda1"]; ["vgcreate"; "VG"; "/dev/sda1"]; ["lvcreate"; "LV"; "VG"; "8"]; -- 1.6.5.rc2