1331722Seadler/* 21558Srgrimes * Copyright (c) 1980, 1986, 1993 31558Srgrimes * The Regents of the University of California. All rights reserved. 41558Srgrimes * 51558Srgrimes * Redistribution and use in source and binary forms, with or without 61558Srgrimes * modification, are permitted provided that the following conditions 71558Srgrimes * are met: 81558Srgrimes * 1. Redistributions of source code must retain the above copyright 91558Srgrimes * notice, this list of conditions and the following disclaimer. 101558Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111558Srgrimes * notice, this list of conditions and the following disclaimer in the 121558Srgrimes * documentation and/or other materials provided with the distribution. 131558Srgrimes * 4. Neither the name of the University nor the names of its contributors 141558Srgrimes * may be used to endorse or promote products derived from this software 151558Srgrimes * without specific prior written permission. 161558Srgrimes * 171558Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 181558Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 191558Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 201558Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 211558Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 221558Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 231558Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 241558Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 251558Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 261558Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 271558Srgrimes * SUCH DAMAGE. 281558Srgrimes */ 291558Srgrimes 30114589Sobrien#if 0 311558Srgrimes#ifndef lint 3223675Speterstatic const char sccsid[] = "@(#)dir.c 8.8 (Berkeley) 4/28/95"; 33114589Sobrien#endif /* not lint */ 3441477Sjulian#endif 35114589Sobrien#include <sys/cdefs.h> 36114589Sobrien__FBSDID("$FreeBSD: stable/11/sbin/fsck_ffs/dir.c 348260 2019-05-25 00:22:07Z mckusick $"); 371558Srgrimes 381558Srgrimes#include <sys/param.h> 3941474Sjulian#include <sys/time.h> 40275030Sbapt#include <sys/types.h> 4174556Smckusick#include <sys/sysctl.h> 4223675Speter 431558Srgrimes#include <ufs/ufs/dinode.h> 441558Srgrimes#include <ufs/ufs/dir.h> 451558Srgrimes#include <ufs/ffs/fs.h> 4623796Sbde 4723675Speter#include <err.h> 481558Srgrimes#include <string.h> 4923675Speter 501558Srgrimes#include "fsck.h" 511558Srgrimes 52260068Sscottlstatic struct dirtemplate emptydir = { 5374556Smckusick 0, DIRBLKSIZ, DT_UNKNOWN, 0, "", 5474556Smckusick 0, 0, DT_UNKNOWN, 0, "" 5574556Smckusick}; 56260068Sscottlstatic struct dirtemplate dirhead = { 571558Srgrimes 0, 12, DT_DIR, 1, ".", 581558Srgrimes 0, DIRBLKSIZ - 12, DT_DIR, 2, ".." 591558Srgrimes}; 601558Srgrimes 6192839Simpstatic int chgino(struct inodesc *); 62348260Smckusickstatic int dircheck(struct inodesc *, struct bufarea *, struct direct *); 6398542Smckusickstatic int expanddir(union dinode *dp, char *name); 6492839Simpstatic void freedir(ino_t ino, ino_t parent); 6592839Simpstatic struct direct *fsck_readdir(struct inodesc *); 6698542Smckusickstatic struct bufarea *getdirblk(ufs2_daddr_t blkno, long size); 6792839Simpstatic int lftempname(char *bufp, ino_t ino); 6892839Simpstatic int mkentry(struct inodesc *); 691558Srgrimes 701558Srgrimes/* 711558Srgrimes * Propagate connected state through the tree. 721558Srgrimes */ 737585Sbdevoid 7492839Simppropagate(void) 751558Srgrimes{ 7692806Sobrien struct inoinfo **inpp, *inp; 771558Srgrimes struct inoinfo **inpend; 781558Srgrimes long change; 791558Srgrimes 801558Srgrimes inpend = &inpsort[inplast]; 811558Srgrimes do { 821558Srgrimes change = 0; 831558Srgrimes for (inpp = inpsort; inpp < inpend; inpp++) { 841558Srgrimes inp = *inpp; 851558Srgrimes if (inp->i_parent == 0) 861558Srgrimes continue; 8741474Sjulian if (inoinfo(inp->i_parent)->ino_state == DFOUND && 88136281Struckman INO_IS_DUNFOUND(inp->i_number)) { 8941474Sjulian inoinfo(inp->i_number)->ino_state = DFOUND; 901558Srgrimes change++; 911558Srgrimes } 921558Srgrimes } 931558Srgrimes } while (change > 0); 941558Srgrimes} 951558Srgrimes 961558Srgrimes/* 971558Srgrimes * Scan each entry in a directory block. 981558Srgrimes */ 997585Sbdeint 10092839Simpdirscan(struct inodesc *idesc) 1011558Srgrimes{ 10292806Sobrien struct direct *dp; 10392806Sobrien struct bufarea *bp; 10474556Smckusick u_int dsize, n; 1051558Srgrimes long blksiz; 1061558Srgrimes char dbuf[DIRBLKSIZ]; 1071558Srgrimes 1081558Srgrimes if (idesc->id_type != DATA) 10923675Speter errx(EEXIT, "wrong type to dirscan %d", idesc->id_type); 1101558Srgrimes if (idesc->id_entryno == 0 && 1111558Srgrimes (idesc->id_filesize & (DIRBLKSIZ - 1)) != 0) 1121558Srgrimes idesc->id_filesize = roundup(idesc->id_filesize, DIRBLKSIZ); 1131558Srgrimes blksiz = idesc->id_numfrags * sblock.fs_fsize; 1141558Srgrimes if (chkrange(idesc->id_blkno, idesc->id_numfrags)) { 1151558Srgrimes idesc->id_filesize -= blksiz; 1161558Srgrimes return (SKIP); 1171558Srgrimes } 1181558Srgrimes idesc->id_loc = 0; 1191558Srgrimes for (dp = fsck_readdir(idesc); dp != NULL; dp = fsck_readdir(idesc)) { 1201558Srgrimes dsize = dp->d_reclen; 12141474Sjulian if (dsize > sizeof(dbuf)) 12241474Sjulian dsize = sizeof(dbuf); 12323675Speter memmove(dbuf, dp, (size_t)dsize); 1241558Srgrimes idesc->id_dirp = (struct direct *)dbuf; 1251558Srgrimes if ((n = (*idesc->id_func)(idesc)) & ALTERED) { 1261558Srgrimes bp = getdirblk(idesc->id_blkno, blksiz); 12723675Speter memmove(bp->b_un.b_buf + idesc->id_loc - dsize, dbuf, 1281558Srgrimes (size_t)dsize); 1291558Srgrimes dirty(bp); 1301558Srgrimes sbdirty(); 131260068Sscottl rerun = 1; 1321558Srgrimes } 1338871Srgrimes if (n & STOP) 1341558Srgrimes return (n); 1351558Srgrimes } 1361558Srgrimes return (idesc->id_filesize > 0 ? KEEPON : STOP); 1371558Srgrimes} 1381558Srgrimes 1391558Srgrimes/* 140348260Smckusick * Get and verify the next entry in a directory. 141348260Smckusick * We also verify that if there is another entry in the block that it is 142348260Smckusick * valid, so if it is not valid it can be subsumed into the current entry. 1431558Srgrimes */ 14423675Speterstatic struct direct * 14592839Simpfsck_readdir(struct inodesc *idesc) 1461558Srgrimes{ 14792806Sobrien struct direct *dp, *ndp; 14892806Sobrien struct bufarea *bp; 149348260Smckusick long size, blksiz, subsume_ndp; 1501558Srgrimes 151348260Smckusick subsume_ndp = 0; 1521558Srgrimes blksiz = idesc->id_numfrags * sblock.fs_fsize; 153348260Smckusick if (idesc->id_filesize <= 0 || idesc->id_loc >= blksiz) 154348260Smckusick return (NULL); 1551558Srgrimes bp = getdirblk(idesc->id_blkno, blksiz); 156348260Smckusick dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); 157348260Smckusick /* 158348260Smckusick * Only need to check current entry if it is the first in the 159348260Smckusick * the block, as later entries will have been checked in the 160348260Smckusick * previous call to this function. 161348260Smckusick */ 162348260Smckusick if (idesc->id_loc % DIRBLKSIZ != 0 || dircheck(idesc, bp, dp) != 0) { 163348260Smckusick /* 164348260Smckusick * Current entry is good, update to point at next. 165348260Smckusick */ 166348260Smckusick idesc->id_loc += dp->d_reclen; 167348260Smckusick idesc->id_filesize -= dp->d_reclen; 168348260Smckusick /* 169348260Smckusick * If at end of directory block, just return this entry. 170348260Smckusick */ 171348260Smckusick if (idesc->id_filesize <= 0 || idesc->id_loc >= blksiz || 172348260Smckusick idesc->id_loc % DIRBLKSIZ == 0) 173348260Smckusick return (dp); 174348260Smckusick /* 175348260Smckusick * If the next entry good, return this entry. 176348260Smckusick */ 177348260Smckusick ndp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); 178348260Smckusick if (dircheck(idesc, bp, ndp) != 0) 179348260Smckusick return (dp); 180348260Smckusick /* 181348260Smckusick * The next entry is bad, so subsume it and the remainder 182348260Smckusick * of this directory block into this entry. 183348260Smckusick */ 184348260Smckusick subsume_ndp = 1; 1851558Srgrimes } 186348260Smckusick /* 187348260Smckusick * Current or next entry is bad. Zap current entry or 188348260Smckusick * subsume next entry into current entry as appropriate. 189348260Smckusick */ 190348260Smckusick size = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ); 191348260Smckusick idesc->id_loc += size; 192348260Smckusick idesc->id_filesize -= size; 193348260Smckusick if (idesc->id_fix == IGNORE) 194348260Smckusick return (NULL); 195348260Smckusick if (subsume_ndp) { 196348260Smckusick memset(ndp, 0, size); 197348260Smckusick dp->d_reclen += size; 198348260Smckusick } else { 199348260Smckusick memset(dp, 0, size); 200348260Smckusick dp->d_reclen = size; 2011558Srgrimes } 202348260Smckusick if (dofix(idesc, "DIRECTORY CORRUPTED")) 203348260Smckusick dirty(bp); 2041558Srgrimes return (dp); 2051558Srgrimes} 2061558Srgrimes 2071558Srgrimes/* 2081558Srgrimes * Verify that a directory entry is valid. 2091558Srgrimes * This is a superset of the checks made in the kernel. 210347475Smckusick * Also optionally clears padding and unused directory space. 211347475Smckusick * 212348260Smckusick * Returns 0 if the entry is bad, 1 if the entry is good. 2131558Srgrimes */ 21423675Speterstatic int 215348260Smckusickdircheck(struct inodesc *idesc, struct bufarea *bp, struct direct *dp) 2161558Srgrimes{ 217114589Sobrien size_t size; 21892806Sobrien char *cp; 219225338Sdelphij u_int8_t namlen; 220347475Smckusick int spaceleft, modified, unused; 2211558Srgrimes 22223675Speter spaceleft = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ); 223348260Smckusick size = DIRSIZ(0, dp); 22441474Sjulian if (dp->d_reclen == 0 || 22523675Speter dp->d_reclen > spaceleft || 226348260Smckusick dp->d_reclen < size || 227348260Smckusick idesc->id_filesize < size || 228347475Smckusick (dp->d_reclen & (DIR_ROUNDUP - 1)) != 0) 22962668Smckusick goto bad; 230348260Smckusick modified = 0; 231347475Smckusick if (dp->d_ino == 0) { 232348260Smckusick if (!zflag || fswritefd < 0) 233348260Smckusick return (1); 234347475Smckusick /* 235348260Smckusick * Special case of an unused directory entry. Normally only 236348260Smckusick * occurs at the beginning of a directory block when the block 237348260Smckusick * contains no entries. Other than the first entry in a 238348260Smckusick * directory block, the kernel coalesces unused space with 239348260Smckusick * the previous entry by extending its d_reclen. However, 240348260Smckusick * when cleaning up a directory, fsck may set d_ino to zero 241348260Smckusick * in the middle of a directory block. If we're clearing out 242348260Smckusick * directory cruft (-z flag), then make sure that all directory 243348260Smckusick * space in entries with d_ino == 0 gets fully cleared. 244347475Smckusick */ 245348260Smckusick if (dp->d_type != 0) { 246348260Smckusick dp->d_type = 0; 247348260Smckusick modified = 1; 248348260Smckusick } 249348260Smckusick if (dp->d_namlen != 0) { 250348260Smckusick dp->d_namlen = 0; 251348260Smckusick modified = 1; 252348260Smckusick } 253348260Smckusick unused = dp->d_reclen - __offsetof(struct direct, d_name); 254348260Smckusick for (cp = dp->d_name; unused > 0; unused--, cp++) { 255348260Smckusick if (*cp != '\0') { 256348260Smckusick *cp = '\0'; 257347475Smckusick modified = 1; 258347475Smckusick } 259347475Smckusick } 260348260Smckusick if (modified) 261348260Smckusick dirty(bp); 262348260Smckusick return (1); 263347475Smckusick } 264348260Smckusick /* 265348260Smckusick * The d_type field should not be tested here. A bad type is an error 266348260Smckusick * in the entry itself but is not a corruption of the directory 267348260Smckusick * structure itself. So blowing away all the remaining entries in the 268348260Smckusick * directory block is inappropriate. Rather the type error should be 269348260Smckusick * checked in pass1 and fixed there. 270348260Smckusick * 271348260Smckusick * The name validation should also be done in pass1 although the 272348260Smckusick * check to see if the name is longer than fits in the space 273348260Smckusick * allocated for it (i.e., the *cp != '\0' fails after exiting the 274348260Smckusick * loop below) then it really is a structural error that requires 275348260Smckusick * the stronger action taken here. 276348260Smckusick */ 27796483Sphk namlen = dp->d_namlen; 278348260Smckusick if (namlen == 0 || dp->d_type > 15) 27962668Smckusick goto bad; 280348260Smckusick for (cp = dp->d_name, size = 0; size < namlen; size++) { 281348260Smckusick if (*cp == '\0' || *cp++ == '/') 28262668Smckusick goto bad; 283348260Smckusick } 28423675Speter if (*cp != '\0') 28562668Smckusick goto bad; 286347475Smckusick if (zflag && fswritefd >= 0) { 287347475Smckusick /* 288347475Smckusick * Clear unused directory entry space, including the d_name 289347475Smckusick * padding. 290347475Smckusick */ 291347475Smckusick /* First figure the number of pad bytes. */ 292347475Smckusick unused = roundup2(namlen + 1, DIR_ROUNDUP) - (namlen + 1); 293347475Smckusick 294347475Smckusick /* Add in the free space to the end of the record. */ 295347475Smckusick unused += dp->d_reclen - DIRSIZ(0, dp); 296347475Smckusick 297347475Smckusick /* 298347475Smckusick * Now clear out the unused space, keeping track if we actually 299347475Smckusick * changed anything. 300347475Smckusick */ 301347475Smckusick for (cp = &dp->d_name[namlen + 1]; unused > 0; unused--, cp++) { 302347475Smckusick if (*cp != '\0') { 303347475Smckusick *cp = '\0'; 304347475Smckusick modified = 1; 305347475Smckusick } 306347475Smckusick } 307347475Smckusick 308348260Smckusick if (modified) 309348260Smckusick dirty(bp); 310347475Smckusick } 31123675Speter return (1); 312347475Smckusick 31362668Smckusickbad: 31462668Smckusick if (debug) 31562668Smckusick printf("Bad dir: ino %d reclen %d namlen %d type %d name %s\n", 31662668Smckusick dp->d_ino, dp->d_reclen, dp->d_namlen, dp->d_type, 31762668Smckusick dp->d_name); 31862668Smckusick return (0); 3191558Srgrimes} 3201558Srgrimes 3217585Sbdevoid 322100935Sphkdirerror(ino_t ino, const char *errmesg) 3231558Srgrimes{ 3241558Srgrimes 3251558Srgrimes fileerror(ino, ino, errmesg); 3261558Srgrimes} 3271558Srgrimes 3287585Sbdevoid 329100935Sphkfileerror(ino_t cwd, ino_t ino, const char *errmesg) 3301558Srgrimes{ 33198542Smckusick union dinode *dp; 3321558Srgrimes char pathbuf[MAXPATHLEN + 1]; 3331558Srgrimes 3341558Srgrimes pwarn("%s ", errmesg); 3351558Srgrimes pinode(ino); 3361558Srgrimes printf("\n"); 3371558Srgrimes getpathname(pathbuf, cwd, ino); 3381558Srgrimes if (ino < ROOTINO || ino > maxino) { 3391558Srgrimes pfatal("NAME=%s\n", pathbuf); 3401558Srgrimes return; 3411558Srgrimes } 3421558Srgrimes dp = ginode(ino); 3431558Srgrimes if (ftypeok(dp)) 3441558Srgrimes pfatal("%s=%s\n", 34598542Smckusick (DIP(dp, di_mode) & IFMT) == IFDIR ? "DIR" : "FILE", 34698542Smckusick pathbuf); 3471558Srgrimes else 3481558Srgrimes pfatal("NAME=%s\n", pathbuf); 3491558Srgrimes} 3501558Srgrimes 3517585Sbdevoid 35292839Simpadjust(struct inodesc *idesc, int lcnt) 3531558Srgrimes{ 35498542Smckusick union dinode *dp; 35541474Sjulian int saveresolved; 3561558Srgrimes 3571558Srgrimes dp = ginode(idesc->id_number); 35898542Smckusick if (DIP(dp, di_nlink) == lcnt) { 35941474Sjulian /* 36041474Sjulian * If we have not hit any unresolved problems, are running 361102231Strhodes * in preen mode, and are on a file system using soft updates, 36241474Sjulian * then just toss any partially allocated files. 36341474Sjulian */ 36474556Smckusick if (resolved && (preen || bkgrdflag) && usedsoftdep) { 36541474Sjulian clri(idesc, "UNREF", 1); 36641474Sjulian return; 36741474Sjulian } else { 36841474Sjulian /* 369102231Strhodes * The file system can be marked clean even if 37041474Sjulian * a file is not linked up, but is cleared. 37141474Sjulian * Hence, resolved should not be cleared when 37241474Sjulian * linkup is answered no, but clri is answered yes. 37341474Sjulian */ 37441474Sjulian saveresolved = resolved; 37541474Sjulian if (linkup(idesc->id_number, (ino_t)0, NULL) == 0) { 37641474Sjulian resolved = saveresolved; 37741474Sjulian clri(idesc, "UNREF", 0); 37841474Sjulian return; 37941474Sjulian } 38041474Sjulian /* 38141474Sjulian * Account for the new reference created by linkup(). 38241474Sjulian */ 38341474Sjulian dp = ginode(idesc->id_number); 38441474Sjulian lcnt--; 38541474Sjulian } 38641474Sjulian } 38741474Sjulian if (lcnt != 0) { 3881558Srgrimes pwarn("LINK COUNT %s", (lfdir == idesc->id_number) ? lfname : 38998542Smckusick ((DIP(dp, di_mode) & IFMT) == IFDIR ? "DIR" : "FILE")); 3901558Srgrimes pinode(idesc->id_number); 3911558Srgrimes printf(" COUNT %d SHOULD BE %d", 39298542Smckusick DIP(dp, di_nlink), DIP(dp, di_nlink) - lcnt); 39334266Sjulian if (preen || usedsoftdep) { 3941558Srgrimes if (lcnt < 0) { 3951558Srgrimes printf("\n"); 3961558Srgrimes pfatal("LINK COUNT INCREASING"); 3971558Srgrimes } 39834266Sjulian if (preen) 39934266Sjulian printf(" (ADJUSTED)\n"); 4001558Srgrimes } 4011558Srgrimes if (preen || reply("ADJUST") == 1) { 40274556Smckusick if (bkgrdflag == 0) { 403134589Sscottl DIP_SET(dp, di_nlink, DIP(dp, di_nlink) - lcnt); 404344887Skib inodirty(dp); 40574556Smckusick } else { 40674556Smckusick cmd.value = idesc->id_number; 40774556Smckusick cmd.size = -lcnt; 40874556Smckusick if (debug) 409100935Sphk printf("adjrefcnt ino %ld amt %lld\n", 410100935Sphk (long)cmd.value, 411100935Sphk (long long)cmd.size); 41274556Smckusick if (sysctl(adjrefcnt, MIBSIZE, 0, 0, 41374556Smckusick &cmd, sizeof cmd) == -1) 41474556Smckusick rwerror("ADJUST INODE", cmd.value); 41574556Smckusick } 4161558Srgrimes } 4171558Srgrimes } 4181558Srgrimes} 4191558Srgrimes 42023675Speterstatic int 42192839Simpmkentry(struct inodesc *idesc) 4221558Srgrimes{ 42392806Sobrien struct direct *dirp = idesc->id_dirp; 4241558Srgrimes struct direct newent; 4251558Srgrimes int newlen, oldlen; 4261558Srgrimes 4271558Srgrimes newent.d_namlen = strlen(idesc->id_name); 4281558Srgrimes newlen = DIRSIZ(0, &newent); 4291558Srgrimes if (dirp->d_ino != 0) 4301558Srgrimes oldlen = DIRSIZ(0, dirp); 4311558Srgrimes else 4321558Srgrimes oldlen = 0; 4331558Srgrimes if (dirp->d_reclen - oldlen < newlen) 4341558Srgrimes return (KEEPON); 4351558Srgrimes newent.d_reclen = dirp->d_reclen - oldlen; 4361558Srgrimes dirp->d_reclen = oldlen; 4371558Srgrimes dirp = (struct direct *)(((char *)dirp) + oldlen); 4381558Srgrimes dirp->d_ino = idesc->id_parent; /* ino to be entered is in id_parent */ 43923675Speter dirp->d_reclen = newent.d_reclen; 44096483Sphk dirp->d_type = inoinfo(idesc->id_parent)->ino_type; 44123675Speter dirp->d_namlen = newent.d_namlen; 44223675Speter memmove(dirp->d_name, idesc->id_name, (size_t)newent.d_namlen + 1); 4431558Srgrimes return (ALTERED|STOP); 4441558Srgrimes} 4451558Srgrimes 44623675Speterstatic int 44792839Simpchgino(struct inodesc *idesc) 4481558Srgrimes{ 44992806Sobrien struct direct *dirp = idesc->id_dirp; 4501558Srgrimes 45123675Speter if (memcmp(dirp->d_name, idesc->id_name, (int)dirp->d_namlen + 1)) 4521558Srgrimes return (KEEPON); 4531558Srgrimes dirp->d_ino = idesc->id_parent; 45496483Sphk dirp->d_type = inoinfo(idesc->id_parent)->ino_type; 4551558Srgrimes return (ALTERED|STOP); 4561558Srgrimes} 4571558Srgrimes 4587585Sbdeint 45992839Simplinkup(ino_t orphan, ino_t parentdir, char *name) 4601558Srgrimes{ 46198542Smckusick union dinode *dp; 4621558Srgrimes int lostdir; 4631558Srgrimes ino_t oldlfdir; 4641558Srgrimes struct inodesc idesc; 4651558Srgrimes char tempname[BUFSIZ]; 4661558Srgrimes 46723675Speter memset(&idesc, 0, sizeof(struct inodesc)); 4681558Srgrimes dp = ginode(orphan); 46998542Smckusick lostdir = (DIP(dp, di_mode) & IFMT) == IFDIR; 4701558Srgrimes pwarn("UNREF %s ", lostdir ? "DIR" : "FILE"); 4711558Srgrimes pinode(orphan); 47298542Smckusick if (preen && DIP(dp, di_size) == 0) 4731558Srgrimes return (0); 47474556Smckusick if (cursnapshot != 0) { 47574556Smckusick pfatal("FILE LINKUP IN SNAPSHOT"); 47674556Smckusick return (0); 47774556Smckusick } 4781558Srgrimes if (preen) 4791558Srgrimes printf(" (RECONNECTED)\n"); 4801558Srgrimes else 4811558Srgrimes if (reply("RECONNECT") == 0) 4821558Srgrimes return (0); 4831558Srgrimes if (lfdir == 0) { 4841558Srgrimes dp = ginode(ROOTINO); 485100935Sphk idesc.id_name = strdup(lfname); 4861558Srgrimes idesc.id_type = DATA; 4871558Srgrimes idesc.id_func = findino; 4881558Srgrimes idesc.id_number = ROOTINO; 4891558Srgrimes if ((ckinode(dp, &idesc) & FOUND) != 0) { 4901558Srgrimes lfdir = idesc.id_parent; 4911558Srgrimes } else { 4921558Srgrimes pwarn("NO lost+found DIRECTORY"); 4931558Srgrimes if (preen || reply("CREATE")) { 4941558Srgrimes lfdir = allocdir(ROOTINO, (ino_t)0, lfmode); 4951558Srgrimes if (lfdir != 0) { 4961558Srgrimes if (makeentry(ROOTINO, lfdir, lfname) != 0) { 49741474Sjulian numdirs++; 4981558Srgrimes if (preen) 4991558Srgrimes printf(" (CREATED)\n"); 5001558Srgrimes } else { 5011558Srgrimes freedir(lfdir, ROOTINO); 5021558Srgrimes lfdir = 0; 5031558Srgrimes if (preen) 5041558Srgrimes printf("\n"); 5051558Srgrimes } 5061558Srgrimes } 5071558Srgrimes } 5081558Srgrimes } 5091558Srgrimes if (lfdir == 0) { 5101558Srgrimes pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY"); 5111558Srgrimes printf("\n\n"); 5121558Srgrimes return (0); 5131558Srgrimes } 5141558Srgrimes } 5151558Srgrimes dp = ginode(lfdir); 51698542Smckusick if ((DIP(dp, di_mode) & IFMT) != IFDIR) { 5171558Srgrimes pfatal("lost+found IS NOT A DIRECTORY"); 5181558Srgrimes if (reply("REALLOCATE") == 0) 5191558Srgrimes return (0); 5201558Srgrimes oldlfdir = lfdir; 5211558Srgrimes if ((lfdir = allocdir(ROOTINO, (ino_t)0, lfmode)) == 0) { 5221558Srgrimes pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n"); 5231558Srgrimes return (0); 5241558Srgrimes } 5251558Srgrimes if ((changeino(ROOTINO, lfname, lfdir) & ALTERED) == 0) { 5261558Srgrimes pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n"); 5271558Srgrimes return (0); 5281558Srgrimes } 529344887Skib inodirty(dp); 5301558Srgrimes idesc.id_type = ADDR; 5311558Srgrimes idesc.id_func = pass4check; 5321558Srgrimes idesc.id_number = oldlfdir; 53341474Sjulian adjust(&idesc, inoinfo(oldlfdir)->ino_linkcnt + 1); 53441474Sjulian inoinfo(oldlfdir)->ino_linkcnt = 0; 5351558Srgrimes dp = ginode(lfdir); 5361558Srgrimes } 53741474Sjulian if (inoinfo(lfdir)->ino_state != DFOUND) { 5381558Srgrimes pfatal("SORRY. NO lost+found DIRECTORY\n\n"); 5391558Srgrimes return (0); 5401558Srgrimes } 5411558Srgrimes (void)lftempname(tempname, orphan); 54241474Sjulian if (makeentry(lfdir, orphan, (name ? name : tempname)) == 0) { 5431558Srgrimes pfatal("SORRY. NO SPACE IN lost+found DIRECTORY"); 5441558Srgrimes printf("\n\n"); 5451558Srgrimes return (0); 5461558Srgrimes } 54741474Sjulian inoinfo(orphan)->ino_linkcnt--; 5481558Srgrimes if (lostdir) { 5491558Srgrimes if ((changeino(orphan, "..", lfdir) & ALTERED) == 0 && 5501558Srgrimes parentdir != (ino_t)-1) 5511558Srgrimes (void)makeentry(orphan, lfdir, ".."); 5521558Srgrimes dp = ginode(lfdir); 553134589Sscottl DIP_SET(dp, di_nlink, DIP(dp, di_nlink) + 1); 554344887Skib inodirty(dp); 55541474Sjulian inoinfo(lfdir)->ino_linkcnt++; 55686514Siedowse pwarn("DIR I=%lu CONNECTED. ", (u_long)orphan); 55715699Snate if (parentdir != (ino_t)-1) { 55837236Sbde printf("PARENT WAS I=%lu\n", (u_long)parentdir); 55941477Sjulian /* 56041477Sjulian * The parent directory, because of the ordering 56141477Sjulian * guarantees, has had the link count incremented 56241477Sjulian * for the child, but no entry was made. This 56341477Sjulian * fixes the parent link count so that fsck does 56441477Sjulian * not need to be rerun. 56541477Sjulian */ 56641474Sjulian inoinfo(parentdir)->ino_linkcnt++; 56715699Snate } 5681558Srgrimes if (preen == 0) 5691558Srgrimes printf("\n"); 5701558Srgrimes } 5711558Srgrimes return (1); 5721558Srgrimes} 5731558Srgrimes 5741558Srgrimes/* 5751558Srgrimes * fix an entry in a directory. 5761558Srgrimes */ 5777585Sbdeint 578100935Sphkchangeino(ino_t dir, const char *name, ino_t newnum) 5791558Srgrimes{ 5801558Srgrimes struct inodesc idesc; 5811558Srgrimes 58223675Speter memset(&idesc, 0, sizeof(struct inodesc)); 5831558Srgrimes idesc.id_type = DATA; 5841558Srgrimes idesc.id_func = chgino; 5851558Srgrimes idesc.id_number = dir; 5861558Srgrimes idesc.id_fix = DONTKNOW; 587100935Sphk idesc.id_name = strdup(name); 5881558Srgrimes idesc.id_parent = newnum; /* new value for name */ 5891558Srgrimes return (ckinode(ginode(dir), &idesc)); 5901558Srgrimes} 5911558Srgrimes 5921558Srgrimes/* 5931558Srgrimes * make an entry in a directory 5941558Srgrimes */ 5957585Sbdeint 596100935Sphkmakeentry(ino_t parent, ino_t ino, const char *name) 5971558Srgrimes{ 59898542Smckusick union dinode *dp; 5991558Srgrimes struct inodesc idesc; 6001558Srgrimes char pathbuf[MAXPATHLEN + 1]; 6018871Srgrimes 6021558Srgrimes if (parent < ROOTINO || parent >= maxino || 6031558Srgrimes ino < ROOTINO || ino >= maxino) 6041558Srgrimes return (0); 60523675Speter memset(&idesc, 0, sizeof(struct inodesc)); 6061558Srgrimes idesc.id_type = DATA; 6071558Srgrimes idesc.id_func = mkentry; 6081558Srgrimes idesc.id_number = parent; 6091558Srgrimes idesc.id_parent = ino; /* this is the inode to enter */ 6101558Srgrimes idesc.id_fix = DONTKNOW; 611100935Sphk idesc.id_name = strdup(name); 6121558Srgrimes dp = ginode(parent); 61398542Smckusick if (DIP(dp, di_size) % DIRBLKSIZ) { 614134589Sscottl DIP_SET(dp, di_size, roundup(DIP(dp, di_size), DIRBLKSIZ)); 615344887Skib inodirty(dp); 6161558Srgrimes } 6171558Srgrimes if ((ckinode(dp, &idesc) & ALTERED) != 0) 6181558Srgrimes return (1); 6191558Srgrimes getpathname(pathbuf, parent, parent); 6201558Srgrimes dp = ginode(parent); 6211558Srgrimes if (expanddir(dp, pathbuf) == 0) 6221558Srgrimes return (0); 6231558Srgrimes return (ckinode(dp, &idesc) & ALTERED); 6241558Srgrimes} 6251558Srgrimes 6261558Srgrimes/* 6271558Srgrimes * Attempt to expand the size of a directory 6281558Srgrimes */ 62923675Speterstatic int 63098542Smckusickexpanddir(union dinode *dp, char *name) 6311558Srgrimes{ 63298542Smckusick ufs2_daddr_t lastbn, newblk; 63392806Sobrien struct bufarea *bp; 6341558Srgrimes char *cp, firstblk[DIRBLKSIZ]; 6351558Srgrimes 63698542Smckusick lastbn = lblkno(&sblock, DIP(dp, di_size)); 63798542Smckusick if (lastbn >= NDADDR - 1 || DIP(dp, di_db[lastbn]) == 0 || 63898542Smckusick DIP(dp, di_size) == 0) 6391558Srgrimes return (0); 6401558Srgrimes if ((newblk = allocblk(sblock.fs_frag)) == 0) 6411558Srgrimes return (0); 642134589Sscottl DIP_SET(dp, di_db[lastbn + 1], DIP(dp, di_db[lastbn])); 643134589Sscottl DIP_SET(dp, di_db[lastbn], newblk); 644134589Sscottl DIP_SET(dp, di_size, DIP(dp, di_size) + sblock.fs_bsize); 645134589Sscottl DIP_SET(dp, di_blocks, DIP(dp, di_blocks) + btodb(sblock.fs_bsize)); 64698542Smckusick bp = getdirblk(DIP(dp, di_db[lastbn + 1]), 64798542Smckusick sblksize(&sblock, DIP(dp, di_size), lastbn + 1)); 6481558Srgrimes if (bp->b_errs) 6491558Srgrimes goto bad; 65023675Speter memmove(firstblk, bp->b_un.b_buf, DIRBLKSIZ); 6511558Srgrimes bp = getdirblk(newblk, sblock.fs_bsize); 6521558Srgrimes if (bp->b_errs) 6531558Srgrimes goto bad; 65423675Speter memmove(bp->b_un.b_buf, firstblk, DIRBLKSIZ); 6551558Srgrimes for (cp = &bp->b_un.b_buf[DIRBLKSIZ]; 6561558Srgrimes cp < &bp->b_un.b_buf[sblock.fs_bsize]; 6571558Srgrimes cp += DIRBLKSIZ) 65823675Speter memmove(cp, &emptydir, sizeof emptydir); 6591558Srgrimes dirty(bp); 66098542Smckusick bp = getdirblk(DIP(dp, di_db[lastbn + 1]), 66198542Smckusick sblksize(&sblock, DIP(dp, di_size), lastbn + 1)); 6621558Srgrimes if (bp->b_errs) 6631558Srgrimes goto bad; 66423675Speter memmove(bp->b_un.b_buf, &emptydir, sizeof emptydir); 6651558Srgrimes pwarn("NO SPACE LEFT IN %s", name); 6661558Srgrimes if (preen) 6671558Srgrimes printf(" (EXPANDED)\n"); 6681558Srgrimes else if (reply("EXPAND") == 0) 6691558Srgrimes goto bad; 6701558Srgrimes dirty(bp); 671344887Skib inodirty(dp); 6721558Srgrimes return (1); 6731558Srgrimesbad: 674134589Sscottl DIP_SET(dp, di_db[lastbn], DIP(dp, di_db[lastbn + 1])); 675134589Sscottl DIP_SET(dp, di_db[lastbn + 1], 0); 676134589Sscottl DIP_SET(dp, di_size, DIP(dp, di_size) - sblock.fs_bsize); 677134589Sscottl DIP_SET(dp, di_blocks, DIP(dp, di_blocks) - btodb(sblock.fs_bsize)); 6781558Srgrimes freeblk(newblk, sblock.fs_frag); 6791558Srgrimes return (0); 6801558Srgrimes} 6811558Srgrimes 6821558Srgrimes/* 6831558Srgrimes * allocate a new directory 6841558Srgrimes */ 6857586Sbdeino_t 68692839Simpallocdir(ino_t parent, ino_t request, int mode) 6871558Srgrimes{ 6881558Srgrimes ino_t ino; 6891558Srgrimes char *cp; 69098542Smckusick union dinode *dp; 69163810Smckusick struct bufarea *bp; 69263810Smckusick struct inoinfo *inp; 6931558Srgrimes struct dirtemplate *dirp; 6941558Srgrimes 6951558Srgrimes ino = allocino(request, IFDIR|mode); 69696483Sphk dirp = &dirhead; 6971558Srgrimes dirp->dot_ino = ino; 6981558Srgrimes dirp->dotdot_ino = parent; 6991558Srgrimes dp = ginode(ino); 70098542Smckusick bp = getdirblk(DIP(dp, di_db[0]), sblock.fs_fsize); 7011558Srgrimes if (bp->b_errs) { 7021558Srgrimes freeino(ino); 7031558Srgrimes return (0); 7041558Srgrimes } 70523675Speter memmove(bp->b_un.b_buf, dirp, sizeof(struct dirtemplate)); 7061558Srgrimes for (cp = &bp->b_un.b_buf[DIRBLKSIZ]; 7071558Srgrimes cp < &bp->b_un.b_buf[sblock.fs_fsize]; 7081558Srgrimes cp += DIRBLKSIZ) 70923675Speter memmove(cp, &emptydir, sizeof emptydir); 7101558Srgrimes dirty(bp); 711134589Sscottl DIP_SET(dp, di_nlink, 2); 712344887Skib inodirty(dp); 7131558Srgrimes if (ino == ROOTINO) { 71498542Smckusick inoinfo(ino)->ino_linkcnt = DIP(dp, di_nlink); 7151558Srgrimes cacheino(dp, ino); 7161558Srgrimes return(ino); 7171558Srgrimes } 718136281Struckman if (!INO_IS_DVALID(parent)) { 7191558Srgrimes freeino(ino); 7201558Srgrimes return (0); 7211558Srgrimes } 7221558Srgrimes cacheino(dp, ino); 72363810Smckusick inp = getinoinfo(ino); 72463810Smckusick inp->i_parent = parent; 72563810Smckusick inp->i_dotdot = parent; 72641474Sjulian inoinfo(ino)->ino_state = inoinfo(parent)->ino_state; 72741474Sjulian if (inoinfo(ino)->ino_state == DSTATE) { 72898542Smckusick inoinfo(ino)->ino_linkcnt = DIP(dp, di_nlink); 72941474Sjulian inoinfo(parent)->ino_linkcnt++; 7301558Srgrimes } 7311558Srgrimes dp = ginode(parent); 732134589Sscottl DIP_SET(dp, di_nlink, DIP(dp, di_nlink) + 1); 733344887Skib inodirty(dp); 7341558Srgrimes return (ino); 7351558Srgrimes} 7361558Srgrimes 7371558Srgrimes/* 7381558Srgrimes * free a directory inode 7391558Srgrimes */ 7407585Sbdestatic void 74192839Simpfreedir(ino_t ino, ino_t parent) 7421558Srgrimes{ 74398542Smckusick union dinode *dp; 7441558Srgrimes 7451558Srgrimes if (ino != parent) { 7461558Srgrimes dp = ginode(parent); 747134589Sscottl DIP_SET(dp, di_nlink, DIP(dp, di_nlink) - 1); 748344887Skib inodirty(dp); 7491558Srgrimes } 7501558Srgrimes freeino(ino); 7511558Srgrimes} 7521558Srgrimes 7531558Srgrimes/* 7541558Srgrimes * generate a temporary name for the lost+found directory. 7551558Srgrimes */ 75623675Speterstatic int 75792839Simplftempname(char *bufp, ino_t ino) 7581558Srgrimes{ 75992806Sobrien ino_t in; 76092806Sobrien char *cp; 7611558Srgrimes int namlen; 7621558Srgrimes 7631558Srgrimes cp = bufp + 2; 7641558Srgrimes for (in = maxino; in > 0; in /= 10) 7651558Srgrimes cp++; 7661558Srgrimes *--cp = 0; 7671558Srgrimes namlen = cp - bufp; 7681558Srgrimes in = ino; 7691558Srgrimes while (cp > bufp) { 7701558Srgrimes *--cp = (in % 10) + '0'; 7711558Srgrimes in /= 10; 7721558Srgrimes } 7731558Srgrimes *cp = '#'; 7741558Srgrimes return (namlen); 7751558Srgrimes} 7761558Srgrimes 7771558Srgrimes/* 7781558Srgrimes * Get a directory block. 7791558Srgrimes * Insure that it is held until another is requested. 7801558Srgrimes */ 78123675Speterstatic struct bufarea * 78298542Smckusickgetdirblk(ufs2_daddr_t blkno, long size) 7831558Srgrimes{ 7841558Srgrimes 785297886Spfg if (pdirbp != NULL) 7861558Srgrimes pdirbp->b_flags &= ~B_INUSE; 787247212Smckusick pdirbp = getdatablk(blkno, size, BT_DIRDATA); 7881558Srgrimes return (pdirbp); 7891558Srgrimes} 790