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 a1226657a5ce40cd7b9bff921a4ad16a221b64d2
Author: zhengyu16 <[email protected]>
AuthorDate: Thu Jan 29 15:53:11 2026 +0800

    fs/v9fs: add readlink lstat link symlink
    
    Add the 9P2000.L Treadlink, Tsymlink and Tlink requests, and the
    readlink(), lstat(), link() and symlink() operations.  The walk follows
    the symbolic links on a path, up to SYMLOOP_MAX.
    
    Rebased on master: the operations table gets the ioctldir and
    permission slots, and the temporary buffers use lib_get_pathbuffer().
    
    Signed-off-by: zhengyu16 <[email protected]>
    Assisted-by: Claude Code:claude-opus-5-5
    Signed-off-by: Marco Casaroli <[email protected]>
---
 fs/v9fs/client.c | 603 +++++++++++++++++++++++++++++++++++++++++++++++++++++--
 fs/v9fs/client.h |   9 +-
 fs/v9fs/v9fs.c   | 217 ++++++++++++++++++--
 3 files changed, 794 insertions(+), 35 deletions(-)

diff --git a/fs/v9fs/client.c b/fs/v9fs/client.c
index 31a7b9f05e1..8a4f857d22e 100644
--- a/fs/v9fs/client.c
+++ b/fs/v9fs/client.c
@@ -97,6 +97,18 @@
 
 /* 9P2000.L unlink flags */
 
+/* 9P QID type bits */
+
+#define V9FS_QTDIR             0x80       /* Directory */
+#define V9FS_QTAPPEND          0x40       /* Append only */
+#define V9FS_QTEXCL            0x20       /* Exclusive use */
+#define V9FS_QTMOUNT           0x10       /* Mounted channel */
+#define V9FS_QTAUTH            0x08       /* Authentication file */
+#define V9FS_QTTMP             0x04       /* Temporary file */
+#define V9FS_QTLINK            0x02       /* Symbolic link */
+#define V9FS_QTHARDLINK        0x01       /* hard link */
+#define V9FS_QTFILE            0x00       /* Regular file */
+
 #define V9FS_REMOVEDIR         0x200
 
 /* QID = uint8_t (type) + uint32_t (version) + uint64_t (path) */
@@ -117,8 +129,12 @@ enum v9fs_flags_e
   V9FS_RLOPEN,
   V9FS_TLCREATE = 14,
   V9FS_RLCREATE,
+  V9FS_TSYMLINK = 16,
+  V9FS_RSYMLINK,
   V9FS_TRENAME = 20,
   V9FS_RRENAME,
+  V9FS_TREADLINK = 22,
+  V9FS_RREADLINK,
   V9FS_TGETATTR = 24,
   V9FS_RGETATTR,
   V9FS_TSETATTR = 26,
@@ -127,6 +143,8 @@ enum v9fs_flags_e
   V9FS_RREADDIR,
   V9FS_TFSYNC = 50,
   V9FS_RFSYNC,
+  V9FS_TLINK = 70,
+  V9FS_RLINK,
   V9FS_TMKDIR = 72,
   V9FS_RMKDIR,
   V9FS_TRENAMEAT = 74,
@@ -361,6 +379,38 @@ begin_packed_struct struct v9fs_rstatfs_s
   uint32_t namelen; /* Maximum length of filenames */
 } end_packed_struct;
 
