123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611 |
- /****************************************************************************
- * fs/mount/fs_automount.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>
- #if defined(CONFIG_FS_AUTOMOUNTER_DEBUG) && !defined(CONFIG_DEBUG_FS)
- # define CONFIG_DEBUG_FS 1
- #endif
- #include <sys/mount.h>
- #include <stdbool.h>
- #include <errno.h>
- #include <assert.h>
- #include <debug.h>
- #include <nuttx/wdog.h>
- #include <nuttx/kmalloc.h>
- #include <nuttx/wqueue.h>
- #include <nuttx/fs/automount.h>
- #include "inode/inode.h"
- /****************************************************************************
- * Pre-processor Definitions
- ****************************************************************************/
- /* Configuration ************************************************************
- *
- * CONFIG_FS_AUTOMOUNTER - Enables AUTOMOUNT support
- */
- /* Pre-requisites */
- #ifndef CONFIG_SCHED_WORKQUEUE
- # error Work queue support is required (CONFIG_SCHED_WORKQUEUE)
- #endif
- /* Return Values */
- #define OK_EXIST 0
- #define OK_NOENT 1
- /****************************************************************************
- * Private Types
- ****************************************************************************/
- /* This structure describes the state of the automounter */
- struct automounter_state_s
- {
- FAR const struct automount_lower_s *lower; /* Board level interfaces */
- struct work_s work; /* Work queue support */
- struct wdog_s wdog; /* Delay to retry un-mounts */
- bool mounted; /* True: Volume has been mounted */
- bool inserted; /* True: Media has been inserted */
- };
- /****************************************************************************
- * Private Function Prototypes
- ****************************************************************************/
- 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);
- /****************************************************************************
- * Private Functions
- ****************************************************************************/
- /****************************************************************************
- * Name: automount_findinode
- *
- * Description:
- * Find the mountpoint inode in the inode tree.
- *
- * Input Parameters:
- * mntpath - Mountpoint path
- *
- * Returned Value:
- * OK_EXIST if the inode exists
- * OK_NOENT if the inode does not exist
- * Negated errno if some failure occurs
- *
- ****************************************************************************/
- static int automount_findinode(FAR const char *path)
- {
- struct inode_search_s desc;
- int ret;
- /* Make sure that we were given a path */
- DEBUGASSERT(path != NULL);
- /* Get exclusive access to the in-memory inode tree. */
- ret = inode_semtake();
- if (ret < 0)
- {
- return ret;
- }
- /* Find the inode */
- SETUP_SEARCH(&desc, path, false);
- ret = inode_search(&desc);
- /* Did we find it? */
- if (ret < 0)
- {
- /* No.. Not found */
- ret = OK_NOENT;
- }
- /* Yes.. is it a mount point? */
- else if (INODE_IS_MOUNTPT(desc.node))
- {
- /* Yes.. we found a mountpoint at this path */
- ret = OK_EXIST;
- }
- else
- {
- /* No.. then something is in the way */
- ret = -ENOTDIR;
- }
- /* Relinquish our exclusive access to the inode try and return the result */
- inode_semgive();
- RELEASE_SEARCH(&desc);
- return ret;
- }
- /****************************************************************************
- * Name: automount_mount
- *
- * Description:
- * Media has been inserted, mount the volume.
- *
- * Input Parameters:
- * priv - A reference to out private state structure
- *
- * Returned Value:
- * None
- *
- ****************************************************************************/
- static void automount_mount(FAR struct automounter_state_s *priv)
- {
- FAR const struct automount_lower_s *lower = priv->lower;
- int ret;
- finfo("Mounting %s\n", lower->mountpoint);
- /* Check if the something is already mounted at the mountpoint. */
- ret = automount_findinode(lower->mountpoint);
- switch (ret)
- {
- case OK_EXIST:
- /* REVISIT: What should we do in this case? I think that this would
- * happen only if a previous unmount failed? I suppose that we should
- * try to unmount again because the mount might be stale.
- */
- fwarn("WARNING: Mountpoint %s already exists\n", lower->mountpoint);
- ret = automount_unmount(priv);
- if (ret < 0)
- {
- /* We failed to unmount (again?). Complain and abort. */
- ferr("ERROR: automount_unmount failed: %d\n", ret);
- return;
- }
- /* We successfully unmounted the file system. Fall through to
- * mount it again.
- */
- case OK_NOENT:
- /* If we get here, then the volume must not be mounted */
- DEBUGASSERT(!priv->mounted);
- /* Mount the file system */
- ret = mount(lower->blockdev, lower->mountpoint, lower->fstype,
- 0, NULL);
- if (ret < 0)
- {
- int errcode = get_errno();
- DEBUGASSERT(errcode > 0);
- ferr("ERROR: Mount failed: %d\n", errcode);
- UNUSED(errcode);
- return;
- }
- /* Indicate that the volume is mounted */
- priv->mounted = true;
- break;
- default:
- ferr("ERROR: automount_findinode failed: %d\n", ret);
- break;
- }
- }
- /****************************************************************************
- * Name: automount_unmount
- *
- * Description:
- * Media has been removed, unmount the volume.
- *
- * Input Parameters:
- * priv - A reference to out private state structure
- *
- * Returned Value:
- * OK if the volume was successfully mounted. A negated errno value
- * otherwise.
- *
- ****************************************************************************/
- static int automount_unmount(FAR struct automounter_state_s *priv)
- {
- FAR const struct automount_lower_s *lower = priv->lower;
- int ret;
- finfo("Unmounting %s\n", lower->mountpoint);
- /* Check if the something is already mounted at the mountpoint. */
- ret = automount_findinode(lower->mountpoint);
- switch (ret)
- {
- case OK_EXIST:
- /* If we get here, then the volume must be mounted */
- DEBUGASSERT(priv->mounted);
- /* Un-mount the volume */
- ret = umount2(lower->mountpoint, MNT_FORCE);
- if (ret < 0)
- {
- int errcode = get_errno();
- DEBUGASSERT(errcode > 0);
- /* We expect the error to be EBUSY meaning that the volume could
- * not be unmounted because there are currently reference via open
- * files or directories.
- */
- if (errcode == EBUSY)
- {
- finfo("WARNING: Volume is busy, try again later\n");
- /* Start a timer to retry the umount2 after a delay */
- ret = wd_start(&priv->wdog, lower->udelay,
- automount_timeout, (wdparm_t)priv);
- if (ret < 0)
- {
- ferr("ERROR: wd_start failed: %d\n", ret);
- return ret;
- }
- }
- /* Other errors are fatal */
- else
- {
- ferr("ERROR: umount2 failed: %d\n", errcode);
- return -errcode;
- }
- }
- /* Fall through */
- case OK_NOENT:
- /* The mountpoint is not present. This is normal behavior in the
- * case where the user manually un-mounted the volume before removing
- * media. Nice job, Mr. user.
- */
- priv->mounted = false;
- return OK;
- default:
- ferr("ERROR: automount_findinode failed: %d\n", ret);
- return ret;
- }
- }
- /****************************************************************************
- * Name: automount_timeout
- *
- * Description:
- * A previous unmount failed because the volume was busy... busy meaning
- * the volume could not be unmounted because there are open references
- * the files or directories in the volume. When this failure occurred,
- * the unmount logic setup a delay and this function is called as a result
- * of that delay timeout.
- *
- * This function will attempt the unmount again.
- *
- * Input Parameters:
- * Standard wdog timeout parameters
- *
- * Returned Value:
- * None
- *
- ****************************************************************************/
- static void automount_timeout(wdparm_t arg)
- {
- FAR struct automounter_state_s *priv =
- (FAR struct automounter_state_s *)arg;
- int ret;
- finfo("Timeout!\n");
- DEBUGASSERT(priv);
- /* Check the state of things. This timeout at the interrupt level and
- * will cancel the timeout if there is any change in the insertion
- * state. So we should still have the saved state as NOT inserted and
- * there should be no pending work.
- */
- finfo("inserted=%d\n", priv->inserted);
- DEBUGASSERT(!priv->inserted && work_available(&priv->work));
- /* Queue work to occur immediately. */
- ret = work_queue(LPWORK, &priv->work, automount_worker, priv, 0);
- if (ret < 0)
- {
- /* NOTE: Currently, work_queue only returns success */
- ferr("ERROR: Failed to schedule work: %d\n", ret);
- }
- }
- /****************************************************************************
- * Name: automount_worker
- *
- * Description:
- * Performs auto-mount actions on the worker thread.
- *
- * Input Parameters:
- * arg - Work argument set by work_queue()
- *
- * Returned Value:
- * None
- *
- ****************************************************************************/
- static void automount_worker(FAR void *arg)
- {
- FAR struct automounter_state_s *priv =
- (FAR struct automounter_state_s *)arg;
- FAR const struct automount_lower_s *lower;
- DEBUGASSERT(priv && priv->lower);
- lower = priv->lower;
- /* Disable interrupts. We are commit now and everything must remain
- * stable.
- */
- AUTOMOUNT_DISABLE(lower);
- /* Are we mounting or unmounting? */
- if (priv->inserted)
- {
- /* We are mounting */
- automount_mount(priv);
- }
- else
- {
- /* We are unmounting */
- automount_unmount(priv);
- }
- /* Re-enable interrupts */
- AUTOMOUNT_ENABLE(lower);
- }
- /****************************************************************************
- * Name: automount_interrupt
- *
- * Description:
- * Called (probably from the interrupt level) when a media change event
- * has been detected.
- *
- * Input Parameters:
- * lower - Persistent board configuration data
- * arg - Data associated with the auto-mounter
- * inserted - True: Media has been inserted. False: media has been removed
- *
- * Returned Value:
- * OK is returned on success; a negated errno value is returned on failure.
- *
- * Assumptions:
- * Interrupts are disabled so that there is no race condition with the
- * timer expiry.
- *
- ****************************************************************************/
- static int automount_interrupt(FAR const struct automount_lower_s *lower,
- FAR void *arg, bool inserted)
- {
- FAR struct automounter_state_s *priv =
- (FAR struct automounter_state_s *)arg;
- int ret;
- DEBUGASSERT(lower && priv && priv->lower == lower);
- finfo("inserted=%d\n", inserted);
- /* Cancel any pending work. We could get called multiple times if, for
- * example there is bounce in the detection mechanism. Work is performed
- * the low priority work queue if it is available.
- *
- * NOTE: The return values are ignored. The error -ENOENT means that
- * there is no work to be canceled. No other errors are expected.
- */
- work_cancel(LPWORK, &priv->work);
- /* Set the media insertion/removal state */
- priv->inserted = inserted;
- /* Queue work to occur after a delay. The delays performs debouncing:
- * If the insertion/removal detection logic has "chatter", then we may
- * receive this interrupt numerous times. Each time, the previous work
- * will be canceled (above) and the new work will scheduled with the
- * delay. So the final mount operation will not be performed until the
- * insertion state is stable for that delay.
- */
- ret = work_queue(LPWORK, &priv->work, automount_worker, priv,
- priv->lower->ddelay);
- if (ret < 0)
- {
- /* NOTE: Currently, work_queue only returns success */
- ferr("ERROR: Failed to schedule work: %d\n", ret);
- }
- else
- {
- /* Cancel any retry delays */
- wd_cancel(&priv->wdog);
- }
- return OK;
- }
- /****************************************************************************
- * Public Functions
- ****************************************************************************/
- /****************************************************************************
- * Name: automount_initialize
- *
- * Description:
- * Configure the auto mounter.
- *
- * Input Parameters:
- * lower - Persistent board configuration data
- *
- * Returned Value:
- * A void* handle. The only use for this handle is with
- * automount_uninitialize(). NULL is returned on any failure.
- *
- ****************************************************************************/
- FAR void *automount_initialize(FAR const struct automount_lower_s *lower)
- {
- FAR struct automounter_state_s *priv;
- int ret;
- finfo("lower=%p\n", lower);
- DEBUGASSERT(lower);
- /* Allocate an auto-mounter state structure */
- priv = (FAR struct automounter_state_s *)
- kmm_zalloc(sizeof(struct automounter_state_s));
- if (!priv)
- {
- ferr("ERROR: Failed to allocate state structure\n");
- return NULL;
- }
- /* Initialize the automounter state structure */
- priv->lower = lower;
- /* Handle the initial state of the mount on the caller's thread */
- priv->inserted = AUTOMOUNT_INSERTED(lower);
- /* Set up the first action at a delay from the initialization time (to
- * allow time for any extended block driver initialization to complete.
- */
- ret = work_queue(LPWORK, &priv->work, automount_worker, priv,
- priv->lower->ddelay);
- if (ret < 0)
- {
- /* NOTE: Currently, work_queue only returns success */
- ferr("ERROR: Failed to schedule work: %d\n", ret);
- }
- /* Attach and enable automounter interrupts */
- ret = AUTOMOUNT_ATTACH(lower, automount_interrupt, priv);
- if (ret < 0)
- {
- ferr("ERROR: Failed to attach automount interrupt: %d\n", ret);
- automount_uninitialize(priv);
- return NULL;
- }
- AUTOMOUNT_ENABLE(lower);
- return priv;
- }
- /****************************************************************************
- * Name: automount_uninitialize
- *
- * Description:
- * Stop the automounter and free resources that it used. NOTE that the
- * mount is left in its last state mounted/unmounted state.
- *
- * Input Parameters:
- * handle - The value previously returned by automount_initialize();
- *
- * Returned Value:
- * None
- *
- ****************************************************************************/
- void automount_uninitialize(FAR void *handle)
- {
- FAR struct automounter_state_s *priv =
- (FAR struct automounter_state_s *)handle;
- FAR const struct automount_lower_s *lower;
- DEBUGASSERT(priv && priv->lower);
- lower = priv->lower;
- /* Disable and detach interrupts */
- AUTOMOUNT_DISABLE(lower);
- AUTOMOUNT_DETACH(lower);
- /* Cancel the watchdog timer */
- wd_cancel(&priv->wdog);
- /* And free the state structure */
- kmm_free(priv);
- }
|