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
follow_symlink(int dirfd,const char * path,struct Vnode ** vnode,char ** fullpath)36 int 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
do_symlink(const char * target,int newfd,const char * path)93 int 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
163 errout_with_vnode:
164 VnodeDrop();
165 free(fullpath);
166 errout:
167 set_errno(-ret);
168 return VFS_ERROR;
169 }
170
symlink(const char * target,const char * path)171 int symlink(const char *target, const char *path)
172 {
173 return do_symlink(target, AT_FDCWD, path);
174 }
175
symlinkat(const char * target,int newdirfd,const char * path)176 int symlinkat(const char *target, int newdirfd, const char *path)
177 {
178 return do_symlink(target, newdirfd, path);
179 }
180