From a25ab8432dcf15d7d43adbc429a7cd9b493b3d91 Mon Sep 17 00:00:00 2001 From: "Luke T. Shumaker" Date: Tue, 4 Feb 2025 08:57:14 -0700 Subject: lib9p: srv: Use a separate pathmap --- lib9p/srv.c | 387 ++++++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 258 insertions(+), 129 deletions(-) (limited to 'lib9p/srv.c') diff --git a/lib9p/srv.c b/lib9p/srv.c index 06117b8..47dd78d 100644 --- a/lib9p/srv.c +++ b/lib9p/srv.c @@ -29,6 +29,10 @@ #ifndef CONFIG_9P_SRV_MAX_REQS #error config.h must define CONFIG_9P_SRV_MAX_REQS #endif +#ifndef CONFIG_9P_SRV_MAX_DEPTH + /* 1=just the root dir, 2=just files in the root dir, 3=1 subdir, ... */ + #error config.h must define CONFIG_9P_SRV_MAX_DEPTH +#endif /* context ********************************************************************/ @@ -47,15 +51,36 @@ int lib9p_srv_acknowledge_flush(struct lib9p_srv_ctx *ctx) { /* structs ********************************************************************/ +typedef typeof( ((struct lib9p_qid){}).path ) srv_path_t; + +struct srv_pathinfo { + implements_lib9p_srv_file *file; + srv_path_t parent_dir; + + /* References from other srv_pathinfos (via .parent_dir) or + * from FIDs. */ + unsigned int gc_refcount; + /* References from fids with FIDFLAG_OPEN_R/FIDFLAG_OPEN_W. */ + unsigned int rd_refcount; + unsigned int wr_refcount; +}; + +#define NAME pathmap +#define KEY_T srv_path_t +#define VAL_T struct srv_pathinfo +/* ( naive ) + ( working space for walk() ) */ +#define CAP ( (CONFIG_9P_SRV_MAX_FIDS*CONFIG_9P_SRV_MAX_DEPTH) + (CONFIG_9P_SRV_MAX_REQS*2) ) +#include "map.h" + #define FIDFLAG_OPEN_R (1<<0) #define FIDFLAG_OPEN_W (1<<1) #define FIDFLAG_RCLOSE (1<<2) -#define FIDFLAG_ISDIR (1<<3) #define FIDFLAG_OPEN (FIDFLAG_OPEN_R|FIDFLAG_OPEN_W) struct _srv_fidinfo { - implements_lib9p_srv_file *file; + srv_path_t path; uint8_t flags; + uint32_t iounit; size_t dir_idx; uint32_t dir_off; }; @@ -98,6 +123,7 @@ struct _srv_sess { /* mutable */ bool initialized; bool closing; + struct pathmap paths; struct reqmap reqs; struct fidmap fids; }; @@ -425,7 +451,7 @@ static void handle_message(struct _lib9p_srv_req *ctx) { resp->tag = req->tag; \ } while (0) -static inline bool util_check_perm(struct lib9p_srv_ctx *ctx, struct lib9p_stat *stat, uint8_t action) { +static inline bool srv_util_check_perm(struct _lib9p_srv_req *ctx, struct lib9p_stat *stat, uint8_t action) { assert(ctx); assert(stat); assert(action); @@ -436,17 +462,102 @@ static inline bool util_check_perm(struct lib9p_srv_ctx *ctx, struct lib9p_stat return mode & action; } -static inline bool util_release(struct lib9p_srv_ctx *ctx, implements_lib9p_srv_file *file) { +/** + * Ensures that `file` is saved into the pathmap, and increments the + * gc_refcount by 1 (for presumptive insertion into the fidmap). + * parent_path's gc_refcount is also incremented as appropriate. + * + * Returns a pointer to the stored pathinfo. + */ +static inline struct srv_pathinfo *srv_util_pathsave(struct _lib9p_srv_req *ctx, + implements_lib9p_srv_file *file, + srv_path_t parent_path) { + assert(ctx); assert(file); - file->_refcount--; - if (file->_refcount == 0) { - if (file->_parent_dir != file) - util_release(ctx, file->_parent_dir); - VCALL(file, free, ctx); + + struct lib9p_qid qid = VCALL(file, qid); + struct srv_pathinfo *pathinfo = pathmap_load(&ctx->parent_sess->paths, qid.path); + if (pathinfo) + assert(pathinfo->file == file); + else { + pathinfo = pathmap_store(&ctx->parent_sess->paths, qid.path, + (struct srv_pathinfo){ + .file = file, + .parent_dir = parent_path, + .gc_refcount = 0, + .rd_refcount = 0, + .wr_refcount = 0, + }); + assert(pathinfo); + if (parent_path != qid.path) { + struct srv_pathinfo *parent = pathmap_load(&ctx->parent_sess->paths, parent_path); + assert(parent); + parent->gc_refcount++; + } } - return lib9p_ctx_has_error(&ctx->basectx); + pathinfo->gc_refcount++; + return pathinfo; } +/** + * Decrement the path's gc_refcount, and trigger garbage collection as + * appropriate. + */ +static inline void srv_util_pathfree(struct _lib9p_srv_req *ctx, srv_path_t path) { + assert(ctx); + + struct srv_pathinfo *pathinfo = pathmap_load(&ctx->parent_sess->paths, path); + assert(pathinfo); + pathinfo->gc_refcount--; + if (pathinfo->gc_refcount == 0) { + if (pathinfo->parent_dir != path) + srv_util_pathfree(ctx, pathinfo->parent_dir); + VCALL(pathinfo->file, free); + pathmap_del(&ctx->parent_sess->paths, path); + } +} + +static inline bool srv_util_pathisdir(struct srv_pathinfo *pathinfo) { + assert(pathinfo); + return VCALL(pathinfo->file, qid).type & LIB9P_QT_DIR; +} + +/** + * Store fid as pointing to pathinfo. Assumes that + * pathinfo->gc_refcount has already been incremented; does *not* + * decrement it on failure. + */ +static inline struct _srv_fidinfo *srv_util_fidsave(struct _lib9p_srv_req *ctx, lib9p_fid_t fid, struct srv_pathinfo *pathinfo, bool overwrite) { + assert(ctx); + assert(fid != LIB9P_FID_NOFID); + assert(pathinfo); + + struct lib9p_qid qid = VCALL(pathinfo->file, qid); + + struct _srv_fidinfo *fidinfo = fidmap_load(&ctx->parent_sess->fids, fid); + if (fidinfo) { + if (overwrite) { + srv_util_pathfree(ctx, fidinfo->path); + } else { + lib9p_error(&ctx->ctx.basectx, + LINUX_EBADF, "FID already in use"); + return NULL; + } + } else { + fidinfo = fidmap_store(&ctx->parent_sess->fids, fid, (struct _srv_fidinfo){}); + if (!fidinfo) { + lib9p_error(&ctx->ctx.basectx, + LINUX_EMFILE, "too many open files"); + return NULL; + } + } + *fidinfo = (struct _srv_fidinfo){ + .path = qid.path, + }; + return fidinfo; +} + + static void handle_Tversion(struct _lib9p_srv_req *ctx, struct lib9p_msg_Tversion *req, struct lib9p_msg_Rversion *resp) { @@ -575,7 +686,7 @@ static void handle_Tattach(struct _lib9p_srv_req *ctx, return; */ lib9p_error(&ctx->ctx.basectx, - LINUX_EOPNOTSUPP, "TODO: auth not implemented"); + LINUX_EOPNOTSUPP, "TODO: auth not (yet?) implemented"); return; } else { if (req->afid != LIB9P_FID_NOFID) { @@ -585,41 +696,31 @@ static void handle_Tattach(struct _lib9p_srv_req *ctx, } } - if (fidmap_load(&ctx->parent_sess->fids, req->fid)) { - lib9p_error(&ctx->ctx.basectx, - LINUX_EBADF, "FID already in use"); - return; + if (req->fid == LIB9P_FID_NOFID) { + lib9p_error(&ctx->ctx.basectx, + LINUX_EBADF, "cannot assign to NOFID"); + return; } - implements_lib9p_srv_file *rootdir = srv->rootdir(&ctx->ctx, req->aname); - assert((rootdir == NULL) == lib9p_ctx_has_error(&ctx->ctx.basectx)); + /* 1. File object */ + implements_lib9p_srv_file *root_file = srv->rootdir(&ctx->ctx, req->aname); + assert((root_file == NULL) == lib9p_ctx_has_error(&ctx->ctx.basectx)); if (lib9p_ctx_has_error(&ctx->ctx.basectx)) return; - rootdir->_parent_dir = rootdir; - rootdir->_refcount++; - if (!fidmap_store(&ctx->parent_sess->fids, req->fid, (struct _srv_fidinfo){ - .file = rootdir, - .flags = FIDFLAG_ISDIR, - })) { - lib9p_error(&ctx->ctx.basectx, - LINUX_EMFILE, "too many open files"); - util_release(&ctx->ctx, rootdir); - return; - } + struct lib9p_qid root_qid = VCALL(root_file, qid); + assert(root_qid.type & LIB9P_QT_DIR); + + /* 2. pathinfo */ + struct srv_pathinfo *root_pathinfo = srv_util_pathsave(ctx, root_file, root_qid.path); - struct lib9p_stat stat = VCALL(rootdir, stat, &ctx->ctx); - if (lib9p_ctx_has_error(&ctx->ctx.basectx)) { - handle_Tclunk(ctx, - &(struct lib9p_msg_Tclunk){.fid = req->fid}, - &(struct lib9p_msg_Rclunk){}); + /* 3. fidinfo */ + if (!srv_util_fidsave(ctx, req->fid, root_pathinfo, false)) { + srv_util_pathfree(ctx, root_qid.path); return; } - lib9p_stat_assert(stat); - assert(stat.file_mode & LIB9P_DM_DIR); - - resp->qid = stat.file_qid; + resp->qid = root_qid; return; } @@ -638,87 +739,68 @@ static void handle_Twalk(struct _lib9p_srv_req *ctx, struct lib9p_msg_Rwalk *resp) { util_handler_common(ctx, req, resp); + if (req->newfid == LIB9P_FID_NOFID) { + lib9p_error(&ctx->ctx.basectx, + LINUX_EBADF, "cannot assign to NOFID"); + return; + } + struct _srv_fidinfo *fidinfo = fidmap_load(&ctx->parent_sess->fids, req->fid); if (!fidinfo) { lib9p_errorf(&ctx->ctx.basectx, LINUX_EBADF, "bad file number %"PRIu32, req->fid); return; } - if (req->newfid != req->fid && fidmap_load(&ctx->parent_sess->fids, req->newfid)) { - lib9p_error(&ctx->ctx.basectx, - LINUX_EBADF, "FID already in use"); - return; - } - implements_lib9p_srv_file *dir = fidinfo->file; - if (req->newfid != req->fid) { - dir = VCALL(dir, clone, &ctx->ctx); - assert((dir == NULL) == lib9p_ctx_has_error(&ctx->ctx.basectx)); - if (lib9p_ctx_has_error(&ctx->ctx.basectx)) - return; - dir->_refcount++; /* ref-A: presumptive insertion into fidmap */ - } - bool isdir = (fidinfo->flags & FIDFLAG_ISDIR); + struct srv_pathinfo *pathinfo = pathmap_load(&ctx->parent_sess->paths, fidinfo->path); + assert(pathinfo); + pathinfo->gc_refcount++; resp->wqid = (struct lib9p_qid *)(&resp[1]); for (resp->nwqid = 0; resp->nwqid < req->nwname; resp->nwqid++) { - implements_lib9p_srv_file *member; + struct srv_pathinfo *new_pathinfo; if (lib9p_str_eq(req->wname[resp->nwqid], lib9p_str(".."))) { - member = dir->_parent_dir; + new_pathinfo = pathmap_load(&ctx->parent_sess->paths, pathinfo->parent_dir); + assert(new_pathinfo); + new_pathinfo->gc_refcount++; } else { - if (!isdir) { + if (!srv_util_pathisdir(pathinfo)) { lib9p_error(&ctx->ctx.basectx, LINUX_ENOTDIR, "not a directory"); break; } - member = VCALL(dir, dopen, &ctx->ctx, req->wname[resp->nwqid]); - assert((member == NULL) == lib9p_ctx_has_error(&ctx->ctx.basectx)); + implements_lib9p_srv_file *member_file = VCALL(pathinfo->file, dopen, &ctx->ctx, req->wname[resp->nwqid]); + assert((member_file == NULL) == lib9p_ctx_has_error(&ctx->ctx.basectx)); if (lib9p_ctx_has_error(&ctx->ctx.basectx)) break; - member->_parent_dir = dir; - dir->_refcount++; /* member->_parent_dir */ + new_pathinfo = srv_util_pathsave(ctx, member_file, VCALL(pathinfo->file, qid).path); } - member->_refcount++; /* presumptively take ref-A */ - struct lib9p_stat stat = VCALL(member, stat, &ctx->ctx); - if (lib9p_ctx_has_error(&ctx->ctx.basectx)) { - util_release(&ctx->ctx, member); /* presumption of taking ref-A failed */ - break; - } - lib9p_stat_assert(stat); - isdir = stat.file_mode & LIB9P_DM_DIR; - if (isdir && !util_check_perm(&ctx->ctx, &stat, 0b001)) { - lib9p_error(&ctx->ctx.basectx, - LINUX_EACCES, "you do not have execute permission on that directory"); - util_release(&ctx->ctx, member); /* presumption of taking ref-A failed */ - break; + if (srv_util_pathisdir(new_pathinfo)) { + struct lib9p_stat stat = VCALL(new_pathinfo->file, stat, &ctx->ctx); + if (lib9p_ctx_has_error(&ctx->ctx.basectx)) + break; + lib9p_stat_assert(stat); + if (!srv_util_check_perm(ctx, &stat, 0b001)) { + lib9p_error(&ctx->ctx.basectx, + LINUX_EACCES, "you do not have execute permission on that directory"); + srv_util_pathfree(ctx, VCALL(new_pathinfo->file, qid).path); + break; + } } - resp->wqid[resp->nwqid] = stat.file_qid; + resp->wqid[resp->nwqid] = VCALL(new_pathinfo->file, qid); - /* presumption of taking ref-A succeeded */ - util_release(&ctx->ctx, dir); - dir = member; + srv_util_pathfree(ctx, VCALL(pathinfo->file, qid).path); + pathinfo = new_pathinfo; } if (resp->nwqid == req->nwname) { - if (req->newfid == req->fid) { - handle_Tclunk(ctx, - &(struct lib9p_msg_Tclunk){.fid = req->fid}, - &(struct lib9p_msg_Rclunk){}); - } - if (!fidmap_store(&ctx->parent_sess->fids, req->newfid, (struct _srv_fidinfo){ - .file = dir, - .flags = isdir ? FIDFLAG_ISDIR : 0, - })) { - lib9p_error(&ctx->ctx.basectx, - LINUX_EMFILE, "too many open files"); - util_release(&ctx->ctx, dir); /* presumption of insertion failed */ - } + if (!srv_util_fidsave(ctx, req->newfid, pathinfo, req->newfid == req->fid)) + srv_util_pathfree(ctx, VCALL(pathinfo->file, qid).path); } else { assert(lib9p_ctx_has_error(&ctx->ctx.basectx)); - if (req->newfid != req->fid) - util_release(&ctx->ctx, dir); /* presumption of insertion failed */ + srv_util_pathfree(ctx, VCALL(pathinfo->file, qid).path); if (resp->nwqid > 0) lib9p_ctx_clear_error(&ctx->ctx.basectx); } @@ -741,7 +823,9 @@ static void handle_Topen(struct _lib9p_srv_req *ctx, LINUX_EALREADY, "FID is already open"); return; } - if (fidinfo->flags & FIDFLAG_ISDIR) { + struct srv_pathinfo *pathinfo = pathmap_load(&ctx->parent_sess->paths, fidinfo->path); + assert(pathinfo); + if (srv_util_pathisdir(pathinfo)) { if ( ((req->mode & LIB9P_O_MODE_MASK) != LIB9P_O_READ) || (req->mode & LIB9P_O_TRUNC) || (req->mode & LIB9P_O_RCLOSE) ) { @@ -752,59 +836,80 @@ static void handle_Topen(struct _lib9p_srv_req *ctx, } /* Variables. */ - lib9p_o_t reqmode = req->mode; - uint8_t fidflags = fidinfo->flags; - implements_lib9p_srv_file *file = fidinfo->file; + lib9p_o_t reqmode = req->mode; + uint8_t fidflags = fidinfo->flags; + uint32_t iounit = fidinfo->iounit; /* Check permissions. */ if (reqmode & LIB9P_O_RCLOSE) { - struct lib9p_stat parent_stat = VCALL(file->_parent_dir, stat, &ctx->ctx); + struct srv_pathinfo *parent = pathmap_load(&ctx->parent_sess->paths, pathinfo->parent_dir); + assert(parent); + struct lib9p_stat parent_stat = VCALL(parent->file, stat, &ctx->ctx); if (lib9p_ctx_has_error(&ctx->ctx.basectx)) return; lib9p_stat_assert(parent_stat); - if (!util_check_perm(&ctx->ctx, &parent_stat, 0b010)) { + if (!srv_util_check_perm(ctx, &parent_stat, 0b010)) { lib9p_error(&ctx->ctx.basectx, LINUX_EACCES, "permission denied to remove-on-close"); return; } fidflags = fidflags | FIDFLAG_RCLOSE; } - struct lib9p_stat stat = VCALL(file, stat, &ctx->ctx); + struct lib9p_stat stat = VCALL(pathinfo->file, stat, &ctx->ctx); if (lib9p_ctx_has_error(&ctx->ctx.basectx)) return; lib9p_stat_assert(stat); - if (stat.file_mode & LIB9P_QT_APPEND) + if ((stat.file_mode & LIB9P_DM_EXCL) && (pathinfo->rd_refcount || pathinfo->wr_refcount)) { + lib9p_error(&ctx->ctx.basectx, + LINUX_EEXIST, "exclusive file is already opened"); + return; + } + if (stat.file_mode & LIB9P_DM_APPEND) reqmode = reqmode & ~LIB9P_O_TRUNC; uint8_t perm_bits = 0; + bool rd = false, wr = false; switch (reqmode & LIB9P_O_MODE_MASK) { case LIB9P_O_READ: perm_bits = 0b100; - fidflags = fidflags | FIDFLAG_OPEN_R; + rd = true; break; case LIB9P_O_WRITE: perm_bits = 0b010; - fidflags = fidflags | FIDFLAG_OPEN_W; + wr = true; break; case LIB9P_O_RDWR: perm_bits = 0b110; - fidflags = fidflags | FIDFLAG_OPEN_R | FIDFLAG_OPEN_W; + rd = wr = true; break; case LIB9P_O_EXEC: perm_bits = 0b001; - fidflags = fidflags | FIDFLAG_OPEN_R; + rd = true; break; } - if (!util_check_perm(&ctx->ctx, &stat, perm_bits)) { + if (!srv_util_check_perm(ctx, &stat, perm_bits)) { lib9p_error(&ctx->ctx.basectx, LINUX_EACCES, "permission denied"); } /* Actually make the call. */ - uint32_t iounit = VCALL(file, io, &ctx->ctx, reqmode); - if (lib9p_ctx_has_error(&ctx->ctx.basectx)) - return; + if ((reqmode & LIB9P_O_TRUNC) || (rd && !pathinfo->rd_refcount) || (wr && !pathinfo->wr_refcount) ) { + iounit = VCALL(pathinfo->file, chio, &ctx->ctx, + fidflags & FIDFLAG_OPEN_R, + fidflags & FIDFLAG_OPEN_W, + reqmode & LIB9P_O_TRUNC); + if (lib9p_ctx_has_error(&ctx->ctx.basectx)) + return; + } /* Success. */ + if (rd) { + fidflags = fidflags | FIDFLAG_OPEN_R; + pathinfo->rd_refcount++; + } + if (wr) { + fidflags = fidflags | FIDFLAG_OPEN_W; + pathinfo->wr_refcount++; + } fidinfo->flags = fidflags; resp->qid = stat.file_qid; resp->iounit = iounit; @@ -838,11 +943,12 @@ static void handle_Tread(struct _lib9p_srv_req *ctx, } /* Variables. */ - implements_lib9p_srv_file *file = fidinfo->file; + struct srv_pathinfo *pathinfo = pathmap_load(&ctx->parent_sess->paths, fidinfo->path); + assert(pathinfo); resp->data = (char *)(&resp[1]); /* Do it. */ - if (fidinfo->flags & FIDFLAG_ISDIR) { + if (srv_util_pathisdir(pathinfo)) { /* Translate byte-offset to object-index. */ size_t idx; if (req->offset == 0) @@ -856,7 +962,7 @@ static void handle_Tread(struct _lib9p_srv_req *ctx, return; } /* Do it. */ - size_t num = VCALL(file, dread, &ctx->ctx, (uint8_t *)resp->data, req->count, idx); + size_t num = VCALL(pathinfo->file, dread, &ctx->ctx, (uint8_t *)resp->data, req->count, idx); /* Translate object-count back to byte-count. */ uint32_t len = 0; for (size_t i = 0; i < num; i++) { @@ -869,7 +975,7 @@ static void handle_Tread(struct _lib9p_srv_req *ctx, fidinfo->dir_idx = idx+num; fidinfo->dir_off = req->offset + len; } else - resp->count = VCALL(file, pread, &ctx->ctx, resp->data, req->count, req->offset); + resp->count = VCALL(pathinfo->file, pread, &ctx->ctx, resp->data, req->count, req->offset); } static void handle_Twrite(struct _lib9p_srv_req *ctx, @@ -891,41 +997,62 @@ static void handle_Twrite(struct _lib9p_srv_req *ctx, } /* Variables. */ - implements_lib9p_srv_file *file = fidinfo->file; + struct srv_pathinfo *pathinfo = pathmap_load(&ctx->parent_sess->paths, fidinfo->path); + assert(pathinfo); /* Do it. */ - resp->count = VCALL(file, pwrite, &ctx->ctx, req->data, req->count, req->offset); + resp->count = VCALL(pathinfo->file, pwrite, &ctx->ctx, req->data, req->count, req->offset); } -static void handle_Tclunk(struct _lib9p_srv_req *ctx, - struct lib9p_msg_Tclunk *req, - struct lib9p_msg_Rclunk *resp) { - util_handler_common(ctx, req, resp); - - struct _srv_fidinfo *fidinfo = fidmap_load(&ctx->parent_sess->fids, req->fid); +static void clunkremove(struct _lib9p_srv_req *ctx, lib9p_fid_t fid, bool remove) { + struct _srv_fidinfo *fidinfo = fidmap_load(&ctx->parent_sess->fids, fid); if (!fidinfo) { lib9p_errorf(&ctx->ctx.basectx, - LINUX_EBADF, "bad file number %"PRIu32, req->fid); + LINUX_EBADF, "bad file number %"PRIu32, fid); return; } - if (fidinfo->flags & FIDFLAG_RCLOSE) { - handle_Tremove(ctx, - &(struct lib9p_msg_Tremove){.fid = req->fid}, - &(struct lib9p_msg_Rremove){}); - return; + if (fidinfo->flags & FIDFLAG_RCLOSE) + remove = true; + struct srv_pathinfo *pathinfo = pathmap_load(&ctx->parent_sess->paths, fidinfo->path); + assert(pathinfo); + + if (remove) { + if (pathinfo->parent_dir == fidinfo->path) { + lib9p_errorf(&ctx->ctx.basectx, + LINUX_EBUSY, "cannot remove root"); + goto clunk; + } + struct srv_pathinfo *parent = pathmap_load(&ctx->parent_sess->paths, pathinfo->parent_dir); + assert(parent); + struct lib9p_stat parent_stat = VCALL(parent->file, stat, &ctx->ctx); + if (!srv_util_check_perm(ctx, &parent_stat, 0b010)) { + lib9p_error(&ctx->ctx.basectx, + LINUX_EACCES, "you do not have write permission on the parent directory"); + goto clunk; + } + VCALL(pathinfo->file, remove, &ctx->ctx); } - VCALL(fidinfo->file, free, &ctx->ctx); - fidmap_del(&ctx->parent_sess->fids, req->fid); + clunk: + srv_util_pathfree(ctx, VCALL(pathinfo->file, qid).path); + fidmap_del(&ctx->parent_sess->fids, fid); } +static void handle_Tclunk(struct _lib9p_srv_req *ctx, + struct lib9p_msg_Tclunk *req, + struct lib9p_msg_Rclunk *resp) { + util_handler_common(ctx, req, resp); + + clunkremove(ctx, req->fid, false); +} + + static void handle_Tremove(struct _lib9p_srv_req *ctx, struct lib9p_msg_Tremove *req, struct lib9p_msg_Rremove *resp) { util_handler_common(ctx, req, resp); - lib9p_error(&ctx->ctx.basectx, - LINUX_EOPNOTSUPP, "remove not (yet?) implemented"); + clunkremove(ctx, req->fid, true); } static void handle_Tstat(struct _lib9p_srv_req *ctx, @@ -939,8 +1066,10 @@ static void handle_Tstat(struct _lib9p_srv_req *ctx, LINUX_EBADF, "bad file number %"PRIu32, req->fid); return; } + struct srv_pathinfo *pathinfo = pathmap_load(&ctx->parent_sess->paths, fidinfo->path); + assert(pathinfo); - resp->stat = VCALL(fidinfo->file, stat, &ctx->ctx); + resp->stat = VCALL(pathinfo->file, stat, &ctx->ctx); if (!lib9p_ctx_has_error(&ctx->ctx.basectx)) lib9p_stat_assert(resp->stat); } -- cgit v1.2.3-2-g168b