Commit 066ae4f8 authored by Peter Maydell's avatar Peter Maydell
Browse files

Merge remote-tracking branch 'remotes/gkurz/tags/for-upstream' into staging



Various bugfixes and code cleanups. Most notably, it fixes metadata handling in
mapped-file security mode (especially for the virtfs root).

# gpg: Signature made Tue 30 May 2017 14:36:22 BST
# gpg:                using DSA key 0x02FC3AEB0101DBC2
# gpg: Good signature from "Greg Kurz <groug@kaod.org>"
# gpg:                 aka "Greg Kurz <groug@free.fr>"
# gpg:                 aka "Greg Kurz <gkurz@linux.vnet.ibm.com>"
# gpg:                 aka "Gregory Kurz (Groug) <groug@free.fr>"
# gpg:                 aka "[jpeg image of size 3330]"
# gpg: WARNING: This key is not certified with a trusted signature!
# gpg:          There is no indication that the signature belongs to the owner.
# Primary key fingerprint: 2BD4 3B44 535E C0A7 9894  DBA2 02FC 3AEB 0101 DBC2

* remotes/gkurz/tags/for-upstream:
  9pfs: local: metadata file for the VirtFS root
  9pfs: local: simplify file opening
  9pfs: local: resolve special directories in paths
  9pfs: check return value of v9fs_co_name_to_path()
  util: drop old utimensat() compat code
  9pfs: assume utimensat() and futimens() are present
  fsdev: fix virtfs-proxy-helper cwd
  9pfs: local: fix unlink of alien files in mapped-file mode
  9pfs: drop pdu_push_and_notify()
  fsdev: don't allow unknown format in marshal/unmarshal
  virtio-9p/xen-9p: move 9p specific bits to core 9p code

Signed-off-by: default avatarPeter Maydell <peter.maydell@linaro.org>
parents 70f31414 81ffbf5a
Loading
Loading
Loading
Loading
+0 −22
Original line number Diff line number Diff line
@@ -3629,25 +3629,6 @@ if compile_prog "" "" ; then
  inotify1=yes
fi

# check if utimensat and futimens are supported
utimens=no
cat > $TMPC << EOF
#define _ATFILE_SOURCE
#include <stddef.h>
#include <fcntl.h>
#include <sys/stat.h>

int main(void)
{
    utimensat(AT_FDCWD, "foo", NULL, 0);
    futimens(0, NULL);
    return 0;
}
EOF
if compile_prog "" "" ; then
  utimens=yes
fi

# check if pipe2 is there
pipe2=no
cat > $TMPC << EOF
@@ -5434,9 +5415,6 @@ fi
if test "$curses" = "yes" ; then
  echo "CONFIG_CURSES=y" >> $config_host_mak
fi
if test "$utimens" = "yes" ; then
  echo "CONFIG_UTIMENSAT=y" >> $config_host_mak
fi
if test "$pipe2" = "yes" ; then
  echo "CONFIG_PIPE2=y" >> $config_host_mak
fi
+2 −2
Original line number Diff line number Diff line
@@ -168,7 +168,7 @@ ssize_t v9fs_iov_vunmarshal(struct iovec *out_sg, int out_num, size_t offset,
            break;
        }
        default:
            break;
            g_assert_not_reached();
        }
        if (copied < 0) {
            return copied;
@@ -281,7 +281,7 @@ ssize_t v9fs_iov_vmarshal(struct iovec *in_sg, int in_num, size_t offset,
            break;
        }
        default:
            break;
            g_assert_not_reached();
        }
        if (copied < 0) {
            return copied;
+6 −5
Original line number Diff line number Diff line
@@ -945,7 +945,8 @@ static int process_requests(int sock)
                                     &spec[0].tv_sec, &spec[0].tv_nsec,
                                     &spec[1].tv_sec, &spec[1].tv_nsec);
            if (retval > 0) {
                retval = qemu_utimens(path.data, spec);
                retval = utimensat(AT_FDCWD, path.data, spec,
                                   AT_SYMLINK_NOFOLLOW);
                if (retval < 0) {
                    retval = -errno;
                }
@@ -1129,14 +1130,14 @@ int main(int argc, char **argv)
        }
    }

    if (chdir("/") < 0) {
        do_perror("chdir");
        goto error;
    }
    if (chroot(rpath) < 0) {
        do_perror("chroot");
        goto error;
    }
    if (chdir("/") < 0) {
        do_perror("chdir");
        goto error;
    }

    get_version = false;
