8c5a98ba5a6ad7e48dadb637f59cc0f3f2ffa845
[unix-history] / usr / src / sys / kern / vfs_lookup.c
/* vfs_lookup.c 4.14 82/03/31 */
#include "../h/param.h"
#include "../h/systm.h"
#include "../h/inode.h"
#include "../h/mount.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../h/buf.h"
#include "../h/conf.h"
/*
* Convert a pathname into a pointer to
* a locked inode.
*
* func = function called to get next char of name
* &uchar if name is in user space
* &schar if name is in system space
* 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 *
namei(func, flag, follow)
int (*func)(), flag, follow;
{
register struct inode *dp;
register char *cp;
register struct buf *bp, *nbp;
register struct direct *ep;
struct inode *pdp;
int i, nlink;
dev_t d;
off_t eo;
/*
* allocate name buffer; copy name
*/
nbp = geteblk();
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+BSIZE) {
#else
cp++;
if (cp >= nbp->b_un.b_addr+BSIZE) {
#endif
u.u_error = ENOENT;
break;
}
}
if (u.u_error) {
dp = NULL;
goto out1;
}
cp = nbp->b_un.b_addr;
/*
* If name starts with '/' start from
* root; otherwise start from current dir.
*/
dp = u.u_cdir;
if (*cp == '/') {
while (*cp == '/')
cp++;
if ((dp = u.u_rdir) == NULL)
dp = rootdir;
}
ilock(dp);
dp->i_count++;
/*
* dp must be a directory and
* must have X permission.
* cp is a path name relative to that directory.
*/
dirloop:
if ((dp->i_mode&IFMT) != IFDIR)
u.u_error = ENOTDIR;
(void) access(dp, IEXEC);
dirloop2:
for (i=0; *cp!='\0' && *cp!='/'; i++) {
#ifdef notdef
if (i >= DIRSIZ) {
u.u_error = ENOENT;
break;
}
u.u_dbuf[i] = *cp++;
#else
if (i < DIRSIZ)
u.u_dbuf[i] = *cp;
cp++;
#endif
}
if (u.u_error)
goto out;
u.u_pdir = dp;
while (i < DIRSIZ)
u.u_dbuf[i++] = '\0';
if (u.u_dbuf[0] == '\0') { /* null name, e.g. "/" or "" */
if (flag) {
u.u_error = ENOENT;
goto out;
}
goto out1;
}
u.u_segflg = 1;
eo = -1;
bp = NULL;
for (u.u_offset=0; u.u_offset < dp->i_size;
u.u_offset += sizeof(struct direct), ep++) {
/*
* 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) {
brelse(bp);
goto out;
}
ep = (struct direct *)bp->b_un.b_addr;
}
/*
* Note first empty directory slot
* in eo for possible creat.
* String compare the directory entry
* and the current component.
*/
if (ep->d_ino == 0) {
if (eo < 0)
eo = u.u_offset;
continue;
}
if (strncmp(u.u_dbuf, ep->d_name, DIRSIZ) != 0)
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, sizeof(struct direct));
brelse(bp);
if (flag==2 && *cp=='\0') {
if (access(dp, IWRITE))
goto out;
/* should fix unlink */
u.u_offset += sizeof(struct direct);
goto out1;
}
/*
* 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++;
cp -= 2; /* back over .. */
goto dirloop2;
}
}
}
d = dp->i_dev;
irele(dp);
pdp = dp;
dp = iget(d, u.u_dent.d_ino);
if (dp == NULL) {
iput(pdp);
goto out1;
}
/*
* Check for symbolic link
*/
if ((dp->i_mode&IFMT)==IFLNK && (follow || *cp=='/')) {
char *ocp;
ocp = cp;
while (*cp++)
;
if (dp->i_size + (cp-ocp) >= BSIZE-1 || ++nlink>8) {
u.u_error = ELOOP;
iput(pdp);
goto out;
}
bcopy(ocp, nbp->b_un.b_addr+dp->i_size,
(unsigned)(cp-ocp));
bp = bread(dp->i_dev, bmap(dp, (daddr_t)0, B_READ));
if (bp->b_flags & B_ERROR) {
brelse(bp);
iput(pdp);
goto out;
}
bcopy(bp->b_un.b_addr, nbp->b_un.b_addr,
(unsigned)dp->i_size);
brelse(bp);
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);
}
goto dirloop;
}
iput(pdp);
if (*cp == '/') {
while (*cp == '/')
cp++;
goto dirloop;
}
goto out1;
}
/*
* Search failed.
*/
if (bp != NULL)
brelse(bp);
if (flag==1 && *cp=='\0' && dp->i_nlink) {
if (access(dp, IWRITE))
goto out;
if (eo>=0)
u.u_offset = eo;
dp->i_flag |= IUPD|ICHG;
dp = NULL;
goto out1;
}
u.u_error = ENOENT;
out:
iput(dp);
dp = NULL;
out1:
brelse(nbp);
return (dp);
}
/*
* Return the next character from the
* kernel string pointed at by dirp.
*/
schar()
{
return (*u.u_dirp++ & 0377);
}
/*
* Return the next character from the
* user string pointed at by dirp.
*/
uchar()
{
register c;
c = fubyte(u.u_dirp++);
if (c == -1) {
u.u_error = EFAULT;
c = 0;
}
return (c);
}
#ifndef vax
strncmp(s1, s2, len)
register char *s1, *s2;
register len;
{
do {
if (*s1 != *s2++)
return (1);
if (*s1++ == '\0')
return (0);
} while (--len);
return (0);
}
#endif