2453 lines
60 KiB
C
2453 lines
60 KiB
C
/*
|
|
* Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved.
|
|
* Copyright (c) 2020-2021 Huawei Device Co., Ltd. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without modification,
|
|
* are permitted provided that the following conditions are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
|
* conditions and the following disclaimer.
|
|
*
|
|
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
|
* of conditions and the following disclaimer in the documentation and/or other materials
|
|
* provided with the distribution.
|
|
*
|
|
* 3. Neither the name of the copyright holder nor the names of its contributors may be used
|
|
* to endorse or promote products derived from this software without specific prior written
|
|
* permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
|
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
|
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
|
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
|
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "fatfs.h"
|
|
#ifdef LOSCFG_FS_FAT
|
|
#include "ff.h"
|
|
#include "disk_pri.h"
|
|
#include "diskio.h"
|
|
#include "fs/file.h"
|
|
#include "fs/fs.h"
|
|
#include "fs/dirent_fs.h"
|
|
#include "fs/mount.h"
|
|
#include "vnode.h"
|
|
#include "path_cache.h"
|
|
#ifdef LOSCFG_FS_FAT_VIRTUAL_PARTITION
|
|
#include "virpartff.h"
|
|
#include "errcode_fat.h"
|
|
#endif
|
|
#include "los_tables.h"
|
|
#include "user_copy.h"
|
|
#include "los_vm_filemap.h"
|
|
#include "los_hash.h"
|
|
#include "los_vm_common.h"
|
|
#include <time.h>
|
|
#include <errno.h>
|
|
#include <dirent.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <fcntl.h>
|
|
|
|
|
|
struct VnodeOps fatfs_vops; /* forward define */
|
|
struct file_operations_vfs fatfs_fops;
|
|
|
|
#define BITMASK4 0x0F
|
|
#define BITMASK5 0x1F
|
|
#define BITMASK6 0x3F
|
|
#define BITMASK7 0x7F
|
|
#define FTIME_MIN_OFFSET 6 /* minute offset in word */
|
|
#define FTIME_HR_OFFSET 11 /* hour offset in word */
|
|
#define FTIME_MTH_OFFSET 5 /* month offset in word */
|
|
#define FTIME_YEAR_OFFSET 9 /* year offset in word */
|
|
#define FTIME_DATE_OFFSET 16 /* date offset in dword */
|
|
#define SEC_MULTIPLIER 2
|
|
#define YEAR_OFFSET 80 /* Year start from 1980 in FATFS, while start from 1900 in struct tm */
|
|
|
|
int fatfs_2_vfs(int result)
|
|
{
|
|
int status = ENOERR;
|
|
|
|
#ifdef LOSCFG_FS_FAT_VIRTUAL_PARTITION
|
|
if (result < 0 || result >= VIRERR_BASE) {
|
|
return result;
|
|
}
|
|
#else
|
|
if (result < 0) {
|
|
return result;
|
|
}
|
|
#endif
|
|
|
|
/* FatFs errno to Libc errno */
|
|
switch (result) {
|
|
case FR_OK:
|
|
break;
|
|
|
|
case FR_NO_FILE:
|
|
case FR_NO_PATH:
|
|
status = ENOENT;
|
|
break;
|
|
|
|
case FR_NO_FILESYSTEM:
|
|
status = ENOTSUP;
|
|
break;
|
|
|
|
case FR_INVALID_NAME:
|
|
status = EINVAL;
|
|
break;
|
|
|
|
case FR_EXIST:
|
|
case FR_INVALID_OBJECT:
|
|
status = EEXIST;
|
|
break;
|
|
|
|
case FR_DISK_ERR:
|
|
case FR_NOT_READY:
|
|
case FR_INT_ERR:
|
|
status = EIO;
|
|
break;
|
|
|
|
case FR_WRITE_PROTECTED:
|
|
status = EROFS;
|
|
break;
|
|
case FR_MKFS_ABORTED:
|
|
case FR_INVALID_PARAMETER:
|
|
status = EINVAL;
|
|
break;
|
|
|
|
case FR_NO_SPACE_LEFT:
|
|
status = ENOSPC;
|
|
break;
|
|
case FR_NO_DIRENTRY:
|
|
status = ENFILE;
|
|
break;
|
|
case FR_NO_EMPTY_DIR:
|
|
status = ENOTEMPTY;
|
|
break;
|
|
case FR_IS_DIR:
|
|
status = EISDIR;
|
|
break;
|
|
case FR_NO_DIR:
|
|
status = ENOTDIR;
|
|
break;
|
|
case FR_NO_EPERM:
|
|
case FR_DENIED:
|
|
status = EPERM;
|
|
break;
|
|
case FR_LOCKED:
|
|
case FR_TIMEOUT:
|
|
status = EBUSY;
|
|
break;
|
|
#ifdef LOSCFG_FS_FAT_VIRTUAL_PARTITION
|
|
case FR_MODIFIED:
|
|
status = -VIRERR_MODIFIED;
|
|
break;
|
|
case FR_CHAIN_ERR:
|
|
status = -VIRERR_CHAIN_ERR;
|
|
break;
|
|
case FR_OCCUPIED:
|
|
status = -VIRERR_OCCUPIED;
|
|
break;
|
|
case FR_NOTCLEAR:
|
|
status = -VIRERR_NOTCLEAR;
|
|
break;
|
|
case FR_NOTFIT:
|
|
status = -VIRERR_NOTFIT;
|
|
break;
|
|
case FR_INVAILD_FATFS:
|
|
status = -VIRERR_INTER_ERR;
|
|
break;
|
|
#endif
|
|
default:
|
|
status = -FAT_ERROR;
|
|
break;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
static bool fatfs_is_last_cluster(FATFS *fs, DWORD cclust)
|
|
{
|
|
switch (fs->fs_type) {
|
|
case FS_FAT12:
|
|
return (cclust == FAT12_END_OF_CLUSTER);
|
|
case FS_FAT16:
|
|
return (cclust == FAT16_END_OF_CLUSTER);
|
|
case FS_FAT32:
|
|
default:
|
|
return (cclust == FAT32_END_OF_CLUSTER);
|
|
}
|
|
}
|
|
|
|
static int fatfs_sync(unsigned long mountflags, FATFS *fs)
|
|
{
|
|
#ifdef LOSCFG_FS_FAT_CACHE
|
|
los_part *part = NULL;
|
|
if (!(mountflags & (MS_NOSYNC | MS_RDONLY))) {
|
|
part = get_part((INT)fs->pdrv);
|
|
if (part == NULL) {
|
|
return -ENODEV;
|
|
}
|
|
|
|
(void)OsSdSync(part->disk_id);
|
|
}
|
|
#endif
|
|
return 0;
|
|
}
|
|
int fatfs_hash_cmp(struct Vnode *vp, void *arg)
|
|
{
|
|
DIR_FILE *dfp_target = (DIR_FILE *)arg;
|
|
DIR_FILE *dfp = (DIR_FILE *)vp->data;
|
|
|
|
return dfp_target->f_dir.sect != dfp->f_dir.sect ||
|
|
dfp_target->f_dir.dptr != dfp->f_dir.dptr ||
|
|
dfp_target->fno.sclst != dfp->fno.sclst;
|
|
}
|
|
|
|
static DWORD fatfs_hash(QWORD sect, DWORD dptr, DWORD sclst)
|
|
{
|
|
DWORD hash = FNV1_32A_INIT;
|
|
hash = LOS_HashFNV32aBuf(§, sizeof(QWORD), hash);
|
|
hash = LOS_HashFNV32aBuf(&dptr, sizeof(DWORD), hash);
|
|
hash = LOS_HashFNV32aBuf(&sclst, sizeof(DWORD), hash);
|
|
|
|
return hash;
|
|
}
|
|
|
|
static mode_t fatfs_get_mode(BYTE attribute, mode_t fs_mode)
|
|
{
|
|
mode_t mask = 0;
|
|
if (attribute & AM_RDO) {
|
|
mask = S_IWUSR | S_IWGRP | S_IWOTH;
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
|
|
#define DIR_SIZE 32
|
|
static FRESULT init_cluster(DIR_FILE *pdfp, DIR *dp_new, FATFS *fs, int type, const char *target, DWORD *clust)
|
|
{
|
|
FRESULT result;
|
|
BYTE *dir = NULL;
|
|
QWORD sect;
|
|
DWORD pclust;
|
|
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 */
|
|
(void)strcpy_s((char *)dir, SS(fs), target);
|
|
} else {
|
|
/* Write the dir cluster */
|
|
mem_set(dir, 0, SS(fs));
|
|
/* 11, the The max length of of the short file name, Create "." entry */
|
|
mem_set(dir + DIR_Name, ' ', 11);
|
|
dir[DIR_Name] = '.';
|
|
dir[DIR_Attr] = AM_DIR;
|
|
st_clust(fs, dir, *clust);
|
|
mem_cpy(dir + DIR_SIZE, dir, DIR_SIZE); /* Create ".." entry */
|
|
dir[DIR_SIZE + 1] = '.'; /* Add extra "." */
|
|
pclust = pdfp->fno.sclst;
|
|
if (fs->fs_type == FS_FAT32 && pclust == fs->dirbase) {
|
|
pclust = 0;
|
|
}
|
|
st_clust(fs, dir + DIR_SIZE, pclust);
|
|
}
|
|
|
|
#ifndef LOSCFG_FS_FAT_VIRTUAL_PARTITION
|
|
fs->winsect = sect++;
|
|
fs->wflag = 1;
|
|
#else
|
|
PARENTFS(fs)->winsect = sect++;
|
|
PARENTFS(fs)->wflag = 1;
|
|
#endif
|
|
result = sync_window(fs);
|
|
if (result != FR_OK) {
|
|
remove_chain(&(dp_new->obj), *clust, 0);
|
|
return result;
|
|
}
|
|
|
|
/* Rest of directory cluster should set to be zero */
|
|
if (type == AM_DIR) {
|
|
mem_set(dir, 0, SS(fs));
|
|
for (n = fs->csize - 1; n > 0; n--) {
|
|
#ifndef LOSCFG_FS_FAT_VIRTUAL_PARTITION
|
|
fs->winsect = sect++;
|
|
fs->wflag = 1;
|
|
#else
|
|
PARENTFS(fs)->winsect = sect++;
|
|
PARENTFS(fs)->wflag = 1;
|
|
#endif
|
|
result = sync_window(fs);
|
|
if (result != FR_OK) {
|
|
remove_chain(&(dp_new->obj), *clust, 0);
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
|
|
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(dfp, 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);
|
|
} else {
|
|
finfo_new->fsize = fs->csize * SS(fs);
|
|
}
|
|
|
|
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;
|
|
FATFS *fs = (FATFS *)(parent->originMount->data);
|
|
DIR_FILE *dfp;
|
|
DIR *dp = NULL;
|
|
FILINFO *finfo = NULL;
|
|
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) {
|
|
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, &path);
|
|
if (result != FR_OK) {
|
|
ret = fatfs_2_vfs(result);
|
|
goto ERROR_UNLOCK;
|
|
}
|
|
|
|
result = dir_find(dp);
|
|
if (result != FR_OK) {
|
|
ret = fatfs_2_vfs(result);
|
|
goto ERROR_UNLOCK;
|
|
}
|
|
|
|
if (dp->fn[NSFLAG] & NS_NONAME) {
|
|
result = FR_INVALID_NAME;
|
|
ret = fatfs_2_vfs(result);
|
|
goto ERROR_UNLOCK;
|
|
}
|
|
|
|
get_fileinfo(dp, finfo);
|
|
dp->obj.objsize = 0;
|
|
|
|
hash = fatfs_hash(dp->sect, dp->dptr, finfo->sclst);
|
|
ret = VfsHashGet(parent->originMount, hash, &vp, fatfs_hash_cmp, dfp);
|
|
if (ret != 0) {
|
|
ret = VnodeAlloc(&fatfs_vops, &vp);
|
|
if (ret != 0) {
|
|
ret = ENOMEM;
|
|
result = FR_NOT_ENOUGH_CORE;
|
|
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);
|
|
if (finfo->fattrib & AM_DIR) {
|
|
vp->type = VNODE_TYPE_DIR;
|
|
finfo->fsize = fs->csize * SS(fs);
|
|
} else {
|
|
vp->type = VNODE_TYPE_REG;
|
|
}
|
|
|
|
ret = VfsHashInsert(vp, hash);
|
|
if (ret != 0) {
|
|
result = FR_INVALID_PARAMETER;
|
|
goto ERROR_UNLOCK;
|
|
}
|
|
} else {
|
|
vp->parent = parent;
|
|
free(dfp); /* hash hit dfp is no needed */
|
|
}
|
|
|
|
unlock_fs(fs, FR_OK);
|
|
FREE_NAMBUF();
|
|
*vpp = vp;
|
|
return 0;
|
|
|
|
ERROR_UNLOCK:
|
|
unlock_fs(fs, result);
|
|
FREE_NAMBUF();
|
|
ERROR_FREE:
|
|
free(dfp);
|
|
ERROR_EXIT:
|
|
return -ret;
|
|
}
|
|
|
|
int fatfs_create(struct Vnode *parent, const char *name, int mode, struct Vnode **vpp)
|
|
{
|
|
return fatfs_create_obj(parent, name, mode, vpp, AM_ARC, NULL);
|
|
}
|
|
|
|
int fatfs_open(struct file *filep)
|
|
{
|
|
struct Vnode *vp = filep->f_vnode;
|
|
FATFS *fs = (FATFS *)vp->originMount->data;
|
|
DIR_FILE *dfp = (DIR_FILE *)vp->data;
|
|
DIR *dp = &(dfp->f_dir);
|
|
FILINFO *finfo = &(dfp->fno);
|
|
FIL *fp;
|
|
int ret;
|
|
|
|
fp = (FIL *)zalloc(sizeof(FIL) + SS(fs));
|
|
if (fp == NULL) {
|
|
ret = ENOMEM;
|
|
goto ERROR_EXIT;
|
|
}
|
|
ret = lock_fs(fs);
|
|
if (ret == FALSE) {
|
|
ret = EBUSY;
|
|
goto ERROR_FREE;
|
|
}
|
|
|
|
fp->dir_sect = dp->sect;
|
|
fp->dir_ptr = dp->dir;
|
|
fp->obj.sclust = finfo->sclst;
|
|
fp->obj.objsize = finfo->fsize;
|
|
#if FF_USE_FASTSEEK
|
|
fp->cltbl = 0; /* Disable fast seek mode */
|
|
#endif
|
|
fp->obj.fs = fs;
|
|
fp->obj.id = fs->id;
|
|
fp->flag = FA_READ | FA_WRITE;
|
|
fp->err = 0;
|
|
fp->sect = 0;
|
|
fp->fptr = 0;
|
|
fp->buf = (BYTE *)fp + sizeof(FIL);
|
|
LOS_ListAdd(&finfo->fp_list, &fp->fp_entry);
|
|
unlock_fs(fs, FR_OK);
|
|
|
|
filep->f_priv = fp;
|
|
return 0;
|
|
|
|
ERROR_FREE:
|
|
free(fp);
|
|
ERROR_EXIT:
|
|
return -ret;
|
|
}
|
|
|
|
int fatfs_close(struct file *filep)
|
|
{
|
|
FIL *fp = (FIL *)filep->f_priv;
|
|
FATFS *fs = fp->obj.fs;
|
|
FRESULT result;
|
|
int ret;
|
|
|
|
ret = lock_fs(fs);
|
|
if (ret == FALSE) {
|
|
return -EBUSY;
|
|
}
|
|
#if !FF_FS_READONLY
|
|
result = f_sync(fp); /* Flush cached data */
|
|
if (result != FR_OK) {
|
|
goto EXIT;
|
|
}
|
|
ret = fatfs_sync(filep->f_vnode->originMount->mountFlags, fs);
|
|
if (ret != 0) {
|
|
unlock_fs(fs, FR_OK);
|
|
return ret;
|
|
}
|
|
#endif
|
|
LOS_ListDelete(&fp->fp_entry);
|
|
free(fp);
|
|
filep->f_priv = NULL;
|
|
EXIT:
|
|
unlock_fs(fs, result);
|
|
return -fatfs_2_vfs(result);
|
|
}
|
|
|
|
int fatfs_read(struct file *filep, char *buff, size_t count)
|
|
{
|
|
FIL *fp = (FIL *)filep->f_priv;
|
|
FATFS *fs = fp->obj.fs;
|
|
struct Vnode *vp = filep->f_vnode;
|
|
FILINFO *finfo = &((DIR_FILE *)(vp->data))->fno;
|
|
size_t rcount;
|
|
FRESULT result;
|
|
int ret;
|
|
|
|
ret = lock_fs(fs);
|
|
if (ret == FALSE) {
|
|
return -EBUSY;
|
|
}
|
|
fp->obj.objsize = finfo->fsize;
|
|
fp->obj.sclust = finfo->sclst;
|
|
result = f_read(fp, buff, count, &rcount);
|
|
if (result != FR_OK) {
|
|
goto EXIT;
|
|
}
|
|
filep->f_pos = fp->fptr;
|
|
EXIT:
|
|
unlock_fs(fs, result);
|
|
return rcount;
|
|
}
|
|
|
|
static FRESULT update_dir(DIR *dp, FILINFO *finfo)
|
|
{
|
|
FATFS *fs = dp->obj.fs;
|
|
DWORD tm;
|
|
BYTE *dbuff = NULL;
|
|
FRESULT result;
|
|
|
|
result = move_window(fs, dp->sect);
|
|
if (result != FR_OK) {
|
|
return result;
|
|
}
|
|
dbuff = fs->win + dp->dptr % SS(fs);
|
|
dbuff[DIR_Attr] = finfo->fattrib;
|
|
st_clust(fs, dbuff, finfo->sclst); /* Update start cluster */
|
|
st_dword(dbuff + DIR_FileSize, (DWORD)finfo->fsize); /* Update file size */
|
|
if (time_status == SYSTEM_TIME_ENABLE) {
|
|
tm = GET_FATTIME();
|
|
} else {
|
|
tm = 0;
|
|
}
|
|
st_dword(dbuff + DIR_ModTime, tm); /* Update mtime */
|
|
st_word(dbuff + DIR_LstAccDate, tm >> FTIME_DATE_OFFSET); /* Update access date */
|
|
#ifndef LOSCFG_FS_FAT_VIRTUAL_PARTITION
|
|
fs->wflag = 1;
|
|
#else
|
|
PARENTFS(fs)->wflag = 1;
|
|
#endif
|
|
return sync_fs(fs);
|
|
}
|
|
|
|
off64_t fatfs_lseek64(struct file *filep, off64_t offset, int whence)
|
|
{
|
|
FIL *fp = (FIL *)filep->f_priv;
|
|
FATFS *fs = fp->obj.fs;
|
|
struct Vnode *vp = filep->f_vnode;
|
|
DIR_FILE *dfp = (DIR_FILE *)vp->data;
|
|
FILINFO *finfo = &(dfp->fno);
|
|
struct Mount *mount = vp->originMount;
|
|
FSIZE_t fpos;
|
|
FRESULT result;
|
|
int ret;
|
|
|
|
switch (whence) {
|
|
case SEEK_CUR:
|
|
offset = filep->f_pos + offset;
|
|
if (offset < 0) {
|
|
return -EINVAL;
|
|
}
|
|
fpos = offset;
|
|
break;
|
|
case SEEK_SET:
|
|
if (offset < 0) {
|
|
return -EINVAL;
|
|
}
|
|
fpos = offset;
|
|
break;
|
|
case SEEK_END:
|
|
offset = (off_t)((long long)finfo->fsize + offset);
|
|
if (offset < 0) {
|
|
return -EINVAL;
|
|
}
|
|
fpos = offset;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (offset >= FAT32_MAXSIZE) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = lock_fs(fs);
|
|
if (ret == FALSE) {
|
|
return -EBUSY;
|
|
}
|
|
|
|
if (fpos > finfo->fsize) {
|
|
if ((filep->f_oflags & O_ACCMODE) == O_RDONLY) {
|
|
result = FR_DENIED;
|
|
goto ERROR_EXIT;
|
|
}
|
|
if (mount->mountFlags & MS_RDONLY) {
|
|
result = FR_WRITE_PROTECTED;
|
|
goto ERROR_EXIT;
|
|
}
|
|
}
|
|
fp->obj.sclust = finfo->sclst;
|
|
fp->obj.objsize = finfo->fsize;
|
|
|
|
result = f_lseek(fp, fpos);
|
|
finfo->fsize = fp->obj.objsize;
|
|
finfo->sclst = fp->obj.sclust;
|
|
if (result != FR_OK) {
|
|
goto ERROR_EXIT;
|
|
}
|
|
|
|
result = f_sync(fp);
|
|
if (result != FR_OK) {
|
|
goto ERROR_EXIT;
|
|
}
|
|
filep->f_pos = fpos;
|
|
|
|
unlock_fs(fs, FR_OK);
|
|
return fpos;
|
|
ERROR_EXIT:
|
|
unlock_fs(fs, result);
|
|
return -fatfs_2_vfs(result);
|
|
}
|
|
|
|
off_t fatfs_lseek(struct file *filep, off_t offset, int whence)
|
|
{
|
|
return (off_t)fatfs_lseek64(filep, offset, whence);
|
|
}
|
|
|
|
static int update_filbuff(FILINFO *finfo, FIL *wfp, const char *data)
|
|
{
|
|
LOS_DL_LIST *list = &finfo->fp_list;
|
|
FATFS *fs = wfp->obj.fs;
|
|
FIL *entry = NULL;
|
|
int ret = 0;
|
|
|
|
LOS_DL_LIST_FOR_EACH_ENTRY(entry, list, FIL, fp_entry) {
|
|
if (entry == wfp) {
|
|
continue;
|
|
}
|
|
if (entry->sect != 0) {
|
|
if (disk_read(fs->pdrv, entry->buf, entry->sect, 1) != RES_OK) {
|
|
ret = -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int fatfs_write(struct file *filep, const char *buff, size_t count)
|
|
{
|
|
FIL *fp = (FIL *)filep->f_priv;
|
|
FATFS *fs = fp->obj.fs;
|
|
struct Vnode *vp = filep->f_vnode;
|
|
FILINFO *finfo = &(((DIR_FILE *)vp->data)->fno);
|
|
size_t wcount;
|
|
FRESULT result;
|
|
int ret;
|
|
|
|
ret = lock_fs(fs);
|
|
if (ret == FALSE) {
|
|
return -EBUSY;
|
|
}
|
|
fp->obj.objsize = finfo->fsize;
|
|
fp->obj.sclust = finfo->sclst;
|
|
result = f_write(fp, buff, count, &wcount);
|
|
if (result != FR_OK) {
|
|
goto ERROR_EXIT;
|
|
}
|
|
|
|
finfo->fsize = fp->obj.objsize;
|
|
finfo->sclst = fp->obj.sclust;
|
|
result = f_sync(fp);
|
|
if (result != FR_OK) {
|
|
goto ERROR_EXIT;
|
|
}
|
|
update_filbuff(finfo, fp, buff);
|
|
|
|
filep->f_pos = fp->fptr;
|
|
|
|
unlock_fs(fs, FR_OK);
|
|
return wcount;
|
|
ERROR_EXIT:
|
|
unlock_fs(fs, result);
|
|
return -fatfs_2_vfs(result);
|
|
}
|
|
|
|
int fatfs_fsync(struct file *filep)
|
|
{
|
|
FIL *fp = filep->f_priv;
|
|
FATFS *fs = fp->obj.fs;
|
|
FRESULT result;
|
|
int ret;
|
|
|
|
ret = lock_fs(fs);
|
|
if (ret == FALSE) {
|
|
return -EBUSY;
|
|
}
|
|
|
|
result = f_sync(fp);
|
|
unlock_fs(fs, result);
|
|
return -fatfs_2_vfs(result);
|
|
}
|
|
|
|
int fatfs_fallocate64(struct file *filep, int mode, off64_t offset, off64_t len)
|
|
{
|
|
FIL *fp = (FIL *)filep->f_priv;
|
|
FATFS *fs = fp->obj.fs;
|
|
struct Vnode *vp = filep->f_vnode;
|
|
FILINFO *finfo = &((DIR_FILE *)(vp->data))->fno;
|
|
FRESULT result;
|
|
int ret;
|
|
|
|
if (offset < 0 || len <= 0) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (len >= FAT32_MAXSIZE || offset >= FAT32_MAXSIZE ||
|
|
len + offset >= FAT32_MAXSIZE) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (mode != FALLOC_FL_KEEP_SIZE) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = lock_fs(fs);
|
|
if (ret == FALSE) {
|
|
return -EBUSY;
|
|
}
|
|
result = f_expand(fp, (FSIZE_t)offset, (FSIZE_t)len, 1);
|
|
if (result == FR_OK) {
|
|
if (finfo->sclst == 0) {
|
|
finfo->sclst = fp->obj.sclust;
|
|
}
|
|
result = f_sync(fp);
|
|
}
|
|
unlock_fs(fs, result);
|
|
|
|
return -fatfs_2_vfs(result);
|
|
}
|
|
|
|
static FRESULT realloc_cluster(FILINFO *finfo, FFOBJID *obj, FSIZE_t size)
|
|
{
|
|
FATFS *fs = obj->fs;
|
|
off64_t remain;
|
|
DWORD cclust;
|
|
DWORD pclust;
|
|
QWORD csize;
|
|
FRESULT result;
|
|
|
|
if (size == 0) { /* Remove cluster chain */
|
|
if (finfo->sclst != 0) {
|
|
result = remove_chain(obj, finfo->sclst, 0);
|
|
if (result != FR_OK) {
|
|
return result;
|
|
}
|
|
finfo->sclst = 0;
|
|
}
|
|
return FR_OK;
|
|
}
|
|
|
|
remain = size;
|
|
csize = SS(fs) * fs->csize;
|
|
if (finfo->sclst == 0) { /* Allocate one cluster if file doesn't have any cluster */
|
|
cclust = create_chain(obj, 0);
|
|
if (cclust == 0) {
|
|
return FR_NO_SPACE_LEFT;
|
|
}
|
|
if (cclust == 1 || cclust == DISK_ERROR) {
|
|
return FR_DISK_ERR;
|
|
}
|
|
finfo->sclst = cclust;
|
|
}
|
|
cclust = finfo->sclst;
|
|
while (remain > csize) { /* Follow or strentch the cluster chain */
|
|
pclust = cclust;
|
|
cclust = create_chain(obj, pclust);
|
|
if (cclust == 0) {
|
|
return FR_NO_SPACE_LEFT;
|
|
}
|
|
if (cclust == 1 || cclust == DISK_ERROR) {
|
|
return FR_DISK_ERR;
|
|
}
|
|
remain -= csize;
|
|
}
|
|
pclust = cclust;
|
|
cclust = get_fat(obj, pclust);
|
|
if ((cclust == BAD_CLUSTER) || (cclust == DISK_ERROR)) {
|
|
return FR_DISK_ERR;
|
|
}
|
|
if (!fatfs_is_last_cluster(obj->fs, cclust)) { /* Remove extra cluster if existing */
|
|
result = remove_chain(obj, cclust, pclust);
|
|
if (result != FR_OK) {
|
|
return result;
|
|
}
|
|
}
|
|
|
|
return FR_OK;
|
|
}
|
|
|
|
int fatfs_fallocate(struct file *filep, int mode, off_t offset, off_t len)
|
|
{
|
|
return fatfs_fallocate64(filep, mode, offset, len);
|
|
}
|
|
|
|
int fatfs_truncate64(struct Vnode *vp, off64_t len)
|
|
{
|
|
FATFS *fs = (FATFS *)vp->originMount->data;
|
|
DIR_FILE *dfp = (DIR_FILE *)vp->data;
|
|
DIR *dp = &(dfp->f_dir);
|
|
FILINFO *finfo = &(dfp->fno);
|
|
FFOBJID object;
|
|
FRESULT result = FR_OK;
|
|
int ret;
|
|
|
|
if (len < 0 || len >= FAT32_MAXSIZE) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = lock_fs(fs);
|
|
if (ret == FALSE) {
|
|
result = FR_TIMEOUT;
|
|
goto ERROR_OUT;
|
|
}
|
|
if (len == finfo->fsize) {
|
|
unlock_fs(fs, FR_OK);
|
|
return 0;
|
|
}
|
|
|
|
object.fs = fs;
|
|
result = realloc_cluster(finfo, &object, (FSIZE_t)len);
|
|
if (result != FR_OK) {
|
|
goto ERROR_UNLOCK;
|
|
}
|
|
finfo->fsize = (FSIZE_t)len;
|
|
|
|
result = update_dir(dp, finfo);
|
|
if (result != FR_OK) {
|
|
goto ERROR_UNLOCK;
|
|
}
|
|
unlock_fs(fs, FR_OK);
|
|
return fatfs_sync(vp->originMount->mountFlags, fs);
|
|
ERROR_UNLOCK:
|
|
unlock_fs(fs, result);
|
|
ERROR_OUT:
|
|
return -fatfs_2_vfs(result);
|
|
}
|
|
|
|
int fatfs_truncate(struct Vnode *vp, off_t len)
|
|
{
|
|
return fatfs_truncate64(vp, len);
|
|
}
|
|
|
|
static int fat_bind_check(struct Vnode *blk_driver, los_part **partition)
|
|
{
|
|
los_part *part = NULL;
|
|
|
|
if (blk_driver == NULL || blk_driver->data == NULL) {
|
|
return ENODEV;
|
|
}
|
|
|
|
struct drv_data *dd = blk_driver->data;
|
|
if (dd->ops == NULL) {
|
|
return ENODEV;
|
|
}
|
|
const struct block_operations *bops = dd->ops;
|
|
if (bops->open == NULL) {
|
|
return EINVAL;
|
|
}
|
|
if (bops->open(blk_driver) < 0) {
|
|
return EBUSY;
|
|
}
|
|
|
|
part = los_part_find(blk_driver);
|
|
if (part == NULL) {
|
|
return ENODEV;
|
|
}
|
|
if (part->part_name != NULL) {
|
|
bops->close(blk_driver);
|
|
return EBUSY;
|
|
}
|
|
|
|
#ifndef FF_MULTI_PARTITION
|
|
if (part->part_no_mbr > 1) {
|
|
bops->close(blk_driver);
|
|
return EPERM;
|
|
}
|
|
#endif
|
|
|
|
*partition = part;
|
|
return 0;
|
|
}
|
|
|
|
int fatfs_mount(struct Mount *mnt, struct Vnode *blk_device, const void *data)
|
|
{
|
|
struct Vnode *vp = NULL;
|
|
FATFS *fs = NULL;
|
|
DIR_FILE *dfp = NULL;
|
|
los_part *part = NULL;
|
|
QWORD start_sector;
|
|
BYTE fmt;
|
|
DWORD hash;
|
|
FRESULT result;
|
|
int ret;
|
|
|
|
ret = fat_bind_check(blk_device, &part);
|
|
if (ret != 0) {
|
|
goto ERROR_EXIT;
|
|
}
|
|
|
|
ret = SetDiskPartName(part, "vfat");
|
|
if (ret != 0) {
|
|
ret = EIO;
|
|
goto ERROR_EXIT;
|
|
}
|
|
|
|
fs = (FATFS *)zalloc(sizeof(FATFS));
|
|
if (fs == NULL) {
|
|
ret = ENOMEM;
|
|
goto ERROR_PARTNAME;
|
|
}
|
|
|
|
#ifdef LOSCFG_FS_FAT_VIRTUAL_PARTITION
|
|
fs->vir_flag = FS_PARENT;
|
|
fs->parent_fs = fs;
|
|
fs->vir_amount = DISK_ERROR;
|
|
fs->vir_avail = FS_VIRDISABLE;
|
|
#endif
|
|
|
|
ret = ff_cre_syncobj(0, &fs->sobj);
|
|
if (ret == 0) { /* create sync object failed */
|
|
ret = EINVAL;
|
|
goto ERROR_WITH_FS;
|
|
}
|
|
|
|
ret = lock_fs(fs);
|
|
if (ret == FALSE) {
|
|
ret = EBUSY;
|
|
goto ERROR_WITH_MUX;
|
|
}
|
|
|
|
fs->fs_type = 0;
|
|
fs->pdrv = part->part_id;
|
|
|
|
#if FF_MAX_SS != FF_MIN_SS /* Get sector size (multiple sector size cfg only) */
|
|
if (disk_ioctl(fs->pdrv, GET_SECTOR_SIZE, &(fs->ssize)) != RES_OK) {
|
|
ret = EIO;
|
|
goto ERROR_WITH_LOCK;
|
|
}
|
|
if (fs->ssize > FF_MAX_SS || fs->ssize < FF_MIN_SS || (fs->ssize & (fs->ssize - 1))) {
|
|
ret = EIO;
|
|
goto ERROR_WITH_LOCK;
|
|
}
|
|
#endif
|
|
|
|
fs->win = (BYTE *)ff_memalloc(SS(fs));
|
|
if (fs->win == NULL) {
|
|
ret = ENOMEM;
|
|
goto ERROR_WITH_LOCK;
|
|
}
|
|
|
|
result = find_fat_partition(fs, part, &fmt, &start_sector);
|
|
if (result != FR_OK) {
|
|
ret = fatfs_2_vfs(result);
|
|
goto ERROR_WITH_FSWIN;
|
|
}
|
|
|
|
result = init_fatobj(fs, fmt, start_sector);
|
|
if (result != FR_OK) {
|
|
ret = fatfs_2_vfs(result);
|
|
goto ERROR_WITH_FSWIN;
|
|
}
|
|
|
|
fs->fs_uid = mnt->vnodeBeCovered->uid;
|
|
fs->fs_gid = mnt->vnodeBeCovered->gid;
|
|
fs->fs_dmask = GetUmask();
|
|
fs->fs_fmask = GetUmask();
|
|
fs->fs_mode = mnt->vnodeBeCovered->mode & (S_IRWXU | S_IRWXG | S_IRWXO);
|
|
|
|
dfp = (DIR_FILE *)zalloc(sizeof(DIR_FILE));
|
|
if (dfp == NULL) {
|
|
ret = ENOMEM;
|
|
goto ERROR_WITH_FSWIN;
|
|
}
|
|
|
|
dfp->f_dir.obj.fs = fs;
|
|
dfp->f_dir.obj.sclust = 0; /* set start clust 0, root */
|
|
dfp->f_dir.obj.attr = AM_DIR;
|
|
dfp->f_dir.obj.objsize = 0; /* dir size is 0 */
|
|
dfp->fno.fsize = 0;
|
|
dfp->fno.fdate = 0;
|
|
dfp->fno.ftime = 0;
|
|
dfp->fno.fattrib = AM_DIR;
|
|
dfp->fno.sclst = 0;
|
|
dfp->fno.fsize = fs->csize * SS(fs);
|
|
dfp->fno.fname[0] = '/'; /* Mark as root dir */
|
|
dfp->fno.fname[1] = '\0';
|
|
LOS_ListInit(&(dfp->fno.fp_list));
|
|
|
|
ret = VnodeAlloc(&fatfs_vops, &vp);
|
|
if (ret != 0) {
|
|
ret = ENOMEM;
|
|
goto ERROR_WITH_FSWIN;
|
|
}
|
|
|
|
mnt->data = fs;
|
|
mnt->vnodeCovered = vp;
|
|
|
|
vp->parent = mnt->vnodeBeCovered;
|
|
vp->fop = &fatfs_fops;
|
|
vp->data = dfp;
|
|
vp->originMount = mnt;
|
|
vp->uid = fs->fs_uid;
|
|
vp->gid = fs->fs_gid;
|
|
vp->mode = mnt->vnodeBeCovered->mode;
|
|
vp->type = VNODE_TYPE_DIR;
|
|
|
|
hash = fatfs_hash(0, 0, 0);
|
|
ret = VfsHashInsert(vp, hash);
|
|
if (ret != 0) {
|
|
ret = -ret;
|
|
goto ERROR_WITH_LOCK;
|
|
}
|
|
unlock_fs(fs, FR_OK);
|
|
|
|
return 0;
|
|
|
|
ERROR_WITH_FSWIN:
|
|
ff_memfree(fs->win);
|
|
ERROR_WITH_LOCK:
|
|
unlock_fs(fs, FR_OK);
|
|
ERROR_WITH_MUX:
|
|
ff_del_syncobj(&fs->sobj);
|
|
ERROR_WITH_FS:
|
|
free(fs);
|
|
ERROR_PARTNAME:
|
|
if (part->part_name) {
|
|
free(part->part_name);
|
|
part->part_name = NULL;
|
|
}
|
|
ERROR_EXIT:
|
|
return -ret;
|
|
}
|
|
|
|
int fatfs_umount(struct Mount *mnt, struct Vnode **blkdriver)
|
|
{
|
|
struct Vnode *device;
|
|
FATFS *fs = (FATFS *)mnt->data;
|
|
los_part *part;
|
|
int ret;
|
|
|
|
ret = lock_fs(fs);
|
|
if (ret == FALSE) {
|
|
return -EBUSY;
|
|
}
|
|
|
|
part = get_part(fs->pdrv);
|
|
if (part == NULL) {
|
|
unlock_fs(fs, FR_OK);
|
|
return -ENODEV;
|
|
}
|
|
device = part->dev;
|
|
if (device == NULL) {
|
|
unlock_fs(fs, FR_OK);
|
|
return -ENODEV;
|
|
}
|
|
#ifdef LOSCFG_FS_FAT_CACHE
|
|
ret = OsSdSync(part->disk_id);
|
|
if (ret != 0) {
|
|
unlock_fs(fs, FR_DISK_ERR);
|
|
return -EIO;
|
|
}
|
|
#endif
|
|
if (part->part_name != NULL) {
|
|
free(part->part_name);
|
|
part->part_name = NULL;
|
|
}
|
|
|
|
struct drv_data *dd = device->data;
|
|
if (dd->ops == NULL) {
|
|
unlock_fs(fs, FR_OK);
|
|
return ENODEV;
|
|
}
|
|
|
|
const struct block_operations *bops = dd->ops;
|
|
if (bops != NULL && bops->close != NULL) {
|
|
bops->close(*blkdriver);
|
|
}
|
|
|
|
if (fs->win != NULL) {
|
|
ff_memfree(fs->win);
|
|
}
|
|
|
|
unlock_fs(fs, FR_OK);
|
|
|
|
ret = ff_del_syncobj(&fs->sobj);
|
|
if (ret == FALSE) {
|
|
return -EINVAL;
|
|
}
|
|
free(fs);
|
|
|
|
*blkdriver = device;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int fatfs_sync_adapt(struct Mount *mnt)
|
|
{
|
|
(void)mnt;
|
|
int ret = 0;
|
|
#ifdef LOSCFG_FS_FAT_CACHE
|
|
struct Vnode *dev = NULL;
|
|
los_part *part = NULL;
|
|
|
|
if (mnt == NULL) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
dev = mnt->vnodeDev;
|
|
part = los_part_find(dev);
|
|
if (part == NULL) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = OsSdSync(part->disk_id);
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
int fatfs_statfs(struct Mount *mnt, struct statfs *info)
|
|
{
|
|
FATFS *fs = (FATFS *)mnt->data;
|
|
DWORD nclst = 0;
|
|
FRESULT result = FR_OK;
|
|
int ret;
|
|
|
|
info->f_type = MSDOS_SUPER_MAGIC;
|
|
#if FF_MAX_SS != FF_MIN_SS
|
|
info->f_bsize = fs->ssize * fs->csize;
|
|
#else
|
|
info->f_bsize = FF_MIN_SS * fs->csize;
|
|
#endif
|
|
info->f_blocks = fs->n_fatent;
|
|
ret = lock_fs(fs);
|
|
if (ret == FALSE) {
|
|
return -EBUSY;
|
|
}
|
|
/* free cluster is unavailable, update it */
|
|
if (fs->free_clst == DISK_ERROR) {
|
|
result = fat_count_free_entries(&nclst, fs);
|
|
}
|
|
info->f_bfree = fs->free_clst;
|
|
info->f_bavail = fs->free_clst;
|
|
unlock_fs(fs, result);
|
|
|
|
#if FF_USE_LFN
|
|
/* Maximum length of filenames */
|
|
info->f_namelen = FF_MAX_LFN;
|
|
#else
|
|
/* Maximum length of filenames: 8 is the basename length, 1 is the dot, 3 is the extension length */
|
|
info->f_namelen = (8 + 1 + 3);
|
|
#endif
|
|
info->f_fsid.__val[0] = MSDOS_SUPER_MAGIC;
|
|
info->f_fsid.__val[1] = 1;
|
|
info->f_frsize = SS(fs) * fs->csize;
|
|
info->f_files = 0;
|
|
info->f_ffree = 0;
|
|
info->f_flags = mnt->mountFlags;
|
|
|
|
return -fatfs_2_vfs(result);
|
|
}
|
|
|
|
static inline int GET_SECONDS(WORD ftime)
|
|
{
|
|
return (ftime & BITMASK5) * SEC_MULTIPLIER;
|
|
}
|
|
static inline int GET_MINUTES(WORD ftime)
|
|
{
|
|
return (ftime >> FTIME_MIN_OFFSET) & BITMASK6;
|
|
}
|
|
static inline int GET_HOURS(WORD ftime)
|
|
{
|
|
return (ftime >> FTIME_HR_OFFSET) & BITMASK5;
|
|
}
|
|
static inline int GET_DAY(WORD fdate)
|
|
{
|
|
return fdate & BITMASK5;
|
|
}
|
|
static inline int GET_MONTH(WORD fdate)
|
|
{
|
|
return (fdate >> FTIME_MTH_OFFSET) & BITMASK4;
|
|
}
|
|
static inline int GET_YEAR(WORD fdate)
|
|
{
|
|
return (fdate >> FTIME_YEAR_OFFSET) & BITMASK7;
|
|
}
|
|
|
|
static time_t fattime_transfer(WORD fdate, WORD ftime)
|
|
{
|
|
struct tm time = { 0 };
|
|
time.tm_sec = GET_SECONDS(ftime);
|
|
time.tm_min = GET_MINUTES(ftime);
|
|
time.tm_hour = GET_HOURS(ftime);
|
|
time.tm_mday = GET_DAY(fdate);
|
|
time.tm_mon = GET_MONTH(fdate);
|
|
time.tm_year = GET_YEAR(fdate) + YEAR_OFFSET; /* Year start from 1980 in FATFS */
|
|
time_t ret = mktime(&time);
|
|
return ret;
|
|
}
|
|
|
|
DWORD fattime_format(time_t time)
|
|
{
|
|
struct tm st;
|
|
DWORD ftime;
|
|
|
|
localtime_r(&time, &st);
|
|
|
|
ftime = (DWORD)st.tm_mday;
|
|
ftime |= ((DWORD)st.tm_mon) << FTIME_MTH_OFFSET;
|
|
ftime |= ((DWORD)((st.tm_year > YEAR_OFFSET) ? (st.tm_year - YEAR_OFFSET) : 0)) << FTIME_YEAR_OFFSET;
|
|
ftime <<= FTIME_DATE_OFFSET;
|
|
|
|
ftime = (DWORD)st.tm_sec / SEC_MULTIPLIER;
|
|
ftime |= ((DWORD)st.tm_min) << FTIME_MIN_OFFSET;
|
|
ftime |= ((DWORD)st.tm_hour) << FTIME_HR_OFFSET;
|
|
|
|
return ftime;
|
|
}
|
|
|
|
int fatfs_stat(struct Vnode *vp, struct stat* sp)
|
|
{
|
|
FATFS *fs = (FATFS *)vp->originMount->data;
|
|
DIR_FILE *dfp = (DIR_FILE *)vp->data;
|
|
FILINFO *finfo = &(dfp->fno);
|
|
time_t time;
|
|
int ret;
|
|
|
|
ret = lock_fs(fs);
|
|
if (ret == FALSE) {
|
|
return EBUSY;
|
|
}
|
|
|
|
sp->st_dev = fs->pdrv;
|
|
sp->st_mode = vp->mode;
|
|
sp->st_nlink = 1;
|
|
sp->st_uid = fs->fs_uid;
|
|
sp->st_gid = fs->fs_gid;
|
|
sp->st_size = finfo->fsize;
|
|
sp->st_blksize = fs->csize * SS(fs);
|
|
if (finfo->fattrib & AM_ARC) {
|
|
sp->st_blocks = finfo->fsize ? ((finfo->fsize - 1) / SS(fs) / fs->csize + 1) : 0;
|
|
} else {
|
|
sp->st_blocks = fs->csize;
|
|
}
|
|
time = fattime_transfer(finfo->fdate, finfo->ftime);
|
|
sp->st_mtime = time;
|
|
|
|
/* Adapt to kstat member "long tv_sec" */
|
|
sp->__st_mtim32.tv_sec = (long)time;
|
|
|
|
unlock_fs(fs, FR_OK);
|
|
return 0;
|
|
}
|
|
|
|
void fatfs_chtime(DIR *dp, struct IATTR *attr)
|
|
{
|
|
BYTE *dir = dp->dir;
|
|
DWORD ftime;
|
|
if (attr->attr_chg_valid & CHG_ATIME) {
|
|
ftime = fattime_format(attr->attr_chg_atime);
|
|
st_word(dir + DIR_LstAccDate, (ftime >> FTIME_DATE_OFFSET));
|
|
}
|
|
|
|
if (attr->attr_chg_valid & CHG_CTIME) {
|
|
ftime = fattime_format(attr->attr_chg_ctime);
|
|
st_dword(dir + DIR_CrtTime, ftime);
|
|
}
|
|
|
|
if (attr->attr_chg_valid & CHG_MTIME) {
|
|
ftime = fattime_format(attr->attr_chg_mtime);
|
|
st_dword(dir + DIR_ModTime, ftime);
|
|
}
|
|
}
|
|
|
|
int fatfs_chattr(struct Vnode *vp, struct IATTR *attr)
|
|
{
|
|
FATFS *fs = (FATFS *)vp->originMount->data;
|
|
DIR_FILE *dfp = (DIR_FILE *)vp->data;
|
|
DIR *dp = &(dfp->f_dir);
|
|
FILINFO *finfo = &(dfp->fno);
|
|
BYTE *dir = dp->dir;
|
|
DWORD ftime;
|
|
FRESULT result;
|
|
int ret;
|
|
|
|
if (finfo->fname[0] == '/') { /* Is root dir of fatfs ? */
|
|
return 0;
|
|
}
|
|
|
|
ret = lock_fs(fs);
|
|
if (ret == FALSE) {
|
|
result = FR_TIMEOUT;
|
|
goto ERROR_OUT;
|
|
}
|
|
|
|
result = move_window(fs, dp->sect);
|
|
if (result != FR_OK) {
|
|
goto ERROR_UNLOCK;
|
|
}
|
|
|
|
if (attr->attr_chg_valid & CHG_MODE) {
|
|
/* FAT only support readonly flag */
|
|
if ((attr->attr_chg_mode & S_IWUSR) == 0 && (finfo->fattrib & AM_RDO) == 0) {
|
|
dir[DIR_Attr] |= AM_RDO;
|
|
finfo->fattrib |= AM_RDO;
|
|
fs->wflag = 1;
|
|
} else if ((attr->attr_chg_mode & S_IWUSR) != 0 && (finfo->fattrib & AM_RDO) != 0) {
|
|
dir[DIR_Attr] &= ~AM_RDO;
|
|
finfo->fattrib &= ~AM_RDO;
|
|
fs->wflag = 1;
|
|
}
|
|
vp->mode = fatfs_get_mode(finfo->fattrib, fs->fs_mode);
|
|
}
|
|
|
|
if (attr->attr_chg_valid & (CHG_ATIME | CHG_CTIME | CHG_MTIME)) {
|
|
fatfs_chtime(dp, attr);
|
|
ftime = ld_dword(dp->dir + DIR_ModTime);
|
|
finfo->fdate = (WORD)(ftime >> FTIME_DATE_OFFSET);
|
|
finfo->ftime = (WORD)ftime;
|
|
}
|
|
|
|
result = sync_window(fs);
|
|
if (result != FR_OK) {
|
|
goto ERROR_UNLOCK;
|
|
}
|
|
|
|
unlock_fs(fs, FR_OK);
|
|
return fatfs_sync(vp->originMount->mountFlags, fs);
|
|
ERROR_UNLOCK:
|
|
unlock_fs(fs, result);
|
|
ERROR_OUT:
|
|
return -fatfs_2_vfs(result);
|
|
}
|
|
|
|
int fatfs_opendir(struct Vnode *vp, struct fs_dirent_s *idir)
|
|
{
|
|
FATFS *fs = vp->originMount->data;
|
|
DIR_FILE *dfp = (DIR_FILE *)vp->data;
|
|
FILINFO *finfo = &(dfp->fno);
|
|
DIR *dp;
|
|
DWORD clst;
|
|
FRESULT result;
|
|
int ret;
|
|
|
|
dp = (DIR*)zalloc(sizeof(DIR));
|
|
if (dp == NULL) {
|
|
return -ENOMEM;
|
|
}
|
|
|
|
ret = lock_fs(fs);
|
|
if (ret == FALSE) {
|
|
return -EBUSY;
|
|
}
|
|
clst = finfo->sclst;
|
|
dp->obj.fs = fs;
|
|
dp->obj.sclust = clst;
|
|
|
|
result = dir_sdi(dp, 0);
|
|
if (result != FR_OK) {
|
|
free(dp);
|
|
unlock_fs(fs, result);
|
|
return -fatfs_2_vfs(result);
|
|
}
|
|
unlock_fs(fs, result);
|
|
idir->u.fs_dir = dp;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int fatfs_readdir(struct Vnode *vp, struct fs_dirent_s *idir)
|
|
{
|
|
FATFS *fs = vp->originMount->data;
|
|
FILINFO fno;
|
|
DIR* dp = (DIR*)idir->u.fs_dir;
|
|
struct dirent *dirp = NULL;
|
|
FRESULT result;
|
|
int ret, i;
|
|
|
|
ret = lock_fs(fs);
|
|
if (ret == FALSE) { /* Lock fs failed */
|
|
return -EBUSY;
|
|
}
|
|
DEF_NAMBUF;
|
|
INIT_NAMBUF(fs);
|
|
for (i = 0; i < idir->read_cnt; i++) {
|
|
/* using dir_read_massive to promote performance */
|
|
result = dir_read_massive(dp, 0);
|
|
if (result == FR_NO_FILE) {
|
|
break;
|
|
} else if (result != FR_OK) {
|
|
goto ERROR_UNLOCK;
|
|
}
|
|
get_fileinfo(dp, &fno);
|
|
/* 0x00 for end of directory and 0xFF for directory is empty */
|
|
if (fno.fname[0] == 0x00 || fno.fname[0] == (TCHAR)0xFF) {
|
|
break;
|
|
}
|
|
|
|
dirp = &(idir->fd_dir[i]);
|
|
if (fno.fattrib & AM_DIR) { /* is dir */
|
|
dirp->d_type = DT_DIR;
|
|
} else {
|
|
dirp->d_type = DT_REG;
|
|
}
|
|
if (strncpy_s(dirp->d_name, sizeof(dirp->d_name), fno.fname, strlen(fno.fname)) != EOK) {
|
|
result = FR_NOT_ENOUGH_CORE;
|
|
goto ERROR_UNLOCK;
|
|
}
|
|
result = dir_next(dp, 0);
|
|
if (result != FR_OK && result != FR_NO_FILE) {
|
|
goto ERROR_UNLOCK;
|
|
}
|
|
idir->fd_position++;
|
|
idir->fd_dir[i].d_off = idir->fd_position;
|
|
idir->fd_dir[i].d_reclen = (uint16_t)sizeof(struct dirent);
|
|
}
|
|
unlock_fs(fs, FR_OK);
|
|
FREE_NAMBUF();
|
|
return i;
|
|
ERROR_UNLOCK:
|
|
unlock_fs(fs, result);
|
|
FREE_NAMBUF();
|
|
return -fatfs_2_vfs(result);
|
|
}
|
|
|
|
int fatfs_rewinddir(struct Vnode *vp, struct fs_dirent_s *dir)
|
|
{
|
|
DIR *dp = (DIR *)dir->u.fs_dir;
|
|
FATFS *fs = dp->obj.fs;
|
|
FRESULT result;
|
|
int ret;
|
|
|
|
ret = lock_fs(fs);
|
|
if (ret == FALSE) {
|
|
return -EBUSY;
|
|
}
|
|
|
|
result = dir_sdi(dp, 0);
|
|
unlock_fs(fs, result);
|
|
return -fatfs_2_vfs(result);
|
|
}
|
|
|
|
int fatfs_closedir(struct Vnode *vp, struct fs_dirent_s *dir)
|
|
{
|
|
DIR *dp = (DIR *)dir->u.fs_dir;
|
|
free(dp);
|
|
dir->u.fs_dir = NULL;
|
|
return 0;
|
|
}
|
|
|
|
static FRESULT rename_check(DIR *dp_new, FILINFO *finfo_new, DIR *dp_old, FILINFO *finfo_old)
|
|
{
|
|
DIR dir_sub;
|
|
FRESULT result;
|
|
if (finfo_new->fattrib & AM_ARC) { /* new path is file */
|
|
if (finfo_old->fattrib & AM_DIR) { /* but old path is dir */
|
|
return FR_NO_DIR;
|
|
}
|
|
} else if (finfo_new->fattrib & AM_DIR) { /* new path is dir */
|
|
if (finfo_old->fattrib & AM_ARC) { /* old path is file */
|
|
return FR_IS_DIR;
|
|
}
|
|
dir_sub.obj.fs = dp_old->obj.fs;
|
|
dir_sub.obj.sclust = finfo_new->sclst;
|
|
result = dir_sdi(&dir_sub, 0);
|
|
if (result != FR_OK) {
|
|
return result;
|
|
}
|
|
result = dir_read(&dir_sub, 0);
|
|
if (result == FR_OK) { /* new path isn't empty file */
|
|
return FR_NO_EMPTY_DIR;
|
|
}
|
|
} else { /* System file or volume label */
|
|
return FR_DENIED;
|
|
}
|
|
return FR_OK;
|
|
}
|
|
|
|
int fatfs_rename(struct Vnode *old_vnode, struct Vnode *new_parent, const char *oldname, const char *newname)
|
|
{
|
|
FATFS *fs = (FATFS *)(old_vnode->originMount->data);
|
|
DIR_FILE *dfp_old = (DIR_FILE *)old_vnode->data;
|
|
DIR *dp_old = &(dfp_old->f_dir);
|
|
FILINFO *finfo_old = &(dfp_old->fno);
|
|
DIR_FILE *dfp_new = NULL;
|
|
DIR* dp_new = NULL;
|
|
FILINFO* finfo_new = NULL;
|
|
DWORD clust;
|
|
FRESULT result;
|
|
int ret;
|
|
|
|
ret = lock_fs(fs);
|
|
if (ret == FALSE) { /* Lock fs failed */
|
|
return -EBUSY;
|
|
}
|
|
|
|
dfp_new = (DIR_FILE *)zalloc(sizeof(DIR_FILE));
|
|
if (dfp_new == NULL) {
|
|
result = FR_NOT_ENOUGH_CORE;
|
|
goto ERROR_UNLOCK;
|
|
}
|
|
dp_new = &(dfp_new->f_dir);
|
|
finfo_new = &(dfp_new->fno);
|
|
|
|
dp_new->obj.sclust = ((DIR_FILE *)(new_parent->data))->fno.sclst;
|
|
dp_new->obj.fs = fs;
|
|
|
|
/* Find new path */
|
|
DEF_NAMBUF;
|
|
INIT_NAMBUF(fs);
|
|
result = create_name(dp_new, &newname);
|
|
if (result != FR_OK) {
|
|
goto ERROR_FREE;
|
|
}
|
|
result = dir_find(dp_new);
|
|
if (result == FR_OK) { /* new path name exist */
|
|
get_fileinfo(dp_new, finfo_new);
|
|
result = rename_check(dp_new, finfo_new, dp_old, finfo_old);
|
|
if (result != FR_OK) {
|
|
goto ERROR_FREE;
|
|
}
|
|
result = dir_remove(dp_old);
|
|
if (result != FR_OK) {
|
|
goto ERROR_FREE;
|
|
}
|
|
clust = finfo_new->sclst;
|
|
if (clust != 0) { /* remove the new path cluster chain if exists */
|
|
result = remove_chain(&(dp_new->obj), clust, 0);
|
|
if (result != FR_OK) {
|
|
goto ERROR_FREE;
|
|
}
|
|
}
|
|
} else { /* new path name not exist */
|
|
result = dir_remove(dp_old);
|
|
if (result != FR_OK) {
|
|
goto ERROR_FREE;
|
|
}
|
|
result = dir_register(dp_new);
|
|
if (result != FR_OK) {
|
|
goto ERROR_FREE;
|
|
}
|
|
}
|
|
|
|
/* update new dir entry with old info */
|
|
result = update_dir(dp_new, finfo_old);
|
|
if (result != FR_OK) {
|
|
goto ERROR_FREE;
|
|
}
|
|
result = dir_read(dp_new, 0);
|
|
if (result != FR_OK) {
|
|
goto ERROR_FREE;
|
|
}
|
|
dp_new->blk_ofs = dir_ofs(dp_new);
|
|
get_fileinfo(dp_new, finfo_new);
|
|
|
|
dfp_new->fno.fp_list.pstNext = dfp_old->fno.fp_list.pstNext;
|
|
dfp_new->fno.fp_list.pstPrev = dfp_old->fno.fp_list.pstPrev;
|
|
ret = memcpy_s(dfp_old, sizeof(DIR_FILE), dfp_new, sizeof(DIR_FILE));
|
|
if (ret != 0) {
|
|
result = FR_NOT_ENOUGH_CORE;
|
|
goto ERROR_FREE;
|
|
}
|
|
free(dfp_new);
|
|
unlock_fs(fs, FR_OK);
|
|
FREE_NAMBUF();
|
|
return fatfs_sync(old_vnode->originMount->mountFlags, fs);
|
|
|
|
ERROR_FREE:
|
|
free(dfp_new);
|
|
ERROR_UNLOCK:
|
|
unlock_fs(fs, result);
|
|
FREE_NAMBUF();
|
|
return -fatfs_2_vfs(result);
|
|
}
|
|
|
|
|
|
static int fatfs_erase(los_part *part, int option)
|
|
{
|
|
int opt = option;
|
|
if ((UINT)opt & FMT_ERASE) {
|
|
opt = (UINT)opt & (~FMT_ERASE);
|
|
if (EraseDiskByID(part->disk_id, part->sector_start, part->sector_count) != LOS_OK) {
|
|
PRINTK("Disk erase error.\n");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (opt != FM_FAT && opt != FM_FAT32) {
|
|
opt = FM_ANY;
|
|
}
|
|
|
|
return opt;
|
|
}
|
|
|
|
static int fatfs_set_part_info(los_part *part)
|
|
{
|
|
los_disk *disk = NULL;
|
|
char *buf = NULL;
|
|
int ret;
|
|
|
|
/* If there is no MBR before, the partition info needs to be changed after mkfs */
|
|
if (part->type != EMMC && part->part_no_mbr == 0) {
|
|
disk = get_disk(part->disk_id);
|
|
if (disk == NULL) {
|
|
return -EIO;
|
|
}
|
|
buf = (char *)zalloc(disk->sector_size);
|
|
if (buf == NULL) {
|
|
return -ENOMEM;
|
|
}
|
|
(void)memset_s(buf, disk->sector_size, 0, disk->sector_size);
|
|
ret = los_disk_read(part->disk_id, buf, 0, 1, TRUE); /* TRUE when not reading large data */
|
|
if (ret < 0) {
|
|
free(buf);
|
|
return -EIO;
|
|
}
|
|
part->sector_start = LD_DWORD_DISK(&buf[PAR_OFFSET + PAR_START_OFFSET]);
|
|
part->sector_count = LD_DWORD_DISK(&buf[PAR_OFFSET + PAR_COUNT_OFFSET]);
|
|
part->part_no_mbr = 1;
|
|
part->filesystem_type = buf[PAR_OFFSET + PAR_TYPE_OFFSET];
|
|
|
|
free(buf);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static FRESULT fatfs_setlabel(los_part *part)
|
|
{
|
|
QWORD start_sector = 0;
|
|
BYTE fmt = 0;
|
|
FATFS fs;
|
|
FRESULT result;
|
|
|
|
#ifdef LOSCFG_FS_FAT_VIRTUAL_PARTITION
|
|
fs.vir_flag = FS_PARENT;
|
|
fs.parent_fs = &fs;
|
|
fs.vir_amount = DISK_ERROR;
|
|
fs.vir_avail = FS_VIRDISABLE;
|
|
#endif
|
|
if (disk_ioctl(fs.pdrv, GET_SECTOR_SIZE, &(fs.ssize)) != RES_OK) {
|
|
return -EIO;
|
|
}
|
|
fs.win = (BYTE *)ff_memalloc(fs.ssize);
|
|
if (fs.win == NULL) {
|
|
return -ENOMEM;
|
|
}
|
|
|
|
result = find_fat_partition(&fs, part, &fmt, &start_sector);
|
|
if (result != FR_OK) {
|
|
free(fs.win);
|
|
return -fatfs_2_vfs(result);
|
|
}
|
|
|
|
result = init_fatobj(&fs, fmt, start_sector);
|
|
if (result != FR_OK) {
|
|
free(fs.win);
|
|
return -fatfs_2_vfs(result);
|
|
}
|
|
|
|
result = set_volumn_label(&fs, FatLabel);
|
|
free(fs.win);
|
|
|
|
return result;
|
|
}
|
|
|
|
int fatfs_mkfs (struct Vnode *device, int sectors, int option)
|
|
{
|
|
BYTE *work_buff = NULL;
|
|
los_part *part = NULL;
|
|
FRESULT result;
|
|
MKFS_PARM opt = {0};
|
|
int ret;
|
|
|
|
part = los_part_find(device);
|
|
if (part == NULL || device->data == NULL) {
|
|
return -ENODEV;
|
|
}
|
|
|
|
if (sectors < 0 || sectors > FAT32_MAX_CLUSTER_SIZE || ((DWORD)sectors & ((DWORD)sectors - 1))) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (option != FMT_FAT && option != FMT_FAT32 && option != FMT_ANY && option != FMT_ERASE) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (part->part_name != NULL) { /* The part is mounted */
|
|
return -EBUSY;
|
|
}
|
|
option = fatfs_erase(part, option);
|
|
work_buff = (BYTE *)zalloc(FF_MAX_SS);
|
|
if (work_buff == NULL) {
|
|
return -ENOMEM;
|
|
}
|
|
|
|
opt.n_sect = sectors;
|
|
opt.fmt = (BYTE)option;
|
|
result = _mkfs(part, &opt, work_buff, FF_MAX_SS);
|
|
free(work_buff);
|
|
if (result != FR_OK) {
|
|
return -fatfs_2_vfs(result);
|
|
}
|
|
|
|
result = fatfs_setlabel(part);
|
|
if (result == FR_OK) {
|
|
#ifdef LOSCFG_FS_FAT_CACHE
|
|
ret = OsSdSync(part->disk_id);
|
|
if (ret != 0) {
|
|
return -EIO;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
ret = fatfs_set_part_info(part);
|
|
if (ret != 0) {
|
|
return -EIO;
|
|
}
|
|
|
|
return -fatfs_2_vfs(result);
|
|
}
|
|
|
|
int fatfs_mkdir(struct Vnode *parent, const char *name, mode_t mode, struct Vnode **vpp)
|
|
{
|
|
return fatfs_create_obj(parent, name, mode, vpp, AM_DIR, NULL);
|
|
}
|
|
|
|
int fatfs_rmdir(struct Vnode *parent, struct Vnode *vp, const char *name)
|
|
{
|
|
FATFS *fs = (FATFS *)vp->originMount->data;
|
|
DIR_FILE *dfp = (DIR_FILE *)vp->data;
|
|
FILINFO *finfo = &(dfp->fno);
|
|
DIR *dp = &(dfp->f_dir);
|
|
DIR dir_sub;
|
|
FRESULT result = FR_OK;
|
|
int ret;
|
|
|
|
if (finfo->fattrib & AM_ARC) {
|
|
result = FR_NO_DIR;
|
|
goto ERROR_OUT;
|
|
}
|
|
|
|
DEF_NAMBUF;
|
|
INIT_NAMBUF(fs);
|
|
|
|
ret = lock_fs(fs);
|
|
if (ret == FALSE) {
|
|
result = FR_TIMEOUT;
|
|
goto ERROR_OUT;
|
|
}
|
|
dir_sub.obj.fs = fs;
|
|
dir_sub.obj.sclust = finfo->sclst;
|
|
result = dir_sdi(&dir_sub, 0);
|
|
if (result != FR_OK) {
|
|
goto ERROR_UNLOCK;
|
|
}
|
|
result = dir_read(&dir_sub, 0);
|
|
if (result == FR_OK) {
|
|
result = FR_NO_EMPTY_DIR;
|
|
goto ERROR_UNLOCK;
|
|
}
|
|
result = dir_remove(dp); /* remove directory entry */
|
|
if (result != FR_OK) {
|
|
goto ERROR_UNLOCK;
|
|
}
|
|
/* Directory entry contains at least one cluster */
|
|
result = remove_chain(&(dp->obj), finfo->sclst, 0);
|
|
if (result != FR_OK) {
|
|
goto ERROR_UNLOCK;
|
|
}
|
|
|
|
unlock_fs(fs, FR_OK);
|
|
FREE_NAMBUF();
|
|
return fatfs_sync(vp->originMount->mountFlags, fs);
|
|
|
|
ERROR_UNLOCK:
|
|
unlock_fs(fs, result);
|
|
FREE_NAMBUF();
|
|
ERROR_OUT:
|
|
return -fatfs_2_vfs(result);
|
|
}
|
|
|
|
int fatfs_reclaim(struct Vnode *vp)
|
|
{
|
|
free(vp->data);
|
|
vp->data = NULL;
|
|
return 0;
|
|
}
|
|
|
|
int fatfs_unlink(struct Vnode *parent, struct Vnode *vp, const char *name)
|
|
{
|
|
FATFS *fs = (FATFS *)vp->originMount->data;
|
|
DIR_FILE *dfp = (DIR_FILE *)vp->data;
|
|
FILINFO *finfo = &(dfp->fno);
|
|
DIR *dp = &(dfp->f_dir);
|
|
FRESULT result = FR_OK;
|
|
int ret;
|
|
|
|
if (finfo->fattrib & AM_DIR) {
|
|
result = FR_IS_DIR;
|
|
goto ERROR_OUT;
|
|
}
|
|
ret = lock_fs(fs);
|
|
if (ret == FALSE) {
|
|
result = FR_TIMEOUT;
|
|
goto ERROR_OUT;
|
|
}
|
|
result = dir_remove(dp); /* remove directory entry */
|
|
if (result != FR_OK) {
|
|
goto ERROR_UNLOCK;
|
|
}
|
|
if (finfo->sclst != 0) { /* if cluster chain exists */
|
|
result = remove_chain(&(dp->obj), finfo->sclst, 0);
|
|
if (result != FR_OK) {
|
|
goto ERROR_UNLOCK;
|
|
}
|
|
}
|
|
result = sync_fs(fs);
|
|
if (result != FR_OK) {
|
|
goto ERROR_UNLOCK;
|
|
}
|
|
unlock_fs(fs, FR_OK);
|
|
return fatfs_sync(vp->originMount->mountFlags, fs);
|
|
|
|
ERROR_UNLOCK:
|
|
unlock_fs(fs, result);
|
|
ERROR_OUT:
|
|
return -fatfs_2_vfs(result);
|
|
}
|
|
|
|
int fatfs_ioctl(struct file *filep, int req, unsigned long arg)
|
|
{
|
|
return -ENOSYS;
|
|
}
|
|
|
|
#define CHECK_FILE_NUM 3
|
|
static inline DWORD combine_time(FILINFO *finfo)
|
|
{
|
|
return (finfo->fdate << FTIME_DATE_OFFSET) | finfo->ftime;
|
|
}
|
|
|
|
static UINT get_oldest_time(DIR_FILE df[], DWORD *oldest_time, UINT len)
|
|
{
|
|
int i;
|
|
DWORD old_time = combine_time(&(df[0].fno));
|
|
DWORD time;
|
|
UINT index = 0;
|
|
for (i = 1; i < len; i++) {
|
|
time = combine_time(&(df[i].fno));
|
|
if (time < old_time) {
|
|
old_time = time;
|
|
index = i;
|
|
}
|
|
}
|
|
*oldest_time = old_time;
|
|
return index;
|
|
}
|
|
|
|
int fatfs_fscheck(struct Vnode* vp, struct fs_dirent_s *dir)
|
|
{
|
|
FATFS *fs = (FATFS *)vp->originMount->data;
|
|
DIR_FILE df[CHECK_FILE_NUM] = {0};
|
|
DIR *dp = NULL;
|
|
FILINFO *finfo = &(((DIR_FILE *)(vp->data))->fno);
|
|
FILINFO fno;
|
|
DWORD old_time = -1;
|
|
DWORD time;
|
|
UINT count;
|
|
UINT index = 0;
|
|
los_part *part = NULL;
|
|
FRESULT result;
|
|
int ret;
|
|
|
|
if (fs->fs_type != FS_FAT32) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if ((finfo->fattrib & AM_DIR) == 0) {
|
|
return -ENOTDIR;
|
|
}
|
|
|
|
ret = fatfs_opendir(vp, dir);
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
|
|
ret = lock_fs(fs);
|
|
if (ret == FALSE) {
|
|
result = FR_TIMEOUT;
|
|
goto ERROR_WITH_DIR;
|
|
}
|
|
|
|
dp = (DIR *)dir->u.fs_dir;
|
|
dp->obj.id = fs->id;
|
|
for (count = 0; count < CHECK_FILE_NUM; count++) {
|
|
if ((result = f_readdir(dp, &fno)) != FR_OK) {
|
|
goto ERROR_UNLOCK;
|
|
} else {
|
|
if (fno.fname[0] == 0 || fno.fname[0] == (TCHAR)0xFF) {
|
|
break;
|
|
}
|
|
(void)memcpy_s(&df[count].f_dir, sizeof(DIR), dp, sizeof(DIR));
|
|
(void)memcpy_s(&df[count].fno, sizeof(FILINFO), &fno, sizeof(FILINFO));
|
|
time = combine_time(&(df[count].fno));
|
|
if (time < old_time) {
|
|
old_time = time;
|
|
index = count;
|
|
}
|
|
}
|
|
}
|
|
while ((result = f_readdir(dp, &fno)) == FR_OK) {
|
|
if (fno.fname[0] == 0 || fno.fname[0] == (TCHAR)0xFF) {
|
|
break;
|
|
}
|
|
time = combine_time(&fno);
|
|
if (time < old_time) {
|
|
(void)memcpy_s(&df[index].f_dir, sizeof(DIR), dp, sizeof(DIR));
|
|
(void)memcpy_s(&df[index].fno, sizeof(FILINFO), &fno, sizeof(FILINFO));
|
|
index = get_oldest_time(df, &old_time, CHECK_FILE_NUM);
|
|
}
|
|
}
|
|
if (result != FR_OK) {
|
|
goto ERROR_UNLOCK;
|
|
}
|
|
|
|
for (index = 0; index < count; index++) {
|
|
result = f_fcheckfat(&df[index]);
|
|
if (result != FR_OK) {
|
|
goto ERROR_UNLOCK;
|
|
}
|
|
}
|
|
|
|
unlock_fs(fs, FR_OK);
|
|
|
|
ret = fatfs_closedir(vp, dir);
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
|
|
#ifdef LOSCFG_FS_FAT_CACHE
|
|
part = get_part((INT)fs->pdrv);
|
|
if (part != NULL) {
|
|
(void)OsSdSync(part->disk_id);
|
|
}
|
|
#endif
|
|
|
|
return 0;
|
|
|
|
ERROR_UNLOCK:
|
|
unlock_fs(fs, result);
|
|
ERROR_WITH_DIR:
|
|
fatfs_closedir(vp, 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);
|
|
}
|
|
|
|
ssize_t fatfs_readpage(struct Vnode *vnode, char *buff, off_t pos)
|
|
{
|
|
FATFS *fs = (FATFS *)(vnode->originMount->data);
|
|
DIR_FILE *dfp = (DIR_FILE *)(vnode->data);
|
|
FILINFO *finfo = &(dfp->fno);
|
|
FAT_ENTRY *ep = &(dfp->fat_entry);
|
|
DWORD clust;
|
|
DWORD sclust;
|
|
QWORD sect;
|
|
QWORD step;
|
|
QWORD n;
|
|
size_t position; /* byte offset */
|
|
BYTE *buf = (BYTE *)buff;
|
|
size_t buflen = PAGE_SIZE;
|
|
FRESULT result;
|
|
int ret;
|
|
|
|
ret = lock_fs(fs);
|
|
if (ret == FALSE) {
|
|
result = FR_TIMEOUT;
|
|
goto ERROR_OUT;
|
|
}
|
|
|
|
if (finfo->fsize <= pos) {
|
|
result = FR_OK;
|
|
goto ERROR_UNLOCK;
|
|
}
|
|
|
|
if (ep->clst == 0) {
|
|
ep->clst = finfo->sclst;
|
|
}
|
|
|
|
if (pos >= ep->pos) {
|
|
clust = ep->clst;
|
|
position = ep->pos;
|
|
} else {
|
|
clust = finfo->sclst;
|
|
position = 0;
|
|
}
|
|
|
|
/* Get to the current cluster */
|
|
n = pos / SS(fs) / fs->csize - position / SS(fs) / fs->csize;
|
|
while (n--) {
|
|
clust = get_fat(&(dfp->f_dir.obj), clust);
|
|
if ((clust == BAD_CLUSTER) || (clust == DISK_ERROR)) {
|
|
result = FR_DISK_ERR;
|
|
goto ERROR_UNLOCK;
|
|
}
|
|
}
|
|
|
|
/* Get to the currnet sector */
|
|
sect = clst2sect(fs, clust);
|
|
sect += (pos / SS(fs)) & (fs->csize - 1);
|
|
|
|
/* How many sectors do we need to read once */
|
|
if (fs->csize < buflen / SS(fs)) {
|
|
step = fs->csize;
|
|
} else {
|
|
step = buflen / SS(fs);
|
|
}
|
|
|
|
n = 0;
|
|
sclust = clust;
|
|
while (n < buflen / SS(fs)) {
|
|
if (disk_read(fs->pdrv, buf, sect, step) != RES_OK) {
|
|
result = FR_DISK_ERR;
|
|
goto ERROR_UNLOCK;
|
|
}
|
|
n += step;
|
|
if (n >= buflen / SS(fs)) {
|
|
break;
|
|
}
|
|
|
|
/* As cluster size is aligned, it must jump to next cluster when cluster size is less than pagesize */
|
|
clust = get_fat(&(dfp->f_dir.obj), clust);
|
|
if ((clust == BAD_CLUSTER) || (clust == DISK_ERROR)) {
|
|
result = FR_DISK_ERR;
|
|
goto ERROR_UNLOCK;
|
|
} else if (fatfs_is_last_cluster(fs, clust)) {
|
|
break; /* read end */
|
|
}
|
|
sect = clst2sect(fs, clust);
|
|
buf += step * SS(fs);
|
|
}
|
|
|
|
ep->clst = sclust;
|
|
ep->pos = pos;
|
|
|
|
unlock_fs(fs, FR_OK);
|
|
|
|
return (ssize_t)min(finfo->fsize - pos, n * SS(fs));
|
|
|
|
ERROR_UNLOCK:
|
|
unlock_fs(fs, result);
|
|
ERROR_OUT:
|
|
return -fatfs_2_vfs(result);
|
|
}
|
|
|
|
ssize_t fatfs_writepage(struct Vnode *vnode, char *buff, off_t pos, size_t buflen)
|
|
{
|
|
FATFS *fs = (FATFS *)(vnode->originMount->data);
|
|
DIR_FILE *dfp = (DIR_FILE *)(vnode->data);
|
|
FILINFO *finfo = &(dfp->fno);
|
|
FAT_ENTRY *ep = &(dfp->fat_entry);
|
|
DWORD clust;
|
|
DWORD sclst;
|
|
QWORD sect;
|
|
QWORD step;
|
|
QWORD n;
|
|
size_t position; /* byte offset */
|
|
BYTE *buf = (BYTE *)buff;
|
|
FRESULT result;
|
|
FIL fil;
|
|
int ret;
|
|
|
|
ret = lock_fs(fs);
|
|
if (ret == FALSE) {
|
|
result = FR_TIMEOUT;
|
|
goto ERROR_OUT;
|
|
}
|
|
|
|
if (finfo->fsize <= pos) {
|
|
result = FR_OK;
|
|
goto ERROR_UNLOCK;
|
|
}
|
|
|
|
if (ep->clst == 0) {
|
|
ep->clst = finfo->sclst;
|
|
}
|
|
|
|
if (pos >= ep->pos) {
|
|
clust = ep->clst;
|
|
position = ep->pos;
|
|
} else {
|
|
clust = finfo->sclst;
|
|
position = 0;
|
|
}
|
|
|
|
/* Get to the current cluster */
|
|
n = pos / SS(fs) / fs->csize - position / SS(fs) / fs->csize;
|
|
while (n--) {
|
|
clust = get_fat(&(dfp->f_dir.obj), clust);
|
|
if ((clust == BAD_CLUSTER) || (clust == DISK_ERROR)) {
|
|
result = FR_DISK_ERR;
|
|
goto ERROR_UNLOCK;
|
|
}
|
|
}
|
|
|
|
/* Get to the currnet sector */
|
|
sect = clst2sect(fs, clust);
|
|
sect += (pos / SS(fs)) & (fs->csize - 1);
|
|
|
|
/* How many sectors do we need to read once */
|
|
if (fs->csize < buflen / SS(fs)) {
|
|
step = fs->csize;
|
|
} else {
|
|
step = buflen / SS(fs);
|
|
}
|
|
|
|
n = 0;
|
|
sclst = clust;
|
|
while (n < buflen / SS(fs)) {
|
|
if (disk_write(fs->pdrv, buf, sect, step) != RES_OK) {
|
|
result = FR_DISK_ERR;
|
|
goto ERROR_UNLOCK;
|
|
}
|
|
n += step;
|
|
if (n >= buflen / SS(fs)) {
|
|
break;
|
|
}
|
|
|
|
/* As cluster size is aligned, it must jump to next cluster when cluster size is less than pagesize */
|
|
clust = get_fat(&(dfp->f_dir.obj), clust);
|
|
if ((clust == BAD_CLUSTER) || (clust == DISK_ERROR)) {
|
|
result = FR_DISK_ERR;
|
|
goto ERROR_UNLOCK;
|
|
} else if (fatfs_is_last_cluster(fs, clust)) {
|
|
break; /* read end */
|
|
}
|
|
sect = clst2sect(fs, clust);
|
|
buf += step * SS(fs);
|
|
}
|
|
|
|
ep->clst = sclst;
|
|
ep->pos = pos;
|
|
|
|
fil.obj.fs = fs;
|
|
if (update_filbuff(finfo, &fil, NULL) < 0) {
|
|
result = FR_DISK_ERR;
|
|
goto ERROR_UNLOCK;
|
|
}
|
|
|
|
unlock_fs(fs, FR_OK);
|
|
|
|
return (ssize_t)min(finfo->fsize - pos, n * SS(fs));
|
|
ERROR_UNLOCK:
|
|
unlock_fs(fs, result);
|
|
ERROR_OUT:
|
|
return -fatfs_2_vfs(result);
|
|
}
|
|
|
|
struct VnodeOps fatfs_vops = {
|
|
/* file ops */
|
|
.Getattr = fatfs_stat,
|
|
.Chattr = fatfs_chattr,
|
|
.Lookup = fatfs_lookup,
|
|
.Rename = fatfs_rename,
|
|
.Create = fatfs_create,
|
|
.ReadPage = fatfs_readpage,
|
|
.WritePage = fatfs_writepage,
|
|
.Unlink = fatfs_unlink,
|
|
.Reclaim = fatfs_reclaim,
|
|
.Truncate = fatfs_truncate,
|
|
.Truncate64 = fatfs_truncate64,
|
|
/* dir ops */
|
|
.Opendir = fatfs_opendir,
|
|
.Readdir = fatfs_readdir,
|
|
.Rewinddir = fatfs_rewinddir,
|
|
.Closedir = fatfs_closedir,
|
|
.Mkdir = fatfs_mkdir,
|
|
.Rmdir = fatfs_rmdir,
|
|
.Fscheck = fatfs_fscheck,
|
|
.Symlink = fatfs_symlink,
|
|
.Readlink = fatfs_readlink,
|
|
};
|
|
|
|
struct MountOps fatfs_mops = {
|
|
.Mount = fatfs_mount,
|
|
.Unmount = fatfs_umount,
|
|
.Statfs = fatfs_statfs,
|
|
.Sync = fatfs_sync_adapt,
|
|
};
|
|
|
|
struct file_operations_vfs fatfs_fops = {
|
|
.open = fatfs_open,
|
|
.read = fatfs_read,
|
|
.write = fatfs_write,
|
|
.seek = fatfs_lseek,
|
|
.close = fatfs_close,
|
|
.mmap = OsVfsFileMmap,
|
|
.fallocate = fatfs_fallocate,
|
|
.fallocate64 = fatfs_fallocate64,
|
|
.fsync = fatfs_fsync,
|
|
.ioctl = fatfs_ioctl,
|
|
};
|
|
|
|
FSMAP_ENTRY(fat_fsmap, "vfat", fatfs_mops, FALSE, TRUE);
|
|
|
|
#endif /* LOSCFG_FS_FAT */
|