Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 34 additions & 1 deletion Documentation/components/filesystem/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
12 changes: 12 additions & 0 deletions Documentation/components/filesystem/nxffs.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
============

Expand Down
2 changes: 2 additions & 0 deletions fs/nxffs/nxffs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
4 changes: 3 additions & 1 deletion fs/nxffs/nxffs_initialize.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
};

/****************************************************************************
Expand Down
82 changes: 64 additions & 18 deletions fs/nxffs/nxffs_ioctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand All @@ -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 */
Expand Down Expand Up @@ -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);
}
29 changes: 24 additions & 5 deletions fs/vfs/fs_dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
27 changes: 27 additions & 0 deletions include/nuttx/fs/fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */

Expand Down
Loading