setgroups check int > NGROUPS, so negative would fail; minor cleanup
[unix-history] / usr / src / sys / kern / vfs_lookup.c
index c60499b..33b856b 100644 (file)
@@ -2,19 +2,9 @@
  * Copyright (c) 1982, 1986, 1989 Regents of the University of California.
  * All rights reserved.
  *
  * Copyright (c) 1982, 1986, 1989 Regents of the University of California.
  * All rights reserved.
  *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by the University of California, Berkeley.  The name of the
- * University may not be used to endorse or promote products derived
- * from this software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ * %sccs.include.redist.c%
  *
  *
- *     @(#)vfs_lookup.c        7.9 (Berkeley) %G%
+ *     @(#)vfs_lookup.c        7.22 (Berkeley) %G%
  */
 
 #include "param.h"
  */
 
 #include "param.h"
@@ -82,32 +72,47 @@ namei(ndp)
        int docache;                    /* == 0 do not cache last component */
        int flag;                       /* LOOKUP, CREATE, RENAME or DELETE */
        int wantparent;                 /* 1 => wantparent or lockparent flag */
        int docache;                    /* == 0 do not cache last component */
        int flag;                       /* LOOKUP, CREATE, RENAME or DELETE */
        int wantparent;                 /* 1 => wantparent or lockparent flag */
+       int lockparent;                 /* 1 => lockparent flag */
+       int getbuf;                     /* 1 => Malloc a pathname buffer */
+       int rdonly;                     /* mounted read-only flag bit(s) */
        int error = 0;
 
        int error = 0;
 
+       /*
+        * Setup: break out flag bits into variables.
+        */
+       ndp->ni_dvp = NULL;
        flag = ndp->ni_nameiop & OPFLAG;
        wantparent = ndp->ni_nameiop & (LOCKPARENT|WANTPARENT);
        flag = ndp->ni_nameiop & OPFLAG;
        wantparent = ndp->ni_nameiop & (LOCKPARENT|WANTPARENT);
+       lockparent = ndp->ni_nameiop & LOCKPARENT;
        docache = (ndp->ni_nameiop & NOCACHE) ^ NOCACHE;
        docache = (ndp->ni_nameiop & NOCACHE) ^ NOCACHE;
+       getbuf = (ndp->ni_nameiop & HASBUF) ^ HASBUF;
        if (flag == DELETE || wantparent)
                docache = 0;
        if (flag == DELETE || wantparent)
                docache = 0;
+       rdonly = MNT_RDONLY;
+       if (ndp->ni_nameiop & REMOTE)
+               rdonly |= MNT_EXRDONLY;
        /*
         * Get a buffer for the name to be translated, and copy the
         * name into the buffer.
         */
        /*
         * Get a buffer for the name to be translated, and copy the
         * name into the buffer.
         */
-       MALLOC(ndp->ni_pnbuf, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK);
-       if (ndp->ni_segflg == UIO_SYSSPACE)
-               error = copystr(ndp->ni_dirp, ndp->ni_pnbuf, MAXPATHLEN,
-                   &ndp->ni_pathlen);
-       else
-               error = copyinstr(ndp->ni_dirp, ndp->ni_pnbuf, MAXPATHLEN,
-                   &ndp->ni_pathlen);
-       if (error) {
-               free(ndp->ni_pnbuf, M_NAMEI);
-               return (error);
+       if (getbuf) {
+               MALLOC(ndp->ni_pnbuf, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK);
+               if (ndp->ni_segflg == UIO_SYSSPACE)
+                       error = copystr(ndp->ni_dirp, ndp->ni_pnbuf,
+                                   MAXPATHLEN, &ndp->ni_pathlen);
+               else
+                       error = copyinstr(ndp->ni_dirp, ndp->ni_pnbuf,
+                                   MAXPATHLEN, &ndp->ni_pathlen);
+               if (error) {
+                       free(ndp->ni_pnbuf, M_NAMEI);
+                       ndp->ni_vp = NULL;
+                       return (error);
+               }
+               ndp->ni_ptr = ndp->ni_pnbuf;
        }
        }
-       ndp->ni_ptr = ndp->ni_pnbuf;
        ndp->ni_loopcnt = 0;
        dp = ndp->ni_cdir;
        ndp->ni_loopcnt = 0;
        dp = ndp->ni_cdir;
-       dp->v_count++;
+       VREF(dp);
 #ifdef KTRACE
        if (KTRPOINT(u.u_procp, KTR_NAMEI))
                ktrnamei(u.u_procp->p_tracep, ndp->ni_pnbuf);
 #ifdef KTRACE
        if (KTRPOINT(u.u_procp, KTR_NAMEI))
                ktrnamei(u.u_procp->p_tracep, ndp->ni_pnbuf);
@@ -126,7 +131,7 @@ start:
                }
                if ((dp = ndp->ni_rdir) == NULL)
                        dp = rootdir;
                }
                if ((dp = ndp->ni_rdir) == NULL)
                        dp = rootdir;
