/* * SCCS id @(#)nami.c 2.1 (Berkeley) 8/21/83 */ #include "param.h" #include #include #include #include #include #include #include #include #ifdef UCB_SYMLINKS #ifndef saveseg5 #include #endif #endif /* * Convert a pathname into a pointer to * an inode. Note that the inode is locked. * * func = function called to get next char of name * &uchar if name is in user space * &schar if name is in system space * flag = LOOKUP if name is sought * CREATE if name is to be created * DELETE if name is to be deleted #ifdef UCB_SYMLINKS * follow = 1 if to follow links at end of name #endif */ struct inode * #ifdef UCB_SYMLINKS namei(func, flag, follow) #else namei(func, flag) #endif int (*func)(); { register struct direct *dirp; struct inode *dp; register c; register char *cp; struct buf *bp; #if defined(UCB_QUOTAS) || defined(UCB_SYMLINKS) struct buf *temp; #endif #ifdef UCB_SYMLINKS int nlink; #endif int i; dev_t d; off_t eo; #ifdef UCB_SYMLINKS nlink = 0; u.u_sbuf = 0; #endif /* * If name starts with '/' start from * root; otherwise start from current dir. */ dp = u.u_cdir; if((c=(*func)()) == '/') if ((dp = u.u_rdir) == NULL) dp = rootdir; iget(dp->i_dev, dp->i_number); while(c == '/') c = (*func)(); if(c == '\0' && flag != LOOKUP) u.u_error = ENOENT; cloop: /* * Here dp contains pointer * to last component matched. */ if(u.u_error) goto out; if(c == '\0') return(dp); /* * If there is another component, * Gather up name into * users' dir buffer. */ cp = &u.u_dbuf[0]; while (c != '/' && c != '\0' && u.u_error == 0 ) { #ifdef MPX_FILS if (mpxip!=NULL && c=='!') break; #endif if(cp < &u.u_dbuf[DIRSIZ]) *cp++ = c; c = (*func)(); } while(cp < &u.u_dbuf[DIRSIZ]) *cp++ = '\0'; while(c == '/') c = (*func)(); #ifdef MPX_FILS if (c == '!' && mpxip != NULL) { iput(dp); plock(mpxip); mpxip->i_count++; return(mpxip); } #endif /* * dp must be a directory and * must have X permission. */ access(dp, IEXEC); seloop: if((dp->i_mode&IFMT) != IFDIR) u.u_error = ENOTDIR; if(u.u_error) goto out; /* * set up to search a directory */ u.u_offset = 0; u.u_segflg = 1; eo = 0; bp = NULL; if (dp == u.u_rdir && u.u_dbuf[0] == '.' && u.u_dbuf[1] == '.' && u.u_dbuf[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) { mapout(bp); brelse(bp); } if(flag==CREATE && c=='\0') { 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; goto out1; } 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) { mapout(bp); brelse(bp); } bp = bread(dp->i_dev, bmap(dp, (daddr_t)(u.u_offset>>BSHIFT), B_READ)); if (bp->b_flags & B_ERROR) { brelse(bp); goto out; } dirp = (struct direct *)mapin(bp); } /* * 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(dirp->d_ino == 0) { dirp++; if(eo == 0) eo = u.u_offset; goto eloop; } #ifdef UCB_QUOTAS /* * See if this could be a quota node. */ if((dirp->d_name[0] == '.') && (dirp->d_name[1] == 'q') && (dirp->d_name[2] == '\0')) { cp = dp->i_quot; /* * If no quota is associated yet or a new quot is * around, then . . . */ if (cp == NULL || cp->i_number != dirp->d_ino) { u.u_dent.d_ino = dirp->d_ino; mapout(bp); cp = iget(dp->i_dev, u.u_dent.d_ino); if (cp != NULL) { prele(cp); /* * If not really a quota node then just put away */ if (!isquot(cp)) { iput(cp); cp = NULL; } } /* * The value of dirp is still valid because * the buffer can not have been released * between the mapout() above and here, * and there is a static relationship between * buffer headers and the buffers proper. */ mapin(bp); if (cp != NULL) { /* * set up hierarchical inode chains * NOTE: this is done wrong since this may * overwrite an inode which has not * been put away yet */ cp->i_quot = dp->i_quot; dp->i_quot = cp; } } if (cp != NULL) { /* * Mark the directory as being the original * owner of the quota. This is necessary so * that quotas do not get copied up the tree. */ dp->i_flag |= IQUOT; } } #endif for(i=0; id_name[i]) { dirp++; goto eloop; } if (u.u_dbuf[i] == '\0') break; } u.u_dent = *dirp; /* * Here a component matched in a directory. * If there is more pathname, go back to * cloop, otherwise return. */ if(bp != NULL) { mapout(bp); brelse(bp); } if(flag==DELETE && c=='\0') { if(access(dp, IWRITE)) goto out; return(dp); } d = dp->i_dev; if ((u.u_dent.d_ino == ROOTINO) && (dp->i_number == ROOTINO) && (u.u_dent.d_name[1] == '.')) for(i=1; ii_count++; plock(dp); /* * Note: permission for ROOTINO already checked. */ goto seloop; } #if defined(UCB_QUOTAS) || defined(UCB_SYMLINKS) prele(dp); temp = cp = iget(d, u.u_dent.d_ino); if (cp == NULL) { if (dp->i_flag & ILOCK) dp->i_count--; else iput(dp); goto out1; } #ifdef UCB_SYMLINKS if ((((struct inode *)temp)->i_mode&IFMT)==IFLNK && (follow || c)) { struct inode *pdp; pdp = (struct inode *)temp; if (pdp->i_size >= BSIZE-2 || ++nlink>8 || u.u_sbuf || !pdp->i_size) { u.u_error = ELOOP; iput(pdp); goto out; } u.u_sbuf = bread(pdp->i_dev, bmap(pdp, (daddr_t)0, B_READ)); if (u.u_sbuf->b_flags & B_ERROR) { brelse(u.u_sbuf); iput(pdp); u.u_sbuf = 0; goto out; } /* Save our readahead chars at end of buffer, get first */ /* symbolic link character */ { segm save5; char *cp; if (c) /* space for readahead chars */ u.u_slength = pdp->i_size+2; else u.u_slength = pdp->i_size+1; u.u_soffset = 0; saveseg5(save5); mapin(u.u_sbuf); cp = (char *)SEG5; if (c) cp[u.u_slength-2] = '/'; cp[u.u_slength-1] = c; c = cp[u.u_soffset++]; mapout(u.u_sbuf); restorseg5(save5); } /* Grab the top-level inode for the new path */ iput(pdp); if (c == '/') { iput(dp); if ((dp = u.u_rdir) == NULL) dp = rootdir; while (c == '/') c = (*func)(); iget(dp->i_dev, dp->i_number); } else plock(dp); goto cloop; } #ifndef UCB_QUOTAS else { iput(dp); dp = (struct inode *)temp; } #endif #endif UCB_SYMLINKS #ifdef UCB_QUOTAS /* * Make sure not to copy the quota node up the tree past * the original height. */ if ((dp->i_flag & IQUOT) && u.u_dent.d_name[0] == '.' && u.u_dent.d_name[1] == '.' && u.u_dent.d_name[2] == '\0') cp = dp->i_quot; /* * Copy quota to new inode */ qcopy(dp, cp); if (dp->i_flag & ILOCK) dp->i_count--; else iput(dp); dp = temp; #endif #else iput(dp); dp = iget(d, u.u_dent.d_ino); if(dp == NULL) goto out1; #endif goto cloop; out: iput(dp); out1: #ifdef UCB_SYMLINKS if (u.u_sbuf) { brelse(u.u_sbuf); u.u_sbuf = u.u_slength = u.u_soffset = 0; } #endif return(NULL); } #ifdef UCB_QUOTAS /* * Copy quota from dp to ip if certain conditions hold. */ qcopy(dp, ip) register struct inode *dp, *ip; { register struct inode *qp; qp = dp->i_quot; if (qp == NULL || qp == ip) return; if (ip->i_quot != NULL) return; ip->i_quot = qp; if (++(qp->i_count) == 0) panic ("qcopy"); } #endif /* * Return the next character from the * kernel string pointed at by dirp. */ schar() { #ifdef UCB_SYMLINKS register c; if (u.u_sbuf) { c = symchar(); if (c >= 0) return(c); } #endif UCB_SYMLINKS return(*u.u_dirp++ & 0377); } /* * Return the next character from the * user string pointed at by dirp. */ uchar() { register c; #ifdef UCB_SYMLINKS if (u.u_sbuf) { c = symchar(); if (c >= 0) return(c); } #endif UCB_SYMLINKS c = fubyte(u.u_dirp++); if(c == -1) u.u_error = EFAULT; else if (c&0200) u.u_error = EINVAL; return(c); } #ifdef UCB_SYMLINKS /* * Get a character from the symbolic name buffer */ symchar() { segm save5; register char c; register char *cp; if (!u.u_sbuf) /* Protect ourselves */ return(-1); if (u.u_soffset > u.u_slength) { brelse(u.u_sbuf); u.u_soffset = u.u_slength = u.u_sbuf = 0; return(-1); } /* Get next character from symbolic link buffer */ saveseg5(save5); mapin(u.u_sbuf); cp = (char *)SEG5; c = cp[u.u_soffset++]; mapout(u.u_sbuf); restorseg5(save5); if (u.u_soffset >= u.u_slength) { brelse(u.u_sbuf); u.u_soffset = u.u_slength = u.u_sbuf = 0; } return(c); }; /* end of symchar */ #endif UCB_SYMLINKS