keep track of pending selects
[unix-history] / usr / src / sys / kern / vfs_lookup.c
index d369fbc..ec47d44 100644 (file)
@@ -14,7 +14,7 @@
  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  *
  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  *
- *     @(#)vfs_lookup.c        7.12 (Berkeley) %G%
+ *     @(#)vfs_lookup.c        7.21 (Berkeley) %G%
  */
 
 #include "param.h"
  */
 
 #include "param.h"
@@ -83,34 +83,46 @@ namei(ndp)
        int flag;                       /* LOOKUP, CREATE, RENAME or DELETE */
        int wantparent;                 /* 1 => wantparent or lockparent flag */
        int lockparent;                 /* 1 => lockparent flag */
        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);
        lockparent = ndp->ni_nameiop & LOCKPARENT;
        docache = (ndp->ni_nameiop & NOCACHE) ^ NOCACHE;
        flag = ndp->ni_nameiop & OPFLAG;
        wantparent = ndp->ni_nameiop & (LOCKPARENT|WANTPARENT);
        lockparent = ndp->ni_nameiop & LOCKPARENT;
        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);
-               ndp->ni_vp = NULL;
-               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);
@@ -129,7 +141,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;
@@ -148,51 +160,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;
@@ -209,7 +202,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;
@@ -229,16 +223,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++;
                }
        }
 
                }
        }
 
@@ -251,21 +247,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
@@ -282,6 +281,8 @@ 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;
@@ -317,11 +318,11 @@ dirloop:
                } else
                        ndp->ni_pnbuf[linklen] = '\0';
                ndp->ni_ptr = 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_next == '\0')
+               if (lockparent && ndp->ni_pathlen == 1)
                        VOP_UNLOCK(dp);
                        VOP_UNLOCK(dp);
+               ndp->ni_pathlen += linklen;
                goto start;
        }
 
                goto start;
        }
 
@@ -330,9 +331,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;
                }
@@ -358,15 +360,25 @@ 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:
@@ -376,6 +388,7 @@ bad2:
 bad:
        vput(dp);
        ndp->ni_vp = NULL;
 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);
 }