fs_automount.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611
  1. /****************************************************************************
  2. * fs/mount/fs_automount.c
  3. *
  4. * Licensed to the Apache Software Foundation (ASF) under one or more
  5. * contributor license agreements. See the NOTICE file distributed with
  6. * this work for additional information regarding copyright ownership. The
  7. * ASF licenses this file to you under the Apache License, Version 2.0 (the
  8. * "License"); you may not use this file except in compliance with the
  9. * License. You may obtain a copy of the License at
  10. *
  11. * http://www.apache.org/licenses/LICENSE-2.0
  12. *
  13. * Unless required by applicable law or agreed to in writing, software
  14. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  15. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  16. * License for the specific language governing permissions and limitations
  17. * under the License.
  18. *
  19. ****************************************************************************/
  20. /****************************************************************************
  21. * Included Files
  22. ****************************************************************************/
  23. #include <nuttx/config.h>
  24. #if defined(CONFIG_FS_AUTOMOUNTER_DEBUG) && !defined(CONFIG_DEBUG_FS)
  25. # define CONFIG_DEBUG_FS 1
  26. #endif
  27. #include <sys/mount.h>
  28. #include <stdbool.h>
  29. #include <errno.h>
  30. #include <assert.h>
  31. #include <debug.h>
  32. #include <nuttx/wdog.h>
  33. #include <nuttx/kmalloc.h>
  34. #include <nuttx/wqueue.h>
  35. #include <nuttx/fs/automount.h>
  36. #include "inode/inode.h"
  37. /****************************************************************************
  38. * Pre-processor Definitions
  39. ****************************************************************************/
  40. /* Configuration ************************************************************
  41. *
  42. * CONFIG_FS_AUTOMOUNTER - Enables AUTOMOUNT support
  43. */
  44. /* Pre-requisites */
  45. #ifndef CONFIG_SCHED_WORKQUEUE
  46. # error Work queue support is required (CONFIG_SCHED_WORKQUEUE)
  47. #endif
  48. /* Return Values */
  49. #define OK_EXIST 0
  50. #define OK_NOENT 1
  51. /****************************************************************************
  52. * Private Types
  53. ****************************************************************************/
  54. /* This structure describes the state of the automounter */
  55. struct automounter_state_s
  56. {
  57. FAR const struct automount_lower_s *lower; /* Board level interfaces */
  58. struct work_s work; /* Work queue support */
  59. struct wdog_s wdog; /* Delay to retry un-mounts */
  60. bool mounted; /* True: Volume has been mounted */
  61. bool inserted; /* True: Media has been inserted */
  62. };
  63. /****************************************************************************
  64. * Private Function Prototypes
  65. ****************************************************************************/
  66. static int automount_findinode(FAR const char *path);
  67. static void automount_mount(FAR struct automounter_state_s *priv);
  68. static int automount_unmount(FAR struct automounter_state_s *priv);
  69. static void automount_timeout(wdparm_t arg);
  70. static void automount_worker(FAR void *arg);
  71. static int automount_interrupt(FAR const struct automount_lower_s *lower,
  72. FAR void *arg, bool inserted);
  73. /****************************************************************************
  74. * Private Functions
  75. ****************************************************************************/
  76. /****************************************************************************
  77. * Name: automount_findinode
  78. *
  79. * Description:
  80. * Find the mountpoint inode in the inode tree.
  81. *
  82. * Input Parameters:
  83. * mntpath - Mountpoint path
  84. *
  85. * Returned Value:
  86. * OK_EXIST if the inode exists
  87. * OK_NOENT if the inode does not exist
  88. * Negated errno if some failure occurs
  89. *
  90. ****************************************************************************/
  91. static int automount_findinode(FAR const char *path)
  92. {
  93. struct inode_search_s desc;
  94. int ret;
  95. /* Make sure that we were given a path */
  96. DEBUGASSERT(path != NULL);
  97. /* Get exclusive access to the in-memory inode tree. */
  98. ret = inode_semtake();
  99. if (ret < 0)
  100. {
  101. return ret;
  102. }
  103. /* Find the inode */
  104. SETUP_SEARCH(&desc, path, false);
  105. ret = inode_search(&desc);
  106. /* Did we find it? */
  107. if (ret < 0)
  108. {
  109. /* No.. Not found */
  110. ret = OK_NOENT;
  111. }
  112. /* Yes.. is it a mount point? */
  113. else if (INODE_IS_MOUNTPT(desc.node))
  114. {
  115. /* Yes.. we found a mountpoint at this path */
  116. ret = OK_EXIST;
  117. }
  118. else
  119. {
  120. /* No.. then something is in the way */
  121. ret = -ENOTDIR;
  122. }
  123. /* Relinquish our exclusive access to the inode try and return the result */
  124. inode_semgive();
  125. RELEASE_SEARCH(&desc);
  126. return ret;
  127. }
  128. /****************************************************************************
  129. * Name: automount_mount
  130. *
  131. * Description:
  132. * Media has been inserted, mount the volume.
  133. *
  134. * Input Parameters:
  135. * priv - A reference to out private state structure
  136. *
  137. * Returned Value:
  138. * None
  139. *
  140. ****************************************************************************/
  141. static void automount_mount(FAR struct automounter_state_s *priv)
  142. {
  143. FAR const struct automount_lower_s *lower = priv->lower;
  144. int ret;
  145. finfo("Mounting %s\n", lower->mountpoint);
  146. /* Check if the something is already mounted at the mountpoint. */
  147. ret = automount_findinode(lower->mountpoint);
  148. switch (ret)
  149. {
  150. case OK_EXIST:
  151. /* REVISIT: What should we do in this case? I think that this would
  152. * happen only if a previous unmount failed? I suppose that we should
  153. * try to unmount again because the mount might be stale.
  154. */
  155. fwarn("WARNING: Mountpoint %s already exists\n", lower->mountpoint);
  156. ret = automount_unmount(priv);
  157. if (ret < 0)
  158. {
  159. /* We failed to unmount (again?). Complain and abort. */
  160. ferr("ERROR: automount_unmount failed: %d\n", ret);
  161. return;
  162. }
  163. /* We successfully unmounted the file system. Fall through to
  164. * mount it again.
  165. */
  166. case OK_NOENT:
  167. /* If we get here, then the volume must not be mounted */
  168. DEBUGASSERT(!priv->mounted);
  169. /* Mount the file system */
  170. ret = mount(lower->blockdev, lower->mountpoint, lower->fstype,
  171. 0, NULL);
  172. if (ret < 0)
  173. {
  174. int errcode = get_errno();
  175. DEBUGASSERT(errcode > 0);
  176. ferr("ERROR: Mount failed: %d\n", errcode);
  177. UNUSED(errcode);
  178. return;
  179. }
  180. /* Indicate that the volume is mounted */
  181. priv->mounted = true;
  182. break;
  183. default:
  184. ferr("ERROR: automount_findinode failed: %d\n", ret);
  185. break;
  186. }
  187. }
  188. /****************************************************************************
  189. * Name: automount_unmount
  190. *
  191. * Description:
  192. * Media has been removed, unmount the volume.
  193. *
  194. * Input Parameters:
  195. * priv - A reference to out private state structure
  196. *
  197. * Returned Value:
  198. * OK if the volume was successfully mounted. A negated errno value
  199. * otherwise.
  200. *
  201. ****************************************************************************/
  202. static int automount_unmount(FAR struct automounter_state_s *priv)
  203. {
  204. FAR const struct automount_lower_s *lower = priv->lower;
  205. int ret;
  206. finfo("Unmounting %s\n", lower->mountpoint);
  207. /* Check if the something is already mounted at the mountpoint. */
  208. ret = automount_findinode(lower->mountpoint);
  209. switch (ret)
  210. {
  211. case OK_EXIST:
  212. /* If we get here, then the volume must be mounted */
  213. DEBUGASSERT(priv->mounted);
  214. /* Un-mount the volume */
  215. ret = umount2(lower->mountpoint, MNT_FORCE);
  216. if (ret < 0)
  217. {
  218. int errcode = get_errno();
  219. DEBUGASSERT(errcode > 0);
  220. /* We expect the error to be EBUSY meaning that the volume could
  221. * not be unmounted because there are currently reference via open
  222. * files or directories.
  223. */
  224. if (errcode == EBUSY)
  225. {
  226. finfo("WARNING: Volume is busy, try again later\n");
  227. /* Start a timer to retry the umount2 after a delay */
  228. ret = wd_start(&priv->wdog, lower->udelay,
  229. automount_timeout, (wdparm_t)priv);
  230. if (ret < 0)
  231. {
  232. ferr("ERROR: wd_start failed: %d\n", ret);
  233. return ret;
  234. }
  235. }
  236. /* Other errors are fatal */
  237. else
  238. {
  239. ferr("ERROR: umount2 failed: %d\n", errcode);
  240. return -errcode;
  241. }
  242. }
  243. /* Fall through */
  244. case OK_NOENT:
  245. /* The mountpoint is not present. This is normal behavior in the
  246. * case where the user manually un-mounted the volume before removing
  247. * media. Nice job, Mr. user.
  248. */
  249. priv->mounted = false;
  250. return OK;
  251. default:
  252. ferr("ERROR: automount_findinode failed: %d\n", ret);
  253. return ret;
  254. }
  255. }
  256. /****************************************************************************
  257. * Name: automount_timeout
  258. *
  259. * Description:
  260. * A previous unmount failed because the volume was busy... busy meaning
  261. * the volume could not be unmounted because there are open references
  262. * the files or directories in the volume. When this failure occurred,
  263. * the unmount logic setup a delay and this function is called as a result
  264. * of that delay timeout.
  265. *
  266. * This function will attempt the unmount again.
  267. *
  268. * Input Parameters:
  269. * Standard wdog timeout parameters
  270. *
  271. * Returned Value:
  272. * None
  273. *
  274. ****************************************************************************/
  275. static void automount_timeout(wdparm_t arg)
  276. {
  277. FAR struct automounter_state_s *priv =
  278. (FAR struct automounter_state_s *)arg;
  279. int ret;
  280. finfo("Timeout!\n");
  281. DEBUGASSERT(priv);
  282. /* Check the state of things. This timeout at the interrupt level and
  283. * will cancel the timeout if there is any change in the insertion
  284. * state. So we should still have the saved state as NOT inserted and
  285. * there should be no pending work.
  286. */
  287. finfo("inserted=%d\n", priv->inserted);
  288. DEBUGASSERT(!priv->inserted && work_available(&priv->work));
  289. /* Queue work to occur immediately. */
  290. ret = work_queue(LPWORK, &priv->work, automount_worker, priv, 0);
  291. if (ret < 0)
  292. {
  293. /* NOTE: Currently, work_queue only returns success */
  294. ferr("ERROR: Failed to schedule work: %d\n", ret);
  295. }
  296. }
  297. /****************************************************************************
  298. * Name: automount_worker
  299. *
  300. * Description:
  301. * Performs auto-mount actions on the worker thread.
  302. *
  303. * Input Parameters:
  304. * arg - Work argument set by work_queue()
  305. *
  306. * Returned Value:
  307. * None
  308. *
  309. ****************************************************************************/
  310. static void automount_worker(FAR void *arg)
  311. {
  312. FAR struct automounter_state_s *priv =
  313. (FAR struct automounter_state_s *)arg;
  314. FAR const struct automount_lower_s *lower;
  315. DEBUGASSERT(priv && priv->lower);
  316. lower = priv->lower;
  317. /* Disable interrupts. We are commit now and everything must remain
  318. * stable.
  319. */
  320. AUTOMOUNT_DISABLE(lower);
  321. /* Are we mounting or unmounting? */
  322. if (priv->inserted)
  323. {
  324. /* We are mounting */
  325. automount_mount(priv);
  326. }
  327. else
  328. {
  329. /* We are unmounting */
  330. automount_unmount(priv);
  331. }
  332. /* Re-enable interrupts */
  333. AUTOMOUNT_ENABLE(lower);
  334. }
  335. /****************************************************************************
  336. * Name: automount_interrupt
  337. *
  338. * Description:
  339. * Called (probably from the interrupt level) when a media change event
  340. * has been detected.
  341. *
  342. * Input Parameters:
  343. * lower - Persistent board configuration data
  344. * arg - Data associated with the auto-mounter
  345. * inserted - True: Media has been inserted. False: media has been removed
  346. *
  347. * Returned Value:
  348. * OK is returned on success; a negated errno value is returned on failure.
  349. *
  350. * Assumptions:
  351. * Interrupts are disabled so that there is no race condition with the
  352. * timer expiry.
  353. *
  354. ****************************************************************************/
  355. static int automount_interrupt(FAR const struct automount_lower_s *lower,
  356. FAR void *arg, bool inserted)
  357. {
  358. FAR struct automounter_state_s *priv =
  359. (FAR struct automounter_state_s *)arg;
  360. int ret;
  361. DEBUGASSERT(lower && priv && priv->lower == lower);
  362. finfo("inserted=%d\n", inserted);
  363. /* Cancel any pending work. We could get called multiple times if, for
  364. * example there is bounce in the detection mechanism. Work is performed
  365. * the low priority work queue if it is available.
  366. *
  367. * NOTE: The return values are ignored. The error -ENOENT means that
  368. * there is no work to be canceled. No other errors are expected.
  369. */
  370. work_cancel(LPWORK, &priv->work);
  371. /* Set the media insertion/removal state */
  372. priv->inserted = inserted;
  373. /* Queue work to occur after a delay. The delays performs debouncing:
  374. * If the insertion/removal detection logic has "chatter", then we may
  375. * receive this interrupt numerous times. Each time, the previous work
  376. * will be canceled (above) and the new work will scheduled with the
  377. * delay. So the final mount operation will not be performed until the
  378. * insertion state is stable for that delay.
  379. */
  380. ret = work_queue(LPWORK, &priv->work, automount_worker, priv,
  381. priv->lower->ddelay);
  382. if (ret < 0)
  383. {
  384. /* NOTE: Currently, work_queue only returns success */
  385. ferr("ERROR: Failed to schedule work: %d\n", ret);
  386. }
  387. else
  388. {
  389. /* Cancel any retry delays */
  390. wd_cancel(&priv->wdog);
  391. }
  392. return OK;
  393. }
  394. /****************************************************************************
  395. * Public Functions
  396. ****************************************************************************/
  397. /****************************************************************************
  398. * Name: automount_initialize
  399. *
  400. * Description:
  401. * Configure the auto mounter.
  402. *
  403. * Input Parameters:
  404. * lower - Persistent board configuration data
  405. *
  406. * Returned Value:
  407. * A void* handle. The only use for this handle is with
  408. * automount_uninitialize(). NULL is returned on any failure.
  409. *
  410. ****************************************************************************/
  411. FAR void *automount_initialize(FAR const struct automount_lower_s *lower)
  412. {
  413. FAR struct automounter_state_s *priv;
  414. int ret;
  415. finfo("lower=%p\n", lower);
  416. DEBUGASSERT(lower);
  417. /* Allocate an auto-mounter state structure */
  418. priv = (FAR struct automounter_state_s *)
  419. kmm_zalloc(sizeof(struct automounter_state_s));
  420. if (!priv)
  421. {
  422. ferr("ERROR: Failed to allocate state structure\n");
  423. return NULL;
  424. }
  425. /* Initialize the automounter state structure */
  426. priv->lower = lower;
  427. /* Handle the initial state of the mount on the caller's thread */
  428. priv->inserted = AUTOMOUNT_INSERTED(lower);
  429. /* Set up the first action at a delay from the initialization time (to
  430. * allow time for any extended block driver initialization to complete.
  431. */
  432. ret = work_queue(LPWORK, &priv->work, automount_worker, priv,
  433. priv->lower->ddelay);
  434. if (ret < 0)
  435. {
  436. /* NOTE: Currently, work_queue only returns success */
  437. ferr("ERROR: Failed to schedule work: %d\n", ret);
  438. }
  439. /* Attach and enable automounter interrupts */
  440. ret = AUTOMOUNT_ATTACH(lower, automount_interrupt, priv);
  441. if (ret < 0)
  442. {
  443. ferr("ERROR: Failed to attach automount interrupt: %d\n", ret);
  444. automount_uninitialize(priv);
  445. return NULL;
  446. }
  447. AUTOMOUNT_ENABLE(lower);
  448. return priv;
  449. }
  450. /****************************************************************************
  451. * Name: automount_uninitialize
  452. *
  453. * Description:
  454. * Stop the automounter and free resources that it used. NOTE that the
  455. * mount is left in its last state mounted/unmounted state.
  456. *
  457. * Input Parameters:
  458. * handle - The value previously returned by automount_initialize();
  459. *
  460. * Returned Value:
  461. * None
  462. *
  463. ****************************************************************************/
  464. void automount_uninitialize(FAR void *handle)
  465. {
  466. FAR struct automounter_state_s *priv =
  467. (FAR struct automounter_state_s *)handle;
  468. FAR const struct automount_lower_s *lower;
  469. DEBUGASSERT(priv && priv->lower);
  470. lower = priv->lower;
  471. /* Disable and detach interrupts */
  472. AUTOMOUNT_DISABLE(lower);
  473. AUTOMOUNT_DETACH(lower);
  474. /* Cancel the watchdog timer */
  475. wd_cancel(&priv->wdog);
  476. /* And free the state structure */
  477. kmm_free(priv);
  478. }