summaryrefslogtreecommitdiff
path: root/lib9p/srv.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib9p/srv.c')
-rw-r--r--lib9p/srv.c206
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,