Soft link: First cut fix for some soft link issues. The fix still has some issues of its own.

This commit is contained in:
Gregory Nutt 2017-02-04 16:35:49 -06:00
parent 4c68324d82
commit 990bed903e
7 changed files with 228 additions and 69 deletions

View File

@ -40,8 +40,8 @@
#include <nuttx/config.h>
#include <stdbool.h>
#include <dirent.h>
#include <string.h>
#include <dirent.h>
#include <assert.h>
#include <errno.h>
@ -253,7 +253,7 @@ FAR DIR *opendir(FAR const char *path)
/* Find the node matching the path. */
memset(&desc, 0, sizeof(struct inode_search_s));
RESET_SEARCH(&desc);
desc.path = path;
ret = inode_search(&desc);

View File

@ -39,7 +39,6 @@
#include <nuttx/config.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
@ -79,7 +78,7 @@ FAR struct inode *inode_find(FAR const char *path, FAR const char **relpath,
* references on the node.
*/
memset(&desc, 0, sizeof(struct inode_search_s));
RESET_SEARCH(&desc);
desc.path = path;
#ifdef CONFIG_PSEUDOFS_SOFTLINKS
desc.nofollow = nofollow;

View File

@ -39,7 +39,6 @@
#include <nuttx/config.h>
#include <string.h>
#include <errno.h>
#include <nuttx/kmalloc.h>
@ -84,7 +83,7 @@ FAR struct inode *inode_unlink(FAR const char *path)
/* Find the node to unlink */
memset(&desc, 0, sizeof(struct inode_search_s));
RESET_SEARCH(&desc);
desc.path = path;
#ifdef CONFIG_PSEUDOFS_SOFTLINKS
desc.nofollow = true;

View File

@ -39,7 +39,6 @@
#include <nuttx/config.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
@ -184,7 +183,7 @@ int inode_reserve(FAR const char *path, FAR struct inode **inode)
/* Find the location to insert the new subtree */
memset(&desc, 0, sizeof(struct inode_search_s));
RESET_SEARCH(&desc);
desc.path = path;
ret = inode_search(&desc);

View File

@ -39,6 +39,7 @@
#include <nuttx/config.h>
#include <stdio.h>
#include <string.h>
#include <limits.h>
#include <assert.h>
@ -48,6 +49,17 @@
#include "inode/inode.h"
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
static int _inode_compare(FAR const char *fname, FAR struct inode *node);
#ifdef CONFIG_PSEUDOFS_SOFTLINKS
static int _inode_linktarget(FAR struct inode *node,
FAR struct inode_search_s *desc);
#endif
static int _inode_search(FAR struct inode_search_s *desc);
/****************************************************************************
* Public Data
****************************************************************************/
@ -66,8 +78,7 @@ FAR struct inode *g_root_inode = NULL;
*
****************************************************************************/
static int _inode_compare(FAR const char *fname,
FAR struct inode *node)
static int _inode_compare(FAR const char *fname, FAR struct inode *node)
{
char *nname = node->i_name;
@ -136,7 +147,7 @@ static int _inode_compare(FAR const char *fname,
}
/****************************************************************************
* Name: inode_linktarget
* Name: _inode_linktarget
*
* Description:
* If the inode is a soft link, then (1) get the name of the full path of
@ -149,8 +160,8 @@ static int _inode_compare(FAR const char *fname,
****************************************************************************/
#ifdef CONFIG_PSEUDOFS_SOFTLINKS
static int inode_linktarget(FAR struct inode *node,
FAR struct inode_search_s *desc)
static int _inode_linktarget(FAR struct inode *node,
FAR struct inode_search_s *desc)
{
unsigned int count = 0;
bool save;
@ -169,13 +180,13 @@ static int inode_linktarget(FAR struct inode *node,
{
/* Reset and reinitialize the search descriptor. */
memset(desc, 0, sizeof(struct inode_search_s));
RESET_SEARCH(desc);
desc->path = (FAR const char *)node->u.i_link;
desc->nofollow = true;
/* Look up inode associated with the target of the symbolic link */
ret = inode_search(desc);
ret = _inode_search(desc);
if (ret < 0)
{
break;
@ -200,25 +211,18 @@ static int inode_linktarget(FAR struct inode *node,
#endif
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: inode_search
* Name: _inode_search
*
* Description:
* Find the inode associated with 'path' returning the inode references
* and references to its companion nodes.
* and references to its companion nodes. This is the internal, common
* implementation of inode_search().
*
* If a mountpoint is encountered in the search prior to encountering the
* terminal node, the search will terminate at the mountpoint inode. That
* inode and the relative path from the mountpoint, 'relpath' will be
* returned.
*
* inode_search will follow soft links in path leading up to the terminal
* node. Whether or no inode_search() will deference that terminal node
* depends on the 'nofollow' input.
*
* If a soft link is encountered that is not the terminal node in the path,
* that link WILL be deferenced unconditionally.
*
@ -227,7 +231,7 @@ static int inode_linktarget(FAR struct inode *node,
*
****************************************************************************/
int inode_search(FAR struct inode_search_s *desc)
static int _inode_search(FAR struct inode_search_s *desc)
{
FAR const char *name;
FAR struct inode *node = g_root_inode;
@ -306,33 +310,6 @@ int inode_search(FAR struct inode_search_s *desc)
relpath = name;
ret = OK;
#ifdef CONFIG_PSEUDOFS_SOFTLINKS
/* Is the terminal node a softlink? Should we follow it? */
if (!desc->nofollow && INODE_IS_SOFTLINK(node))
{
int status;
/* The terminating inode is a valid soft link: Return the
* inode, corresponding to link target.
*/
status = inode_linktarget(node, desc);
if (status < 0)
{
ret = status;
}
else
{
/* Return, skipping setting of 'desc' return values
* at the normal exit point.
*/
return OK;
}
}
#endif
break;
}
else
@ -354,7 +331,7 @@ int inode_search(FAR struct inode_search_s *desc)
* link, and (3) continue searching with that inode instead.
*/
status = inode_linktarget(node, desc);
status = _inode_linktarget(node, desc);
if (status < 0)
{
/* Probably means that the the target of the symbolic
@ -373,29 +350,30 @@ int inode_search(FAR struct inode_search_s *desc)
/* The node was a valid symbolic link and we have
* jumped to a different, spot in the the pseudo
* file system tree.
*
* Continue from this new inode.
*/
node = newnode;
/* Check if this took us to a mountpoint. */
if (INODE_IS_MOUNTPT(node))
if (INODE_IS_MOUNTPT(newnode))
{
/* Yes.. return the mountpoint information.
* REVISIT: The relpath is incorrect in this case.
* It is relative to symbolic link, not to the
* root of the mount.
*/
/* Yes.. return the path to the link target */
relpath = name;
desc->linktgt = node->u.i_link;
/* Return the mountpoint information. */
node = newnode;
above = NULL;
left = NULL;
relpath = name;
ret = OK;
break;
}
/* Continue from this new inode. */
node = newnode;
}
}
}
@ -432,6 +410,148 @@ int inode_search(FAR struct inode_search_s *desc)
return ret;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: inode_search
*
* Description:
* Find the inode associated with 'path' returning the inode references
* and references to its companion nodes.
*
* If a mountpoint is encountered in the search prior to encountering the
* terminal node, the search will terminate at the mountpoint inode. That
* inode and the relative path from the mountpoint, 'relpath' will be
* returned.
*
* inode_search will follow soft links in path leading up to the terminal
* node. Whether or no inode_search() will deference that terminal node
* depends on the 'nofollow' input.
*
* If a soft link is encountered that is not the terminal node in the path,
* that link WILL be deferenced unconditionally.
*
* Assumptions:
* The caller holds the g_inode_sem semaphore
*
****************************************************************************/
int inode_search(FAR struct inode_search_s *desc)
{
int ret;
/* Perform the common _inode_search() logic. This does everything except
* operations special operations that must be performed on the terminal
* node if node is a symbolic link.
*/
DEBUGASSERT(desc != NULL);
#ifdef CONFIG_PSEUDOFS_SOFTLINKS
desc->linktgt = NULL;
#endif
ret = _inode_search(desc);
#ifdef CONFIG_PSEUDOFS_SOFTLINKS
if (ret >= 0)
{
FAR struct inode *node;
/* Search completed successfully */
node = desc->node;
DEBUGASSERT(node != NULL);
/* Is the terminal node a softlink? Should we follow it? */
if (!desc->nofollow && INODE_IS_SOFTLINK(node))
{
/* Save some things we need that will be clobbered by the call to
* _inode_linktgt().
*/
FAR struct inode *link = node;
FAR const char *relpath = desc->relpath; /* Will always be "" here */
/* The terminating inode is a valid soft link: Return the inode,
* corresponding to link target.
*/
ret = _inode_linktarget(link, desc);
if (ret < 0)
{
/* The most likely cause for failure is that the target of the
* symbolic link does not exist.
*/
return ret;
}
/* The dereferenced node might be a mountpoint */
node = desc->node;
DEBUGASSERT(node != NULL);
if (INODE_IS_MOUNTPT(node))
{
/* Yes... set up for the MOUNTPOINT logic below. */
desc->relpath = relpath;
desc->linktgt = link->u.i_link;
}
}
/* Handle a special case. This special occurs with either (1)
* inode_search() terminates early because it encountered a MOUNTPOINT
* at an intermediate node in the path, or (2) inode_search()
* terminates because it reached the terminal node and 'nofollow' is
* false and the above logic converted the symbolic link to a
* MOUNTPOINT.
*
* We can detect the special cases because desc->linktgt will be
* non-NULL.
*/
if (desc->linktgt != NULL && INODE_IS_MOUNTPT(node))
{
/* There would be no problem in this case if the link was to
* either to the root directory of the MOUNTPOINT or to a
* regular file within the the mounted volume. However,
* there is a problem if the symbolic link is to a directory
* within the mounted volume. In that case, the 'relpath'
* will be relative to the symbolic link and not to the
* MOUNTPOINT.
*
* We will handle the worst case by creating the full path
* excluding the symbolic link and performing the look-up
* again.
*/
if (desc->relpath != NULL && *desc->relpath != '\0')
{
snprintf(desc->fullpath, PATH_MAX, "%s/%s",
desc->linktgt, desc->relpath);
}
else
{
strncpy(desc->fullpath, desc->linktgt, PATH_MAX);
}
/* Reset the search description and perform the search again. */
RESET_SEARCH(desc);
desc->path = desc->fullpath;
ret = _inode_search(desc);
}
}
#endif
return ret;
}
/****************************************************************************
* Name: inode_nextname
*
@ -447,7 +567,7 @@ FAR const char *inode_nextname(FAR const char *name)
* path segment.
*/
while (*name && *name != '/')
while (*name != '\0' && *name != '/')
{
name++;
}
@ -456,7 +576,7 @@ FAR const char *inode_nextname(FAR const char *name)
* the next character (which might also be the NUL terminator).
*/
if (*name)
while (*name == '/')
{
name++;
}

View File

@ -41,6 +41,7 @@
****************************************************************************/
#include <nuttx/config.h>
#include <nuttx/compiler.h>
#include <sys/types.h>
#include <stdint.h>
@ -48,11 +49,41 @@
#include <dirent.h>
#include <nuttx/fs/fs.h>
#include <nuttx/compiler.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#ifdef CONFIG_PSEUDOFS_SOFTLINKS
# define RESET_SEARCH(d) \
do \
{ \
(d)->path = NULL; \
(d)->node = NULL; \
(d)->peer = NULL; \
(d)->parent = NULL; \
(d)->relpath = NULL; \
(d)->linktgt = NULL; \
(d)->nofollow = false; \
} \
while (0)
#else
# define RESET_SEARCH(d) \
do \
{ \
(d)->path = NULL; \
(d)->node = NULL; \
(d)->peer = NULL; \
(d)->parent = NULL; \
(d)->relpath = NULL; \
} \
while (0)
#endif
/****************************************************************************
* Public Types
****************************************************************************/
/* This is the type of the argument to inode_search().
*
* path - INPUT: Path of inode to find
@ -66,10 +97,19 @@
* relpath - INPUT: (not used)
* OUTPUT: If the returned inode is a mountpoint, this is the
* relative path from the mountpoint.
* linktgt - INPUT: (not used)
* OUTPUT: If a symobolic link into a mounted file system is
* detected while traversing the path, then the link
* will be converted to a mountpoint inode if the
* mountpoint link is in an intermediate node of the
* path or at the final node of the path with nofollow=true.
* nofollow - INPUT: true: terminal node is returned; false: if the
* terminal is a soft link, then return the inode of
* the link target.
* - OUTPUT: (not used)
* fullpath - INPUT: Not used
* - OUTPUT: May hold an intermediate path which is probably of
* no interest to the caller.
*/
struct inode_search_s
@ -80,7 +120,9 @@ struct inode_search_s
FAR struct inode *parent; /* Node "above" the found inode */
FAR const char *relpath; /* Relative path into the mountpoint */
#ifdef CONFIG_PSEUDOFS_SOFTLINKS
FAR const char *linktgt; /* Target of symbolic link if linked to a directory */
bool nofollow; /* true: Don't follow terminal soft link */
char fullpath[PATH_MAX]; /* Path expansion buffer */
#endif
};

View File

@ -137,7 +137,7 @@ static int automount_findinode(FAR const char *path)
/* Find the inode */
memset(&desc, 0, sizeof(struct inode_search_s));
RESET_SEARCH(&desc);
desc.path = path;
ret = inode_search(&desc);