V10/lsys/fs/fsnami.c
/*
* namei code for fs filesystem
*/
#include "sys/param.h"
#include "sys/systm.h"
#include "sys/inode.h"
#include "sys/buf.h"
#include "sys/filsys.h"
#include "sys/dir.h"
#include "sys/user.h"
struct dent {
struct direct dir;
off_t off;
int nentry;
};
fsnami(p, flagp, follow)
struct nx *p;
register struct argnamei *flagp;
{
register struct inode *dp;
register char *cp;
register struct buf *bp;
register struct inode *dip;
register int i;
struct dent dent;
struct inode *domkfile();
ino_t dsearch();
cp = p->cp;
dp = p->dp;
/*
* dp is the inode to search; cp is the pathname to find.
* dp must be a directory, with execute permissions.
* (this test is done too early; it forbids a filesystem
* consisting of a single file with i-number ROOTINO)
*/
dirloop:
if((dp->i_mode&IFMT) != IFDIR) {
u.u_error = ENOTDIR;
goto outnull;
}
if (access(dp, IEXEC))
goto outnull;
for (i=0; *cp!='\0' && *cp!='/'; i++) {
if (i >= DIRSIZ) {
u.u_error = ENOENT;
goto outnull;
}
dent.dir.d_name[i] = *cp++;
}
while (i < DIRSIZ)
dent.dir.d_name[i++] = '\0';
while (*cp == '/')
cp++;
if (dent.dir.d_name[0] == '\0') { /* null name, e.g. "/" or "" */
if (flagp->flag != NI_SEARCH) {
u.u_error = ENOENT;
goto outnull;
}
/*
* should pass NI_CREAT too, but dp is known to be a directory
*/
goto out;
}
u.u_segflg = SEGSYS; /* for various routines that write directories */
if (dsearch(dp, &dent) == 0) {
if (u.u_error)
goto outnull;
/*
* name not found
* if we wanted to create or link, that's OK
*/
if (*cp == 0) switch (flagp->flag) {
case NI_CREAT:
case NI_NXCREAT:
case NI_MKDIR:
case NI_LINK:
dip = dp;
dp = domkfile(dip, flagp, &dent);
iput(dip);
goto out;
}
u.u_error = ENOENT;
goto outnull;
}
/*
* name found
* did we want to remove it?
* (do before ".." check)
*/
if(flagp->flag == NI_DEL && *cp == 0) {
dormfile(dp, &dent);
goto outnull;
}
if(flagp->flag == NI_RMDIR && *cp == 0) {
dormdir(dp, &dent);
goto outnull;
}
/*
* special code for ".." in a root directory
*/
if (dent.dir.d_name[0] == '.'
&& dent.dir.d_name[1] == '.'
&& dent.dir.d_name[2] == '\0') {
if (dp == rootdir || dp == u.u_rdir)
goto dirloop;
if (dent.dir.d_ino == ROOTINO && dp->i_number == ROOTINO) {
dip = dp->i_mpoint;
i = dp->i_fstyp;
iput(dp);
dp = dip;
plock(dp);
dp->i_count++;
while (*--cp == '/')
;
cp--; /* skip over ".." and / */
if (dp->i_fstyp != i)
goto outmore;
goto dirloop;
}
}
/*
* fetch the inode for the filename we found
*/
dip = dp;
prele(dip);
dp = iget(dip, dip->i_dev, dent.dir.d_ino);
if (dp == NULL) {
idec(dip);
goto out; /* sic */
}
if (dp->i_count == 1 && fsiread(dip, dp) < 0) {
idec(dip);
dp = NULL; /* fsiread iput it */
goto out;
}
if(dip->i_fstyp != dp->i_fstyp) {
idec(dip);
goto outmore;
}
/*
* symlink? If so and if wanted, follow.
*/
if ((dp->i_mode&IFMT)==IFLNK && (follow || *cp!='\0')) {
char *ocp;
ocp = cp;
while (*cp++)
;
i = cp - ocp; /* strlen(ocp) + 1 for NUL */
if (dp->i_size > BSIZE(dp->i_dev)
|| dp->i_size + 1 + i > p->nlen /* +1 for '/' */
|| ++p->nlink>8) {
u.u_error = ELOOP;
idec(dip);
goto outnull;
}
cp = p->nbuf;
if (i == 1) /* empty pathname, just put NUL there */
cp[dp->i_size] = 0;
else {
bcopy(ocp, cp + dp->i_size + 1, i); /* remaining pathname */
cp[dp->i_size] = '/';
}
bp = bread(dp->i_dev, bmap(dp, (daddr_t)0, B_READ));
if (bp->b_flags & B_ERROR) {
brelse(bp);
idec(dip);
goto outnull;
}
bcopy(bp->b_un.b_addr, cp, dp->i_size); /* new prefix */
brelse(bp);
i = dp->i_fstyp;
iput(dp);
if (*cp != '/')
dp = dip;
else {
idec(dip);
while (*cp == '/')
cp++;
if ((dp = u.u_rdir) == NULL)
dp = rootdir;
plock(dp);
dp->i_count++;
}
if (i != dp->i_fstyp)
goto outmore;
goto dirloop;
}
idec(dip);
/*
* more pathname to walk?
*/
if (*cp)
goto dirloop;
/*
* final checks before returning:
* -- some operations are in error if filename exists
* -- save last piece of filename for accounting
*/
if (flagp->flag == NI_LINK || flagp->flag == NI_NXCREAT
|| flagp->flag == NI_MKDIR) {
u.u_error = EEXIST;
goto outnull;
}
if(flagp->flag == NI_SEARCH && flagp->un.buf)
bcopy(dent.dir.d_name, flagp->un.buf,
MIN(flagp->len, sizeof(dent.dir.d_name)));
/*
* here to return the inode in dp
*/
out:
p->dp = dp;
return(0);
/*
* here to discard dp and return NULL
*/
outnull:
iput(dp);
p->dp = NULL;
return (0);
/*
* here if we crossed into another filesystem's domain:
* return dp (the first inode in that filesystem)
* and cp (the remainder of the pathname)
* and value 1, so namei knows it should continue
*/
outmore:
p->cp = cp;
p->dp = dp;
return (1);
}
/*
* create a new file of some sort in directory dp
* name is in dent->d_name
* offset is in dent->d_off
* return inode if it is needed
*/
struct inode *
domkfile(dp, flagp, dent)
register struct inode *dp;
struct argnamei *flagp;
struct dent *dent;
{
register struct inode *dip;
struct direct x[2];
register i;
if(access(dp, IWRITE))
return(NULL);
if (dp->i_nlink == 0) {
u.u_error = EINVAL;
return (NULL);
}
u.u_count = sizeof(struct direct);
u.u_base = (caddr_t)&dent->dir;
u.u_offset = ltoL(dent->off);
switch(flagp->flag) {
case NI_CREAT: /* create a new file */
case NI_NXCREAT:
dip = ialloc(dp);
if(dip == NULL)
return(NULL);
dip->i_flag |= IACC|IUPD|ICHG;
dip->i_mode = flagp->un.mode;
if((dip->i_mode & IFMT) == 0)
dip->i_mode |= IFREG;
dip->i_nlink = 1;
dip->i_uid = u.u_uid;
dip->i_gid = dp->i_mode & ISGID ? u.u_gid : dp->i_gid;
if (u.u_uid && !groupmember(dip->i_gid))
dip->i_mode &= ~ISGID;
fsiupdat(dip, &time, &time, 1);
dent->dir.d_ino = dip->i_number;
flagp->un.mode = ~flagp->un.mode;
writei(dp);
return(dip);
case NI_MKDIR: /* make a new directory */
dip = ialloc(dp);
if(dip == NULL)
return(NULL);
dent->dir.d_ino = dip->i_number;
dip->i_mode = flagp->un.mode;
dip->i_nlink = 1;
dip->i_uid = u.u_uid;
dip->i_gid = dp->i_gid;
dip->i_flag |= IACC|IUPD|ICHG;
x[0].d_ino = dip->i_number;
x[1].d_ino = dp->i_number;
for(i = 0; i < DIRSIZ; i++)
x[0].d_name[i] = x[1].d_name[i] = 0;
x[0].d_name[0] = x[1].d_name[0] = x[1].d_name[1] = '.';
u.u_count = sizeof(x);
u.u_base = (caddr_t)x;
u.u_offset = ltoL(0);
u.u_segflg = SEGSYS;
writei(dip);
if (u.u_error) {
dip->i_nlink--;
iput(dip);
return(NULL);
}
dip->i_nlink++;
dp->i_nlink++;
fsiupdat(dip, &time, &time, 1);
u.u_count = sizeof(struct direct);
u.u_base = (caddr_t)&dent->dir;
u.u_offset = ltoL(dent->off);
writei(dp);
iput(dip);
return(NULL);
case NI_LINK: /* make a link */
if(dp->i_dev != flagp->un.il->i_dev) {
u.u_error = EXDEV;
return(NULL);
}
dent->dir.d_ino = flagp->un.il->i_number;
writei(dp);
return(NULL);
}
panic("domkfile");
}
/*
* delete a non-directory file
*/
dormfile(dp, dent)
register struct inode *dp;
register struct dent *dent;
{
register struct inode *dip;
if(access(dp, IWRITE))
return;
if(dp->i_number == dent->dir.d_ino) { /* for '.' */
dip = dp;
dp->i_count++;
} else {
dip = iget(dp, dp->i_dev, dent->dir.d_ino);
if (dip == NULL)
return;
if (dip->i_count == 1 && fsiread(dp, dip) < 0)
return;
}
if(dip->i_mpoint != dp->i_mpoint) {
u.u_error = EBUSY;
iput(dip);
return;
}
if((dip->i_mode&IFMT) == IFDIR && !suser()) {
iput(dip);
return;
}
if(dip->i_flag&ITEXT)
xrele(dip); /* free busy text */
u.u_base = (caddr_t)&dent->dir;
u.u_count = sizeof(struct direct);
u.u_offset = ltoL(dent->off);
dent->dir.d_ino = 0;
writei(dp); /* segflg already set*/
dip->i_nlink--;
dip->i_flag |= ICHG;
iput(dip);
}
/*
* remove a directory (fsnami)
* dp is inode of containing directory
* dent->dir is the entry (which exists)
* u.u_offset is the offset in containing directory of this entry
*/
dormdir(dp, dent)
register struct inode *dp;
register struct dent *dent;
{
register struct inode *dip;
register int nentry;
struct dent tdent;
ino_t dotino, dotdotino;
ino_t dsearch();
if(access(dp, IWRITE))
return;
if(dp->i_number == dent->dir.d_ino
|| strncmp(dent->dir.d_name, "..", DIRSIZ)==0) { /* gets "." and "" */
u.u_error = EINVAL;
return;
}
if((dip = iget(dp, dp->i_dev, dent->dir.d_ino)) == NULL)
return;
if(dip->i_number == ROOTINO) {
u.u_error = EINVAL;
iput(dip);
return;
}
if(dip->i_mpoint != dp->i_mpoint) {
u.u_error = EBUSY;
iput(dip);
return;
}
if (dip->i_count == 1 && fsiread(dp, dip) < 0)
return;
if((dip->i_mode & IFMT) != IFDIR) {
u.u_error = ENOTDIR;
iput(dip);
return;
}
/* search for ., .., other entries in dir */
cpdirent("/", &tdent); /* cannot be found; just count entries */
dsearch(dip, &tdent);
nentry = tdent.nentry;
tdent.dir.d_name[0] = '.';
if (dotino = dsearch(dip, &tdent))
nentry--;
tdent.dir.d_name[1] = '.';
if(dotdotino = dsearch(dip, &tdent))
nentry--;
if (nentry > 0) {
u.u_error = EHASF; /* removing nonempty directory */
iput(dip);
return;
}
if (dotino) {
if (dip->i_number == dotino)
dip->i_nlink--;
/* else error? */
}
if (dotdotino) {
if (dp->i_number == dotdotino)
dp->i_nlink--;
/* else error? */
}
u.u_base = (caddr_t)&dent->dir;
u.u_count = sizeof(struct direct);
u.u_offset = ltoL(dent->off);
dent->dir.d_ino = 0;
writei(dp);
dp->i_flag |= ICHG;
fsiupdat(dp, &time, &time, 1);
dip->i_nlink--;
dip->i_flag |= ICHG;
iput(dip);
}
cpdirent(s, dent)
register char *s;
register struct dent *dent;
{
register char *dp = dent->dir.d_name;
while (dp < &dent->dir.d_name[DIRSIZ]) {
*dp++ = *s;
if (*s)
s++;
}
}
/*
* search directory ip for entry dent->dir.d_name
* success: return ino, leave dent->dir with copy of entry,
* dent->off pointing at entry
* dent->nentry with count of entries
* fail: return 0, leave dent->off pointing at empty slot
*/
ino_t
dsearch(ip, dent)
struct inode *ip;
struct dent *dent;
{
register struct direct *dp, *dpe;
register char *nm;
register off_t off;
struct buf *bp;
register int bsize;
register daddr_t n, nblock;
dent->dir.d_ino = 0;
nm = dent->dir.d_name;
bsize = BSIZE(ip->i_dev);
dent->nentry = 0;
bp = NULL;
dent->off = -1;
nblock = (ip->i_size+bsize-1) / bsize;
for (n=0, off=0; n<nblock; n++) {
if (bp)
brelse(bp);
bp = bread(ip->i_dev, bmap(ip, n, B_READ));
if (bp->b_flags & B_ERROR) {
u.u_error = EIO;
goto out;
}
dp = (struct direct *)bp->b_un.b_addr;
dpe = &dp[min(bsize, ip->i_size-off) / sizeof(struct direct)];
for (; dp < dpe; dp++, off+=sizeof(struct direct)) {
if (dp->d_ino == 0) {
if (dent->off<0)
dent->off = off;
continue;
}
dent->nentry++;
if (nm[2]==dp->d_name[2] /* hash */
&& strncmp(nm, dp->d_name, DIRSIZ) == 0) {
dent->off = off;
dent->dir.d_ino = dp->d_ino;
goto out;
}
}
}
out:
if (dent->off<0)
dent->off = off;
if (bp)
brelse(bp);
return(dent->dir.d_ino);
}