aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
Diffstat (limited to 'hw')
-rw-r--r--hw/9pfs/9p-handle.c5
-rw-r--r--hw/9pfs/9p-local.c186
-rw-r--r--hw/9pfs/9p-util.c43
-rw-r--r--hw/9pfs/9p-util.h2
-rw-r--r--hw/9pfs/9p.c51
-rw-r--r--hw/9pfs/9p.h2
-rw-r--r--hw/9pfs/virtio-9p-device.c8
-rw-r--r--hw/9pfs/xen-9p-backend.c6
8 files changed, 164 insertions, 139 deletions
diff --git a/hw/9pfs/9p-handle.c b/hw/9pfs/9p-handle.c
index 1687661bc9..9875f1894c 100644
--- a/hw/9pfs/9p-handle.c
+++ b/hw/9pfs/9p-handle.c
@@ -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;
}
diff --git a/hw/9pfs/9p-local.c b/hw/9pfs/9p-local.c
index a2486566af..1e78b7c9e9 100644
--- a/hw/9pfs/9p-local.c
+++ b/hw/9pfs/9p-local.c
@@ -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,13 +144,17 @@ static void local_mapped_file_attr(int dirfd, const char *name,
char buf[ATTR_MAX];
int map_dirfd;
- map_dirfd = openat_dir(dirfd, VIRTFS_META_DIR);
- if (map_dirfd == -1) {
- return;
- }
+ if (strcmp(name, ".")) {
+ map_dirfd = openat_dir(dirfd, VIRTFS_META_DIR);
+ if (map_dirfd == -1) {
+ return;
+ }
- fp = local_fopenat(map_dirfd, name, "r");
- close_preserve_errno(map_dirfd);
+ 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,26 +232,38 @@ 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;
-
- ret = mkdirat(dirfd, VIRTFS_META_DIR, 0700);
- if (ret < 0 && errno != EEXIST) {
- return -1;
- }
-
- map_dirfd = openat_dir(dirfd, VIRTFS_META_DIR);
- if (map_dirfd == -1) {
- return -1;
- }
+ 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;
+ }
- fp = local_fopenat(map_dirfd, name, "r");
- if (!fp) {
- if (errno == ENOENT) {
- goto update_map_file;
- } else {
- close_preserve_errno(map_dirfd);
+ map_dirfd = openat_dir(dirfd, VIRTFS_META_DIR);
+ if (map_dirfd == -1) {
return -1;
}
+
+ fp = local_fopenat(map_dirfd, name, "r");
+ if (!fp) {
+ if (errno == ENOENT) {
+ goto update_map_file;
+ } else {
+ close_preserve_errno(map_dirfd);
+ return -1;
+ }
+ }
}
memset(buf, 0, ATTR_MAX);
while (fgets(buf, ATTR_MAX, fp)) {
@@ -240,12 +281,26 @@ static int local_set_mapped_file_attrat(int dirfd, const char *name,
fclose(fp);
update_map_file:
- fp = local_fopenat(map_dirfd, name, "w");
- close_preserve_errno(map_dirfd);
+ 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);
- 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.
- */
+ if (map_dirfd != -1) {
+ ret = unlinkat(map_dirfd, name, 0);
+ close_preserve_errno(map_dirfd);
+ if (ret < 0 && errno != 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 {
+ 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", 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 {
- /* We want the path of the export root to be relative, otherwise
- * "*at()" syscalls would treat it as "/" in the host.
- */
- v9fs_path_sprintf(target, "%s", ".");
+ assert(!strchr(name, '/'));
+ v9fs_path_sprintf(target, "./%s", name);
}
return 0;
}
diff --git a/hw/9pfs/9p-util.c b/hw/9pfs/9p-util.c
index fdb4d57376..f709c27a1f 100644
--- a/hw/9pfs/9p-util.c
+++ b/hw/9pfs/9p-util.c
@@ -14,49 +14,6 @@
#include "qemu/xattr.h"
#include "9p-util.h"
-int relative_openat_nofollow(int dirfd, const char *path, int flags,
- mode_t mode)
-{
- int fd;
-
- fd = dup(dirfd);
- if (fd == -1) {
- return -1;
- }
-
- while (*path) {
- const char *c;
- int next_fd;
- char *head;
-
- /* Only relative paths without consecutive slashes */
- assert(path[0] != '/');
-
- head = g_strdup(path);
- c = strchr(path, '/');
- if (c) {
- head[c - path] = 0;
- next_fd = openat_dir(fd, head);
- } else {
- next_fd = openat_file(fd, head, flags, mode);
- }
- g_free(head);
- if (next_fd == -1) {
- close_preserve_errno(fd);
- return -1;
- }
- close(fd);
- fd = next_fd;
-
- if (!c) {
- break;
- }
- path = c + 1;
- }
-
- return fd;
-}
-
ssize_t fgetxattrat_nofollow(int dirfd, const char *filename, const char *name,
void *value, size_t size)
{
diff --git a/hw/9pfs/9p-util.h b/hw/9pfs/9p-util.h
index 517027c520..91299a24b8 100644
--- a/hw/9pfs/9p-util.h
+++ b/hw/9pfs/9p-util.h
@@ -50,8 +50,6 @@ static inline int openat_file(int dirfd, const char *name, int flags,
return fd;
}
-int relative_openat_nofollow(int dirfd, const char *path, int flags,
- mode_t mode);
ssize_t fgetxattrat_nofollow(int dirfd, const char *path, const char *name,
void *value, size_t size);
int fsetxattrat_nofollow(int dirfd, const char *path, const char *name,
diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c
index ab3e22f231..96d2683348 100644
--- a/hw/9pfs/9p.c
+++ b/hw/9pfs/9p.c
@@ -65,11 +65,6 @@ ssize_t pdu_unmarshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...)
return ret;
}
-static void pdu_push_and_notify(V9fsPDU *pdu)
-{
- pdu->s->transport->push_and_notify(pdu);
-}
-
static int omode_to_uflags(int8_t mode)
{
int ret = 0;
@@ -668,7 +663,7 @@ static void coroutine_fn pdu_complete(V9fsPDU *pdu, ssize_t len)
pdu->size = len;
pdu->id = id;
- pdu_push_and_notify(pdu);
+ pdu->s->transport->push_and_notify(pdu);
/* Now wakeup anybody waiting in flush for this request */
if (!qemu_co_queue_next(&pdu->complete)) {
@@ -2576,7 +2571,10 @@ static int coroutine_fn v9fs_complete_rename(V9fsPDU *pdu, V9fsFidState *fidp,
err = -EINVAL;
goto out;
}
- v9fs_co_name_to_path(pdu, &dirfidp->path, name->data, &new_path);
+ err = v9fs_co_name_to_path(pdu, &dirfidp->path, name->data, &new_path);
+ if (err < 0) {
+ goto out;
+ }
} else {
old_name = fidp->path.data;
end = strrchr(old_name, '/');
@@ -2588,8 +2586,11 @@ static int coroutine_fn v9fs_complete_rename(V9fsPDU *pdu, V9fsFidState *fidp,
new_name = g_malloc0(end - old_name + name->size + 1);
strncat(new_name, old_name, end - old_name);
strncat(new_name + (end - old_name), name->data, name->size);
- v9fs_co_name_to_path(pdu, NULL, new_name, &new_path);
+ err = v9fs_co_name_to_path(pdu, NULL, new_name, &new_path);
g_free(new_name);
+ if (err < 0) {
+ goto out;
+ }
}
err = v9fs_co_rename(pdu, &fidp->path, &new_path);
if (err < 0) {
@@ -2669,20 +2670,26 @@ out_nofid:
v9fs_string_free(&name);
}
-static void coroutine_fn v9fs_fix_fid_paths(V9fsPDU *pdu, V9fsPath *olddir,
- V9fsString *old_name,
- V9fsPath *newdir,
- V9fsString *new_name)
+static int coroutine_fn v9fs_fix_fid_paths(V9fsPDU *pdu, V9fsPath *olddir,
+ V9fsString *old_name,
+ V9fsPath *newdir,
+ V9fsString *new_name)
{
V9fsFidState *tfidp;
V9fsPath oldpath, newpath;
V9fsState *s = pdu->s;
-
+ int err;
v9fs_path_init(&oldpath);
v9fs_path_init(&newpath);
- v9fs_co_name_to_path(pdu, olddir, old_name->data, &oldpath);
- v9fs_co_name_to_path(pdu, newdir, new_name->data, &newpath);
+ err = v9fs_co_name_to_path(pdu, olddir, old_name->data, &oldpath);
+ if (err < 0) {
+ goto out;
+ }
+ err = v9fs_co_name_to_path(pdu, newdir, new_name->data, &newpath);
+ if (err < 0) {
+ goto out;
+ }
/*
* Fixup fid's pointing to the old name to
@@ -2694,8 +2701,10 @@ static void coroutine_fn v9fs_fix_fid_paths(V9fsPDU *pdu, V9fsPath *olddir,
v9fs_fix_path(&tfidp->path, &newpath, strlen(oldpath.data));
}
}
+out:
v9fs_path_free(&oldpath);
v9fs_path_free(&newpath);
+ return err;
}
static int coroutine_fn v9fs_complete_renameat(V9fsPDU *pdu, int32_t olddirfid,
@@ -2729,8 +2738,8 @@ static int coroutine_fn v9fs_complete_renameat(V9fsPDU *pdu, int32_t olddirfid,
}
if (s->ctx.export_flags & V9FS_PATHNAME_FSCONTEXT) {
/* Only for path based fid we need to do the below fixup */
- v9fs_fix_fid_paths(pdu, &olddirfidp->path, old_name,
- &newdirfidp->path, new_name);
+ err = v9fs_fix_fid_paths(pdu, &olddirfidp->path, old_name,
+ &newdirfidp->path, new_name);
}
out:
if (olddirfidp) {
@@ -3446,12 +3455,16 @@ static inline bool is_read_only_op(V9fsPDU *pdu)
}
}
-void pdu_submit(V9fsPDU *pdu)
+void pdu_submit(V9fsPDU *pdu, P9MsgHeader *hdr)
{
Coroutine *co;
CoroutineEntry *handler;
V9fsState *s = pdu->s;
+ pdu->size = le32_to_cpu(hdr->size_le);
+ pdu->id = hdr->id;
+ pdu->tag = le16_to_cpu(hdr->tag_le);
+
if (pdu->id >= ARRAY_SIZE(pdu_co_handlers) ||
(pdu_co_handlers[pdu->id] == NULL)) {
handler = v9fs_op_not_supp;
@@ -3462,6 +3475,8 @@ void pdu_submit(V9fsPDU *pdu)
if (is_ro_export(&s->ctx) && !is_read_only_op(pdu)) {
handler = v9fs_fs_ro;
}
+
+ qemu_co_queue_init(&pdu->complete);
co = qemu_coroutine_create(handler, pdu);
qemu_coroutine_enter(co);
}
diff --git a/hw/9pfs/9p.h b/hw/9pfs/9p.h
index 5312d8a424..c886ba78d2 100644
--- a/hw/9pfs/9p.h
+++ b/hw/9pfs/9p.h
@@ -347,7 +347,7 @@ ssize_t pdu_marshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...);
ssize_t pdu_unmarshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...);
V9fsPDU *pdu_alloc(V9fsState *s);
void pdu_free(V9fsPDU *pdu);
-void pdu_submit(V9fsPDU *pdu);
+void pdu_submit(V9fsPDU *pdu, P9MsgHeader *hdr);
void v9fs_reset(V9fsState *s);
struct V9fsTransport {
diff --git a/hw/9pfs/virtio-9p-device.c b/hw/9pfs/virtio-9p-device.c
index 3782f43702..245abd8aae 100644
--- a/hw/9pfs/virtio-9p-device.c
+++ b/hw/9pfs/virtio-9p-device.c
@@ -70,13 +70,7 @@ static void handle_9p_output(VirtIODevice *vdev, VirtQueue *vq)
goto out_free_req;
}
- pdu->size = le32_to_cpu(out.size_le);
-
- pdu->id = out.id;
- pdu->tag = le16_to_cpu(out.tag_le);
-
- qemu_co_queue_init(&pdu->complete);
- pdu_submit(pdu);
+ pdu_submit(pdu, &out);
}
return;
diff --git a/hw/9pfs/xen-9p-backend.c b/hw/9pfs/xen-9p-backend.c
index 5df97c90fa..922cc967be 100644
--- a/hw/9pfs/xen-9p-backend.c
+++ b/hw/9pfs/xen-9p-backend.c
@@ -243,14 +243,10 @@ static int xen_9pfs_receive(Xen9pfsRing *ring)
/* cannot fail, because we only handle one request per ring at a time */
pdu = pdu_alloc(&ring->priv->state);
- pdu->size = le32_to_cpu(h.size_le);
- pdu->id = h.id;
- pdu->tag = le32_to_cpu(h.tag_le);
ring->out_size = le32_to_cpu(h.size_le);
ring->out_cons = cons + le32_to_cpu(h.size_le);
- qemu_co_queue_init(&pdu->complete);
- pdu_submit(pdu);
+ pdu_submit(pdu, &h);
return 0;
}