feat: fatfs支持符号链接功能

1 将fatfs_creat与fatfs_mkdir抽象成fatfs_creat_obj,通过参数type控制创建的对象类型;
2 新增fatfs_symlink与fatfs_readlink接口,其中:
   1) fatfs_symlink通过fatfs_creat_obj创建出符号链接文件,并为其申请新簇,将target内容写入簇的第一个sector;
   2) fatfs_readlink读出符号链接文件对应簇中第一个sector的内容

close #I3V8D1

Signed-off-by: chenjing <chenjing139@huawei.com>
Change-Id: I38dfaa28af156399817530923534d5b73b4c64af
This commit is contained in:
chenjing 2021-06-10 18:09:46 +08:00
parent 6eddc869d3
commit e50cf0be6f
1 changed files with 275 additions and 257 deletions

View File

@ -231,12 +231,232 @@ static mode_t fatfs_get_mode(BYTE attribute, mode_t fs_mode)
fs_mode &= ~mask;
if (attribute & AM_DIR) {
fs_mode |= S_IFDIR;
} else if (attribute & AM_LNK) {
fs_mode |= S_IFLNK;
} else {
fs_mode |= S_IFREG;
}
return fs_mode;
}
static enum VnodeType fatfstype_2_vnodetype(BYTE type) {
switch (type) {
case AM_ARC:
return VNODE_TYPE_REG;
case AM_DIR:
return VNODE_TYPE_DIR;
case AM_LNK:
return VNODE_TYPE_LNK;
default:
return VNODE_TYPE_UNKNOWN;
}
}
static FRESULT init_cluster(DIR *dp_new, FATFS *fs, int type, const char *target, DWORD *clust)
{
FRESULT result;
BYTE *dir = NULL;
QWORD sect;
UINT n;
/* Allocate a new cluster */
*clust = create_chain(&(dp_new->obj), 0);
if (*clust == 0) {
return FR_NO_SPACE_LEFT;
}
if (*clust == 1 || *clust == DISK_ERROR) {
return FR_DISK_ERR;
}
result = sync_window(fs); /* Flush FAT */
if (result != FR_OK) {
remove_chain(&(dp_new->obj), *clust, 0);
return result;
}
/* Initialize the new cluster */
#ifndef LOSCFG_FS_FAT_VIRTUAL_PARTITION
dir = fs->win;
#else
dir = PARENTFS(fs)->win;
#endif
sect = clst2sect(fs, *clust);
mem_set(dir, 0, SS(fs));
if (type == AM_LNK && target) {
/* Write target to symlink */
strcpy_s((char *)dir, SS(fs), target);
}
for (n = fs->csize; n > 0; n--) {
#ifndef LOSCFG_FS_FAT_VIRTUAL_PARTITION
fs->winsect = sect++;
fs->wflag = 1;
#else
PARENTFS(fs)->winsect = sect++;
PARENTFS(fs)->wflag = 1;
result = sync_window(fs);
if (result != FR_OK) {
remove_chain(&(dp_new->obj), *clust, 0);
return result;
}
#endif
if (type == AM_LNK) {
/* No need to clean the rest sectors of the cluster for symlink */
break;
}
}
return FR_OK;
}
static int fatfs_create_obj(struct Vnode *parent, const char *name, int mode, struct Vnode **vpp, BYTE type, const char *target)
{
struct Vnode *vp = NULL;
FATFS *fs = (FATFS *)parent->originMount->data;
DIR_FILE *dfp = (DIR_FILE *)parent->data;
FILINFO *finfo = &(dfp->fno);
DIR_FILE *dfp_new = NULL;
FILINFO *finfo_new = NULL;
DIR *dp_new = NULL;
QWORD time;
DWORD hash;
DWORD clust = 0;
FRESULT result;
int ret;
if ((type != AM_ARC) && (type != AM_DIR) && (type != AM_LNK)) {
result = FR_INVALID_NAME;
goto ERROR_EXIT;
}
dfp_new = (DIR_FILE *)zalloc(sizeof(DIR_FILE));
if (dfp_new == NULL) {
result = FR_NOT_ENOUGH_CORE;
goto ERROR_EXIT;
}
ret = lock_fs(fs);
if (ret == FALSE) { /* lock failed */
result = FR_TIMEOUT;
goto ERROR_FREE;
}
if (finfo->fattrib & AM_ARC || finfo->fattrib & AM_LNK) {
result = FR_NO_DIR;
goto ERROR_UNLOCK;
}
finfo_new = &(dfp_new->fno);
LOS_ListInit(&finfo_new->fp_list);
dp_new = &(dfp_new->f_dir);
dp_new->obj.fs = fs;
dp_new->obj.sclust = finfo->sclst;
DEF_NAMBUF;
INIT_NAMBUF(fs);
result = create_name(dp_new, &name);
if (result != FR_OK) {
goto ERROR_UNLOCK;
}
result = dir_find(dp_new);
if (result == FR_OK) {
result = FR_EXIST;
goto ERROR_UNLOCK;
}
if (type == AM_DIR || type == AM_LNK) {
result = init_cluster(dp_new, fs, type, target, &clust);
if (result != FR_OK) {
goto ERROR_UNLOCK;
}
}
result = dir_register(dp_new);
if (result != FR_OK) {
goto ERROR_REMOVE_CHAIN;
}
/* Set the directory entry attribute */
if (time_status == SYSTEM_TIME_ENABLE) {
time = GET_FATTIME();
} else {
time = 0;
}
st_dword(dp_new->dir + DIR_CrtTime, time);
st_dword(dp_new->dir + DIR_ModTime, time);
st_word(dp_new->dir + DIR_LstAccDate, time >> FTIME_DATE_OFFSET);
dp_new->dir[DIR_Attr] = type;
if (((DWORD)mode & S_IWUSR) == 0) {
dp_new->dir[DIR_Attr] |= AM_RDO;
}
st_clust(fs, dp_new->dir, clust);
if (type == AM_ARC) {
st_dword(dp_new->dir + DIR_FileSize, 0);
} else if (type == AM_LNK) {
st_dword(dp_new->dir + DIR_FileSize, strlen(target));
}
#ifdef LOSCFG_FS_FAT_VIRTUAL_PARTITION
PARENTFS(fs)->wflag = 1;
#else
fs->wflag = 1;
#endif
result = sync_fs(fs);
if (result != FR_OK) {
goto ERROR_REMOVE_CHAIN;
}
result = dir_read(dp_new, 0);
if (result != FR_OK) {
goto ERROR_REMOVE_CHAIN;
}
dp_new->blk_ofs = dir_ofs(dp_new);
get_fileinfo(dp_new, finfo_new);
if (type == AM_ARC) {
dp_new->obj.objsize = 0;
} else if (type == AM_LNK) {
dp_new->obj.objsize = strlen(target);
}
ret = VnodeAlloc(&fatfs_vops, &vp);
if (ret != 0) {
result = FR_NOT_ENOUGH_CORE;
goto ERROR_REMOVE_CHAIN;
}
vp->parent = parent;
vp->fop = &fatfs_fops;
vp->data = dfp_new;
vp->originMount = parent->originMount;
vp->uid = fs->fs_uid;
vp->gid = fs->fs_gid;
vp->mode = fatfs_get_mode(finfo_new->fattrib, fs->fs_mode);
vp->type = fatfstype_2_vnodetype(type);
hash = fatfs_hash(dp_new->sect, dp_new->dptr, finfo_new->sclst);
ret = VfsHashInsert(vp, hash);
if (ret != 0) {
result = FR_NOT_ENOUGH_CORE;
goto ERROR_REMOVE_CHAIN;
}
*vpp = vp;
unlock_fs(fs, FR_OK);
FREE_NAMBUF();
return fatfs_sync(parent->originMount->mountFlags, fs);
ERROR_REMOVE_CHAIN:
remove_chain(&(dp_new->obj), clust, 0);
ERROR_UNLOCK:
unlock_fs(fs, result);
FREE_NAMBUF();
ERROR_FREE:
free(dfp_new);
ERROR_EXIT:
return -fatfs_2_vfs(result);
}
int fatfs_lookup(struct Vnode *parent, const char *path, int len, struct Vnode **vpp)
{
struct Vnode *vp = NULL;
@ -336,119 +556,7 @@ ERROR_EXIT:
int fatfs_create(struct Vnode *parent, const char *name, int mode, struct Vnode **vpp)
{
struct Vnode *vp = NULL;
FATFS *fs = (FATFS *)parent->originMount->data;
DIR_FILE *dfp;
DIR *dp = NULL;
FILINFO *finfo = NULL;
QWORD time;
DWORD hash;
FRESULT result;
int ret;
dfp = (DIR_FILE *)zalloc(sizeof(DIR_FILE));
if (dfp == NULL) {
ret = ENOMEM;
goto ERROR_EXIT;
}
ret = lock_fs(fs);
if (ret == FALSE) { /* lock failed */
ret = EBUSY;
goto ERROR_FREE;
}
finfo = &(dfp->fno);
LOS_ListInit(&finfo->fp_list);
dp = &(dfp->f_dir);
dp->obj.fs = fs;
dp->obj.sclust = ((DIR_FILE *)(parent->data))->fno.sclst;
DEF_NAMBUF;
INIT_NAMBUF(fs);
result = create_name(dp, &name);
if (result != FR_OK) {
ret = fatfs_2_vfs(result);
goto ERROR_UNLOCK;
}
result = dir_find(dp);
if (result == FR_OK) {
ret = EEXIST;
goto ERROR_UNLOCK;
}
result = dir_register(dp);
if (result != FR_OK) {
ret = fatfs_2_vfs(result);
goto ERROR_UNLOCK;
}
/* Set the directory entry attribute */
if (time_status == SYSTEM_TIME_ENABLE) {
time = GET_FATTIME();
} else {
time = 0;
}
st_dword(dp->dir + DIR_CrtTime, time);
st_dword(dp->dir + DIR_ModTime, time);
st_word(dp->dir + DIR_LstAccDate, time >> FTIME_DATE_OFFSET);
dp->dir[DIR_Attr] = AM_ARC;
if (((DWORD)mode & S_IWUSR) == 0) {
dp->dir[DIR_Attr] |= AM_RDO;
}
st_clust(fs, dp->dir, 0);
st_dword(dp->dir + DIR_FileSize, 0);
#ifdef LOSCFG_FS_FAT_VIRTUAL_PARTITION
PARENTFS(fs)->wflag = 1;
#else
fs->wflag = 1;
#endif
result = sync_fs(fs);
if (result != FR_OK) {
ret = fatfs_2_vfs(result);
goto ERROR_UNLOCK;
}
result = dir_read(dp, 0);
if (result != FR_OK) {
ret = fatfs_2_vfs(result);
goto ERROR_UNLOCK;
}
dp->blk_ofs = dir_ofs(dp);
get_fileinfo(dp, finfo);
dp->obj.objsize = 0;
ret = VnodeAlloc(&fatfs_vops, &vp);
if (ret != 0) {
ret = ENOMEM;
goto ERROR_UNLOCK;
}
vp->parent = parent;
vp->fop = &fatfs_fops;
vp->data = dfp;
vp->originMount = parent->originMount;
vp->uid = fs->fs_uid;
vp->gid = fs->fs_gid;
vp->mode = fatfs_get_mode(finfo->fattrib, fs->fs_mode);
vp->type = VNODE_TYPE_REG;
hash = fatfs_hash(dp->sect, dp->dptr, finfo->sclst);
ret = VfsHashInsert(vp, hash);
if (ret != 0) {
ret = EINVAL;
goto ERROR_UNLOCK;
}
*vpp = vp;
unlock_fs(fs, result);
FREE_NAMBUF();
return fatfs_sync(parent->originMount->mountFlags, fs);
ERROR_UNLOCK:
unlock_fs(fs, result);
FREE_NAMBUF();
ERROR_FREE:
free(dfp);
ERROR_EXIT:
return -ret;
return fatfs_create_obj(parent, name, mode, vpp, AM_ARC, NULL);
}
int fatfs_open(struct file *filep)
@ -1690,150 +1798,7 @@ int fatfs_mkfs (struct Vnode *device, int sectors, int option)
int fatfs_mkdir(struct Vnode *parent, const char *name, mode_t mode, struct Vnode **vpp)
{
struct Vnode *vp = NULL;
FATFS *fs = (FATFS *)parent->originMount->data;
DIR_FILE *dfp = (DIR_FILE *)parent->data;
FILINFO *finfo = &(dfp->fno);
DIR_FILE *dfp_new = NULL;
QWORD sect;
DWORD clust;
BYTE *dir = NULL;
DWORD hash;
FRESULT result = FR_OK;
int ret;
UINT n;
ret = lock_fs(fs);
if (ret == FALSE) {
result = FR_TIMEOUT;
goto ERROR_OUT;
}
if (finfo->fattrib & AM_ARC) {
result = FR_NO_DIR;
goto ERROR_UNLOCK;
}
DEF_NAMBUF;
INIT_NAMBUF(fs);
dfp_new = (DIR_FILE *)zalloc(sizeof(DIR_FILE));
if (dfp_new == NULL) {
result = FR_NOT_ENOUGH_CORE;
goto ERROR_UNLOCK;
}
LOS_ListInit(&(dfp_new->fno.fp_list));
dfp_new->f_dir.obj.sclust = finfo->sclst;
dfp_new->f_dir.obj.fs = fs;
result = create_name(&(dfp_new->f_dir), &name);
if (result != FR_OK) {
goto ERROR_FREE;
}
result = dir_find(&(dfp_new->f_dir));
if (result == FR_OK) {
result = FR_EXIST;
goto ERROR_FREE;
}
/* Allocate new chain for directory */
clust = create_chain(&(dfp_new->f_dir.obj), 0);
if (clust == 0) {
result = FR_NO_SPACE_LEFT;
goto ERROR_FREE;
}
if (clust == 1 || clust == DISK_ERROR) {
result = FR_DISK_ERR;
goto ERROR_FREE;
}
result = sync_window(fs); /* Flush FAT */
if (result != FR_OK) {
goto ERROR_REMOVE_CHAIN;
}
/* Initialize the new directory */
#ifndef LOSCFG_FS_FAT_VIRTUAL_PARTITION
dir = fs->win;
#else
dir = PARENTFS(fs)->win;
#endif
sect = clst2sect(fs, clust);
mem_set(dir, 0, SS(fs));
for (n = fs->csize; n > 0; n--) { /* Write zero to directory */
#ifndef LOSCFG_FS_FAT_VIRTUAL_PARTITION
fs->winsect = sect++;
fs->wflag = 1;
#else
PARENTFS(fs)->winsect = sect++;
PARENTFS(fs)->wflag = 1;
result = sync_window(fs);
if (result != FR_OK) break;
#endif
}
if (result != FR_OK) {
goto ERROR_REMOVE_CHAIN;
}
result = dir_register(&(dfp_new->f_dir));
if (result != FR_OK) {
goto ERROR_REMOVE_CHAIN;
}
dir = dfp_new->f_dir.dir;
st_dword(dir + DIR_ModTime, 0); /* Set the time */
st_clust(fs, dir, clust); /* Set the start cluster */
dir[DIR_Attr] = AM_DIR; /* Set the attrib */
if ((mode & S_IWUSR) == 0) {
dir[DIR_Attr] |= AM_RDO;
}
#ifndef LOSCFG_FS_FAT_VIRTUAL_PARTITION
fs->wflag = 1;
#else
PARENTFS(fs)->wflag = 1;
#endif
result = sync_fs(fs);
if (result != FR_OK) {
goto ERROR_REMOVE_CHAIN;
}
/* Set the FILINFO struct */
result = dir_read(&(dfp_new->f_dir), 0);
if (result != FR_OK) {
goto ERROR_REMOVE_CHAIN;
}
dfp_new->f_dir.blk_ofs = dir_ofs(&(dfp_new->f_dir));
get_fileinfo(&(dfp_new->f_dir), &(dfp_new->fno));
ret = VnodeAlloc(&fatfs_vops, &vp);
if (ret != 0) {
result = FR_NOT_ENOUGH_CORE;
goto ERROR_REMOVE_CHAIN;
}
vp->parent = parent;
vp->fop = &fatfs_fops;
vp->data = dfp_new;
vp->originMount = parent->originMount;
vp->uid = fs->fs_uid;
vp->gid = fs->fs_gid;
vp->mode = fatfs_get_mode(dfp_new->fno.fattrib, fs->fs_mode);
vp->type = VNODE_TYPE_DIR;
hash = fatfs_hash(dfp_new->f_dir.sect, dfp_new->f_dir.dptr, dfp_new->fno.sclst);
ret = VfsHashInsert(vp, hash);
if (ret != 0) {
result = FR_NOT_ENOUGH_CORE;
goto ERROR_REMOVE_CHAIN;
}
unlock_fs(fs, FR_OK);
FREE_NAMBUF();
*vpp = vp;
return fatfs_sync(vp->originMount->mountFlags, fs);
ERROR_REMOVE_CHAIN:
remove_chain(&(dfp_new->f_dir.obj), clust, 0);
ERROR_FREE:
free(dfp_new);
ERROR_UNLOCK:
unlock_fs(fs, result);
FREE_NAMBUF();
ERROR_OUT:
return -fatfs_2_vfs(result);
return fatfs_create_obj(parent, name, mode, vpp, AM_DIR, NULL);
}
int fatfs_rmdir(struct Vnode *parent, struct Vnode *vp, const char *name)
@ -2064,6 +2029,57 @@ ERROR_WITH_DIR:
return -fatfs_2_vfs(result);
}
int fatfs_symlink(struct Vnode *parentVnode, struct Vnode **newVnode, const char *path, const char *target)
{
return fatfs_create_obj(parentVnode, path, 0, newVnode, AM_LNK, target);
}
ssize_t fatfs_readlink(struct Vnode *vnode, char *buffer, size_t bufLen)
{
int ret;
FRESULT res = FR_OK;
DWORD clust;
QWORD sect;
DIR_FILE *dfp = (DIR_FILE *)(vnode->data);
DIR *dp = &(dfp->f_dir);
FATFS *fs = dp->obj.fs;
FILINFO *finfo = &(dfp->fno);
size_t targetLen = finfo->fsize;
size_t cnt;
ret = lock_fs(fs);
if (ret == FALSE) {
return -EBUSY;
}
clust = finfo->sclst;
sect = clst2sect(fs, clust); /* Get current sector */
if (sect == 0) {
res = FR_DISK_ERR;
goto ERROUT;
}
if (move_window(fs, sect) != FR_OK) {
res = FR_DISK_ERR;
goto ERROUT;
}
cnt = (bufLen - 1) < targetLen ? (bufLen - 1) : targetLen;
ret = LOS_CopyFromKernel(buffer, bufLen, fs->win, cnt);
if (ret != EOK) {
res = FR_INVALID_PARAMETER;
goto ERROUT;
}
buffer[cnt] = '\0';
unlock_fs(fs, FR_OK);
return cnt;
ERROUT:
unlock_fs(fs, res);
return -fatfs_2_vfs(res);
}
struct VnodeOps fatfs_vops = {
/* file ops */
.Getattr = fatfs_stat,
@ -2083,6 +2099,8 @@ struct VnodeOps fatfs_vops = {
.Mkdir = fatfs_mkdir,
.Rmdir = fatfs_rmdir,
.Fscheck = fatfs_fscheck,
.Symlink = fatfs_symlink,
.Readlink = fatfs_readlink,
};
struct MountOps fatfs_mops = {