This is an automated email from the ASF dual-hosted git repository.

acassis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git

commit aee91253501219d7955fa67fa00466442bc3ad69
Author: chenrun1 <[email protected]>
AuthorDate: Sat Jun 8 13:02:28 2024 +0800

    v9fs:File system based on 9P2000L.
    
    Signed-off-by: chenrun1 <[email protected]>
---
 fs/Kconfig             |    3 +-
 fs/Makefile            |    2 +-
 fs/mount/fs_gettype.c  |    6 +
 fs/mount/fs_mount.c    |    9 +-
 fs/v9fs/CMakeLists.txt |   29 +
 fs/v9fs/Kconfig        |   24 +
 fs/v9fs/Make.defs      |   35 +
 fs/v9fs/client.c       | 1763 ++++++++++++++++++++++++++++++++++++++++++++++++
 fs/v9fs/client.h       |  126 ++++
 fs/v9fs/transport.c    |  103 +++
 fs/v9fs/v9fs.c         |  961 ++++++++++++++++++++++++++
 fs/v9fs/virtio_9p.c    |  268 ++++++++
 include/sys/statfs.h   |    1 +
 13 files changed, 3327 insertions(+), 3 deletions(-)

diff --git a/fs/Kconfig b/fs/Kconfig
index 17f5f52749..e27224adf0 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -134,4 +134,5 @@ source "fs/userfs/Kconfig"
 source "fs/hostfs/Kconfig"
 source "fs/rpmsgfs/Kconfig"
 source "fs/zipfs/Kconfig"
-source "fs/mnemofs/Kconfig"
\ No newline at end of file
+source "fs/mnemofs/Kconfig"
+source "fs/v9fs/Kconfig"
diff --git a/fs/Makefile b/fs/Makefile
index db747317bd..29449b7983 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -58,7 +58,7 @@ include littlefs/Make.defs
 include rpmsgfs/Make.defs
 include zipfs/Make.defs
 include mnemofs/Make.defs
-
+include v9fs/Make.defs
 endif
 
 CFLAGS += ${INCDIR_PREFIX}$(TOPDIR)$(DELIM)fs
diff --git a/fs/mount/fs_gettype.c b/fs/mount/fs_gettype.c
index b5bfb35da7..fce945784c 100644
--- a/fs/mount/fs_gettype.c
+++ b/fs/mount/fs_gettype.c
@@ -153,6 +153,12 @@ FAR const char *fs_gettype(FAR struct statfs *statbuf)
         break;
 #endif
 
+#ifdef CONFIG_FS_V9FS
+      case V9FS_MAGIC:
+        fstype = "v9fs";
+        break;
+#endif
+
       default:
         fstype = "Unrecognized";
         break;
diff --git a/fs/mount/fs_mount.c b/fs/mount/fs_mount.c
index 4780f34613..40c6b54182 100644
--- a/fs/mount/fs_mount.c
+++ b/fs/mount/fs_mount.c
@@ -68,7 +68,8 @@
     defined(CONFIG_FS_PROCFS) || defined(CONFIG_NFS) || \
     defined(CONFIG_FS_TMPFS) || defined(CONFIG_FS_USERFS) || \
     defined(CONFIG_FS_CROMFS) || defined(CONFIG_FS_UNIONFS) || \
-    defined(CONFIG_FS_HOSTFS) || defined(CONFIG_FS_RPMSGFS)
+    defined(CONFIG_FS_HOSTFS) || defined(CONFIG_FS_RPMSGFS) || \
+    defined(CONFIG_FS_V9FS)
 #  define NODFS_SUPPORT
 #endif
 
@@ -190,6 +191,9 @@ extern const struct mountpt_operations g_rpmsgfs_operations;
 #ifdef CONFIG_FS_ZIPFS
 extern const struct mountpt_operations g_zipfs_operations;
 #endif
+#ifdef CONFIG_FS_V9FS
+extern const struct mountpt_operations g_v9fs_operations;
+#endif
 
 static const struct fsmap_t g_nonbdfsmap[] =
 {
@@ -225,6 +229,9 @@ static const struct fsmap_t g_nonbdfsmap[] =
 #endif
 #ifdef CONFIG_FS_ZIPFS
     { "zipfs", &g_zipfs_operations},
+#endif
+#ifdef CONFIG_FS_V9FS
+    { "v9fs", &g_v9fs_operations},
 #endif
     { NULL, NULL },
 };
