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