feat: support link/symlink/readlink

新增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: I3864d6069b627b705a369e8e32dc1eb922dc0157
Signed-off-by: chenjing <chenjing139@huawei.com>
This commit is contained in:
chenjing
2021-06-04 10:30:12 +08:00
parent 6dee4ae603
commit 6eddc869d3
23 changed files with 1861 additions and 59 deletions

View File

@@ -74,6 +74,9 @@ static void Jffs2SetVtype(struct jffs2_inode *node, struct Vnode *pVnode)
case S_IFDIR:
pVnode->type = VNODE_TYPE_DIR;
break;
case S_IFLNK:
pVnode->type = VNODE_TYPE_LNK;
break;
default:
pVnode->type = VNODE_TYPE_UNKNOWN;
break;
@@ -130,7 +133,6 @@ int VfsJffs2Bind(struct Mount *mnt, struct Vnode *blkDriver, const void *data)
LOS_MuxUnlock(&g_jffs2FsLock);
goto ERROR_WITH_VNODE;
}
rootNode->i_vnode = pv;
pv->type = VNODE_TYPE_DIR;
pv->data = (void *)rootNode;
pv->originMount = mnt;
@@ -197,18 +199,15 @@ int VfsJffs2Lookup(struct Vnode *parentVnode, const char *path, int len, struct
return -ENOENT;
}
if (node->i_vnode) {
*ppVnode = node->i_vnode;
(void)VfsHashGet(parentVnode->originMount, node->i_ino, &newVnode, NULL, NULL);
LOS_MuxUnlock(&g_jffs2FsLock);
if (newVnode) {
if (newVnode->data == NULL) {
LOS_Panic("#####VfsHashGet error#####\n");
}
newVnode->parent = parentVnode;
*ppVnode = newVnode;
return 0;
}
(void)VfsHashGet(parentVnode->originMount, node->i_ino, &newVnode, NULL, NULL);
LOS_MuxUnlock(&g_jffs2FsLock);
if (newVnode) {
if (newVnode->data == NULL) {
LOS_Panic("#####VfsHashGet error#####\n");
}
newVnode->parent = parentVnode;
*ppVnode = newVnode;
return 0;
}
ret = VnodeAlloc(&g_jffs2Vops, &newVnode);
if (ret != 0) {
@@ -219,11 +218,6 @@ int VfsJffs2Lookup(struct Vnode *parentVnode, const char *path, int len, struct
}
Jffs2SetVtype(node, newVnode);
node->i_vnode = newVnode;
if (&g_jffs2Vops != parentVnode->vop) {
LOS_Panic("jffs2 vop failed");
}
newVnode->vop = parentVnode->vop;
newVnode->fop = parentVnode->fop;
newVnode->data = node;
newVnode->parent = parentVnode;
@@ -260,11 +254,6 @@ int VfsJffs2Create(struct Vnode *parentVnode, const char *path, int mode, struct
}
newVnode->type = VNODE_TYPE_REG;
newNode->i_vnode = newVnode;
newVnode->vop = parentVnode->vop;
if (&g_jffs2Vops != parentVnode->vop) {
LOS_Panic("jffs2 vop failed");
}
newVnode->fop = parentVnode->fop;
newVnode->data = newNode;
newVnode->parent = parentVnode;
@@ -527,17 +516,12 @@ int VfsJffs2Mkdir(struct Vnode *parentNode, const char *dirName, mode_t mode, st
ret = jffs2_mkdir((struct jffs2_inode *)parentNode->data, (const unsigned char *)dirName, mode, &node);
if (ret != 0) {
VnodeFree(newVnode);
LOS_MuxUnlock(&g_jffs2FsLock);
VnodeFree(newVnode);
return ret;
}
newVnode->type = VNODE_TYPE_DIR;
node->i_vnode = newVnode;
newVnode->vop = parentNode->vop;
if (&g_jffs2Vops != parentNode->vop) {
LOS_Panic("jffs2 vop failed");
}
newVnode->fop = parentNode->fop;
newVnode->data = node;
newVnode->parent = parentNode;
@@ -606,6 +590,8 @@ int VfsJffs2Chattr(struct Vnode *pVnode, struct IATTR *attr)
int VfsJffs2Rmdir(struct Vnode *parentVnode, struct Vnode *targetVnode, const char *path)
{
int ret;
struct jffs2_inode *parentInode = (struct jffs2_inode *)parentVnode->data;
struct jffs2_inode *targetInode = (struct jffs2_inode *)targetVnode->data;
if (!parentVnode || !targetVnode) {
return -EINVAL;
@@ -613,16 +599,125 @@ int VfsJffs2Rmdir(struct Vnode *parentVnode, struct Vnode *targetVnode, const ch
LOS_MuxLock(&g_jffs2FsLock, (uint32_t)JFFS2_WAITING_FOREVER);
ret = jffs2_rmdir((struct jffs2_inode *)parentVnode->data, (struct jffs2_inode *)targetVnode->data,
(const unsigned char *)path);
ret = jffs2_rmdir(parentInode, targetInode, (const unsigned char *)path);
if (ret == 0) {
(void)jffs2_iput(targetInode);
}
LOS_MuxUnlock(&g_jffs2FsLock);
return ret;
}
int VfsJffs2Link(struct Vnode *oldVnode, struct Vnode *newParentVnode, struct Vnode **newVnode, const char *newName)
{
int ret;
struct jffs2_inode *oldInode = oldVnode->data;
struct jffs2_inode *newParentInode = newParentVnode->data;
struct Vnode *pVnode = NULL;
ret = VnodeAlloc(&g_jffs2Vops, &pVnode);
if (ret != 0) {
return -ENOMEM;
}
LOS_MuxLock(&g_jffs2FsLock, (uint32_t)JFFS2_WAITING_FOREVER);
ret = jffs2_link(oldInode, newParentInode, (const unsigned char *)newName);
if (ret != 0) {
LOS_MuxUnlock(&g_jffs2FsLock);
VnodeFree(pVnode);
return ret;
}
pVnode->type = VNODE_TYPE_REG;
pVnode->fop = &g_jffs2Fops;
pVnode->parent = newParentVnode;
pVnode->originMount = newParentVnode->originMount;
pVnode->data = oldInode;
pVnode->uid = oldVnode->uid;
pVnode->gid = oldVnode->gid;
pVnode->mode = oldVnode->mode;
*newVnode = pVnode;
(void)VfsHashInsert(*newVnode, oldInode->i_ino);
LOS_MuxUnlock(&g_jffs2FsLock);
return ret;
}
int VfsJffs2Symlink(struct Vnode *parentVnode, struct Vnode **newVnode, const char *path, const char *target)
{
int ret;
struct jffs2_inode *inode = NULL;
struct Vnode *pVnode = NULL;
ret = VnodeAlloc(&g_jffs2Vops, &pVnode);
if (ret != 0) {
return -ENOMEM;
}
LOS_MuxLock(&g_jffs2FsLock, (uint32_t)JFFS2_WAITING_FOREVER);
ret = jffs2_symlink((struct jffs2_inode *)parentVnode->data, &inode, (const unsigned char *)path, target);
if (ret != 0) {
LOS_MuxUnlock(&g_jffs2FsLock);
VnodeFree(pVnode);
return ret;
}
pVnode->type = VNODE_TYPE_LNK;
pVnode->fop = &g_jffs2Fops;
pVnode->parent = parentVnode;
pVnode->originMount = parentVnode->originMount;
pVnode->data = inode;
pVnode->uid = inode->i_uid;
pVnode->gid = inode->i_gid;
pVnode->mode = inode->i_mode;
*newVnode = pVnode;
(void)VfsHashInsert(*newVnode, inode->i_ino);
LOS_MuxUnlock(&g_jffs2FsLock);
return ret;
}
ssize_t VfsJffs2Readlink(struct Vnode *vnode, char *buffer, size_t bufLen)
{
ssize_t ret = 0;
struct jffs2_inode *inode = NULL;
struct jffs2_inode_info *f = NULL;
ssize_t targetLen;
ssize_t cnt;
LOS_MuxLock(&g_jffs2FsLock, (uint32_t)JFFS2_WAITING_FOREVER);
inode = (struct jffs2_inode *)vnode->data;
f = JFFS2_INODE_INFO(inode);
targetLen = strlen((const char *)f->target);
if (bufLen == 0) {
LOS_MuxUnlock(&g_jffs2FsLock);
return 0;
}
cnt = (bufLen - 1) < targetLen ? (bufLen - 1) : targetLen;
if (LOS_CopyFromKernel(buffer, bufLen, (const char *)f->target, cnt) != 0) {
cnt = 0;
ret = -EFAULT;
}
buffer[cnt] = '\0';
LOS_MuxUnlock(&g_jffs2FsLock);
if (ret < 0) {
return ret;
}
return cnt;
}
int VfsJffs2Unlink(struct Vnode *parentVnode, struct Vnode *targetVnode, const char *path)
{
int ret;
struct jffs2_inode *parentInode = (struct jffs2_inode *)parentVnode->data;
struct jffs2_inode *targetInode = (struct jffs2_inode *)targetVnode->data;
if (!parentVnode || !targetVnode) {
PRINTK("%s-%d parentVnode=%x, targetVnode=%x\n", __FUNCTION__, __LINE__, parentVnode, targetVnode);
@@ -631,8 +726,11 @@ int VfsJffs2Unlink(struct Vnode *parentVnode, struct Vnode *targetVnode, const c
LOS_MuxLock(&g_jffs2FsLock, (uint32_t)JFFS2_WAITING_FOREVER);
ret = jffs2_unlink((struct jffs2_inode *)parentVnode->data, (struct jffs2_inode *)targetVnode->data,
(const unsigned char *)path);
ret = jffs2_unlink(parentInode, targetInode, (const unsigned char *)path);
if (ret == 0) {
(void)jffs2_iput(targetInode);
}
LOS_MuxUnlock(&g_jffs2FsLock);
return ret;
@@ -664,8 +762,6 @@ int VfsJffs2Rename(struct Vnode *fromVnode, struct Vnode *toParentVnode, const c
fromNode = (struct jffs2_inode *)fromVnode->data;
ret = jffs2_rename((struct jffs2_inode *)fromParentVnode->data, fromNode,
(const unsigned char *)fromName, (struct jffs2_inode *)toParentVnode->data, (const unsigned char *)toName);
/* Careful with this: we can safely free the fromVnode AND toVnode but not fromNode, so reset the i_vnode field OR
it will be a jungle field. With a new lookup process, we'll allocate a new vnode for it. */
fromVnode->parent = toParentVnode;
LOS_MuxUnlock(&g_jffs2FsLock);
@@ -686,6 +782,7 @@ int VfsJffs2Stat(struct Vnode *pVnode, struct stat *buf)
switch (node->i_mode & S_IFMT) {
case S_IFREG:
case S_IFDIR:
case S_IFLNK:
buf->st_mode = node->i_mode;
break;
@@ -713,22 +810,7 @@ int VfsJffs2Stat(struct Vnode *pVnode, struct stat *buf)
int VfsJffs2Reclaim(struct Vnode *pVnode)
{
int ret;
struct jffs2_inode *node = NULL;
LOS_MuxLock(&g_jffs2FsLock, (uint32_t)JFFS2_WAITING_FOREVER);
node = pVnode->data;
if (node == NULL) {
return LOS_OK;
}
node->i_vnode = NULL;
ret = jffs2_iput(node);
LOS_MuxUnlock(&g_jffs2FsLock);
return ret;
return 0;
}
int VfsJffs2Statfs(struct Mount *mnt, struct statfs *buf)
@@ -797,6 +879,9 @@ struct VnodeOps g_jffs2Vops = {
.Reclaim = VfsJffs2Reclaim,
.Truncate = VfsJffs2Truncate,
.Truncate64 = VfsJffs2Truncate64,
.Link = VfsJffs2Link,
.Symlink = VfsJffs2Symlink,
.Readlink = VfsJffs2Readlink,
};
struct file_operations_vfs g_jffs2Fops = {