xref: /third_party/NuttX/fs/vfs/fs_symlink.c (revision beacf11b)
1/*
2 * Copyright (c) 2021-2021 Huawei Device Co., Ltd. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without modification,
5 * are permitted provided that the following conditions are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright notice, this list of
8 *    conditions and the following disclaimer.
9 *
10 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
11 *    of conditions and the following disclaimer in the documentation and/or other materials
12 *    provided with the distribution.
13 *
14 * 3. Neither the name of the copyright holder nor the names of its contributors may be used
15 *    to endorse or promote products derived from this software without specific prior written
16 *    permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
22 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
25 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
27 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
28 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include "unistd.h"
32#include "errno.h"
33#include "vnode.h"
34#include "path_cache.h"
35
36int follow_symlink(int dirfd, const char *path, struct Vnode **vnode, char **fullpath)
37{
38  int ret;
39  struct Vnode *newvnode = NULL;
40  char *pathname = (char *)path;
41  char buffer[PATH_MAX] = {0};
42
43  if (path == NULL) {
44      return -EINVAL;
45  }
46
47  for (int i = 0; i < CONFIG_FS_MAX_LNK_CNT; i++)
48    {
49      if (*fullpath)
50        {
51          free(*fullpath);
52          *fullpath = NULL;
53        }
54
55      ret = vfs_normalize_pathat(dirfd, pathname, fullpath);
56      if (ret < 0)
57        {
58          return ret;
59        }
60
61      ret = VnodeLookupFullpath(*fullpath, &newvnode, 0);
62      if (ret != OK)
63        {
64          /* The object of fullpath is not exist. Return its parent's vnode. */
65          *vnode = newvnode;
66          return ret;
67        }
68      if (newvnode->type != VNODE_TYPE_LNK)
69        {
70          /* The object of fullpath is exist, and is not a symbol link. Return its vnode. */
71          *vnode = newvnode;
72          return ret;
73        }
74      if (newvnode->vop->Readlink == NULL)
75        {
76          ret = -ENOSYS;
77          return ret;
78        }
79
80      /* The object of fullpath is a symbol link. Read its target and find the source file successively. */
81      pathname = buffer;
82      ret = newvnode->vop->Readlink(newvnode, pathname, PATH_MAX);
83      if (ret < 0)
84        {
85          return ret;
86        }
87    }
88
89  /* Failed to find the source file in CONFIG_FS_MAX_LNK_CNT times. */
90  return -ELOOP;
91}
92
93int do_symlink(const char *target, int newfd, const char *path)
94{
95  struct Vnode *parent_vnode = NULL;
96  struct Vnode *new_vnode = NULL;
97  char *fullpath = NULL;
98  char *newname = NULL;
99  struct Mount *mount = NULL;
100  int ret;
101
102  if (!path)
103    {
104      ret = -EFAULT;
105      goto errout;
106    }
107
108  if (*path == '\0')
109    {
110      ret = -EINVAL;
111      goto errout;
112    }
113
114  if (strlen(target) >= PATH_MAX)
115    {
116      ret = -ENAMETOOLONG;
117      goto errout;
118    }
119
120  ret = vfs_normalize_pathat(newfd, path, &fullpath);
121  if (ret < 0)
122    {
123      goto errout;
124    }
125
126  newname = strrchr(fullpath, '/') + 1;
127
128  VnodeHold();
129  ret = VnodeLookup(fullpath, &parent_vnode, 0);
130  if (ret == 0)
131    {
132      ret = -EEXIST;
133      goto errout_with_vnode;
134    }
135
136  if (!parent_vnode->vop || !parent_vnode->vop->Symlink)
137    {
138      ret = -ENOSYS;
139      goto errout_with_vnode;
140    }
141  mount = parent_vnode->originMount;
142  if ((mount != NULL) && (mount->mountFlags & MS_RDONLY))
143    {
144      ret = -EROFS;
145      goto errout_with_vnode;
146    }
147
148  parent_vnode->useCount++;
149  ret = parent_vnode->vop->Symlink(parent_vnode, &new_vnode, (const char *)newname, (const char *)target);
150  parent_vnode->useCount--;
151  if (ret < 0)
152    {
153      goto errout_with_vnode;
154    }
155
156  PathCacheAlloc(parent_vnode, new_vnode, newname, strlen(newname));
157  VnodeDrop();
158
159  free(fullpath);
160
161  return OK;
162
163errout_with_vnode:
164  VnodeDrop();
165  free(fullpath);
166errout:
167  set_errno(-ret);
168  return VFS_ERROR;
169}
170
171int symlink(const char *target, const char *path)
172{
173  return do_symlink(target, AT_FDCWD, path);
174}
175
176int symlinkat(const char *target, int newdirfd, const char *path)
177{
178  return do_symlink(target, newdirfd, path);
179}
180