use shorts to move data
[unix-history] / usr / src / sys / kern / vfs_lookup.c
index d0e6431..c861d1a 100644 (file)
@@ -1,8 +1,11 @@
-/*     vfs_lookup.c    4.6     81/04/28        */
+/*     vfs_lookup.c    4.15    82/04/19        */
+
+/* merged into kernel: @(#)nami.c 2.3 4/8/82 */
 
 #include "../h/param.h"
 #include "../h/systm.h"
 #include "../h/inode.h"
 
 #include "../h/param.h"
 #include "../h/systm.h"
 #include "../h/inode.h"
+#include "../h/fs.h"
 #include "../h/mount.h"
 #include "../h/dir.h"
 #include "../h/user.h"
 #include "../h/mount.h"
 #include "../h/dir.h"
 #include "../h/user.h"
@@ -11,7 +14,7 @@
 
 /*
  * Convert a pathname into a pointer to
 
 /*
  * Convert a pathname into a pointer to
- * an inode. Note that the inode is locked.
+ * a locked inode.
  *
  * func = function called to get next char of name
  *     &uchar if name is in user space
  *
  * func = function called to get next char of name
  *     &uchar if name is in user space
  * flag = 0 if name is sought
  *     1 if name is to be created
  *     2 if name is to be deleted
  * flag = 0 if name is sought
  *     1 if name is to be created
  *     2 if name is to be deleted
+ * follow = 1 if links are to be followed at the end of the name
  */
 struct inode *
  */
 struct inode *