diff --git a/fs/v9fs/CMakeLists.txt b/fs/v9fs/CMakeLists.txt
new file mode 100644
index 0000000000..08af7f6ad8
--- /dev/null
+++ b/fs/v9fs/CMakeLists.txt
@@ -0,0 +1,29 @@
+# 
##############################################################################
+# fs/v9fs/CMakeLists.txt
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more 
contributor
+# license agreements.  See the NOTICE file distributed with this work for
+# additional information regarding copyright ownership.  The ASF licenses this
+# file to you under the Apache License, Version 2.0 (the "License"); you may 
not
+# use this file except in compliance with the License.  You may obtain a copy 
of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+# License for the specific language governing permissions and limitations under
+# the License.
+#
+# 
##############################################################################
+
+if(CONFIG_FS_V9FS)
+  set(V9FS client.c transport.c v9fs.c)
+
+  if(CONFIG_DRIVERS_VIRTIO_9P)
+    list(APPEND V9FS virtio_9p.c)
+  endif()
+
+  target_sources(fs PRIVATE ${V9FS})
+endif()
diff --git a/fs/v9fs/Kconfig b/fs/v9fs/Kconfig
new file mode 100644
index 0000000000..1398f62359
--- /dev/null
+++ b/fs/v9fs/Kconfig
@@ -0,0 +1,24 @@
+#
+# For a description of the syntax of this configuration file,
+# see the file kconfig-language.txt in the NuttX tools repository.
+#
+
+config FS_V9FS
+       bool "V9FS file system"
+       default n
+       depends on !DISABLE_MOUNTPOINT
+       ---help---
+               Enable V9FS filesystem support
+
+if FS_V9FS
+
+config V9FS_DEFAULT_MSIZE
+  int "V9FS Default message max size"
+  default 65536
+
+config V9FS_VIRTIO_9P
+       bool "Virtio 9P support"
+       depends on DRIVERS_VIRTIO
+       default n
+
+endif
diff --git a/fs/v9fs/Make.defs b/fs/v9fs/Make.defs
new file mode 100644
index 0000000000..a9daa15e4d
--- /dev/null
+++ b/fs/v9fs/Make.defs
@@ -0,0 +1,35 @@
+############################################################################
+# fs/v9fs/Make.defs
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.  The
+# ASF licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the
+# License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+############################################################################
+
+ifeq ($(CONFIG_FS_V9FS),y)
+# Files required for V9FS file system support
+
+CSRCS += client.c transport.c v9fs.c
+
+ifeq ($(CONFIG_V9FS_VIRTIO_9P),y)
+  CSRCS += virtio_9p.c
+endif
+
+# Include V9FS build support
+
+DEPPATH += --dep-path v9fs
+VPATH += :v9fs
+
+endif
diff --git a/fs/v9fs/client.c b/fs/v9fs/client.c
new file mode 100644
index 0000000000..196b3acbf4
--- /dev/null
+++ b/fs/v9fs/client.c
@@ -0,0 +1,1763 @@
+/****************************************************************************
+ * fs/v9fs/client.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <sys/param.h>
+#include <fcntl.h>
+
+#include <nuttx/semaphore.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/fs/fs.h>
+#include <nuttx/lib/lib.h>
+
+#include "client.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* 9P Magic Numbers */
+
+#define V9FS_NOTAG             ((uint16_t)(~0))
+#define V9FS_NOFID             ((uint32_t)(~0))
+#define V9FS_HDRSZ             7          /* size[4] type[1] tag[2] */
+#define V9FS_IOHDRSZ           24         /* Reserve enough space for
+                                           * TWRITE/TREAD
+                                           */
+#define V9FS_READDIRHDRSZ      24         /* Reserve enough space for
+                                           * READDIR
+                                           */
+#define V9FS_BIT8SZ            1          /* uint8  -> V9FS_BIT8SZ  */
+#define V9FS_BIT16SZ           2          /* uint16 -> V9FS_BIT16SZ */
+#define V9FS_BIT32SZ           4          /* uint32 -> V9FS_BIT32SZ */
+#define V9FS_BIT64SZ           8          /* uint64 -> V9FS_BIT64SZ */
+#define V9FS_DEFAULT_VERSION   "9P2000.L" /* Current implementations are
+                                           * based on the "9P2000L"
+                                           * protocol.
+                                           */
+
+/* 9P setattr valid bits */
+
+#define V9FS_SETATTR_MODE      (1 << 0)
+#define V9FS_SETATTR_UID       (1 << 1)
+#define V9FS_SETATTR_GID       (1 << 2)
+#define V9FS_SETATTR_SIZE      (1 << 3)
+#define V9FS_SETATTR_ATIME     (1 << 4)
+#define V9FS_SETATTR_MTIME     (1 << 5)
+#define V9FS_SETATTR_CTIME     (1 << 6)
+#define V9FS_SETATTR_ATIMESET  (1 << 7)
+#define V9FS_SETATTR_MTIMESET  (1 << 8)
+
+/* 9P2000.L open flags */
+
+#define V9FS_RDONLY            00000000
+#define V9FS_WRONLY            00000001
+#define V9FS_RDWR              00000002
+#define V9FS_CREATE            00000100
+#define V9FS_EXCL              00000200
+#define V9FS_NOCTTY            00000400
+#define V9FS_TRUNC             00001000
+#define V9FS_APPEND            00002000
+#define V9FS_NONBLOCK          00004000
+#define V9FS_DSYNC             00010000
+#define V9FS_FASYNC            00020000
+#define V9FS_DIRECT            00040000
+#define V9FS_DIRECTORY         00200000
+#define V9FS_NOFOLLOW          00400000
+#define V9FS_NOATIME           01000000
+#define V9FS_CLOEXEC           02000000
+#define V9FS_SYNC              04000000
+
+/* 9P2000.L stat flags */
+
+#define V9FS_STATS_ALL         0x00003fff
+
+/* 9P2000.L unlink flags */
+
+#define V9FS_REMOVEDIR         0x200
+
+/* QID = uint8_t (type) + uint32_t (version) + uint64_t (path) */
+
+#define V9FS_QIDSZ             (V9FS_BIT8SZ + V9FS_BIT32SZ + V9FS_BIT64SZ)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+enum v9fs_flags_e
+{
+  V9FS_TLERROR = 6,
+  V9FS_RLERROR,
+  V9FS_TSTATFS = 8,
+  V9FS_RSTATFS,
+  V9FS_TLOPEN = 12,
+  V9FS_RLOPEN,
+  V9FS_TLCREATE = 14,
+  V9FS_RLCREATE,
+  V9FS_TRENAME = 20,
+  V9FS_RRENAME,
+  V9FS_TGETATTR = 24,
+  V9FS_RGETATTR,
+  V9FS_TSETATTR = 26,
+  V9FS_RSETATTR,
+  V9FS_TREADDIR = 40,
+  V9FS_RREADDIR,
+  V9FS_TFSYNC = 50,
+  V9FS_RFSYNC,
+  V9FS_TMKDIR = 72,
+  V9FS_RMKDIR,
+  V9FS_TRENAMEAT = 74,
+  V9FS_RRENAMEAT,
+  V9FS_TUNLINKAT = 76,
+  V9FS_RUNLINKAT,
+  V9FS_TVERSION = 100,
+  V9FS_RVERSION,
+  V9FS_TATTACH = 104,
+  V9FS_RATTACH,
+  V9FS_TERROR = 106,
+  V9FS_RERROR,
+  V9FS_TWALK = 110,
+  V9FS_RWALK,
+  V9FS_TREAD = 116,
+  V9FS_RREAD,
+  V9FS_TWRITE = 118,
+  V9FS_RWRITE,
+  V9FS_TCLUNK = 120,
+  V9FS_RCLUNK,
+  V9FS_TREMOVE = 122,
+  V9FS_RREMOVE,
+  V9FS_TSTAT = 124,
+  V9FS_RSTAT,
+  V9FS_TWSTAT = 126,
+  V9FS_RWSTAT,
+};
+
+begin_packed_struct struct v9fs_qid_s
+{
+  uint8_t  type;
+  uint32_t version;
+  uint64_t path;
+} end_packed_struct;
+
+begin_packed_struct struct v9fs_header_s
+{
+  uint32_t size;
+  uint8_t type;
+  uint16_t tag;
+} end_packed_struct;
+
+begin_packed_struct struct v9fs_attach_s
+{
+  struct v9fs_header_s header;
+  uint32_t fid;
+  uint16_t afid;
+  uint8_t buffer[2 * (V9FS_BIT16SZ + NAME_MAX) + V9FS_BIT32SZ];
+} end_packed_struct;
+
+begin_packed_struct struct v9fs_rattach_s
+{
+  struct v9fs_header_s header;
+  struct v9fs_qid_s qid;
+} end_packed_struct;
+
+begin_packed_struct struct v9fs_clunk_s
+{
+  struct v9fs_header_s header;
+  uint32_t fid;
+} end_packed_struct;
+
+#define v9fs_remove_s v9fs_clunk_s
+
+begin_packed_struct struct v9fs_version_s
+{
+  struct v9fs_header_s header;
+  uint32_t msize;
+  uint16_t version_len;
+  char version[NAME_MAX];
+} end_packed_struct;
+
+begin_packed_struct struct v9fs_setattr_s
+{
+  struct v9fs_header_s header;
+  uint32_t fid;
+  uint32_t valid;
+  uint32_t mode;
+  uint32_t uid;
+  uint32_t gid;
+  uint64_t size;
+  uint64_t atime_sec;
+  uint64_t atime_nsec;
+  uint64_t mtime_sec;
+  uint64_t mtime_nsec;
+} end_packed_struct;
+
+begin_packed_struct struct v9fs_mkdir_s
+{
+  struct v9fs_header_s header;
+  uint32_t fid;
+  uint16_t name_len;
+  uint8_t buffer[NAME_MAX + V9FS_BIT32SZ * 2];
+} end_packed_struct;
+
+#define v9fs_rmkdir_s v9fs_rattach_s
+
+begin_packed_struct struct v9fs_unlink_s
+{
+  struct v9fs_header_s header;
+  uint32_t fid;
+  uint16_t name_len;
+  uint8_t buffer[NAME_MAX + V9FS_BIT32SZ];
+} end_packed_struct;
+
+begin_packed_struct struct v9fs_lerror_s
+{
+  struct v9fs_header_s header;
+  uint32_t ecode;
+} end_packed_struct;
+
+begin_packed_struct struct v9fs_open_s
+{
+  struct v9fs_header_s header;
+  uint32_t fid;
+  uint32_t flags;
+} end_packed_struct;
+
+begin_packed_struct struct v9fs_ropen_s
+{
+  struct v9fs_header_s header;
+  struct v9fs_qid_s qid;
+  uint32_t iounit;
+} end_packed_struct;
+
+begin_packed_struct struct v9fs_create_s
+{
+  struct v9fs_header_s header;
+  uint32_t fid;
+  uint16_t name_len;
+  uint8_t buffer[NAME_MAX + V9FS_BIT32SZ * 3];
+} end_packed_struct;
+
+#define v9fs_rcreate_s v9fs_ropen_s
+
+begin_packed_struct struct v9fs_walk_s
+{
+  struct v9fs_header_s header;
+  uint32_t fid;
+  uint32_t newfid;
+  uint16_t nwname;
+} end_packed_struct;
+
+begin_packed_struct struct v9fs_rwalk_s
+{
+  struct v9fs_header_s header;
+  uint16_t nwqid;
+  struct v9fs_qid_s wqid[1];
+} end_packed_struct;
+
+begin_packed_struct struct v9fs_write_s
+{
+  struct v9fs_header_s header;
+  uint32_t fid;
+  uint64_t offset;
+  uint32_t count;
+} end_packed_struct;
+
+begin_packed_struct struct v9fs_rwrite_s
+{
+  struct v9fs_header_s header;
+  uint32_t count;
+} end_packed_struct;
+
+#define v9fs_read_s v9fs_write_s
+#define v9fs_rread_s v9fs_rwrite_s
+
+#define v9fs_readdir_s v9fs_write_s
+#define v9fs_rreaddir_s v9fs_rwrite_s
+
+begin_packed_struct struct v9fs_fsync_s
+{
+  struct v9fs_header_s header;
+  uint32_t fid;
+  uint32_t datasync;
+} end_packed_struct;
+
+begin_packed_struct struct v9fs_rename_s
+{
+  struct v9fs_header_s header;
+  uint32_t fid;
+  uint32_t newfid;
+  uint16_t name_len;
+  uint8_t buffer[NAME_MAX];
+} end_packed_struct;
+
+begin_packed_struct struct v9fs_stat_s
+{
+  struct v9fs_header_s header;
+  uint32_t fid;
+  uint64_t mask;
+} end_packed_struct;
+
+begin_packed_struct struct v9fs_rstat_s
+{
+  struct v9fs_header_s header;
+  uint64_t vaild;
+  struct v9fs_qid_s qid;
+  uint32_t mode;
+  uint32_t uid;
+  uint32_t gid;
+  uint64_t nlink;
+  uint64_t rdev;
+  uint64_t size;
+  uint64_t blksize;
+  uint64_t blocks;
+  uint64_t atime_sec;
+  uint64_t atime_nsec;
+  uint64_t mtime_sec;
+  uint64_t mtime_nsec;
+  uint64_t ctime_sec;
+  uint64_t ctime_nsec;
+  uint64_t btime_sec;
+  uint64_t btime_nsec;
+  uint64_t gen;
+  uint64_t data_version;
+} end_packed_struct;
+
+#define v9fs_statfs_s v9fs_clunk_s
+
+begin_packed_struct struct v9fs_rstatfs_s
+{
+  struct v9fs_header_s header;
+  uint32_t type;    /* Type of file system (see below) */
+  uint32_t bsize;   /* Optimal transfer block size */
+  uint64_t blocks;  /* Total data blocks in file system */
+  uint64_t bfree;   /* Free blocks in fs */
+  uint64_t bavail;  /* Free blocks avail to non-superuser */
+  uint64_t files;   /* Total file nodes in file system */
+  uint64_t ffree;   /* Free file nodes in fs */
+  uint64_t fsid;    /* File system id */
+  uint32_t namelen; /* Maximum length of filenames */
+} end_packed_struct;
+
+struct v9fs_map_s
+{
+  int original;
+  int conversion;
+};
+
+struct v9fs_fid_s
+{
+  uint32_t iounit;
+  uint32_t refcount;
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * v9fs_get_tagid
+ ****************************************************************************/
+
+static inline uint16_t v9fs_get_tagid(FAR struct v9fs_client_s *client)
+{
+  uint16_t tag;
+
+  nxmutex_lock(&client->lock);
+  tag = client->tag_id;
+  client->tag_id++;
+  if (client->tag_id == V9FS_NOTAG)
+    {
+      /* Bypassing V9FS_NOTAG */
+
+      client->tag_id = 1;
+    }
+
+  nxmutex_unlock(&client->lock);
+  return tag;
+}
+
+/****************************************************************************
+ * v9fs_map_setattr_flags
+ ****************************************************************************/
+
+static uint32_t v9fs_map_setattr_flags(int flags)
+{
+  uint32_t rflags = 0;
+  size_t i;
+  static const struct v9fs_map_s flags_map[] =
+    {
+      { CH_STAT_MODE,  V9FS_SETATTR_MODE },
+      { CH_STAT_UID,   V9FS_SETATTR_UID },
+      { CH_STAT_GID,   V9FS_SETATTR_GID },
+      { CH_STAT_SIZE,  V9FS_SETATTR_SIZE },
+      { CH_STAT_ATIME, V9FS_SETATTR_ATIMESET },
+      { CH_STAT_MTIME, V9FS_SETATTR_MTIMESET },
+    };
+
+  for (i = 0; i < nitems(flags_map); i++)
+    {
+      if (flags & flags_map[i].original)
+        {
+          rflags |= flags_map[i].conversion;
+        }
+    }
+
+  return rflags;
+}
+
+/****************************************************************************
+ * v9fs_map_open_flags
+ ****************************************************************************/
+
+static uint32_t v9fs_map_open_flags(int flags)
+{
+  uint32_t rflags = 0;
+  size_t i;
+  static const struct v9fs_map_s flags_map[] =
+    {
+      { O_CREAT,     V9FS_CREATE },
+      { O_NOCTTY,    V9FS_NOCTTY },
+      { O_TRUNC,     V9FS_TRUNC},
+      { O_APPEND,    V9FS_APPEND },
+      { O_NONBLOCK,  V9FS_NONBLOCK },
+      { O_DSYNC,     V9FS_DSYNC },
+      { FASYNC,      V9FS_FASYNC },
+      { O_DIRECT,    V9FS_DIRECT },
+      { O_DIRECTORY, V9FS_DIRECTORY },
+      { O_NOFOLLOW,  V9FS_NOFOLLOW },
+      { O_NOATIME,   V9FS_NOATIME },
+      { O_CLOEXEC,   V9FS_CLOEXEC },
+      { O_EXCL,      V9FS_EXCL },
+      { O_SYNC,      V9FS_SYNC},
+    };
+
+  switch (flags & 3)
+    {
+      case O_RDONLY:
+        rflags |= V9FS_RDONLY;
+        break;
+
+      case O_WRONLY:
+        rflags |= V9FS_WRONLY;
+        break;
+
+      case O_RDWR:
+        rflags |= V9FS_RDWR;
+        break;
+    }
+
+  for (i = 0; i < nitems(flags_map); i++)
+    {
+      if (flags & flags_map[i].original)
+        {
+          rflags |= flags_map[i].conversion;
+        }
+    }
+
+  return rflags;
+}
+
+/****************************************************************************
+ * v9fs_fid_create
+ ****************************************************************************/
+
+static int v9fs_fid_create(FAR struct v9fs_client_s *client)
+{
+  FAR struct v9fs_fid_s *fid;
+  int ret;
+
+  fid = kmm_zalloc(sizeof(struct v9fs_fid_s));
+  if (fid == NULL)
+    {
+      return -ENOMEM;
+    }
+
+  fid->refcount = 1;
+  nxmutex_lock(&client->lock);
+  ret = idr_alloc(client->fids, fid, 0, 0);
+  nxmutex_unlock(&client->lock);
+  if (ret >= 0)
+    {
+      return ret;
+    }
+
+  /* Failed to initialize fid */
+
+  kmm_free(fid);
+  return ret;
+}
+
+/****************************************************************************
+ * v9fs_fid_destroy
+ ****************************************************************************/
+
+static void v9fs_fid_destroy(FAR struct v9fs_client_s *client,
+                             uint32_t fid)
+{
+  FAR struct v9fs_fid_s *fidp;
+
+  nxmutex_lock(&client->lock);
+  fidp = idr_find(client->fids, fid);
+  if (fidp == NULL)
+    {
+      nxmutex_unlock(&client->lock);
+      return;
+    }
+
+  idr_remove(client->fids, fid);
+  nxmutex_unlock(&client->lock);
+  kmm_free(fidp);
+}
+
+/****************************************************************************
+ * v9fs_client_rpc
+ ****************************************************************************/
+
+static int v9fs_client_rpc(FAR struct v9fs_transport_s *transport,
+                           FAR struct iovec *wiov, size_t wcount,
+                           FAR struct iovec *riov, size_t rcount,
+                           uint16_t tag)
+{
+  struct v9fs_payload_s payload;
+  int ret;
+
+  nxsem_init(&payload.resp, 0, 0);
+  payload.wiov = wiov;
+  payload.riov = riov;
+  payload.wcount = wcount;
+  payload.rcount = rcount;
+  payload.tag = tag;
+  payload.ret = -EIO;
+
+  ret = v9fs_transport_request(transport, &payload);
+  if (ret < 0)
+    {
+      nxsem_destroy(&payload.resp);
+      return ret;
+    }
+
+  nxsem_wait(&payload.resp);
+  nxsem_destroy(&payload.resp);
+
+  return payload.ret;
+}
+
+/****************************************************************************
+ * v9fs_client_clunk
+ ****************************************************************************/
+
+static int v9fs_client_clunk(FAR struct v9fs_client_s *client,
+                             uint32_t fid)
+{
+  struct v9fs_clunk_s request;
+  struct v9fs_lerror_s response;
+  struct iovec wiov[1];
+  struct iovec riov[1];
+  int ret;
+
+  /* size[4] Tclunk tag[2] fid[4]
+   * size[4] Rclunk tag[2]
+   */
+
+  request.header.size = V9FS_HDRSZ + V9FS_BIT32SZ;
+  request.header.type = V9FS_TCLUNK;
+  request.header.tag = v9fs_get_tagid(client);
+  request.fid = fid;
+
+  /* A free buffer is reserved at the time of receipt to handle whether
+   * there is an error number message
+   */
+
+  wiov[0].iov_base = &request;
+  wiov[0].iov_len = V9FS_HDRSZ + V9FS_BIT32SZ;
+  riov[0].iov_base = &response;
+  riov[0].iov_len = V9FS_HDRSZ + V9FS_BIT32SZ;
+
+  ret = v9fs_client_rpc(client->transport, wiov, 1, riov, 1,
+                        request.header.tag);
+
+  /* Even if the clunk returns an error, the fid is no longer valid for
+   * the server, so we need to remove the fid on the client side.
+   */
+
+  v9fs_fid_destroy(client, fid);
+  return ret;
+}
+
+/****************************************************************************
+ * v9fs_client_attach
+ ****************************************************************************/
+
+static int v9fs_client_attach(FAR struct v9fs_client_s *client, uint32_t fid,
+                              FAR const char *uname, FAR const char *aname)
+{
+  struct v9fs_attach_s request;
+  struct v9fs_rattach_s response;
+  struct iovec wiov[1];
+  struct iovec riov[1];
+  uint16_t uname_len = strlen(uname);
+  uint16_t aname_len = strlen(aname);
+  uint32_t uid = getuid();
+  off_t offset = 0;
+  uint32_t newfid;
+  int ret;
+
+  /* size[4] Tattach tag[2] fid[4] afid[4] uname[s] aname[s] n_uname[4]
+   * size[4] Rattach tag[2] qid[13]
+   */
+
+  ret = v9fs_fid_create(client);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  newfid = ret;
+  request.header.size = V9FS_HDRSZ + V9FS_BIT32SZ * 3 + V9FS_BIT16SZ * 2 +
+                        uname_len + aname_len;
+  request.header.type = V9FS_TATTACH;
+  request.header.tag = v9fs_get_tagid(client);
+  request.fid = newfid;
+  request.afid = fid;
+
+  memcpy(&request.buffer[offset], &uname_len, V9FS_BIT16SZ);
+  offset += V9FS_BIT16SZ;
+  memcpy(&request.buffer[offset], uname, uname_len);
+  offset += uname_len;
+  memcpy(&request.buffer[offset], &aname_len, V9FS_BIT16SZ);
+  offset += V9FS_BIT16SZ;
+  memcpy(&request.buffer[offset], aname, aname_len);
+  offset += aname_len;
+  memcpy(&request.buffer[offset], &uid, V9FS_BIT32SZ);
+
+  wiov[0].iov_base = &request;
+  wiov[0].iov_len = V9FS_HDRSZ + V9FS_BIT32SZ * 3 + V9FS_BIT16SZ * 2 +
+                    uname_len + aname_len;
+  riov[0].iov_base = &response;
+  riov[0].iov_len = V9FS_HDRSZ + V9FS_QIDSZ;
+
+  ret = v9fs_client_rpc(client->transport, wiov, 1, riov, 1,
+                        request.header.tag);
+  if (ret < 0)
+    {
+      v9fs_fid_destroy(client, newfid);
+      return ret;
+    }
+
+  return newfid;
+}
+
+/****************************************************************************
+ * v9fs_client_version
+ ****************************************************************************/
+
+static int v9fs_client_version(FAR struct v9fs_client_s *client)
+{
+  struct v9fs_version_s request;
+  struct v9fs_version_s response;
+  struct iovec wiov[1];
+  struct iovec riov[1];
+  int len = strlen(V9FS_DEFAULT_VERSION);
+  int ret;
+
+  /* size[4] Tversion tag[2] msize[4] version[s]
+   * size[4] Rversion tag[2] msize[4] version[s]
+   */
+
+  request.header.size = V9FS_HDRSZ + V9FS_BIT32SZ + V9FS_BIT16SZ + len;
+  request.header.type = V9FS_TVERSION;
+  request.header.tag = V9FS_NOTAG;
+  request.msize = client->msize;
+  request.version_len = len;
+  memcpy(request.version, V9FS_DEFAULT_VERSION, len);
+
+  wiov[0].iov_base = &request;
+  wiov[0].iov_len = V9FS_HDRSZ + V9FS_BIT32SZ + V9FS_BIT16SZ + len;
+  riov[0].iov_base = &response;
+  riov[0].iov_len = V9FS_HDRSZ + V9FS_BIT32SZ + V9FS_BIT16SZ + len;
+
+  ret = v9fs_client_rpc(client->transport, wiov, 1, riov, 1,
+                        request.header.tag);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  if (memcmp(response.version, V9FS_DEFAULT_VERSION, len))
+    {
+      return -EREMOTEIO;
+    }
+
+  if (response.msize < client->msize)
+    {
+      client->msize = response.msize;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * v9fs_client_statfs
+ ****************************************************************************/
+
+int v9fs_client_statfs(FAR struct v9fs_client_s *client,
+                       FAR struct statfs *buf)
+{
+  struct v9fs_statfs_s request;
+  struct v9fs_rstatfs_s response;
+  struct iovec wiov[1];
+  struct iovec riov[1];
+  int ret;
+
+  /* size[4] Tstatfs tag[2] fid[4]
+   * size[4] Rstatfs tag[2] type[4] bsize[4] blocks[8] bfree[8] bavail[8]
+   *                        files[8] ffree[8] fsid[8] namelen[4]
+   */
+
+  request.header.size = V9FS_HDRSZ + V9FS_BIT32SZ;
+  request.header.type = V9FS_TSTATFS;
+  request.header.tag = v9fs_get_tagid(client);
+  request.fid = client->root_fid;
+
+  wiov[0].iov_base = &request;
+  wiov[0].iov_len = V9FS_HDRSZ + V9FS_BIT32SZ;
+  riov[0].iov_base = &response;
+  riov[0].iov_len = sizeof(response);
+
+  ret = v9fs_client_rpc(client->transport, wiov, 1, riov, 1,
+                        request.header.tag);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  buf->f_bsize   = response.bsize;
+  buf->f_blocks  = response.blocks;
+  buf->f_bfree   = response.bfree;
+  buf->f_bavail  = response.bavail;
+  buf->f_files   = response.files;
+  buf->f_ffree   = response.ffree;
+  buf->f_namelen = response.namelen;
+  buf->f_type    = V9FS_MAGIC;
+
+  return 0;
+}
+
+/****************************************************************************
+ * v9fs_client_stat
+ ****************************************************************************/
+
+int v9fs_client_stat(FAR struct v9fs_client_s *client, uint32_t fid,
+                     FAR struct stat *buf)
+{
+  struct v9fs_stat_s request;
+  struct v9fs_rstat_s response;
+  struct iovec wiov[1];
+  struct iovec riov[1];
+  int ret;
+
+  /* size[4] Tgetattr tag[2] fid[4] request_mask[8]
+   * size[4] Rgetattr tag[2] valid[8] qid[13] mode[4] uid[4] gid[4] nlink[8]
+   *         rdev[8] size[8] blksize[8] blocks[8]
+   *         atime_sec[8] atime_nsec[8] mtime_sec[8] mtime_nsec[8]
+   *         ctime_sec[8] ctime_nsec[8] btime_sec[8] btime_nsec[8]
+   *         gen[8] data_version[8]
+   */
+
+  request.header.size = V9FS_HDRSZ + V9FS_BIT32SZ + V9FS_BIT64SZ;
+  request.header.type = V9FS_TGETATTR;
+  request.header.tag = v9fs_get_tagid(client);
+  request.fid = fid;
+  request.mask = V9FS_STATS_ALL;
+
+  wiov[0].iov_base = &request;
+  wiov[0].iov_len = V9FS_HDRSZ + V9FS_BIT32SZ + V9FS_BIT64SZ;
+  riov[0].iov_base = &response;
+  riov[0].iov_len = sizeof(response);
+
+  ret = v9fs_client_rpc(client->transport, wiov, 1, riov, 1,
+                        request.header.tag);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  buf->st_mode  = response.mode;
+  buf->st_uid   = response.uid;
+  buf->st_gid   = response.gid;
+  buf->st_nlink = response.nlink;
+  buf->st_rdev  = response.rdev;
+  buf->st_size  = response.size;
+  buf->st_blksize = response.blksize;
+  buf->st_blocks  = response.blocks;
+  buf->st_atim.tv_sec  = response.atime_sec;
+  buf->st_atim.tv_nsec = response.atime_nsec;
+  buf->st_mtim.tv_sec  = response.mtime_sec;
+  buf->st_mtim.tv_nsec = response.mtime_nsec;
+  buf->st_ctim.tv_sec = response.ctime_sec;
+  buf->st_ctim.tv_nsec = response.ctime_nsec;
+
+  return 0;
+}
+
+/****************************************************************************
+ * v9fs_client_getsize
+ ****************************************************************************/
+
+off_t v9fs_client_getsize(FAR struct v9fs_client_s *client, uint32_t fid)
+{
+  struct stat buf;
+  int ret;
+
+  ret = v9fs_client_stat(client, fid, &buf);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  return buf.st_size;
+}
+
+/****************************************************************************
+ * v9fs_client_chstat
+ ****************************************************************************/
+
+int v9fs_client_chstat(FAR struct v9fs_client_s *client, uint32_t fid,
+                       FAR const struct stat *buf, int flags)
+{
+  struct v9fs_setattr_s request;
+  struct v9fs_lerror_s response;
+  struct iovec wiov[1];
+  struct iovec riov[1];
+
+  /* size[4] Tsetattr tag[2] fid[4] valid[4] mode[4] uid[4] gid[4] size[8]
+   *                  atime_sec[8] atime_nsec[8] mtime_sec[8] mtime_nsec[8]
+   * size[4] Rsetattr tag[2]
+   */
+
+  request.header.size = V9FS_HDRSZ + V9FS_BIT32SZ * 5 + V9FS_BIT64SZ * 5;
+  request.header.type = V9FS_TSETATTR;
+  request.header.tag = v9fs_get_tagid(client);
+
+  request.fid = fid;
+  request.valid = v9fs_map_setattr_flags(flags);
+  request.mode = v9fs_map_open_flags(buf->st_mode);
+  request.uid = buf->st_uid;
+  request.gid = buf->st_gid;
+  request.size = buf->st_size;
+  request.atime_sec = buf->st_atim.tv_sec;
+  request.atime_nsec = buf->st_atim.tv_nsec;
+  request.mtime_sec = buf->st_mtim.tv_sec;
+  request.mtime_nsec = buf->st_mtim.tv_nsec;
+
+  /* A free buffer is reserved at the time of receipt to handle whether
+   * there is an error number message
+   */
+
+  wiov[0].iov_base = &request;
+  wiov[0].iov_len = V9FS_HDRSZ + V9FS_BIT32SZ * 5 + V9FS_BIT64SZ * 5;
+  riov[0].iov_base = &response;
+  riov[0].iov_len = V9FS_HDRSZ + V9FS_BIT32SZ;
+
+  return v9fs_client_rpc(client->transport, wiov, 1, riov, 1,
+                         request.header.tag);
+}
+
+/****************************************************************************
+ * v9fs_client_read
+ ****************************************************************************/
+
+ssize_t v9fs_client_read(FAR struct v9fs_client_s *client, uint32_t fid,
+                         FAR void *buffer, off_t offset, size_t buflen)
+{
+  FAR struct v9fs_fid_s *fidp;
+  struct v9fs_read_s request;
+  struct v9fs_rread_s response;
+  struct iovec wiov[1];
+  struct iovec riov[2];
+  size_t nread = 0;
+  int ret = 0;
+
+  /* size[4] Tread tag[2] fid[4] offset[8] count[4]
+   * size[4] Rread tag[2] count[4] data[count]
+   */
+
+  fidp = idr_find(client->fids, fid);
+  if (fidp == NULL)
+    {
+      return -ENOENT;
+    }
+
+  while (buflen > 0)
+    {
+      request.header.size = V9FS_HDRSZ + V9FS_BIT32SZ + V9FS_BIT64SZ +
+                            V9FS_BIT32SZ;
+      request.header.type = V9FS_TREAD;
+      request.header.tag = v9fs_get_tagid(client);
+      request.fid = fid;
+      request.offset = offset;
+      request.count = buflen > fidp->iounit ? fidp->iounit : buflen;
+
+      wiov[0].iov_base = &request;
+      wiov[0].iov_len = V9FS_HDRSZ + V9FS_BIT32SZ + V9FS_BIT64SZ +
+                        V9FS_BIT32SZ;
+      riov[0].iov_base = &response;
+      riov[0].iov_len = V9FS_HDRSZ + V9FS_BIT32SZ;
+      riov[1].iov_base = (FAR void *)buffer;
+      riov[1].iov_len = request.count;
+
+      ret = v9fs_client_rpc(client->transport, wiov, 1, riov, 2,
+                            request.header.tag);
+      if (ret < 0)
+        {
+          break;
+        }
+
+      if (response.count == 0)
+        {
+          break;
+        }
+
+      nread += response.count;
+      offset += response.count;
+      buffer += response.count;
+      buflen -= response.count;
+    }
+
+  return nread ? nread : ret;
+}
+
+/****************************************************************************
+ * v9fs_client_readdir
+ ****************************************************************************/
+
+ssize_t v9fs_client_readdir(FAR struct v9fs_client_s *client, uint32_t fid,
+                            FAR void *buffer, off_t offset, size_t buflen)
+{
+  FAR struct v9fs_fid_s *fidp;
+  struct v9fs_readdir_s request;
+  struct v9fs_rreaddir_s response;
+  struct iovec wiov[1];
+  struct iovec riov[2];
+  int ret;
+
+  /* size[4] Treaddir tag[2] fid[4] offset[8] count[4]
+   * size[4] Rreaddir tag[2] count[4] data[count]
+   */
+
+  fidp = idr_find(client->fids, fid);
+  if (fidp == NULL)
+    {
+      return -ENOENT;
+    }
+
+  request.header.size = V9FS_HDRSZ + V9FS_BIT32SZ + V9FS_BIT64SZ +
+                        V9FS_BIT32SZ;
+  request.header.type = V9FS_TREADDIR;
+  request.header.tag = v9fs_get_tagid(client);
+  request.fid = fid;
+  request.offset = offset;
+  request.count = buflen > fidp->iounit ? fidp->iounit : buflen;
+
+  wiov[0].iov_base = &request;
+  wiov[0].iov_len = V9FS_HDRSZ + V9FS_BIT32SZ + V9FS_BIT64SZ + V9FS_BIT32SZ;
+  riov[0].iov_base = &response;
+  riov[0].iov_len = V9FS_HDRSZ + V9FS_BIT32SZ;
+  riov[1].iov_base = buffer;
+  riov[1].iov_len = request.count;
+
+  ret = v9fs_client_rpc(client->transport, wiov, 1, riov, 2,
+                        request.header.tag);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  return response.count;
+}
+
+/****************************************************************************
+ * v9fs_client_convertdir
+ ****************************************************************************/
+
+ssize_t v9fs_client_convertdir(FAR const uint8_t *buffer, size_t bufsize,
+                               off_t head, FAR off_t *offset,
+                               FAR struct dirent *entry)
+{
+  uint64_t off;
+  uint16_t name_len;
+  off_t next = head;
+
+  /* Ensure there is enough data for the fixed parts
+   * (qid, offset, type, name_len)
+   */
+
+  if (bufsize < V9FS_QIDSZ + V9FS_BIT64SZ + V9FS_BIT8SZ + V9FS_BIT16SZ)
+    {
+      return -EIO;
+    }
+
+  /* Skip the qid part (13 Bytes) */
+
+  next += V9FS_QIDSZ;
+
+  /* Read the offset (8 bytes) */
+
+  memcpy(&off, buffer + next, sizeof(uint64_t));
+  next += sizeof(uint64_t);
+
+  /* Read the type (1 byte) */
+
+  memcpy(&entry->d_type, buffer + next, sizeof(uint8_t));
+  next += sizeof(uint8_t);
+
+  /* Read the name_len (2 bytes) */
+
+  memcpy(&name_len, buffer + next, sizeof(uint16_t));
+  next += sizeof(uint16_t);
+
+  /* Ensure there is enough data for the name */
+
+  if (bufsize - next < name_len)
+    {
+      return -EIO;
+    }
+
+  /* Copy the file name to entry->d_name and null-terminate it */
+
+  if (name_len > NAME_MAX)
+    {
+      return -ENAMETOOLONG;
+    }
+
+  memcpy(entry->d_name, buffer + next, name_len);
+  entry->d_name[name_len] = '\0';
+  next += name_len;
+  *offset = off;
+
+  return next - head;
+}
+
+/****************************************************************************
+ * v9fs_client_write
+ ****************************************************************************/
+
+ssize_t v9fs_client_write(FAR struct v9fs_client_s *client, uint32_t fid,
+                          FAR const void *buffer, off_t offset,
+                          size_t buflen)
+{
+  FAR struct v9fs_fid_s *fidp;
+  struct v9fs_write_s request;
+  struct v9fs_rwrite_s response;
+  struct iovec wiov[2];
+  struct iovec riov[1];
+  size_t nwrite = 0;
+  int ret = 0;
+
+  /* size[4] Twrite tag[2] fid[4] offset[8] count[4] data[count]
+   * size[4] Rwrite tag[2] count[4]
+   */
+
+  fidp = idr_find(client->fids, fid);
+  if (fidp == NULL)
+    {
+      return -ENOENT;
+    }
+
+  while (buflen > 0)
+    {
+      request.header.size = V9FS_HDRSZ + V9FS_BIT32SZ + V9FS_BIT64SZ +
+                            V9FS_BIT32SZ + request.count;
+      request.header.type = V9FS_TWRITE;
+      request.header.tag = v9fs_get_tagid(client);
+      request.fid = fid;
+      request.offset = offset;
+      request.count = buflen > fidp->iounit ? fidp->iounit : buflen;
+
+      wiov[0].iov_base = &request;
+      wiov[0].iov_len = V9FS_HDRSZ + V9FS_BIT32SZ + V9FS_BIT64SZ +
+                        V9FS_BIT32SZ;
+      wiov[1].iov_base = (FAR void *)buffer;
+      wiov[1].iov_len = request.count;
+      riov[0].iov_base = &response;
+      riov[0].iov_len = V9FS_HDRSZ + V9FS_BIT32SZ;
+
+      ret = v9fs_client_rpc(client->transport, wiov, 2, riov, 1,
+                            request.header.tag);
+      if (ret < 0)
+        {
+          break;
+        }
+
+      nwrite += response.count;
+      offset += response.count;
+      buffer += response.count;
+      buflen -= response.count;
+    }
+
+  return nwrite ? nwrite : ret;
+}
+
+/****************************************************************************
+ * v9fs_client_fsync
+ ****************************************************************************/
+
+int v9fs_client_fsync(FAR struct v9fs_client_s *client, uint32_t fid)
+{
+  struct v9fs_fsync_s request;
+  struct v9fs_lerror_s response;
+  struct iovec wiov[1];
+  struct iovec riov[1];
+
+  /* size[4] Tfsync tag[2] fid[4] datasync[4]
+   * size[4] Rfsync tag[2]
+   * 0x01 - Sync data only
+   * 0x00 - Complete update data (including timestamps and permissions)
+   */
+
+  request.header.size = V9FS_HDRSZ + V9FS_BIT32SZ * 2;
+  request.header.type = V9FS_TFSYNC;
+  request.header.tag = v9fs_get_tagid(client);
+  request.fid = fid;
+  request.datasync = 0;
+
+  wiov[0].iov_base = &request;
+  wiov[0].iov_len = V9FS_HDRSZ + V9FS_BIT32SZ * 2;
+  riov[0].iov_base = &response;
+  riov[0].iov_len = V9FS_HDRSZ + V9FS_BIT32SZ;
+
+  return v9fs_client_rpc(client->transport, wiov, 1, riov, 1,
+                         request.header.tag);
+}
+
+/****************************************************************************
+ * v9fs_client_rename
+ ****************************************************************************/
+
+int v9fs_client_rename(FAR struct v9fs_client_s *client, uint32_t fid,
+                       uint32_t newfid, FAR const char *name)
+{
+  struct v9fs_rename_s request;
+  struct v9fs_lerror_s response;
+  struct iovec wiov[1];
+  struct iovec riov[1];
+
+  /* size[4] Trename tag[2] fid[4] dfid[4] name[s]
+   * size[4] Rrename tag[2]
+   */
+
+  request.name_len = strlen(name);
+  request.fid = fid;
+  request.newfid = newfid;
+  request.header.size = V9FS_HDRSZ + V9FS_BIT32SZ * 2 + V9FS_BIT16SZ +
+                         request.name_len;
+  request.header.type = V9FS_TRENAME;
+  request.header.tag = v9fs_get_tagid(client);
+  memcpy(&request.buffer, name, request.name_len);
+
+  wiov[0].iov_base = &request;
+  wiov[0].iov_len = V9FS_HDRSZ + V9FS_BIT32SZ * 2 + V9FS_BIT16SZ +
+                    request.name_len;
+  riov[0].iov_base = &response;
+  riov[0].iov_len = V9FS_HDRSZ + V9FS_BIT32SZ;
+
+  return v9fs_client_rpc(client->transport, wiov, 1, riov, 1,
+                         request.header.tag);
+}
+
+/****************************************************************************
+ * v9fs_client_remove
+ ****************************************************************************/
+
+int v9fs_client_remove(FAR struct v9fs_client_s *client, uint32_t fid)
+{
+  struct v9fs_remove_s request;
+  struct v9fs_lerror_s response;
+  struct iovec wiov[1];
+  struct iovec riov[1];
+
+  /* size[4] Tremove tag[2] fid[4]
+   * size[4] Rremove tag[2]
+   */
+
+  request.header.size = V9FS_HDRSZ + V9FS_BIT32SZ;
+  request.header.type = V9FS_TREMOVE;
+  request.header.tag = v9fs_get_tagid(client);
+  request.fid = fid;
+
+  /* A free buffer is reserved at the time of receipt to handle whether
+   * there is an error number message
+   */
+
+  wiov[0].iov_base = &request;
+  wiov[0].iov_len = V9FS_HDRSZ + V9FS_BIT32SZ;
+  riov[0].iov_base = &response;
+  riov[0].iov_len = V9FS_HDRSZ + V9FS_BIT32SZ;
+
+  return v9fs_client_rpc(client->transport, wiov, 1, riov, 1,
+                         request.header.tag);
+}
+
+/****************************************************************************
+ * v9fs_client_unlink
+ ****************************************************************************/
+
+int v9fs_client_unlink(FAR struct v9fs_client_s *client, uint32_t fid,
+                       FAR const char *name, bool isdir)
+{
+  struct v9fs_unlink_s request;
+  struct v9fs_lerror_s response;
+  struct iovec wiov[1];
+  struct iovec riov[1];
+  int flags = isdir ? V9FS_REMOVEDIR : 0;
+  int ret;
+
+  /* size[4] Tunlinkat tag[2] dirfd[4] name[s] flags[4]
+   * size[4] Runlinkat tag[2]
+   */
+
+  request.name_len = strlen(name);
+  request.header.size = V9FS_HDRSZ + V9FS_BIT32SZ * 2 + V9FS_BIT16SZ +
+                        request.name_len;
+  request.header.type = V9FS_TUNLINKAT;
+  request.header.tag = v9fs_get_tagid(client);
+  request.fid = fid;
+  memcpy(&request.buffer, name, request.name_len);
+  memcpy(&request.buffer[request.name_len], &flags, V9FS_BIT32SZ);
+
+  wiov[0].iov_base = &request;
+  wiov[0].iov_len = V9FS_HDRSZ + V9FS_BIT32SZ * 2 + V9FS_BIT16SZ +
+                    request.name_len;
+  riov[0].iov_base = &response;
+  riov[0].iov_len = V9FS_HDRSZ + V9FS_BIT32SZ;
+
+  ret = v9fs_client_rpc(client->transport, wiov, 1, riov, 1,
+                        request.header.tag);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  return v9fs_client_clunk(client, fid);
+}
+
+/****************************************************************************
+ * v9fs_client_mkdir
+ ****************************************************************************/
+
+int v9fs_client_mkdir(FAR struct v9fs_client_s *client, uint32_t fid,
+                      FAR const char *name, int mode)
+{
+  struct v9fs_mkdir_s request;
+  struct v9fs_rmkdir_s response;
+  struct iovec wiov[1];
+  struct iovec riov[1];
+  uint32_t gid = getgid();
+  off_t offset = 0;
+
+  /* size[4] Tmkdir tag[2] dfid[4] name[s] mode[4] gid[4]
+   * size[4] Rmkdir tag[2] qid[13]
+   */
+
+  request.fid = fid;
+  request.name_len = strlen(name);
+  request.header.size = V9FS_HDRSZ + V9FS_BIT32SZ * 3 + V9FS_BIT16SZ +
+                        request.name_len;
+  request.header.type = V9FS_TMKDIR;
+  request.header.tag = v9fs_get_tagid(client);
+  memcpy(&request.buffer[offset], name, request.name_len);
+  offset += request.name_len;
+  memcpy(&request.buffer[offset], &mode, V9FS_BIT32SZ);
+  offset += V9FS_BIT32SZ;
+  memcpy(&request.buffer[offset], &gid, V9FS_BIT32SZ);
+
+  wiov[0].iov_base = &request;
+  wiov[0].iov_len = V9FS_HDRSZ + V9FS_BIT32SZ * 3 + V9FS_BIT16SZ +
+                    request.name_len;
+  riov[0].iov_base = &response;
+  riov[0].iov_len = V9FS_HDRSZ + V9FS_QIDSZ;
+
+  return v9fs_client_rpc(client->transport, wiov, 1, riov, 1,
+                         request.header.tag);
+}
+
+/****************************************************************************
+ * v9fs_client_create
+ ****************************************************************************/
+
+int v9fs_client_create(FAR struct v9fs_client_s *client, uint32_t fid,
+                       FAR const char *name, int oflags, int mode)
+{
+  FAR struct v9fs_fid_s *fidp;
+  struct v9fs_create_s request;
+  struct v9fs_rcreate_s response;
+  struct iovec wiov[1];
+  struct iovec riov[1];
+  uint32_t flags = v9fs_map_open_flags(oflags);
+  uint32_t gid = getgid();
+  off_t offset = 0;
+  int ret;
+
+  /* size[4] Tlcreate tag[2] fid[4] name[s] flags[4] mode[4] gid[4]
+   * size[4] Rlcreate tag[2] qid[13] iounit[4]
+   */
+
+  fidp = idr_find(client->fids, fid);
+  if (fidp == NULL)
+    {
+      return -ENOENT;
+    }
+
+  request.fid = fid;
+  request.name_len = strlen(name);
+  request.header.size = V9FS_HDRSZ + V9FS_BIT32SZ * 4 + V9FS_BIT16SZ +
+                        request.name_len;
+  request.header.type = V9FS_TLCREATE;
+  request.header.tag = v9fs_get_tagid(client);
+  memcpy(&request.buffer[offset], name, request.name_len);
+  offset += request.name_len;
+  memcpy(&request.buffer[offset], &flags, V9FS_BIT32SZ);
+  offset += V9FS_BIT32SZ;
+  memcpy(&request.buffer[offset], &mode, V9FS_BIT32SZ);
+  offset += V9FS_BIT32SZ;
+  memcpy(&request.buffer[offset], &gid, V9FS_BIT32SZ);
+
+  wiov[0].iov_base = &request;
+  wiov[0].iov_len = V9FS_HDRSZ + V9FS_BIT32SZ * 4 + V9FS_BIT16SZ +
+                    request.name_len;
+  riov[0].iov_base = &response;
+  riov[0].iov_len = V9FS_HDRSZ + V9FS_QIDSZ + V9FS_BIT32SZ;
+
+  ret = v9fs_client_rpc(client->transport, wiov, 1, riov, 1,
+                        request.header.tag);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  fidp->iounit = response.iounit;
+  if (!fidp->iounit || fidp->iounit > client->msize - V9FS_IOHDRSZ)
+    {
+      fidp->iounit = client->msize - V9FS_IOHDRSZ;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * v9fs_client_open
+ ****************************************************************************/
+
+int v9fs_client_open(FAR struct v9fs_client_s *client,
+                     uint32_t fid, int oflags)
+{
+  FAR struct v9fs_fid_s *fidp;
+  struct v9fs_open_s request;
+  struct v9fs_ropen_s response;
+  struct iovec wiov[1];
+  struct iovec riov[1];
+  int ret;
+
+  /* size[4] Tlopen tag[2] fid[4] flags[4]
+   * size[4] Rlopen tag[2] qid[13] iounit[4]
+   */
+
+  fidp = idr_find(client->fids, fid);
+  if (fidp == NULL)
+    {
+      return -ENOENT;
+    }
+
+  request.fid = fid;
+  request.flags = v9fs_map_open_flags(oflags);
+  request.header.size = V9FS_HDRSZ + V9FS_BIT32SZ * 2;
+  request.header.type = V9FS_TLOPEN;
+  request.header.tag = v9fs_get_tagid(client);
+
+  wiov[0].iov_base = &request;
+  wiov[0].iov_len = V9FS_HDRSZ + V9FS_BIT32SZ * 2;
+  riov[0].iov_base = &response;
+  riov[0].iov_len = V9FS_HDRSZ + V9FS_QIDSZ + V9FS_BIT32SZ;
+
+  ret = v9fs_client_rpc(client->transport, wiov, 1, riov, 1,
+                        request.header.tag);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  fidp->iounit = response.iounit;
+  if (!fidp->iounit || fidp->iounit > client->msize - V9FS_IOHDRSZ)
+    {
+      fidp->iounit = client->msize - V9FS_IOHDRSZ;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * v9fs_client_walk
+ ****************************************************************************/
+
+int v9fs_client_walk(FAR struct v9fs_client_s *client, FAR const char *path,
+                     FAR const char **childname)
+{
+  struct v9fs_walk_s request;
+  struct v9fs_rwalk_s response;
+  FAR char *request_payload;
+  FAR char *response_payload;
+  FAR const char *start;
+  FAR const char *end;
+  struct iovec wiov[2];
+  struct iovec riov[2];
+  uint16_t nwname = 0;
+  uint32_t newfid;
+  size_t total_len = 0;
+  size_t offset = 0;
+  size_t name_len;
+  int ret;
+
+  /* size[4] Twalk tag[2] fid[4] newfid[4] nwname[2] nwname*(wname[s])
+   * size[4] Rwalk tag[2] nwqid[2] nwqid*(wqid[13])
+   */
+
+  /* Parse path info, We need to skip parsing the root path */
+
+  start = path;
+  while (*start != '\0')
+    {
+      end = strchr(start, '/');
+      if (end == NULL)
+        {
+          if (childname == NULL)
+            {
+              nwname++;
+              total_len += strlen(start);
+            }
+
+          break;
+        }
+      else if (end != start)
+        {
+          nwname++;
+          total_len += end - start;
+        }
+
+      start = end + 1;
+    }
+
+  /* Request = sizeof(struct v9fs_walk_s) + nwname * sizeof(uint16_t) +
+   *           total_len
+   * Response = sizeof(struct v9fs_rwalk_s) + V9FS_QIDSZ * (nwname + 1)
+   * When PATH_MAX = 255, we support a maximum recursive depth of
+   * (255 - 7 - 2) / 13 = 18 layers, PATH MAX size affects recursion depth
+   */
+
+  total_len += V9FS_BIT16SZ * nwname;
+  if (total_len > PATH_MAX || nwname * V9FS_QIDSZ > PATH_MAX)
+    {
+      return -ENAMETOOLONG;
+    }
+
+  request_payload = lib_get_pathbuffer();
+  if (request_payload == NULL)
+    {
+      return -ENOMEM;
+    }
+
+  response_payload = lib_get_pathbuffer();
+  if (response_payload == NULL)
+    {
+      lib_put_pathbuffer(request_payload);
+      return -ENOMEM;
+    }
+
+  newfid = v9fs_fid_create(client);
+  if (newfid < 0)
+    {
+      goto err;
+    }
+
+  request.header.size = V9FS_HDRSZ + V9FS_BIT32SZ * 2 + V9FS_BIT16SZ +
+                        total_len;
+  request.header.type = V9FS_TWALK;
+  request.header.tag = v9fs_get_tagid(client);
+  request.fid = client->root_fid;
+  request.newfid = newfid;
+  request.nwname = nwname;
+  start = path;
+  while (*start != '\0')
+    {
+      end = strchr(start, '/');
+      if (end == start)
+        {
+          start++;
+          continue;
+        }
+
+      if (end == NULL)
+        {
+          if (childname != NULL)
+            {
+              *childname = start;
+              break;
+            }
+
+          name_len = strlen(start);
+        }
+      else
+        {
+          name_len = end - start;
+        }
+
+      memcpy(&request_payload[offset], &name_len, sizeof(uint16_t));
+      offset += sizeof(uint16_t);
+      memcpy(&request_payload[offset], start, name_len);
+      offset += name_len;
+      start = end + 1;
+    }
+
+  wiov[0].iov_base = &request;
+  wiov[0].iov_len = V9FS_HDRSZ + V9FS_BIT32SZ * 2 + V9FS_BIT16SZ;
+  wiov[1].iov_base = request_payload;
+  wiov[1].iov_len = total_len;
+  riov[0].iov_base = &response;
+  riov[0].iov_len = sizeof(response);
+  riov[1].iov_base = response_payload;
+  riov[1].iov_len = V9FS_QIDSZ * (nwname + 1);
+
+  ret = v9fs_client_rpc(client->transport, wiov, total_len ? 2 : 1,
+                        riov, 2, request.header.tag);
+  if (ret < 0)
+    {
+      v9fs_fid_destroy(client, newfid);
+      newfid = ret;
+    }
+
+err:
+  lib_put_pathbuffer(request_payload);
+  lib_put_pathbuffer(response_payload);
+  return newfid;
+}
+
+/****************************************************************************
+ * v9fs_client_init
+ ****************************************************************************/
+
+int v9fs_client_init(FAR struct v9fs_client_s *client,
+                     FAR const char *data)
+{
+  FAR const char *options;
+  char transport[NAME_MAX];
+  char aname[PATH_MAX] =
+    {
+      0
+    };
+
+  char uname[NAME_MAX] =
+    {
+      0
+    };
+
+  size_t length;
+  int ret;
+
+  /* Parse commandline */
+
+  options = data;
+  while (*options != '\0')
+    {
+      FAR const char *sep = strchr(options, ',');
+      length = sep ? sep - options : strlen(options);
+
+      if (strncmp(options, "uname=", 6) == 0)
+        {
+          strlcpy(uname, options + 6, length - 5);
+        }
+      else if (strncmp(options, "aname=", 6) == 0)
+        {
+          strlcpy(aname, options + 6, length - 5);
+        }
+      else if (strncmp(options, "trans=", 6) == 0)
+        {
+          strlcpy(transport, options + 6, length - 5);
+        }
+      else if (strncmp(options, "msize=", 6) == 0)
+        {
+          client->msize = atoi(options + 6);
+        }
+
+      options += length + 1;
+    }
+
+  if (client->msize == 0)
+    {
+      /* Set default size */
+
+      client->msize = (CONFIG_V9FS_DEFAULT_MSIZE + V9FS_IOHDRSZ);
+    }
+
+  /* Initialize the client function */
+
+  ret = v9fs_transport_create(&client->transport, transport, data);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  client->fids = idr_init_base(1);
+  if (client->fids == NULL)
+    {
+      v9fs_transport_destroy(client->transport);
+      return -ENOMEM;
+    }
+
+  nxmutex_init(&client->lock);
+
+  /* Do Version */
+
+  ret = v9fs_client_version(client);
+  if (ret < 0)
+    {
+      goto out;
+    }
+
+  /* Do Attach */
+
+  ret = v9fs_client_attach(client, V9FS_NOFID, uname, aname);
+  if (ret < 0)
+    {
+      goto out;
+    }
+
+  client->root_fid = ret;
+  client->tag_id = 1;
+  return 0;
+
+out:
+  v9fs_transport_destroy(client->transport);
+  nxmutex_destroy(&client->lock);
+  idr_destroy(client->fids);
+  return ret;
+}
+
+/****************************************************************************
+ * v9fs_client_uninit
+ ****************************************************************************/
+
+int v9fs_client_uninit(FAR struct v9fs_client_s *client)
+{
+  int ret;
+
+  ret = v9fs_client_clunk(client, client->root_fid);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  v9fs_transport_destroy(client->transport);
+  nxmutex_destroy(&client->lock);
+  idr_destroy(client->fids);
+  return 0;
+}
+
+/****************************************************************************
+ * v9fs_transport_done
+ ****************************************************************************/
+
+void v9fs_transport_done(FAR struct v9fs_payload_s *cookie, int ret)
+{
+  FAR struct v9fs_lerror_s *error = cookie->riov[0].iov_base;
+
+  /* Recive message = riov[0] (Header) + iov[1] (Payload) + ...
+   * So we first check if the type on the rheader is RLERROR. If it is,
+   * it means that payload[1] is ecode.
+   */
+
+  if (error->header.type == V9FS_RLERROR)
+    {
+      /* Therefore, we assign the error code to the ecode of the cookie
+       * and check it in the next process
+       */
+
+      cookie->ret = -error->ecode;
+    }
+  else
+    {
+      cookie->ret = ret;
+    }
+
+  nxsem_post(&cookie->resp);
+}
+
+/****************************************************************************
+ * v9fs_fid_put
+ ****************************************************************************/
+
+int v9fs_fid_put(FAR struct v9fs_client_s *client, uint32_t fid)
+{
+  FAR struct v9fs_fid_s *fidp;
+
+  nxmutex_lock(&client->lock);
+  fidp = idr_find(client->fids, fid);
+  if (fidp == NULL)
+    {
+      nxmutex_unlock(&client->lock);
+      return -ENOENT;
+    }
+
+  fidp->refcount--;
+  if (fidp->refcount > 0)
+    {
+      nxmutex_unlock(&client->lock);
+      return fidp->refcount;
+    }
+
+  nxmutex_unlock(&client->lock);
+  return v9fs_client_clunk(client, fid);
+}
+
+/****************************************************************************
+ * v9fs_fid_get
+ ****************************************************************************/
+
+int v9fs_fid_get(FAR struct v9fs_client_s *client, uint32_t fid)
+{
+  FAR struct v9fs_fid_s *fidp;
+
+  nxmutex_lock(&client->lock);
+  fidp = idr_find(client->fids, fid);
+  if (fidp == NULL)
+    {
+      nxmutex_unlock(&client->lock);
+      return -ENOENT;
+    }
+
+  fidp->refcount++;
+  nxmutex_unlock(&client->lock);
+  return 0;
+}
diff --git a/fs/v9fs/client.h b/fs/v9fs/client.h
new file mode 100644
index 0000000000..e9a3dc132b
--- /dev/null
+++ b/fs/v9fs/client.h
@@ -0,0 +1,126 @@
+/****************************************************************************
+ * fs/v9fs/client.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __FS_V9FS_CLIENT_H
+#define __FS_V9FS_CLIENT_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/idr.h>
+#include <nuttx/list.h>
+#include <nuttx/mutex.h>
+
+#include <dirent.h>
+#include <sys/stat.h>
+#include <sys/statfs.h>
+
+/****************************************************************************
+ * Type Definitions
+ ****************************************************************************/
+
+struct v9fs_payload_s
+{
+  FAR struct iovec *wiov;
+  FAR struct iovec *riov;
+  struct list_node  node;
+  size_t            wcount;
+  size_t            rcount;
+  sem_t             resp;
+  uint16_t          tag;
+  int               ret;
+};
+
+struct v9fs_transport_s
+{
+  FAR const struct v9fs_transport_ops_s *ops;
+
+  /* The remainder of the structure is used by the "lower-half" driver
+   * for whatever state storage that it may need.
+   */
+};
+
+struct v9fs_transport_ops_s
+{
+  CODE int (*create)(FAR struct v9fs_transport_s **transport,
+                     FAR const char *args);
+  CODE int (*request)(FAR struct v9fs_transport_s *transport,
+                      FAR struct v9fs_payload_s *payload);
+  CODE void (*destroy)(FAR struct v9fs_transport_s *transport);
+};
+
+struct v9fs_client_s
+{
+  FAR struct v9fs_transport_s *transport;
+  FAR struct idr_s            *fids;
+  unsigned int                 msize;
+  uint32_t                     root_fid;
+  uint32_t                     tag_id;
+  mutex_t                      lock;
+};
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+int v9fs_client_statfs(FAR struct v9fs_client_s *client,
+                       FAR struct statfs *buf);
+int v9fs_client_stat(FAR struct v9fs_client_s *client, uint32_t fid,
+                     FAR struct stat *buf);
+off_t v9fs_client_getsize(FAR struct v9fs_client_s *client, uint32_t fid);
+int v9fs_client_chstat(FAR struct v9fs_client_s *client, uint32_t fid,
+                       FAR const struct stat *buf, int flags);
+ssize_t v9fs_client_read(FAR struct v9fs_client_s *client, uint32_t fid,
+                         FAR void *buffer, off_t offset, size_t buflen);
+ssize_t v9fs_client_convertdir(FAR const uint8_t *buffer, size_t bufsize,
+                               off_t head, FAR off_t *offset,
+                               FAR struct dirent *entry);
+ssize_t v9fs_client_readdir(FAR struct v9fs_client_s *client, uint32_t fid,
+                            FAR void *buffer, off_t offset, size_t buflen);
+ssize_t v9fs_client_write(FAR struct v9fs_client_s *client, uint32_t fid,
+                          FAR const void *buffer, off_t offset,
+                          size_t buflen);
+int v9fs_client_fsync(FAR struct v9fs_client_s *client, uint32_t fid);
+int v9fs_client_rename(FAR struct v9fs_client_s *client, uint32_t fid,
+                       uint32_t newfid, FAR const char *name);
+int v9fs_client_remove(FAR struct v9fs_client_s *client, uint32_t fid);
+int v9fs_client_unlink(FAR struct v9fs_client_s *client, uint32_t fid,
+                       FAR const char *name, bool isdir);
+int v9fs_client_mkdir(FAR struct v9fs_client_s *client, uint32_t fid,
+                      FAR const char *name, int mode);
+int v9fs_client_create(FAR struct v9fs_client_s *client, uint32_t fid,
+                       FAR const char *name, int oflags, int mode);
+int v9fs_client_open(FAR struct v9fs_client_s *client,
+                      uint32_t fid, int oflags);
+int v9fs_client_walk(FAR struct v9fs_client_s *client, FAR const char *path,
+                     FAR const char **childname);
+int v9fs_client_init(FAR struct v9fs_client_s *client, FAR const char *data);
+int v9fs_client_uninit(FAR struct v9fs_client_s *client);
+int v9fs_transport_create(FAR struct v9fs_transport_s **transport,
+                          FAR const char *trans_type, FAR const char *data);
+int v9fs_transport_request(FAR struct v9fs_transport_s *transport,
+                           FAR struct v9fs_payload_s *payload);
+void v9fs_transport_destroy(FAR struct v9fs_transport_s *transport);
+void v9fs_transport_done(FAR struct v9fs_payload_s *cookie, int ret);
+int v9fs_fid_put(FAR struct v9fs_client_s *client, uint32_t fid);
+int v9fs_fid_get(FAR struct v9fs_client_s *client, uint32_t fid);
+
+#endif /* __FS_V9FS_CLIENT_H */
diff --git a/fs/v9fs/transport.c b/fs/v9fs/transport.c
new file mode 100644
index 0000000000..e9b48b28c8
--- /dev/null
+++ b/fs/v9fs/transport.c
@@ -0,0 +1,103 @@
+/****************************************************************************
+ * fs/v9fs/transport.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <string.h>
+
+#include "client.h"
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct v9fs_transport_ops_map_s
+{
+  FAR const char *type;
+  FAR const struct v9fs_transport_ops_s *ops;
+};
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+#ifdef CONFIG_V9FS_VIRTIO_9P
+extern const struct v9fs_transport_ops_s g_virtio_9p_transport_ops;
+#endif
+
+static const struct v9fs_transport_ops_map_s g_transport_ops_map[] =
+{
+#ifdef CONFIG_V9FS_VIRTIO_9P
+  { "virtio", &g_virtio_9p_transport_ops },
+#endif
+  { NULL, NULL},
+};
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: v9fs_transport_request
+ ****************************************************************************/
+
+int v9fs_transport_request(FAR struct v9fs_transport_s *transport,
+                           FAR struct v9fs_payload_s *payload)
+{
+  return transport->ops->request(transport, payload);
+}
+
+/****************************************************************************
+ * Name: v9fs_transport_destroy
+ ****************************************************************************/
+
+void v9fs_transport_destroy(FAR struct v9fs_transport_s *transport)
+{
+  transport->ops->destroy(transport);
+}
+
+/****************************************************************************
+ * Name: v9fs_register_transport
+ ****************************************************************************/
+
+int v9fs_transport_create(FAR struct v9fs_transport_s **transport,
+                          FAR const char *trans_type, FAR const char *data)
+{
+  FAR const struct v9fs_transport_ops_map_s *map;
+
+  /* Find the corresponding driver layer */
+
+  for (map = g_transport_ops_map; map->type; map++)
+    {
+      if (strcmp(trans_type, map->type) == 0)
+        {
+          break;
+        }
+    }
+
+  if (map->ops == NULL)
+    {
+      return -ENOENT;
+    }
+
+  return map->ops->create(transport, data);
+}
diff --git a/fs/v9fs/v9fs.c b/fs/v9fs/v9fs.c
new file mode 100644
index 0000000000..aaa04d91e4
--- /dev/null
+++ b/fs/v9fs/v9fs.c
@@ -0,0 +1,961 @@
+/****************************************************************************
+ * fs/v9fs/v9fs.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <debug.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <libgen.h>
+#include <string.h>
+
+#include <nuttx/fs/fs.h>
+#include <nuttx/kmalloc.h>
+
+#include "inode/inode.h"
+#include "client.h"
+
+/****************************************************************************
+ * Private Type
+ ****************************************************************************/
+
+struct v9fs_vfs_file_s
+{
+  uint32_t fid;
+  mutex_t  lock;
+};
+
+struct v9fs_vfs_dirent_s
+{
+  struct fs_dirent_s base;
+  uint32_t           fid;
+  mutex_t            lock;
+  off_t              offset;
+  off_t              head;
+  size_t             size;
+  uint8_t            buffer[1];
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int v9fs_vfs_open(FAR struct file *filep, FAR const char *relpath,
+                         int oflags, mode_t mode);
+static int v9fs_vfs_close(FAR struct file *filep);
+static ssize_t v9fs_vfs_read(FAR struct file *filep, FAR char *buffer,
+                             size_t buflen);
+static ssize_t v9fs_vfs_write(FAR struct file *filep,
+                              FAR const char *buffer, size_t buflen);
+static off_t v9fs_vfs_seek(FAR struct file *filep, off_t offset,
+                           int whence);
+static int v9fs_vfs_sync(FAR struct file *filep);
+static int v9fs_vfs_dup(FAR const struct file *oldp, FAR struct file *newp);
+static int v9fs_vfs_fstat(FAR const struct file *filep,
+                          FAR struct stat *buf);
+static int v9fs_vfs_fchstat(FAR const struct file *filep,
+                            FAR const struct stat *buf, int flags);
+static int v9fs_vfs_truncate(FAR struct file *filep, off_t length);
+static int v9fs_vfs_opendir(FAR struct inode *mountpt,
+                            FAR const char *relpath,
+                            FAR struct fs_dirent_s **dir);
+static int v9fs_vfs_closedir(FAR struct inode *mountpt,
+                             FAR struct fs_dirent_s *dir);
+static int v9fs_vfs_readdir(FAR struct inode *mountpt,
+                            FAR struct fs_dirent_s *dir,
+                            FAR struct dirent *entry);
+static int v9fs_vfs_rewinddir(FAR struct inode *mountpt,
+                              FAR struct fs_dirent_s *dir);
+static int v9fs_vfs_statfs(FAR struct inode *mountpt,
+                           FAR struct statfs *buf);
+static int v9fs_vfs_unlink(FAR struct inode *mountpt,
+                           FAR const char *relpath);
+static int v9fs_vfs_mkdir(FAR struct inode *mountpt,
+                          FAR const char *relpath, mode_t mode);
+static int v9fs_vfs_rmdir(FAR struct inode *mountpt,
+                          FAR const char *relpath);
+static int v9fs_vfs_rename(FAR struct inode *mountpt,
+                           FAR const char *oldrelpath,
+                           FAR const char *newrelpath);
+static int v9fs_vfs_stat(FAR struct inode *mountpt,
+                         FAR const char *relpath, FAR struct stat *buf);
+static int v9fs_vfs_chstat(FAR struct inode *mountpt,
+                           FAR const char *relpath,
+                           FAR const struct stat *buf, int flags);
+static int v9fs_vfs_bind(FAR struct inode *driver, FAR const void *data,
+                         FAR void **handle);
+static int v9fs_vfs_unbind(FAR void *handle, FAR struct inode **blkdriver,
+                           unsigned int flags);
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+const struct mountpt_operations g_v9fs_operations =
+{
+  v9fs_vfs_open,                /* open */
+  v9fs_vfs_close,               /* close */
+  v9fs_vfs_read,                /* read */
+  v9fs_vfs_write,               /* write */
+  v9fs_vfs_seek,                /* seek */
+  NULL,                         /* ioctl */
+  NULL,                         /* mmap */
+  v9fs_vfs_truncate,            /* truncate */
+  NULL,                         /* poll */
+
+  v9fs_vfs_sync,                /* sync */
+  v9fs_vfs_dup,                 /* dup */
+  v9fs_vfs_fstat,               /* fstat */
+  v9fs_vfs_fchstat,             /* fchstat */
+
+  v9fs_vfs_opendir,             /* opendir */
+  v9fs_vfs_closedir,            /* closedir */
+  v9fs_vfs_readdir,             /* readdir */
+  v9fs_vfs_rewinddir,           /* rewinddir */
+
+  v9fs_vfs_bind,                /* bind */
+  v9fs_vfs_unbind,              /* unbind */
+  v9fs_vfs_statfs,              /* statfs */
+
+  v9fs_vfs_unlink,              /* unlink */
+  v9fs_vfs_mkdir,               /* mkdir */
+  v9fs_vfs_rmdir,               /* rmdir */
+  v9fs_vfs_rename,              /* rename */
+  v9fs_vfs_stat,                /* stat */
+  v9fs_vfs_chstat               /* chstat */
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: v9fs_vfs_open
+ ****************************************************************************/
+
+static int v9fs_vfs_open(FAR struct file *filep, FAR const char *relpath,
+                         int oflags, mode_t mode)
+{
+  FAR struct v9fs_vfs_file_s *file;
+  FAR struct v9fs_client_s *client;
+  int ret;
+
+  /* Sanity checks */
+
+  DEBUGASSERT(filep != NULL && filep->f_inode != NULL);
+
+  /* Recover out private data from the struct file instance */
+
+  client = filep->f_inode->i_private;
+
+  file = kmm_zalloc(sizeof(struct v9fs_vfs_file_s));
+  if (file == NULL)
+    {
+      return -ENOMEM;
+    }
+
+  ret = v9fs_client_walk(client, relpath, NULL);
+  if (ret >= 0)
+    {
+      file->fid = ret;
+      ret = v9fs_client_open(client, file->fid, oflags);
+      if (ret < 0)
+        {
+          ferr("ERROR: Failed to open the fid: %d\n", ret);
+          goto err_put;
+        }
+    }
+  else if ((oflags & O_CREAT) != 0)
+    {
+      /* We expect to create this file */
+
+      ret = v9fs_client_walk(client, relpath, &relpath);
+      if (ret < 0)
+        {
+          ferr("ERROR: Can't find the parent fid of relpath: %d\n", ret);
+          goto err_free;
+        }
+
+      file->fid = ret;
+      ret = v9fs_client_create(client, file->fid, relpath, oflags, mode);
+      if (ret < 0)
+        {
+          ferr("ERROR: Failed to create the file: %d\n", ret);
+          goto err_put;
+        }
+    }
+  else
+    {
+      /* We expect to open this file */
+
+      ferr("ERROR: Can't find the fid of relpath: %d\n", ret);
+      ret = -ENOENT;
+      goto err_free;
+    }
+
+  if ((oflags & O_APPEND) != 0)
+    {
+      filep->f_pos = v9fs_client_getsize(client, file->fid);
+      if (filep->f_pos < 0)
+        {
+          ret = filep->f_pos;
+          ferr("ERROR: Failed to get the file status: %d\n", ret);
+          goto err_put;
+        }
+    }
+
+  nxmutex_init(&file->lock);
+  filep->f_priv = file;
+  return 0;
+
+err_put:
+  v9fs_fid_put(client, file->fid);
+err_free:
+  kmm_free(file);
+  return ret;
+}
+
+/****************************************************************************
+ * Name: v9fs_vfs_close
+ ****************************************************************************/
+
+static int v9fs_vfs_close(FAR struct file *filep)
+{
+  FAR struct v9fs_client_s *client;
+  FAR struct v9fs_vfs_file_s *file;
+
+  /* Sanity checks */
+
+  DEBUGASSERT(filep != NULL && filep->f_inode != NULL);
+
+  /* Recover out private data from the struct file instance */
+
+  client = filep->f_inode->i_private;
+  file = filep->f_priv;
+
+  v9fs_fid_put(client, file->fid);
+  nxmutex_destroy(&file->lock);
+  kmm_free(file);
+  return 0;
+}
+
+/****************************************************************************
+ * Name: v9fs_vfs_read
+ ****************************************************************************/
+
+static ssize_t v9fs_vfs_read(FAR struct file *filep, FAR char *buffer,
+                             size_t buflen)
+{
+  FAR struct v9fs_vfs_file_s *file;
+  FAR struct v9fs_client_s *client;
+  ssize_t ret;
+
+  /* Sanity checks */
+
+  DEBUGASSERT(filep != NULL && filep->f_inode != NULL);
+
+  /* Recover out private data from the struct file instance */
+
+  client = filep->f_inode->i_private;
+  file = filep->f_priv;
+
+  nxmutex_lock(&file->lock);
+  ret = v9fs_client_read(client, file->fid, buffer, filep->f_pos, buflen);
+  if (ret > 0)
+    {
+      filep->f_pos += ret;
+    }
+
+  nxmutex_unlock(&file->lock);
+  return ret;
+}
+
+/****************************************************************************
+ * Name: v9fs_vfs_write
+ ****************************************************************************/
+
+static ssize_t v9fs_vfs_write(FAR struct file *filep, FAR const char *buffer,
+                              size_t buflen)
+{
+  FAR struct v9fs_vfs_file_s *file;
+  FAR struct v9fs_client_s *client;
+  ssize_t ret;
+
+  /* Sanity checks */
+
+  DEBUGASSERT(filep != NULL && filep->f_inode != NULL);
+
+  /* Recover out private data from the struct file instance */
+
+  client = filep->f_inode->i_private;
+  file = filep->f_priv;
+
+  nxmutex_lock(&file->lock);
+  ret = v9fs_client_write(client, file->fid, buffer, filep->f_pos, buflen);
+  if (ret > 0)
+    {
+      filep->f_pos += ret;
+    }
+
+  nxmutex_unlock(&file->lock);
+  return ret;
+}
+
+/****************************************************************************
+ * Name: v9fs_vfs_seek
+ ****************************************************************************/
+
+static off_t v9fs_vfs_seek(FAR struct file *filep, off_t offset, int whence)
+{
+  FAR struct v9fs_vfs_file_s *file;
+  FAR struct v9fs_client_s *client;
+  off_t ret;
+
+  /* Sanity checks */
+
+  DEBUGASSERT(filep != NULL && filep->f_inode != NULL);
+
+  /* Recover out private data from the struct file instance */
+
+  client = filep->f_inode->i_private;
+  file = filep->f_priv;
+
+  nxmutex_lock(&file->lock);
+  switch (whence)
+    {
+      case SEEK_SET:
+        ret = offset;
+        break;
+      case SEEK_CUR:
+        ret = filep->f_pos + offset;
+        break;
+      case SEEK_END:
+        ret = v9fs_client_getsize(client, file->fid);
+        if (ret >= 0)
+          {
+            ret += offset;
+          }
+
+        break;
+      default:
+        ferr("ERROR: Invalid whence: %d\n", whence);
+        ret = -EINVAL;
+        break;
+    }
+
+  if (ret >= 0)
+    {
+      filep->f_pos = ret;
+    }
+
+  nxmutex_unlock(&file->lock);
+  return ret;
+}
+
+/****************************************************************************
+ * Name: v9fs_vfs_sync
+ ****************************************************************************/
+
+static int v9fs_vfs_sync(FAR struct file *filep)
+{
+  FAR struct v9fs_vfs_file_s *file;
+  FAR struct v9fs_client_s *client;
+
+  client = filep->f_inode->i_private;
+  file = filep->f_priv;
+
+  return v9fs_client_fsync(client, file->fid);
+}
+
+/****************************************************************************
+ * Name: v9fs_vfs_dup
+ ****************************************************************************/
+
+static int v9fs_vfs_dup(FAR const struct file *oldp, FAR struct file *newp)
+{
+  FAR struct v9fs_vfs_file_s *newfile;
+  FAR struct v9fs_vfs_file_s *file;
+  FAR struct v9fs_client_s *client;
+  int ret;
+
+  /* Sanity checks */
+
+  DEBUGASSERT(oldp != NULL && oldp->f_inode != NULL &&
+              newp != NULL && newp->f_inode != NULL);
+
+  /* Recover out private data from the struct file instance */
+
+  client = oldp->f_inode->i_private;
+  file = oldp->f_priv;
+
+  newfile = kmm_zalloc(sizeof(struct v9fs_vfs_file_s));
+  if (newfile == NULL)
+    {
+      return -ENOMEM;
+    }
+
+  ret = v9fs_fid_get(client, file->fid);
+  if (ret < 0)
+    {
+      kmm_free(newfile);
+      return ret;
+    }
+
+  nxmutex_init(&newfile->lock);
+  newfile->fid = file->fid;
+  newp->f_priv = newfile;
+  return ret;
+}
+
+/****************************************************************************
+ * Name: v9fs_vfs_fstat
+ ****************************************************************************/
+
+static int v9fs_vfs_fstat(FAR const struct file *filep,
+                          FAR struct stat *buf)
+{
+  FAR struct v9fs_vfs_file_s *file;
+  FAR struct v9fs_client_s *client;
+
+  /* Sanity checks */
+
+  DEBUGASSERT(filep != NULL && filep->f_inode != NULL);
+
+  /* Recover out private data from the struct file instance */
+
+  client = filep->f_inode->i_private;
+  file = filep->f_priv;
+  memset(buf, 0, sizeof(struct stat));
+
+  return v9fs_client_stat(client, file->fid, buf);
+}
+
+/****************************************************************************
+ * Name: v9fs_vfs_fchstat
+ ****************************************************************************/
+
+static int v9fs_vfs_fchstat(FAR const struct file *filep,
+                            FAR const struct stat *buf, int flags)
+{
+  FAR struct v9fs_vfs_file_s *file;
+  FAR struct v9fs_client_s *client;
+
+  /* Sanity checks */
+
+  DEBUGASSERT(filep != NULL && filep->f_inode != NULL);
+
+  /* Recover out private data from the struct file instance */
+
+  client = filep->f_inode->i_private;
+  file = filep->f_priv;
+
+  return v9fs_client_chstat(client, file->fid, buf, flags);
+}
+
+/****************************************************************************
+ * Name: v9fs_vfs_truncate
+ ****************************************************************************/
+
+static int v9fs_vfs_truncate(FAR struct file *filep, off_t length)
+{
+  struct stat buf;
+
+  buf.st_size = length;
+  return v9fs_vfs_fchstat(filep, &buf, CH_STAT_SIZE);
+}
+
+/****************************************************************************
+ * Name: v9fs_vfs_opendir
+ ****************************************************************************/
+
+static int v9fs_vfs_opendir(FAR struct inode *mountpt,
+                            FAR const char *relpath,
+                            FAR struct fs_dirent_s **dir)
+{
+  FAR struct v9fs_vfs_dirent_s *fsdir;
+  FAR struct v9fs_client_s *client;
+  uint32_t fid;
+  int ret;
+
+  /* Sanity checks */
+
+  DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL);
+
+  /* Recover out private data from the struct file instance */
+
+  client = mountpt->i_private;
+
+  fsdir = kmm_zalloc(sizeof(struct v9fs_vfs_dirent_s) + client->msize);
+  if (fsdir == NULL)
+    {
+      return -ENOMEM;
+    }
+
+  ret = v9fs_client_walk(client, relpath, NULL);
+  if (ret < 0)
+    {
+      ferr("ERROR: Can't find the fid of the relpath: %d\n", ret);
+      goto err;
+    }
+
+  fid = ret;
+  ret = v9fs_client_open(client, fid, 0);
+  if (ret < 0)
+    {
+      ferr("ERROR: Failed to open the fid: %d\n", ret);
+      v9fs_fid_put(client, fid);
+      goto err;
+    }
+
+  nxmutex_init(&fsdir->lock);
+  fsdir->fid = fid;
+  *dir = &fsdir->base;
+  return 0;
+
+err:
+  kmm_free(fsdir);
+  return ret;
+}
+
+/****************************************************************************
+ * Name: v9fs_vfs_closedir
+ ****************************************************************************/
+
+static int v9fs_vfs_closedir(FAR struct inode *mountpt,
+                             FAR struct fs_dirent_s *dir)
+{
+  FAR struct v9fs_vfs_dirent_s *fsdir;
+  FAR struct v9fs_client_s *client;
+
+  /* Sanity checks */
+
+  DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL);
+
+  /* Recover out private data from the struct file instance */
+
+  fsdir = (FAR struct v9fs_vfs_dirent_s *)dir;
+  client = mountpt->i_private;
+
+  v9fs_fid_put(client, fsdir->fid);
+  nxmutex_destroy(&fsdir->lock);
+  kmm_free(fsdir);
+  return 0;
+}
+
+/****************************************************************************
+ * Name: v9fs_readdir
+ ****************************************************************************/
+
+static int v9fs_vfs_readdir(FAR struct inode *mountpt,
+                            FAR struct fs_dirent_s *dir,
+                            FAR struct dirent *entry)
+{
+  FAR struct v9fs_vfs_dirent_s *fsdir;
+  FAR struct v9fs_client_s *client;
+  ssize_t ret;
+
+  /* Sanity checks */
+
+  DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL);
+
+  /* Recover out private data from the struct file instance */
+
+  fsdir = (FAR struct v9fs_vfs_dirent_s *)dir;
+  client = mountpt->i_private;
+
+  nxmutex_lock(&fsdir->lock);
+  for (; ; )
+    {
+      if (fsdir->head == fsdir->size)
+        {
+          ret = v9fs_client_readdir(client, fsdir->fid, fsdir->buffer,
+                                    fsdir->offset, client->msize);
+          if (ret < 0)
+            {
+              break;
+            }
+
+          fsdir->head = 0;
+          fsdir->size = ret;
+        }
+
+      ret = v9fs_client_convertdir(fsdir->buffer, fsdir->size, fsdir->head,
+                                   &fsdir->offset, entry);
+      if (ret < 0)
+        {
+          break;
+        }
+
+      fsdir->head += ret;
+      if (strcmp(entry->d_name, ".") != 0 &&
+          strcmp(entry->d_name, "..") != 0)
+        {
+          break;
+        }
+    }
+
+  nxmutex_unlock(&fsdir->lock);
+  return ret < 0 ? ret : 0;
+}
+
+/****************************************************************************
+ * Name: v9fs_vfs_rewinddir
+ ****************************************************************************/
+
+static int v9fs_vfs_rewinddir(FAR struct inode *mountpt,
+                              FAR struct fs_dirent_s *dir)
+{
+  FAR struct v9fs_vfs_dirent_s *fsdir;
+
+  /* Sanity checks */
+
+  DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL);
+
+  /* Recover out private data from the struct file instance */
+
+  fsdir = (FAR struct v9fs_vfs_dirent_s *)dir;
+
+  nxmutex_lock(&fsdir->lock);
+  fsdir->head = 0;
+  fsdir->offset = 0;
+  fsdir->size = 0;
+  nxmutex_unlock(&fsdir->lock);
+  return 0;
+}
+
+/****************************************************************************
+ * Name: v9fs_vfs_statfs
+ ****************************************************************************/
+
+static int v9fs_vfs_statfs(FAR struct inode *mountpt,
+                           FAR struct statfs *buf)
+{
+  FAR struct v9fs_client_s *client;
+
+  /* Sanity checks */
+
+  DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL);
+
+  /* Recover out private data from the struct file instance */
+
+  client = mountpt->i_private;
+
+  memset(buf, 0, sizeof(struct statfs));
+  return v9fs_client_statfs(client, buf);
+}
+
+/****************************************************************************
+ * Name: v9fs_vfs_unlink
+ ****************************************************************************/
+
+static int v9fs_vfs_unlink(FAR struct inode *mountpt,
+                           FAR const char *relpath)
+{
+  FAR struct v9fs_client_s *client;
+  FAR const char *filename;
+  uint32_t fid;
+  int ret;
+
+  /* Sanity checks */
+
+  DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL);
+
+  /* Recover out private data from the struct file instance */
+
+  client = mountpt->i_private;
+
+  ret = v9fs_client_walk(client, relpath, &filename);
+  if (ret < 0)
+    {
+      ferr("ERROR: Can't find the parent fid of relpath: %d\n", ret);
+      return ret;
+    }
+
+  fid = ret;
+  ret = v9fs_client_unlink(client, fid, filename, false);
+  v9fs_fid_put(client, fid);
+  if (ret == -EOPNOTSUPP)
+    {
+      /* Maybe the server does not support this method of deletion.
+       * Let's change the protocol to send
+       */
+
+      ret = v9fs_client_walk(client, relpath, NULL);
+      if (ret < 0)
+        {
+          ferr("ERROR: Can't find the fid of relpath: %d\n", ret);
+          return ret;
+        }
+
+      fid = ret;
+      ret = v9fs_client_remove(client, fid);
+      v9fs_fid_put(client, fid);
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: v9fs_vfs_mkdir
+ ****************************************************************************/
+
+static int v9fs_vfs_mkdir(FAR struct inode *mountpt,
+                          FAR const char *relpath, mode_t mode)
+{
+  FAR struct v9fs_client_s *client;
+  uint32_t fid;
+  int ret;
+
+  /* Sanity checks */
+
+  DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL);
+
+  /* Recover out private data from the struct file instance */
+
+  client = mountpt->i_private;
+
+  ret = v9fs_client_walk(client, relpath, &relpath);
+  if (ret < 0)
+    {
+      ferr("ERROR: Can't find the parent fid of relpath: %d\n", ret);
+      return ret;
+    }
+
+  fid = ret;
+  ret = v9fs_client_mkdir(client, fid, relpath, mode);
+  v9fs_fid_put(client, fid);
+  return ret;
+}
+
+/****************************************************************************
+ * Name: v9fs_vfs_rmdir
+ ****************************************************************************/
+
+static int v9fs_vfs_rmdir(FAR struct inode *mountpt,
+                          FAR const char *relpath)
+{
+  FAR struct v9fs_client_s *client;
+  FAR const char *dirname;
+  uint32_t fid;
+  int ret;
+
+  /* Sanity checks */
+
+  DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL);
+
+  /* Recover out private data from the struct file instance */
+
+  client = mountpt->i_private;
+
+  ret = v9fs_client_walk(client, relpath, &dirname);
+  if (ret < 0)
+    {
+      ferr("ERROR: Can't find the parent fid of relpath: %d\n", ret);
+      return ret;
+    }
+
+  fid = ret;
+  ret = v9fs_client_unlink(client, fid, dirname, true);
+  v9fs_fid_put(client, fid);
+  if (ret == -EOPNOTSUPP)
+    {
+      /* Maybe the server does not support this method of deletion.
+       * Let's change the protocol to send
+       */
+
+      ret = v9fs_client_walk(client, relpath, NULL);
+      if (ret < 0)
+        {
+          ferr("ERROR: Can't find the fid of relpath: %d\n", ret);
+          return ret;
+        }
+
+      fid = ret;
+      ret = v9fs_client_remove(client, fid);
+      v9fs_fid_put(client, fid);
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: v9fs_vfs_rename
+ ****************************************************************************/
+
+static int v9fs_vfs_rename(FAR struct inode *mountpt,
+                           FAR const char *oldrelpath,
+                           FAR const char *newrelpath)
+{
+  FAR struct v9fs_client_s *client;
+  uint32_t oldfid;
+  uint32_t newpfid;
+  int ret;
+
+  /* Sanity checks */
+
+  DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL);
+
+  /* Recover out private data from the struct file instance */
+
+  client = mountpt->i_private;
+
+  ret = v9fs_client_walk(client, oldrelpath, NULL);
+  if (ret < 0)
+    {
+      ferr("ERROR: Can't find the fid of the oldrelpath: %d\n", ret);
+      return ret;
+    }
+
+  oldfid = ret;
+  ret = v9fs_client_walk(client, newrelpath, &newrelpath);
+  if (ret < 0)
+    {
+      ferr("ERROR: Can't find the new parent fid of the newrelpath: %d\n",
+           ret);
+      v9fs_fid_put(client, oldfid);
+      return ret;
+    }
+
+  newpfid = ret;
+  ret = v9fs_client_rename(client, oldfid, newpfid, newrelpath);
+  v9fs_fid_put(client, oldfid);
+  v9fs_fid_put(client, newpfid);
+  return ret;
+}
+
+/****************************************************************************
+ * Name: v9fs_vfs_stat
+ ****************************************************************************/
+
+static int v9fs_vfs_stat(FAR struct inode *mountpt, FAR const char *relpath,
+                         FAR struct stat *buf)
+{
+  FAR struct v9fs_client_s *client;
+  uint32_t fid;
+  int ret;
+
+  /* Sanity checks */
+
+  DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL);
+
+  /* Recover out private data from the struct file instance */
+
+  client = mountpt->i_private;
+  memset(buf, 0, sizeof(struct stat));
+
+  ret = v9fs_client_walk(client, relpath, NULL);
+  if (ret < 0)
+    {
+      ferr("ERROR: Can't find the fid of the relpath: %d\n", ret);
+      return ret;
+    }
+
+  fid = ret;
+  ret = v9fs_client_stat(client, fid, buf);
+  v9fs_fid_put(client, fid);
+  return ret;
+}
+
+/****************************************************************************
+ * Name: v9fs_vfs_chstat
+ ****************************************************************************/
+
+static int v9fs_vfs_chstat(FAR struct inode *mountpt,
+                           FAR const char *relpath,
+                           FAR const struct stat *buf, int flags)
+{
+  FAR struct v9fs_client_s *client;
+  uint32_t fid;
+  int ret;
+
+  /* Sanity checks */
+
+  DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL);
+
+  /* Recover out private data from the struct file instance */
+
+  client = mountpt->i_private;
+
+  ret = v9fs_client_walk(client, relpath, NULL);
+  if (ret < 0)
+    {
+      ferr("ERROR: Can't find the fid of the relpath %d\n", ret);
+      return ret;
+    }
+
+  fid = ret;
+  ret = v9fs_client_chstat(client, fid, buf, flags);
+  v9fs_fid_put(client, fid);
+  return ret;
+}
+
+/****************************************************************************
+ * Name: v9fs_vfs_unbind
+ ****************************************************************************/
+
+static int v9fs_vfs_unbind(FAR void *handle, FAR struct inode **blkdriver,
+                           unsigned int flags)
+{
+  FAR struct v9fs_client_s *client;
+  int ret;
+
+  client = handle;
+
+  ret = v9fs_client_uninit(client);
+  if (ret < 0)
+    {
+      ferr("ERROR: Failed to clunk root fid\n");
+      return ret;
+    }
+
+  kmm_free(client);
+  return ret;
+}
+
+/****************************************************************************
+ * Name: v9fs_vfs_bind
+ ****************************************************************************/
+
+static int v9fs_vfs_bind(FAR struct inode *driver, FAR const void *data,
+                         FAR void **handle)
+{
+  FAR struct v9fs_client_s *client;
+  int ret;
+
+  client = kmm_zalloc(sizeof(struct v9fs_client_s));
+  if (client == NULL)
+    {
+      return -ENOMEM;
+    }
+
+  ret = v9fs_client_init(client, data);
+  if (ret < 0)
+    {
+      ferr("ERROR: Failed to initialize for client: %d\n", ret);
+      kmm_free(client);
+      return ret;
+    }
+
+  *handle = client;
+  return ret;
+}
diff --git a/fs/v9fs/virtio_9p.c b/fs/v9fs/virtio_9p.c
new file mode 100644
index 0000000000..d777fbe513
--- /dev/null
+++ b/fs/v9fs/virtio_9p.c
@@ -0,0 +1,268 @@
+/****************************************************************************
+ * fs/v9fs/virtio_9p.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <debug.h>
+#include <errno.h>
+
+#include <nuttx/spinlock.h>
+#include <nuttx/virtio/virtio.h>
+
+#include "client.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define VIRTIO_9P_MOUNT_TAG 0
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct virtio_9p_config_s
+{
+  uint16_t tag_len;
+  char tag[NAME_MAX];
+};
+
+struct virtio_9p_priv_s
+{
+  FAR struct virtio_device *vdev;
+  struct v9fs_transport_s   transport;
+  struct virtio_driver      vdrv;
+  spinlock_t                lock;
+  char                      tag[0];
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* VirtIO transport api for 9p */
+
+static int virtio_9p_create(FAR struct v9fs_transport_s **transport,
+                            FAR const char *args);
+static void virtio_9p_destroy(FAR struct v9fs_transport_s *transport);
+static int virtio_9p_request(FAR struct v9fs_transport_s *transport,
+                             FAR struct v9fs_payload_s *payload);
+static void virtio_9p_done(FAR struct virtqueue *vq);
+static int virtio_9p_probe(FAR struct virtio_device *vdev);
+static void virtio_9p_remove(FAR struct virtio_device *vdev);
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+const struct v9fs_transport_ops_s g_virtio_9p_transport_ops =
+{
+  virtio_9p_create,  /* create */
+  virtio_9p_request, /* request */
+  virtio_9p_destroy, /* close */
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: virtio_9p_create
+ ****************************************************************************/
+
+static int virtio_9p_create(FAR struct v9fs_transport_s **transport,
+                            FAR const char *args)
+{
+  FAR struct virtio_9p_priv_s *priv;
+  FAR const char *start;
+  FAR const char *end;
+  size_t length;
+  int ret;
+
+  /* Parse device name */
+
+  start = strstr(args, "tag=");
+  if (start == NULL)
+    {
+      return -EINVAL;
+    }
+
+  start += 4;
+  end = strchr(start, ',');
+  length = end ? end - start + 1 : strlen(start) + 1;
+  priv = kmm_zalloc(sizeof(struct virtio_9p_priv_s) + length);
+  if (priv == NULL)
+    {
+      return -ENOMEM;
+    }
+
+  spin_lock_init(&priv->lock);
+  memcpy(priv->tag, start, length);
+  priv->vdrv.device = VIRTIO_ID_9P;
+  priv->vdrv.probe = virtio_9p_probe;
+  priv->vdrv.remove = virtio_9p_remove;
+  priv->transport.ops = &g_virtio_9p_transport_ops;
+  *transport = &priv->transport;
+  ret = virtio_register_driver(&priv->vdrv);
+  if (ret < 0)
+    {
+      kmm_free(priv);
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: virtio_9p_destroy
+ ****************************************************************************/
+
+static void virtio_9p_destroy(FAR struct v9fs_transport_s *transport)
+{
+  FAR struct virtio_9p_priv_s *priv =
+            container_of(transport, struct virtio_9p_priv_s, transport);
+  virtio_unregister_driver(&priv->vdrv);
+  kmm_free(priv);
+}
+
+/****************************************************************************
+ * Name: virtio_9p_request
+ ****************************************************************************/
+
+static int virtio_9p_request(FAR struct v9fs_transport_s *transport,
+                             FAR struct v9fs_payload_s *payload)
+{
+  FAR struct virtio_9p_priv_s *priv =
+             container_of(transport, struct virtio_9p_priv_s, transport);
+  FAR struct virtqueue *vq = priv->vdev->vrings_info[0].vq;
+  struct virtqueue_buf vb[payload->wcount + payload->rcount];
+  irqstate_t flags;
+  size_t i;
+  int ret;
+
+  for (i = 0; i < payload->wcount; i++)
+    {
+      vb[i].buf = payload->wiov[i].iov_base;
+      vb[i].len = payload->wiov[i].iov_len;
+    }
+
+  for (i = 0; i < payload->rcount; i++)
+    {
+      vb[payload->wcount + i].buf = payload->riov[i].iov_base;
+      vb[payload->wcount + i].len = payload->riov[i].iov_len;
+    }
+
+  flags = spin_lock_irqsave(&priv->lock);
+  ret = virtqueue_add_buffer(vq, vb, payload->wcount, payload->rcount,
+                             payload);
+  if (ret < 0)
+    {
+      vrterr("virtqueue_add_buffer failed, ret=%d\n", ret);
+      goto out;
+    }
+
+  virtqueue_kick(vq);
+out:
+  spin_unlock_irqrestore(&priv->lock, flags);
+  return ret;
+}
+
+/****************************************************************************
+ * Name: virtio_9p_done
+ ****************************************************************************/
+
+static void virtio_9p_done(FAR struct virtqueue *vq)
+{
+  FAR struct virtio_9p_priv_s *priv = vq->vq_dev->priv;
+  FAR struct v9fs_payload_s *payload;
+
+  for (; ; )
+    {
+      payload = virtqueue_get_buffer_lock(vq, NULL, NULL, &priv->lock);
+      if (payload == NULL)
+        {
+          break;
+        }
+
+      v9fs_transport_done(payload, 0);
+    }
+}
+
+/****************************************************************************
+ * Name: virtio_9p_probe
+ ****************************************************************************/
+
+static int virtio_9p_probe(FAR struct virtio_device *vdev)
+{
+  FAR struct virtio_9p_priv_s *priv = container_of(vdev->priv,
+                                      struct virtio_9p_priv_s, vdrv);
+  struct virtio_9p_config_s config;
+  FAR const char *vqname[1];
+  vq_callback callback[1];
+  int ret;
+
+  virtio_set_status(vdev, VIRTIO_CONFIG_STATUS_DRIVER);
+  virtio_negotiate_features(vdev, 1 << VIRTIO_9P_MOUNT_TAG);
+  virtio_set_status(vdev, VIRTIO_CONFIG_FEATURES_OK);
+
+  if (!virtio_has_feature(vdev, VIRTIO_9P_MOUNT_TAG))
+    {
+      virtio_reset_device(vdev);
+      return -EINVAL;
+    }
+
+  virtio_read_config_member(vdev, struct virtio_9p_config_s, tag_len,
+                            &config.tag_len);
+  virtio_read_config(vdev, offsetof(struct virtio_9p_config_s, tag),
+                     &config.tag, config.tag_len);
+  config.tag[config.tag_len] = '\0';
+  if (strcmp(config.tag, priv->tag))
+    {
+      virtio_reset_device(vdev);
+      return -ENOENT;
+    }
+
+  vqname[0] = "virtio_9p_vq";
+  callback[0] = virtio_9p_done;
+  ret = virtio_create_virtqueues(vdev, 0, 1, vqname, callback);
+  if (ret < 0)
+    {
+      vrterr("virtio_device_create_virtqueue failed, ret=%d\n", ret);
+      virtio_reset_device(vdev);
+      return ret;
+    }
+
+  priv->vdev = vdev;
+  vdev->priv = priv;
+  virtio_set_status(vdev, VIRTIO_CONFIG_STATUS_DRIVER_OK);
+  virtqueue_enable_cb(vdev->vrings_info[0].vq);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: virtio_9p_remove
+ ****************************************************************************/
+
+static void virtio_9p_remove(FAR struct virtio_device *vdev)
+{
+  virtio_delete_virtqueues(vdev);
+  virtio_reset_device(vdev);
+}
diff --git a/include/sys/statfs.h b/include/sys/statfs.h
index 83cc8a0db9..a1158f860a 100644
--- a/include/sys/statfs.h
+++ b/include/sys/statfs.h
@@ -96,6 +96,7 @@
 #define CROMFS_MAGIC          0x4d4f5243
 #define RPMSGFS_MAGIC         0x54534f47
 #define ZIPFS_MAGIC           0x504b
+#define V9FS_MAGIC            0x01021997
 
 #if defined(CONFIG_FS_LARGEFILE)
 #  define statfs64            statfs

Reply via email to