#ifdef FS_IOC_GETVERSION
+0 −5
Original line number Diff line number Diff line
@@ -378,7 +378,6 @@ static int handle_utimensat(FsContext *ctx, V9fsPath *fs_path,
                            const struct timespec *buf)
{
    int ret;
#ifdef CONFIG_UTIMENSAT
    int fd;
    struct handle_data *data = (struct handle_data *)ctx->private;

@@ -388,10 +387,6 @@ static int handle_utimensat(FsContext *ctx, V9fsPath *fs_path,
    }
    ret = futimens(fd, buf);
    close(fd);
#else
    ret = -1;
    errno = ENOSYS;
#endif
    return ret;
}

+128 −58
Original line number Diff line number Diff line
@@ -53,13 +53,37 @@ int local_open_nofollow(FsContext *fs_ctx, const char *path, int flags,
                        mode_t mode)
{
    LocalData *data = fs_ctx->private;

    /* All paths are relative to the path data->mountfd points to */
    while (*path == '/') {
        path++;
    int fd = data->mountfd;

    while (*path && fd != -1) {
        const char *c;
        int next_fd;
        char *head;

        /* Only relative paths without consecutive slashes */
        assert(*path != '/');

        head = g_strdup(path);
        c = strchrnul(path, '/');
        if (*c) {
            /* Intermediate path element */
            head[c - path] = 0;
            path = c + 1;
            next_fd = openat_dir(fd, head);
        } else {
            /* Rightmost path element */
            next_fd = openat_file(fd, head, flags, mode);
            path = c;
        }
        g_free(head);
        if (fd != data->mountfd) {
            close_preserve_errno(fd);
        }
        fd = next_fd;
    }

    return relative_openat_nofollow(data->mountfd, path, flags, mode);
    assert(fd != data->mountfd);
    return fd;
}

int local_opendir_nofollow(FsContext *fs_ctx, const char *path)
@@ -83,6 +107,7 @@ static void unlinkat_preserve_errno(int dirfd, const char *path, int flags)
}

#define VIRTFS_META_DIR ".virtfs_metadata"
#define VIRTFS_META_ROOT_FILE VIRTFS_META_DIR "_root"

