diff --git a/Documentation/components/filesystem/index.rst b/Documentation/components/filesystem/index.rst index 3e40bdf42cf..1e44ca94a65 100644 --- a/Documentation/components/filesystem/index.rst +++ b/Documentation/components/filesystem/index.rst @@ -502,12 +502,45 @@ belonging to a unified interface: This works like ``sync()`` but instead of the file, it syncs the entire filesystem's metadata. - + :param FAR struct inode * mountpt: Mount point inode of the file system. :returns: Status of syncing file system metadata operation. :retval OK (0): Success. :retval < 0: Error. +.. c:function:: int ioctldir(FAR struct inode *mountpt, FAR struct fs_dirent_s *dir, int cmd, unsigned long arg) + + Carries out an ioctl command issued on a descriptor for the mountpoint + directory rather than on a file inside the volume. It takes the same + ``(mountpt, dir)`` pair as the other directory operations. Typical commands + act on the volume as a whole rather than on any one open file, such as + ``FIOC_REFORMAT``, ``FIOC_OPTIMIZE`` or ``FIOC_INTEGRITY``. + + The command reaches this method when it is issued on a descriptor for the + mountpoint directory, which the caller obtains with + ``open(mountpoint, O_RDONLY | O_DIRECTORY)``. No file inside the volume + needs to be open, which matters for commands that a file system refuses to + perform while one is. + + The method is consulted before the VFS acts on the command, so it must + answer ``-ENOTTY`` for anything it does not recognize; the VFS then applies + its own handling. + + This method is optional. A file system that leaves it ``NULL`` behaves as + before: the VFS answers ``-ENOTTY`` for any command on a directory + descriptor that it does not handle itself. + + :param FAR struct inode * mountpt: Mount point inode of the file system. + :param FAR struct fs_dirent_s * dir: The open mountpoint directory the + command was issued on. + :param int cmd: The command to carry out, as defined in + ``include/nuttx/fs/ioctl.h``. + :param unsigned long arg: Additional argument, if the command requires one. + :returns: Status of the ioctl operation. + :retval OK (0): Success. + :retval -ENOTTY: The file system does not recognize the command. + :retval < 0: Error. + The file systems can have their own implementations for these functions under-the-hood, but the user does not have to worry about the underlying file diff --git a/Documentation/components/filesystem/nxffs.rst b/Documentation/components/filesystem/nxffs.rst index 50acafbc9fe..bdd5f3d88c9 100644 --- a/Documentation/components/filesystem/nxffs.rst +++ b/Documentation/components/filesystem/nxffs.rst @@ -143,6 +143,18 @@ The file system supports to ioctls: file system will increase the amount of wear on the FLASH if you use this frequently! +Both act on the volume rather than on any one file, so issue them on a +descriptor for the mountpoint directory:: + + int fd = open("/mnt/nxffs", O_RDONLY | O_DIRECTORY); + ioctl(fd, FIOC_REFORMAT, 0); + close(fd); + +``FIOC_REFORMAT`` in particular is only reachable this way. It refuses to +run while any file on the volume is open, so issuing it on a descriptor for +a file *inside* the volume can never succeed -- that descriptor is itself +such a file, and the request returns ``-EBUSY``. + Things to Do ============ diff --git a/fs/nxffs/nxffs.h b/fs/nxffs/nxffs.h index e6394776c32..8c0acb2f744 100644 --- a/fs/nxffs/nxffs.h +++ b/fs/nxffs/nxffs.h @@ -1094,6 +1094,8 @@ ssize_t nxffs_read(FAR struct file *filep, FAR char *buffer, size_t buflen); ssize_t nxffs_write(FAR struct file *filep, FAR const char *buffer, size_t buflen); int nxffs_ioctl(FAR struct file *filep, int cmd, unsigned long arg); +int nxffs_ioctldir(FAR struct inode *mountpt, FAR struct fs_dirent_s *dir, + int cmd, unsigned long arg); int nxffs_dup(FAR const struct file *oldp, FAR struct file *newp); int nxffs_fstat(FAR const struct file *filep, FAR struct stat *buf); diff --git a/fs/nxffs/nxffs_initialize.c b/fs/nxffs/nxffs_initialize.c index 0a0b754b499..5a3314cb132 100644 --- a/fs/nxffs/nxffs_initialize.c +++ b/fs/nxffs/nxffs_initialize.c @@ -86,7 +86,9 @@ const struct mountpt_operations g_nxffs_operations = NULL, /* rmdir -- no directories */ NULL, /* rename -- cannot rename in place if name is longer */ nxffs_stat, /* stat */ - NULL /* chstat */ + NULL, /* chstat */ + NULL, /* syncfs */ + nxffs_ioctldir /* ioctldir */ }; /**************************************************************************** diff --git a/fs/nxffs/nxffs_ioctl.c b/fs/nxffs/nxffs_ioctl.c index 6d28f344975..51394b513a2 100644 --- a/fs/nxffs/nxffs_ioctl.c +++ b/fs/nxffs/nxffs_ioctl.c @@ -38,33 +38,23 @@ #include "nxffs.h" /**************************************************************************** - * Public Functions + * Private Functions ****************************************************************************/ /**************************************************************************** - * Name: nxffs_ioctl + * Name: nxffs_volume_cmd * * Description: - * Standard mountpoint ioctl method. + * Carry out an ioctl against the volume. Shared by the per-file and the + * per-volume entry points, which differ only in how they name the volume. * ****************************************************************************/ -int nxffs_ioctl(FAR struct file *filep, int cmd, unsigned long arg) +static int nxffs_volume_cmd(FAR struct nxffs_volume_s *volume, int cmd, + unsigned long arg) { - FAR struct nxffs_volume_s *volume; int ret; - finfo("cmd: %d arg: %08lx\n", cmd, arg); - - /* Sanity checks */ - - DEBUGASSERT(filep->f_priv != NULL); - - /* Recover the file system state from the open file */ - - volume = filep->f_inode->i_private; - DEBUGASSERT(volume != NULL); - /* Get exclusive access to the volume. Note that the volume lock * protects the open file list. */ @@ -73,7 +63,7 @@ int nxffs_ioctl(FAR struct file *filep, int cmd, unsigned long arg) if (ret < 0) { ferr("ERROR: nxmutex_lock failed: %d\n", ret); - goto errout; + return ret; } /* Only a reformat and optimize commands are supported */ @@ -113,6 +103,62 @@ int nxffs_ioctl(FAR struct file *filep, int cmd, unsigned long arg) errout_with_lock: nxmutex_unlock(&volume->lock); -errout: return ret; } + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nxffs_ioctl + * + * Description: + * Standard mountpoint ioctl method. + * + ****************************************************************************/ + +int nxffs_ioctl(FAR struct file *filep, int cmd, unsigned long arg) +{ + FAR struct nxffs_volume_s *volume; + + finfo("cmd: %d arg: %08lx\n", cmd, arg); + + /* Sanity checks */ + + DEBUGASSERT(filep->f_priv != NULL); + + /* Recover the file system state from the open file */ + + volume = filep->f_inode->i_private; + DEBUGASSERT(volume != NULL); + + return nxffs_volume_cmd(volume, cmd, arg); +} + +/**************************************************************************** + * Name: nxffs_ioctldir + * + * Description: + * ioctl method for a descriptor on the mountpoint directory. + * FIOC_REFORMAT is only reachable this way: it refuses to run while any + * file on the volume is open, and the per-file method is itself such a + * file. The open directory is not needed -- the volume is recovered from + * the mountpoint inode -- so dir is unused. + * + ****************************************************************************/ + +int nxffs_ioctldir(FAR struct inode *mountpt, FAR struct fs_dirent_s *dir, + int cmd, unsigned long arg) +{ + FAR struct nxffs_volume_s *volume; + + UNUSED(dir); + + finfo("cmd: %d arg: %08lx\n", cmd, arg); + + volume = mountpt->i_private; + DEBUGASSERT(volume != NULL); + + return nxffs_volume_cmd(volume, cmd, arg); +} diff --git a/fs/vfs/fs_dir.c b/fs/vfs/fs_dir.c index bf8b899bb9a..6b93e90e7a0 100644 --- a/fs/vfs/fs_dir.c +++ b/fs/vfs/fs_dir.c @@ -556,15 +556,34 @@ static off_t dir_seek(FAR struct file *filep, off_t offset, int whence) static int dir_ioctl(FAR struct file *filep, int cmd, unsigned long arg) { FAR struct fs_dirent_s *dir = filep->f_priv; - int ret = OK; + int ret = -ENOTTY; - if (cmd == FIOC_FILEPATH) +#ifndef CONFIG_DISABLE_MOUNTPOINT + /* If this directory belongs to a mounted volume whose file system offers + * volume-wide commands, give it the first chance: it is the one route to + * them that does not require an unrelated file to be open. Anything the + * file system does not recognize falls through to the VFS defaults. + */ + + if (INODE_IS_MOUNTPT(dir->fd_root) && + dir->fd_root->u.i_mops != NULL && + dir->fd_root->u.i_mops->ioctldir != NULL) { - strlcpy((FAR char *)(uintptr_t)arg, dir->fd_path, PATH_MAX); + ret = dir->fd_root->u.i_mops->ioctldir(dir->fd_root, dir, cmd, arg); } - else if (cmd != BIOC_FLUSH) +#endif + + if (ret == -ENOTTY) { - ret = -ENOTTY; + if (cmd == FIOC_FILEPATH) + { + strlcpy((FAR char *)(uintptr_t)arg, dir->fd_path, PATH_MAX); + ret = OK; + } + else if (cmd == BIOC_FLUSH) + { + ret = OK; + } } return ret; diff --git a/include/nuttx/fs/fs.h b/include/nuttx/fs/fs.h index f7c44e387a4..818f734e5a8 100644 --- a/include/nuttx/fs/fs.h +++ b/include/nuttx/fs/fs.h @@ -360,6 +360,33 @@ struct mountpt_operations CODE int (*chstat)(FAR struct inode *mountpt, FAR const char *relpath, FAR const struct stat *buf, int flags); CODE int (*syncfs)(FAR struct inode *mountpt); + + /* ioctl issued on a descriptor for the mountpoint directory rather than + * on a file inside the volume. It belongs with the directory operations + * above -- it takes the same (mountpt, dir) pair as opendir/readdir -- but + * is placed here at the end so the positional initialisers every file + * system uses stay unchanged; a file system that does not implement it + * simply leaves the slot NULL. + * + * Commands such as FIOC_REFORMAT, FIOC_OPTIMIZE and FIOC_INTEGRITY act on + * the volume, not on any one file, but the only route to a file system + * has historically been the per-file ioctl method. That forces a caller + * to open an unrelated file just to name the volume, and a file system + * whose volume operation is incompatible with an open file then cannot + * implement the command at all. + * + * A file system that has such commands implements this method; the ioctl + * arrives with the mountpoint inode and the open directory, and no open + * file in sight. It is consulted before the VFS acts on the command, so + * it must answer -ENOTTY for anything it does not recognise; the VFS then + * applies its own handling. Leaving it NULL keeps the previous behaviour, + * in which the VFS answers -ENOTTY for any command it does not handle + * itself. + */ + + CODE int (*ioctldir)(FAR struct inode *mountpt, + FAR struct fs_dirent_s *dir, + int cmd, unsigned long arg); }; #endif /* CONFIG_DISABLE_MOUNTPOINT */