www.pudn.com > efs.rar > fs_namei.c
/***********************************************************************
* fs_namei.c
*
* Posix interface for EFS2
* Copyright (C) 2002, 2003, 2004, 2005, 2006 Qualcomm, Inc.
*
* This file includes the functions related to EFS2's path lookup operations
*
***********************************************************************/
/*===========================================================================
EDIT HISTORY FOR MODULE
This section contains comments describing changes made to the module.
Notice that changes are listed in reverse chronological order.
$Header: //depot/asic/MSMSHARED/services/efs/MSM_EFS.01.02/fs_namei.c#3 $ $DateTime: 2006/05/12 15:17:44 $ $Author: davidb $
when who what, where, why
-------- --- ------------------------------------------------------
2006-05-10 dlb Clarify some mountpoint names.
2005-07-13 dlb Mountpoint cleanup.
2004-10-15 dlb Update copyright line.
2004-10-07 dlb Whitespace cleanup.
2004-07-26 dlb Allow mountpoints to grab entire name.
2003-12-04 dlb Corrected symlink leak.
2003-06-17 jkl Clean up code.
2002-08-20 adm Created.
===========================================================================*/
#include
#include
#include
#include "err.h"
#include "fs_namei.h"
#include "fs_inode.h"
#include "fs_mount.h"
#include "fs_fcntl.h"
static char link_buf[FS_PATH_MAX];
/* Name lookup. */
int
fs_namei (struct nameidata *ndp, const char *path, int flag)
{
int i;
int result = -ENOENT;
const char *namestart, *nameend;
int path_len;
const char *path_last;
int symlink_count = 0;
/* Because of symlinks, copy the entire path into our working buffer. */
path_len = strlen (path);
if (path_len > FS_PATH_MAX)
return -ENAMETOOLONG;
strcpy (ndp->namebuf, path);
path_last = ndp->namebuf + path_len;
ndp->path = ndp->namebuf;
ndp->vp = 0;
/* Is this relative or absolute? XXX: Change this when we have a
* current directory that can be different. */
if (*path == '/')
ndp->dvp = fs_root_vnode;
else
ndp->dvp = fs_root_vnode;
fs_vnode_ref (ndp->dvp);
/* Now do the scan of the path itself. */
namestart = ndp->path;
while (*namestart == '/')
namestart++;
for (;;)
{
/* Coming into the loop, namestart points to the beginning of the
* interesting part of the path. */
nameend = namestart;
/* Scan for the end of the name. */
while (*nameend != 0 && *nameend != '/')
nameend++;
if (nameend - namestart > FS_NAME_MAX) {
result = -ENAMETOOLONG;
goto cleanup;
}
ndp->next = namestart;
ndp->length = nameend - namestart;
/* Trailing slashes cause lookups of zero length filenames. For the
* EFS filesystem, the zero length filename is how '.' is stored, so
* this just conveniently works. A special check will be needed to
* support other filesystems that don't do this. */
result = ndp->dvp->vops->lookup (ndp);
/* The above call is permitted to adjust 'length' */
nameend = namestart + ndp->length;
/* The lookup is allowed to fail if this is the last component of the
* name. */
if (result != 0 && *nameend == 0)
break;
/* Otherwise, a missing entry in the middle is a problem. */
if (result != 0) {
/* cleanup will return the result. */
goto cleanup;
}
/* If this entry is a symlink, and we should be following it, resolve
* the symlink. This resolution then backs up. */
if (S_ISLNK (ndp->vp->mode) &&
!(*nameend == 0 && ((flag & O_NOFOLLOW) != 0)))
{
symlink_count++;
if (symlink_count > 8) {
result = -ELOOP;
goto cleanup;
}
result = ndp->vp->vops->readlink (ndp->vp, link_buf, FS_PATH_MAX);
if (result < 0)
goto cleanup;
/* Start at the beginning of the buffer, since we don't need the path
* components that are there now. */
namestart = ndp->path;
ndp->next = namestart;
ndp->length = nameend - namestart;
/* If the symlink starts with a '/', start over at the root dir. */
if (link_buf[0] == '/') {
fs_vnode_unref (ndp->dvp);
ndp->dvp = fs_root_vnode;
fs_vnode_ref (ndp->dvp);
}
/* Do we need to adjust the name past the end of the buffer. */
if (result != ndp->length) {
if ((path_last + (result - ndp->length)) - ndp->path > FS_PATH_MAX) {
result = -ENAMETOOLONG;
goto cleanup;
}
memmove ((char *) namestart + result, nameend,
path_last - nameend + 1);
path_last += result - ndp->length;
}
/* Now copy in the new name. */
memcpy ((char *) namestart, link_buf, result);
/* Skip any initial slashes. */
while (namestart[0] == '/')
namestart++;
result = 0;
/* Unref the vnode for the symlink. */
fs_vnode_unref (ndp->vp);
ndp->vp = 0;
/* Start from the new name. */
} else {
/* Check if we have crossed a mountpoint. */
for (i = 0; i < FS_MAX_MOUNTS; i++) {
if (fs_mpoints[i].mount_at_dev == ndp->vp->dev &&
fs_mpoints[i].mount_at_inode == ndp->vp->inum)
break;
}
if (i < FS_MAX_MOUNTS) {
/* Mountpoint was hit, so just cross into the new place. */
fs_vnode_unref (ndp->vp);
ndp->vp = fs_mpoints[i].root_node;
fs_vnode_ref (ndp->vp);
}
/* If we are done, return now. */
if (*nameend == 0)
break;
fs_vnode_unref (ndp->dvp);
ndp->dvp = ndp->vp;
ndp->vp = 0;
/* Skip over any slashes at separating the components of the path.
* Making this a while allows multiple slashes to be used without harm.
*/
while (*nameend == '/')
nameend++;
namestart = nameend;
}
}
/* Check for the proper existence operations. First, is the file
* forbidden to exist. */
if ((flag & O_EXCL) != 0 &&
ndp->vp != NULL)
{
result = -EEXIST;
goto cleanup;
}
/* Also check if we are allowed to create the entry. */
if ((flag & O_CREAT) == 0 &&
ndp->vp == NULL)
{
result = -ENOENT;
goto cleanup;
}
/* Finally, return the valid entry. */
return 0;
cleanup:
/* Dereference any nodes we've visited. */
if (ndp->dvp != NULL) {
fs_vnode_unref (ndp->dvp);
ndp->dvp = NULL;
}
if (ndp->vp != NULL) {
fs_vnode_unref (ndp->vp);
ndp->vp = NULL;
}
return result;
}