| /**************************************************************************** |
| * 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/kmalloc.h> |
| #include <nuttx/mtd/mtd.h> |
| #include <nuttx/mutex.h> |
| |
| #include <sys/stat.h> |
| #include <sys/statfs.h> |
| |
| #include "littlefs/lfs.h" |
| #include "littlefs/lfs_util.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 */ |
| |
| 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 = kmm_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: |
| kmm_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) |
| { |
| kmm_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 inode *inode; |
| FAR struct inode *drv; |
| |
| /* Recover our private data from the struct file instance */ |
| |
| inode = filep->f_inode; |
| fs = inode->i_private; |
| drv = fs->drv; |
| |
| if (INODE_IS_MTD(drv)) |
| { |
| return MTD_IOCTL(drv->u.i_mtd, cmd, arg); |
| } |
| else |
| { |
| if (drv->u.i_bops->ioctl != NULL) |
| { |
| return drv->u.i_bops->ioctl(drv, cmd, arg); |
| } |
| else |
| { |
| return -ENOTTY; |
| } |
| } |
| } |
| |
| /**************************************************************************** |
| * 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 = kmm_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: |
| kmm_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); |
| |
| kmm_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 = kmm_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); |
| kmm_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); |
| kmm_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; |
| } |