summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xbuild-aux/valgrind1
-rw-r--r--lib9p/tests/test_server/CMakeLists.txt1
-rw-r--r--lib9p/tests/test_server/fs_slowread.c101
-rw-r--r--lib9p/tests/test_server/fs_slowread.h22
-rw-r--r--lib9p/tests/test_server/main.c5
-rwxr-xr-xlib9p/tests/testclient-p9p4
-rw-r--r--lib9p/tests/testclient-p9p.explog20
-rw-r--r--lib9p/tests/testclient-sess.c31
-rw-r--r--lib9p/tests/testclient-sess.explog32
9 files changed, 205 insertions, 12 deletions
diff --git a/build-aux/valgrind b/build-aux/valgrind
index 728faca..7ad2712 100755
--- a/build-aux/valgrind
+++ b/build-aux/valgrind
@@ -6,6 +6,7 @@
exec \
valgrind \
+ --fair-sched=yes \
--error-exitcode=2 \
--leak-check=full \
--show-leak-kinds=all \
diff --git a/lib9p/tests/test_server/CMakeLists.txt b/lib9p/tests/test_server/CMakeLists.txt
index 19c8edb..eb16165 100644
--- a/lib9p/tests/test_server/CMakeLists.txt
+++ b/lib9p/tests/test_server/CMakeLists.txt
@@ -10,6 +10,7 @@ if (PICO_PLATFORM STREQUAL "host")
add_library(test_server_objs OBJECT
main.c
fs_shutdown.c
+ fs_slowread.c
)
target_include_directories(test_server_objs PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/config)
target_include_directories(test_server_objs PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
diff --git a/lib9p/tests/test_server/fs_slowread.c b/lib9p/tests/test_server/fs_slowread.c
new file mode 100644
index 0000000..520edd2
--- /dev/null
+++ b/lib9p/tests/test_server/fs_slowread.c
@@ -0,0 +1,101 @@
+/* lib9p/tests/test_server/fs_slowread.c - slowread API endpoint
+ *
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include "fs_slowread.h"
+
+LO_IMPLEMENTATION_C(lib9p_srv_file, struct slowread_file, slowread_file, static);
+
+LO_IMPLEMENTATION_H(lib9p_srv_fio, struct slowread_file, slowread_file);
+LO_IMPLEMENTATION_C(lib9p_srv_fio, struct slowread_file, slowread_file, static);
+
+/* srv_file *******************************************************************/
+
+static void slowread_file_free(struct slowread_file *self) {
+ assert(self);
+}
+static struct lib9p_qid slowread_file_qid(struct slowread_file *self) {
+ assert(self);
+ return (struct lib9p_qid){
+ .type = LIB9P_QT_FILE,
+ .vers = 1,
+ .path = self->pathnum,
+ };
+}
+
+static struct lib9p_stat slowread_file_stat(struct slowread_file *self, struct lib9p_srv_ctx *ctx) {
+ assert(self);
+ assert(ctx);
+ return (struct lib9p_stat){
+ .kern_type = 0,
+ .kern_dev = 0,
+ .file_qid = slowread_file_qid(self),
+ .file_mode = 0444,
+ .file_atime = UTIL9P_ATIME,
+ .file_mtime = UTIL9P_MTIME,
+ .file_size = 6,
+ .file_name = lib9p_str(self->name),
+ .file_owner_uid = lib9p_str("root"),
+ .file_owner_gid = lib9p_str("root"),
+ .file_last_modified_uid = lib9p_str("root"),
+ .file_extension = lib9p_str(NULL),
+ .file_owner_n_uid = 0,
+ .file_owner_n_gid = 0,
+ .file_last_modified_n_uid = 0,
+ };
+}
+static void slowread_file_wstat(struct slowread_file *self, struct lib9p_srv_ctx *ctx, struct lib9p_stat) {
+ assert(self);
+ assert(ctx);
+ lib9p_error(&ctx->basectx, LINUX_EROFS, "cannot wstat API file");
+}
+static void slowread_file_remove(struct slowread_file *self, struct lib9p_srv_ctx *ctx) {
+ assert(self);
+ assert(ctx);
+ lib9p_error(&ctx->basectx, LINUX_EROFS, "cannot remove API file");
+}
+
+LIB9P_SRV_NOTDIR(struct slowread_file, slowread_file)
+
+static lo_interface lib9p_srv_fio slowread_file_fopen(struct slowread_file *self, struct lib9p_srv_ctx *ctx, bool, bool, bool) {
+ assert(self);
+ assert(ctx);
+ return lo_box_slowread_file_as_lib9p_srv_fio(self);
+}
+
+/* srv_fio ********************************************************************/
+
+static void slowread_file_iofree(struct slowread_file *self) {
+ assert(self);
+}
+
+static uint32_t slowread_file_iounit(struct slowread_file *self) {
+ assert(self);
+ return 0;
+}
+
+static uint32_t slowread_file_pwrite(struct slowread_file *LM_UNUSED(self),
+ struct lib9p_srv_ctx *LM_UNUSED(ctx),
+ void *LM_UNUSED(buf), uint32_t LM_UNUSED(byte_count),
+ uint64_t LM_UNUSED(offset)) {
+ assert_notreached("not writable");
+}
+static void slowread_file_pread(struct slowread_file *self, struct lib9p_srv_ctx *ctx,
+ uint32_t byte_count, uint64_t LM_UNUSED(byte_offset),
+ struct iovec *ret) {
+ assert(self);
+ assert(ctx);
+ assert(ret);
+
+ while (!lib9p_srv_flush_requested(ctx))
+ cr_yield();
+ if (self->flushable)
+ lib9p_srv_acknowledge_flush(ctx);
+ else
+ *ret = (struct iovec){
+ .iov_base = "Sloth\n",
+ .iov_len = 6 < byte_count ? 6 : byte_count,
+ };
+}
diff --git a/lib9p/tests/test_server/fs_slowread.h b/lib9p/tests/test_server/fs_slowread.h
new file mode 100644
index 0000000..ef4b65f
--- /dev/null
+++ b/lib9p/tests/test_server/fs_slowread.h
@@ -0,0 +1,22 @@
+/* lib9p/tests/test_server/fs_slowread.h - slowread API endpoint
+ *
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#ifndef _LIB9P_TESTS_TEST_SERVER_FS_SLOWREAD_H_
+#define _LIB9P_TESTS_TEST_SERVER_FS_SLOWREAD_H_
+
+#include <util9p/static.h>
+#include <libhw/host_net.h>
+
+struct slowread_file {
+ char *name;
+ uint64_t pathnum;
+
+ bool flushable;
+};
+LO_IMPLEMENTATION_H(lib9p_srv_file, struct slowread_file, slowread_file);
+#define lo_box_slowread_file_as_lib9p_srv_file(obj) util9p_box(slowread_file, obj)
+
+#endif /* _LIB9P_TESTS_TEST_SERVER_FS_SLOWREAD_H_ */
diff --git a/lib9p/tests/test_server/main.c b/lib9p/tests/test_server/main.c
index 01a1738..e5e54e9 100644
--- a/lib9p/tests/test_server/main.c
+++ b/lib9p/tests/test_server/main.c
@@ -20,6 +20,7 @@
#include "static.h"
#include "fs_shutdown.h"
+#include "fs_slowread.h"
/* configuration **************************************************************/
@@ -74,6 +75,10 @@ struct lib9p_srv_file root =
API_FILE("shutdown", shutdown,
.listeners = globals.listeners,
.nlisteners = LM_ARRAY_LEN(globals.listeners)),
+ API_FILE("slowread", slowread,
+ .flushable = false),
+ API_FILE("slowread-flushable", slowread,
+ .flushable = true),
);
static lo_interface lib9p_srv_file get_root(struct lib9p_srv_ctx *LM_UNUSED(ctx), struct lib9p_s LM_UNUSED(treename)) {
diff --git a/lib9p/tests/testclient-p9p b/lib9p/tests/testclient-p9p
index 5e4c2c8..55ca069 100755
--- a/lib9p/tests/testclient-p9p
+++ b/lib9p/tests/testclient-p9p
@@ -25,7 +25,9 @@ out=$("${client[@]}" ls -l '')
expect_lines \
'd-r-xr-xr-x M 0 root root 0 Oct 7 2024 Documentation' \
'--r--r--r-- M 0 root root 166 Oct 7 2024 README.md' \
- '---w--w--w- M 0 root root 0 Oct 7 2024 shutdown'
+ '---w--w--w- M 0 root root 0 Oct 7 2024 shutdown' \
+ '--r--r--r-- M 0 root root 6 Oct 7 2024 slowread' \
+ '--r--r--r-- M 0 root root 6 Oct 7 2024 slowread-flushable'
out=$("${client[@]}" ls -l 'Documentation/')
expect_lines \
diff --git a/lib9p/tests/testclient-p9p.explog b/lib9p/tests/testclient-p9p.explog
index c9c3f55..7874de4 100644
--- a/lib9p/tests/testclient-p9p.explog
+++ b/lib9p/tests/testclient-p9p.explog
@@ -7,20 +7,20 @@
> Tauth { tag=0 afid=0 uname="lukeshu" aname="" n_uid=0 }
< Rerror { tag=0 errstr="authentication not required" errnum=95 }
> Tattach { tag=0 fid=0 afid=NOFID uname="lukeshu" aname="" n_uid=0 }
-< Rattach { tag=0 qid={ type=(DIR) vers=1 path=5 } }
+< Rattach { tag=0 qid={ type=(DIR) vers=1 path=7 } }
> Twalk { tag=0 fid=0 newfid=1 nwname=0 wname=[ ] }
< Rwalk { tag=0 nwqid=0 wqid=[ ] }
> Tstat { tag=0 fid=1 }
-< Rstat { tag=0 stat={ kern_type=0 kern_dev=0 file_qid={ type=(DIR) vers=1 path=5 } file_mode=(DIR|0555) file_atime=1728337905 file_mtime=1728337904 file_size=0 file_name="" file_owner_uid="root" file_owner_gid="root" file_last_modified_uid="root" file_extension="" file_owner_n_uid=0 file_owner_n_gid=0 file_last_modified_n_uid=0 } }
+< Rstat { tag=0 stat={ kern_type=0 kern_dev=0 file_qid={ type=(DIR) vers=1 path=7 } file_mode=(DIR|0555) file_atime=1728337905 file_mtime=1728337904 file_size=0 file_name="" file_owner_uid="root" file_owner_gid="root" file_last_modified_uid="root" file_extension="" file_owner_n_uid=0 file_owner_n_gid=0 file_last_modified_n_uid=0 } }
> Tclunk { tag=0 fid=1 }
< Rclunk { tag=0 }
> Twalk { tag=0 fid=0 newfid=1 nwname=0 wname=[ ] }
< Rwalk { tag=0 nwqid=0 wqid=[ ] }
> Topen { tag=0 fid=1 mode=(MODE_READ) }
-< Ropen { tag=0 qid={ type=(DIR) vers=1 path=5 } iounit=0 }
+< Ropen { tag=0 qid={ type=(DIR) vers=1 path=7 } iounit=0 }
> Tread { tag=0 fid=1 offset=0 count=4096 }
-< Rread { tag=0 count=213 data=<bytedata> }
-> Tread { tag=0 fid=1 offset=213 count=4096 }
+< Rread { tag=0 count=361 data=<bytedata> }
+> Tread { tag=0 fid=1 offset=361 count=4096 }
< Rread { tag=0 count=0 data="" }
> Tclunk { tag=0 fid=1 }
< Rclunk { tag=0 }
@@ -29,7 +29,7 @@
> Tauth { tag=0 afid=0 uname="lukeshu" aname="" n_uid=0 }
< Rerror { tag=0 errstr="authentication not required" errnum=95 }
> Tattach { tag=0 fid=0 afid=NOFID uname="lukeshu" aname="" n_uid=0 }
-< Rattach { tag=0 qid={ type=(DIR) vers=1 path=5 } }
+< Rattach { tag=0 qid={ type=(DIR) vers=1 path=7 } }
> Twalk { tag=0 fid=0 newfid=1 nwname=1 wname=["Documentation" ] }
< Rwalk { tag=0 nwqid=1 wqid=[{ type=(DIR) vers=1 path=2 } ] }
> Tstat { tag=0 fid=1 }
@@ -51,7 +51,7 @@
> Tauth { tag=0 afid=0 uname="lukeshu" aname="" n_uid=0 }
< Rerror { tag=0 errstr="authentication not required" errnum=95 }
> Tattach { tag=0 fid=0 afid=NOFID uname="lukeshu" aname="" n_uid=0 }
-< Rattach { tag=0 qid={ type=(DIR) vers=1 path=5 } }
+< Rattach { tag=0 qid={ type=(DIR) vers=1 path=7 } }
> Twalk { tag=0 fid=0 newfid=1 nwname=1 wname=["README.md" ] }
< Rwalk { tag=0 nwqid=1 wqid=[{ type=(0) vers=1 path=3 } ] }
> Topen { tag=0 fid=1 mode=(MODE_READ) }
@@ -67,7 +67,7 @@
> Tauth { tag=0 afid=0 uname="lukeshu" aname="" n_uid=0 }
< Rerror { tag=0 errstr="authentication not required" errnum=95 }
> Tattach { tag=0 fid=0 afid=NOFID uname="lukeshu" aname="" n_uid=0 }
-< Rattach { tag=0 qid={ type=(DIR) vers=1 path=5 } }
+< Rattach { tag=0 qid={ type=(DIR) vers=1 path=7 } }
> Twalk { tag=0 fid=0 newfid=1 nwname=2 wname=["Documentation", "x" ] }
< Rwalk { tag=0 nwqid=2 wqid=[{ type=(DIR) vers=1 path=2 }, { type=(0) vers=1 path=1 } ] }
> Topen { tag=0 fid=1 mode=(MODE_READ) }
@@ -83,7 +83,7 @@
> Tauth { tag=0 afid=0 uname="lukeshu" aname="" n_uid=0 }
< Rerror { tag=0 errstr="authentication not required" errnum=95 }
> Tattach { tag=0 fid=0 afid=NOFID uname="lukeshu" aname="" n_uid=0 }
-< Rattach { tag=0 qid={ type=(DIR) vers=1 path=5 } }
+< Rattach { tag=0 qid={ type=(DIR) vers=1 path=7 } }
> Twalk { tag=0 fid=0 newfid=1 nwname=2 wname=["Documentation", "x" ] }
< Rwalk { tag=0 nwqid=2 wqid=[{ type=(DIR) vers=1 path=2 }, { type=(0) vers=1 path=1 } ] }
> Tstat { tag=0 fid=1 }
@@ -95,7 +95,7 @@
> Tauth { tag=0 afid=0 uname="lukeshu" aname="" n_uid=0 }
< Rerror { tag=0 errstr="authentication not required" errnum=95 }
> Tattach { tag=0 fid=0 afid=NOFID uname="lukeshu" aname="" n_uid=0 }
-< Rattach { tag=0 qid={ type=(DIR) vers=1 path=5 } }
+< Rattach { tag=0 qid={ type=(DIR) vers=1 path=7 } }
> Twalk { tag=0 fid=0 newfid=1 nwname=1 wname=["shutdown" ] }
< Rwalk { tag=0 nwqid=1 wqid=[{ type=(0) vers=1 path=4 } ] }
> Topen { tag=0 fid=1 mode=(TRUNC|MODE_WRITE) }
diff --git a/lib9p/tests/testclient-sess.c b/lib9p/tests/testclient-sess.c
index 54b8722..423dc2c 100644
--- a/lib9p/tests/testclient-sess.c
+++ b/lib9p/tests/testclient-sess.c
@@ -96,6 +96,37 @@ int main(int argc, char *argv[]) {
recv9p(); /* Rversion */
ctx.version = LIB9P_VER_9P2000_u;
+ /* main session *******************************************************/
+ send9p(Tversion, .tag=0, .max_msg_size=(8*1024), .version=lib9p_str("9P2000"));
+ recv9p(); /* Rversion */
+ ctx.version = LIB9P_VER_9P2000;
+ send9p(Tattach, .tag=0, .fid=0, .afid=LIB9P_FID_NOFID, .uname=lib9p_str("nobody"), .aname=lib9p_str(""));
+ recv9p(); /* Rattach */
+
+ /* flush, but original response comes back first */
+ wname[0] = lib9p_str("slowread"); send9p(Twalk, .tag=0, .fid=0, .newfid=1, .nwname=1, .wname=wname);
+ recv9p(); /* Rwalk */
+ send9p(Topen, .tag=0, .fid=1, .mode=LIB9P_O_MODE_READ);
+ recv9p(); /* Ropen */
+ send9p(Tread, .tag=1, .fid=1, .offset=0, .count=6);
+ send9p(Tflush, .tag=2, .oldtag=1);
+ recv9p(); /* Rread */
+ recv9p(); /* Rflush */
+
+ /* flush, original request is aborted with error */
+ wname[0] = lib9p_str("slowread-flushable"); send9p(Twalk, .tag=1, .fid=0, .newfid=2, .nwname=1, .wname=wname);
+ recv9p(); /* Rwalk */
+ send9p(Topen, .tag=0, .fid=2, .mode=LIB9P_O_MODE_READ);
+ recv9p(); /* Ropen */
+ send9p(Tread, .tag=1, .fid=2, .offset=0, .count=6);
+ send9p(Tflush, .tag=2, .oldtag=1);
+ recv9p(); /* Rerror */
+ recv9p(); /* Rflush */
+
+ /* flush, unknown tag */
+ send9p(Tflush, .tag=0, .oldtag=99);
+ recv9p(); /* Rflush */
+
/* shutdown ***********************************************************/
send9p(Tversion, .tag=0, .max_msg_size=(8*1024), .version=lib9p_str("9P2000"));
recv9p(); /* Rversion */
diff --git a/lib9p/tests/testclient-sess.explog b/lib9p/tests/testclient-sess.explog
index 4932553..e086341 100644
--- a/lib9p/tests/testclient-sess.explog
+++ b/lib9p/tests/testclient-sess.explog
@@ -15,11 +15,41 @@
> Tversion { tag=0 max_msg_size=57 version="9P2000.u" }
< Rversion { tag=0 max_msg_size=57 version="9P2000.u" }
+# main session #################################################################
+> Tversion { tag=0 max_msg_size=8192 version="9P2000" }
+< Rversion { tag=0 max_msg_size=4120 version="9P2000" }
+> Tattach { tag=0 fid=0 afid=NOFID uname="nobody" aname="" n_uid=0 }
+< Rattach { tag=0 qid={ type=(DIR) vers=1 path=7 } }
+
+# flush, but original response comes back first
+> Twalk { tag=0 fid=0 newfid=1 nwname=1 wname=["slowread" ] }
+< Rwalk { tag=0 nwqid=1 wqid=[{ type=(0) vers=1 path=5 } ] }
+> Topen { tag=0 fid=1 mode=(MODE_READ) }
+< Ropen { tag=0 qid={ type=(0) vers=1 path=5 } iounit=0 }
+> Tread { tag=1 fid=1 offset=0 count=6 }
+> Tflush { tag=2 oldtag=1 }
+< Rread { tag=1 count=6 data="Sloth\n" }
+< Rflush { tag=2 }
+
+# flush, succeeds
+> Twalk { tag=1 fid=0 newfid=2 nwname=1 wname=["slowread-flushable" ] }
+< Rwalk { tag=1 nwqid=1 wqid=[{ type=(0) vers=1 path=6 } ] }
+> Topen { tag=0 fid=2 mode=(MODE_READ) }
+< Ropen { tag=0 qid={ type=(0) vers=1 path=6 } iounit=0 }
+> Tread { tag=1 fid=2 offset=0 count=6 }
+> Tflush { tag=2 oldtag=1 }
+< Rflush { tag=2 }
+< Rerror { tag=1 errstr="request canceled by flush" errnum=125 }
+
+# flush, unknown tag
+> Tflush { tag=0 oldtag=99 }
+< Rflush { tag=0 }
+
# shutdown #####################################################################
> Tversion { tag=0 max_msg_size=8192 version="9P2000" }
< Rversion { tag=0 max_msg_size=4120 version="9P2000" }
> Tattach { tag=0 fid=0 afid=NOFID uname="nobody" aname="" n_uid=0 }
-< Rattach { tag=0 qid={ type=(DIR) vers=1 path=5 } }
+< Rattach { tag=0 qid={ type=(DIR) vers=1 path=7 } }
> Twalk { tag=0 fid=0 newfid=0 nwname=1 wname=["shutdown" ] }
< Rwalk { tag=0 nwqid=1 wqid=[{ type=(0) vers=1 path=4 } ] }
> Topen { tag=0 fid=0 mode=(MODE_WRITE) }