diff options
-rwxr-xr-x | build-aux/stack.c.gen | 13 | ||||
-rw-r--r-- | cmd/sbc_harness/config/config.h | 17 | ||||
-rw-r--r-- | cmd/sbc_harness/main.c | 6 | ||||
-rw-r--r-- | lib9p/include/lib9p/srv.h | 21 | ||||
-rw-r--r-- | lib9p/internal.h | 6 | ||||
-rw-r--r-- | lib9p/srv.c | 399 | ||||
-rwxr-xr-x | lib9p/tests/runtest | 2 | ||||
-rw-r--r-- | lib9p/tests/test_server/config/config.h | 14 | ||||
-rw-r--r-- | lib9p/tests/test_server/main.c | 31 | ||||
-rw-r--r-- | lib9p_util/static.c | 64 |
10 files changed, 357 insertions, 216 deletions
diff --git a/build-aux/stack.c.gen b/build-aux/stack.c.gen index 9f2d60f..2a23565 100755 --- a/build-aux/stack.c.gen +++ b/build-aux/stack.c.gen @@ -457,7 +457,9 @@ def main( # 3=just 1 level of subdirectories # 4=just 2 levels of subdirectories # ... - sbc_9p_max_depth = 3 + # + # TODO: Sniff this from config.h + CONFIG_9P_SRV_MAX_DEPTH = 3 def sbc_skip_call(chain: list[str], call: str) -> bool: if ( @@ -468,9 +470,12 @@ def main( ): return True if ( - len(chain) >= sbc_9p_max_depth - and "/srv.c:util_release" in call - and all(("/srv.c:util_release" in c) for c in chain[-sbc_9p_max_depth:]) + len(chain) >= CONFIG_9P_SRV_MAX_DEPTH + and "/srv.c:srv_util_pathfree" in call + and all( + ("/srv.c:srv_util_pathfree" in c) + for c in chain[-CONFIG_9P_SRV_MAX_DEPTH:] + ) ): return True return False diff --git a/cmd/sbc_harness/config/config.h b/cmd/sbc_harness/config/config.h index 309fac1..fa3236c 100644 --- a/cmd/sbc_harness/config/config.h +++ b/cmd/sbc_harness/config/config.h @@ -56,9 +56,10 @@ * struct padding, (2) nul-terminator byes for strings. */ #define CONFIG_9P_MAX_HOSTMSG_SIZE CONFIG_9P_MAX_MSG_SIZE+16 -#define CONFIG_9P_MAX_FIDS 16 -#define CONFIG_9P_MAX_REQS 2 #define CONFIG_9P_MAX_ERR_SIZE 128 /* 128 is what Plan 9 4e uses */ +#define CONFIG_9P_SRV_MAX_FIDS 16 +#define CONFIG_9P_SRV_MAX_REQS 2 +#define CONFIG_9P_SRV_MAX_DEPTH 3 #define CONFIG_9P_ENABLE_9P2000 1 /* bool */ #define CONFIG_9P_ENABLE_9P2000_u 1 /* bool */ #define CONFIG_9P_ENABLE_9P2000_e 0 /* bool */ @@ -94,11 +95,11 @@ #define CONFIG_COROUTINE_GDB 1 /* bool */ #define _CONFIG_9P_NUM_SOCKS 7 -#define CONFIG_COROUTINE_NUM ( \ - 1 /* usb_common */ + \ - 1 /* usb_keyboard */ + \ - CONFIG_W5500_NUM /* irq handler */ + \ - _CONFIG_9P_NUM_SOCKS /* 9P accept()+read() */ + \ - (CONFIG_9P_MAX_REQS*_CONFIG_9P_NUM_SOCKS) /* 9P work+write() */ ) +#define CONFIG_COROUTINE_NUM ( \ + 1 /* usb_common */ + \ + 1 /* usb_keyboard */ + \ + CONFIG_W5500_NUM /* irq handler */ + \ + _CONFIG_9P_NUM_SOCKS /* 9P accept()+read() */ + \ + (CONFIG_9P_SRV_MAX_REQS*_CONFIG_9P_NUM_SOCKS) /* 9P work+write() */ ) #endif /* _CONFIG_H_ */ diff --git a/cmd/sbc_harness/main.c b/cmd/sbc_harness/main.c index 00f1c4a..b17725d 100644 --- a/cmd/sbc_harness/main.c +++ b/cmd/sbc_harness/main.c @@ -1,6 +1,6 @@ /* sbc_harness/main.c - Main entry point and event loop for sbc-harness * - * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> + * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> * SPDX-License-Identifier: AGPL-3.0-or-later */ @@ -68,7 +68,7 @@ static COROUTINE read9p_cr(void *) { } const char *hexdig = "0123456789ABCDEF"; -static_assert(CONFIG_9P_MAX_REQS*_CONFIG_9P_NUM_SOCKS <= 16); +static_assert(CONFIG_9P_SRV_MAX_REQS*_CONFIG_9P_NUM_SOCKS <= 16); COROUTINE init_cr(void *) { cr_begin(); @@ -122,7 +122,7 @@ COROUTINE init_cr(void *) { char name[] = {'r', 'e', 'a', 'd', '-', hexdig[i], '\0'}; coroutine_add(name, read9p_cr, NULL); } - for (int i = 0; i < CONFIG_9P_MAX_REQS*_CONFIG_9P_NUM_SOCKS; i++) { + for (int i = 0; i < CONFIG_9P_SRV_MAX_REQS*_CONFIG_9P_NUM_SOCKS; i++) { char name[] = {'w', 'r', 'i', 't', 'e', '-', hexdig[i], '\0'}; coroutine_add(name, lib9p_srv_write_cr, &globals.srv); } diff --git a/lib9p/include/lib9p/srv.h b/lib9p/include/lib9p/srv.h index d45d9a4..e9d2d7b 100644 --- a/lib9p/include/lib9p/srv.h +++ b/lib9p/include/lib9p/srv.h @@ -37,29 +37,18 @@ int lib9p_srv_acknowledge_flush(struct lib9p_srv_ctx *ctx); struct lib9p_srv_file_vtable; -struct __lib9p_srv_file; -typedef struct __lib9p_srv_file { +typedef struct { struct lib9p_srv_file_vtable *vtable; - - BEGIN_PRIVATE(LIB9P_SRV_H) - /* Managed by srv.c, but should be cloned by ->vtable->clone(). */ - struct __lib9p_srv_file *_parent_dir; /* clone this - - /* Managed by srv.c, but should be initialized to 0 by ->vtable->clone(). */ - /* ref type 1: an entry in fidmap - * ref type 2: ->_parent_dir of another file */ - unsigned int _refcount; - END_PRIVATE(LIB9P_SRV_H) } implements_lib9p_srv_file; struct lib9p_srv_file_vtable { /* all - resource management */ - implements_lib9p_srv_file *(*clone )(implements_lib9p_srv_file *, struct lib9p_srv_ctx *); - void (*free )(implements_lib9p_srv_file *, struct lib9p_srv_ctx *); + void (*free )(implements_lib9p_srv_file *); /* must not error */ + struct lib9p_qid (*qid )(implements_lib9p_srv_file *); /* must not error */ + uint32_t (*chio )(implements_lib9p_srv_file *, struct lib9p_srv_ctx *, + bool rd, bool wr, bool trunc); /* all - syscalls */ - uint32_t (*io )(implements_lib9p_srv_file *, struct lib9p_srv_ctx *, - lib9p_o_t flags); struct lib9p_stat (*stat )(implements_lib9p_srv_file *, struct lib9p_srv_ctx *); void (*wstat )(implements_lib9p_srv_file *, struct lib9p_srv_ctx *, struct lib9p_stat new); diff --git a/lib9p/internal.h b/lib9p/internal.h index eb67992..d939d46 100644 --- a/lib9p/internal.h +++ b/lib9p/internal.h @@ -28,12 +28,6 @@ #ifndef CONFIG_9P_MAX_HOSTMSG_SIZE #error config.h must define CONFIG_9P_MAX_HOSTMSG_SIZE #endif -#ifndef CONFIG_9P_MAX_FIDS - #error config.h must define CONFIG_9P_MAX_FIDS -#endif -#ifndef CONFIG_9P_MAX_REQS - #error config.h must define CONFIG_9P_MAX_REQS -#endif #ifndef CONFIG_9P_MAX_ERR_SIZE #error config.h must define CONFIG_9P_MAX_ERR_SIZE #endif diff --git a/lib9p/srv.c b/lib9p/srv.c index 10a8a96..47dd78d 100644 --- a/lib9p/srv.c +++ b/lib9p/srv.c @@ -20,8 +20,20 @@ #define IMPLEMENTATION_FOR_LIB9P_SRV_H YES #include <lib9p/srv.h> + #include "internal.h" +#ifndef CONFIG_9P_SRV_MAX_FIDS + #error config.h must define CONFIG_9P_SRV_MAX_FIDS +#endif +#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 ********************************************************************/ bool lib9p_srv_flush_requested(struct lib9p_srv_ctx *ctx) { @@ -39,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; }; @@ -55,13 +88,13 @@ struct _srv_fidinfo { #define NAME fidmap #define KEY_T lib9p_fid_t #define VAL_T struct _srv_fidinfo -#define CAP CONFIG_9P_MAX_FIDS +#define CAP CONFIG_9P_SRV_MAX_FIDS #include "map.h" #define NAME reqmap #define KEY_T lib9p_tag_t #define VAL_T struct _lib9p_srv_req * -#define CAP CONFIG_9P_MAX_REQS +#define CAP CONFIG_9P_SRV_MAX_REQS #include "map.h" /* The hierarchy of concepts is: @@ -90,6 +123,7 @@ struct _srv_sess { /* mutable */ bool initialized; bool closing; + struct pathmap paths; struct reqmap reqs; struct fidmap fids; }; @@ -417,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); @@ -428,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++; + } + } + 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); } - return lib9p_ctx_has_error(&ctx->basectx); } +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) { @@ -567,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) { @@ -577,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; } @@ -630,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); } @@ -733,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) ) { @@ -744,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; @@ -830,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) @@ -848,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++) { @@ -861,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, @@ -883,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, @@ -931,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); } diff --git a/lib9p/tests/runtest b/lib9p/tests/runtest index d963c53..bb83a83 100755 --- a/lib9p/tests/runtest +++ b/lib9p/tests/runtest @@ -43,7 +43,7 @@ expect_lines \ out=$("${client[@]}" stat 'Documentation/x') expect_lines \ - "'x' 'root' 'root' 'root' q (0000000000000009 1 ) m 0444 at 1728337905 mt 1728337904 l 4 t 0 d 0" + "'x' 'root' 'root' 'root' q (0000000000000008 1 ) m 0444 at 1728337905 mt 1728337904 l 4 t 0 d 0" out=$("${client[@]}" write 'shutdown' <<<1) expect_lines '' diff --git a/lib9p/tests/test_server/config/config.h b/lib9p/tests/test_server/config/config.h index e4036d6..d852470 100644 --- a/lib9p/tests/test_server/config/config.h +++ b/lib9p/tests/test_server/config/config.h @@ -36,9 +36,10 @@ * struct padding, (2) array pointers. */ #define CONFIG_9P_MAX_HOSTMSG_SIZE CONFIG_9P_MAX_MSG_SIZE+16 -#define CONFIG_9P_MAX_FIDS 16 -#define CONFIG_9P_MAX_REQS 2 #define CONFIG_9P_MAX_ERR_SIZE 128 /* 128 is what Plan 9 4e uses */ +#define CONFIG_9P_SRV_MAX_FIDS 16 +#define CONFIG_9P_SRV_MAX_REQS 2 +#define CONFIG_9P_SRV_MAX_DEPTH 3 #define CONFIG_9P_ENABLE_9P2000 1 /* bool */ #define CONFIG_9P_ENABLE_9P2000_u 1 /* bool */ #define CONFIG_9P_ENABLE_9P2000_e 0 /* bool */ @@ -54,9 +55,10 @@ #define CONFIG_COROUTINE_DEBUG 0 /* bool */ #define CONFIG_COROUTINE_VALGRIND 1 /* bool */ #define CONFIG_COROUTINE_GDB 1 /* bool */ -#define CONFIG_COROUTINE_NUM (1 /* usb_common */ +\ - 1 /* usb_keyboard */ +\ - CONFIG_SRV9P_NUM_CONNS /* accept+read */ +\ - (CONFIG_9P_MAX_REQS*CONFIG_SRV9P_NUM_CONNS) /* work+write */ ) +#define CONFIG_COROUTINE_NUM ( \ + 1 /* usb_common */ + \ + 1 /* usb_keyboard */ + \ + CONFIG_SRV9P_NUM_CONNS /* accept+read */ + \ + (CONFIG_9P_SRV_MAX_REQS*CONFIG_SRV9P_NUM_CONNS) /* work+write */ ) #endif /* _CONFIG_H_ */ diff --git a/lib9p/tests/test_server/main.c b/lib9p/tests/test_server/main.c index 71c6cc2..07fc74b 100644 --- a/lib9p/tests/test_server/main.c +++ b/lib9p/tests/test_server/main.c @@ -47,23 +47,30 @@ struct api_file { uint64_t pathnum; }; -static implements_lib9p_srv_file *api_clone(implements_lib9p_srv_file *self, struct lib9p_srv_ctx *) { return self; } -static void api_free(implements_lib9p_srv_file *, struct lib9p_srv_ctx *) {} +static void api_free(implements_lib9p_srv_file *) {} +static uint32_t api_chio(implements_lib9p_srv_file *, struct lib9p_srv_ctx *, bool, bool, bool) { return 0; } -static uint32_t api_io(implements_lib9p_srv_file *, struct lib9p_srv_ctx *, lib9p_o_t) { return 0; } static void api_wstat(implements_lib9p_srv_file *, struct lib9p_srv_ctx *ctx, struct lib9p_stat) { lib9p_error(&ctx->basectx, LINUX_EROFS, "cannot wstat API file"); } static void api_remove(implements_lib9p_srv_file *, struct lib9p_srv_ctx *ctx) { lib9p_error(&ctx->basectx, LINUX_EROFS, "cannot remove API file"); } +static struct lib9p_qid api_qid(implements_lib9p_srv_file *_self) { + struct api_file *self = VCALL_SELF(struct api_file, implements_lib9p_srv_file, _self); + assert(self); + + return (struct lib9p_qid){ + .type = LIB9P_QT_FILE, + .vers = 1, + .path = self->pathnum, + }; +} static struct lib9p_stat api_stat(implements_lib9p_srv_file *_self, struct lib9p_srv_ctx *) { struct api_file *self = VCALL_SELF(struct api_file, implements_lib9p_srv_file, _self); + assert(self); + return (struct lib9p_stat){ - .kern_type = 0, - .kern_dev = 0, - .file_qid = { - .type = LIB9P_QT_FILE, - .vers = 1, - .path = self->pathnum, - }, + .kern_type = 0, + .kern_dev = 0, + .file_qid = api_qid(self), .file_mode = 0222, .file_atime = 1728337905, .file_mtime = 1728337904, @@ -87,10 +94,10 @@ static uint32_t api_pwrite(implements_lib9p_srv_file *, struct lib9p_srv_ctx *, } static struct lib9p_srv_file_vtable api_file_vtable = { - .clone = api_clone, .free = api_free, + .qid = api_qid, + .chio = api_chio, - .io = api_io, .stat = api_stat, .wstat = api_wstat, .remove = api_remove, diff --git a/lib9p_util/static.c b/lib9p_util/static.c index 7851714..0f0efad 100644 --- a/lib9p_util/static.c +++ b/lib9p_util/static.c @@ -12,23 +12,15 @@ /* common *********************************************************************/ -static implements_lib9p_srv_file *util9p_static_common_clone(implements_lib9p_srv_file *_self, struct lib9p_srv_ctx *ctx) { +static void util9p_static_common_free(implements_lib9p_srv_file *_self) { _util9p_static_common *self = VCALL_SELF(_util9p_static_common, implements_lib9p_srv_file, _self); assert(self); - assert(ctx); - - return self; -} - -static void util9p_static_common_free(implements_lib9p_srv_file *_self, struct lib9p_srv_ctx *ctx) { - _util9p_static_common *self = VCALL_SELF(_util9p_static_common, implements_lib9p_srv_file, _self); - assert(self); - assert(ctx); /* do nothing */ } -static uint32_t util9p_static_common_io(implements_lib9p_srv_file *_self, struct lib9p_srv_ctx *ctx, lib9p_o_t LM_UNUSED(flags)) { +static uint32_t util9p_static_common_chio(implements_lib9p_srv_file *_self, struct lib9p_srv_ctx *ctx, + bool LM_UNUSED(rd), bool LM_UNUSED(wr), bool LM_UNUSED(trunc)) { _util9p_static_common *self = VCALL_SELF(_util9p_static_common, implements_lib9p_srv_file, _self); assert(self); assert(ctx); @@ -55,19 +47,26 @@ static void util9p_static_common_remove(implements_lib9p_srv_file *_self, struct /* dir ************************************************************************/ +static struct lib9p_qid util9p_static_dir_qid(implements_lib9p_srv_file *_self) { + struct util9p_static_dir *self = VCALL_SELF(struct util9p_static_dir, implements_lib9p_srv_file, _self); + assert(self); + + return (struct lib9p_qid){ + .type = LIB9P_QT_DIR, + .vers = 1, + .path = self->pathnum, + }; +} + static struct lib9p_stat util9p_static_dir_stat(implements_lib9p_srv_file *_self, struct lib9p_srv_ctx *ctx) { struct util9p_static_dir *self = VCALL_SELF(struct util9p_static_dir, implements_lib9p_srv_file, _self); assert(self); assert(ctx); return (struct lib9p_stat){ - .kern_type = 0, - .kern_dev = 0, - .file_qid = { - .type = LIB9P_QT_DIR, - .vers = 1, - .path = self->pathnum, - }, + .kern_type = 0, + .kern_dev = 0, + .file_qid = util9p_static_dir_qid(self), .file_mode = LIB9P_DM_DIR | (self->perm & 0555), .file_atime = self->atime, .file_mtime = self->mtime, @@ -145,10 +144,10 @@ static size_t util9p_static_dir_dread(implements_lib9p_srv_file *_self, struct l } struct lib9p_srv_file_vtable util9p_static_dir_vtable = { - .clone = util9p_static_common_clone, .free = util9p_static_common_free, + .qid = util9p_static_dir_qid, + .chio = util9p_static_common_chio, - .io = util9p_static_common_io, .stat = util9p_static_dir_stat, .wstat = util9p_static_common_wstat, .remove = util9p_static_common_remove, @@ -172,19 +171,26 @@ static inline size_t util9p_static_file_size(struct util9p_static_file *file) { return (size_t)((uintptr_t)file->data_end - (uintptr_t)file->data_start); } +static struct lib9p_qid util9p_static_file_qid(implements_lib9p_srv_file *_self) { + struct util9p_static_file *self = VCALL_SELF(struct util9p_static_file, implements_lib9p_srv_file, _self); + assert(self); + + return (struct lib9p_qid){ + .type = LIB9P_QT_FILE, + .vers = 1, + .path = self->pathnum, + }; +} + static struct lib9p_stat util9p_static_file_stat(implements_lib9p_srv_file *_self, struct lib9p_srv_ctx *ctx) { struct util9p_static_file *self = VCALL_SELF(struct util9p_static_file, implements_lib9p_srv_file, _self); assert(self); assert(ctx); return (struct lib9p_stat){ - .kern_type = 0, - .kern_dev = 0, - .file_qid = { - .type = LIB9P_QT_FILE, - .vers = 1, - .path = self->pathnum, - }, + .kern_type = 0, + .kern_dev = 0, + .file_qid = util9p_static_file_qid(self), .file_mode = self->perm & 0444, .file_atime = self->atime, .file_mtime = self->mtime, @@ -225,10 +231,10 @@ static uint32_t util9p_static_file_pread(implements_lib9p_srv_file *_self, struc } struct lib9p_srv_file_vtable util9p_static_file_vtable = { - .clone = util9p_static_common_clone, .free = util9p_static_common_free, + .qid = util9p_static_file_qid, + .chio = util9p_static_common_chio, - .io = util9p_static_common_io, .stat = util9p_static_file_stat, .wstat = util9p_static_common_wstat, .remove = util9p_static_common_remove, |