aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--hw/virtio-9p-local.c5
-rw-r--r--hw/virtio-9p.c155
-rw-r--r--hw/virtio-9p.h23
3 files changed, 182 insertions, 1 deletions
diff --git a/hw/virtio-9p-local.c b/hw/virtio-9p-local.c
index dd7277baf1..269fc62beb 100644
--- a/hw/virtio-9p-local.c
+++ b/hw/virtio-9p-local.c
@@ -442,7 +442,10 @@ static int local_rename(FsContext *ctx, const char *oldpath,
static int local_chown(FsContext *fs_ctx, const char *path, FsCred *credp)
{
- if (fs_ctx->fs_sm == SM_MAPPED) {
+ if ((credp->fc_uid == -1 && credp->fc_gid == -1) ||
+ (fs_ctx->fs_sm == SM_PASSTHROUGH)) {
+ return lchown(rpath(fs_ctx, path), credp->fc_uid, credp->fc_gid);
+ } else if (fs_ctx->fs_sm == SM_MAPPED) {
return local_set_xattr(rpath(fs_ctx, path), credp);
} else if (fs_ctx->fs_sm == SM_PASSTHROUGH) {
return lchown(rpath(fs_ctx, path), credp->fc_uid, credp->fc_gid);
diff --git a/hw/virtio-9p.c b/hw/virtio-9p.c
index 687f63d6cb..39e63cb773 100644
--- a/hw/virtio-9p.c
+++ b/hw/virtio-9p.c
@@ -665,6 +665,15 @@ static size_t pdu_unmarshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...)
&statp->n_muid);
break;
}
+ case 'I': {
+ V9fsIattr *iattr = va_arg(ap, V9fsIattr *);
+ offset += pdu_unmarshal(pdu, offset, "ddddqqqqq",
+ &iattr->valid, &iattr->mode,
+ &iattr->uid, &iattr->gid, &iattr->size,
+ &iattr->atime_sec, &iattr->atime_nsec,
+ &iattr->mtime_sec, &iattr->mtime_nsec);
+ break;
+ }
default:
break;
}
@@ -1240,6 +1249,151 @@ out:
qemu_free(vs);
}
+/* From Linux kernel code */
+#define ATTR_MODE (1 << 0)
+#define ATTR_UID (1 << 1)
+#define ATTR_GID (1 << 2)
+#define ATTR_SIZE (1 << 3)
+#define ATTR_ATIME (1 << 4)
+#define ATTR_MTIME (1 << 5)
+#define ATTR_CTIME (1 << 6)
+#define ATTR_MASK 127
+#define ATTR_ATIME_SET (1 << 7)
+#define ATTR_MTIME_SET (1 << 8)
+
+static void v9fs_setattr_post_truncate(V9fsState *s, V9fsSetattrState *vs,
+ int err)
+{
+ if (err == -1) {
+ err = -errno;
+ goto out;
+ }
+ err = vs->offset;
+
+out:
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs);
+}
+
+static void v9fs_setattr_post_chown(V9fsState *s, V9fsSetattrState *vs, int err)
+{
+ if (err == -1) {
+ err = -errno;
+ goto out;
+ }
+
+ if (vs->v9iattr.valid & (ATTR_SIZE)) {
+ err = v9fs_do_truncate(s, &vs->fidp->path, vs->v9iattr.size);
+ }
+ v9fs_setattr_post_truncate(s, vs, err);
+ return;
+
+out:
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs);
+}
+
+static void v9fs_setattr_post_utimensat(V9fsState *s, V9fsSetattrState *vs,
+ int err)
+{
+ if (err == -1) {
+ err = -errno;
+ goto out;
+ }
+
+ /* If the only valid entry in iattr is ctime we can call
+ * chown(-1,-1) to update the ctime of the file
+ */
+ if ((vs->v9iattr.valid & (ATTR_UID | ATTR_GID)) ||
+ ((vs->v9iattr.valid & ATTR_CTIME)
+ && !((vs->v9iattr.valid & ATTR_MASK) & ~ATTR_CTIME))) {
+ if (!(vs->v9iattr.valid & ATTR_UID)) {
+ vs->v9iattr.uid = -1;
+ }
+ if (!(vs->v9iattr.valid & ATTR_GID)) {
+ vs->v9iattr.gid = -1;
+ }
+ err = v9fs_do_chown(s, &vs->fidp->path, vs->v9iattr.uid,
+ vs->v9iattr.gid);
+ }
+ v9fs_setattr_post_chown(s, vs, err);
+ return;
+
+out:
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs);
+}
+
+static void v9fs_setattr_post_chmod(V9fsState *s, V9fsSetattrState *vs, int err)
+{
+ if (err == -1) {
+ err = -errno;
+ goto out;
+ }
+
+ if (vs->v9iattr.valid & (ATTR_ATIME | ATTR_MTIME)) {
+ struct timespec times[2];
+ if (vs->v9iattr.valid & ATTR_ATIME) {
+ if (vs->v9iattr.valid & ATTR_ATIME_SET) {
+ times[0].tv_sec = vs->v9iattr.atime_sec;
+ times[0].tv_nsec = vs->v9iattr.atime_nsec;
+ } else {
+ times[0].tv_nsec = UTIME_NOW;
+ }
+ } else {
+ times[0].tv_nsec = UTIME_OMIT;
+ }
+
+ if (vs->v9iattr.valid & ATTR_MTIME) {
+ if (vs->v9iattr.valid & ATTR_MTIME_SET) {
+ times[1].tv_sec = vs->v9iattr.mtime_sec;
+ times[1].tv_nsec = vs->v9iattr.mtime_nsec;
+ } else {
+ times[1].tv_nsec = UTIME_NOW;
+ }
+ } else {
+ times[1].tv_nsec = UTIME_OMIT;
+ }
+ err = v9fs_do_utimensat(s, &vs->fidp->path, times);
+ }
+ v9fs_setattr_post_utimensat(s, vs, err);
+ return;
+
+out:
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs);
+}
+
+static void v9fs_setattr(V9fsState *s, V9fsPDU *pdu)
+{
+ int32_t fid;
+ V9fsSetattrState *vs;
+ int err = 0;
+
+ vs = qemu_malloc(sizeof(*vs));
+ vs->pdu = pdu;
+ vs->offset = 7;
+
+ pdu_unmarshal(pdu, vs->offset, "dI", &fid, &vs->v9iattr);
+
+ vs->fidp = lookup_fid(s, fid);
+ if (vs->fidp == NULL) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ if (vs->v9iattr.valid & ATTR_MODE) {
+ err = v9fs_do_chmod(s, &vs->fidp->path, vs->v9iattr.mode);
+ }
+
+ v9fs_setattr_post_chmod(s, vs, err);
+ return;
+
+out:
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs);
+}
+
static void v9fs_walk_complete(V9fsState *s, V9fsWalkState *vs, int err)
{
complete_pdu(s, vs->pdu, err);
@@ -2514,6 +2668,7 @@ static pdu_handler_t *pdu_handlers[] = {
[P9_TREADDIR] = v9fs_readdir,
[P9_TSTATFS] = v9fs_statfs,
[P9_TGETATTR] = v9fs_getattr,
+ [P9_TSETATTR] = v9fs_setattr,
[P9_TVERSION] = v9fs_version,
[P9_TATTACH] = v9fs_attach,
[P9_TSTAT] = v9fs_stat,
diff --git a/hw/virtio-9p.h b/hw/virtio-9p.h
index 1feed82a09..ebf44a79ff 100644
--- a/hw/virtio-9p.h
+++ b/hw/virtio-9p.h
@@ -17,6 +17,8 @@ enum {
P9_RSTATFS,
P9_TGETATTR = 24,
P9_RGETATTR,
+ P9_TSETATTR = 26,
+ P9_RSETATTR,
P9_TREADDIR = 40,
P9_RREADDIR,
P9_TVERSION = 100,
@@ -290,6 +292,27 @@ typedef struct V9fsWstatState
V9fsString nname;
} V9fsWstatState;
+typedef struct V9fsIattr
+{
+ int32_t valid;
+ int32_t mode;
+ int32_t uid;
+ int32_t gid;
+ int64_t size;
+ int64_t atime_sec;
+ int64_t atime_nsec;
+ int64_t mtime_sec;
+ int64_t mtime_nsec;
+} V9fsIattr;
+
+typedef struct V9fsSetattrState
+{
+ V9fsPDU *pdu;
+ size_t offset;
+ V9fsIattr v9iattr;
+ V9fsFidState *fidp;
+} V9fsSetattrState;
+
struct virtio_9p_config
{
/* number of characters in tag */