Whamcloud - gitweb
b=17604
[fs/lustre-release.git] / libsysio / src / namei.c
index b4d8569..63e610e 100644 (file)
@@ -9,7 +9,7 @@
  *    terms of the GNU Lesser General Public License
  *    (see cit/LGPL or http://www.gnu.org/licenses/lgpl.html)
  *
- *    Cplant(TM) Copyright 1998-2003 Sandia Corporation. 
+ *    Cplant(TM) Copyright 1998-2006 Sandia Corporation. 
  *    Under the terms of Contract DE-AC04-94AL85000, there is a non-exclusive
  *    license for use of this work by or on behalf of the US Government.
  *    Export of this program may require a license from the United States
  * lee@sandia.gov
  */
 
-#if defined(AUTOMOUNT_FILE_NAME) && defined(__linux__)
-#define _BSD_SOURCE
-#endif
-
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
 #include <assert.h>
+#include <unistd.h>
 #include <sys/param.h>
 #include <sys/types.h>
 #include <sys/stat.h>
@@ -87,15 +84,26 @@ lookup(struct pnode *parent,
        struct qstr *name,
        struct pnode **pnop,
        struct intent *intnt,
-       const char *path)
+       const char *path,
+       int check_permissions)
 {
-       struct pnode *pno;
        int     err;
+       struct pnode *pno;
 
        if (!parent->p_base->pb_ino)
                return -ENOTDIR;
 
        /*
+        * Sometimes we don't want to check permissions. At initialization
+        * time, for instance.
+        */
+       if (check_permissions) {
+               err = _sysio_permitted(parent, X_OK);
+               if (err)
+                       return err;
+       }
+
+       /*
         * Short-circuit `.' and `..'; We don't cache those.
         */
        pno = NULL;
@@ -146,6 +154,7 @@ lookup(struct pnode *parent,
  * ND_NOFOLLOW         symbolic links are not followed
  * ND_NEGOK            if terminal/leaf does not exist, return
  *                      path node (alias) anyway.
+ * ND_NOPERMCHECK      do not check permissions
  */
 int
 _sysio_path_walk(struct pnode *parent, struct nameidata *nd)
@@ -177,6 +186,30 @@ _sysio_path_walk(struct pnode *parent, struct nameidata *nd)
                parent = nd->nd_root;
        }
 
+#ifdef DEFER_INIT_CWD
+       if (!parent) {
+               const char *icwd;
+
+               if (!_sysio_init_cwd && !nd->nd_root)
+                       abort();
+
+               /*
+                * Finally have to set the current working directory. We can
+                * not tolerate errors here or else risk leaving the process
+                * in a very unexpected location. We abort then unless all goes
+                * well.
+                */
+               icwd = _sysio_init_cwd;
+               _sysio_init_cwd = NULL;
+               parent = nd->nd_root;
+               if (!parent)
+                       abort();
+               (void )_sysio_namei(nd->nd_root, icwd, 0, NULL, &parent);
+               if (_sysio_p_chdir(parent) != 0)
+                       abort();
+       }
+#endif
+
        /*
         * (Re)Validate the parent.
         */
@@ -200,7 +233,7 @@ _sysio_path_walk(struct pnode *parent, struct nameidata *nd)
         */
        for (;;) {
                ino = nd->nd_pno->p_base->pb_ino;
-               if (S_ISLNK(ino->i_mode) &&
+               if (S_ISLNK(ino->i_stbuf.st_mode) &&
                    (next.len || !(nd->nd_flags & ND_NOFOLLOW))) {
                        char    *lpath;
                        ssize_t cc;
@@ -231,12 +264,14 @@ _sysio_path_walk(struct pnode *parent, struct nameidata *nd)
                        lpath[cc] = '\0';                       /* NUL term */
                        /*
                         * Handle symbolic links with recursion. Yuck!
+                        * Pass the NULL intent for recursive symlink
+                        * except the last component.
                         */
                        ND_INIT(&nameidata,
-                               (nd->nd_flags | ND_NEGOK),
+                               nd->nd_flags,
                                lpath,
                                nd->nd_root,
-                               nd->nd_intent);
+                               !next.len ? nd->nd_intent : NULL);
                        nameidata.nd_slicnt = nd->nd_slicnt + 1;
                        err =
                            _sysio_path_walk(nd->nd_pno->p_parent, &nameidata);
@@ -249,10 +284,10 @@ _sysio_path_walk(struct pnode *parent, struct nameidata *nd)
                }
 #ifdef AUTOMOUNT_FILE_NAME
                else if (ino &&
-                        S_ISDIR(ino->i_mode) &&
+                        S_ISDIR(ino->i_stbuf.st_mode) &&
                         (nd->nd_pno->p_mount->mnt_flags & MOUNT_F_AUTO) &&
                         nd->nd_amcnt < MAX_MOUNT_DEPTH &&
-                        ino->i_mode & S_ISUID) {
+                        ino->i_stbuf.st_mode & S_ISUID) {
                        struct pnode *pno;
 
                        /*
@@ -266,7 +301,8 @@ _sysio_path_walk(struct pnode *parent, struct nameidata *nd)
                                   &_sysio_mount_file_name,
                                   &pno,
                                   NULL,
-                                  NULL);
+                                  NULL,
+                                  1);
                        if (pno)
                                P_RELE(pno);
                        if (!err && _sysio_automount(pno) == 0) {
@@ -336,7 +372,7 @@ _sysio_path_walk(struct pnode *parent, struct nameidata *nd)
                /*
                 * Parent must be a directory.
                 */
-               if (ino && !S_ISDIR(ino->i_mode)) {
+               if (ino && !S_ISDIR(ino->i_stbuf.st_mode)) {
                        err = -ENOTDIR;
                        break;
                }
@@ -379,7 +415,8 @@ _sysio_path_walk(struct pnode *parent, struct nameidata *nd)
                           (path || !next.len)
                             ? nd->nd_intent
                             : NULL,
-                          (path && next.len) ? path : NULL);
+                          (path && next.len) ? path : NULL,
+                          !(nd->nd_flags & ND_NOPERMCHECK));
                if (err) {
                        if (err == -ENOENT &&
                            !next.len &&
@@ -415,7 +452,8 @@ _sysio_path_walk(struct pnode *parent, struct nameidata *nd)
                 * Make sure the last processed component was a directory. The
                 * trailing slashes are illegal behind anything else.
                 */
-               if (!(err || S_ISDIR(nd->nd_pno->p_base->pb_ino->i_mode)))
+               if (!(err ||
+                     S_ISDIR(nd->nd_pno->p_base->pb_ino->i_stbuf.st_mode)))
                        err = -ENOTDIR;
        }