fs_inodesearch.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614
  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 the end of the node name? */
  83. if (!*nname)
  84. {
  85. /* Yes.. also at the 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 the 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 filesystem.
  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 requests
  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
  265. * the node we are looking for or else (2) this node is a
  266. * mountpoint and will handle the remaining part of the
  267. * pathname
  268. */
  269. relpath = name;
  270. ret = OK;
  271. break;
  272. }
  273. else
  274. {
  275. /* More nodes to be examined in the path "below" this one. */
  276. #ifdef CONFIG_PSEUDOFS_SOFTLINKS
  277. /* Was the node a soft link? If so, then we need need to
  278. * continue below the target of the link, not the link itself.
  279. */
  280. if (INODE_IS_SOFTLINK(node))
  281. {
  282. int status;
  283. /* If this intermediate inode in the is a soft link, then
  284. * (1) get the name of the full path of the soft link, (2)
  285. * recursively look-up the inode referenced by the soft
  286. * link, and (3) continue searching with that inode instead.
  287. */
  288. status = _inode_linktarget(node, desc);
  289. if (status < 0)
  290. {
  291. /* Probably means that the target of the symbolic link
  292. * does not exist.
  293. */
  294. ret = status;
  295. break;
  296. }
  297. else
  298. {
  299. FAR struct inode *newnode = desc->node;
  300. if (newnode != node)
  301. {
  302. /* The node was a valid symbolic link and we have
  303. * jumped to a different, spot in the pseudo file
  304. * system tree.
  305. */
  306. /* Check if this took us to a mountpoint. */
  307. if (INODE_IS_MOUNTPT(newnode))
  308. {
  309. /* Return the mountpoint information.
  310. * NOTE that the last path to the link target
  311. * was already set by _inode_linktarget().
  312. */
  313. node = newnode;
  314. above = NULL;
  315. left = NULL;
  316. relpath = name;
  317. ret = OK;
  318. break;
  319. }
  320. /* Continue from this new inode. */
  321. node = newnode;
  322. }
  323. }
  324. }
  325. #endif
  326. /* Keep looking at the next level "down" */
  327. above = node;
  328. left = NULL;
  329. node = node->i_child;
  330. }
  331. }
  332. }
  333. /* The node may or may not be null as per one of the following four cases
  334. * cases:
  335. *
  336. * With node = NULL
  337. *
  338. * (1) We went left past the final peer: The new node name is larger
  339. * than any existing node name at that level.
  340. * (2) We broke out in the middle of the list of peers because the name
  341. * was not found in the ordered list.
  342. * (3) We went down past the final parent: The new node name is
  343. * "deeper" than anything that we currently have in the tree.
  344. *
  345. * With node != NULL
  346. *
  347. * (4) When the node matching the full path is found
  348. */
  349. desc->path = name;
  350. desc->node = node;
  351. desc->peer = left;
  352. desc->parent = above;
  353. desc->relpath = relpath;
  354. return ret;
  355. }
  356. /****************************************************************************
  357. * Public Functions
  358. ****************************************************************************/
  359. /****************************************************************************
  360. * Name: inode_search
  361. *
  362. * Description:
  363. * Find the inode associated with 'path' returning the inode references
  364. * and references to its companion nodes.
  365. *
  366. * If a mountpoint is encountered in the search prior to encountering the
  367. * terminal node, the search will terminate at the mountpoint inode. That
  368. * inode and the relative path from the mountpoint, 'relpath' will be
  369. * returned.
  370. *
  371. * inode_search will follow soft links in path leading up to the terminal
  372. * node. Whether or no inode_search() will deference that terminal node
  373. * depends on the 'nofollow' input.
  374. *
  375. * If a soft link is encountered that is not the terminal node in the path,
  376. * that link WILL be deferenced unconditionally.
  377. *
  378. * Assumptions:
  379. * The caller holds the g_inode_sem semaphore
  380. *
  381. ****************************************************************************/
  382. int inode_search(FAR struct inode_search_s *desc)
  383. {
  384. int ret;
  385. /* Perform the common _inode_search() logic. This does everything except
  386. * operations special operations that must be performed on the terminal
  387. * node if node is a symbolic link.
  388. */
  389. DEBUGASSERT(desc != NULL);
  390. #ifdef CONFIG_PSEUDOFS_SOFTLINKS
  391. desc->linktgt = NULL;
  392. #endif
  393. ret = _inode_search(desc);
  394. #ifdef CONFIG_PSEUDOFS_SOFTLINKS
  395. if (ret >= 0)
  396. {
  397. FAR struct inode *node;
  398. /* Search completed successfully */
  399. node = desc->node;
  400. DEBUGASSERT(node != NULL);
  401. /* Is the terminal node a softlink? Should we follow it? */
  402. if (!desc->nofollow && INODE_IS_SOFTLINK(node))
  403. {
  404. /* Save some things we need that will be clobbered by the call to
  405. * _inode_linktgt().
  406. */
  407. FAR const char *relpath = desc->relpath; /* Will always be "" here */
  408. /* The terminating inode is a valid soft link: Return the inode,
  409. * corresponding to link target. _inode_linktarget() will follow
  410. * a link (or a series of links to links) and will return the
  411. * link target of the final symbolic link in the series.
  412. */
  413. ret = _inode_linktarget(node, desc);
  414. if (ret < 0)
  415. {
  416. /* The most likely cause for failure is that the target of the
  417. * symbolic link does not exist.
  418. */
  419. return ret;
  420. }
  421. /* The dereferenced node might be a mountpoint */
  422. node = desc->node;
  423. DEBUGASSERT(node != NULL && desc->linktgt != NULL);
  424. if (INODE_IS_MOUNTPT(node))
  425. {
  426. /* Yes... set up for the MOUNTPOINT logic below. */
  427. desc->relpath = relpath;
  428. }
  429. }
  430. /* Handle a special case. This special occurs with either (1)
  431. * inode_search() terminates early because it encountered a MOUNTPOINT
  432. * at an intermediate node in the path, or (2) inode_search()
  433. * terminates because it reached the terminal node and 'nofollow' is
  434. * false and the above logic converted the symbolic link to a
  435. * MOUNTPOINT.
  436. *
  437. * We can detect the special cases because desc->linktgt will be
  438. * non-NULL.
  439. */
  440. if (desc->linktgt != NULL && INODE_IS_MOUNTPT(node))
  441. {
  442. FAR char *buffer;
  443. /* There would be no problem in this case if the link was to
  444. * either to the root directory of the MOUNTPOINT or to a
  445. * regular file within the mounted volume. However, there
  446. * is a problem if the symbolic link is to a directory within
  447. * the mounted volume. In that case, the 'relpath' will be
  448. * relative to the symbolic link and not to the MOUNTPOINT.
  449. *
  450. * We will handle the worst case by creating the full path
  451. * excluding the symbolic link and performing the look-up
  452. * again.
  453. */
  454. if (desc->relpath != NULL && *desc->relpath != '\0')
  455. {
  456. (void)asprintf(&buffer, "%s/%s",
  457. desc->linktgt, desc->relpath);
  458. }
  459. else
  460. {
  461. buffer = strdup(desc->linktgt);
  462. }
  463. if (buffer == NULL)
  464. {
  465. ret = -ENOMEM;
  466. }
  467. else
  468. {
  469. /* Reset the search description and perform the search again. */
  470. RELEASE_SEARCH(desc);
  471. SETUP_SEARCH(desc, buffer, false);
  472. desc->buffer = buffer;
  473. ret = _inode_search(desc);
  474. }
  475. }
  476. }
  477. #endif
  478. return ret;
  479. }
  480. /****************************************************************************
  481. * Name: inode_nextname
  482. *
  483. * Description:
  484. * Given a path with node names separated by '/', return the next path
  485. * segment name.
  486. *
  487. ****************************************************************************/
  488. FAR const char *inode_nextname(FAR const char *name)
  489. {
  490. /* Search for the '/' delimiter or the NUL terminator at the end of the
  491. * path segment.
  492. */
  493. while (*name != '\0' && *name != '/')
  494. {
  495. name++;
  496. }
  497. /* If we found the '/' delimiter, then the path segment we want begins at
  498. * the next character (which might also be the NUL terminator).
  499. */
  500. while (*name == '/')
  501. {
  502. name++;
  503. }
  504. return name;
  505. }