summaryrefslogtreecommitdiff
path: root/lib9p
diff options
context:
space:
mode:
authorLuke T. Shumaker <lukeshu@lukeshu.com>2024-10-10 10:26:28 -0600
committerLuke T. Shumaker <lukeshu@lukeshu.com>2024-10-10 10:26:28 -0600
commitdb92bd35807305e6daec5ec40bd67cbc43fe88ee (patch)
tree8458933923545c87632e953a1c10a215efa78a0c /lib9p
parentbb5afed7a0eeaf361be1e29b3a3ab8ace2865b39 (diff)
lib9p: Implemnt Topen, add permission checks in Twalk
Diffstat (limited to 'lib9p')
-rw-r--r--lib9p/9p.c4
-rw-r--r--lib9p/idl/2002-9P2000.9p9
-rw-r--r--lib9p/include/lib9p/9p.generated.h30
-rw-r--r--lib9p/include/lib9p/9p.h6
-rw-r--r--lib9p/include/lib9p/srv.h37
-rw-r--r--lib9p/srv.c151
6 files changed, 174 insertions, 63 deletions
diff --git a/lib9p/9p.c b/lib9p/9p.c
index 0e71071..b1d4c32 100644
--- a/lib9p/9p.c
+++ b/lib9p/9p.c
@@ -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");