diff --git a/hw/file-op-9p.h b/hw/file-op-9p.h index 3a21cbec529030c35ef0640059af41a8b04d6456..e54f3585ea73299b2123b02aef01cc38c2904dfa 100644 --- a/hw/file-op-9p.h +++ b/hw/file-op-9p.h @@ -78,6 +78,8 @@ typedef struct FileOperations ssize_t (*lgetxattr)(FsContext *, const char *, const char *, void *, size_t); ssize_t (*llistxattr)(FsContext *, const char *, void *, size_t); + int (*lsetxattr)(FsContext *, const char *, + const char *, void *, size_t, int); void *opaque; } FileOperations; #endif diff --git a/hw/virtio-9p-debug.c b/hw/virtio-9p-debug.c index 4554eb6f6df436a49d599af2adfde72d98110995..ad680545c6b92c709f5a1efe314f29515107c606 100644 --- a/hw/virtio-9p-debug.c +++ b/hw/virtio-9p-debug.c @@ -588,6 +588,15 @@ void pprint_pdu(V9fsPDU *pdu) case P9_RXATTRWALK: fprintf(llogfile, "RXATTRWALK: ("); pprint_int64(pdu, 1, &offset, "xattrsize"); + case P9_TXATTRCREATE: + fprintf(llogfile, "TXATTRCREATE: ("); + pprint_int32(pdu, 0, &offset, "fid"); + pprint_str(pdu, 0, &offset, ", name"); + pprint_int64(pdu, 0, &offset, ", xattrsize"); + pprint_int32(pdu, 0, &offset, ", flags"); + break; + case P9_RXATTRCREATE: + fprintf(llogfile, "RXATTRCREATE: ("); break; default: fprintf(llogfile, "unknown(%d): (", pdu->id); diff --git a/hw/virtio-9p-local.c b/hw/virtio-9p-local.c index a6b5e6603a4b39f03c54eed5b951913bf635b80a..6c39256475f0946b0006c3c97b703c385c2d35e8 100644 --- a/hw/virtio-9p-local.c +++ b/hw/virtio-9p-local.c @@ -486,6 +486,12 @@ static ssize_t local_llistxattr(FsContext *ctx, const char *path, return llistxattr(rpath(ctx, path), value, size); } +static int local_lsetxattr(FsContext *ctx, const char *path, const char *name, + void *value, size_t size, int flags) +{ + return lsetxattr(rpath(ctx, path), name, value, size, flags); +} + FileOperations local_ops = { .lstat = local_lstat, .readlink = local_readlink, @@ -516,4 +522,5 @@ FileOperations local_ops = { .statfs = local_statfs, .lgetxattr = local_lgetxattr, .llistxattr = local_llistxattr, + .lsetxattr = local_lsetxattr, }; diff --git a/hw/virtio-9p.c b/hw/virtio-9p.c index 656c721f95e72be319102a816c155cb117678651..a2ca422abcc2e083e3afee28897800cc247212b2 100644 --- a/hw/virtio-9p.c +++ b/hw/virtio-9p.c @@ -278,6 +278,14 @@ static ssize_t v9fs_do_llistxattr(V9fsState *s, V9fsString *path, value, size); } +static int v9fs_do_lsetxattr(V9fsState *s, V9fsString *path, + V9fsString *xattr_name, + void *value, size_t size, int flags) +{ + return s->ops->lsetxattr(&s->ctx, path->data, + xattr_name->data, value, size, flags); +} + static void v9fs_string_init(V9fsString *str) { str->data = NULL; @@ -431,8 +439,39 @@ static V9fsFidState *alloc_fid(V9fsState *s, int32_t fid) return f; } +static int v9fs_xattr_fid_clunk(V9fsState *s, V9fsFidState *fidp) +{ + int retval = 0; + + if (fidp->fs.xattr.copied_len == -1) { + /* getxattr/listxattr fid */ + goto free_value; + } + /* + * if this is fid for setxattr. clunk should + * result in setxattr localcall + */ + if (fidp->fs.xattr.len != fidp->fs.xattr.copied_len) { + /* clunk after partial write */ + retval = -EINVAL; + goto free_out; + } + retval = v9fs_do_lsetxattr(s, &fidp->path, &fidp->fs.xattr.name, + fidp->fs.xattr.value, + fidp->fs.xattr.len, + fidp->fs.xattr.flags); +free_out: + v9fs_string_free(&fidp->fs.xattr.name); +free_value: + if (fidp->fs.xattr.value) { + qemu_free(fidp->fs.xattr.value); + } + return retval; +} + static int free_fid(V9fsState *s, int32_t fid) { + int retval = 0; V9fsFidState **fidpp, *fidp; for (fidpp = &s->fid_list; *fidpp; fidpp = &(*fidpp)->next) { @@ -453,14 +492,12 @@ static int free_fid(V9fsState *s, int32_t fid) } else if (fidp->fid_type == P9_FID_DIR) { v9fs_do_closedir(s, fidp->fs.dir); } else if (fidp->fid_type == P9_FID_XATTR) { - if (fidp->fs.xattr.value) { - qemu_free(fidp->fs.xattr.value); - } + retval = v9fs_xattr_fid_clunk(s, fidp); } v9fs_string_free(&fidp->path); qemu_free(fidp); - return 0; + return retval; } #define P9_QID_TYPE_DIR 0x80 @@ -2211,6 +2248,48 @@ out: qemu_free(vs); } +static void v9fs_xattr_write(V9fsState *s, V9fsWriteState *vs) +{ + int i, to_copy; + ssize_t err = 0; + int write_count; + int64_t xattr_len; + + xattr_len = vs->fidp->fs.xattr.len; + write_count = xattr_len - vs->off; + if (write_count > vs->count) { + write_count = vs->count; + } else if (write_count < 0) { + /* + * write beyond XATTR value len specified in + * xattrcreate + */ + err = -ENOSPC; + goto out; + } + vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", write_count); + err = vs->offset; + vs->fidp->fs.xattr.copied_len += write_count; + /* + * Now copy the content from sg list + */ + for (i = 0; i < vs->cnt; i++) { + if (write_count > vs->sg[i].iov_len) { + to_copy = vs->sg[i].iov_len; + } else { + to_copy = write_count; + } + memcpy((char *)vs->fidp->fs.xattr.value + vs->off, + vs->sg[i].iov_base, to_copy); + /* updating vs->off since we are not using below */ + vs->off += to_copy; + write_count -= to_copy; + } +out: + complete_pdu(s, vs->pdu, err); + qemu_free(vs); +} + static void v9fs_write(V9fsState *s, V9fsPDU *pdu) { int32_t fid; @@ -2226,7 +2305,7 @@ static void v9fs_write(V9fsState *s, V9fsPDU *pdu) vs->len = 0; pdu_unmarshal(vs->pdu, vs->offset, "dqdv", &fid, &vs->off, &vs->count, - vs->sg, &vs->cnt); + vs->sg, &vs->cnt); vs->fidp = lookup_fid(s, fid); if (vs->fidp == NULL) { @@ -2234,11 +2313,21 @@ static void v9fs_write(V9fsState *s, V9fsPDU *pdu) goto out; } - if (vs->fidp->fs.fd == -1) { + if (vs->fidp->fid_type == P9_FID_FILE) { + if (vs->fidp->fs.fd == -1) { + err = -EINVAL; + goto out; + } + } else if (vs->fidp->fid_type == P9_FID_XATTR) { + /* + * setxattr operation + */ + v9fs_xattr_write(s, vs); + return; + } else { err = -EINVAL; goto out; } - err = v9fs_do_lseek(s, vs->fidp->fs.fd, vs->off, SEEK_SET); v9fs_write_post_lseek(s, vs, err); @@ -3276,6 +3365,41 @@ out: qemu_free(vs); } +static void v9fs_xattrcreate(V9fsState *s, V9fsPDU *pdu) +{ + int flags; + int32_t fid; + ssize_t err = 0; + V9fsXattrState *vs; + + vs = qemu_malloc(sizeof(*vs)); + vs->pdu = pdu; + vs->offset = 7; + + pdu_unmarshal(vs->pdu, vs->offset, "dsqd", + &fid, &vs->name, &vs->size, &flags); + + vs->file_fidp = lookup_fid(s, fid); + if (vs->file_fidp == NULL) { + err = -EINVAL; + goto out; + } + + /* Make the file fid point to xattr */ + vs->xattr_fidp = vs->file_fidp; + vs->xattr_fidp->fid_type = P9_FID_XATTR; + vs->xattr_fidp->fs.xattr.copied_len = 0; + vs->xattr_fidp->fs.xattr.len = vs->size; + vs->xattr_fidp->fs.xattr.flags = flags; + v9fs_string_init(&vs->xattr_fidp->fs.xattr.name); + v9fs_string_copy(&vs->xattr_fidp->fs.xattr.name, &vs->name); + vs->xattr_fidp->fs.xattr.value = qemu_malloc(vs->size); + +out: + complete_pdu(s, vs->pdu, err); + v9fs_string_free(&vs->name); + qemu_free(vs); +} typedef void (pdu_handler_t)(V9fsState *s, V9fsPDU *pdu); @@ -3285,6 +3409,7 @@ static pdu_handler_t *pdu_handlers[] = { [P9_TGETATTR] = v9fs_getattr, [P9_TSETATTR] = v9fs_setattr, [P9_TXATTRWALK] = v9fs_xattrwalk, + [P9_TXATTRCREATE] = v9fs_xattrcreate, [P9_TMKNOD] = v9fs_mknod, [P9_TRENAME] = v9fs_rename, [P9_TMKDIR] = v9fs_mkdir, diff --git a/hw/virtio-9p.h b/hw/virtio-9p.h index 005df492904d7a69921a2ccd00bcb9f220cb2ecc..016ba1dcf7b9aef8489a9e8558a724ce6d35200b 100644 --- a/hw/virtio-9p.h +++ b/hw/virtio-9p.h @@ -31,6 +31,8 @@ enum { P9_RSETATTR, P9_TXATTRWALK = 30, P9_RXATTRWALK, + P9_TXATTRCREATE = 32, + P9_RXATTRCREATE, P9_TREADDIR = 40, P9_RREADDIR, P9_TLINK = 70,