1*16681Smckusick /* vfs_lookup.c 6.11 84/07/07 */ 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(); 17*16681Smckusick struct buf *freenamebuf; 189166Ssam int dirchk = 0; 1915798Smckusick 2030Sbill /* 2115798Smckusick * Structures associated with name cacheing. 2215798Smckusick */ 2315798Smckusick #define NCHHASH 32 /* size of hash table */ 2415798Smckusick 2515798Smckusick #if ((NCHHASH)&((NCHHASH)-1)) != 0 2615798Smckusick #define NHASH(h, i, d) ((unsigned)((h) + (i) + 13 * (int)(d)) % (NCHHASH)) 2715798Smckusick #else 2815798Smckusick #define NHASH(h, i, d) ((unsigned)((h) + (i) + 13 * (int)(d)) & ((NCHHASH)-1)) 2915798Smckusick #endif 3015798Smckusick 3115798Smckusick union nchash { 3215798Smckusick union nchash *nch_head[2]; 3315798Smckusick struct nch *nch_chain[2]; 3415798Smckusick } nchash[NCHHASH]; 3515798Smckusick #define nch_forw nch_chain[0] 3615798Smckusick #define nch_back nch_chain[1] 3715798Smckusick 3815809Smckusick struct nch *nchhead, **nchtail; /* LRU chain pointers */ 3915809Smckusick struct nchstats nchstats; /* cache effectiveness statistics */ 4015798Smckusick 4115798Smckusick /* 427534Sroot * Convert a pathname into a pointer to a locked inode, 437534Sroot * with side effects usable in creating and removing files. 447534Sroot * This is a very central and rather complicated routine. 4530Sbill * 467534Sroot * The func argument gives the routine which returns successive 479166Ssam * characters of the name to be translated. 487534Sroot * 499166Ssam * The flag argument is (LOOKUP, CREATE, DELETE) depending on whether 509166Ssam * the name is to be (looked up, created, deleted). If flag has 519166Ssam * LOCKPARENT or'ed into it and the target of the pathname exists, 529166Ssam * namei returns both the target and its parent directory locked. 539166Ssam * If the file system is not maintained in a strict tree hierarchy, 549166Ssam * this can result in a deadlock situation. When creating and 559166Ssam * LOCKPARENT is specified, the target may not be ".". When deleting 569166Ssam * and LOCKPARENT is specified, the target may be ".", but the caller 579166Ssam * must check to insure it does an irele and iput instead of two iputs. 589166Ssam * 599166Ssam * The follow argument is 1 when symbolic links are to be followed 609166Ssam * when they occur at the end of the name translation process. 619166Ssam * 6215798Smckusick * Name caching works as follows: 637534Sroot * 6415798Smckusick * names found by directory scans are retained in a cache 6515798Smckusick * for future reference. It is managed LRU, so frequently 6615798Smckusick * used names will hang around. Cache is indexed by hash value 6715798Smckusick * obtained from (ino,dev,name) where ino & dev refer to the 6815798Smckusick * directory containing name. 6915798Smckusick * 7015798Smckusick * For simplicity (and economy of storage), names longer than 7115798Smckusick * some (small) maximum length are not cached, they occur 7215798Smckusick * infrequently in any case, and are almost never of interest. 7315798Smckusick * 7415798Smckusick * Upon reaching the last segment of a path, if the reference 7515798Smckusick * is for DELETE, or NOCACHE is set (rewrite), and the 7615798Smckusick * name is located in the cache, it will be dropped. 7715798Smckusick * 7815798Smckusick * We must be sure never to enter the name ".." into the cache 7915798Smckusick * because of the extremely kludgey way that rename() alters 8015798Smckusick * ".." in a situation like 8115798Smckusick * mv a/x b/x 8215798Smckusick * where x is a directory, and x/.. is the ".." in question. 8315798Smckusick * 8415798Smckusick * Overall outline of namei: 8515798Smckusick * 867534Sroot * copy in name 877534Sroot * get starting directory 887534Sroot * dirloop: 897534Sroot * check accessibility of directory 907534Sroot * dirloop2: 917534Sroot * copy next component of name to u.u_dent 927534Sroot * handle degenerate case where name is null string 9315798Smckusick * look for name in cache, if found, then if at end of path 9415798Smckusick * and deleting or creating, drop it, else to haveino 957534Sroot * search for name in directory, to found or notfound 967534Sroot * notfound: 979166Ssam * if creating, return locked directory, leaving info on avail. slots 987534Sroot * else return error 997534Sroot * found: 1007534Sroot * if at end of path and deleting, return information to allow delete 10115798Smckusick * if at end of path and rewriting (create and LOCKPARENT), lock target 1029166Ssam * inode and return info to allow rewrite 1037534Sroot * if .. and on mounted filesys, look in mount table for parent 10415798Smckusick * if not at end, if neither creating nor deleting, add name to cache 10515798Smckusick * haveino: 1067534Sroot * if symbolic link, massage name in buffer and continue at dirloop 1077534Sroot * if more components of name, do next level at dirloop 1087534Sroot * return the answer as locked inode 1099166Ssam * 1109166Ssam * NOTE: (LOOKUP | LOCKPARENT) currently returns the parent inode, 1119166Ssam * but unlocked. 11230Sbill */ 11330Sbill struct inode * 1145972Swnj namei(func, flag, follow) 1155972Swnj int (*func)(), flag, follow; 11630Sbill { 1177534Sroot register char *cp; /* pointer into pathname argument */ 1187534Sroot /* these variables refer to things which must be freed or unlocked */ 1197534Sroot register struct inode *dp = 0; /* the directory we are searching */ 12015798Smckusick register struct nch *ncp; /* cache slot for entry */ 1217534Sroot register struct fs *fs; /* file system that directory is in */ 1227534Sroot register struct buf *bp = 0; /* a buffer of directory entries */ 1237534Sroot register struct direct *ep; /* the current directory entry */ 1247534Sroot int entryoffsetinblock; /* offset of ep in bp's buffer */ 1257534Sroot register struct buf *nbp; /* buffer storing path name argument */ 1267534Sroot /* these variables hold information about the search for a slot */ 1277534Sroot enum {NONE, COMPACT, FOUND} slotstatus; 1287534Sroot int slotoffset = -1; /* offset of area with free space */ 1297534Sroot int slotsize; /* size of area at slotoffset */ 1307534Sroot int slotfreespace; /* amount of space free in slot */ 1317534Sroot int slotneeded; /* size of the entry we're seeking */ 1327534Sroot /* */ 13315660Smckusick int numdirpasses; /* strategy for directory search */ 13415660Smckusick int endsearch; /* offset to end directory search */ 1357534Sroot int prevoff; /* u.u_offset of previous entry */ 1367534Sroot int nlink = 0; /* number of symbolic links taken */ 1377534Sroot struct inode *pdp; /* saved dp during symlink work */ 1387534Sroot int i; 1399166Ssam int lockparent; 14015798Smckusick int docache; 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 ".." */ 14430Sbill 1459166Ssam lockparent = flag & LOCKPARENT; 14615798Smckusick docache = (flag & NOCACHE) ^ NOCACHE; 14715798Smckusick flag &= ~(LOCKPARENT|NOCACHE); 14815798Smckusick if (flag == DELETE) 14915798Smckusick docache = 0; 15030Sbill /* 1517534Sroot * Get a buffer for the name to be translated, and copy the 1527534Sroot * name into the buffer. 1535972Swnj */ 154*16681Smckusick nbp = freenamebuf; 155*16681Smckusick if (nbp == NULL) 156*16681Smckusick nbp = geteblk(MAXPATHLEN); 157*16681Smckusick else 158*16681Smckusick freenamebuf = nbp->av_forw; 1597534Sroot for (cp = nbp->b_un.b_addr; *cp = (*func)(); ) { 16016643Ssam if ((*cp&0377) == ('/'|0200) || (*cp&0200) && flag != DELETE) { 1616066Sroot u.u_error = EPERM; 1627534Sroot goto bad; 1636066Sroot } 1646066Sroot cp++; 1656571Smckusic if (cp >= nbp->b_un.b_addr + MAXPATHLEN) { 1665972Swnj u.u_error = ENOENT; 1677534Sroot goto bad; 1685972Swnj } 1695972Swnj } 1707534Sroot if (u.u_error) 1717534Sroot goto bad; 1727534Sroot 1735972Swnj /* 1747534Sroot * Get starting directory. 17530Sbill */ 1767534Sroot cp = nbp->b_un.b_addr; 1775972Swnj if (*cp == '/') { 1785972Swnj while (*cp == '/') 1795972Swnj cp++; 18030Sbill if ((dp = u.u_rdir) == NULL) 18130Sbill dp = rootdir; 1827534Sroot } else 1837534Sroot dp = u.u_cdir; 1847534Sroot fs = dp->i_fs; 18516666Smckusick ILOCK(dp); 1865972Swnj dp->i_count++; 1877534Sroot u.u_pdir = (struct inode *)0xc0000000; /* illegal */ 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 /* 2077534Sroot * Copy next component of name to u.u_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 } 2157534Sroot u.u_dent.d_name[i++] = *cp; 21615798Smckusick hash += (unsigned char)*cp * i; 2175972Swnj } 2186571Smckusic u.u_dent.d_namlen = i; 2197534Sroot u.u_dent.d_name[i] = 0; 22016658Smckusick isdotdot = (i == 2 && 22116658Smckusick u.u_dent.d_name[0] == '.' && u.u_dent.d_name[1] == '.'); 2227534Sroot 2237534Sroot /* 2247534Sroot * Check for degenerate name (e.g. / or "") 2257534Sroot * which is a way of talking about a directory, 2267534Sroot * e.g. like "/." or ".". 2277534Sroot */ 2287534Sroot if (u.u_dent.d_name[0] == 0) { 22915798Smckusick if (flag != LOOKUP || lockparent) { 23014937Smckusick u.u_error = EISDIR; 2317534Sroot goto bad; 2325972Swnj } 233*16681Smckusick nbp->av_forw = freenamebuf; 234*16681Smckusick freenamebuf = nbp; 2356571Smckusic return (dp); 2365972Swnj } 2377534Sroot 2386571Smckusic /* 23915798Smckusick * We now have a segment name to search for, and a directory to search. 24015798Smckusick * 24115798Smckusick * Before tediously performing a linear scan of the directory, 24215798Smckusick * check the name cache to see if the directory/name pair 24315798Smckusick * we are looking for is known already. We don't do this 24415798Smckusick * if the segment name is long, simply so the cache can avoid 24515798Smckusick * holding long names (which would either waste space, or 24615798Smckusick * add greatly to the complexity). 24715798Smckusick */ 24815798Smckusick if (u.u_dent.d_namlen > NCHNAMLEN) { 24915798Smckusick nchstats.ncs_long++; 25015798Smckusick docache = 0; 25115798Smckusick } else { 25215798Smckusick nhp = &nchash[NHASH(hash, dp->i_number, dp->i_dev)]; 25315798Smckusick for (ncp = nhp->nch_forw; ncp != (struct nch *)nhp; 25415798Smckusick ncp = ncp->nc_forw) { 25515798Smckusick if (ncp->nc_ino == dp->i_number && 25615798Smckusick ncp->nc_dev == dp->i_dev && 25715798Smckusick ncp->nc_nlen == u.u_dent.d_namlen && 25815798Smckusick !bcmp(ncp->nc_name, u.u_dent.d_name, ncp->nc_nlen)) 25915798Smckusick break; 26015798Smckusick } 26115798Smckusick 26215798Smckusick if (ncp == (struct nch *)nhp) { 26315798Smckusick nchstats.ncs_miss++; 26415798Smckusick ncp = NULL; 26515798Smckusick } else { 26616658Smckusick if (ncp->nc_id != ncp->nc_ip->i_id) { 26716643Ssam nchstats.ncs_falsehits++; 26816658Smckusick } else if (*cp == '\0' && !docache) { 26916658Smckusick nchstats.ncs_badhits++; 27016658Smckusick } else { 27115798Smckusick 27215798Smckusick /* 27315798Smckusick * move this slot to end of LRU 27415798Smckusick * chain, if not already there 27515798Smckusick */ 27615798Smckusick if (ncp->nc_nxt) { 27715798Smckusick /* remove from LRU chain */ 27815798Smckusick *ncp->nc_prev = ncp->nc_nxt; 27915798Smckusick ncp->nc_nxt->nc_prev = ncp->nc_prev; 28015798Smckusick 28115798Smckusick /* and replace at end of it */ 28215798Smckusick ncp->nc_nxt = NULL; 28315798Smckusick ncp->nc_prev = nchtail; 28415798Smckusick *nchtail = ncp; 28515798Smckusick nchtail = &ncp->nc_nxt; 28615798Smckusick } 28715798Smckusick 28816658Smckusick /* 28916658Smckusick * Get the next inode in the path. 29016666Smckusick * See comment above other `IUNLOCK' code for 29116658Smckusick * an explaination of the locking protocol. 29216658Smckusick */ 29315798Smckusick pdp = dp; 29415798Smckusick dp = ncp->nc_ip; 29515798Smckusick if (dp == NULL) 29615798Smckusick panic("nami: null cache ino"); 29716643Ssam if (pdp == dp) 29816643Ssam dp->i_count++; 29916666Smckusick else { 30016658Smckusick if (isdotdot) { 30116666Smckusick IUNLOCK(pdp); 30216658Smckusick igrab(dp); 30316658Smckusick } else { 30416658Smckusick igrab(dp); 30516666Smckusick IUNLOCK(pdp); 30616658Smckusick } 30716643Ssam } 30815798Smckusick 30916658Smckusick /* 31016658Smckusick * Verify that the inode that we got 31116658Smckusick * did not change while we were waiting 31216658Smckusick * for it to be locked. 31316658Smckusick */ 31416658Smckusick if (ncp->nc_id != ncp->nc_ip->i_id) { 31516658Smckusick iput(dp); 31616666Smckusick ILOCK(pdp); 31716658Smckusick dp = pdp; 31816658Smckusick nchstats.ncs_falsehits++; 31916658Smckusick } else { 32016658Smckusick u.u_dent.d_ino = dp->i_number; 32116658Smckusick /* u_dent.d_reclen is garbage ... */ 32216658Smckusick nchstats.ncs_goodhits++; 32316658Smckusick goto haveino; 32416658Smckusick } 32516658Smckusick } 32615798Smckusick 32715798Smckusick /* 32816643Ssam * Last component and we are renaming or deleting, 32916643Ssam * the cache entry is invalid, or otherwise don't 33016643Ssam * want cache entry to exist. 33115798Smckusick */ 33215798Smckusick 33315798Smckusick /* remove from LRU chain */ 33415798Smckusick *ncp->nc_prev = ncp->nc_nxt; 33515798Smckusick if (ncp->nc_nxt) 33615798Smckusick ncp->nc_nxt->nc_prev = ncp->nc_prev; 33715798Smckusick else 33815798Smckusick nchtail = ncp->nc_prev; 33915798Smckusick 34015798Smckusick /* remove from hash chain */ 34115798Smckusick remque(ncp); 34215798Smckusick 34315798Smckusick /* insert at head of LRU list (first to grab) */ 34415798Smckusick ncp->nc_nxt = nchhead; 34515798Smckusick ncp->nc_prev = &nchhead; 34615798Smckusick nchhead->nc_prev = &ncp->nc_nxt; 34715798Smckusick nchhead = ncp; 34815798Smckusick 34915798Smckusick /* and make a dummy hash chain */ 35015798Smckusick ncp->nc_forw = ncp; 35115798Smckusick ncp->nc_back = ncp; 35215798Smckusick 35315798Smckusick ncp = NULL; 35415798Smckusick } 35515798Smckusick } 35615798Smckusick 35715798Smckusick /* 3587534Sroot * Suppress search for slots unless creating 3597534Sroot * file and at end of pathname, in which case 3607534Sroot * we watch for a place to put the new file in 3617534Sroot * case it doesn't already exist. 3626571Smckusic */ 3637534Sroot slotstatus = FOUND; 3649166Ssam if (flag == CREATE && *cp == 0) { 3657534Sroot slotstatus = NONE; 3667534Sroot slotfreespace = 0; 3677534Sroot slotneeded = DIRSIZ(&u.u_dent); 3687534Sroot } 36915660Smckusick /* 37015660Smckusick * If this is the same directory that this process 37115660Smckusick * previously searched, pick up where we last left off. 37215798Smckusick * We cache only lookups as these are the most common 37315660Smckusick * and have the greatest payoff. Caching CREATE has little 37415660Smckusick * benefit as it usually must search the entire directory 37515660Smckusick * to determine that the entry does not exist. Caching the 37615660Smckusick * location of the last DELETE has not reduced profiling time 37715660Smckusick * and hence has been removed in the interest of simplicity. 37815660Smckusick */ 37915660Smckusick if (flag != LOOKUP || dp->i_number != u.u_ncache.nc_inumber || 38015660Smckusick dp->i_dev != u.u_ncache.nc_dev) { 38115660Smckusick u.u_offset = 0; 38215660Smckusick numdirpasses = 1; 38315660Smckusick } else { 38415798Smckusick if ((dp->i_flag & ICHG) || dp->i_ctime >= u.u_ncache.nc_time) { 38515660Smckusick u.u_ncache.nc_prevoffset &= ~(DIRBLKSIZ - 1); 38615660Smckusick u.u_ncache.nc_time = time.tv_sec; 38715660Smckusick } 38815660Smckusick u.u_offset = u.u_ncache.nc_prevoffset; 38915660Smckusick entryoffsetinblock = blkoff(fs, u.u_offset); 39015660Smckusick if (entryoffsetinblock != 0) { 39115660Smckusick bp = blkatoff(dp, u.u_offset, (char **)0); 39215660Smckusick if (bp == 0) 39315660Smckusick goto bad; 39415660Smckusick } 39515660Smckusick numdirpasses = 2; 39615798Smckusick nchstats.ncs_2passes++; 39715660Smckusick } 39815660Smckusick endsearch = roundup(dp->i_size, DIRBLKSIZ); 3997534Sroot 40015660Smckusick searchloop: 40115660Smckusick while (u.u_offset < endsearch) { 4025972Swnj /* 4035972Swnj * If offset is on a block boundary, 4045972Swnj * read the next directory block. 4055972Swnj * Release previous if it exists. 4065972Swnj */ 4076571Smckusic if (blkoff(fs, u.u_offset) == 0) { 4085972Swnj if (bp != NULL) 4095972Swnj brelse(bp); 4107605Ssam bp = blkatoff(dp, u.u_offset, (char **)0); 4117534Sroot if (bp == 0) 4127534Sroot goto bad; 4137534Sroot entryoffsetinblock = 0; 4145972Swnj } 4157534Sroot 4165972Swnj /* 4177534Sroot * If still looking for a slot, and at a DIRBLKSIZE 41816657Smckusick * boundary, have to start looking for free space again. 4196571Smckusic */ 4207534Sroot if (slotstatus == NONE && 4217534Sroot (entryoffsetinblock&(DIRBLKSIZ-1)) == 0) { 4227534Sroot slotoffset = -1; 4237534Sroot slotfreespace = 0; 4247534Sroot } 4257534Sroot 4267534Sroot /* 42716657Smckusick * Get pointer to next entry. 42816657Smckusick * Full validation checks are slow, so we only check 42916657Smckusick * enough to insure forward progress through the 43016657Smckusick * directory. Complete checks can be run by patching 43116657Smckusick * "dirchk" to be true. 4327534Sroot */ 4337534Sroot ep = (struct direct *)(bp->b_un.b_addr + entryoffsetinblock); 43416657Smckusick if (ep->d_reclen <= 0 || 43516657Smckusick dirchk && dirbadentry(ep, entryoffsetinblock)) { 4367534Sroot dirbad(dp, "mangled entry"); 43716657Smckusick i = DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1)); 4386571Smckusic u.u_offset += i; 4397534Sroot entryoffsetinblock += i; 4406571Smckusic continue; 4416571Smckusic } 4427534Sroot 4436571Smckusic /* 4447534Sroot * If an appropriate sized slot has not yet been found, 4456571Smckusic * check to see if one is available. Also accumulate space 4466571Smckusic * in the current block so that we can determine if 4476571Smckusic * compaction is viable. 4486571Smckusic */ 4497534Sroot if (slotstatus != FOUND) { 4507534Sroot int size = ep->d_reclen; 4517534Sroot 4526571Smckusic if (ep->d_ino != 0) 4536571Smckusic size -= DIRSIZ(ep); 4546571Smckusic if (size > 0) { 4557534Sroot if (size >= slotneeded) { 4567534Sroot slotstatus = FOUND; 4577534Sroot slotoffset = u.u_offset; 4587534Sroot slotsize = ep->d_reclen; 4597534Sroot } else if (slotstatus == NONE) { 4607534Sroot slotfreespace += size; 4617534Sroot if (slotoffset == -1) 4627534Sroot slotoffset = u.u_offset; 4637534Sroot if (slotfreespace >= slotneeded) { 4647534Sroot slotstatus = COMPACT; 4657534Sroot slotsize = 4667534Sroot u.u_offset+ep->d_reclen - 4677534Sroot slotoffset; 4687534Sroot } 4696571Smckusic } 4706571Smckusic } 4716571Smckusic } 4727534Sroot 4736571Smckusic /* 4747534Sroot * Check for a name match. 4755972Swnj */ 4767534Sroot if (ep->d_ino) { 4777534Sroot if (ep->d_namlen == u.u_dent.d_namlen && 4787534Sroot !bcmp(u.u_dent.d_name, ep->d_name, ep->d_namlen)) 4797534Sroot goto found; 4807534Sroot } 4817534Sroot prevoff = u.u_offset; 4826571Smckusic u.u_offset += ep->d_reclen; 4837534Sroot entryoffsetinblock += ep->d_reclen; 4847534Sroot } 48515798Smckusick /* notfound: */ 48615660Smckusick /* 48715798Smckusick * If we started in the middle of the directory and failed 48815660Smckusick * to find our target, we must check the beginning as well. 48915660Smckusick */ 49015660Smckusick if (numdirpasses == 2) { 49115660Smckusick numdirpasses--; 49215660Smckusick u.u_offset = 0; 49315660Smckusick endsearch = u.u_ncache.nc_prevoffset; 49415660Smckusick goto searchloop; 49515660Smckusick } 4967534Sroot /* 4977534Sroot * If creating, and at end of pathname and current 4989166Ssam * directory has not been removed, then can consider 4999166Ssam * allowing file to be created. 5007534Sroot */ 5019166Ssam if (flag == CREATE && *cp == 0 && dp->i_nlink != 0) { 5025972Swnj /* 5037534Sroot * Access for write is interpreted as allowing 5047534Sroot * creation of files in the directory. 5055972Swnj */ 5067534Sroot if (access(dp, IWRITE)) 5077534Sroot goto bad; 5085972Swnj /* 5097534Sroot * Return an indication of where the new directory 5107534Sroot * entry should be put. If we didn't find a slot, 5117534Sroot * then set u.u_count to 0 indicating that the 5127534Sroot * new slot belongs at the end of the directory. 5137534Sroot * If we found a slot, then the new entry can be 5147534Sroot * put in the range [u.u_offset..u.u_offset+u.u_count) 5155972Swnj */ 51615660Smckusick if (slotstatus == NONE) { 51715660Smckusick u.u_offset = roundup(dp->i_size, DIRBLKSIZ); 5187534Sroot u.u_count = 0; 51915660Smckusick } else { 5207534Sroot u.u_offset = slotoffset; 5217534Sroot u.u_count = slotsize; 5225972Swnj } 5237534Sroot dp->i_flag |= IUPD|ICHG; 5247534Sroot if (bp) 5257534Sroot brelse(bp); 526*16681Smckusick nbp->av_forw = freenamebuf; 527*16681Smckusick freenamebuf = nbp; 5285972Swnj /* 5297534Sroot * We return with the directory locked, so that 5307534Sroot * the parameters we set up above will still be 5317534Sroot * valid if we actually decide to do a direnter(). 5327534Sroot * We return NULL to indicate that the entry doesn't 5337534Sroot * currently exist, leaving a pointer to the (locked) 5347534Sroot * directory inode in u.u_pdir. 5355972Swnj */ 5367534Sroot u.u_pdir = dp; 5377534Sroot return (NULL); 5387534Sroot } 5397534Sroot u.u_error = ENOENT; 5407534Sroot goto bad; 5417534Sroot found: 54215798Smckusick if (numdirpasses == 2) 54315798Smckusick nchstats.ncs_pass2++; 5447534Sroot /* 5457534Sroot * Check that directory length properly reflects presence 5467534Sroot * of this entry. 5477534Sroot */ 5487605Ssam if (entryoffsetinblock + DIRSIZ(ep) > dp->i_size) { 5497534Sroot dirbad(dp, "i_size too small"); 5507605Ssam dp->i_size = entryoffsetinblock + DIRSIZ(ep); 5517534Sroot dp->i_flag |= IUPD|ICHG; 5527534Sroot } 5537534Sroot 5547534Sroot /* 55515660Smckusick * Found component in pathname. 55615798Smckusick * If the final component of path name, save information 55715660Smckusick * in the cache as to where the entry was found. 5587534Sroot */ 55915660Smckusick if (*cp == '\0' && flag == LOOKUP) { 56015660Smckusick u.u_ncache.nc_prevoffset = u.u_offset; 56115660Smckusick u.u_ncache.nc_inumber = dp->i_number; 56215660Smckusick u.u_ncache.nc_dev = dp->i_dev; 56315660Smckusick u.u_ncache.nc_time = time.tv_sec; 56415660Smckusick } 56515660Smckusick /* 56615660Smckusick * Save directory entry in u.u_dent, 56715660Smckusick * and release directory buffer. 56815660Smckusick */ 5697825Sroot bcopy((caddr_t)ep, (caddr_t)&u.u_dent, (u_int)DIRSIZ(ep)); 5707534Sroot brelse(bp); 5717534Sroot bp = NULL; 5727534Sroot 5737534Sroot /* 5747534Sroot * If deleting, and at end of pathname, return 5757534Sroot * parameters which can be used to remove file. 5769166Ssam * If the lockparent flag isn't set, we return only 5779166Ssam * the directory (in u.u_pdir), otherwise we go 5789166Ssam * on and lock the inode, being careful with ".". 5797534Sroot */ 5809166Ssam if (flag == DELETE && *cp == 0) { 5817534Sroot /* 5827534Sroot * Write access to directory required to delete files. 5837534Sroot */ 5847534Sroot if (access(dp, IWRITE)) 5857534Sroot goto bad; 5869166Ssam u.u_pdir = dp; /* for dirremove() */ 5877534Sroot /* 5887534Sroot * Return pointer to current entry in u.u_offset, 5897534Sroot * and distance past previous entry (if there 5907534Sroot * is a previous entry in this block) in u.u_count. 5917534Sroot * Save directory inode pointer in u.u_pdir for dirremove(). 5927534Sroot */ 5937534Sroot if ((u.u_offset&(DIRBLKSIZ-1)) == 0) 5947534Sroot u.u_count = 0; 5957534Sroot else 5967534Sroot u.u_count = u.u_offset - prevoff; 5979166Ssam if (lockparent) { 5989166Ssam if (dp->i_number == u.u_dent.d_ino) 5999166Ssam dp->i_count++; 6009166Ssam else { 6019166Ssam dp = iget(dp->i_dev, fs, u.u_dent.d_ino); 6029166Ssam if (dp == NULL) { 6039166Ssam iput(u.u_pdir); 6049166Ssam goto bad; 6059166Ssam } 60615798Smckusick /* 60716046Skarels * If directory is "sticky", then user must own 60815798Smckusick * the directory, or the file in it, else he 60915798Smckusick * may not delete it (unless he's root). This 61015798Smckusick * implements append-only directories. 61115798Smckusick */ 61216046Skarels if ((u.u_pdir->i_mode & ISVTX) && 61315798Smckusick u.u_uid != 0 && 61415798Smckusick u.u_uid != u.u_pdir->i_uid && 61515798Smckusick dp->i_uid != u.u_uid) { 61615798Smckusick iput(u.u_pdir); 61715798Smckusick u.u_error = EPERM; 61815798Smckusick goto bad; 61915798Smckusick } 6209166Ssam } 6219166Ssam } 622*16681Smckusick nbp->av_forw = freenamebuf; 623*16681Smckusick freenamebuf = nbp; 6247534Sroot return (dp); 6257534Sroot } 6267534Sroot 6277534Sroot /* 6287534Sroot * Special handling for ".." allowing chdir out of mounted 6297534Sroot * file system: indirect .. in root inode to reevaluate 6307534Sroot * in directory file system was mounted on. 6317534Sroot */ 63216658Smckusick if (isdotdot) { 6337534Sroot if (dp == u.u_rdir) 6347534Sroot u.u_dent.d_ino = dp->i_number; 6357534Sroot else if (u.u_dent.d_ino == ROOTINO && 6367534Sroot dp->i_number == ROOTINO) { 6377534Sroot for (i = 1; i < NMOUNT; i++) 6387534Sroot if (mount[i].m_bufp != NULL && 6397534Sroot mount[i].m_dev == dp->i_dev) { 6406571Smckusic iput(dp); 6417534Sroot dp = mount[i].m_inodp; 64216666Smckusick ILOCK(dp); 6435972Swnj dp->i_count++; 6447534Sroot fs = dp->i_fs; 6457534Sroot cp -= 2; /* back over .. */ 6467534Sroot goto dirloop2; 6475972Swnj } 64830Sbill } 6497534Sroot } 6507534Sroot 6517534Sroot /* 6529166Ssam * If rewriting (rename), return the inode and the 6539166Ssam * information required to rewrite the present directory 6549166Ssam * Must get inode of directory entry to verify it's a 6559166Ssam * regular file, or empty directory. 6569166Ssam */ 6579166Ssam if ((flag == CREATE && lockparent) && *cp == 0) { 6589166Ssam if (access(dp, IWRITE)) 6599166Ssam goto bad; 6609166Ssam u.u_pdir = dp; /* for dirrewrite() */ 6619166Ssam /* 6629166Ssam * Careful about locking second inode. 6639166Ssam * This can only occur if the target is ".". 6649166Ssam */ 6659166Ssam if (dp->i_number == u.u_dent.d_ino) { 6669166Ssam u.u_error = EISDIR; /* XXX */ 6679166Ssam goto bad; 6689166Ssam } 6699166Ssam dp = iget(dp->i_dev, fs, u.u_dent.d_ino); 6709166Ssam if (dp == NULL) { 6719166Ssam iput(u.u_pdir); 6729166Ssam goto bad; 6739166Ssam } 674*16681Smckusick nbp->av_forw = freenamebuf; 675*16681Smckusick freenamebuf = nbp; 6769166Ssam return (dp); 6779166Ssam } 6789166Ssam 6799166Ssam /* 68012011Smckusick * Check for symbolic link, which may require us to massage the 68112011Smckusick * name before we continue translation. We do not `iput' the 68212011Smckusick * directory because we may need it again if the symbolic link 68312011Smckusick * is relative to the current directory. Instead we save it 68412011Smckusick * unlocked as "pdp". We must get the target inode before unlocking 68512011Smckusick * the directory to insure that the inode will not be removed 68612011Smckusick * before we get it. We prevent deadlock by always fetching 68712011Smckusick * inodes from the root, moving down the directory tree. Thus 68812011Smckusick * when following backward pointers ".." we must unlock the 68912011Smckusick * parent directory before getting the requested directory. 69012011Smckusick * There is a potential race condition here if both the current 69112011Smckusick * and parent directories are removed before the `iget' for the 69212011Smckusick * inode associated with ".." returns. We hope that this occurs 69312011Smckusick * infrequently since we cannot avoid this race condition without 69412492Ssam * implementing a sophisticated deadlock detection algorithm. 69512011Smckusick * Note also that this simple deadlock detection scheme will not 69612011Smckusick * work if the file system has any hard links other than ".." 69712011Smckusick * that point backwards in the directory structure. 6987534Sroot */ 6997534Sroot pdp = dp; 70015798Smckusick if (isdotdot) { 70116666Smckusick IUNLOCK(pdp); /* race to get the inode */ 70212011Smckusick dp = iget(dp->i_dev, fs, u.u_dent.d_ino); 70312011Smckusick if (dp == NULL) 70412011Smckusick goto bad2; 70512011Smckusick } else if (dp->i_number == u.u_dent.d_ino) { 70612011Smckusick dp->i_count++; /* we want ourself, ie "." */ 70712011Smckusick } else { 70812011Smckusick dp = iget(dp->i_dev, fs, u.u_dent.d_ino); 70916666Smckusick IUNLOCK(pdp); 71012011Smckusick if (dp == NULL) 71112011Smckusick goto bad2; 71212011Smckusick } 71315798Smckusick 71415798Smckusick /* 71515798Smckusick * insert name into cache (if we want it, and it isn't "." or "..") 71615798Smckusick * 71715798Smckusick * all other cases where making a cache entry would be wrong 71815798Smckusick * have already departed from the code sequence somewhere above. 71915798Smckusick */ 72016643Ssam if (docache) { 72115798Smckusick if (ncp != NULL) 72215798Smckusick panic("nami: duplicating cache"); 72315798Smckusick 72415798Smckusick /* 72515798Smckusick * free the cache slot at head of lru chain 72615798Smckusick */ 72715798Smckusick if (ncp = nchhead) { 72815798Smckusick /* remove from lru chain */ 72915798Smckusick *ncp->nc_prev = ncp->nc_nxt; 73015798Smckusick if (ncp->nc_nxt) 73115798Smckusick ncp->nc_nxt->nc_prev = ncp->nc_prev; 73215798Smckusick else 73315798Smckusick nchtail = ncp->nc_prev; 73415798Smckusick 73515798Smckusick /* remove from old hash chain */ 73615798Smckusick remque(ncp); 73715798Smckusick 73815798Smckusick /* grab the inode we just found */ 73915798Smckusick ncp->nc_ip = dp; 74015798Smckusick 74115798Smckusick /* fill in cache info */ 74215798Smckusick ncp->nc_ino = pdp->i_number; /* parents inum */ 74315798Smckusick ncp->nc_dev = pdp->i_dev; /* & device */ 74415798Smckusick ncp->nc_idev = dp->i_dev; /* our device */ 74516643Ssam ncp->nc_id = dp->i_id; /* identifier */ 74615798Smckusick ncp->nc_nlen = u.u_dent.d_namlen; 74715798Smckusick bcopy(u.u_dent.d_name, ncp->nc_name, ncp->nc_nlen); 74815798Smckusick 74915798Smckusick /* link at end of lru chain */ 75015798Smckusick ncp->nc_nxt = NULL; 75115798Smckusick ncp->nc_prev = nchtail; 75215798Smckusick *nchtail = ncp; 75315798Smckusick nchtail = &ncp->nc_nxt; 75415798Smckusick 75515798Smckusick /* and insert on hash chain */ 75615798Smckusick insque(ncp, nhp); 75715798Smckusick } 75815798Smckusick } 75915798Smckusick 76015798Smckusick haveino: 7617534Sroot fs = dp->i_fs; 7627534Sroot 7637534Sroot /* 7647534Sroot * Check for symbolic link 7657534Sroot */ 7667534Sroot if ((dp->i_mode & IFMT) == IFLNK && (follow || *cp == '/')) { 7677825Sroot u_int pathlen = strlen(cp) + 1; 7687534Sroot 7697534Sroot if (dp->i_size + pathlen >= MAXPATHLEN - 1 || 7707534Sroot ++nlink > MAXSYMLINKS) { 7717534Sroot u.u_error = ELOOP; 7727534Sroot goto bad2; 7737534Sroot } 7748957Sroot ovbcopy(cp, nbp->b_un.b_addr + dp->i_size, pathlen); 7757751Sroot u.u_error = 7769166Ssam rdwri(UIO_READ, dp, nbp->b_un.b_addr, (int)dp->i_size, 7777825Sroot 0, 1, (int *)0); 7787534Sroot if (u.u_error) 7797534Sroot goto bad2; 7807534Sroot cp = nbp->b_un.b_addr; 7817534Sroot iput(dp); 7825972Swnj if (*cp == '/') { 7837534Sroot irele(pdp); 7845972Swnj while (*cp == '/') 7855972Swnj cp++; 7867534Sroot if ((dp = u.u_rdir) == NULL) 7877534Sroot dp = rootdir; 78816666Smckusick ILOCK(dp); 7897534Sroot dp->i_count++; 7907534Sroot } else { 7917534Sroot dp = pdp; 79216666Smckusick ILOCK(dp); 7935972Swnj } 7947534Sroot fs = dp->i_fs; 7957534Sroot goto dirloop; 79630Sbill } 7977534Sroot 79830Sbill /* 7997534Sroot * Not a symbolic link. If more pathname, 8007534Sroot * continue at next component, else return. 80130Sbill */ 8027534Sroot if (*cp == '/') { 8037534Sroot while (*cp == '/') 8047534Sroot cp++; 8059166Ssam irele(pdp); 8067534Sroot goto dirloop; 80730Sbill } 808*16681Smckusick nbp->av_forw = freenamebuf; 809*16681Smckusick freenamebuf = nbp; 8109166Ssam if (lockparent) 8119166Ssam u.u_pdir = pdp; 8129166Ssam else 8139166Ssam irele(pdp); 8147534Sroot return (dp); 8157534Sroot bad2: 8167534Sroot irele(pdp); 8177534Sroot bad: 8187534Sroot if (bp) 8197534Sroot brelse(bp); 8207534Sroot if (dp) 8217534Sroot iput(dp); 822*16681Smckusick nbp->av_forw = freenamebuf; 823*16681Smckusick freenamebuf = nbp; 8246571Smckusic return (NULL); 82530Sbill } 82630Sbill 82715798Smckusick 8287534Sroot dirbad(ip, how) 8297534Sroot struct inode *ip; 8307534Sroot char *how; 8317534Sroot { 8327534Sroot 8337534Sroot printf("%s: bad dir ino %d at offset %d: %s\n", 8347534Sroot ip->i_fs->fs_fsmnt, ip->i_number, u.u_offset, how); 8357534Sroot } 8367534Sroot 83716657Smckusick /* 83816657Smckusick * Do consistency checking on a directory entry: 83916657Smckusick * record length must be multiple of 4 84016657Smckusick * record length must not be non-negative 84116657Smckusick * entry must fit in rest of its DIRBLKSIZ block 84216657Smckusick * record must be large enough to contain entry 84316657Smckusick * name is not longer than MAXNAMLEN 84416657Smckusick * name must be as long as advertised, and null terminated 84516657Smckusick */ 84616657Smckusick dirbadentry(ep, entryoffsetinblock) 8477534Sroot register struct direct *ep; 84816657Smckusick int entryoffsetinblock; 8497534Sroot { 8507534Sroot register int i; 8517534Sroot 85216657Smckusick if ((ep->d_reclen & 0x3) != 0 || ep->d_reclen <= 0 || 85316657Smckusick ep->d_reclen > DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1)) || 85416657Smckusick ep->d_reclen < DIRSIZ(ep) || ep->d_namlen > MAXNAMLEN) 85516657Smckusick return (1); 8567534Sroot for (i = 0; i < ep->d_namlen; i++) 8577534Sroot if (ep->d_name[i] == 0) 8587534Sroot return (1); 8597534Sroot return (ep->d_name[i]); 8607534Sroot } 8617534Sroot 86230Sbill /* 8637534Sroot * Write a directory entry after a call to namei, using the parameters 8647534Sroot * which it left in the u. area. The argument ip is the inode which 8657534Sroot * the new directory entry will refer to. The u. area field u.u_pdir is 8667534Sroot * a pointer to the directory to be written, which was left locked by 8677534Sroot * namei. Remaining parameters (u.u_offset, u.u_count) indicate 8687534Sroot * how the space for the new entry is to be gotten. 8697534Sroot */ 8707534Sroot direnter(ip) 8717534Sroot struct inode *ip; 8725972Swnj { 8737534Sroot register struct direct *ep, *nep; 8747534Sroot struct buf *bp; 87511639Ssam int loc, spacefree, error = 0; 8768631Sroot u_int dsize; 8778631Sroot int newentrysize; 8787534Sroot char *dirbuf; 8795972Swnj 8807534Sroot u.u_dent.d_ino = ip->i_number; 8817534Sroot u.u_segflg = 1; 8827534Sroot newentrysize = DIRSIZ(&u.u_dent); 8837534Sroot if (u.u_count == 0) { 8847534Sroot /* 8857534Sroot * If u.u_count is 0, then namei could find no space in the 8867534Sroot * directory. In this case u.u_offset will be on a directory 8877534Sroot * block boundary and we will write the new entry into a fresh 8887534Sroot * block. 8897534Sroot */ 8907534Sroot if (u.u_offset&(DIRBLKSIZ-1)) 8917534Sroot panic("wdir: newblk"); 8927534Sroot u.u_dent.d_reclen = DIRBLKSIZ; 89310849Ssam error = rdwri(UIO_WRITE, u.u_pdir, (caddr_t)&u.u_dent, 8948631Sroot newentrysize, u.u_offset, 1, (int *)0); 8957534Sroot iput(u.u_pdir); 89610849Ssam return (error); 8977534Sroot } 8987534Sroot 8997534Sroot /* 9007534Sroot * If u.u_count is non-zero, then namei found space for the 9017534Sroot * new entry in the range u.u_offset to u.u_offset+u.u_count. 9027534Sroot * in the directory. To use this space, we may have to compact 9037534Sroot * the entries located there, by copying them together towards 9047534Sroot * the beginning of the block, leaving the free space in 9057534Sroot * one usable chunk at the end. 9067534Sroot */ 9077534Sroot 9087534Sroot /* 9097534Sroot * Increase size of directory if entry eats into new space. 9107534Sroot * This should never push the size past a new multiple of 9117534Sroot * DIRBLKSIZE. 9127534Sroot */ 9139166Ssam if (u.u_offset + u.u_count > u.u_pdir->i_size) 9147534Sroot u.u_pdir->i_size = u.u_offset + u.u_count; 9157534Sroot 9167534Sroot /* 9177534Sroot * Get the block containing the space for the new directory 91810849Ssam * entry. Should return error by result instead of u.u_error. 9197534Sroot */ 9207605Ssam bp = blkatoff(u.u_pdir, u.u_offset, (char **)&dirbuf); 9219166Ssam if (bp == 0) { 9229166Ssam iput(u.u_pdir); 92310849Ssam return (u.u_error); 9249166Ssam } 9257534Sroot 9267534Sroot /* 9277534Sroot * Find space for the new entry. In the simple case, the 9287534Sroot * entry at offset base will have the space. If it does 9297534Sroot * not, then namei arranged that compacting the region 9307534Sroot * u.u_offset to u.u_offset+u.u_count would yield the space. 9317534Sroot */ 9327534Sroot ep = (struct direct *)dirbuf; 9337534Sroot dsize = DIRSIZ(ep); 93411639Ssam spacefree = ep->d_reclen - dsize; 9357534Sroot for (loc = ep->d_reclen; loc < u.u_count; ) { 9367534Sroot nep = (struct direct *)(dirbuf + loc); 9377534Sroot if (ep->d_ino) { 9387534Sroot /* trim the existing slot */ 9397534Sroot ep->d_reclen = dsize; 9407534Sroot ep = (struct direct *)((char *)ep + dsize); 9417534Sroot } else { 9427534Sroot /* overwrite; nothing there; header is ours */ 94311639Ssam spacefree += dsize; 9447534Sroot } 9457534Sroot dsize = DIRSIZ(nep); 94611639Ssam spacefree += nep->d_reclen - dsize; 9477534Sroot loc += nep->d_reclen; 9487825Sroot bcopy((caddr_t)nep, (caddr_t)ep, dsize); 9497534Sroot } 9507534Sroot /* 9517534Sroot * Update the pointer fields in the previous entry (if any), 9527534Sroot * copy in the new entry, and write out the block. 9537534Sroot */ 9547534Sroot if (ep->d_ino == 0) { 95511639Ssam if (spacefree + dsize < newentrysize) 9567534Sroot panic("wdir: compact1"); 95711639Ssam u.u_dent.d_reclen = spacefree + dsize; 9587534Sroot } else { 95911639Ssam if (spacefree < newentrysize) 9607534Sroot panic("wdir: compact2"); 96111639Ssam u.u_dent.d_reclen = spacefree; 9627534Sroot ep->d_reclen = dsize; 9637534Sroot ep = (struct direct *)((char *)ep + dsize); 9647534Sroot } 9658672Sroot bcopy((caddr_t)&u.u_dent, (caddr_t)ep, (u_int)newentrysize); 9667534Sroot bwrite(bp); 9677534Sroot u.u_pdir->i_flag |= IUPD|ICHG; 9687534Sroot iput(u.u_pdir); 96910849Ssam return (error); 9705972Swnj } 9716571Smckusic 9729166Ssam /* 9739166Ssam * Remove a directory entry after a call to namei, using the 9749166Ssam * parameters which it left in the u. area. The u. entry 9759166Ssam * u_offset contains the offset into the directory of the 9769166Ssam * entry to be eliminated. The u_count field contains the 9779166Ssam * size of the previous record in the directory. If this 9789166Ssam * is 0, the first entry is being deleted, so we need only 9799166Ssam * zero the inode number to mark the entry as free. If the 9809166Ssam * entry isn't the first in the directory, we must reclaim 9819166Ssam * the space of the now empty record by adding the record size 9829166Ssam * to the size of the previous entry. 9839166Ssam */ 9847534Sroot dirremove() 9856571Smckusic { 9867534Sroot register struct inode *dp = u.u_pdir; 9877534Sroot register struct buf *bp; 9887534Sroot struct direct *ep; 9896571Smckusic 9909269Ssam if (u.u_count == 0) { 9917534Sroot /* 9927534Sroot * First entry in block: set d_ino to zero. 9937534Sroot */ 9949269Ssam u.u_dent.d_ino = 0; 9958619Sroot (void) rdwri(UIO_WRITE, dp, (caddr_t)&u.u_dent, 9968631Sroot (int)DIRSIZ(&u.u_dent), u.u_offset, 1, (int *)0); 9979269Ssam } else { 9987534Sroot /* 9997534Sroot * Collapse new free space into previous entry. 10007534Sroot */ 10017825Sroot bp = blkatoff(dp, (int)(u.u_offset - u.u_count), (char **)&ep); 10027534Sroot if (bp == 0) 10037534Sroot return (0); 10047534Sroot ep->d_reclen += u.u_dent.d_reclen; 10057534Sroot bwrite(bp); 10067534Sroot dp->i_flag |= IUPD|ICHG; 10077534Sroot } 10087534Sroot return (1); 10096571Smckusic } 10107534Sroot 10117605Ssam /* 10129166Ssam * Rewrite an existing directory entry to point at the inode 10139166Ssam * supplied. The parameters describing the directory entry are 10149166Ssam * set up by a call to namei. 10159166Ssam */ 10169166Ssam dirrewrite(dp, ip) 10179166Ssam struct inode *dp, *ip; 10189166Ssam { 10199166Ssam 10209166Ssam u.u_dent.d_ino = ip->i_number; 10219166Ssam u.u_error = rdwri(UIO_WRITE, dp, (caddr_t)&u.u_dent, 10229166Ssam (int)DIRSIZ(&u.u_dent), u.u_offset, 1, (int *)0); 10239166Ssam iput(dp); 10249166Ssam } 10259166Ssam 10269166Ssam /* 10277605Ssam * Return buffer with contents of block "offset" 10287605Ssam * from the beginning of directory "ip". If "res" 10297605Ssam * is non-zero, fill it in with a pointer to the 10307605Ssam * remaining space in the directory. 10317605Ssam */ 10327534Sroot struct buf * 10337605Ssam blkatoff(ip, offset, res) 10347534Sroot struct inode *ip; 10357534Sroot off_t offset; 10367534Sroot char **res; 10377534Sroot { 10387534Sroot register struct fs *fs = ip->i_fs; 10398672Sroot daddr_t lbn = lblkno(fs, offset); 10407534Sroot int base = blkoff(fs, offset); 10417534Sroot int bsize = blksize(fs, ip, lbn); 10428672Sroot daddr_t bn = fsbtodb(fs, bmap(ip, lbn, B_WRITE, base, bsize)); 10437534Sroot register struct buf *bp; 10447534Sroot 10457534Sroot if (u.u_error) 10467534Sroot return (0); 10477534Sroot bp = bread(ip->i_dev, bn, bsize); 10487534Sroot if (bp->b_flags & B_ERROR) { 10497534Sroot brelse(bp); 10507534Sroot return (0); 10517534Sroot } 10527534Sroot if (res) 10537534Sroot *res = bp->b_un.b_addr + base; 10547534Sroot return (bp); 10557534Sroot } 10569166Ssam 10579166Ssam /* 10589166Ssam * Check if a directory is empty or not. 10599166Ssam * Inode supplied must be locked. 106012817Ssam * 106112817Ssam * Using a struct dirtemplate here is not precisely 106212817Ssam * what we want, but better than using a struct direct. 106312817Ssam * 106412817Ssam * NB: does not handle corrupted directories. 10659166Ssam */ 10669166Ssam dirempty(ip) 10679863Ssam register struct inode *ip; 10689166Ssam { 10699166Ssam register off_t off; 107012817Ssam struct dirtemplate dbuf; 107112817Ssam register struct direct *dp = (struct direct *)&dbuf; 10729863Ssam int error, count; 107312817Ssam #define MINDIRSIZ (sizeof (struct dirtemplate) / 2) 10749166Ssam 10759166Ssam for (off = 0; off < ip->i_size; off += dp->d_reclen) { 107612817Ssam error = rdwri(UIO_READ, ip, (caddr_t)dp, MINDIRSIZ, 107712817Ssam off, 1, &count); 107812817Ssam /* 107912817Ssam * Since we read MINDIRSIZ, residual must 108012817Ssam * be 0 unless we're at end of file. 108112817Ssam */ 108212817Ssam if (error || count != 0) 10839166Ssam return (0); 108412817Ssam /* skip empty entries */ 10859166Ssam if (dp->d_ino == 0) 10869166Ssam continue; 108712817Ssam /* accept only "." and ".." */ 108812817Ssam if (dp->d_namlen > 2) 108912817Ssam return (0); 10909166Ssam if (dp->d_name[0] != '.') 10919166Ssam return (0); 109212817Ssam /* 109312817Ssam * At this point d_namlen must be 1 or 2. 109412817Ssam * 1 implies ".", 2 implies ".." if second 109512817Ssam * char is also "." 109612817Ssam */ 109712817Ssam if (dp->d_namlen == 1 || dp->d_name[1] == '.') 10989166Ssam continue; 10999166Ssam return (0); 11009166Ssam } 11019166Ssam return (1); 11029166Ssam } 110312815Smckusick 110412815Smckusick /* 110512815Smckusick * Check if source directory is in the path of the target directory. 110612815Smckusick * Target is supplied locked, source is unlocked. 110712815Smckusick * The target is always iput() before returning. 110812815Smckusick */ 110912815Smckusick checkpath(source, target) 111012815Smckusick struct inode *source, *target; 111112815Smckusick { 111212815Smckusick struct dirtemplate dirbuf; 111312815Smckusick register struct inode *ip; 111412815Smckusick int error = 0; 111512815Smckusick 111612815Smckusick ip = target; 111712815Smckusick if (ip->i_number == source->i_number) { 111812815Smckusick error = EEXIST; 111912815Smckusick goto out; 112012815Smckusick } 112112815Smckusick if (ip->i_number == ROOTINO) 112212815Smckusick goto out; 112312815Smckusick 112412815Smckusick for (;;) { 112512815Smckusick if ((ip->i_mode&IFMT) != IFDIR) { 112612815Smckusick error = ENOTDIR; 112712815Smckusick break; 112812815Smckusick } 112912815Smckusick error = rdwri(UIO_READ, ip, (caddr_t)&dirbuf, 113012815Smckusick sizeof (struct dirtemplate), (off_t)0, 1, (int *)0); 113112815Smckusick if (error != 0) 113212815Smckusick break; 113312815Smckusick if (dirbuf.dotdot_namlen != 2 || 113416658Smckusick dirbuf.dotdot_name[0] != '.' || 113516658Smckusick dirbuf.dotdot_name[1] != '.') { 113612815Smckusick error = ENOTDIR; 113712815Smckusick break; 113812815Smckusick } 113912815Smckusick if (dirbuf.dotdot_ino == source->i_number) { 114012815Smckusick error = EINVAL; 114112815Smckusick break; 114212815Smckusick } 114312815Smckusick if (dirbuf.dotdot_ino == ROOTINO) 114412815Smckusick break; 114512815Smckusick iput(ip); 114612815Smckusick ip = iget(ip->i_dev, ip->i_fs, dirbuf.dotdot_ino); 114712815Smckusick if (ip == NULL) { 114812815Smckusick error = u.u_error; 114912815Smckusick break; 115012815Smckusick } 115112815Smckusick } 115212815Smckusick 115312815Smckusick out: 115412815Smckusick if (error == ENOTDIR) 115512815Smckusick printf("checkpath: .. not a directory\n"); 115612815Smckusick if (ip != NULL) 115712815Smckusick iput(ip); 115812815Smckusick return (error); 115912815Smckusick } 116015798Smckusick 116115798Smckusick /* 116215798Smckusick * Name cache initialization, from main() when we are booting 116315798Smckusick */ 116415798Smckusick nchinit() 116515798Smckusick { 116615798Smckusick register union nchash *nchp; 116715798Smckusick register struct nch *ncp; 116815798Smckusick 116915798Smckusick nchhead = 0; 117015798Smckusick nchtail = &nchhead; 117115798Smckusick 117215798Smckusick for (ncp = nch; ncp < &nch[nchsize]; ncp++) { 117315798Smckusick ncp->nc_forw = ncp; /* hash chain */ 117415798Smckusick ncp->nc_back = ncp; 117515798Smckusick 117615798Smckusick ncp->nc_nxt = NULL; /* lru chain */ 117715798Smckusick *nchtail = ncp; 117815798Smckusick ncp->nc_prev = nchtail; 117915798Smckusick nchtail = &ncp->nc_nxt; 118015798Smckusick 118115798Smckusick /* all else is zero already */ 118215798Smckusick } 118315798Smckusick 118415798Smckusick for (nchp = nchash; nchp < &nchash[NCHHASH]; nchp++) { 118515798Smckusick nchp->nch_head[0] = nchp; 118615798Smckusick nchp->nch_head[1] = nchp; 118715798Smckusick } 118815798Smckusick } 118915798Smckusick 119015798Smckusick /* 119115798Smckusick * Cache flush, called when filesys is umounted to 119215798Smckusick * remove entries that would now be invalid 119315798Smckusick * 119415798Smckusick * The line "nxtcp = nchhead" near the end is to avoid potential problems 119515798Smckusick * if the cache lru chain is modified while we are dumping the 119615798Smckusick * inode. This makes the algorithm O(n^2), but do you think I care? 119715798Smckusick */ 119815798Smckusick nchinval(dev) 119915798Smckusick register dev_t dev; 120015798Smckusick { 120115798Smckusick register struct nch *ncp, *nxtcp; 120215798Smckusick 120315798Smckusick for (ncp = nchhead; ncp; ncp = nxtcp) { 120415798Smckusick nxtcp = ncp->nc_nxt; 120515798Smckusick 120615798Smckusick if (ncp->nc_ip == NULL || 120715798Smckusick (ncp->nc_idev != dev && ncp->nc_dev != dev)) 120815798Smckusick continue; 120915798Smckusick 121016658Smckusick /* free the resources we had */ 121115798Smckusick ncp->nc_idev = NODEV; 121215798Smckusick ncp->nc_dev = NODEV; 121316658Smckusick ncp->nc_id = NULL; 121415798Smckusick ncp->nc_ino = 0; 121516658Smckusick ncp->nc_ip = NULL; 121615798Smckusick 121716658Smckusick 121815798Smckusick /* remove the entry from its hash chain */ 121915798Smckusick remque(ncp); 122015798Smckusick /* and make a dummy one */ 122115798Smckusick ncp->nc_forw = ncp; 122215798Smckusick ncp->nc_back = ncp; 122315798Smckusick 122415798Smckusick /* delete this entry from LRU chain */ 122515798Smckusick *ncp->nc_prev = nxtcp; 122615798Smckusick if (nxtcp) 122715798Smckusick nxtcp->nc_prev = ncp->nc_prev; 122815798Smckusick else 122915798Smckusick nchtail = ncp->nc_prev; 123015798Smckusick 123115798Smckusick /* cause rescan of list, it may have altered */ 123215798Smckusick nxtcp = nchhead; 123315798Smckusick /* put the now-free entry at head of LRU */ 123415798Smckusick ncp->nc_nxt = nxtcp; 123515798Smckusick ncp->nc_prev = &nchhead; 123615798Smckusick nxtcp->nc_prev = &ncp->nc_nxt; 123715798Smckusick nchhead = ncp; 123815798Smckusick } 123915798Smckusick } 1240