]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
machine-image: add partial discovery of block devices as images
authorLennart Poettering <lennart@poettering.net>
Wed, 4 Oct 2017 15:36:58 +0000 (17:36 +0200)
committerLennart Poettering <lennart@poettering.net>
Thu, 26 Oct 2017 15:54:56 +0000 (17:54 +0200)
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
src/nspawn/nspawn.c
src/shared/machine-image.c
src/shared/machine-image.h

index 18e0e348965a3ee02b4ce1bded6a15a9b59a5925..e534470feb17b469aacab8f0aeb47690c967fcf4 100644 (file)
@@ -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;
 
index 4e3803be822168984b6523167e424e1919c1dd0f..d1e38556c84d15817546688e993162e451c1dc7b 100644 (file)
@@ -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);
index 859e5ffc1a3b43b12135d588a5ed049b4acc510f..a8af1b73eb528f738deda9087f99730efba78b36 100644 (file)
@@ -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);
index 7410168c4feeb7106f97df81885d4d545e36db51..50d89e4392bb6cd599fe7458c2be4331ac420748 100644 (file)
@@ -33,6 +33,7 @@ typedef enum ImageType {
         IMAGE_DIRECTORY,
         IMAGE_SUBVOLUME,
         IMAGE_RAW,
+        IMAGE_BLOCK,
         _IMAGE_TYPE_MAX,
         _IMAGE_TYPE_INVALID = -1
 } ImageType;