Esempio n. 1
0
static int image_make(
                const char *pretty,
                int dfd,
                const char *path,
                const char *filename,
                Image **ret) {

        struct stat st;
        bool read_only;
        int r;

        assert(filename);

        /* We explicitly *do* follow symlinks here, since we want to
         * allow symlinking trees into /var/lib/machines/, and treat
         * them normally. */

        if (fstatat(dfd, filename, &st, 0) < 0)
                return -errno;

        read_only =
                (path && path_startswith(path, "/usr")) ||
                (faccessat(dfd, filename, W_OK, AT_EACCESS) < 0 && errno == EROFS);

        if (S_ISDIR(st.st_mode)) {
                _cleanup_close_ int fd = -1;
                unsigned file_attr = 0;

                if (!ret)
                        return 1;

                if (!pretty)
                        pretty = filename;

                fd = openat(dfd, filename, O_CLOEXEC|O_NOCTTY|O_DIRECTORY);
                if (fd < 0)
                        return -errno;

                /* btrfs subvolumes have inode 256 */
                if (st.st_ino == 256) {

                        r = btrfs_is_filesystem(fd);
                        if (r < 0)
                                return r;
                        if (r) {
                                BtrfsSubvolInfo info;

                                /* It's a btrfs subvolume */

                                r = btrfs_subvol_get_info_fd(fd, 0, &info);
                                if (r < 0)
                                        return r;

                                r = image_new(IMAGE_SUBVOLUME,
                                              pretty,
                                              path,
                                              filename,
                                              info.read_only || read_only,
                                              info.otime,
                                              0,
                                              ret);
                                if (r < 0)
                                        return r;

                                if (btrfs_quota_scan_ongoing(fd) == 0) {
                                        BtrfsQuotaInfo quota;

                                        r = btrfs_subvol_get_subtree_quota_fd(fd, 0, &quota);
                                        if (r >= 0) {
                                                (*ret)->usage = quota.referenced;
                                                (*ret)->usage_exclusive = quota.exclusive;

                                                (*ret)->limit = quota.referenced_max;
                                                (*ret)->limit_exclusive = quota.exclusive_max;
                                        }
                                }

                                return 1;
                        }
                }

                /* If the IMMUTABLE bit is set, we consider the
                 * directory read-only. Since the ioctl is not
                 * supported everywhere we ignore failures. */
                (void) read_attr_fd(fd, &file_attr);

                /* It's just a normal directory. */
                r = image_new(IMAGE_DIRECTORY,
                              pretty,
                              path,
                              filename,
                              read_only || (file_attr & FS_IMMUTABLE_FL),
                              0,
                              0,
                              ret);
                if (r < 0)
                        return r;

                return 1;

        } else if (S_ISREG(st.st_mode) && endswith(filename, ".raw")) {
                usec_t crtime = 0;

                /* It's a RAW disk image */

                if (!ret)
                        return 1;

                fd_getcrtime_at(dfd, filename, &crtime, 0);

                if (!pretty)
                        pretty = strndupa(filename, strlen(filename) - 4);

                r = image_new(IMAGE_RAW,
                              pretty,
                              path,
                              filename,
                              !(st.st_mode & 0222) || read_only,
                              crtime,
                              timespec_load(&st.st_mtim),
                              ret);
                if (r < 0)
                        return r;

                (*ret)->usage = (*ret)->usage_exclusive = st.st_blocks * 512;
                (*ret)->limit = (*ret)->limit_exclusive = st.st_size;

                return 1;
        }

        return 0;
}
Esempio n. 2
0
static int image_make(
                const char *pretty,
                int dfd,
                const char *path,
                const char *filename,
                const struct stat *st,
                Image **ret) {

        _cleanup_free_ char *pretty_buffer = NULL;
        struct stat stbuf;
        bool read_only;
        int r;

        assert(dfd >= 0 || dfd == AT_FDCWD);
        assert(filename);

        /* 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.
         *
         * This function returns -ENOENT if we can't find the image after all, and -EMEDIUMTYPE if it's not a file we
         * recognize. */

        if (!st) {
                if (fstatat(dfd, filename, &stbuf, 0) < 0)
                        return -errno;

                st = &stbuf;
        }

        read_only =
                (path && path_startswith(path, "/usr")) ||
                (faccessat(dfd, filename, W_OK, AT_EACCESS) < 0 && errno == EROFS);

        if (S_ISDIR(st->st_mode)) {
                _cleanup_close_ int fd = -1;
                unsigned file_attr = 0;

                if (!ret)
                        return 0;

                if (!pretty) {
                        r = extract_pretty(filename, NULL, &pretty_buffer);
                        if (r < 0)
                                return r;

                        pretty = pretty_buffer;
                }

                fd = openat(dfd, filename, O_CLOEXEC|O_NOCTTY|O_DIRECTORY);
                if (fd < 0)
                        return -errno;

                /* btrfs subvolumes have inode 256 */
                if (st->st_ino == 256) {

                        r = btrfs_is_filesystem(fd);
                        if (r < 0)
                                return r;
                        if (r) {
                                BtrfsSubvolInfo info;

                                /* It's a btrfs subvolume */

                                r = btrfs_subvol_get_info_fd(fd, 0, &info);
                                if (r < 0)
                                        return r;

                                r = image_new(IMAGE_SUBVOLUME,
                                              pretty,
                                              path,
                                              filename,
                                              info.read_only || read_only,
                                              info.otime,
                                              0,
                                              ret);
                                if (r < 0)
                                        return r;

                                if (btrfs_quota_scan_ongoing(fd) == 0) {
                                        BtrfsQuotaInfo quota;

                                        r = btrfs_subvol_get_subtree_quota_fd(fd, 0, &quota);
                                        if (r >= 0) {
                                                (*ret)->usage = quota.referenced;
                                                (*ret)->usage_exclusive = quota.exclusive;

                                                (*ret)->limit = quota.referenced_max;
                                                (*ret)->limit_exclusive = quota.exclusive_max;
                                        }
                                }

                                return 0;
                        }
                }

                /* If the IMMUTABLE bit is set, we consider the
                 * directory read-only. Since the ioctl is not
                 * supported everywhere we ignore failures. */
                (void) read_attr_fd(fd, &file_attr);

                /* It's just a normal directory. */
                r = image_new(IMAGE_DIRECTORY,
                              pretty,
                              path,
                              filename,
                              read_only || (file_attr & FS_IMMUTABLE_FL),
                              0,
                              0,
                              ret);
                if (r < 0)
                        return r;

                return 0;

        } else if (S_ISREG(st->st_mode) && endswith(filename, ".raw")) {
                usec_t crtime = 0;

                /* It's a RAW disk image */

                if (!ret)
                        return 0;

                (void) fd_getcrtime_at(dfd, filename, &crtime, 0);

                if (!pretty) {
                        r = extract_pretty(filename, ".raw", &pretty_buffer);
                        if (r < 0)
                                return r;

                        pretty = pretty_buffer;
                }

                r = image_new(IMAGE_RAW,
                              pretty,
                              path,
                              filename,
                              !(st->st_mode & 0222) || read_only,
                              crtime,
                              timespec_load(&st->st_mtim),
                              ret);
                if (r < 0)
                        return r;

                (*ret)->usage = (*ret)->usage_exclusive = st->st_blocks * 512;
                (*ret)->limit = (*ret)->limit_exclusive = st->st_size;

                return 0;

        } else if (S_ISBLK(st->st_mode)) {
                _cleanup_close_ int block_fd = -1;
                uint64_t size = UINT64_MAX;

                /* A block device */

                if (!ret)
                        return 0;

                if (!pretty) {
                        r = extract_pretty(filename, NULL, &pretty_buffer);
                        if (r < 0)
                                return r;

                        pretty = pretty_buffer;
                }

                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 {
                        /* Refresh stat data after opening the node */
                        if (fstat(block_fd, &stbuf) < 0)
                                return -errno;
                        st = &stbuf;

                        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 BLKGETSIZE64 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 0;
        }

        return -EMEDIUMTYPE;
}
Esempio n. 3
0
int tar_export_start(TarExport *e, const char *path, int fd, ImportCompressType compress) {
    _cleanup_close_ int sfd = -1;
    int r;

    assert(e);
    assert(path);
    assert(fd >= 0);
    assert(compress < _IMPORT_COMPRESS_TYPE_MAX);
    assert(compress != IMPORT_COMPRESS_UNKNOWN);

    if (e->output_fd >= 0)
        return -EBUSY;

    sfd = open(path, O_DIRECTORY|O_RDONLY|O_NOCTTY|O_CLOEXEC);
    if (sfd < 0)
        return -errno;

    if (fstat(sfd, &e->st) < 0)
        return -errno;

    r = fd_nonblock(fd, true);
    if (r < 0)
        return r;

    r = free_and_strdup(&e->path, path);
    if (r < 0)
        return r;

    e->quota_referenced = (uint64_t) -1;

    if (e->st.st_ino == 256) { /* might be a btrfs subvolume? */
        BtrfsQuotaInfo q;

        r = btrfs_subvol_get_subtree_quota_fd(sfd, 0, &q);
        if (r >= 0)
            e->quota_referenced = q.referenced;

        e->temp_path = mfree(e->temp_path);

        r = tempfn_random(path, NULL, &e->temp_path);
        if (r < 0)
            return r;

        /* Let's try to make a snapshot, if we can, so that the export is atomic */
        r = btrfs_subvol_snapshot_fd(sfd, e->temp_path, BTRFS_SNAPSHOT_READ_ONLY|BTRFS_SNAPSHOT_RECURSIVE);
        if (r < 0) {
            log_debug_errno(r, "Couldn't create snapshot %s of %s, not exporting atomically: %m", e->temp_path, path);
            e->temp_path = mfree(e->temp_path);
        }
    }

    r = import_compress_init(&e->compress, compress);
    if (r < 0)
        return r;

    r = sd_event_add_io(e->event, &e->output_event_source, fd, EPOLLOUT, tar_export_on_output, e);
    if (r == -EPERM) {
        r = sd_event_add_defer(e->event, &e->output_event_source, tar_export_on_defer, e);
        if (r < 0)
            return r;

        r = sd_event_source_set_enabled(e->output_event_source, SD_EVENT_ON);
    }
    if (r < 0)
        return r;

    e->tar_fd = import_fork_tar_c(e->temp_path ?: e->path, &e->tar_pid);
    if (e->tar_fd < 0) {
        e->output_event_source = sd_event_source_unref(e->output_event_source);
        return e->tar_fd;
    }

    e->output_fd = fd;
    return r;
}