From 7a01e6de0240fb40448a5d2d6a99e2b58c925925 Mon Sep 17 00:00:00 2001 From: GuEe-GUI <2991707448@qq.com> Date: Mon, 1 Dec 2025 16:19:30 +0800 Subject: [PATCH] [DFS] Support 9PFS What is 9PFS (https://en.wikipedia.org/wiki/9P_(protocol)): 9P (or the Plan 9 Filesystem Protocol or Styx) is a network protocol developed for the Plan 9 from Bell Labs distributed operating system as the means of connecting the components of a Plan 9 system. Files are key objects in Plan 9. They represent windows, network connections, processes, and almost anything else available in the operating system. rt-thread could share filesystem in VM mode with 9pfs such as QEMU... Signed-off-by: GuEe-GUI <2991707448@qq.com> --- components/dfs/Kconfig | 6 + .../dfs/dfs_v1/filesystems/9pfs/SConscript | 11 + .../dfs/dfs_v1/filesystems/9pfs/dfs_9pfs.c | 1186 +++++++++++++++++ .../dfs/dfs_v1/filesystems/9pfs/dfs_9pfs.h | 247 ++++ components/finsh/msh_file.c | 2 +- 5 files changed, 1451 insertions(+), 1 deletion(-) create mode 100644 components/dfs/dfs_v1/filesystems/9pfs/SConscript create mode 100644 components/dfs/dfs_v1/filesystems/9pfs/dfs_9pfs.c create mode 100644 components/dfs/dfs_v1/filesystems/9pfs/dfs_9pfs.h diff --git a/components/dfs/Kconfig b/components/dfs/Kconfig index 23799d1b44e..5fed4359e21 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 00000000000..7d875570e41 --- /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 00000000000..f874a5f3837 --- /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 +#include +#include + +#define DBG_TAG "dfs.9p" +#define DBG_LVL DBG_INFO +#include + +#include "dfs_9pfs.h" +#include +#include +#include + +#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 00000000000..8761249bd09 --- /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 + +#include +#include +#include +#include + +#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 d757f0c707c..25fc9ac1a7a 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;