From 7e7777a1ec6b18759028a220d04732fe9de6fd46 Mon Sep 17 00:00:00 2001 From: chenjing Date: Fri, 4 Jun 2021 10:27:40 +0800 Subject: [PATCH] feat: support jffs2 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: I621884b0ec773eb86a7ed3b340d6134cfeb9d971 Signed-off-by: chenjing --- fs/jffs2/dir.c | 161 ++++++++++++++++++++++++++++++++++++++++++ fs/jffs2/jffs2_fs_i.h | 1 - fs/jffs2/os-linux.h | 1 + 3 files changed, 162 insertions(+), 1 deletion(-) diff --git a/fs/jffs2/dir.c b/fs/jffs2/dir.c index 80069e3..812f969 100644 --- a/fs/jffs2/dir.c +++ b/fs/jffs2/dir.c @@ -104,6 +104,167 @@ int jffs2_link(struct jffs2_inode *old_d_inode, struct jffs2_inode *dir_i, const return ret; } +int jffs2_symlink(struct jffs2_inode *dir_i, struct jffs2_inode **d_inode, const unsigned char *d_name, const char *target) +{ + struct jffs2_inode_info *f, *dir_f; + struct jffs2_sb_info *c; + struct jffs2_inode *inode; + struct jffs2_raw_inode *ri; + struct jffs2_raw_dirent *rd; + struct jffs2_full_dnode *fn; + struct jffs2_full_dirent *fd; + int namelen; + uint32_t alloclen; + int ret, targetlen = strlen(target); + + /* FIXME: If you care. We'd need to use frags for the target + if it grows much more than this */ + if (targetlen > 254) + return -ENAMETOOLONG; + + ri = jffs2_alloc_raw_inode(); + + if (!ri) + return -ENOMEM; + + c = JFFS2_SB_INFO(dir_i->i_sb); + + /* Try to reserve enough space for both node and dirent. + * Just the node will do for now, though + */ + namelen = strlen((char *)d_name); + ret = jffs2_reserve_space(c, sizeof(*ri) + targetlen, &alloclen, + ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE); + + if (ret) { + jffs2_free_raw_inode(ri); + return ret; + } + + inode = jffs2_new_inode(dir_i, S_IFLNK | S_IRWXUGO, ri); + + if (IS_ERR(inode)) { + jffs2_free_raw_inode(ri); + jffs2_complete_reservation(c); + return PTR_ERR(inode); + } + + f = JFFS2_INODE_INFO(inode); + + inode->i_size = targetlen; + ri->isize = ri->dsize = ri->csize = cpu_to_je32(inode->i_size); + ri->totlen = cpu_to_je32(sizeof(*ri) + inode->i_size); + ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4)); + + ri->compr = JFFS2_COMPR_NONE; + ri->data_crc = cpu_to_je32(crc32(0, target, targetlen)); + ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8)); + + fn = jffs2_write_dnode(c, f, ri, (const unsigned char *)target, targetlen, ALLOC_NORMAL); + + jffs2_free_raw_inode(ri); + + if (IS_ERR(fn)) { + /* Eeek. Wave bye bye */ + mutex_unlock(&f->sem); + jffs2_complete_reservation(c); + ret = PTR_ERR(fn); + goto fail; + } + + /* We use f->target field to store the target path. */ + + f->target = (unsigned char *)malloc(targetlen + 1); + if (!f->target) { + pr_warn("Can't allocate %d bytes of memory\n", targetlen + 1); + mutex_unlock(&f->sem); + jffs2_complete_reservation(c); + ret = -ENOMEM; + goto fail; + } + + ret = LOS_CopyToKernel((char *)f->target, targetlen + 1, target, targetlen + 1); + if (ret != EOK) { + (void)free(f->target); + f->target = NULL; + mutex_unlock(&f->sem); + jffs2_complete_reservation(c); + goto fail; + } + + jffs2_dbg(1, "%s(): symlink's target '%s' cached\n", + __func__, (char *)f->target); + + /* No data here. Only a metadata node, which will be + obsoleted by the first data write + */ + f->metadata = fn; + mutex_unlock(&f->sem); + + jffs2_complete_reservation(c); + + ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &alloclen, + ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen)); + if (ret) + goto fail; + + rd = jffs2_alloc_raw_dirent(); + if (!rd) { + /* Argh. Now we treat it like a normal delete */ + jffs2_complete_reservation(c); + ret = -ENOMEM; + goto fail; + } + + dir_f = JFFS2_INODE_INFO(dir_i); + mutex_lock(&dir_f->sem); + + rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); + rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT); + rd->totlen = cpu_to_je32(sizeof(*rd) + namelen); + rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)); + + rd->pino = cpu_to_je32(dir_i->i_ino); + rd->version = cpu_to_je32(++dir_f->highest_version); + rd->ino = cpu_to_je32(inode->i_ino); + rd->mctime = cpu_to_je32(Jffs2CurSec()); + rd->nsize = namelen; + rd->type = DT_LNK; + rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8)); + rd->name_crc = cpu_to_je32(crc32(0, (const char *)d_name, namelen)); + + fd = jffs2_write_dirent(c, dir_f, rd, (const unsigned char *)d_name, namelen, ALLOC_NORMAL); + + if (IS_ERR(fd)) { + /* dirent failed to write. Delete the inode normally + as if it were the final unlink() */ + jffs2_complete_reservation(c); + jffs2_free_raw_dirent(rd); + mutex_unlock(&dir_f->sem); + ret = PTR_ERR(fd); + goto fail; + } + + dir_i->i_mtime = dir_i->i_ctime = je32_to_cpu(rd->mctime); + + jffs2_free_raw_dirent(rd); + + /* Link the fd into the inode's list, obsoleting an old + one if necessary. */ + jffs2_add_fd_to_list(c, fd, &dir_f->dents); + + mutex_unlock(&dir_f->sem); + jffs2_complete_reservation(c); + + *d_inode = inode; + return 0; + + fail: + inode->i_nlink = 0; + jffs2_iput(inode); + return ret; +} + int jffs2_mkdir(struct jffs2_inode *dir_i, const unsigned char *d_name, int mode, struct jffs2_inode **new_i) { struct jffs2_inode_info *f, *dir_f; diff --git a/fs/jffs2/jffs2_fs_i.h b/fs/jffs2/jffs2_fs_i.h index d574ed0..77681a2 100644 --- a/fs/jffs2/jffs2_fs_i.h +++ b/fs/jffs2/jffs2_fs_i.h @@ -68,7 +68,6 @@ struct jffs2_inode { time_t i_atime; time_t i_mtime; time_t i_ctime; - struct Vnode *i_vnode; off_t i_size; struct super_block *i_sb; LOS_DL_LIST i_hashlist; diff --git a/fs/jffs2/os-linux.h b/fs/jffs2/os-linux.h index 261cf17..b45f83b 100644 --- a/fs/jffs2/os-linux.h +++ b/fs/jffs2/os-linux.h @@ -150,6 +150,7 @@ struct jffs2_inode *jffs2_lookup(struct jffs2_inode *dir_i, const unsigned char int jffs2_create(struct jffs2_inode *dir_i, const unsigned char *d_name, int mode, struct jffs2_inode **new_i); int jffs2_mkdir (struct jffs2_inode *dir_i, const unsigned char *d_name, int mode, struct jffs2_inode **new_i); int jffs2_link (struct jffs2_inode *old_d_inode, struct jffs2_inode *dir_i, const unsigned char *d_name); +int jffs2_symlink(struct jffs2_inode *dir_i, struct jffs2_inode **d_inode, const unsigned char *d_name, const char *target); int jffs2_unlink(struct jffs2_inode *dir_i, struct jffs2_inode *d_inode, const unsigned char *d_name); int jffs2_rmdir (struct jffs2_inode *dir_i, struct jffs2_inode *d_inode, const unsigned char *d_name); int jffs2_rename (struct jffs2_inode *old_dir_i, struct jffs2_inode *d_inode, const unsigned char *old_d_name, -- Gitee