summaryrefslogtreecommitdiff
path: root/lib9p/srv.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib9p/srv.c')
-rw-r--r--lib9p/srv.c399
1 files changed, 268 insertions, 131 deletions
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);
}