diff --git a/fs/fat/os_adapt/fatfs.c b/fs/fat/os_adapt/fatfs.c index e5d9df2d..f60fc1b6 100644 --- a/fs/fat/os_adapt/fatfs.c +++ b/fs/fat/os_adapt/fatfs.c @@ -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 = {