X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=hw%2F9pfs%2Fvirtio-9p.c;h=0777ece8165ea6b7dd26590329f15d37a817a890;hb=cc720ddb5435ceaa4cda397ca3e74b099752b763;hp=ad70768dce8f6c252034ebd8ad4a080442b82e17;hpb=5f524c1ebcc5e0dec0de8940d34f9adb3c7887a1;p=qemu.git diff --git a/hw/9pfs/virtio-9p.c b/hw/9pfs/virtio-9p.c index ad70768dc..0777ece81 100644 --- a/hw/9pfs/virtio-9p.c +++ b/hw/9pfs/virtio-9p.c @@ -17,11 +17,13 @@ #include "hw/virtio-pci.h" #include "virtio-9p.h" #include "fsdev/qemu-fsdev.h" -#include "virtio-9p-debug.h" #include "virtio-9p-xattr.h" #include "virtio-9p-coth.h" +#include "trace.h" -int debug_9p_pdu; +int open_fd_hw; +int total_open_fd; +static int open_fd_rc; enum { Oread = 0x00, @@ -69,6 +71,55 @@ static int omode_to_uflags(int8_t mode) return ret; } +static int dotl_to_at_flags(int flags) +{ + int rflags = 0; + if (flags & P9_DOTL_AT_REMOVEDIR) { + rflags |= AT_REMOVEDIR; + } + return rflags; +} + +struct dotl_openflag_map { + int dotl_flag; + int open_flag; +}; + +static int dotl_to_open_flags(int flags) +{ + int i; + /* + * We have same bits for P9_DOTL_READONLY, P9_DOTL_WRONLY + * and P9_DOTL_NOACCESS + */ + int oflags = flags & O_ACCMODE; + + struct dotl_openflag_map dotl_oflag_map[] = { + { P9_DOTL_CREATE, O_CREAT }, + { P9_DOTL_EXCL, O_EXCL }, + { P9_DOTL_NOCTTY , O_NOCTTY }, + { P9_DOTL_TRUNC, O_TRUNC }, + { P9_DOTL_APPEND, O_APPEND }, + { P9_DOTL_NONBLOCK, O_NONBLOCK } , + { P9_DOTL_DSYNC, O_DSYNC }, + { P9_DOTL_FASYNC, FASYNC }, + { P9_DOTL_DIRECT, O_DIRECT }, + { P9_DOTL_LARGEFILE, O_LARGEFILE }, + { P9_DOTL_DIRECTORY, O_DIRECTORY }, + { P9_DOTL_NOFOLLOW, O_NOFOLLOW }, + { P9_DOTL_NOATIME, O_NOATIME }, + { P9_DOTL_SYNC, O_SYNC }, + }; + + for (i = 0; i < ARRAY_SIZE(dotl_oflag_map); i++) { + if (flags & dotl_oflag_map[i].dotl_flag) { + oflags |= dotl_oflag_map[i].open_flag; + } + } + + return oflags; +} + void cred_init(FsCred *credp) { credp->fc_uid = -1; @@ -77,20 +128,35 @@ void cred_init(FsCred *credp) credp->fc_rdev = -1; } -static void v9fs_string_init(V9fsString *str) +static int get_dotl_openflags(V9fsState *s, int oflags) +{ + int flags; + /* + * Filter the client open flags + */ + flags = dotl_to_open_flags(oflags); + flags &= ~(O_NOCTTY | O_ASYNC | O_CREAT); + /* + * Ignore direct disk access hint until the server supports it. + */ + flags &= ~O_DIRECT; + return flags; +} + +void v9fs_string_init(V9fsString *str) { str->data = NULL; str->size = 0; } -static void v9fs_string_free(V9fsString *str) +void v9fs_string_free(V9fsString *str) { g_free(str->data); str->data = NULL; str->size = 0; } -static void v9fs_string_null(V9fsString *str) +void v9fs_string_null(V9fsString *str) { v9fs_string_free(str); } @@ -189,7 +255,7 @@ alloc_print: return vsprintf(*strp, fmt, ap); } -static void GCC_FMT_ATTR(2, 3) +void GCC_FMT_ATTR(2, 3) v9fs_string_sprintf(V9fsString *str, const char *fmt, ...) { va_list ap; @@ -205,22 +271,54 @@ v9fs_string_sprintf(V9fsString *str, const char *fmt, ...) str->size = err; } -static void v9fs_string_copy(V9fsString *lhs, V9fsString *rhs) +void v9fs_string_copy(V9fsString *lhs, V9fsString *rhs) { v9fs_string_free(lhs); v9fs_string_sprintf(lhs, "%s", rhs->data); } +void v9fs_path_init(V9fsPath *path) +{ + path->data = NULL; + path->size = 0; +} + +void v9fs_path_free(V9fsPath *path) +{ + g_free(path->data); + path->data = NULL; + path->size = 0; +} + +void v9fs_path_copy(V9fsPath *lhs, V9fsPath *rhs) +{ + v9fs_path_free(lhs); + lhs->data = g_malloc(rhs->size); + memcpy(lhs->data, rhs->data, rhs->size); + lhs->size = rhs->size; +} + +int v9fs_name_to_path(V9fsState *s, V9fsPath *dirpath, + const char *name, V9fsPath *path) +{ + int err; + err = s->ops->name_to_path(&s->ctx, dirpath, name, path); + if (err < 0) { + err = -errno; + } + return err; +} + /* * Return TRUE if s1 is an ancestor of s2. * * E.g. "a/b" is an ancestor of "a/b/c" but not of "a/bc/d". * As a special case, We treat s1 as ancestor of s2 if they are same! */ -static int v9fs_path_is_ancestor(V9fsString *s1, V9fsString *s2) +static int v9fs_path_is_ancestor(V9fsPath *s1, V9fsPath *s2) { - if (!strncmp(s1->data, s2->data, s1->size)) { - if (s2->data[s1->size] == '\0' || s2->data[s1->size] == '/') { + if (!strncmp(s1->data, s2->data, s1->size - 1)) { + if (s2->data[s1->size - 1] == '\0' || s2->data[s1->size - 1] == '/') { return 1; } } @@ -232,16 +330,61 @@ static size_t v9fs_string_size(V9fsString *str) return str->size; } -static V9fsFidState *lookup_fid(V9fsState *s, int32_t fid) +/* + * returns 0 if fid got re-opened, 1 if not, < 0 on error */ +static int v9fs_reopen_fid(V9fsPDU *pdu, V9fsFidState *f) { + int err = 1; + if (f->fid_type == P9_FID_FILE) { + if (f->fs.fd == -1) { + do { + err = v9fs_co_open(pdu, f, f->open_flags); + } while (err == -EINTR && !pdu->cancelled); + } + } else if (f->fid_type == P9_FID_DIR) { + if (f->fs.dir == NULL) { + do { + err = v9fs_co_opendir(pdu, f); + } while (err == -EINTR && !pdu->cancelled); + } + } + return err; +} + +static V9fsFidState *get_fid(V9fsPDU *pdu, int32_t fid) +{ + int err; V9fsFidState *f; + V9fsState *s = pdu->s; for (f = s->fid_list; f; f = f->next) { + BUG_ON(f->clunked); if (f->fid == fid) { + /* + * Update the fid ref upfront so that + * we don't get reclaimed when we yield + * in open later. + */ + f->ref++; + /* + * check whether we need to reopen the + * file. We might have closed the fd + * while trying to free up some file + * descriptors. + */ + err = v9fs_reopen_fid(pdu, f); + if (err < 0) { + f->ref--; + return NULL; + } + /* + * Mark the fid as referenced so that the LRU + * reclaim won't close the file descriptor + */ + f->flags |= FID_REFERENCED; return f; } } - return NULL; } @@ -249,23 +392,29 @@ static V9fsFidState *alloc_fid(V9fsState *s, int32_t fid) { V9fsFidState *f; - f = lookup_fid(s, fid); - if (f) { - return NULL; + for (f = s->fid_list; f; f = f->next) { + /* If fid is already there return NULL */ + BUG_ON(f->clunked); + if (f->fid == fid) { + return NULL; + } } - f = g_malloc0(sizeof(V9fsFidState)); - f->fid = fid; f->fid_type = P9_FID_NONE; - + f->ref = 1; + /* + * Mark the fid as referenced so that the LRU + * reclaim won't close the file descriptor + */ + f->flags |= FID_REFERENCED; f->next = s->fid_list; s->fid_list = f; return f; } -static int v9fs_xattr_fid_clunk(V9fsState *s, V9fsFidState *fidp) +static int v9fs_xattr_fid_clunk(V9fsPDU *pdu, V9fsFidState *fidp) { int retval = 0; @@ -283,12 +432,12 @@ static int v9fs_xattr_fid_clunk(V9fsState *s, V9fsFidState *fidp) goto free_out; } if (fidp->fs.xattr.len) { - retval = v9fs_co_lsetxattr(s, &fidp->path, &fidp->fs.xattr.name, + retval = v9fs_co_lsetxattr(pdu, &fidp->path, &fidp->fs.xattr.name, fidp->fs.xattr.value, fidp->fs.xattr.len, fidp->fs.xattr.flags); } else { - retval = v9fs_co_lremovexattr(s, &fidp->path, &fidp->fs.xattr.name); + retval = v9fs_co_lremovexattr(pdu, &fidp->path, &fidp->fs.xattr.name); } free_out: v9fs_string_free(&fidp->fs.xattr.name); @@ -299,9 +448,41 @@ free_value: return retval; } -static int free_fid(V9fsState *s, int32_t fid) +static int free_fid(V9fsPDU *pdu, V9fsFidState *fidp) { int retval = 0; + + if (fidp->fid_type == P9_FID_FILE) { + /* If we reclaimed the fd no need to close */ + if (fidp->fs.fd != -1) { + retval = v9fs_co_close(pdu, &fidp->fs); + } + } else if (fidp->fid_type == P9_FID_DIR) { + if (fidp->fs.dir != NULL) { + retval = v9fs_co_closedir(pdu, &fidp->fs); + } + } else if (fidp->fid_type == P9_FID_XATTR) { + retval = v9fs_xattr_fid_clunk(pdu, fidp); + } + v9fs_path_free(&fidp->path); + g_free(fidp); + return retval; +} + +static void put_fid(V9fsPDU *pdu, V9fsFidState *fidp) +{ + BUG_ON(!fidp->ref); + fidp->ref--; + /* + * Don't free the fid if it is in reclaim list + */ + if (!fidp->ref && fidp->clunked) { + free_fid(pdu, fidp); + } +} + +static V9fsFidState *clunk_fid(V9fsState *s, int32_t fid) +{ V9fsFidState **fidpp, *fidp; for (fidpp = &s->fid_list; *fidpp; fidpp = &(*fidpp)->next) { @@ -309,24 +490,127 @@ static int free_fid(V9fsState *s, int32_t fid) break; } } - if (*fidpp == NULL) { - return -ENOENT; + return NULL; } - fidp = *fidpp; *fidpp = fidp->next; + fidp->clunked = 1; + return fidp; +} - if (fidp->fid_type == P9_FID_FILE) { - retval = v9fs_co_close(s, fidp); - } else if (fidp->fid_type == P9_FID_DIR) { - retval = v9fs_co_closedir(s, fidp); - } else if (fidp->fid_type == P9_FID_XATTR) { - retval = v9fs_xattr_fid_clunk(s, fidp); +void v9fs_reclaim_fd(V9fsPDU *pdu) +{ + int reclaim_count = 0; + V9fsState *s = pdu->s; + V9fsFidState *f, *reclaim_list = NULL; + + for (f = s->fid_list; f; f = f->next) { + /* + * Unlink fids cannot be reclaimed. Check + * for them and skip them. Also skip fids + * currently being operated on. + */ + if (f->ref || f->flags & FID_NON_RECLAIMABLE) { + continue; + } + /* + * if it is a recently referenced fid + * we leave the fid untouched and clear the + * reference bit. We come back to it later + * in the next iteration. (a simple LRU without + * moving list elements around) + */ + if (f->flags & FID_REFERENCED) { + f->flags &= ~FID_REFERENCED; + continue; + } + /* + * Add fids to reclaim list. + */ + if (f->fid_type == P9_FID_FILE) { + if (f->fs.fd != -1) { + /* + * Up the reference count so that + * a clunk request won't free this fid + */ + f->ref++; + f->rclm_lst = reclaim_list; + reclaim_list = f; + f->fs_reclaim.fd = f->fs.fd; + f->fs.fd = -1; + reclaim_count++; + } + } else if (f->fid_type == P9_FID_DIR) { + if (f->fs.dir != NULL) { + /* + * Up the reference count so that + * a clunk request won't free this fid + */ + f->ref++; + f->rclm_lst = reclaim_list; + reclaim_list = f; + f->fs_reclaim.dir = f->fs.dir; + f->fs.dir = NULL; + reclaim_count++; + } + } + if (reclaim_count >= open_fd_rc) { + break; + } + } + /* + * Now close the fid in reclaim list. Free them if they + * are already clunked. + */ + while (reclaim_list) { + f = reclaim_list; + reclaim_list = f->rclm_lst; + if (f->fid_type == P9_FID_FILE) { + v9fs_co_close(pdu, &f->fs_reclaim); + } else if (f->fid_type == P9_FID_DIR) { + v9fs_co_closedir(pdu, &f->fs_reclaim); + } + f->rclm_lst = NULL; + /* + * Now drop the fid reference, free it + * if clunked. + */ + put_fid(pdu, f); } - v9fs_string_free(&fidp->path); - g_free(fidp); - return retval; +} + +static int v9fs_mark_fids_unreclaim(V9fsPDU *pdu, V9fsPath *path) +{ + int err; + V9fsState *s = pdu->s; + V9fsFidState *fidp, head_fid; + + head_fid.next = s->fid_list; + for (fidp = s->fid_list; fidp; fidp = fidp->next) { + if (fidp->path.size != path->size) { + continue; + } + if (!memcmp(fidp->path.data, path->data, path->size)) { + /* Mark the fid non reclaimable. */ + fidp->flags |= FID_NON_RECLAIMABLE; + + /* reopen the file/dir if already closed */ + err = v9fs_reopen_fid(pdu, fidp); + if (err < 0) { + return -1; + } + /* + * Go back to head of fid list because + * the list could have got updated when + * switched to the worker thread + */ + if (err == 0) { + fidp = &head_fid; + } + } + } + return 0; } #define P9_QID_TYPE_DIR 0x80 @@ -359,6 +643,7 @@ static void stat_to_qid(const struct stat *stbuf, V9fsQID *qidp) { size_t size; + memset(&qidp->path, 0, sizeof(qidp->path)); size = MIN(sizeof(stbuf->st_ino), sizeof(qidp->path)); memcpy(&qidp->path, &stbuf->st_ino, size); qidp->version = stbuf->st_mtime ^ (stbuf->st_size << 8); @@ -371,12 +656,12 @@ static void stat_to_qid(const struct stat *stbuf, V9fsQID *qidp) } } -static int fid_to_qid(V9fsState *s, V9fsFidState *fidp, V9fsQID *qidp) +static int fid_to_qid(V9fsPDU *pdu, V9fsFidState *fidp, V9fsQID *qidp) { struct stat stbuf; int err; - err = v9fs_co_lstat(s, &fidp->path, &stbuf); + err = v9fs_co_lstat(pdu, &fidp->path, &stbuf); if (err < 0) { return err; } @@ -389,8 +674,9 @@ static V9fsPDU *alloc_pdu(V9fsState *s) V9fsPDU *pdu = NULL; if (!QLIST_EMPTY(&s->free_list)) { - pdu = QLIST_FIRST(&s->free_list); - QLIST_REMOVE(pdu, next); + pdu = QLIST_FIRST(&s->free_list); + QLIST_REMOVE(pdu, next); + QLIST_INSERT_HEAD(&s->active_list, pdu, next); } return pdu; } @@ -398,10 +684,14 @@ static V9fsPDU *alloc_pdu(V9fsState *s) static void free_pdu(V9fsState *s, V9fsPDU *pdu) { if (pdu) { - if (debug_9p_pdu) { - pprint_pdu(pdu); + /* + * Cancelled pdu are added back to the freelist + * by flush request . + */ + if (!pdu->cancelled) { + QLIST_REMOVE(pdu, next); + QLIST_INSERT_HEAD(&s->free_list, pdu, next); } - QLIST_INSERT_HEAD(&s->free_list, pdu, next); } } @@ -679,6 +969,7 @@ static void complete_pdu(V9fsState *s, V9fsPDU *pdu, ssize_t len) if (s->proto_version == V9FS_PROTO_2000L) { id = P9_RLERROR; } + trace_v9fs_rerror(pdu->tag, pdu->id, err); /* Trace ERROR */ } /* fill out the header */ @@ -694,6 +985,9 @@ static void complete_pdu(V9fsState *s, V9fsPDU *pdu, ssize_t len) /* FIXME: we should batch these completions */ virtio_notify(&s->vdev, s->vq); + /* Now wakeup anybody waiting in flush for this request */ + qemu_co_queue_next(&pdu->complete); + free_pdu(s, pdu); } @@ -813,7 +1107,7 @@ static uint32_t stat_to_v9mode(const struct stat *stbuf) return mode; } -static int stat_to_v9stat(V9fsState *s, V9fsString *name, +static int stat_to_v9stat(V9fsPDU *pdu, V9fsPath *name, const struct stat *stbuf, V9fsStat *v9stat) { @@ -839,7 +1133,7 @@ static int stat_to_v9stat(V9fsState *s, V9fsString *name, v9fs_string_null(&v9stat->extension); if (v9stat->mode & P9_STAT_MODE_SYMLINK) { - err = v9fs_co_readlink(s, name, &v9stat->extension); + err = v9fs_co_readlink(pdu, name, &v9stat->extension); if (err < 0) { return err; } @@ -965,13 +1259,21 @@ static void print_sg(struct iovec *sg, int cnt) printf("}\n"); } -static void v9fs_fix_path(V9fsString *dst, V9fsString *src, int len) +/* Will call this only for path name based fid */ +static void v9fs_fix_path(V9fsPath *dst, V9fsPath *src, int len) { - V9fsString str; - v9fs_string_init(&str); - v9fs_string_copy(&str, dst); - v9fs_string_sprintf(dst, "%s%s", src->data, str.data+len); - v9fs_string_free(&str); + V9fsPath str; + v9fs_path_init(&str); + v9fs_path_copy(&str, dst); + v9fs_string_sprintf((V9fsString *)dst, "%s%s", src->data, str.data+len); + v9fs_path_free(&str); + /* +1 to include terminating NULL */ + dst->size++; +} + +static inline bool is_ro_export(FsContext *ctx) +{ + return ctx->export_flags & V9FS_RDONLY; } static void v9fs_version(void *opaque) @@ -982,6 +1284,7 @@ static void v9fs_version(void *opaque) size_t offset = 7; pdu_unmarshal(pdu, offset, "ds", &s->msize, &version); + trace_v9fs_version(pdu->tag, pdu->id, s->msize, version.data); if (!strcmp(version.data, "9P2000.u")) { s->proto_version = V9FS_PROTO_2000U; @@ -992,6 +1295,8 @@ static void v9fs_version(void *opaque) } offset += pdu_marshal(pdu, offset, "ds", s->msize, &version); + trace_v9fs_version_return(pdu->tag, pdu->id, s->msize, version.data); + complete_pdu(s, pdu, offset); v9fs_string_free(&version); @@ -1010,23 +1315,33 @@ static void v9fs_attach(void *opaque) ssize_t err; pdu_unmarshal(pdu, offset, "ddssd", &fid, &afid, &uname, &aname, &n_uname); + trace_v9fs_attach(pdu->tag, pdu->id, fid, afid, uname.data, aname.data); fidp = alloc_fid(s, fid); if (fidp == NULL) { err = -EINVAL; - goto out; + goto out_nofid; } fidp->uid = n_uname; - v9fs_string_sprintf(&fidp->path, "%s", "/"); - err = fid_to_qid(s, fidp, &qid); + err = v9fs_co_name_to_path(pdu, NULL, "/", &fidp->path); + if (err < 0) { + err = -EINVAL; + clunk_fid(s, fid); + goto out; + } + err = fid_to_qid(pdu, fidp, &qid); if (err < 0) { err = -EINVAL; - free_fid(s, fid); + clunk_fid(s, fid); goto out; } offset += pdu_marshal(pdu, offset, "Q", &qid); err = offset; + trace_v9fs_attach_return(pdu->tag, pdu->id, + qid.type, qid.version, qid.path); out: + put_fid(pdu, fidp); +out_nofid: complete_pdu(s, pdu, err); v9fs_string_free(&uname); v9fs_string_free(&aname); @@ -1044,23 +1359,29 @@ static void v9fs_stat(void *opaque) V9fsState *s = pdu->s; pdu_unmarshal(pdu, offset, "d", &fid); - fidp = lookup_fid(s, fid); + trace_v9fs_stat(pdu->tag, pdu->id, fid); + + fidp = get_fid(pdu, fid); if (fidp == NULL) { err = -ENOENT; - goto out; + goto out_nofid; } - err = v9fs_co_lstat(s, &fidp->path, &stbuf); + err = v9fs_co_lstat(pdu, &fidp->path, &stbuf); if (err < 0) { goto out; } - err = stat_to_v9stat(s, &fidp->path, &stbuf, &v9stat); + err = stat_to_v9stat(pdu, &fidp->path, &stbuf, &v9stat); if (err < 0) { goto out; } offset += pdu_marshal(pdu, offset, "wS", 0, &v9stat); err = offset; + trace_v9fs_stat_return(pdu->tag, pdu->id, v9stat.mode, + v9stat.atime, v9stat.mtime, v9stat.length); v9fs_stat_free(&v9stat); out: + put_fid(pdu, fidp); +out_nofid: complete_pdu(s, pdu, err); } @@ -1077,24 +1398,39 @@ static void v9fs_getattr(void *opaque) V9fsState *s = pdu->s; pdu_unmarshal(pdu, offset, "dq", &fid, &request_mask); + trace_v9fs_getattr(pdu->tag, pdu->id, fid, request_mask); - fidp = lookup_fid(s, fid); + fidp = get_fid(pdu, fid); if (fidp == NULL) { retval = -ENOENT; - goto out; + goto out_nofid; } /* * Currently we only support BASIC fields in stat, so there is no * need to look at request_mask. */ - retval = v9fs_co_lstat(s, &fidp->path, &stbuf); + retval = v9fs_co_lstat(pdu, &fidp->path, &stbuf); if (retval < 0) { goto out; } stat_to_v9stat_dotl(s, &stbuf, &v9stat_dotl); + + /* fill st_gen if requested and supported by underlying fs */ + if (request_mask & P9_STATS_GEN) { + retval = v9fs_co_st_gen(pdu, &fidp->path, stbuf.st_mode, &v9stat_dotl); + if (retval < 0) { + goto out; + } + v9stat_dotl.st_result_mask |= P9_STATS_GEN; + } retval = offset; retval += pdu_marshal(pdu, offset, "A", &v9stat_dotl); + trace_v9fs_getattr_return(pdu->tag, pdu->id, v9stat_dotl.st_result_mask, + v9stat_dotl.st_mode, v9stat_dotl.st_uid, + v9stat_dotl.st_gid); out: + put_fid(pdu, fidp); +out_nofid: complete_pdu(s, pdu, retval); } @@ -1122,13 +1458,13 @@ static void v9fs_setattr(void *opaque) pdu_unmarshal(pdu, offset, "dI", &fid, &v9iattr); - fidp = lookup_fid(s, fid); + fidp = get_fid(pdu, fid); if (fidp == NULL) { err = -EINVAL; - goto out; + goto out_nofid; } if (v9iattr.valid & ATTR_MODE) { - err = v9fs_co_chmod(s, &fidp->path, v9iattr.mode); + err = v9fs_co_chmod(pdu, &fidp->path, v9iattr.mode); if (err < 0) { goto out; } @@ -1155,7 +1491,7 @@ static void v9fs_setattr(void *opaque) } else { times[1].tv_nsec = UTIME_OMIT; } - err = v9fs_co_utimensat(s, &fidp->path, times); + err = v9fs_co_utimensat(pdu, &fidp->path, times); if (err < 0) { goto out; } @@ -1173,20 +1509,22 @@ static void v9fs_setattr(void *opaque) if (!(v9iattr.valid & ATTR_GID)) { v9iattr.gid = -1; } - err = v9fs_co_chown(s, &fidp->path, v9iattr.uid, + err = v9fs_co_chown(pdu, &fidp->path, v9iattr.uid, v9iattr.gid); if (err < 0) { goto out; } } if (v9iattr.valid & (ATTR_SIZE)) { - err = v9fs_co_truncate(s, &fidp->path, v9iattr.size); + err = v9fs_co_truncate(pdu, &fidp->path, v9iattr.size); if (err < 0) { goto out; } } err = offset; out: + put_fid(pdu, fidp); +out_nofid: complete_pdu(s, pdu, err); } @@ -1206,52 +1544,60 @@ static void v9fs_walk(void *opaque) int name_idx; V9fsQID *qids = NULL; int i, err = 0; - V9fsString path; + V9fsPath dpath, path; uint16_t nwnames; struct stat stbuf; size_t offset = 7; int32_t fid, newfid; V9fsString *wnames = NULL; V9fsFidState *fidp; - V9fsFidState *newfidp; + V9fsFidState *newfidp = NULL;; V9fsPDU *pdu = opaque; V9fsState *s = pdu->s; offset += pdu_unmarshal(pdu, offset, "ddw", &fid, &newfid, &nwnames); + trace_v9fs_walk(pdu->tag, pdu->id, fid, newfid, nwnames); + if (nwnames && nwnames <= P9_MAXWELEM) { wnames = g_malloc0(sizeof(wnames[0]) * nwnames); qids = g_malloc0(sizeof(qids[0]) * nwnames); for (i = 0; i < nwnames; i++) { offset += pdu_unmarshal(pdu, offset, "s", &wnames[i]); } - } else if (nwnames > P9_MAXWELEM) { err = -EINVAL; - goto out; + goto out_nofid; } - fidp = lookup_fid(s, fid); + fidp = get_fid(pdu, fid); if (fidp == NULL) { err = -ENOENT; - goto out; + goto out_nofid; + } + v9fs_path_init(&dpath); + v9fs_path_init(&path); + /* + * Both dpath and path initially poin to fidp. + * Needed to handle request with nwnames == 0 + */ + v9fs_path_copy(&dpath, &fidp->path); + v9fs_path_copy(&path, &fidp->path); + for (name_idx = 0; name_idx < nwnames; name_idx++) { + err = v9fs_co_name_to_path(pdu, &dpath, wnames[name_idx].data, &path); + if (err < 0) { + goto out; + } + err = v9fs_co_lstat(pdu, &path, &stbuf); + if (err < 0) { + goto out; + } + stat_to_qid(&stbuf, &qids[name_idx]); + v9fs_path_copy(&dpath, &path); } if (fid == newfid) { BUG_ON(fidp->fid_type != P9_FID_NONE); - v9fs_string_init(&path); - for (name_idx = 0; name_idx < nwnames; name_idx++) { - v9fs_string_sprintf(&path, "%s/%s", - fidp->path.data, wnames[name_idx].data); - v9fs_string_copy(&fidp->path, &path); - - err = v9fs_co_lstat(s, &fidp->path, &stbuf); - if (err < 0) { - v9fs_string_free(&path); - goto out; - } - stat_to_qid(&stbuf, &qids[name_idx]); - } - v9fs_string_free(&path); + v9fs_path_copy(&fidp->path, &path); } else { newfidp = alloc_fid(s, newfid); if (newfidp == NULL) { @@ -1259,24 +1605,18 @@ static void v9fs_walk(void *opaque) goto out; } newfidp->uid = fidp->uid; - v9fs_string_init(&path); - v9fs_string_copy(&newfidp->path, &fidp->path); - for (name_idx = 0; name_idx < nwnames; name_idx++) { - v9fs_string_sprintf(&path, "%s/%s", newfidp->path.data, - wnames[name_idx].data); - v9fs_string_copy(&newfidp->path, &path); - err = v9fs_co_lstat(s, &newfidp->path, &stbuf); - if (err < 0) { - free_fid(s, newfidp->fid); - v9fs_string_free(&path); - goto out; - } - stat_to_qid(&stbuf, &qids[name_idx]); - } - v9fs_string_free(&path); + v9fs_path_copy(&newfidp->path, &path); } err = v9fs_walk_marshal(pdu, nwnames, qids); + trace_v9fs_walk_return(pdu->tag, pdu->id, nwnames, qids); out: + put_fid(pdu, fidp); + if (newfidp) { + put_fid(pdu, newfidp); + } + v9fs_path_free(&dpath); + v9fs_path_free(&path); +out_nofid: complete_pdu(s, pdu, err); if (nwnames && nwnames <= P9_MAXWELEM) { for (name_idx = 0; name_idx < nwnames; name_idx++) { @@ -1285,18 +1625,20 @@ out: g_free(wnames); g_free(qids); } + return; } -static int32_t get_iounit(V9fsState *s, V9fsString *name) +static int32_t get_iounit(V9fsPDU *pdu, V9fsPath *path) { struct statfs stbuf; int32_t iounit = 0; + V9fsState *s = pdu->s; /* * iounit should be multiples of f_bsize (host filesystem block size * and as well as less than (client msize - P9_IOHDRSZ)) */ - if (!v9fs_co_statfs(s, name, &stbuf)) { + if (!v9fs_co_statfs(pdu, path, &stbuf)) { iounit = stbuf.f_bsize; iounit *= (s->msize - P9_IOHDRSZ)/stbuf.f_bsize; } @@ -1309,10 +1651,10 @@ static int32_t get_iounit(V9fsState *s, V9fsString *name) static void v9fs_open(void *opaque) { int flags; - int iounit; int32_t fid; int32_t mode; V9fsQID qid; + int iounit = 0; ssize_t err = 0; size_t offset = 7; struct stat stbuf; @@ -1325,20 +1667,22 @@ static void v9fs_open(void *opaque) } else { pdu_unmarshal(pdu, offset, "db", &fid, &mode); } - fidp = lookup_fid(s, fid); + trace_v9fs_open(pdu->tag, pdu->id, fid, mode); + + fidp = get_fid(pdu, fid); if (fidp == NULL) { err = -ENOENT; - goto out; + goto out_nofid; } BUG_ON(fidp->fid_type != P9_FID_NONE); - err = v9fs_co_lstat(s, &fidp->path, &stbuf); + err = v9fs_co_lstat(pdu, &fidp->path, &stbuf); if (err < 0) { goto out; } stat_to_qid(&stbuf, &qid); if (S_ISDIR(stbuf.st_mode)) { - err = v9fs_co_opendir(s, fidp); + err = v9fs_co_opendir(pdu, fidp); if (err < 0) { goto out; } @@ -1347,23 +1691,40 @@ static void v9fs_open(void *opaque) err = offset; } else { if (s->proto_version == V9FS_PROTO_2000L) { - flags = mode; - flags &= ~(O_NOCTTY | O_ASYNC | O_CREAT); - /* Ignore direct disk access hint until the server supports it. */ - flags &= ~O_DIRECT; + flags = get_dotl_openflags(s, mode); } else { flags = omode_to_uflags(mode); } - err = v9fs_co_open(s, fidp, flags); + if (is_ro_export(&s->ctx)) { + if (mode & O_WRONLY || mode & O_RDWR || + mode & O_APPEND || mode & O_TRUNC) { + err = -EROFS; + goto out; + } + flags |= O_NOATIME; + } + err = v9fs_co_open(pdu, fidp, flags); if (err < 0) { goto out; } fidp->fid_type = P9_FID_FILE; - iounit = get_iounit(s, &fidp->path); + fidp->open_flags = flags; + if (flags & O_EXCL) { + /* + * We let the host file system do O_EXCL check + * We should not reclaim such fd + */ + fidp->flags |= FID_NON_RECLAIMABLE; + } + iounit = get_iounit(pdu, &fidp->path); offset += pdu_marshal(pdu, offset, "Qd", &qid, iounit); err = offset; } + trace_v9fs_open_return(pdu->tag, pdu->id, + qid.type, qid.version, qid.path, iounit); out: + put_fid(pdu, fidp); +out_nofid: complete_pdu(s, pdu, err); } @@ -1373,7 +1734,6 @@ static void v9fs_lcreate(void *opaque) gid_t gid; ssize_t err = 0; ssize_t offset = 7; - V9fsString fullname; V9fsString name; V9fsFidState *fidp; struct stat stbuf; @@ -1381,43 +1741,42 @@ static void v9fs_lcreate(void *opaque) int32_t iounit; V9fsPDU *pdu = opaque; - v9fs_string_init(&fullname); pdu_unmarshal(pdu, offset, "dsddd", &dfid, &name, &flags, &mode, &gid); + trace_v9fs_lcreate(pdu->tag, pdu->id, dfid, flags, mode, gid); - fidp = lookup_fid(pdu->s, dfid); + fidp = get_fid(pdu, dfid); if (fidp == NULL) { err = -ENOENT; - goto out; + goto out_nofid; } - v9fs_string_sprintf(&fullname, "%s/%s", fidp->path.data, name.data); - /* Ignore direct disk access hint until the server supports it. */ - flags &= ~O_DIRECT; - - err = v9fs_co_open2(pdu->s, fidp, fullname.data, gid, flags, mode); + flags = get_dotl_openflags(pdu->s, flags); + err = v9fs_co_open2(pdu, fidp, &name, gid, + flags | O_CREAT, mode, &stbuf); if (err < 0) { goto out; } fidp->fid_type = P9_FID_FILE; - iounit = get_iounit(pdu->s, &fullname); - - err = v9fs_co_lstat(pdu->s, &fullname, &stbuf); - if (err < 0) { - fidp->fid_type = P9_FID_NONE; - if (fidp->fs.fd > 0) { - close(fidp->fs.fd); - } - goto out; + fidp->open_flags = flags; + if (flags & O_EXCL) { + /* + * We let the host file system do O_EXCL check + * We should not reclaim such fd + */ + fidp->flags |= FID_NON_RECLAIMABLE; } - v9fs_string_copy(&fidp->path, &fullname); + iounit = get_iounit(pdu, &fidp->path); stat_to_qid(&stbuf, &qid); offset += pdu_marshal(pdu, offset, "Qd", &qid, iounit); err = offset; + trace_v9fs_lcreate_return(pdu->tag, pdu->id, + qid.type, qid.version, qid.path, iounit); out: + put_fid(pdu, fidp); +out_nofid: complete_pdu(pdu->s, pdu, err); v9fs_string_free(&name); - v9fs_string_free(&fullname); } static void v9fs_fsync(void *opaque) @@ -1431,16 +1790,19 @@ static void v9fs_fsync(void *opaque) V9fsState *s = pdu->s; pdu_unmarshal(pdu, offset, "dd", &fid, &datasync); - fidp = lookup_fid(s, fid); + trace_v9fs_fsync(pdu->tag, pdu->id, fid, datasync); + + fidp = get_fid(pdu, fid); if (fidp == NULL) { err = -ENOENT; - goto out; + goto out_nofid; } - err = v9fs_co_fsync(s, fidp, datasync); + err = v9fs_co_fsync(pdu, fidp, datasync); if (!err) { err = offset; } -out: + put_fid(pdu, fidp); +out_nofid: complete_pdu(s, pdu, err); } @@ -1449,16 +1811,27 @@ static void v9fs_clunk(void *opaque) int err; int32_t fid; size_t offset = 7; + V9fsFidState *fidp; V9fsPDU *pdu = opaque; V9fsState *s = pdu->s; pdu_unmarshal(pdu, offset, "d", &fid); - err = free_fid(s, fid); - if (err < 0) { - goto out; + trace_v9fs_clunk(pdu->tag, pdu->id, fid); + + fidp = clunk_fid(s, fid); + if (fidp == NULL) { + err = -ENOENT; + goto out_nofid; } + /* + * Bump the ref so that put_fid will + * free the fid. + */ + fidp->ref++; err = offset; -out: + + put_fid(pdu, fidp); +out_nofid: complete_pdu(s, pdu, err); } @@ -1486,10 +1859,10 @@ static int v9fs_xattr_read(V9fsState *s, V9fsPDU *pdu, return offset; } -static int v9fs_do_readdir_with_stat(V9fsState *s, V9fsPDU *pdu, +static int v9fs_do_readdir_with_stat(V9fsPDU *pdu, V9fsFidState *fidp, int32_t max_count) { - V9fsString name; + V9fsPath path; V9fsStat v9stat; int len, err = 0; int32_t count = 0; @@ -1498,7 +1871,7 @@ static int v9fs_do_readdir_with_stat(V9fsState *s, V9fsPDU *pdu, struct dirent *dent, *result; /* save the directory position */ - saved_dir_pos = v9fs_co_telldir(s, fidp); + saved_dir_pos = v9fs_co_telldir(pdu, fidp); if (saved_dir_pos < 0) { return saved_dir_pos; } @@ -1506,17 +1879,20 @@ static int v9fs_do_readdir_with_stat(V9fsState *s, V9fsPDU *pdu, dent = g_malloc(sizeof(struct dirent)); while (1) { - v9fs_string_init(&name); - err = v9fs_co_readdir_r(s, fidp, dent, &result); + v9fs_path_init(&path); + err = v9fs_co_readdir_r(pdu, fidp, dent, &result); if (err || !result) { break; } - v9fs_string_sprintf(&name, "%s/%s", fidp->path.data, dent->d_name); - err = v9fs_co_lstat(s, &name, &stbuf); + err = v9fs_co_name_to_path(pdu, &fidp->path, dent->d_name, &path); + if (err < 0) { + goto out; + } + err = v9fs_co_lstat(pdu, &path, &stbuf); if (err < 0) { goto out; } - err = stat_to_v9stat(s, &name, &stbuf, &v9stat); + err = stat_to_v9stat(pdu, &path, &stbuf, &v9stat); if (err < 0) { goto out; } @@ -1524,20 +1900,20 @@ static int v9fs_do_readdir_with_stat(V9fsState *s, V9fsPDU *pdu, len = pdu_marshal(pdu, 11 + count, "S", &v9stat); if ((len != (v9stat.size + 2)) || ((count + len) > max_count)) { /* Ran out of buffer. Set dir back to old position and return */ - v9fs_co_seekdir(s, fidp, saved_dir_pos); + v9fs_co_seekdir(pdu, fidp, saved_dir_pos); v9fs_stat_free(&v9stat); - v9fs_string_free(&name); + v9fs_path_free(&path); g_free(dent); return count; } count += len; v9fs_stat_free(&v9stat); - v9fs_string_free(&name); + v9fs_path_free(&path); saved_dir_pos = dent->d_off; } out: g_free(dent); - v9fs_string_free(&name); + v9fs_path_free(&path); if (err < 0) { return err; } @@ -1557,17 +1933,19 @@ static void v9fs_read(void *opaque) V9fsState *s = pdu->s; pdu_unmarshal(pdu, offset, "dqd", &fid, &off, &max_count); - fidp = lookup_fid(s, fid); + trace_v9fs_read(pdu->tag, pdu->id, fid, off, max_count); + + fidp = get_fid(pdu, fid); if (fidp == NULL) { err = -EINVAL; - goto out; + goto out_nofid; } if (fidp->fid_type == P9_FID_DIR) { if (off == 0) { - v9fs_co_rewinddir(s, fidp); + v9fs_co_rewinddir(pdu, fidp); } - count = v9fs_do_readdir_with_stat(s, pdu, fidp, max_count); + count = v9fs_do_readdir_with_stat(pdu, fidp, max_count); if (count < 0) { err = count; goto out; @@ -1590,12 +1968,12 @@ static void v9fs_read(void *opaque) } /* Loop in case of EINTR */ do { - len = v9fs_co_preadv(s, fidp, sg, cnt, off); + len = v9fs_co_preadv(pdu, fidp, sg, cnt, off); if (len >= 0) { off += len; count += len; } - } while (len == -EINTR); + } while (len == -EINTR && !pdu->cancelled); if (len < 0) { /* IO error return the error */ err = len; @@ -1611,7 +1989,10 @@ static void v9fs_read(void *opaque) } else { err = -EINVAL; } + trace_v9fs_read_return(pdu->tag, pdu->id, count, err); out: + put_fid(pdu, fidp); +out_nofid: complete_pdu(s, pdu, err); } @@ -1624,7 +2005,7 @@ static size_t v9fs_readdir_data_size(V9fsString *name) return 24 + v9fs_string_size(name); } -static int v9fs_do_readdir(V9fsState *s, V9fsPDU *pdu, +static int v9fs_do_readdir(V9fsPDU *pdu, V9fsFidState *fidp, int32_t max_count) { size_t size; @@ -1636,7 +2017,7 @@ static int v9fs_do_readdir(V9fsState *s, V9fsPDU *pdu, struct dirent *dent, *result; /* save the directory position */ - saved_dir_pos = v9fs_co_telldir(s, fidp); + saved_dir_pos = v9fs_co_telldir(pdu, fidp); if (saved_dir_pos < 0) { return saved_dir_pos; } @@ -1644,7 +2025,7 @@ static int v9fs_do_readdir(V9fsState *s, V9fsPDU *pdu, dent = g_malloc(sizeof(struct dirent)); while (1) { - err = v9fs_co_readdir_r(s, fidp, dent, &result); + err = v9fs_co_readdir_r(pdu, fidp, dent, &result); if (err || !result) { break; } @@ -1652,7 +2033,7 @@ static int v9fs_do_readdir(V9fsState *s, V9fsPDU *pdu, v9fs_string_sprintf(&name, "%s", dent->d_name); if ((count + v9fs_readdir_data_size(&name)) > max_count) { /* Ran out of buffer. Set dir back to old position and return */ - v9fs_co_seekdir(s, fidp, saved_dir_pos); + v9fs_co_seekdir(pdu, fidp, saved_dir_pos); v9fs_string_free(&name); g_free(dent); return count; @@ -1696,17 +2077,23 @@ static void v9fs_readdir(void *opaque) pdu_unmarshal(pdu, offset, "dqd", &fid, &initial_offset, &max_count); - fidp = lookup_fid(s, fid); - if (fidp == NULL || !fidp->fs.dir) { + trace_v9fs_readdir(pdu->tag, pdu->id, fid, initial_offset, max_count); + + fidp = get_fid(pdu, fid); + if (fidp == NULL) { + retval = -EINVAL; + goto out_nofid; + } + if (!fidp->fs.dir) { retval = -EINVAL; goto out; } if (initial_offset == 0) { - v9fs_co_rewinddir(s, fidp); + v9fs_co_rewinddir(pdu, fidp); } else { - v9fs_co_seekdir(s, fidp, initial_offset); + v9fs_co_seekdir(pdu, fidp, initial_offset); } - count = v9fs_do_readdir(s, pdu, fidp, max_count); + count = v9fs_do_readdir(pdu, fidp, max_count); if (count < 0) { retval = count; goto out; @@ -1714,7 +2101,10 @@ static void v9fs_readdir(void *opaque) retval = offset; retval += pdu_marshal(pdu, offset, "d", count); retval += count; + trace_v9fs_readdir_return(pdu->tag, pdu->id, count, retval); out: + put_fid(pdu, fidp); +out_nofid: complete_pdu(s, pdu, retval); } @@ -1779,10 +2169,12 @@ static void v9fs_write(void *opaque) V9fsState *s = pdu->s; pdu_unmarshal(pdu, offset, "dqdv", &fid, &off, &count, sg, &cnt); - fidp = lookup_fid(s, fid); + trace_v9fs_write(pdu->tag, pdu->id, fid, off, count, cnt); + + fidp = get_fid(pdu, fid); if (fidp == NULL) { err = -EINVAL; - goto out; + goto out_nofid; } if (fidp->fid_type == P9_FID_FILE) { if (fidp->fs.fd == -1) { @@ -1806,12 +2198,12 @@ static void v9fs_write(void *opaque) } /* Loop in case of EINTR */ do { - len = v9fs_co_pwritev(s, fidp, sg, cnt, off); + len = v9fs_co_pwritev(pdu, fidp, sg, cnt, off); if (len >= 0) { off += len; total += len; } - } while (len == -EINTR); + } while (len == -EINTR && !pdu->cancelled); if (len < 0) { /* IO error return the error */ err = len; @@ -1821,7 +2213,10 @@ static void v9fs_write(void *opaque) } while (total < count && len > 0); offset += pdu_marshal(pdu, offset, "d", total); err = offset; + trace_v9fs_write_return(pdu->tag, pdu->id, total, err); out: + put_fid(pdu, fidp); +out_nofid: complete_pdu(s, pdu, err); } @@ -1834,58 +2229,73 @@ static void v9fs_create(void *opaque) V9fsQID qid; int32_t perm; int8_t mode; + V9fsPath path; struct stat stbuf; V9fsString name; V9fsString extension; - V9fsString fullname; int iounit; V9fsPDU *pdu = opaque; - v9fs_string_init(&fullname); + v9fs_path_init(&path); pdu_unmarshal(pdu, offset, "dsdbs", &fid, &name, &perm, &mode, &extension); - fidp = lookup_fid(pdu->s, fid); + trace_v9fs_create(pdu->tag, pdu->id, fid, name.data, perm, mode); + + fidp = get_fid(pdu, fid); if (fidp == NULL) { err = -EINVAL; - goto out; - } - - v9fs_string_sprintf(&fullname, "%s/%s", fidp->path.data, name.data); - err = v9fs_co_lstat(pdu->s, &fullname, &stbuf); - if (!err) { - err = -EEXIST; - goto out; - } else if (err != -ENOENT) { - goto out; + goto out_nofid; } if (perm & P9_STAT_MODE_DIR) { - err = v9fs_co_mkdir(pdu->s, fullname.data, perm & 0777, - fidp->uid, -1); + err = v9fs_co_mkdir(pdu, fidp, &name, perm & 0777, + fidp->uid, -1, &stbuf); + if (err < 0) { + goto out; + } + err = v9fs_co_name_to_path(pdu, &fidp->path, name.data, &path); if (err < 0) { goto out; } - err = v9fs_co_opendir(pdu->s, fidp); + v9fs_path_copy(&fidp->path, &path); + err = v9fs_co_opendir(pdu, fidp); if (err < 0) { goto out; } fidp->fid_type = P9_FID_DIR; } else if (perm & P9_STAT_MODE_SYMLINK) { - err = v9fs_co_symlink(pdu->s, fidp, extension.data, - fullname.data, -1); + err = v9fs_co_symlink(pdu, fidp, &name, + extension.data, -1 , &stbuf); if (err < 0) { goto out; } + err = v9fs_co_name_to_path(pdu, &fidp->path, name.data, &path); + if (err < 0) { + goto out; + } + v9fs_path_copy(&fidp->path, &path); } else if (perm & P9_STAT_MODE_LINK) { - int32_t nfid = atoi(extension.data); - V9fsFidState *nfidp = lookup_fid(pdu->s, nfid); - if (nfidp == NULL) { + int32_t ofid = atoi(extension.data); + V9fsFidState *ofidp = get_fid(pdu, ofid); + if (ofidp == NULL) { err = -EINVAL; goto out; } - err = v9fs_co_link(pdu->s, &nfidp->path, &fullname); + err = v9fs_co_link(pdu, ofidp, fidp, &name); + put_fid(pdu, ofidp); + if (err < 0) { + goto out; + } + err = v9fs_co_name_to_path(pdu, &fidp->path, name.data, &path); + if (err < 0) { + fidp->fid_type = P9_FID_NONE; + goto out; + } + v9fs_path_copy(&fidp->path, &path); + err = v9fs_co_lstat(pdu, &fidp->path, &stbuf); if (err < 0) { + fidp->fid_type = P9_FID_NONE; goto out; } } else if (perm & P9_STAT_MODE_DEVICE) { @@ -1911,49 +2321,67 @@ static void v9fs_create(void *opaque) } nmode |= perm & 0777; - err = v9fs_co_mknod(pdu->s, &fullname, fidp->uid, -1, - makedev(major, minor), nmode); + err = v9fs_co_mknod(pdu, fidp, &name, fidp->uid, -1, + makedev(major, minor), nmode, &stbuf); if (err < 0) { goto out; } + err = v9fs_co_name_to_path(pdu, &fidp->path, name.data, &path); + if (err < 0) { + goto out; + } + v9fs_path_copy(&fidp->path, &path); } else if (perm & P9_STAT_MODE_NAMED_PIPE) { - err = v9fs_co_mknod(pdu->s, &fullname, fidp->uid, -1, - 0, S_IFIFO | (perm & 0777)); + err = v9fs_co_mknod(pdu, fidp, &name, fidp->uid, -1, + 0, S_IFIFO | (perm & 0777), &stbuf); + if (err < 0) { + goto out; + } + err = v9fs_co_name_to_path(pdu, &fidp->path, name.data, &path); if (err < 0) { goto out; } + v9fs_path_copy(&fidp->path, &path); } else if (perm & P9_STAT_MODE_SOCKET) { - err = v9fs_co_mknod(pdu->s, &fullname, fidp->uid, -1, - 0, S_IFSOCK | (perm & 0777)); + err = v9fs_co_mknod(pdu, fidp, &name, fidp->uid, -1, + 0, S_IFSOCK | (perm & 0777), &stbuf); if (err < 0) { goto out; } + err = v9fs_co_name_to_path(pdu, &fidp->path, name.data, &path); + if (err < 0) { + goto out; + } + v9fs_path_copy(&fidp->path, &path); } else { - err = v9fs_co_open2(pdu->s, fidp, fullname.data, -1, - omode_to_uflags(mode)|O_CREAT, perm); + err = v9fs_co_open2(pdu, fidp, &name, -1, + omode_to_uflags(mode)|O_CREAT, perm, &stbuf); if (err < 0) { goto out; } fidp->fid_type = P9_FID_FILE; - } - err = v9fs_co_lstat(pdu->s, &fullname, &stbuf); - if (err < 0) { - fidp->fid_type = P9_FID_NONE; - if (fidp->fs.fd) { - close(fidp->fs.fd); + fidp->open_flags = omode_to_uflags(mode); + if (fidp->open_flags & O_EXCL) { + /* + * We let the host file system do O_EXCL check + * We should not reclaim such fd + */ + fidp->flags |= FID_NON_RECLAIMABLE; } - goto out; } - iounit = get_iounit(pdu->s, &fidp->path); - v9fs_string_copy(&fidp->path, &fullname); + iounit = get_iounit(pdu, &fidp->path); stat_to_qid(&stbuf, &qid); offset += pdu_marshal(pdu, offset, "Qd", &qid, iounit); err = offset; + trace_v9fs_create_return(pdu->tag, pdu->id, + qid.type, qid.version, qid.path, iounit); out: + put_fid(pdu, fidp); +out_nofid: complete_pdu(pdu->s, pdu, err); v9fs_string_free(&name); v9fs_string_free(&extension); - v9fs_string_free(&fullname); + v9fs_path_free(&path); } static void v9fs_symlink(void *opaque) @@ -1961,7 +2389,6 @@ static void v9fs_symlink(void *opaque) V9fsPDU *pdu = opaque; V9fsString name; V9fsString symname; - V9fsString fullname; V9fsFidState *dfidp; V9fsQID qid; struct stat stbuf; @@ -1970,39 +2397,56 @@ static void v9fs_symlink(void *opaque) gid_t gid; size_t offset = 7; - v9fs_string_init(&fullname); pdu_unmarshal(pdu, offset, "dssd", &dfid, &name, &symname, &gid); + trace_v9fs_symlink(pdu->tag, pdu->id, dfid, name.data, symname.data, gid); - dfidp = lookup_fid(pdu->s, dfid); + dfidp = get_fid(pdu, dfid); if (dfidp == NULL) { err = -EINVAL; - goto out; + goto out_nofid; } - - v9fs_string_sprintf(&fullname, "%s/%s", dfidp->path.data, name.data); - err = v9fs_co_symlink(pdu->s, dfidp, symname.data, fullname.data, gid); - if (err < 0) { - goto out; - } - err = v9fs_co_lstat(pdu->s, &fullname, &stbuf); + err = v9fs_co_symlink(pdu, dfidp, &name, symname.data, gid, &stbuf); if (err < 0) { goto out; } stat_to_qid(&stbuf, &qid); offset += pdu_marshal(pdu, offset, "Q", &qid); err = offset; + trace_v9fs_symlink_return(pdu->tag, pdu->id, + qid.type, qid.version, qid.path); out: + put_fid(pdu, dfidp); +out_nofid: complete_pdu(pdu->s, pdu, err); v9fs_string_free(&name); v9fs_string_free(&symname); - v9fs_string_free(&fullname); } static void v9fs_flush(void *opaque) { + int16_t tag; + size_t offset = 7; + V9fsPDU *cancel_pdu; V9fsPDU *pdu = opaque; V9fsState *s = pdu->s; - /* A nop call with no return */ + + pdu_unmarshal(pdu, offset, "w", &tag); + trace_v9fs_flush(pdu->tag, pdu->id, tag); + + QLIST_FOREACH(cancel_pdu, &s->active_list, next) { + if (cancel_pdu->tag == tag) { + break; + } + } + if (cancel_pdu) { + cancel_pdu->cancelled = 1; + /* + * Wait for pdu to complete. + */ + qemu_co_queue_wait(&cancel_pdu->complete); + cancel_pdu->cancelled = 0; + free_pdu(pdu->s, cancel_pdu); + } complete_pdu(s, pdu, 7); return; } @@ -2013,38 +2457,36 @@ static void v9fs_link(void *opaque) V9fsState *s = pdu->s; int32_t dfid, oldfid; V9fsFidState *dfidp, *oldfidp; - V9fsString name, fullname; + V9fsString name;; size_t offset = 7; int err = 0; - v9fs_string_init(&fullname); - pdu_unmarshal(pdu, offset, "dds", &dfid, &oldfid, &name); + trace_v9fs_link(pdu->tag, pdu->id, dfid, oldfid, name.data); - dfidp = lookup_fid(s, dfid); + dfidp = get_fid(pdu, dfid); if (dfidp == NULL) { err = -ENOENT; - goto out; + goto out_nofid; } - oldfidp = lookup_fid(s, oldfid); + oldfidp = get_fid(pdu, oldfid); if (oldfidp == NULL) { err = -ENOENT; goto out; } - - v9fs_string_sprintf(&fullname, "%s/%s", dfidp->path.data, name.data); - err = v9fs_co_link(s, &oldfidp->path, &fullname); + err = v9fs_co_link(pdu, oldfidp, dfidp, &name); if (!err) { err = offset; } - v9fs_string_free(&fullname); - out: + put_fid(pdu, dfidp); +out_nofid: v9fs_string_free(&name); complete_pdu(s, pdu, err); } +/* Only works with path name based fid */ static void v9fs_remove(void *opaque) { int32_t fid; @@ -2054,44 +2496,103 @@ static void v9fs_remove(void *opaque) V9fsPDU *pdu = opaque; pdu_unmarshal(pdu, offset, "d", &fid); + trace_v9fs_remove(pdu->tag, pdu->id, fid); - fidp = lookup_fid(pdu->s, fid); + fidp = get_fid(pdu, fid); if (fidp == NULL) { err = -EINVAL; - goto out; + goto out_nofid; + } + /* if fs driver is not path based, return EOPNOTSUPP */ + if (!(pdu->s->ctx.export_flags & V9FS_PATHNAME_FSCONTEXT)) { + err = -EOPNOTSUPP; + goto out_err; } - err = v9fs_co_remove(pdu->s, &fidp->path); + /* + * IF the file is unlinked, we cannot reopen + * the file later. So don't reclaim fd + */ + err = v9fs_mark_fids_unreclaim(pdu, &fidp->path); + if (err < 0) { + goto out_err; + } + err = v9fs_co_remove(pdu, &fidp->path); if (!err) { err = offset; } - +out_err: /* For TREMOVE we need to clunk the fid even on failed remove */ - free_fid(pdu->s, fidp->fid); -out: + clunk_fid(pdu->s, fidp->fid); + put_fid(pdu, fidp); +out_nofid: complete_pdu(pdu->s, pdu, err); } -static int v9fs_complete_rename(V9fsState *s, V9fsFidState *fidp, +static void v9fs_unlinkat(void *opaque) +{ + int err = 0; + V9fsString name; + int32_t dfid, flags; + size_t offset = 7; + V9fsPath path; + V9fsFidState *dfidp; + V9fsPDU *pdu = opaque; + + pdu_unmarshal(pdu, offset, "dsd", &dfid, &name, &flags); + flags = dotl_to_at_flags(flags); + + dfidp = get_fid(pdu, dfid); + if (dfidp == NULL) { + err = -EINVAL; + goto out_nofid; + } + /* + * IF the file is unlinked, we cannot reopen + * the file later. So don't reclaim fd + */ + v9fs_path_init(&path); + err = v9fs_co_name_to_path(pdu, &dfidp->path, name.data, &path); + if (err < 0) { + goto out_err; + } + err = v9fs_mark_fids_unreclaim(pdu, &path); + if (err < 0) { + goto out_err; + } + err = v9fs_co_unlinkat(pdu, &dfidp->path, &name, flags); + if (!err) { + err = offset; + } +out_err: + put_fid(pdu, dfidp); + v9fs_path_free(&path); +out_nofid: + complete_pdu(pdu->s, pdu, err); + v9fs_string_free(&name); +} + + +/* Only works with path name based fid */ +static int v9fs_complete_rename(V9fsPDU *pdu, V9fsFidState *fidp, int32_t newdirfid, V9fsString *name) { char *end; int err = 0; + V9fsPath new_path; + V9fsFidState *tfidp; + V9fsState *s = pdu->s; + V9fsFidState *dirfidp = NULL; char *old_name, *new_name; + v9fs_path_init(&new_path); if (newdirfid != -1) { - V9fsFidState *dirfidp; - dirfidp = lookup_fid(s, newdirfid); + dirfidp = get_fid(pdu, newdirfid); if (dirfidp == NULL) { err = -ENOENT; - goto out; + goto out_nofid; } BUG_ON(dirfidp->fid_type != P9_FID_NONE); - - new_name = g_malloc0(dirfidp->path.size + name->size + 2); - - strcpy(new_name, dirfidp->path.data); - strcat(new_name, "/"); - strcat(new_name + dirfidp->path.size, name->data); + v9fs_co_name_to_path(pdu, &dirfidp->path, name->data, &new_path); } else { old_name = fidp->path.data; end = strrchr(old_name, '/'); @@ -2101,44 +2602,35 @@ static int v9fs_complete_rename(V9fsState *s, V9fsFidState *fidp, end = old_name; } 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); + g_free(new_name); } - - v9fs_string_free(name); - name->data = new_name; - name->size = strlen(new_name); - - if (strcmp(new_name, fidp->path.data) != 0) { - err = v9fs_co_rename(s, &fidp->path, name); - if (err < 0) { - goto out; - } - V9fsFidState *tfidp; - /* - * Fixup fid's pointing to the old name to - * start pointing to the new name - */ - for (tfidp = s->fid_list; tfidp; tfidp = tfidp->next) { - if (fidp == tfidp) { - /* - * we replace name of this fid towards the end - * so that our below strcmp will work - */ - continue; - } - if (v9fs_path_is_ancestor(&fidp->path, &tfidp->path)) { - /* replace the name */ - v9fs_fix_path(&tfidp->path, name, strlen(fidp->path.data)); - } + err = v9fs_co_rename(pdu, &fidp->path, &new_path); + if (err < 0) { + goto out; + } + /* + * Fixup fid's pointing to the old name to + * start pointing to the new name + */ + for (tfidp = s->fid_list; tfidp; tfidp = tfidp->next) { + if (v9fs_path_is_ancestor(&fidp->path, &tfidp->path)) { + /* replace the name */ + v9fs_fix_path(&tfidp->path, &new_path, strlen(fidp->path.data)); } - v9fs_string_copy(&fidp->path, name); } out: + if (dirfidp) { + put_fid(pdu, dirfidp); + } + v9fs_path_free(&new_path); +out_nofid: return err; } +/* Only works with path name based fid */ static void v9fs_rename(void *opaque) { int32_t fid; @@ -2152,22 +2644,125 @@ static void v9fs_rename(void *opaque) pdu_unmarshal(pdu, offset, "dds", &fid, &newdirfid, &name); - fidp = lookup_fid(s, fid); + fidp = get_fid(pdu, fid); if (fidp == NULL) { err = -ENOENT; - goto out; + goto out_nofid; } BUG_ON(fidp->fid_type != P9_FID_NONE); - - err = v9fs_complete_rename(s, fidp, newdirfid, &name); + /* if fs driver is not path based, return EOPNOTSUPP */ + if (!(pdu->s->ctx.export_flags & V9FS_PATHNAME_FSCONTEXT)) { + err = -EOPNOTSUPP; + goto out; + } + v9fs_path_write_lock(s); + err = v9fs_complete_rename(pdu, fidp, newdirfid, &name); + v9fs_path_unlock(s); if (!err) { err = offset; } out: + put_fid(pdu, fidp); +out_nofid: complete_pdu(s, pdu, err); v9fs_string_free(&name); } +static void 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; + + + 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); + + /* + * Fixup fid's pointing to the old name to + * start pointing to the new name + */ + for (tfidp = s->fid_list; tfidp; tfidp = tfidp->next) { + if (v9fs_path_is_ancestor(&oldpath, &tfidp->path)) { + /* replace the name */ + v9fs_fix_path(&tfidp->path, &newpath, strlen(oldpath.data)); + } + } + v9fs_path_free(&oldpath); + v9fs_path_free(&newpath); +} + +static int v9fs_complete_renameat(V9fsPDU *pdu, int32_t olddirfid, + V9fsString *old_name, int32_t newdirfid, + V9fsString *new_name) +{ + int err = 0; + V9fsState *s = pdu->s; + V9fsFidState *newdirfidp = NULL, *olddirfidp = NULL; + + olddirfidp = get_fid(pdu, olddirfid); + if (olddirfidp == NULL) { + err = -ENOENT; + goto out; + } + if (newdirfid != -1) { + newdirfidp = get_fid(pdu, newdirfid); + if (newdirfidp == NULL) { + err = -ENOENT; + goto out; + } + } else { + newdirfidp = get_fid(pdu, olddirfid); + } + + err = v9fs_co_renameat(pdu, &olddirfidp->path, old_name, + &newdirfidp->path, new_name); + if (err < 0) { + goto out; + } + 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); + } +out: + if (olddirfidp) { + put_fid(pdu, olddirfidp); + } + if (newdirfidp) { + put_fid(pdu, newdirfidp); + } + return err; +} + +static void v9fs_renameat(void *opaque) +{ + ssize_t err = 0; + size_t offset = 7; + V9fsPDU *pdu = opaque; + V9fsState *s = pdu->s; + int32_t olddirfid, newdirfid; + V9fsString old_name, new_name; + + pdu_unmarshal(pdu, offset, "dsds", &olddirfid, + &old_name, &newdirfid, &new_name); + + v9fs_path_write_lock(s); + err = v9fs_complete_renameat(pdu, olddirfid, + &old_name, newdirfid, &new_name); + v9fs_path_unlock(s); + if (!err) { + err = offset; + } + complete_pdu(s, pdu, err); + v9fs_string_free(&old_name); + v9fs_string_free(&new_name); +} + static void v9fs_wstat(void *opaque) { int32_t fid; @@ -2181,19 +2776,22 @@ static void v9fs_wstat(void *opaque) V9fsState *s = pdu->s; pdu_unmarshal(pdu, offset, "dwS", &fid, &unused, &v9stat); - fidp = lookup_fid(s, fid); + trace_v9fs_wstat(pdu->tag, pdu->id, fid, + v9stat.mode, v9stat.atime, v9stat.mtime); + + fidp = get_fid(pdu, fid); if (fidp == NULL) { err = -EINVAL; - goto out; + goto out_nofid; } /* do we need to sync the file? */ if (donttouch_stat(&v9stat)) { - err = v9fs_co_fsync(s, fidp, 0); + err = v9fs_co_fsync(pdu, fidp, 0); goto out; } if (v9stat.mode != -1) { uint32_t v9_mode; - err = v9fs_co_lstat(s, &fidp->path, &stbuf); + err = v9fs_co_lstat(pdu, &fidp->path, &stbuf); if (err < 0) { goto out; } @@ -2204,7 +2802,7 @@ static void v9fs_wstat(void *opaque) err = -EIO; goto out; } - err = v9fs_co_chmod(s, &fidp->path, + err = v9fs_co_chmod(pdu, &fidp->path, v9mode_to_mode(v9stat.mode, &v9stat.extension)); if (err < 0) { @@ -2225,31 +2823,33 @@ static void v9fs_wstat(void *opaque) } else { times[1].tv_nsec = UTIME_OMIT; } - err = v9fs_co_utimensat(s, &fidp->path, times); + err = v9fs_co_utimensat(pdu, &fidp->path, times); if (err < 0) { goto out; } } if (v9stat.n_gid != -1 || v9stat.n_uid != -1) { - err = v9fs_co_chown(s, &fidp->path, v9stat.n_uid, v9stat.n_gid); + err = v9fs_co_chown(pdu, &fidp->path, v9stat.n_uid, v9stat.n_gid); if (err < 0) { goto out; } } if (v9stat.name.size != 0) { - err = v9fs_complete_rename(s, fidp, -1, &v9stat.name); + err = v9fs_complete_rename(pdu, fidp, -1, &v9stat.name); if (err < 0) { goto out; } } if (v9stat.length != -1) { - err = v9fs_co_truncate(s, &fidp->path, v9stat.length); + err = v9fs_co_truncate(pdu, &fidp->path, v9stat.length); if (err < 0) { goto out; } } err = offset; out: + put_fid(pdu, fidp); +out_nofid: v9fs_stat_free(&v9stat); complete_pdu(s, pdu, err); } @@ -2310,18 +2910,20 @@ static void v9fs_statfs(void *opaque) V9fsState *s = pdu->s; pdu_unmarshal(pdu, offset, "d", &fid); - fidp = lookup_fid(s, fid); + fidp = get_fid(pdu, fid); if (fidp == NULL) { retval = -ENOENT; - goto out; + goto out_nofid; } - retval = v9fs_co_statfs(s, &fidp->path, &stbuf); + retval = v9fs_co_statfs(pdu, &fidp->path, &stbuf); if (retval < 0) { goto out; } retval = offset; retval += v9fs_fill_statfs(s, pdu, &stbuf); out: + put_fid(pdu, fidp); +out_nofid: complete_pdu(s, pdu, retval); return; } @@ -2338,36 +2940,33 @@ static void v9fs_mknod(void *opaque) size_t offset = 7; V9fsString name; struct stat stbuf; - V9fsString fullname; V9fsFidState *fidp; V9fsPDU *pdu = opaque; V9fsState *s = pdu->s; - v9fs_string_init(&fullname); pdu_unmarshal(pdu, offset, "dsdddd", &fid, &name, &mode, &major, &minor, &gid); + trace_v9fs_mknod(pdu->tag, pdu->id, fid, mode, major, minor); - fidp = lookup_fid(s, fid); + fidp = get_fid(pdu, fid); if (fidp == NULL) { err = -ENOENT; - goto out; + goto out_nofid; } - v9fs_string_sprintf(&fullname, "%s/%s", fidp->path.data, name.data); - err = v9fs_co_mknod(s, &fullname, fidp->uid, gid, - makedev(major, minor), mode); - if (err < 0) { - goto out; - } - err = v9fs_co_lstat(s, &fullname, &stbuf); + err = v9fs_co_mknod(pdu, fidp, &name, fidp->uid, gid, + makedev(major, minor), mode, &stbuf); if (err < 0) { goto out; } stat_to_qid(&stbuf, &qid); err = offset; err += pdu_marshal(pdu, offset, "Q", &qid); + trace_v9fs_mknod_return(pdu->tag, pdu->id, + qid.type, qid.version, qid.path); out: + put_fid(pdu, fidp); +out_nofid: complete_pdu(s, pdu, err); - v9fs_string_free(&fullname); v9fs_string_free(&name); } @@ -2394,27 +2993,35 @@ static void v9fs_lock(void *opaque) pdu_unmarshal(pdu, offset, "dbdqqds", &fid, &flock->type, &flock->flags, &flock->start, &flock->length, &flock->proc_id, &flock->client_id); + + trace_v9fs_lock(pdu->tag, pdu->id, fid, + flock->type, flock->start, flock->length); + status = P9_LOCK_ERROR; /* We support only block flag now (that too ignored currently) */ if (flock->flags & ~P9_LOCK_FLAGS_BLOCK) { err = -EINVAL; - goto out; + goto out_nofid; } - fidp = lookup_fid(s, fid); + fidp = get_fid(pdu, fid); if (fidp == NULL) { err = -ENOENT; - goto out; + goto out_nofid; } - err = v9fs_co_fstat(s, fidp->fs.fd, &stbuf); + err = v9fs_co_fstat(pdu, fidp, &stbuf); if (err < 0) { goto out; } status = P9_LOCK_SUCCESS; out: + put_fid(pdu, fidp); +out_nofid: err = offset; err += pdu_marshal(pdu, offset, "b", status); + trace_v9fs_lock_return(pdu->tag, pdu->id, status); complete_pdu(s, pdu, err); + v9fs_string_free(&flock->client_id); g_free(flock); } @@ -2437,22 +3044,30 @@ static void v9fs_getlock(void *opaque) &glock->start, &glock->length, &glock->proc_id, &glock->client_id); - fidp = lookup_fid(s, fid); + trace_v9fs_getlock(pdu->tag, pdu->id, fid, + glock->type, glock->start, glock->length); + + fidp = get_fid(pdu, fid); if (fidp == NULL) { err = -ENOENT; - goto out; + goto out_nofid; } - err = v9fs_co_fstat(s, fidp->fs.fd, &stbuf); + err = v9fs_co_fstat(pdu, fidp, &stbuf); if (err < 0) { goto out; } - glock->type = F_UNLCK; + glock->type = P9_LOCK_TYPE_UNLCK; offset += pdu_marshal(pdu, offset, "bqqds", glock->type, glock->start, glock->length, glock->proc_id, &glock->client_id); err = offset; + trace_v9fs_getlock_return(pdu->tag, pdu->id, glock->type, glock->start, + glock->length, glock->proc_id); out: + put_fid(pdu, fidp); +out_nofid: complete_pdu(s, pdu, err); + v9fs_string_free(&glock->client_id); g_free(glock); } @@ -2462,36 +3077,35 @@ static void v9fs_mkdir(void *opaque) size_t offset = 7; int32_t fid; struct stat stbuf; - V9fsString name, fullname; V9fsQID qid; + V9fsString name; V9fsFidState *fidp; gid_t gid; int mode; int err = 0; - v9fs_string_init(&fullname); pdu_unmarshal(pdu, offset, "dsdd", &fid, &name, &mode, &gid); - fidp = lookup_fid(pdu->s, fid); + trace_v9fs_mkdir(pdu->tag, pdu->id, fid, name.data, mode, gid); + + fidp = get_fid(pdu, fid); if (fidp == NULL) { err = -ENOENT; - goto out; + goto out_nofid; } - v9fs_string_sprintf(&fullname, "%s/%s", fidp->path.data, name.data); - err = v9fs_co_mkdir(pdu->s, fullname.data, mode, fidp->uid, gid); - if (err < 0) { - goto out; - } - err = v9fs_co_lstat(pdu->s, &fullname, &stbuf); + err = v9fs_co_mkdir(pdu, fidp, &name, mode, fidp->uid, gid, &stbuf); if (err < 0) { goto out; } stat_to_qid(&stbuf, &qid); offset += pdu_marshal(pdu, offset, "Q", &qid); err = offset; + trace_v9fs_mkdir_return(pdu->tag, pdu->id, + qid.type, qid.version, qid.path, err); out: + put_fid(pdu, fidp); +out_nofid: complete_pdu(pdu->s, pdu, err); - v9fs_string_free(&fullname); v9fs_string_free(&name); } @@ -2503,30 +3117,32 @@ static void v9fs_xattrwalk(void *opaque) size_t offset = 7; int32_t fid, newfid; V9fsFidState *file_fidp; - V9fsFidState *xattr_fidp; + V9fsFidState *xattr_fidp = NULL; V9fsPDU *pdu = opaque; V9fsState *s = pdu->s; pdu_unmarshal(pdu, offset, "dds", &fid, &newfid, &name); - file_fidp = lookup_fid(s, fid); + trace_v9fs_xattrwalk(pdu->tag, pdu->id, fid, newfid, name.data); + + file_fidp = get_fid(pdu, fid); if (file_fidp == NULL) { err = -ENOENT; - goto out; + goto out_nofid; } xattr_fidp = alloc_fid(s, newfid); if (xattr_fidp == NULL) { err = -EINVAL; goto out; } - v9fs_string_copy(&xattr_fidp->path, &file_fidp->path); + v9fs_path_copy(&xattr_fidp->path, &file_fidp->path); if (name.data[0] == 0) { /* * listxattr request. Get the size first */ - size = v9fs_co_llistxattr(s, &xattr_fidp->path, NULL, 0); + size = v9fs_co_llistxattr(pdu, &xattr_fidp->path, NULL, 0); if (size < 0) { err = size; - free_fid(s, xattr_fidp->fid); + clunk_fid(s, xattr_fidp->fid); goto out; } /* @@ -2537,11 +3153,11 @@ static void v9fs_xattrwalk(void *opaque) xattr_fidp->fs.xattr.copied_len = -1; if (size) { xattr_fidp->fs.xattr.value = g_malloc(size); - err = v9fs_co_llistxattr(s, &xattr_fidp->path, + err = v9fs_co_llistxattr(pdu, &xattr_fidp->path, xattr_fidp->fs.xattr.value, xattr_fidp->fs.xattr.len); if (err < 0) { - free_fid(s, xattr_fidp->fid); + clunk_fid(s, xattr_fidp->fid); goto out; } } @@ -2552,11 +3168,11 @@ static void v9fs_xattrwalk(void *opaque) * specific xattr fid. We check for xattr * presence also collect the xattr size */ - size = v9fs_co_lgetxattr(s, &xattr_fidp->path, + size = v9fs_co_lgetxattr(pdu, &xattr_fidp->path, &name, NULL, 0); if (size < 0) { err = size; - free_fid(s, xattr_fidp->fid); + clunk_fid(s, xattr_fidp->fid); goto out; } /* @@ -2567,18 +3183,24 @@ static void v9fs_xattrwalk(void *opaque) xattr_fidp->fs.xattr.copied_len = -1; if (size) { xattr_fidp->fs.xattr.value = g_malloc(size); - err = v9fs_co_lgetxattr(s, &xattr_fidp->path, + err = v9fs_co_lgetxattr(pdu, &xattr_fidp->path, &name, xattr_fidp->fs.xattr.value, xattr_fidp->fs.xattr.len); if (err < 0) { - free_fid(s, xattr_fidp->fid); + clunk_fid(s, xattr_fidp->fid); goto out; } } offset += pdu_marshal(pdu, offset, "q", size); err = offset; } + trace_v9fs_xattrwalk_return(pdu->tag, pdu->id, size); out: + put_fid(pdu, file_fidp); + if (xattr_fidp) { + put_fid(pdu, xattr_fidp); + } +out_nofid: complete_pdu(s, pdu, err); v9fs_string_free(&name); } @@ -2598,11 +3220,12 @@ static void v9fs_xattrcreate(void *opaque) pdu_unmarshal(pdu, offset, "dsqd", &fid, &name, &size, &flags); + trace_v9fs_xattrcreate(pdu->tag, pdu->id, fid, name.data, size, flags); - file_fidp = lookup_fid(s, fid); + file_fidp = get_fid(pdu, fid); if (file_fidp == NULL) { err = -EINVAL; - goto out; + goto out_nofid; } /* Make the file fid point to xattr */ xattr_fidp = file_fidp; @@ -2618,7 +3241,8 @@ static void v9fs_xattrcreate(void *opaque) xattr_fidp->fs.xattr.value = NULL; } err = offset; -out: + put_fid(pdu, file_fidp); +out_nofid: complete_pdu(s, pdu, err); v9fs_string_free(&name); } @@ -2633,21 +3257,25 @@ static void v9fs_readlink(void *opaque) V9fsFidState *fidp; pdu_unmarshal(pdu, offset, "d", &fid); - fidp = lookup_fid(pdu->s, fid); + trace_v9fs_readlink(pdu->tag, pdu->id, fid); + fidp = get_fid(pdu, fid); if (fidp == NULL) { err = -ENOENT; - goto out; + goto out_nofid; } v9fs_string_init(&target); - err = v9fs_co_readlink(pdu->s, &fidp->path, &target); + err = v9fs_co_readlink(pdu, &fidp->path, &target); if (err < 0) { goto out; } offset += pdu_marshal(pdu, offset, "s", &target); err = offset; + trace_v9fs_readlink_return(pdu->tag, pdu->id, target.data); v9fs_string_free(&target); out: + put_fid(pdu, fidp); +out_nofid: complete_pdu(pdu->s, pdu, err); } @@ -2662,7 +3290,9 @@ static CoroutineEntry *pdu_co_handlers[] = { [P9_TRENAME] = v9fs_rename, [P9_TLOCK] = v9fs_lock, [P9_TGETLOCK] = v9fs_getlock, + [P9_TRENAMEAT] = v9fs_renameat, [P9_TREADLINK] = v9fs_readlink, + [P9_TUNLINKAT] = v9fs_unlinkat, [P9_TMKDIR] = v9fs_mkdir, [P9_TVERSION] = v9fs_version, [P9_TLOPEN] = v9fs_open, @@ -2692,20 +3322,54 @@ static void v9fs_op_not_supp(void *opaque) complete_pdu(pdu->s, pdu, -EOPNOTSUPP); } +static void v9fs_fs_ro(void *opaque) +{ + V9fsPDU *pdu = opaque; + complete_pdu(pdu->s, pdu, -EROFS); +} + +static inline bool is_read_only_op(V9fsPDU *pdu) +{ + switch (pdu->id) { + case P9_TREADDIR: + case P9_TSTATFS: + case P9_TGETATTR: + case P9_TXATTRWALK: + case P9_TLOCK: + case P9_TGETLOCK: + case P9_TREADLINK: + case P9_TVERSION: + case P9_TLOPEN: + case P9_TATTACH: + case P9_TSTAT: + case P9_TWALK: + case P9_TCLUNK: + case P9_TFSYNC: + case P9_TOPEN: + case P9_TREAD: + case P9_TAUTH: + case P9_TFLUSH: + return 1; + default: + return 0; + } +} + static void submit_pdu(V9fsState *s, V9fsPDU *pdu) { Coroutine *co; CoroutineEntry *handler; - if (debug_9p_pdu) { - pprint_pdu(pdu); - } if (pdu->id >= ARRAY_SIZE(pdu_co_handlers) || (pdu_co_handlers[pdu->id] == NULL)) { handler = v9fs_op_not_supp; } else { handler = pdu_co_handlers[pdu->id]; } + + if (is_ro_export(&s->ctx) && !is_read_only_op(pdu)) { + handler = v9fs_fs_ro; + } co = qemu_coroutine_create(handler); qemu_coroutine_enter(co, pdu); } @@ -2728,7 +3392,19 @@ void handle_9p_output(VirtIODevice *vdev, VirtQueue *vq) memcpy(&pdu->size, ptr, 4); pdu->id = ptr[4]; memcpy(&pdu->tag, ptr + 5, 2); + qemu_co_queue_init(&pdu->complete); submit_pdu(s, pdu); } free_pdu(s, pdu); } + +void virtio_9p_set_fd_limit(void) +{ + struct rlimit rlim; + if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) { + fprintf(stderr, "Failed to get the resource limit\n"); + exit(1); + } + open_fd_hw = rlim.rlim_cur - MIN(400, rlim.rlim_cur/3); + open_fd_rc = rlim.rlim_cur/2; +}