1*18663Smckusick /* vfs_lookup.c 6.22 85/04/19 */ 230Sbill 317100Sbloom #include "param.h" 417100Sbloom #include "systm.h" 517100Sbloom #include "inode.h" 617100Sbloom #include "fs.h" 717100Sbloom #include "mount.h" 817100Sbloom #include "dir.h" 917100Sbloom #include "user.h" 1017100Sbloom #include "buf.h" 1117100Sbloom #include "conf.h" 1217100Sbloom #include "uio.h" 1317100Sbloom #include "kernel.h" 1430Sbill 157605Ssam struct buf *blkatoff(); 1616681Smckusick struct buf *freenamebuf; 179166Ssam int dirchk = 0; 1815798Smckusick 1930Sbill /* 2015798Smckusick * Structures associated with name cacheing. 2115798Smckusick */ 2215798Smckusick #define NCHHASH 32 /* size of hash table */ 2315798Smckusick 2415798Smckusick #if ((NCHHASH)&((NCHHASH)-1)) != 0 2515798Smckusick #define NHASH(h, i, d) ((unsigned)((h) + (i) + 13 * (int)(d)) % (NCHHASH)) 2615798Smckusick #else 2715798Smckusick #define NHASH(h, i, d) ((unsigned)((h) + (i) + 13 * (int)(d)) & ((NCHHASH)-1)) 2815798Smckusick #endif 2915798Smckusick 3015798Smckusick union nchash { 3115798Smckusick union nchash *nch_head[2]; 3215798Smckusick struct nch *nch_chain[2]; 3315798Smckusick } nchash[NCHHASH]; 3415798Smckusick #define nch_forw nch_chain[0] 3515798Smckusick #define nch_back nch_chain[1] 3615798Smckusick 3715809Smckusick struct nch *nchhead, **nchtail; /* LRU chain pointers */ 3815809Smckusick struct nchstats nchstats; /* cache effectiveness statistics */ 3915798Smckusick 4015798Smckusick /* 417534Sroot * Convert a pathname into a pointer to a locked inode, 427534Sroot * with side effects usable in creating and removing files. 437534Sroot * This is a very central and rather complicated routine. 4430Sbill * 4516688Smckusick * The segflg defines whether the name is to be copied from user 4616688Smckusick * space or kernel space. 477534Sroot * 489166Ssam * The flag argument is (LOOKUP, CREATE, DELETE) depending on whether 499166Ssam * the name is to be (looked up, created, deleted). If flag has 509166Ssam * LOCKPARENT or'ed into it and the target of the pathname exists, 519166Ssam * namei returns both the target and its parent directory locked. 529166Ssam * If the file system is not maintained in a strict tree hierarchy, 539166Ssam * this can result in a deadlock situation. When creating and 549166Ssam * LOCKPARENT is specified, the target may not be ".". When deleting 559166Ssam * and LOCKPARENT is specified, the target may be ".", but the caller 569166Ssam * must check to insure it does an irele and iput instead of two iputs. 579166Ssam * 5816688Smckusick * The FOLLOW flag is set when symbolic links are to be followed 599166Ssam * when they occur at the end of the name translation process. 609166Ssam * 6115798Smckusick * Name caching works as follows: 627534Sroot * 6315798Smckusick * names found by directory scans are retained in a cache 6415798Smckusick * for future reference. It is managed LRU, so frequently 6515798Smckusick * used names will hang around. Cache is indexed by hash value 6615798Smckusick * obtained from (ino,dev,name) where ino & dev refer to the 6715798Smckusick * directory containing name. 6815798Smckusick * 6915798Smckusick * For simplicity (and economy of storage), names longer than 7015798Smckusick * some (small) maximum length are not cached, they occur 7115798Smckusick * infrequently in any case, and are almost never of interest. 7215798Smckusick * 7315798Smckusick * Upon reaching the last segment of a path, if the reference 7415798Smckusick * is for DELETE, or NOCACHE is set (rewrite), and the 7515798Smckusick * name is located in the cache, it will be dropped. 7615798Smckusick * 7715798Smckusick * We must be sure never to enter the name ".." into the cache 7815798Smckusick * because of the extremely kludgey way that rename() alters 7915798Smckusick * ".." in a situation like 8015798Smckusick * mv a/x b/x 8115798Smckusick * where x is a directory, and x/.. is the ".." in question. 8215798Smckusick * 8315798Smckusick * Overall outline of namei: 8415798Smckusick * 857534Sroot * copy in name 867534Sroot * get starting directory 877534Sroot * dirloop: 887534Sroot * check accessibility of directory 897534Sroot * dirloop2: 9016688Smckusick * copy next component of name to ndp->ni_dent 917534Sroot * handle degenerate case where name is null string 9215798Smckusick * look for name in cache, if found, then if at end of path 9315798Smckusick * and deleting or creating, drop it, else to haveino 947534Sroot * search for name in directory, to found or notfound 957534Sroot * notfound: 969166Ssam * if creating, return locked directory, leaving info on avail. slots 977534Sroot * else return error 987534Sroot * found: 997534Sroot * if at end of path and deleting, return information to allow delete 10015798Smckusick * if at end of path and rewriting (create and LOCKPARENT), lock target 1019166Ssam * inode and return info to allow rewrite 1027534Sroot * if .. and on mounted filesys, look in mount table for parent 10315798Smckusick * if not at end, if neither creating nor deleting, add name to cache 10415798Smckusick * haveino: 1057534Sroot * if symbolic link, massage name in buffer and continue at dirloop 1067534Sroot * if more components of name, do next level at dirloop 1077534Sroot * return the answer as locked inode 1089166Ssam * 1099166Ssam * NOTE: (LOOKUP | LOCKPARENT) currently returns the parent inode, 1109166Ssam * but unlocked. 11130Sbill */ 11230Sbill struct inode * 11316688Smckusick namei(ndp) 11416688Smckusick register struct nameidata *ndp; 11530Sbill { 1167534Sroot register char *cp; /* pointer into pathname argument */ 1177534Sroot /* these variables refer to things which must be freed or unlocked */ 1187534Sroot register struct inode *dp = 0; /* the directory we are searching */ 11915798Smckusick register struct nch *ncp; /* cache slot for entry */ 1207534Sroot register struct fs *fs; /* file system that directory is in */ 1217534Sroot register struct buf *bp = 0; /* a buffer of directory entries */ 1227534Sroot register struct direct *ep; /* the current directory entry */ 1237534Sroot int entryoffsetinblock; /* offset of ep in bp's buffer */ 1247534Sroot register struct buf *nbp; /* buffer storing path name argument */ 1257534Sroot /* these variables hold information about the search for a slot */ 1267534Sroot enum {NONE, COMPACT, FOUND} slotstatus; 1277534Sroot int slotoffset = -1; /* offset of area with free space */ 1287534Sroot int slotsize; /* size of area at slotoffset */ 1297534Sroot int slotfreespace; /* amount of space free in slot */ 1307534Sroot int slotneeded; /* size of the entry we're seeking */ 1317534Sroot /* */ 13215660Smckusick int numdirpasses; /* strategy for directory search */ 13315660Smckusick int endsearch; /* offset to end directory search */ 13416688Smckusick int prevoff; /* ndp->ni_offset of previous entry */ 1357534Sroot int nlink = 0; /* number of symbolic links taken */ 1367534Sroot struct inode *pdp; /* saved dp during symlink work */ 13716688Smckusick int error, i; 1389166Ssam int lockparent; 13918109Smckusick int docache; /* == 0 do not cache last component */ 14018109Smckusick int makeentry; /* != 0 if name to be added to cache */ 14115798Smckusick unsigned hash; /* value of name hash for entry */ 14215798Smckusick union nchash *nhp; /* cache chain head for entry */ 14315798Smckusick int isdotdot; /* != 0 if current name is ".." */ 14416688Smckusick int flag; /* op ie, LOOKUP, CREATE, or DELETE */ 14518027Smckusick off_t enduseful; /* pointer past last used dir slot */ 14630Sbill 14716688Smckusick lockparent = ndp->ni_nameiop & LOCKPARENT; 14816688Smckusick docache = (ndp->ni_nameiop & NOCACHE) ^ NOCACHE; 14916688Smckusick flag = ndp->ni_nameiop &~ (LOCKPARENT|NOCACHE|FOLLOW); 15018109Smckusick if (flag == DELETE || lockparent) 15115798Smckusick docache = 0; 15230Sbill /* 1537534Sroot * Get a buffer for the name to be translated, and copy the 1547534Sroot * name into the buffer. 1555972Swnj */ 15616681Smckusick nbp = freenamebuf; 15716681Smckusick if (nbp == NULL) 15816681Smckusick nbp = geteblk(MAXPATHLEN); 15916681Smckusick else 16016681Smckusick freenamebuf = nbp->av_forw; 16116688Smckusick if (ndp->ni_segflg == UIO_SYSSPACE) 16216705Smckusick error = copystr(ndp->ni_dirp, nbp->b_un.b_addr, MAXPATHLEN, 16316705Smckusick (u_int *)0); 16416688Smckusick else 16516705Smckusick error = copyinstr(ndp->ni_dirp, nbp->b_un.b_addr, MAXPATHLEN, 16616705Smckusick (u_int *)0); 16716688Smckusick if (error) { 16816688Smckusick u.u_error = error; 16916688Smckusick goto bad; 1705972Swnj } 1717534Sroot 1725972Swnj /* 1737534Sroot * Get starting directory. 17430Sbill */ 1757534Sroot cp = nbp->b_un.b_addr; 1765972Swnj if (*cp == '/') { 1775972Swnj while (*cp == '/') 1785972Swnj cp++; 17930Sbill if ((dp = u.u_rdir) == NULL) 18030Sbill dp = rootdir; 1817534Sroot } else 1827534Sroot dp = u.u_cdir; 1837534Sroot fs = dp->i_fs; 18416666Smckusick ILOCK(dp); 1855972Swnj dp->i_count++; 18616688Smckusick ndp->ni_pdir = (struct inode *)0xc0000000; /* illegal */ 18718027Smckusick ndp->ni_endoff = 0; 1887534Sroot 1897534Sroot /* 1907534Sroot * We come to dirloop to search a new directory. 1917534Sroot * The directory must be locked so that it can be 1927534Sroot * iput, and fs must be already set to dp->i_fs. 1937534Sroot */ 1946571Smckusic dirloop: 19530Sbill /* 1967534Sroot * Check accessiblity of directory. 19730Sbill */ 1987534Sroot if ((dp->i_mode&IFMT) != IFDIR) { 19930Sbill u.u_error = ENOTDIR; 2007534Sroot goto bad; 2017534Sroot } 2027534Sroot if (access(dp, IEXEC)) 2037534Sroot goto bad; 2047534Sroot 2056384Swnj dirloop2: 2067534Sroot /* 20716688Smckusick * Copy next component of name to ndp->ni_dent. 2087534Sroot */ 20915798Smckusick hash = 0; 2107534Sroot for (i = 0; *cp != 0 && *cp != '/'; cp++) { 2116571Smckusic if (i >= MAXNAMLEN) { 2125972Swnj u.u_error = ENOENT; 2137534Sroot goto bad; 2145972Swnj } 21516688Smckusick if ((*cp&0377) == ('/'|0200) || (*cp&0200) && flag != DELETE) { 21616688Smckusick u.u_error = EPERM; 21716688Smckusick goto bad; 21816688Smckusick } 21916688Smckusick ndp->ni_dent.d_name[i++] = *cp; 22015798Smckusick hash += (unsigned char)*cp * i; 2215972Swnj } 22216688Smckusick ndp->ni_dent.d_namlen = i; 22316688Smckusick ndp->ni_dent.d_name[i] = '\0'; 22416658Smckusick isdotdot = (i == 2 && 22516688Smckusick ndp->ni_dent.d_name[0] == '.' && ndp->ni_dent.d_name[1] == '.'); 22618109Smckusick makeentry = 1; 22718109Smckusick if (*cp == '\0' && docache == 0) 22818109Smckusick makeentry = 0; 2297534Sroot 2307534Sroot /* 2317534Sroot * Check for degenerate name (e.g. / or "") 2327534Sroot * which is a way of talking about a directory, 2337534Sroot * e.g. like "/." or ".". 2347534Sroot */ 23516688Smckusick if (ndp->ni_dent.d_name[0] == '\0') { 23615798Smckusick if (flag != LOOKUP || lockparent) { 23714937Smckusick u.u_error = EISDIR; 2387534Sroot goto bad; 2395972Swnj } 24016681Smckusick nbp->av_forw = freenamebuf; 24116681Smckusick freenamebuf = nbp; 2426571Smckusic return (dp); 2435972Swnj } 2447534Sroot 2456571Smckusic /* 24615798Smckusick * We now have a segment name to search for, and a directory to search. 24715798Smckusick * 24815798Smckusick * Before tediously performing a linear scan of the directory, 24915798Smckusick * check the name cache to see if the directory/name pair 25015798Smckusick * we are looking for is known already. We don't do this 25115798Smckusick * if the segment name is long, simply so the cache can avoid 25215798Smckusick * holding long names (which would either waste space, or 25315798Smckusick * add greatly to the complexity). 25415798Smckusick */ 25516688Smckusick if (ndp->ni_dent.d_namlen > NCHNAMLEN) { 25615798Smckusick nchstats.ncs_long++; 25718109Smckusick makeentry = 0; 25815798Smckusick } else { 25915798Smckusick nhp = &nchash[NHASH(hash, dp->i_number, dp->i_dev)]; 26015798Smckusick for (ncp = nhp->nch_forw; ncp != (struct nch *)nhp; 26115798Smckusick ncp = ncp->nc_forw) { 26215798Smckusick if (ncp->nc_ino == dp->i_number && 26315798Smckusick ncp->nc_dev == dp->i_dev && 26416688Smckusick ncp->nc_nlen == ndp->ni_dent.d_namlen && 26516688Smckusick !bcmp(ncp->nc_name, ndp->ni_dent.d_name, 26616688Smckusick ncp->nc_nlen)) 26715798Smckusick break; 26815798Smckusick } 26915798Smckusick 27015798Smckusick if (ncp == (struct nch *)nhp) { 27115798Smckusick nchstats.ncs_miss++; 27215798Smckusick ncp = NULL; 27315798Smckusick } else { 27416658Smckusick if (ncp->nc_id != ncp->nc_ip->i_id) { 27516643Ssam nchstats.ncs_falsehits++; 27618109Smckusick } else if (!makeentry) { 27716658Smckusick nchstats.ncs_badhits++; 27816658Smckusick } else { 27915798Smckusick 28015798Smckusick /* 28115798Smckusick * move this slot to end of LRU 28215798Smckusick * chain, if not already there 28315798Smckusick */ 28415798Smckusick if (ncp->nc_nxt) { 28515798Smckusick /* remove from LRU chain */ 28615798Smckusick *ncp->nc_prev = ncp->nc_nxt; 28715798Smckusick ncp->nc_nxt->nc_prev = ncp->nc_prev; 28815798Smckusick 28915798Smckusick /* and replace at end of it */ 29015798Smckusick ncp->nc_nxt = NULL; 29115798Smckusick ncp->nc_prev = nchtail; 29215798Smckusick *nchtail = ncp; 29315798Smckusick nchtail = &ncp->nc_nxt; 29415798Smckusick } 29515798Smckusick 29616658Smckusick /* 29716658Smckusick * Get the next inode in the path. 29816666Smckusick * See comment above other `IUNLOCK' code for 29916658Smckusick * an explaination of the locking protocol. 30016658Smckusick */ 30115798Smckusick pdp = dp; 30218109Smckusick if (!isdotdot || dp != u.u_rdir) 30318109Smckusick dp = ncp->nc_ip; 30415798Smckusick if (dp == NULL) 30515798Smckusick panic("nami: null cache ino"); 30618109Smckusick if (pdp == dp) { 30716643Ssam dp->i_count++; 30818109Smckusick } else if (isdotdot) { 30918109Smckusick IUNLOCK(pdp); 31018109Smckusick igrab(dp); 31118109Smckusick } else { 31218109Smckusick igrab(dp); 31318109Smckusick IUNLOCK(pdp); 31416643Ssam } 31515798Smckusick 31616658Smckusick /* 31716658Smckusick * Verify that the inode that we got 31816658Smckusick * did not change while we were waiting 31916658Smckusick * for it to be locked. 32016658Smckusick */ 32116658Smckusick if (ncp->nc_id != ncp->nc_ip->i_id) { 32216658Smckusick iput(dp); 32316666Smckusick ILOCK(pdp); 32416658Smckusick dp = pdp; 32516658Smckusick nchstats.ncs_falsehits++; 32616658Smckusick } else { 32716688Smckusick ndp->ni_dent.d_ino = dp->i_number; 32816688Smckusick /* ni_dent.d_reclen is garbage ... */ 32916658Smckusick nchstats.ncs_goodhits++; 33016658Smckusick goto haveino; 33116658Smckusick } 33216658Smckusick } 33315798Smckusick 33415798Smckusick /* 33516643Ssam * Last component and we are renaming or deleting, 33616643Ssam * the cache entry is invalid, or otherwise don't 33716643Ssam * want cache entry to exist. 33815798Smckusick */ 33915798Smckusick 34015798Smckusick /* remove from LRU chain */ 34115798Smckusick *ncp->nc_prev = ncp->nc_nxt; 34215798Smckusick if (ncp->nc_nxt) 34315798Smckusick ncp->nc_nxt->nc_prev = ncp->nc_prev; 34415798Smckusick else 34515798Smckusick nchtail = ncp->nc_prev; 34615798Smckusick 34715798Smckusick /* remove from hash chain */ 34815798Smckusick remque(ncp); 34915798Smckusick 35015798Smckusick /* insert at head of LRU list (first to grab) */ 35115798Smckusick ncp->nc_nxt = nchhead; 35215798Smckusick ncp->nc_prev = &nchhead; 35315798Smckusick nchhead->nc_prev = &ncp->nc_nxt; 35415798Smckusick nchhead = ncp; 35515798Smckusick 35615798Smckusick /* and make a dummy hash chain */ 35715798Smckusick ncp->nc_forw = ncp; 35815798Smckusick ncp->nc_back = ncp; 35915798Smckusick 36015798Smckusick ncp = NULL; 36115798Smckusick } 36215798Smckusick } 36315798Smckusick 36415798Smckusick /* 3657534Sroot * Suppress search for slots unless creating 3667534Sroot * file and at end of pathname, in which case 3677534Sroot * we watch for a place to put the new file in 3687534Sroot * case it doesn't already exist. 3696571Smckusic */ 3707534Sroot slotstatus = FOUND; 3719166Ssam if (flag == CREATE && *cp == 0) { 3727534Sroot slotstatus = NONE; 3737534Sroot slotfreespace = 0; 37416688Smckusick slotneeded = DIRSIZ(&ndp->ni_dent); 3757534Sroot } 37615660Smckusick /* 37715660Smckusick * If this is the same directory that this process 37815660Smckusick * previously searched, pick up where we last left off. 37915798Smckusick * We cache only lookups as these are the most common 38015660Smckusick * and have the greatest payoff. Caching CREATE has little 38115660Smckusick * benefit as it usually must search the entire directory 38215660Smckusick * to determine that the entry does not exist. Caching the 38315660Smckusick * location of the last DELETE has not reduced profiling time 38415660Smckusick * and hence has been removed in the interest of simplicity. 38515660Smckusick */ 38615660Smckusick if (flag != LOOKUP || dp->i_number != u.u_ncache.nc_inumber || 38715660Smckusick dp->i_dev != u.u_ncache.nc_dev) { 38816688Smckusick ndp->ni_offset = 0; 38915660Smckusick numdirpasses = 1; 39015660Smckusick } else { 39115798Smckusick if ((dp->i_flag & ICHG) || dp->i_ctime >= u.u_ncache.nc_time) { 39217698Smckusick if (u.u_ncache.nc_prevoffset > dp->i_size) 39317698Smckusick u.u_ncache.nc_prevoffset = 0; 39417698Smckusick else 39517698Smckusick u.u_ncache.nc_prevoffset &= ~(DIRBLKSIZ - 1); 39615660Smckusick u.u_ncache.nc_time = time.tv_sec; 39715660Smckusick } 39816688Smckusick ndp->ni_offset = u.u_ncache.nc_prevoffset; 39916688Smckusick entryoffsetinblock = blkoff(fs, ndp->ni_offset); 40015660Smckusick if (entryoffsetinblock != 0) { 40116688Smckusick bp = blkatoff(dp, ndp->ni_offset, (char **)0); 40215660Smckusick if (bp == 0) 40315660Smckusick goto bad; 40415660Smckusick } 40515660Smckusick numdirpasses = 2; 40615798Smckusick nchstats.ncs_2passes++; 40715660Smckusick } 40815660Smckusick endsearch = roundup(dp->i_size, DIRBLKSIZ); 40918027Smckusick enduseful = 0; 4107534Sroot 41115660Smckusick searchloop: 41216688Smckusick while (ndp->ni_offset < endsearch) { 4135972Swnj /* 4145972Swnj * If offset is on a block boundary, 4155972Swnj * read the next directory block. 4165972Swnj * Release previous if it exists. 4175972Swnj */ 41816688Smckusick if (blkoff(fs, ndp->ni_offset) == 0) { 4195972Swnj if (bp != NULL) 4205972Swnj brelse(bp); 42116688Smckusick bp = blkatoff(dp, ndp->ni_offset, (char **)0); 4227534Sroot if (bp == 0) 4237534Sroot goto bad; 4247534Sroot entryoffsetinblock = 0; 4255972Swnj } 4267534Sroot 4275972Swnj /* 4287534Sroot * If still looking for a slot, and at a DIRBLKSIZE 42916657Smckusick * boundary, have to start looking for free space again. 4306571Smckusic */ 4317534Sroot if (slotstatus == NONE && 4327534Sroot (entryoffsetinblock&(DIRBLKSIZ-1)) == 0) { 4337534Sroot slotoffset = -1; 4347534Sroot slotfreespace = 0; 4357534Sroot } 4367534Sroot 4377534Sroot /* 43816657Smckusick * Get pointer to next entry. 43916657Smckusick * Full validation checks are slow, so we only check 44016657Smckusick * enough to insure forward progress through the 44116657Smckusick * directory. Complete checks can be run by patching 44216657Smckusick * "dirchk" to be true. 4437534Sroot */ 4447534Sroot ep = (struct direct *)(bp->b_un.b_addr + entryoffsetinblock); 44516657Smckusick if (ep->d_reclen <= 0 || 44616657Smckusick dirchk && dirbadentry(ep, entryoffsetinblock)) { 44716688Smckusick dirbad(dp, ndp->ni_offset, "mangled entry"); 44816657Smckusick i = DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1)); 44916688Smckusick ndp->ni_offset += i; 4507534Sroot entryoffsetinblock += i; 4516571Smckusic continue; 4526571Smckusic } 4537534Sroot 4546571Smckusic /* 4557534Sroot * If an appropriate sized slot has not yet been found, 4566571Smckusic * check to see if one is available. Also accumulate space 4576571Smckusic * in the current block so that we can determine if 4586571Smckusic * compaction is viable. 4596571Smckusic */ 4607534Sroot if (slotstatus != FOUND) { 4617534Sroot int size = ep->d_reclen; 4627534Sroot 4636571Smckusic if (ep->d_ino != 0) 4646571Smckusic size -= DIRSIZ(ep); 4656571Smckusic if (size > 0) { 4667534Sroot if (size >= slotneeded) { 4677534Sroot slotstatus = FOUND; 46816688Smckusick slotoffset = ndp->ni_offset; 4697534Sroot slotsize = ep->d_reclen; 4707534Sroot } else if (slotstatus == NONE) { 4717534Sroot slotfreespace += size; 4727534Sroot if (slotoffset == -1) 47316688Smckusick slotoffset = ndp->ni_offset; 4747534Sroot if (slotfreespace >= slotneeded) { 4757534Sroot slotstatus = COMPACT; 47616688Smckusick slotsize = ndp->ni_offset + 47716688Smckusick ep->d_reclen - slotoffset; 4787534Sroot } 4796571Smckusic } 4806571Smckusic } 4816571Smckusic } 4827534Sroot 4836571Smckusic /* 4847534Sroot * Check for a name match. 4855972Swnj */ 4867534Sroot if (ep->d_ino) { 48716688Smckusick if (ep->d_namlen == ndp->ni_dent.d_namlen && 48816688Smckusick !bcmp(ndp->ni_dent.d_name, ep->d_name, 48916688Smckusick ep->d_namlen)) 4907534Sroot goto found; 4917534Sroot } 49216688Smckusick prevoff = ndp->ni_offset; 49316688Smckusick ndp->ni_offset += ep->d_reclen; 4947534Sroot entryoffsetinblock += ep->d_reclen; 49518027Smckusick if (ep->d_ino) 49618027Smckusick enduseful = ndp->ni_offset; 4977534Sroot } 49815798Smckusick /* notfound: */ 49915660Smckusick /* 50015798Smckusick * If we started in the middle of the directory and failed 50115660Smckusick * to find our target, we must check the beginning as well. 50215660Smckusick */ 50315660Smckusick if (numdirpasses == 2) { 50415660Smckusick numdirpasses--; 50516688Smckusick ndp->ni_offset = 0; 50615660Smckusick endsearch = u.u_ncache.nc_prevoffset; 50715660Smckusick goto searchloop; 50815660Smckusick } 5097534Sroot /* 5107534Sroot * If creating, and at end of pathname and current 5119166Ssam * directory has not been removed, then can consider 5129166Ssam * allowing file to be created. 5137534Sroot */ 5149166Ssam if (flag == CREATE && *cp == 0 && dp->i_nlink != 0) { 5155972Swnj /* 5167534Sroot * Access for write is interpreted as allowing 5177534Sroot * creation of files in the directory. 5185972Swnj */ 5197534Sroot if (access(dp, IWRITE)) 5207534Sroot goto bad; 5215972Swnj /* 5227534Sroot * Return an indication of where the new directory 5237534Sroot * entry should be put. If we didn't find a slot, 52416688Smckusick * then set ndp->ni_count to 0 indicating that the new 52516688Smckusick * slot belongs at the end of the directory. If we found 52616688Smckusick * a slot, then the new entry can be put in the range 52716688Smckusick * [ndp->ni_offset .. ndp->ni_offset + ndp->ni_count) 5285972Swnj */ 52915660Smckusick if (slotstatus == NONE) { 53016688Smckusick ndp->ni_offset = roundup(dp->i_size, DIRBLKSIZ); 53116688Smckusick ndp->ni_count = 0; 53218027Smckusick enduseful = ndp->ni_offset; 53315660Smckusick } else { 53416688Smckusick ndp->ni_offset = slotoffset; 53516688Smckusick ndp->ni_count = slotsize; 53618027Smckusick if (enduseful < slotoffset + slotsize) 53718027Smckusick enduseful = slotoffset + slotsize; 5385972Swnj } 53918027Smckusick ndp->ni_endoff = roundup(enduseful, DIRBLKSIZ); 5407534Sroot dp->i_flag |= IUPD|ICHG; 5417534Sroot if (bp) 5427534Sroot brelse(bp); 54316681Smckusick nbp->av_forw = freenamebuf; 54416681Smckusick freenamebuf = nbp; 5455972Swnj /* 5467534Sroot * We return with the directory locked, so that 5477534Sroot * the parameters we set up above will still be 5487534Sroot * valid if we actually decide to do a direnter(). 5497534Sroot * We return NULL to indicate that the entry doesn't 5507534Sroot * currently exist, leaving a pointer to the (locked) 55116688Smckusick * directory inode in ndp->ni_pdir. 5525972Swnj */ 55316688Smckusick ndp->ni_pdir = dp; 5547534Sroot return (NULL); 5557534Sroot } 5567534Sroot u.u_error = ENOENT; 5577534Sroot goto bad; 5587534Sroot found: 55915798Smckusick if (numdirpasses == 2) 56015798Smckusick nchstats.ncs_pass2++; 5617534Sroot /* 5627534Sroot * Check that directory length properly reflects presence 5637534Sroot * of this entry. 5647534Sroot */ 5657605Ssam if (entryoffsetinblock + DIRSIZ(ep) > dp->i_size) { 56616688Smckusick dirbad(dp, ndp->ni_offset, "i_size too small"); 5677605Ssam dp->i_size = entryoffsetinblock + DIRSIZ(ep); 5687534Sroot dp->i_flag |= IUPD|ICHG; 5697534Sroot } 5707534Sroot 5717534Sroot /* 57215660Smckusick * Found component in pathname. 57315798Smckusick * If the final component of path name, save information 57415660Smckusick * in the cache as to where the entry was found. 5757534Sroot */ 57615660Smckusick if (*cp == '\0' && flag == LOOKUP) { 57716688Smckusick u.u_ncache.nc_prevoffset = ndp->ni_offset; 57815660Smckusick u.u_ncache.nc_inumber = dp->i_number; 57915660Smckusick u.u_ncache.nc_dev = dp->i_dev; 58015660Smckusick u.u_ncache.nc_time = time.tv_sec; 58115660Smckusick } 58215660Smckusick /* 58318109Smckusick * Save directory entry's inode number and reclen in ndp->ni_dent, 58415660Smckusick * and release directory buffer. 58515660Smckusick */ 58618109Smckusick ndp->ni_dent.d_ino = ep->d_ino; 58718109Smckusick ndp->ni_dent.d_reclen = ep->d_reclen; 5887534Sroot brelse(bp); 5897534Sroot bp = NULL; 5907534Sroot 5917534Sroot /* 5927534Sroot * If deleting, and at end of pathname, return 5937534Sroot * parameters which can be used to remove file. 5949166Ssam * If the lockparent flag isn't set, we return only 59516688Smckusick * the directory (in ndp->ni_pdir), otherwise we go 5969166Ssam * on and lock the inode, being careful with ".". 5977534Sroot */ 5989166Ssam if (flag == DELETE && *cp == 0) { 5997534Sroot /* 6007534Sroot * Write access to directory required to delete files. 6017534Sroot */ 6027534Sroot if (access(dp, IWRITE)) 6037534Sroot goto bad; 60416688Smckusick ndp->ni_pdir = dp; /* for dirremove() */ 6057534Sroot /* 60616688Smckusick * Return pointer to current entry in ndp->ni_offset, 6077534Sroot * and distance past previous entry (if there 60816688Smckusick * is a previous entry in this block) in ndp->ni_count. 60916688Smckusick * Save directory inode pointer in ndp->ni_pdir for dirremove(). 6107534Sroot */ 61116688Smckusick if ((ndp->ni_offset&(DIRBLKSIZ-1)) == 0) 61216688Smckusick ndp->ni_count = 0; 6137534Sroot else 61416688Smckusick ndp->ni_count = ndp->ni_offset - prevoff; 6159166Ssam if (lockparent) { 61616688Smckusick if (dp->i_number == ndp->ni_dent.d_ino) 6179166Ssam dp->i_count++; 6189166Ssam else { 61916688Smckusick dp = iget(dp->i_dev, fs, ndp->ni_dent.d_ino); 6209166Ssam if (dp == NULL) { 62116688Smckusick iput(ndp->ni_pdir); 6229166Ssam goto bad; 6239166Ssam } 62415798Smckusick /* 62516046Skarels * If directory is "sticky", then user must own 62615798Smckusick * the directory, or the file in it, else he 62715798Smckusick * may not delete it (unless he's root). This 62815798Smckusick * implements append-only directories. 62915798Smckusick */ 63016688Smckusick if ((ndp->ni_pdir->i_mode & ISVTX) && 63115798Smckusick u.u_uid != 0 && 63216688Smckusick u.u_uid != ndp->ni_pdir->i_uid && 63315798Smckusick dp->i_uid != u.u_uid) { 63416688Smckusick iput(ndp->ni_pdir); 63515798Smckusick u.u_error = EPERM; 63615798Smckusick goto bad; 63715798Smckusick } 6389166Ssam } 6399166Ssam } 64016681Smckusick nbp->av_forw = freenamebuf; 64116681Smckusick freenamebuf = nbp; 6427534Sroot return (dp); 6437534Sroot } 6447534Sroot 6457534Sroot /* 6467534Sroot * Special handling for ".." allowing chdir out of mounted 6477534Sroot * file system: indirect .. in root inode to reevaluate 6487534Sroot * in directory file system was mounted on. 6497534Sroot */ 65016658Smckusick if (isdotdot) { 65118109Smckusick if (dp == u.u_rdir) { 65216688Smckusick ndp->ni_dent.d_ino = dp->i_number; 65318109Smckusick makeentry = 0; 65418109Smckusick } else if (ndp->ni_dent.d_ino == ROOTINO && 6557534Sroot dp->i_number == ROOTINO) { 6567534Sroot for (i = 1; i < NMOUNT; i++) 6577534Sroot if (mount[i].m_bufp != NULL && 6587534Sroot mount[i].m_dev == dp->i_dev) { 6596571Smckusic iput(dp); 6607534Sroot dp = mount[i].m_inodp; 66116666Smckusick ILOCK(dp); 6625972Swnj dp->i_count++; 6637534Sroot fs = dp->i_fs; 6647534Sroot cp -= 2; /* back over .. */ 6657534Sroot goto dirloop2; 6665972Swnj } 66730Sbill } 6687534Sroot } 6697534Sroot 6707534Sroot /* 6719166Ssam * If rewriting (rename), return the inode and the 6729166Ssam * information required to rewrite the present directory 6739166Ssam * Must get inode of directory entry to verify it's a 6749166Ssam * regular file, or empty directory. 6759166Ssam */ 6769166Ssam if ((flag == CREATE && lockparent) && *cp == 0) { 6779166Ssam if (access(dp, IWRITE)) 6789166Ssam goto bad; 67916688Smckusick ndp->ni_pdir = dp; /* for dirrewrite() */ 6809166Ssam /* 6819166Ssam * Careful about locking second inode. 6829166Ssam * This can only occur if the target is ".". 6839166Ssam */ 68416688Smckusick if (dp->i_number == ndp->ni_dent.d_ino) { 6859166Ssam u.u_error = EISDIR; /* XXX */ 6869166Ssam goto bad; 6879166Ssam } 68816688Smckusick dp = iget(dp->i_dev, fs, ndp->ni_dent.d_ino); 6899166Ssam if (dp == NULL) { 69016688Smckusick iput(ndp->ni_pdir); 6919166Ssam goto bad; 6929166Ssam } 69316681Smckusick nbp->av_forw = freenamebuf; 69416681Smckusick freenamebuf = nbp; 6959166Ssam return (dp); 6969166Ssam } 6979166Ssam 6989166Ssam /* 69912011Smckusick * Check for symbolic link, which may require us to massage the 70012011Smckusick * name before we continue translation. We do not `iput' the 70112011Smckusick * directory because we may need it again if the symbolic link 70212011Smckusick * is relative to the current directory. Instead we save it 70312011Smckusick * unlocked as "pdp". We must get the target inode before unlocking 70412011Smckusick * the directory to insure that the inode will not be removed 70512011Smckusick * before we get it. We prevent deadlock by always fetching 70612011Smckusick * inodes from the root, moving down the directory tree. Thus 70712011Smckusick * when following backward pointers ".." we must unlock the 70812011Smckusick * parent directory before getting the requested directory. 70912011Smckusick * There is a potential race condition here if both the current 71012011Smckusick * and parent directories are removed before the `iget' for the 71112011Smckusick * inode associated with ".." returns. We hope that this occurs 71212011Smckusick * infrequently since we cannot avoid this race condition without 71312492Ssam * implementing a sophisticated deadlock detection algorithm. 71412011Smckusick * Note also that this simple deadlock detection scheme will not 71512011Smckusick * work if the file system has any hard links other than ".." 71612011Smckusick * that point backwards in the directory structure. 7177534Sroot */ 7187534Sroot pdp = dp; 71915798Smckusick if (isdotdot) { 72016666Smckusick IUNLOCK(pdp); /* race to get the inode */ 72116688Smckusick dp = iget(dp->i_dev, fs, ndp->ni_dent.d_ino); 72212011Smckusick if (dp == NULL) 72312011Smckusick goto bad2; 72416688Smckusick } else if (dp->i_number == ndp->ni_dent.d_ino) { 72512011Smckusick dp->i_count++; /* we want ourself, ie "." */ 72612011Smckusick } else { 72716688Smckusick dp = iget(dp->i_dev, fs, ndp->ni_dent.d_ino); 72816666Smckusick IUNLOCK(pdp); 72912011Smckusick if (dp == NULL) 73012011Smckusick goto bad2; 73112011Smckusick } 73215798Smckusick 73315798Smckusick /* 73415798Smckusick * insert name into cache (if we want it, and it isn't "." or "..") 73515798Smckusick * 73615798Smckusick * all other cases where making a cache entry would be wrong 73715798Smckusick * have already departed from the code sequence somewhere above. 73815798Smckusick */ 73918109Smckusick if (makeentry) { 74015798Smckusick if (ncp != NULL) 74115798Smckusick panic("nami: duplicating cache"); 74215798Smckusick 74315798Smckusick /* 74415798Smckusick * free the cache slot at head of lru chain 74515798Smckusick */ 74615798Smckusick if (ncp = nchhead) { 74715798Smckusick /* remove from lru chain */ 74815798Smckusick *ncp->nc_prev = ncp->nc_nxt; 74915798Smckusick if (ncp->nc_nxt) 75015798Smckusick ncp->nc_nxt->nc_prev = ncp->nc_prev; 75115798Smckusick else 75215798Smckusick nchtail = ncp->nc_prev; 75315798Smckusick 75415798Smckusick /* remove from old hash chain */ 75515798Smckusick remque(ncp); 75615798Smckusick 75715798Smckusick /* grab the inode we just found */ 75815798Smckusick ncp->nc_ip = dp; 75915798Smckusick 76015798Smckusick /* fill in cache info */ 76115798Smckusick ncp->nc_ino = pdp->i_number; /* parents inum */ 76215798Smckusick ncp->nc_dev = pdp->i_dev; /* & device */ 76315798Smckusick ncp->nc_idev = dp->i_dev; /* our device */ 76416643Ssam ncp->nc_id = dp->i_id; /* identifier */ 76516688Smckusick ncp->nc_nlen = ndp->ni_dent.d_namlen; 76616688Smckusick bcopy(ndp->ni_dent.d_name, ncp->nc_name, ncp->nc_nlen); 76715798Smckusick 76815798Smckusick /* link at end of lru chain */ 76915798Smckusick ncp->nc_nxt = NULL; 77015798Smckusick ncp->nc_prev = nchtail; 77115798Smckusick *nchtail = ncp; 77215798Smckusick nchtail = &ncp->nc_nxt; 77315798Smckusick 77415798Smckusick /* and insert on hash chain */ 77515798Smckusick insque(ncp, nhp); 77615798Smckusick } 77715798Smckusick } 77815798Smckusick 77915798Smckusick haveino: 7807534Sroot fs = dp->i_fs; 7817534Sroot 7827534Sroot /* 7837534Sroot * Check for symbolic link 7847534Sroot */ 78516688Smckusick if ((dp->i_mode & IFMT) == IFLNK && 78616688Smckusick ((ndp->ni_nameiop & FOLLOW) || *cp == '/')) { 7877825Sroot u_int pathlen = strlen(cp) + 1; 7887534Sroot 7897534Sroot if (dp->i_size + pathlen >= MAXPATHLEN - 1 || 7907534Sroot ++nlink > MAXSYMLINKS) { 7917534Sroot u.u_error = ELOOP; 7927534Sroot goto bad2; 7937534Sroot } 7948957Sroot ovbcopy(cp, nbp->b_un.b_addr + dp->i_size, pathlen); 7957751Sroot u.u_error = 7969166Ssam rdwri(UIO_READ, dp, nbp->b_un.b_addr, (int)dp->i_size, 7977825Sroot 0, 1, (int *)0); 7987534Sroot if (u.u_error) 7997534Sroot goto bad2; 8007534Sroot cp = nbp->b_un.b_addr; 8017534Sroot iput(dp); 8025972Swnj if (*cp == '/') { 8037534Sroot irele(pdp); 8045972Swnj while (*cp == '/') 8055972Swnj cp++; 8067534Sroot if ((dp = u.u_rdir) == NULL) 8077534Sroot dp = rootdir; 80816666Smckusick ILOCK(dp); 8097534Sroot dp->i_count++; 8107534Sroot } else { 8117534Sroot dp = pdp; 81216666Smckusick ILOCK(dp); 8135972Swnj } 8147534Sroot fs = dp->i_fs; 8157534Sroot goto dirloop; 81630Sbill } 8177534Sroot 81830Sbill /* 8197534Sroot * Not a symbolic link. If more pathname, 8207534Sroot * continue at next component, else return. 82130Sbill */ 8227534Sroot if (*cp == '/') { 8237534Sroot while (*cp == '/') 8247534Sroot cp++; 8259166Ssam irele(pdp); 8267534Sroot goto dirloop; 82730Sbill } 82816681Smckusick nbp->av_forw = freenamebuf; 82916681Smckusick freenamebuf = nbp; 8309166Ssam if (lockparent) 83116688Smckusick ndp->ni_pdir = pdp; 8329166Ssam else 8339166Ssam irele(pdp); 8347534Sroot return (dp); 8357534Sroot bad2: 8367534Sroot irele(pdp); 8377534Sroot bad: 8387534Sroot if (bp) 8397534Sroot brelse(bp); 8407534Sroot if (dp) 8417534Sroot iput(dp); 84216681Smckusick nbp->av_forw = freenamebuf; 84316681Smckusick freenamebuf = nbp; 8446571Smckusic return (NULL); 84530Sbill } 84630Sbill 84715798Smckusick 84816688Smckusick dirbad(ip, offset, how) 8497534Sroot struct inode *ip; 85016688Smckusick off_t offset; 8517534Sroot char *how; 8527534Sroot { 8537534Sroot 8547534Sroot printf("%s: bad dir ino %d at offset %d: %s\n", 85516688Smckusick ip->i_fs->fs_fsmnt, ip->i_number, offset, how); 8567534Sroot } 8577534Sroot 85816657Smckusick /* 85916657Smckusick * Do consistency checking on a directory entry: 86016657Smckusick * record length must be multiple of 4 86116657Smckusick * record length must not be non-negative 86216657Smckusick * entry must fit in rest of its DIRBLKSIZ block 86316657Smckusick * record must be large enough to contain entry 86416657Smckusick * name is not longer than MAXNAMLEN 86516657Smckusick * name must be as long as advertised, and null terminated 86616657Smckusick */ 86716657Smckusick dirbadentry(ep, entryoffsetinblock) 8687534Sroot register struct direct *ep; 86916657Smckusick int entryoffsetinblock; 8707534Sroot { 8717534Sroot register int i; 8727534Sroot 87316657Smckusick if ((ep->d_reclen & 0x3) != 0 || ep->d_reclen <= 0 || 87416657Smckusick ep->d_reclen > DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1)) || 87516657Smckusick ep->d_reclen < DIRSIZ(ep) || ep->d_namlen > MAXNAMLEN) 87616657Smckusick return (1); 8777534Sroot for (i = 0; i < ep->d_namlen; i++) 87816688Smckusick if (ep->d_name[i] == '\0') 8797534Sroot return (1); 8807534Sroot return (ep->d_name[i]); 8817534Sroot } 8827534Sroot 88330Sbill /* 8847534Sroot * Write a directory entry after a call to namei, using the parameters 8857534Sroot * which it left in the u. area. The argument ip is the inode which 88616688Smckusick * the new directory entry will refer to. The u. area field ndp->ni_pdir is 8877534Sroot * a pointer to the directory to be written, which was left locked by 88816688Smckusick * namei. Remaining parameters (ndp->ni_offset, ndp->ni_count) indicate 8897534Sroot * how the space for the new entry is to be gotten. 8907534Sroot */ 89116688Smckusick direnter(ip, ndp) 8927534Sroot struct inode *ip; 89316688Smckusick register struct nameidata *ndp; 8945972Swnj { 8957534Sroot register struct direct *ep, *nep; 89618027Smckusick register struct inode *dp = ndp->ni_pdir; 8977534Sroot struct buf *bp; 89811639Ssam int loc, spacefree, error = 0; 8998631Sroot u_int dsize; 9008631Sroot int newentrysize; 9017534Sroot char *dirbuf; 9025972Swnj 90316688Smckusick ndp->ni_dent.d_ino = ip->i_number; 90416688Smckusick newentrysize = DIRSIZ(&ndp->ni_dent); 90516688Smckusick if (ndp->ni_count == 0) { 9067534Sroot /* 90716688Smckusick * If ndp->ni_count is 0, then namei could find no space in the 90816688Smckusick * directory. In this case ndp->ni_offset will be on a directory 9097534Sroot * block boundary and we will write the new entry into a fresh 9107534Sroot * block. 9117534Sroot */ 91216688Smckusick if (ndp->ni_offset&(DIRBLKSIZ-1)) 9137534Sroot panic("wdir: newblk"); 91416688Smckusick ndp->ni_dent.d_reclen = DIRBLKSIZ; 91518027Smckusick error = rdwri(UIO_WRITE, dp, (caddr_t)&ndp->ni_dent, 91616688Smckusick newentrysize, ndp->ni_offset, 1, (int *)0); 91718104Smckusick if (DIRBLKSIZ > dp->i_fs->fs_fsize) 91818104Smckusick panic("wdir: blksize"); /* XXX - should grow w/bmap() */ 91918104Smckusick else 92018104Smckusick dp->i_size = roundup(dp->i_size, DIRBLKSIZ); 92118027Smckusick iput(dp); 92210849Ssam return (error); 9237534Sroot } 9247534Sroot 9257534Sroot /* 92616688Smckusick * If ndp->ni_count is non-zero, then namei found space for the new 92716688Smckusick * entry in the range ndp->ni_offset to ndp->ni_offset + ndp->ni_count. 9287534Sroot * in the directory. To use this space, we may have to compact 9297534Sroot * the entries located there, by copying them together towards 9307534Sroot * the beginning of the block, leaving the free space in 9317534Sroot * one usable chunk at the end. 9327534Sroot */ 9337534Sroot 9347534Sroot /* 9357534Sroot * Increase size of directory if entry eats into new space. 9367534Sroot * This should never push the size past a new multiple of 9377534Sroot * DIRBLKSIZE. 93818104Smckusick * 93918104Smckusick * N.B. - THIS IS AN ARTIFACT OF 4.2 AND SHOULD NEVER HAPPEN. 9407534Sroot */ 94118027Smckusick if (ndp->ni_offset + ndp->ni_count > dp->i_size) 94218027Smckusick dp->i_size = ndp->ni_offset + ndp->ni_count; 9437534Sroot 9447534Sroot /* 9457534Sroot * Get the block containing the space for the new directory 94610849Ssam * entry. Should return error by result instead of u.u_error. 9477534Sroot */ 94818027Smckusick bp = blkatoff(dp, ndp->ni_offset, (char **)&dirbuf); 9499166Ssam if (bp == 0) { 95018027Smckusick iput(dp); 95110849Ssam return (u.u_error); 9529166Ssam } 9537534Sroot 9547534Sroot /* 9557534Sroot * Find space for the new entry. In the simple case, the 9567534Sroot * entry at offset base will have the space. If it does 9577534Sroot * not, then namei arranged that compacting the region 95816688Smckusick * ndp->ni_offset to ndp->ni_offset+ndp->ni_count would yield the space. 9597534Sroot */ 9607534Sroot ep = (struct direct *)dirbuf; 9617534Sroot dsize = DIRSIZ(ep); 96211639Ssam spacefree = ep->d_reclen - dsize; 96316688Smckusick for (loc = ep->d_reclen; loc < ndp->ni_count; ) { 9647534Sroot nep = (struct direct *)(dirbuf + loc); 9657534Sroot if (ep->d_ino) { 9667534Sroot /* trim the existing slot */ 9677534Sroot ep->d_reclen = dsize; 9687534Sroot ep = (struct direct *)((char *)ep + dsize); 9697534Sroot } else { 9707534Sroot /* overwrite; nothing there; header is ours */ 97111639Ssam spacefree += dsize; 9727534Sroot } 9737534Sroot dsize = DIRSIZ(nep); 97411639Ssam spacefree += nep->d_reclen - dsize; 9757534Sroot loc += nep->d_reclen; 9767825Sroot bcopy((caddr_t)nep, (caddr_t)ep, dsize); 9777534Sroot } 9787534Sroot /* 9797534Sroot * Update the pointer fields in the previous entry (if any), 9807534Sroot * copy in the new entry, and write out the block. 9817534Sroot */ 9827534Sroot if (ep->d_ino == 0) { 98311639Ssam if (spacefree + dsize < newentrysize) 9847534Sroot panic("wdir: compact1"); 98516688Smckusick ndp->ni_dent.d_reclen = spacefree + dsize; 9867534Sroot } else { 98711639Ssam if (spacefree < newentrysize) 9887534Sroot panic("wdir: compact2"); 98916688Smckusick ndp->ni_dent.d_reclen = spacefree; 9907534Sroot ep->d_reclen = dsize; 9917534Sroot ep = (struct direct *)((char *)ep + dsize); 9927534Sroot } 99316688Smckusick bcopy((caddr_t)&ndp->ni_dent, (caddr_t)ep, (u_int)newentrysize); 9947534Sroot bwrite(bp); 99518027Smckusick dp->i_flag |= IUPD|ICHG; 99618027Smckusick if (ndp->ni_endoff && ndp->ni_endoff < dp->i_size) 99718027Smckusick itrunc(dp, ndp->ni_endoff); 99818027Smckusick iput(dp); 99910849Ssam return (error); 10005972Swnj } 10016571Smckusic 10029166Ssam /* 10039166Ssam * Remove a directory entry after a call to namei, using the 10049166Ssam * parameters which it left in the u. area. The u. entry 100516688Smckusick * ni_offset contains the offset into the directory of the 100616688Smckusick * entry to be eliminated. The ni_count field contains the 10079166Ssam * size of the previous record in the directory. If this 10089166Ssam * is 0, the first entry is being deleted, so we need only 10099166Ssam * zero the inode number to mark the entry as free. If the 10109166Ssam * entry isn't the first in the directory, we must reclaim 10119166Ssam * the space of the now empty record by adding the record size 10129166Ssam * to the size of the previous entry. 10139166Ssam */ 101416688Smckusick dirremove(ndp) 101516688Smckusick register struct nameidata *ndp; 10166571Smckusic { 101716688Smckusick register struct inode *dp = ndp->ni_pdir; 10187534Sroot register struct buf *bp; 10197534Sroot struct direct *ep; 10206571Smckusic 102116688Smckusick if (ndp->ni_count == 0) { 10227534Sroot /* 10237534Sroot * First entry in block: set d_ino to zero. 10247534Sroot */ 102516688Smckusick ndp->ni_dent.d_ino = 0; 102616688Smckusick (void) rdwri(UIO_WRITE, dp, (caddr_t)&ndp->ni_dent, 102716688Smckusick (int)DIRSIZ(&ndp->ni_dent), ndp->ni_offset, 1, (int *)0); 10289269Ssam } else { 10297534Sroot /* 10307534Sroot * Collapse new free space into previous entry. 10317534Sroot */ 103216688Smckusick bp = blkatoff(dp, (int)(ndp->ni_offset - ndp->ni_count), 103316688Smckusick (char **)&ep); 10347534Sroot if (bp == 0) 10357534Sroot return (0); 103616688Smckusick ep->d_reclen += ndp->ni_dent.d_reclen; 10377534Sroot bwrite(bp); 10387534Sroot dp->i_flag |= IUPD|ICHG; 10397534Sroot } 10407534Sroot return (1); 10416571Smckusic } 10427534Sroot 10437605Ssam /* 10449166Ssam * Rewrite an existing directory entry to point at the inode 10459166Ssam * supplied. The parameters describing the directory entry are 10469166Ssam * set up by a call to namei. 10479166Ssam */ 104816688Smckusick dirrewrite(dp, ip, ndp) 10499166Ssam struct inode *dp, *ip; 105016688Smckusick struct nameidata *ndp; 10519166Ssam { 10529166Ssam 105316688Smckusick ndp->ni_dent.d_ino = ip->i_number; 105416688Smckusick u.u_error = rdwri(UIO_WRITE, dp, (caddr_t)&ndp->ni_dent, 105516688Smckusick (int)DIRSIZ(&ndp->ni_dent), ndp->ni_offset, 1, (int *)0); 10569166Ssam iput(dp); 10579166Ssam } 10589166Ssam 10599166Ssam /* 10607605Ssam * Return buffer with contents of block "offset" 10617605Ssam * from the beginning of directory "ip". If "res" 10627605Ssam * is non-zero, fill it in with a pointer to the 10637605Ssam * remaining space in the directory. 10647605Ssam */ 10657534Sroot struct buf * 10667605Ssam blkatoff(ip, offset, res) 10677534Sroot struct inode *ip; 10687534Sroot off_t offset; 10697534Sroot char **res; 10707534Sroot { 10717534Sroot register struct fs *fs = ip->i_fs; 10728672Sroot daddr_t lbn = lblkno(fs, offset); 10737534Sroot int bsize = blksize(fs, ip, lbn); 10747534Sroot register struct buf *bp; 1075*18663Smckusick daddr_t bn; 10767534Sroot 1077*18663Smckusick bn = bmap(ip, lbn, B_READ, bsize); 10787534Sroot if (u.u_error) 10797534Sroot return (0); 1080*18663Smckusick if (bn == (daddr_t)-1) { 1081*18663Smckusick dirbad(ip, offset, "hole in dir"); 1082*18663Smckusick return (0); 1083*18663Smckusick } 1084*18663Smckusick bp = bread(ip->i_dev, fsbtodb(fs, bn), bsize); 10857534Sroot if (bp->b_flags & B_ERROR) { 10867534Sroot brelse(bp); 10877534Sroot return (0); 10887534Sroot } 10897534Sroot if (res) 1090*18663Smckusick *res = bp->b_un.b_addr + blkoff(fs, offset); 10917534Sroot return (bp); 10927534Sroot } 10939166Ssam 10949166Ssam /* 10959166Ssam * Check if a directory is empty or not. 10969166Ssam * Inode supplied must be locked. 109712817Ssam * 109812817Ssam * Using a struct dirtemplate here is not precisely 109912817Ssam * what we want, but better than using a struct direct. 110012817Ssam * 110112817Ssam * NB: does not handle corrupted directories. 11029166Ssam */ 110316777Smckusick dirempty(ip, parentino) 11049863Ssam register struct inode *ip; 110516777Smckusick ino_t parentino; 11069166Ssam { 11079166Ssam register off_t off; 110812817Ssam struct dirtemplate dbuf; 110912817Ssam register struct direct *dp = (struct direct *)&dbuf; 11109863Ssam int error, count; 111112817Ssam #define MINDIRSIZ (sizeof (struct dirtemplate) / 2) 11129166Ssam 11139166Ssam for (off = 0; off < ip->i_size; off += dp->d_reclen) { 111418028Smckusick if (dp->d_reclen <= 0) 111518028Smckusick return (0); 111612817Ssam error = rdwri(UIO_READ, ip, (caddr_t)dp, MINDIRSIZ, 111712817Ssam off, 1, &count); 111812817Ssam /* 111912817Ssam * Since we read MINDIRSIZ, residual must 112012817Ssam * be 0 unless we're at end of file. 112112817Ssam */ 112212817Ssam if (error || count != 0) 11239166Ssam return (0); 112412817Ssam /* skip empty entries */ 11259166Ssam if (dp->d_ino == 0) 11269166Ssam continue; 112712817Ssam /* accept only "." and ".." */ 112812817Ssam if (dp->d_namlen > 2) 112912817Ssam return (0); 11309166Ssam if (dp->d_name[0] != '.') 11319166Ssam return (0); 113212817Ssam /* 113312817Ssam * At this point d_namlen must be 1 or 2. 113412817Ssam * 1 implies ".", 2 implies ".." if second 113512817Ssam * char is also "." 113612817Ssam */ 113716777Smckusick if (dp->d_namlen == 1) 11389166Ssam continue; 113916777Smckusick if (dp->d_name[1] == '.' && dp->d_ino == parentino) 114016777Smckusick continue; 11419166Ssam return (0); 11429166Ssam } 11439166Ssam return (1); 11449166Ssam } 114512815Smckusick 114612815Smckusick /* 114712815Smckusick * Check if source directory is in the path of the target directory. 114812815Smckusick * Target is supplied locked, source is unlocked. 114912815Smckusick * The target is always iput() before returning. 115012815Smckusick */ 115112815Smckusick checkpath(source, target) 115212815Smckusick struct inode *source, *target; 115312815Smckusick { 115412815Smckusick struct dirtemplate dirbuf; 115512815Smckusick register struct inode *ip; 115612815Smckusick int error = 0; 115712815Smckusick 115812815Smckusick ip = target; 115912815Smckusick if (ip->i_number == source->i_number) { 116012815Smckusick error = EEXIST; 116112815Smckusick goto out; 116212815Smckusick } 116312815Smckusick if (ip->i_number == ROOTINO) 116412815Smckusick goto out; 116512815Smckusick 116612815Smckusick for (;;) { 116712815Smckusick if ((ip->i_mode&IFMT) != IFDIR) { 116812815Smckusick error = ENOTDIR; 116912815Smckusick break; 117012815Smckusick } 117112815Smckusick error = rdwri(UIO_READ, ip, (caddr_t)&dirbuf, 117212815Smckusick sizeof (struct dirtemplate), (off_t)0, 1, (int *)0); 117312815Smckusick if (error != 0) 117412815Smckusick break; 117512815Smckusick if (dirbuf.dotdot_namlen != 2 || 117616658Smckusick dirbuf.dotdot_name[0] != '.' || 117716658Smckusick dirbuf.dotdot_name[1] != '.') { 117812815Smckusick error = ENOTDIR; 117912815Smckusick break; 118012815Smckusick } 118112815Smckusick if (dirbuf.dotdot_ino == source->i_number) { 118212815Smckusick error = EINVAL; 118312815Smckusick break; 118412815Smckusick } 118512815Smckusick if (dirbuf.dotdot_ino == ROOTINO) 118612815Smckusick break; 118712815Smckusick iput(ip); 118812815Smckusick ip = iget(ip->i_dev, ip->i_fs, dirbuf.dotdot_ino); 118912815Smckusick if (ip == NULL) { 119012815Smckusick error = u.u_error; 119112815Smckusick break; 119212815Smckusick } 119312815Smckusick } 119412815Smckusick 119512815Smckusick out: 119612815Smckusick if (error == ENOTDIR) 119712815Smckusick printf("checkpath: .. not a directory\n"); 119812815Smckusick if (ip != NULL) 119912815Smckusick iput(ip); 120012815Smckusick return (error); 120112815Smckusick } 120215798Smckusick 120315798Smckusick /* 120415798Smckusick * Name cache initialization, from main() when we are booting 120515798Smckusick */ 120615798Smckusick nchinit() 120715798Smckusick { 120815798Smckusick register union nchash *nchp; 120915798Smckusick register struct nch *ncp; 121015798Smckusick 121115798Smckusick nchhead = 0; 121215798Smckusick nchtail = &nchhead; 121315798Smckusick 121415798Smckusick for (ncp = nch; ncp < &nch[nchsize]; ncp++) { 121515798Smckusick ncp->nc_forw = ncp; /* hash chain */ 121615798Smckusick ncp->nc_back = ncp; 121715798Smckusick 121815798Smckusick ncp->nc_nxt = NULL; /* lru chain */ 121915798Smckusick *nchtail = ncp; 122015798Smckusick ncp->nc_prev = nchtail; 122115798Smckusick nchtail = &ncp->nc_nxt; 122215798Smckusick 122315798Smckusick /* all else is zero already */ 122415798Smckusick } 122515798Smckusick 122615798Smckusick for (nchp = nchash; nchp < &nchash[NCHHASH]; nchp++) { 122715798Smckusick nchp->nch_head[0] = nchp; 122815798Smckusick nchp->nch_head[1] = nchp; 122915798Smckusick } 123015798Smckusick } 123115798Smckusick 123215798Smckusick /* 123315798Smckusick * Cache flush, called when filesys is umounted to 123415798Smckusick * remove entries that would now be invalid 123515798Smckusick * 123615798Smckusick * The line "nxtcp = nchhead" near the end is to avoid potential problems 123715798Smckusick * if the cache lru chain is modified while we are dumping the 123815798Smckusick * inode. This makes the algorithm O(n^2), but do you think I care? 123915798Smckusick */ 124015798Smckusick nchinval(dev) 124115798Smckusick register dev_t dev; 124215798Smckusick { 124315798Smckusick register struct nch *ncp, *nxtcp; 124415798Smckusick 124515798Smckusick for (ncp = nchhead; ncp; ncp = nxtcp) { 124615798Smckusick nxtcp = ncp->nc_nxt; 124715798Smckusick 124815798Smckusick if (ncp->nc_ip == NULL || 124915798Smckusick (ncp->nc_idev != dev && ncp->nc_dev != dev)) 125015798Smckusick continue; 125115798Smckusick 125216658Smckusick /* free the resources we had */ 125315798Smckusick ncp->nc_idev = NODEV; 125415798Smckusick ncp->nc_dev = NODEV; 125516658Smckusick ncp->nc_id = NULL; 125615798Smckusick ncp->nc_ino = 0; 125716658Smckusick ncp->nc_ip = NULL; 125815798Smckusick 125916658Smckusick 126015798Smckusick /* remove the entry from its hash chain */ 126115798Smckusick remque(ncp); 126215798Smckusick /* and make a dummy one */ 126315798Smckusick ncp->nc_forw = ncp; 126415798Smckusick ncp->nc_back = ncp; 126515798Smckusick 126615798Smckusick /* delete this entry from LRU chain */ 126715798Smckusick *ncp->nc_prev = nxtcp; 126815798Smckusick if (nxtcp) 126915798Smckusick nxtcp->nc_prev = ncp->nc_prev; 127015798Smckusick else 127115798Smckusick nchtail = ncp->nc_prev; 127215798Smckusick 127315798Smckusick /* cause rescan of list, it may have altered */ 127415798Smckusick nxtcp = nchhead; 127515798Smckusick /* put the now-free entry at head of LRU */ 127615798Smckusick ncp->nc_nxt = nxtcp; 127715798Smckusick ncp->nc_prev = &nchhead; 127815798Smckusick nxtcp->nc_prev = &ncp->nc_nxt; 127915798Smckusick nchhead = ncp; 128015798Smckusick } 128115798Smckusick } 128217704Smckusick 128317704Smckusick /* 128417704Smckusick * Name cache invalidation of all entries. 128517704Smckusick */ 128617704Smckusick cacheinvalall() 128717704Smckusick { 128817704Smckusick register struct nch *ncp; 128917704Smckusick 129017704Smckusick for (ncp = nch; ncp < &nch[nchsize]; ncp++) { 129117704Smckusick ncp->nc_id = 0; 129217704Smckusick } 129317704Smckusick } 1294