From ddaf328d0b5790966eed19ebfe3189303404c233 Mon Sep 17 00:00:00 2001 From: chenjing Date: Fri, 4 Jun 2021 10:25:53 +0800 Subject: [PATCH] feat: support link/symlink/readlink MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增link/symlink/readlink接口的系统调用及内核实现,当前仅支持jffs2文件系统。具体接口说明如下: 一、hard link 接口原型: int link(const char *oldpath, const char *newpath); int linkat(int olddirfd, const char *oldpath, int newdirfd, const char *newpath, int flags); 作用: 创建oldpath的硬链接,名为newpath。 功能说明: 1、newpath与oldpath必须在同一挂载分区内。 2、若newpath已存在,不会覆盖,错误码EEXIST。 3、oldpath必须为普通文件或者软链接文件。 4、如果oldpath是一个软链接文件,那么: 若调用link接口或者linkat(flags=0),创建出软链接文件的硬链接; 若调用linkat(flags = AT_SYMLINK_FOLLOW),创建出软链接所指向源文件的硬链接。 5、oldpath与newpath对应同一个文件,对oldpath与newpath任一名字的操作都是直接操作文件,没有“原始文件”的说法。 6、使用cp命令拷贝一个硬链接文件,生成文件的拷贝,新文件的nlink数为1。 7、删除oldpath或newpath,底层文件仍存在,可以通过另一个path访问。只有当两个path都删除之后,才会真正将文件删除,空间释放。 二、symbol link 接口原型: int symlink(const char *target, const char *linkpath); int symlinkat(const char *target, int newdirfd, const char *linkpath); 作用: 创建一个软链接文件linkpath,存储字符串target。 功能说明: 1、target可以为任意字符串(长度小于PATH_MAX)。 2、若linkpath文件名已存在,不会覆盖,错误码EEXIST。 3、用readlink函数可读取软链接的target内容。 4、软链接文件本身大小为target长度。 5、ls时软链接文件类型显示为 'l'。 6、symlink最大循环次数为CONFIG_FS_MAX_LNK_CNT(目前为40),超出则返回错误,错误码ELOOP。 7、使用cp命令拷贝一个软链接文件: 若target是一个文件:创建一个源文件的拷贝,类型为普通文件; 若target非文件:拷贝失败。 三、readlink 接口原型: ssize_t readlink(const char *pathname, char *buf, size_t bufsiz); ssize_t readlinkat(int dirfd, const char *pathname, char *buf, size_t bufsiz); 作用: 读取软链接文件存放的的target内容。 功能说明: 1、pathname必须为软链接文件,否则错误码EINVAL。 2、如果bufsiz小于target长度,则截断target。 close #I3Q0OD Change-Id: I4967101f2b45ba3e459a8df1a07f73e133706e10 Signed-off-by: chenjing --- fs/vfs/fs_link.c | 150 +++++++++++++++++++++++++++++++++++++ fs/vfs/fs_open.c | 34 ++------- fs/vfs/fs_readlink.c | 106 ++++++++++++++++++++++++++ fs/vfs/fs_symlink.c | 168 ++++++++++++++++++++++++++++++++++++++++++ include/nuttx/fs/fs.h | 1 + 5 files changed, 433 insertions(+), 26 deletions(-) create mode 100644 fs/vfs/fs_link.c create mode 100644 fs/vfs/fs_readlink.c create mode 100644 fs/vfs/fs_symlink.c diff --git a/fs/vfs/fs_link.c b/fs/vfs/fs_link.c new file mode 100644 index 0000000..59417b0 --- /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 5d6b94e..954370c 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 0000000..d1623aa --- /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 0000000..e1e38c7 --- /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 5dcf6eb..18f6def 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