1*16666Smckusick /* vfs_lookup.c 6.10 84/07/04 */ 230Sbill 330Sbill #include "../h/param.h" 430Sbill #include "../h/systm.h" 530Sbill #include "../h/inode.h" 66571Smckusic #include "../h/fs.h" 730Sbill #include "../h/mount.h" 830Sbill #include "../h/dir.h" 930Sbill #include "../h/user.h" 1030Sbill #include "../h/buf.h" 112275Swnj #include "../h/conf.h" 127825Sroot #include "../h/uio.h" 139166Ssam #include "../h/nami.h" 1415660Smckusick #include "../h/kernel.h" 1530Sbill 167605Ssam struct buf *blkatoff(); 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 * 457534Sroot * The func argument gives the routine which returns successive 469166Ssam * characters of the name to be translated. 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 * 589166Ssam * The follow argument is 1 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: 907534Sroot * copy next component of name to u.u_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 * 1135972Swnj namei(func, flag, follow) 1145972Swnj int (*func)(), flag, follow; 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 */ 1347534Sroot int prevoff; /* u.u_offset of previous entry */ 1357534Sroot int nlink = 0; /* number of symbolic links taken */ 1367534Sroot struct inode *pdp; /* saved dp during symlink work */ 1377534Sroot int i; 1389166Ssam int lockparent; 13915798Smckusick int docache; 14015798Smckusick unsigned hash; /* value of name hash for entry */ 14115798Smckusick union nchash *nhp; /* cache chain head for entry */ 14215798Smckusick int isdotdot; /* != 0 if current name is ".." */ 14330Sbill 1449166Ssam lockparent = flag & LOCKPARENT; 14515798Smckusick docache = (flag & NOCACHE) ^ NOCACHE; 14615798Smckusick flag &= ~(LOCKPARENT|NOCACHE); 14715798Smckusick if (flag == DELETE) 14815798Smckusick docache = 0; 14930Sbill /* 1507534Sroot * Get a buffer for the name to be translated, and copy the 1517534Sroot * name into the buffer. 1525972Swnj */ 1536571Smckusic nbp = geteblk(MAXPATHLEN); 1547534Sroot for (cp = nbp->b_un.b_addr; *cp = (*func)(); ) { 15516643Ssam if ((*cp&0377) == ('/'|0200) || (*cp&0200) && flag != DELETE) { 1566066Sroot u.u_error = EPERM; 1577534Sroot goto bad; 1586066Sroot } 1596066Sroot cp++; 1606571Smckusic if (cp >= nbp->b_un.b_addr + MAXPATHLEN) { 1615972Swnj u.u_error = ENOENT; 1627534Sroot goto bad; 1635972Swnj } 1645972Swnj } 1657534Sroot if (u.u_error) 1667534Sroot goto bad; 1677534Sroot 1685972Swnj /* 1697534Sroot * Get starting directory. 17030Sbill */ 1717534Sroot cp = nbp->b_un.b_addr; 1725972Swnj if (*cp == '/') { 1735972Swnj while (*cp == '/') 1745972Swnj cp++; 17530Sbill if ((dp = u.u_rdir) == NULL) 17630Sbill dp = rootdir; 1777534Sroot } else 1787534Sroot dp = u.u_cdir; 1797534Sroot fs = dp->i_fs; 180*16666Smckusick ILOCK(dp); 1815972Swnj dp->i_count++; 1827534Sroot u.u_pdir = (struct inode *)0xc0000000; /* illegal */ 1837534Sroot 1847534Sroot /* 1857534Sroot * We come to dirloop to search a new directory. 1867534Sroot * The directory must be locked so that it can be 1877534Sroot * iput, and fs must be already set to dp->i_fs. 1887534Sroot */ 1896571Smckusic dirloop: 19030Sbill /* 1917534Sroot * Check accessiblity of directory. 19230Sbill */ 1937534Sroot if ((dp->i_mode&IFMT) != IFDIR) { 19430Sbill u.u_error = ENOTDIR; 1957534Sroot goto bad; 1967534Sroot } 1977534Sroot if (access(dp, IEXEC)) 1987534Sroot goto bad; 1997534Sroot 2006384Swnj dirloop2: 2017534Sroot /* 2027534Sroot * Copy next component of name to u.u_dent. 2037534Sroot */ 20415798Smckusick hash = 0; 2057534Sroot for (i = 0; *cp != 0 && *cp != '/'; cp++) { 2066571Smckusic if (i >= MAXNAMLEN) { 2075972Swnj u.u_error = ENOENT; 2087534Sroot goto bad; 2095972Swnj } 2107534Sroot u.u_dent.d_name[i++] = *cp; 21115798Smckusick hash += (unsigned char)*cp * i; 2125972Swnj } 2136571Smckusic u.u_dent.d_namlen = i; 2147534Sroot u.u_dent.d_name[i] = 0; 21516658Smckusick isdotdot = (i == 2 && 21616658Smckusick u.u_dent.d_name[0] == '.' && u.u_dent.d_name[1] == '.'); 2177534Sroot 2187534Sroot /* 2197534Sroot * Check for degenerate name (e.g. / or "") 2207534Sroot * which is a way of talking about a directory, 2217534Sroot * e.g. like "/." or ".". 2227534Sroot */ 2237534Sroot if (u.u_dent.d_name[0] == 0) { 22415798Smckusick if (flag != LOOKUP || lockparent) { 22514937Smckusick u.u_error = EISDIR; 2267534Sroot goto bad; 2275972Swnj } 2286571Smckusic brelse(nbp); 2296571Smckusic return (dp); 2305972Swnj } 2317534Sroot 2326571Smckusic /* 23315798Smckusick * We now have a segment name to search for, and a directory to search. 23415798Smckusick * 23515798Smckusick * Before tediously performing a linear scan of the directory, 23615798Smckusick * check the name cache to see if the directory/name pair 23715798Smckusick * we are looking for is known already. We don't do this 23815798Smckusick * if the segment name is long, simply so the cache can avoid 23915798Smckusick * holding long names (which would either waste space, or 24015798Smckusick * add greatly to the complexity). 24115798Smckusick */ 24215798Smckusick if (u.u_dent.d_namlen > NCHNAMLEN) { 24315798Smckusick nchstats.ncs_long++; 24415798Smckusick docache = 0; 24515798Smckusick } else { 24615798Smckusick nhp = &nchash[NHASH(hash, dp->i_number, dp->i_dev)]; 24715798Smckusick for (ncp = nhp->nch_forw; ncp != (struct nch *)nhp; 24815798Smckusick ncp = ncp->nc_forw) { 24915798Smckusick if (ncp->nc_ino == dp->i_number && 25015798Smckusick ncp->nc_dev == dp->i_dev && 25115798Smckusick ncp->nc_nlen == u.u_dent.d_namlen && 25215798Smckusick !bcmp(ncp->nc_name, u.u_dent.d_name, ncp->nc_nlen)) 25315798Smckusick break; 25415798Smckusick } 25515798Smckusick 25615798Smckusick if (ncp == (struct nch *)nhp) { 25715798Smckusick nchstats.ncs_miss++; 25815798Smckusick ncp = NULL; 25915798Smckusick } else { 26016658Smckusick if (ncp->nc_id != ncp->nc_ip->i_id) { 26116643Ssam nchstats.ncs_falsehits++; 26216658Smckusick } else if (*cp == '\0' && !docache) { 26316658Smckusick nchstats.ncs_badhits++; 26416658Smckusick } else { 26515798Smckusick 26615798Smckusick /* 26715798Smckusick * move this slot to end of LRU 26815798Smckusick * chain, if not already there 26915798Smckusick */ 27015798Smckusick if (ncp->nc_nxt) { 27115798Smckusick /* remove from LRU chain */ 27215798Smckusick *ncp->nc_prev = ncp->nc_nxt; 27315798Smckusick ncp->nc_nxt->nc_prev = ncp->nc_prev; 27415798Smckusick 27515798Smckusick /* and replace at end of it */ 27615798Smckusick ncp->nc_nxt = NULL; 27715798Smckusick ncp->nc_prev = nchtail; 27815798Smckusick *nchtail = ncp; 27915798Smckusick nchtail = &ncp->nc_nxt; 28015798Smckusick } 28115798Smckusick 28216658Smckusick /* 28316658Smckusick * Get the next inode in the path. 284*16666Smckusick * See comment above other `IUNLOCK' code for 28516658Smckusick * an explaination of the locking protocol. 28616658Smckusick */ 28715798Smckusick pdp = dp; 28815798Smckusick dp = ncp->nc_ip; 28915798Smckusick if (dp == NULL) 29015798Smckusick panic("nami: null cache ino"); 29116643Ssam if (pdp == dp) 29216643Ssam dp->i_count++; 293*16666Smckusick else { 29416658Smckusick if (isdotdot) { 295*16666Smckusick IUNLOCK(pdp); 29616658Smckusick igrab(dp); 29716658Smckusick } else { 29816658Smckusick igrab(dp); 299*16666Smckusick IUNLOCK(pdp); 30016658Smckusick } 30116643Ssam } 30215798Smckusick 30316658Smckusick /* 30416658Smckusick * Verify that the inode that we got 30516658Smckusick * did not change while we were waiting 30616658Smckusick * for it to be locked. 30716658Smckusick */ 30816658Smckusick if (ncp->nc_id != ncp->nc_ip->i_id) { 30916658Smckusick iput(dp); 310*16666Smckusick ILOCK(pdp); 31116658Smckusick dp = pdp; 31216658Smckusick nchstats.ncs_falsehits++; 31316658Smckusick } else { 31416658Smckusick u.u_dent.d_ino = dp->i_number; 31516658Smckusick /* u_dent.d_reclen is garbage ... */ 31616658Smckusick nchstats.ncs_goodhits++; 31716658Smckusick goto haveino; 31816658Smckusick } 31916658Smckusick } 32015798Smckusick 32115798Smckusick /* 32216643Ssam * Last component and we are renaming or deleting, 32316643Ssam * the cache entry is invalid, or otherwise don't 32416643Ssam * want cache entry to exist. 32515798Smckusick */ 32615798Smckusick 32715798Smckusick /* remove from LRU chain */ 32815798Smckusick *ncp->nc_prev = ncp->nc_nxt; 32915798Smckusick if (ncp->nc_nxt) 33015798Smckusick ncp->nc_nxt->nc_prev = ncp->nc_prev; 33115798Smckusick else 33215798Smckusick nchtail = ncp->nc_prev; 33315798Smckusick 33415798Smckusick /* remove from hash chain */ 33515798Smckusick remque(ncp); 33615798Smckusick 33715798Smckusick /* insert at head of LRU list (first to grab) */ 33815798Smckusick ncp->nc_nxt = nchhead; 33915798Smckusick ncp->nc_prev = &nchhead; 34015798Smckusick nchhead->nc_prev = &ncp->nc_nxt; 34115798Smckusick nchhead = ncp; 34215798Smckusick 34315798Smckusick /* and make a dummy hash chain */ 34415798Smckusick ncp->nc_forw = ncp; 34515798Smckusick ncp->nc_back = ncp; 34615798Smckusick 34715798Smckusick ncp = NULL; 34815798Smckusick } 34915798Smckusick } 35015798Smckusick 35115798Smckusick /* 3527534Sroot * Suppress search for slots unless creating 3537534Sroot * file and at end of pathname, in which case 3547534Sroot * we watch for a place to put the new file in 3557534Sroot * case it doesn't already exist. 3566571Smckusic */ 3577534Sroot slotstatus = FOUND; 3589166Ssam if (flag == CREATE && *cp == 0) { 3597534Sroot slotstatus = NONE; 3607534Sroot slotfreespace = 0; 3617534Sroot slotneeded = DIRSIZ(&u.u_dent); 3627534Sroot } 36315660Smckusick /* 36415660Smckusick * If this is the same directory that this process 36515660Smckusick * previously searched, pick up where we last left off. 36615798Smckusick * We cache only lookups as these are the most common 36715660Smckusick * and have the greatest payoff. Caching CREATE has little 36815660Smckusick * benefit as it usually must search the entire directory 36915660Smckusick * to determine that the entry does not exist. Caching the 37015660Smckusick * location of the last DELETE has not reduced profiling time 37115660Smckusick * and hence has been removed in the interest of simplicity. 37215660Smckusick */ 37315660Smckusick if (flag != LOOKUP || dp->i_number != u.u_ncache.nc_inumber || 37415660Smckusick dp->i_dev != u.u_ncache.nc_dev) { 37515660Smckusick u.u_offset = 0; 37615660Smckusick numdirpasses = 1; 37715660Smckusick } else { 37815798Smckusick if ((dp->i_flag & ICHG) || dp->i_ctime >= u.u_ncache.nc_time) { 37915660Smckusick u.u_ncache.nc_prevoffset &= ~(DIRBLKSIZ - 1); 38015660Smckusick u.u_ncache.nc_time = time.tv_sec; 38115660Smckusick } 38215660Smckusick u.u_offset = u.u_ncache.nc_prevoffset; 38315660Smckusick entryoffsetinblock = blkoff(fs, u.u_offset); 38415660Smckusick if (entryoffsetinblock != 0) { 38515660Smckusick bp = blkatoff(dp, u.u_offset, (char **)0); 38615660Smckusick if (bp == 0) 38715660Smckusick goto bad; 38815660Smckusick } 38915660Smckusick numdirpasses = 2; 39015798Smckusick nchstats.ncs_2passes++; 39115660Smckusick } 39215660Smckusick endsearch = roundup(dp->i_size, DIRBLKSIZ); 3937534Sroot 39415660Smckusick searchloop: 39515660Smckusick while (u.u_offset < endsearch) { 3965972Swnj /* 3975972Swnj * If offset is on a block boundary, 3985972Swnj * read the next directory block. 3995972Swnj * Release previous if it exists. 4005972Swnj */ 4016571Smckusic if (blkoff(fs, u.u_offset) == 0) { 4025972Swnj if (bp != NULL) 4035972Swnj brelse(bp); 4047605Ssam bp = blkatoff(dp, u.u_offset, (char **)0); 4057534Sroot if (bp == 0) 4067534Sroot goto bad; 4077534Sroot entryoffsetinblock = 0; 4085972Swnj } 4097534Sroot 4105972Swnj /* 4117534Sroot * If still looking for a slot, and at a DIRBLKSIZE 41216657Smckusick * boundary, have to start looking for free space again. 4136571Smckusic */ 4147534Sroot if (slotstatus == NONE && 4157534Sroot (entryoffsetinblock&(DIRBLKSIZ-1)) == 0) { 4167534Sroot slotoffset = -1; 4177534Sroot slotfreespace = 0; 4187534Sroot } 4197534Sroot 4207534Sroot /* 42116657Smckusick * Get pointer to next entry. 42216657Smckusick * Full validation checks are slow, so we only check 42316657Smckusick * enough to insure forward progress through the 42416657Smckusick * directory. Complete checks can be run by patching 42516657Smckusick * "dirchk" to be true. 4267534Sroot */ 4277534Sroot ep = (struct direct *)(bp->b_un.b_addr + entryoffsetinblock); 42816657Smckusick if (ep->d_reclen <= 0 || 42916657Smckusick dirchk && dirbadentry(ep, entryoffsetinblock)) { 4307534Sroot dirbad(dp, "mangled entry"); 43116657Smckusick i = DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1)); 4326571Smckusic u.u_offset += i; 4337534Sroot entryoffsetinblock += i; 4346571Smckusic continue; 4356571Smckusic } 4367534Sroot 4376571Smckusic /* 4387534Sroot * If an appropriate sized slot has not yet been found, 4396571Smckusic * check to see if one is available. Also accumulate space 4406571Smckusic * in the current block so that we can determine if 4416571Smckusic * compaction is viable. 4426571Smckusic */ 4437534Sroot if (slotstatus != FOUND) { 4447534Sroot int size = ep->d_reclen; 4457534Sroot 4466571Smckusic if (ep->d_ino != 0) 4476571Smckusic size -= DIRSIZ(ep); 4486571Smckusic if (size > 0) { 4497534Sroot if (size >= slotneeded) { 4507534Sroot slotstatus = FOUND; 4517534Sroot slotoffset = u.u_offset; 4527534Sroot slotsize = ep->d_reclen; 4537534Sroot } else if (slotstatus == NONE) { 4547534Sroot slotfreespace += size; 4557534Sroot if (slotoffset == -1) 4567534Sroot slotoffset = u.u_offset; 4577534Sroot if (slotfreespace >= slotneeded) { 4587534Sroot slotstatus = COMPACT; 4597534Sroot slotsize = 4607534Sroot u.u_offset+ep->d_reclen - 4617534Sroot slotoffset; 4627534Sroot } 4636571Smckusic } 4646571Smckusic } 4656571Smckusic } 4667534Sroot 4676571Smckusic /* 4687534Sroot * Check for a name match. 4695972Swnj */ 4707534Sroot if (ep->d_ino) { 4717534Sroot if (ep->d_namlen == u.u_dent.d_namlen && 4727534Sroot !bcmp(u.u_dent.d_name, ep->d_name, ep->d_namlen)) 4737534Sroot goto found; 4747534Sroot } 4757534Sroot prevoff = u.u_offset; 4766571Smckusic u.u_offset += ep->d_reclen; 4777534Sroot entryoffsetinblock += ep->d_reclen; 4787534Sroot } 47915798Smckusick /* notfound: */ 48015660Smckusick /* 48115798Smckusick * If we started in the middle of the directory and failed 48215660Smckusick * to find our target, we must check the beginning as well. 48315660Smckusick */ 48415660Smckusick if (numdirpasses == 2) { 48515660Smckusick numdirpasses--; 48615660Smckusick u.u_offset = 0; 48715660Smckusick endsearch = u.u_ncache.nc_prevoffset; 48815660Smckusick goto searchloop; 48915660Smckusick } 4907534Sroot /* 4917534Sroot * If creating, and at end of pathname and current 4929166Ssam * directory has not been removed, then can consider 4939166Ssam * allowing file to be created. 4947534Sroot */ 4959166Ssam if (flag == CREATE && *cp == 0 && dp->i_nlink != 0) { 4965972Swnj /* 4977534Sroot * Access for write is interpreted as allowing 4987534Sroot * creation of files in the directory. 4995972Swnj */ 5007534Sroot if (access(dp, IWRITE)) 5017534Sroot goto bad; 5025972Swnj /* 5037534Sroot * Return an indication of where the new directory 5047534Sroot * entry should be put. If we didn't find a slot, 5057534Sroot * then set u.u_count to 0 indicating that the 5067534Sroot * new slot belongs at the end of the directory. 5077534Sroot * If we found a slot, then the new entry can be 5087534Sroot * put in the range [u.u_offset..u.u_offset+u.u_count) 5095972Swnj */ 51015660Smckusick if (slotstatus == NONE) { 51115660Smckusick u.u_offset = roundup(dp->i_size, DIRBLKSIZ); 5127534Sroot u.u_count = 0; 51315660Smckusick } else { 5147534Sroot u.u_offset = slotoffset; 5157534Sroot u.u_count = slotsize; 5165972Swnj } 5177534Sroot dp->i_flag |= IUPD|ICHG; 5187534Sroot if (bp) 5197534Sroot brelse(bp); 5207534Sroot brelse(nbp); 5215972Swnj /* 5227534Sroot * We return with the directory locked, so that 5237534Sroot * the parameters we set up above will still be 5247534Sroot * valid if we actually decide to do a direnter(). 5257534Sroot * We return NULL to indicate that the entry doesn't 5267534Sroot * currently exist, leaving a pointer to the (locked) 5277534Sroot * directory inode in u.u_pdir. 5285972Swnj */ 5297534Sroot u.u_pdir = dp; 5307534Sroot return (NULL); 5317534Sroot } 5327534Sroot u.u_error = ENOENT; 5337534Sroot goto bad; 5347534Sroot found: 53515798Smckusick if (numdirpasses == 2) 53615798Smckusick nchstats.ncs_pass2++; 5377534Sroot /* 5387534Sroot * Check that directory length properly reflects presence 5397534Sroot * of this entry. 5407534Sroot */ 5417605Ssam if (entryoffsetinblock + DIRSIZ(ep) > dp->i_size) { 5427534Sroot dirbad(dp, "i_size too small"); 5437605Ssam dp->i_size = entryoffsetinblock + DIRSIZ(ep); 5447534Sroot dp->i_flag |= IUPD|ICHG; 5457534Sroot } 5467534Sroot 5477534Sroot /* 54815660Smckusick * Found component in pathname. 54915798Smckusick * If the final component of path name, save information 55015660Smckusick * in the cache as to where the entry was found. 5517534Sroot */ 55215660Smckusick if (*cp == '\0' && flag == LOOKUP) { 55315660Smckusick u.u_ncache.nc_prevoffset = u.u_offset; 55415660Smckusick u.u_ncache.nc_inumber = dp->i_number; 55515660Smckusick u.u_ncache.nc_dev = dp->i_dev; 55615660Smckusick u.u_ncache.nc_time = time.tv_sec; 55715660Smckusick } 55815660Smckusick /* 55915660Smckusick * Save directory entry in u.u_dent, 56015660Smckusick * and release directory buffer. 56115660Smckusick */ 5627825Sroot bcopy((caddr_t)ep, (caddr_t)&u.u_dent, (u_int)DIRSIZ(ep)); 5637534Sroot brelse(bp); 5647534Sroot bp = NULL; 5657534Sroot 5667534Sroot /* 5677534Sroot * If deleting, and at end of pathname, return 5687534Sroot * parameters which can be used to remove file. 5699166Ssam * If the lockparent flag isn't set, we return only 5709166Ssam * the directory (in u.u_pdir), otherwise we go 5719166Ssam * on and lock the inode, being careful with ".". 5727534Sroot */ 5739166Ssam if (flag == DELETE && *cp == 0) { 5747534Sroot /* 5757534Sroot * Write access to directory required to delete files. 5767534Sroot */ 5777534Sroot if (access(dp, IWRITE)) 5787534Sroot goto bad; 5799166Ssam u.u_pdir = dp; /* for dirremove() */ 5807534Sroot /* 5817534Sroot * Return pointer to current entry in u.u_offset, 5827534Sroot * and distance past previous entry (if there 5837534Sroot * is a previous entry in this block) in u.u_count. 5847534Sroot * Save directory inode pointer in u.u_pdir for dirremove(). 5857534Sroot */ 5867534Sroot if ((u.u_offset&(DIRBLKSIZ-1)) == 0) 5877534Sroot u.u_count = 0; 5887534Sroot else 5897534Sroot u.u_count = u.u_offset - prevoff; 5909166Ssam if (lockparent) { 5919166Ssam if (dp->i_number == u.u_dent.d_ino) 5929166Ssam dp->i_count++; 5939166Ssam else { 5949166Ssam dp = iget(dp->i_dev, fs, u.u_dent.d_ino); 5959166Ssam if (dp == NULL) { 5969166Ssam iput(u.u_pdir); 5979166Ssam goto bad; 5989166Ssam } 59915798Smckusick /* 60016046Skarels * If directory is "sticky", then user must own 60115798Smckusick * the directory, or the file in it, else he 60215798Smckusick * may not delete it (unless he's root). This 60315798Smckusick * implements append-only directories. 60415798Smckusick */ 60516046Skarels if ((u.u_pdir->i_mode & ISVTX) && 60615798Smckusick u.u_uid != 0 && 60715798Smckusick u.u_uid != u.u_pdir->i_uid && 60815798Smckusick dp->i_uid != u.u_uid) { 60915798Smckusick iput(u.u_pdir); 61015798Smckusick u.u_error = EPERM; 61115798Smckusick goto bad; 61215798Smckusick } 6139166Ssam } 6149166Ssam } 6157534Sroot brelse(nbp); 6167534Sroot return (dp); 6177534Sroot } 6187534Sroot 6197534Sroot /* 6207534Sroot * Special handling for ".." allowing chdir out of mounted 6217534Sroot * file system: indirect .. in root inode to reevaluate 6227534Sroot * in directory file system was mounted on. 6237534Sroot */ 62416658Smckusick if (isdotdot) { 6257534Sroot if (dp == u.u_rdir) 6267534Sroot u.u_dent.d_ino = dp->i_number; 6277534Sroot else if (u.u_dent.d_ino == ROOTINO && 6287534Sroot dp->i_number == ROOTINO) { 6297534Sroot for (i = 1; i < NMOUNT; i++) 6307534Sroot if (mount[i].m_bufp != NULL && 6317534Sroot mount[i].m_dev == dp->i_dev) { 6326571Smckusic iput(dp); 6337534Sroot dp = mount[i].m_inodp; 634*16666Smckusick ILOCK(dp); 6355972Swnj dp->i_count++; 6367534Sroot fs = dp->i_fs; 6377534Sroot cp -= 2; /* back over .. */ 6387534Sroot goto dirloop2; 6395972Swnj } 64030Sbill } 6417534Sroot } 6427534Sroot 6437534Sroot /* 6449166Ssam * If rewriting (rename), return the inode and the 6459166Ssam * information required to rewrite the present directory 6469166Ssam * Must get inode of directory entry to verify it's a 6479166Ssam * regular file, or empty directory. 6489166Ssam */ 6499166Ssam if ((flag == CREATE && lockparent) && *cp == 0) { 6509166Ssam if (access(dp, IWRITE)) 6519166Ssam goto bad; 6529166Ssam u.u_pdir = dp; /* for dirrewrite() */ 6539166Ssam /* 6549166Ssam * Careful about locking second inode. 6559166Ssam * This can only occur if the target is ".". 6569166Ssam */ 6579166Ssam if (dp->i_number == u.u_dent.d_ino) { 6589166Ssam u.u_error = EISDIR; /* XXX */ 6599166Ssam goto bad; 6609166Ssam } 6619166Ssam dp = iget(dp->i_dev, fs, u.u_dent.d_ino); 6629166Ssam if (dp == NULL) { 6639166Ssam iput(u.u_pdir); 6649166Ssam goto bad; 6659166Ssam } 6669166Ssam brelse(nbp); 6679166Ssam return (dp); 6689166Ssam } 6699166Ssam 6709166Ssam /* 67112011Smckusick * Check for symbolic link, which may require us to massage the 67212011Smckusick * name before we continue translation. We do not `iput' the 67312011Smckusick * directory because we may need it again if the symbolic link 67412011Smckusick * is relative to the current directory. Instead we save it 67512011Smckusick * unlocked as "pdp". We must get the target inode before unlocking 67612011Smckusick * the directory to insure that the inode will not be removed 67712011Smckusick * before we get it. We prevent deadlock by always fetching 67812011Smckusick * inodes from the root, moving down the directory tree. Thus 67912011Smckusick * when following backward pointers ".." we must unlock the 68012011Smckusick * parent directory before getting the requested directory. 68112011Smckusick * There is a potential race condition here if both the current 68212011Smckusick * and parent directories are removed before the `iget' for the 68312011Smckusick * inode associated with ".." returns. We hope that this occurs 68412011Smckusick * infrequently since we cannot avoid this race condition without 68512492Ssam * implementing a sophisticated deadlock detection algorithm. 68612011Smckusick * Note also that this simple deadlock detection scheme will not 68712011Smckusick * work if the file system has any hard links other than ".." 68812011Smckusick * that point backwards in the directory structure. 6897534Sroot */ 6907534Sroot pdp = dp; 69115798Smckusick if (isdotdot) { 692*16666Smckusick IUNLOCK(pdp); /* race to get the inode */ 69312011Smckusick dp = iget(dp->i_dev, fs, u.u_dent.d_ino); 69412011Smckusick if (dp == NULL) 69512011Smckusick goto bad2; 69612011Smckusick } else if (dp->i_number == u.u_dent.d_ino) { 69712011Smckusick dp->i_count++; /* we want ourself, ie "." */ 69812011Smckusick } else { 69912011Smckusick dp = iget(dp->i_dev, fs, u.u_dent.d_ino); 700*16666Smckusick IUNLOCK(pdp); 70112011Smckusick if (dp == NULL) 70212011Smckusick goto bad2; 70312011Smckusick } 70415798Smckusick 70515798Smckusick /* 70615798Smckusick * insert name into cache (if we want it, and it isn't "." or "..") 70715798Smckusick * 70815798Smckusick * all other cases where making a cache entry would be wrong 70915798Smckusick * have already departed from the code sequence somewhere above. 71015798Smckusick */ 71116643Ssam if (docache) { 71215798Smckusick if (ncp != NULL) 71315798Smckusick panic("nami: duplicating cache"); 71415798Smckusick 71515798Smckusick /* 71615798Smckusick * free the cache slot at head of lru chain 71715798Smckusick */ 71815798Smckusick if (ncp = nchhead) { 71915798Smckusick /* remove from lru chain */ 72015798Smckusick *ncp->nc_prev = ncp->nc_nxt; 72115798Smckusick if (ncp->nc_nxt) 72215798Smckusick ncp->nc_nxt->nc_prev = ncp->nc_prev; 72315798Smckusick else 72415798Smckusick nchtail = ncp->nc_prev; 72515798Smckusick 72615798Smckusick /* remove from old hash chain */ 72715798Smckusick remque(ncp); 72815798Smckusick 72915798Smckusick /* grab the inode we just found */ 73015798Smckusick ncp->nc_ip = dp; 73115798Smckusick 73215798Smckusick /* fill in cache info */ 73315798Smckusick ncp->nc_ino = pdp->i_number; /* parents inum */ 73415798Smckusick ncp->nc_dev = pdp->i_dev; /* & device */ 73515798Smckusick ncp->nc_idev = dp->i_dev; /* our device */ 73616643Ssam ncp->nc_id = dp->i_id; /* identifier */ 73715798Smckusick ncp->nc_nlen = u.u_dent.d_namlen; 73815798Smckusick bcopy(u.u_dent.d_name, ncp->nc_name, ncp->nc_nlen); 73915798Smckusick 74015798Smckusick /* link at end of lru chain */ 74115798Smckusick ncp->nc_nxt = NULL; 74215798Smckusick ncp->nc_prev = nchtail; 74315798Smckusick *nchtail = ncp; 74415798Smckusick nchtail = &ncp->nc_nxt; 74515798Smckusick 74615798Smckusick /* and insert on hash chain */ 74715798Smckusick insque(ncp, nhp); 74815798Smckusick } 74915798Smckusick } 75015798Smckusick 75115798Smckusick haveino: 7527534Sroot fs = dp->i_fs; 7537534Sroot 7547534Sroot /* 7557534Sroot * Check for symbolic link 7567534Sroot */ 7577534Sroot if ((dp->i_mode & IFMT) == IFLNK && (follow || *cp == '/')) { 7587825Sroot u_int pathlen = strlen(cp) + 1; 7597534Sroot 7607534Sroot if (dp->i_size + pathlen >= MAXPATHLEN - 1 || 7617534Sroot ++nlink > MAXSYMLINKS) { 7627534Sroot u.u_error = ELOOP; 7637534Sroot goto bad2; 7647534Sroot } 7658957Sroot ovbcopy(cp, nbp->b_un.b_addr + dp->i_size, pathlen); 7667751Sroot u.u_error = 7679166Ssam rdwri(UIO_READ, dp, nbp->b_un.b_addr, (int)dp->i_size, 7687825Sroot 0, 1, (int *)0); 7697534Sroot if (u.u_error) 7707534Sroot goto bad2; 7717534Sroot cp = nbp->b_un.b_addr; 7727534Sroot iput(dp); 7735972Swnj if (*cp == '/') { 7747534Sroot irele(pdp); 7755972Swnj while (*cp == '/') 7765972Swnj cp++; 7777534Sroot if ((dp = u.u_rdir) == NULL) 7787534Sroot dp = rootdir; 779*16666Smckusick ILOCK(dp); 7807534Sroot dp->i_count++; 7817534Sroot } else { 7827534Sroot dp = pdp; 783*16666Smckusick ILOCK(dp); 7845972Swnj } 7857534Sroot fs = dp->i_fs; 7867534Sroot goto dirloop; 78730Sbill } 7887534Sroot 78930Sbill /* 7907534Sroot * Not a symbolic link. If more pathname, 7917534Sroot * continue at next component, else return. 79230Sbill */ 7937534Sroot if (*cp == '/') { 7947534Sroot while (*cp == '/') 7957534Sroot cp++; 7969166Ssam irele(pdp); 7977534Sroot goto dirloop; 79830Sbill } 7995972Swnj brelse(nbp); 8009166Ssam if (lockparent) 8019166Ssam u.u_pdir = pdp; 8029166Ssam else 8039166Ssam irele(pdp); 8047534Sroot return (dp); 8057534Sroot bad2: 8067534Sroot irele(pdp); 8077534Sroot bad: 8087534Sroot if (bp) 8097534Sroot brelse(bp); 8107534Sroot if (dp) 8117534Sroot iput(dp); 8127534Sroot brelse(nbp); 8136571Smckusic return (NULL); 81430Sbill } 81530Sbill 81615798Smckusick 8177534Sroot dirbad(ip, how) 8187534Sroot struct inode *ip; 8197534Sroot char *how; 8207534Sroot { 8217534Sroot 8227534Sroot printf("%s: bad dir ino %d at offset %d: %s\n", 8237534Sroot ip->i_fs->fs_fsmnt, ip->i_number, u.u_offset, how); 8247534Sroot } 8257534Sroot 82616657Smckusick /* 82716657Smckusick * Do consistency checking on a directory entry: 82816657Smckusick * record length must be multiple of 4 82916657Smckusick * record length must not be non-negative 83016657Smckusick * entry must fit in rest of its DIRBLKSIZ block 83116657Smckusick * record must be large enough to contain entry 83216657Smckusick * name is not longer than MAXNAMLEN 83316657Smckusick * name must be as long as advertised, and null terminated 83416657Smckusick */ 83516657Smckusick dirbadentry(ep, entryoffsetinblock) 8367534Sroot register struct direct *ep; 83716657Smckusick int entryoffsetinblock; 8387534Sroot { 8397534Sroot register int i; 8407534Sroot 84116657Smckusick if ((ep->d_reclen & 0x3) != 0 || ep->d_reclen <= 0 || 84216657Smckusick ep->d_reclen > DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1)) || 84316657Smckusick ep->d_reclen < DIRSIZ(ep) || ep->d_namlen > MAXNAMLEN) 84416657Smckusick return (1); 8457534Sroot for (i = 0; i < ep->d_namlen; i++) 8467534Sroot if (ep->d_name[i] == 0) 8477534Sroot return (1); 8487534Sroot return (ep->d_name[i]); 8497534Sroot } 8507534Sroot 85130Sbill /* 8527534Sroot * Write a directory entry after a call to namei, using the parameters 8537534Sroot * which it left in the u. area. The argument ip is the inode which 8547534Sroot * the new directory entry will refer to. The u. area field u.u_pdir is 8557534Sroot * a pointer to the directory to be written, which was left locked by 8567534Sroot * namei. Remaining parameters (u.u_offset, u.u_count) indicate 8577534Sroot * how the space for the new entry is to be gotten. 8587534Sroot */ 8597534Sroot direnter(ip) 8607534Sroot struct inode *ip; 8615972Swnj { 8627534Sroot register struct direct *ep, *nep; 8637534Sroot struct buf *bp; 86411639Ssam int loc, spacefree, error = 0; 8658631Sroot u_int dsize; 8668631Sroot int newentrysize; 8677534Sroot char *dirbuf; 8685972Swnj 8697534Sroot u.u_dent.d_ino = ip->i_number; 8707534Sroot u.u_segflg = 1; 8717534Sroot newentrysize = DIRSIZ(&u.u_dent); 8727534Sroot if (u.u_count == 0) { 8737534Sroot /* 8747534Sroot * If u.u_count is 0, then namei could find no space in the 8757534Sroot * directory. In this case u.u_offset will be on a directory 8767534Sroot * block boundary and we will write the new entry into a fresh 8777534Sroot * block. 8787534Sroot */ 8797534Sroot if (u.u_offset&(DIRBLKSIZ-1)) 8807534Sroot panic("wdir: newblk"); 8817534Sroot u.u_dent.d_reclen = DIRBLKSIZ; 88210849Ssam error = rdwri(UIO_WRITE, u.u_pdir, (caddr_t)&u.u_dent, 8838631Sroot newentrysize, u.u_offset, 1, (int *)0); 8847534Sroot iput(u.u_pdir); 88510849Ssam return (error); 8867534Sroot } 8877534Sroot 8887534Sroot /* 8897534Sroot * If u.u_count is non-zero, then namei found space for the 8907534Sroot * new entry in the range u.u_offset to u.u_offset+u.u_count. 8917534Sroot * in the directory. To use this space, we may have to compact 8927534Sroot * the entries located there, by copying them together towards 8937534Sroot * the beginning of the block, leaving the free space in 8947534Sroot * one usable chunk at the end. 8957534Sroot */ 8967534Sroot 8977534Sroot /* 8987534Sroot * Increase size of directory if entry eats into new space. 8997534Sroot * This should never push the size past a new multiple of 9007534Sroot * DIRBLKSIZE. 9017534Sroot */ 9029166Ssam if (u.u_offset + u.u_count > u.u_pdir->i_size) 9037534Sroot u.u_pdir->i_size = u.u_offset + u.u_count; 9047534Sroot 9057534Sroot /* 9067534Sroot * Get the block containing the space for the new directory 90710849Ssam * entry. Should return error by result instead of u.u_error. 9087534Sroot */ 9097605Ssam bp = blkatoff(u.u_pdir, u.u_offset, (char **)&dirbuf); 9109166Ssam if (bp == 0) { 9119166Ssam iput(u.u_pdir); 91210849Ssam return (u.u_error); 9139166Ssam } 9147534Sroot 9157534Sroot /* 9167534Sroot * Find space for the new entry. In the simple case, the 9177534Sroot * entry at offset base will have the space. If it does 9187534Sroot * not, then namei arranged that compacting the region 9197534Sroot * u.u_offset to u.u_offset+u.u_count would yield the space. 9207534Sroot */ 9217534Sroot ep = (struct direct *)dirbuf; 9227534Sroot dsize = DIRSIZ(ep); 92311639Ssam spacefree = ep->d_reclen - dsize; 9247534Sroot for (loc = ep->d_reclen; loc < u.u_count; ) { 9257534Sroot nep = (struct direct *)(dirbuf + loc); 9267534Sroot if (ep->d_ino) { 9277534Sroot /* trim the existing slot */ 9287534Sroot ep->d_reclen = dsize; 9297534Sroot ep = (struct direct *)((char *)ep + dsize); 9307534Sroot } else { 9317534Sroot /* overwrite; nothing there; header is ours */ 93211639Ssam spacefree += dsize; 9337534Sroot } 9347534Sroot dsize = DIRSIZ(nep); 93511639Ssam spacefree += nep->d_reclen - dsize; 9367534Sroot loc += nep->d_reclen; 9377825Sroot bcopy((caddr_t)nep, (caddr_t)ep, dsize); 9387534Sroot } 9397534Sroot /* 9407534Sroot * Update the pointer fields in the previous entry (if any), 9417534Sroot * copy in the new entry, and write out the block. 9427534Sroot */ 9437534Sroot if (ep->d_ino == 0) { 94411639Ssam if (spacefree + dsize < newentrysize) 9457534Sroot panic("wdir: compact1"); 94611639Ssam u.u_dent.d_reclen = spacefree + dsize; 9477534Sroot } else { 94811639Ssam if (spacefree < newentrysize) 9497534Sroot panic("wdir: compact2"); 95011639Ssam u.u_dent.d_reclen = spacefree; 9517534Sroot ep->d_reclen = dsize; 9527534Sroot ep = (struct direct *)((char *)ep + dsize); 9537534Sroot } 9548672Sroot bcopy((caddr_t)&u.u_dent, (caddr_t)ep, (u_int)newentrysize); 9557534Sroot bwrite(bp); 9567534Sroot u.u_pdir->i_flag |= IUPD|ICHG; 9577534Sroot iput(u.u_pdir); 95810849Ssam return (error); 9595972Swnj } 9606571Smckusic 9619166Ssam /* 9629166Ssam * Remove a directory entry after a call to namei, using the 9639166Ssam * parameters which it left in the u. area. The u. entry 9649166Ssam * u_offset contains the offset into the directory of the 9659166Ssam * entry to be eliminated. The u_count field contains the 9669166Ssam * size of the previous record in the directory. If this 9679166Ssam * is 0, the first entry is being deleted, so we need only 9689166Ssam * zero the inode number to mark the entry as free. If the 9699166Ssam * entry isn't the first in the directory, we must reclaim 9709166Ssam * the space of the now empty record by adding the record size 9719166Ssam * to the size of the previous entry. 9729166Ssam */ 9737534Sroot dirremove() 9746571Smckusic { 9757534Sroot register struct inode *dp = u.u_pdir; 9767534Sroot register struct buf *bp; 9777534Sroot struct direct *ep; 9786571Smckusic 9799269Ssam if (u.u_count == 0) { 9807534Sroot /* 9817534Sroot * First entry in block: set d_ino to zero. 9827534Sroot */ 9839269Ssam u.u_dent.d_ino = 0; 9848619Sroot (void) rdwri(UIO_WRITE, dp, (caddr_t)&u.u_dent, 9858631Sroot (int)DIRSIZ(&u.u_dent), u.u_offset, 1, (int *)0); 9869269Ssam } else { 9877534Sroot /* 9887534Sroot * Collapse new free space into previous entry. 9897534Sroot */ 9907825Sroot bp = blkatoff(dp, (int)(u.u_offset - u.u_count), (char **)&ep); 9917534Sroot if (bp == 0) 9927534Sroot return (0); 9937534Sroot ep->d_reclen += u.u_dent.d_reclen; 9947534Sroot bwrite(bp); 9957534Sroot dp->i_flag |= IUPD|ICHG; 9967534Sroot } 9977534Sroot return (1); 9986571Smckusic } 9997534Sroot 10007605Ssam /* 10019166Ssam * Rewrite an existing directory entry to point at the inode 10029166Ssam * supplied. The parameters describing the directory entry are 10039166Ssam * set up by a call to namei. 10049166Ssam */ 10059166Ssam dirrewrite(dp, ip) 10069166Ssam struct inode *dp, *ip; 10079166Ssam { 10089166Ssam 10099166Ssam u.u_dent.d_ino = ip->i_number; 10109166Ssam u.u_error = rdwri(UIO_WRITE, dp, (caddr_t)&u.u_dent, 10119166Ssam (int)DIRSIZ(&u.u_dent), u.u_offset, 1, (int *)0); 10129166Ssam iput(dp); 10139166Ssam } 10149166Ssam 10159166Ssam /* 10167605Ssam * Return buffer with contents of block "offset" 10177605Ssam * from the beginning of directory "ip". If "res" 10187605Ssam * is non-zero, fill it in with a pointer to the 10197605Ssam * remaining space in the directory. 10207605Ssam */ 10217534Sroot struct buf * 10227605Ssam blkatoff(ip, offset, res) 10237534Sroot struct inode *ip; 10247534Sroot off_t offset; 10257534Sroot char **res; 10267534Sroot { 10277534Sroot register struct fs *fs = ip->i_fs; 10288672Sroot daddr_t lbn = lblkno(fs, offset); 10297534Sroot int base = blkoff(fs, offset); 10307534Sroot int bsize = blksize(fs, ip, lbn); 10318672Sroot daddr_t bn = fsbtodb(fs, bmap(ip, lbn, B_WRITE, base, bsize)); 10327534Sroot register struct buf *bp; 10337534Sroot 10347534Sroot if (u.u_error) 10357534Sroot return (0); 10367534Sroot bp = bread(ip->i_dev, bn, bsize); 10377534Sroot if (bp->b_flags & B_ERROR) { 10387534Sroot brelse(bp); 10397534Sroot return (0); 10407534Sroot } 10417534Sroot if (res) 10427534Sroot *res = bp->b_un.b_addr + base; 10437534Sroot return (bp); 10447534Sroot } 10459166Ssam 10469166Ssam /* 10479166Ssam * Check if a directory is empty or not. 10489166Ssam * Inode supplied must be locked. 104912817Ssam * 105012817Ssam * Using a struct dirtemplate here is not precisely 105112817Ssam * what we want, but better than using a struct direct. 105212817Ssam * 105312817Ssam * NB: does not handle corrupted directories. 10549166Ssam */ 10559166Ssam dirempty(ip) 10569863Ssam register struct inode *ip; 10579166Ssam { 10589166Ssam register off_t off; 105912817Ssam struct dirtemplate dbuf; 106012817Ssam register struct direct *dp = (struct direct *)&dbuf; 10619863Ssam int error, count; 106212817Ssam #define MINDIRSIZ (sizeof (struct dirtemplate) / 2) 10639166Ssam 10649166Ssam for (off = 0; off < ip->i_size; off += dp->d_reclen) { 106512817Ssam error = rdwri(UIO_READ, ip, (caddr_t)dp, MINDIRSIZ, 106612817Ssam off, 1, &count); 106712817Ssam /* 106812817Ssam * Since we read MINDIRSIZ, residual must 106912817Ssam * be 0 unless we're at end of file. 107012817Ssam */ 107112817Ssam if (error || count != 0) 10729166Ssam return (0); 107312817Ssam /* skip empty entries */ 10749166Ssam if (dp->d_ino == 0) 10759166Ssam continue; 107612817Ssam /* accept only "." and ".." */ 107712817Ssam if (dp->d_namlen > 2) 107812817Ssam return (0); 10799166Ssam if (dp->d_name[0] != '.') 10809166Ssam return (0); 108112817Ssam /* 108212817Ssam * At this point d_namlen must be 1 or 2. 108312817Ssam * 1 implies ".", 2 implies ".." if second 108412817Ssam * char is also "." 108512817Ssam */ 108612817Ssam if (dp->d_namlen == 1 || dp->d_name[1] == '.') 10879166Ssam continue; 10889166Ssam return (0); 10899166Ssam } 10909166Ssam return (1); 10919166Ssam } 109212815Smckusick 109312815Smckusick /* 109412815Smckusick * Check if source directory is in the path of the target directory. 109512815Smckusick * Target is supplied locked, source is unlocked. 109612815Smckusick * The target is always iput() before returning. 109712815Smckusick */ 109812815Smckusick checkpath(source, target) 109912815Smckusick struct inode *source, *target; 110012815Smckusick { 110112815Smckusick struct dirtemplate dirbuf; 110212815Smckusick register struct inode *ip; 110312815Smckusick int error = 0; 110412815Smckusick 110512815Smckusick ip = target; 110612815Smckusick if (ip->i_number == source->i_number) { 110712815Smckusick error = EEXIST; 110812815Smckusick goto out; 110912815Smckusick } 111012815Smckusick if (ip->i_number == ROOTINO) 111112815Smckusick goto out; 111212815Smckusick 111312815Smckusick for (;;) { 111412815Smckusick if ((ip->i_mode&IFMT) != IFDIR) { 111512815Smckusick error = ENOTDIR; 111612815Smckusick break; 111712815Smckusick } 111812815Smckusick error = rdwri(UIO_READ, ip, (caddr_t)&dirbuf, 111912815Smckusick sizeof (struct dirtemplate), (off_t)0, 1, (int *)0); 112012815Smckusick if (error != 0) 112112815Smckusick break; 112212815Smckusick if (dirbuf.dotdot_namlen != 2 || 112316658Smckusick dirbuf.dotdot_name[0] != '.' || 112416658Smckusick dirbuf.dotdot_name[1] != '.') { 112512815Smckusick error = ENOTDIR; 112612815Smckusick break; 112712815Smckusick } 112812815Smckusick if (dirbuf.dotdot_ino == source->i_number) { 112912815Smckusick error = EINVAL; 113012815Smckusick break; 113112815Smckusick } 113212815Smckusick if (dirbuf.dotdot_ino == ROOTINO) 113312815Smckusick break; 113412815Smckusick iput(ip); 113512815Smckusick ip = iget(ip->i_dev, ip->i_fs, dirbuf.dotdot_ino); 113612815Smckusick if (ip == NULL) { 113712815Smckusick error = u.u_error; 113812815Smckusick break; 113912815Smckusick } 114012815Smckusick } 114112815Smckusick 114212815Smckusick out: 114312815Smckusick if (error == ENOTDIR) 114412815Smckusick printf("checkpath: .. not a directory\n"); 114512815Smckusick if (ip != NULL) 114612815Smckusick iput(ip); 114712815Smckusick return (error); 114812815Smckusick } 114915798Smckusick 115015798Smckusick /* 115115798Smckusick * Name cache initialization, from main() when we are booting 115215798Smckusick */ 115315798Smckusick nchinit() 115415798Smckusick { 115515798Smckusick register union nchash *nchp; 115615798Smckusick register struct nch *ncp; 115715798Smckusick 115815798Smckusick nchhead = 0; 115915798Smckusick nchtail = &nchhead; 116015798Smckusick 116115798Smckusick for (ncp = nch; ncp < &nch[nchsize]; ncp++) { 116215798Smckusick ncp->nc_forw = ncp; /* hash chain */ 116315798Smckusick ncp->nc_back = ncp; 116415798Smckusick 116515798Smckusick ncp->nc_nxt = NULL; /* lru chain */ 116615798Smckusick *nchtail = ncp; 116715798Smckusick ncp->nc_prev = nchtail; 116815798Smckusick nchtail = &ncp->nc_nxt; 116915798Smckusick 117015798Smckusick /* all else is zero already */ 117115798Smckusick } 117215798Smckusick 117315798Smckusick for (nchp = nchash; nchp < &nchash[NCHHASH]; nchp++) { 117415798Smckusick nchp->nch_head[0] = nchp; 117515798Smckusick nchp->nch_head[1] = nchp; 117615798Smckusick } 117715798Smckusick } 117815798Smckusick 117915798Smckusick /* 118015798Smckusick * Cache flush, called when filesys is umounted to 118115798Smckusick * remove entries that would now be invalid 118215798Smckusick * 118315798Smckusick * The line "nxtcp = nchhead" near the end is to avoid potential problems 118415798Smckusick * if the cache lru chain is modified while we are dumping the 118515798Smckusick * inode. This makes the algorithm O(n^2), but do you think I care? 118615798Smckusick */ 118715798Smckusick nchinval(dev) 118815798Smckusick register dev_t dev; 118915798Smckusick { 119015798Smckusick register struct nch *ncp, *nxtcp; 119115798Smckusick 119215798Smckusick for (ncp = nchhead; ncp; ncp = nxtcp) { 119315798Smckusick nxtcp = ncp->nc_nxt; 119415798Smckusick 119515798Smckusick if (ncp->nc_ip == NULL || 119615798Smckusick (ncp->nc_idev != dev && ncp->nc_dev != dev)) 119715798Smckusick continue; 119815798Smckusick 119916658Smckusick /* free the resources we had */ 120015798Smckusick ncp->nc_idev = NODEV; 120115798Smckusick ncp->nc_dev = NODEV; 120216658Smckusick ncp->nc_id = NULL; 120315798Smckusick ncp->nc_ino = 0; 120416658Smckusick ncp->nc_ip = NULL; 120515798Smckusick 120616658Smckusick 120715798Smckusick /* remove the entry from its hash chain */ 120815798Smckusick remque(ncp); 120915798Smckusick /* and make a dummy one */ 121015798Smckusick ncp->nc_forw = ncp; 121115798Smckusick ncp->nc_back = ncp; 121215798Smckusick 121315798Smckusick /* delete this entry from LRU chain */ 121415798Smckusick *ncp->nc_prev = nxtcp; 121515798Smckusick if (nxtcp) 121615798Smckusick nxtcp->nc_prev = ncp->nc_prev; 121715798Smckusick else 121815798Smckusick nchtail = ncp->nc_prev; 121915798Smckusick 122015798Smckusick /* cause rescan of list, it may have altered */ 122115798Smckusick nxtcp = nchhead; 122215798Smckusick /* put the now-free entry at head of LRU */ 122315798Smckusick ncp->nc_nxt = nxtcp; 122415798Smckusick ncp->nc_prev = &nchhead; 122515798Smckusick nxtcp->nc_prev = &ncp->nc_nxt; 122615798Smckusick nchhead = ncp; 122715798Smckusick } 122815798Smckusick } 1229