19111Stwisti/*- 212886Shseigel * SPDX-License-Identifier: BSD-3-Clause 39111Stwisti * 49111Stwisti * Copyright (c) 1980, 1986, 1993 59111Stwisti * The Regents of the University of California. All rights reserved. 69111Stwisti * 79111Stwisti * Redistribution and use in source and binary forms, with or without 89111Stwisti * modification, are permitted provided that the following conditions 99111Stwisti * are met: 109111Stwisti * 1. Redistributions of source code must retain the above copyright 119111Stwisti * notice, this list of conditions and the following disclaimer. 129111Stwisti * 2. Redistributions in binary form must reproduce the above copyright 139111Stwisti * notice, this list of conditions and the following disclaimer in the 149111Stwisti * documentation and/or other materials provided with the distribution. 159111Stwisti * 3. Neither the name of the University nor the names of its contributors 169111Stwisti * may be used to endorse or promote products derived from this software 179111Stwisti * without specific prior written permission. 189111Stwisti * 199111Stwisti * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 209111Stwisti * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 219111Stwisti * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 229111Stwisti * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 239111Stwisti * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 249111Stwisti * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 259111Stwisti * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 269111Stwisti * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 279111Stwisti * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 289111Stwisti * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 299111Stwisti * SUCH DAMAGE. 309111Stwisti */ 319111Stwisti 329111Stwisti#include <sys/param.h> 339111Stwisti#include <sys/sysctl.h> 349111Stwisti 359111Stwisti#include <ufs/ufs/dinode.h> 369111Stwisti#include <ufs/ufs/dir.h> 379111Stwisti#include <ufs/ffs/fs.h> 389111Stwisti 3910762Sjprovino#include <err.h> 409111Stwisti#include <errno.h> 419111Stwisti#include <stdint.h> 429111Stwisti#include <string.h> 439111Stwisti 449111Stwisti#include "fsck.h" 459111Stwisti 469111Stwisti#define MINDIRSIZE (sizeof (struct dirtemplate)) 479111Stwisti 489111Stwististatic int fix_extraneous(struct inoinfo *, struct inodesc *); 499111Stwististatic int deleteentry(struct inodesc *); 509111Stwististatic int blksort(const void *, const void *); 519111Stwististatic int pass2check(struct inodesc *); 5210047Snever 5310047Snevervoid 5410047Sneverpass2(void) 5510047Snever{ 5610047Snever struct inode ip; 5710047Snever union dinode *dp; 5810047Snever struct inoinfo **inpp, *inp; 5910047Snever struct inoinfo **inpend; 6010047Snever struct inodesc curino; 6110047Snever union dinode dino; 6210047Snever int i; 639111Stwisti char pathbuf[MAXPATHLEN + 1]; 649111Stwisti 659111Stwisti switch (inoinfo(UFS_ROOTINO)->ino_state) { 669111Stwisti 679111Stwisti case USTATE: 6812953Scoleenp pfatal("ROOT INODE UNALLOCATED"); 699266Scoleenp if (reply("ALLOCATE") == 0) { 7012953Scoleenp ckfini(0); 719111Stwisti exit(EEXIT); 729266Scoleenp } 739111Stwisti if (allocdir(UFS_ROOTINO, UFS_ROOTINO, 0755) != UFS_ROOTINO) 749111Stwisti errx(EEXIT, "CANNOT ALLOCATE ROOT INODE"); 759111Stwisti break; 769266Scoleenp 779111Stwisti case DCLEAR: 7812953Scoleenp pfatal("DUPS/BAD IN ROOT INODE"); 799111Stwisti if (reply("REALLOCATE")) { 809266Scoleenp freedirino(UFS_ROOTINO, UFS_ROOTINO); 8110420Salanb if (allocdir(UFS_ROOTINO, UFS_ROOTINO, 0755) != 8212953Scoleenp UFS_ROOTINO) 8310420Salanb errx(EEXIT, "CANNOT ALLOCATE ROOT INODE"); 849111Stwisti break; 859111Stwisti } 869111Stwisti if (reply("CONTINUE") == 0) { 879111Stwisti ckfini(0); 889111Stwisti exit(EEXIT); 8912953Scoleenp } 9012953Scoleenp break; 9112953Scoleenp 9212953Scoleenp case FSTATE: 939111Stwisti case FCLEAR: 949111Stwisti case FZLINK: 959111Stwisti pfatal("ROOT INODE NOT DIRECTORY"); 969111Stwisti if (reply("REALLOCATE")) { 979111Stwisti freeino(UFS_ROOTINO); 989111Stwisti if (allocdir(UFS_ROOTINO, UFS_ROOTINO, 0755) != 999111Stwisti UFS_ROOTINO) 1009111Stwisti errx(EEXIT, "CANNOT ALLOCATE ROOT INODE"); 1019111Stwisti break; 10212953Scoleenp } 1039111Stwisti if (reply("FIX") == 0) { 1049111Stwisti ckfini(0); 1059111Stwisti exit(EEXIT); 1069111Stwisti } 1079111Stwisti ginode(UFS_ROOTINO, &ip); 10812953Scoleenp dp = ip.i_dp; 1099111Stwisti DIP_SET(dp, di_mode, DIP(dp, di_mode) & ~IFMT); 1109111Stwisti DIP_SET(dp, di_mode, DIP(dp, di_mode) | IFDIR); 1119111Stwisti inodirty(&ip); 1129111Stwisti irelse(&ip); 11312953Scoleenp break; 1149111Stwisti 1159111Stwisti case DSTATE: 1169111Stwisti case DZLINK: 1179111Stwisti break; 11812953Scoleenp 1199111Stwisti default: 12012953Scoleenp errx(EEXIT, "BAD STATE %d FOR ROOT INODE", 1219111Stwisti inoinfo(UFS_ROOTINO)->ino_state); 1229111Stwisti } 1239111Stwisti inoinfo(UFS_ROOTINO)->ino_state = DFOUND; 1249111Stwisti inoinfo(UFS_WINO)->ino_state = FSTATE; 1259111Stwisti inoinfo(UFS_WINO)->ino_type = DT_WHT; 1269111Stwisti /* 1279111Stwisti * Sort the directory list into disk block order. 1289111Stwisti */ 1299111Stwisti qsort((char *)inpsort, (size_t)inplast, sizeof *inpsort, blksort); 1309111Stwisti /* 1319111Stwisti * Check the integrity of each directory. 1329111Stwisti */ 1339111Stwisti memset(&curino, 0, sizeof(struct inodesc)); 1349111Stwisti curino.id_type = DATA; 1359111Stwisti curino.id_func = pass2check; 13612953Scoleenp inpend = &inpsort[inplast]; 1379111Stwisti for (inpp = inpsort; inpp < inpend; inpp++) { 1389111Stwisti if (got_siginfo) { 13912953Scoleenp printf("%s: phase 2: dir %td of %d (%d%%)\n", cdevname, 1409111Stwisti inpp - inpsort, (int)inplast, 1419111Stwisti (int)((inpp - inpsort) * 100 / inplast)); 1429111Stwisti got_siginfo = 0; 1439111Stwisti } 14412953Scoleenp if (got_sigalarm) { 1459111Stwisti setproctitle("%s p2 %d%%", cdevname, 14612953Scoleenp (int)((inpp - inpsort) * 100 / inplast)); 1479111Stwisti got_sigalarm = 0; 1489111Stwisti } 1499111Stwisti inp = *inpp; 15012953Scoleenp if (inp->i_isize == 0) 1519111Stwisti continue; 1529111Stwisti if (inp->i_isize < MINDIRSIZE) { 1539111Stwisti direrror(inp->i_number, "DIRECTORY TOO SHORT"); 1549111Stwisti inp->i_isize = roundup(MINDIRSIZE, DIRBLKSIZ); 1559111Stwisti if (reply("FIX") == 1) { 1569111Stwisti ginode(inp->i_number, &ip); 1579111Stwisti DIP_SET(ip.i_dp, di_size, inp->i_isize); 1589111Stwisti inodirty(&ip); 1599111Stwisti irelse(&ip); 1609111Stwisti } 1619111Stwisti } else if ((inp->i_isize & (DIRBLKSIZ - 1)) != 0) { 16212953Scoleenp getpathname(pathbuf, inp->i_number, inp->i_number); 1639111Stwisti if (usedsoftdep) 1649111Stwisti pfatal("%s %s: LENGTH %jd NOT MULTIPLE OF %d", 1659111Stwisti "DIRECTORY", pathbuf, 16612953Scoleenp (intmax_t)inp->i_isize, DIRBLKSIZ); 1679111Stwisti else 1689111Stwisti pwarn("%s %s: LENGTH %jd NOT MULTIPLE OF %d", 1699111Stwisti "DIRECTORY", pathbuf, 1709111Stwisti (intmax_t)inp->i_isize, DIRBLKSIZ); 1719111Stwisti if (preen) 17212953Scoleenp printf(" (ADJUSTED)\n"); 17312953Scoleenp inp->i_isize = roundup(inp->i_isize, DIRBLKSIZ); 17412953Scoleenp if (preen || reply("ADJUST") == 1) { 1759111Stwisti ginode(inp->i_number, &ip); 1769111Stwisti DIP_SET(ip.i_dp, di_size, 1779111Stwisti roundup(inp->i_isize, DIRBLKSIZ)); 1789111Stwisti inodirty(&ip); 17912953Scoleenp irelse(&ip); 1809111Stwisti } 1819111Stwisti } 18212953Scoleenp dp = &dino; 1839111Stwisti memset(dp, 0, sizeof(struct ufs2_dinode)); 18412953Scoleenp DIP_SET(dp, di_mode, IFDIR); 1859111Stwisti DIP_SET(dp, di_size, inp->i_isize); 18612953Scoleenp for (i = 0; i < MIN(inp->i_numblks, UFS_NDADDR); i++) 1879111Stwisti DIP_SET(dp, di_db[i], inp->i_blks[i]); 1889111Stwisti if (inp->i_numblks > UFS_NDADDR) 1899111Stwisti for (i = 0; i < UFS_NIADDR; i++) 19012953Scoleenp DIP_SET(dp, di_ib[i], 1919111Stwisti inp->i_blks[UFS_NDADDR + i]); 19212953Scoleenp curino.id_number = inp->i_number; 19312953Scoleenp curino.id_parent = inp->i_parent; 19412953Scoleenp (void)ckinode(dp, &curino); 19512953Scoleenp } 1969111Stwisti /* 19712953Scoleenp * Now that the parents of all directories have been found, 1989111Stwisti * make another pass to verify the value of `..' 1999111Stwisti */ 20012953Scoleenp for (inpp = inpsort; inpp < inpend; inpp++) { 2019111Stwisti inp = *inpp; 2029111Stwisti if (inp->i_parent == 0 || inp->i_isize == 0) 2039111Stwisti continue; 2049111Stwisti if (inoinfo(inp->i_parent)->ino_state == DFOUND && 2059111Stwisti INO_IS_DUNFOUND(inp->i_number)) { 2069111Stwisti inoinfo(inp->i_number)->ino_state = DFOUND; 2079111Stwisti check_dirdepth(inp); 20812953Scoleenp } 2099111Stwisti if (inp->i_dotdot == inp->i_parent || 2109111Stwisti inp->i_dotdot == (ino_t)-1) 2119111Stwisti continue; 2129111Stwisti if (inp->i_dotdot == 0) { 2139111Stwisti inp->i_dotdot = inp->i_parent; 2149111Stwisti if (debug) 2159111Stwisti fileerror(inp->i_parent, inp->i_number, 2169111Stwisti "DEFERRED MISSING '..' FIX"); 2179111Stwisti (void)makeentry(inp->i_number, inp->i_parent, ".."); 2189111Stwisti inoinfo(inp->i_parent)->ino_linkcnt--; 2199111Stwisti continue; 22012953Scoleenp } 22112953Scoleenp /* 22212953Scoleenp * Here we have: 22312953Scoleenp * inp->i_number is directory with bad ".." in it. 2249111Stwisti * inp->i_dotdot is current value of "..". 22512953Scoleenp * inp->i_parent is directory to which ".." should point. 2269111Stwisti */ 2279111Stwisti getpathname(pathbuf, inp->i_parent, inp->i_number); 2289111Stwisti printf("BAD INODE NUMBER FOR '..' in DIR I=%ju (%s)\n", 2299111Stwisti (uintmax_t)inp->i_number, pathbuf); 2309111Stwisti getpathname(pathbuf, inp->i_dotdot, inp->i_dotdot); 2319111Stwisti printf("CURRENTLY POINTS TO I=%ju (%s), ", 2329111Stwisti (uintmax_t)inp->i_dotdot, pathbuf); 23312953Scoleenp getpathname(pathbuf, inp->i_parent, inp->i_parent); 2349111Stwisti printf("SHOULD POINT TO I=%ju (%s)", 2359111Stwisti (uintmax_t)inp->i_parent, pathbuf); 2369111Stwisti if (cursnapshot != 0) { 2379111Stwisti /* 2389111Stwisti * We need to: 2399111Stwisti * setcwd(inp->i_number); 2409111Stwisti * setdotdot(inp->i_dotdot, inp->i_parent); 2419111Stwisti */ 2429111Stwisti cmd.value = inp->i_number; 2439111Stwisti if (sysctlbyname("vfs.ffs.setcwd", 0, 0, 2449111Stwisti &cmd, sizeof cmd) == -1) { 2459111Stwisti /* kernel lacks support for these functions */ 2469111Stwisti printf(" (IGNORED)\n"); 2479111Stwisti continue; 2489111Stwisti } 2499111Stwisti cmd.value = inp->i_dotdot; /* verify same value */ 2509111Stwisti cmd.size = inp->i_parent; /* new parent */ 25112953Scoleenp if (sysctlbyname("vfs.ffs.setdotdot", 0, 0, 25212953Scoleenp &cmd, sizeof cmd) == -1) { 25312953Scoleenp printf(" (FIX FAILED: %s)\n", strerror(errno)); 2549111Stwisti continue; 2559111Stwisti } 2569111Stwisti printf(" (FIXED)\n"); 2579111Stwisti inoinfo(inp->i_parent)->ino_linkcnt--; 2589111Stwisti inp->i_dotdot = inp->i_parent; 2599111Stwisti continue; 2609111Stwisti } 2619111Stwisti if (preen) 2629111Stwisti printf(" (FIXED)\n"); 2639111Stwisti else if (reply("FIX") == 0) 26412953Scoleenp continue; 2659111Stwisti inoinfo(inp->i_dotdot)->ino_linkcnt++; 2669111Stwisti inoinfo(inp->i_parent)->ino_linkcnt--; 2679111Stwisti inp->i_dotdot = inp->i_parent; 2689111Stwisti (void)changeino(inp->i_number, "..", inp->i_parent, 2699111Stwisti getinoinfo(inp->i_parent)->i_depth + 1); 2709111Stwisti } 2719111Stwisti /* 2729111Stwisti * Mark all the directories that can be found from the root. 2739111Stwisti */ 27412953Scoleenp propagate(); 2759111Stwisti} 2769111Stwisti 2779111Stwististatic int 2789111Stwistipass2check(struct inodesc *idesc) 2799111Stwisti{ 2809111Stwisti struct direct *dirp = idesc->id_dirp; 2819111Stwisti char dirname[MAXPATHLEN + 1]; 28212953Scoleenp struct inoinfo *inp; 28312953Scoleenp int n, entrysize, ret = 0; 28412953Scoleenp struct inode ip; 28512953Scoleenp union dinode *dp; 28611352Scoleenp const char *errmsg; 28711352Scoleenp struct direct proto, *newdirp; 28812623Sjcm 28912953Scoleenp /* 29012623Sjcm * check for "." 2919111Stwisti */ 29212953Scoleenp if (idesc->id_entryno != 0) 2939111Stwisti goto chk1; 2949111Stwisti if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) { 2959111Stwisti if (dirp->d_ino != idesc->id_number) { 2969111Stwisti direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'"); 2979111Stwisti if (reply("FIX") == 1) { 2989111Stwisti dirp->d_ino = idesc->id_number; 2999111Stwisti ret |= ALTERED; 3009111Stwisti } 3019111Stwisti } 3029111Stwisti if (dirp->d_type != DT_DIR) { 3039111Stwisti direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'"); 3049111Stwisti if (reply("FIX") == 1) { 3059111Stwisti dirp->d_type = DT_DIR; 3069111Stwisti ret |= ALTERED; 3079111Stwisti } 3089111Stwisti } 3099111Stwisti goto chk1; 3109111Stwisti } 3119111Stwisti proto.d_ino = idesc->id_number; 3129111Stwisti proto.d_type = DT_DIR; 3139111Stwisti proto.d_namlen = 1; 3149111Stwisti (void)strcpy(proto.d_name, "."); 3159111Stwisti entrysize = DIRSIZ(0, &proto); 3169111Stwisti direrror(idesc->id_number, "MISSING '.'"); 3179111Stwisti errmsg = "ADD '.' ENTRY"; 3189248Scoleenp if (dirp->d_reclen < entrysize + DIRSIZ(0, dirp)) { 3199111Stwisti /* Not enough space to add '.', replace first entry with '.' */ 32012953Scoleenp if (dirp->d_ino != 0) { 3219111Stwisti pwarn("\nFIRST ENTRY IN DIRECTORY CONTAINS %s\n", 3229111Stwisti dirp->d_name); 3239111Stwisti errmsg = "REPLACE WITH '.'"; 3249111Stwisti } 3259111Stwisti if (reply(errmsg) == 0) 3269111Stwisti goto chk1; 3279111Stwisti proto.d_reclen = dirp->d_reclen; 3289111Stwisti memmove(dirp, &proto, (size_t)entrysize); 3299111Stwisti ret |= ALTERED; 3309111Stwisti } else { 3319111Stwisti /* Move over first entry and add '.' entry */ 3329111Stwisti if (reply(errmsg) == 0) 3339111Stwisti goto chk1; 3349111Stwisti newdirp = (struct direct *)((char *)(dirp) + entrysize); 3359111Stwisti dirp->d_reclen -= entrysize; 33612953Scoleenp memmove(newdirp, dirp, dirp->d_reclen); 3379111Stwisti proto.d_reclen = entrysize; 3389111Stwisti memmove(dirp, &proto, (size_t)entrysize); 3399111Stwisti idesc->id_entryno++; 3409111Stwisti inoinfo(idesc->id_number)->ino_linkcnt--; 3419111Stwisti dirp = newdirp; 3429111Stwisti ret |= ALTERED; 34312953Scoleenp } 34412953Scoleenpchk1: 3459111Stwisti if (idesc->id_entryno > 1) 3469111Stwisti goto chk2; 3479111Stwisti inp = getinoinfo(idesc->id_number); 3489111Stwisti proto.d_ino = inp->i_parent; 3499111Stwisti proto.d_type = DT_DIR; 3509111Stwisti proto.d_namlen = 2; 3519111Stwisti (void)strcpy(proto.d_name, ".."); 3529111Stwisti entrysize = DIRSIZ(0, &proto); 3539111Stwisti if (idesc->id_entryno == 0) { 3549111Stwisti n = DIRSIZ(0, dirp); 3559111Stwisti if (dirp->d_reclen < n + entrysize) 3569111Stwisti goto chk2; 3579111Stwisti proto.d_reclen = dirp->d_reclen - n; 3589111Stwisti dirp->d_reclen = n; 3599111Stwisti idesc->id_entryno++; 36013254Sjwilhelm inoinfo(dirp->d_ino)->ino_linkcnt--; 36113254Sjwilhelm dirp = (struct direct *)((char *)(dirp) + n); 3629111Stwisti memset(dirp, 0, (size_t)proto.d_reclen); 3639111Stwisti dirp->d_reclen = proto.d_reclen; 3649111Stwisti } 3659111Stwisti if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) { 36611352Scoleenp if (dirp->d_ino >= maxino) { 36712623Sjcm direrror(idesc->id_number, "BAD INODE NUMBER FOR '..'"); 3689111Stwisti /* 3699111Stwisti * If we know parent set it now, otherwise let it 3709111Stwisti * point to the root inode and it will get cleaned 3719111Stwisti * up later if that is not correct. 3729111Stwisti */ 3739111Stwisti if (inp->i_parent != 0) 3749111Stwisti dirp->d_ino = inp->i_parent; 3759111Stwisti else 3769111Stwisti dirp->d_ino = UFS_ROOTINO; 3779111Stwisti if (reply("FIX") == 1) 3789111Stwisti ret |= ALTERED; 3799111Stwisti } 3809111Stwisti inp->i_dotdot = dirp->d_ino; 3819111Stwisti if (dirp->d_type != DT_DIR) { 3829111Stwisti direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'"); 3839111Stwisti dirp->d_type = DT_DIR; 3849111Stwisti if (reply("FIX") == 1) 3859111Stwisti ret |= ALTERED; 3869111Stwisti } 38712953Scoleenp goto chk2; 3889111Stwisti } 3899111Stwisti fileerror(inp->i_parent != 0 ? inp->i_parent : idesc->id_number, 3909111Stwisti idesc->id_number, "MISSING '..'"); 3919266Scoleenp errmsg = "ADD '..' ENTRY"; 39212953Scoleenp if (dirp->d_reclen < entrysize + DIRSIZ(0, dirp)) { 3939266Scoleenp /* No space to add '..', replace second entry with '..' */ 39412953Scoleenp if (dirp->d_ino != 0) { 3959111Stwisti pfatal("SECOND ENTRY IN DIRECTORY CONTAINS %s\n", 3969111Stwisti dirp->d_name); 3979111Stwisti errmsg = "REPLACE WITH '..'"; 3989111Stwisti } 3999111Stwisti if (reply(errmsg) == 0) { 4009111Stwisti inp->i_dotdot = (ino_t)-1; 4019111Stwisti goto chk2; 4029111Stwisti } 4039248Scoleenp if (proto.d_ino == 0) { 4049111Stwisti /* Defer processing until parent known */ 40512953Scoleenp idesc->id_entryno++; 4069111Stwisti if (debug) 4079111Stwisti printf("(FIX DEFERRED)\n"); 4089111Stwisti } 4099111Stwisti inp->i_dotdot = proto.d_ino; 4109111Stwisti proto.d_reclen = dirp->d_reclen; 4119111Stwisti memmove(dirp, &proto, (size_t)entrysize); 4129111Stwisti ret |= ALTERED; 4139111Stwisti } else { 4149111Stwisti /* Move over second entry and add '..' entry */ 4159111Stwisti if (reply(errmsg) == 0) { 4169111Stwisti inp->i_dotdot = (ino_t)-1; 4179111Stwisti goto chk2; 4189111Stwisti } 4199111Stwisti if (proto.d_ino == 0) { 4209111Stwisti /* Defer processing until parent known */ 4219111Stwisti idesc->id_entryno++; 4229111Stwisti if (debug) 4239111Stwisti printf("(FIX DEFERRED)\n"); 4249111Stwisti } 4259111Stwisti inp->i_dotdot = proto.d_ino; 4269111Stwisti if (dirp->d_ino == 0) { 4279111Stwisti proto.d_reclen = dirp->d_reclen; 4289111Stwisti memmove(dirp, &proto, (size_t)entrysize); 4299111Stwisti } else { 4309111Stwisti newdirp = (struct direct *)((char *)(dirp) + entrysize); 4319111Stwisti dirp->d_reclen -= entrysize; 4329111Stwisti memmove(newdirp, dirp, dirp->d_reclen); 4339111Stwisti proto.d_reclen = entrysize; 4349111Stwisti memmove(dirp, &proto, (size_t)entrysize); 4359111Stwisti if (dirp->d_ino != 0) { 4369111Stwisti idesc->id_entryno++; 4379111Stwisti inoinfo(dirp->d_ino)->ino_linkcnt--; 4389111Stwisti } 4399111Stwisti dirp = newdirp; 4409111Stwisti } 44110800Snever ret |= ALTERED; 4429111Stwisti } 4439111Stwistichk2: 4449111Stwisti if (dirp->d_ino == 0) 4459111Stwisti return (ret|KEEPON); 4469111Stwisti if (dirp->d_namlen <= 2 && 4479111Stwisti dirp->d_name[0] == '.' && 4489111Stwisti idesc->id_entryno >= 2) { 4499111Stwisti if (dirp->d_namlen == 1) { 4509111Stwisti direrror(idesc->id_number, "EXTRA '.' ENTRY"); 4519111Stwisti dirp->d_ino = 0; 4529111Stwisti if (reply("FIX") == 1) 4539111Stwisti ret |= ALTERED; 4549111Stwisti return (KEEPON | ret); 4559111Stwisti } 4569111Stwisti if (dirp->d_name[1] == '.') { 4579111Stwisti direrror(idesc->id_number, "EXTRA '..' ENTRY"); 4589111Stwisti dirp->d_ino = 0; 4599111Stwisti if (reply("FIX") == 1) 4609453Stwisti ret |= ALTERED; 4619111Stwisti return (KEEPON | ret); 4629111Stwisti } 4639111Stwisti } 4649111Stwisti idesc->id_entryno++; 4659111Stwisti n = 0; 4669111Stwisti if (dirp->d_ino >= maxino) { 4679111Stwisti fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE"); 4689111Stwisti n = reply("REMOVE"); 4699111Stwisti } else if (((dirp->d_ino == UFS_WINO && dirp->d_type != DT_WHT) || 4709111Stwisti (dirp->d_ino != UFS_WINO && dirp->d_type == DT_WHT))) { 4719111Stwisti fileerror(idesc->id_number, dirp->d_ino, "BAD WHITEOUT ENTRY"); 4729111Stwisti dirp->d_ino = UFS_WINO; 4739111Stwisti dirp->d_type = DT_WHT; 4749111Stwisti if (reply("FIX") == 1) 4759111Stwisti ret |= ALTERED; 4769111Stwisti } else { 4779111Stwistiagain: 4789111Stwisti switch (inoinfo(dirp->d_ino)->ino_state) { 4799111Stwisti case USTATE: 4809111Stwisti if (idesc->id_entryno <= 2) 4819111Stwisti break; 4829111Stwisti fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED"); 4839111Stwisti n = reply("REMOVE"); 4849111Stwisti break; 4859111Stwisti 4869111Stwisti case DCLEAR: 4879111Stwisti case FCLEAR: 4889111Stwisti if (idesc->id_entryno <= 2) 4899111Stwisti break; 4909111Stwisti if (inoinfo(dirp->d_ino)->ino_state == FCLEAR) 4919111Stwisti errmsg = "DUP/BAD"; 4929111Stwisti else if (!preen && !usedsoftdep) 4939111Stwisti errmsg = "ZERO LENGTH DIRECTORY"; 4949111Stwisti else if (cursnapshot == 0) { 4959111Stwisti n = 1; 4969111Stwisti break; 4979111Stwisti } else { 4989111Stwisti getpathname(dirname, idesc->id_number, 4999111Stwisti dirp->d_ino); 5009111Stwisti pwarn("ZERO LENGTH DIRECTORY %s I=%ju", 5019111Stwisti dirname, (uintmax_t)dirp->d_ino); 5029287Stwisti /* 5039111Stwisti * We need to: 5049111Stwisti * setcwd(idesc->id_parent); 5059111Stwisti * rmdir(dirp->d_name); 5069111Stwisti */ 5079287Stwisti cmd.value = idesc->id_number; 5089111Stwisti if (sysctlbyname("vfs.ffs.setcwd", 0, 0, 5099111Stwisti &cmd, sizeof cmd) == -1) { 5109111Stwisti /* kernel lacks support */ 5119111Stwisti printf(" (IGNORED)\n"); 5129111Stwisti n = 1; 5139111Stwisti break; 5149111Stwisti } 5159111Stwisti if (rmdir(dirp->d_name) == -1) { 5169111Stwisti printf(" (REMOVAL FAILED: %s)\n", 5179111Stwisti strerror(errno)); 5189111Stwisti n = 1; 5199111Stwisti break; 5209111Stwisti } 5219111Stwisti /* ".." reference to parent is removed */ 5229111Stwisti inoinfo(idesc->id_number)->ino_linkcnt--; 5239111Stwisti printf(" (REMOVED)\n"); 5249111Stwisti break; 5259111Stwisti } 5269111Stwisti fileerror(idesc->id_number, dirp->d_ino, errmsg); 5279111Stwisti if ((n = reply("REMOVE")) == 1) 5289111Stwisti break; 5299111Stwisti ginode(dirp->d_ino, &ip); 5309111Stwisti dp = ip.i_dp; 5319111Stwisti inoinfo(dirp->d_ino)->ino_state = 5329111Stwisti (DIP(dp, di_mode) & IFMT) == IFDIR ? DSTATE : FSTATE; 5339111Stwisti inoinfo(dirp->d_ino)->ino_linkcnt = DIP(dp, di_nlink); 5349111Stwisti irelse(&ip); 5359111Stwisti goto again; 5369111Stwisti 5379111Stwisti case DSTATE: 5389111Stwisti case DZLINK: 5399111Stwisti if (inoinfo(idesc->id_number)->ino_state == DFOUND) 5409111Stwisti inoinfo(dirp->d_ino)->ino_state = DFOUND; 5419111Stwisti /* FALLTHROUGH */ 54210047Snever 54310047Snever case DFOUND: 54410047Snever inp = getinoinfo(dirp->d_ino); 5459111Stwisti if (idesc->id_entryno > 2) { 5469111Stwisti if (inp->i_parent == 0) { 5479111Stwisti inp->i_parent = idesc->id_number; 5489111Stwisti check_dirdepth(inp); 5499111Stwisti } else if ((n = fix_extraneous(inp, idesc))) { 55011079Srbackman break; 5519111Stwisti } 5529111Stwisti } 5539111Stwisti /* FALLTHROUGH */ 5549111Stwisti 5559111Stwisti case FSTATE: 5569111Stwisti case FZLINK: 5579111Stwisti if (dirp->d_type != inoinfo(dirp->d_ino)->ino_type) { 5589111Stwisti fileerror(idesc->id_number, dirp->d_ino, 5599111Stwisti "BAD TYPE VALUE"); 5609111Stwisti dirp->d_type = inoinfo(dirp->d_ino)->ino_type; 5619111Stwisti if (reply("FIX") == 1) 5629111Stwisti ret |= ALTERED; 5639111Stwisti } 5649111Stwisti inoinfo(dirp->d_ino)->ino_linkcnt--; 5659111Stwisti break; 5669111Stwisti 5679111Stwisti default: 5689111Stwisti errx(EEXIT, "BAD STATE %d FOR INODE I=%ju", 5699111Stwisti inoinfo(dirp->d_ino)->ino_state, 5709111Stwisti (uintmax_t)dirp->d_ino); 5719111Stwisti } 5729111Stwisti } 5739111Stwisti if (n == 0) 5749111Stwisti return (ret|KEEPON); 5759111Stwisti dirp->d_ino = 0; 5769111Stwisti return (ret|KEEPON|ALTERED); 5779111Stwisti} 5789111Stwisti 5799111Stwististatic int 5809111Stwistifix_extraneous(struct inoinfo *inp, struct inodesc *idesc) 5819111Stwisti{ 5829111Stwisti char *cp; 5839111Stwisti struct inode ip; 5849111Stwisti struct inodesc dotdesc; 5859111Stwisti char oldname[MAXPATHLEN + 1]; 5869111Stwisti char newname[MAXPATHLEN + 1]; 5879111Stwisti 5889111Stwisti /* 5899111Stwisti * If we have not yet found "..", look it up now so we know 5909111Stwisti * which inode the directory itself believes is its parent. 5919111Stwisti */ 5929111Stwisti if (inp->i_dotdot == 0) { 5939111Stwisti memset(&dotdesc, 0, sizeof(struct inodesc)); 5949111Stwisti dotdesc.id_type = DATA; 5959111Stwisti dotdesc.id_number = idesc->id_dirp->d_ino; 5969111Stwisti dotdesc.id_func = findino; 5979111Stwisti dotdesc.id_name = strdup(".."); 59810240Snever ginode(dotdesc.id_number, &ip); 59910240Snever if ((ckinode(ip.i_dp, &dotdesc) & FOUND)) 60010240Snever inp->i_dotdot = dotdesc.id_parent; 60110240Snever irelse(&ip); 60210240Snever free(dotdesc.id_name); 60310240Snever } 60410240Snever /* 6059111Stwisti * We have the previously found old name (inp->i_parent) and the 6069111Stwisti * just found new name (idesc->id_number). We have five cases: 6079111Stwisti * 1) ".." is missing - can remove either name, choose to delete 6089111Stwisti * new one and let fsck create ".." pointing to old name. 609 * 2) Both new and old are in same directory, choose to delete 610 * the new name and let fsck fix ".." if it is wrong. 611 * 3) ".." does not point to the new name, so delete it and let 612 * fsck fix ".." to point to the old one if it is wrong. 613 * 4) ".." points to the old name only, so delete the new one. 614 * 5) ".." points to the new name only, so delete the old one. 615 * 616 * For cases 1-4 we eliminate the new name; 617 * for case 5 we eliminate the old name. 618 */ 619 if (inp->i_dotdot == 0 || /* Case 1 */ 620 idesc->id_number == inp->i_parent || /* Case 2 */ 621 inp->i_dotdot != idesc->id_number || /* Case 3 */ 622 inp->i_dotdot == inp->i_parent) { /* Case 4 */ 623 getpathname(newname, idesc->id_number, idesc->id_number); 624 if (strcmp(newname, "/") != 0) 625 strcat (newname, "/"); 626 strcat(newname, idesc->id_dirp->d_name); 627 getpathname(oldname, inp->i_number, inp->i_number); 628 pwarn("%s IS AN EXTRANEOUS HARD LINK TO DIRECTORY %s", 629 newname, oldname); 630 if (cursnapshot != 0) { 631 /* 632 * We need to 633 * setcwd(idesc->id_number); 634 * unlink(idesc->id_dirp->d_name); 635 */ 636 cmd.value = idesc->id_number; 637 if (sysctlbyname("vfs.ffs.setcwd", 0, 0, 638 &cmd, sizeof cmd) == -1) { 639 printf(" (IGNORED)\n"); 640 return (0); 641 } 642 cmd.value = (intptr_t)idesc->id_dirp->d_name; 643 cmd.size = inp->i_number; /* verify same name */ 644 if (sysctlbyname("vfs.ffs.unlink", 0, 0, 645 &cmd, sizeof cmd) == -1) { 646 printf(" (UNLINK FAILED: %s)\n", 647 strerror(errno)); 648 return (0); 649 } 650 printf(" (REMOVED)\n"); 651 return (0); 652 } 653 if (preen) { 654 printf(" (REMOVED)\n"); 655 return (1); 656 } 657 return (reply("REMOVE")); 658 } 659 /* 660 * None of the first four cases above, so must be case (5). 661 * Eliminate the old name and make the new the name the parent. 662 */ 663 getpathname(oldname, inp->i_parent, inp->i_number); 664 getpathname(newname, inp->i_number, inp->i_number); 665 pwarn("%s IS AN EXTRANEOUS HARD LINK TO DIRECTORY %s", oldname, 666 newname); 667 if (cursnapshot != 0) { 668 /* 669 * We need to 670 * setcwd(inp->i_parent); 671 * unlink(last component of oldname pathname); 672 */ 673 cmd.value = inp->i_parent; 674 if (sysctlbyname("vfs.ffs.setcwd", 0, 0, 675 &cmd, sizeof cmd) == -1) { 676 printf(" (IGNORED)\n"); 677 return (0); 678 } 679 if ((cp = strchr(oldname, '/')) == NULL) { 680 printf(" (IGNORED)\n"); 681 return (0); 682 } 683 cmd.value = (intptr_t)(cp + 1); 684 cmd.size = inp->i_number; /* verify same name */ 685 if (sysctlbyname("vfs.ffs.unlink", 0, 0, 686 &cmd, sizeof cmd) == -1) { 687 printf(" (UNLINK FAILED: %s)\n", 688 strerror(errno)); 689 return (0); 690 } 691 printf(" (REMOVED)\n"); 692 inp->i_parent = idesc->id_number; /* reparent to correct dir */ 693 return (0); 694 } 695 if (!preen && !reply("REMOVE")) 696 return (0); 697 memset(&dotdesc, 0, sizeof(struct inodesc)); 698 dotdesc.id_type = DATA; 699 dotdesc.id_number = inp->i_parent; /* directory in which name appears */ 700 dotdesc.id_parent = inp->i_number; /* inode number in entry to delete */ 701 dotdesc.id_func = deleteentry; 702 ginode(dotdesc.id_number, &ip); 703 if ((ckinode(ip.i_dp, &dotdesc) & FOUND) && preen) 704 printf(" (REMOVED)\n"); 705 irelse(&ip); 706 inp->i_parent = idesc->id_number; /* reparent to correct directory */ 707 inoinfo(inp->i_number)->ino_linkcnt++; /* name gone, return reference */ 708 return (0); 709} 710 711static int 712deleteentry(struct inodesc *idesc) 713{ 714 struct direct *dirp = idesc->id_dirp; 715 716 if (idesc->id_entryno++ < 2 || dirp->d_ino != idesc->id_parent) 717 return (KEEPON); 718 dirp->d_ino = 0; 719 return (ALTERED|STOP|FOUND); 720} 721 722/* 723 * Routine to sort disk blocks. 724 */ 725static int 726blksort(const void *arg1, const void *arg2) 727{ 728 729 return ((*(struct inoinfo * const *)arg1)->i_blks[0] - 730 (*(struct inoinfo * const *)arg2)->i_blks[0]); 731} 732