1*15798Smckusick /* vfs_lookup.c 6.4 84/01/03 */ 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; 18*15798Smckusick 1930Sbill /* 20*15798Smckusick * Structures associated with name cacheing. 21*15798Smckusick */ 22*15798Smckusick #define NCHHASH 32 /* size of hash table */ 23*15798Smckusick 24*15798Smckusick #if ((NCHHASH)&((NCHHASH)-1)) != 0 25*15798Smckusick #define NHASH(h, i, d) ((unsigned)((h) + (i) + 13 * (int)(d)) % (NCHHASH)) 26*15798Smckusick #else 27*15798Smckusick #define NHASH(h, i, d) ((unsigned)((h) + (i) + 13 * (int)(d)) & ((NCHHASH)-1)) 28*15798Smckusick #endif 29*15798Smckusick 30*15798Smckusick union nchash { 31*15798Smckusick union nchash *nch_head[2]; 32*15798Smckusick struct nch *nch_chain[2]; 33*15798Smckusick } nchash[NCHHASH]; 34*15798Smckusick #define nch_forw nch_chain[0] 35*15798Smckusick #define nch_back nch_chain[1] 36*15798Smckusick 37*15798Smckusick struct nch *nchhead, **nchtail; /* LRU chain pointers */ 38*15798Smckusick 39*15798Smckusick struct nchstats { /* stats on usefulness */ 40*15798Smckusick long ncs_goodhits; /* hits that we can reall use */ 41*15798Smckusick long ncs_badhits; /* hits we must drop */ 42*15798Smckusick long ncs_miss; /* misses */ 43*15798Smckusick long ncs_long; /* long names that ignore cache */ 44*15798Smckusick long ncs_pass2; /* names found with passes == 2 */ 45*15798Smckusick long ncs_2passes; /* number of times we attempt it */ 46*15798Smckusick } nchstats; 47*15798Smckusick 48*15798Smckusick /* 497534Sroot * Convert a pathname into a pointer to a locked inode, 507534Sroot * with side effects usable in creating and removing files. 517534Sroot * This is a very central and rather complicated routine. 5230Sbill * 537534Sroot * The func argument gives the routine which returns successive 549166Ssam * characters of the name to be translated. 557534Sroot * 569166Ssam * The flag argument is (LOOKUP, CREATE, DELETE) depending on whether 579166Ssam * the name is to be (looked up, created, deleted). If flag has 589166Ssam * LOCKPARENT or'ed into it and the target of the pathname exists, 599166Ssam * namei returns both the target and its parent directory locked. 609166Ssam * If the file system is not maintained in a strict tree hierarchy, 619166Ssam * this can result in a deadlock situation. When creating and 629166Ssam * LOCKPARENT is specified, the target may not be ".". When deleting 639166Ssam * and LOCKPARENT is specified, the target may be ".", but the caller 649166Ssam * must check to insure it does an irele and iput instead of two iputs. 659166Ssam * 669166Ssam * The follow argument is 1 when symbolic links are to be followed 679166Ssam * when they occur at the end of the name translation process. 689166Ssam * 69*15798Smckusick * Name caching works as follows: 707534Sroot * 71*15798Smckusick * names found by directory scans are retained in a cache 72*15798Smckusick * for future reference. It is managed LRU, so frequently 73*15798Smckusick * used names will hang around. Cache is indexed by hash value 74*15798Smckusick * obtained from (ino,dev,name) where ino & dev refer to the 75*15798Smckusick * directory containing name. 76*15798Smckusick * 77*15798Smckusick * For simplicity (and economy of storage), names longer than 78*15798Smckusick * some (small) maximum length are not cached, they occur 79*15798Smckusick * infrequently in any case, and are almost never of interest. 80*15798Smckusick * 81*15798Smckusick * Upon reaching the last segment of a path, if the reference 82*15798Smckusick * is for DELETE, or NOCACHE is set (rewrite), and the 83*15798Smckusick * name is located in the cache, it will be dropped. 84*15798Smckusick * 85*15798Smckusick * We must be sure never to enter the name ".." into the cache 86*15798Smckusick * because of the extremely kludgey way that rename() alters 87*15798Smckusick * ".." in a situation like 88*15798Smckusick * mv a/x b/x 89*15798Smckusick * where x is a directory, and x/.. is the ".." in question. 90*15798Smckusick * 91*15798Smckusick * Overall outline of namei: 92*15798Smckusick * 937534Sroot * copy in name 947534Sroot * get starting directory 957534Sroot * dirloop: 967534Sroot * check accessibility of directory 977534Sroot * dirloop2: 987534Sroot * copy next component of name to u.u_dent 997534Sroot * handle degenerate case where name is null string 100*15798Smckusick * look for name in cache, if found, then if at end of path 101*15798Smckusick * and deleting or creating, drop it, else to haveino 1027534Sroot * search for name in directory, to found or notfound 1037534Sroot * notfound: 1049166Ssam * if creating, return locked directory, leaving info on avail. slots 1057534Sroot * else return error 1067534Sroot * found: 1077534Sroot * if at end of path and deleting, return information to allow delete 108*15798Smckusick * if at end of path and rewriting (create and LOCKPARENT), lock target 1099166Ssam * inode and return info to allow rewrite 1107534Sroot * if .. and on mounted filesys, look in mount table for parent 111*15798Smckusick * if not at end, if neither creating nor deleting, add name to cache 112*15798Smckusick * haveino: 1137534Sroot * if symbolic link, massage name in buffer and continue at dirloop 1147534Sroot * if more components of name, do next level at dirloop 1157534Sroot * return the answer as locked inode 1169166Ssam * 1179166Ssam * NOTE: (LOOKUP | LOCKPARENT) currently returns the parent inode, 1189166Ssam * but unlocked. 11930Sbill */ 12030Sbill struct inode * 1215972Swnj namei(func, flag, follow) 1225972Swnj int (*func)(), flag, follow; 12330Sbill { 1247534Sroot register char *cp; /* pointer into pathname argument */ 1257534Sroot /* these variables refer to things which must be freed or unlocked */ 1267534Sroot register struct inode *dp = 0; /* the directory we are searching */ 127*15798Smckusick register struct nch *ncp; /* cache slot for entry */ 1287534Sroot register struct fs *fs; /* file system that directory is in */ 1297534Sroot register struct buf *bp = 0; /* a buffer of directory entries */ 1307534Sroot register struct direct *ep; /* the current directory entry */ 1317534Sroot int entryoffsetinblock; /* offset of ep in bp's buffer */ 1327534Sroot register struct buf *nbp; /* buffer storing path name argument */ 1337534Sroot /* these variables hold information about the search for a slot */ 1347534Sroot enum {NONE, COMPACT, FOUND} slotstatus; 1357534Sroot int slotoffset = -1; /* offset of area with free space */ 1367534Sroot int slotsize; /* size of area at slotoffset */ 1377534Sroot int slotfreespace; /* amount of space free in slot */ 1387534Sroot int slotneeded; /* size of the entry we're seeking */ 1397534Sroot /* */ 14015660Smckusick int numdirpasses; /* strategy for directory search */ 14115660Smckusick int endsearch; /* offset to end directory search */ 1427534Sroot int prevoff; /* u.u_offset of previous entry */ 1437534Sroot int nlink = 0; /* number of symbolic links taken */ 1447534Sroot struct inode *pdp; /* saved dp during symlink work */ 1457534Sroot int i; 1469166Ssam int lockparent; 147*15798Smckusick int docache; 148*15798Smckusick unsigned hash; /* value of name hash for entry */ 149*15798Smckusick union nchash *nhp; /* cache chain head for entry */ 150*15798Smckusick int isdotdot; /* != 0 if current name is ".." */ 15130Sbill 1529166Ssam lockparent = flag & LOCKPARENT; 153*15798Smckusick docache = (flag & NOCACHE) ^ NOCACHE; 154*15798Smckusick flag &= ~(LOCKPARENT|NOCACHE); 155*15798Smckusick if (flag == DELETE) 156*15798Smckusick docache = 0; 15730Sbill /* 1587534Sroot * Get a buffer for the name to be translated, and copy the 1597534Sroot * name into the buffer. 1605972Swnj */ 1616571Smckusic nbp = geteblk(MAXPATHLEN); 1627534Sroot for (cp = nbp->b_un.b_addr; *cp = (*func)(); ) { 163*15798Smckusick if ((*cp&0377) == ('/'|0200) || (*cp&0200) && flag != LOOKUP) { 1646066Sroot u.u_error = EPERM; 1657534Sroot goto bad; 1666066Sroot } 1676066Sroot cp++; 1686571Smckusic if (cp >= nbp->b_un.b_addr + MAXPATHLEN) { 1695972Swnj u.u_error = ENOENT; 1707534Sroot goto bad; 1715972Swnj } 1725972Swnj } 1737534Sroot if (u.u_error) 1747534Sroot goto bad; 1757534Sroot 1765972Swnj /* 1777534Sroot * Get starting directory. 17830Sbill */ 1797534Sroot cp = nbp->b_un.b_addr; 1805972Swnj if (*cp == '/') { 1815972Swnj while (*cp == '/') 1825972Swnj cp++; 18330Sbill if ((dp = u.u_rdir) == NULL) 18430Sbill dp = rootdir; 1857534Sroot } else 1867534Sroot dp = u.u_cdir; 1877534Sroot fs = dp->i_fs; 1885972Swnj ilock(dp); 1895972Swnj dp->i_count++; 1907534Sroot u.u_pdir = (struct inode *)0xc0000000; /* illegal */ 1917534Sroot 1927534Sroot /* 1937534Sroot * We come to dirloop to search a new directory. 1947534Sroot * The directory must be locked so that it can be 1957534Sroot * iput, and fs must be already set to dp->i_fs. 1967534Sroot */ 1976571Smckusic dirloop: 19830Sbill /* 1997534Sroot * Check accessiblity of directory. 20030Sbill */ 2017534Sroot if ((dp->i_mode&IFMT) != IFDIR) { 20230Sbill u.u_error = ENOTDIR; 2037534Sroot goto bad; 2047534Sroot } 2057534Sroot if (access(dp, IEXEC)) 2067534Sroot goto bad; 2077534Sroot 2086384Swnj dirloop2: 2097534Sroot /* 2107534Sroot * Copy next component of name to u.u_dent. 2117534Sroot */ 212*15798Smckusick hash = 0; 2137534Sroot for (i = 0; *cp != 0 && *cp != '/'; cp++) { 2146571Smckusic if (i >= MAXNAMLEN) { 2155972Swnj u.u_error = ENOENT; 2167534Sroot goto bad; 2175972Swnj } 2187534Sroot u.u_dent.d_name[i++] = *cp; 219*15798Smckusick hash += (unsigned char)*cp * i; 2205972Swnj } 2216571Smckusic u.u_dent.d_namlen = i; 2227534Sroot u.u_dent.d_name[i] = 0; 2237534Sroot 2247534Sroot /* 2257534Sroot * Check for degenerate name (e.g. / or "") 2267534Sroot * which is a way of talking about a directory, 2277534Sroot * e.g. like "/." or ".". 2287534Sroot */ 2297534Sroot if (u.u_dent.d_name[0] == 0) { 230*15798Smckusick if (flag != LOOKUP || lockparent) { 23114937Smckusick u.u_error = EISDIR; 2327534Sroot goto bad; 2335972Swnj } 2346571Smckusic brelse(nbp); 2356571Smckusic return (dp); 2365972Swnj } 2377534Sroot 2386571Smckusic /* 239*15798Smckusick * We now have a segment name to search for, and a directory to search. 240*15798Smckusick * 241*15798Smckusick * Before tediously performing a linear scan of the directory, 242*15798Smckusick * check the name cache to see if the directory/name pair 243*15798Smckusick * we are looking for is known already. We don't do this 244*15798Smckusick * if the segment name is long, simply so the cache can avoid 245*15798Smckusick * holding long names (which would either waste space, or 246*15798Smckusick * add greatly to the complexity). 247*15798Smckusick */ 248*15798Smckusick if (u.u_dent.d_namlen > NCHNAMLEN) { 249*15798Smckusick nchstats.ncs_long++; 250*15798Smckusick docache = 0; 251*15798Smckusick } else { 252*15798Smckusick nhp = &nchash[NHASH(hash, dp->i_number, dp->i_dev)]; 253*15798Smckusick for (ncp = nhp->nch_forw; ncp != (struct nch *)nhp; 254*15798Smckusick ncp = ncp->nc_forw) { 255*15798Smckusick if (ncp->nc_ino == dp->i_number && 256*15798Smckusick ncp->nc_dev == dp->i_dev && 257*15798Smckusick ncp->nc_nlen == u.u_dent.d_namlen && 258*15798Smckusick !bcmp(ncp->nc_name, u.u_dent.d_name, ncp->nc_nlen)) 259*15798Smckusick break; 260*15798Smckusick } 261*15798Smckusick 262*15798Smckusick if (ncp == (struct nch *)nhp) { 263*15798Smckusick nchstats.ncs_miss++; 264*15798Smckusick ncp = NULL; 265*15798Smckusick } else { 266*15798Smckusick if (*cp == '/' || docache) { 267*15798Smckusick 268*15798Smckusick nchstats.ncs_goodhits++; 269*15798Smckusick 270*15798Smckusick /* 271*15798Smckusick * move this slot to end of LRU 272*15798Smckusick * chain, if not already there 273*15798Smckusick */ 274*15798Smckusick if (ncp->nc_nxt) { 275*15798Smckusick /* remove from LRU chain */ 276*15798Smckusick *ncp->nc_prev = ncp->nc_nxt; 277*15798Smckusick ncp->nc_nxt->nc_prev = ncp->nc_prev; 278*15798Smckusick 279*15798Smckusick /* and replace at end of it */ 280*15798Smckusick ncp->nc_nxt = NULL; 281*15798Smckusick ncp->nc_prev = nchtail; 282*15798Smckusick *nchtail = ncp; 283*15798Smckusick nchtail = &ncp->nc_nxt; 284*15798Smckusick } 285*15798Smckusick 286*15798Smckusick pdp = dp; 287*15798Smckusick dp = ncp->nc_ip; 288*15798Smckusick if (dp == NULL) 289*15798Smckusick panic("nami: null cache ino"); 290*15798Smckusick if (pdp != dp) { 291*15798Smckusick ilock(dp); 292*15798Smckusick dp->i_count++; 293*15798Smckusick iunlock(pdp); 294*15798Smckusick } else 295*15798Smckusick dp->i_count++; 296*15798Smckusick 297*15798Smckusick u.u_dent.d_ino = dp->i_number; 298*15798Smckusick /* u_dent.d_reclen is garbage ... */ 299*15798Smckusick 300*15798Smckusick goto haveino; 301*15798Smckusick } 302*15798Smckusick 303*15798Smckusick /* 304*15798Smckusick * last segment and we are renaming or deleting 305*15798Smckusick * or otherwise don't want cache entry to exist 306*15798Smckusick */ 307*15798Smckusick 308*15798Smckusick nchstats.ncs_badhits++; 309*15798Smckusick 310*15798Smckusick /* remove from LRU chain */ 311*15798Smckusick *ncp->nc_prev = ncp->nc_nxt; 312*15798Smckusick if (ncp->nc_nxt) 313*15798Smckusick ncp->nc_nxt->nc_prev = ncp->nc_prev; 314*15798Smckusick else 315*15798Smckusick nchtail = ncp->nc_prev; 316*15798Smckusick 317*15798Smckusick /* remove from hash chain */ 318*15798Smckusick remque(ncp); 319*15798Smckusick 320*15798Smckusick /* release ref on the inode */ 321*15798Smckusick irele(ncp->nc_ip); 322*15798Smckusick ncp->nc_ip = NULL; 323*15798Smckusick 324*15798Smckusick /* insert at head of LRU list (first to grab) */ 325*15798Smckusick ncp->nc_nxt = nchhead; 326*15798Smckusick ncp->nc_prev = &nchhead; 327*15798Smckusick nchhead->nc_prev = &ncp->nc_nxt; 328*15798Smckusick nchhead = ncp; 329*15798Smckusick 330*15798Smckusick /* and make a dummy hash chain */ 331*15798Smckusick ncp->nc_forw = ncp; 332*15798Smckusick ncp->nc_back = ncp; 333*15798Smckusick 334*15798Smckusick ncp = NULL; 335*15798Smckusick } 336*15798Smckusick } 337*15798Smckusick 338*15798Smckusick /* 3397534Sroot * Suppress search for slots unless creating 3407534Sroot * file and at end of pathname, in which case 3417534Sroot * we watch for a place to put the new file in 3427534Sroot * case it doesn't already exist. 3436571Smckusic */ 3447534Sroot slotstatus = FOUND; 3459166Ssam if (flag == CREATE && *cp == 0) { 3467534Sroot slotstatus = NONE; 3477534Sroot slotfreespace = 0; 3487534Sroot slotneeded = DIRSIZ(&u.u_dent); 3497534Sroot } 35015660Smckusick /* 35115660Smckusick * If this is the same directory that this process 35215660Smckusick * previously searched, pick up where we last left off. 353*15798Smckusick * We cache only lookups as these are the most common 35415660Smckusick * and have the greatest payoff. Caching CREATE has little 35515660Smckusick * benefit as it usually must search the entire directory 35615660Smckusick * to determine that the entry does not exist. Caching the 35715660Smckusick * location of the last DELETE has not reduced profiling time 35815660Smckusick * and hence has been removed in the interest of simplicity. 35915660Smckusick */ 36015660Smckusick if (flag != LOOKUP || dp->i_number != u.u_ncache.nc_inumber || 36115660Smckusick dp->i_dev != u.u_ncache.nc_dev) { 36215660Smckusick u.u_offset = 0; 36315660Smckusick numdirpasses = 1; 36415660Smckusick } else { 365*15798Smckusick if ((dp->i_flag & ICHG) || dp->i_ctime >= u.u_ncache.nc_time) { 36615660Smckusick u.u_ncache.nc_prevoffset &= ~(DIRBLKSIZ - 1); 36715660Smckusick u.u_ncache.nc_time = time.tv_sec; 36815660Smckusick } 36915660Smckusick u.u_offset = u.u_ncache.nc_prevoffset; 37015660Smckusick entryoffsetinblock = blkoff(fs, u.u_offset); 37115660Smckusick if (entryoffsetinblock != 0) { 37215660Smckusick bp = blkatoff(dp, u.u_offset, (char **)0); 37315660Smckusick if (bp == 0) 37415660Smckusick goto bad; 37515660Smckusick } 37615660Smckusick numdirpasses = 2; 377*15798Smckusick nchstats.ncs_2passes++; 37815660Smckusick } 37915660Smckusick endsearch = roundup(dp->i_size, DIRBLKSIZ); 3807534Sroot 38115660Smckusick searchloop: 38215660Smckusick while (u.u_offset < endsearch) { 3835972Swnj /* 3845972Swnj * If offset is on a block boundary, 3855972Swnj * read the next directory block. 3865972Swnj * Release previous if it exists. 3875972Swnj */ 3886571Smckusic if (blkoff(fs, u.u_offset) == 0) { 3895972Swnj if (bp != NULL) 3905972Swnj brelse(bp); 3917605Ssam bp = blkatoff(dp, u.u_offset, (char **)0); 3927534Sroot if (bp == 0) 3937534Sroot goto bad; 3947534Sroot entryoffsetinblock = 0; 3955972Swnj } 3967534Sroot 3975972Swnj /* 3987534Sroot * If still looking for a slot, and at a DIRBLKSIZE 3997534Sroot * boundary, have to start looking for free space 4007534Sroot * again. 4016571Smckusic */ 4027534Sroot if (slotstatus == NONE && 4037534Sroot (entryoffsetinblock&(DIRBLKSIZ-1)) == 0) { 4047534Sroot slotoffset = -1; 4057534Sroot slotfreespace = 0; 4067534Sroot } 4077534Sroot 4087534Sroot /* 4097534Sroot * Get pointer to next entry, and do consistency checking: 4107534Sroot * record length must be multiple of 4 4117605Ssam * record length must not be zero 4127534Sroot * entry must fit in rest of this DIRBLKSIZ block 4137534Sroot * record must be large enough to contain name 4147605Ssam * When dirchk is set we also check: 4157605Ssam * name is not longer than MAXNAMLEN 4167534Sroot * name must be as long as advertised, and null terminated 4177605Ssam * Checking last two conditions is done only when dirchk is 4187605Ssam * set, to save time. 4197534Sroot */ 4207534Sroot ep = (struct direct *)(bp->b_un.b_addr + entryoffsetinblock); 4217534Sroot i = DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1)); 4227605Ssam if ((ep->d_reclen & 0x3) || ep->d_reclen == 0 || 4237605Ssam ep->d_reclen > i || DIRSIZ(ep) > ep->d_reclen || 4247605Ssam dirchk && (ep->d_namlen > MAXNAMLEN || dirbadname(ep))) { 4257534Sroot dirbad(dp, "mangled entry"); 4266571Smckusic u.u_offset += i; 4277534Sroot entryoffsetinblock += i; 4286571Smckusic continue; 4296571Smckusic } 4307534Sroot 4316571Smckusic /* 4327534Sroot * If an appropriate sized slot has not yet been found, 4336571Smckusic * check to see if one is available. Also accumulate space 4346571Smckusic * in the current block so that we can determine if 4356571Smckusic * compaction is viable. 4366571Smckusic */ 4377534Sroot if (slotstatus != FOUND) { 4387534Sroot int size = ep->d_reclen; 4397534Sroot 4406571Smckusic if (ep->d_ino != 0) 4416571Smckusic size -= DIRSIZ(ep); 4426571Smckusic if (size > 0) { 4437534Sroot if (size >= slotneeded) { 4447534Sroot slotstatus = FOUND; 4457534Sroot slotoffset = u.u_offset; 4467534Sroot slotsize = ep->d_reclen; 4477534Sroot } else if (slotstatus == NONE) { 4487534Sroot slotfreespace += size; 4497534Sroot if (slotoffset == -1) 4507534Sroot slotoffset = u.u_offset; 4517534Sroot if (slotfreespace >= slotneeded) { 4527534Sroot slotstatus = COMPACT; 4537534Sroot slotsize = 4547534Sroot u.u_offset+ep->d_reclen - 4557534Sroot slotoffset; 4567534Sroot } 4576571Smckusic } 4586571Smckusic } 4596571Smckusic } 4607534Sroot 4616571Smckusic /* 4627534Sroot * Check for a name match. 4635972Swnj */ 4647534Sroot if (ep->d_ino) { 4657534Sroot if (ep->d_namlen == u.u_dent.d_namlen && 4667534Sroot !bcmp(u.u_dent.d_name, ep->d_name, ep->d_namlen)) 4677534Sroot goto found; 4687534Sroot } 4697534Sroot prevoff = u.u_offset; 4706571Smckusic u.u_offset += ep->d_reclen; 4717534Sroot entryoffsetinblock += ep->d_reclen; 4727534Sroot } 473*15798Smckusick /* notfound: */ 47415660Smckusick /* 475*15798Smckusick * If we started in the middle of the directory and failed 47615660Smckusick * to find our target, we must check the beginning as well. 47715660Smckusick */ 47815660Smckusick if (numdirpasses == 2) { 47915660Smckusick numdirpasses--; 48015660Smckusick u.u_offset = 0; 48115660Smckusick endsearch = u.u_ncache.nc_prevoffset; 48215660Smckusick goto searchloop; 48315660Smckusick } 4847534Sroot /* 4857534Sroot * If creating, and at end of pathname and current 4869166Ssam * directory has not been removed, then can consider 4879166Ssam * allowing file to be created. 4887534Sroot */ 4899166Ssam if (flag == CREATE && *cp == 0 && dp->i_nlink != 0) { 4905972Swnj /* 4917534Sroot * Access for write is interpreted as allowing 4927534Sroot * creation of files in the directory. 4935972Swnj */ 4947534Sroot if (access(dp, IWRITE)) 4957534Sroot goto bad; 4965972Swnj /* 4977534Sroot * Return an indication of where the new directory 4987534Sroot * entry should be put. If we didn't find a slot, 4997534Sroot * then set u.u_count to 0 indicating that the 5007534Sroot * new slot belongs at the end of the directory. 5017534Sroot * If we found a slot, then the new entry can be 5027534Sroot * put in the range [u.u_offset..u.u_offset+u.u_count) 5035972Swnj */ 50415660Smckusick if (slotstatus == NONE) { 50515660Smckusick u.u_offset = roundup(dp->i_size, DIRBLKSIZ); 5067534Sroot u.u_count = 0; 50715660Smckusick } else { 5087534Sroot u.u_offset = slotoffset; 5097534Sroot u.u_count = slotsize; 5105972Swnj } 5117534Sroot dp->i_flag |= IUPD|ICHG; 5127534Sroot if (bp) 5137534Sroot brelse(bp); 5147534Sroot brelse(nbp); 5155972Swnj /* 5167534Sroot * We return with the directory locked, so that 5177534Sroot * the parameters we set up above will still be 5187534Sroot * valid if we actually decide to do a direnter(). 5197534Sroot * We return NULL to indicate that the entry doesn't 5207534Sroot * currently exist, leaving a pointer to the (locked) 5217534Sroot * directory inode in u.u_pdir. 5225972Swnj */ 5237534Sroot u.u_pdir = dp; 5247534Sroot return (NULL); 5257534Sroot } 5267534Sroot u.u_error = ENOENT; 5277534Sroot goto bad; 5287534Sroot found: 529*15798Smckusick if (numdirpasses == 2) 530*15798Smckusick nchstats.ncs_pass2++; 5317534Sroot /* 5327534Sroot * Check that directory length properly reflects presence 5337534Sroot * of this entry. 5347534Sroot */ 5357605Ssam if (entryoffsetinblock + DIRSIZ(ep) > dp->i_size) { 5367534Sroot dirbad(dp, "i_size too small"); 5377605Ssam dp->i_size = entryoffsetinblock + DIRSIZ(ep); 5387534Sroot dp->i_flag |= IUPD|ICHG; 5397534Sroot } 5407534Sroot 5417534Sroot /* 54215660Smckusick * Found component in pathname. 543*15798Smckusick * If the final component of path name, save information 54415660Smckusick * in the cache as to where the entry was found. 5457534Sroot */ 54615660Smckusick if (*cp == '\0' && flag == LOOKUP) { 54715660Smckusick u.u_ncache.nc_prevoffset = u.u_offset; 54815660Smckusick u.u_ncache.nc_inumber = dp->i_number; 54915660Smckusick u.u_ncache.nc_dev = dp->i_dev; 55015660Smckusick u.u_ncache.nc_time = time.tv_sec; 55115660Smckusick } 55215660Smckusick /* 55315660Smckusick * Save directory entry in u.u_dent, 55415660Smckusick * and release directory buffer. 55515660Smckusick */ 5567825Sroot bcopy((caddr_t)ep, (caddr_t)&u.u_dent, (u_int)DIRSIZ(ep)); 5577534Sroot brelse(bp); 5587534Sroot bp = NULL; 5597534Sroot 5607534Sroot /* 5617534Sroot * If deleting, and at end of pathname, return 5627534Sroot * parameters which can be used to remove file. 5639166Ssam * If the lockparent flag isn't set, we return only 5649166Ssam * the directory (in u.u_pdir), otherwise we go 5659166Ssam * on and lock the inode, being careful with ".". 5667534Sroot */ 5679166Ssam if (flag == DELETE && *cp == 0) { 5687534Sroot /* 5697534Sroot * Write access to directory required to delete files. 5707534Sroot */ 5717534Sroot if (access(dp, IWRITE)) 5727534Sroot goto bad; 5739166Ssam u.u_pdir = dp; /* for dirremove() */ 5747534Sroot /* 5757534Sroot * Return pointer to current entry in u.u_offset, 5767534Sroot * and distance past previous entry (if there 5777534Sroot * is a previous entry in this block) in u.u_count. 5787534Sroot * Save directory inode pointer in u.u_pdir for dirremove(). 5797534Sroot */ 5807534Sroot if ((u.u_offset&(DIRBLKSIZ-1)) == 0) 5817534Sroot u.u_count = 0; 5827534Sroot else 5837534Sroot u.u_count = u.u_offset - prevoff; 5849166Ssam if (lockparent) { 5859166Ssam if (dp->i_number == u.u_dent.d_ino) 5869166Ssam dp->i_count++; 5879166Ssam else { 5889166Ssam dp = iget(dp->i_dev, fs, u.u_dent.d_ino); 5899166Ssam if (dp == NULL) { 5909166Ssam iput(u.u_pdir); 5919166Ssam goto bad; 5929166Ssam } 593*15798Smckusick /* 594*15798Smckusick * If directory is setuid, then user must own 595*15798Smckusick * the directory, or the file in it, else he 596*15798Smckusick * may not delete it (unless he's root). This 597*15798Smckusick * implements append-only directories. 598*15798Smckusick */ 599*15798Smckusick if ((u.u_pdir->i_mode & ISUID) && 600*15798Smckusick u.u_uid != 0 && 601*15798Smckusick u.u_uid != u.u_pdir->i_uid && 602*15798Smckusick dp->i_uid != u.u_uid) { 603*15798Smckusick iput(u.u_pdir); 604*15798Smckusick u.u_error = EPERM; 605*15798Smckusick goto bad; 606*15798Smckusick } 6079166Ssam } 6089166Ssam } 6097534Sroot brelse(nbp); 6107534Sroot return (dp); 6117534Sroot } 6127534Sroot 6137534Sroot /* 6147534Sroot * Special handling for ".." allowing chdir out of mounted 6157534Sroot * file system: indirect .. in root inode to reevaluate 6167534Sroot * in directory file system was mounted on. 6177534Sroot */ 618*15798Smckusick isdotdot = 0; 619*15798Smckusick if (bcmp(u.u_dent.d_name, "..", 3) == 0) { 620*15798Smckusick isdotdot++; 6217534Sroot if (dp == u.u_rdir) 6227534Sroot u.u_dent.d_ino = dp->i_number; 6237534Sroot else if (u.u_dent.d_ino == ROOTINO && 6247534Sroot dp->i_number == ROOTINO) { 6257534Sroot for (i = 1; i < NMOUNT; i++) 6267534Sroot if (mount[i].m_bufp != NULL && 6277534Sroot mount[i].m_dev == dp->i_dev) { 6286571Smckusic iput(dp); 6297534Sroot dp = mount[i].m_inodp; 6305972Swnj ilock(dp); 6315972Swnj dp->i_count++; 6327534Sroot fs = dp->i_fs; 6337534Sroot cp -= 2; /* back over .. */ 6347534Sroot goto dirloop2; 6355972Swnj } 63630Sbill } 6377534Sroot } 6387534Sroot 6397534Sroot /* 6409166Ssam * If rewriting (rename), return the inode and the 6419166Ssam * information required to rewrite the present directory 6429166Ssam * Must get inode of directory entry to verify it's a 6439166Ssam * regular file, or empty directory. 6449166Ssam */ 6459166Ssam if ((flag == CREATE && lockparent) && *cp == 0) { 6469166Ssam if (access(dp, IWRITE)) 6479166Ssam goto bad; 6489166Ssam u.u_pdir = dp; /* for dirrewrite() */ 6499166Ssam /* 6509166Ssam * Careful about locking second inode. 6519166Ssam * This can only occur if the target is ".". 6529166Ssam */ 6539166Ssam if (dp->i_number == u.u_dent.d_ino) { 6549166Ssam u.u_error = EISDIR; /* XXX */ 6559166Ssam goto bad; 6569166Ssam } 6579166Ssam dp = iget(dp->i_dev, fs, u.u_dent.d_ino); 6589166Ssam if (dp == NULL) { 6599166Ssam iput(u.u_pdir); 6609166Ssam goto bad; 6619166Ssam } 6629166Ssam brelse(nbp); 6639166Ssam return (dp); 6649166Ssam } 6659166Ssam 6669166Ssam /* 66712011Smckusick * Check for symbolic link, which may require us to massage the 66812011Smckusick * name before we continue translation. We do not `iput' the 66912011Smckusick * directory because we may need it again if the symbolic link 67012011Smckusick * is relative to the current directory. Instead we save it 67112011Smckusick * unlocked as "pdp". We must get the target inode before unlocking 67212011Smckusick * the directory to insure that the inode will not be removed 67312011Smckusick * before we get it. We prevent deadlock by always fetching 67412011Smckusick * inodes from the root, moving down the directory tree. Thus 67512011Smckusick * when following backward pointers ".." we must unlock the 67612011Smckusick * parent directory before getting the requested directory. 67712011Smckusick * There is a potential race condition here if both the current 67812011Smckusick * and parent directories are removed before the `iget' for the 67912011Smckusick * inode associated with ".." returns. We hope that this occurs 68012011Smckusick * infrequently since we cannot avoid this race condition without 68112492Ssam * implementing a sophisticated deadlock detection algorithm. 68212011Smckusick * Note also that this simple deadlock detection scheme will not 68312011Smckusick * work if the file system has any hard links other than ".." 68412011Smckusick * that point backwards in the directory structure. 6857534Sroot */ 6867534Sroot pdp = dp; 687*15798Smckusick if (isdotdot) { 68812011Smckusick iunlock(pdp); /* race to get the inode */ 68912011Smckusick dp = iget(dp->i_dev, fs, u.u_dent.d_ino); 69012011Smckusick if (dp == NULL) 69112011Smckusick goto bad2; 69212011Smckusick } else if (dp->i_number == u.u_dent.d_ino) { 69312011Smckusick dp->i_count++; /* we want ourself, ie "." */ 69412011Smckusick } else { 69512011Smckusick dp = iget(dp->i_dev, fs, u.u_dent.d_ino); 69612011Smckusick iunlock(pdp); 69712011Smckusick if (dp == NULL) 69812011Smckusick goto bad2; 69912011Smckusick } 700*15798Smckusick 701*15798Smckusick /* 702*15798Smckusick * insert name into cache (if we want it, and it isn't "." or "..") 703*15798Smckusick * 704*15798Smckusick * all other cases where making a cache entry would be wrong 705*15798Smckusick * have already departed from the code sequence somewhere above. 706*15798Smckusick */ 707*15798Smckusick if (bcmp(u.u_dent.d_name, ".", 2) != 0 && !isdotdot && docache) { 708*15798Smckusick if (ncp != NULL) 709*15798Smckusick panic("nami: duplicating cache"); 710*15798Smckusick 711*15798Smckusick /* 712*15798Smckusick * free the cache slot at head of lru chain 713*15798Smckusick */ 714*15798Smckusick if (ncp = nchhead) { 715*15798Smckusick /* remove from lru chain */ 716*15798Smckusick *ncp->nc_prev = ncp->nc_nxt; 717*15798Smckusick if (ncp->nc_nxt) 718*15798Smckusick ncp->nc_nxt->nc_prev = ncp->nc_prev; 719*15798Smckusick else 720*15798Smckusick nchtail = ncp->nc_prev; 721*15798Smckusick 722*15798Smckusick /* remove from old hash chain */ 723*15798Smckusick remque(ncp); 724*15798Smckusick 725*15798Smckusick /* drop hold on inode (if we had one) */ 726*15798Smckusick if (ncp->nc_ip) 727*15798Smckusick irele(ncp->nc_ip); 728*15798Smckusick 729*15798Smckusick /* grab the inode we just found */ 730*15798Smckusick ncp->nc_ip = dp; 731*15798Smckusick dp->i_count++; 732*15798Smckusick 733*15798Smckusick /* fill in cache info */ 734*15798Smckusick ncp->nc_ino = pdp->i_number; /* parents inum */ 735*15798Smckusick ncp->nc_dev = pdp->i_dev; /* & device */ 736*15798Smckusick ncp->nc_idev = dp->i_dev; /* our device */ 737*15798Smckusick ncp->nc_nlen = u.u_dent.d_namlen; 738*15798Smckusick bcopy(u.u_dent.d_name, ncp->nc_name, ncp->nc_nlen); 739*15798Smckusick 740*15798Smckusick /* link at end of lru chain */ 741*15798Smckusick ncp->nc_nxt = NULL; 742*15798Smckusick ncp->nc_prev = nchtail; 743*15798Smckusick *nchtail = ncp; 744*15798Smckusick nchtail = &ncp->nc_nxt; 745*15798Smckusick 746*15798Smckusick /* and insert on hash chain */ 747*15798Smckusick insque(ncp, nhp); 748*15798Smckusick } 749*15798Smckusick } 750*15798Smckusick 751*15798Smckusick 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; 7797534Sroot ilock(dp); 7807534Sroot dp->i_count++; 7817534Sroot } else { 7827534Sroot dp = pdp; 7837534Sroot 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 816*15798Smckusick 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 8267534Sroot dirbadname(ep) 8277534Sroot register struct direct *ep; 8287534Sroot { 8297534Sroot register int i; 8307534Sroot 8317534Sroot for (i = 0; i < ep->d_namlen; i++) 8327534Sroot if (ep->d_name[i] == 0) 8337534Sroot return (1); 8347534Sroot return (ep->d_name[i]); 8357534Sroot } 8367534Sroot 83730Sbill /* 8387534Sroot * Write a directory entry after a call to namei, using the parameters 8397534Sroot * which it left in the u. area. The argument ip is the inode which 8407534Sroot * the new directory entry will refer to. The u. area field u.u_pdir is 8417534Sroot * a pointer to the directory to be written, which was left locked by 8427534Sroot * namei. Remaining parameters (u.u_offset, u.u_count) indicate 8437534Sroot * how the space for the new entry is to be gotten. 8447534Sroot */ 8457534Sroot direnter(ip) 8467534Sroot struct inode *ip; 8475972Swnj { 8487534Sroot register struct direct *ep, *nep; 8497534Sroot struct buf *bp; 85011639Ssam int loc, spacefree, error = 0; 8518631Sroot u_int dsize; 8528631Sroot int newentrysize; 8537534Sroot char *dirbuf; 8545972Swnj 8557534Sroot u.u_dent.d_ino = ip->i_number; 8567534Sroot u.u_segflg = 1; 8577534Sroot newentrysize = DIRSIZ(&u.u_dent); 8587534Sroot if (u.u_count == 0) { 8597534Sroot /* 8607534Sroot * If u.u_count is 0, then namei could find no space in the 8617534Sroot * directory. In this case u.u_offset will be on a directory 8627534Sroot * block boundary and we will write the new entry into a fresh 8637534Sroot * block. 8647534Sroot */ 8657534Sroot if (u.u_offset&(DIRBLKSIZ-1)) 8667534Sroot panic("wdir: newblk"); 8677534Sroot u.u_dent.d_reclen = DIRBLKSIZ; 86810849Ssam error = rdwri(UIO_WRITE, u.u_pdir, (caddr_t)&u.u_dent, 8698631Sroot newentrysize, u.u_offset, 1, (int *)0); 8707534Sroot iput(u.u_pdir); 87110849Ssam return (error); 8727534Sroot } 8737534Sroot 8747534Sroot /* 8757534Sroot * If u.u_count is non-zero, then namei found space for the 8767534Sroot * new entry in the range u.u_offset to u.u_offset+u.u_count. 8777534Sroot * in the directory. To use this space, we may have to compact 8787534Sroot * the entries located there, by copying them together towards 8797534Sroot * the beginning of the block, leaving the free space in 8807534Sroot * one usable chunk at the end. 8817534Sroot */ 8827534Sroot 8837534Sroot /* 8847534Sroot * Increase size of directory if entry eats into new space. 8857534Sroot * This should never push the size past a new multiple of 8867534Sroot * DIRBLKSIZE. 8877534Sroot */ 8889166Ssam if (u.u_offset + u.u_count > u.u_pdir->i_size) 8897534Sroot u.u_pdir->i_size = u.u_offset + u.u_count; 8907534Sroot 8917534Sroot /* 8927534Sroot * Get the block containing the space for the new directory 89310849Ssam * entry. Should return error by result instead of u.u_error. 8947534Sroot */ 8957605Ssam bp = blkatoff(u.u_pdir, u.u_offset, (char **)&dirbuf); 8969166Ssam if (bp == 0) { 8979166Ssam iput(u.u_pdir); 89810849Ssam return (u.u_error); 8999166Ssam } 9007534Sroot 9017534Sroot /* 9027534Sroot * Find space for the new entry. In the simple case, the 9037534Sroot * entry at offset base will have the space. If it does 9047534Sroot * not, then namei arranged that compacting the region 9057534Sroot * u.u_offset to u.u_offset+u.u_count would yield the space. 9067534Sroot */ 9077534Sroot ep = (struct direct *)dirbuf; 9087534Sroot dsize = DIRSIZ(ep); 90911639Ssam spacefree = ep->d_reclen - dsize; 9107534Sroot for (loc = ep->d_reclen; loc < u.u_count; ) { 9117534Sroot nep = (struct direct *)(dirbuf + loc); 9127534Sroot if (ep->d_ino) { 9137534Sroot /* trim the existing slot */ 9147534Sroot ep->d_reclen = dsize; 9157534Sroot ep = (struct direct *)((char *)ep + dsize); 9167534Sroot } else { 9177534Sroot /* overwrite; nothing there; header is ours */ 91811639Ssam spacefree += dsize; 9197534Sroot } 9207534Sroot dsize = DIRSIZ(nep); 92111639Ssam spacefree += nep->d_reclen - dsize; 9227534Sroot loc += nep->d_reclen; 9237825Sroot bcopy((caddr_t)nep, (caddr_t)ep, dsize); 9247534Sroot } 9257534Sroot /* 9267534Sroot * Update the pointer fields in the previous entry (if any), 9277534Sroot * copy in the new entry, and write out the block. 9287534Sroot */ 9297534Sroot if (ep->d_ino == 0) { 93011639Ssam if (spacefree + dsize < newentrysize) 9317534Sroot panic("wdir: compact1"); 93211639Ssam u.u_dent.d_reclen = spacefree + dsize; 9337534Sroot } else { 93411639Ssam if (spacefree < newentrysize) 9357534Sroot panic("wdir: compact2"); 93611639Ssam u.u_dent.d_reclen = spacefree; 9377534Sroot ep->d_reclen = dsize; 9387534Sroot ep = (struct direct *)((char *)ep + dsize); 9397534Sroot } 9408672Sroot bcopy((caddr_t)&u.u_dent, (caddr_t)ep, (u_int)newentrysize); 9417534Sroot bwrite(bp); 9427534Sroot u.u_pdir->i_flag |= IUPD|ICHG; 9437534Sroot iput(u.u_pdir); 94410849Ssam return (error); 9455972Swnj } 9466571Smckusic 9479166Ssam /* 9489166Ssam * Remove a directory entry after a call to namei, using the 9499166Ssam * parameters which it left in the u. area. The u. entry 9509166Ssam * u_offset contains the offset into the directory of the 9519166Ssam * entry to be eliminated. The u_count field contains the 9529166Ssam * size of the previous record in the directory. If this 9539166Ssam * is 0, the first entry is being deleted, so we need only 9549166Ssam * zero the inode number to mark the entry as free. If the 9559166Ssam * entry isn't the first in the directory, we must reclaim 9569166Ssam * the space of the now empty record by adding the record size 9579166Ssam * to the size of the previous entry. 9589166Ssam */ 9597534Sroot dirremove() 9606571Smckusic { 9617534Sroot register struct inode *dp = u.u_pdir; 9627534Sroot register struct buf *bp; 9637534Sroot struct direct *ep; 9646571Smckusic 9659269Ssam if (u.u_count == 0) { 9667534Sroot /* 9677534Sroot * First entry in block: set d_ino to zero. 9687534Sroot */ 9699269Ssam u.u_dent.d_ino = 0; 9708619Sroot (void) rdwri(UIO_WRITE, dp, (caddr_t)&u.u_dent, 9718631Sroot (int)DIRSIZ(&u.u_dent), u.u_offset, 1, (int *)0); 9729269Ssam } else { 9737534Sroot /* 9747534Sroot * Collapse new free space into previous entry. 9757534Sroot */ 9767825Sroot bp = blkatoff(dp, (int)(u.u_offset - u.u_count), (char **)&ep); 9777534Sroot if (bp == 0) 9787534Sroot return (0); 9797534Sroot ep->d_reclen += u.u_dent.d_reclen; 9807534Sroot bwrite(bp); 9817534Sroot dp->i_flag |= IUPD|ICHG; 9827534Sroot } 9837534Sroot return (1); 9846571Smckusic } 9857534Sroot 9867605Ssam /* 9879166Ssam * Rewrite an existing directory entry to point at the inode 9889166Ssam * supplied. The parameters describing the directory entry are 9899166Ssam * set up by a call to namei. 9909166Ssam */ 9919166Ssam dirrewrite(dp, ip) 9929166Ssam struct inode *dp, *ip; 9939166Ssam { 9949166Ssam 9959166Ssam u.u_dent.d_ino = ip->i_number; 9969166Ssam u.u_error = rdwri(UIO_WRITE, dp, (caddr_t)&u.u_dent, 9979166Ssam (int)DIRSIZ(&u.u_dent), u.u_offset, 1, (int *)0); 9989166Ssam iput(dp); 9999166Ssam } 10009166Ssam 10019166Ssam /* 10027605Ssam * Return buffer with contents of block "offset" 10037605Ssam * from the beginning of directory "ip". If "res" 10047605Ssam * is non-zero, fill it in with a pointer to the 10057605Ssam * remaining space in the directory. 10067605Ssam */ 10077534Sroot struct buf * 10087605Ssam blkatoff(ip, offset, res) 10097534Sroot struct inode *ip; 10107534Sroot off_t offset; 10117534Sroot char **res; 10127534Sroot { 10137534Sroot register struct fs *fs = ip->i_fs; 10148672Sroot daddr_t lbn = lblkno(fs, offset); 10157534Sroot int base = blkoff(fs, offset); 10167534Sroot int bsize = blksize(fs, ip, lbn); 10178672Sroot daddr_t bn = fsbtodb(fs, bmap(ip, lbn, B_WRITE, base, bsize)); 10187534Sroot register struct buf *bp; 10197534Sroot 10207534Sroot if (u.u_error) 10217534Sroot return (0); 10227534Sroot bp = bread(ip->i_dev, bn, bsize); 10237534Sroot if (bp->b_flags & B_ERROR) { 10247534Sroot brelse(bp); 10257534Sroot return (0); 10267534Sroot } 10277534Sroot if (res) 10287534Sroot *res = bp->b_un.b_addr + base; 10297534Sroot return (bp); 10307534Sroot } 10319166Ssam 10329166Ssam /* 10339166Ssam * Check if a directory is empty or not. 10349166Ssam * Inode supplied must be locked. 103512817Ssam * 103612817Ssam * Using a struct dirtemplate here is not precisely 103712817Ssam * what we want, but better than using a struct direct. 103812817Ssam * 103912817Ssam * NB: does not handle corrupted directories. 10409166Ssam */ 10419166Ssam dirempty(ip) 10429863Ssam register struct inode *ip; 10439166Ssam { 10449166Ssam register off_t off; 104512817Ssam struct dirtemplate dbuf; 104612817Ssam register struct direct *dp = (struct direct *)&dbuf; 10479863Ssam int error, count; 104812817Ssam #define MINDIRSIZ (sizeof (struct dirtemplate) / 2) 10499166Ssam 10509166Ssam for (off = 0; off < ip->i_size; off += dp->d_reclen) { 105112817Ssam error = rdwri(UIO_READ, ip, (caddr_t)dp, MINDIRSIZ, 105212817Ssam off, 1, &count); 105312817Ssam /* 105412817Ssam * Since we read MINDIRSIZ, residual must 105512817Ssam * be 0 unless we're at end of file. 105612817Ssam */ 105712817Ssam if (error || count != 0) 10589166Ssam return (0); 105912817Ssam /* skip empty entries */ 10609166Ssam if (dp->d_ino == 0) 10619166Ssam continue; 106212817Ssam /* accept only "." and ".." */ 106312817Ssam if (dp->d_namlen > 2) 106412817Ssam return (0); 10659166Ssam if (dp->d_name[0] != '.') 10669166Ssam return (0); 106712817Ssam /* 106812817Ssam * At this point d_namlen must be 1 or 2. 106912817Ssam * 1 implies ".", 2 implies ".." if second 107012817Ssam * char is also "." 107112817Ssam */ 107212817Ssam if (dp->d_namlen == 1 || dp->d_name[1] == '.') 10739166Ssam continue; 10749166Ssam return (0); 10759166Ssam } 10769166Ssam return (1); 10779166Ssam } 107812815Smckusick 107912815Smckusick /* 108012815Smckusick * Check if source directory is in the path of the target directory. 108112815Smckusick * Target is supplied locked, source is unlocked. 108212815Smckusick * The target is always iput() before returning. 108312815Smckusick */ 108412815Smckusick checkpath(source, target) 108512815Smckusick struct inode *source, *target; 108612815Smckusick { 108712815Smckusick struct dirtemplate dirbuf; 108812815Smckusick register struct inode *ip; 108912815Smckusick int error = 0; 109012815Smckusick 109112815Smckusick ip = target; 109212815Smckusick if (ip->i_number == source->i_number) { 109312815Smckusick error = EEXIST; 109412815Smckusick goto out; 109512815Smckusick } 109612815Smckusick if (ip->i_number == ROOTINO) 109712815Smckusick goto out; 109812815Smckusick 109912815Smckusick for (;;) { 110012815Smckusick if ((ip->i_mode&IFMT) != IFDIR) { 110112815Smckusick error = ENOTDIR; 110212815Smckusick break; 110312815Smckusick } 110412815Smckusick error = rdwri(UIO_READ, ip, (caddr_t)&dirbuf, 110512815Smckusick sizeof (struct dirtemplate), (off_t)0, 1, (int *)0); 110612815Smckusick if (error != 0) 110712815Smckusick break; 110812815Smckusick if (dirbuf.dotdot_namlen != 2 || 110912815Smckusick bcmp(dirbuf.dotdot_name, "..", 3) != 0) { 111012815Smckusick error = ENOTDIR; 111112815Smckusick break; 111212815Smckusick } 111312815Smckusick if (dirbuf.dotdot_ino == source->i_number) { 111412815Smckusick error = EINVAL; 111512815Smckusick break; 111612815Smckusick } 111712815Smckusick if (dirbuf.dotdot_ino == ROOTINO) 111812815Smckusick break; 111912815Smckusick iput(ip); 112012815Smckusick ip = iget(ip->i_dev, ip->i_fs, dirbuf.dotdot_ino); 112112815Smckusick if (ip == NULL) { 112212815Smckusick error = u.u_error; 112312815Smckusick break; 112412815Smckusick } 112512815Smckusick } 112612815Smckusick 112712815Smckusick out: 112812815Smckusick if (error == ENOTDIR) 112912815Smckusick printf("checkpath: .. not a directory\n"); 113012815Smckusick if (ip != NULL) 113112815Smckusick iput(ip); 113212815Smckusick return (error); 113312815Smckusick } 1134*15798Smckusick 1135*15798Smckusick /* 1136*15798Smckusick * Name cache initialization, from main() when we are booting 1137*15798Smckusick */ 1138*15798Smckusick nchinit() 1139*15798Smckusick { 1140*15798Smckusick register union nchash *nchp; 1141*15798Smckusick register struct nch *ncp; 1142*15798Smckusick 1143*15798Smckusick nchhead = 0; 1144*15798Smckusick nchtail = &nchhead; 1145*15798Smckusick 1146*15798Smckusick for (ncp = nch; ncp < &nch[nchsize]; ncp++) { 1147*15798Smckusick ncp->nc_forw = ncp; /* hash chain */ 1148*15798Smckusick ncp->nc_back = ncp; 1149*15798Smckusick 1150*15798Smckusick ncp->nc_nxt = NULL; /* lru chain */ 1151*15798Smckusick *nchtail = ncp; 1152*15798Smckusick ncp->nc_prev = nchtail; 1153*15798Smckusick nchtail = &ncp->nc_nxt; 1154*15798Smckusick 1155*15798Smckusick /* all else is zero already */ 1156*15798Smckusick } 1157*15798Smckusick 1158*15798Smckusick for (nchp = nchash; nchp < &nchash[NCHHASH]; nchp++) { 1159*15798Smckusick nchp->nch_head[0] = nchp; 1160*15798Smckusick nchp->nch_head[1] = nchp; 1161*15798Smckusick } 1162*15798Smckusick } 1163*15798Smckusick 1164*15798Smckusick /* 1165*15798Smckusick * Cache flush, called when filesys is umounted to 1166*15798Smckusick * remove entries that would now be invalid 1167*15798Smckusick * 1168*15798Smckusick * The line "nxtcp = nchhead" near the end is to avoid potential problems 1169*15798Smckusick * if the cache lru chain is modified while we are dumping the 1170*15798Smckusick * inode. This makes the algorithm O(n^2), but do you think I care? 1171*15798Smckusick */ 1172*15798Smckusick nchinval(dev) 1173*15798Smckusick register dev_t dev; 1174*15798Smckusick { 1175*15798Smckusick register struct nch *ncp, *nxtcp; 1176*15798Smckusick 1177*15798Smckusick for (ncp = nchhead; ncp; ncp = nxtcp) { 1178*15798Smckusick nxtcp = ncp->nc_nxt; 1179*15798Smckusick 1180*15798Smckusick if (ncp->nc_ip == NULL || 1181*15798Smckusick (ncp->nc_idev != dev && ncp->nc_dev != dev)) 1182*15798Smckusick continue; 1183*15798Smckusick 1184*15798Smckusick ncp->nc_idev = NODEV; 1185*15798Smckusick ncp->nc_dev = NODEV; 1186*15798Smckusick ncp->nc_ino = 0; 1187*15798Smckusick 1188*15798Smckusick /* remove the entry from its hash chain */ 1189*15798Smckusick remque(ncp); 1190*15798Smckusick /* and make a dummy one */ 1191*15798Smckusick ncp->nc_forw = ncp; 1192*15798Smckusick ncp->nc_back = ncp; 1193*15798Smckusick 1194*15798Smckusick /* delete this entry from LRU chain */ 1195*15798Smckusick *ncp->nc_prev = nxtcp; 1196*15798Smckusick if (nxtcp) 1197*15798Smckusick nxtcp->nc_prev = ncp->nc_prev; 1198*15798Smckusick else 1199*15798Smckusick nchtail = ncp->nc_prev; 1200*15798Smckusick 1201*15798Smckusick /* free the inode we had */ 1202*15798Smckusick irele(ncp->nc_ip); 1203*15798Smckusick ncp->nc_ip = NULL; 1204*15798Smckusick 1205*15798Smckusick /* cause rescan of list, it may have altered */ 1206*15798Smckusick nxtcp = nchhead; 1207*15798Smckusick /* put the now-free entry at head of LRU */ 1208*15798Smckusick ncp->nc_nxt = nxtcp; 1209*15798Smckusick ncp->nc_prev = &nchhead; 1210*15798Smckusick nxtcp->nc_prev = &ncp->nc_nxt; 1211*15798Smckusick nchhead = ncp; 1212*15798Smckusick } 1213*15798Smckusick } 1214