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 */