1*12492Ssam /* vfs_lookup.c 4.38 83/05/18 */ 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" 1430Sbill 157605Ssam struct buf *blkatoff(); 169166Ssam int dirchk = 0; 1730Sbill /* 187534Sroot * Convert a pathname into a pointer to a locked inode, 197534Sroot * with side effects usable in creating and removing files. 207534Sroot * This is a very central and rather complicated routine. 2130Sbill * 227534Sroot * The func argument gives the routine which returns successive 239166Ssam * characters of the name to be translated. 247534Sroot * 259166Ssam * The flag argument is (LOOKUP, CREATE, DELETE) depending on whether 269166Ssam * the name is to be (looked up, created, deleted). If flag has 279166Ssam * LOCKPARENT or'ed into it and the target of the pathname exists, 289166Ssam * namei returns both the target and its parent directory locked. 299166Ssam * If the file system is not maintained in a strict tree hierarchy, 309166Ssam * this can result in a deadlock situation. When creating and 319166Ssam * LOCKPARENT is specified, the target may not be ".". When deleting 329166Ssam * and LOCKPARENT is specified, the target may be ".", but the caller 339166Ssam * must check to insure it does an irele and iput instead of two iputs. 349166Ssam * 359166Ssam * The follow argument is 1 when symbolic links are to be followed 369166Ssam * when they occur at the end of the name translation process. 379166Ssam * 387534Sroot * Overall outline: 397534Sroot * 407534Sroot * copy in name 417534Sroot * get starting directory 427534Sroot * dirloop: 437534Sroot * check accessibility of directory 447534Sroot * dirloop2: 457534Sroot * copy next component of name to u.u_dent 467534Sroot * handle degenerate case where name is null string 477534Sroot * search for name in directory, to found or notfound 487534Sroot * notfound: 499166Ssam * if creating, return locked directory, leaving info on avail. slots 507534Sroot * else return error 517534Sroot * found: 527534Sroot * if at end of path and deleting, return information to allow delete 539166Ssam * if at end of path and rewriting (create and LOCKPARENT), lock targe 549166Ssam * inode and return info to allow rewrite 557534Sroot * if .. and on mounted filesys, look in mount table for parent 567534Sroot * if symbolic link, massage name in buffer and continue at dirloop 577534Sroot * if more components of name, do next level at dirloop 587534Sroot * return the answer as locked inode 599166Ssam * 609166Ssam * NOTE: (LOOKUP | LOCKPARENT) currently returns the parent inode, 619166Ssam * but unlocked. 6230Sbill */ 6330Sbill struct inode * 645972Swnj namei(func, flag, follow) 655972Swnj int (*func)(), flag, follow; 6630Sbill { 677534Sroot register char *cp; /* pointer into pathname argument */ 687534Sroot /* these variables refer to things which must be freed or unlocked */ 697534Sroot register struct inode *dp = 0; /* the directory we are searching */ 707534Sroot register struct fs *fs; /* file system that directory is in */ 717534Sroot register struct buf *bp = 0; /* a buffer of directory entries */ 727534Sroot register struct direct *ep; /* the current directory entry */ 737534Sroot int entryoffsetinblock; /* offset of ep in bp's buffer */ 747534Sroot register struct buf *nbp; /* buffer storing path name argument */ 757534Sroot /* these variables hold information about the search for a slot */ 767534Sroot enum {NONE, COMPACT, FOUND} slotstatus; 777534Sroot int slotoffset = -1; /* offset of area with free space */ 787534Sroot int slotsize; /* size of area at slotoffset */ 797534Sroot int slotfreespace; /* amount of space free in slot */ 807534Sroot int slotneeded; /* size of the entry we're seeking */ 817534Sroot /* */ 827534Sroot int dirsize; 837534Sroot int prevoff; /* u.u_offset of previous entry */ 847534Sroot int nlink = 0; /* number of symbolic links taken */ 857534Sroot struct inode *pdp; /* saved dp during symlink work */ 867534Sroot int i; 879166Ssam int lockparent; 8830Sbill 899166Ssam lockparent = flag & LOCKPARENT; 909166Ssam flag &= ~LOCKPARENT; 9130Sbill /* 927534Sroot * Get a buffer for the name to be translated, and copy the 937534Sroot * name into the buffer. 945972Swnj */ 956571Smckusic nbp = geteblk(MAXPATHLEN); 967534Sroot for (cp = nbp->b_un.b_addr; *cp = (*func)(); ) { 977534Sroot if ((*cp&0377) == ('/'|0200) || (*cp&0200) && flag != 2) { 986066Sroot u.u_error = EPERM; 997534Sroot goto bad; 1006066Sroot } 1016066Sroot cp++; 1026571Smckusic if (cp >= nbp->b_un.b_addr + MAXPATHLEN) { 1035972Swnj u.u_error = ENOENT; 1047534Sroot goto bad; 1055972Swnj } 1065972Swnj } 1077534Sroot if (u.u_error) 1087534Sroot goto bad; 1097534Sroot 1105972Swnj /* 1117534Sroot * Get starting directory. 11230Sbill */ 1137534Sroot cp = nbp->b_un.b_addr; 1145972Swnj if (*cp == '/') { 1155972Swnj while (*cp == '/') 1165972Swnj cp++; 11730Sbill if ((dp = u.u_rdir) == NULL) 11830Sbill dp = rootdir; 1197534Sroot } else 1207534Sroot dp = u.u_cdir; 1217534Sroot fs = dp->i_fs; 1225972Swnj ilock(dp); 1235972Swnj dp->i_count++; 1247534Sroot u.u_pdir = (struct inode *)0xc0000000; /* illegal */ 1257534Sroot 1267534Sroot /* 1277534Sroot * We come to dirloop to search a new directory. 1287534Sroot * The directory must be locked so that it can be 1297534Sroot * iput, and fs must be already set to dp->i_fs. 1307534Sroot */ 1316571Smckusic dirloop: 13230Sbill /* 1337534Sroot * Check accessiblity of directory. 13430Sbill */ 1357534Sroot if ((dp->i_mode&IFMT) != IFDIR) { 13630Sbill u.u_error = ENOTDIR; 1377534Sroot goto bad; 1387534Sroot } 1397534Sroot if (access(dp, IEXEC)) 1407534Sroot goto bad; 1417534Sroot 1426384Swnj dirloop2: 1437534Sroot /* 1447534Sroot * Copy next component of name to u.u_dent. 1457534Sroot */ 1467534Sroot for (i = 0; *cp != 0 && *cp != '/'; cp++) { 1476571Smckusic if (i >= MAXNAMLEN) { 1485972Swnj u.u_error = ENOENT; 1497534Sroot goto bad; 1505972Swnj } 1517534Sroot u.u_dent.d_name[i++] = *cp; 1525972Swnj } 1536571Smckusic u.u_dent.d_namlen = i; 1547534Sroot u.u_dent.d_name[i] = 0; 1557534Sroot 1567534Sroot /* 1577534Sroot * Check for degenerate name (e.g. / or "") 1587534Sroot * which is a way of talking about a directory, 1597534Sroot * e.g. like "/." or ".". 1607534Sroot */ 1617534Sroot if (u.u_dent.d_name[0] == 0) { 1629870Ssam if (flag || lockparent) { 1635972Swnj u.u_error = ENOENT; 1647534Sroot goto bad; 1655972Swnj } 1666571Smckusic brelse(nbp); 1676571Smckusic return (dp); 1685972Swnj } 1697534Sroot 1706571Smckusic /* 1717534Sroot * Suppress search for slots unless creating 1727534Sroot * file and at end of pathname, in which case 1737534Sroot * we watch for a place to put the new file in 1747534Sroot * case it doesn't already exist. 1756571Smckusic */ 1767534Sroot slotstatus = FOUND; 1779166Ssam if (flag == CREATE && *cp == 0) { 1787534Sroot slotstatus = NONE; 1797534Sroot slotfreespace = 0; 1807534Sroot slotneeded = DIRSIZ(&u.u_dent); 1817534Sroot } 1827534Sroot 1837534Sroot dirsize = roundup(dp->i_size, DIRBLKSIZ); 1846571Smckusic u.u_offset = 0; 1857534Sroot while (u.u_offset < dirsize) { 1865972Swnj /* 1875972Swnj * If offset is on a block boundary, 1885972Swnj * read the next directory block. 1895972Swnj * Release previous if it exists. 1905972Swnj */ 1916571Smckusic if (blkoff(fs, u.u_offset) == 0) { 1925972Swnj if (bp != NULL) 1935972Swnj brelse(bp); 1947605Ssam bp = blkatoff(dp, u.u_offset, (char **)0); 1957534Sroot if (bp == 0) 1967534Sroot goto bad; 1977534Sroot entryoffsetinblock = 0; 1985972Swnj } 1997534Sroot 2005972Swnj /* 2017534Sroot * If still looking for a slot, and at a DIRBLKSIZE 2027534Sroot * boundary, have to start looking for free space 2037534Sroot * again. 2046571Smckusic */ 2057534Sroot if (slotstatus == NONE && 2067534Sroot (entryoffsetinblock&(DIRBLKSIZ-1)) == 0) { 2077534Sroot slotoffset = -1; 2087534Sroot slotfreespace = 0; 2097534Sroot } 2107534Sroot 2117534Sroot /* 2127534Sroot * Get pointer to next entry, and do consistency checking: 2137534Sroot * record length must be multiple of 4 2147605Ssam * record length must not be zero 2157534Sroot * entry must fit in rest of this DIRBLKSIZ block 2167534Sroot * record must be large enough to contain name 2177605Ssam * When dirchk is set we also check: 2187605Ssam * name is not longer than MAXNAMLEN 2197534Sroot * name must be as long as advertised, and null terminated 2207605Ssam * Checking last two conditions is done only when dirchk is 2217605Ssam * set, to save time. 2227534Sroot */ 2237534Sroot ep = (struct direct *)(bp->b_un.b_addr + entryoffsetinblock); 2247534Sroot i = DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1)); 2257605Ssam if ((ep->d_reclen & 0x3) || ep->d_reclen == 0 || 2267605Ssam ep->d_reclen > i || DIRSIZ(ep) > ep->d_reclen || 2277605Ssam dirchk && (ep->d_namlen > MAXNAMLEN || dirbadname(ep))) { 2287534Sroot dirbad(dp, "mangled entry"); 2296571Smckusic u.u_offset += i; 2307534Sroot entryoffsetinblock += i; 2316571Smckusic continue; 2326571Smckusic } 2337534Sroot 2346571Smckusic /* 2357534Sroot * If an appropriate sized slot has not yet been found, 2366571Smckusic * check to see if one is available. Also accumulate space 2376571Smckusic * in the current block so that we can determine if 2386571Smckusic * compaction is viable. 2396571Smckusic */ 2407534Sroot if (slotstatus != FOUND) { 2417534Sroot int size = ep->d_reclen; 2427534Sroot 2436571Smckusic if (ep->d_ino != 0) 2446571Smckusic size -= DIRSIZ(ep); 2456571Smckusic if (size > 0) { 2467534Sroot if (size >= slotneeded) { 2477534Sroot slotstatus = FOUND; 2487534Sroot slotoffset = u.u_offset; 2497534Sroot slotsize = ep->d_reclen; 2507534Sroot } else if (slotstatus == NONE) { 2517534Sroot slotfreespace += size; 2527534Sroot if (slotoffset == -1) 2537534Sroot slotoffset = u.u_offset; 2547534Sroot if (slotfreespace >= slotneeded) { 2557534Sroot slotstatus = COMPACT; 2567534Sroot slotsize = 2577534Sroot u.u_offset+ep->d_reclen - 2587534Sroot slotoffset; 2597534Sroot } 2606571Smckusic } 2616571Smckusic } 2626571Smckusic } 2637534Sroot 2646571Smckusic /* 2657534Sroot * Check for a name match. 2665972Swnj */ 2677534Sroot if (ep->d_ino) { 2687534Sroot if (ep->d_namlen == u.u_dent.d_namlen && 2697534Sroot !bcmp(u.u_dent.d_name, ep->d_name, ep->d_namlen)) 2707534Sroot goto found; 2717534Sroot } 2727534Sroot prevoff = u.u_offset; 2736571Smckusic u.u_offset += ep->d_reclen; 2747534Sroot entryoffsetinblock += ep->d_reclen; 2757534Sroot } 2767534Sroot /* notfound: */ 2777534Sroot /* 2787534Sroot * If creating, and at end of pathname and current 2799166Ssam * directory has not been removed, then can consider 2809166Ssam * allowing file to be created. 2817534Sroot */ 2829166Ssam if (flag == CREATE && *cp == 0 && dp->i_nlink != 0) { 2835972Swnj /* 2847534Sroot * Access for write is interpreted as allowing 2857534Sroot * creation of files in the directory. 2865972Swnj */ 2877534Sroot if (access(dp, IWRITE)) 2887534Sroot goto bad; 2895972Swnj /* 2907534Sroot * Return an indication of where the new directory 2917534Sroot * entry should be put. If we didn't find a slot, 2927534Sroot * then set u.u_count to 0 indicating that the 2937534Sroot * new slot belongs at the end of the directory. 2947534Sroot * If we found a slot, then the new entry can be 2957534Sroot * put in the range [u.u_offset..u.u_offset+u.u_count) 2965972Swnj */ 2977534Sroot if (slotstatus == NONE) 2987534Sroot u.u_count = 0; 2997534Sroot else { 3007534Sroot u.u_offset = slotoffset; 3017534Sroot u.u_count = slotsize; 3025972Swnj } 3037534Sroot dp->i_flag |= IUPD|ICHG; 3047534Sroot if (bp) 3057534Sroot brelse(bp); 3067534Sroot brelse(nbp); 3075972Swnj /* 3087534Sroot * We return with the directory locked, so that 3097534Sroot * the parameters we set up above will still be 3107534Sroot * valid if we actually decide to do a direnter(). 3117534Sroot * We return NULL to indicate that the entry doesn't 3127534Sroot * currently exist, leaving a pointer to the (locked) 3137534Sroot * directory inode in u.u_pdir. 3145972Swnj */ 3157534Sroot u.u_pdir = dp; 3167534Sroot return (NULL); 3177534Sroot } 3187534Sroot u.u_error = ENOENT; 3197534Sroot goto bad; 3207534Sroot found: 3217534Sroot /* 3227534Sroot * Check that directory length properly reflects presence 3237534Sroot * of this entry. 3247534Sroot */ 3257605Ssam if (entryoffsetinblock + DIRSIZ(ep) > dp->i_size) { 3267534Sroot dirbad(dp, "i_size too small"); 3277605Ssam dp->i_size = entryoffsetinblock + DIRSIZ(ep); 3287534Sroot dp->i_flag |= IUPD|ICHG; 3297534Sroot } 3307534Sroot 3317534Sroot /* 3327534Sroot * Found component in pathname; save directory 3337534Sroot * entry in u.u_dent, and release directory buffer. 3347534Sroot */ 3357825Sroot bcopy((caddr_t)ep, (caddr_t)&u.u_dent, (u_int)DIRSIZ(ep)); 3367534Sroot brelse(bp); 3377534Sroot bp = NULL; 3387534Sroot 3397534Sroot /* 3407534Sroot * If deleting, and at end of pathname, return 3417534Sroot * parameters which can be used to remove file. 3429166Ssam * If the lockparent flag isn't set, we return only 3439166Ssam * the directory (in u.u_pdir), otherwise we go 3449166Ssam * on and lock the inode, being careful with ".". 3457534Sroot */ 3469166Ssam if (flag == DELETE && *cp == 0) { 3477534Sroot /* 3487534Sroot * Write access to directory required to delete files. 3497534Sroot */ 3507534Sroot if (access(dp, IWRITE)) 3517534Sroot goto bad; 3529166Ssam u.u_pdir = dp; /* for dirremove() */ 3537534Sroot /* 3547534Sroot * Return pointer to current entry in u.u_offset, 3557534Sroot * and distance past previous entry (if there 3567534Sroot * is a previous entry in this block) in u.u_count. 3577534Sroot * Save directory inode pointer in u.u_pdir for dirremove(). 3587534Sroot */ 3597534Sroot if ((u.u_offset&(DIRBLKSIZ-1)) == 0) 3607534Sroot u.u_count = 0; 3617534Sroot else 3627534Sroot u.u_count = u.u_offset - prevoff; 3639166Ssam if (lockparent) { 3649166Ssam if (dp->i_number == u.u_dent.d_ino) 3659166Ssam dp->i_count++; 3669166Ssam else { 3679166Ssam dp = iget(dp->i_dev, fs, u.u_dent.d_ino); 3689166Ssam if (dp == NULL) { 3699166Ssam iput(u.u_pdir); 3709166Ssam goto bad; 3719166Ssam } 3729166Ssam } 3739166Ssam } 3747534Sroot brelse(nbp); 3757534Sroot return (dp); 3767534Sroot } 3777534Sroot 3787534Sroot /* 3797534Sroot * Special handling for ".." allowing chdir out of mounted 3807534Sroot * file system: indirect .. in root inode to reevaluate 3817534Sroot * in directory file system was mounted on. 3827534Sroot */ 3837534Sroot if (u.u_dent.d_name[0] == '.' && u.u_dent.d_name[1] == '.' && 3847534Sroot u.u_dent.d_name[2] == '\0') { 3857534Sroot if (dp == u.u_rdir) 3867534Sroot u.u_dent.d_ino = dp->i_number; 3877534Sroot else if (u.u_dent.d_ino == ROOTINO && 3887534Sroot dp->i_number == ROOTINO) { 3897534Sroot for (i = 1; i < NMOUNT; i++) 3907534Sroot if (mount[i].m_bufp != NULL && 3917534Sroot mount[i].m_dev == dp->i_dev) { 3926571Smckusic iput(dp); 3937534Sroot dp = mount[i].m_inodp; 3945972Swnj ilock(dp); 3955972Swnj dp->i_count++; 3967534Sroot fs = dp->i_fs; 3977534Sroot cp -= 2; /* back over .. */ 3987534Sroot goto dirloop2; 3995972Swnj } 40030Sbill } 4017534Sroot } 4027534Sroot 4037534Sroot /* 4049166Ssam * If rewriting (rename), return the inode and the 4059166Ssam * information required to rewrite the present directory 4069166Ssam * Must get inode of directory entry to verify it's a 4079166Ssam * regular file, or empty directory. 4089166Ssam */ 4099166Ssam if ((flag == CREATE && lockparent) && *cp == 0) { 4109166Ssam if (access(dp, IWRITE)) 4119166Ssam goto bad; 4129166Ssam u.u_pdir = dp; /* for dirrewrite() */ 4139166Ssam /* 4149166Ssam * Careful about locking second inode. 4159166Ssam * This can only occur if the target is ".". 4169166Ssam */ 4179166Ssam if (dp->i_number == u.u_dent.d_ino) { 4189166Ssam u.u_error = EISDIR; /* XXX */ 4199166Ssam goto bad; 4209166Ssam } 4219166Ssam dp = iget(dp->i_dev, fs, u.u_dent.d_ino); 4229166Ssam if (dp == NULL) { 4239166Ssam iput(u.u_pdir); 4249166Ssam goto bad; 4259166Ssam } 4269166Ssam brelse(nbp); 4279166Ssam return (dp); 4289166Ssam } 4299166Ssam 4309166Ssam /* 43112011Smckusick * Check for symbolic link, which may require us to massage the 43212011Smckusick * name before we continue translation. We do not `iput' the 43312011Smckusick * directory because we may need it again if the symbolic link 43412011Smckusick * is relative to the current directory. Instead we save it 43512011Smckusick * unlocked as "pdp". We must get the target inode before unlocking 43612011Smckusick * the directory to insure that the inode will not be removed 43712011Smckusick * before we get it. We prevent deadlock by always fetching 43812011Smckusick * inodes from the root, moving down the directory tree. Thus 43912011Smckusick * when following backward pointers ".." we must unlock the 44012011Smckusick * parent directory before getting the requested directory. 44112011Smckusick * There is a potential race condition here if both the current 44212011Smckusick * and parent directories are removed before the `iget' for the 44312011Smckusick * inode associated with ".." returns. We hope that this occurs 44412011Smckusick * infrequently since we cannot avoid this race condition without 445*12492Ssam * implementing a sophisticated deadlock detection algorithm. 44612011Smckusick * Note also that this simple deadlock detection scheme will not 44712011Smckusick * work if the file system has any hard links other than ".." 44812011Smckusick * that point backwards in the directory structure. 4497534Sroot */ 4507534Sroot pdp = dp; 45112011Smckusick if (bcmp(u.u_dent.d_name, "..", 3) == 0) { 45212011Smckusick iunlock(pdp); /* race to get the inode */ 45312011Smckusick dp = iget(dp->i_dev, fs, u.u_dent.d_ino); 45412011Smckusick if (dp == NULL) 45512011Smckusick goto bad2; 45612011Smckusick } else if (dp->i_number == u.u_dent.d_ino) { 45712011Smckusick dp->i_count++; /* we want ourself, ie "." */ 45812011Smckusick } else { 45912011Smckusick dp = iget(dp->i_dev, fs, u.u_dent.d_ino); 46012011Smckusick iunlock(pdp); 46112011Smckusick if (dp == NULL) 46212011Smckusick goto bad2; 46312011Smckusick } 4647534Sroot fs = dp->i_fs; 4657534Sroot 4667534Sroot /* 4677534Sroot * Check for symbolic link 4687534Sroot */ 4697534Sroot if ((dp->i_mode & IFMT) == IFLNK && (follow || *cp == '/')) { 4707825Sroot u_int pathlen = strlen(cp) + 1; 4717534Sroot 4727534Sroot if (dp->i_size + pathlen >= MAXPATHLEN - 1 || 4737534Sroot ++nlink > MAXSYMLINKS) { 4747534Sroot u.u_error = ELOOP; 4757534Sroot goto bad2; 4767534Sroot } 4778957Sroot ovbcopy(cp, nbp->b_un.b_addr + dp->i_size, pathlen); 4787751Sroot u.u_error = 4799166Ssam rdwri(UIO_READ, dp, nbp->b_un.b_addr, (int)dp->i_size, 4807825Sroot 0, 1, (int *)0); 4817534Sroot if (u.u_error) 4827534Sroot goto bad2; 4837534Sroot cp = nbp->b_un.b_addr; 4847534Sroot iput(dp); 4855972Swnj if (*cp == '/') { 4867534Sroot irele(pdp); 4875972Swnj while (*cp == '/') 4885972Swnj cp++; 4897534Sroot if ((dp = u.u_rdir) == NULL) 4907534Sroot dp = rootdir; 4917534Sroot ilock(dp); 4927534Sroot dp->i_count++; 4937534Sroot } else { 4947534Sroot dp = pdp; 4957534Sroot ilock(dp); 4965972Swnj } 4977534Sroot fs = dp->i_fs; 4987534Sroot goto dirloop; 49930Sbill } 5007534Sroot 50130Sbill /* 5027534Sroot * Not a symbolic link. If more pathname, 5037534Sroot * continue at next component, else return. 50430Sbill */ 5057534Sroot if (*cp == '/') { 5067534Sroot while (*cp == '/') 5077534Sroot cp++; 5089166Ssam irele(pdp); 5097534Sroot goto dirloop; 51030Sbill } 5115972Swnj brelse(nbp); 5129166Ssam if (lockparent) 5139166Ssam u.u_pdir = pdp; 5149166Ssam else 5159166Ssam irele(pdp); 5167534Sroot return (dp); 5177534Sroot bad2: 5187534Sroot irele(pdp); 5197534Sroot bad: 5207534Sroot if (bp) 5217534Sroot brelse(bp); 5227534Sroot if (dp) 5237534Sroot iput(dp); 5247534Sroot brelse(nbp); 5256571Smckusic return (NULL); 52630Sbill } 52730Sbill 5287534Sroot dirbad(ip, how) 5297534Sroot struct inode *ip; 5307534Sroot char *how; 5317534Sroot { 5327534Sroot 5337534Sroot printf("%s: bad dir ino %d at offset %d: %s\n", 5347534Sroot ip->i_fs->fs_fsmnt, ip->i_number, u.u_offset, how); 5357534Sroot } 5367534Sroot 5377534Sroot dirbadname(ep) 5387534Sroot register struct direct *ep; 5397534Sroot { 5407534Sroot register int i; 5417534Sroot 5427534Sroot for (i = 0; i < ep->d_namlen; i++) 5437534Sroot if (ep->d_name[i] == 0) 5447534Sroot return (1); 5457534Sroot return (ep->d_name[i]); 5467534Sroot } 5477534Sroot 54830Sbill /* 5497534Sroot * Write a directory entry after a call to namei, using the parameters 5507534Sroot * which it left in the u. area. The argument ip is the inode which 5517534Sroot * the new directory entry will refer to. The u. area field u.u_pdir is 5527534Sroot * a pointer to the directory to be written, which was left locked by 5537534Sroot * namei. Remaining parameters (u.u_offset, u.u_count) indicate 5547534Sroot * how the space for the new entry is to be gotten. 5557534Sroot */ 5567534Sroot direnter(ip) 5577534Sroot struct inode *ip; 5585972Swnj { 5597534Sroot register struct direct *ep, *nep; 5607534Sroot struct buf *bp; 56111639Ssam int loc, spacefree, error = 0; 5628631Sroot u_int dsize; 5638631Sroot int newentrysize; 5647534Sroot char *dirbuf; 5655972Swnj 5667534Sroot u.u_dent.d_ino = ip->i_number; 5677534Sroot u.u_segflg = 1; 5687534Sroot newentrysize = DIRSIZ(&u.u_dent); 5697534Sroot if (u.u_count == 0) { 5707534Sroot /* 5717534Sroot * If u.u_count is 0, then namei could find no space in the 5727534Sroot * directory. In this case u.u_offset will be on a directory 5737534Sroot * block boundary and we will write the new entry into a fresh 5747534Sroot * block. 5757534Sroot */ 5767534Sroot if (u.u_offset&(DIRBLKSIZ-1)) 5777534Sroot panic("wdir: newblk"); 5787534Sroot u.u_dent.d_reclen = DIRBLKSIZ; 57910849Ssam error = rdwri(UIO_WRITE, u.u_pdir, (caddr_t)&u.u_dent, 5808631Sroot newentrysize, u.u_offset, 1, (int *)0); 5817534Sroot iput(u.u_pdir); 58210849Ssam return (error); 5837534Sroot } 5847534Sroot 5857534Sroot /* 5867534Sroot * If u.u_count is non-zero, then namei found space for the 5877534Sroot * new entry in the range u.u_offset to u.u_offset+u.u_count. 5887534Sroot * in the directory. To use this space, we may have to compact 5897534Sroot * the entries located there, by copying them together towards 5907534Sroot * the beginning of the block, leaving the free space in 5917534Sroot * one usable chunk at the end. 5927534Sroot */ 5937534Sroot 5947534Sroot /* 5957534Sroot * Increase size of directory if entry eats into new space. 5967534Sroot * This should never push the size past a new multiple of 5977534Sroot * DIRBLKSIZE. 5987534Sroot */ 5999166Ssam if (u.u_offset + u.u_count > u.u_pdir->i_size) 6007534Sroot u.u_pdir->i_size = u.u_offset + u.u_count; 6017534Sroot 6027534Sroot /* 6037534Sroot * Get the block containing the space for the new directory 60410849Ssam * entry. Should return error by result instead of u.u_error. 6057534Sroot */ 6067605Ssam bp = blkatoff(u.u_pdir, u.u_offset, (char **)&dirbuf); 6079166Ssam if (bp == 0) { 6089166Ssam iput(u.u_pdir); 60910849Ssam return (u.u_error); 6109166Ssam } 6117534Sroot 6127534Sroot /* 6137534Sroot * Find space for the new entry. In the simple case, the 6147534Sroot * entry at offset base will have the space. If it does 6157534Sroot * not, then namei arranged that compacting the region 6167534Sroot * u.u_offset to u.u_offset+u.u_count would yield the space. 6177534Sroot */ 6187534Sroot ep = (struct direct *)dirbuf; 6197534Sroot dsize = DIRSIZ(ep); 62011639Ssam spacefree = ep->d_reclen - dsize; 6217534Sroot for (loc = ep->d_reclen; loc < u.u_count; ) { 6227534Sroot nep = (struct direct *)(dirbuf + loc); 6237534Sroot if (ep->d_ino) { 6247534Sroot /* trim the existing slot */ 6257534Sroot ep->d_reclen = dsize; 6267534Sroot ep = (struct direct *)((char *)ep + dsize); 6277534Sroot } else { 6287534Sroot /* overwrite; nothing there; header is ours */ 62911639Ssam spacefree += dsize; 6307534Sroot } 6317534Sroot dsize = DIRSIZ(nep); 63211639Ssam spacefree += nep->d_reclen - dsize; 6337534Sroot loc += nep->d_reclen; 6347825Sroot bcopy((caddr_t)nep, (caddr_t)ep, dsize); 6357534Sroot } 6367534Sroot /* 6377534Sroot * Update the pointer fields in the previous entry (if any), 6387534Sroot * copy in the new entry, and write out the block. 6397534Sroot */ 6407534Sroot if (ep->d_ino == 0) { 64111639Ssam if (spacefree + dsize < newentrysize) 6427534Sroot panic("wdir: compact1"); 64311639Ssam u.u_dent.d_reclen = spacefree + dsize; 6447534Sroot } else { 64511639Ssam if (spacefree < newentrysize) 6467534Sroot panic("wdir: compact2"); 64711639Ssam u.u_dent.d_reclen = spacefree; 6487534Sroot ep->d_reclen = dsize; 6497534Sroot ep = (struct direct *)((char *)ep + dsize); 6507534Sroot } 6518672Sroot bcopy((caddr_t)&u.u_dent, (caddr_t)ep, (u_int)newentrysize); 6527534Sroot bwrite(bp); 6537534Sroot u.u_pdir->i_flag |= IUPD|ICHG; 6547534Sroot iput(u.u_pdir); 65510849Ssam return (error); 6565972Swnj } 6576571Smckusic 6589166Ssam /* 6599166Ssam * Remove a directory entry after a call to namei, using the 6609166Ssam * parameters which it left in the u. area. The u. entry 6619166Ssam * u_offset contains the offset into the directory of the 6629166Ssam * entry to be eliminated. The u_count field contains the 6639166Ssam * size of the previous record in the directory. If this 6649166Ssam * is 0, the first entry is being deleted, so we need only 6659166Ssam * zero the inode number to mark the entry as free. If the 6669166Ssam * entry isn't the first in the directory, we must reclaim 6679166Ssam * the space of the now empty record by adding the record size 6689166Ssam * to the size of the previous entry. 6699166Ssam */ 6707534Sroot dirremove() 6716571Smckusic { 6727534Sroot register struct inode *dp = u.u_pdir; 6737534Sroot register struct buf *bp; 6747534Sroot struct direct *ep; 6756571Smckusic 6769269Ssam if (u.u_count == 0) { 6777534Sroot /* 6787534Sroot * First entry in block: set d_ino to zero. 6797534Sroot */ 6809269Ssam u.u_dent.d_ino = 0; 6818619Sroot (void) rdwri(UIO_WRITE, dp, (caddr_t)&u.u_dent, 6828631Sroot (int)DIRSIZ(&u.u_dent), u.u_offset, 1, (int *)0); 6839269Ssam } else { 6847534Sroot /* 6857534Sroot * Collapse new free space into previous entry. 6867534Sroot */ 6877825Sroot bp = blkatoff(dp, (int)(u.u_offset - u.u_count), (char **)&ep); 6887534Sroot if (bp == 0) 6897534Sroot return (0); 6907534Sroot ep->d_reclen += u.u_dent.d_reclen; 6917534Sroot bwrite(bp); 6927534Sroot dp->i_flag |= IUPD|ICHG; 6937534Sroot } 6947534Sroot return (1); 6956571Smckusic } 6967534Sroot 6977605Ssam /* 6989166Ssam * Rewrite an existing directory entry to point at the inode 6999166Ssam * supplied. The parameters describing the directory entry are 7009166Ssam * set up by a call to namei. 7019166Ssam */ 7029166Ssam dirrewrite(dp, ip) 7039166Ssam struct inode *dp, *ip; 7049166Ssam { 7059166Ssam 7069166Ssam u.u_dent.d_ino = ip->i_number; 7079166Ssam u.u_error = rdwri(UIO_WRITE, dp, (caddr_t)&u.u_dent, 7089166Ssam (int)DIRSIZ(&u.u_dent), u.u_offset, 1, (int *)0); 7099166Ssam iput(dp); 7109166Ssam } 7119166Ssam 7129166Ssam /* 7137605Ssam * Return buffer with contents of block "offset" 7147605Ssam * from the beginning of directory "ip". If "res" 7157605Ssam * is non-zero, fill it in with a pointer to the 7167605Ssam * remaining space in the directory. 7177605Ssam */ 7187534Sroot struct buf * 7197605Ssam blkatoff(ip, offset, res) 7207534Sroot struct inode *ip; 7217534Sroot off_t offset; 7227534Sroot char **res; 7237534Sroot { 7247534Sroot register struct fs *fs = ip->i_fs; 7258672Sroot daddr_t lbn = lblkno(fs, offset); 7267534Sroot int base = blkoff(fs, offset); 7277534Sroot int bsize = blksize(fs, ip, lbn); 7288672Sroot daddr_t bn = fsbtodb(fs, bmap(ip, lbn, B_WRITE, base, bsize)); 7297534Sroot register struct buf *bp; 7307534Sroot 7317534Sroot if (u.u_error) 7327534Sroot return (0); 7337534Sroot bp = bread(ip->i_dev, bn, bsize); 7347534Sroot if (bp->b_flags & B_ERROR) { 7357534Sroot brelse(bp); 7367534Sroot return (0); 7377534Sroot } 7387534Sroot if (res) 7397534Sroot *res = bp->b_un.b_addr + base; 7407534Sroot return (bp); 7417534Sroot } 7429166Ssam 7439166Ssam /* 7449166Ssam * Check if a directory is empty or not. 7459166Ssam * Inode supplied must be locked. 7469166Ssam */ 7479166Ssam dirempty(ip) 7489863Ssam register struct inode *ip; 7499166Ssam { 7509166Ssam register off_t off; 7519166Ssam struct direct dbuf; 7529166Ssam register struct direct *dp = &dbuf; 7539863Ssam int error, count; 7549166Ssam 7559166Ssam for (off = 0; off < ip->i_size; off += dp->d_reclen) { 7569166Ssam error = rdwri(UIO_READ, ip, (caddr_t)dp, 7579863Ssam sizeof (struct direct), off, 1, &count); 7589863Ssam count = sizeof (struct direct) - count; 7599863Ssam #define MINDIRSIZ (sizeof (struct direct) - (MAXNAMLEN + 1)) 7609863Ssam if (error || count < MINDIRSIZ || count < DIRSIZ(dp)) 7619166Ssam return (0); 7629166Ssam if (dp->d_ino == 0) 7639166Ssam continue; 7649166Ssam if (dp->d_name[0] != '.') 7659166Ssam return (0); 7669166Ssam if (dp->d_namlen == 1 || 7679166Ssam (dp->d_namlen == 2 && dp->d_name[1] == '.')) 7689166Ssam continue; 7699166Ssam return (0); 7709166Ssam } 7719166Ssam return (1); 7729166Ssam } 773