1*9269Ssam /* vfs_lookup.c 4.31 82/11/17 */ 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) { 1627534Sroot if (flag) { 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 /* 4317534Sroot * Check for symbolic link, which may require us 4327534Sroot * to massage the name before we continue translation. 4337534Sroot * To avoid deadlock have to unlock the current directory, 4347534Sroot * but don't iput it because we may need it again (if 4357534Sroot * the symbolic link is relative to .). Instead save 4367534Sroot * it (unlocked) as pdp. 4377534Sroot */ 4387534Sroot pdp = dp; 4397534Sroot iunlock(pdp); 4407534Sroot dp = iget(dp->i_dev, fs, u.u_dent.d_ino); 4417534Sroot if (dp == NULL) 4427534Sroot goto bad2; 4437534Sroot fs = dp->i_fs; 4447534Sroot 4457534Sroot /* 4467534Sroot * Check for symbolic link 4477534Sroot */ 4487534Sroot if ((dp->i_mode & IFMT) == IFLNK && (follow || *cp == '/')) { 4497825Sroot u_int pathlen = strlen(cp) + 1; 4507534Sroot 4517534Sroot if (dp->i_size + pathlen >= MAXPATHLEN - 1 || 4527534Sroot ++nlink > MAXSYMLINKS) { 4537534Sroot u.u_error = ELOOP; 4547534Sroot goto bad2; 4557534Sroot } 4568957Sroot ovbcopy(cp, nbp->b_un.b_addr + dp->i_size, pathlen); 4577751Sroot u.u_error = 4589166Ssam rdwri(UIO_READ, dp, nbp->b_un.b_addr, (int)dp->i_size, 4597825Sroot 0, 1, (int *)0); 4607534Sroot if (u.u_error) 4617534Sroot goto bad2; 4627534Sroot cp = nbp->b_un.b_addr; 4637534Sroot iput(dp); 4645972Swnj if (*cp == '/') { 4657534Sroot irele(pdp); 4665972Swnj while (*cp == '/') 4675972Swnj cp++; 4687534Sroot if ((dp = u.u_rdir) == NULL) 4697534Sroot dp = rootdir; 4707534Sroot ilock(dp); 4717534Sroot dp->i_count++; 4727534Sroot } else { 4737534Sroot dp = pdp; 4747534Sroot ilock(dp); 4755972Swnj } 4767534Sroot fs = dp->i_fs; 4777534Sroot goto dirloop; 47830Sbill } 4797534Sroot 48030Sbill /* 4817534Sroot * Not a symbolic link. If more pathname, 4827534Sroot * continue at next component, else return. 48330Sbill */ 4847534Sroot if (*cp == '/') { 4857534Sroot while (*cp == '/') 4867534Sroot cp++; 4879166Ssam irele(pdp); 4887534Sroot goto dirloop; 48930Sbill } 4905972Swnj brelse(nbp); 4919166Ssam if (lockparent) 4929166Ssam u.u_pdir = pdp; 4939166Ssam else 4949166Ssam irele(pdp); 4957534Sroot return (dp); 4967534Sroot bad2: 4977534Sroot irele(pdp); 4987534Sroot bad: 4997534Sroot if (bp) 5007534Sroot brelse(bp); 5017534Sroot if (dp) 5027534Sroot iput(dp); 5037534Sroot brelse(nbp); 5046571Smckusic return (NULL); 50530Sbill } 50630Sbill 5077534Sroot dirbad(ip, how) 5087534Sroot struct inode *ip; 5097534Sroot char *how; 5107534Sroot { 5117534Sroot 5127534Sroot printf("%s: bad dir ino %d at offset %d: %s\n", 5137534Sroot ip->i_fs->fs_fsmnt, ip->i_number, u.u_offset, how); 5147534Sroot } 5157534Sroot 5167534Sroot dirbadname(ep) 5177534Sroot register struct direct *ep; 5187534Sroot { 5197534Sroot register int i; 5207534Sroot 5217534Sroot for (i = 0; i < ep->d_namlen; i++) 5227534Sroot if (ep->d_name[i] == 0) 5237534Sroot return (1); 5247534Sroot return (ep->d_name[i]); 5257534Sroot } 5267534Sroot 52730Sbill /* 5287534Sroot * Write a directory entry after a call to namei, using the parameters 5297534Sroot * which it left in the u. area. The argument ip is the inode which 5307534Sroot * the new directory entry will refer to. The u. area field u.u_pdir is 5317534Sroot * a pointer to the directory to be written, which was left locked by 5327534Sroot * namei. Remaining parameters (u.u_offset, u.u_count) indicate 5337534Sroot * how the space for the new entry is to be gotten. 5347534Sroot */ 5357534Sroot direnter(ip) 5367534Sroot struct inode *ip; 5375972Swnj { 5387534Sroot register struct direct *ep, *nep; 5397534Sroot struct buf *bp; 5407825Sroot int loc, freespace; 5418631Sroot u_int dsize; 5428631Sroot int newentrysize; 5437534Sroot char *dirbuf; 5445972Swnj 5457534Sroot u.u_dent.d_ino = ip->i_number; 5467534Sroot u.u_segflg = 1; 5477534Sroot newentrysize = DIRSIZ(&u.u_dent); 5487534Sroot if (u.u_count == 0) { 5497534Sroot /* 5507534Sroot * If u.u_count is 0, then namei could find no space in the 5517534Sroot * directory. In this case u.u_offset will be on a directory 5527534Sroot * block boundary and we will write the new entry into a fresh 5537534Sroot * block. 5547534Sroot */ 5557534Sroot if (u.u_offset&(DIRBLKSIZ-1)) 5567534Sroot panic("wdir: newblk"); 5577534Sroot u.u_dent.d_reclen = DIRBLKSIZ; 5588631Sroot (void) rdwri(UIO_WRITE, u.u_pdir, (caddr_t)&u.u_dent, 5598631Sroot newentrysize, u.u_offset, 1, (int *)0); 5607534Sroot iput(u.u_pdir); 5617534Sroot return; 5627534Sroot } 5637534Sroot 5647534Sroot /* 5657534Sroot * If u.u_count is non-zero, then namei found space for the 5667534Sroot * new entry in the range u.u_offset to u.u_offset+u.u_count. 5677534Sroot * in the directory. To use this space, we may have to compact 5687534Sroot * the entries located there, by copying them together towards 5697534Sroot * the beginning of the block, leaving the free space in 5707534Sroot * one usable chunk at the end. 5717534Sroot */ 5727534Sroot 5737534Sroot /* 5747534Sroot * Increase size of directory if entry eats into new space. 5757534Sroot * This should never push the size past a new multiple of 5767534Sroot * DIRBLKSIZE. 5777534Sroot */ 5789166Ssam if (u.u_offset + u.u_count > u.u_pdir->i_size) 5797534Sroot u.u_pdir->i_size = u.u_offset + u.u_count; 5807534Sroot 5817534Sroot /* 5827534Sroot * Get the block containing the space for the new directory 5837534Sroot * entry. 5847534Sroot */ 5857605Ssam bp = blkatoff(u.u_pdir, u.u_offset, (char **)&dirbuf); 5869166Ssam if (bp == 0) { 5879166Ssam iput(u.u_pdir); 5887534Sroot return; 5899166Ssam } 5907534Sroot 5917534Sroot /* 5927534Sroot * Find space for the new entry. In the simple case, the 5937534Sroot * entry at offset base will have the space. If it does 5947534Sroot * not, then namei arranged that compacting the region 5957534Sroot * u.u_offset to u.u_offset+u.u_count would yield the space. 5967534Sroot */ 5977534Sroot ep = (struct direct *)dirbuf; 5987534Sroot dsize = DIRSIZ(ep); 5997534Sroot freespace = ep->d_reclen - dsize; 6007534Sroot for (loc = ep->d_reclen; loc < u.u_count; ) { 6017534Sroot nep = (struct direct *)(dirbuf + loc); 6027534Sroot if (ep->d_ino) { 6037534Sroot /* trim the existing slot */ 6047534Sroot ep->d_reclen = dsize; 6057534Sroot ep = (struct direct *)((char *)ep + dsize); 6067534Sroot } else { 6077534Sroot /* overwrite; nothing there; header is ours */ 6087534Sroot freespace += dsize; 6097534Sroot } 6107534Sroot dsize = DIRSIZ(nep); 6117534Sroot freespace += nep->d_reclen - dsize; 6127534Sroot loc += nep->d_reclen; 6137825Sroot bcopy((caddr_t)nep, (caddr_t)ep, dsize); 6147534Sroot } 6157534Sroot /* 6167534Sroot * Update the pointer fields in the previous entry (if any), 6177534Sroot * copy in the new entry, and write out the block. 6187534Sroot */ 6197534Sroot if (ep->d_ino == 0) { 6207534Sroot if (freespace + dsize < newentrysize) 6217534Sroot panic("wdir: compact1"); 6227534Sroot u.u_dent.d_reclen = freespace + dsize; 6237534Sroot } else { 6247534Sroot if (freespace < newentrysize) 6257534Sroot panic("wdir: compact2"); 6267534Sroot u.u_dent.d_reclen = freespace; 6277534Sroot ep->d_reclen = dsize; 6287534Sroot ep = (struct direct *)((char *)ep + dsize); 6297534Sroot } 6308672Sroot bcopy((caddr_t)&u.u_dent, (caddr_t)ep, (u_int)newentrysize); 6317534Sroot bwrite(bp); 6327534Sroot u.u_pdir->i_flag |= IUPD|ICHG; 6337534Sroot iput(u.u_pdir); 6345972Swnj } 6356571Smckusic 6369166Ssam /* 6379166Ssam * Remove a directory entry after a call to namei, using the 6389166Ssam * parameters which it left in the u. area. The u. entry 6399166Ssam * u_offset contains the offset into the directory of the 6409166Ssam * entry to be eliminated. The u_count field contains the 6419166Ssam * size of the previous record in the directory. If this 6429166Ssam * is 0, the first entry is being deleted, so we need only 6439166Ssam * zero the inode number to mark the entry as free. If the 6449166Ssam * entry isn't the first in the directory, we must reclaim 6459166Ssam * the space of the now empty record by adding the record size 6469166Ssam * to the size of the previous entry. 6479166Ssam */ 6487534Sroot dirremove() 6496571Smckusic { 6507534Sroot register struct inode *dp = u.u_pdir; 6517534Sroot register struct buf *bp; 6527534Sroot struct direct *ep; 6536571Smckusic 654*9269Ssam if (u.u_count == 0) { 6557534Sroot /* 6567534Sroot * First entry in block: set d_ino to zero. 6577534Sroot */ 658*9269Ssam u.u_dent.d_ino = 0; 6598619Sroot (void) rdwri(UIO_WRITE, dp, (caddr_t)&u.u_dent, 6608631Sroot (int)DIRSIZ(&u.u_dent), u.u_offset, 1, (int *)0); 661*9269Ssam } else { 6627534Sroot /* 6637534Sroot * Collapse new free space into previous entry. 6647534Sroot */ 6657825Sroot bp = blkatoff(dp, (int)(u.u_offset - u.u_count), (char **)&ep); 6667534Sroot if (bp == 0) 6677534Sroot return (0); 6687534Sroot ep->d_reclen += u.u_dent.d_reclen; 6697534Sroot bwrite(bp); 6707534Sroot dp->i_flag |= IUPD|ICHG; 6717534Sroot } 6727534Sroot return (1); 6736571Smckusic } 6747534Sroot 6757605Ssam /* 6769166Ssam * Rewrite an existing directory entry to point at the inode 6779166Ssam * supplied. The parameters describing the directory entry are 6789166Ssam * set up by a call to namei. 6799166Ssam */ 6809166Ssam dirrewrite(dp, ip) 6819166Ssam struct inode *dp, *ip; 6829166Ssam { 6839166Ssam 6849166Ssam u.u_dent.d_ino = ip->i_number; 6859166Ssam u.u_error = rdwri(UIO_WRITE, dp, (caddr_t)&u.u_dent, 6869166Ssam (int)DIRSIZ(&u.u_dent), u.u_offset, 1, (int *)0); 6879166Ssam iput(dp); 6889166Ssam } 6899166Ssam 6909166Ssam /* 6917605Ssam * Return buffer with contents of block "offset" 6927605Ssam * from the beginning of directory "ip". If "res" 6937605Ssam * is non-zero, fill it in with a pointer to the 6947605Ssam * remaining space in the directory. 6957605Ssam */ 6967534Sroot struct buf * 6977605Ssam blkatoff(ip, offset, res) 6987534Sroot struct inode *ip; 6997534Sroot off_t offset; 7007534Sroot char **res; 7017534Sroot { 7027534Sroot register struct fs *fs = ip->i_fs; 7038672Sroot daddr_t lbn = lblkno(fs, offset); 7047534Sroot int base = blkoff(fs, offset); 7057534Sroot int bsize = blksize(fs, ip, lbn); 7068672Sroot daddr_t bn = fsbtodb(fs, bmap(ip, lbn, B_WRITE, base, bsize)); 7077534Sroot register struct buf *bp; 7087534Sroot 7097534Sroot if (u.u_error) 7107534Sroot return (0); 7117534Sroot bp = bread(ip->i_dev, bn, bsize); 7127534Sroot if (bp->b_flags & B_ERROR) { 7137534Sroot brelse(bp); 7147534Sroot return (0); 7157534Sroot } 7167534Sroot if (res) 7177534Sroot *res = bp->b_un.b_addr + base; 7187534Sroot return (bp); 7197534Sroot } 7209166Ssam 7219166Ssam /* 7229166Ssam * Check if a directory is empty or not. 7239166Ssam * Inode supplied must be locked. 7249166Ssam */ 7259166Ssam dirempty(ip) 7269166Ssam struct inode *ip; 7279166Ssam { 7289166Ssam register off_t off; 7299166Ssam struct direct dbuf; 7309166Ssam register struct direct *dp = &dbuf; 7319166Ssam int error; 7329166Ssam 7339166Ssam for (off = 0; off < ip->i_size; off += dp->d_reclen) { 7349166Ssam error = rdwri(UIO_READ, ip, (caddr_t)dp, 7359166Ssam sizeof (struct direct), off, 1, (int *)0); 7369166Ssam if (error) 7379166Ssam return (0); 7389166Ssam if (dp->d_ino == 0) 7399166Ssam continue; 7409166Ssam if (dp->d_name[0] != '.') 7419166Ssam return (0); 7429166Ssam if (dp->d_namlen == 1 || 7439166Ssam (dp->d_namlen == 2 && dp->d_name[1] == '.')) 7449166Ssam continue; 7459166Ssam return (0); 7469166Ssam } 7479166Ssam return (1); 7489166Ssam } 749