+begin_packed_struct struct v9fs_symlink_s
+{
+  struct v9fs_header_s header;
+  uint32_t fid;
+  uint16_t name_len;
+  uint8_t buffer[V9FS_BIT16SZ + 2 * NAME_MAX + V9FS_BIT32SZ];
+} end_packed_struct;
+
+#define v9fs_rsymlink_s v9fs_rattach_s
+
+begin_packed_struct struct v9fs_link_s
+{
+  struct v9fs_header_s header;
+  uint32_t dfid;
+  uint32_t fid;
+  uint16_t name_len;
+  uint8_t buffer[NAME_MAX];
+} end_packed_struct;
+
+begin_packed_struct struct v9fs_readlink_s
+{
+  struct v9fs_header_s header;
+  uint32_t fid;
+} end_packed_struct;
+
+begin_packed_struct struct v9fs_rreadlink_s
+{
+  struct v9fs_header_s header;
+  uint16_t target_len;
+  uint8_t target[PATH_MAX];
+} end_packed_struct;
+
 struct v9fs_map_s
 {
   int original;
@@ -1475,17 +1525,111 @@ int v9fs_client_getname(FAR struct v9fs_client_s 
*client, uint32_t fid,
   return 0;
 }
 
+#ifdef CONFIG_FS_LINKS
 /****************************************************************************
- * v9fs_client_walk
+ * v9fs_is_last_qid_symlink
  ****************************************************************************/
 
-int v9fs_client_walk(FAR struct v9fs_client_s *client, FAR const char *path,
-                     FAR const char **childname)
+static inline bool
+v9fs_is_last_qid_symlink(FAR struct v9fs_rwalk_s *response,
+                         FAR char *response_payload)
+{
+  FAR struct v9fs_qid_s *qids = (FAR struct v9fs_qid_s *)response_payload;
+
+  if (response->nwqid == 1)
+    {
+      return (response->wqid[0].type & V9FS_QTLINK) != 0;
+    }
+
+  if (response->nwqid > 1)
+    {
+      return (qids[response->nwqid - 2].type & V9FS_QTLINK) != 0;
+    }
+
+  return false;
+}
+
+/****************************************************************************
+ * v9fs_build_path_to_component
+ ****************************************************************************/
+
+static int v9fs_build_path_to_component(FAR const char *path,
+                                        int target_comp,
+                                        FAR char *out_path,
+                                        size_t out_size)
+{
+  FAR const char *start = path;
+  FAR const char *end;
+  FAR char *dest = out_path;
+  size_t remaining = out_size;
+  int comp_idx = 0;
+  size_t len;
+
+  while (comp_idx < target_comp && *start != '\0')
+    {
+      while (*start == '/')
+        {
+          start++;
+        }
+
+      end = strchr(start, '/');
+      if (end == NULL)
+        {
+          /* Last component - copy remaining path */
+
+          len = strlen(start);
+          if (len >= remaining)
+            {
+              return -ENAMETOOLONG;
+            }
+
+          memcpy(dest, start, len);
+          dest[len] = '\0';
+          return 0;
+        }
+
+      /* Copy component */
+
+      len = end - start;
+      if (len >= remaining - 1)
+        {
+          return -ENAMETOOLONG;
+        }
+
+      memcpy(dest, start, len);
+      dest += len;
+      remaining -= len;
+      comp_idx++;
+
+      /* Add separator only if not the last component */
+
+      if (comp_idx < target_comp)
+        {
+          *dest++ = '/';
+          remaining--;
+        }
+
+      start = end + 1;
+    }
+
+  *dest = '\0';
+  return 0;
+}
+#endif
+
+/****************************************************************************
+ * v9fs_client_walk_impl
+ ****************************************************************************/
+
+static int v9fs_client_walk_impl(FAR struct v9fs_client_s *client,
+                                 FAR const char *path,
+                                 FAR const char **childname,
+                                 FAR struct v9fs_rwalk_s *response,
+                                 FAR char *response_payload,
+                                 FAR uint16_t *nwname_out)
 {
   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];
@@ -1526,6 +1670,11 @@ int v9fs_client_walk(FAR struct v9fs_client_s *client, 
FAR const char *path,
       start = end + 1;
     }
 
+  if (nwname_out != NULL)
+    {
+      *nwname_out = nwname;
+    }
+
   /* Request = sizeof(struct v9fs_walk_s) + nwname * sizeof(uint16_t) +
    *           total_len
    * Response = sizeof(struct v9fs_rwalk_s) + V9FS_QIDSZ * (nwname + 1)
@@ -1545,13 +1694,6 @@ int v9fs_client_walk(FAR struct v9fs_client_s *client, 
FAR const char *path,
       return -ENOMEM;
     }
 
-  response_payload = lib_get_pathbuffer();
-  if (response_payload == NULL)
-    {
-      lib_put_pathbuffer(request_payload);
-      return -ENOMEM;
-    }
-
   ret = v9fs_fid_create(client, path);
   if (ret < 0)
     {
@@ -1603,33 +1745,326 @@ int v9fs_client_walk(FAR struct v9fs_client_s *client, 
FAR const char *path,
   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[0].iov_base = response;
+  riov[0].iov_len = sizeof(*response);
   riov[1].iov_base = response_payload;
-  riov[1].iov_len = V9FS_QIDSZ * (nwname + 1);
+
+  /* First qid is in response.wqid[0], remaining (nwname-1) qids in
+   * 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);
+                        riov, nwname > 1 ? 2 : 1, request.header.tag);
   if (ret < 0)
     {
       v9fs_fid_destroy(client, newfid);
       goto err;
     }
 
-  /* There are differences in different server implementations, so it is
-   * necessary to check whether the returned nwqid satisfies the requested
-   * number.
+  /* Per 9P protocol specification:
+   * - nwqid is either nwname (full success) or the index of the first
+   *   elementwise walk that failed
+   * - nwqid cannot be zero unless nwname is zero
+   * - Only if nwqid == nwname will newfid be affected; otherwise newfid
+   *   should be unaffected (destroyed on client side)
+   *
+   * Walk results:
+   * 1. nwqid == nwname: fully successful, newfid represents the target file
+   * 2. 0 < nwqid < nwname: partial success, walk stopped (possibly at
+   *    symlink or non-existent path), newfid should be destroyed
    */
 
-  if (response.nwqid != nwname)
+  if (response->nwqid != nwname)
     {
       ret = -ENOENT;
+      v9fs_fid_destroy(client, newfid);
     }
 
 err:
   lib_put_pathbuffer(request_payload);
+  return ret < 0 ? ret : newfid;
+}
+
+/****************************************************************************
+ * v9fs_client_walk_internal
+ ****************************************************************************/
+
+static int v9fs_client_walk_internal(FAR struct v9fs_client_s *client,
+                                     FAR const char *path,
+                                     FAR const char **childname,
+                                     bool nofollow,
+                                     int symloop_count)
+{
+  struct v9fs_rwalk_s response;
+  FAR char *response_payload;
+  uint16_t nwname;
+  int fid;
+  int ret;
+#ifdef CONFIG_FS_LINKS
+  FAR char *symlink_path = NULL;
+  FAR char *readlink_buf;
+  FAR char *new_path;
+  bool is_symlink;
+
+  if (symloop_count >= SYMLOOP_MAX)
+    {
+      return -ELOOP;
+    }
+#endif
+
+  response_payload = lib_get_pathbuffer();
+  if (response_payload == NULL)
+    {
+      return -ENOMEM;
+    }
+
+  ret = fid = v9fs_client_walk_impl(client, path, childname,
+                                    &response, response_payload, &nwname);
+
+#ifdef CONFIG_FS_LINKS
+  /* Handle walk failure that might be caused by symlink */
+
+  if (ret < 0)
+    {
+      /* Case 1: Partial walk failed (ENOENT), check if stopped at symlink */
+
+      if (ret == -ENOENT && response.header.type == V9FS_RWALK &&
+          v9fs_is_last_qid_symlink(&response, response_payload))
+        {
+          symlink_path = lib_get_pathbuffer();
+          if (symlink_path == NULL)
+            {
+              ret = -ENOMEM;
+              goto errout_with_response;
+            }
+
+          ret = v9fs_build_path_to_component(path, response.nwqid,
+                                             symlink_path, PATH_MAX);
+          if (ret < 0)
+            {
+              goto errout_with_symlink;
+            }
+
+          fid = v9fs_client_walk_impl(client, symlink_path, NULL,
+                                      &response, response_payload,
+                                      NULL);
+          if (fid < 0)
+            {
+              ret = fid;
+              goto errout_with_symlink;
+            }
+        }
+
+      /* Case 2: ENOTDIR - some servers (e.g., qemu 6.2.0) return lerror
+       * instead of rwalk even on partial success. Any component could be
+       * a symlink, so we need to walk each component to find it.
+       */
+
+      else if (ret == -ENOTDIR)
+        {
+          FAR const char *start = path;
+          FAR const char *end;
+          int comp_count = 0;
+          int comp_idx;
+
+          /* Count path components */
+
+          while (*start != '\0')
+            {
+              while (*start == '/')
+                {
+                  start++;
+                }
+
+              if (*start == '\0')
+                {
+                  break;
+                }
+
+              comp_count++;
+              end = strchr(start, '/');
+              if (end == NULL)
+                {
+                  break;
+                }
+
+              start = end + 1;
+            }
+
+          symlink_path = lib_get_pathbuffer();
+          if (symlink_path == NULL)
+            {
+              ret = -ENOMEM;
+              goto errout_with_response;
+            }
+
+          /* Try walking each component to find the symlink */
+
+          for (comp_idx = 1; comp_idx <= comp_count; comp_idx++)
+            {
+              ret = v9fs_build_path_to_component(path, comp_idx,
+                                                 symlink_path, PATH_MAX);
+              if (ret < 0)
+                {
+                  goto errout_with_symlink;
+                }
+
+              fid = v9fs_client_walk_impl(client, symlink_path, NULL,
+                                          &response, response_payload,
+                                          NULL);
+              if (fid < 0)
+                {
+                  /* Walk failed, no point trying longer paths */
+
+                  break;
+                }
+
+              /* Check if this component is a symlink */
+
+              if (v9fs_is_last_qid_symlink(&response, response_payload))
+                {
+                  /* Found the symlink, process it */
+
+                  break;
+                }
+
+              /* Not a symlink, clean up and try next component */
+
+              v9fs_fid_put(client, fid);
+              fid = -1;
+            }
+
+          if (fid < 0)
+            {
+              /* No symlink found, return the original ENOTDIR error */
+
+              ret = -ENOTDIR;
+              goto errout_with_symlink;
+            }
+        }
+
+      /* Case 3: Other errors */
+
+      else
+        {
+          goto errout_with_response;
+        }
+    }
+
+  /* Check if we need to follow a symlink */
+
+  is_symlink = v9fs_is_last_qid_symlink(&response, response_payload);
+
+  if (is_symlink &&
+      (response.nwqid < nwname || childname != NULL || !nofollow))
+    {
+      FAR const char *start = path;
+      FAR const char *end;
+      int comp_idx = 0;
+
+      readlink_buf = lib_get_pathbuffer();
+      if (readlink_buf == NULL)
+        {
+          ret = -ENOMEM;
+          goto errout_with_fid;
+        }
+
+      new_path = lib_get_pathbuffer();
+      if (new_path == NULL)
+        {
+          ret = -ENOMEM;
+          goto errout_with_readlink;
+        }
+
+      ret = v9fs_client_readlink(client, fid, readlink_buf, PATH_MAX - 1);
+      if (ret < 0)
+        {
+          goto errout_with_newpath;
+        }
+
+      readlink_buf[ret] = '\0';
+
+      /* Only relative symlinks are supported */
+
+      if (readlink_buf[0] == '/')
+        {
+          ret = -ENOTSUP;
+          goto errout_with_newpath;
+        }
+
+      /* Find the end of symlink component in original path */
+
+      while (comp_idx < response.nwqid - 1 && *start != '\0')
+        {
+          end = strchr(start, '/');
+          if (end != start)
+            {
+              comp_idx++;
+            }
+
+          if (end == NULL)
+            {
+              break;
+            }
+
+          start = end + 1;
+        }
+
+      while (*start == '/')
+        {
+          start++;
+        }
+
+      end = strchr(start, '/');
+      if (end == NULL)
+        {
+          end = start + strlen(start);
+        }
+
+      /* Construct: prefix + symlink_target + suffix */
+
+      snprintf(new_path, PATH_MAX, "%.*s%s%s",
+               (int)(start - path), path, readlink_buf, end);
+      ret = v9fs_client_walk_internal(client, new_path, childname,
+                                      nofollow, symloop_count + 1);
+      goto errout_with_newpath;
+    }
+
+  /* Success path */
+
+  goto errout_with_symlink;
+
+errout_with_newpath:
+  lib_put_pathbuffer(new_path);
+
+errout_with_readlink:
+  lib_put_pathbuffer(readlink_buf);
+
+errout_with_fid:
+  v9fs_fid_put(client, fid);
+
+errout_with_symlink:
+  if (symlink_path != NULL)
+    {
+      lib_put_pathbuffer(symlink_path);
+    }
+
+errout_with_response:
+#endif
   lib_put_pathbuffer(response_payload);
-  return ret == 0 ? newfid : ret;
+  return ret;
+}
+
+/****************************************************************************
+ * v9fs_client_walk
+ ****************************************************************************/
+
+int v9fs_client_walk(FAR struct v9fs_client_s *client, FAR const char *path,
+                     FAR const char **childname, bool nofollow)
+{
+  return v9fs_client_walk_internal(client, path, childname, nofollow, 0);
 }
 
 /****************************************************************************
@@ -1855,3 +2290,129 @@ ssize_t v9fs_parse_size(FAR const void *buffer)
 
   return ptr->size;
 }
+
+#ifdef CONFIG_FS_LINKS
+/****************************************************************************
+ * v9fs_client_symlink
+ ****************************************************************************/
+
+int v9fs_client_symlink(FAR struct v9fs_client_s *client, uint32_t fid,
+                        FAR const char *name, FAR const char *symtgt,
+                        int mode)
+{
+  struct v9fs_symlink_s request;
+  struct v9fs_rsymlink_s response;
+  struct iovec wiov[1];
+  struct iovec riov[1];
+  uint32_t gid = getgid();
+  off_t offset = 0;
+  uint16_t symtgt_len;
+
+  /* size[4] Tsymlink tag[2] fid[4] name[s] symtgt[s] gid[4]
+   * size[4] Rsymlink tag[2] qid[13]
+   */
+
+  request.fid = fid;
+  request.name_len = strlen(name);
+  symtgt_len = strlen(symtgt);
+  request.header.size = V9FS_HDRSZ + V9FS_BIT32SZ * 2 + V9FS_BIT16SZ * 2 +
+                        request.name_len + symtgt_len;
+  request.header.type = V9FS_TSYMLINK;
+  request.header.tag = v9fs_get_tagid(client);
+
+  memcpy(&request.buffer[offset], name, request.name_len);
+  offset += request.name_len;
+  memcpy(&request.buffer[offset], &symtgt_len, V9FS_BIT16SZ);
+  offset += V9FS_BIT16SZ;
+  memcpy(&request.buffer[offset], symtgt, symtgt_len);
+  offset += symtgt_len;
+  memcpy(&request.buffer[offset], &gid, V9FS_BIT32SZ);
+
+  wiov[0].iov_base = &request;
+  wiov[0].iov_len = V9FS_HDRSZ + V9FS_BIT32SZ * 2 + V9FS_BIT16SZ * 2 +
+                    request.name_len + symtgt_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_link
+ ****************************************************************************/
+
+int v9fs_client_link(FAR struct v9fs_client_s *client, uint32_t dfid,
+                     uint32_t fid, FAR const char *name)
+{
+  struct v9fs_link_s request;
+  struct v9fs_lerror_s response;
+  struct iovec wiov[1];
+  struct iovec riov[1];
+
+  /* size[4] Tlink tag[2] dfid[4] fid[4] name[s]
+   * size[4] Rlink tag[2]
+   */
+
+  request.dfid = dfid;
+  request.fid = fid;
+  request.name_len = strlen(name);
+  request.header.size = V9FS_HDRSZ + V9FS_BIT32SZ * 2 + V9FS_BIT16SZ +
+                        request.name_len;
+  request.header.type = V9FS_TLINK;
+  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_readlink
+ ****************************************************************************/
+
+ssize_t v9fs_client_readlink(FAR struct v9fs_client_s *client, uint32_t fid,
+                             FAR char *buffer, size_t buflen)
+{
+  struct v9fs_readlink_s request;
+  struct v9fs_rreadlink_s response;
+  struct iovec wiov[1];
+  struct iovec riov[1];
+  int ret;
+
+  /* size[4] Treadlink tag[2] fid[4]
+   * size[4] Rreadlink tag[2] target[s]
+   */
+
+  request.fid = fid;
+  request.header.size = V9FS_HDRSZ + V9FS_BIT32SZ;
+  request.header.type = V9FS_TREADLINK;
+  request.header.tag = v9fs_get_tagid(client);
+
+  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;
+    }
+
+  if (response.target_len > buflen)
+    {
+      return -ENAMETOOLONG;
+    }
+
+  memcpy(buffer, response.target, response.target_len);
+  return response.target_len;
+}
+#endif
diff --git a/fs/v9fs/client.h b/fs/v9fs/client.h
index 0ec6c014644..f018042e276 100644
--- a/fs/v9fs/client.h
+++ b/fs/v9fs/client.h
@@ -116,7 +116,14 @@ int v9fs_client_open(FAR struct v9fs_client_s *client,
 int v9fs_client_getname(FAR struct v9fs_client_s *client, uint32_t fid,
                         FAR char *path);
 int v9fs_client_walk(FAR struct v9fs_client_s *client, FAR const char *path,
-                     FAR const char **childname);
+                     FAR const char **childname, bool nofollow);
+int v9fs_client_symlink(FAR struct v9fs_client_s *client, uint32_t fid,
+                        FAR const char *name, FAR const char *symtgt,
+                        int mode);
+int v9fs_client_link(FAR struct v9fs_client_s *client, uint32_t dfid,
+                     uint32_t fid, FAR const char *name);
+ssize_t v9fs_client_readlink(FAR struct v9fs_client_s *client, uint32_t fid,
+                             FAR char *buffer, size_t buflen);
 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,
diff --git a/fs/v9fs/v9fs.c b/fs/v9fs/v9fs.c
index f6777035612..28a40efee9a 100644
--- a/fs/v9fs/v9fs.c
+++ b/fs/v9fs/v9fs.c
@@ -108,6 +108,20 @@ static int v9fs_vfs_stat(FAR struct inode *mountpt,
 static int v9fs_vfs_chstat(FAR struct inode *mountpt,
                            FAR const char *relpath,
                            FAR const struct stat *buf, int flags);
+#ifdef CONFIG_FS_LINKS
+static int v9fs_vfs_link(FAR struct inode *mountpt,
+                         FAR const char *relpath1,
+                         FAR const char *relpath2);
+static int v9fs_vfs_symlink(FAR struct inode *mountpt,
+                            FAR const char *path1,
+                            FAR const char *relpath2);
+static ssize_t v9fs_vfs_readlink(FAR struct inode *mountpt,
+                                 FAR const char *relpath,
+                                 FAR char *buffer, size_t buflen);
+static int v9fs_vfs_lstat(FAR struct inode *mountpt,
+                          FAR const char *relpath,
+                          FAR struct stat *buf);
+#endif
 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,
@@ -150,7 +164,16 @@ const struct mountpt_operations g_v9fs_operations =
   v9fs_vfs_rmdir,               /* rmdir */
   v9fs_vfs_rename,              /* rename */
   v9fs_vfs_stat,                /* stat */
-  v9fs_vfs_chstat               /* chstat */
+  v9fs_vfs_chstat,              /* chstat */
+  NULL,                         /* syncfs */
+  NULL,                         /* ioctldir */
+  NULL,                         /* permission */
+#ifdef CONFIG_FS_LINKS
+  v9fs_vfs_link,                /* link */
+  v9fs_vfs_symlink,             /* symlink */
+  v9fs_vfs_readlink,            /* readlink */
+  v9fs_vfs_lstat,               /* lstat */
+#endif
 };
 
 /****************************************************************************
@@ -182,7 +205,7 @@ static int v9fs_vfs_open(FAR struct file *filep, FAR const 
char *relpath,
       return -ENOMEM;
     }
 
-  ret = v9fs_client_walk(client, relpath, NULL);
+  ret = v9fs_client_walk(client, relpath, NULL, false);
   if (ret >= 0)
     {
       file->fid = ret;
@@ -197,7 +220,7 @@ static int v9fs_vfs_open(FAR struct file *filep, FAR const 
char *relpath,
     {
       /* We expect to create this file */
 
-      ret = v9fs_client_walk(client, relpath, &relpath);
+      ret = v9fs_client_walk(client, relpath, &relpath, false);
       if (ret < 0)
         {
           ferr("ERROR: Can't find the parent fid of relpath: %d\n", ret);
@@ -543,7 +566,7 @@ static int v9fs_vfs_opendir(FAR struct inode *mountpt,
       return -ENOMEM;
     }
 
-  ret = v9fs_client_walk(client, relpath, NULL);
+  ret = v9fs_client_walk(client, relpath, NULL, false);
   if (ret < 0)
     {
       ferr("ERROR: Can't find the fid of the relpath: %d\n", ret);
@@ -726,7 +749,7 @@ static int v9fs_vfs_unlink(FAR struct inode *mountpt,
 
   client = mountpt->i_private;
 
-  ret = v9fs_client_walk(client, relpath, &filename);
+  ret = v9fs_client_walk(client, relpath, &filename, true);
   if (ret < 0)
     {
       ferr("ERROR: Can't find the parent fid of relpath: %d\n", ret);
@@ -742,7 +765,7 @@ static int v9fs_vfs_unlink(FAR struct inode *mountpt,
        * Let's change the protocol to send
        */
 
-      ret = v9fs_client_walk(client, relpath, NULL);
+      ret = v9fs_client_walk(client, relpath, NULL, true);
       if (ret < 0)
         {
           ferr("ERROR: Can't find the fid of relpath: %d\n", ret);
@@ -776,7 +799,7 @@ static int v9fs_vfs_mkdir(FAR struct inode *mountpt,
 
   client = mountpt->i_private;
 
-  ret = v9fs_client_walk(client, relpath, &relpath);
+  ret = v9fs_client_walk(client, relpath, &relpath, false);
   if (ret < 0)
     {
       ferr("ERROR: Can't find the parent fid of relpath: %d\n", ret);
@@ -809,7 +832,7 @@ static int v9fs_vfs_rmdir(FAR struct inode *mountpt,
 
   client = mountpt->i_private;
 
-  ret = v9fs_client_walk(client, relpath, &dirname);
+  ret = v9fs_client_walk(client, relpath, &dirname, true);
   if (ret < 0)
     {
       ferr("ERROR: Can't find the parent fid of relpath: %d\n", ret);
@@ -825,7 +848,7 @@ static int v9fs_vfs_rmdir(FAR struct inode *mountpt,
        * Let's change the protocol to send
        */
 
-      ret = v9fs_client_walk(client, relpath, NULL);
+      ret = v9fs_client_walk(client, relpath, NULL, true);
       if (ret < 0)
         {
           ferr("ERROR: Can't find the fid of relpath: %d\n", ret);
@@ -861,7 +884,7 @@ static int v9fs_vfs_rename(FAR struct inode *mountpt,
 
   client = mountpt->i_private;
 
-  ret = v9fs_client_walk(client, oldrelpath, NULL);
+  ret = v9fs_client_walk(client, oldrelpath, NULL, true);
   if (ret < 0)
     {
       ferr("ERROR: Can't find the fid of the oldrelpath: %d\n", ret);
@@ -869,7 +892,7 @@ static int v9fs_vfs_rename(FAR struct inode *mountpt,
     }
 
   oldfid = ret;
-  ret = v9fs_client_walk(client, newrelpath, &newrelpath);
+  ret = v9fs_client_walk(client, newrelpath, &newrelpath, false);
   if (ret < 0)
     {
       ferr("ERROR: Can't find the new parent fid of the newrelpath: %d\n",
@@ -905,7 +928,7 @@ static int v9fs_vfs_stat(FAR struct inode *mountpt, FAR 
const char *relpath,
   client = mountpt->i_private;
   memset(buf, 0, sizeof(struct stat));
 
-  ret = v9fs_client_walk(client, relpath, NULL);
+  ret = v9fs_client_walk(client, relpath, NULL, false);
   if (ret < 0)
     {
       ferr("ERROR: Can't find the fid of the relpath: %d\n", ret);
@@ -938,7 +961,7 @@ static int v9fs_vfs_chstat(FAR struct inode *mountpt,
 
   client = mountpt->i_private;
 
-  ret = v9fs_client_walk(client, relpath, NULL);
+  ret = v9fs_client_walk(client, relpath, NULL, false);
   if (ret < 0)
     {
       ferr("ERROR: Can't find the fid of the relpath %d\n", ret);
@@ -1001,3 +1024,171 @@ static int v9fs_vfs_bind(FAR struct inode *driver, FAR 
const void *data,
   *handle = client;
   return ret;
 }
+
+#ifdef CONFIG_FS_LINKS
+/****************************************************************************
+ * Name: v9fs_vfs_link
+ ****************************************************************************/
+
+static int v9fs_vfs_link(FAR struct inode *mountpt,
+                         FAR const char *relpath1,
+                         FAR const char *relpath2)
+{
+  FAR struct v9fs_client_s *client;
+  uint32_t dfid;
+  uint32_t fid;
+  int ret;
+
+  /* Sanity checks */
+
+  DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL);
+
+  /* Recover out private data from the struct inode instance */
+
+  client = mountpt->i_private;
+
+  /* Get the fid of the target file */
+
+  ret = v9fs_client_walk(client, relpath1, NULL, false);
+  if (ret < 0)
+    {
+      ferr("ERROR: Can't find the target file: %d\n", ret);
+      return ret;
+    }
+
+  fid = ret;
+
+  /* Get the parent directory fid for the new link */
+
+  ret = v9fs_client_walk(client, relpath2, &relpath2, false);
+  if (ret < 0)
+    {
+      ferr("ERROR: Can't find parent directory: %d\n", ret);
+      v9fs_fid_put(client, fid);
+      return ret;
+    }
+
+  dfid = ret;
+
+  /* Create the hard link */
+
+  ret = v9fs_client_link(client, dfid, fid, relpath2);
+  v9fs_fid_put(client, dfid);
+  v9fs_fid_put(client, fid);
+  return ret;
+}
+
+/****************************************************************************
+ * Name: v9fs_vfs_symlink
+ ****************************************************************************/
+
+static int v9fs_vfs_symlink(FAR struct inode *mountpt,
+                            FAR const char *path1,
+                            FAR const char *relpath2)
+{
+  FAR struct v9fs_client_s *client;
+  uint32_t dfid;
+  int ret;
+
+  /* Sanity checks */
+
+  DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL);
+
+  /* Recover out private data from the struct inode instance */
+
+  client = mountpt->i_private;
+
+  /* Get the parent directory fid for the new symlink */
+
+  ret = v9fs_client_walk(client, relpath2, &relpath2, false);
+  if (ret < 0)
+    {
+      ferr("ERROR: Can't find parent directory: %d\n", ret);
+      return ret;
+    }
+
+  dfid = ret;
+
+  /* Create the symbolic link */
+
+  ret = v9fs_client_symlink(client, dfid, relpath2, path1, 0777);
+  v9fs_fid_put(client, dfid);
+  return ret;
+}
+
+/****************************************************************************
+ * Name: v9fs_vfs_readlink
+ ****************************************************************************/
+
+static ssize_t v9fs_vfs_readlink(FAR struct inode *mountpt,
+                                 FAR const char *relpath,
+                                 FAR char *buffer, size_t buflen)
+{
+  FAR struct v9fs_client_s *client;
+  uint32_t fid;
+  ssize_t ret;
+
+  /* Sanity checks */
+
+  DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL);
+
+  /* Recover out private data from the struct inode instance */
+
+  client = mountpt->i_private;
+
+  /* Get the fid of the symbolic link */
+
+  ret = v9fs_client_walk(client, relpath, NULL, true);
+  if (ret < 0)
+    {
+      ferr("ERROR: Can't find the symbolic link: %zd\n", ret);
+      return ret;
+    }
+
+  fid = ret;
+
+  /* Read the symbolic link target */
+
+  ret = v9fs_client_readlink(client, fid, buffer, buflen);
+  v9fs_fid_put(client, fid);
+  return ret;
+}
+
+/****************************************************************************
+ * Name: v9fs_vfs_lstat
+ ****************************************************************************/
+
+static int v9fs_vfs_lstat(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 inode instance */
+
+  client = mountpt->i_private;
+
+  /* Get the fid of the file (don't follow symlinks) */
+
+  ret = v9fs_client_walk(client, relpath, NULL, true);
+  if (ret < 0)
+    {
+      ferr("ERROR: Can't find the file: %d\n", ret);
+      return ret;
+    }
+
+  fid = ret;
+
+  /* Get file stats without following symlinks */
+
+  ret = v9fs_client_stat(client, fid, buf);
+  v9fs_fid_put(client, fid);
+  return ret;
+}
+#endif /* CONFIG_FS_LINKS */

Reply via email to