-               dp->v_count++;
+               VREF(dp);
        }
        VOP_LOCK(dp);
        ndp->ni_endoff = 0;
        }
        VOP_LOCK(dp);
        ndp->ni_endoff = 0;
@@ -145,51 +150,32 @@ dirloop:
         * that ni_ptr would be left pointing to the last component, but since
         * the ni_pnbuf gets free'd, that is not a good idea.
         */
         * that ni_ptr would be left pointing to the last component, but since
         * the ni_pnbuf gets free'd, that is not a good idea.
         */
-#ifdef notdef
-       for (cp = ndp->ni_ptr; *cp != 0 && *cp != '/'; cp++) {
-               if ((*cp & 0200) == 0)
-                       continue;
-               if ((*cp&0377) == ('/'|0200) || flag != DELETE) {
-                       error = EINVAL;
-                       goto bad;
-               }
-       }
-       ndp->ni_namelen = cp - ndp->ni_ptr;
-       if (ndp->ni_namelen >= MAXNAMLEN) {
-               error = ENAMETOOLONG;
-               goto bad;
-       }
-       ndp->ni_pathlen -= ndp->ni_namelen;
-#ifdef NAMEI_DIAGNOSTIC
-       { char c = *cp;
-       *cp = '\0';
-       printf("{%s}: ", ndp->ni_ptr);
-       *cp = c; }
-#endif
-#else fornow
-       ndp->ni_hash = 0;
-       for (cp = ndp->ni_ptr, i = 0; *cp != 0 && *cp != '/'; cp++) {
-               if (i >= MAXNAMLEN) {
-                       error = ENAMETOOLONG;
-                       goto bad;
-               }
-               if (*cp & 0200)
-                       if ((*cp&0377) == ('/'|0200) || flag != DELETE) {
-                               error = EINVAL;
+       if (getbuf) {
+               ndp->ni_hash = 0;
+               for (cp = ndp->ni_ptr, i = 0; *cp != 0 && *cp != '/'; cp++) {
+                       if (i >= MAXNAMLEN) {
+                               error = ENAMETOOLONG;
                                goto bad;
                        }
                                goto bad;
                        }
-               ndp->ni_dent.d_name[i++] = *cp;
-               ndp->ni_hash += (unsigned char)*cp * i;
-       }
-       ndp->ni_namelen = i;
-       ndp->ni_dent.d_namlen = i;
-       ndp->ni_dent.d_name[i] = '\0';
-       ndp->ni_pathlen -= i;
+                       if (*cp & 0200)
+                               if ((*cp&0377) == ('/'|0200) ||
+                                   flag != DELETE) {
+                                       error = EINVAL;
+                                       goto bad;
+                               }
+                       ndp->ni_dent.d_name[i++] = *cp;
+                       ndp->ni_hash += (unsigned char)*cp * i;
+               }
+               ndp->ni_namelen = i;
+               ndp->ni_dent.d_namlen = i;
+               ndp->ni_dent.d_name[i] = '\0';
+               ndp->ni_pathlen -= i;
+               ndp->ni_next = cp;
 #ifdef NAMEI_DIAGNOSTIC
 #ifdef NAMEI_DIAGNOSTIC
-       printf("{%s}: ", ndp->ni_dent.d_name);
+               printf("{%s}: ", ndp->ni_dent.d_name);
 #endif
 #endif
-#endif fornow
-       ndp->ni_next = cp;
+       }
+       cp = ndp->ni_next;
        ndp->ni_makeentry = 1;
        if (*cp == '\0' && docache == 0)
                ndp->ni_makeentry = 0;
        ndp->ni_makeentry = 1;
        if (*cp == '\0' && docache == 0)
                ndp->ni_makeentry = 0;
@@ -206,7 +192,8 @@ dirloop:
                        error = EISDIR;
                        goto bad;
                }
                        error = EISDIR;
                        goto bad;
                }
-               free(ndp->ni_pnbuf, M_NAMEI);
+               if (getbuf)
+                       free(ndp->ni_pnbuf, M_NAMEI);
                if (!(ndp->ni_nameiop & LOCKLEAF))
                        VOP_UNLOCK(dp);
                ndp->ni_vp = dp;
                if (!(ndp->ni_nameiop & LOCKLEAF))
                        VOP_UNLOCK(dp);
                ndp->ni_vp = dp;
