diff options
author | Luke T. Shumaker <lukeshu@lukeshu.com> | 2024-10-10 10:26:28 -0600 |
---|---|---|
committer | Luke T. Shumaker <lukeshu@lukeshu.com> | 2024-10-10 10:26:28 -0600 |
commit | db92bd35807305e6daec5ec40bd67cbc43fe88ee (patch) | |
tree | 8458933923545c87632e953a1c10a215efa78a0c /lib9p | |
parent | bb5afed7a0eeaf361be1e29b3a3ab8ace2865b39 (diff) |
lib9p: Implemnt Topen, add permission checks in Twalk
Diffstat (limited to 'lib9p')
-rw-r--r-- | lib9p/9p.c | 4 | ||||
-rw-r--r-- | lib9p/idl/2002-9P2000.9p | 9 | ||||
-rw-r--r-- | lib9p/include/lib9p/9p.generated.h | 30 | ||||
-rw-r--r-- | lib9p/include/lib9p/9p.h | 6 | ||||
-rw-r--r-- | lib9p/include/lib9p/srv.h | 37 | ||||
-rw-r--r-- | lib9p/srv.c | 151 |
6 files changed, 174 insertions, 63 deletions
@@ -14,6 +14,8 @@ #include "internal.h" int lib9p_error(struct lib9p_ctx *ctx, uint32_t linux_errno, char const *msg) { + if (lib9p_ctx_has_error(ctx)) + return -1; strncpy(ctx->err_msg, msg, sizeof(ctx->err_msg)); ctx->err_msg[sizeof(ctx->err_msg)-1] = '\0'; ctx->err_num = linux_errno; @@ -24,6 +26,8 @@ int lib9p_errorf(struct lib9p_ctx *ctx, uint32_t linux_errno, char const *fmt, . int n; va_list args; + if (lib9p_ctx_has_error(ctx)) + return -1; va_start(args, fmt); n = vsnprintf(ctx->err_msg, sizeof(ctx->err_msg), fmt, args); va_end(args); diff --git a/lib9p/idl/2002-9P2000.9p b/lib9p/idl/2002-9P2000.9p index ba1382e..e45c0e1 100644 --- a/lib9p/idl/2002-9P2000.9p +++ b/lib9p/idl/2002-9P2000.9p @@ -53,7 +53,7 @@ bitfield dm = 4 "1=OTHER_W" "0=OTHER_X" - "PERM=0777" # {OWNER,GROUP,OTHER}_{R,W,X} + "PERM_MASK=0777" # {OWNER,GROUP,OTHER}_{R,W,X} # QID Type - see `struct qid` below bitfield qt = 1 @@ -103,8 +103,8 @@ struct stat = "stat_size[2,val=end-&kern_type]" # "O"pen flags (flags to pass to Topen and Tcreate) bitfield o = 1 - "0=rwx_0" # low bit of the 2-bit READ/WRITE/RDWR/EXEC enum - "1=rwx_1" # high bit of the 2-bit READ/WRITE/RDWR/EXEC enum" + "0=mode_0" # low bit of the 2-bit READ/WRITE/RDWR/EXEC enum + "1=mode_1" # high bit of the 2-bit READ/WRITE/RDWR/EXEC enum" #"2=unused" #"3=unused" "4=TRUNC" @@ -117,6 +117,9 @@ bitfield o = 1 "RDWR = 2" # make available for this FID: Tread() and Twrite() "EXEC = 3" # make available for this FID: Tread() + "MODE_MASK = 0b00000011" + "FLAG_MASK = 0b11111100" + msg Tversion = "size[4,val=end-&size] typ[1,val=100] tag[tag] max_msg_size[4] version[s]" msg Rversion = "size[4,val=end-&size] typ[1,val=101] tag[tag] max_msg_size[4] version[s]" msg Tauth = "size[4,val=end-&size] typ[1,val=102] tag[tag] afid[fid] uname[s] aname[s]" diff --git a/lib9p/include/lib9p/9p.generated.h b/lib9p/include/lib9p/9p.generated.h index 6248ec7..584960c 100644 --- a/lib9p/include/lib9p/9p.generated.h +++ b/lib9p/include/lib9p/9p.generated.h @@ -80,7 +80,7 @@ typedef uint32_t lib9p_dm_t; #define LIB9P_DM_OTHER_W ((lib9p_dm_t)(1<<1)) #define LIB9P_DM_OTHER_X ((lib9p_dm_t)(1<<0)) -#define LIB9P_DM_PERM ((lib9p_dm_t)(0777)) +#define LIB9P_DM_PERM_MASK ((lib9p_dm_t)(0777)) typedef uint8_t lib9p_qt_t; @@ -125,19 +125,21 @@ struct lib9p_stat { typedef uint8_t lib9p_o_t; -/* unused ((lib9p_o_t)(1<<7)) */ -#define LIB9P_O_RCLOSE ((lib9p_o_t)(1<<6)) -/* unused ((lib9p_o_t)(1<<5)) */ -#define LIB9P_O_TRUNC ((lib9p_o_t)(1<<4)) -/* unused ((lib9p_o_t)(1<<3)) */ -/* unused ((lib9p_o_t)(1<<2)) */ -#define LIB9P_O_rwx_1 ((lib9p_o_t)(1<<1)) -#define LIB9P_O_rwx_0 ((lib9p_o_t)(1<<0)) - -#define LIB9P_O_READ ((lib9p_o_t)(0)) -#define LIB9P_O_WRITE ((lib9p_o_t)(1)) -#define LIB9P_O_RDWR ((lib9p_o_t)(2)) -#define LIB9P_O_EXEC ((lib9p_o_t)(3)) +/* unused ((lib9p_o_t)(1<<7)) */ +#define LIB9P_O_RCLOSE ((lib9p_o_t)(1<<6)) +/* unused ((lib9p_o_t)(1<<5)) */ +#define LIB9P_O_TRUNC ((lib9p_o_t)(1<<4)) +/* unused ((lib9p_o_t)(1<<3)) */ +/* unused ((lib9p_o_t)(1<<2)) */ +#define LIB9P_O_mode_1 ((lib9p_o_t)(1<<1)) +#define LIB9P_O_mode_0 ((lib9p_o_t)(1<<0)) + +#define LIB9P_O_READ ((lib9p_o_t)(0)) +#define LIB9P_O_WRITE ((lib9p_o_t)(1)) +#define LIB9P_O_RDWR ((lib9p_o_t)(2)) +#define LIB9P_O_EXEC ((lib9p_o_t)(3)) +#define LIB9P_O_MODE_MASK ((lib9p_o_t)(0b00000011)) +#define LIB9P_O_FLAG_MASK ((lib9p_o_t)(0b11111100)) #endif /* defined(CONFIG_9P_ENABLE_9P2000) || defined(CONFIG_9P_ENABLE_9P2000_e) || defined(CONFIG_9P_ENABLE_9P2000_u) */ /* messages *******************************************************************/ diff --git a/lib9p/include/lib9p/9p.h b/lib9p/include/lib9p/9p.h index 3ad52f1..c3c6a8b 100644 --- a/lib9p/include/lib9p/9p.h +++ b/lib9p/include/lib9p/9p.h @@ -41,6 +41,12 @@ struct lib9p_ctx { char err_msg[CONFIG_9P_MAX_ERR_SIZE]; }; +static void lib9p_ctx_clear_error(struct lib9p_ctx *ctx) { + assert(ctx); + ctx->err_num = 0; + ctx->err_msg[0] = '\0'; +} + static bool lib9p_ctx_has_error(struct lib9p_ctx *ctx) { assert(ctx); return ctx->err_num || ctx->err_msg[0]; diff --git a/lib9p/include/lib9p/srv.h b/lib9p/include/lib9p/srv.h index d79e7db..5df6b1a 100644 --- a/lib9p/include/lib9p/srv.h +++ b/lib9p/include/lib9p/srv.h @@ -35,7 +35,6 @@ static inline int lib9p_srv_acknowledge_flush(struct lib9p_srv_ctx *ctx) { /* vtables you must implement *************************************************/ struct lib9p_srv_file; -struct lib9p_srv_io; struct lib9p_srv_file_vtable { /* all - resource management */ @@ -43,28 +42,26 @@ struct lib9p_srv_file_vtable { void (*free )(struct lib9p_srv_ctx *, struct lib9p_srv_file *); /* all - syscalls */ - struct lib9p_srv_io *(*io )(struct lib9p_srv_ctx *, struct lib9p_srv_file *, + uint32_t (*io )(struct lib9p_srv_ctx *, struct lib9p_srv_file *, lib9p_o_t flags); struct lib9p_stat (*stat )(struct lib9p_srv_ctx *, struct lib9p_srv_file *); void (*wstat )(struct lib9p_srv_ctx *, struct lib9p_srv_file *, struct lib9p_stat new); void (*remove )(struct lib9p_srv_ctx *, struct lib9p_srv_file *); - /* directories */ + /* directories - base */ struct lib9p_srv_file *(*dopen )(struct lib9p_srv_ctx *, struct lib9p_srv_file *, char *childname); struct lib9p_srv_file *(*dcreate)(struct lib9p_srv_ctx *, struct lib9p_srv_file *, char *childname, lib9p_dm_t perm, lib9p_o_t flags); -}; -struct lib9p_srv_io_dir_vtable { + /* directories - once opened */ size_t (*readdir)(struct lib9p_srv_ctx *, struct lib9p_srv_io_dir *, struct lib9p_stat *buf, size_t count, size_t offset); -}; -struct lib9p_srv_io_file_vtable { + /* non-directories - once opened */ uint32_t (*pread )(struct lib9p_srv_ctx *, struct lib9p_srv_io_file *, void *buf, uint32_t count, uint64_t offset); @@ -78,25 +75,19 @@ struct lib9p_srv_io_file_vtable { struct lib9p_srv_file { struct lib9p_srv_file_vtable *vtable; - /* Managed by srv.c */ - struct lib9p_srv_file *_parent_dir; - - /* This is where your implementation data goes. */ - char data[0]; -}; - -struct lib9p_srv_io { - union { - struct lib9p_srv_io_dir_vtable *dir; - struct lib9p_srv_io_file_vtable *file; - } vtable; + /* Managed by srv.c, but should be cloned by ->vtable->clone(). */ + struct lib9p_srv_file *_parent_dir; /* clone this - struct lib9p_srv_file *file; - struct lib9p_qid qid; - uint32_t iounit; + /* Managed by srv.c, and should be initialized to 0 by ->vtable->clone(). */ + /* ref type 1: an entry in fidmap + * ref type 2: ->_parent_dir of another file */ + struct lib9p_srv_file *_refcount; + lib9p_o_t _io_flags; + struct lib9p_qid _io_qid; + uint32_t _io_unit; /* This is where your implementation data goes. */ - char data[0]; + char data[0]; }; /* main server entrypoints ****************************************************/ diff --git a/lib9p/srv.c b/lib9p/srv.c index 9d1b4ed..8940afe 100644 --- a/lib9p/srv.c +++ b/lib9p/srv.c @@ -365,12 +365,39 @@ static void handle_message(struct _lib9p_srv_req *ctx) { } } +#define handler_common(ctx, req, resp) do { \ + assert(ctx); \ + assert(req); \ + assert(resp); \ + resp->tag = req->tag; \ + } while (0) + +static inline bool check_perm(struct lib9p_srv_ctx *ctx, struct lib9p_stat *stat, uint8_t action) { + assert(ctx); + assert(stat); + assert(action); + + /* TODO actually check user and group instead of just assuming "other". */ + uint8_t mode = (uint8_t)(stat->file_mode & 07); + + return mode & action; +} + +static inline bool release(struct lib9p_srv_ctx *ctx, struct lib9p_srv_file *file) { + assert(file); + file->_refcount--; + if (file->_refcount == 0) { + if (file->_parent_dir != file) + release(ctx, file->_parent_dir); + file->vtable->free(ctx, file); + } + return lib9p_ctx_has_error(&ctx->basectx); +} + static void handle_Tversion(struct _lib9p_srv_req *ctx, struct lib9p_msg_Tversion *req, struct lib9p_msg_Rversion *resp) { - assert(ctx); - assert(req); - assert(resp); + handler_common(ctx, req, resp); enum lib9p_version version = LIB9P_VER_unknown; @@ -440,13 +467,6 @@ static void handle_Tversion(struct _lib9p_srv_req *ctx, ctx->parent_sess->rerror_overhead = min_msg_size; } -#define handler_common(ctx, req, resp) do { \ - assert(ctx); \ - assert(req); \ - assert(resp); \ - resp->tag = req->tag; \ - } while (0) - static void handle_Tauth(struct _lib9p_srv_req *ctx, struct lib9p_msg_Tauth *req, struct lib9p_msg_Rauth *resp) { @@ -522,11 +542,12 @@ static void handle_Tattach(struct _lib9p_srv_req *ctx, if (lib9p_ctx_has_error(&ctx->ctx.basectx)) return; rootdir->_parent_dir = rootdir; + rootdir->_refcount++; if (!fidmap_store(&ctx->parent_sess->fids, req->fid, rootdir)) { lib9p_error(&ctx->ctx.basectx, LINUX_EMFILE, "too many open files"); - rootdir->vtable->free(&ctx->ctx, rootdir); + release(&ctx->ctx, rootdir); return; } @@ -546,7 +567,7 @@ static void handle_Tattach(struct _lib9p_srv_req *ctx, static void handle_Tflush(struct _lib9p_srv_req *ctx, struct lib9p_msg_Tflush *req, struct lib9p_msg_Rflush *resp) { - handler_common(ctx, req, resp);; + handler_common(ctx, req, resp); lib9p_error(&ctx->ctx.basectx, LINUX_EOPNOTSUPP, "flush not yet implemented"); @@ -577,6 +598,7 @@ static void handle_Twalk(struct _lib9p_srv_req *ctx, assert((dirp == NULL) == lib9p_ctx_has_error(&ctx->ctx.basectx)); if (lib9p_ctx_has_error(&ctx->ctx.basectx)) return; + dirp->_refcount++; /* ref-A: presumptive insertion into fidmap */ } resp->wqid = (struct lib9p_qid *)(&resp[1]); @@ -588,7 +610,9 @@ static void handle_Twalk(struct _lib9p_srv_req *ctx, struct lib9p_stat stat = dirp->vtable->stat(&ctx->ctx, dirp); if (lib9p_ctx_has_error(&ctx->ctx.basectx)) break; - if (!(stat.file_qid.type & LIB9P_QT_DIR) || !(stat.file_mode & LIB9P_DM_DIR)) { + assert( ((bool)(stat.file_qid.type & LIB9P_QT_DIR)) == + ((bool)(stat.file_mode & LIB9P_DM_DIR)) ); + if (!(stat.file_mode & LIB9P_DM_DIR)) { lib9p_error(&ctx->ctx.basectx, LINUX_ENOTDIR, "not a directory"); break; @@ -598,14 +622,27 @@ static void handle_Twalk(struct _lib9p_srv_req *ctx, assert((memberp == NULL) == lib9p_ctx_has_error(&ctx->ctx.basectx)); if (lib9p_ctx_has_error(&ctx->ctx.basectx)) break; + memberp->_parent_dir = dirp; + dirp->_refcount++; /* memberp->_parent_dir */ } + memberp->_refcount++; /* presumptively take ref-A */ struct lib9p_stat stat = memberp->vtable->stat(&ctx->ctx, memberp); - if (lib9p_ctx_has_error(&ctx->ctx.basectx)) + if (lib9p_ctx_has_error(&ctx->ctx.basectx)) { + release(&ctx->ctx, memberp); /* presumption of taking ref-A failed */ + break; + } + if (!check_perm(&ctx->ctx, &stat, 0b001)) { + lib9p_error(&ctx->ctx.basectx, + LINUX_EACCES, "you do not have execute permission on that directory"); + release(&ctx->ctx, memberp); /* presumption of taking ref-A failed */ break; + } + resp->wqid[resp->nwqid] = stat.file_qid; - dirp->vtable->free(&ctx->ctx, dirp); + /* presumption of taking ref-A succeeded */ + release(&ctx->ctx, dirp); dirp = memberp; } if (resp->nwqid == req->nwname) { @@ -615,21 +652,89 @@ static void handle_Twalk(struct _lib9p_srv_req *ctx, if (!fidmap_store(&ctx->parent_sess->fids, req->newfid, dirp)) { lib9p_error(&ctx->ctx.basectx, LINUX_EMFILE, "too many open files"); - dirp->vtable->free(&ctx->ctx, dirp); + release(&ctx->ctx, dirp); /* presumption of insertion failed */ } - } else if (resp->nwqid > 0) { - ctx->ctx.basectx.err_num = 0; - ctx->ctx.basectx.err_msg[0] = '\0'; + } else { + assert(lib9p_ctx_has_error(&ctx->ctx.basectx)); + if (req->newfid != req->fid) + release(&ctx->ctx, dirp); /* presumption of insertion failed */ + if (resp->nwqid > 0) + lib9p_ctx_clear_error(&ctx->ctx.basectx); } } static void handle_Topen(struct _lib9p_srv_req *ctx, struct lib9p_msg_Topen *req, struct lib9p_msg_Ropen *resp) { - handler_common(ctx, req, resp);; + handler_common(ctx, req, resp); - lib9p_error(&ctx->ctx.basectx, - LINUX_EOPNOTSUPP, "open not yet implemented"); + struct lib9p_srv_file **filepp = fidmap_load(&ctx->parent_sess->fids, req->fid); + if (!filepp) { + lib9p_errorf(&ctx->ctx.basectx, + LINUX_EBADF, "bad file number %"PRIu32, req->fid); + return; + } + struct lib9p_srv_file *filep = *filepp; + + struct lib9p_stat stat = filep->vtable->stat(&ctx->ctx, filep); + if (lib9p_ctx_has_error(&ctx->ctx.basectx)) + return; + assert( ((bool)(stat.file_qid.type & LIB9P_QT_DIR)) == + ((bool)(stat.file_mode & LIB9P_DM_DIR)) ); + + lib9p_o_t flags = req->mode; + if (stat.file_mode & LIB9P_DM_DIR) { + if ( ((flags & LIB9P_O_MODE_MASK) != LIB9P_O_READ) || + (flags & LIB9P_O_TRUNC) || + (flags & LIB9P_O_RCLOSE) ) { + lib9p_error(&ctx->ctx.basectx, + LINUX_EISDIR, "directories cannot be written, executed, truncated, or removed-on-close"); + return; + } + } else { + if (stat.file_mode & LIB9P_QT_APPEND) + flags = flags & ~LIB9P_O_TRUNC; + } + if (flags & LIB9P_O_RCLOSE) { + struct lib9p_stat parent_stat = filep->_parent_dir->vtable->stat(&ctx->ctx, filep->_parent_dir); + if (lib9p_ctx_has_error(&ctx->ctx.basectx)) + return; + if (!check_perm(&ctx->ctx, &parent_stat, 0b010)) { + lib9p_error(&ctx->ctx.basectx, + LINUX_EACCES, "permission denied to remove-on-close"); + return; + } + } + uint8_t perm_bits = 0; + switch (flags & LIB9P_O_MODE_MASK) { + case LIB9P_O_READ: + perm_bits = 0b100; + break; + case LIB9P_O_WRITE: + perm_bits = 0b010; + break; + case LIB9P_O_RDWR: + perm_bits = 0b110; + break; + case LIB9P_O_EXEC: + perm_bits = 0b001; + break; + } + if (!check_perm(&ctx->ctx, &stat, perm_bits)) { + lib9p_error(&ctx->ctx.basectx, + LINUX_EACCES, "permission denied"); + } + + uint32_t iounit = filep->vtable->io(&ctx->ctx, filep, flags); + if (lib9p_ctx_has_error(&ctx->ctx.basectx)) + return; + + filep->_io_flags = flags; + filep->_io_unit = iounit; + filep->_io_qid = stat.file_qid; + + resp->qid = filep->_io_qid; + resp->iounit = filep->_io_unit; } static void handle_Tcreate(struct _lib9p_srv_req *ctx, @@ -702,7 +807,7 @@ static void handle_Tstat(struct _lib9p_srv_req *ctx, static void handle_Twstat(struct _lib9p_srv_req *ctx, struct lib9p_msg_Twstat *req, struct lib9p_msg_Rwstat *resp) { - handler_common(ctx, req, resp);; + handler_common(ctx, req, resp); lib9p_error(&ctx->ctx.basectx, LINUX_EOPNOTSUPP, "wstat not yet implemented"); |