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 326b100474SJulian Elischer #if 0 33c69284caSDavid E. O'Brien #ifndef lint 34780a5c1eSPeter Wemm static const char sccsid[] = "@(#)dir.c 8.8 (Berkeley) 4/28/95"; 358fae3551SRodney W. Grimes #endif /* not lint */ 36c69284caSDavid E. O'Brien #endif 37c69284caSDavid E. O'Brien #include <sys/cdefs.h> 38c69284caSDavid E. O'Brien __FBSDID("$FreeBSD$"); 398fae3551SRodney W. Grimes 408fae3551SRodney W. Grimes #include <sys/param.h> 41d33e92f9SJulian Elischer #include <sys/time.h> 4213eb765fSBaptiste Daroussin #include <sys/types.h> 437578c6abSKirk McKusick #include <sys/sysctl.h> 44780a5c1eSPeter Wemm 458fae3551SRodney W. Grimes #include <ufs/ufs/dinode.h> 468fae3551SRodney W. Grimes #include <ufs/ufs/dir.h> 478fae3551SRodney W. Grimes #include <ufs/ffs/fs.h> 4804aba254SBruce Evans 49780a5c1eSPeter Wemm #include <err.h> 508fae3551SRodney W. Grimes #include <string.h> 51780a5c1eSPeter Wemm 528fae3551SRodney W. Grimes #include "fsck.h" 538fae3551SRodney W. Grimes 547703a6ffSScott Long static struct dirtemplate emptydir = { 557578c6abSKirk McKusick 0, DIRBLKSIZ, DT_UNKNOWN, 0, "", 567578c6abSKirk McKusick 0, 0, DT_UNKNOWN, 0, "" 577578c6abSKirk McKusick }; 587703a6ffSScott Long static struct dirtemplate dirhead = { 598fae3551SRodney W. Grimes 0, 12, DT_DIR, 1, ".", 608fae3551SRodney W. Grimes 0, DIRBLKSIZ - 12, DT_DIR, 2, ".." 618fae3551SRodney W. Grimes }; 628fae3551SRodney W. Grimes 63b70cd7eeSWarner Losh static int chgino(struct inodesc *); 64bfc5d3f9SKirk McKusick static int dircheck(struct inodesc *, struct bufarea *, struct direct *); 655cc52631SKirk McKusick static int expanddir(struct inode *ip, char *name); 66b70cd7eeSWarner Losh static struct direct *fsck_readdir(struct inodesc *); 671c85e6a3SKirk McKusick static struct bufarea *getdirblk(ufs2_daddr_t blkno, long size); 68b70cd7eeSWarner Losh static int lftempname(char *bufp, ino_t ino); 69b70cd7eeSWarner Losh static int mkentry(struct inodesc *); 708fae3551SRodney W. Grimes 718fae3551SRodney W. Grimes /* 728fae3551SRodney W. Grimes * Propagate connected state through the tree. 738fae3551SRodney W. Grimes */ 7431f4ab50SBruce Evans void 75b70cd7eeSWarner Losh propagate(void) 768fae3551SRodney W. Grimes { 773d438ad6SDavid E. O'Brien struct inoinfo **inpp, *inp; 788fae3551SRodney W. Grimes struct inoinfo **inpend; 798fae3551SRodney W. Grimes long change; 808fae3551SRodney W. Grimes 818fae3551SRodney W. Grimes inpend = &inpsort[inplast]; 828fae3551SRodney W. Grimes do { 838fae3551SRodney W. Grimes change = 0; 848fae3551SRodney W. Grimes for (inpp = inpsort; inpp < inpend; inpp++) { 858fae3551SRodney W. Grimes inp = *inpp; 868fae3551SRodney W. Grimes if (inp->i_parent == 0) 878fae3551SRodney W. Grimes continue; 88d33e92f9SJulian Elischer if (inoinfo(inp->i_parent)->ino_state == DFOUND && 89af6726e6SDon Lewis INO_IS_DUNFOUND(inp->i_number)) { 90d33e92f9SJulian Elischer inoinfo(inp->i_number)->ino_state = DFOUND; 91fe5e6e2cSKirk McKusick check_dirdepth(inp); 928fae3551SRodney W. Grimes change++; 938fae3551SRodney W. Grimes } 948fae3551SRodney W. Grimes } 958fae3551SRodney W. Grimes } while (change > 0); 968fae3551SRodney W. Grimes } 978fae3551SRodney W. Grimes 988fae3551SRodney W. Grimes /* 99fe5e6e2cSKirk McKusick * Check that the recorded depth of the directory is correct. 100fe5e6e2cSKirk McKusick */ 101fe5e6e2cSKirk McKusick void 102fe5e6e2cSKirk McKusick check_dirdepth(struct inoinfo *inp) 103fe5e6e2cSKirk McKusick { 104fe5e6e2cSKirk McKusick struct inoinfo *parentinp; 105fe5e6e2cSKirk McKusick struct inode ip; 106fe5e6e2cSKirk McKusick union dinode *dp; 107fe5e6e2cSKirk McKusick int saveresolved; 108*e4a905d1SKirk McKusick size_t size; 109fe5e6e2cSKirk McKusick static int updateasked, dirdepthupdate; 110fe5e6e2cSKirk McKusick 111fe5e6e2cSKirk McKusick if ((parentinp = getinoinfo(inp->i_parent)) == NULL) { 112fe5e6e2cSKirk McKusick pfatal("check_dirdepth: UNKNOWN PARENT DIR"); 113fe5e6e2cSKirk McKusick return; 114fe5e6e2cSKirk McKusick } 115fe5e6e2cSKirk McKusick /* 116fe5e6e2cSKirk McKusick * If depth is correct, nothing to do. 117fe5e6e2cSKirk McKusick */ 118fe5e6e2cSKirk McKusick if (parentinp->i_depth + 1 == inp->i_depth) 119fe5e6e2cSKirk McKusick return; 120fe5e6e2cSKirk McKusick /* 121fe5e6e2cSKirk McKusick * Only the root inode should have depth of 0, so if any other 122fe5e6e2cSKirk McKusick * directory has a depth of 0 then this is an old filesystem 123fe5e6e2cSKirk McKusick * that has not been tracking directory depth. Ask just once 124fe5e6e2cSKirk McKusick * whether it should start tracking directory depth. 125fe5e6e2cSKirk McKusick */ 126fe5e6e2cSKirk McKusick if (inp->i_depth == 0 && updateasked == 0) { 127fe5e6e2cSKirk McKusick updateasked = 1; 128fe5e6e2cSKirk McKusick if (preen) { 129fe5e6e2cSKirk McKusick pwarn("UPDATING FILESYSTEM TO TRACK DIRECTORY DEPTH"); 130fe5e6e2cSKirk McKusick dirdepthupdate = 1; 131fe5e6e2cSKirk McKusick } else { 132fe5e6e2cSKirk McKusick /* 133fe5e6e2cSKirk McKusick * The file system can be marked clean even if 134fe5e6e2cSKirk McKusick * a directory does not have the right depth. 135fe5e6e2cSKirk McKusick * Hence, resolved should not be cleared when 136fe5e6e2cSKirk McKusick * the filesystem does not update directory depths. 137fe5e6e2cSKirk McKusick */ 138fe5e6e2cSKirk McKusick saveresolved = resolved; 139fe5e6e2cSKirk McKusick dirdepthupdate = 140fe5e6e2cSKirk McKusick reply("UPDATE FILESYSTEM TO TRACK DIRECTORY DEPTH"); 141fe5e6e2cSKirk McKusick resolved = saveresolved; 142fe5e6e2cSKirk McKusick } 143fe5e6e2cSKirk McKusick } 144fe5e6e2cSKirk McKusick /* 145*e4a905d1SKirk McKusick * If we are not converting or we are running in no-write mode 146*e4a905d1SKirk McKusick * there is nothing more to do. 147fe5e6e2cSKirk McKusick */ 148*e4a905d1SKirk McKusick if ((inp->i_depth == 0 && dirdepthupdate == 0) || 149*e4a905d1SKirk McKusick (fswritefd < 0 && bkgrdflag == 0)) 150fe5e6e2cSKirk McKusick return; 151fe5e6e2cSKirk McKusick /* 152fe5e6e2cSKirk McKusick * Individual directory at wrong depth. Report it and correct if 153fe5e6e2cSKirk McKusick * in preen mode or ask if in interactive mode. Note that if a 154fe5e6e2cSKirk McKusick * directory is renamed to a new location that is at a different 155fe5e6e2cSKirk McKusick * level in the tree, its depth will be recalculated, but none of 156fe5e6e2cSKirk McKusick * the directories that it contains will be updated. Thus it is 157fe5e6e2cSKirk McKusick * not unexpected to find directories with incorrect depths. No 158fe5e6e2cSKirk McKusick * operational harm will come from this though new directory 159fe5e6e2cSKirk McKusick * placement in the subtree may not be as optimal until the depths 160fe5e6e2cSKirk McKusick * of the affected directories are corrected. 161fe5e6e2cSKirk McKusick * 162fe5e6e2cSKirk McKusick * To avoid much spurious output on otherwise clean filesystems 163fe5e6e2cSKirk McKusick * we only generate detailed output when the debug flag is given. 164fe5e6e2cSKirk McKusick */ 165fe5e6e2cSKirk McKusick ginode(inp->i_number, &ip); 166fe5e6e2cSKirk McKusick dp = ip.i_dp; 167fe5e6e2cSKirk McKusick if (inp->i_depth != 0 && debug) { 168fe5e6e2cSKirk McKusick pwarn("DIRECTORY"); 169fe5e6e2cSKirk McKusick prtinode(&ip); 170fe5e6e2cSKirk McKusick printf(" DEPTH %d SHOULD BE %d", inp->i_depth, 171fe5e6e2cSKirk McKusick parentinp->i_depth + 1); 172fe5e6e2cSKirk McKusick if (preen == 0 && reply("ADJUST") == 0) { 173fe5e6e2cSKirk McKusick irelse(&ip); 174fe5e6e2cSKirk McKusick return; 175fe5e6e2cSKirk McKusick } 176fe5e6e2cSKirk McKusick if (preen) 177fe5e6e2cSKirk McKusick printf(" (ADJUSTED)\n"); 178fe5e6e2cSKirk McKusick } 179fe5e6e2cSKirk McKusick inp->i_depth = parentinp->i_depth + 1; 180*e4a905d1SKirk McKusick if (bkgrdflag == 0) { 181fe5e6e2cSKirk McKusick DIP_SET(dp, di_dirdepth, inp->i_depth); 182fe5e6e2cSKirk McKusick inodirty(&ip); 183*e4a905d1SKirk McKusick } else { 184*e4a905d1SKirk McKusick cmd.value = inp->i_number; 185*e4a905d1SKirk McKusick cmd.size = (int64_t)inp->i_depth - DIP(dp, di_dirdepth); 186*e4a905d1SKirk McKusick if (debug) 187*e4a905d1SKirk McKusick printf("adjdepth ino %ld amt %jd\n", (long)cmd.value, 188*e4a905d1SKirk McKusick (intmax_t)cmd.size); 189*e4a905d1SKirk McKusick size = MIBSIZE; 190*e4a905d1SKirk McKusick if (sysctlnametomib("vfs.ffs.adjdepth", adjdepth, &size) < 0 || 191*e4a905d1SKirk McKusick sysctl(adjdepth, MIBSIZE, 0, 0, &cmd, sizeof cmd) == -1) 192*e4a905d1SKirk McKusick rwerror("ADJUST INODE DEPTH", cmd.value); 193*e4a905d1SKirk McKusick } 194fe5e6e2cSKirk McKusick irelse(&ip); 195fe5e6e2cSKirk McKusick } 196fe5e6e2cSKirk McKusick 197fe5e6e2cSKirk McKusick /* 1988fae3551SRodney W. Grimes * Scan each entry in a directory block. 1998fae3551SRodney W. Grimes */ 20031f4ab50SBruce Evans int 201b70cd7eeSWarner Losh dirscan(struct inodesc *idesc) 2028fae3551SRodney W. Grimes { 2033d438ad6SDavid E. O'Brien struct direct *dp; 2043d438ad6SDavid E. O'Brien struct bufarea *bp; 2057578c6abSKirk McKusick u_int dsize, n; 2068fae3551SRodney W. Grimes long blksiz; 2078fae3551SRodney W. Grimes char dbuf[DIRBLKSIZ]; 2088fae3551SRodney W. Grimes 2098fae3551SRodney W. Grimes if (idesc->id_type != DATA) 210780a5c1eSPeter Wemm errx(EEXIT, "wrong type to dirscan %d", idesc->id_type); 2118fae3551SRodney W. Grimes if (idesc->id_entryno == 0 && 2128fae3551SRodney W. Grimes (idesc->id_filesize & (DIRBLKSIZ - 1)) != 0) 2138fae3551SRodney W. Grimes idesc->id_filesize = roundup(idesc->id_filesize, DIRBLKSIZ); 2148fae3551SRodney W. Grimes blksiz = idesc->id_numfrags * sblock.fs_fsize; 2158fae3551SRodney W. Grimes if (chkrange(idesc->id_blkno, idesc->id_numfrags)) { 2168fae3551SRodney W. Grimes idesc->id_filesize -= blksiz; 2178fae3551SRodney W. Grimes return (SKIP); 2188fae3551SRodney W. Grimes } 2198fae3551SRodney W. Grimes idesc->id_loc = 0; 2208fae3551SRodney W. Grimes for (dp = fsck_readdir(idesc); dp != NULL; dp = fsck_readdir(idesc)) { 2218fae3551SRodney W. Grimes dsize = dp->d_reclen; 222d33e92f9SJulian Elischer if (dsize > sizeof(dbuf)) 223d33e92f9SJulian Elischer dsize = sizeof(dbuf); 224780a5c1eSPeter Wemm memmove(dbuf, dp, (size_t)dsize); 2258fae3551SRodney W. Grimes idesc->id_dirp = (struct direct *)dbuf; 2268fae3551SRodney W. Grimes if ((n = (*idesc->id_func)(idesc)) & ALTERED) { 2278fae3551SRodney W. Grimes bp = getdirblk(idesc->id_blkno, blksiz); 2285cc52631SKirk McKusick if (bp->b_errs != 0) 2295cc52631SKirk McKusick return (STOP); 230780a5c1eSPeter Wemm memmove(bp->b_un.b_buf + idesc->id_loc - dsize, dbuf, 2318fae3551SRodney W. Grimes (size_t)dsize); 2328fae3551SRodney W. Grimes dirty(bp); 2338fae3551SRodney W. Grimes sbdirty(); 2348fae3551SRodney W. Grimes } 2358fae3551SRodney W. Grimes if (n & STOP) 2368fae3551SRodney W. Grimes return (n); 2378fae3551SRodney W. Grimes } 2388fae3551SRodney W. Grimes return (idesc->id_filesize > 0 ? KEEPON : STOP); 2398fae3551SRodney W. Grimes } 2408fae3551SRodney W. Grimes 2418fae3551SRodney W. Grimes /* 242bfc5d3f9SKirk McKusick * Get and verify the next entry in a directory. 243bfc5d3f9SKirk McKusick * We also verify that if there is another entry in the block that it is 244bfc5d3f9SKirk McKusick * valid, so if it is not valid it can be subsumed into the current entry. 2458fae3551SRodney W. Grimes */ 246780a5c1eSPeter Wemm static struct direct * 247b70cd7eeSWarner Losh fsck_readdir(struct inodesc *idesc) 2488fae3551SRodney W. Grimes { 2493d438ad6SDavid E. O'Brien struct direct *dp, *ndp; 2503d438ad6SDavid E. O'Brien struct bufarea *bp; 251bfc5d3f9SKirk McKusick long size, blksiz, subsume_ndp; 2528fae3551SRodney W. Grimes 253bfc5d3f9SKirk McKusick subsume_ndp = 0; 2548fae3551SRodney W. Grimes blksiz = idesc->id_numfrags * sblock.fs_fsize; 2558fae3551SRodney W. Grimes if (idesc->id_filesize <= 0 || idesc->id_loc >= blksiz) 256bfc5d3f9SKirk McKusick return (NULL); 257bfc5d3f9SKirk McKusick bp = getdirblk(idesc->id_blkno, blksiz); 2585cc52631SKirk McKusick if (bp->b_errs != 0) 2595cc52631SKirk McKusick return (NULL); 260bfc5d3f9SKirk McKusick dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); 261bfc5d3f9SKirk McKusick /* 262bfc5d3f9SKirk McKusick * Only need to check current entry if it is the first in the 263bfc5d3f9SKirk McKusick * the block, as later entries will have been checked in the 264bfc5d3f9SKirk McKusick * previous call to this function. 265bfc5d3f9SKirk McKusick */ 266bfc5d3f9SKirk McKusick if (idesc->id_loc % DIRBLKSIZ != 0 || dircheck(idesc, bp, dp) != 0) { 267bfc5d3f9SKirk McKusick /* 268bfc5d3f9SKirk McKusick * Current entry is good, update to point at next. 269bfc5d3f9SKirk McKusick */ 2708fae3551SRodney W. Grimes idesc->id_loc += dp->d_reclen; 2718fae3551SRodney W. Grimes idesc->id_filesize -= dp->d_reclen; 272bfc5d3f9SKirk McKusick /* 273bfc5d3f9SKirk McKusick * If at end of directory block, just return this entry. 274bfc5d3f9SKirk McKusick */ 275bfc5d3f9SKirk McKusick if (idesc->id_filesize <= 0 || idesc->id_loc >= blksiz || 276bfc5d3f9SKirk McKusick idesc->id_loc % DIRBLKSIZ == 0) 2778fae3551SRodney W. Grimes return (dp); 278bfc5d3f9SKirk McKusick /* 279bfc5d3f9SKirk McKusick * If the next entry good, return this entry. 280bfc5d3f9SKirk McKusick */ 2818fae3551SRodney W. Grimes ndp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); 282bfc5d3f9SKirk McKusick if (dircheck(idesc, bp, ndp) != 0) 283bfc5d3f9SKirk McKusick return (dp); 284bfc5d3f9SKirk McKusick /* 285bfc5d3f9SKirk McKusick * The next entry is bad, so subsume it and the remainder 286bfc5d3f9SKirk McKusick * of this directory block into this entry. 287bfc5d3f9SKirk McKusick */ 288bfc5d3f9SKirk McKusick subsume_ndp = 1; 289bfc5d3f9SKirk McKusick } 290bfc5d3f9SKirk McKusick /* 291bfc5d3f9SKirk McKusick * Current or next entry is bad. Zap current entry or 292bfc5d3f9SKirk McKusick * subsume next entry into current entry as appropriate. 293bfc5d3f9SKirk McKusick */ 2948fae3551SRodney W. Grimes size = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ); 2958fae3551SRodney W. Grimes idesc->id_loc += size; 2968fae3551SRodney W. Grimes idesc->id_filesize -= size; 297780a5c1eSPeter Wemm if (idesc->id_fix == IGNORE) 298bfc5d3f9SKirk McKusick return (NULL); 299bfc5d3f9SKirk McKusick if (subsume_ndp) { 300bfc5d3f9SKirk McKusick memset(ndp, 0, size); 3018fae3551SRodney W. Grimes dp->d_reclen += size; 302bfc5d3f9SKirk McKusick } else { 303bfc5d3f9SKirk McKusick memset(dp, 0, size); 304bfc5d3f9SKirk McKusick dp->d_reclen = size; 3050061238fSKirk McKusick } 306bfc5d3f9SKirk McKusick if (dofix(idesc, "DIRECTORY CORRUPTED")) 307bfc5d3f9SKirk McKusick dirty(bp); 3088fae3551SRodney W. Grimes return (dp); 3098fae3551SRodney W. Grimes } 3108fae3551SRodney W. Grimes 3118fae3551SRodney W. Grimes /* 3128fae3551SRodney W. Grimes * Verify that a directory entry is valid. 3138fae3551SRodney W. Grimes * This is a superset of the checks made in the kernel. 3140061238fSKirk McKusick * Also optionally clears padding and unused directory space. 3150061238fSKirk McKusick * 316bfc5d3f9SKirk McKusick * Returns 0 if the entry is bad, 1 if the entry is good. 3178fae3551SRodney W. Grimes */ 318780a5c1eSPeter Wemm static int 319bfc5d3f9SKirk McKusick dircheck(struct inodesc *idesc, struct bufarea *bp, struct direct *dp) 3208fae3551SRodney W. Grimes { 321c69284caSDavid E. O'Brien size_t size; 3223d438ad6SDavid E. O'Brien char *cp; 323fdc61a88SXin LI u_int8_t namlen; 3240061238fSKirk McKusick int spaceleft, modified, unused; 3258fae3551SRodney W. Grimes 3268fae3551SRodney W. Grimes spaceleft = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ); 327bfc5d3f9SKirk McKusick size = DIRSIZ(0, dp); 328d33e92f9SJulian Elischer if (dp->d_reclen == 0 || 329780a5c1eSPeter Wemm dp->d_reclen > spaceleft || 330bfc5d3f9SKirk McKusick dp->d_reclen < size || 331bfc5d3f9SKirk McKusick idesc->id_filesize < size || 3320061238fSKirk McKusick (dp->d_reclen & (DIR_ROUNDUP - 1)) != 0) 333142d8d2fSKirk McKusick goto bad; 334bfc5d3f9SKirk McKusick modified = 0; 3350061238fSKirk McKusick if (dp->d_ino == 0) { 336bfc5d3f9SKirk McKusick if (!zflag || fswritefd < 0) 337bfc5d3f9SKirk McKusick return (1); 3380061238fSKirk McKusick /* 339bfc5d3f9SKirk McKusick * Special case of an unused directory entry. Normally only 340bfc5d3f9SKirk McKusick * occurs at the beginning of a directory block when the block 341bfc5d3f9SKirk McKusick * contains no entries. Other than the first entry in a 342bfc5d3f9SKirk McKusick * directory block, the kernel coalesces unused space with 343bfc5d3f9SKirk McKusick * the previous entry by extending its d_reclen. However, 344bfc5d3f9SKirk McKusick * when cleaning up a directory, fsck may set d_ino to zero 345bfc5d3f9SKirk McKusick * in the middle of a directory block. If we're clearing out 346bfc5d3f9SKirk McKusick * directory cruft (-z flag), then make sure that all directory 347bfc5d3f9SKirk McKusick * space in entries with d_ino == 0 gets fully cleared. 3480061238fSKirk McKusick */ 3490061238fSKirk McKusick if (dp->d_type != 0) { 3500061238fSKirk McKusick dp->d_type = 0; 3510061238fSKirk McKusick modified = 1; 3520061238fSKirk McKusick } 3530061238fSKirk McKusick if (dp->d_namlen != 0) { 3540061238fSKirk McKusick dp->d_namlen = 0; 3550061238fSKirk McKusick modified = 1; 3560061238fSKirk McKusick } 357bfc5d3f9SKirk McKusick unused = dp->d_reclen - __offsetof(struct direct, d_name); 358bfc5d3f9SKirk McKusick for (cp = dp->d_name; unused > 0; unused--, cp++) { 359bfc5d3f9SKirk McKusick if (*cp != '\0') { 360bfc5d3f9SKirk McKusick *cp = '\0'; 3610061238fSKirk McKusick modified = 1; 3620061238fSKirk McKusick } 3630061238fSKirk McKusick } 364bfc5d3f9SKirk McKusick if (modified) 365bfc5d3f9SKirk McKusick dirty(bp); 366bfc5d3f9SKirk McKusick return (1); 3670061238fSKirk McKusick } 368bfc5d3f9SKirk McKusick /* 369bfc5d3f9SKirk McKusick * The d_type field should not be tested here. A bad type is an error 370bfc5d3f9SKirk McKusick * in the entry itself but is not a corruption of the directory 371bfc5d3f9SKirk McKusick * structure itself. So blowing away all the remaining entries in the 372bfc5d3f9SKirk McKusick * directory block is inappropriate. Rather the type error should be 373bfc5d3f9SKirk McKusick * checked in pass1 and fixed there. 374bfc5d3f9SKirk McKusick * 375bfc5d3f9SKirk McKusick * The name validation should also be done in pass1 although the 376bfc5d3f9SKirk McKusick * check to see if the name is longer than fits in the space 377bfc5d3f9SKirk McKusick * allocated for it (i.e., the *cp != '\0' fails after exiting the 378bfc5d3f9SKirk McKusick * loop below) then it really is a structural error that requires 379bfc5d3f9SKirk McKusick * the stronger action taken here. 380bfc5d3f9SKirk McKusick */ 3818fae3551SRodney W. Grimes namlen = dp->d_namlen; 382bfc5d3f9SKirk McKusick if (namlen == 0 || dp->d_type > 15) 383142d8d2fSKirk McKusick goto bad; 384bfc5d3f9SKirk McKusick for (cp = dp->d_name, size = 0; size < namlen; size++) { 385bfc5d3f9SKirk McKusick if (*cp == '\0' || *cp++ == '/') 386142d8d2fSKirk McKusick goto bad; 387bfc5d3f9SKirk McKusick } 388780a5c1eSPeter Wemm if (*cp != '\0') 389142d8d2fSKirk McKusick goto bad; 3900061238fSKirk McKusick if (zflag && fswritefd >= 0) { 3910061238fSKirk McKusick /* 3920061238fSKirk McKusick * Clear unused directory entry space, including the d_name 3930061238fSKirk McKusick * padding. 3940061238fSKirk McKusick */ 3950061238fSKirk McKusick /* First figure the number of pad bytes. */ 3960061238fSKirk McKusick unused = roundup2(namlen + 1, DIR_ROUNDUP) - (namlen + 1); 3970061238fSKirk McKusick 3980061238fSKirk McKusick /* Add in the free space to the end of the record. */ 3990061238fSKirk McKusick unused += dp->d_reclen - DIRSIZ(0, dp); 4000061238fSKirk McKusick 4010061238fSKirk McKusick /* 4020061238fSKirk McKusick * Now clear out the unused space, keeping track if we actually 4030061238fSKirk McKusick * changed anything. 4040061238fSKirk McKusick */ 4050061238fSKirk McKusick for (cp = &dp->d_name[namlen + 1]; unused > 0; unused--, cp++) { 4060061238fSKirk McKusick if (*cp != '\0') { 4070061238fSKirk McKusick *cp = '\0'; 4080061238fSKirk McKusick modified = 1; 4090061238fSKirk McKusick } 4100061238fSKirk McKusick } 4110061238fSKirk McKusick 412bfc5d3f9SKirk McKusick if (modified) 413bfc5d3f9SKirk McKusick dirty(bp); 4140061238fSKirk McKusick } 4158fae3551SRodney W. Grimes return (1); 4160061238fSKirk McKusick 417142d8d2fSKirk McKusick bad: 418142d8d2fSKirk McKusick if (debug) 419142d8d2fSKirk McKusick printf("Bad dir: ino %d reclen %d namlen %d type %d name %s\n", 420142d8d2fSKirk McKusick dp->d_ino, dp->d_reclen, dp->d_namlen, dp->d_type, 421142d8d2fSKirk McKusick dp->d_name); 422142d8d2fSKirk McKusick return (0); 4238fae3551SRodney W. Grimes } 4248fae3551SRodney W. Grimes 42531f4ab50SBruce Evans void 426599304a4SPoul-Henning Kamp direrror(ino_t ino, const char *errmesg) 4278fae3551SRodney W. Grimes { 4288fae3551SRodney W. Grimes 4298fae3551SRodney W. Grimes fileerror(ino, ino, errmesg); 4308fae3551SRodney W. Grimes } 4318fae3551SRodney W. Grimes 43231f4ab50SBruce Evans void 433599304a4SPoul-Henning Kamp fileerror(ino_t cwd, ino_t ino, const char *errmesg) 4348fae3551SRodney W. Grimes { 4355cc52631SKirk McKusick struct inode ip; 4361c85e6a3SKirk McKusick union dinode *dp; 4378fae3551SRodney W. Grimes char pathbuf[MAXPATHLEN + 1]; 4388fae3551SRodney W. Grimes 4398fae3551SRodney W. Grimes pwarn("%s ", errmesg); 4401dc349abSEd Maste if (ino < UFS_ROOTINO || ino > maxino) { 4419fc5d538SKirk McKusick pfatal("out-of-range inode number %ju", (uintmax_t)ino); 4428fae3551SRodney W. Grimes return; 4438fae3551SRodney W. Grimes } 4445cc52631SKirk McKusick ginode(ino, &ip); 4455cc52631SKirk McKusick dp = ip.i_dp; 4465cc52631SKirk McKusick prtinode(&ip); 4479fc5d538SKirk McKusick printf("\n"); 4489fc5d538SKirk McKusick getpathname(pathbuf, cwd, ino); 4498fae3551SRodney W. Grimes if (ftypeok(dp)) 4508fae3551SRodney W. Grimes pfatal("%s=%s\n", 451d8ba45e2SEd Maste (DIP(dp, di_mode) & IFMT) == IFDIR ? "DIR" : "FILE", 4521c85e6a3SKirk McKusick pathbuf); 4538fae3551SRodney W. Grimes else 4548fae3551SRodney W. Grimes pfatal("NAME=%s\n", pathbuf); 4555cc52631SKirk McKusick irelse(&ip); 4568fae3551SRodney W. Grimes } 4578fae3551SRodney W. Grimes 45831f4ab50SBruce Evans void 459b70cd7eeSWarner Losh adjust(struct inodesc *idesc, int lcnt) 4608fae3551SRodney W. Grimes { 4615cc52631SKirk McKusick struct inode ip; 4621c85e6a3SKirk McKusick union dinode *dp; 463d33e92f9SJulian Elischer int saveresolved; 4648fae3551SRodney W. Grimes 4655cc52631SKirk McKusick ginode(idesc->id_number, &ip); 4665cc52631SKirk McKusick dp = ip.i_dp; 4671c85e6a3SKirk McKusick if (DIP(dp, di_nlink) == lcnt) { 468d33e92f9SJulian Elischer /* 469d33e92f9SJulian Elischer * If we have not hit any unresolved problems, are running 470d33e92f9SJulian Elischer * in preen mode, and are on a file system using soft updates, 471d33e92f9SJulian Elischer * then just toss any partially allocated files. 472d33e92f9SJulian Elischer */ 4737578c6abSKirk McKusick if (resolved && (preen || bkgrdflag) && usedsoftdep) { 474d33e92f9SJulian Elischer clri(idesc, "UNREF", 1); 4755cc52631SKirk McKusick irelse(&ip); 476d33e92f9SJulian Elischer return; 4778fae3551SRodney W. Grimes } else { 478d33e92f9SJulian Elischer /* 479d33e92f9SJulian Elischer * The file system can be marked clean even if 480d33e92f9SJulian Elischer * a file is not linked up, but is cleared. 481d33e92f9SJulian Elischer * Hence, resolved should not be cleared when 482d33e92f9SJulian Elischer * linkup is answered no, but clri is answered yes. 483d33e92f9SJulian Elischer */ 484d33e92f9SJulian Elischer saveresolved = resolved; 485d33e92f9SJulian Elischer if (linkup(idesc->id_number, (ino_t)0, NULL) == 0) { 486d33e92f9SJulian Elischer resolved = saveresolved; 487d33e92f9SJulian Elischer clri(idesc, "UNREF", 0); 4885cc52631SKirk McKusick irelse(&ip); 489d33e92f9SJulian Elischer return; 490d33e92f9SJulian Elischer } 491d33e92f9SJulian Elischer /* 492d33e92f9SJulian Elischer * Account for the new reference created by linkup(). 493d33e92f9SJulian Elischer */ 494d33e92f9SJulian Elischer lcnt--; 495d33e92f9SJulian Elischer } 496d33e92f9SJulian Elischer } 497d33e92f9SJulian Elischer if (lcnt != 0) { 4988fae3551SRodney W. Grimes pwarn("LINK COUNT %s", (lfdir == idesc->id_number) ? lfname : 499d8ba45e2SEd Maste ((DIP(dp, di_mode) & IFMT) == IFDIR ? "DIR" : "FILE")); 5005cc52631SKirk McKusick prtinode(&ip); 5018fae3551SRodney W. Grimes printf(" COUNT %d SHOULD BE %d", 5021c85e6a3SKirk McKusick DIP(dp, di_nlink), DIP(dp, di_nlink) - lcnt); 503b1897c19SJulian Elischer if (preen || usedsoftdep) { 5048fae3551SRodney W. Grimes if (lcnt < 0) { 5058fae3551SRodney W. Grimes printf("\n"); 5068fae3551SRodney W. Grimes pfatal("LINK COUNT INCREASING"); 5078fae3551SRodney W. Grimes } 508b1897c19SJulian Elischer if (preen) 5098fae3551SRodney W. Grimes printf(" (ADJUSTED)\n"); 5108fae3551SRodney W. Grimes } 5118fae3551SRodney W. Grimes if (preen || reply("ADJUST") == 1) { 5127578c6abSKirk McKusick if (bkgrdflag == 0) { 513c3b2344bSScott Long DIP_SET(dp, di_nlink, DIP(dp, di_nlink) - lcnt); 5145cc52631SKirk McKusick inodirty(&ip); 5157578c6abSKirk McKusick } else { 5167578c6abSKirk McKusick cmd.value = idesc->id_number; 5177578c6abSKirk McKusick cmd.size = -lcnt; 5187578c6abSKirk McKusick if (debug) 519599304a4SPoul-Henning Kamp printf("adjrefcnt ino %ld amt %lld\n", 520599304a4SPoul-Henning Kamp (long)cmd.value, 521599304a4SPoul-Henning Kamp (long long)cmd.size); 5227578c6abSKirk McKusick if (sysctl(adjrefcnt, MIBSIZE, 0, 0, 5237578c6abSKirk McKusick &cmd, sizeof cmd) == -1) 524*e4a905d1SKirk McKusick rwerror("ADJUST INODE LINK COUNT", 525*e4a905d1SKirk McKusick cmd.value); 5267578c6abSKirk McKusick } 5278fae3551SRodney W. Grimes } 5288fae3551SRodney W. Grimes } 5295cc52631SKirk McKusick irelse(&ip); 5308fae3551SRodney W. Grimes } 5318fae3551SRodney W. Grimes 532780a5c1eSPeter Wemm static int 533b70cd7eeSWarner Losh mkentry(struct inodesc *idesc) 5348fae3551SRodney W. Grimes { 5353d438ad6SDavid E. O'Brien struct direct *dirp = idesc->id_dirp; 5368fae3551SRodney W. Grimes struct direct newent; 5378fae3551SRodney W. Grimes int newlen, oldlen; 5388fae3551SRodney W. Grimes 5398fae3551SRodney W. Grimes newent.d_namlen = strlen(idesc->id_name); 5408fae3551SRodney W. Grimes newlen = DIRSIZ(0, &newent); 5418fae3551SRodney W. Grimes if (dirp->d_ino != 0) 5428fae3551SRodney W. Grimes oldlen = DIRSIZ(0, dirp); 5438fae3551SRodney W. Grimes else 5448fae3551SRodney W. Grimes oldlen = 0; 5458fae3551SRodney W. Grimes if (dirp->d_reclen - oldlen < newlen) 5468fae3551SRodney W. Grimes return (KEEPON); 5478fae3551SRodney W. Grimes newent.d_reclen = dirp->d_reclen - oldlen; 5488fae3551SRodney W. Grimes dirp->d_reclen = oldlen; 5498fae3551SRodney W. Grimes dirp = (struct direct *)(((char *)dirp) + oldlen); 5508fae3551SRodney W. Grimes dirp->d_ino = idesc->id_parent; /* ino to be entered is in id_parent */ 551780a5c1eSPeter Wemm dirp->d_reclen = newent.d_reclen; 552d33e92f9SJulian Elischer dirp->d_type = inoinfo(idesc->id_parent)->ino_type; 5539fef3122SDavid Greenman dirp->d_namlen = newent.d_namlen; 554780a5c1eSPeter Wemm memmove(dirp->d_name, idesc->id_name, (size_t)newent.d_namlen + 1); 5558fae3551SRodney W. Grimes return (ALTERED|STOP); 5568fae3551SRodney W. Grimes } 5578fae3551SRodney W. Grimes 558780a5c1eSPeter Wemm static int 559b70cd7eeSWarner Losh chgino(struct inodesc *idesc) 5608fae3551SRodney W. Grimes { 5613d438ad6SDavid E. O'Brien struct direct *dirp = idesc->id_dirp; 5628fae3551SRodney W. Grimes 563780a5c1eSPeter Wemm if (memcmp(dirp->d_name, idesc->id_name, (int)dirp->d_namlen + 1)) 5648fae3551SRodney W. Grimes return (KEEPON); 5658fae3551SRodney W. Grimes dirp->d_ino = idesc->id_parent; 566d33e92f9SJulian Elischer dirp->d_type = inoinfo(idesc->id_parent)->ino_type; 5678fae3551SRodney W. Grimes return (ALTERED|STOP); 5688fae3551SRodney W. Grimes } 5698fae3551SRodney W. Grimes 57031f4ab50SBruce Evans int 571b70cd7eeSWarner Losh linkup(ino_t orphan, ino_t parentdir, char *name) 5728fae3551SRodney W. Grimes { 5735cc52631SKirk McKusick struct inode ip; 5741c85e6a3SKirk McKusick union dinode *dp; 575fe5e6e2cSKirk McKusick int lostdir, depth; 5768fae3551SRodney W. Grimes ino_t oldlfdir; 577f4fc3895SKirk McKusick struct inoinfo *inp; 5788fae3551SRodney W. Grimes struct inodesc idesc; 5798fae3551SRodney W. Grimes char tempname[BUFSIZ]; 5808fae3551SRodney W. Grimes 581780a5c1eSPeter Wemm memset(&idesc, 0, sizeof(struct inodesc)); 5825cc52631SKirk McKusick ginode(orphan, &ip); 5835cc52631SKirk McKusick dp = ip.i_dp; 584d8ba45e2SEd Maste lostdir = (DIP(dp, di_mode) & IFMT) == IFDIR; 5858fae3551SRodney W. Grimes pwarn("UNREF %s ", lostdir ? "DIR" : "FILE"); 5865cc52631SKirk McKusick prtinode(&ip); 5879fc5d538SKirk McKusick printf("\n"); 5885cc52631SKirk McKusick if (preen && DIP(dp, di_size) == 0) { 5895cc52631SKirk McKusick irelse(&ip); 5908fae3551SRodney W. Grimes return (0); 5915cc52631SKirk McKusick } 5925cc52631SKirk McKusick irelse(&ip); 5937578c6abSKirk McKusick if (cursnapshot != 0) { 5947578c6abSKirk McKusick pfatal("FILE LINKUP IN SNAPSHOT"); 5957578c6abSKirk McKusick return (0); 5967578c6abSKirk McKusick } 5978fae3551SRodney W. Grimes if (preen) 5988fae3551SRodney W. Grimes printf(" (RECONNECTED)\n"); 5995cc52631SKirk McKusick else if (reply("RECONNECT") == 0) 6008fae3551SRodney W. Grimes return (0); 6018fae3551SRodney W. Grimes if (lfdir == 0) { 6025cc52631SKirk McKusick ginode(UFS_ROOTINO, &ip); 603599304a4SPoul-Henning Kamp idesc.id_name = strdup(lfname); 6048fae3551SRodney W. Grimes idesc.id_type = DATA; 6058fae3551SRodney W. Grimes idesc.id_func = findino; 6061dc349abSEd Maste idesc.id_number = UFS_ROOTINO; 6075cc52631SKirk McKusick if ((ckinode(ip.i_dp, &idesc) & FOUND) != 0) { 6088fae3551SRodney W. Grimes lfdir = idesc.id_parent; 6098fae3551SRodney W. Grimes } else { 6108fae3551SRodney W. Grimes pwarn("NO lost+found DIRECTORY"); 6118fae3551SRodney W. Grimes if (preen || reply("CREATE")) { 6121dc349abSEd Maste lfdir = allocdir(UFS_ROOTINO, (ino_t)0, lfmode); 6138fae3551SRodney W. Grimes if (lfdir != 0) { 6141dc349abSEd Maste if (makeentry(UFS_ROOTINO, lfdir, 6151dc349abSEd Maste lfname) != 0) { 616d33e92f9SJulian Elischer numdirs++; 6178fae3551SRodney W. Grimes if (preen) 6188fae3551SRodney W. Grimes printf(" (CREATED)\n"); 6198fae3551SRodney W. Grimes } else { 62052f97104SKirk McKusick freedirino(lfdir, UFS_ROOTINO); 6218fae3551SRodney W. Grimes lfdir = 0; 6228fae3551SRodney W. Grimes if (preen) 6238fae3551SRodney W. Grimes printf("\n"); 6248fae3551SRodney W. Grimes } 6258fae3551SRodney W. Grimes } 6268fae3551SRodney W. Grimes } 6278fae3551SRodney W. Grimes } 6285cc52631SKirk McKusick irelse(&ip); 629e5d0d1c5SKirk McKusick free(idesc.id_name); 6308fae3551SRodney W. Grimes if (lfdir == 0) { 6318fae3551SRodney W. Grimes pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY"); 6328fae3551SRodney W. Grimes printf("\n\n"); 6338fae3551SRodney W. Grimes return (0); 6348fae3551SRodney W. Grimes } 6358fae3551SRodney W. Grimes } 6365cc52631SKirk McKusick ginode(lfdir, &ip); 6375cc52631SKirk McKusick dp = ip.i_dp; 638d8ba45e2SEd Maste if ((DIP(dp, di_mode) & IFMT) != IFDIR) { 6398fae3551SRodney W. Grimes pfatal("lost+found IS NOT A DIRECTORY"); 6405cc52631SKirk McKusick if (reply("REALLOCATE") == 0) { 6415cc52631SKirk McKusick irelse(&ip); 6428fae3551SRodney W. Grimes return (0); 6435cc52631SKirk McKusick } 6448fae3551SRodney W. Grimes oldlfdir = lfdir; 6451dc349abSEd Maste if ((lfdir = allocdir(UFS_ROOTINO, (ino_t)0, lfmode)) == 0) { 6468fae3551SRodney W. Grimes pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n"); 6475cc52631SKirk McKusick irelse(&ip); 6488fae3551SRodney W. Grimes return (0); 6498fae3551SRodney W. Grimes } 650fe5e6e2cSKirk McKusick if ((changeino(UFS_ROOTINO, lfname, lfdir, 1) & ALTERED) == 0) { 6518fae3551SRodney W. Grimes pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n"); 6525cc52631SKirk McKusick irelse(&ip); 6538fae3551SRodney W. Grimes return (0); 6548fae3551SRodney W. Grimes } 6555cc52631SKirk McKusick idesc.id_type = inoinfo(oldlfdir)->ino_idtype; 6567180f1abSKirk McKusick idesc.id_func = freeblock; 6578fae3551SRodney W. Grimes idesc.id_number = oldlfdir; 658d33e92f9SJulian Elischer adjust(&idesc, inoinfo(oldlfdir)->ino_linkcnt + 1); 659d33e92f9SJulian Elischer inoinfo(oldlfdir)->ino_linkcnt = 0; 6605cc52631SKirk McKusick inodirty(&ip); 6615cc52631SKirk McKusick irelse(&ip); 6625cc52631SKirk McKusick ginode(lfdir, &ip); 6635cc52631SKirk McKusick dp = ip.i_dp; 6648fae3551SRodney W. Grimes } 665d33e92f9SJulian Elischer if (inoinfo(lfdir)->ino_state != DFOUND) { 6668fae3551SRodney W. Grimes pfatal("SORRY. NO lost+found DIRECTORY\n\n"); 6675cc52631SKirk McKusick irelse(&ip); 6688fae3551SRodney W. Grimes return (0); 6698fae3551SRodney W. Grimes } 6708fae3551SRodney W. Grimes (void)lftempname(tempname, orphan); 671d33e92f9SJulian Elischer if (makeentry(lfdir, orphan, (name ? name : tempname)) == 0) { 6728fae3551SRodney W. Grimes pfatal("SORRY. NO SPACE IN lost+found DIRECTORY"); 6738fae3551SRodney W. Grimes printf("\n\n"); 6745cc52631SKirk McKusick irelse(&ip); 6758fae3551SRodney W. Grimes return (0); 6768fae3551SRodney W. Grimes } 677d33e92f9SJulian Elischer inoinfo(orphan)->ino_linkcnt--; 6788fae3551SRodney W. Grimes if (lostdir) { 679fe5e6e2cSKirk McKusick depth = DIP(dp, di_dirdepth) + 1; 680fe5e6e2cSKirk McKusick if ((changeino(orphan, "..", lfdir, depth) & ALTERED) == 0 && 6818fae3551SRodney W. Grimes parentdir != (ino_t)-1) 6828fae3551SRodney W. Grimes (void)makeentry(orphan, lfdir, ".."); 683c3b2344bSScott Long DIP_SET(dp, di_nlink, DIP(dp, di_nlink) + 1); 6845cc52631SKirk McKusick inodirty(&ip); 685d33e92f9SJulian Elischer inoinfo(lfdir)->ino_linkcnt++; 686bf58d635SIan Dowse pwarn("DIR I=%lu CONNECTED. ", (u_long)orphan); 687f4fc3895SKirk McKusick inp = getinoinfo(parentdir); 68852f97104SKirk McKusick if (parentdir != (ino_t)-1 && inp != NULL) { 689b1046626SBruce Evans printf("PARENT WAS I=%lu\n", (u_long)parentdir); 6906b100474SJulian Elischer /* 691f4fc3895SKirk McKusick * If the parent directory did not have to 692f4fc3895SKirk McKusick * be replaced then because of the ordering 6936b100474SJulian Elischer * guarantees, has had the link count incremented 6946b100474SJulian Elischer * for the child, but no entry was made. This 6956b100474SJulian Elischer * fixes the parent link count so that fsck does 6966b100474SJulian Elischer * not need to be rerun. 6976b100474SJulian Elischer */ 69852f97104SKirk McKusick if ((inp->i_flags & INFO_NEW) != 0) 699d33e92f9SJulian Elischer inoinfo(parentdir)->ino_linkcnt++; 700119e9fc2SNate Williams } 7018fae3551SRodney W. Grimes if (preen == 0) 7028fae3551SRodney W. Grimes printf("\n"); 7038fae3551SRodney W. Grimes } 7045cc52631SKirk McKusick irelse(&ip); 7058fae3551SRodney W. Grimes return (1); 7068fae3551SRodney W. Grimes } 7078fae3551SRodney W. Grimes 7088fae3551SRodney W. Grimes /* 7098fae3551SRodney W. Grimes * fix an entry in a directory. 7108fae3551SRodney W. Grimes */ 71131f4ab50SBruce Evans int 712fe5e6e2cSKirk McKusick changeino(ino_t dir, const char *name, ino_t newnum, int depth) 7138fae3551SRodney W. Grimes { 7148fae3551SRodney W. Grimes struct inodesc idesc; 7155cc52631SKirk McKusick struct inode ip; 7165cc52631SKirk McKusick int error; 7178fae3551SRodney W. Grimes 718780a5c1eSPeter Wemm memset(&idesc, 0, sizeof(struct inodesc)); 7198fae3551SRodney W. Grimes idesc.id_type = DATA; 7208fae3551SRodney W. Grimes idesc.id_func = chgino; 7218fae3551SRodney W. Grimes idesc.id_number = dir; 7228fae3551SRodney W. Grimes idesc.id_fix = DONTKNOW; 723599304a4SPoul-Henning Kamp idesc.id_name = strdup(name); 7248fae3551SRodney W. Grimes idesc.id_parent = newnum; /* new value for name */ 7255cc52631SKirk McKusick ginode(dir, &ip); 726fe5e6e2cSKirk McKusick if (((error = ckinode(ip.i_dp, &idesc)) & ALTERED) && newnum != 0) { 727fe5e6e2cSKirk McKusick DIP_SET(ip.i_dp, di_dirdepth, depth); 728fe5e6e2cSKirk McKusick getinoinfo(dir)->i_depth = depth; 729fe5e6e2cSKirk McKusick } 730e5d0d1c5SKirk McKusick free(idesc.id_name); 7315cc52631SKirk McKusick irelse(&ip); 7325cc52631SKirk McKusick return (error); 7338fae3551SRodney W. Grimes } 7348fae3551SRodney W. Grimes 7358fae3551SRodney W. Grimes /* 7368fae3551SRodney W. Grimes * make an entry in a directory 7378fae3551SRodney W. Grimes */ 73831f4ab50SBruce Evans int 739599304a4SPoul-Henning Kamp makeentry(ino_t parent, ino_t ino, const char *name) 7408fae3551SRodney W. Grimes { 7415cc52631SKirk McKusick struct inode ip; 7421c85e6a3SKirk McKusick union dinode *dp; 7438fae3551SRodney W. Grimes struct inodesc idesc; 7445cc52631SKirk McKusick int retval; 7458fae3551SRodney W. Grimes char pathbuf[MAXPATHLEN + 1]; 7468fae3551SRodney W. Grimes 7471dc349abSEd Maste if (parent < UFS_ROOTINO || parent >= maxino || 7481dc349abSEd Maste ino < UFS_ROOTINO || ino >= maxino) 7498fae3551SRodney W. Grimes return (0); 750780a5c1eSPeter Wemm memset(&idesc, 0, sizeof(struct inodesc)); 7518fae3551SRodney W. Grimes idesc.id_type = DATA; 7528fae3551SRodney W. Grimes idesc.id_func = mkentry; 7538fae3551SRodney W. Grimes idesc.id_number = parent; 7548fae3551SRodney W. Grimes idesc.id_parent = ino; /* this is the inode to enter */ 7558fae3551SRodney W. Grimes idesc.id_fix = DONTKNOW; 756599304a4SPoul-Henning Kamp idesc.id_name = strdup(name); 7575cc52631SKirk McKusick ginode(parent, &ip); 7585cc52631SKirk McKusick dp = ip.i_dp; 7591c85e6a3SKirk McKusick if (DIP(dp, di_size) % DIRBLKSIZ) { 760c3b2344bSScott Long DIP_SET(dp, di_size, roundup(DIP(dp, di_size), DIRBLKSIZ)); 7615cc52631SKirk McKusick inodirty(&ip); 7628fae3551SRodney W. Grimes } 7635cc52631SKirk McKusick if ((ckinode(dp, &idesc) & ALTERED) != 0) { 7645cc52631SKirk McKusick irelse(&ip); 765e5d0d1c5SKirk McKusick free(idesc.id_name); 7668fae3551SRodney W. Grimes return (1); 7675cc52631SKirk McKusick } 7688fae3551SRodney W. Grimes getpathname(pathbuf, parent, parent); 7695cc52631SKirk McKusick if (expanddir(&ip, pathbuf) == 0) { 7705cc52631SKirk McKusick irelse(&ip); 771e5d0d1c5SKirk McKusick free(idesc.id_name); 7728fae3551SRodney W. Grimes return (0); 7735cc52631SKirk McKusick } 7745cc52631SKirk McKusick retval = ckinode(dp, &idesc) & ALTERED; 7755cc52631SKirk McKusick irelse(&ip); 776e5d0d1c5SKirk McKusick free(idesc.id_name); 7775cc52631SKirk McKusick return (retval); 7788fae3551SRodney W. Grimes } 7798fae3551SRodney W. Grimes 7808fae3551SRodney W. Grimes /* 7818fae3551SRodney W. Grimes * Attempt to expand the size of a directory 7828fae3551SRodney W. Grimes */ 783780a5c1eSPeter Wemm static int 7845cc52631SKirk McKusick expanddir(struct inode *ip, char *name) 7858fae3551SRodney W. Grimes { 786997f81afSKirk McKusick ufs2_daddr_t lastlbn, oldblk, newblk, indirblk; 787997f81afSKirk McKusick size_t filesize, lastlbnsize; 788997f81afSKirk McKusick struct bufarea *bp, *nbp; 7897180f1abSKirk McKusick struct inodesc idesc; 7905cc52631SKirk McKusick union dinode *dp; 791460ed610SKirk McKusick long cg, indiralloced; 792997f81afSKirk McKusick char *cp; 7938fae3551SRodney W. Grimes 794997f81afSKirk McKusick nbp = NULL; 795997f81afSKirk McKusick indiralloced = newblk = indirblk = 0; 796460ed610SKirk McKusick memset(&idesc, 0, sizeof(struct inodesc)); 797460ed610SKirk McKusick idesc.id_type = ADDR; 798997f81afSKirk McKusick pwarn("NO SPACE LEFT IN %s", name); 799997f81afSKirk McKusick if (!preen && reply("EXPAND") == 0) 8008fae3551SRodney W. Grimes return (0); 801460ed610SKirk McKusick cg = ino_to_cg(&sblock, ip->i_number); 8025cc52631SKirk McKusick dp = ip->i_dp; 803997f81afSKirk McKusick filesize = DIP(dp, di_size); 804997f81afSKirk McKusick lastlbn = lblkno(&sblock, filesize); 805997f81afSKirk McKusick /* 806997f81afSKirk McKusick * We only expand lost+found to a single indirect block. 807997f81afSKirk McKusick */ 808997f81afSKirk McKusick if ((DIP(dp, di_mode) & IFMT) != IFDIR || filesize == 0 || 809997f81afSKirk McKusick lastlbn >= UFS_NDADDR + NINDIR(&sblock)) 810997f81afSKirk McKusick goto bad; 811997f81afSKirk McKusick /* 812997f81afSKirk McKusick * If last block is a fragment, expand it to a full size block. 813997f81afSKirk McKusick */ 814997f81afSKirk McKusick lastlbnsize = sblksize(&sblock, filesize, lastlbn); 815c8a7a3ffSKirk McKusick if (lastlbnsize > 0 && lastlbnsize < sblock.fs_bsize) { 816997f81afSKirk McKusick oldblk = DIP(dp, di_db[lastlbn]); 817997f81afSKirk McKusick bp = getdirblk(oldblk, lastlbnsize); 8188fae3551SRodney W. Grimes if (bp->b_errs) 8198fae3551SRodney W. Grimes goto bad; 820460ed610SKirk McKusick newblk = allocblk(cg, sblock.fs_frag, std_checkblkavail); 821460ed610SKirk McKusick if (newblk == 0) 822997f81afSKirk McKusick goto bad; 823997f81afSKirk McKusick nbp = getdatablk(newblk, sblock.fs_bsize, BT_DIRDATA); 824997f81afSKirk McKusick if (nbp->b_errs) 825997f81afSKirk McKusick goto bad; 826997f81afSKirk McKusick DIP_SET(dp, di_db[lastlbn], newblk); 827997f81afSKirk McKusick DIP_SET(dp, di_size, filesize + sblock.fs_bsize - lastlbnsize); 828997f81afSKirk McKusick DIP_SET(dp, di_blocks, DIP(dp, di_blocks) + 829997f81afSKirk McKusick btodb(sblock.fs_bsize - lastlbnsize)); 8305cc52631SKirk McKusick inodirty(ip); 831997f81afSKirk McKusick memmove(nbp->b_un.b_buf, bp->b_un.b_buf, lastlbnsize); 832997f81afSKirk McKusick memset(&nbp->b_un.b_buf[lastlbnsize], 0, 833997f81afSKirk McKusick sblock.fs_bsize - lastlbnsize); 834997f81afSKirk McKusick for (cp = &nbp->b_un.b_buf[lastlbnsize]; 835997f81afSKirk McKusick cp < &nbp->b_un.b_buf[sblock.fs_bsize]; 836997f81afSKirk McKusick cp += DIRBLKSIZ) 837997f81afSKirk McKusick memmove(cp, &emptydir, sizeof emptydir); 838997f81afSKirk McKusick dirty(nbp); 8395cc52631SKirk McKusick brelse(nbp); 840460ed610SKirk McKusick binval(bp); 841997f81afSKirk McKusick idesc.id_blkno = oldblk; 842997f81afSKirk McKusick idesc.id_numfrags = numfrags(&sblock, lastlbnsize); 843997f81afSKirk McKusick (void)freeblock(&idesc); 8445cc52631SKirk McKusick if (preen) 8455cc52631SKirk McKusick printf(" (EXPANDED)\n"); 846997f81afSKirk McKusick return (1); 847997f81afSKirk McKusick } 848460ed610SKirk McKusick if ((newblk = allocblk(cg, sblock.fs_frag, std_checkblkavail)) == 0) 849997f81afSKirk McKusick goto bad; 8508fae3551SRodney W. Grimes bp = getdirblk(newblk, sblock.fs_bsize); 8518fae3551SRodney W. Grimes if (bp->b_errs) 8528fae3551SRodney W. Grimes goto bad; 853997f81afSKirk McKusick memset(bp->b_un.b_buf, 0, sblock.fs_bsize); 854997f81afSKirk McKusick for (cp = bp->b_un.b_buf; 8558fae3551SRodney W. Grimes cp < &bp->b_un.b_buf[sblock.fs_bsize]; 8568fae3551SRodney W. Grimes cp += DIRBLKSIZ) 857780a5c1eSPeter Wemm memmove(cp, &emptydir, sizeof emptydir); 8588fae3551SRodney W. Grimes dirty(bp); 859997f81afSKirk McKusick if (lastlbn < UFS_NDADDR) { 860997f81afSKirk McKusick DIP_SET(dp, di_db[lastlbn], newblk); 861997f81afSKirk McKusick } else { 862997f81afSKirk McKusick /* 863997f81afSKirk McKusick * Allocate indirect block if needed. 864997f81afSKirk McKusick */ 865997f81afSKirk McKusick if ((indirblk = DIP(dp, di_ib[0])) == 0) { 866460ed610SKirk McKusick indirblk = allocblk(cg, sblock.fs_frag, 867460ed610SKirk McKusick std_checkblkavail); 868460ed610SKirk McKusick if (indirblk == 0) { 869460ed610SKirk McKusick binval(bp); 8708fae3551SRodney W. Grimes goto bad; 871460ed610SKirk McKusick } 872997f81afSKirk McKusick indiralloced = 1; 873997f81afSKirk McKusick } 874997f81afSKirk McKusick nbp = getdatablk(indirblk, sblock.fs_bsize, BT_LEVEL1); 875997f81afSKirk McKusick if (nbp->b_errs) 876997f81afSKirk McKusick goto bad; 877997f81afSKirk McKusick if (indiralloced) { 878997f81afSKirk McKusick memset(nbp->b_un.b_buf, 0, sblock.fs_bsize); 879997f81afSKirk McKusick DIP_SET(dp, di_ib[0], indirblk); 880997f81afSKirk McKusick DIP_SET(dp, di_blocks, 881997f81afSKirk McKusick DIP(dp, di_blocks) + btodb(sblock.fs_bsize)); 882997f81afSKirk McKusick } 883997f81afSKirk McKusick IBLK_SET(nbp, lastlbn - UFS_NDADDR, newblk); 884997f81afSKirk McKusick dirty(nbp); 8855cc52631SKirk McKusick brelse(nbp); 886997f81afSKirk McKusick } 887997f81afSKirk McKusick DIP_SET(dp, di_size, filesize + sblock.fs_bsize); 888997f81afSKirk McKusick DIP_SET(dp, di_blocks, DIP(dp, di_blocks) + btodb(sblock.fs_bsize)); 8895cc52631SKirk McKusick inodirty(ip); 8908fae3551SRodney W. Grimes if (preen) 8918fae3551SRodney W. Grimes printf(" (EXPANDED)\n"); 8928fae3551SRodney W. Grimes return (1); 8938fae3551SRodney W. Grimes bad: 894997f81afSKirk McKusick pfatal(" (EXPANSION FAILED)\n"); 895460ed610SKirk McKusick if (nbp != NULL) { 896460ed610SKirk McKusick binval(bp); 8975cc52631SKirk McKusick brelse(nbp); 898460ed610SKirk McKusick } 899997f81afSKirk McKusick if (newblk != 0) { 9007180f1abSKirk McKusick idesc.id_blkno = newblk; 9017180f1abSKirk McKusick idesc.id_numfrags = sblock.fs_frag; 9027180f1abSKirk McKusick (void)freeblock(&idesc); 903997f81afSKirk McKusick } 904997f81afSKirk McKusick if (indiralloced) { 905997f81afSKirk McKusick idesc.id_blkno = indirblk; 906997f81afSKirk McKusick idesc.id_numfrags = sblock.fs_frag; 907997f81afSKirk McKusick (void)freeblock(&idesc); 908997f81afSKirk McKusick } 9098fae3551SRodney W. Grimes return (0); 9108fae3551SRodney W. Grimes } 9118fae3551SRodney W. Grimes 9128fae3551SRodney W. Grimes /* 9138fae3551SRodney W. Grimes * allocate a new directory 9148fae3551SRodney W. Grimes */ 9153eeb5bdcSBruce Evans ino_t 916b70cd7eeSWarner Losh allocdir(ino_t parent, ino_t request, int mode) 9178fae3551SRodney W. Grimes { 9188fae3551SRodney W. Grimes ino_t ino; 9198fae3551SRodney W. Grimes char *cp; 9205cc52631SKirk McKusick struct inode ip; 9211c85e6a3SKirk McKusick union dinode *dp; 92291ea1615SKirk McKusick struct bufarea *bp; 9238fae3551SRodney W. Grimes struct dirtemplate *dirp; 924fe5e6e2cSKirk McKusick struct inoinfo *inp, *parentinp; 9258fae3551SRodney W. Grimes 926d8ba45e2SEd Maste ino = allocino(request, IFDIR|mode); 92784a0e3f9SKirk McKusick if (ino == 0) 92884a0e3f9SKirk McKusick return (0); 9298fae3551SRodney W. Grimes dirp = &dirhead; 9308fae3551SRodney W. Grimes dirp->dot_ino = ino; 9318fae3551SRodney W. Grimes dirp->dotdot_ino = parent; 9325cc52631SKirk McKusick ginode(ino, &ip); 9335cc52631SKirk McKusick dp = ip.i_dp; 9341c85e6a3SKirk McKusick bp = getdirblk(DIP(dp, di_db[0]), sblock.fs_fsize); 9358fae3551SRodney W. Grimes if (bp->b_errs) { 9368fae3551SRodney W. Grimes freeino(ino); 9375cc52631SKirk McKusick irelse(&ip); 9388fae3551SRodney W. Grimes return (0); 9398fae3551SRodney W. Grimes } 940780a5c1eSPeter Wemm memmove(bp->b_un.b_buf, dirp, sizeof(struct dirtemplate)); 9418fae3551SRodney W. Grimes for (cp = &bp->b_un.b_buf[DIRBLKSIZ]; 9428fae3551SRodney W. Grimes cp < &bp->b_un.b_buf[sblock.fs_fsize]; 9438fae3551SRodney W. Grimes cp += DIRBLKSIZ) 944780a5c1eSPeter Wemm memmove(cp, &emptydir, sizeof emptydir); 9458fae3551SRodney W. Grimes dirty(bp); 946c3b2344bSScott Long DIP_SET(dp, di_nlink, 2); 9475cc52631SKirk McKusick inodirty(&ip); 9481dc349abSEd Maste if (ino == UFS_ROOTINO) { 949f4fc3895SKirk McKusick inp = cacheino(dp, ino); 950f4fc3895SKirk McKusick inp->i_parent = parent; 951f4fc3895SKirk McKusick inp->i_dotdot = parent; 95252f97104SKirk McKusick inp->i_flags |= INFO_NEW; 95352f97104SKirk McKusick inoinfo(ino)->ino_type = DT_DIR; 95452f97104SKirk McKusick inoinfo(ino)->ino_linkcnt = DIP(dp, di_nlink); 9555cc52631SKirk McKusick irelse(&ip); 9568fae3551SRodney W. Grimes return(ino); 9578fae3551SRodney W. Grimes } 958af6726e6SDon Lewis if (!INO_IS_DVALID(parent)) { 9598fae3551SRodney W. Grimes freeino(ino); 9605cc52631SKirk McKusick irelse(&ip); 9618fae3551SRodney W. Grimes return (0); 9628fae3551SRodney W. Grimes } 963f4fc3895SKirk McKusick inp = cacheino(dp, ino); 96491ea1615SKirk McKusick inp->i_parent = parent; 96591ea1615SKirk McKusick inp->i_dotdot = parent; 96652f97104SKirk McKusick inp->i_flags |= INFO_NEW; 967fe5e6e2cSKirk McKusick if ((parentinp = getinoinfo(inp->i_parent)) == NULL) { 968fe5e6e2cSKirk McKusick pfatal("allocdir: UNKNOWN PARENT DIR"); 969fe5e6e2cSKirk McKusick } else { 970fe5e6e2cSKirk McKusick inp->i_depth = parentinp->i_depth + 1; 971fe5e6e2cSKirk McKusick DIP_SET(dp, di_dirdepth, inp->i_depth); 972fe5e6e2cSKirk McKusick } 97352f97104SKirk McKusick inoinfo(ino)->ino_type = DT_DIR; 974d33e92f9SJulian Elischer inoinfo(ino)->ino_state = inoinfo(parent)->ino_state; 975d33e92f9SJulian Elischer if (inoinfo(ino)->ino_state == DSTATE) { 9761c85e6a3SKirk McKusick inoinfo(ino)->ino_linkcnt = DIP(dp, di_nlink); 977d33e92f9SJulian Elischer inoinfo(parent)->ino_linkcnt++; 9788fae3551SRodney W. Grimes } 9795cc52631SKirk McKusick irelse(&ip); 9805cc52631SKirk McKusick ginode(parent, &ip); 9815cc52631SKirk McKusick dp = ip.i_dp; 982c3b2344bSScott Long DIP_SET(dp, di_nlink, DIP(dp, di_nlink) + 1); 9835cc52631SKirk McKusick inodirty(&ip); 9845cc52631SKirk McKusick irelse(&ip); 9858fae3551SRodney W. Grimes return (ino); 9868fae3551SRodney W. Grimes } 9878fae3551SRodney W. Grimes 9888fae3551SRodney W. Grimes /* 9898fae3551SRodney W. Grimes * free a directory inode 9908fae3551SRodney W. Grimes */ 99152f97104SKirk McKusick void 99252f97104SKirk McKusick freedirino(ino_t ino, ino_t parent) 9938fae3551SRodney W. Grimes { 9945cc52631SKirk McKusick struct inode ip; 9951c85e6a3SKirk McKusick union dinode *dp; 9968fae3551SRodney W. Grimes 9978fae3551SRodney W. Grimes if (ino != parent) { 9985cc52631SKirk McKusick ginode(parent, &ip); 9995cc52631SKirk McKusick dp = ip.i_dp; 1000c3b2344bSScott Long DIP_SET(dp, di_nlink, DIP(dp, di_nlink) - 1); 10015cc52631SKirk McKusick inodirty(&ip); 10025cc52631SKirk McKusick irelse(&ip); 10038fae3551SRodney W. Grimes } 100452f97104SKirk McKusick removecachedino(ino); 10058fae3551SRodney W. Grimes freeino(ino); 10068fae3551SRodney W. Grimes } 10078fae3551SRodney W. Grimes 10088fae3551SRodney W. Grimes /* 10098fae3551SRodney W. Grimes * generate a temporary name for the lost+found directory. 10108fae3551SRodney W. Grimes */ 1011780a5c1eSPeter Wemm static int 1012b70cd7eeSWarner Losh lftempname(char *bufp, ino_t ino) 10138fae3551SRodney W. Grimes { 10143d438ad6SDavid E. O'Brien ino_t in; 10153d438ad6SDavid E. O'Brien char *cp; 10168fae3551SRodney W. Grimes int namlen; 10178fae3551SRodney W. Grimes 10188fae3551SRodney W. Grimes cp = bufp + 2; 10198fae3551SRodney W. Grimes for (in = maxino; in > 0; in /= 10) 10208fae3551SRodney W. Grimes cp++; 10218fae3551SRodney W. Grimes *--cp = 0; 10228fae3551SRodney W. Grimes namlen = cp - bufp; 10238fae3551SRodney W. Grimes in = ino; 10248fae3551SRodney W. Grimes while (cp > bufp) { 10258fae3551SRodney W. Grimes *--cp = (in % 10) + '0'; 10268fae3551SRodney W. Grimes in /= 10; 10278fae3551SRodney W. Grimes } 10288fae3551SRodney W. Grimes *cp = '#'; 10298fae3551SRodney W. Grimes return (namlen); 10308fae3551SRodney W. Grimes } 10318fae3551SRodney W. Grimes 10328fae3551SRodney W. Grimes /* 10338fae3551SRodney W. Grimes * Get a directory block. 10348fae3551SRodney W. Grimes * Insure that it is held until another is requested. 10358fae3551SRodney W. Grimes */ 1036780a5c1eSPeter Wemm static struct bufarea * 10371c85e6a3SKirk McKusick getdirblk(ufs2_daddr_t blkno, long size) 10388fae3551SRodney W. Grimes { 10398fae3551SRodney W. Grimes 10405cc52631SKirk McKusick if (pdirbp != NULL && pdirbp->b_errs == 0) 10415cc52631SKirk McKusick brelse(pdirbp); 1042ed75b5a1SKirk McKusick pdirbp = getdatablk(blkno, size, BT_DIRDATA); 10438fae3551SRodney W. Grimes return (pdirbp); 10448fae3551SRodney W. Grimes } 1045