-namei(func, flag)
-int (*func)();
+namei(func, flag, follow)
+       int (*func)(), flag, follow;
 {
        register struct inode *dp;
 {
        register struct inode *dp;
-       register c;
        register char *cp;
        register char *cp;
-       struct buf *bp;
+       register struct buf *bp, *nbp;
        register struct direct *ep;
        register struct direct *ep;
-       int i;
+       register struct fs *fs;
+       struct inode *pdp;
+       enum {NONE, COMPACT, FOUND} slot;
+       int entryfree, entrysize;
+       int spccnt, size, newsize;
+       int loc, prevoff, curoff;
+       int i, nlink, bsize;
+       unsigned pathlen;
+       daddr_t lbn, bn;
        dev_t d;
        dev_t d;
-       off_t eo;
-#ifdef CHAOS
-       extern long cdevpath;
-#endif
 
 
+       /*
+        * allocate name buffer; copy name
+        */
+       nbp = geteblk(MAXPATHLEN);
+       nlink = 0;
+       for (i = 0, cp = nbp->b_un.b_addr; *cp = (*func)(); i++) {
+               if ((*cp & 0377) == ('/'|0200)) {
+                       u.u_error = EPERM;
+                       break;
+               }
+#ifdef notdef
+               if (*cp++ & 0200 && flag == 1 ||
+                   cp >= nbp->b_un.b_addr + MAXPATHLEN) {
+#else
+               cp++;
+               if (cp >= nbp->b_un.b_addr + MAXPATHLEN) {
+#endif
+                       u.u_error = ENOENT;
+                       break;
+               }
+       }
+       if (u.u_error) {
+               brelse(nbp);
+               return (NULL);
+       }
+       cp = nbp->b_un.b_addr;
        /*
         * If name starts with '/' start from
         * root; otherwise start from current dir.
         */
        /*
         * If name starts with '/' start from
         * root; otherwise start from current dir.
         */
-
        dp = u.u_cdir;
        dp = u.u_cdir;
-       if((c=(*func)()) == '/')
+       if (*cp == '/') {
+               while (*cp == '/')
+                       cp++;
                if ((dp = u.u_rdir) == NULL)
                        dp = rootdir;
                if ((dp = u.u_rdir) == NULL)
                        dp = rootdir;
-       (void) iget(dp->i_dev, dp->i_number);
-       while(c == '/')
-               c = (*func)();
-       if(c == '\0' && flag != 0)
-               u.u_error = ENOENT;
-
-cloop:
-       /*
-        * Here dp contains pointer
-        * to last component matched.
-        */
-
-       if(u.u_error)
-               goto out;
-       if(c == '\0')
-               return(dp);
-
-#ifdef CHAOS
-       if((dp->i_mode & IFMT) == IFCHR &&
-          (cdevpath & (1 << major(dp->i_un.i_rdev)))) {
-               u.u_dirp--;
-               return(dp);
        }
        }
-#endif
+       ilock(dp);
+       dp->i_count++;
+       fs = dp->i_fs;
+       newsize = 0;
+dirloop:
        /*
        /*
-        * If there is another component,
-        * Gather up name into
-        * users' dir buffer.
+        * dp must be a directory and
+        * must have X permission.
+        * cp is a path name relative to that directory.
         */
         */
-
-       cp = &u.u_dbuf[0];
-       while (c != '/' && c != '\0' && u.u_error == 0 ) {
-               if (mpxip!=NULL && c=='!')
-                       break;
-               if (flag==1 && c == ('/'|0200)) {
+       if ((dp->i_mode&IFMT) != IFDIR)
+               u.u_error = ENOTDIR;
+       (void) access(dp, IEXEC);
+dirloop2:
+       for (i = 0; *cp != '\0' && *cp != '/'; cp++) {
+#ifdef notdef
+               if (i >= MAXNAMLEN) {
                        u.u_error = ENOENT;
                        u.u_error = ENOENT;
-                       goto out;
+                       break;
+               }
+               u.u_dent.d_name[i] = *cp;
+#else
+               if (i < MAXNAMLEN) {
+                       u.u_dent.d_name[i] = *cp;
+                       i++;
                }
                }
-               if (cp < &u.u_dbuf[DIRSIZ])
-                       *cp++ = c;
-               c = (*func)();
+#endif
        }
        }
-       while(cp < &u.u_dbuf[DIRSIZ])
-               *cp++ = '\0';
-       while(c == '/')
-               c = (*func)();
-       if (c == '!' && mpxip != NULL) {
+       if (u.u_error) {
                iput(dp);
                iput(dp);
-               plock(mpxip);
-               mpxip->i_count++;
-               return(mpxip);
+               brelse(nbp);
+               return (NULL);
+       }
+       u.u_dent.d_namlen = i;
+       u.u_dent.d_name[i] = '\0';
+       newsize = DIRSIZ(&u.u_dent);
+       u.u_pdir = dp;
+       if (u.u_dent.d_name[0] == '\0') {       /* null name, e.g. "/" or "" */
+               if (flag != 0) {
+                       u.u_error = ENOENT;
+                       iput(dp);
+                       dp = NULL;
+               }
+               u.u_offset = 0;
+               u.u_count = newsize;
+               brelse(nbp);
+               return (dp);
        }
        }
-
-seloop:
-       /*
-        * dp must be a directory and
-        * must have X permission.
-        */
-
-       if((dp->i_mode&IFMT) != IFDIR)
-               u.u_error = ENOTDIR;
-       (void) access(dp, IEXEC);
-       if(u.u_error)
-               goto out;
-
        /*
         * set up to search a directory
         */
        /*
         * set up to search a directory
         */
+       if (flag == 1)
+               slot = NONE;
+       else
+               slot = FOUND;
        u.u_offset = 0;
        u.u_segflg = 1;
        u.u_offset = 0;
        u.u_segflg = 1;
-       eo = 0;
        bp = NULL;
        bp = NULL;
-       if (dp == u.u_rdir && u.u_dent.d_name[0] == '.' &&
-           u.u_dent.d_name[1] == '.' && u.u_dent.d_name[2] == 0)
-               goto cloop;
-
-eloop:
-
-       /*
-        * If at the end of the directory,
-        * the search failed. Report what
-        * is appropriate as per flag.
-        */
-
-       if(u.u_offset >= dp->i_size) {
-               if(bp != NULL)
-                       brelse(bp);
-               if(flag==1 && c=='\0' && dp->i_nlink) {
-                       if(access(dp, IWRITE))
-                               goto out;
-                       u.u_pdir = dp;
-                       if(eo)
-                               u.u_offset = eo-sizeof(struct direct);
-                       else
-                               dp->i_flag |= IUPD|ICHG;
-                       return(NULL);
-               }
-               u.u_error = ENOENT;
-               goto out;
-       }
-
-       /*
-        * If offset is on a block boundary,
-        * read the next directory block.
-        * Release previous if it exists.
-        */
-
-       if((u.u_offset&BMASK) == 0) {
-               if(bp != NULL)
-                       brelse(bp);
-               bp = bread(dp->i_dev,
-                       bmap(dp, (daddr_t)(u.u_offset>>BSHIFT), B_READ));
-               if (bp->b_flags & B_ERROR) {
+       spccnt = 0;
+       loc = 0;
+       while (u.u_offset < dp->i_size) {
+               /*
+                * check to see if enough space has been accumulated to make
+                * an entry by compaction. Reset the free space counter each
+                * time a directory block is crossed.
+                */
+               if (slot == NONE) {
+                       if (spccnt >= newsize) {
+                               slot = COMPACT;
+                               entrysize = u.u_offset - entryfree;
+                       } else if (loc % DIRBLKSIZ == 0) {
+                               entryfree = NULL;
+                               spccnt = 0;
+                       }
+               }
+               /*
+                * If offset is on a block boundary,
+                * read the next directory block.
+                * Release previous if it exists.
+                */
+               if (blkoff(fs, u.u_offset) == 0) {
+                       if (bp != NULL)
+                               brelse(bp);
+                       lbn = (daddr_t)lblkno(fs, u.u_offset);
+                       bsize = blksize(fs, dp, lbn);
+                       if ((bn = bmap(dp, lbn, B_READ)) < 0) {
+                               printf("hole in dir: %s i = %d\n",
+                                   fs->fs_fsmnt, dp->i_number);
+                               if (fs->fs_ronly != 0 ||
+                                   (bn = bmap(dp, lbn, B_WRITE, bsize)) < 0) {
+                                       u.u_offset += bsize;
+                                       bp = NULL;
+                                       continue;
+                               }
+                       }
+                       bp = bread(dp->i_dev, fsbtodb(fs, bn), bsize);
+                       if (bp->b_flags & B_ERROR) {
+                               brelse(bp);
+                               iput(dp);
+                               brelse(nbp);
+                               return (NULL);
+                       }
+                       loc = 0;
+               } else {
+                       loc += ep->d_reclen;
+               }
+               /*
+                * calculate the next directory entry and run
+                * some rudimentary bounds checks to make sure
+                * that it is reasonable. If the check fails
+                * resync at the beginning of the next directory
+                * block.
+                */
+               ep = (struct direct *)(bp->b_un.b_addr + loc);
+               i = DIRBLKSIZ - (loc & (DIRBLKSIZ - 1));
+               if (ep->d_reclen <= 0 || ep->d_reclen > i) {
+                       loc += i;
+                       u.u_offset += i;
+                       continue;
+               }
+               /*
+                * If an appropriate sized hole has not yet been found,
+                * check to see if one is available. Also accumulate space
+                * in the current block so that we can determine if
+                * compaction is viable.
+                */
+               if (slot != FOUND) {
+                       size = ep->d_reclen;
+                       if (ep->d_ino != 0)
+                               size -= DIRSIZ(ep);
+                       if (size > 0) {
+                               if (size >= newsize) {
+                                       slot = FOUND;
+                                       entryfree = u.u_offset;
+                                       entrysize = DIRSIZ(ep) + newsize;
+                               }
+                               if (entryfree == NULL)
+                                       entryfree = u.u_offset;
+                               spccnt += size;
+                       }
+               }
+               /*
+                * String compare the directory entry
+                * and the current component.
+                * If they do not match, continue to the next entry.
+                */
+               prevoff = curoff;
+               curoff = u.u_offset;
+               u.u_offset += ep->d_reclen;
+               if (ep->d_ino == 0)
+                       continue;
+               if (ep->d_namlen != u.u_dent.d_namlen)
+                       continue;
+               if (bcmp(u.u_dent.d_name, ep->d_name, ep->d_namlen))
+                       continue;
+               /*
+                * Here a component matched in a directory.
+                * If there is more pathname, go back to
+                * dirloop, otherwise return.
+                */
+               bcopy((caddr_t)ep, (caddr_t)&u.u_dent, DIRSIZ(ep));
+               brelse(bp);
+               if (flag == 2 && *cp == '\0') {
+                       brelse(nbp);
+                       if (access(dp, IWRITE)) {
+                               iput(dp);
+                               return (NULL);
+                       }
+                       if (curoff % DIRBLKSIZ == 0) {
+                               u.u_offset = curoff;
+                               u.u_count = 0;
+                               return (dp);
+                       }
+                       u.u_offset = prevoff;
+                       u.u_count = DIRSIZ((struct direct *)
+                           (bp->b_un.b_addr + blkoff(fs, prevoff)));
+                       return (dp);
+               }
+               /*
+                * Special handling for ".."
+                */
+               if (u.u_dent.d_name[0] == '.' && u.u_dent.d_name[1] == '.' &&
+                   u.u_dent.d_name[2] == '\0') {
+                       if (dp == u.u_rdir)
+                               u.u_dent.d_ino = dp->i_number;
+                       else if (u.u_dent.d_ino == ROOTINO &&
+                          dp->i_number == ROOTINO) {
+                               for (i = 1; i < NMOUNT; i++)
+                                       if (mount[i].m_bufp != NULL &&
+                                          mount[i].m_dev == dp->i_dev) {
+                                               iput(dp);
+                                               dp = mount[i].m_inodp;
+                                               ilock(dp);
+                                               dp->i_count++;
+                                               fs = dp->i_fs;
+                                               cp -= 2;     /* back over .. */
+                                               goto dirloop2;
+                                       }
+                       }
+               }
+               d = dp->i_dev;
+               irele(dp);
+               pdp = dp;
+               dp = iget(d, fs, u.u_dent.d_ino);
+               if (dp == NULL)  {
+                       iput(pdp);
+                       brelse(nbp);
+                       return (NULL);
+               }
+               fs = dp->i_fs;
+               /*
+                * Check for symbolic link
+                */
+               if ((dp->i_mode & IFMT) == IFLNK && (follow || *cp == '/')) {
+                       pathlen = strlen(cp) + 1;
+                       if (dp->i_size + pathlen >= MAXPATHLEN - 1 ||
+                           ++nlink > MAXSYMLINKS) {
+                               u.u_error = ELOOP;
+                               iput(pdp);
+                               iput(dp);
+                               brelse(nbp);
+                               return (NULL);
+                       }
+                       bcopy(cp, nbp->b_un.b_addr + dp->i_size, pathlen);
+                       bn =  bmap(dp, (daddr_t)0, B_READ);
+                       if (bn < 0) {
+                               printf("hole in symlink: %s i = %d\n",
+                                   fs->fs_fsmnt, dp->i_number);
+                               iput(pdp);
+                               iput(dp);
+                               brelse(nbp);
+                               return (NULL);
+                       }
+                       bp = bread(dp->i_dev, fsbtodb(fs, bn),
+                                  (int)blksize(fs, dp, (daddr_t)0));
+                       if (bp->b_flags & B_ERROR) {
+                               brelse(bp);
+                               iput(pdp);
+                               iput(dp);
+                               brelse(nbp);
+                               return (NULL);
+                       }
+                       bcopy(bp->b_un.b_addr, nbp->b_un.b_addr,
+                         (unsigned)dp->i_size);
                        brelse(bp);
                        brelse(bp);
-                       goto out;
+                       cp = nbp->b_un.b_addr;
+                       iput(dp);
+                       if (*cp == '/') {
+                               iput(pdp);
+                               while (*cp == '/')
+                                       cp++;
+                               if ((dp = u.u_rdir) == NULL)
+                                       dp = rootdir;
+                               ilock(dp);
+                               dp->i_count++;
+                       } else {
+                               dp = pdp;
+                               ilock(dp);
+                       }
+                       fs = dp->i_fs;
+                       goto dirloop;
                }
                }
-               ep = (struct direct *)bp->b_un.b_addr;
-       } else
-               ep++;
-
-       /*
-        * Note first empty directory slot
-        * in eo for possible creat.
-        * String compare the directory entry
-        * and the current component.
-        * If they do not match, go back to eloop.
-        */
-
-       u.u_offset += sizeof(struct direct);
-       if(ep->d_ino == 0) {
-               if(eo == 0)
-                       eo = u.u_offset;
-               goto eloop;
-       }
-       for(i=0; i<DIRSIZ; i++) {
-               if(u.u_dbuf[i] != ep->d_name[i])
-                       goto eloop;
-               if(u.u_dbuf[i] == 0)
-                       break;
+               iput(pdp);
+               if (*cp == '/') {
+                       while (*cp == '/')
+                               cp++;
+                       goto dirloop;
+               }
+               /*
+                * End of path, so return name matched.
+                */
+               u.u_offset -= ep->d_reclen;
+               u.u_count = newsize;
+               brelse(nbp);
+               return (dp);
        }
        }
-
        /*
        /*
-        * Here a component matched in a directory.
-        * If there is more pathname, go back to
-        * cloop, otherwise return.
+        * Search failed.
+        * Report what is appropriate as per flag.
         */
         */
-       bcopy((caddr_t)ep, (caddr_t)&u.u_dent, sizeof(struct direct));
-       if(bp != NULL)
+       if (bp != NULL)
                brelse(bp);
                brelse(bp);
-       if(flag==2 && c=='\0') {
-               if(access(dp, IWRITE))
-                       goto out;
-               return(dp);
+       if (flag == 1 && *cp == '\0' && dp->i_nlink != 0) {
+               brelse(nbp);
+               if (access(dp, IWRITE)) {
+                       iput(dp);
+                       return (NULL);
+               }
+               if (slot == NONE) {
+                       u.u_count = 0;
+               } else {
+                       u.u_offset = entryfree;
+                       u.u_count = entrysize;
+               }
+               dp->i_flag |= IUPD|ICHG;
+               return (NULL);
        }
        }
-       d = dp->i_dev;
-       if(u.u_dent.d_ino == ROOTINO)
-       if(dp->i_number == ROOTINO)
-       if(u.u_dent.d_name[1] == '.')
-               for(i=1; i<NMOUNT; i++)
-                       if(mount[i].m_bufp != NULL)
-                       if(mount[i].m_dev == d) {
-                               iput(dp);
-                               dp = mount[i].m_inodp;
-                               dp->i_count++;
-                               plock(dp);
-                               goto seloop;
-                       }
+       u.u_error = ENOENT;
        iput(dp);
        iput(dp);
-       dp = iget(d, u.u_dent.d_ino);
-       if(dp == NULL)
-               return(NULL);
-       goto cloop;
-
-out:
-       iput(dp);
-       return(NULL);
+       brelse(nbp);
+       return (NULL);
 }
 
 /*
 }
 
 /*
@@ -230,7 +390,7 @@ out:
 schar()
 {
 
 schar()
 {
 
-       return(*u.u_dirp++ & 0377);
+       return (*u.u_dirp++ & 0377);
 }
 
 /*
 }
 
 /*
@@ -242,7 +402,32 @@ uchar()
        register c;
 
        c = fubyte(u.u_dirp++);
        register c;
 
        c = fubyte(u.u_dirp++);
-       if(c == -1)
+       if (c == -1) {
                u.u_error = EFAULT;
                u.u_error = EFAULT;
-       return(c);
+               c = 0;
+       }
+       return (c);
+}
+
+#ifndef vax
+bcmp(s1, s2, len)
+       register char *s1, *s2;
+       register int len;
+{
+
+       while (--len)
+               if (*s1++ != *s2++)
+                       return (1);
+       return (0);
+}
+
+strlen(s1)
+       register char *s1;
+{
+       register int len;
+
+       for (len = 0; *s1++ != '\0'; len++)
+               /* void */;
+       return (len);
 }
 }
+#endif