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;
}