fs_automount: add options for signaling when volume is mounted and unmounted

Signed-off-by: Petro Karashchenko <petro.karashchenko@gmail.com>
This commit is contained in:
Petro Karashchenko 2022-01-25 14:44:54 +02:00 committed by Xiang Xiao
parent f826e053ae
commit 76a9af90c3
5 changed files with 392 additions and 7 deletions

View File

@ -255,7 +255,7 @@ void sam_automount_initialize(void)
{
ferr("ERROR: Failed to initialize auto-mounter for HSMCI0\n");
}
#endif
#endif /* CONFIG_SAMV7_HSMCI0_AUTOMOUNT */
}
/****************************************************************************

View File

@ -42,6 +42,22 @@ config FS_AUTOMOUNTER_DEBUG
system debug is not enable. This is useful primarily for in vivo
unit testing of the auto-mount feature.
config FS_AUTOMOUNTER_DRIVER
bool "Auto-mounter driver"
default n
depends on FS_AUTOMOUNTER
---help---
Enabling this option will lead to registering of a character driver
on FS_AUTOMOUNTER_VFS_PATH + mount point path for auto-mounter.
Example: /var/mnt/sdcard0
config FS_AUTOMOUNTER_VFS_PATH
string "Path to auto-mounter driver"
default "/var"
depends on FS_AUTOMOUNTER_DRIVER
---help---
The path to where auto-mounter driver will exist in the VFS namespace.
config FS_NEPOLL_DESCRIPTORS
int "Maximum number of default epoll descriptors for epoll_create1(2)"
default 8

View File

@ -40,6 +40,14 @@
#include <nuttx/wqueue.h>
#include <nuttx/fs/automount.h>
#ifdef CONFIG_FS_AUTOMOUNTER_DRIVER
# include <stdio.h>
# include <nuttx/signal.h>
# include <nuttx/fs/fs.h>
# include <nuttx/fs/ioctl.h>
#endif /* CONFIG_FS_AUTOMOUNTER_DRIVER */
#include "inode/inode.h"
/****************************************************************************
@ -51,6 +59,10 @@
* CONFIG_FS_AUTOMOUNTER - Enables AUTOMOUNT support
*/
#ifndef CONFIG_FS_AUTOMOUNTER_VFS_PATH
# define CONFIG_FS_AUTOMOUNTER_VFS_PATH "/var"
#endif
/* Pre-requisites */
#ifndef CONFIG_SCHED_WORKQUEUE
@ -75,24 +87,307 @@ struct automounter_state_s
struct wdog_s wdog; /* Delay to retry un-mounts */
bool mounted; /* True: Volume has been mounted */
bool inserted; /* True: Media has been inserted */
#ifdef CONFIG_FS_AUTOMOUNTER_DRIVER
sem_t exclsem; /* Supports exclusive access to the device */
bool registered; /* True: if driver has been registered */
/* The following is a singly linked list of open references to the
* automounter device.
*/
FAR struct automounter_open_s *ao_open;
#endif /* CONFIG_FS_AUTOMOUNTER_DRIVER */
};
/* This structure describes the state of one open automounter driver
* instance
*/
#ifdef CONFIG_FS_AUTOMOUNTER_DRIVER
struct automounter_open_s
{
/* Supports a singly linked list */
FAR struct automounter_open_s *ao_flink;
/* Mount event notification information */
pid_t ao_pid;
struct automount_notify_s ao_notify;
struct sigwork_s ao_work;
};
#endif /* CONFIG_FS_AUTOMOUNTER_DRIVER */
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
#ifdef CONFIG_FS_AUTOMOUNTER_DRIVER
static void automount_notify(FAR struct automounter_state_s *priv);
static int automount_open(FAR struct file *filep);
static int automount_close(FAR struct file *filep);
static int automount_ioctl(FAR struct file *filep, int cmd,
unsigned long arg);
#endif /* CONFIG_FS_AUTOMOUNTER_DRIVER */
static int automount_findinode(FAR const char *path);
static void automount_mount(FAR struct automounter_state_s *priv);
static int automount_unmount(FAR struct automounter_state_s *priv);
static void automount_timeout(wdparm_t arg);
static void automount_worker(FAR void *arg);
static int automount_interrupt(FAR const struct automount_lower_s *lower,
FAR void *arg, bool inserted);
FAR void *arg, bool inserted);
/****************************************************************************
* Private Data
****************************************************************************/
#ifdef CONFIG_FS_AUTOMOUNTER_DRIVER
static const struct file_operations automount_fops =
{
automount_open, /* open */
automount_close, /* close */
NULL, /* read */
NULL, /* write */
NULL, /* seek */
automount_ioctl, /* ioctl */
NULL /* poll */
#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
, NULL /* unlink */
#endif
};
#endif /* CONFIG_FS_AUTOMOUNTER_DRIVER */
/****************************************************************************
* Private Functions
****************************************************************************/
#ifdef CONFIG_FS_AUTOMOUNTER_DRIVER
/****************************************************************************
* Name: automount_notify
****************************************************************************/
static void automount_notify(FAR struct automounter_state_s *priv)
{
FAR struct automounter_open_s *opriv;
int ret;
/* Get exclusive access to the driver structure */
ret = nxsem_wait_uninterruptible(&priv->exclsem);
if (ret < 0)
{
ierr("ERROR: nxsem_wait_uninterruptible failed: %d\n", ret);
return;
}
/* Visit each opened reference to the device */
for (opriv = priv->ao_open; opriv != NULL; opriv = opriv->ao_flink)
{
/* Have any signal events occurred? */
if ((priv->mounted && opriv->ao_notify.an_mount) ||
(!priv->mounted && opriv->ao_notify.an_umount))
{
/* Yes.. Signal the waiter */
opriv->ao_notify.an_event.sigev_value.sival_int = priv->mounted;
nxsig_notification(opriv->ao_pid, &opriv->ao_notify.an_event,
SI_QUEUE, &opriv->ao_work);
}
}
nxsem_post(&priv->exclsem);
}
/****************************************************************************
* Name: automount_open
****************************************************************************/
static int automount_open(FAR struct file *filep)
{
FAR struct inode *inode = filep->f_inode;
FAR struct automounter_state_s *priv = inode->i_private;
FAR struct automounter_open_s *opriv;
int ret;
/* Get exclusive access to the driver structure */
ret = nxsem_wait(&priv->exclsem);
if (ret < 0)
{
ierr("ERROR: nxsem_wait failed: %d\n", ret);
return ret;
}
/* Allocate a new open structure */
opriv = (FAR struct automounter_open_s *)kmm_zalloc(
sizeof(struct automounter_open_s));
if (opriv == NULL)
{
ierr("ERROR: Failed to allocate open structure\n");
ret = -ENOMEM;
goto errout_with_exclsem;
}
/* Attach the open structure to the device */
opriv->ao_flink = priv->ao_open;
priv->ao_open = opriv;
/* Attach the open structure to the file structure */
filep->f_priv = (FAR void *)opriv;
ret = OK;
errout_with_exclsem:
nxsem_post(&priv->exclsem);
return ret;
}
/****************************************************************************
* Name: automount_close
****************************************************************************/
static int automount_close(FAR struct file *filep)
{
FAR struct inode *inode;
FAR struct automounter_state_s *priv;
FAR struct automounter_open_s *opriv;
FAR struct automounter_open_s *curr;
FAR struct automounter_open_s *prev;
int ret;
DEBUGASSERT(filep && filep->f_priv && filep->f_inode);
opriv = filep->f_priv;
inode = filep->f_inode;
DEBUGASSERT(inode->i_private);
priv = (FAR struct automounter_state_s *)inode->i_private;
/* Get exclusive access to the driver structure */
ret = nxsem_wait(&priv->exclsem);
if (ret < 0)
{
ierr("ERROR: nxsem_wait failed: %d\n", ret);
return ret;
}
/* Find the open structure in the list of open structures for the device */
for (prev = NULL, curr = priv->ao_open;
curr != NULL && curr != opriv;
prev = curr, curr = curr->ao_flink);
DEBUGASSERT(curr);
if (curr == NULL)
{
ierr("ERROR: Failed to find open entry\n");
ret = -ENOENT;
goto errout_with_exclsem;
}
/* Remove the structure from the device */
if (prev != NULL)
{
prev->ao_flink = opriv->ao_flink;
}
else
{
priv->ao_open = opriv->ao_flink;
}
/* Cancel any pending notification */
nxsig_cancel_notification(&opriv->ao_work);
/* And free the open structure */
kmm_free(opriv);
ret = OK;
errout_with_exclsem:
nxsem_post(&priv->exclsem);
return ret;
}
/****************************************************************************
* Name: automount_ioctl
****************************************************************************/
static int automount_ioctl(FAR struct file *filep, int cmd,
unsigned long arg)
{
FAR struct inode *inode;
FAR struct automounter_state_s *priv;
FAR struct automounter_open_s *opriv;
int ret;
DEBUGASSERT(filep && filep->f_priv && filep->f_inode);
opriv = filep->f_priv;
inode = filep->f_inode;
DEBUGASSERT(inode->i_private);
priv = (FAR struct automounter_state_s *)inode->i_private;
/* Get exclusive access to the driver structure */
ret = nxsem_wait(&priv->exclsem);
if (ret < 0)
{
ierr("ERROR: nxsem_wait failed: %d\n", ret);
return ret;
}
/* Handle the ioctl command */
ret = -EINVAL;
switch (cmd)
{
/* Command: FIOC_NOTIFY
* Description: Register to receive a signal whenever volume is mounted
* or unmounted by automounter.
* Argument: A read-only pointer to an instance of struct
* automount_notify_s
* Return: Zero (OK) on success. Minus one will be returned on
* failure with the errno value set appropriately.
*/
case FIOC_NOTIFY:
{
FAR struct automount_notify_s *notify =
(FAR struct automount_notify_s *)((uintptr_t)arg);
if (notify != NULL)
{
/* Save the notification events */
opriv->ao_notify.an_mount = notify->an_mount;
opriv->ao_notify.an_umount = notify->an_umount;
opriv->ao_notify.an_event = notify->an_event;
opriv->ao_pid = getpid();
ret = OK;
}
}
break;
default:
ierr("ERROR: Unrecognized command: %d\n", cmd);
ret = -ENOTTY;
break;
}
nxsem_post(&priv->exclsem);
return ret;
}
#endif /* CONFIG_FS_AUTOMOUNTER_DRIVER */
/****************************************************************************
* Name: automount_findinode
*
@ -229,6 +524,11 @@ static void automount_mount(FAR struct automounter_state_s *priv)
/* Indicate that the volume is mounted */
priv->mounted = true;
#ifdef CONFIG_FS_AUTOMOUNTER_DRIVER
automount_notify(priv);
#endif /* CONFIG_FS_AUTOMOUNTER_DRIVER */
break;
default:
@ -313,7 +613,15 @@ static int automount_unmount(FAR struct automounter_state_s *priv)
* media. Nice job, Mr. user.
*/
priv->mounted = false;
if (priv->mounted)
{
priv->mounted = false;
#ifdef CONFIG_FS_AUTOMOUNTER_DRIVER
automount_notify(priv);
#endif /* CONFIG_FS_AUTOMOUNTER_DRIVER */
}
return OK;
default:
@ -515,6 +823,9 @@ FAR void *automount_initialize(FAR const struct automount_lower_s *lower)
{
FAR struct automounter_state_s *priv;
int ret;
#ifdef CONFIG_FS_AUTOMOUNTER_DRIVER
char devpath[PATH_MAX];
#endif /* CONFIG_FS_AUTOMOUNTER_DRIVER */
finfo("lower=%p\n", lower);
DEBUGASSERT(lower);
@ -524,7 +835,7 @@ FAR void *automount_initialize(FAR const struct automount_lower_s *lower)
priv = (FAR struct automounter_state_s *)
kmm_zalloc(sizeof(struct automounter_state_s));
if (!priv)
if (priv == NULL)
{
ferr("ERROR: Failed to allocate state structure\n");
return NULL;
@ -551,6 +862,27 @@ FAR void *automount_initialize(FAR const struct automount_lower_s *lower)
ferr("ERROR: Failed to schedule work: %d\n", ret);
}
#ifdef CONFIG_FS_AUTOMOUNTER_DRIVER
/* Initialize the new automount driver instance */
nxsem_init(&priv->exclsem, 0, 1);
/* Register driver */
sprintf(devpath, CONFIG_FS_AUTOMOUNTER_VFS_PATH "%s", lower->mountpoint);
ret = register_driver(devpath, &automount_fops, 0444, priv);
if (ret < 0)
{
ferr("ERROR: Failed to register automount driver: %d\n", ret);
automount_uninitialize(priv);
return NULL;
}
priv->registered = true;
#endif /* CONFIG_FS_AUTOMOUNTER_DRIVER */
/* Attach and enable automounter interrupts */
ret = AUTOMOUNT_ATTACH(lower, automount_interrupt, priv);
@ -594,6 +926,20 @@ void automount_uninitialize(FAR void *handle)
AUTOMOUNT_DISABLE(lower);
AUTOMOUNT_DETACH(lower);
#ifdef CONFIG_FS_AUTOMOUNTER_DRIVER
if (priv->registered)
{
char devpath[PATH_MAX];
sprintf(devpath, CONFIG_FS_AUTOMOUNTER_VFS_PATH "%s",
lower->mountpoint);
unregister_driver(devpath);
}
nxsem_destroy(&priv->exclsem);
#endif /* CONFIG_FS_AUTOMOUNTER_DRIVER */
/* Cancel the watchdog timer */
wd_cancel(&priv->wdog);

View File

@ -27,6 +27,7 @@
#include <stdint.h>
#include <stdbool.h>
#include <signal.h>
#include <nuttx/irq.h>
@ -70,6 +71,22 @@ typedef CODE int
(*automount_handler_t)(FAR const struct automount_lower_s *lower,
FAR void *arg, bool inserted);
#ifdef CONFIG_FS_AUTOMOUNTER_DRIVER
/* A reference to this structure is provided with the FIOC_NOTIFY IOCTL
* command and describes the conditions under which the client would like
* to receive notification.
*/
struct automount_notify_s
{
bool an_mount; /* FS mount to be notified */
bool an_umount; /* FS umount to be notified */
struct sigevent an_event; /* Describe the way a task is to be notified */
};
#endif /* CONFIG_FS_AUTOMOUNTER_DRIVER */
/* A reference to a structure of this type must be passed to the FS
* automounter. This structure provides information about the volume to be
* mounted and provides board-specific hooks.

View File

@ -147,11 +147,12 @@
#define FIOC_INTEGRITY _FIOC(0x0005) /* Run a consistency check on the
* file system media.
* IN: None
* OUT: None */
* OUT: None
*/
#define FIOC_DUMP _FIOC(0x0006) /* Dump logical content of media.
* IN: None
* OUT: None */
* OUT: None
*/
#define FIONREAD _FIOC(0x0007) /* IN: Location to return value (int *)
* OUT: Bytes readable from this fd
*/
@ -176,6 +177,11 @@
#define FIONCLEX _FIOC(0x000d) /* IN: None
* OUT: None
*/
#define FIOC_NOTIFY _FIOC(0x000e) /* IN: Pointer to struct automount_notify_s
* holding automount notification
* configuration
* OUT: None
*/
/* NuttX file system ioctl definitions **************************************/