@@ -226,16 +213,18 @@ dirloop:
                for (;;) {
                        if (dp == ndp->ni_rdir || dp == rootdir) {
                                ndp->ni_dvp = dp;
                for (;;) {
                        if (dp == ndp->ni_rdir || dp == rootdir) {
                                ndp->ni_dvp = dp;
-                               dp->v_count++;
+                               ndp->ni_vp = dp;
+                               VREF(dp);
                                goto nextname;
                        }
                                goto nextname;
                        }
-                       if ((dp->v_flag & VROOT) == 0)
+                       if ((dp->v_flag & VROOT) == 0 ||
+                               (ndp->ni_nameiop & NOCROSSMOUNT))
                                break;
                        tdp = dp;
                                break;
                        tdp = dp;
-                       dp = dp->v_mount->m_vnodecovered;
+                       dp = dp->v_mount->mnt_vnodecovered;
                        vput(tdp);
                        vput(tdp);
+                       VREF(dp);
                        VOP_LOCK(dp);
                        VOP_LOCK(dp);
-                       dp->v_count++;
                }
        }
 
                }
        }
 
@@ -248,21 +237,24 @@ dirloop:
 #ifdef NAMEI_DIAGNOSTIC
                printf("not found\n");
 #endif
 #ifdef NAMEI_DIAGNOSTIC
                printf("not found\n");
 #endif
+               if (flag == LOOKUP || flag == DELETE ||
+                   error != ENOENT || *cp != 0)
+                       goto bad;
                /*
                 * If creating and at end of pathname, then can consider
                 * allowing file to be created.
                 */
                /*
                 * If creating and at end of pathname, then can consider
                 * allowing file to be created.
                 */
-               if (ndp->ni_dvp->v_mount->m_flag & M_RDONLY)
+               if (ndp->ni_dvp->v_mount->mnt_flag & rdonly) {
                        error = EROFS;
                        error = EROFS;
-               if (flag == LOOKUP || flag == DELETE ||
-                   error != ENOENT || *cp != 0)
                        goto bad;
                        goto bad;
+               }
                /*
                 * We return with ni_vp NULL to indicate that the entry
                 * doesn't currently exist, leaving a pointer to the
                 * (possibly locked) directory inode in ndp->ni_dvp.
                 */
                /*
                 * We return with ni_vp NULL to indicate that the entry
                 * doesn't currently exist, leaving a pointer to the
                 * (possibly locked) directory inode in ndp->ni_dvp.
                 */
-               FREE(ndp->ni_pnbuf, M_NAMEI);
+               if (getbuf)
+                       FREE(ndp->ni_pnbuf, M_NAMEI);
                return (0);     /* should this be ENOENT? */
        }
 #ifdef NAMEI_DIAGNOSTIC
                return (0);     /* should this be ENOENT? */
        }
 #ifdef NAMEI_DIAGNOSTIC
@@ -279,11 +271,13 @@ dirloop:
                struct uio auio;
                int linklen;
 
                struct uio auio;
                int linklen;
 
+               if (!getbuf)
+                       panic("namei: unexpected symlink");
                if (++ndp->ni_loopcnt > MAXSYMLINKS) {
                        error = ELOOP;
                        goto bad2;
                }
                if (++ndp->ni_loopcnt > MAXSYMLINKS) {
                        error = ELOOP;
                        goto bad2;
                }
-               if (ndp->ni_pathlen == 1)
+               if (ndp->ni_pathlen > 1)
                        MALLOC(cp, char *, MAXPATHLEN, M_NAMEI, M_WAITOK);
                else
                        cp = ndp->ni_pnbuf;
                        MALLOC(cp, char *, MAXPATHLEN, M_NAMEI, M_WAITOK);
                else
                        cp = ndp->ni_pnbuf;
