From eb38edce88ac2f511ed9593a1c25c58a77158219 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 4 Oct 2017 17:36:58 +0200 Subject: [PATCH] machine-image: add partial discovery of block devices as images This adds some basic discovery of block device images for nspawn and friends. Note that this doesn't add searching for block devices using udev, but instead expects users to symlink relevant block devices into /var/lib/machines. Discovery is hence done exactly like for dir/subvol/raw file images, except that what is found may be a (symlink to) a block device. For now, we do not support cloning these images, but removal, renaming and read-only flags are supported to the point where that makes sense. Fixe: #6990 --- src/machine/image-dbus.c | 1 + src/nspawn/nspawn.c | 2 +- src/shared/machine-image.c | 118 ++++++++++++++++++++++++++++++++++++++++++--- src/shared/machine-image.h | 1 + 4 files changed, 114 insertions(+), 8 deletions(-) diff --git a/src/machine/image-dbus.c b/src/machine/image-dbus.c index 18e0e34..e534470 100644 --- a/src/machine/image-dbus.c +++ b/src/machine/image-dbus.c @@ -431,6 +431,7 @@ int bus_image_method_get_os_release( break; case IMAGE_RAW: + case IMAGE_BLOCK: r = raw_image_get_os_release(image, &v, error); break; diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 4e3803b..d1e3855 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -2089,7 +2089,7 @@ static int determine_names(void) { return -ENOENT; } - if (i->type == IMAGE_RAW) + if (IN_SET(i->type, IMAGE_RAW, IMAGE_BLOCK)) r = free_and_strdup(&arg_image, i->path); else r = free_and_strdup(&arg_directory, i->path); diff --git a/src/shared/machine-image.c b/src/shared/machine-image.c index 859e5ff..a8af1b7 100644 --- a/src/shared/machine-image.c +++ b/src/shared/machine-image.c @@ -171,9 +171,8 @@ static int image_make( assert(filename); - /* We explicitly *do* follow symlinks here, since we want to - * allow symlinking trees into /var/lib/machines/, and treat - * them normally. */ + /* We explicitly *do* follow symlinks here, since we want to allow symlinking trees, raw files and block + * devices into /var/lib/machines/, and treat them normally. */ if (fstatat(dfd, filename, &st, 0) < 0) return -errno; @@ -286,6 +285,58 @@ static int image_make( (*ret)->limit = (*ret)->limit_exclusive = st.st_size; return 1; + + } else if (S_ISBLK(st.st_mode)) { + _cleanup_close_ int block_fd = -1; + uint64_t size = UINT64_MAX; + + /* A block device */ + + if (!ret) + return 1; + + if (!pretty) + pretty = filename; + + block_fd = openat(dfd, filename, O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_NOCTTY); + if (block_fd < 0) + log_debug_errno(errno, "Failed to open block device %s/%s, ignoring: %m", path, filename); + else { + if (fstat(block_fd, &st) < 0) + return -errno; + if (!S_ISBLK(st.st_mode)) /* Verify that what we opened is actually what we think it is */ + return -ENOTTY; + + if (!read_only) { + int state = 0; + + if (ioctl(block_fd, BLKROGET, &state) < 0) + log_debug_errno(errno, "Failed to issue BLKROGET on device %s/%s, ignoring: %m", path, filename); + else if (state) + read_only = true; + } + + if (ioctl(block_fd, BLKGETSIZE64, &size) < 0) + log_debug_errno(errno, "Failed to issue BLKFLSBUF on device %s/%s, ignoring: %m", path, filename); + + block_fd = safe_close(block_fd); + } + + r = image_new(IMAGE_BLOCK, + pretty, + path, + filename, + !(st.st_mode & 0222) || read_only, + 0, + 0, + ret); + if (r < 0) + return r; + + if (size != 0 && size != UINT64_MAX) + (*ret)->usage = (*ret)->usage_exclusive = (*ret)->limit = (*ret)->limit_exclusive = size; + + return 1; } return 0; @@ -446,6 +497,17 @@ int image_remove(Image *i) { break; + case IMAGE_BLOCK: + + /* If this is inside of /dev, then it's a real block device, hence let's not touch the device node + * itself (but let's remove the stuff stored alongside it). If it's anywhere else, let's try to unlink + * the thing (it's most likely a symlink after all). */ + + if (path_startswith(i->path, "/dev")) + break; + + /* fallthrough */ + case IMAGE_RAW: if (unlink(i->path) < 0) return -errno; @@ -536,6 +598,15 @@ int image_rename(Image *i, const char *new_name) { new_path = file_in_same_dir(i->path, new_name); break; + case IMAGE_BLOCK: + + /* Refuse renaming raw block devices in /dev, the names are picked by udev after all. */ + if (path_startswith(i->path, "/dev")) + return -EROFS; + + new_path = file_in_same_dir(i->path, new_name); + break; + case IMAGE_RAW: { const char *fn; @@ -659,6 +730,7 @@ int image_clone(Image *i, const char *new_name, bool read_only) { r = copy_file_atomic(i->path, new_path, read_only ? 0444 : 0644, FS_NOCOW_FL, COPY_REFLINK); break; + case IMAGE_BLOCK: default: return -EOPNOTSUPP; } @@ -737,6 +809,26 @@ int image_read_only(Image *i, bool b) { break; } + case IMAGE_BLOCK: { + _cleanup_close_ int fd = -1; + struct stat st; + int state = b; + + fd = open(i->path, O_CLOEXEC|O_RDONLY|O_NONBLOCK|O_NOCTTY); + if (fd < 0) + return -errno; + + if (fstat(fd, &st) < 0) + return -errno; + if (!S_ISBLK(st.st_mode)) + return -ENOTTY; + + if (ioctl(fd, BLKROSET, &state) < 0) + return -errno; + + break; + } + default: return -EOPNOTSUPP; } @@ -772,13 +864,24 @@ int image_path_lock(const char *path, int operation, LockFile *global, LockFile return -EBUSY; if (stat(path, &st) >= 0) { - if (asprintf(&p, "/run/systemd/nspawn/locks/inode-%lu:%lu", (unsigned long) st.st_dev, (unsigned long) st.st_ino) < 0) + if (S_ISBLK(st.st_mode)) + r = asprintf(&p, "/run/systemd/nspawn/locks/block-%u:%u", major(st.st_rdev), minor(st.st_rdev)); + else if (S_ISDIR(st.st_mode) || S_ISREG(st.st_mode)) + r = asprintf(&p, "/run/systemd/nspawn/locks/inode-%lu:%lu", (unsigned long) st.st_dev, (unsigned long) st.st_ino); + else + return -ENOTTY; + + if (r < 0) return -ENOMEM; } - r = make_lock_file_for(path, operation, &t); - if (r < 0) - return r; + /* For block devices we don't need the "local" lock, as the major/minor lock above should be sufficient, since + * block devices are device local anyway. */ + if (!path_startswith(path, "/dev")) { + r = make_lock_file_for(path, operation, &t); + if (r < 0) + return r; + } if (p) { mkdir_p("/run/systemd/nspawn/locks", 0700); @@ -860,6 +963,7 @@ static const char* const image_type_table[_IMAGE_TYPE_MAX] = { [IMAGE_DIRECTORY] = "directory", [IMAGE_SUBVOLUME] = "subvolume", [IMAGE_RAW] = "raw", + [IMAGE_BLOCK] = "block", }; DEFINE_STRING_TABLE_LOOKUP(image_type, ImageType); diff --git a/src/shared/machine-image.h b/src/shared/machine-image.h index 7410168..50d89e4 100644 --- a/src/shared/machine-image.h +++ b/src/shared/machine-image.h @@ -33,6 +33,7 @@ typedef enum ImageType { IMAGE_DIRECTORY, IMAGE_SUBVOLUME, IMAGE_RAW, + IMAGE_BLOCK, _IMAGE_TYPE_MAX, _IMAGE_TYPE_INVALID = -1 } ImageType; -- 2.7.4