static FILE *local_fopenat(int dirfd, const char *name, const char *mode)
{
@@ -119,6 +144,7 @@ static void local_mapped_file_attr(int dirfd, const char *name,
    char buf[ATTR_MAX];
    int map_dirfd;

    if (strcmp(name, ".")) {
        map_dirfd = openat_dir(dirfd, VIRTFS_META_DIR);
        if (map_dirfd == -1) {
            return;
@@ -126,6 +152,9 @@ static void local_mapped_file_attr(int dirfd, const char *name,

        fp = local_fopenat(map_dirfd, name, "r");
        close_preserve_errno(map_dirfd);
    } else {
        fp = local_fopenat(dirfd, VIRTFS_META_ROOT_FILE, "r");
    }
    if (!fp) {
        return;
    }
@@ -203,8 +232,19 @@ static int local_set_mapped_file_attrat(int dirfd, const char *name,
    int ret;
    char buf[ATTR_MAX];
    int uid = -1, gid = -1, mode = -1, rdev = -1;
    int map_dirfd;
    int map_dirfd = -1, map_fd;
    bool is_root = !strcmp(name, ".");

    if (is_root) {
        fp = local_fopenat(dirfd, VIRTFS_META_ROOT_FILE, "r");
        if (!fp) {
            if (errno == ENOENT) {
                goto update_map_file;
            } else {
                return -1;
            }
        }
    } else {
        ret = mkdirat(dirfd, VIRTFS_META_DIR, 0700);
        if (ret < 0 && errno != EEXIST) {
            return -1;
@@ -224,6 +264,7 @@ static int local_set_mapped_file_attrat(int dirfd, const char *name,
                return -1;
            }
        }
    }
    memset(buf, 0, ATTR_MAX);
    while (fgets(buf, ATTR_MAX, fp)) {
        if (!strncmp(buf, "virtfs.uid", 10)) {
@@ -240,12 +281,26 @@ static int local_set_mapped_file_attrat(int dirfd, const char *name,
    fclose(fp);

update_map_file:
    if (is_root) {
        fp = local_fopenat(dirfd, VIRTFS_META_ROOT_FILE, "w");
    } else {
        fp = local_fopenat(map_dirfd, name, "w");
        /* We can't go this far with map_dirfd not being a valid file descriptor
         * but some versions of gcc aren't smart enough to see it.
         */
        if (map_dirfd != -1) {
            close_preserve_errno(map_dirfd);
        }
    }
    if (!fp) {
        return -1;
    }

    map_fd = fileno(fp);
    assert(map_fd != -1);
    ret = fchmod(map_fd, 0600);
    assert(ret == 0);

    if (credp->fc_uid != -1) {
        uid = credp->fc_uid;
    }
@@ -454,7 +509,8 @@ static off_t local_telldir(FsContext *ctx, V9fsFidOpenState *fs)

static bool local_is_mapped_file_metadata(FsContext *fs_ctx, const char *name)
{
    return !strcmp(name, VIRTFS_META_DIR);
    return
        !strcmp(name, VIRTFS_META_DIR) || !strcmp(name, VIRTFS_META_ROOT_FILE);
}

static struct dirent *local_readdir(FsContext *ctx, V9fsFidOpenState *fs)
@@ -471,7 +527,7 @@ again:
        entry->d_type = DT_UNKNOWN;
    } else if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
        if (local_is_mapped_file_metadata(ctx, entry->d_name)) {
            /* skip the meta data directory */
            /* skip the meta data */
            goto again;
        }
        entry->d_type = DT_UNKNOWN;
@@ -992,6 +1048,14 @@ static int local_unlinkat_common(FsContext *ctx, int dirfd, const char *name,
    if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
        int map_dirfd;

        /* We need to remove the metadata as well:
         * - the metadata directory if we're removing a directory
         * - the metadata file in the parent's metadata directory
         *
         * If any of these are missing (ie, ENOENT) then we're probably
         * trying to remove something that wasn't created in mapped-file
         * mode. We just ignore the error.
         */
        if (flags == AT_REMOVEDIR) {
            int fd;

@@ -999,32 +1063,20 @@ static int local_unlinkat_common(FsContext *ctx, int dirfd, const char *name,
            if (fd == -1) {
                goto err_out;
            }
            /*
             * If directory remove .virtfs_metadata contained in the
             * directory
             */
            ret = unlinkat(fd, VIRTFS_META_DIR, AT_REMOVEDIR);
            close_preserve_errno(fd);
            if (ret < 0 && errno != ENOENT) {
                /*
                 * We didn't had the .virtfs_metadata file. May be file created
                 * in non-mapped mode ?. Ignore ENOENT.
                 */
                goto err_out;
            }
        }
        /*
         * Now remove the name from parent directory
         * .virtfs_metadata directory.
         */
        map_dirfd = openat_dir(dirfd, VIRTFS_META_DIR);
        if (map_dirfd != -1) {
            ret = unlinkat(map_dirfd, name, 0);
            close_preserve_errno(map_dirfd);
            if (ret < 0 && errno != ENOENT) {
            /*
             * We didn't had the .virtfs_metadata file. May be file created
             * in non-mapped mode ?. Ignore ENOENT.
             */
                goto err_out;
            }
        } else if (errno != ENOENT) {
            goto err_out;
        }
    }
@@ -1138,14 +1190,32 @@ static int local_name_to_path(FsContext *ctx, V9fsPath *dir_path,
    }

    if (dir_path) {
        v9fs_path_sprintf(target, "%s/%s", dir_path->data, name);
    } else if (strcmp(name, "/")) {
        v9fs_path_sprintf(target, "%s", name);
        if (!strcmp(name, ".")) {
            /* "." relative to "foo/bar" is "foo/bar" */
            v9fs_path_copy(target, dir_path);
        } else if (!strcmp(name, "..")) {
            if (!strcmp(dir_path->data, ".")) {
                /* ".." relative to the root is "." */
                v9fs_path_sprintf(target, ".");
            } else {
        /* We want the path of the export root to be relative, otherwise
         * "*at()" syscalls would treat it as "/" in the host.
                char *tmp = g_path_get_dirname(dir_path->data);
                /* Symbolic links are resolved by the client. We can assume
                 * that ".." relative to "foo/bar" is equivalent to "foo"
                 */
        v9fs_path_sprintf(target, "%s", ".");
                v9fs_path_sprintf(target, "%s", tmp);
                g_free(tmp);
            }
        } else {
            assert(!strchr(name, '/'));
            v9fs_path_sprintf(target, "%s/%s", dir_path->data, name);
        }
    } else if (!strcmp(name, "/") || !strcmp(name, ".") ||
               !strcmp(name, "..")) {
            /* This is the root fid */
        v9fs_path_sprintf(target, ".");
    } else {
        assert(!strchr(name, '/'));
        v9fs_path_sprintf(target, "./%s", name);
    }
    return 0;
}
Loading