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[PATH_MAX] = {0};
41
42 (void)strcpy_s(pathname, PATH_MAX, path);
43
44 for (int i = 0; i < CONFIG_FS_MAX_LNK_CNT; i++)
45 {
46 if (*fullpath)
47 {
48 free(*fullpath);
49 *fullpath = NULL;
50 }
51
52 ret = vfs_normalize_pathat(dirfd, pathname, fullpath);
53 if (ret < 0)
54 {
55 return ret;
56 }
57
58 ret = VnodeLookup(*fullpath, &newvnode, 0);
59 if (ret != OK)
60 {
61 /* The object of fullpath is not exist. Return its parent's vnode. */
62 *vnode = newvnode;
63 return ret;
64 }
65 if (newvnode->type != VNODE_TYPE_LNK)
66 {
67 /* The object of fullpath is exist, and is not a symbol link. Return its vnode. */
68 *vnode = newvnode;
69 return ret;
70 }
71 if (newvnode->vop->Readlink == NULL)
72 {
73 ret = -ENOSYS;
74 return ret;
75 }
76
77 /* The object of fullpath is a symbol link. Read its target and find the source file successively. */
78 (void)memset_s(pathname, PATH_MAX, 0, PATH_MAX);
79 ret = newvnode->vop->Readlink(newvnode, pathname, PATH_MAX);
80 if (ret < 0)
81 {
82 return ret;
83 }
84 }
85
86 /* Failed to find the source file in CONFIG_FS_MAX_LNK_CNT times. */
87 return -ELOOP;
88 }
89
do_symlink(const char * target,int newfd,const char * path)90 int do_symlink(const char *target, int newfd, const char *path)
91 {
92 struct Vnode *parent_vnode = NULL;
93 struct Vnode *new_vnode = NULL;
94 char *fullpath = NULL;
95 char *newname = NULL;
96 struct Mount *mount = NULL;
97 int ret;
98
99 if (!path)
100 {
101 ret = -EFAULT;
102 goto errout;
103 }
104
105 if (*path == '\0')
106 {
107 ret = -EINVAL;
108 goto errout;
109 }
110
111 if (strlen(target) >= PATH_MAX)
112 {
113 ret = -ENAMETOOLONG;
114 goto errout;
115 }
116
117 ret = vfs_normalize_pathat(newfd, path, &fullpath);
118 if (ret < 0)
119 {
120 goto errout;
121 }
122
123 newname = strrchr(fullpath, '/') + 1;
124
125 VnodeHold();
126 ret = VnodeLookup(fullpath, &parent_vnode, 0);
127 if (ret == 0)
128 {
129 ret = -EEXIST;
130 goto errout_with_vnode;
131 }
132
133 if (!parent_vnode->vop || !parent_vnode->vop->Symlink)
134 {
135 ret = -ENOSYS;
136 goto errout_with_vnode;
137 }
138 mount = parent_vnode->originMount;
139 if ((mount != NULL) && (mount->mountFlags & MS_RDONLY))
140 {
141 ret = -EROFS;
142 goto errout_with_vnode;
143 }
144
145 parent_vnode->useCount++;
146 ret = parent_vnode->vop->Symlink(parent_vnode, &new_vnode, (const char *)newname, (const char *)target);
147 parent_vnode->useCount--;
148 if (ret < 0)
149 {
150 goto errout_with_vnode;
151 }
152
153 PathCacheAlloc(parent_vnode, new_vnode, newname, strlen(newname));
154 VnodeDrop();
155
156 free(fullpath);
157
158 return OK;
159
160 errout_with_vnode:
161 VnodeDrop();
162 free(fullpath);
163 errout:
164 set_errno(-ret);
165 return VFS_ERROR;
166 }
167
symlink(const char * target,const char * path)168 int symlink(const char *target, const char *path)
169 {
170 return do_symlink(target, AT_FDCWD, path);
171 }
172
symlinkat(const char * target,int newdirfd,const char * path)173 int symlinkat(const char *target, int newdirfd, const char *path)
174 {
175 return do_symlink(target, newdirfd, path);
176 }
177