fs_inodesearch.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613
  1. /****************************************************************************
  2. * fs/inode/fs_inodesearch.c
  3. *
  4. * Copyright (C) 2007-2009, 2011-2012, 2016-2017 Gregory Nutt. All rights reserved.
  5. * Author: Gregory Nutt <gnutt@nuttx.org>
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions
  9. * are met:
  10. *
  11. * 1. Redistributions of source code must retain the above copyright
  12. * notice, this list of conditions and the following disclaimer.
  13. * 2. Redistributions in binary form must reproduce the above copyright
  14. * notice, this list of conditions and the following disclaimer in
  15. * the documentation and/or other materials provided with the
  16. * distribution.
  17. * 3. Neither the name NuttX nor the names of its contributors may be
  18. * used to endorse or promote products derived from this software
  19. * without specific prior written permission.
  20. *
  21. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  22. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  23. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  24. * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  25. * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  26. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  27. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  28. * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
  29. * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  30. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  31. * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  32. * POSSIBILITY OF SUCH DAMAGE.
  33. *
  34. ****************************************************************************/
  35. /****************************************************************************
  36. * Included Files
  37. ****************************************************************************/
  38. #include <nuttx/config.h>
  39. #include <stdio.h>
  40. #include <string.h>
  41. #include <limits.h>
  42. #include <assert.h>
  43. #include <errno.h>
  44. #include <nuttx/fs/fs.h>
  45. #include "inode/inode.h"
  46. /****************************************************************************
  47. * Private Function Prototypes
  48. ****************************************************************************/
  49. static int _inode_compare(FAR const char *fname, FAR struct inode *node);
  50. #ifdef CONFIG_PSEUDOFS_SOFTLINKS
  51. static int _inode_linktarget(FAR struct inode *node,
  52. FAR struct inode_search_s *desc);
  53. #endif
  54. static int _inode_search(FAR struct inode_search_s *desc);
  55. /****************************************************************************
  56. * Public Data
  57. ****************************************************************************/
  58. FAR struct inode *g_root_inode = NULL;
  59. /****************************************************************************
  60. * Private Functions
  61. ****************************************************************************/
  62. /****************************************************************************
  63. * Name: _inode_compare
  64. *
  65. * Description:
  66. * Compare two inode names
  67. *
  68. ****************************************************************************/
  69. static int _inode_compare(FAR const char *fname, FAR struct inode *node)
  70. {
  71. char *nname = node->i_name;
  72. if (!nname)
  73. {
  74. return 1;
  75. }
  76. if (!fname)
  77. {
  78. return -1;
  79. }
  80. for (; ; )
  81. {
  82. /* At end of node name? */
  83. if (!*nname)
  84. {
  85. /* Yes.. also end of find name? */
  86. if (!*fname || *fname == '/')
  87. {
  88. /* Yes.. return match */
  89. return 0;
  90. }
  91. else
  92. {
  93. /* No... return find name > node name */
  94. return 1;
  95. }
  96. }
  97. /* At end of find name? */
  98. else if (!*fname || *fname == '/')
  99. {
  100. /* Yes... return find name < node name */
  101. return -1;
  102. }
  103. /* Check for non-matching characters */
  104. else if (*fname > *nname)
  105. {
  106. return 1;
  107. }
  108. else if (*fname < *nname)
  109. {
  110. return -1;
  111. }
  112. /* Not at the end of either string and all of the
  113. * characters still match. keep looking.
  114. */
  115. else
  116. {
  117. fname++;
  118. nname++;
  119. }
  120. }
  121. }
  122. /****************************************************************************
  123. * Name: _inode_linktarget
  124. *
  125. * Description:
  126. * If the inode is a soft link, then (1) get the name of the full path of
  127. * the soft link, (2) recursively look-up the inode referenced by the soft
  128. * link, and (3) return the inode referenced by the soft link.
  129. *
  130. * Assumptions:
  131. * The caller holds the g_inode_sem semaphore
  132. *
  133. ****************************************************************************/
  134. #ifdef CONFIG_PSEUDOFS_SOFTLINKS
  135. static int _inode_linktarget(FAR struct inode *node,
  136. FAR struct inode_search_s *desc)
  137. {
  138. unsigned int count = 0;
  139. bool save;
  140. int ret = -ENOENT;
  141. DEBUGASSERT(desc != NULL && node != NULL);
  142. /* An infinite loop is avoided only by the loop count.
  143. *
  144. * REVISIT: The ELOOP error should be reported to the application in that
  145. * case but there is no simple mechanism to do that.
  146. */
  147. save = desc->nofollow;
  148. while (INODE_IS_SOFTLINK(node))
  149. {
  150. FAR const char *link = (FAR const char *)node->u.i_link;
  151. /* Reset and reinitialize the search descriptor. */
  152. RELEASE_SEARCH(desc);
  153. SETUP_SEARCH(desc, link, true);
  154. /* Look up inode associated with the target of the symbolic link */
  155. ret = _inode_search(desc);
  156. if (ret < 0)
  157. {
  158. break;
  159. }
  160. /* Limit the number of symbolic links that we pass through */
  161. if (++count > SYMLOOP_MAX)
  162. {
  163. ret = -ELOOP;
  164. break;
  165. }
  166. /* Set up for the next time through the loop */
  167. node = desc->node;
  168. DEBUGASSERT(node != NULL);
  169. desc->linktgt = link;
  170. }
  171. desc->nofollow = save;
  172. return ret;
  173. }
  174. #endif
  175. /****************************************************************************
  176. * Name: _inode_search
  177. *
  178. * Description:
  179. * Find the inode associated with 'path' returning the inode references
  180. * and references to its companion nodes. This is the internal, common
  181. * implementation of inode_search().
  182. *
  183. * If a mountpoint is encountered in the search prior to encountering the
  184. * terminal node, the search will terminate at the mountpoint inode. That
  185. * inode and the relative path from the mountpoint, 'relpath' will be
  186. * returned.
  187. *
  188. * If a soft link is encountered that is not the terminal node in the path,
  189. * that link WILL be deferenced unconditionally.
  190. *
  191. * Assumptions:
  192. * The caller holds the g_inode_sem semaphore
  193. *
  194. ****************************************************************************/
  195. static int _inode_search(FAR struct inode_search_s *desc)
  196. {
  197. FAR const char *name;
  198. FAR struct inode *node = g_root_inode;
  199. FAR struct inode *left = NULL;
  200. FAR struct inode *above = NULL;
  201. FAR const char *relpath = NULL;
  202. int ret = -ENOENT;
  203. /* Get the search path, skipping over the leading '/'. The leading '/' is
  204. * mandatory because only absolte paths are expected in this context.
  205. */
  206. DEBUGASSERT(desc != NULL && desc->path != NULL);
  207. name = desc->path;
  208. if (*name != '/')
  209. {
  210. return -EINVAL;
  211. }
  212. /* Skip over the leading '/' */
  213. while (*name == '/')
  214. {
  215. name++;
  216. }
  217. /* Special case the root directory. There is no root inode and there is
  218. * no name for the root.
  219. */
  220. if (*name == '\0')
  221. {
  222. /* This is a bug. I don't know how to handle this case yet. */
  223. return -ENOSYS;
  224. }
  225. /* Traverse the pseudo file system node tree until either (1) all nodes
  226. * have been examined without finding the matching node, or (2) the
  227. * matching node is found.
  228. */
  229. while (node != NULL)
  230. {
  231. int result = _inode_compare(name, node);
  232. /* Case 1: The name is less than the name of the node.
  233. * Since the names are ordered, these means that there
  234. * is no peer node with this name and that there can be
  235. * no match in the fileystem.
  236. */
  237. if (result < 0)
  238. {
  239. node = NULL;
  240. break;
  241. }
  242. /* Case 2: the name is greater than the name of the node.
  243. * In this case, the name may still be in the list to the
  244. * "right"
  245. */
  246. else if (result > 0)
  247. {
  248. /* Continue looking to the "right" of this inode. */
  249. left = node;
  250. node = node->i_peer;
  251. }
  252. /* The names match */
  253. else
  254. {
  255. /* Now there are three remaining possibilities:
  256. * (1) This is the node that we are looking for.
  257. * (2) The node we are looking for is "below" this one.
  258. * (3) This node is a mountpoint and will absorb all request
  259. * below this one
  260. */
  261. name = inode_nextname(name);
  262. if (*name == '\0' || INODE_IS_MOUNTPT(node))
  263. {
  264. /* Either (1) we are at the end of the path, so this must be the
  265. * node we are looking for or else (2) this node is a mountpoint
  266. * and will handle the remaining part of the pathname
  267. */
  268. relpath = name;
  269. ret = OK;
  270. break;
  271. }
  272. else
  273. {
  274. /* More nodes to be examined in the path "below" this one. */
  275. #ifdef CONFIG_PSEUDOFS_SOFTLINKS
  276. /* Was the node a soft link? If so, then we need need to
  277. * continue below the target of the link, not the link itself.
  278. */
  279. if (INODE_IS_SOFTLINK(node))
  280. {
  281. int status;
  282. /* If this intermediate inode in the is a soft link, then
  283. * (1) get the name of the full path of the soft link, (2)
  284. * recursively look-up the inode referenced by the soft
  285. * link, and (3) continue searching with that inode instead.
  286. */
  287. status = _inode_linktarget(node, desc);
  288. if (status < 0)
  289. {
  290. /* Probably means that the target of the symbolic link
  291. * does not exist.
  292. */
  293. ret = status;
  294. break;
  295. }
  296. else
  297. {
  298. FAR struct inode *newnode = desc->node;
  299. if (newnode != node)
  300. {
  301. /* The node was a valid symbolic link and we have
  302. * jumped to a different, spot in the pseudo file
  303. * system tree.
  304. */
  305. /* Check if this took us to a mountpoint. */
  306. if (INODE_IS_MOUNTPT(newnode))
  307. {
  308. /* Return the mountpoint information.
  309. * NOTE that the last path to the link target
  310. * was already set by _inode_linktarget().
  311. */
  312. node = newnode;
  313. above = NULL;
  314. left = NULL;
  315. relpath = name;
  316. ret = OK;
  317. break;
  318. }
  319. /* Continue from this new inode. */
  320. node = newnode;
  321. }
  322. }
  323. }
  324. #endif
  325. /* Keep looking at the next level "down" */
  326. above = node;
  327. left = NULL;
  328. node = node->i_child;
  329. }
  330. }
  331. }
  332. /* The node may or may not be null as per one of the following four cases
  333. * cases:
  334. *
  335. * With node = NULL
  336. *
  337. * (1) We went left past the final peer: The new node name is larger
  338. * than any existing node name at that level.
  339. * (2) We broke out in the middle of the list of peers because the name
  340. * was not found in the ordered list.
  341. * (3) We went down past the final parent: The new node name is
  342. * "deeper" than anything that we currently have in the tree.
  343. *
  344. * With node != NULL
  345. *
  346. * (4) When the node matching the full path is found
  347. */
  348. desc->path = name;
  349. desc->node = node;
  350. desc->peer = left;
  351. desc->parent = above;
  352. desc->relpath = relpath;
  353. return ret;
  354. }
  355. /****************************************************************************
  356. * Public Functions
  357. ****************************************************************************/
  358. /****************************************************************************
  359. * Name: inode_search
  360. *
  361. * Description:
  362. * Find the inode associated with 'path' returning the inode references
  363. * and references to its companion nodes.
  364. *
  365. * If a mountpoint is encountered in the search prior to encountering the
  366. * terminal node, the search will terminate at the mountpoint inode. That
  367. * inode and the relative path from the mountpoint, 'relpath' will be
  368. * returned.
  369. *
  370. * inode_search will follow soft links in path leading up to the terminal
  371. * node. Whether or no inode_search() will deference that terminal node
  372. * depends on the 'nofollow' input.
  373. *
  374. * If a soft link is encountered that is not the terminal node in the path,
  375. * that link WILL be deferenced unconditionally.
  376. *
  377. * Assumptions:
  378. * The caller holds the g_inode_sem semaphore
  379. *
  380. ****************************************************************************/
  381. int inode_search(FAR struct inode_search_s *desc)
  382. {
  383. int ret;
  384. /* Perform the common _inode_search() logic. This does everything except
  385. * operations special operations that must be performed on the terminal
  386. * node if node is a symbolic link.
  387. */
  388. DEBUGASSERT(desc != NULL);
  389. #ifdef CONFIG_PSEUDOFS_SOFTLINKS
  390. desc->linktgt = NULL;
  391. #endif
  392. ret = _inode_search(desc);
  393. #ifdef CONFIG_PSEUDOFS_SOFTLINKS
  394. if (ret >= 0)
  395. {
  396. FAR struct inode *node;
  397. /* Search completed successfully */
  398. node = desc->node;
  399. DEBUGASSERT(node != NULL);
  400. /* Is the terminal node a softlink? Should we follow it? */
  401. if (!desc->nofollow && INODE_IS_SOFTLINK(node))
  402. {
  403. /* Save some things we need that will be clobbered by the call to
  404. * _inode_linktgt().
  405. */
  406. FAR const char *relpath = desc->relpath; /* Will always be "" here */
  407. /* The terminating inode is a valid soft link: Return the inode,
  408. * corresponding to link target. _inode_linktarget() will follow
  409. * a link (or a series of links to links) and will return the
  410. * link target of the final symbolic link in the series.
  411. */
  412. ret = _inode_linktarget(node, desc);
  413. if (ret < 0)
  414. {
  415. /* The most likely cause for failure is that the target of the
  416. * symbolic link does not exist.
  417. */
  418. return ret;
  419. }
  420. /* The dereferenced node might be a mountpoint */
  421. node = desc->node;
  422. DEBUGASSERT(node != NULL && desc->linktgt != NULL);
  423. if (INODE_IS_MOUNTPT(node))
  424. {
  425. /* Yes... set up for the MOUNTPOINT logic below. */
  426. desc->relpath = relpath;
  427. }
  428. }
  429. /* Handle a special case. This special occurs with either (1)
  430. * inode_search() terminates early because it encountered a MOUNTPOINT
  431. * at an intermediate node in the path, or (2) inode_search()
  432. * terminates because it reached the terminal node and 'nofollow' is
  433. * false and the above logic converted the symbolic link to a
  434. * MOUNTPOINT.
  435. *
  436. * We can detect the special cases because desc->linktgt will be
  437. * non-NULL.
  438. */
  439. if (desc->linktgt != NULL && INODE_IS_MOUNTPT(node))
  440. {
  441. FAR char *buffer;
  442. /* There would be no problem in this case if the link was to
  443. * either to the root directory of the MOUNTPOINT or to a
  444. * regular file within the mounted volume. However, there
  445. * is a problem if the symbolic link is to a directory within
  446. * the mounted volume. In that case, the 'relpath' will be
  447. * relative to the symbolic link and not to the MOUNTPOINT.
  448. *
  449. * We will handle the worst case by creating the full path
  450. * excluding the symbolic link and performing the look-up
  451. * again.
  452. */
  453. if (desc->relpath != NULL && *desc->relpath != '\0')
  454. {
  455. (void)asprintf(&buffer, "%s/%s",
  456. desc->linktgt, desc->relpath);
  457. }
  458. else
  459. {
  460. buffer = strdup(desc->linktgt);
  461. }
  462. if (buffer == NULL)
  463. {
  464. ret = -ENOMEM;
  465. }
  466. else
  467. {
  468. /* Reset the search description and perform the search again. */
  469. RELEASE_SEARCH(desc);
  470. SETUP_SEARCH(desc, buffer, false);
  471. desc->buffer = buffer;
  472. ret = _inode_search(desc);
  473. }
  474. }
  475. }
  476. #endif
  477. return ret;
  478. }
  479. /****************************************************************************
  480. * Name: inode_nextname
  481. *
  482. * Description:
  483. * Given a path with node names separated by '/', return the next path
  484. * segment name.
  485. *
  486. ****************************************************************************/
  487. FAR const char *inode_nextname(FAR const char *name)
  488. {
  489. /* Search for the '/' delimiter or the NUL terminator at the end of the
  490. * path segment.
  491. */
  492. while (*name != '\0' && *name != '/')
  493. {
  494. name++;
  495. }
  496. /* If we found the '/' delimiter, then the path segment we want begins at
  497. * the next character (which might also be the NUL terminator).
  498. */
  499. while (*name == '/')
  500. {
  501. name++;
  502. }
  503. return name;
  504. }