2.9BSD/usr/net/sys/sys/nami.c
/*
* SCCS id @(#)nami.c 2.1 (Berkeley) 8/21/83
*/
#include "param.h"
#include <sys/systm.h>
#include <sys/inode.h>
#include <sys/filsys.h>
#include <sys/mount.h>
#include <sys/dir.h>
#include <sys/user.h>
#include <sys/buf.h>
#include <sys/quota.h>
#ifdef UCB_SYMLINKS
#ifndef saveseg5
#include <sys/seg.h>
#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; i<DIRSIZ; i++) {
if(u.u_dbuf[i] != dirp->d_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; i<nmount; i++)
if ((mount[i].m_inodp != NULL)
&& (mount[i].m_dev == d)) {
iput(dp);
dp = mount[i].m_inodp;
dp->i_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