* fs/fatfs/fatfs_vfs.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you 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.
*
****************************************************************************/
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <time.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <debug.h>
#include <assert.h>
#include <nuttx/crc16.h>
#include <nuttx/nuttx.h>
#include <nuttx/fs/fs.h>
#include <nuttx/fs/ioctl.h>
#include <nuttx/kmalloc.h>
#include <nuttx/mutex.h>
#include <nuttx/reboot_notifier.h>
#include <sys/stat.h>
#include <sys/statfs.h>
#include "inode/inode.h"
#include "fs_heap.h"
#include "ff.h"
#include "diskio.h"
* Pre-processor Definitions
****************************************************************************/
#define FATFS_BytesPerSectorShift_FIELD 108
* Private Types
****************************************************************************/
struct fatfs_dir_s
{
struct fs_dirent_s base;
DIR dir;
};
struct fatfs_file_s
{
char path[PATH_MAX + 3];
FIL f;
};
struct fatfs_mountpt_s
{
FATFS fat;
BYTE pdrv;
mutex_t lock;
struct notifier_block nb;
};
struct fatfs_driver_s
{
FAR struct inode *drv;
int ratio;
};
* Private Function Prototypes
****************************************************************************/
static int fatfs_open(FAR struct file *filep, FAR const char *relpath,
int oflags, mode_t mode);
static int fatfs_close(FAR struct file *filep);
static ssize_t fatfs_read(FAR struct file *filep, FAR char *buffer,
size_t buflen);
static ssize_t fatfs_write(FAR struct file *filep, FAR const char *buffer,
size_t buflen);
static off_t fatfs_seek(FAR struct file *filep, off_t offset, int whence);
static int fatfs_ioctl(FAR struct file *filep, int cmd,
unsigned long arg);
static int fatfs_sync(FAR struct file *filep);
static int fatfs_dup(FAR const struct file *oldp, FAR struct file *newp);
static int fatfs_fstat(FAR const struct file *filep,
FAR struct stat *buf);
static int fatfs_fchstat(FAR const struct file *filep,
FAR const struct stat *buf, int flags);
static int fatfs_truncate(FAR struct file *filep, off_t length);
static int fatfs_opendir(FAR struct inode *mountpt,
FAR const char *relpath,
FAR struct fs_dirent_s **dir);
static int fatfs_closedir(FAR struct inode *mountpt,
FAR struct fs_dirent_s *dir);
static int fatfs_readdir(FAR struct inode *mountpt,
FAR struct fs_dirent_s *dir,
FAR struct dirent *entry);
static int fatfs_rewinddir(FAR struct inode *mountpt,
FAR struct fs_dirent_s *dir);
static int fatfs_bind(FAR struct inode *driver,
FAR const void *data, FAR void **handle);
static int fatfs_unbind(FAR void *handle, FAR struct inode **driver,
unsigned int flags);
static int fatfs_statfs(FAR struct inode *mountpt,
FAR struct statfs *buf);
static int fatfs_unlink(FAR struct inode *mountpt,
FAR const char *relpath);
static int fatfs_mkdir(FAR struct inode *mountpt,
FAR const char *relpath, mode_t mode);
static int fatfs_rmdir(FAR struct inode *mountpt,
FAR const char *relpath);
static int fatfs_rename(FAR struct inode *mountpt,
FAR const char *oldrelpath,
FAR const char *newrelpath);
static int fatfs_stat(FAR struct inode *mountpt,
FAR const char *relpath, FAR struct stat *buf);
static int fatfs_chstat(FAR struct inode *mountpt,
FAR const char *relpath,
FAR const struct stat *buf, int flags);
* Private Data
****************************************************************************/
static mutex_t g_lock = NXMUTEX_INITIALIZER;
static struct fatfs_driver_s g_drv[FF_VOLUMES];
* Public Data
****************************************************************************/
* We use the old-fashioned kind of initializers so that this will compile
* with any compiler.
*/
const struct mountpt_operations g_fatfs_operations =
{
fatfs_open,
fatfs_close,
fatfs_read,
fatfs_write,
fatfs_seek,
fatfs_ioctl,
NULL,
fatfs_truncate,
NULL,
NULL,
NULL,
fatfs_sync,
fatfs_dup,
fatfs_fstat,
fatfs_fchstat,
fatfs_opendir,
fatfs_closedir,
fatfs_readdir,
fatfs_rewinddir,
fatfs_bind,
fatfs_unbind,
fatfs_statfs,
fatfs_unlink,
fatfs_mkdir,
fatfs_rmdir,
fatfs_rename,
fatfs_stat,
fatfs_chstat
};
* Private Functions
****************************************************************************/
static BYTE fatfs_alloc_slot(FAR struct inode *drv)
{
BYTE i;
if (nxmutex_lock(&g_lock) < 0)
{
return UCHAR_MAX;
}
for (i = 0; i < FF_VOLUMES; i++)
{
if (g_drv[i].drv == NULL)
{
g_drv[i].drv = drv;
g_drv[i].ratio = 1;
nxmutex_unlock(&g_lock);
return i;
}
}
nxmutex_unlock(&g_lock);
return UCHAR_MAX;
}
static FAR struct inode *fatfs_free_slot(BYTE pdrv)
{
FAR struct inode *drv;
if (nxmutex_lock(&g_lock) < 0)
{
return NULL;
}
drv = g_drv[pdrv].drv;
g_drv[pdrv].drv = NULL;
nxmutex_unlock(&g_lock);
return drv;
}
static int fatfs_convert_result(FRESULT ret)
{
if (ret == FR_OK)
{
return 0;
}
if (ret != FR_NO_FILE && ret != FR_NO_PATH && ret != FR_EXIST)
{
ferr("ERROR: fatfs_convert_result %p return %d\n", return_address(0),
ret);
}
switch (ret)
{
case FR_DISK_ERR:
case FR_NOT_READY:
case FR_MKFS_ABORTED:
return -EIO;
case FR_NO_FILE:
case FR_NO_PATH:
return -ENOENT;
case FR_INVALID_NAME:
case FR_INVALID_OBJECT:
case FR_INVALID_DRIVE:
case FR_NOT_ENABLED:
case FR_NO_FILESYSTEM:
case FR_INVALID_PARAMETER:
return -EINVAL;
case FR_DENIED:
case FR_WRITE_PROTECTED:
case FR_LOCKED:
return -EPERM;
case FR_EXIST:
return -EEXIST;
case FR_TIMEOUT:
return -ETIMEDOUT;
case FR_NOT_ENOUGH_CORE:
return -ENOMEM;
case FR_TOO_MANY_OPEN_FILES:
return -ENMFILE;
case FR_INT_ERR:
default:
return -EFAULT;
}
}
static int fatfs_convert_oflags(int oflags)
{
int ret = 0;
if ((oflags & O_RDONLY) != 0)
{
ret |= FA_READ;
}
if ((oflags & O_WRONLY) != 0)
{
ret |= FA_WRITE;
}
if ((oflags & O_CREAT) != 0)
{
ret |= FA_OPEN_ALWAYS;
}
if ((oflags & O_APPEND) != 0)
{
ret |= FA_OPEN_APPEND;
}
if ((oflags & O_TRUNC) != 0)
{
ret |= FA_CREATE_ALWAYS;
}
return ret;
}
static int fatfs_reboot_notifier(FAR struct notifier_block *notify,
unsigned long action, FAR void *data)
{
FAR struct fatfs_mountpt_s *fs = container_of(notify,
struct fatfs_mountpt_s, nb);
char path[3];
int ret = 0;
path[0] = '0' + fs->pdrv;
path[1] = ':';
path[2] = '\0';
if (action == SYS_HALT)
{
return ret;
}
nxmutex_lock(&fs->lock);
ret = fatfs_convert_result(f_unmount(path));
nxmutex_unlock(&fs->lock);
return ret;
}
* Name: fatfs_open
****************************************************************************/
static int fatfs_open(FAR struct file *filep, FAR const char *relpath,
int oflags, mode_t mode)
{
FAR struct fatfs_mountpt_s *fs;
FAR struct fatfs_file_s *fp;
int ret;
fs = filep->f_inode->i_private;
fp = fs_heap_malloc(sizeof(*fp) - FF_MAX_SS + SS(&fs->fat));
if (!fp)
{
return -ENOMEM;
}
ret = nxmutex_lock(&fs->lock);
if (ret < 0)
{
fs_heap_free(fp);
return ret;
}
fp->path[0] = '0' + fs->pdrv;
fp->path[1] = ':';
fp->path[2] = '\0';
oflags = fatfs_convert_oflags(oflags);
strlcat(fp->path, relpath, sizeof(fp->path));
ret = fatfs_convert_result(f_open(&fp->f, fp->path, oflags));
if (ret < 0)
{
fs_heap_free(fp);
goto errsem;
}
if (filep->f_oflags & O_APPEND)
{
filep->f_pos = f_size(&fp->f);
}
filep->f_priv = fp;
errsem:
nxmutex_unlock(&fs->lock);
return ret;
}
* Name: fatfs_close
****************************************************************************/
static int fatfs_close(FAR struct file *filep)
{
FAR struct fatfs_mountpt_s *fs;
FAR struct fatfs_file_s *fp;
int ret;
fp = filep->f_priv;
fs = filep->f_inode->i_private;
ret = nxmutex_lock(&fs->lock);
if (ret < 0)
{
return ret;
}
ret = fatfs_convert_result(f_close(&fp->f));
nxmutex_unlock(&fs->lock);
if (ret >= 0)
{
fs_heap_free(fp);
}
return ret;
}
* Name: fatfs_read
****************************************************************************/
static ssize_t fatfs_read(FAR struct file *filep, FAR char *buffer,
size_t buflen)
{
FAR struct fatfs_mountpt_s *fs;
FAR struct fatfs_file_s *fp;
ssize_t ret;
UINT size;
fp = filep->f_priv;
fs = filep->f_inode->i_private;
ret = nxmutex_lock(&fs->lock);
if (ret < 0)
{
return ret;
}
if (filep->f_pos != f_tell(&fp->f))
{
ret = fatfs_convert_result(f_lseek(&fp->f, filep->f_pos));
if (ret < 0)
{
goto errout_with_sem;
}
}
ret = fatfs_convert_result(f_read(&fp->f, buffer, buflen, &size));
if (ret >= 0)
{
filep->f_pos += size;
ret = size;
}
errout_with_sem:
nxmutex_unlock(&fs->lock);
return ret;
}
* Name: fatfs_write
****************************************************************************/
static ssize_t fatfs_write(FAR struct file *filep, FAR const char *buffer,
size_t buflen)
{
FAR struct fatfs_mountpt_s *fs;
FAR struct fatfs_file_s *fp;
ssize_t ret;
UINT size;
fp = filep->f_priv;
fs = filep->f_inode->i_private;
if (filep->f_pos > f_size(&fp->f))
{
ret = fatfs_truncate(filep, filep->f_pos);
if (ret < 0)
{
return ret;
}
}
ret = nxmutex_lock(&fs->lock);
if (ret < 0)
{
return ret;
}
if (filep->f_pos != f_tell(&fp->f))
{
ret = fatfs_convert_result(f_lseek(&fp->f, filep->f_pos));
if (ret < 0)
{
goto errout_with_sem;
}
}
if ((filep->f_oflags & O_APPEND) && f_tell(&fp->f) < f_size(&fp->f))
{
ret = fatfs_convert_result(f_lseek(&fp->f, f_size(&fp->f)));
if (ret < 0)
{
goto errout_with_sem;
}
filep->f_pos = f_size(&fp->f);
}
ret = fatfs_convert_result(f_write(&fp->f, buffer, buflen, &size));
if (ret >= 0)
{
filep->f_pos += size;
ret = size;
}
errout_with_sem:
nxmutex_unlock(&fs->lock);
return ret;
}
* Name: fatfs_seek
****************************************************************************/
static off_t fatfs_seek(FAR struct file *filep, off_t offset, int whence)
{
FAR struct fatfs_mountpt_s *fs;
FAR struct fatfs_file_s *fp;
off_t ret;
fp = filep->f_priv;
fs = filep->f_inode->i_private;
ret = nxmutex_lock(&fs->lock);
if (ret < 0)
{
return ret;
}
switch (whence)
{
case SEEK_SET:
break;
case SEEK_CUR:
offset += filep->f_pos;
break;
case SEEK_END:
offset += f_size(&fp->f);
break;
default:
nxmutex_unlock(&fs->lock);
return -EINVAL;
}
if (offset >= 0)
{
filep->f_pos = offset;
}
else
{
ret = -EINVAL;
}
nxmutex_unlock(&fs->lock);
return ret < 0 ? ret : offset;
}
* Name: fatfs_ioctl
****************************************************************************/
static int fatfs_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
{
FAR struct fatfs_mountpt_s *fs;
FAR struct fatfs_file_s *fp;
int ret;
fp = filep->f_priv;
fs = filep->f_inode->i_private;
ret = nxmutex_lock(&fs->lock);
if (ret < 0)
{
return ret;
}
if (cmd == FIOC_FILEPATH)
{
FAR char *ptr = (FAR char *)((uintptr_t)arg);
inode_getpath(filep->f_inode, ptr, PATH_MAX);
strcat(ptr, &fp->path[2]);
}
else
{
FAR struct inode *drv = g_drv[fs->pdrv].drv;
if (drv->u.i_bops->ioctl)
{
ret = drv->u.i_bops->ioctl(drv, cmd, arg);
}
else
{
ret = -ENOTTY;
}
}
nxmutex_unlock(&fs->lock);
return ret;
}
* Name: fatfs_sync
*
* Description: Synchronize the file state on disk to match internal, in-
* memory state.
*
****************************************************************************/
static int fatfs_sync(FAR struct file *filep)
{
FAR struct fatfs_mountpt_s *fs;
FAR struct fatfs_file_s *fp;
int ret;
fp = filep->f_priv;
fs = filep->f_inode->i_private;
ret = nxmutex_lock(&fs->lock);
if (ret < 0)
{
return ret;
}
ret = fatfs_convert_result(f_sync(&fp->f));
nxmutex_unlock(&fs->lock);
return ret;
}
* Name: fatfs_dup
*
* Description: Duplicate open file data in the new file structure.
* Deep copy implementation for file cache optimization.
*
****************************************************************************/
static int fatfs_dup(FAR const struct file *oldp, FAR struct file *newp)
{
FAR struct fatfs_mountpt_s *fs;
FAR struct fatfs_file_s *oldfp;
FAR struct fatfs_file_s *newfp;
size_t alloc_size;
int oflags;
int ret;
oldfp = oldp->f_priv;
fs = oldp->f_inode->i_private;
alloc_size = sizeof(*newfp) - FF_MAX_SS + SS(&fs->fat);
newfp = fs_heap_malloc(alloc_size);
if (newfp == NULL)
{
return -ENOMEM;
}
ret = nxmutex_lock(&fs->lock);
if (ret < 0)
{
fs_heap_free(newfp);
return ret;
}
memcpy(newfp, oldfp, alloc_size);
oflags = fatfs_convert_oflags(newp->f_oflags);
newfp->f.flag = oflags;
newp->f_priv = newfp;
nxmutex_unlock(&fs->lock);
return OK;
}
* Name: fatfs_stat_i
****************************************************************************/
static int fatfs_stat_i(FAR struct fatfs_mountpt_s *fs,
FAR const char *path, FAR struct stat *buf)
{
struct tm gm;
FILINFO fno;
int ret;
memset(buf, 0, sizeof(struct stat));
buf->st_mode |= S_IRWXO | S_IRWXG | S_IRWXU;
if (path[2] == '\0')
{
buf->st_mode |= S_IFDIR;
return OK;
}
ret = fatfs_convert_result(f_stat(path, &fno));
if (ret < 0)
{
return ret;
}
if (fno.fattrib & AM_DIR)
{
buf->st_mode |= S_IFDIR;
}
else
{
buf->st_mode |= S_IFREG;
}
buf->st_size = fno.fsize;
memset(&gm, 0, sizeof(gm));
gm.tm_year = 80 + (fno.fdate >> 9);
gm.tm_mon = ((fno.fdate >> 5) & 0xf) - 1;
gm.tm_mday = fno.fdate & 0x1f;
gm.tm_hour = fno.ftime >> 11;
gm.tm_min = (fno.ftime >> 5) & 0x3f;
gm.tm_sec = (fno.ftime & 0x1f) << 1;
buf->st_mtime = timegm(&gm);
buf->st_atime = buf->st_mtime;
buf->st_ctime = buf->st_mtime;
buf->st_blksize = SS(&fs->fat);
buf->st_blocks = (buf->st_size + buf->st_blksize - 1) /
buf->st_blksize;
return ret;
}
* Name: fatfs_fstat
*
* Description:
* Obtain information about an open file associated with the file
* descriptor 'fd', and will write it to the area pointed to by 'buf'.
*
****************************************************************************/
static int fatfs_fstat(FAR const struct file *filep, FAR struct stat *buf)
{
FAR struct fatfs_mountpt_s *fs;
FAR struct fatfs_file_s *fp;
int ret;
fp = filep->f_priv;
fs = filep->f_inode->i_private;
ret = nxmutex_lock(&fs->lock);
if (ret < 0)
{
return ret;
}
ret = fatfs_convert_result(f_sync(&fp->f));
if (ret < 0)
{
goto errsem;
}
ret = fatfs_stat_i(fs, fp->path, buf);
errsem:
nxmutex_unlock(&fs->lock);
return ret;
}
* Name: fatfs_chstat_i
****************************************************************************/
static int fatfs_chstat_i(FAR const char *path,
FAR const struct stat *buf, int flags)
{
if (flags & CH_STAT_MODE)
{
const mode_t VALID_MODE_MASK = S_IFREG | S_IFDIR |
S_IRWXU | S_IRWXG | S_IRWXO;
if (buf->st_mode & ~VALID_MODE_MASK)
{
return -EPERM;
}
}
if ((flags & CH_STAT_GID) || (flags & CH_STAT_UID))
{
return -EPERM;
}
if (flags & CH_STAT_MTIME)
{
struct tm gm;
FILINFO fno;
gmtime_r(&buf->st_mtime, &gm);
fno.fdate = ((gm.tm_year - 80) << 9) | ((gm.tm_mon + 1) << 5) |
gm.tm_mday;
fno.ftime = (gm.tm_hour << 11) | (gm.tm_min << 5) | (gm.tm_sec >> 1);
return fatfs_convert_result(f_utime(path, &fno));
}
return OK;
}
* Name: fatfs_fchstat
****************************************************************************/
static int fatfs_fchstat(FAR const struct file *filep,
FAR const struct stat *buf, int flags)
{
FAR struct fatfs_mountpt_s *fs;
FAR struct fatfs_file_s *fp;
int ret;
fp = filep->f_priv;
fs = filep->f_inode->i_private;
ret = nxmutex_lock(&fs->lock);
if (ret < 0)
{
return ret;
}
ret = fatfs_chstat_i(fp->path, buf, flags);
nxmutex_unlock(&fs->lock);
return ret;
}
* Name: fatfs_truncate
*
* Description:
* Set the length of the open, regular file associated with the file
* structure 'filep' to 'length'.
*
****************************************************************************/
static int fatfs_truncate(FAR struct file *filep, off_t length)
{
FAR struct fatfs_mountpt_s *fs;
FAR struct fatfs_file_s *fp;
FAR char *buffer = NULL;
int ret;
fp = filep->f_priv;
fs = filep->f_inode->i_private;
ret = nxmutex_lock(&fs->lock);
if (ret < 0)
{
return ret;
}
if (length < f_size(&fp->f))
{
ret = fatfs_convert_result(f_lseek(&fp->f, length));
if (ret < 0)
{
goto errsem;
}
ret = fatfs_convert_result(f_truncate(&fp->f));
}
else if (length > f_size(&fp->f))
{
ret = fatfs_convert_result(f_lseek(&fp->f, f_size(&fp->f)));
if (ret < 0)
{
goto errsem;
}
length -= f_size(&fp->f);
buffer = lib_get_tempbuffer(SS(&fs->fat));
if (buffer == NULL)
{
goto errbuf;
}
memset(buffer, 0, SS(&fs->fat));
while (length > 0)
{
UINT size;
if (length >= SS(&fs->fat))
{
ret = fatfs_convert_result(f_write(&fp->f, buffer,
SS(&fs->fat), &size));
}
else
{
ret = fatfs_convert_result(f_write(&fp->f, buffer,
length, &size));
}
if (ret < 0)
{
goto errbuf;
}
length -= size;
}
ret = fatfs_convert_result(f_truncate(&fp->f));
}
errbuf:
lib_put_tempbuffer(buffer);
errsem:
nxmutex_unlock(&fs->lock);
return ret;
}
* Name: fatfs_opendir
*
* Description: Open a directory for read access
*
****************************************************************************/
static int fatfs_opendir(FAR struct inode *mountpt,
FAR const char *relpath,
FAR struct fs_dirent_s **dir)
{
FAR struct fatfs_mountpt_s *fs;
char path[strlen(relpath) + 3];
FAR struct fatfs_dir_s *fdir;
int ret;
fs = mountpt->i_private;
fdir = fs_heap_malloc(sizeof(*fdir));
if (fdir == NULL)
{
return -ENOMEM;
}
ret = nxmutex_lock(&fs->lock);
if (ret < 0)
{
fs_heap_free(fdir);
return ret;
}
path[0] = '0' + fs->pdrv;
path[1] = ':';
path[2] = '\0';
ret = fatfs_convert_result(f_opendir(&fdir->dir, strcat(path, relpath)));
if (ret >= 0)
{
*dir = &fdir->base;
}
nxmutex_unlock(&fs->lock);
if (ret < 0)
{
fs_heap_free(fdir);
}
return ret;
}
* Name: fatfs_closedir
*
* Description: Close a directory
*
****************************************************************************/
static int fatfs_closedir(FAR struct inode *mountpt,
FAR struct fs_dirent_s *dir)
{
FAR struct fatfs_mountpt_s *fs;
FAR struct fatfs_dir_s *fdir;
int ret;
fs = mountpt->i_private;
fdir = (FAR struct fatfs_dir_s *)dir;
ret = nxmutex_lock(&fs->lock);
if (ret < 0)
{
return ret;
}
ret = fatfs_convert_result(f_closedir(&fdir->dir));
nxmutex_unlock(&fs->lock);
if (ret >= 0)
{
fs_heap_free(fdir);
}
return ret;
}
* Name: fatfs_readdir
*
* Description: Read the next directory entry
*
****************************************************************************/
static int fatfs_readdir(FAR struct inode *mountpt,
FAR struct fs_dirent_s *dir,
FAR struct dirent *entry)
{
FAR struct fatfs_mountpt_s *fs;
FAR struct fatfs_dir_s *fdir;
FILINFO fno;
int ret;
fdir = (FAR struct fatfs_dir_s *)dir;
fs = mountpt->i_private;
ret = nxmutex_lock(&fs->lock);
if (ret < 0)
{
return ret;
}
ret = fatfs_convert_result(f_readdir(&fdir->dir, &fno));
if (ret < 0)
{
goto errsem;
}
if (!fno.fname[0])
{
ret = -ENOENT;
goto errsem;
}
if (fno.fattrib & AM_DIR)
{
entry->d_type = DTYPE_DIRECTORY;
}
else
{
entry->d_type = DTYPE_FILE;
}
strlcpy(entry->d_name, fno.fname, sizeof(entry->d_name));
errsem:
nxmutex_unlock(&fs->lock);
return ret;
}
* Name: fatfs_rewindir
*
* Description: Reset directory read to the first entry
*
****************************************************************************/
static int fatfs_rewinddir(FAR struct inode *mountpt,
FAR struct fs_dirent_s *dir)
{
FAR struct fatfs_mountpt_s *fs;
FAR struct fatfs_dir_s *fdir;
int ret;
fdir = (FAR struct fatfs_dir_s *)dir;
fs = mountpt->i_private;
ret = nxmutex_lock(&fs->lock);
if (ret < 0)
{
return ret;
}
ret = fatfs_convert_result(f_rewinddir(&fdir->dir));
nxmutex_unlock(&fs->lock);
return ret;
}
* Name: fatfs_bind
****************************************************************************/
static bool fatfs_match_option(FAR const char *options,
FAR const char *option_name)
{
FAR const char *p;
size_t length = strlen(option_name);
for (p = strstr(options, option_name); p; p = strstr(p + 1, option_name))
{
if ((p == options || *(p - 1) == ',') &&
(p[length] == ',' || p[length] == '\0'))
{
return true;
}
}
return false;
}
static int fatfs_bind(FAR struct inode *driver, FAR const void *data,
FAR void **handle)
{
FAR struct fatfs_mountpt_s *fs;
FAR unsigned char *sector;
struct geometry geo;
MKFS_PARM opt;
char path[3];
int ret;
fs = fs_heap_zalloc(sizeof(*fs));
if (!fs)
{
return -ENOMEM;
}
* responsible for one reference on the driver inode and does not
* have to addref() here (but does have to release in unbind().
*/
fs->pdrv = fatfs_alloc_slot(driver);
if (fs->pdrv == UCHAR_MAX)
{
ret = -ENMFILE;
goto errout_with_fs;
}
path[0] = '0' + fs->pdrv;
path[1] = ':';
path[2] = '\0';
if (driver->u.i_bops->open)
{
ret = driver->u.i_bops->open(driver);
if (ret < 0)
{
ferr("ERROR: %d driver open failed\n", fs->pdrv);
goto errout_with_pdrv;
}
ret = driver->u.i_bops->geometry(driver, &geo);
if (ret < 0)
{
ferr("Geometry failed: %d\n", ret);
goto errout_with_open;
}
sector = lib_get_tempbuffer(geo.geo_sectorsize);
if (sector == NULL)
{
ret = -ENOMEM;
goto errout_with_open;
}
ret = driver->u.i_bops->read(driver, sector, 0, 1);
if (ret < 0)
{
ferr("Read failed: %d\n", ret);
lib_put_tempbuffer(sector);
goto errout_with_open;
}
g_drv[fs->pdrv].ratio = (1 <<
sector[FATFS_BytesPerSectorShift_FIELD]) /
geo.geo_sectorsize;
if (g_drv[fs->pdrv].ratio != 1 &&
g_drv[fs->pdrv].ratio != CONFIG_FS_FATFS_SECTOR_RATIO)
{
g_drv[fs->pdrv].ratio = CONFIG_FS_FATFS_SECTOR_RATIO;
}
lib_put_tempbuffer(sector);
}
nxmutex_init(&fs->lock);
if (data && fatfs_match_option(data, "forceformat"))
{
memset(&opt, 0, sizeof(opt));
opt.fmt = FM_EXFAT | FM_SFD;
opt.n_fat = 2;
g_drv[fs->pdrv].ratio = CONFIG_FS_FATFS_SECTOR_RATIO;
ret = fatfs_convert_result(f_mkfs(path, &opt, NULL, FF_MAX_SS));
if (ret < 0)
{
if (ret == -EIO && CONFIG_FS_FATFS_SECTOR_RATIO != 1)
{
g_drv[fs->pdrv].ratio = 1;
ret = fatfs_convert_result(f_mkfs(path, &opt,
NULL, FF_MAX_SS));
}
if (ret < 0)
{
goto errout_with_open;
}
}
}
ret = fatfs_convert_result(f_mount(&fs->fat, path, 1));
if (ret < 0)
{
if ((ret != -EIO && ret != -EINVAL) ||
!data || !fatfs_match_option(data, "autoformat"))
{
goto errout_with_open;
}
g_drv[fs->pdrv].ratio = CONFIG_FS_FATFS_SECTOR_RATIO;
memset(&opt, 0, sizeof(opt));
opt.fmt = FM_EXFAT | FM_SFD;
opt.n_fat = 2;
ret = fatfs_convert_result(f_mkfs(path, &opt, NULL, FF_MAX_SS));
if (ret < 0)
{
if (ret == -EIO && CONFIG_FS_FATFS_SECTOR_RATIO != 1)
{
g_drv[fs->pdrv].ratio = 1;
ret = fatfs_convert_result(f_mkfs(path, &opt,
NULL, FF_MAX_SS));
}
if (ret < 0)
{
goto errout_with_open;
}
}
ret = fatfs_convert_result(f_mount(&fs->fat, path, 1));
if (ret < 0)
{
goto errout_with_open;
}
}
fs->nb.notifier_call = fatfs_reboot_notifier;
register_reboot_notifier(&fs->nb);
*handle = fs;
return OK;
errout_with_open:
f_unmount(path);
nxmutex_destroy(&fs->lock);
if (driver->u.i_bops->close)
{
driver->u.i_bops->close(driver);
}
errout_with_pdrv:
fatfs_free_slot(fs->pdrv);
errout_with_fs:
fs_heap_free(fs);
return ret;
}
* Name: fatfs_unbind
*
* Description: This implements the filesystem portion of the umount
* operation.
*
****************************************************************************/
static int fatfs_unbind(FAR void *handle, FAR struct inode **driver,
unsigned int flags)
{
FAR struct fatfs_mountpt_s *fs = handle;
char path[3];
int ret;
path[0] = '0' + fs->pdrv;
path[1] = ':';
path[2] = '\0';
ret = fatfs_convert_result(f_unmount(path));
* mucking with inodes in this context. So, we will just return
* our contained reference to the driver inode and let the
* umount logic dispose of it.
*/
*driver = fatfs_free_slot(fs->pdrv);
if ((*driver)->u.i_bops->close)
{
(*driver)->u.i_bops->close(*driver);
}
unregister_reboot_notifier(&fs->nb);
nxmutex_destroy(&fs->lock);
fs_heap_free(fs);
return ret;
}
* Name: fatfs_statfs
*
* Description: Return filesystem statistics
*
****************************************************************************/
static int fatfs_statfs(FAR struct inode *mountpt, FAR struct statfs *buf)
{
FAR struct fatfs_mountpt_s *fs;
FAR FATFS *fat;
DWORD nclst;
char path[3];
int ret;
fs = mountpt->i_private;
ret = nxmutex_lock(&fs->lock);
if (ret < 0)
{
return ret;
}
path[0] = '0' + fs->pdrv;
path[1] = ':';
path[2] = '\0';
ret = fatfs_convert_result(f_getfree(path, &nclst, &fat));
if (ret < 0)
{
goto errsem;
}
buf->f_type = FATFS_SUPER_MAGIC;
buf->f_namelen = FF_MAX_LFN;
buf->f_bsize = fat->csize * SS(fat);
buf->f_blocks = fat->n_fatent - 2;
buf->f_bfree = nclst;
buf->f_bavail = nclst;
errsem:
nxmutex_unlock(&fs->lock);
return ret;
}
* Name: fatfs_unlink
*
* Description: Remove a file
*
****************************************************************************/
static int fatfs_unlink(FAR struct inode *mountpt, FAR const char *relpath)
{
FAR struct fatfs_mountpt_s *fs;
char path[strlen(relpath) + 3];
int ret;
fs = mountpt->i_private;
ret = nxmutex_lock(&fs->lock);
if (ret < 0)
{
return ret;
}
path[0] = '0' + fs->pdrv;
path[1] = ':';
path[2] = '\0';
ret = fatfs_convert_result(f_unlink(strcat(path, relpath)));
nxmutex_unlock(&fs->lock);
return ret;
}
* Name: fatfs_mkdir
*
* Description: Create a directory
*
****************************************************************************/
static int fatfs_mkdir(FAR struct inode *mountpt, FAR const char *relpath,
mode_t mode)
{
FAR struct fatfs_mountpt_s *fs;
char path[strlen(relpath) + 3];
int ret;
fs = mountpt->i_private;
ret = nxmutex_lock(&fs->lock);
if (ret < 0)
{
return ret;
}
path[0] = '0' + fs->pdrv;
path[1] = ':';
path[2] = '\0';
ret = fatfs_convert_result(f_mkdir(strcat(path, relpath)));
nxmutex_unlock(&fs->lock);
return ret;
}
* Name: fatfs_rmdir
*
* Description: Remove a directory
*
****************************************************************************/
static int fatfs_rmdir(FAR struct inode *mountpt, FAR const char *relpath)
{
FAR struct fatfs_mountpt_s *fs;
char path[strlen(relpath) + 3];
FILINFO fno;
int ret;
fs = mountpt->i_private;
ret = nxmutex_lock(&fs->lock);
if (ret < 0)
{
return ret;
}
path[0] = '0' + fs->pdrv;
path[1] = ':';
path[2] = '\0';
ret = fatfs_convert_result(f_stat(strcat(path, relpath), &fno));
if (ret < 0)
{
goto errout_with_sem;
}
if (fno.fattrib & AM_DIR)
{
ret = fatfs_convert_result(f_rmdir(path));
}
else
{
ret = -ENOTDIR;
}
errout_with_sem:
nxmutex_unlock(&fs->lock);
return ret;
}
* Name: fatfs_rename
*
* Description: Rename a file or directory
*
****************************************************************************/
static int fatfs_rename(FAR struct inode *mountpt,
FAR const char *oldrelpath,
FAR const char *newrelpath)
{
FAR struct fatfs_mountpt_s *fs;
char oldpath[strlen(oldrelpath) + 3];
char newpath[strlen(newrelpath) + 3];
int ret;
fs = mountpt->i_private;
ret = nxmutex_lock(&fs->lock);
if (ret < 0)
{
return ret;
}
oldpath[0] = '0' + fs->pdrv;
oldpath[1] = ':';
oldpath[2] = '\0';
newpath[0] = '0' + fs->pdrv;
newpath[1] = ':';
newpath[2] = '\0';
ret = fatfs_convert_result(f_rename(strcat(oldpath, oldrelpath),
strcat(newpath, newrelpath)));
nxmutex_unlock(&fs->lock);
return ret;
}
* Name: fatfs_stat
*
* Description: Return information about a file or directory
*
****************************************************************************/
static int fatfs_stat(FAR struct inode *mountpt, FAR const char *relpath,
FAR struct stat *buf)
{
FAR struct fatfs_mountpt_s *fs;
char path[strlen(relpath) + 3];
int ret;
fs = mountpt->i_private;
ret = nxmutex_lock(&fs->lock);
if (ret < 0)
{
return ret;
}
path[0] = '0' + fs->pdrv;
path[1] = ':';
path[2] = '\0';
ret = fatfs_stat_i(fs, strcat(path, relpath), buf);
nxmutex_unlock(&fs->lock);
return ret;
}
* Name: fatfs_chstat
****************************************************************************/
static int fatfs_chstat(FAR struct inode *mountpt, FAR const char *relpath,
FAR const struct stat *buf, int flags)
{
FAR struct fatfs_mountpt_s *fs;
char path[strlen(relpath) + 3];
int ret;
fs = mountpt->i_private;
ret = nxmutex_lock(&fs->lock);
if (ret < 0)
{
return ret;
}
path[0] = '0' + fs->pdrv;
path[1] = ':';
path[2] = '\0';
ret = fatfs_chstat_i(strcat(path, relpath), buf, flags);
nxmutex_unlock(&fs->lock);
return ret;
}
* Name: disk_check
****************************************************************************/
#ifdef CONFIG_FS_FATFS_DEBUG
static void disk_check(FAR struct inode *drv, LBA_t sector, UINT count,
FAR const BYTE *buff)
{
uint16_t crc16_check;
struct geometry geo;
uint16_t crc16_ori;
ssize_t size;
int ret;
ret = drv->u.i_bops->geometry(drv, &geo);
if (ret < 0)
{
ferr("Geometry failed: %d\n", ret);
return;
}
crc16_ori = crc16(buff, geo.geo_sectorsize * count);
size = drv->u.i_bops->read(drv, (FAR BYTE *)buff, sector, count);
if (size != count)
{
ferr("Read failed: %zd\n", size);
lib_dumpbuffer("buff:", buff, geo.geo_sectorsize * count);
DEBUGASSERT(0);
}
else
{
crc16_check = crc16(buff, geo.geo_sectorsize * count);
if (crc16_ori != crc16_check)
{
ferr("CRC16 check failed\n");
lib_dumpbuffer("check:", buff, geo.geo_sectorsize * count);
DEBUGASSERT(0);
}
}
}
#endif
* Name: disk_status
*
* Description:
* Get drive status.
*
* Input Parameters:
* pdrv - Physical drive nmuber to identify the drive.
*
* Returned Value:
* The status of disk is returned.
*
****************************************************************************/
DSTATUS disk_status(BYTE pdrv)
{
DEBUGASSERT(pdrv < FF_VOLUMES);
if (g_drv[pdrv].drv)
{
return OK;
}
return STA_NOINIT;
}
* Name: disk_initialize
*
* Description:
* Initialize a drive
*
* Input Parameters:
* pdrv - Physical drive nmuber to identify the drive.
*
* Returned Value:
* The status of disk is returned.
*
****************************************************************************/
DSTATUS disk_initialize(BYTE pdrv)
{
return disk_status(pdrv);
}
* Name: disk_read
*
* Description:
* Read Sector(s)
*
* Input Parameters:
* pdrv - Physical drive nmuber to identify the drive.
* buff - Data buffer to store read data.
* sector - Start sector in LBA
* count - Number of sectors to read
*
* Returned Value:
* RES_OK if the read was successfully; A other value (REX_*) is returned
* on any failure.
*
****************************************************************************/
DRESULT disk_read(BYTE pdrv, BYTE *buff, LBA_t sector, UINT count)
{
FAR struct inode *drv;
ssize_t size;
DEBUGASSERT(pdrv < FF_VOLUMES);
drv = g_drv[pdrv].drv;
count *= g_drv[pdrv].ratio;
size = drv->u.i_bops->read(drv, buff, sector * g_drv[pdrv].ratio, count);
if (size != count)
{
ferr("Read failed: %zd\n", size);
return RES_ERROR;
}
#ifdef CONFIG_FS_FATFS_DEBUG
disk_check(drv, sector * g_drv[pdrv].ratio, count, buff);
#endif
return RES_OK;
}
* Name: disk_write
*
* Description:
* Write Sector(s)
*
* Input Parameters:
* pdrv - Physical drive nmuber to identify the drive.
* buff - Data to be written
* sector - Start sector in LBA
* count - Number of sectors to write
*
* Returned Value:
* RES_OK if the write was successfully; A other value (REX_*) is returned
* on any failure.
*
****************************************************************************/
DRESULT disk_write(BYTE pdrv, const BYTE *buff, LBA_t sector, UINT count)
{
FAR struct inode *drv;
ssize_t size;
DEBUGASSERT(pdrv < FF_VOLUMES);
drv = g_drv[pdrv].drv;
count *= g_drv[pdrv].ratio;
size = drv->u.i_bops->write(drv, buff, sector * g_drv[pdrv].ratio, count);
if (size != count)
{
ferr("Write failed: %zd\n", size);
return RES_ERROR;
}
#ifdef CONFIG_FS_FATFS_DEBUG
disk_check(drv, sector * g_drv[pdrv].ratio, count, buff);
#endif
return RES_OK;
}
* Name: disk_ioctl
*
* Description:
* Control disk.
*
* Input Parameters:
* pdrv - Physical drive nmuber to identify the drive.
* cmd - Control code
* buff - Buffer to send/receive control data
* count - Number of sectors to write
*
* Returned Value:
* The status of disk is returned.
*
****************************************************************************/
DRESULT disk_ioctl(BYTE pdrv, BYTE cmd, void *buff)
{
FAR struct inode *drv;
int ret = -ENOTTY;
DEBUGASSERT(pdrv < FF_VOLUMES);
drv = g_drv[pdrv].drv;
switch (cmd)
{
case CTRL_SYNC:
{
if (drv->u.i_bops->ioctl)
{
ret = drv->u.i_bops->ioctl(drv, BIOC_FLUSH, 0);
}
if (ret == -ENOTTY)
{
ret = 0;
}
}
break;
case GET_SECTOR_COUNT:
case GET_SECTOR_SIZE:
case GET_BLOCK_SIZE:
{
struct geometry geo;
ret = drv->u.i_bops->geometry(drv, &geo);
if (ret < 0)
{
ferr("ERROR: geometry failed: %d\n", ret);
break;
}
if (cmd == GET_SECTOR_COUNT)
{
*(FAR LBA_t *)buff = geo.geo_nsectors / g_drv[pdrv].ratio;
}
else if (cmd == GET_SECTOR_SIZE)
{
*(FAR WORD *)buff = geo.geo_sectorsize * g_drv[pdrv].ratio;
}
else if (cmd == GET_BLOCK_SIZE)
{
*(FAR DWORD *)buff = geo.geo_sectorsize * g_drv[pdrv].ratio;
}
}
break;
};
return ret < 0 ? RES_ERROR : RES_OK;
}
#if FF_FS_NORTC == 0
* Name: get_fattime
*
* Description:
* get time
*
****************************************************************************/
DWORD get_fattime(void)
{
struct tm gm;
time_t now;
WORD fdate;
WORD ftime;
now = time(NULL);
gmtime_r(&now, &gm);
fdate = ((gm.tm_year - 80) << 9) | ((gm.tm_mon + 1) << 5) | gm.tm_mday;
ftime = (gm.tm_hour << 11) | (gm.tm_min << 5) | (gm.tm_sec >> 1);
return (fdate << 16) | ftime;
}
#endif
#if FF_USE_LFN == 3
* Name: ff_memalloc
*
* Description:
* Allocate a memory block
*
****************************************************************************/
void *ff_memalloc(UINT msize)
{
return fs_heap_malloc(msize);
}
* Name: ff_memfree
*
* Description:
* Free a memory block
*
****************************************************************************/
void ff_memfree(void *mblock)
{
fs_heap_free(mblock);
}
#endif