18a16b7a1SPedro F. Giffuni /*- 28a16b7a1SPedro F. Giffuni * SPDX-License-Identifier: BSD-3-Clause 38a16b7a1SPedro F. Giffuni * 48fae3551SRodney W. Grimes * Copyright (c) 1980, 1986, 1993 58fae3551SRodney W. Grimes * The Regents of the University of California. All rights reserved. 68fae3551SRodney W. Grimes * 78fae3551SRodney W. Grimes * Redistribution and use in source and binary forms, with or without 88fae3551SRodney W. Grimes * modification, are permitted provided that the following conditions 98fae3551SRodney W. Grimes * are met: 108fae3551SRodney W. Grimes * 1. Redistributions of source code must retain the above copyright 118fae3551SRodney W. Grimes * notice, this list of conditions and the following disclaimer. 128fae3551SRodney W. Grimes * 2. Redistributions in binary form must reproduce the above copyright 138fae3551SRodney W. Grimes * notice, this list of conditions and the following disclaimer in the 148fae3551SRodney W. Grimes * documentation and/or other materials provided with the distribution. 15fbbd9655SWarner Losh * 3. Neither the name of the University nor the names of its contributors 168fae3551SRodney W. Grimes * may be used to endorse or promote products derived from this software 178fae3551SRodney W. Grimes * without specific prior written permission. 188fae3551SRodney W. Grimes * 198fae3551SRodney W. Grimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 208fae3551SRodney W. Grimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 218fae3551SRodney W. Grimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 228fae3551SRodney W. Grimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 238fae3551SRodney W. Grimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 248fae3551SRodney W. Grimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 258fae3551SRodney W. Grimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 268fae3551SRodney W. Grimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 278fae3551SRodney W. Grimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 288fae3551SRodney W. Grimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 298fae3551SRodney W. Grimes * SUCH DAMAGE. 308fae3551SRodney W. Grimes */ 318fae3551SRodney W. Grimes 328fae3551SRodney W. Grimes #include <sys/param.h> 33d33e92f9SJulian Elischer #include <sys/time.h> 3413eb765fSBaptiste Daroussin #include <sys/types.h> 357578c6abSKirk McKusick #include <sys/sysctl.h> 36780a5c1eSPeter Wemm 378fae3551SRodney W. Grimes #include <ufs/ufs/dinode.h> 388fae3551SRodney W. Grimes #include <ufs/ufs/dir.h> 398fae3551SRodney W. Grimes #include <ufs/ffs/fs.h> 4004aba254SBruce Evans 41780a5c1eSPeter Wemm #include <err.h> 428fae3551SRodney W. Grimes #include <string.h> 43780a5c1eSPeter Wemm 448fae3551SRodney W. Grimes #include "fsck.h" 458fae3551SRodney W. Grimes 467703a6ffSScott Long static struct dirtemplate emptydir = { 477578c6abSKirk McKusick 0, DIRBLKSIZ, DT_UNKNOWN, 0, "", 487578c6abSKirk McKusick 0, 0, DT_UNKNOWN, 0, "" 497578c6abSKirk McKusick }; 507703a6ffSScott Long static struct dirtemplate dirhead = { 518fae3551SRodney W. Grimes 0, 12, DT_DIR, 1, ".", 528fae3551SRodney W. Grimes 0, DIRBLKSIZ - 12, DT_DIR, 2, ".." 538fae3551SRodney W. Grimes }; 548fae3551SRodney W. Grimes 55b70cd7eeSWarner Losh static int chgino(struct inodesc *); 56bfc5d3f9SKirk McKusick static int dircheck(struct inodesc *, struct bufarea *, struct direct *); 575cc52631SKirk McKusick static int expanddir(struct inode *ip, char *name); 58b70cd7eeSWarner Losh static struct direct *fsck_readdir(struct inodesc *); 591c85e6a3SKirk McKusick static struct bufarea *getdirblk(ufs2_daddr_t blkno, long size); 60b70cd7eeSWarner Losh static int lftempname(char *bufp, ino_t ino); 61b70cd7eeSWarner Losh static int mkentry(struct inodesc *); 628fae3551SRodney W. Grimes 638fae3551SRodney W. Grimes /* 648fae3551SRodney W. Grimes * Propagate connected state through the tree. 658fae3551SRodney W. Grimes */ 6631f4ab50SBruce Evans void 67b70cd7eeSWarner Losh propagate(void) 688fae3551SRodney W. Grimes { 693d438ad6SDavid E. O'Brien struct inoinfo **inpp, *inp; 708fae3551SRodney W. Grimes struct inoinfo **inpend; 718fae3551SRodney W. Grimes long change; 728fae3551SRodney W. Grimes 738fae3551SRodney W. Grimes inpend = &inpsort[inplast]; 748fae3551SRodney W. Grimes do { 758fae3551SRodney W. Grimes change = 0; 768fae3551SRodney W. Grimes for (inpp = inpsort; inpp < inpend; inpp++) { 778fae3551SRodney W. Grimes inp = *inpp; 788fae3551SRodney W. Grimes if (inp->i_parent == 0) 798fae3551SRodney W. Grimes continue; 80d33e92f9SJulian Elischer if (inoinfo(inp->i_parent)->ino_state == DFOUND && 81af6726e6SDon Lewis INO_IS_DUNFOUND(inp->i_number)) { 82d33e92f9SJulian Elischer inoinfo(inp->i_number)->ino_state = DFOUND; 83fe5e6e2cSKirk McKusick check_dirdepth(inp); 848fae3551SRodney W. Grimes change++; 858fae3551SRodney W. Grimes } 868fae3551SRodney W. Grimes } 878fae3551SRodney W. Grimes } while (change > 0); 888fae3551SRodney W. Grimes } 898fae3551SRodney W. Grimes 908fae3551SRodney W. Grimes /* 91fe5e6e2cSKirk McKusick * Check that the recorded depth of the directory is correct. 92fe5e6e2cSKirk McKusick */ 93fe5e6e2cSKirk McKusick void 94fe5e6e2cSKirk McKusick check_dirdepth(struct inoinfo *inp) 95fe5e6e2cSKirk McKusick { 96fe5e6e2cSKirk McKusick struct inoinfo *parentinp; 97fe5e6e2cSKirk McKusick struct inode ip; 98fe5e6e2cSKirk McKusick union dinode *dp; 99fe5e6e2cSKirk McKusick int saveresolved; 100e4a905d1SKirk McKusick size_t size; 101fe5e6e2cSKirk McKusick static int updateasked, dirdepthupdate; 102fe5e6e2cSKirk McKusick 103fe5e6e2cSKirk McKusick if ((parentinp = getinoinfo(inp->i_parent)) == NULL) { 104fe5e6e2cSKirk McKusick pfatal("check_dirdepth: UNKNOWN PARENT DIR"); 105fe5e6e2cSKirk McKusick return; 106fe5e6e2cSKirk McKusick } 107fe5e6e2cSKirk McKusick /* 108fe5e6e2cSKirk McKusick * If depth is correct, nothing to do. 109fe5e6e2cSKirk McKusick */ 110fe5e6e2cSKirk McKusick if (parentinp->i_depth + 1 == inp->i_depth) 111fe5e6e2cSKirk McKusick return; 112fe5e6e2cSKirk McKusick /* 113fe5e6e2cSKirk McKusick * Only the root inode should have depth of 0, so if any other 114fe5e6e2cSKirk McKusick * directory has a depth of 0 then this is an old filesystem 115fe5e6e2cSKirk McKusick * that has not been tracking directory depth. Ask just once 116fe5e6e2cSKirk McKusick * whether it should start tracking directory depth. 117fe5e6e2cSKirk McKusick */ 118fe5e6e2cSKirk McKusick if (inp->i_depth == 0 && updateasked == 0) { 119fe5e6e2cSKirk McKusick updateasked = 1; 120fe5e6e2cSKirk McKusick if (preen) { 12111ce203eSKirk McKusick pwarn("UPDATING FILESYSTEM TO TRACK DIRECTORY DEPTH\n"); 122fe5e6e2cSKirk McKusick dirdepthupdate = 1; 123fe5e6e2cSKirk McKusick } else { 124fe5e6e2cSKirk McKusick /* 125fe5e6e2cSKirk McKusick * The file system can be marked clean even if 126fe5e6e2cSKirk McKusick * a directory does not have the right depth. 127fe5e6e2cSKirk McKusick * Hence, resolved should not be cleared when 128fe5e6e2cSKirk McKusick * the filesystem does not update directory depths. 129fe5e6e2cSKirk McKusick */ 130fe5e6e2cSKirk McKusick saveresolved = resolved; 131fe5e6e2cSKirk McKusick dirdepthupdate = 132fe5e6e2cSKirk McKusick reply("UPDATE FILESYSTEM TO TRACK DIRECTORY DEPTH"); 133fe5e6e2cSKirk McKusick resolved = saveresolved; 134fe5e6e2cSKirk McKusick } 135fe5e6e2cSKirk McKusick } 136fe5e6e2cSKirk McKusick /* 137e4a905d1SKirk McKusick * If we are not converting or we are running in no-write mode 138e4a905d1SKirk McKusick * there is nothing more to do. 139fe5e6e2cSKirk McKusick */ 140e4a905d1SKirk McKusick if ((inp->i_depth == 0 && dirdepthupdate == 0) || 141e4a905d1SKirk McKusick (fswritefd < 0 && bkgrdflag == 0)) 142fe5e6e2cSKirk McKusick return; 143fe5e6e2cSKirk McKusick /* 144fe5e6e2cSKirk McKusick * Individual directory at wrong depth. Report it and correct if 145fe5e6e2cSKirk McKusick * in preen mode or ask if in interactive mode. Note that if a 146fe5e6e2cSKirk McKusick * directory is renamed to a new location that is at a different 147fe5e6e2cSKirk McKusick * level in the tree, its depth will be recalculated, but none of 148fe5e6e2cSKirk McKusick * the directories that it contains will be updated. Thus it is 149fe5e6e2cSKirk McKusick * not unexpected to find directories with incorrect depths. No 150fe5e6e2cSKirk McKusick * operational harm will come from this though new directory 151fe5e6e2cSKirk McKusick * placement in the subtree may not be as optimal until the depths 152fe5e6e2cSKirk McKusick * of the affected directories are corrected. 153fe5e6e2cSKirk McKusick * 154fe5e6e2cSKirk McKusick * To avoid much spurious output on otherwise clean filesystems 155fe5e6e2cSKirk McKusick * we only generate detailed output when the debug flag is given. 156fe5e6e2cSKirk McKusick */ 157fe5e6e2cSKirk McKusick ginode(inp->i_number, &ip); 158fe5e6e2cSKirk McKusick dp = ip.i_dp; 159fe5e6e2cSKirk McKusick if (inp->i_depth != 0 && debug) { 160fe5e6e2cSKirk McKusick pwarn("DIRECTORY"); 161fe5e6e2cSKirk McKusick prtinode(&ip); 162fe5e6e2cSKirk McKusick printf(" DEPTH %d SHOULD BE %d", inp->i_depth, 163fe5e6e2cSKirk McKusick parentinp->i_depth + 1); 164fe5e6e2cSKirk McKusick if (preen == 0 && reply("ADJUST") == 0) { 165fe5e6e2cSKirk McKusick irelse(&ip); 166fe5e6e2cSKirk McKusick return; 167fe5e6e2cSKirk McKusick } 168fe5e6e2cSKirk McKusick if (preen) 169fe5e6e2cSKirk McKusick printf(" (ADJUSTED)\n"); 170fe5e6e2cSKirk McKusick } 171fe5e6e2cSKirk McKusick inp->i_depth = parentinp->i_depth + 1; 172e4a905d1SKirk McKusick if (bkgrdflag == 0) { 173fe5e6e2cSKirk McKusick DIP_SET(dp, di_dirdepth, inp->i_depth); 174fe5e6e2cSKirk McKusick inodirty(&ip); 175e4a905d1SKirk McKusick } else { 176e4a905d1SKirk McKusick cmd.value = inp->i_number; 177e4a905d1SKirk McKusick cmd.size = (int64_t)inp->i_depth - DIP(dp, di_dirdepth); 178e4a905d1SKirk McKusick if (debug) 179e4a905d1SKirk McKusick printf("adjdepth ino %ld amt %jd\n", (long)cmd.value, 180e4a905d1SKirk McKusick (intmax_t)cmd.size); 181e4a905d1SKirk McKusick size = MIBSIZE; 182e4a905d1SKirk McKusick if (sysctlnametomib("vfs.ffs.adjdepth", adjdepth, &size) < 0 || 183e4a905d1SKirk McKusick sysctl(adjdepth, MIBSIZE, 0, 0, &cmd, sizeof cmd) == -1) 184e4a905d1SKirk McKusick rwerror("ADJUST INODE DEPTH", cmd.value); 185e4a905d1SKirk McKusick } 186fe5e6e2cSKirk McKusick irelse(&ip); 187fe5e6e2cSKirk McKusick } 188fe5e6e2cSKirk McKusick 189fe5e6e2cSKirk McKusick /* 1908fae3551SRodney W. Grimes * Scan each entry in a directory block. 1918fae3551SRodney W. Grimes */ 19231f4ab50SBruce Evans int 193b70cd7eeSWarner Losh dirscan(struct inodesc *idesc) 1948fae3551SRodney W. Grimes { 1953d438ad6SDavid E. O'Brien struct direct *dp; 1963d438ad6SDavid E. O'Brien struct bufarea *bp; 1977578c6abSKirk McKusick u_int dsize, n; 1988fae3551SRodney W. Grimes long blksiz; 1998fae3551SRodney W. Grimes char dbuf[DIRBLKSIZ]; 2008fae3551SRodney W. Grimes 2018fae3551SRodney W. Grimes if (idesc->id_type != DATA) 202780a5c1eSPeter Wemm errx(EEXIT, "wrong type to dirscan %d", idesc->id_type); 2038fae3551SRodney W. Grimes if (idesc->id_entryno == 0 && 2048fae3551SRodney W. Grimes (idesc->id_filesize & (DIRBLKSIZ - 1)) != 0) 2058fae3551SRodney W. Grimes idesc->id_filesize = roundup(idesc->id_filesize, DIRBLKSIZ); 2068fae3551SRodney W. Grimes blksiz = idesc->id_numfrags * sblock.fs_fsize; 2078fae3551SRodney W. Grimes if (chkrange(idesc->id_blkno, idesc->id_numfrags)) { 2088fae3551SRodney W. Grimes idesc->id_filesize -= blksiz; 2098fae3551SRodney W. Grimes return (SKIP); 2108fae3551SRodney W. Grimes } 2118fae3551SRodney W. Grimes idesc->id_loc = 0; 2128fae3551SRodney W. Grimes for (dp = fsck_readdir(idesc); dp != NULL; dp = fsck_readdir(idesc)) { 2138fae3551SRodney W. Grimes dsize = dp->d_reclen; 214d33e92f9SJulian Elischer if (dsize > sizeof(dbuf)) 215d33e92f9SJulian Elischer dsize = sizeof(dbuf); 216780a5c1eSPeter Wemm memmove(dbuf, dp, (size_t)dsize); 2178fae3551SRodney W. Grimes idesc->id_dirp = (struct direct *)dbuf; 2188fae3551SRodney W. Grimes if ((n = (*idesc->id_func)(idesc)) & ALTERED) { 2198fae3551SRodney W. Grimes bp = getdirblk(idesc->id_blkno, blksiz); 2205cc52631SKirk McKusick if (bp->b_errs != 0) 2215cc52631SKirk McKusick return (STOP); 222780a5c1eSPeter Wemm memmove(bp->b_un.b_buf + idesc->id_loc - dsize, dbuf, 2238fae3551SRodney W. Grimes (size_t)dsize); 2248fae3551SRodney W. Grimes dirty(bp); 2258fae3551SRodney W. Grimes sbdirty(); 2268fae3551SRodney W. Grimes } 2278fae3551SRodney W. Grimes if (n & STOP) 2288fae3551SRodney W. Grimes return (n); 2298fae3551SRodney W. Grimes } 2308fae3551SRodney W. Grimes return (idesc->id_filesize > 0 ? KEEPON : STOP); 2318fae3551SRodney W. Grimes } 2328fae3551SRodney W. Grimes 2338fae3551SRodney W. Grimes /* 234bfc5d3f9SKirk McKusick * Get and verify the next entry in a directory. 235bfc5d3f9SKirk McKusick * We also verify that if there is another entry in the block that it is 236bfc5d3f9SKirk McKusick * valid, so if it is not valid it can be subsumed into the current entry. 2378fae3551SRodney W. Grimes */ 238780a5c1eSPeter Wemm static struct direct * 239b70cd7eeSWarner Losh fsck_readdir(struct inodesc *idesc) 2408fae3551SRodney W. Grimes { 2413d438ad6SDavid E. O'Brien struct direct *dp, *ndp; 2423d438ad6SDavid E. O'Brien struct bufarea *bp; 243bfc5d3f9SKirk McKusick long size, blksiz, subsume_ndp; 2448fae3551SRodney W. Grimes 245bfc5d3f9SKirk McKusick subsume_ndp = 0; 2468fae3551SRodney W. Grimes blksiz = idesc->id_numfrags * sblock.fs_fsize; 2478fae3551SRodney W. Grimes if (idesc->id_filesize <= 0 || idesc->id_loc >= blksiz) 248bfc5d3f9SKirk McKusick return (NULL); 249bfc5d3f9SKirk McKusick bp = getdirblk(idesc->id_blkno, blksiz); 2505cc52631SKirk McKusick if (bp->b_errs != 0) 2515cc52631SKirk McKusick return (NULL); 252bfc5d3f9SKirk McKusick dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); 253bfc5d3f9SKirk McKusick /* 254bfc5d3f9SKirk McKusick * Only need to check current entry if it is the first in the 255*f7cee4faSElyes Haouas * block, as later entries will have been checked in the 256bfc5d3f9SKirk McKusick * previous call to this function. 257bfc5d3f9SKirk McKusick */ 258bfc5d3f9SKirk McKusick if (idesc->id_loc % DIRBLKSIZ != 0 || dircheck(idesc, bp, dp) != 0) { 259bfc5d3f9SKirk McKusick /* 260bfc5d3f9SKirk McKusick * Current entry is good, update to point at next. 261bfc5d3f9SKirk McKusick */ 2628fae3551SRodney W. Grimes idesc->id_loc += dp->d_reclen; 2638fae3551SRodney W. Grimes idesc->id_filesize -= dp->d_reclen; 264bfc5d3f9SKirk McKusick /* 265bfc5d3f9SKirk McKusick * If at end of directory block, just return this entry. 266bfc5d3f9SKirk McKusick */ 267bfc5d3f9SKirk McKusick if (idesc->id_filesize <= 0 || idesc->id_loc >= blksiz || 268bfc5d3f9SKirk McKusick idesc->id_loc % DIRBLKSIZ == 0) 2698fae3551SRodney W. Grimes return (dp); 270bfc5d3f9SKirk McKusick /* 271bfc5d3f9SKirk McKusick * If the next entry good, return this entry. 272bfc5d3f9SKirk McKusick */ 2738fae3551SRodney W. Grimes ndp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); 274bfc5d3f9SKirk McKusick if (dircheck(idesc, bp, ndp) != 0) 275bfc5d3f9SKirk McKusick return (dp); 276bfc5d3f9SKirk McKusick /* 277bfc5d3f9SKirk McKusick * The next entry is bad, so subsume it and the remainder 278bfc5d3f9SKirk McKusick * of this directory block into this entry. 279bfc5d3f9SKirk McKusick */ 280bfc5d3f9SKirk McKusick subsume_ndp = 1; 281bfc5d3f9SKirk McKusick } 282bfc5d3f9SKirk McKusick /* 283bfc5d3f9SKirk McKusick * Current or next entry is bad. Zap current entry or 284bfc5d3f9SKirk McKusick * subsume next entry into current entry as appropriate. 285bfc5d3f9SKirk McKusick */ 2868fae3551SRodney W. Grimes size = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ); 2878fae3551SRodney W. Grimes idesc->id_loc += size; 2888fae3551SRodney W. Grimes idesc->id_filesize -= size; 289780a5c1eSPeter Wemm if (idesc->id_fix == IGNORE) 290bfc5d3f9SKirk McKusick return (NULL); 291bfc5d3f9SKirk McKusick if (subsume_ndp) { 292bfc5d3f9SKirk McKusick memset(ndp, 0, size); 2938fae3551SRodney W. Grimes dp->d_reclen += size; 294bfc5d3f9SKirk McKusick } else { 295bfc5d3f9SKirk McKusick memset(dp, 0, size); 296bfc5d3f9SKirk McKusick dp->d_reclen = size; 2970061238fSKirk McKusick } 298bfc5d3f9SKirk McKusick if (dofix(idesc, "DIRECTORY CORRUPTED")) 299bfc5d3f9SKirk McKusick dirty(bp); 3008fae3551SRodney W. Grimes return (dp); 3018fae3551SRodney W. Grimes } 3028fae3551SRodney W. Grimes 3038fae3551SRodney W. Grimes /* 3048fae3551SRodney W. Grimes * Verify that a directory entry is valid. 3058fae3551SRodney W. Grimes * This is a superset of the checks made in the kernel. 3060061238fSKirk McKusick * Also optionally clears padding and unused directory space. 3070061238fSKirk McKusick * 308bfc5d3f9SKirk McKusick * Returns 0 if the entry is bad, 1 if the entry is good. 3098fae3551SRodney W. Grimes */ 310780a5c1eSPeter Wemm static int 311bfc5d3f9SKirk McKusick dircheck(struct inodesc *idesc, struct bufarea *bp, struct direct *dp) 3128fae3551SRodney W. Grimes { 313c69284caSDavid E. O'Brien size_t size; 3143d438ad6SDavid E. O'Brien char *cp; 315fdc61a88SXin LI u_int8_t namlen; 3160061238fSKirk McKusick int spaceleft, modified, unused; 3178fae3551SRodney W. Grimes 3188fae3551SRodney W. Grimes spaceleft = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ); 319bfc5d3f9SKirk McKusick size = DIRSIZ(0, dp); 320d33e92f9SJulian Elischer if (dp->d_reclen == 0 || 321780a5c1eSPeter Wemm dp->d_reclen > spaceleft || 322bfc5d3f9SKirk McKusick dp->d_reclen < size || 323bfc5d3f9SKirk McKusick idesc->id_filesize < size || 3240061238fSKirk McKusick (dp->d_reclen & (DIR_ROUNDUP - 1)) != 0) 325142d8d2fSKirk McKusick goto bad; 326bfc5d3f9SKirk McKusick modified = 0; 3270061238fSKirk McKusick if (dp->d_ino == 0) { 328bfc5d3f9SKirk McKusick if (!zflag || fswritefd < 0) 329bfc5d3f9SKirk McKusick return (1); 3300061238fSKirk McKusick /* 331bfc5d3f9SKirk McKusick * Special case of an unused directory entry. Normally only 332bfc5d3f9SKirk McKusick * occurs at the beginning of a directory block when the block 333bfc5d3f9SKirk McKusick * contains no entries. Other than the first entry in a 334bfc5d3f9SKirk McKusick * directory block, the kernel coalesces unused space with 335bfc5d3f9SKirk McKusick * the previous entry by extending its d_reclen. However, 336bfc5d3f9SKirk McKusick * when cleaning up a directory, fsck may set d_ino to zero 337bfc5d3f9SKirk McKusick * in the middle of a directory block. If we're clearing out 338bfc5d3f9SKirk McKusick * directory cruft (-z flag), then make sure that all directory 339bfc5d3f9SKirk McKusick * space in entries with d_ino == 0 gets fully cleared. 3400061238fSKirk McKusick */ 3410061238fSKirk McKusick if (dp->d_type != 0) { 3420061238fSKirk McKusick dp->d_type = 0; 3430061238fSKirk McKusick modified = 1; 3440061238fSKirk McKusick } 3450061238fSKirk McKusick if (dp->d_namlen != 0) { 3460061238fSKirk McKusick dp->d_namlen = 0; 3470061238fSKirk McKusick modified = 1; 3480061238fSKirk McKusick } 349bfc5d3f9SKirk McKusick unused = dp->d_reclen - __offsetof(struct direct, d_name); 350bfc5d3f9SKirk McKusick for (cp = dp->d_name; unused > 0; unused--, cp++) { 351bfc5d3f9SKirk McKusick if (*cp != '\0') { 352bfc5d3f9SKirk McKusick *cp = '\0'; 3530061238fSKirk McKusick modified = 1; 3540061238fSKirk McKusick } 3550061238fSKirk McKusick } 356bfc5d3f9SKirk McKusick if (modified) 357bfc5d3f9SKirk McKusick dirty(bp); 358bfc5d3f9SKirk McKusick return (1); 3590061238fSKirk McKusick } 360bfc5d3f9SKirk McKusick /* 361bfc5d3f9SKirk McKusick * The d_type field should not be tested here. A bad type is an error 362bfc5d3f9SKirk McKusick * in the entry itself but is not a corruption of the directory 363bfc5d3f9SKirk McKusick * structure itself. So blowing away all the remaining entries in the 364bfc5d3f9SKirk McKusick * directory block is inappropriate. Rather the type error should be 365bfc5d3f9SKirk McKusick * checked in pass1 and fixed there. 366bfc5d3f9SKirk McKusick * 367bfc5d3f9SKirk McKusick * The name validation should also be done in pass1 although the 368bfc5d3f9SKirk McKusick * check to see if the name is longer than fits in the space 369bfc5d3f9SKirk McKusick * allocated for it (i.e., the *cp != '\0' fails after exiting the 370bfc5d3f9SKirk McKusick * loop below) then it really is a structural error that requires 371bfc5d3f9SKirk McKusick * the stronger action taken here. 372bfc5d3f9SKirk McKusick */ 3738fae3551SRodney W. Grimes namlen = dp->d_namlen; 374bfc5d3f9SKirk McKusick if (namlen == 0 || dp->d_type > 15) 375142d8d2fSKirk McKusick goto bad; 376bfc5d3f9SKirk McKusick for (cp = dp->d_name, size = 0; size < namlen; size++) { 377bfc5d3f9SKirk McKusick if (*cp == '\0' || *cp++ == '/') 378142d8d2fSKirk McKusick goto bad; 379bfc5d3f9SKirk McKusick } 380780a5c1eSPeter Wemm if (*cp != '\0') 381142d8d2fSKirk McKusick goto bad; 3820061238fSKirk McKusick if (zflag && fswritefd >= 0) { 3830061238fSKirk McKusick /* 3840061238fSKirk McKusick * Clear unused directory entry space, including the d_name 3850061238fSKirk McKusick * padding. 3860061238fSKirk McKusick */ 3870061238fSKirk McKusick /* First figure the number of pad bytes. */ 3880061238fSKirk McKusick unused = roundup2(namlen + 1, DIR_ROUNDUP) - (namlen + 1); 3890061238fSKirk McKusick 3900061238fSKirk McKusick /* Add in the free space to the end of the record. */ 3910061238fSKirk McKusick unused += dp->d_reclen - DIRSIZ(0, dp); 3920061238fSKirk McKusick 3930061238fSKirk McKusick /* 3940061238fSKirk McKusick * Now clear out the unused space, keeping track if we actually 3950061238fSKirk McKusick * changed anything. 3960061238fSKirk McKusick */ 3970061238fSKirk McKusick for (cp = &dp->d_name[namlen + 1]; unused > 0; unused--, cp++) { 3980061238fSKirk McKusick if (*cp != '\0') { 3990061238fSKirk McKusick *cp = '\0'; 4000061238fSKirk McKusick modified = 1; 4010061238fSKirk McKusick } 4020061238fSKirk McKusick } 4030061238fSKirk McKusick 404bfc5d3f9SKirk McKusick if (modified) 405bfc5d3f9SKirk McKusick dirty(bp); 4060061238fSKirk McKusick } 4078fae3551SRodney W. Grimes return (1); 4080061238fSKirk McKusick 409142d8d2fSKirk McKusick bad: 410142d8d2fSKirk McKusick if (debug) 411142d8d2fSKirk McKusick printf("Bad dir: ino %d reclen %d namlen %d type %d name %s\n", 412142d8d2fSKirk McKusick dp->d_ino, dp->d_reclen, dp->d_namlen, dp->d_type, 413142d8d2fSKirk McKusick dp->d_name); 414142d8d2fSKirk McKusick return (0); 4158fae3551SRodney W. Grimes } 4168fae3551SRodney W. Grimes 41731f4ab50SBruce Evans void 418599304a4SPoul-Henning Kamp direrror(ino_t ino, const char *errmesg) 4198fae3551SRodney W. Grimes { 4208fae3551SRodney W. Grimes 4218fae3551SRodney W. Grimes fileerror(ino, ino, errmesg); 4228fae3551SRodney W. Grimes } 4238fae3551SRodney W. Grimes 42431f4ab50SBruce Evans void 425599304a4SPoul-Henning Kamp fileerror(ino_t cwd, ino_t ino, const char *errmesg) 4268fae3551SRodney W. Grimes { 4275cc52631SKirk McKusick struct inode ip; 4281c85e6a3SKirk McKusick union dinode *dp; 4298fae3551SRodney W. Grimes char pathbuf[MAXPATHLEN + 1]; 4308fae3551SRodney W. Grimes 4318fae3551SRodney W. Grimes pwarn("%s ", errmesg); 43211ce203eSKirk McKusick if (ino < UFS_ROOTINO || ino >= maxino) { 4339fc5d538SKirk McKusick pfatal("out-of-range inode number %ju", (uintmax_t)ino); 4348fae3551SRodney W. Grimes return; 4358fae3551SRodney W. Grimes } 4365cc52631SKirk McKusick ginode(ino, &ip); 4375cc52631SKirk McKusick dp = ip.i_dp; 4385cc52631SKirk McKusick prtinode(&ip); 4399fc5d538SKirk McKusick printf("\n"); 4409fc5d538SKirk McKusick getpathname(pathbuf, cwd, ino); 4418fae3551SRodney W. Grimes if (ftypeok(dp)) 4428fae3551SRodney W. Grimes pfatal("%s=%s\n", 443d8ba45e2SEd Maste (DIP(dp, di_mode) & IFMT) == IFDIR ? "DIR" : "FILE", 4441c85e6a3SKirk McKusick pathbuf); 4458fae3551SRodney W. Grimes else 4468fae3551SRodney W. Grimes pfatal("NAME=%s\n", pathbuf); 4475cc52631SKirk McKusick irelse(&ip); 4488fae3551SRodney W. Grimes } 4498fae3551SRodney W. Grimes 45031f4ab50SBruce Evans void 451b70cd7eeSWarner Losh adjust(struct inodesc *idesc, int lcnt) 4528fae3551SRodney W. Grimes { 4535cc52631SKirk McKusick struct inode ip; 4541c85e6a3SKirk McKusick union dinode *dp; 455d33e92f9SJulian Elischer int saveresolved; 4568fae3551SRodney W. Grimes 4575cc52631SKirk McKusick ginode(idesc->id_number, &ip); 4585cc52631SKirk McKusick dp = ip.i_dp; 4591c85e6a3SKirk McKusick if (DIP(dp, di_nlink) == lcnt) { 460d33e92f9SJulian Elischer /* 461d33e92f9SJulian Elischer * If we have not hit any unresolved problems, are running 462d33e92f9SJulian Elischer * in preen mode, and are on a file system using soft updates, 463d33e92f9SJulian Elischer * then just toss any partially allocated files. 464d33e92f9SJulian Elischer */ 4657578c6abSKirk McKusick if (resolved && (preen || bkgrdflag) && usedsoftdep) { 466d33e92f9SJulian Elischer clri(idesc, "UNREF", 1); 4675cc52631SKirk McKusick irelse(&ip); 468d33e92f9SJulian Elischer return; 4698fae3551SRodney W. Grimes } else { 470d33e92f9SJulian Elischer /* 471d33e92f9SJulian Elischer * The file system can be marked clean even if 472d33e92f9SJulian Elischer * a file is not linked up, but is cleared. 473d33e92f9SJulian Elischer * Hence, resolved should not be cleared when 474d33e92f9SJulian Elischer * linkup is answered no, but clri is answered yes. 475d33e92f9SJulian Elischer */ 476d33e92f9SJulian Elischer saveresolved = resolved; 477d33e92f9SJulian Elischer if (linkup(idesc->id_number, (ino_t)0, NULL) == 0) { 478d33e92f9SJulian Elischer resolved = saveresolved; 479d33e92f9SJulian Elischer clri(idesc, "UNREF", 0); 4805cc52631SKirk McKusick irelse(&ip); 481d33e92f9SJulian Elischer return; 482d33e92f9SJulian Elischer } 483d33e92f9SJulian Elischer /* 484d33e92f9SJulian Elischer * Account for the new reference created by linkup(). 485d33e92f9SJulian Elischer */ 486d33e92f9SJulian Elischer lcnt--; 487d33e92f9SJulian Elischer } 488d33e92f9SJulian Elischer } 489d33e92f9SJulian Elischer if (lcnt != 0) { 4908fae3551SRodney W. Grimes pwarn("LINK COUNT %s", (lfdir == idesc->id_number) ? lfname : 491d8ba45e2SEd Maste ((DIP(dp, di_mode) & IFMT) == IFDIR ? "DIR" : "FILE")); 4925cc52631SKirk McKusick prtinode(&ip); 4938fae3551SRodney W. Grimes printf(" COUNT %d SHOULD BE %d", 4941c85e6a3SKirk McKusick DIP(dp, di_nlink), DIP(dp, di_nlink) - lcnt); 495b1897c19SJulian Elischer if (preen || usedsoftdep) { 4968fae3551SRodney W. Grimes if (lcnt < 0) { 4978fae3551SRodney W. Grimes printf("\n"); 4988fae3551SRodney W. Grimes pfatal("LINK COUNT INCREASING"); 4998fae3551SRodney W. Grimes } 500b1897c19SJulian Elischer if (preen) 5018fae3551SRodney W. Grimes printf(" (ADJUSTED)\n"); 5028fae3551SRodney W. Grimes } 5038fae3551SRodney W. Grimes if (preen || reply("ADJUST") == 1) { 5047578c6abSKirk McKusick if (bkgrdflag == 0) { 505c3b2344bSScott Long DIP_SET(dp, di_nlink, DIP(dp, di_nlink) - lcnt); 5065cc52631SKirk McKusick inodirty(&ip); 5077578c6abSKirk McKusick } else { 5087578c6abSKirk McKusick cmd.value = idesc->id_number; 5097578c6abSKirk McKusick cmd.size = -lcnt; 5107578c6abSKirk McKusick if (debug) 511599304a4SPoul-Henning Kamp printf("adjrefcnt ino %ld amt %lld\n", 512599304a4SPoul-Henning Kamp (long)cmd.value, 513599304a4SPoul-Henning Kamp (long long)cmd.size); 5147578c6abSKirk McKusick if (sysctl(adjrefcnt, MIBSIZE, 0, 0, 5157578c6abSKirk McKusick &cmd, sizeof cmd) == -1) 516e4a905d1SKirk McKusick rwerror("ADJUST INODE LINK COUNT", 517e4a905d1SKirk McKusick cmd.value); 5187578c6abSKirk McKusick } 5198fae3551SRodney W. Grimes } 5208fae3551SRodney W. Grimes } 5215cc52631SKirk McKusick irelse(&ip); 5228fae3551SRodney W. Grimes } 5238fae3551SRodney W. Grimes 524780a5c1eSPeter Wemm static int 525b70cd7eeSWarner Losh mkentry(struct inodesc *idesc) 5268fae3551SRodney W. Grimes { 5273d438ad6SDavid E. O'Brien struct direct *dirp = idesc->id_dirp; 5288fae3551SRodney W. Grimes struct direct newent; 5298fae3551SRodney W. Grimes int newlen, oldlen; 5308fae3551SRodney W. Grimes 5318fae3551SRodney W. Grimes newent.d_namlen = strlen(idesc->id_name); 5328fae3551SRodney W. Grimes newlen = DIRSIZ(0, &newent); 5338fae3551SRodney W. Grimes if (dirp->d_ino != 0) 5348fae3551SRodney W. Grimes oldlen = DIRSIZ(0, dirp); 5358fae3551SRodney W. Grimes else 5368fae3551SRodney W. Grimes oldlen = 0; 5378fae3551SRodney W. Grimes if (dirp->d_reclen - oldlen < newlen) 5388fae3551SRodney W. Grimes return (KEEPON); 5398fae3551SRodney W. Grimes newent.d_reclen = dirp->d_reclen - oldlen; 5408fae3551SRodney W. Grimes dirp->d_reclen = oldlen; 5418fae3551SRodney W. Grimes dirp = (struct direct *)(((char *)dirp) + oldlen); 5428fae3551SRodney W. Grimes dirp->d_ino = idesc->id_parent; /* ino to be entered is in id_parent */ 543780a5c1eSPeter Wemm dirp->d_reclen = newent.d_reclen; 544d33e92f9SJulian Elischer dirp->d_type = inoinfo(idesc->id_parent)->ino_type; 5459fef3122SDavid Greenman dirp->d_namlen = newent.d_namlen; 546780a5c1eSPeter Wemm memmove(dirp->d_name, idesc->id_name, (size_t)newent.d_namlen + 1); 5478fae3551SRodney W. Grimes return (ALTERED|STOP); 5488fae3551SRodney W. Grimes } 5498fae3551SRodney W. Grimes 550780a5c1eSPeter Wemm static int 551b70cd7eeSWarner Losh chgino(struct inodesc *idesc) 5528fae3551SRodney W. Grimes { 5533d438ad6SDavid E. O'Brien struct direct *dirp = idesc->id_dirp; 5548fae3551SRodney W. Grimes 555780a5c1eSPeter Wemm if (memcmp(dirp->d_name, idesc->id_name, (int)dirp->d_namlen + 1)) 5568fae3551SRodney W. Grimes return (KEEPON); 5578fae3551SRodney W. Grimes dirp->d_ino = idesc->id_parent; 558d33e92f9SJulian Elischer dirp->d_type = inoinfo(idesc->id_parent)->ino_type; 5598fae3551SRodney W. Grimes return (ALTERED|STOP); 5608fae3551SRodney W. Grimes } 5618fae3551SRodney W. Grimes 56231f4ab50SBruce Evans int 563b70cd7eeSWarner Losh linkup(ino_t orphan, ino_t parentdir, char *name) 5648fae3551SRodney W. Grimes { 5655cc52631SKirk McKusick struct inode ip; 5661c85e6a3SKirk McKusick union dinode *dp; 567fe5e6e2cSKirk McKusick int lostdir, depth; 5688fae3551SRodney W. Grimes ino_t oldlfdir; 569f4fc3895SKirk McKusick struct inoinfo *inp; 5708fae3551SRodney W. Grimes struct inodesc idesc; 5718fae3551SRodney W. Grimes char tempname[BUFSIZ]; 5728fae3551SRodney W. Grimes 573780a5c1eSPeter Wemm memset(&idesc, 0, sizeof(struct inodesc)); 5745cc52631SKirk McKusick ginode(orphan, &ip); 5755cc52631SKirk McKusick dp = ip.i_dp; 576d8ba45e2SEd Maste lostdir = (DIP(dp, di_mode) & IFMT) == IFDIR; 5778fae3551SRodney W. Grimes pwarn("UNREF %s ", lostdir ? "DIR" : "FILE"); 5785cc52631SKirk McKusick prtinode(&ip); 5799fc5d538SKirk McKusick printf("\n"); 5805cc52631SKirk McKusick if (preen && DIP(dp, di_size) == 0) { 5815cc52631SKirk McKusick irelse(&ip); 5828fae3551SRodney W. Grimes return (0); 5835cc52631SKirk McKusick } 5845cc52631SKirk McKusick irelse(&ip); 5857578c6abSKirk McKusick if (cursnapshot != 0) { 5867578c6abSKirk McKusick pfatal("FILE LINKUP IN SNAPSHOT"); 5877578c6abSKirk McKusick return (0); 5887578c6abSKirk McKusick } 5898fae3551SRodney W. Grimes if (preen) 5908fae3551SRodney W. Grimes printf(" (RECONNECTED)\n"); 5915cc52631SKirk McKusick else if (reply("RECONNECT") == 0) 5928fae3551SRodney W. Grimes return (0); 5938fae3551SRodney W. Grimes if (lfdir == 0) { 5945cc52631SKirk McKusick ginode(UFS_ROOTINO, &ip); 595599304a4SPoul-Henning Kamp idesc.id_name = strdup(lfname); 5968fae3551SRodney W. Grimes idesc.id_type = DATA; 5978fae3551SRodney W. Grimes idesc.id_func = findino; 5981dc349abSEd Maste idesc.id_number = UFS_ROOTINO; 5995cc52631SKirk McKusick if ((ckinode(ip.i_dp, &idesc) & FOUND) != 0) { 6008fae3551SRodney W. Grimes lfdir = idesc.id_parent; 6018fae3551SRodney W. Grimes } else { 6028fae3551SRodney W. Grimes pwarn("NO lost+found DIRECTORY"); 6038fae3551SRodney W. Grimes if (preen || reply("CREATE")) { 6041dc349abSEd Maste lfdir = allocdir(UFS_ROOTINO, (ino_t)0, lfmode); 6058fae3551SRodney W. Grimes if (lfdir != 0) { 6061dc349abSEd Maste if (makeentry(UFS_ROOTINO, lfdir, 6071dc349abSEd Maste lfname) != 0) { 608d33e92f9SJulian Elischer numdirs++; 6098fae3551SRodney W. Grimes if (preen) 6108fae3551SRodney W. Grimes printf(" (CREATED)\n"); 6118fae3551SRodney W. Grimes } else { 61252f97104SKirk McKusick freedirino(lfdir, UFS_ROOTINO); 6138fae3551SRodney W. Grimes lfdir = 0; 6148fae3551SRodney W. Grimes if (preen) 6158fae3551SRodney W. Grimes printf("\n"); 6168fae3551SRodney W. Grimes } 6178fae3551SRodney W. Grimes } 6188fae3551SRodney W. Grimes } 6198fae3551SRodney W. Grimes } 6205cc52631SKirk McKusick irelse(&ip); 621e5d0d1c5SKirk McKusick free(idesc.id_name); 6228fae3551SRodney W. Grimes if (lfdir == 0) { 6238fae3551SRodney W. Grimes pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY"); 6248fae3551SRodney W. Grimes printf("\n\n"); 6258fae3551SRodney W. Grimes return (0); 6268fae3551SRodney W. Grimes } 6278fae3551SRodney W. Grimes } 6285cc52631SKirk McKusick ginode(lfdir, &ip); 6295cc52631SKirk McKusick dp = ip.i_dp; 630d8ba45e2SEd Maste if ((DIP(dp, di_mode) & IFMT) != IFDIR) { 6318fae3551SRodney W. Grimes pfatal("lost+found IS NOT A DIRECTORY"); 6325cc52631SKirk McKusick if (reply("REALLOCATE") == 0) { 6335cc52631SKirk McKusick irelse(&ip); 6348fae3551SRodney W. Grimes return (0); 6355cc52631SKirk McKusick } 6368fae3551SRodney W. Grimes oldlfdir = lfdir; 6371dc349abSEd Maste if ((lfdir = allocdir(UFS_ROOTINO, (ino_t)0, lfmode)) == 0) { 6388fae3551SRodney W. Grimes pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n"); 6395cc52631SKirk McKusick irelse(&ip); 6408fae3551SRodney W. Grimes return (0); 6418fae3551SRodney W. Grimes } 642fe5e6e2cSKirk McKusick if ((changeino(UFS_ROOTINO, lfname, lfdir, 1) & ALTERED) == 0) { 6438fae3551SRodney W. Grimes pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n"); 6445cc52631SKirk McKusick irelse(&ip); 6458fae3551SRodney W. Grimes return (0); 6468fae3551SRodney W. Grimes } 6475cc52631SKirk McKusick idesc.id_type = inoinfo(oldlfdir)->ino_idtype; 6487180f1abSKirk McKusick idesc.id_func = freeblock; 6498fae3551SRodney W. Grimes idesc.id_number = oldlfdir; 650d33e92f9SJulian Elischer adjust(&idesc, inoinfo(oldlfdir)->ino_linkcnt + 1); 651d33e92f9SJulian Elischer inoinfo(oldlfdir)->ino_linkcnt = 0; 6525cc52631SKirk McKusick inodirty(&ip); 6535cc52631SKirk McKusick irelse(&ip); 6545cc52631SKirk McKusick ginode(lfdir, &ip); 6555cc52631SKirk McKusick dp = ip.i_dp; 6568fae3551SRodney W. Grimes } 657d33e92f9SJulian Elischer if (inoinfo(lfdir)->ino_state != DFOUND) { 6588fae3551SRodney W. Grimes pfatal("SORRY. NO lost+found DIRECTORY\n\n"); 6595cc52631SKirk McKusick irelse(&ip); 6608fae3551SRodney W. Grimes return (0); 6618fae3551SRodney W. Grimes } 6628fae3551SRodney W. Grimes (void)lftempname(tempname, orphan); 663d33e92f9SJulian Elischer if (makeentry(lfdir, orphan, (name ? name : tempname)) == 0) { 6648fae3551SRodney W. Grimes pfatal("SORRY. NO SPACE IN lost+found DIRECTORY"); 6658fae3551SRodney W. Grimes printf("\n\n"); 6665cc52631SKirk McKusick irelse(&ip); 6678fae3551SRodney W. Grimes return (0); 6688fae3551SRodney W. Grimes } 669d33e92f9SJulian Elischer inoinfo(orphan)->ino_linkcnt--; 6708fae3551SRodney W. Grimes if (lostdir) { 671fe5e6e2cSKirk McKusick depth = DIP(dp, di_dirdepth) + 1; 672fe5e6e2cSKirk McKusick if ((changeino(orphan, "..", lfdir, depth) & ALTERED) == 0 && 6738fae3551SRodney W. Grimes parentdir != (ino_t)-1) 6748fae3551SRodney W. Grimes (void)makeentry(orphan, lfdir, ".."); 675c3b2344bSScott Long DIP_SET(dp, di_nlink, DIP(dp, di_nlink) + 1); 6765cc52631SKirk McKusick inodirty(&ip); 677d33e92f9SJulian Elischer inoinfo(lfdir)->ino_linkcnt++; 678bf58d635SIan Dowse pwarn("DIR I=%lu CONNECTED. ", (u_long)orphan); 679f4fc3895SKirk McKusick inp = getinoinfo(parentdir); 68052f97104SKirk McKusick if (parentdir != (ino_t)-1 && inp != NULL) { 681b1046626SBruce Evans printf("PARENT WAS I=%lu\n", (u_long)parentdir); 6826b100474SJulian Elischer /* 683f4fc3895SKirk McKusick * If the parent directory did not have to 684f4fc3895SKirk McKusick * be replaced then because of the ordering 6856b100474SJulian Elischer * guarantees, has had the link count incremented 6866b100474SJulian Elischer * for the child, but no entry was made. This 6876b100474SJulian Elischer * fixes the parent link count so that fsck does 6886b100474SJulian Elischer * not need to be rerun. 6896b100474SJulian Elischer */ 69052f97104SKirk McKusick if ((inp->i_flags & INFO_NEW) != 0) 691d33e92f9SJulian Elischer inoinfo(parentdir)->ino_linkcnt++; 692119e9fc2SNate Williams } 6938fae3551SRodney W. Grimes if (preen == 0) 6948fae3551SRodney W. Grimes printf("\n"); 6958fae3551SRodney W. Grimes } 6965cc52631SKirk McKusick irelse(&ip); 6978fae3551SRodney W. Grimes return (1); 6988fae3551SRodney W. Grimes } 6998fae3551SRodney W. Grimes 7008fae3551SRodney W. Grimes /* 7018fae3551SRodney W. Grimes * fix an entry in a directory. 7028fae3551SRodney W. Grimes */ 70331f4ab50SBruce Evans int 704fe5e6e2cSKirk McKusick changeino(ino_t dir, const char *name, ino_t newnum, int depth) 7058fae3551SRodney W. Grimes { 7068fae3551SRodney W. Grimes struct inodesc idesc; 7075cc52631SKirk McKusick struct inode ip; 7085cc52631SKirk McKusick int error; 7098fae3551SRodney W. Grimes 710780a5c1eSPeter Wemm memset(&idesc, 0, sizeof(struct inodesc)); 7118fae3551SRodney W. Grimes idesc.id_type = DATA; 7128fae3551SRodney W. Grimes idesc.id_func = chgino; 7138fae3551SRodney W. Grimes idesc.id_number = dir; 7148fae3551SRodney W. Grimes idesc.id_fix = DONTKNOW; 715599304a4SPoul-Henning Kamp idesc.id_name = strdup(name); 7168fae3551SRodney W. Grimes idesc.id_parent = newnum; /* new value for name */ 7175cc52631SKirk McKusick ginode(dir, &ip); 718fe5e6e2cSKirk McKusick if (((error = ckinode(ip.i_dp, &idesc)) & ALTERED) && newnum != 0) { 719fe5e6e2cSKirk McKusick DIP_SET(ip.i_dp, di_dirdepth, depth); 72052671206SKirk McKusick inodirty(&ip); 721fe5e6e2cSKirk McKusick getinoinfo(dir)->i_depth = depth; 722fe5e6e2cSKirk McKusick } 723e5d0d1c5SKirk McKusick free(idesc.id_name); 7245cc52631SKirk McKusick irelse(&ip); 7255cc52631SKirk McKusick return (error); 7268fae3551SRodney W. Grimes } 7278fae3551SRodney W. Grimes 7288fae3551SRodney W. Grimes /* 7298fae3551SRodney W. Grimes * make an entry in a directory 7308fae3551SRodney W. Grimes */ 73131f4ab50SBruce Evans int 732599304a4SPoul-Henning Kamp makeentry(ino_t parent, ino_t ino, const char *name) 7338fae3551SRodney W. Grimes { 7345cc52631SKirk McKusick struct inode ip; 7351c85e6a3SKirk McKusick union dinode *dp; 7368fae3551SRodney W. Grimes struct inodesc idesc; 7375cc52631SKirk McKusick int retval; 7388fae3551SRodney W. Grimes char pathbuf[MAXPATHLEN + 1]; 7398fae3551SRodney W. Grimes 7401dc349abSEd Maste if (parent < UFS_ROOTINO || parent >= maxino || 7411dc349abSEd Maste ino < UFS_ROOTINO || ino >= maxino) 7428fae3551SRodney W. Grimes return (0); 743780a5c1eSPeter Wemm memset(&idesc, 0, sizeof(struct inodesc)); 7448fae3551SRodney W. Grimes idesc.id_type = DATA; 7458fae3551SRodney W. Grimes idesc.id_func = mkentry; 7468fae3551SRodney W. Grimes idesc.id_number = parent; 7478fae3551SRodney W. Grimes idesc.id_parent = ino; /* this is the inode to enter */ 7488fae3551SRodney W. Grimes idesc.id_fix = DONTKNOW; 749599304a4SPoul-Henning Kamp idesc.id_name = strdup(name); 7505cc52631SKirk McKusick ginode(parent, &ip); 7515cc52631SKirk McKusick dp = ip.i_dp; 7521c85e6a3SKirk McKusick if (DIP(dp, di_size) % DIRBLKSIZ) { 753c3b2344bSScott Long DIP_SET(dp, di_size, roundup(DIP(dp, di_size), DIRBLKSIZ)); 7545cc52631SKirk McKusick inodirty(&ip); 7558fae3551SRodney W. Grimes } 7565cc52631SKirk McKusick if ((ckinode(dp, &idesc) & ALTERED) != 0) { 7575cc52631SKirk McKusick irelse(&ip); 758e5d0d1c5SKirk McKusick free(idesc.id_name); 7598fae3551SRodney W. Grimes return (1); 7605cc52631SKirk McKusick } 7618fae3551SRodney W. Grimes getpathname(pathbuf, parent, parent); 7625cc52631SKirk McKusick if (expanddir(&ip, pathbuf) == 0) { 7635cc52631SKirk McKusick irelse(&ip); 764e5d0d1c5SKirk McKusick free(idesc.id_name); 7658fae3551SRodney W. Grimes return (0); 7665cc52631SKirk McKusick } 7675cc52631SKirk McKusick retval = ckinode(dp, &idesc) & ALTERED; 7685cc52631SKirk McKusick irelse(&ip); 769e5d0d1c5SKirk McKusick free(idesc.id_name); 7705cc52631SKirk McKusick return (retval); 7718fae3551SRodney W. Grimes } 7728fae3551SRodney W. Grimes 7738fae3551SRodney W. Grimes /* 7748fae3551SRodney W. Grimes * Attempt to expand the size of a directory 7758fae3551SRodney W. Grimes */ 776780a5c1eSPeter Wemm static int 7775cc52631SKirk McKusick expanddir(struct inode *ip, char *name) 7788fae3551SRodney W. Grimes { 779997f81afSKirk McKusick ufs2_daddr_t lastlbn, oldblk, newblk, indirblk; 780997f81afSKirk McKusick size_t filesize, lastlbnsize; 781997f81afSKirk McKusick struct bufarea *bp, *nbp; 7827180f1abSKirk McKusick struct inodesc idesc; 7835cc52631SKirk McKusick union dinode *dp; 784460ed610SKirk McKusick long cg, indiralloced; 785997f81afSKirk McKusick char *cp; 7868fae3551SRodney W. Grimes 787997f81afSKirk McKusick nbp = NULL; 788997f81afSKirk McKusick indiralloced = newblk = indirblk = 0; 789460ed610SKirk McKusick memset(&idesc, 0, sizeof(struct inodesc)); 790460ed610SKirk McKusick idesc.id_type = ADDR; 791997f81afSKirk McKusick pwarn("NO SPACE LEFT IN %s", name); 792997f81afSKirk McKusick if (!preen && reply("EXPAND") == 0) 7938fae3551SRodney W. Grimes return (0); 794460ed610SKirk McKusick cg = ino_to_cg(&sblock, ip->i_number); 7955cc52631SKirk McKusick dp = ip->i_dp; 796997f81afSKirk McKusick filesize = DIP(dp, di_size); 797997f81afSKirk McKusick lastlbn = lblkno(&sblock, filesize); 798997f81afSKirk McKusick /* 799997f81afSKirk McKusick * We only expand lost+found to a single indirect block. 800997f81afSKirk McKusick */ 801997f81afSKirk McKusick if ((DIP(dp, di_mode) & IFMT) != IFDIR || filesize == 0 || 802997f81afSKirk McKusick lastlbn >= UFS_NDADDR + NINDIR(&sblock)) 803997f81afSKirk McKusick goto bad; 804997f81afSKirk McKusick /* 805997f81afSKirk McKusick * If last block is a fragment, expand it to a full size block. 806997f81afSKirk McKusick */ 807997f81afSKirk McKusick lastlbnsize = sblksize(&sblock, filesize, lastlbn); 808c8a7a3ffSKirk McKusick if (lastlbnsize > 0 && lastlbnsize < sblock.fs_bsize) { 809997f81afSKirk McKusick oldblk = DIP(dp, di_db[lastlbn]); 810997f81afSKirk McKusick bp = getdirblk(oldblk, lastlbnsize); 8118fae3551SRodney W. Grimes if (bp->b_errs) 8128fae3551SRodney W. Grimes goto bad; 813460ed610SKirk McKusick newblk = allocblk(cg, sblock.fs_frag, std_checkblkavail); 814460ed610SKirk McKusick if (newblk == 0) 815997f81afSKirk McKusick goto bad; 816997f81afSKirk McKusick nbp = getdatablk(newblk, sblock.fs_bsize, BT_DIRDATA); 817997f81afSKirk McKusick if (nbp->b_errs) 818997f81afSKirk McKusick goto bad; 819997f81afSKirk McKusick DIP_SET(dp, di_db[lastlbn], newblk); 820997f81afSKirk McKusick DIP_SET(dp, di_size, filesize + sblock.fs_bsize - lastlbnsize); 821997f81afSKirk McKusick DIP_SET(dp, di_blocks, DIP(dp, di_blocks) + 822997f81afSKirk McKusick btodb(sblock.fs_bsize - lastlbnsize)); 8235cc52631SKirk McKusick inodirty(ip); 824997f81afSKirk McKusick memmove(nbp->b_un.b_buf, bp->b_un.b_buf, lastlbnsize); 825997f81afSKirk McKusick memset(&nbp->b_un.b_buf[lastlbnsize], 0, 826997f81afSKirk McKusick sblock.fs_bsize - lastlbnsize); 827997f81afSKirk McKusick for (cp = &nbp->b_un.b_buf[lastlbnsize]; 828997f81afSKirk McKusick cp < &nbp->b_un.b_buf[sblock.fs_bsize]; 829997f81afSKirk McKusick cp += DIRBLKSIZ) 830997f81afSKirk McKusick memmove(cp, &emptydir, sizeof emptydir); 831997f81afSKirk McKusick dirty(nbp); 8325cc52631SKirk McKusick brelse(nbp); 833460ed610SKirk McKusick binval(bp); 834997f81afSKirk McKusick idesc.id_blkno = oldblk; 835997f81afSKirk McKusick idesc.id_numfrags = numfrags(&sblock, lastlbnsize); 836997f81afSKirk McKusick (void)freeblock(&idesc); 8375cc52631SKirk McKusick if (preen) 8385cc52631SKirk McKusick printf(" (EXPANDED)\n"); 839997f81afSKirk McKusick return (1); 840997f81afSKirk McKusick } 841460ed610SKirk McKusick if ((newblk = allocblk(cg, sblock.fs_frag, std_checkblkavail)) == 0) 842997f81afSKirk McKusick goto bad; 8438fae3551SRodney W. Grimes bp = getdirblk(newblk, sblock.fs_bsize); 8448fae3551SRodney W. Grimes if (bp->b_errs) 8458fae3551SRodney W. Grimes goto bad; 846997f81afSKirk McKusick memset(bp->b_un.b_buf, 0, sblock.fs_bsize); 847997f81afSKirk McKusick for (cp = bp->b_un.b_buf; 8488fae3551SRodney W. Grimes cp < &bp->b_un.b_buf[sblock.fs_bsize]; 8498fae3551SRodney W. Grimes cp += DIRBLKSIZ) 850780a5c1eSPeter Wemm memmove(cp, &emptydir, sizeof emptydir); 8518fae3551SRodney W. Grimes dirty(bp); 852997f81afSKirk McKusick if (lastlbn < UFS_NDADDR) { 853997f81afSKirk McKusick DIP_SET(dp, di_db[lastlbn], newblk); 854997f81afSKirk McKusick } else { 855997f81afSKirk McKusick /* 856997f81afSKirk McKusick * Allocate indirect block if needed. 857997f81afSKirk McKusick */ 858997f81afSKirk McKusick if ((indirblk = DIP(dp, di_ib[0])) == 0) { 859460ed610SKirk McKusick indirblk = allocblk(cg, sblock.fs_frag, 860460ed610SKirk McKusick std_checkblkavail); 861460ed610SKirk McKusick if (indirblk == 0) { 862460ed610SKirk McKusick binval(bp); 8638fae3551SRodney W. Grimes goto bad; 864460ed610SKirk McKusick } 865997f81afSKirk McKusick indiralloced = 1; 866997f81afSKirk McKusick } 867997f81afSKirk McKusick nbp = getdatablk(indirblk, sblock.fs_bsize, BT_LEVEL1); 868997f81afSKirk McKusick if (nbp->b_errs) 869997f81afSKirk McKusick goto bad; 870997f81afSKirk McKusick if (indiralloced) { 871997f81afSKirk McKusick memset(nbp->b_un.b_buf, 0, sblock.fs_bsize); 872997f81afSKirk McKusick DIP_SET(dp, di_ib[0], indirblk); 873997f81afSKirk McKusick DIP_SET(dp, di_blocks, 874997f81afSKirk McKusick DIP(dp, di_blocks) + btodb(sblock.fs_bsize)); 87552671206SKirk McKusick inodirty(ip); 876997f81afSKirk McKusick } 877997f81afSKirk McKusick IBLK_SET(nbp, lastlbn - UFS_NDADDR, newblk); 878997f81afSKirk McKusick dirty(nbp); 8795cc52631SKirk McKusick brelse(nbp); 880997f81afSKirk McKusick } 881997f81afSKirk McKusick DIP_SET(dp, di_size, filesize + sblock.fs_bsize); 882997f81afSKirk McKusick DIP_SET(dp, di_blocks, DIP(dp, di_blocks) + btodb(sblock.fs_bsize)); 8835cc52631SKirk McKusick inodirty(ip); 8848fae3551SRodney W. Grimes if (preen) 8858fae3551SRodney W. Grimes printf(" (EXPANDED)\n"); 8868fae3551SRodney W. Grimes return (1); 8878fae3551SRodney W. Grimes bad: 888997f81afSKirk McKusick pfatal(" (EXPANSION FAILED)\n"); 889460ed610SKirk McKusick if (nbp != NULL) { 890460ed610SKirk McKusick binval(bp); 8915cc52631SKirk McKusick brelse(nbp); 892460ed610SKirk McKusick } 893997f81afSKirk McKusick if (newblk != 0) { 8947180f1abSKirk McKusick idesc.id_blkno = newblk; 8957180f1abSKirk McKusick idesc.id_numfrags = sblock.fs_frag; 8967180f1abSKirk McKusick (void)freeblock(&idesc); 897997f81afSKirk McKusick } 898997f81afSKirk McKusick if (indiralloced) { 899997f81afSKirk McKusick idesc.id_blkno = indirblk; 900997f81afSKirk McKusick idesc.id_numfrags = sblock.fs_frag; 901997f81afSKirk McKusick (void)freeblock(&idesc); 902997f81afSKirk McKusick } 9038fae3551SRodney W. Grimes return (0); 9048fae3551SRodney W. Grimes } 9058fae3551SRodney W. Grimes 9068fae3551SRodney W. Grimes /* 9078fae3551SRodney W. Grimes * allocate a new directory 9088fae3551SRodney W. Grimes */ 9093eeb5bdcSBruce Evans ino_t 910b70cd7eeSWarner Losh allocdir(ino_t parent, ino_t request, int mode) 9118fae3551SRodney W. Grimes { 9128fae3551SRodney W. Grimes ino_t ino; 9138fae3551SRodney W. Grimes char *cp; 9145cc52631SKirk McKusick struct inode ip; 9151c85e6a3SKirk McKusick union dinode *dp; 91691ea1615SKirk McKusick struct bufarea *bp; 9178fae3551SRodney W. Grimes struct dirtemplate *dirp; 918fe5e6e2cSKirk McKusick struct inoinfo *inp, *parentinp; 9198fae3551SRodney W. Grimes 920d8ba45e2SEd Maste ino = allocino(request, IFDIR|mode); 92184a0e3f9SKirk McKusick if (ino == 0) 92284a0e3f9SKirk McKusick return (0); 9238fae3551SRodney W. Grimes dirp = &dirhead; 9248fae3551SRodney W. Grimes dirp->dot_ino = ino; 9258fae3551SRodney W. Grimes dirp->dotdot_ino = parent; 9265cc52631SKirk McKusick ginode(ino, &ip); 9275cc52631SKirk McKusick dp = ip.i_dp; 9281c85e6a3SKirk McKusick bp = getdirblk(DIP(dp, di_db[0]), sblock.fs_fsize); 9298fae3551SRodney W. Grimes if (bp->b_errs) { 9308fae3551SRodney W. Grimes freeino(ino); 9315cc52631SKirk McKusick irelse(&ip); 9328fae3551SRodney W. Grimes return (0); 9338fae3551SRodney W. Grimes } 934780a5c1eSPeter Wemm memmove(bp->b_un.b_buf, dirp, sizeof(struct dirtemplate)); 9358fae3551SRodney W. Grimes for (cp = &bp->b_un.b_buf[DIRBLKSIZ]; 9368fae3551SRodney W. Grimes cp < &bp->b_un.b_buf[sblock.fs_fsize]; 9378fae3551SRodney W. Grimes cp += DIRBLKSIZ) 938780a5c1eSPeter Wemm memmove(cp, &emptydir, sizeof emptydir); 9398fae3551SRodney W. Grimes dirty(bp); 940c3b2344bSScott Long DIP_SET(dp, di_nlink, 2); 9415cc52631SKirk McKusick inodirty(&ip); 9421dc349abSEd Maste if (ino == UFS_ROOTINO) { 943f4fc3895SKirk McKusick inp = cacheino(dp, ino); 944f4fc3895SKirk McKusick inp->i_parent = parent; 945f4fc3895SKirk McKusick inp->i_dotdot = parent; 94652f97104SKirk McKusick inp->i_flags |= INFO_NEW; 94752f97104SKirk McKusick inoinfo(ino)->ino_type = DT_DIR; 94852f97104SKirk McKusick inoinfo(ino)->ino_linkcnt = DIP(dp, di_nlink); 9495cc52631SKirk McKusick irelse(&ip); 9508fae3551SRodney W. Grimes return(ino); 9518fae3551SRodney W. Grimes } 952af6726e6SDon Lewis if (!INO_IS_DVALID(parent)) { 9538fae3551SRodney W. Grimes freeino(ino); 9545cc52631SKirk McKusick irelse(&ip); 9558fae3551SRodney W. Grimes return (0); 9568fae3551SRodney W. Grimes } 957f4fc3895SKirk McKusick inp = cacheino(dp, ino); 95891ea1615SKirk McKusick inp->i_parent = parent; 95991ea1615SKirk McKusick inp->i_dotdot = parent; 96052f97104SKirk McKusick inp->i_flags |= INFO_NEW; 961fe5e6e2cSKirk McKusick if ((parentinp = getinoinfo(inp->i_parent)) == NULL) { 962fe5e6e2cSKirk McKusick pfatal("allocdir: UNKNOWN PARENT DIR"); 963fe5e6e2cSKirk McKusick } else { 964fe5e6e2cSKirk McKusick inp->i_depth = parentinp->i_depth + 1; 965fe5e6e2cSKirk McKusick DIP_SET(dp, di_dirdepth, inp->i_depth); 96652671206SKirk McKusick inodirty(&ip); 967fe5e6e2cSKirk McKusick } 96852f97104SKirk McKusick inoinfo(ino)->ino_type = DT_DIR; 969d33e92f9SJulian Elischer inoinfo(ino)->ino_state = inoinfo(parent)->ino_state; 970d33e92f9SJulian Elischer if (inoinfo(ino)->ino_state == DSTATE) { 9711c85e6a3SKirk McKusick inoinfo(ino)->ino_linkcnt = DIP(dp, di_nlink); 972d33e92f9SJulian Elischer inoinfo(parent)->ino_linkcnt++; 9738fae3551SRodney W. Grimes } 9745cc52631SKirk McKusick irelse(&ip); 9755cc52631SKirk McKusick ginode(parent, &ip); 9765cc52631SKirk McKusick dp = ip.i_dp; 977c3b2344bSScott Long DIP_SET(dp, di_nlink, DIP(dp, di_nlink) + 1); 9785cc52631SKirk McKusick inodirty(&ip); 9795cc52631SKirk McKusick irelse(&ip); 9808fae3551SRodney W. Grimes return (ino); 9818fae3551SRodney W. Grimes } 9828fae3551SRodney W. Grimes 9838fae3551SRodney W. Grimes /* 9848fae3551SRodney W. Grimes * free a directory inode 9858fae3551SRodney W. Grimes */ 98652f97104SKirk McKusick void 98752f97104SKirk McKusick freedirino(ino_t ino, ino_t parent) 9888fae3551SRodney W. Grimes { 9895cc52631SKirk McKusick struct inode ip; 9901c85e6a3SKirk McKusick union dinode *dp; 9918fae3551SRodney W. Grimes 9928fae3551SRodney W. Grimes if (ino != parent) { 9935cc52631SKirk McKusick ginode(parent, &ip); 9945cc52631SKirk McKusick dp = ip.i_dp; 995c3b2344bSScott Long DIP_SET(dp, di_nlink, DIP(dp, di_nlink) - 1); 9965cc52631SKirk McKusick inodirty(&ip); 9975cc52631SKirk McKusick irelse(&ip); 9988fae3551SRodney W. Grimes } 99952f97104SKirk McKusick removecachedino(ino); 10008fae3551SRodney W. Grimes freeino(ino); 10018fae3551SRodney W. Grimes } 10028fae3551SRodney W. Grimes 10038fae3551SRodney W. Grimes /* 10048fae3551SRodney W. Grimes * generate a temporary name for the lost+found directory. 10058fae3551SRodney W. Grimes */ 1006780a5c1eSPeter Wemm static int 1007b70cd7eeSWarner Losh lftempname(char *bufp, ino_t ino) 10088fae3551SRodney W. Grimes { 10093d438ad6SDavid E. O'Brien ino_t in; 10103d438ad6SDavid E. O'Brien char *cp; 10118fae3551SRodney W. Grimes int namlen; 10128fae3551SRodney W. Grimes 10138fae3551SRodney W. Grimes cp = bufp + 2; 10148fae3551SRodney W. Grimes for (in = maxino; in > 0; in /= 10) 10158fae3551SRodney W. Grimes cp++; 10168fae3551SRodney W. Grimes *--cp = 0; 10178fae3551SRodney W. Grimes namlen = cp - bufp; 10188fae3551SRodney W. Grimes in = ino; 10198fae3551SRodney W. Grimes while (cp > bufp) { 10208fae3551SRodney W. Grimes *--cp = (in % 10) + '0'; 10218fae3551SRodney W. Grimes in /= 10; 10228fae3551SRodney W. Grimes } 10238fae3551SRodney W. Grimes *cp = '#'; 10248fae3551SRodney W. Grimes return (namlen); 10258fae3551SRodney W. Grimes } 10268fae3551SRodney W. Grimes 10278fae3551SRodney W. Grimes /* 10288fae3551SRodney W. Grimes * Get a directory block. 10298fae3551SRodney W. Grimes * Insure that it is held until another is requested. 10308fae3551SRodney W. Grimes */ 1031780a5c1eSPeter Wemm static struct bufarea * 10321c85e6a3SKirk McKusick getdirblk(ufs2_daddr_t blkno, long size) 10338fae3551SRodney W. Grimes { 10348fae3551SRodney W. Grimes 10355cc52631SKirk McKusick if (pdirbp != NULL && pdirbp->b_errs == 0) 10365cc52631SKirk McKusick brelse(pdirbp); 1037ed75b5a1SKirk McKusick pdirbp = getdatablk(blkno, size, BT_DIRDATA); 10388fae3551SRodney W. Grimes return (pdirbp); 10398fae3551SRodney W. Grimes } 1040