@@ -296,27 +290,29 @@ dirloop:
                auio.uio_segflg = UIO_SYSSPACE;
                auio.uio_resid = MAXPATHLEN;
                if (error = VOP_READLINK(dp, &auio, ndp->ni_cred)) {
                auio.uio_segflg = UIO_SYSSPACE;
                auio.uio_resid = MAXPATHLEN;
                if (error = VOP_READLINK(dp, &auio, ndp->ni_cred)) {
-                       if (ndp->ni_pathlen == 1)
+                       if (ndp->ni_pathlen > 1)
                                free(cp, M_NAMEI);
                        goto bad2;
                }
                linklen = MAXPATHLEN - auio.uio_resid;
                if (linklen + ndp->ni_pathlen >= MAXPATHLEN) {
                                free(cp, M_NAMEI);
                        goto bad2;
                }
                linklen = MAXPATHLEN - auio.uio_resid;
                if (linklen + ndp->ni_pathlen >= MAXPATHLEN) {
-                       if (ndp->ni_pathlen == 1)
+                       if (ndp->ni_pathlen > 1)
                                free(cp, M_NAMEI);
                        error = ENAMETOOLONG;
                        goto bad2;
                }
                                free(cp, M_NAMEI);
                        error = ENAMETOOLONG;
                        goto bad2;
                }
-               if (ndp->ni_pathlen == 1) {
+               if (ndp->ni_pathlen > 1) {
                        bcopy(ndp->ni_next, cp + linklen, ndp->ni_pathlen);
                        FREE(ndp->ni_pnbuf, M_NAMEI);
                        ndp->ni_pnbuf = cp;
                } else
                        ndp->ni_pnbuf[linklen] = '\0';
                ndp->ni_ptr = cp;
                        bcopy(ndp->ni_next, cp + linklen, ndp->ni_pathlen);
                        FREE(ndp->ni_pnbuf, M_NAMEI);
                        ndp->ni_pnbuf = cp;
                } else
                        ndp->ni_pnbuf[linklen] = '\0';
                ndp->ni_ptr = cp;
-               ndp->ni_pathlen += linklen;
                vput(dp);
                dp = ndp->ni_dvp;
                vput(dp);
                dp = ndp->ni_dvp;
+               if (lockparent && ndp->ni_pathlen == 1)
+                       VOP_UNLOCK(dp);
+               ndp->ni_pathlen += linklen;
                goto start;
        }
 
                goto start;
        }
 
@@ -325,9 +321,10 @@ dirloop:
         * if so find the root of the mounted file system.
         */
 mntloop:
         * if so find the root of the mounted file system.
         */
 mntloop:
-       while (dp->v_type == VDIR && (mp = dp->v_mountedhere)) {
-               while(mp->m_flag & M_MLOCK) {
-                       mp->m_flag |= M_MWAIT;
+       while (dp->v_type == VDIR && (mp = dp->v_mountedhere) &&
+              (ndp->ni_nameiop & NOCROSSMOUNT) == 0) {
+               while(mp->mnt_flag & MNT_MLOCK) {
+                       mp->mnt_flag |= MNT_MWAIT;
                        sleep((caddr_t)mp, PVFS);
                        goto mntloop;
                }
                        sleep((caddr_t)mp, PVFS);
                        goto mntloop;
                }
@@ -353,22 +350,35 @@ nextname:
                goto dirloop;
        }
        /*
                goto dirloop;
        }
        /*
-        * Check for read-only file systems and executing texts
+        * Check for read-only file systems.
         */
         */
-       if (flag != LOOKUP && (error = vn_access(dp, VWRITE, ndp->ni_cred)))
-               goto bad2;
+       if (flag == DELETE || flag == RENAME) {
+               /*
+                * Disallow directory write attempts on read-only
+                * file systems.
+                */
+               if ((dp->v_mount->mnt_flag & rdonly) ||
+                   (wantparent && (ndp->ni_dvp->v_mount->mnt_flag & rdonly))) {
+                       error = EROFS;
+                       goto bad2;
+               }
+       }
        if (!wantparent)
                vrele(ndp->ni_dvp);
        if ((ndp->ni_nameiop & LOCKLEAF) == 0)
                VOP_UNLOCK(dp);
        if (!wantparent)
                vrele(ndp->ni_dvp);
        if ((ndp->ni_nameiop & LOCKLEAF) == 0)
                VOP_UNLOCK(dp);
-       FREE(ndp->ni_pnbuf, M_NAMEI);
+       if (getbuf)
+               FREE(ndp->ni_pnbuf, M_NAMEI);
        return (0);
 
 bad2:
        return (0);
 
 bad2:
+       if (lockparent && *ndp->ni_next == '\0')
+               VOP_UNLOCK(ndp->ni_dvp);
        vrele(ndp->ni_dvp);
 bad:
        vput(dp);
        ndp->ni_vp = NULL;
        vrele(ndp->ni_dvp);
 bad:
        vput(dp);
        ndp->ni_vp = NULL;
-       FREE(ndp->ni_pnbuf, M_NAMEI);
+       if (getbuf)
+               FREE(ndp->ni_pnbuf, M_NAMEI);
        return (error);
 }
        return (error);
 }