diff --git a/fs/vfs/fs_link.c b/fs/vfs/fs_link.c new file mode 100644 index 0000000000000000000000000000000000000000..59417b06435a18f3485fc029e9447ee5a9740ffb --- /dev/null +++ b/fs/vfs/fs_link.c @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "unistd.h" +#include "errno.h" +#include "fs/vnode.h" + +int do_link(int oldfd, const char *oldpath, int newfd, const char *newpath, int flag) +{ + struct Vnode *new_parent_vnode = NULL; + struct Vnode *new_vnode = NULL; + struct Vnode *old_vnode = NULL; + char *fulloldpath = NULL; + char *fullnewpath = NULL; + char *newname = NULL; + int ret; + + if (!oldpath || !newpath) + { + ret = -EFAULT; + goto errout; + } + + if (*oldpath == '\0' || *newpath == '\0' || flag & ~AT_SYMLINK_FOLLOW) + { + ret = -EINVAL; + goto errout; + } + + ret = vfs_normalize_pathat(newfd, newpath, &fullnewpath); + if (ret < 0) + { + goto errout; + } + + if (!(flag & AT_SYMLINK_FOLLOW)) + { + ret = vfs_normalize_pathat(oldfd, oldpath, &fulloldpath); + if (ret < 0) + { + goto errout_with_newpath; + } + } + + newname = strrchr(fullnewpath, '/') + 1; + + VnodeHold(); + + if (flag & AT_SYMLINK_FOLLOW) + { + ret = follow_symlink(oldfd, oldpath, &old_vnode, &fulloldpath); + if (ret < 0) + { + goto errout_with_vnode; + } + } + else + { + ret = VnodeLookup(fulloldpath, &old_vnode, 0); + if (ret < 0) + { + goto errout_with_vnode; + } + } + + if (old_vnode->type != VNODE_TYPE_REG && old_vnode->type != VNODE_TYPE_LNK) + { + ret = -EPERM; + goto errout_with_vnode; + } + + ret = VnodeLookup(fullnewpath, &new_parent_vnode, 0); + if (ret == OK) + { + ret = -EEXIST; + goto errout_with_vnode; + } + + if (old_vnode->originMount != new_parent_vnode->originMount) + { + ret = -EXDEV; + goto errout_with_vnode; + } + + if (!old_vnode->vop || !old_vnode->vop->Link) + { + ret = -ENOSYS; + goto errout_with_vnode; + } + new_parent_vnode->useCount++; + ret = old_vnode->vop->Link(old_vnode, new_parent_vnode, &new_vnode, newname); + new_parent_vnode->useCount--; + if (ret < 0) + { + goto errout_with_vnode; + } + PathCacheAlloc(new_parent_vnode, new_vnode, newname, strlen(newname)); + VnodeDrop(); + + free(fulloldpath); + free(fullnewpath); + + return OK; + +errout_with_vnode: + VnodeDrop(); + free(fulloldpath); +errout_with_newpath: + free(fullnewpath); +errout: + set_errno(-ret); + return VFS_ERROR; +} + +int link(const char *oldpath, const char *newpath) +{ + return do_link(AT_FDCWD, oldpath, AT_FDCWD, newpath, 0); +} + +int linkat(int olddirfd, const char *oldpath, int newdirfd, const char *newpath, int flags) +{ + return do_link(olddirfd, oldpath, newdirfd, newpath, flags); +} \ No newline at end of file diff --git a/fs/vfs/fs_open.c b/fs/vfs/fs_open.c index 5d6b94e61e926c4f8b552473c6582d8d20b3a817..954370c56ea6d84d6eaf3422c97ca8050fb2394d 100755 --- a/fs/vfs/fs_open.c +++ b/fs/vfs/fs_open.c @@ -158,7 +158,7 @@ static int do_creat(struct Vnode *parentNode, char *fullpath, mode_t mode, struc return OK; } -int fp_open(char *fullpath, int oflags, mode_t mode) +int fp_open(int dirfd, const char *path, int oflags, mode_t mode) { int ret; int fd; @@ -166,9 +166,10 @@ int fp_open(char *fullpath, int oflags, mode_t mode) struct file *filep = NULL; struct Vnode *vnode = NULL; struct Vnode *parentVnode = NULL; + char *fullpath = NULL; VnodeHold(); - ret = VnodeLookup(fullpath, &vnode, 0); + ret = follow_symlink(dirfd, path, &vnode, &fullpath); if (ret == OK) { /* if file exist */ @@ -305,6 +306,10 @@ errout_with_count: vnode->useCount--; VnodeDrop(); errout: + if (fullpath) + { + free(fullpath); + } set_errno(-ret); return VFS_ERROR; } @@ -313,25 +318,6 @@ int do_open(int dirfd, const char *path, int oflags, mode_t mode) { int ret; int fd; - char *fullpath = NULL; - char *relativepath = NULL; - - /* Get relative path by dirfd*/ - ret = get_path_from_fd(dirfd, &relativepath); - if (ret < 0) - { - goto errout; - } - - ret = vfs_normalize_path((const char *)relativepath, path, &fullpath); - if (relativepath) - { - free(relativepath); - } - if (ret < 0) - { - goto errout; - } if ((oflags & (O_WRONLY | O_CREAT)) != 0) { @@ -339,7 +325,7 @@ int do_open(int dirfd, const char *path, int oflags, mode_t mode) mode &= (S_IRWXU | S_IRWXG | S_IRWXO); } - fd = fp_open(fullpath, oflags, mode); + fd = fp_open(dirfd, path, oflags, mode); if (fd < 0) { ret = -get_errno(); @@ -349,10 +335,6 @@ int do_open(int dirfd, const char *path, int oflags, mode_t mode) return fd; errout: - if (fullpath) - { - free(fullpath); - } set_errno(-ret); return VFS_ERROR; } diff --git a/fs/vfs/fs_readlink.c b/fs/vfs/fs_readlink.c new file mode 100644 index 0000000000000000000000000000000000000000..d1623aa2036b3f956383b0162a8158e0f2ab4b44 --- /dev/null +++ b/fs/vfs/fs_readlink.c @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "unistd.h" +#include "errno.h" +#include "fs/vnode.h" + +ssize_t do_readlink(int dirfd, const char *path, char *buf, size_t bufsize) +{ + struct Vnode *vnode = NULL; + char *fullpath = NULL; + ssize_t ret; + + if (!path) + { + ret = -EFAULT; + goto errout; + } + + if (*path == '\0') + { + ret = -EINVAL; + goto errout; + } + + ret = vfs_normalize_pathat(dirfd, path, &fullpath); + if (ret < 0) + { + goto errout; + } + + VnodeHold(); + ret = VnodeLookup(fullpath, &vnode, 0); + if (ret < 0) + { + goto errout_with_vnode; + } + + if (vnode->type != VNODE_TYPE_LNK) + { + ret = -EINVAL; + goto errout_with_vnode; + } + + if (!vnode->vop || !vnode->vop->Readlink) + { + ret = -ENOSYS; + goto errout_with_vnode; + } + + ret = vnode->vop->Readlink(vnode, buf, bufsize); + if (ret < 0) + { + goto errout_with_vnode; + } + + VnodeDrop(); + + free(fullpath); + + return ret; + +errout_with_vnode: + VnodeDrop(); + free(fullpath); +errout: + set_errno(-ret); + return VFS_ERROR; +} + +ssize_t readlink(const char *pathname, char *buf, size_t bufsize) +{ + return do_readlink(AT_FDCWD, pathname, buf, bufsize); +} + +ssize_t readlinkat(int dirfd, const char *pathname, char *buf, size_t bufsize) +{ + return do_readlink(dirfd, pathname, buf, bufsize); +} diff --git a/fs/vfs/fs_symlink.c b/fs/vfs/fs_symlink.c new file mode 100644 index 0000000000000000000000000000000000000000..e1e38c76aa6d54b23043cc2a0272411ee3887ea9 --- /dev/null +++ b/fs/vfs/fs_symlink.c @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2021-2021 Huawei Device Co., Ltd. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "unistd.h" +#include "errno.h" +#include "fs/vnode.h" + +int follow_symlink(int dirfd, const char *path, struct Vnode **vnode, char **fullpath) +{ + int ret; + struct Vnode *newvnode = NULL; + char pathname[PATH_MAX] = {0}; + + (void)strcpy_s(pathname, PATH_MAX, path); + + for (int i = 0; i < CONFIG_FS_MAX_LNK_CNT; i++) + { + if (*fullpath) + { + free(*fullpath); + *fullpath = NULL; + } + + ret = vfs_normalize_pathat(dirfd, pathname, fullpath); + if (ret < 0) + { + return ret; + } + + ret = VnodeLookup(*fullpath, &newvnode, 0); + if (ret != OK) + { + /* The object of fullpath is not exist. Return its parent's vnode. */ + *vnode = newvnode; + return ret; + } + if (newvnode->type != VNODE_TYPE_LNK) + { + /* The object of fullpath is exist, and is not a symbol link. Return its vnode. */ + *vnode = newvnode; + return ret; + } + if (newvnode->vop->Readlink == NULL) + { + ret = -ENOSYS; + return ret; + } + + /* The object of fullpath is a symbol link. Read its target and find the source file successively. */ + (void)memset_s(pathname, PATH_MAX, 0, PATH_MAX); + ret = newvnode->vop->Readlink(newvnode, pathname, PATH_MAX); + if (ret < 0) + { + return ret; + } + } + + /* Failed to find the source file in CONFIG_FS_MAX_LNK_CNT times. */ + return -ELOOP; +} + +int do_symlink(const char *target, int newfd, const char *path) +{ + struct Vnode *parent_vnode = NULL; + struct Vnode *new_vnode = NULL; + char *fullpath = NULL; + char *newname = NULL; + int ret; + + if (!path) + { + ret = -EFAULT; + goto errout; + } + + if (*path == '\0') + { + ret = -EINVAL; + goto errout; + } + + if (strlen(target) >= PATH_MAX) + { + ret = -ENAMETOOLONG; + goto errout; + } + + ret = vfs_normalize_pathat(newfd, path, &fullpath); + if (ret < 0) + { + goto errout; + } + + newname = strrchr(fullpath, '/') + 1; + + VnodeHold(); + ret = VnodeLookup(fullpath, &parent_vnode, 0); + if (ret == 0) + { + ret = -EEXIST; + goto errout_with_vnode; + } + + if (!parent_vnode->vop || !parent_vnode->vop->Symlink) + { + ret = -ENOSYS; + goto errout_with_vnode; + } + + parent_vnode->useCount++; + ret = parent_vnode->vop->Symlink(parent_vnode, &new_vnode, (const char *)newname, (const char *)target); + parent_vnode->useCount--; + if (ret < 0) + { + goto errout_with_vnode; + } + + PathCacheAlloc(parent_vnode, new_vnode, newname, strlen(newname)); + VnodeDrop(); + + free(fullpath); + + return OK; + +errout_with_vnode: + VnodeDrop(); + free(fullpath); +errout: + set_errno(-ret); + return VFS_ERROR; +} + +int symlink(const char *target, const char *path) +{ + return do_symlink(target, AT_FDCWD, path); +} + +int symlinkat(const char *target, int newdirfd, const char *path) +{ + return do_symlink(target, newdirfd, path); +} diff --git a/include/nuttx/fs/fs.h b/include/nuttx/fs/fs.h index 5dcf6eb3ec6bf4341260bbb160e7a4f0546da0fe..18f6def33bb3b7df40e3c0b1ae16d29b85f23955 100644 --- a/include/nuttx/fs/fs.h +++ b/include/nuttx/fs/fs.h @@ -1110,6 +1110,7 @@ void weak_function files_initialize(void); int vfs_normalize_path(const char *directory, const char *filename, char **pathname); int vfs_normalize_pathat(int fd, const char *filename, char **pathname); +int follow_symlink(int dirfd, const char *path, struct Vnode **vnode, char **fullpath); #ifdef __cplusplus #if __cplusplus