diff options
Diffstat (limited to 'lib9p/srv.c')
-rw-r--r-- | lib9p/srv.c | 206 |
1 files changed, 118 insertions, 88 deletions
diff --git a/lib9p/srv.c b/lib9p/srv.c index 74aa158..a931106 100644 --- a/lib9p/srv.c +++ b/lib9p/srv.c @@ -15,9 +15,22 @@ /* structs ********************************************************************/ +#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 { + struct lib9p_srv_file *file; + uint8_t flags; + size_t dir_idx; + uint32_t dir_off; +}; + #define NAME fidmap #define KEY_T uint32_t -#define VAL_T struct lib9p_srv_file * +#define VAL_T struct _srv_fidinfo #define CAP CONFIG_9P_MAX_FIDS #include "map.h" @@ -464,8 +477,8 @@ static void handle_Tversion(struct _lib9p_srv_req *ctx, if (ctx->parent_sess->fids.len) { /* Close all FIDs. */ uint32_t fid; - struct lib9p_srv_file **filepp __attribute__((unused)); - MAP_FOREACH(&ctx->parent_sess->fids, fid, filepp) { + struct _srv_fidinfo *fidinfo __attribute__((unused)); + MAP_FOREACH(&ctx->parent_sess->fids, fid, fidinfo) { handle_Tclunk(ctx, &(struct lib9p_msg_Tclunk){.fid = fid}, &(struct lib9p_msg_Rclunk){}); @@ -555,7 +568,10 @@ static void handle_Tattach(struct _lib9p_srv_req *ctx, rootdir->_parent_dir = rootdir; rootdir->_refcount++; - if (!fidmap_store(&ctx->parent_sess->fids, req->fid, rootdir)) { + 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"); release(&ctx->ctx, rootdir); @@ -569,6 +585,7 @@ static void handle_Tattach(struct _lib9p_srv_req *ctx, &(struct lib9p_msg_Rclunk){}); return; } + assert(stat.file_mode & LIB9P_DM_DIR); resp->qid = stat.file_qid; @@ -589,8 +606,8 @@ static void handle_Twalk(struct _lib9p_srv_req *ctx, struct lib9p_msg_Rwalk *resp) { handler_common(ctx, req, resp); - struct lib9p_srv_file **dirpp = fidmap_load(&ctx->parent_sess->fids, req->fid); - if (!dirpp) { + 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; @@ -601,74 +618,74 @@ static void handle_Twalk(struct _lib9p_srv_req *ctx, return; } - struct lib9p_srv_file *dirp; - if (req->newfid == req->fid) - dirp = *dirpp; - else { - dirp = (*dirpp)->vtable->clone(&ctx->ctx, *dirpp); - assert((dirp == NULL) == lib9p_ctx_has_error(&ctx->ctx.basectx)); + struct lib9p_srv_file *dir = fidinfo->file; + if (req->newfid != req->fid) { + dir = dir->vtable->clone(&ctx->ctx, dir); + assert((dir == 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 */ + dir->_refcount++; /* ref-A: presumptive insertion into fidmap */ } + bool isdir = (fidinfo->flags & FIDFLAG_ISDIR); resp->wqid = (struct lib9p_qid *)(&resp[1]); for (resp->nwqid = 0; resp->nwqid < req->nwname; resp->nwqid++) { - struct lib9p_srv_file *memberp; + struct lib9p_srv_file *member; if (strcmp(req->wname[resp->nwqid].utf8, "..")) { - memberp = dirp->_parent_dir; + member = dir->_parent_dir; } else { - struct lib9p_stat stat = dirp->vtable->stat(&ctx->ctx, dirp); - if (lib9p_ctx_has_error(&ctx->ctx.basectx)) - break; - assert( ((bool)(stat.file_qid.type & LIB9P_QT_DIR)) == - ((bool)(stat.file_mode & LIB9P_DM_DIR)) ); - if (!(stat.file_mode & LIB9P_DM_DIR)) { + if (!isdir) { lib9p_error(&ctx->ctx.basectx, LINUX_ENOTDIR, "not a directory"); break; } - memberp = dirp->vtable->dopen(&ctx->ctx, dirp, req->wname[resp->nwqid].utf8); - assert((memberp == NULL) == lib9p_ctx_has_error(&ctx->ctx.basectx)); + member = dir->vtable->dopen(&ctx->ctx, dir, req->wname[resp->nwqid].utf8); + assert((member == 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 */ + member->_parent_dir = dir; + dir->_refcount++; /* member->_parent_dir */ } - memberp->_refcount++; /* presumptively take ref-A */ + member->_refcount++; /* presumptively take ref-A */ - struct lib9p_stat stat = memberp->vtable->stat(&ctx->ctx, memberp); + struct lib9p_stat stat = member->vtable->stat(&ctx->ctx, member); if (lib9p_ctx_has_error(&ctx->ctx.basectx)) { - release(&ctx->ctx, memberp); /* presumption of taking ref-A failed */ + release(&ctx->ctx, member); /* presumption of taking ref-A failed */ break; } - if (!check_perm(&ctx->ctx, &stat, 0b001)) { + isdir = stat.file_mode & LIB9P_DM_DIR; + if (isdir && !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 */ + release(&ctx->ctx, member); /* presumption of taking ref-A failed */ break; } resp->wqid[resp->nwqid] = stat.file_qid; /* presumption of taking ref-A succeeded */ - release(&ctx->ctx, dirp); - dirp = memberp; + release(&ctx->ctx, dir); + dir = member; } if (resp->nwqid == req->nwname) { - if (req->newfid == req->fid) - *dirpp = dirp; - else - if (!fidmap_store(&ctx->parent_sess->fids, req->newfid, dirp)) { - lib9p_error(&ctx->ctx.basectx, - LINUX_EMFILE, "too many open files"); - release(&ctx->ctx, dirp); /* presumption of insertion failed */ - } + 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"); + release(&ctx->ctx, dir); /* presumption of insertion failed */ + } } else { assert(lib9p_ctx_has_error(&ctx->ctx.basectx)); if (req->newfid != req->fid) - release(&ctx->ctx, dirp); /* presumption of insertion failed */ + release(&ctx->ctx, dir); /* presumption of insertion failed */ if (resp->nwqid > 0) lib9p_ctx_clear_error(&ctx->ctx.basectx); } @@ -679,35 +696,35 @@ static void handle_Topen(struct _lib9p_srv_req *ctx, struct lib9p_msg_Ropen *resp) { handler_common(ctx, req, resp); - struct lib9p_srv_file **filepp = fidmap_load(&ctx->parent_sess->fids, req->fid); - if (!filepp) { + /* Check that the FID is valid for this. */ + 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; } - struct lib9p_srv_file *filep = *filepp; - - struct lib9p_stat stat = filep->vtable->stat(&ctx->ctx, filep); - if (lib9p_ctx_has_error(&ctx->ctx.basectx)) + if (fidinfo->flags & FIDFLAG_OPEN) { + lib9p_error(&ctx->ctx.basectx, + LINUX_EALREADY, "FID is already open"); 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) ) { + } + if (fidinfo->flags & FIDFLAG_ISDIR) { + if ( ((req->mode & LIB9P_O_MODE_MASK) != LIB9P_O_READ) || + (req->mode & LIB9P_O_TRUNC) || + (req->mode & 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); + + lib9p_o_t reqmode = req->mode; + uint8_t fidflags = fidinfo->flags; + struct lib9p_srv_file *file = fidinfo->file; + + /* Check permissions. */ + if (reqmode & LIB9P_O_RCLOSE) { + struct lib9p_stat parent_stat = file->_parent_dir->vtable->stat(&ctx->ctx, file->_parent_dir); if (lib9p_ctx_has_error(&ctx->ctx.basectx)) return; if (!check_perm(&ctx->ctx, &parent_stat, 0b010)) { @@ -715,20 +732,30 @@ static void handle_Topen(struct _lib9p_srv_req *ctx, LINUX_EACCES, "permission denied to remove-on-close"); return; } + fidflags = fidflags | FIDFLAG_RCLOSE; } + struct lib9p_stat stat = file->vtable->stat(&ctx->ctx, file); + if (lib9p_ctx_has_error(&ctx->ctx.basectx)) + return; + if (stat.file_mode & LIB9P_QT_APPEND) + reqmode = reqmode & ~LIB9P_O_TRUNC; uint8_t perm_bits = 0; - switch (flags & LIB9P_O_MODE_MASK) { + switch (reqmode & LIB9P_O_MODE_MASK) { case LIB9P_O_READ: perm_bits = 0b100; + fidflags = fidflags | FIDFLAG_OPEN_R; break; case LIB9P_O_WRITE: perm_bits = 0b010; + fidflags = fidflags | FIDFLAG_OPEN_W; break; case LIB9P_O_RDWR: perm_bits = 0b110; + fidflags = fidflags | FIDFLAG_OPEN_R | FIDFLAG_OPEN_W; break; case LIB9P_O_EXEC: perm_bits = 0b001; + fidflags = fidflags | FIDFLAG_OPEN_R; break; } if (!check_perm(&ctx->ctx, &stat, perm_bits)) { @@ -736,12 +763,13 @@ static void handle_Topen(struct _lib9p_srv_req *ctx, LINUX_EACCES, "permission denied"); } - uint32_t iounit = filep->vtable->io(&ctx->ctx, filep, flags); + /* Actually make the call. */ + uint32_t iounit = file->vtable->io(&ctx->ctx, file, reqmode); if (lib9p_ctx_has_error(&ctx->ctx.basectx)) return; - filep->_io_flags = filep->_io_flags | flags; - filep->_io_isdir = stat.file_mode & LIB9P_DM_DIR; + /* Success. */ + fidinfo->flags = fidflags; resp->qid = stat.file_qid; resp->iounit = iounit; } @@ -760,50 +788,46 @@ static void handle_Tread(struct _lib9p_srv_req *ctx, struct lib9p_msg_Rread *resp) { handler_common(ctx, req, resp); - struct lib9p_srv_file **filepp = fidmap_load(&ctx->parent_sess->fids, req->fid); - if (!filepp) { + /* Check that the FID is valid for this. */ + 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; } - struct lib9p_srv_file *filep = *filepp; - - switch (filep->_io_flags & LIB9P_O_MODE_MASK) { - case LIB9P_O_READ: - case LIB9P_O_RDWR: - /* ok */ - break; - default: + if (!(fidinfo->flags & FIDFLAG_OPEN_R)) { lib9p_error(&ctx->ctx.basectx, LINUX_EINVAL, "FID not open for reading"); return; } + struct lib9p_srv_file *file = fidinfo->file; resp->data.dat = (char *)(&resp[1]); - if (filep->_io_isdir) { + + if (fidinfo->flags & FIDFLAG_ISDIR) { size_t idx; if (req->offset == 0) idx = 0; - else if (req->offset == filep->_io_dir_off) - idx = filep->_io_dir_idx; + else if (req->offset == fidinfo->dir_off) + idx = fidinfo->dir_idx; else { lib9p_errorf(&ctx->ctx.basectx, LINUX_EINVAL, "invalid offset (must be 0 or %"PRIu32"): %"PRIu32, - filep->_io_dir_off, req->offset); + fidinfo->dir_off, req->offset); return; } - size_t num = filep->vtable->dread(&ctx->ctx, filep, (uint8_t *)resp->data.dat, req->count, idx); - filep->_io_dir_idx = idx+num; + size_t num = file->vtable->dread(&ctx->ctx, file, (uint8_t *)resp->data.dat, req->count, idx); + fidinfo->dir_idx = idx+num; uint32_t len = 0; for (size_t i = 0; i < num; i++) { uint32_t i_len; lib9p_validate_stat(&ctx->ctx.basectx, req->count, (uint8_t *)resp->data.dat, &i_len, NULL); len += i_len; } - filep->_io_dir_off = req->offset + len; + fidinfo->dir_off = req->offset + len; resp->data.len = len; } else - resp->data.len = filep->vtable->pread(&ctx->ctx, filep, resp->data.dat, req->count, req->offset); + resp->data.len = file->vtable->pread(&ctx->ctx, file, resp->data.dat, req->count, req->offset); } static void handle_Twrite(struct _lib9p_srv_req *ctx, @@ -820,14 +844,20 @@ static void handle_Tclunk(struct _lib9p_srv_req *ctx, struct lib9p_msg_Rclunk *resp) { handler_common(ctx, req, resp); - struct lib9p_srv_file **filepp = fidmap_load(&ctx->parent_sess->fids, req->fid); - if (!filepp) { + 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 (fidinfo->flags & FIDFLAG_RCLOSE) { + handle_Tremove(ctx, + &(struct lib9p_msg_Tremove){.fid = req->fid}, + &(struct lib9p_msg_Rremove){}); + return; + } - (*filepp)->vtable->free(&ctx->ctx, *filepp); + fidinfo->file->vtable->free(&ctx->ctx, fidinfo->file); fidmap_del(&ctx->parent_sess->fids, req->fid); } @@ -845,14 +875,14 @@ static void handle_Tstat(struct _lib9p_srv_req *ctx, struct lib9p_msg_Rstat *resp) { handler_common(ctx, req, resp); - struct lib9p_srv_file **filepp = fidmap_load(&ctx->parent_sess->fids, req->fid); - if (!filepp) { + 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; } - resp->stat = (*filepp)->vtable->stat(&ctx->ctx, *filepp); + resp->stat = fidinfo->file->vtable->stat(&ctx->ctx, fidinfo->file); } static void handle_Twstat(struct _lib9p_srv_req *ctx, |