summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--components/dfs/Kconfig6
-rw-r--r--components/dfs/dfs_v1/filesystems/9pfs/SConscript11
-rw-r--r--components/dfs/dfs_v1/filesystems/9pfs/dfs_9pfs.c1186
-rw-r--r--components/dfs/dfs_v1/filesystems/9pfs/dfs_9pfs.h247
-rw-r--r--components/finsh/msh_file.c2
5 files changed, 1451 insertions, 1 deletions
diff --git a/components/dfs/Kconfig b/components/dfs/Kconfig
index 23799d1b44..5fed4359e2 100644
--- a/components/dfs/Kconfig
+++ b/components/dfs/Kconfig
@@ -163,6 +163,12 @@ endif
default y
if RT_USING_DFS_V1
+ config RT_USING_DFS_9PFS
+ bool "Using Plan 9 remote filesystem"
+ select RT_USING_ADT_BITMAP
+ depends on RT_USING_MEMHEAP
+ default n
+
config RT_USING_DFS_ISO9660
bool "Using ISO9660 filesystem"
depends on RT_USING_MEMHEAP
diff --git a/components/dfs/dfs_v1/filesystems/9pfs/SConscript b/components/dfs/dfs_v1/filesystems/9pfs/SConscript
new file mode 100644
index 0000000000..7d875570e4
--- /dev/null
+++ b/components/dfs/dfs_v1/filesystems/9pfs/SConscript
@@ -0,0 +1,11 @@
+# RT-Thread building script for component
+
+from building import *
+
+cwd = GetCurrentDir()
+src = Glob('*.c')
+CPPPATH = [cwd]
+
+group = DefineGroup('Filesystem', src, depend = ['RT_USING_DFS', 'RT_USING_DFS_9PFS'], CPPPATH = CPPPATH)
+
+Return('group')
diff --git a/components/dfs/dfs_v1/filesystems/9pfs/dfs_9pfs.c b/components/dfs/dfs_v1/filesystems/9pfs/dfs_9pfs.c
new file mode 100644
index 0000000000..f874a5f383
--- /dev/null
+++ b/components/dfs/dfs_v1/filesystems/9pfs/dfs_9pfs.c
@@ -0,0 +1,1186 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2023-02-25 GuEe-GUI the first version
+ */
+
+#include <rthw.h>
+#include <rtthread.h>
+#include <rtservice.h>
+
+#define DBG_TAG "dfs.9p"
+#define DBG_LVL DBG_INFO
+#include <rtdbg.h>
+
+#include "dfs_9pfs.h"
+#include <drivers/misc.h>
+#include <drivers/byteorder.h>
+#include <posix/string.h>
+
+#define OREAD 0 /* open for read */
+#define OWRITE 1 /* write */
+#define ORDWR 2 /* read and write */
+#define OEXEC 3 /* execute, == read but check execute permission */
+#define OEXCL 4
+#define OTRUNC 16 /* or'ed in (except for exec), truncate file first */
+#define OCEXEC 32 /* or'ed in, close on exec */
+#define ORCLOSE 64 /* or'ed in, remove on close */
+#define OAPPEND 128 /* or'ed in, append */
+
+#define NOTAG ((rt_uint16_t)~0)
+#define NOFID ((rt_uint32_t)~0)
+#define TAG 1
+
+static rt_list_t _protocol_nodes = RT_LIST_OBJECT_INIT(_protocol_nodes);
+static struct rt_spinlock _protocol_lock = { 0 };
+
+#ifndef rt_le8_to_cpu
+#define rt_le8_to_cpu
+#endif
+#ifndef rt_cpu_to_le8
+#define rt_cpu_to_le8
+#endif
+
+#define P9_OF_OPS_GET(width, dir) \
+rt_inline rt_uint##width##_t get_##dir##_##value##width##_of( \
+ struct p9_connection *conn, unsigned idx) \
+{ \
+ rt_uint##width##_t *vp = (void *)&conn->dir##_buffer[idx]; \
+ return rt_le##width##_to_cpu(*vp); \
+}
+
+#define P9_OF_OPS_PUT(width, dir) \
+rt_inline void put_##dir##_##value##width##_of( \
+ struct p9_connection *conn, unsigned idx, \
+ rt_uint##width##_t value) \
+{ \
+ rt_uint##width##_t *vp = (void *)&conn->dir##_buffer[idx]; \
+ *vp = rt_cpu_to_le##width(value); \
+}
+
+#define P9_OPS_PUT(width) \
+rt_inline rt_off_t put_value##width(struct p9_connection *conn, \
+ rt_off_t off, rt_uint##width##_t value) \
+{ \
+ ((rt_uint##width##_t *)(conn->tx_buffer + off))[0] = \
+ rt_cpu_to_le##width(value); \
+ return off + sizeof(value); \
+}
+
+#define P9_OPS_GROUP(width) \
+ P9_OF_OPS_GET(width, rx) \
+ P9_OF_OPS_GET(width, tx) \
+ P9_OF_OPS_PUT(width, rx) \
+ P9_OF_OPS_PUT(width, tx) \
+ P9_OPS_PUT(width)
+
+P9_OPS_GROUP(8)
+P9_OPS_GROUP(16)
+P9_OPS_GROUP(32)
+P9_OPS_GROUP(64)
+#undef P9_OF_OPS_GET
+#undef P9_OF_OPS_PUT
+#undef P9_OPS_GET
+#undef P9_OPS_GROUP
+
+rt_inline rt_uint32_t put_header(struct p9_connection *conn,
+ rt_uint8_t hd, rt_uint16_t tag_flags)
+{
+ rt_uint8_t *stack = conn->tx_buffer;
+
+ ((rt_uint32_t *)stack)[0] = rt_cpu_to_le32(0);
+ stack += sizeof(rt_uint32_t);
+
+ stack[0] = hd;
+ stack += sizeof(rt_uint8_t);
+
+ ((rt_uint16_t *)stack)[0] = rt_cpu_to_le16(tag_flags);
+ stack += sizeof(rt_uint16_t);
+
+ return stack - conn->tx_buffer;
+}
+
+rt_inline rt_off_t put_bytes(struct p9_connection *conn, rt_off_t off,
+ rt_uint8_t *bytes, rt_uint32_t count)
+{
+ rt_uint8_t *stack = conn->tx_buffer + off;
+
+ ((rt_uint16_t *)stack)[0] = rt_cpu_to_le16(count);
+ stack += sizeof(rt_uint16_t);
+
+ rt_memcpy(stack, bytes, count);
+ stack += count;
+
+ return stack - conn->tx_buffer;
+}
+
+rt_inline rt_off_t put_string(struct p9_connection *conn, rt_off_t off,
+ char *string)
+{
+ rt_uint32_t len = rt_strlen(string);
+ rt_uint8_t *stack = conn->tx_buffer + off;
+
+ ((rt_uint16_t *)stack)[0] = rt_cpu_to_le16(len);
+ stack += sizeof(rt_uint16_t);
+
+ rt_memcpy(stack, string, len);
+ stack += len;
+
+ return stack - conn->tx_buffer;
+}
+
+struct p9_connection *p9_connection_alloc(struct p9_protocol *p9p,
+ const char *aname, rt_uint32_t buffer_size)
+{
+ struct p9_connection *conn;
+
+ if (!p9p || !aname)
+ {
+ return RT_NULL;
+ }
+
+ /* Buffer size expected to be 1M */
+ buffer_size = rt_max_t(rt_uint32_t, buffer_size, 0x100000);
+
+ if (!(conn = rt_malloc(sizeof(*conn) + buffer_size)))
+ {
+ return RT_NULL;
+ }
+
+ conn->msg_size = buffer_size;
+ conn->fid = P9_ROOT_FID;
+#ifdef RT_USING_SMART
+ conn->uname = "rt-smart";
+#else
+ conn->uname = "rt-thread";
+#endif
+ conn->aname = (char *)aname;
+
+ conn->rx_buffer = (void *)conn + sizeof(*conn);
+ conn->tx_buffer = conn->rx_buffer + buffer_size / 2;
+ rt_mutex_init(&conn->lock, conn->aname, RT_IPC_FLAG_PRIO);
+
+ conn->protocol = p9p;
+
+ return conn;
+}
+
+rt_err_t p9_connection_free(struct p9_connection *conn)
+{
+ rt_free(conn);
+
+ return RT_EOK;
+}
+
+int p9_transaction(struct p9_connection *conn,
+ rt_uint32_t tx_size, rt_uint32_t *out_rx_size)
+{
+ rt_err_t err;
+ rt_uint32_t rx_size;
+
+ RT_ASSERT(conn != RT_NULL);
+ RT_ASSERT(conn->protocol != RT_NULL);
+
+ rx_size = conn->msg_size;
+ if ((err = conn->protocol->transport(conn->protocol,
+ conn->tx_buffer, tx_size, conn->rx_buffer, &rx_size)))
+ {
+ return P9_TRANSPORT_ERROR;
+ }
+
+ if (get_rx_value16_of(conn, P9_MSG_TAG) !=
+ get_tx_value16_of(conn, P9_MSG_TAG))
+ {
+ return P9_UNEXPECTED_TAG;
+ }
+
+ if (get_rx_value8_of(conn, P9_MSG_ID) == P9_MSG_ERR)
+ {
+ rt_uint32_t err_len = rt_min_t(rt_uint32_t, sizeof(conn->error) - 1,
+ get_rx_value16_of(conn, P9_MSG_ERR_STR_LEN));
+
+ rt_strncpy(conn->error, (void *)&conn->rx_buffer[P9_MSG_ERR_STR], err_len);
+
+ return P9_R_ERROR;
+ }
+
+ if ((get_tx_value8_of(conn, P9_MSG_ID) + 1) !=
+ get_rx_value8_of(conn, P9_MSG_ID))
+ {
+ return P9_UNEXPECTED_MSG;
+ }
+
+ if (out_rx_size)
+ {
+ *out_rx_size = rx_size;
+ }
+
+ return 0;
+}
+
+int p9_version(struct p9_connection *conn)
+{
+ int rc;
+ rt_uint32_t size;
+
+ RT_ASSERT(conn != RT_NULL);
+
+ rt_mutex_take(&conn->lock, RT_WAITING_FOREVER);
+
+ /*
+ * size[4] Tversion tag[2] msize[4] version[s]
+ * size[4] Rversion tag[2] msize[4] version[s]
+ */
+ size = put_header(conn, P9_HD_TVERSION, NOTAG);
+ size = put_value32(conn, size, conn->msg_size);
+ size = put_string(conn, size, P9_VERSION);
+ put_tx_value32_of(conn, P9_MSG_SIZE, size);
+
+ if (!(rc = p9_transaction(conn, size, RT_NULL)))
+ {
+ char *version;
+ rt_uint32_t version_len;
+ rt_uint32_t msg_size = get_rx_value32_of(conn, P9_MSG_VER_MSIZE);
+
+ conn->msg_size = rt_min_t(rt_uint32_t, conn->msg_size, msg_size);
+
+ version = (char *)&conn->rx_buffer[P9_MSG_VER_STR];
+ version_len = get_rx_value16_of(conn, P9_MSG_VER_STR_LEN);
+
+ LOG_D("Version: %s", version);
+
+ if (!rt_strncmp(P9_VERSION_UNKNOWN, version, version_len))
+ {
+ rc = P9_UNKNOWN_VERSION;
+ }
+ }
+
+ rt_mutex_release(&conn->lock);
+
+ return rc;
+}
+
+int p9_attach(struct p9_connection *conn)
+{
+ int rc;
+ rt_uint32_t size;
+
+ RT_ASSERT(conn != RT_NULL);
+
+ if (19 + rt_strlen(conn->uname) + rt_strlen(conn->aname) > conn->msg_size)
+ {
+ return P9_MSG_TOO_LONG;
+ }
+
+ rt_mutex_take(&conn->lock, RT_WAITING_FOREVER);
+
+ /*
+ * size[4] Tattach tag[2] fid[4] afid[4] uname[s] aname[s]
+ * size[4] Rattach tag[2] qid[13]
+ */
+ size = put_header(conn, P9_HD_TATTACH, TAG);
+ size = put_value32(conn, size, conn->fid);
+ size = put_value32(conn, size, NOFID);
+ size = put_string(conn, size, conn->uname);
+ size = put_string(conn, size, conn->aname);
+ size = put_value32(conn, size, RT_UINT32_MAX);
+ put_tx_value32_of(conn, P9_MSG_SIZE, size);
+
+ rc = p9_transaction(conn, size, RT_NULL);
+
+ rt_mutex_release(&conn->lock);
+
+ return rc;
+}
+
+static int p9_clunk_lockess(struct p9_connection *conn, rt_uint32_t fid)
+{
+ rt_uint32_t size;
+
+ /*
+ * size[4] Tclunk tag[2] fid[4]
+ * size[4] Rclunk tag[2]
+ */
+ size = put_header(conn, P9_HD_TCLUNK, TAG);
+ size = put_value32(conn, size, fid);
+ put_tx_value32_of(conn, P9_MSG_SIZE, size);
+
+ return p9_transaction(conn, size, RT_NULL);
+}
+
+int p9_clunk(struct p9_connection *conn, rt_uint32_t fid)
+{
+ int rc;
+
+ RT_ASSERT(conn != RT_NULL);
+
+ rt_mutex_take(&conn->lock, RT_WAITING_FOREVER);
+
+ rc = p9_clunk_lockess(conn, fid);
+
+ rt_mutex_release(&conn->lock);
+
+ return rc;
+}
+
+rt_err_t dfs_9pfs_add_tag(struct p9_protocol *p9p)
+{
+ rt_ubase_t level;
+
+ if (!p9p || !p9p->tag || !p9p->transport)
+ {
+ return -RT_EINVAL;
+ }
+
+ rt_list_init(&p9p->list);
+
+ level = rt_spin_lock_irqsave(&_protocol_lock);
+ rt_list_insert_before(&_protocol_nodes, &p9p->list);
+ rt_spin_unlock_irqrestore(&_protocol_lock, level);
+
+ LOG_D("Add tag(%s) on %s", p9p->tag, p9p->name);
+
+ return RT_EOK;
+}
+
+rt_err_t dfs_9pfs_del_tag(struct p9_protocol *p9p)
+{
+ rt_ubase_t level;
+
+ if (!p9p)
+ {
+ return -RT_EINVAL;
+ }
+
+ level = rt_spin_lock_irqsave(&_protocol_lock);
+ rt_list_remove(&p9p->list);
+ rt_spin_unlock_irqrestore(&_protocol_lock, level);
+
+ LOG_D("Delete tag(%s) on %s", p9p->tag, p9p->name);
+
+ return RT_EOK;
+}
+
+static int p9_to_fs_err(int rc)
+{
+ const int p9_err[] =
+ {
+ [0] = RT_EOK,
+ [-P9_ERROR] = -EIO,
+ [-P9_UNKNOWN_VERSION] = -ENOSYS,
+ [-P9_R_ERROR] = -EIO,
+ [-P9_MSG_TOO_LONG] = -ENOSPC,
+ [-P9_UNEXPECTED_MSG] = -EINVAL,
+ [-P9_UNEXPECTED_TAG] = -EINVAL,
+ [-P9_TRANSPORT_ERROR] = -EIO,
+ [-P9_NO_TRANSPORT] = -EINVAL,
+ [-P9_NULL_PATH] = -EINVAL,
+ [-P9_PATH_ELEMENT_TOO_LONG] = -EINVAL,
+ [-P9_READ_UNEXPECTED_DATA] = -EINVAL,
+ [-P9_NO_BUFFER] = -ENOSPC,
+ [-P9_MSG_SIZE_TOO_BIG] = -ENOSPC,
+ };
+
+ return p9_err[-rc];
+}
+
+static int p9_alloc_fid(struct p9_connection *conn)
+{
+ int fid = rt_bitmap_next_clear_bit(conn->fid_map, 0, DFS_FD_MAX);
+
+ if (fid < DFS_FD_MAX)
+ {
+ rt_bitmap_set_bit(conn->fid_map, fid);
+
+ return fid + P9_FILE_FID;
+ }
+
+ return NOFID;
+}
+
+static void p9_free_fid(struct p9_connection *conn, int fid)
+{
+ if (fid >= P9_FILE_FID)
+ {
+ rt_bitmap_clear_bit(conn->fid_map, fid - P9_FILE_FID);
+ }
+}
+
+static int p9_free_fid_clunk(struct p9_connection *conn, int fid)
+{
+ int rc;
+
+ if (!(rc = p9_clunk_lockess(conn, fid)))
+ {
+ p9_free_fid(conn, fid);
+ }
+
+ return rc;
+}
+
+static rt_uint8_t fs_to_p9_flags(rt_uint32_t raw_flags)
+{
+ rt_uint8_t flags = 0;
+
+ switch (raw_flags & 3)
+ {
+ case O_RDONLY: flags = OREAD; break;
+ case O_WRONLY: flags = OWRITE; break;
+ case O_RDWR: flags = ORDWR; break;
+ default: RT_ASSERT(0); break;
+ }
+
+ if (raw_flags & O_TRUNC)
+ {
+ flags |= OTRUNC;
+ }
+
+ if (raw_flags & O_APPEND)
+ {
+ flags |= OAPPEND;
+ }
+
+ if (raw_flags & O_EXCL)
+ {
+ flags |= OEXCL;
+ }
+
+ return flags;
+}
+
+static char *p9_basename(const char *path)
+{
+ const char *basename = strrchr(path, '/');
+
+ return (char *)(basename ? basename + 1 : path);
+}
+
+static int p9_walk_path_raw(struct p9_connection *conn, const char *path,
+ rt_bool_t submode)
+{
+ rt_uint32_t size;
+ const char *split;
+ int rc, count = 0, len, fid = conn->fid, new_fid;
+
+ new_fid = p9_alloc_fid(conn);
+
+ if (new_fid < 0)
+ {
+ return P9_NO_BUFFER;
+ }
+
+ if (submode)
+ {
+ --count;
+ }
+
+ /* Pass '/' */
+ ++path;
+
+ /*
+ * size[4] Twalk tag[2] fid[4] newfid[4] nwname[2] nwname*(wname[s])
+ * size[4] Rwalk tag[2] nwqid[2] nwqid*(wqid[13])
+ */
+ size = put_header(conn, P9_HD_TWALK, TAG);
+ size = put_value32(conn, size, fid);
+ size = put_value32(conn, size, new_fid);
+ size = put_value16(conn, size, 0); /* Fill later */
+
+ do {
+ split = strchrnul(path, '/');
+
+ len = split - path;
+ size = put_bytes(conn, size, (rt_uint8_t *)path, len);
+
+ path += len + 1;
+ ++count;
+ } while (*split && count < P9_MSG_WALK_MAX_ELMT);
+
+ if (count < P9_MSG_WALK_MAX_ELMT)
+ {
+ put_value16(conn, P9_MSG_WALK_TX_ELMT, count);
+
+ if (!(rc = p9_transaction(conn, size, RT_NULL)))
+ {
+ if (count == get_rx_value16_of(conn, P9_MSG_WALK_RX_ELMT))
+ {
+ rc = new_fid;
+ }
+ else
+ {
+ /* Should be `P9_PARTIAL_WALK` if support longer path. */
+ rc = P9_ERROR;
+ p9_free_fid(conn, new_fid);
+ }
+ }
+ }
+ else
+ {
+ rc = P9_PATH_ELEMENT_TOO_LONG;
+ p9_free_fid(conn, new_fid);
+ }
+
+ return rc;
+}
+
+rt_inline int p9_walk_path(struct p9_connection *conn, const char *path)
+{
+ return p9_walk_path_raw(conn, path, RT_FALSE);
+}
+
+rt_inline int p9_walk_subpath(struct p9_connection *conn, const char *path)
+{
+ return p9_walk_path_raw(conn, path, RT_TRUE);
+}
+
+static int dfs_9pfs_open(struct dfs_file *fd)
+{
+ int rc;
+ rt_uint32_t size;
+ rt_bool_t is_root;
+ struct p9_connection *conn;
+ struct p9_file *p9f = rt_calloc(1, sizeof(*p9f));
+
+ if (!p9f)
+ {
+ return -RT_ENOMEM;
+ }
+ conn = fd->vnode->fs->data;
+
+ p9f->fid = conn->fid;
+ p9f->connection = conn;
+ is_root = !rt_strcmp(fd->vnode->path, "/");
+
+ rt_mutex_take(&conn->lock, RT_WAITING_FOREVER);
+
+ if (!is_root)
+ {
+ if ((rc = p9_walk_path(p9f->connection, fd->vnode->path)) > 0)
+ {
+ p9f->fid = rc;
+ }
+ else if (fd->flags & O_CREAT)
+ {
+ rt_uint32_t parent_fid, perm = 0755;
+
+ if (fd->flags & O_DIRECTORY)
+ {
+ perm |= P9_STAT_MODE_DIR;
+ }
+
+ parent_fid = p9_walk_subpath(conn, fd->vnode->path);
+
+ /*
+ * size[4] Tcreate tag[2] fid[4] name[s] perm[4] mode[1] extension[s]
+ * size[4] Rcreate tag[2] qid[13] iounit[4]
+ */
+ size = put_header(conn, P9_HD_TCREATE, TAG);
+ size = put_value32(conn, size, parent_fid);
+ size = put_string(conn, size, p9_basename(fd->vnode->path));
+ size = put_value32(conn, size, perm);
+ size = put_value8(conn, size, fs_to_p9_flags(fd->flags));
+ size = put_string(conn, size, "");
+ put_tx_value32_of(conn, P9_MSG_SIZE, size);
+
+ if (!(rc = p9_transaction(conn, size, RT_NULL)))
+ {
+ p9f->iounit = get_rx_value32_of(conn, P9_MSG_CREATE_IOUNIT);
+
+ if ((rc = p9_walk_path(p9f->connection, fd->vnode->path)) > 0)
+ {
+ p9f->fid = rc;
+ }
+ }
+
+ p9_free_fid_clunk(conn, parent_fid);
+ }
+ }
+
+ if (is_root || rc >= 0)
+ {
+ /*
+ * size[4] Tstat tag[2] fid[4]
+ * size[4] Rstat tag[2] stat[n]
+ */
+ size = put_header(conn, P9_HD_TSTAT, TAG);
+ size = put_value32(conn, size, p9f->fid);
+ put_tx_value32_of(conn, P9_MSG_SIZE, size);
+
+ if (!(rc = p9_transaction(conn, size, RT_NULL)))
+ {
+ fd->vnode->size = get_rx_value64_of(conn, P9_MSG_STAT_LEN);
+
+ /*
+ * size[4] Topen tag[2] fid[4] mode[1]
+ * size[4] Ropen tag[2] qid[13] iounit[4]
+ */
+ size = put_header(conn, P9_HD_TOPEN, TAG);
+ size = put_value32(conn, size, p9f->fid);
+ size = put_value8(conn, size, fs_to_p9_flags(fd->flags));
+ put_tx_value32_of(conn, P9_MSG_SIZE, size);
+
+ if (!(rc = p9_transaction(conn, size, RT_NULL)))
+ {
+ p9f->iounit = get_rx_value32_of(conn, P9_MSG_OPEN_IOUNIT);
+ }
+ }
+ }
+
+ rt_mutex_release(&conn->lock);
+
+ if (!is_root && rc)
+ {
+ if (p9f->fid != conn->fid)
+ {
+ p9_free_fid(conn, p9f->fid);
+ }
+
+ rt_free(p9f);
+
+ return p9_to_fs_err(rc);
+ }
+
+ fd->vnode->data = p9f;
+
+ return 0;
+}
+
+static int dfs_9pfs_close(struct dfs_file *fd)
+{
+ int rc = 0;
+ struct p9_file *p9f = fd->vnode->data;
+ struct p9_connection *conn = p9f->connection;
+
+ if (p9f->fid != conn->fid)
+ {
+ rt_mutex_take(&conn->lock, RT_WAITING_FOREVER);
+ rc = p9_free_fid_clunk(conn, p9f->fid);
+ rt_mutex_release(&conn->lock);
+ }
+
+ if (!rc)
+ {
+ rt_free(p9f);
+ }
+
+ return p9_to_fs_err(rc);
+}
+
+static ssize_t dfs_9pfs_read(struct dfs_file *fd, void *buf, size_t count)
+{
+ int rc;
+ size_t tcount = 0;
+ rt_uint32_t max_count, set, size, got;
+ struct p9_file *p9f = fd->vnode->data;
+ struct p9_connection *conn = p9f->connection;
+
+ max_count = conn->msg_size - P9_MSG_READ_DATA;
+
+ rt_mutex_take(&conn->lock, RT_WAITING_FOREVER);
+
+ while ((ssize_t)count > 0)
+ {
+ set = rt_min_t(rt_uint32_t, max_count, count);
+
+ /*
+ * size[4] Tread tag[2] fid[4] offset[8] count[4]
+ * size[4] Rread tag[2] count[4] data[count]
+ */
+ size = put_header(conn, P9_HD_TREAD, TAG);
+ size = put_value32(conn, size, p9f->fid);
+ size = put_value64(conn, size, fd->pos);
+ size = put_value32(conn, size, set);
+ put_tx_value32_of(conn, P9_MSG_SIZE, size);
+
+ if (!(rc = p9_transaction(conn, size, RT_NULL)))
+ {
+ got = get_rx_value32_of(conn, P9_MSG_READ_COUNT);
+
+ if (got == 0)
+ {
+ break;
+ }
+ else if (got <= set)
+ {
+ fd->pos += got;
+
+ rt_memcpy(buf, conn->rx_buffer + P9_MSG_READ_DATA, got);
+ }
+ else
+ {
+ tcount = p9_to_fs_err(P9_READ_UNEXPECTED_DATA);
+ break;
+ }
+
+ count -= got;
+ tcount += got;
+ }
+ }
+
+ rt_mutex_release(&conn->lock);
+
+ return tcount;
+}
+
+static ssize_t dfs_9pfs_write(struct dfs_file *fd, const void *buf, size_t count)
+{
+ int rc;
+ size_t tcount = 0;
+ rt_uint32_t max_count, set, size, got;
+ struct p9_file *p9f = fd->vnode->data;
+ struct p9_connection *conn = p9f->connection;
+
+ max_count = conn->msg_size - P9_MSG_WRITE_DATA;
+
+ rt_mutex_take(&conn->lock, RT_WAITING_FOREVER);
+
+ while ((ssize_t)count > 0)
+ {
+ set = rt_min_t(rt_uint32_t, max_count, count);
+
+ /*
+ * size[4] Twrite tag[2] fid[4] offset[8] count[4] data[count]
+ * size[4] Rwrite tag[2] count[4]
+ */
+ size = put_header(conn, P9_HD_TWRITE, TAG);
+ size = put_value32(conn, size, p9f->fid);
+ size = put_value64(conn, size, fd->pos);
+ size = put_value32(conn, size, set);
+ rt_memcpy(conn->tx_buffer + P9_MSG_WRITE_DATA, buf, set);
+ size += set;
+ put_tx_value32_of(conn, P9_MSG_SIZE, size);
+
+ if (!(rc = p9_transaction(conn, size, RT_NULL)))
+ {
+ got = get_rx_value32_of(conn, P9_MSG_WRITE_COUNT);
+
+ if (got <= set)
+ {
+ fd->pos += set;
+ }
+ else
+ {
+ tcount = p9_to_fs_err(P9_READ_UNEXPECTED_DATA);
+ break;
+ }
+
+ count -= got;
+ tcount += got;
+ }
+ }
+
+ rt_mutex_release(&conn->lock);
+
+ return tcount;
+}
+
+static int dfs_9pfs_flush(struct dfs_file *fd)
+{
+ int rc;
+ rt_uint32_t size;
+ struct p9_file *p9f = fd->vnode->data;
+ struct p9_connection *conn = p9f->connection;
+
+ /*
+ * size[4] Tfsync tag[2] fid[4] datasync[4]
+ * size[4] Rfsync tag[2]
+ */
+ size = put_header(conn, P9_HD_TFSYNC, TAG);
+ size = put_value32(conn, size, p9f->fid);
+ size = put_value32(conn, size, 1);
+ put_tx_value32_of(conn, P9_MSG_SIZE, size);
+
+ rc = p9_transaction(conn, size, RT_NULL);
+
+ return p9_to_fs_err(rc);
+}
+
+static off_t dfs_9pfs_lseek(struct dfs_file *fd, off_t offset)
+{
+ int ret = -EIO;
+
+ if (offset <= fd->vnode->size)
+ {
+ fd->pos = offset;
+ ret = fd->pos;
+ }
+
+ return ret;
+}
+
+static int dfs_9pfs_getdents(struct dfs_file *fd, struct dirent *dirp, uint32_t count)
+{
+ int rc;
+ rt_uint64_t off;
+ rt_uint32_t ret, end, stat_size, size;
+ struct p9_file *p9f = fd->vnode->data;
+ struct p9_connection *conn = p9f->connection;
+
+ count = (count / sizeof(struct dirent));
+
+ if (!count)
+ {
+ return -EINVAL;
+ }
+ end = fd->pos + count;
+ count = 0;
+
+ rt_mutex_take(&conn->lock, RT_WAITING_FOREVER);
+
+ /*
+ * size[4] Tread tag[2] fid[4] offset[8] count[4]
+ * size[4] Rread tag[2] count[4] stat[n]
+ */
+ size = put_header(conn, P9_HD_TREAD, TAG);
+ size = put_value32(conn, size, p9f->fid);
+ size = put_value64(conn, size, 0);
+ size = put_value32(conn, size, fd->vnode->size);
+ put_tx_value32_of(conn, P9_MSG_SIZE, size);
+
+ if (!(rc = p9_transaction(conn, size, RT_NULL)))
+ {
+ ret = get_rx_value32_of(conn, P9_MSG_READ_COUNT);
+ off = P9_MSG_READ_DATA - P9_MSG_STAT_SIZE;
+
+ for (int i = 0; (rt_int32_t)ret > 0; off += stat_size, ret -= stat_size, ++i)
+ {
+ /*
+ * size[2] type[2] dev[4] qid[1+4+8] mode[4] atime[4] mtime[4] length[8]
+ */
+ stat_size = get_rx_value16_of(conn, off + P9_MSG_STAT_SIZE) + sizeof(rt_uint16_t);
+
+ if (i < fd->pos)
+ {
+ continue;
+ }
+
+ if (get_rx_value16_of(conn, off + P9_MSG_STAT_TYPE) & P9_STAT_MODE_DIR)
+ {
+ dirp->d_type = DT_DIR;
+ }
+ else
+ {
+ dirp->d_type = DT_REG;
+ }
+
+ /*
+ * name_size[2] name[name_size]
+ * uid_size[2] uid[uid_size]
+ * gid_size[2] gid[gid_size]
+ * muid_size[2] muid[muid_size]
+ * extension_size[2] extension[extension_size]
+ * n_uid[4]
+ * n_gid[4]
+ * n_muid[4]
+ */
+ dirp->d_namlen = get_rx_value16_of(conn, off + P9_MSG_STAT_NAME_LEN);
+ dirp->d_reclen = (rt_uint16_t)sizeof(struct dirent);
+ rt_strncpy(dirp->d_name,
+ (void *)conn->rx_buffer + off + P9_MSG_STAT_NAME,
+ dirp->d_namlen);
+ dirp->d_name[dirp->d_namlen] = '\0';
+
+ if (!rt_strcmp(dirp->d_name, ".") || !rt_strcmp(dirp->d_name, ".."))
+ {
+ ++fd->pos;
+ continue;
+ }
+
+ ++fd->pos;
+ ++dirp;
+ ++count;
+
+ if (fd->pos >= end)
+ {
+ break;
+ }
+ }
+ }
+
+ rt_mutex_release(&conn->lock);
+
+ count *= sizeof(struct dirent);
+
+ return count;
+}
+
+static const struct dfs_file_ops _9pfs_fops =
+{
+ .open = dfs_9pfs_open,
+ .close = dfs_9pfs_close,
+ .read = dfs_9pfs_read,
+ .write = dfs_9pfs_write,
+ .flush = dfs_9pfs_flush,
+ .lseek = dfs_9pfs_lseek,
+ .getdents = dfs_9pfs_getdents,
+};
+
+static int dfs_9pfs_mount(struct dfs_filesystem *fs,
+ unsigned long rwflag, const void *data)
+{
+ rt_ubase_t level;
+ struct p9_protocol *p9p, *p9p_tmp;
+ struct p9_connection *conn = RT_NULL;
+
+ if (!data)
+ {
+ return (int)-RT_EINVAL;
+ }
+
+ level = rt_spin_lock_irqsave(&_protocol_lock);
+
+ rt_list_for_each_entry(p9p_tmp, &_protocol_nodes, list)
+ {
+ if (!rt_strcmp(p9p_tmp->tag, data))
+ {
+ p9p = p9p_tmp;
+ break;
+ }
+ }
+
+ rt_spin_unlock_irqrestore(&_protocol_lock, level);
+
+ if (!p9p)
+ {
+ return (int)-RT_EINVAL;
+ }
+
+ if (!(conn = p9_connection_alloc(p9p, p9p->tag, 0)))
+ {
+ return (int)-RT_ENOMEM;
+ }
+
+ if (p9_version(conn) || p9_attach(conn))
+ {
+ p9_connection_free(conn);
+ return (int)-RT_EINVAL;
+ }
+
+ fs->data = conn;
+
+ return (int)RT_EOK;
+}
+
+static int dfs_9pfs_unmount(struct dfs_filesystem *fs)
+{
+ struct p9_connection *conn = fs->data;
+
+ p9_clunk(conn, conn->fid);
+
+ return p9_connection_free(conn);
+}
+
+static int dfs_9pfs_statfs(struct dfs_filesystem *fs, struct statfs *buf)
+{
+ int rc;
+ rt_uint32_t size;
+ struct p9_connection *conn = fs->data;
+
+ rt_mutex_take(&conn->lock, RT_WAITING_FOREVER);
+
+ /*
+ * size[4] Tstatfs tag[2] fid[4]
+ * size[4] Rstatfs tag[2]
+ * f_type[4]
+ * f_bsize[4]
+ * f_blocks[8]
+ * f_bfree[8]
+ * f_bavail[8]
+ * f_files[8]
+ * f_ffree[8]
+ * fsid_val[8]
+ * f_namelen[4]
+ */
+ size = put_header(conn, P9_HD_TSTATFS, TAG);
+ size = put_value32(conn, size, conn->fid);
+ put_tx_value32_of(conn, P9_MSG_SIZE, size);
+
+ if (!(rc = p9_transaction(conn, size, RT_NULL)))
+ {
+ buf->f_bsize = get_rx_value32_of(conn, P9_MSG_F_BSIZE);
+ buf->f_blocks = get_rx_value64_of(conn, P9_MSG_F_BLOCKS);
+ buf->f_bfree = get_rx_value64_of(conn, P9_MSG_F_BFREE);
+ buf->f_bavail = get_rx_value64_of(conn, P9_MSG_F_BAVAIL);
+ }
+
+ rt_mutex_release(&conn->lock);
+
+ return p9_to_fs_err(rc);
+}
+
+static int dfs_9pfs_unlink(struct dfs_filesystem *fs, const char *pathname)
+{
+ rt_uint32_t size;
+ int rc = 0, fid;
+ struct p9_connection *conn = fs->data;
+
+ rt_mutex_take(&conn->lock, RT_WAITING_FOREVER);
+
+ if ((rc = p9_walk_path(conn, pathname)) > 0)
+ {
+ fid = rc;
+
+ /*
+ * size[4] Tremove tag[2] fid[4]
+ * size[4] Rremove tag[2]
+ */
+ size = put_header(conn, P9_HD_TREMOVE, TAG);
+ size = put_value32(conn, size, fid);
+ put_tx_value32_of(conn, P9_MSG_SIZE, size);
+
+ rc = p9_transaction(conn, size, RT_NULL);
+ p9_free_fid_clunk(conn, fid);
+ }
+
+ rt_mutex_release(&conn->lock);
+
+ return p9_to_fs_err(rc);
+}
+
+static int dfs_9pfs_stat(struct dfs_filesystem *fs,
+ const char *filename, struct stat *st)
+{
+ int rc = 0, fid;
+ rt_uint32_t size, mode;
+ struct p9_connection *conn = fs->data;
+
+ rt_mutex_take(&conn->lock, RT_WAITING_FOREVER);
+
+ if ((rc = p9_walk_path(conn, filename)) > 0)
+ {
+ fid = rc;
+
+ /*
+ * size[4] Tstat tag[2] fid[4]
+ * size[4] Rstat tag[2] stat[n]
+ */
+ size = put_header(conn, P9_HD_TSTAT, TAG);
+ size = put_value32(conn, size, fid);
+ put_tx_value32_of(conn, P9_MSG_SIZE, size);
+
+ if (!(rc = p9_transaction(conn, size, RT_NULL)))
+ {
+ st->st_dev = 0;
+
+ mode = get_rx_value32_of(conn, P9_MSG_STAT_MODE);
+ st->st_mode = 0;
+ if (mode & P9_STAT_MODE_DIR)
+ {
+ st->st_mode |= S_IFDIR;
+ }
+ else
+ {
+ st->st_mode |= S_IFREG;
+ }
+ if (mode & P9_STAT_MODE_SYMLINK)
+ {
+ st->st_mode |= S_IFLNK;
+ }
+ if (mode & P9_STAT_MODE_SOCKET)
+ {
+ st->st_mode |= S_IFSOCK;
+ }
+ if (mode & P9_STAT_MODE_NAMED_PIPE)
+ {
+ st->st_mode |= S_IFIFO;
+ }
+ if (mode & P9_STAT_MODE_SETUID)
+ {
+ st->st_mode |= S_ISUID;
+ }
+ if (mode & P9_STAT_MODE_SETGID)
+ {
+ st->st_mode |= S_ISGID;
+ }
+ if (mode & P9_STAT_MODE_SETVTX)
+ {
+ st->st_mode |= S_ISVTX;
+ }
+
+ st->st_atime = get_rx_value32_of(conn, P9_MSG_STAT_ATIME);
+ st->st_mtime = get_rx_value32_of(conn, P9_MSG_STAT_MTIME);
+ st->st_size = get_rx_value64_of(conn, P9_MSG_STAT_LEN);
+
+ /*
+ * name_size[2] name[name_size]
+ * uid_size[2] uid[uid_size]
+ * gid_size[2] gid[gid_size]
+ * muid_size[2] muid[muid_size]
+ * extension_size[2] extension[extension_size]
+ * n_uid[4]
+ * n_gid[4]
+ * n_muid[4]
+ */
+ size = P9_MSG_STAT_NAME_LEN;
+ size += sizeof(rt_uint16_t) + get_rx_value16_of(conn, size);
+ size += sizeof(rt_uint16_t) + get_rx_value16_of(conn, size);
+ size += sizeof(rt_uint16_t) + get_rx_value16_of(conn, size);
+ size += sizeof(rt_uint16_t) + get_rx_value16_of(conn, size);
+ st->st_uid = get_rx_value32_of(conn, size) + sizeof(rt_uint32_t);
+ st->st_gid = get_rx_value32_of(conn, size) + sizeof(rt_uint32_t);
+ }
+
+ p9_free_fid_clunk(conn, fid);
+ }
+
+ rt_mutex_release(&conn->lock);
+
+ return p9_to_fs_err(rc);
+}
+
+static int dfs_9pfs_rename(struct dfs_filesystem *fs,
+ const char *oldpath, const char *newpath)
+{
+ int rc = 0, fid;
+ rt_uint32_t size;
+ struct p9_connection *conn = fs->data;
+
+ rt_mutex_take(&conn->lock, RT_WAITING_FOREVER);
+
+ if ((fid = p9_walk_path(conn, oldpath)) > 0)
+ {
+ /*
+ * size[4] Trename tag[2] fid[4] new_fid[4] name[s]
+ * size[4] Rrename tag[2]
+ */
+ size = put_header(conn, P9_HD_TRENAME, TAG);
+ size = put_value32(conn, size, fid);
+ size = put_value32(conn, size, NOFID);
+ size = put_string(conn, size, p9_basename(newpath));
+ put_tx_value32_of(conn, P9_MSG_SIZE, size);
+
+ rc = p9_transaction(conn, size, RT_NULL);
+ p9_free_fid_clunk(conn, fid);
+ }
+
+ rt_mutex_release(&conn->lock);
+
+ return p9_to_fs_err(rc);
+}
+
+static const struct dfs_filesystem_ops _9pfs =
+{
+ .name = "9p",
+ .flags = DFS_FS_FLAG_DEFAULT,
+ .fops = &_9pfs_fops,
+
+ .mount = dfs_9pfs_mount,
+ .unmount = dfs_9pfs_unmount,
+
+ .statfs = dfs_9pfs_statfs,
+
+ .unlink = dfs_9pfs_unlink,
+ .stat = dfs_9pfs_stat,
+ .rename = dfs_9pfs_rename,
+};
+
+int dfs_9pfs_init(void)
+{
+ /* register 9p file system */
+ return dfs_register(&_9pfs);
+}
+INIT_COMPONENT_EXPORT(dfs_9pfs_init);
diff --git a/components/dfs/dfs_v1/filesystems/9pfs/dfs_9pfs.h b/components/dfs/dfs_v1/filesystems/9pfs/dfs_9pfs.h
new file mode 100644
index 0000000000..8761249bd0
--- /dev/null
+++ b/components/dfs/dfs_v1/filesystems/9pfs/dfs_9pfs.h
@@ -0,0 +1,247 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2023-02-25 GuEe-GUI the first version
+ */
+
+#ifndef __DFS_9PFS_H__
+#define __DFS_9PFS_H__
+
+#include <rtdef.h>
+
+#include <dfs.h>
+#include <dfs_fs.h>
+#include <dfs_file.h>
+#include <bitmap.h>
+
+#define P9_ERROR -1
+#define P9_UNKNOWN_VERSION -2
+#define P9_R_ERROR -3
+#define P9_MSG_TOO_LONG -4
+#define P9_UNEXPECTED_MSG -5
+#define P9_UNEXPECTED_TAG -6
+#define P9_TRANSPORT_ERROR -7
+#define P9_NO_TRANSPORT -8
+#define P9_NULL_PATH -9
+#define P9_PATH_ELEMENT_TOO_LONG -10
+#define P9_READ_UNEXPECTED_DATA -11
+#define P9_NO_BUFFER -12
+#define P9_MSG_SIZE_TOO_BIG -13
+
+#define P9_PARTIAL_WALK 1
+
+#define P9_VERSION "9P2000.u"
+#define P9_VERSION_UNKNOWN "unknown"
+
+#define P9_MSG_F_TYPE 7
+#define P9_MSG_F_BSIZE 11
+#define P9_MSG_F_BLOCKS 15
+#define P9_MSG_F_BFREE 23
+#define P9_MSG_F_BAVAIL 31
+#define P9_MSG_F_FILES 39
+#define P9_MSG_F_FFREE 47
+#define P9_MSG_FSID_VAL 55
+#define P9_MSG_F_NAMELEN 63
+#define P9_MSG_SIZE 0
+#define P9_MSG_ID 4
+#define P9_MSG_ERR 0x6b
+#define P9_MSG_ERR_STR 9
+#define P9_MSG_ERR_STR_LEN 7
+#define P9_MSG_TAG 5
+#define P9_MSG_VER_MSIZE 7
+#define P9_MSG_VER_STR_LEN 11
+#define P9_MSG_VER_STR 13
+#define P9_MSG_WALK_TX_ELMT 15
+#define P9_MSG_WALK_RX_ELMT 7
+#define P9_MSG_WALK_MAX_ELMT 16
+#define P9_MSG_QID_SIZE 13
+#define P9_MSG_WALK_RX_HDR_SIZE 9
+#define P9_MSG_OPEN_IOUNIT 20
+#define P9_MSG_OPEN_MODE_MASK 0x5f
+#define P9_MSG_CREATE_IOUNIT 20
+#define P9_MSG_READ_COUNT 7
+#define P9_MSG_READ_DATA 11
+#define P9_MSG_WRITE_COUNT 7
+#define P9_MSG_WRITE_DATA 23
+#define P9_MSG_RENAME_DATA 15
+#define P9_MSG_STAT_SIZE 9
+#define P9_MSG_STAT_TYPE 11
+#define P9_MSG_STAT_DEV 13
+#define P9_MSG_STAT_QID_TYPE 17
+#define P9_MSG_STAT_QID_VERSION 18
+#define P9_MSG_STAT_QID_PATH 22
+#define P9_MSG_STAT_MODE 30
+#define P9_MSG_STAT_ATIME 34
+#define P9_MSG_STAT_MTIME 38
+#define P9_MSG_STAT_LEN 42
+#define P9_MSG_STAT_NAME_LEN 50
+#define P9_MSG_STAT_NAME 52
+
+#define P9_HD_TSTATFS 8
+#define P9_HD_RSTATFS (P9_HD_TSTATFS + 1)
+#define P9_HD_TRENAME 20
+#define P9_HD_RRENAME (P9_HD_TRENAME + 1)
+#define P9_HD_TFSYNC 50
+#define P9_HD_RFSYNC (P9_HD_TFSYNC + 1)
+#define P9_HD_TVERSION 100
+#define P9_HD_RVERSION (P9_HD_TVERSION + 1)
+#define P9_HD_TAUTH 102
+#define P9_HD_RAUTH (P9_HD_TAUTH + 1)
+#define P9_HD_TATTACH 104
+#define P9_HD_RATTACH (P9_HD_TATTACH + 1)
+#define P9_HD_TERROR 106
+#define P9_HD_RERROR (P9_HD_TERROR + 1)
+#define P9_HD_TFLUSH 108
+#define P9_HD_RFLUSH (P9_HD_TFLUSH + 1)
+#define P9_HD_TWALK 110
+#define P9_HD_RWALK (P9_HD_TWALK + 1)
+#define P9_HD_TOPEN 112
+#define P9_HD_ROPEN (P9_HD_TOPEN + 1)
+#define P9_HD_TCREATE 114
+#define P9_HD_RCREATE (P9_HD_TCREATE + 1)
+#define P9_HD_TREAD 116
+#define P9_HD_RREAD (P9_HD_TREAD + 1)
+#define P9_HD_TWRITE 118
+#define P9_HD_RWRITE (P9_HD_TWRITE + 1)
+#define P9_HD_TCLUNK 120
+#define P9_HD_RCLUNK (P9_HD_TCLUNK + 1)
+#define P9_HD_TREMOVE 122
+#define P9_HD_RREMOVE (P9_HD_TREMOVE + 1)
+#define P9_HD_TSTAT 124
+#define P9_HD_RSTAT (P9_HD_TSTAT + 1)
+
+#define P9_STAT_MODE_DIR 0x80000000
+#define P9_STAT_MODE_APPEND 0x40000000
+#define P9_STAT_MODE_EXCL 0x20000000
+#define P9_STAT_MODE_MOUNT 0x10000000
+#define P9_STAT_MODE_AUTH 0x08000000
+#define P9_STAT_MODE_TMP 0x04000000
+#define P9_STAT_MODE_SYMLINK 0x02000000
+#define P9_STAT_MODE_LINK 0x01000000
+#define P9_STAT_MODE_DEVICE 0x00800000
+#define P9_STAT_MODE_NAMED_PIPE 0x00200000
+#define P9_STAT_MODE_SOCKET 0x00100000
+#define P9_STAT_MODE_SETUID 0x00080000
+#define P9_STAT_MODE_SETGID 0x00040000
+#define P9_STAT_MODE_SETVTX 0x00010000
+
+struct p9_protocol
+{
+ rt_list_t list;
+ char *name;
+
+ const char *tag;
+ rt_err_t (*transport)(struct p9_protocol *p9p,
+ rt_uint8_t *tx_data, rt_uint32_t tx_size,
+ rt_uint8_t *rx_data, rt_uint32_t *ref_rx_size);
+};
+
+struct p9_connection
+{
+ char *uname; /* User name */
+ char *aname; /* Tree/mount name/path */
+ rt_uint32_t fid; /* Represents mount point */
+ rt_uint32_t msg_size;
+
+ char error[200];
+ rt_uint8_t *rx_buffer;
+ rt_uint8_t *tx_buffer;
+ struct rt_mutex lock;
+
+ struct p9_protocol *protocol;
+ RT_BITMAP_DECLARE(fid_map, DFS_FD_MAX);
+};
+
+struct p9_file
+{
+#define P9_ROOT_FID 1
+#define P9_FILE_FID 2
+ rt_uint32_t fid; /* Identifies the file to P9 server */
+ rt_uint32_t iounit; /* Maximum read size in bytes */
+
+ struct p9_connection *connection;
+};
+
+/**
+ * @brief Allocate a new 9P connection.
+ *
+ * @param p9p Pointer to the 9P protocol structure.
+ * @param aname Mount name/path.
+ * @param buffer_size Size of the buffer to allocate.
+ * @return Pointer to the allocated p9_connection structure, or RT_NULL on failure.
+ */
+struct p9_connection *p9_connection_alloc(struct p9_protocol *p9p,
+ const char *aname, rt_uint32_t buffer_size);
+
+/**
+ * @brief Free a 9P connection and release its resources.
+ *
+ * @param conn Pointer to the p9_connection to free.
+ * @return RT_EOK on success, error code otherwise.
+ */
+rt_err_t p9_connection_free(struct p9_connection *conn);
+
+/**
+ * @brief Perform a 9P transaction.
+ *
+ * @param conn Pointer to the p9_connection.
+ * @param tx_size Size of the transmit buffer.
+ * @param out_rx_size Pointer to store the received buffer size.
+ * @return 0 on success, negative error code otherwise.
+ */
+int p9_transaction(struct p9_connection *conn,
+ rt_uint32_t tx_size, rt_uint32_t *out_rx_size);
+
+/**
+ * @brief Negotiate 9P protocol version with the server.
+ *
+ * @param conn Pointer to the p9_connection.
+ * @return 0 on success, negative error code otherwise.
+ */
+int p9_version(struct p9_connection *conn);
+
+/**
+ * @brief Attach to the 9P server.
+ *
+ * @param conn Pointer to the p9_connection.
+ * @return 0 on success, negative error code otherwise.
+ */
+int p9_attach(struct p9_connection *conn);
+
+/**
+ * @brief Clunk (close) a file identifier (fid) on the 9P server.
+ *
+ * @param conn Pointer to the p9_connection.
+ * @param fid File identifier to clunk.
+ * @return 0 on success, negative error code otherwise.
+ */
+int p9_clunk(struct p9_connection *conn, rt_uint32_t fid);
+
+/**
+ * @brief Register a 9P protocol tag.
+ *
+ * @param p9p Pointer to the p9_protocol structure.
+ * @return RT_EOK on success, error code otherwise.
+ */
+rt_err_t dfs_9pfs_add_tag(struct p9_protocol *p9p);
+
+/**
+ * @brief Unregister a 9P protocol tag.
+ *
+ * @param p9p Pointer to the p9_protocol structure.
+ * @return RT_EOK on success, error code otherwise.
+ */
+rt_err_t dfs_9pfs_del_tag(struct p9_protocol *p9p);
+
+/**
+ * @brief Initialize the 9P filesystem.
+ *
+ * @return 0 on success, negative error code otherwise.
+ */
+int dfs_9pfs_init(void);
+
+#endif /* __DFS_9PFS_H__ */
diff --git a/components/finsh/msh_file.c b/components/finsh/msh_file.c
index d757f0c707..25fc9ac1a7 100644
--- a/components/finsh/msh_file.c
+++ b/components/finsh/msh_file.c
@@ -585,7 +585,7 @@ static int cmd_mount(int argc, char **argv)
/* mount a filesystem to the specified directory */
rt_kprintf("mount device %s(%s) onto %s ... ", device, fstype, path);
- if (rt_strcmp(fstype, "nfs") == 0)
+ if (rt_strcmp(fstype, "nfs") == 0 || rt_strcmp(fstype, "9p") == 0)
{
data = argv[1];
device = 0;