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:
@@ -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 = {
|
||||
|
||||
Reference in New Issue
Block a user