zephyr/fs/fat_fs.c

310 lines
5.6 KiB
C

/*
* Copyright (c) 2016 Intel Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <errno.h>
#include <init.h>
#include <ff.h>
#include <fs.h>
#include <misc/__assert.h>
static FATFS fat_fs; /* FatFs work area */
static int translate_error(int error)
{
switch (error) {
case FR_OK:
return 0;
case FR_NO_FILE:
case FR_NO_PATH:
case FR_INVALID_NAME:
return -ENOENT;
case FR_DENIED:
return -EACCES;
case FR_EXIST:
return -EEXIST;
case FR_INVALID_OBJECT:
return -EBADF;
case FR_WRITE_PROTECTED:
return -EROFS;
case FR_INVALID_DRIVE:
case FR_NOT_ENABLED:
case FR_NO_FILESYSTEM:
return -ENODEV;
case FR_NOT_ENOUGH_CORE:
return -ENOMEM;
case FR_TOO_MANY_OPEN_FILES:
return -EMFILE;
case FR_INVALID_PARAMETER:
return -EINVAL;
case FR_LOCKED:
case FR_TIMEOUT:
case FR_MKFS_ABORTED:
case FR_DISK_ERR:
case FR_INT_ERR:
case FR_NOT_READY:
return -EIO;
}
return -EIO;
}
int fs_open(ZFILE *zfp, const char *file_name)
{
FRESULT res;
uint8_t fs_mode;
fs_mode = FA_READ | FA_WRITE | FA_OPEN_ALWAYS;
res = f_open(&zfp->fp, file_name, fs_mode);
return translate_error(res);
}
int fs_close(ZFILE *zfp)
{
FRESULT res;
res = f_close(&zfp->fp);
return translate_error(res);
}
int fs_unlink(const char *path)
{
FRESULT res;
res = f_unlink(path);
return translate_error(res);
}
ssize_t fs_read(ZFILE *zfp, void *ptr, size_t size)
{
FRESULT res;
unsigned int br;
res = f_read(&zfp->fp, ptr, size, &br);
if (res != FR_OK) {
return translate_error(res);
}
return br;
}
ssize_t fs_write(ZFILE *zfp, const void *ptr, size_t size)
{
FRESULT res;
unsigned int bw;
res = f_write(&zfp->fp, ptr, size, &bw);
if (res != FR_OK) {
return translate_error(res);
}
return bw;
}
int fs_seek(ZFILE *zfp, off_t offset, int whence)
{
FRESULT res = FR_OK;
off_t pos;
switch (whence) {
case SEEK_SET:
pos = offset;
break;
case SEEK_CUR:
pos = f_tell(&zfp->fp) + offset;
break;
case SEEK_END:
pos = f_size(&zfp->fp) + offset;
break;
default:
return -EINVAL;
}
if ((pos < 0) || (pos > f_size(&zfp->fp))) {
return -EINVAL;
}
res = f_lseek(&zfp->fp, pos);
return translate_error(res);
}
int fs_truncate(ZFILE *zfp, off_t length)
{
FRESULT res = FR_OK;
off_t cur_length = f_size(&zfp->fp);
/* f_lseek expands file if new position is larger than file size */
res = f_lseek(&zfp->fp, length);
if (res != FR_OK) {
return translate_error(res);
}
if (length < cur_length) {
res = f_truncate(&zfp->fp);
} else {
/*
* Get actual length after expansion. This could be
* less if there was not enough space in the volume
* to expand to the requested length
*/
length = f_tell(&zfp->fp);
res = f_lseek(&zfp->fp, cur_length);
if (res != FR_OK) {
return translate_error(res);
}
/*
* The FS module does caching and optimization of
* writes. Here we write 1 byte at a time to avoid
* using additional code and memory for doing any
* optimization.
*/
uint32_t bw;
uint8_t c = 0;
for (int i = cur_length; i < length; i++) {
res = f_write(&zfp->fp, &c, 1, &bw);
if (res != FR_OK) {
break;
}
}
}
return translate_error(res);
}
int fs_sync(ZFILE *zfp)
{
FRESULT res = FR_OK;
res = f_sync(&zfp->fp);
return translate_error(res);
}
int fs_mkdir(const char *path)
{
FRESULT res;
res = f_mkdir(path);
return translate_error(res);
}
int fs_opendir(ZDIR *zdp, const char *path)
{
FRESULT res;
res = f_opendir(&zdp->dp, path);
return translate_error(res);
}
int fs_readdir(ZDIR *zdp, struct zfs_dirent *entry)
{
FRESULT res;
FILINFO fno;
res = f_readdir(&zdp->dp, &fno);
if (res == FR_OK) {
entry->type = ((fno.fattrib & AM_DIR) ?
DIR_ENTRY_DIR : DIR_ENTRY_FILE);
strcpy(entry->name, fno.fname);
entry->size = fno.fsize;
}
return translate_error(res);
}
int fs_closedir(ZDIR *zdp)
{
FRESULT res;
res = f_closedir(&zdp->dp);
return translate_error(res);
}
int fs_stat(const char *path, struct zfs_dirent *entry)
{
FRESULT res;
FILINFO fno;
res = f_stat(path, &fno);
if (res == FR_OK) {
entry->type = ((fno.fattrib & AM_DIR) ?
DIR_ENTRY_DIR : DIR_ENTRY_FILE);
strcpy(entry->name, fno.fname);
entry->size = fno.fsize;
}
return translate_error(res);
}
int fs_statvfs(struct zfs_statvfs *stat)
{
FATFS *fs;
FRESULT res;
res = f_getfree("", &stat->f_bfree, &fs);
if (res != FR_OK) {
return -EIO;
}
/*
* _MIN_SS holds the sector size. It is one of the configuration
* constants used by the FS module
*/
stat->f_bsize = _MIN_SS;
stat->f_frsize = fs->csize * stat->f_bsize;
stat->f_blocks = (fs->n_fatent - 2);
return translate_error(res);
}
static int fs_init(struct device *dev)
{
FRESULT res;
ARG_UNUSED(dev);
res = f_mount(&fat_fs, "", 1);
/* If no file system found then create one */
if (res == FR_NO_FILESYSTEM) {
uint8_t work[_MAX_SS];
res = f_mkfs("", (FM_FAT | FM_SFD), 0, work, sizeof(work));
if (res == FR_OK) {
res = f_mount(&fat_fs, "", 1);
}
}
__ASSERT((res == FR_OK), "FS init failed (%d)", translate_error(res));
return translate_error(res);
}
SYS_INIT(fs_init, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);