Richard W.M. Jones
2014-Apr-16 11:09 UTC
[Libguestfs] [PATCH] disk-create: Fix this API so it works correctly with block devices (RHBZ#1088262).
When you call guestfs_disk_create on a block device with format=raw then it will try to discard the blocks on the device. --- configure.ac | 1 + daemon/blkdiscard.c | 3 +++ generator/actions.ml | 4 ++++ src/create.c | 46 +++++++++++++++++++++++++++++++++++++++------- 4 files changed, 47 insertions(+), 7 deletions(-) diff --git a/configure.ac b/configure.ac index 887feea..014332e 100644 --- a/configure.ac +++ b/configure.ac @@ -291,6 +291,7 @@ AC_CHECK_HEADERS([\ byteswap.h \ endian.h \ errno.h \ + linux/fs.h \ linux/raid/md_u.h \ printf.h \ sys/inotify.h \ diff --git a/daemon/blkdiscard.c b/daemon/blkdiscard.c index 7b63b99..612c97f 100644 --- a/daemon/blkdiscard.c +++ b/daemon/blkdiscard.c @@ -25,7 +25,10 @@ #include <fcntl.h> #include <unistd.h> #include <sys/ioctl.h> + +#ifdef HAVE_LINUX_FS_H #include <linux/fs.h> +#endif #include "daemon.h" #include "actions.h" diff --git a/generator/actions.ml b/generator/actions.ml index 8825493..3f30d8c 100644 --- a/generator/actions.ml +++ b/generator/actions.ml @@ -3084,6 +3084,10 @@ size of the backing file, which is discovered automatically. You are encouraged to also pass C<backingformat> to describe the format of C<backingfile>. +If C<filename> refers to a block device, then the device is +formatted. The C<size> is ignored since block devices have an +intrinsic size. + The other optional parameters are: =over 4 diff --git a/src/create.c b/src/create.c index 40a5cac..0cfe6be 100644 --- a/src/create.c +++ b/src/create.c @@ -27,8 +27,13 @@ #include <sys/types.h> #include <sys/stat.h> #include <sys/wait.h> +#include <sys/ioctl.h> #include <errno.h> +#ifdef HAVE_LINUX_FS_H +#include <linux/fs.h> +#endif + #include "guestfs.h" #include "guestfs-internal.h" #include "guestfs-internal-actions.h" @@ -90,6 +95,36 @@ guestfs__disk_create (guestfs_h *g, const char *filename, } static int +disk_create_raw_block (guestfs_h *g, const char *filename) +{ + int fd; + + fd = open (filename, O_WRONLY|O_NOCTTY|O_CLOEXEC, 0666); + if (fd == -1) { + perrorf (g, _("cannot open block device: %s"), filename); + return -1; + } + + /* Just discard blocks, if possible. However don't try too hard. */ +#if defined(BLKGETSIZE64) && defined(BLKDISCARD) + uint64_t size; + uint64_t range[2]; + + if (ioctl (fd, BLKGETSIZE64, &size) == 0) { + range[0] = 0; + range[1] = size; + if (ioctl (fd, BLKDISCARD, range) == 0) + debug (g, "disk_create: %s: BLKDISCARD failed on this device: %m", + filename); + } +#endif + + close (fd); + + return 0; +} + +static int disk_create_raw (guestfs_h *g, const char *filename, int64_t size, const struct guestfs_disk_create_argv *optargs) { @@ -123,18 +158,15 @@ disk_create_raw (guestfs_h *g, const char *filename, int64_t size, return -1; } - /* This version refuses to overwrite block devices or char devices. - * XXX It would be possible to make it work with block devices. - */ if (stat (filename, &statbuf) == 0) { - if (S_ISBLK (statbuf.st_mode)) { - error (g, _("refusing to overwrite block device '%s'"), filename); - return -1; - } + /* Refuse to overwrite char devices. */ if (S_ISCHR (statbuf.st_mode)) { error (g, _("refusing to overwrite char device '%s'"), filename); return -1; } + /* Block devices have to be handled specially. */ + if (S_ISBLK (statbuf.st_mode)) + return disk_create_raw_block (g, filename); } fd = open (filename, O_WRONLY|O_CREAT|O_NOCTTY|O_TRUNC|O_CLOEXEC, 0666); -- 1.8.5.3