diff --git a/fs/Kconfig b/fs/Kconfig index 240f712b201..3fd8ff918d1 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -134,3 +134,4 @@ source "fs/littlefs/Kconfig" source "fs/unionfs/Kconfig" source "fs/userfs/Kconfig" source "fs/hostfs/Kconfig" +source fs/exfat/Kconfig diff --git a/fs/Makefile b/fs/Makefile index 6bbee4089fb..db3031e2d07 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -56,6 +56,7 @@ include unionfs/Make.defs include userfs/Make.defs include hostfs/Make.defs include littlefs/Make.defs +include exfat/Make.defs endif diff --git a/fs/exfat/Kconfig b/fs/exfat/Kconfig new file mode 100644 index 00000000000..ad3a67c4e45 --- /dev/null +++ b/fs/exfat/Kconfig @@ -0,0 +1,7 @@ +config FS_EXFAT + bool "EXFAT File System" + default n + select FS_LARGEFILE + depends on !DISABLE_MOUNTPOINT + ---help--- + Build the EXFAT file system. https://en.wikipedia.org/wiki/ExFAT diff --git a/fs/exfat/Make.defs b/fs/exfat/Make.defs new file mode 100644 index 00000000000..6f09d986071 --- /dev/null +++ b/fs/exfat/Make.defs @@ -0,0 +1,64 @@ +############################################################################ +# fs/exfat/Make.defs +# +# 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. +# +############################################################################ + +ifeq ($(CONFIG_FS_EXFAT),y) +# Files required for exfat file system support + +CSRCS += exfat_vfs.c exfat_io.c + +DEPPATH += --dep-path exfat +VPATH += :exfat + +CSRCS += cluster.c lookup.c mount.c node.c repair.c +CSRCS += time.c utils.c utf.c log.c + +DEPPATH += --dep-path exfat/exfat/libexfat +VPATH += : exfat/exfat/libexfat + +CSRCS += cbm.c fat.c mkexfat.c rootdir.c uctc.c uct.c +CSRCS += vbr.c + +DEPPATH += --dep-path exfat/exfat/mkfs +VPATH += : exfat/exfat/mkfs + +CFLAGS += ${shell $(INCDIR) $(INCDIROPT) "$(CC)" exfat/exfat/libexfat} +CFLAGS += ${shell $(INCDIR) $(INCDIROPT) "$(CC)" exfat/exfat/mkfs} +CFLAGS += ${shell $(INCDIR) $(INCDIROPT) "$(CC)" exfat/} +CFLAGS += -D__GLIBC__ + +EXFAT_VERSION ?= 1.3.0 +EXFAT_TARBALL = v$(EXFAT_VERSION).tar.gz + +$(EXFAT_TARBALL): + $(Q) curl -L -o exfat/$(EXFAT_TARBALL) https://https://github.com/relan/exfat/archive/$(EXFAT_TARBALL) + +.exfatunpack: $(EXFAT_TARBALL) + $(Q) tar zxf exfat/$(EXFAT_TARBALL) -C exfat + $(Q) mv exfat/exfat-$(EXFAT_VERSION) exfat/exfat + $(Q) touch exfat/.exfatunpack + +context::# .exfatunpack + +distclean:: +# $(call DELFILE, exfat/.exfatunpack) +# $(call DELFILE, exfat/$(EXFAT_TARBALL)) +# $(call DELDIR, exfat/exfat) + +endif diff --git a/fs/exfat/config.h b/fs/exfat/config.h new file mode 100644 index 00000000000..ff492f149b5 --- /dev/null +++ b/fs/exfat/config.h @@ -0,0 +1,46 @@ +/* libexfat/config.h. Generated from config.h.in by configure. */ +/* libexfat/config.h.in. Generated from configure.ac by autoheader. */ + +/* Name of package */ +#define PACKAGE "exfat" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "relan@users.noreply.github.com" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "Free exFAT implementation" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "Free exFAT implementation 1.3.0" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "exfat" + +/* Define to the home page for this package. */ +#define PACKAGE_URL "https://github.com/relan/exfat" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "1.3.0" + +/* Define if block devices are not supported. */ +/* #undef USE_UBLIO */ + +/* Version number of package */ +#define VERSION "1.3.0" + +/* Enable large inode numbers on Mac OS X 10.5. */ +#ifndef _DARWIN_USE_64_BIT_INODE +# define _DARWIN_USE_64_BIT_INODE 1 +#endif + +/* Enable vsyslog(). */ +#define _DEFAULT_SOURCE /**/ + +/* Number of bits in a file offset, on hosts where this is settable. */ +/* #undef _FILE_OFFSET_BITS */ + +/* Define for large files, on AIX-style hosts. */ +/* #undef _LARGE_FILES */ + +/* Enable pread() and pwrite(). */ +#define _XOPEN_SOURCE 500 diff --git a/fs/exfat/exfat_io.c b/fs/exfat/exfat_io.c new file mode 100644 index 00000000000..6ef77773c67 --- /dev/null +++ b/fs/exfat/exfat_io.c @@ -0,0 +1,157 @@ +/**************************************************************************** + * fs/exfat/exfat_io.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 +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "exfat.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct exfat_dev +{ + struct file file; + enum exfat_mode mode; + off_t size; /* in bytes */ +}; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +FAR struct exfat_dev *exfat_open(FAR const char *spec, enum exfat_mode mode) +{ + FAR struct exfat_dev *dev; + struct stat stbuf; + int ret; + + dev = kmm_malloc(sizeof(*dev)); + if (dev == NULL) + { + return NULL; + } + + ret = file_open(&dev->file, spec, (mode != EXFAT_MODE_RO) ? + O_RDWR : O_RDONLY); + if (ret < 0) + { + if (mode == EXFAT_MODE_ANY) + { + mode = EXFAT_MODE_RO; + ret = file_open(&dev->file, spec, O_RDONLY); + } + + if (ret < 0) + { + return NULL; + } + } + + dev->mode = mode; + + ret = file_fstat(&dev->file, &stbuf); + if (ret < 0) + { + goto errout; + } + + if (!S_ISBLK(stbuf.st_mode) && !S_ISCHR(stbuf.st_mode) && + !S_ISREG(stbuf.st_mode)) + { + goto errout; + } + + dev->size = stbuf.st_size; + return dev; + +errout: + file_close(&dev->file); + kmm_free(dev); + return NULL; +} + +int exfat_close(FAR struct exfat_dev *dev) +{ + int ret; + + ret = file_close(&dev->file); + kmm_free(dev); + return ret; +} + +int exfat_fsync(FAR struct exfat_dev *dev) +{ + int ret; + + ret = file_ioctl(&dev->file, BIOC_FLUSH, 0); + return ret == -ENOTTY ? OK : ret; +} + +enum exfat_mode exfat_get_mode(FAR const struct exfat_dev *dev) +{ + return dev->mode; +} + +off_t exfat_get_size(FAR const struct exfat_dev *dev) +{ + return dev->size; +} + +off_t exfat_seek(FAR struct exfat_dev *dev, off_t offset, int whence) +{ + return file_seek(&dev->file, offset, whence); +} + +ssize_t exfat_read(FAR struct exfat_dev *dev, FAR void *buffer, size_t size) +{ + return file_read(&dev->file, buffer, size); +} + +ssize_t exfat_write(FAR struct exfat_dev *dev, FAR const void *buffer, + size_t size) +{ + return file_write(&dev->file, buffer, size); +} + +ssize_t exfat_pread(FAR struct exfat_dev *dev, FAR void *buffer, size_t size, + off_t offset) +{ + return file_pread(&dev->file, buffer, size, offset); +} + +ssize_t exfat_pwrite(FAR struct exfat_dev *dev, FAR const void *buffer, + size_t size, off_t offset) +{ + return file_pwrite(&dev->file, buffer, size, offset); +} diff --git a/fs/exfat/exfat_vfs.c b/fs/exfat/exfat_vfs.c new file mode 100644 index 00000000000..93c3cbd9f13 --- /dev/null +++ b/fs/exfat/exfat_vfs.c @@ -0,0 +1,1356 @@ +/**************************************************************************** + * fs/exfat/exfat_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 + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include "mkexfat.h" + +#include "inode/inode.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct exfatfs_file_s +{ + FAR struct exfat_node *node; +}; + +struct exfatfs_dir_s +{ + FAR struct exfat_node *entry; + struct exfat_iterator it; +}; + +struct exfatfs_mountpt_s +{ + FAR struct inode *drv; + sem_t sem; + struct exfat ef; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void exfatfs_semgive(FAR struct exfatfs_mountpt_s *fs); +static int exfatfs_semtake(FAR struct exfatfs_mountpt_s *fs); + +static int exfatfs_open(FAR struct file *filep, FAR const char *relpath, + int oflags, mode_t mode); +static int exfatfs_close(FAR struct file *filep); +static ssize_t exfatfs_read(FAR struct file *filep, FAR char *buffer, + size_t buflen); +static ssize_t exfatfs_write(FAR struct file *filep, FAR const char *buffer, + size_t buflen); +static off_t exfatfs_seek(FAR struct file *filep, off_t offset, + int whence); +static int exfatfs_ioctl(FAR struct file *filep, int cmd, + unsigned long arg); + +static int exfatfs_sync(FAR struct file *filep); +static int exfatfs_dup(FAR const struct file *oldp, + FAR struct file *newp); +static int exfatfs_fstat(FAR const struct file *filep, + FAR struct stat *buf); +static int exfatfs_fchstat(FAR const struct file *filep, + FAR const struct stat *buf, int flags); +static int exfatfs_truncate(FAR struct file *filep, + off_t length); + +static int exfatfs_opendir(FAR struct inode *mountpt, + FAR const char *relpath, + FAR struct fs_dirent_s *dir); +static int exfatfs_closedir(FAR struct inode *mountpt, + FAR struct fs_dirent_s *dir); +static int exfatfs_readdir(FAR struct inode *mountpt, + FAR struct fs_dirent_s *dir); +static int exfatfs_rewinddir(FAR struct inode *mountpt, + FAR struct fs_dirent_s *dir); + +static int exfatfs_bind(FAR struct inode *driver, + FAR const void *data, FAR void **handle); +static int exfatfs_unbind(FAR void *handle, FAR struct inode **driver, + unsigned int flags); +static int exfatfs_statfs(FAR struct inode *mountpt, + FAR struct statfs *buf); + +static int exfatfs_unlink(FAR struct inode *mountpt, + FAR const char *relpath); +static int exfatfs_mkdir(FAR struct inode *mountpt, + FAR const char *relpath, mode_t mode); +static int exfatfs_rmdir(FAR struct inode *mountpt, + FAR const char *relpath); +static int exfatfs_rename(FAR struct inode *mountpt, + FAR const char *oldrelpath, + FAR const char *newrelpath); +static int exfatfs_stat(FAR struct inode *mountpt, + FAR const char *relpath, FAR struct stat *buf); +static int exfatfs_chstat(FAR struct inode *mountpt, + FAR const char *relpath, + FAR const struct stat *buf, int flags); +static int exfatfs_format(FAR struct exfat *ef, FAR const char *spec, + int sector_bits, int spc_bits, + FAR const char *volume_label, + uint32_t volume_serial, uint64_t first_sector); + +/**************************************************************************** + * 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 exfatfs_operations = +{ + exfatfs_open, /* open */ + exfatfs_close, /* close */ + exfatfs_read, /* read */ + exfatfs_write, /* write */ + exfatfs_seek, /* seek */ + exfatfs_ioctl, /* ioctl */ + + exfatfs_sync, /* sync */ + exfatfs_dup, /* dup */ + exfatfs_fstat, /* fstat */ + exfatfs_fchstat, /* fchstat */ + exfatfs_truncate, /* truncate */ + + exfatfs_opendir, /* opendir */ + exfatfs_closedir, /* closedir */ + exfatfs_readdir, /* readdir */ + exfatfs_rewinddir, /* rewinddir */ + + exfatfs_bind, /* bind */ + exfatfs_unbind, /* unbind */ + exfatfs_statfs, /* statfs */ + + exfatfs_unlink, /* unlink */ + exfatfs_mkdir, /* mkdir */ + exfatfs_rmdir, /* rmdir */ + exfatfs_rename, /* rename */ + exfatfs_stat, /* stat */ + exfatfs_chstat /* chstat */ +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: exfatfs_semtake + ****************************************************************************/ + +static int exfatfs_semtake(FAR struct exfatfs_mountpt_s *fs) +{ + return nxsem_wait_uninterruptible(&fs->sem); +} + +/**************************************************************************** + * Name: exfatfs_semgive + ****************************************************************************/ + +static void exfatfs_semgive(FAR struct exfatfs_mountpt_s *fs) +{ + nxsem_post(&fs->sem); +} + +static void exfatfs_release_node(FAR struct exfat *ef, + FAR struct exfat_node *node) +{ + exfat_put_node(ef, node); + if (node->references == 0) + { + exfat_cleanup_node(ef, node); + } +} + +/**************************************************************************** + * Name: exfatfs_open + ****************************************************************************/ + +static int exfatfs_open(FAR struct file *filep, FAR const char *relpath, + int oflags, mode_t mode) +{ + FAR struct exfatfs_mountpt_s *fs; + FAR struct exfatfs_file_s *priv; + FAR struct exfat_node *node; + FAR struct inode *inode; + int ret; + + priv = kmm_malloc(sizeof(struct exfatfs_file_s)); + if (!priv) + { + return -ENOMEM; + } + + /* 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; + + /* Take the semaphore */ + + ret = exfatfs_semtake(fs); + if (ret < 0) + { + goto semout; + } + + ret = exfat_lookup(&fs->ef, &node, relpath); + + /* Three possibilities: (1) a node exists for the relpath and + * node describes the file position (2) the node does not + * exist, or (3) some error occurred. + */ + + if (ret == OK) + { + bool readonly; + if ((oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) + { + /* Already exists -- can't create it exclusively */ + + ret = -EEXIST; + goto nodeout; + } + + /* Check if the caller has sufficient privileges to open the file */ + + readonly = (node->attrib & EXFAT_ATTRIB_RO) != 0; + if (((oflags & O_WRONLY) != 0) && readonly) + { + ret = -EACCES; + goto nodeout; + } + + /* If O_TRUNC is specified and the file is opened for writing, + * then truncate the file. This operation requires that the file is + * writeable, but we have already checked that. O_TRUNC without write + * access is ignored. + */ + + if ((oflags & (O_TRUNC | O_WRONLY)) == (O_TRUNC | O_WRONLY)) + { + /* Truncate the file to zero length */ + + ret = exfat_truncate(&fs->ef, node, 0, true); + if (ret < 0) + { + goto nodeout; + } + } + } + else if (ret == -ENOENT) + { + /* The file does not exist. Were we asked to create it? */ + + if ((oflags & O_CREAT) == 0) + { + /* No.. then we fail with -ENOENT */ + + goto errout; + } + + /* Yes.. create the file */ + + ret = exfat_mknod(&fs->ef, relpath); + if (ret < 0) + { + goto errout; + } + + ret = exfat_lookup(&fs->ef, &node, relpath); + if (ret < 0) + { + goto errout; + } + } + else + { + goto errout; + } + + priv->node = node; + filep->f_priv = priv; + exfatfs_semgive(fs); + return OK; + +nodeout: + exfatfs_release_node(&fs->ef, node); +errout: + exfatfs_semgive(fs); +semout: + kmm_free(priv); + return ret; +} + +/**************************************************************************** + * Name: exfatfs_close + ****************************************************************************/ + +static int exfatfs_close(FAR struct file *filep) +{ + FAR struct exfatfs_mountpt_s *fs; + FAR struct exfatfs_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 + */ + + priv = filep->f_priv; + inode = filep->f_inode; + fs = inode->i_private; + + /* Take the semaphore */ + + ret = exfatfs_semtake(fs); + if (ret < 0) + { + return ret; + } + + exfatfs_release_node(&fs->ef, priv->node); + kmm_free(filep->f_priv); + exfatfs_semgive(fs); + return OK; +} + +/**************************************************************************** + * Name: exfatfs_read + ****************************************************************************/ + +static ssize_t exfatfs_read(FAR struct file *filep, FAR char *buffer, + size_t buflen) +{ + FAR struct exfatfs_mountpt_s *fs; + FAR struct exfatfs_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; + + /* Take the semaphore */ + + ret = exfatfs_semtake(fs); + if (ret < 0) + { + return ret; + } + + ret = exfat_generic_pread(&fs->ef, priv->node, buffer, + buflen, filep->f_pos); + if (ret > 0) + { + filep->f_pos += ret; + } + + /* update atime, so we need to flush this node */ + + exfat_flush_node(&fs->ef, priv->node); + exfatfs_semgive(fs); + return ret; +} + +/**************************************************************************** + * Name: exfatfs_write + ****************************************************************************/ + +static ssize_t exfatfs_write(FAR struct file *filep, const char *buffer, + size_t buflen) +{ + FAR struct exfatfs_mountpt_s *fs; + FAR struct exfatfs_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; + + /* Take the semaphore */ + + ret = exfatfs_semtake(fs); + if (ret < 0) + { + return ret; + } + + ret = exfat_generic_pwrite(&fs->ef, priv->node, buffer, + buflen, filep->f_pos); + if (ret > 0) + { + filep->f_pos += ret; + } + + exfat_flush_node(&fs->ef, priv->node); + exfatfs_semgive(fs); + return ret; +} + +/**************************************************************************** + * Name: exfatfs_seek + ****************************************************************************/ + +static off_t exfatfs_seek(FAR struct file *filep, off_t offset, int whence) +{ + FAR struct exfatfs_mountpt_s *fs; + FAR struct exfatfs_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 = exfatfs_semtake(fs); + if (ret < 0) + { + return ret; + } + + /* Map the offset according to the whence option */ + + switch (whence) + { + case SEEK_SET: + filep->f_pos = offset; + break; + + case SEEK_CUR: + offset += filep->f_pos; + break; + + case SEEK_END: + filep->f_pos = priv->node->size; + break; + + default: + ret = -EINVAL; + } + + exfatfs_semgive(fs); + return !ret ? filep->f_pos : ret; +} + +/**************************************************************************** + * Name: exfatfs_ioctl + ****************************************************************************/ + +static int exfatfs_ioctl(FAR struct file *filep, int cmd, unsigned long arg) +{ + FAR struct exfatfs_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; + + return drv->u.i_bops->ioctl(drv, cmd, arg); +} + +/**************************************************************************** + * Name: exfatfs_sync + * + * Description: Synchronize the file state on disk to match internal, in- + * memory state. + * + ****************************************************************************/ + +static int exfatfs_sync(FAR struct file *filep) +{ + FAR struct exfatfs_mountpt_s *fs; + FAR struct exfatfs_file_s *file; + FAR struct inode *inode; + int ret; + + /* Recover our private data from the struct file instance */ + + file = filep->f_priv; + inode = filep->f_inode; + fs = inode->i_private; + + ret = exfatfs_semtake(fs); + if (ret < 0) + { + return ret; + } + + ret = exfat_flush_node(&fs->ef, file->node); + exfatfs_semgive(fs); + + return ret; +} + +/**************************************************************************** + * Name: exfatfs_dup + * + * Description: Duplicate open file data in the new file structure. + * + ****************************************************************************/ + +static int exfatfs_dup(FAR const struct file *oldp, FAR struct file *newp) +{ + FAR struct exfatfs_mountpt_s *fs; + FAR struct exfatfs_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 = exfatfs_semtake(fs); + if (ret < 0) + { + return ret; + } + + exfat_get_node(priv->node); + newp->f_priv = priv; + exfatfs_semgive(fs); + + return ret; +} + +/**************************************************************************** + * Name: exfatfs_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 exfatfs_fstat(FAR const struct file *filep, FAR struct stat *buf) +{ + FAR struct exfatfs_mountpt_s *fs; + FAR struct exfatfs_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 get file size */ + + ret = exfatfs_semtake(fs); + if (ret < 0) + { + return ret; + } + + exfat_stat(&fs->ef, priv->node, buf); + exfatfs_semgive(fs); + + return OK; +} + +/**************************************************************************** + * Name: exfatfs_chstat_i + ****************************************************************************/ + +static int exfatfs_chstat_i(FAR struct exfatfs_mountpt_s *fs, + FAR struct exfat_node *node, + 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) && buf->st_gid != fs->ef.gid) + { + return -EPERM; + } + + if ((flags & CH_STAT_UID) && buf->st_uid != fs->ef.uid) + { + return -EPERM; + } + + if (flags & CH_STAT_ATIME || flags & CH_STAT_MTIME) + { + struct timespec tv[2]; + + memset(tv, 0, sizeof(tv)); + if (flags & CH_STAT_ATIME) + { + tv[0] = buf->st_atim; + } + else + { + tv[0].tv_sec = node->atime; + } + + if (flags & CH_STAT_MTIME) + { + tv[1] = buf->st_mtim; + } + else + { + tv[1].tv_sec = node->mtime; + } + + exfat_utimes(node, (const struct timespec *)&tv); + return exfat_flush_node(&fs->ef, node); + } + + return OK; +} + +/**************************************************************************** + * Name: exfatfs_fchstat + ****************************************************************************/ + +static int exfatfs_fchstat(FAR const struct file *filep, + FAR const struct stat *buf, int flags) +{ + FAR struct exfatfs_mountpt_s *fs; + FAR struct exfatfs_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 get file size */ + + ret = exfatfs_semtake(fs); + if (ret < 0) + { + return ret; + } + + ret = exfatfs_chstat_i(fs, priv->node, buf, flags); + exfatfs_semgive(fs); + return ret; +} + +/**************************************************************************** + * Name: exfatfs_truncate + * + * Description: + * Set the length of the open, regular file associated with the file + * structure 'filep' to 'length'. + * + ****************************************************************************/ + +static int exfatfs_truncate(FAR struct file *filep, off_t length) +{ + FAR struct exfatfs_mountpt_s *fs; + FAR struct exfatfs_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 = exfatfs_semtake(fs); + if (ret < 0) + { + return ret; + } + + ret = exfat_truncate(&fs->ef, priv->node, length, true); + exfatfs_semgive(fs); + + return ret; +} + +/**************************************************************************** + * Name: exfatfs_opendir + * + * Description: Open a directory for read access + * + ****************************************************************************/ + +static int exfatfs_opendir(FAR struct inode *mountpt, + FAR const char *relpath, + FAR struct fs_dirent_s *dir) +{ + FAR struct exfatfs_mountpt_s *fs; + FAR struct exfatfs_dir_s *priv; + FAR struct exfat_node *node; + int ret; + + /* Recover our private data from the inode instance */ + + fs = mountpt->i_private; + + /* Take the semaphore */ + + ret = exfatfs_semtake(fs); + if (ret < 0) + { + return ret; + } + + ret = exfat_lookup(&fs->ef, &node, relpath); + if (ret != 0) + { + goto lookout; + } + + if (!(node->attrib & EXFAT_ATTRIB_DIR)) + { + ret = -ENOTDIR; + goto errout; + } + + /* Allocate memory for the open directory */ + + priv = kmm_malloc(sizeof(*priv)); + if (priv == NULL) + { + ret = -ENOMEM; + goto errout; + } + + ret = exfat_opendir(&fs->ef, node, &priv->it); + if (ret != 0) + { + goto openerr; + } + + priv->entry = exfat_readdir(&priv->it); + exfatfs_release_node(&fs->ef, node); + exfatfs_semgive(fs); + + dir->u.exfat = priv; + return OK; + +openerr: + kmm_free(priv); +errout: + exfatfs_release_node(&fs->ef, node); +lookout: + exfatfs_semgive(fs); + return ret; +} + +/**************************************************************************** + * Name: exfatfs_closedir + * + * Description: Close a directory + * + ****************************************************************************/ + +static int exfatfs_closedir(FAR struct inode *mountpt, + FAR struct fs_dirent_s *dir) +{ + struct exfatfs_mountpt_s *fs; + FAR struct exfatfs_dir_s *priv; + int ret; + + /* Recover our private data from the inode instance */ + + priv = dir->u.exfat; + fs = mountpt->i_private; + + /* Take the semaphore */ + + ret = exfatfs_semtake(fs); + if (ret < 0) + { + return ret; + } + + if (priv->entry) + { + exfatfs_release_node(&fs->ef, priv->entry); + } + + exfat_closedir(&fs->ef, &priv->it); + kmm_free(priv); + exfatfs_semgive(fs); + + return OK; +} + +/**************************************************************************** + * Name: exfatfs_readdir + * + * Description: Read the next directory entry + * + ****************************************************************************/ + +static int exfatfs_readdir(FAR struct inode *mountpt, + FAR struct fs_dirent_s *dir) +{ + FAR struct exfatfs_mountpt_s *fs; + FAR struct exfatfs_dir_s *priv; + FAR struct exfat_node *node; + int ret; + + /* Recover our private data from the inode instance */ + + priv = dir->u.exfat; + fs = mountpt->i_private; + + /* Take the semaphore */ + + ret = exfatfs_semtake(fs); + if (ret < 0) + { + return ret; + } + + if (priv->entry) + { + if (priv->entry->attrib == EXFAT_ATTRIB_DIR) + { + dir->fd_dir.d_type = DTYPE_DIRECTORY; + } + else + { + dir->fd_dir.d_type = DTYPE_FILE; + } + + exfat_get_name(priv->entry, dir->fd_dir.d_name); + node = priv->entry; + priv->entry = exfat_readdir(&priv->it); + exfatfs_release_node(&fs->ef, node); + } + else + { + ret = -ENOENT; + } + + exfatfs_semgive(fs); + return ret; +} + +/**************************************************************************** + * Name: exfatfs_rewindir + * + * Description: Reset directory read to the first entry + * + ****************************************************************************/ + +static int exfatfs_rewinddir(FAR struct inode *mountpt, + FAR struct fs_dirent_s *dir) +{ + FAR struct exfatfs_mountpt_s *fs; + FAR struct exfatfs_dir_s *priv; + int ret; + + /* Recover our private data from the inode instance */ + + priv = dir->u.exfat; + fs = mountpt->i_private; + + /* Take the semaphore */ + + ret = exfatfs_semtake(fs); + if (ret < 0) + { + return ret; + } + + if (priv->entry) + { + exfatfs_release_node(&fs->ef, priv->entry); + } + + priv->it.current = NULL; + priv->entry = exfat_readdir(&priv->it); + exfatfs_semgive(fs); + + return OK; +} + +/**************************************************************************** + * Name: exfatfs_bind + ****************************************************************************/ + +static int exfatfs_bind(FAR struct inode *driver, FAR const void *data, + FAR void **handle) +{ + FAR struct exfatfs_mountpt_s *fs; + struct geometry geo; + char spec[PATH_MAX]; + int sector_bits; + int sector_size; + int 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 */ + nxsem_init(&fs->sem, 0, 0); /* Initialize the access control semaphore */ + + ret = driver->u.i_bops->geometry(driver, &geo); + if (ret < 0) + { + goto errout_with_fs; + } + + ret = inode_getpath(fs->drv, spec); + if (ret < 0) + { + goto errout_with_fs; + } + + sector_size = ROUND_UP(geo.geo_sectorsize, + sizeof(struct exfat_super_block)); + sector_bits = flsl(sector_size) - 1; + + /* Force format the device if -o forceformat/audoformat */ + + if (data && exfat_match_option(data, "forceformat")) + { + ret = exfatfs_format(&fs->ef, spec, sector_bits, -1, NULL, 0, 0); + if (ret < 0) + { + goto errout_with_fs; + } + } + + ret = exfat_mount(&fs->ef, spec, data); + if (ret < 0) + { + /* Auto format the device if -o autoformat */ + + if (ret != -EIO || + !data || !exfat_match_option(data, "autoformat")) + { + goto errout_with_fs; + } + + ret = exfatfs_format(&fs->ef, spec, sector_bits, -1, NULL, 0, 0); + if (ret < 0) + { + goto errout_with_fs; + } + + /* Try to mount the device again */ + + ret = exfat_mount(&fs->ef, spec, data); + if (ret < 0) + { + goto errout_with_fs; + } + } + + *handle = fs; + exfatfs_semgive(fs); + return OK; + +errout_with_fs: + nxsem_destroy(&fs->sem); + kmm_free(fs); +errout_with_block: + return ret; +} + +/**************************************************************************** + * Name: exfatfs_unbind + * + * Description: This implements the filesystem portion of the umount + * operation. + * + ****************************************************************************/ + +static int exfatfs_unbind(FAR void *handle, FAR struct inode **driver, + unsigned int flags) +{ + FAR struct exfatfs_mountpt_s *fs = handle; + FAR struct inode *drv = fs->drv; + int ret; + + /* Take the semaphore */ + + ret = exfatfs_semtake(fs); + if (ret < 0) + { + return ret; + } + + exfat_unmount(&fs->ef); + exfatfs_semgive(fs); + + /* 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 */ + + nxsem_destroy(&fs->sem); + kmm_free(fs); + + return ret; +} + +/**************************************************************************** + * Name: exfatfs_statfs + * + * Description: Return filesystem statistics + * + ****************************************************************************/ + +static int exfatfs_statfs(FAR struct inode *mountpt, FAR struct statfs *buf) +{ + FAR struct exfatfs_mountpt_s *fs; + + /* Get the mountpoint private data from the inode structure */ + + fs = mountpt->i_private; + + /* Return something for the file system description */ + + memset(buf, 0, sizeof(*buf)); + buf->f_type = EXFAT_SUPER_MAGIC; + buf->f_namelen = EXFAT_ENAME_MAX; + buf->f_bsize = CLUSTER_SIZE(*(fs->ef.sb)); + buf->f_blocks = le32_to_cpu(fs->ef.sb->cluster_count); + buf->f_bfree = exfat_count_free_clusters(&fs->ef); + buf->f_bavail = exfat_count_free_clusters(&fs->ef); + + return OK; +} + +/**************************************************************************** + * Name: exfatfs_unlink + * + * Description: Remove a file + * + ****************************************************************************/ + +static int exfatfs_unlink(FAR struct inode *mountpt, + FAR const char *relpath) +{ + FAR struct exfatfs_mountpt_s *fs; + FAR struct exfat_node *node; + int ret; + + /* Get the mountpoint private data from the inode structure */ + + fs = mountpt->i_private; + + /* Take the semaphore */ + + ret = exfatfs_semtake(fs); + if (ret < 0) + { + return ret; + } + + ret = exfat_lookup(&fs->ef, &node, relpath); + if (ret != 0) + { + goto errout; + } + + ret = exfat_unlink(&fs->ef, node); + exfatfs_release_node(&fs->ef, node); + if (ret != 0) + { + goto errout; + } + +errout: + exfatfs_semgive(fs); + return ret; +} + +/**************************************************************************** + * Name: exfatfs_mkdir + * + * Description: Create a directory + * + ****************************************************************************/ + +static int exfatfs_mkdir(FAR struct inode *mountpt, FAR const char *relpath, + mode_t mode) +{ + FAR struct exfatfs_mountpt_s *fs; + int ret; + + /* Get the mountpoint private data from the inode structure */ + + fs = mountpt->i_private; + + /* Take the semaphore */ + + ret = exfatfs_semtake(fs); + if (ret < 0) + { + return ret; + } + + ret = exfat_mkdir(&fs->ef, relpath); + exfatfs_semgive(fs); + + return ret; +} + +/**************************************************************************** + * Name: exfatfs_rmdir + * + * Description: Remove a directory + * + ****************************************************************************/ + +static int exfatfs_rmdir(FAR struct inode *mountpt, FAR const char *relpath) +{ + FAR struct exfatfs_mountpt_s *fs; + FAR struct exfat_node *node; + int ret; + + /* Get the mountpoint private data from the inode structure */ + + fs = mountpt->i_private; + + /* Take the semaphore */ + + ret = exfatfs_semtake(fs); + if (ret < 0) + { + return ret; + } + + ret = exfat_lookup(&fs->ef, &node, relpath); + if (ret != 0) + { + goto errout; + } + + ret = exfat_rmdir(&fs->ef, node); + exfatfs_release_node(&fs->ef, node); + if (ret != 0) + { + goto errout; + } + +errout: + exfatfs_semgive(fs); + return ret; +} + +/**************************************************************************** + * Name: exfatfs_rename + * + * Description: Rename a file or directory + * + ****************************************************************************/ + +static int exfatfs_rename(FAR struct inode *mountpt, + FAR const char *oldrelpath, + FAR const char *newrelpath) +{ + FAR struct exfatfs_mountpt_s *fs; + int ret; + + /* Get the mountpoint private data from the inode structure */ + + fs = mountpt->i_private; + + /* Take the semaphore */ + + ret = exfatfs_semtake(fs); + if (ret < 0) + { + return ret; + } + + ret = exfat_rename(&fs->ef, oldrelpath, newrelpath); + exfatfs_semgive(fs); + + return ret; +} + +/**************************************************************************** + * Name: exfatfs_stat + * + * Description: Return information about a file or directory + * + ****************************************************************************/ + +static int exfatfs_stat(FAR struct inode *mountpt, FAR const char *relpath, + FAR struct stat *buf) +{ + FAR struct exfatfs_mountpt_s *fs; + FAR struct exfat_node *node; + int ret; + + /* Get the mountpoint private data from the inode structure */ + + fs = mountpt->i_private; + + /* Call LFS to get file size */ + + ret = exfatfs_semtake(fs); + if (ret < 0) + { + return ret; + } + + ret = exfat_lookup(&fs->ef, &node, relpath); + if (ret != 0) + { + goto errout; + } + + exfat_stat(&fs->ef, node, buf); + exfatfs_release_node(&fs->ef, node); + +errout: + exfatfs_semgive(fs); + return ret; +} + +/**************************************************************************** + * Name: exfatfs_chstat + ****************************************************************************/ + +static int exfatfs_chstat(FAR struct inode *mountpt, FAR const char *relpath, + FAR const struct stat *buf, int flags) +{ + FAR struct exfatfs_mountpt_s *fs; + FAR struct exfat_node *node; + int ret; + + /* Get the mountpoint private data from the inode structure */ + + fs = mountpt->i_private; + + /* Call LFS to get file size */ + + ret = exfatfs_semtake(fs); + if (ret < 0) + { + return ret; + } + + ret = exfat_lookup(&fs->ef, &node, relpath); + if (ret != 0) + { + goto errout; + } + + ret = exfatfs_chstat_i(fs, node, buf, flags); + exfatfs_release_node(&fs->ef, node); + +errout: + exfatfs_semgive(fs); + return ret; +} + +/**************************************************************************** + * Name: exfatfs_format + ****************************************************************************/ + +static int exfatfs_format(FAR struct exfat *ef, FAR const char *spec, + int sector_bits, int spc_bits, + FAR const char *volume_label, + uint32_t volume_serial, uint64_t first_sector) +{ + FAR struct exfat_dev *dev; + int ret; + + dev = exfat_open(spec, EXFAT_MODE_RW); + if (dev == NULL) + { + return -ENOMEM; + } + + ret = exfat_mkfs(dev, sector_bits, spc_bits, volume_label, + volume_serial, first_sector); + if (ret != 0) + { + exfat_close(dev); + return ret; + } + + return exfat_close(dev); +} diff --git a/fs/mount/fs_gettype.c b/fs/mount/fs_gettype.c index 326ce478fe8..f6082fbe09e 100644 --- a/fs/mount/fs_gettype.c +++ b/fs/mount/fs_gettype.c @@ -105,6 +105,12 @@ FAR const char *fs_gettype(FAR struct statfs *statbuf) break; #endif +#ifdef CONFIG_FS_EXFAT + case EXFAT_SUPER_MAGIC: + fstype = "exfatfs"; + break; +#endif + #ifdef CONFIG_NFS case NFS_SUPER_MAGIC: fstype = "nfs"; diff --git a/fs/mount/fs_mount.c b/fs/mount/fs_mount.c index 39bb8da53b9..c0d01756141 100644 --- a/fs/mount/fs_mount.c +++ b/fs/mount/fs_mount.c @@ -51,7 +51,8 @@ */ #if defined(CONFIG_FS_FAT) || defined(CONFIG_FS_ROMFS) || \ - defined(CONFIG_FS_SMARTFS) || defined(CONFIG_FS_LITTLEFS) + defined(CONFIG_FS_SMARTFS) || defined(CONFIG_FS_LITTLEFS) || \ + defined(CONFIG_FS_EXFAT) # define BDFS_SUPPORT 1 #endif @@ -101,6 +102,9 @@ extern const struct mountpt_operations smartfs_operations; #ifdef CONFIG_FS_LITTLEFS extern const struct mountpt_operations littlefs_operations; #endif +#ifdef CONFIG_FS_EXFAT +extern const struct mountpt_operations exfatfs_operations; +#endif static const struct fsmap_t g_bdfsmap[] = { @@ -115,6 +119,9 @@ static const struct fsmap_t g_bdfsmap[] = #endif #ifdef CONFIG_FS_LITTLEFS { "littlefs", &littlefs_operations }, +#endif +#ifdef CONFIG_FS_EXFAT + { "exfatfs", &exfatfs_operations }, #endif { NULL, NULL }, }; diff --git a/include/nuttx/fs/dirent.h b/include/nuttx/fs/dirent.h index a9a9675469e..a5b80eea33f 100644 --- a/include/nuttx/fs/dirent.h +++ b/include/nuttx/fs/dirent.h @@ -279,6 +279,9 @@ struct fs_dirent_s #ifdef CONFIG_FS_LITTLEFS FAR void *littlefs; #endif +#ifdef CONFIG_FS_EXFAT + FAR void *exfat; +#endif #ifdef CONFIG_FS_UNIONFS struct fs_unionfsdir_s unionfs; #endif diff --git a/include/sys/statfs.h b/include/sys/statfs.h index 50ea1982de7..b3dffe1165c 100644 --- a/include/sys/statfs.h +++ b/include/sys/statfs.h @@ -82,6 +82,7 @@ #define _XIAFS_SUPER_MAGIC 0x012fd16d #define SPIFFS_SUPER_MAGIC 0x20090315 #define LITTLEFS_SUPER_MAGIC 0x0a732923 +#define EXFAT_SUPER_MAGIC 0x45584641 /* NuttX specific file-systems */