summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke T. Shumaker <lukeshu@lukeshu.com>2024-10-10 21:48:16 -0600
committerLuke T. Shumaker <lukeshu@lukeshu.com>2024-10-10 21:48:16 -0600
commit2145808d29f495cf872d9b3ffe05420ff9fc5be6 (patch)
tree78ec87bcb6bdf9a19bd6f5a2cb6cb3f8333bfdbc
parentdb92bd35807305e6daec5ec40bd67cbc43fe88ee (diff)
implement dread
-rw-r--r--cmd/srv9p/static.c37
-rw-r--r--lib9p/9p.c47
-rw-r--r--lib9p/9p.generated.c24
-rwxr-xr-xlib9p/idl.gen18
-rw-r--r--lib9p/include/lib9p/9p.h29
-rw-r--r--lib9p/include/lib9p/srv.h26
-rw-r--r--lib9p/internal.h4
-rw-r--r--lib9p/srv.c56
8 files changed, 205 insertions, 36 deletions
diff --git a/cmd/srv9p/static.c b/cmd/srv9p/static.c
index 912ec03..a6da53f 100644
--- a/cmd/srv9p/static.c
+++ b/cmd/srv9p/static.c
@@ -99,6 +99,35 @@ static struct lib9p_srv_file *static_dir_dcreate(struct lib9p_srv_ctx *ctx, stru
return NULL;
}
+static size_t static_dir_dread(struct lib9p_srv_ctx *ctx, struct lib9p_srv_file *_dir,
+ uint8_t *buf,
+ uint32_t byte_count,
+ size_t _obj_offset) {
+ assert(ctx);
+ struct static_dir *dir = (struct static_dir *)_dir;
+ assert(dir);
+
+ uint32_t byte_offset = 0;
+ size_t obj_offset = _obj_offset;
+ while (dir->members[obj_offset]) {
+ struct lib9p_srv_file *filep = dir->members[obj_offset];
+ struct lib9p_stat stat = filep->vtable->stat(ctx, filep);
+ if (lib9p_ctx_has_error(&ctx->basectx))
+ break;
+ uint32_t nbytes = lib9p_marshal_stat(&ctx->basectx, byte_count-byte_offset, &stat,
+ &buf[byte_offset]);
+ if (!nbytes) {
+ if (obj_offset == _obj_offset)
+ lib9p_error(&ctx->basectx,
+ LINUX_ERANGE, "stat object does not fit into negotiated max message size");
+ break;
+ }
+ byte_offset += nbytes;
+ obj_offset++;
+ }
+ return obj_offset - _obj_offset;
+}
+
struct lib9p_srv_file_vtable static_dir_vtable = {
.clone = static_dir_clone,
.free = static_dir_free,
@@ -110,10 +139,6 @@ struct lib9p_srv_file_vtable static_dir_vtable = {
.dopen = static_dir_dopen,
.dcreate = static_dir_dcreate,
-};
-/*
- struct lib9p_srv_io_dir_vtable static_dir_io_vtable = {
- .readdir = TODO,
- };
-*/
+ .dread = static_dir_dread,
+};
diff --git a/lib9p/9p.c b/lib9p/9p.c
index b1d4c32..832ae82 100644
--- a/lib9p/9p.c
+++ b/lib9p/9p.c
@@ -98,3 +98,50 @@ bool lib9p_marshal(struct lib9p_ctx *ctx, enum lib9p_msg_type typ, void *body,
struct _vtable_msg vtable = _lib9p_vtables[ctx->version].msgs[typ];
return vtable.marshal(&subctx, body);
}
+
+bool lib9p_validate_stat(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes,
+ uint32_t *ret_net_size, ssize_t *ret_host_size) {
+ struct _validate_ctx subctx = {
+ .ctx = ctx,
+ .net_size = net_size,
+ .net_bytes = net_bytes,
+
+ .net_offset = 0,
+ .host_extra = 0,
+ };
+ if (_lib9p_validate_stat(&subctx))
+ return true;
+ if (ret_net_size)
+ *ret_net_size = subctx.net_offset;
+ if (ret_host_size)
+ if (__builtin_add_overflow(sizeof(struct lib9p_stat), subctx.host_extra, ret_host_size))
+ return lib9p_error(ctx, LINUX_EMSGSIZE, "unmarshalled stat object overflows SSIZE_MAX");
+ return false;
+}
+
+uint32_t lib9p_unmarshal_stat(struct lib9p_ctx *ctx, uint8_t *net_bytes,
+ struct lib9p_stat *ret_obj, void *ret_extra) {
+ struct _unmarshal_ctx subctx = {
+ .ctx = ctx,
+ .net_bytes = net_bytes,
+ .net_offset = 0,
+
+ .extra = ret_extra,
+ };
+ _lib9p_unmarshal_stat(&subctx, ret_obj);
+ return subctx.net_offset;
+}
+
+uint32_t lib9p_marshal_stat(struct lib9p_ctx *ctx, uint32_t max_net_size, struct lib9p_stat *obj,
+ uint8_t *ret_bytes) {
+ struct lib9p_ctx _ctx = *ctx;
+ _ctx.max_msg_size = max_net_size;
+ struct _marshal_ctx subctx = {
+ .ctx = &_ctx,
+ .net_bytes = ret_bytes,
+ .net_offset = 0,
+ };
+ if (_lib9p_marshal_stat(&subctx, obj))
+ return 0;
+ return subctx.net_offset;
+}
diff --git a/lib9p/9p.generated.c b/lib9p/9p.generated.c
index 341acd0..660c925 100644
--- a/lib9p/9p.generated.c
+++ b/lib9p/9p.generated.c
@@ -1530,8 +1530,8 @@ static ALWAYS_INLINE bool marshal_qid(struct _marshal_ctx *ctx, struct lib9p_qid
static ALWAYS_INLINE bool marshal_stat(struct _marshal_ctx *ctx, struct lib9p_stat *val) {
uint32_t _stat_size_offset;
-
- uint32_t _kern_type_offset; return false
+ uint32_t _kern_type_offset;
+ return false
|| ({ _stat_size_offset = ctx->net_offset; ({ ctx->net_offset += 2; false; }); })
|| ({ _kern_type_offset = ctx->net_offset; marshal_2(ctx, &val->kern_type); })
|| marshal_4(ctx, &val->kern_dev)
@@ -1908,8 +1908,8 @@ static FLATTEN bool marshal_Rstat(struct _marshal_ctx *ctx, struct lib9p_msg_Rst
uint32_t _size_offset;
uint32_t _typ_offset;
uint32_t _nstat_offset;
-
- uint32_t _stat_offset; return false
+ uint32_t _stat_offset;
+ return false
|| ({ _size_offset = ctx->net_offset; ({ ctx->net_offset += 4; false; }); })
|| ({ _typ_offset = ctx->net_offset; ({ ctx->net_offset += 1; false; }); })
|| marshal_tag(ctx, &val->tag)
@@ -1925,8 +1925,8 @@ static FLATTEN bool marshal_Twstat(struct _marshal_ctx *ctx, struct lib9p_msg_Tw
uint32_t _size_offset;
uint32_t _typ_offset;
uint32_t _nstat_offset;
-
- uint32_t _stat_offset; return false
+ uint32_t _stat_offset;
+ return false
|| ({ _size_offset = ctx->net_offset; ({ ctx->net_offset += 4; false; }); })
|| ({ _typ_offset = ctx->net_offset; ({ ctx->net_offset += 1; false; }); })
|| marshal_tag(ctx, &val->tag)
@@ -2041,7 +2041,7 @@ static FLATTEN bool marshal_Rswrite(struct _marshal_ctx *ctx, struct lib9p_msg_R
;
}
-/* vtables ********************************************************************/
+/* vtables / exports **********************************************************/
#define _MSG(typ) [LIB9P_TYP_##typ] = { \
.basesize = sizeof(struct lib9p_msg_##typ), \
@@ -2157,3 +2157,13 @@ struct _vtable_version _lib9p_vtables[LIB9P_VER_NUM] = {
}},
#endif /* defined(CONFIG_9P_ENABLE_9P2000_u) */
};
+
+FLATTEN bool _lib9p_validate_stat(struct _validate_ctx *ctx) {
+ return validate_stat(ctx);
+}
+FLATTEN void _lib9p_unmarshal_stat(struct _unmarshal_ctx *ctx, struct lib9p_stat *out) {
+ unmarshal_stat(ctx, out);
+}
+FLATTEN bool _lib9p_marshal_stat(struct _marshal_ctx *ctx, struct lib9p_stat *val) {
+ return marshal_stat(ctx, val);
+}
diff --git a/lib9p/idl.gen b/lib9p/idl.gen
index 8e7639a..c0a2444 100755
--- a/lib9p/idl.gen
+++ b/lib9p/idl.gen
@@ -1045,7 +1045,7 @@ static ALWAYS_INLINE bool marshal_8(struct _marshal_ctx *ctx, uint64_t *val) {
for tok in member.val.tokens:
if isinstance(tok, ExprSym) and tok.name.startswith("&"):
if tok.name[1:] not in mark_offset:
- ret += f"\n\tuint32_t _{tok.name[1:]}_offset;"
+ ret += f"\tuint32_t _{tok.name[1:]}_offset;\n"
mark_offset.add(tok.name[1:])
# Pass 2 - main pass
@@ -1092,9 +1092,9 @@ static ALWAYS_INLINE bool marshal_8(struct _marshal_ctx *ctx, uint64_t *val) {
ret += "\t ;\n"
ret += "}\n"
- # vtables ##################################################################
+ # vtables / exports ########################################################
ret += f"""
-/* vtables ********************************************************************/
+/* vtables / exports **********************************************************/
#define _MSG(typ) [{idprefix.upper()}TYP_##typ] = {{ \\
.basesize = sizeof(struct {idprefix}msg_##typ), \\
@@ -1123,6 +1123,18 @@ struct _vtable_version _{idprefix}vtables[{c_ver_enum('NUM')}] = {{
ret += ifdef_pop(0)
ret += "};\n"
+ ret += f"""
+FLATTEN bool _{idprefix}validate_stat(struct _validate_ctx *ctx) {{
+ return validate_stat(ctx);
+}}
+FLATTEN void _{idprefix}unmarshal_stat(struct _unmarshal_ctx *ctx, struct lib9p_stat *out) {{
+ unmarshal_stat(ctx, out);
+}}
+FLATTEN bool _{idprefix}marshal_stat(struct _marshal_ctx *ctx, struct lib9p_stat *val) {{
+ return marshal_stat(ctx, val);
+}}
+"""
+
############################################################################
return ret
diff --git a/lib9p/include/lib9p/9p.h b/lib9p/include/lib9p/9p.h
index c3c6a8b..1e1667d 100644
--- a/lib9p/include/lib9p/9p.h
+++ b/lib9p/include/lib9p/9p.h
@@ -113,7 +113,7 @@ void lib9p_unmarshal(struct lib9p_ctx *ctx, uint8_t *net_bytes,
* @param typ : the message type
* @param msg : the message to encode
*
- * @return ret_bytes : the buffer to encode to, must be at be at least lib9p_ctx_max_msg_size(ctx) bytes
+ * @return ret_bytes : the buffer to encode to, must be at be at least ctx->max_msg_size bytes
* @return whether there was an error (false=success, true=error)
*
* @errno LINUX_ERANGE: reply does not fit in ctx->max_msg_size
@@ -121,4 +121,31 @@ void lib9p_unmarshal(struct lib9p_ctx *ctx, uint8_t *net_bytes,
bool lib9p_marshal(struct lib9p_ctx *ctx, enum lib9p_msg_type typ, void *body,
uint8_t *ret_bytes);
+/**
+ * TODO
+ *
+ * @return ret_net_size: number of bytes consumed; <=net_size
+ * @return ret_host_size: number of bytes that lib9p_unmarshal_stat would take
+ * @return whether there was an error
+ */
+bool lib9p_validate_stat(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes,
+ uint32_t *ret_net_size, ssize_t *ret_host_size);
+
+/**
+ * TODO
+ *
+ * @return ret_obj : where to put the stat object itself
+ * @return ret_extra : where to put strings for the stat object
+ * @return consumed net_bytes
+ */
+uint32_t lib9p_unmarshal_stat(struct lib9p_ctx *ctx, uint8_t *net_bytes,
+ struct lib9p_stat *ret_obj, void *ret_extra);
+
+/**
+ * @return ret_bytes: the buffer to encode into
+ * @return the number of bytes written, or 0 if the stat object does not fit in max_net_size
+ */
+uint32_t lib9p_marshal_stat(struct lib9p_ctx *ctx, uint32_t max_net_size, struct lib9p_stat *obj,
+ uint8_t *ret_bytes);
+
#endif _LIB9P_9P_H_
diff --git a/lib9p/include/lib9p/srv.h b/lib9p/include/lib9p/srv.h
index 5df6b1a..7aca76c 100644
--- a/lib9p/include/lib9p/srv.h
+++ b/lib9p/include/lib9p/srv.h
@@ -57,17 +57,20 @@ struct lib9p_srv_file_vtable {
lib9p_dm_t perm, lib9p_o_t flags);
/* 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);
+ size_t /* <- obj cnt */(*dread )(struct lib9p_srv_ctx *, struct lib9p_srv_file *,
+ uint8_t *buf,
+ uint32_t byte_count, /* <- num bytes */
+ size_t obj_offset); /* <- starting at this object */
/* non-directories - once opened */
- uint32_t (*pread )(struct lib9p_srv_ctx *, struct lib9p_srv_io_file *,
- void *buf, uint32_t count,
- uint64_t offset);
- uint32_t (*pwrite )(struct lib9p_srv_ctx *, struct lib9p_srv_io_file *,
- void *buf, uint32_t count,
- uint64_t offset);
+ uint32_t (*pread )(struct lib9p_srv_ctx *, struct lib9p_srv_file *,
+ void *buf,
+ uint32_t byte_count,
+ uint64_t byte_offset);
+ uint32_t (*pwrite )(struct lib9p_srv_ctx *, struct lib9p_srv_file *,
+ void *buf,
+ uint32_t byte_count,
+ uint64_t byte_offset);
};
/* objects you'll deal with ***************************************************/
@@ -83,8 +86,9 @@ struct lib9p_srv_file {
* 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;
+ bool _io_isdir;
+ size_t _io_dir_idx;
+ uint32_t _io_dir_off;
/* This is where your implementation data goes. */
char data[0];
diff --git a/lib9p/internal.h b/lib9p/internal.h
index 2b5f8f3..013024e 100644
--- a/lib9p/internal.h
+++ b/lib9p/internal.h
@@ -90,6 +90,10 @@ struct _vtable_version {
extern struct _vtable_version _lib9p_vtables[LIB9P_VER_NUM];
+bool _lib9p_validate_stat(struct _validate_ctx *ctx);
+void _lib9p_unmarshal_stat(struct _unmarshal_ctx *ctx, struct lib9p_stat *out);
+bool _lib9p_marshal_stat(struct _marshal_ctx *ctx, struct lib9p_stat *val);
+
/* unmarshal utilities ********************************************************/
static ALWAYS_INLINE uint8_t decode_u8le(uint8_t *in) {
diff --git a/lib9p/srv.c b/lib9p/srv.c
index 8940afe..04cd119 100644
--- a/lib9p/srv.c
+++ b/lib9p/srv.c
@@ -729,12 +729,10 @@ static void handle_Topen(struct _lib9p_srv_req *ctx,
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;
+ filep->_io_flags = filep->_io_flags | flags;
+ filep->_io_isdir = stat.file_mode & LIB9P_DM_DIR;
+ resp->qid = stat.file_qid;
+ resp->iounit = iounit;
}
static void handle_Tcreate(struct _lib9p_srv_req *ctx,
@@ -751,8 +749,50 @@ static void handle_Tread(struct _lib9p_srv_req *ctx,
struct lib9p_msg_Rread *resp) {
handler_common(ctx, req, resp);
- lib9p_error(&ctx->ctx.basectx,
- LINUX_EOPNOTSUPP, "read 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;
+
+ switch (filep->_io_flags & LIB9P_O_MODE_MASK) {
+ case LIB9P_O_READ:
+ case LIB9P_O_RDWR:
+ /* ok */
+ break;
+ default:
+ lib9p_error(&ctx->ctx.basectx,
+ LINUX_EINVAL, "FID not open for reading");
+ return;
+ }
+
+ resp->data.dat = (char *)(&resp[1]);
+ if (filep->_io_isdir) {
+ size_t idx;
+ if (req->offset == 0)
+ idx = 0;
+ else if (req->offset == filep->_io_dir_off)
+ idx = filep->_io_dir_idx;
+ else {
+ lib9p_errorf(&ctx->ctx.basectx,
+ LINUX_EINVAL, "invalid offset (must be 0 or %"PRIu32"): %"PRIu32,
+ filep->_io_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;
+ 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;
+ resp->data.len = len;
+ } else
+ resp->data.len = filep->vtable->pread(&ctx->ctx, filep, resp->data.dat, req->count, req->offset);
}
static void handle_Twrite(struct _lib9p_srv_req *ctx,