nuttx/fs/littlefs/lfs_vfs.c

1490 lines
38 KiB
C
Raw Normal View History

/****************************************************************************
* fs/littlefs/lfs_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 <errno.h>
#include <fcntl.h>
#include <string.h>
#include <nuttx/fs/fs.h>
#include <nuttx/fs/ioctl.h>
#include <nuttx/kmalloc.h>
#include <nuttx/mtd/mtd.h>
#include <nuttx/mutex.h>
#include <sys/stat.h>
#include <sys/statfs.h>
#include "inode/inode.h"
#include "littlefs/lfs.h"
#include "littlefs/lfs_util.h"
#include "fs_heap.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#ifndef CONFIG_C99_BOOL
# error littlefs requires CONFIG_C99_BOOL to be selected
#endif
/****************************************************************************
* Private Types
****************************************************************************/
struct littlefs_dir_s
{
struct fs_dirent_s base;
struct lfs_dir dir;
};
struct littlefs_file_s
{
struct lfs_file file;
int refs;
};
/* This structure represents the overall mountpoint state. An instance of
* this structure is retained as inode private data on each mountpoint that
* is mounted with a littlefs filesystem.
*/
struct littlefs_mountpt_s
{
mutex_t lock;
FAR struct inode *drv;
struct mtd_geometry_s geo;
struct lfs_config cfg;
struct lfs lfs;
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
static int littlefs_open(FAR struct file *filep, FAR const char *relpath,
int oflags, mode_t mode);
static int littlefs_close(FAR struct file *filep);
static ssize_t littlefs_read(FAR struct file *filep, FAR char *buffer,
size_t buflen);
static ssize_t littlefs_write(FAR struct file *filep, FAR const char *buffer,
size_t buflen);
static off_t littlefs_seek(FAR struct file *filep, off_t offset,
int whence);
static int littlefs_ioctl(FAR struct file *filep, int cmd,
unsigned long arg);
static int littlefs_sync(FAR struct file *filep);
static int littlefs_dup(FAR const struct file *oldp,
FAR struct file *newp);
static int littlefs_fstat(FAR const struct file *filep,
FAR struct stat *buf);
static int littlefs_truncate(FAR struct file *filep,
off_t length);
static int littlefs_opendir(FAR struct inode *mountpt,
FAR const char *relpath,
FAR struct fs_dirent_s **dir);
static int littlefs_closedir(FAR struct inode *mountpt,
FAR struct fs_dirent_s *dir);
static int littlefs_readdir(FAR struct inode *mountpt,
FAR struct fs_dirent_s *dir,
FAR struct dirent *entry);
static int littlefs_rewinddir(FAR struct inode *mountpt,
FAR struct fs_dirent_s *dir);
static int littlefs_bind(FAR struct inode *driver,
FAR const void *data, FAR void **handle);
static int littlefs_unbind(FAR void *handle, FAR struct inode **driver,
unsigned int flags);
static int littlefs_statfs(FAR struct inode *mountpt,
FAR struct statfs *buf);
static int littlefs_unlink(FAR struct inode *mountpt,
FAR const char *relpath);
static int littlefs_mkdir(FAR struct inode *mountpt,
FAR const char *relpath, mode_t mode);
static int littlefs_rmdir(FAR struct inode *mountpt,
FAR const char *relpath);
static int littlefs_rename(FAR struct inode *mountpt,
FAR const char *oldrelpath,
FAR const char *newrelpath);
static int littlefs_stat(FAR struct inode *mountpt,
FAR const char *relpath, FAR struct stat *buf);
/****************************************************************************
* Public Data
****************************************************************************/
/* See fs_mount.c -- this structure is explicitly extern'ed there.
* We use the old-fashioned kind of initializers so that this will compile
* with any compiler.
*/
const struct mountpt_operations g_littlefs_operations =
{
littlefs_open, /* open */
littlefs_close, /* close */
littlefs_read, /* read */
littlefs_write, /* write */
littlefs_seek, /* seek */
littlefs_ioctl, /* ioctl */
NULL, /* mmap */
littlefs_truncate, /* truncate */
NULL, /* poll */
littlefs_sync, /* sync */
littlefs_dup, /* dup */
littlefs_fstat, /* fstat */
NULL, /* fchstat */
littlefs_opendir, /* opendir */
littlefs_closedir, /* closedir */
littlefs_readdir, /* readdir */
littlefs_rewinddir, /* rewinddir */
littlefs_bind, /* bind */
littlefs_unbind, /* unbind */
littlefs_statfs, /* statfs */
littlefs_unlink, /* unlink */
littlefs_mkdir, /* mkdir */
littlefs_rmdir, /* rmdir */
littlefs_rename, /* rename */
littlefs_stat, /* stat */
NULL /* chstat */
};
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: littlefs_convert_result
****************************************************************************/
static int littlefs_convert_result(int ret)
{
switch (ret)
{
case LFS_ERR_IO:
return -EIO;
case LFS_ERR_CORRUPT:
return -EFAULT;
case LFS_ERR_NOENT:
return -ENOENT;
case LFS_ERR_EXIST:
return -EEXIST;
case LFS_ERR_NOTDIR:
return -ENOTDIR;
case LFS_ERR_ISDIR:
return -EISDIR;
case LFS_ERR_NOTEMPTY:
return -ENOTEMPTY;
case LFS_ERR_BADF:
return -EBADF;
case LFS_ERR_FBIG:
return -EFBIG;
case LFS_ERR_INVAL:
return -EINVAL;
case LFS_ERR_NOSPC:
return -ENOSPC;
case LFS_ERR_NOMEM:
return -ENOMEM;
case LFS_ERR_NOATTR:
return -ENODATA;
case LFS_ERR_NAMETOOLONG:
return -ENAMETOOLONG;
case LFS_ERR_OK:
default:
return ret;
}
}
/****************************************************************************
* Name: littlefs_convert_oflags
****************************************************************************/
static int littlefs_convert_oflags(int oflags)
{
int ret = 0;
if ((oflags & O_RDONLY) != 0)
{
ret |= LFS_O_RDONLY;
}
if ((oflags & O_WRONLY) != 0)
{
ret |= LFS_O_WRONLY;
}
if ((oflags & O_CREAT) != 0)
{
ret |= LFS_O_CREAT;
}
if ((oflags & O_EXCL) != 0)
{
ret |= LFS_O_EXCL;
}
if ((oflags & O_APPEND) != 0)
{
ret |= LFS_O_APPEND;
}
if ((oflags & O_TRUNC) != 0)
{
ret |= LFS_O_TRUNC;
}
return ret;
}
/****************************************************************************
* Name: littlefs_open
****************************************************************************/
static int littlefs_open(FAR struct file *filep, FAR const char *relpath,
int oflags, mode_t mode)
{
FAR struct littlefs_mountpt_s *fs;
FAR struct littlefs_file_s *priv;
FAR struct inode *inode;
int ret;
/* Get the mountpoint inode reference from the file structure and the
* mountpoint private data from the inode structure
*/
inode = filep->f_inode;
fs = inode->i_private;
/* Allocate memory for the open file */
priv = fs_heap_malloc(sizeof(*priv));
if (priv == NULL)
{
return -ENOMEM;
}
priv->refs = 1;
/* Lock */
ret = nxmutex_lock(&fs->lock);
if (ret < 0)
{
goto errlock;
}
/* Try to open the file */
oflags = littlefs_convert_oflags(oflags);
ret = littlefs_convert_result(lfs_file_open(&fs->lfs, &priv->file,
relpath, oflags));
if (ret < 0)
{
/* Error opening file */
goto errout;
}
/* In append mode, we need to set the file pointer to the end of the
* file.
*/
if (oflags & LFS_O_APPEND)
{
ret = littlefs_convert_result(lfs_file_seek(&fs->lfs, &priv->file,
0, LFS_SEEK_END));
if (ret >= 0)
{
filep->f_pos = ret;
}
else
{
goto errout_with_file;
}
}
/* Sync here in case of O_TRUNC haven't actually done immediately,
* e.g. total 8M, fileA 6M, O_TRUNC re-wrting fileA 6M, meet error.
*/
lfs_file_sync(&fs->lfs, &priv->file);
nxmutex_unlock(&fs->lock);
/* Attach the private date to the struct file instance */
filep->f_priv = priv;
return OK;
errout_with_file:
lfs_file_close(&fs->lfs, &priv->file);
errout:
nxmutex_unlock(&fs->lock);
errlock:
fs_heap_free(priv);
return ret;
}
/****************************************************************************
* Name: littlefs_close
****************************************************************************/
static int littlefs_close(FAR struct file *filep)
{
FAR struct littlefs_mountpt_s *fs;
FAR struct littlefs_file_s *priv;
FAR struct inode *inode;
int ret;
/* Recover our private data from the struct file instance */
priv = filep->f_priv;
inode = filep->f_inode;
fs = inode->i_private;
/* Close the file */
ret = nxmutex_lock(&fs->lock);
if (ret < 0)
{
return ret;
}
if (--priv->refs <= 0)
{
ret = littlefs_convert_result(lfs_file_close(&fs->lfs, &priv->file));
}
nxmutex_unlock(&fs->lock);
if (priv->refs <= 0)
{
fs_heap_free(priv);
}
return ret;
}
/****************************************************************************
* Name: littlefs_read
****************************************************************************/
static ssize_t littlefs_read(FAR struct file *filep, FAR char *buffer,
size_t buflen)
{
FAR struct littlefs_mountpt_s *fs;
FAR struct littlefs_file_s *priv;
FAR struct inode *inode;
ssize_t ret;
/* Recover our private data from the struct file instance */
priv = filep->f_priv;
inode = filep->f_inode;
fs = inode->i_private;
/* Call LFS to perform the read */
ret = nxmutex_lock(&fs->lock);
if (ret < 0)
{
return ret;
}
if (filep->f_pos != priv->file.pos)
{
ret = littlefs_convert_result(lfs_file_seek(&fs->lfs, &priv->file,
filep->f_pos,
LFS_SEEK_SET));
if (ret < 0)
{
goto out;
}
}
ret = littlefs_convert_result(lfs_file_read(&fs->lfs, &priv->file,
buffer, buflen));
if (ret > 0)
{
filep->f_pos += ret;
}
out:
nxmutex_unlock(&fs->lock);
return ret;
}
/****************************************************************************
* Name: littlefs_write
****************************************************************************/
static ssize_t littlefs_write(FAR struct file *filep, const char *buffer,
size_t buflen)
{
FAR struct littlefs_mountpt_s *fs;
FAR struct littlefs_file_s *priv;
FAR struct inode *inode;
ssize_t ret;
/* Recover our private data from the struct file instance */
priv = filep->f_priv;
inode = filep->f_inode;
fs = inode->i_private;
/* Call LFS to perform the write */
ret = nxmutex_lock(&fs->lock);
if (ret < 0)
{
return ret;
}
if (filep->f_pos != priv->file.pos)
{
ret = littlefs_convert_result(lfs_file_seek(&fs->lfs, &priv->file,
filep->f_pos,
LFS_SEEK_SET));
if (ret < 0)
{
goto out;
}
}
ret = littlefs_convert_result(lfs_file_write(&fs->lfs, &priv->file,
buffer, buflen));
if (ret > 0)
{
filep->f_pos += ret;
}
out:
nxmutex_unlock(&fs->lock);
return ret;
}
/****************************************************************************
* Name: littlefs_seek
****************************************************************************/
static off_t littlefs_seek(FAR struct file *filep, off_t offset, int whence)
{
FAR struct littlefs_mountpt_s *fs;
FAR struct littlefs_file_s *priv;
FAR struct inode *inode;
off_t ret;
/* Recover our private data from the struct file instance */
priv = filep->f_priv;
inode = filep->f_inode;
fs = inode->i_private;
/* Call LFS to perform the seek */
ret = nxmutex_lock(&fs->lock);
if (ret < 0)
{
return ret;
}
ret = littlefs_convert_result(lfs_file_seek(&fs->lfs, &priv->file,
offset, whence));
if (ret >= 0)
{
filep->f_pos = ret;
}
nxmutex_unlock(&fs->lock);
return ret;
}
/****************************************************************************
* Name: littlefs_ioctl
****************************************************************************/
static int littlefs_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
{
FAR struct littlefs_mountpt_s *fs;
FAR struct littlefs_file_s *priv;
FAR struct inode *inode;
FAR struct inode *drv;
int ret;
/* Recover our private data from the struct file instance */
priv = filep->f_priv;
inode = filep->f_inode;
fs = inode->i_private;
drv = fs->drv;
ret = nxmutex_lock(&fs->lock);
if (ret < 0)
{
return ret;
}
switch (cmd)
{
case FIOC_FILEPATH:
{
FAR char *path = (FAR char *)(uintptr_t)arg;
ret = inode_getpath(inode, path, PATH_MAX);
if (ret >= 0)
{
size_t len = strlen(path);
if (path[len - 1] != '/')
{
path[len++] = '/';
}
ret = littlefs_convert_result(lfs_file_path(&fs->lfs,
&priv->file,
path + len,
PATH_MAX - len));
}
}
break;
default:
{
if (INODE_IS_MTD(drv))
{
ret = MTD_IOCTL(drv->u.i_mtd, cmd, arg);
}
else
{
if (drv->u.i_bops->ioctl != NULL)
{
ret = drv->u.i_bops->ioctl(drv, cmd, arg);
}
else
{
ret = -ENOTTY;
}
}
}
}
nxmutex_unlock(&fs->lock);
return ret;
}
/****************************************************************************
* Name: littlefs_sync
*
* Description: Synchronize the file state on disk to match internal, in-
* memory state.
*
****************************************************************************/
static int littlefs_sync(FAR struct file *filep)
{
FAR struct littlefs_mountpt_s *fs;
FAR struct littlefs_file_s *priv;
FAR struct inode *inode;
int ret;
/* Recover our private data from the struct file instance */
priv = filep->f_priv;
inode = filep->f_inode;
fs = inode->i_private;
ret = nxmutex_lock(&fs->lock);
if (ret < 0)
{
return ret;
}
ret = littlefs_convert_result(lfs_file_sync(&fs->lfs, &priv->file));
nxmutex_unlock(&fs->lock);
return ret;
}
/****************************************************************************
* Name: littlefs_dup
*
* Description: Duplicate open file data in the new file structure.
*
****************************************************************************/
static int littlefs_dup(FAR const struct file *oldp, FAR struct file *newp)
{
FAR struct littlefs_mountpt_s *fs;
FAR struct littlefs_file_s *priv;
FAR struct inode *inode;
int ret;
/* Recover our private data from the struct file instance */
priv = oldp->f_priv;
inode = oldp->f_inode;
fs = inode->i_private;
ret = nxmutex_lock(&fs->lock);
if (ret < 0)
{
return ret;
}
priv->refs++;
newp->f_priv = priv;
nxmutex_unlock(&fs->lock);
return ret;
}
/****************************************************************************
* Name: littlefs_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 littlefs_fstat(FAR const struct file *filep, FAR struct stat *buf)
{
FAR struct littlefs_mountpt_s *fs;
FAR struct littlefs_file_s *priv;
FAR struct inode *inode;
int ret;
memset(buf, 0, sizeof(*buf));
/* Recover our private data from the struct file instance */
priv = filep->f_priv;
inode = filep->f_inode;
fs = inode->i_private;
/* Call LFS to get file size */
ret = nxmutex_lock(&fs->lock);
if (ret < 0)
{
return ret;
}
buf->st_size = lfs_file_size(&fs->lfs, &priv->file);
nxmutex_unlock(&fs->lock);
if (buf->st_size < 0)
{
return littlefs_convert_result(buf->st_size);
}
buf->st_mode = S_IRWXO | S_IRWXG | S_IRWXU | S_IFREG;
buf->st_blksize = fs->cfg.block_size;
buf->st_blocks = (buf->st_size + buf->st_blksize - 1) / buf->st_blksize;
return OK;
}
/****************************************************************************
* Name: littlefs_truncate
*
* Description:
* Set the length of the open, regular file associated with the file
* structure 'filep' to 'length'.
*
****************************************************************************/
static int littlefs_truncate(FAR struct file *filep, off_t length)
{
FAR struct littlefs_mountpt_s *fs;
FAR struct littlefs_file_s *priv;
FAR struct inode *inode;
int ret;
/* Recover our private data from the struct file instance */
priv = filep->f_priv;
inode = filep->f_inode;
fs = inode->i_private;
/* Call LFS to perform the truncate */
ret = nxmutex_lock(&fs->lock);
if (ret < 0)
{
return ret;
}
ret = littlefs_convert_result(lfs_file_truncate(&fs->lfs, &priv->file,
length));
nxmutex_unlock(&fs->lock);
return ret;
}
/****************************************************************************
* Name: littlefs_opendir
*
* Description: Open a directory for read access
*
****************************************************************************/
static int littlefs_opendir(FAR struct inode *mountpt,
FAR const char *relpath,
FAR struct fs_dirent_s **dir)
{
FAR struct littlefs_mountpt_s *fs;
FAR struct littlefs_dir_s *ldir;
int ret;
/* Recover our private data from the inode instance */
fs = mountpt->i_private;
/* Allocate memory for the open directory */
ldir = fs_heap_malloc(sizeof(*ldir));
if (ldir == NULL)
{
return -ENOMEM;
}
/* Take the lock */
ret = nxmutex_lock(&fs->lock);
if (ret < 0)
{
goto errlock;
}
/* Call the LFS's opendir function */
ret = littlefs_convert_result(lfs_dir_open(&fs->lfs, &ldir->dir, relpath));
if (ret < 0)
{
goto errout;
}
nxmutex_unlock(&fs->lock);
*dir = &ldir->base;
return OK;
errout:
nxmutex_unlock(&fs->lock);
errlock:
fs_heap_free(ldir);
return ret;
}
/****************************************************************************
* Name: littlefs_closedir
*
* Description: Close a directory
*
****************************************************************************/
static int littlefs_closedir(FAR struct inode *mountpt,
FAR struct fs_dirent_s *dir)
{
FAR struct littlefs_mountpt_s *fs;
FAR struct littlefs_dir_s *ldir;
int ret;
/* Recover our private data from the inode instance */
ldir = (FAR struct littlefs_dir_s *)dir;
fs = mountpt->i_private;
/* Call the LFS's closedir function */
ret = nxmutex_lock(&fs->lock);
if (ret < 0)
{
return ret;
}
lfs_dir_close(&fs->lfs, &ldir->dir);
nxmutex_unlock(&fs->lock);
fs_heap_free(ldir);
return OK;
}
/****************************************************************************
* Name: littlefs_readdir
*
* Description: Read the next directory entry
*
****************************************************************************/
static int littlefs_readdir(FAR struct inode *mountpt,
FAR struct fs_dirent_s *dir,
FAR struct dirent *entry)
{
FAR struct littlefs_mountpt_s *fs;
FAR struct littlefs_dir_s *ldir;
struct lfs_info info;
int ret;
/* Recover our private data from the inode instance */
ldir = (FAR struct littlefs_dir_s *)dir;
fs = mountpt->i_private;
/* Call the LFS's readdir function */
ret = nxmutex_lock(&fs->lock);
if (ret < 0)
{
return ret;
}
ret = littlefs_convert_result(lfs_dir_read(&fs->lfs, &ldir->dir, &info));
if (ret > 0)
{
if (info.type == LFS_TYPE_REG)
{
entry->d_type = DTYPE_FILE;
}
else
{
entry->d_type = DTYPE_DIRECTORY;
}
strlcpy(entry->d_name, info.name, sizeof(entry->d_name));
}
else if (ret == 0)
{
ret = -ENOENT;
}
nxmutex_unlock(&fs->lock);
return ret;
}
/****************************************************************************
* Name: littlefs_rewindir
*
* Description: Reset directory read to the first entry
*
****************************************************************************/
static int littlefs_rewinddir(FAR struct inode *mountpt,
FAR struct fs_dirent_s *dir)
{
FAR struct littlefs_mountpt_s *fs;
FAR struct littlefs_dir_s *ldir;
int ret;
/* Recover our private data from the inode instance */
ldir = (FAR struct littlefs_dir_s *)dir;
fs = mountpt->i_private;
/* Call the LFS's rewinddir function */
ret = nxmutex_lock(&fs->lock);
if (ret < 0)
{
return ret;
}
ret = littlefs_convert_result(lfs_dir_rewind(&fs->lfs, &ldir->dir));
nxmutex_unlock(&fs->lock);
return ret;
}
/****************************************************************************
* Name: littlefs_bind
*
* Description: This implements a portion of the mount operation. This
* function allocates and initializes the mountpoint private data and
* binds the driver inode to the filesystem private data. The final
* binding of the private data (containing the driver) to the
* mountpoint is performed by mount().
*
****************************************************************************/
static int littlefs_read_block(FAR const struct lfs_config *c,
lfs_block_t block, lfs_off_t off,
FAR void *buffer, lfs_size_t size)
{
FAR struct littlefs_mountpt_s *fs = c->context;
FAR struct mtd_geometry_s *geo = &fs->geo;
FAR struct inode *drv = fs->drv;
int ret;
block = (block * c->block_size + off) / geo->blocksize;
size = size / geo->blocksize;
if (INODE_IS_MTD(drv))
{
ret = MTD_BREAD(drv->u.i_mtd, block, size, buffer);
}
else
{
ret = drv->u.i_bops->read(drv, buffer, block, size);
}
return ret >= 0 ? OK : ret;
}
/****************************************************************************
* Name: littlefs_write_block
****************************************************************************/
static int littlefs_write_block(FAR const struct lfs_config *c,
lfs_block_t block, lfs_off_t off,
FAR const void *buffer, lfs_size_t size)
{
FAR struct littlefs_mountpt_s *fs = c->context;
FAR struct mtd_geometry_s *geo = &fs->geo;
FAR struct inode *drv = fs->drv;
int ret;
block = (block * c->block_size + off) / geo->blocksize;
size = size / geo->blocksize;
if (INODE_IS_MTD(drv))
{
ret = MTD_BWRITE(drv->u.i_mtd, block, size, buffer);
}
else
{
ret = drv->u.i_bops->write(drv, buffer, block, size);
}
return ret >= 0 ? OK : ret;
}
/****************************************************************************
* Name: littlefs_erase_block
****************************************************************************/
static int littlefs_erase_block(FAR const struct lfs_config *c,
lfs_block_t block)
{
FAR struct littlefs_mountpt_s *fs = c->context;
FAR struct inode *drv = fs->drv;
int ret = OK;
if (INODE_IS_MTD(drv))
{
FAR struct mtd_geometry_s *geo = &fs->geo;
size_t size = c->block_size / geo->erasesize;
block = block * c->block_size / geo->erasesize;
ret = MTD_ERASE(drv->u.i_mtd, block, size);
}
return ret >= 0 ? OK : ret;
}
/****************************************************************************
* Name: littlefs_sync_block
****************************************************************************/
static int littlefs_sync_block(FAR const struct lfs_config *c)
{
FAR struct littlefs_mountpt_s *fs = c->context;
FAR struct inode *drv = fs->drv;
int ret;
if (INODE_IS_MTD(drv))
{
ret = MTD_IOCTL(drv->u.i_mtd, BIOC_FLUSH, 0);
}
else
{
if (drv->u.i_bops->ioctl != NULL)
{
ret = drv->u.i_bops->ioctl(drv, BIOC_FLUSH, 0);
}
else
{
ret = -ENOTTY;
}
}
return ret == -ENOTTY ? OK : ret;
}
/****************************************************************************
* Name: littlefs_bind
****************************************************************************/
static int littlefs_bind(FAR struct inode *driver, FAR const void *data,
FAR void **handle)
{
FAR struct littlefs_mountpt_s *fs;
int ret;
/* Open the block driver */
if (INODE_IS_BLOCK(driver) && driver->u.i_bops->open)
{
ret = driver->u.i_bops->open(driver);
if (ret < 0)
{
return ret;
}
}
/* Create an instance of the mountpt state structure */
fs = fs_heap_zalloc(sizeof(*fs));
if (!fs)
{
ret = -ENOMEM;
goto errout_with_block;
}
/* Initialize the allocated mountpt state structure. The filesystem is
* responsible for one reference on the driver inode and does not
* have to addref() here (but does have to release in unbind().
*/
fs->drv = driver; /* Save the driver reference */
nxmutex_init(&fs->lock); /* Initialize the access control mutex */
if (INODE_IS_MTD(driver))
{
/* Get MTD geometry directly */
ret = MTD_IOCTL(driver->u.i_mtd, MTDIOC_GEOMETRY,
(unsigned long)&fs->geo);
}
else
{
/* Try to get FLT MTD geometry first */
if (driver->u.i_bops->ioctl != NULL)
{
ret = driver->u.i_bops->ioctl(driver, MTDIOC_GEOMETRY,
(unsigned long)&fs->geo);
}
else
{
ret = -ENOTTY;
}
if (ret < 0)
{
struct geometry geometry;
/* Not FLT MTD device, get normal block geometry */
ret = driver->u.i_bops->geometry(driver, &geometry);
if (ret >= 0)
{
/* And convert to MTD geometry */
fs->geo.blocksize = geometry.geo_sectorsize;
fs->geo.erasesize = geometry.geo_sectorsize;
fs->geo.neraseblocks = geometry.geo_nsectors;
}
}
}
if (ret < 0)
{
goto errout_with_fs;
}
/* Initialize lfs_config structure */
fs->cfg.context = fs;
fs->cfg.read = littlefs_read_block;
fs->cfg.prog = littlefs_write_block;
fs->cfg.erase = littlefs_erase_block;
fs->cfg.sync = littlefs_sync_block;
fs->cfg.read_size = fs->geo.blocksize *
CONFIG_FS_LITTLEFS_READ_SIZE_FACTOR;
fs->cfg.prog_size = fs->geo.blocksize *
CONFIG_FS_LITTLEFS_PROGRAM_SIZE_FACTOR;
fs->cfg.block_size = fs->geo.erasesize *
CONFIG_FS_LITTLEFS_BLOCK_SIZE_FACTOR;
fs->cfg.block_count = fs->geo.neraseblocks /
CONFIG_FS_LITTLEFS_BLOCK_SIZE_FACTOR;
fs->cfg.block_cycles = CONFIG_FS_LITTLEFS_BLOCK_CYCLE;
fs->cfg.cache_size = fs->geo.blocksize *
CONFIG_FS_LITTLEFS_CACHE_SIZE_FACTOR;
#if CONFIG_FS_LITTLEFS_LOOKAHEAD_SIZE == 0
fs->cfg.lookahead_size = lfs_min(lfs_alignup(fs->cfg.block_count, 64) / 8,
fs->cfg.read_size);
#else
fs->cfg.lookahead_size = CONFIG_FS_LITTLEFS_LOOKAHEAD_SIZE;
#endif
/* Then get information about the littlefs filesystem on the devices
* managed by this driver.
*/
/* Force format the device if -o forceformat */
if (data && strcmp(data, "forceformat") == 0)
{
ret = littlefs_convert_result(lfs_format(&fs->lfs, &fs->cfg));
if (ret < 0)
{
goto errout_with_fs;
}
}
ret = littlefs_convert_result(lfs_mount(&fs->lfs, &fs->cfg));
if (ret < 0)
{
/* Auto format the device if -o autoformat */
if (ret != -EFAULT || !data || strcmp(data, "autoformat"))
{
goto errout_with_fs;
}
ret = littlefs_convert_result(lfs_format(&fs->lfs, &fs->cfg));
if (ret < 0)
{
goto errout_with_fs;
}
/* Try to mount the device again */
ret = littlefs_convert_result(lfs_mount(&fs->lfs, &fs->cfg));
if (ret < 0)
{
goto errout_with_fs;
}
}
*handle = fs;
return OK;
errout_with_fs:
nxmutex_destroy(&fs->lock);
fs_heap_free(fs);
errout_with_block:
if (INODE_IS_BLOCK(driver) && driver->u.i_bops->close)
{
driver->u.i_bops->close(driver);
}
return ret;
}
/****************************************************************************
* Name: littlefs_unbind
*
* Description: This implements the filesystem portion of the umount
* operation.
*
****************************************************************************/
static int littlefs_unbind(FAR void *handle, FAR struct inode **driver,
unsigned int flags)
{
FAR struct littlefs_mountpt_s *fs = handle;
FAR struct inode *drv = fs->drv;
int ret;
/* Unmount */
ret = nxmutex_lock(&fs->lock);
if (ret < 0)
{
return ret;
}
ret = littlefs_convert_result(lfs_unmount(&fs->lfs));
nxmutex_unlock(&fs->lock);
if (ret >= 0)
{
/* Close the block driver */
if (INODE_IS_BLOCK(drv) && drv->u.i_bops->close)
{
drv->u.i_bops->close(drv);
}
/* We hold a reference to the driver but should not but
* 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.
*/
if (driver)
{
*driver = drv;
}
/* Release the mountpoint private data */
nxmutex_destroy(&fs->lock);
fs_heap_free(fs);
}
return ret;
}
/****************************************************************************
* Name: littlefs_statfs
*
* Description: Return filesystem statistics
*
****************************************************************************/
static int littlefs_statfs(FAR struct inode *mountpt, FAR struct statfs *buf)
{
FAR struct littlefs_mountpt_s *fs;
int ret;
/* Get the mountpoint private data from the inode structure */
fs = mountpt->i_private;
/* Return something for the file system description */
buf->f_type = LITTLEFS_SUPER_MAGIC;
buf->f_namelen = LFS_NAME_MAX;
buf->f_bsize = fs->cfg.block_size;
buf->f_blocks = fs->cfg.block_count;
buf->f_bfree = fs->cfg.block_count;
buf->f_bavail = fs->cfg.block_count;
ret = nxmutex_lock(&fs->lock);
if (ret < 0)
{
return ret;
}
ret = littlefs_convert_result(lfs_fs_size(&fs->lfs));
if (ret > 0)
{
buf->f_bfree -= ret;
buf->f_bavail -= ret;
ret = 0;
}
nxmutex_unlock(&fs->lock);
return ret;
}
/****************************************************************************
* Name: littlefs_unlink
*
* Description: Remove a file
*
****************************************************************************/
static int littlefs_unlink(FAR struct inode *mountpt,
FAR const char *relpath)
{
FAR struct littlefs_mountpt_s *fs;
int ret;
/* Get the mountpoint private data from the inode structure */
fs = mountpt->i_private;
/* Call the LFS to perform the unlink */
ret = nxmutex_lock(&fs->lock);
if (ret < 0)
{
return ret;
}
ret = littlefs_convert_result(lfs_remove(&fs->lfs, relpath));
nxmutex_unlock(&fs->lock);
return ret;
}
/****************************************************************************
* Name: littlefs_mkdir
*
* Description: Create a directory
*
****************************************************************************/
static int littlefs_mkdir(FAR struct inode *mountpt, FAR const char *relpath,
mode_t mode)
{
FAR struct littlefs_mountpt_s *fs;
int ret;
/* Get the mountpoint private data from the inode structure */
fs = mountpt->i_private;
/* Call LFS to do the mkdir */
ret = nxmutex_lock(&fs->lock);
if (ret < 0)
{
return ret;
}
ret = lfs_mkdir(&fs->lfs, relpath);
nxmutex_unlock(&fs->lock);
return ret;
}
/****************************************************************************
* Name: littlefs_rmdir
*
* Description: Remove a directory
*
****************************************************************************/
static int littlefs_rmdir(FAR struct inode *mountpt, FAR const char *relpath)
{
struct stat buf;
int ret;
ret = littlefs_stat(mountpt, relpath, &buf);
if (ret < 0)
{
return ret;
}
if (S_ISDIR(buf.st_mode))
{
return littlefs_unlink(mountpt, relpath);
}
else
{
return -ENOTDIR;
}
}
/****************************************************************************
* Name: littlefs_rename
*
* Description: Rename a file or directory
*
****************************************************************************/
static int littlefs_rename(FAR struct inode *mountpt,
FAR const char *oldrelpath,
FAR const char *newrelpath)
{
FAR struct littlefs_mountpt_s *fs;
int ret;
/* Get the mountpoint private data from the inode structure */
fs = mountpt->i_private;
/* Call LFS to do the rename */
ret = nxmutex_lock(&fs->lock);
if (ret < 0)
{
return ret;
}
ret = littlefs_convert_result(lfs_rename(&fs->lfs, oldrelpath,
newrelpath));
nxmutex_unlock(&fs->lock);
return ret;
}
/****************************************************************************
* Name: littlefs_stat
*
* Description: Return information about a file or directory
*
****************************************************************************/
static int littlefs_stat(FAR struct inode *mountpt, FAR const char *relpath,
FAR struct stat *buf)
{
FAR struct littlefs_mountpt_s *fs;
struct lfs_info info;
int ret;
memset(buf, 0, sizeof(*buf));
/* Get the mountpoint private data from the inode structure */
fs = mountpt->i_private;
/* Call the LFS to do the stat operation */
ret = nxmutex_lock(&fs->lock);
if (ret < 0)
{
return ret;
}
ret = lfs_stat(&fs->lfs, relpath, &info);
nxmutex_unlock(&fs->lock);
if (ret >= 0)
{
/* Convert info to stat */
buf->st_mode = S_IRWXO | S_IRWXG | S_IRWXU;
if (info.type == LFS_TYPE_REG)
{
buf->st_mode |= S_IFREG;
buf->st_size = info.size;
}
else
{
buf->st_mode |= S_IFDIR;
buf->st_size = 0;
}
buf->st_blksize = fs->cfg.block_size;
buf->st_blocks = (buf->st_size + buf->st_blksize - 1) /
buf->st_blksize;
}
return ret;
}