17c478bd9Sstevel@tonic-gate /* 2355d6bb5Sswilcox * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 37c478bd9Sstevel@tonic-gate * Use is subject to license terms. 47c478bd9Sstevel@tonic-gate */ 57c478bd9Sstevel@tonic-gate 67c478bd9Sstevel@tonic-gate /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 77c478bd9Sstevel@tonic-gate /* All Rights Reserved */ 87c478bd9Sstevel@tonic-gate 97c478bd9Sstevel@tonic-gate /* 107c478bd9Sstevel@tonic-gate * Copyright (c) 1980, 1986, 1990 The Regents of the University of California. 117c478bd9Sstevel@tonic-gate * All rights reserved. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * Redistribution and use in source and binary forms are permitted 147c478bd9Sstevel@tonic-gate * provided that: (1) source distributions retain this entire copyright 157c478bd9Sstevel@tonic-gate * notice and comment, and (2) distributions including binaries display 167c478bd9Sstevel@tonic-gate * the following acknowledgement: ``This product includes software 177c478bd9Sstevel@tonic-gate * developed by the University of California, Berkeley and its contributors'' 187c478bd9Sstevel@tonic-gate * in the documentation or other materials provided with the distribution 197c478bd9Sstevel@tonic-gate * and in all advertising materials mentioning features or use of this 207c478bd9Sstevel@tonic-gate * software. Neither the name of the University nor the names of its 217c478bd9Sstevel@tonic-gate * contributors may be used to endorse or promote products derived 227c478bd9Sstevel@tonic-gate * from this software without specific prior written permission. 237c478bd9Sstevel@tonic-gate * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 247c478bd9Sstevel@tonic-gate * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 257c478bd9Sstevel@tonic-gate * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 267c478bd9Sstevel@tonic-gate */ 277c478bd9Sstevel@tonic-gate 287c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 297c478bd9Sstevel@tonic-gate 307c478bd9Sstevel@tonic-gate #include <sys/param.h> 317c478bd9Sstevel@tonic-gate #include <sys/types.h> 327c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h> 337c478bd9Sstevel@tonic-gate #include <sys/mntent.h> 34355d6bb5Sswilcox #include <string.h> 35355d6bb5Sswilcox #include <stdarg.h> 367c478bd9Sstevel@tonic-gate #include <sys/fs/ufs_fs.h> 377c478bd9Sstevel@tonic-gate #include <sys/vnode.h> 387c478bd9Sstevel@tonic-gate #include <sys/fs/ufs_inode.h> 397c478bd9Sstevel@tonic-gate #define _KERNEL 407c478bd9Sstevel@tonic-gate #include <sys/fs/ufs_fsdir.h> 417c478bd9Sstevel@tonic-gate #undef _KERNEL 427c478bd9Sstevel@tonic-gate #include "fsck.h" 437c478bd9Sstevel@tonic-gate 44355d6bb5Sswilcox struct rc_queue { 45355d6bb5Sswilcox struct rc_queue *rc_next; 46355d6bb5Sswilcox fsck_ino_t rc_orphan; 47355d6bb5Sswilcox fsck_ino_t rc_parent; 48355d6bb5Sswilcox caddr_t rc_name; 49355d6bb5Sswilcox }; 507c478bd9Sstevel@tonic-gate 51355d6bb5Sswilcox caddr_t lfname = "lost+found"; /* name to use for l+f dir */ 52355d6bb5Sswilcox static int lfmode = 01700; /* mode to use when creating l+f dir */ 53355d6bb5Sswilcox static struct dirtemplate emptydir = { 0, DIRBLKSIZ }; 54355d6bb5Sswilcox static struct dirtemplate dirhead = { 55355d6bb5Sswilcox 0, 12, 1, ".", 0, DIRBLKSIZ - 12, 2, ".." 56355d6bb5Sswilcox }; 57355d6bb5Sswilcox 58355d6bb5Sswilcox static void lftempname(char *, fsck_ino_t); 59355d6bb5Sswilcox static int do_reconnect(fsck_ino_t, fsck_ino_t, caddr_t); 60355d6bb5Sswilcox static caddr_t mkuniqname(caddr_t, caddr_t, fsck_ino_t, fsck_ino_t); 61355d6bb5Sswilcox static int chgino(struct inodesc *); 62355d6bb5Sswilcox static int dircheck(struct inodesc *, struct direct *); 63355d6bb5Sswilcox static int expanddir(fsck_ino_t, char *); 64355d6bb5Sswilcox static void freedir(fsck_ino_t, fsck_ino_t); 65355d6bb5Sswilcox static struct direct *fsck_readdir(struct inodesc *); 66355d6bb5Sswilcox static struct bufarea *getdirblk(daddr32_t, size_t); 67355d6bb5Sswilcox static int mkentry(struct inodesc *); 68355d6bb5Sswilcox static fsck_ino_t newdir(fsck_ino_t, fsck_ino_t, int, caddr_t); 69355d6bb5Sswilcox static fsck_ino_t reallocdir(fsck_ino_t, fsck_ino_t, int, caddr_t); 707c478bd9Sstevel@tonic-gate 717c478bd9Sstevel@tonic-gate /* 727c478bd9Sstevel@tonic-gate * Propagate connected state through the tree. 737c478bd9Sstevel@tonic-gate */ 74355d6bb5Sswilcox void 75355d6bb5Sswilcox propagate(void) 767c478bd9Sstevel@tonic-gate { 777c478bd9Sstevel@tonic-gate struct inoinfo **inpp, *inp; 787c478bd9Sstevel@tonic-gate struct inoinfo **inpend; 79355d6bb5Sswilcox int change, inorphan; 807c478bd9Sstevel@tonic-gate 817c478bd9Sstevel@tonic-gate inpend = &inpsort[inplast]; 827c478bd9Sstevel@tonic-gate do { 837c478bd9Sstevel@tonic-gate change = 0; 847c478bd9Sstevel@tonic-gate for (inpp = inpsort; inpp < inpend; inpp++) { 857c478bd9Sstevel@tonic-gate inp = *inpp; 867c478bd9Sstevel@tonic-gate if (inp->i_parent == 0) 877c478bd9Sstevel@tonic-gate continue; 887c478bd9Sstevel@tonic-gate if (statemap[inp->i_parent] == DFOUND && 89355d6bb5Sswilcox INO_IS_DUNFOUND(inp->i_number)) { 90355d6bb5Sswilcox inorphan = statemap[inp->i_number] & INORPHAN; 91355d6bb5Sswilcox statemap[inp->i_number] = DFOUND | inorphan; 927c478bd9Sstevel@tonic-gate change++; 937c478bd9Sstevel@tonic-gate } 947c478bd9Sstevel@tonic-gate } 957c478bd9Sstevel@tonic-gate } while (change > 0); 967c478bd9Sstevel@tonic-gate } 977c478bd9Sstevel@tonic-gate 987c478bd9Sstevel@tonic-gate /* 997c478bd9Sstevel@tonic-gate * Scan each entry in a directory block. 1007c478bd9Sstevel@tonic-gate */ 101355d6bb5Sswilcox int 102355d6bb5Sswilcox dirscan(struct inodesc *idesc) 1037c478bd9Sstevel@tonic-gate { 1047c478bd9Sstevel@tonic-gate struct direct *dp; 1057c478bd9Sstevel@tonic-gate struct bufarea *bp; 106355d6bb5Sswilcox uint_t dsize, n; 107355d6bb5Sswilcox size_t blksiz; 108355d6bb5Sswilcox union { /* keep lint happy about alignment */ 1097c478bd9Sstevel@tonic-gate char dbuf[DIRBLKSIZ]; 110355d6bb5Sswilcox struct direct dir; 111355d6bb5Sswilcox } u; 1127c478bd9Sstevel@tonic-gate 1137c478bd9Sstevel@tonic-gate if (idesc->id_type != DATA) 1147c478bd9Sstevel@tonic-gate errexit("wrong type to dirscan %d\n", idesc->id_type); 1157c478bd9Sstevel@tonic-gate if (idesc->id_entryno == 0 && 1167c478bd9Sstevel@tonic-gate (idesc->id_filesize & (DIRBLKSIZ - 1)) != 0) 1177c478bd9Sstevel@tonic-gate idesc->id_filesize = roundup(idesc->id_filesize, DIRBLKSIZ); 1187c478bd9Sstevel@tonic-gate blksiz = idesc->id_numfrags * sblock.fs_fsize; 1197c478bd9Sstevel@tonic-gate if (chkrange(idesc->id_blkno, idesc->id_numfrags)) { 1207c478bd9Sstevel@tonic-gate idesc->id_filesize -= (offset_t)blksiz; 1217c478bd9Sstevel@tonic-gate return (SKIP); 1227c478bd9Sstevel@tonic-gate } 1237c478bd9Sstevel@tonic-gate idesc->id_loc = 0; 1247c478bd9Sstevel@tonic-gate for (dp = fsck_readdir(idesc); dp != NULL; dp = fsck_readdir(idesc)) { 1257c478bd9Sstevel@tonic-gate /* 1267c478bd9Sstevel@tonic-gate * If we were just passed a corrupt directory entry with 127355d6bb5Sswilcox * d_reclen > DIRBLKSIZ, we don't want to memmove() all over 128355d6bb5Sswilcox * our stack. This directory gets cleaned up later. 1297c478bd9Sstevel@tonic-gate */ 130355d6bb5Sswilcox dsize = MIN(dp->d_reclen, sizeof (u.dbuf)); 131355d6bb5Sswilcox (void) memmove((void *)u.dbuf, (void *)dp, (size_t)dsize); 132355d6bb5Sswilcox idesc->id_dirp = &u.dir; 1337c478bd9Sstevel@tonic-gate if ((n = (*idesc->id_func)(idesc)) & ALTERED) { 134355d6bb5Sswilcox /* 135355d6bb5Sswilcox * We can ignore errors from getdirblk() here, 136355d6bb5Sswilcox * as the block is still in memory thanks to 137355d6bb5Sswilcox * buffering and fsck_readdir(). If there was 138355d6bb5Sswilcox * an error reading it before, then all decisions 139355d6bb5Sswilcox * leading to getting us here were based on the 140355d6bb5Sswilcox * resulting zeros. As such, we have nothing 141355d6bb5Sswilcox * to worry about at this point. 142355d6bb5Sswilcox */ 1437c478bd9Sstevel@tonic-gate bp = getdirblk(idesc->id_blkno, blksiz); 144355d6bb5Sswilcox (void) memmove((void *)(bp->b_un.b_buf + 145355d6bb5Sswilcox idesc->id_loc - dsize), 146355d6bb5Sswilcox (void *)u.dbuf, (size_t)dsize); 1477c478bd9Sstevel@tonic-gate dirty(bp); 1487c478bd9Sstevel@tonic-gate sbdirty(); 1497c478bd9Sstevel@tonic-gate } 1507c478bd9Sstevel@tonic-gate if (n & STOP) 1517c478bd9Sstevel@tonic-gate return (n); 1527c478bd9Sstevel@tonic-gate } 1537c478bd9Sstevel@tonic-gate return (idesc->id_filesize > 0 ? KEEPON : STOP); 1547c478bd9Sstevel@tonic-gate } 1557c478bd9Sstevel@tonic-gate 1567c478bd9Sstevel@tonic-gate /* 1577c478bd9Sstevel@tonic-gate * Get current entry in a directory (and peek at the next entry). 1587c478bd9Sstevel@tonic-gate */ 159355d6bb5Sswilcox static struct direct * 160355d6bb5Sswilcox fsck_readdir(struct inodesc *idesc) 1617c478bd9Sstevel@tonic-gate { 1627c478bd9Sstevel@tonic-gate struct direct *dp, *ndp = 0; 1637c478bd9Sstevel@tonic-gate struct bufarea *bp; 164355d6bb5Sswilcox ushort_t size; /* of directory entry */ 165355d6bb5Sswilcox size_t blksiz; 1667c478bd9Sstevel@tonic-gate int dofixret; 167355d6bb5Sswilcox int salvaged; /* when to report SALVAGED in preen mode */ 1687c478bd9Sstevel@tonic-gate int origloc = idesc->id_loc; 1697c478bd9Sstevel@tonic-gate 1707c478bd9Sstevel@tonic-gate blksiz = idesc->id_numfrags * sblock.fs_fsize; 1717c478bd9Sstevel@tonic-gate /* 172355d6bb5Sswilcox * Sanity check id_filesize and id_loc fields. The latter 173355d6bb5Sswilcox * has to be within the block we're looking at, as well as 174355d6bb5Sswilcox * aligned to a four-byte boundary. The alignment is due to 175355d6bb5Sswilcox * a struct direct containing four-byte integers. It's 176355d6bb5Sswilcox * unfortunate that the four is a magic number, but there's 177355d6bb5Sswilcox * really no good way to derive it from the ufs header files. 1787c478bd9Sstevel@tonic-gate */ 179355d6bb5Sswilcox if ((idesc->id_filesize <= 0) || (idesc->id_loc >= blksiz) || 180355d6bb5Sswilcox ((idesc->id_loc & 3) != 0)) 1817c478bd9Sstevel@tonic-gate return (NULL); 182355d6bb5Sswilcox /* 183355d6bb5Sswilcox * We don't have to worry about holes in the directory's 184355d6bb5Sswilcox * block list, because that was checked for when the 185355d6bb5Sswilcox * inode was first encountered during pass1. We never 186355d6bb5Sswilcox * scan a directory until after we've vetted its block list. 187355d6bb5Sswilcox */ 188355d6bb5Sswilcox /* 189355d6bb5Sswilcox * We can ignore errors from getdirblk() here, as dircheck() 190355d6bb5Sswilcox * will reject any entries that would have been in the bad 191355d6bb5Sswilcox * sectors (fsck_bread() fills in zeros on failures). The main 192355d6bb5Sswilcox * reject keys are that d_reclen would be zero and/or that it 193355d6bb5Sswilcox * is less than the minimal size of a directory entry. Since 194355d6bb5Sswilcox * entries can't span sectors, there's no worry about having 195355d6bb5Sswilcox * a good beginning in one sector and the rest in the next, 196355d6bb5Sswilcox * where that second sector was unreadable and therefore 197355d6bb5Sswilcox * replaced with zeros. 198355d6bb5Sswilcox */ 1997c478bd9Sstevel@tonic-gate bp = getdirblk(idesc->id_blkno, blksiz); 200355d6bb5Sswilcox /* LINTED b_buf is aligned and id_loc was verified above */ 2017c478bd9Sstevel@tonic-gate dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); 2027c478bd9Sstevel@tonic-gate 2037c478bd9Sstevel@tonic-gate /* 2047c478bd9Sstevel@tonic-gate * Check the current entry in the directory. 2057c478bd9Sstevel@tonic-gate */ 2067c478bd9Sstevel@tonic-gate if (dircheck(idesc, dp) == 0) { 2077c478bd9Sstevel@tonic-gate /* 2087c478bd9Sstevel@tonic-gate * If we are in here, then either the current directory 2097c478bd9Sstevel@tonic-gate * entry is bad or the next directory entry is bad. 2107c478bd9Sstevel@tonic-gate */ 2117c478bd9Sstevel@tonic-gate next_is_bad: 2127c478bd9Sstevel@tonic-gate /* 2137c478bd9Sstevel@tonic-gate * Find the amount of space left to the end of the 2147c478bd9Sstevel@tonic-gate * directory block for either directory entry. 2157c478bd9Sstevel@tonic-gate */ 2167c478bd9Sstevel@tonic-gate size = DIRBLKSIZ - (idesc->id_loc & (DIRBLKSIZ - 1)); 2177c478bd9Sstevel@tonic-gate 2187c478bd9Sstevel@tonic-gate /* 2197c478bd9Sstevel@tonic-gate * Advance to the end of the directory block. 2207c478bd9Sstevel@tonic-gate */ 2217c478bd9Sstevel@tonic-gate idesc->id_loc += size; 2227c478bd9Sstevel@tonic-gate idesc->id_filesize -= (offset_t)size; 2237c478bd9Sstevel@tonic-gate 2247c478bd9Sstevel@tonic-gate /* 2257c478bd9Sstevel@tonic-gate * Ask the question before we fix the in-core directory 2267c478bd9Sstevel@tonic-gate * block because dofix() may reuse the buffer. 2277c478bd9Sstevel@tonic-gate */ 228355d6bb5Sswilcox salvaged = (idesc->id_fix == DONTKNOW); 2297c478bd9Sstevel@tonic-gate dofixret = dofix(idesc, "DIRECTORY CORRUPTED"); 230355d6bb5Sswilcox 231355d6bb5Sswilcox /* 232355d6bb5Sswilcox * If there was an error reading the block, then that 233355d6bb5Sswilcox * same error can reasonably be expected to have occurred 234355d6bb5Sswilcox * when it was read previously. As such, the decision 235355d6bb5Sswilcox * to come here was based on the results of that partially- 236355d6bb5Sswilcox * zerod block, and so anything we change should be 237355d6bb5Sswilcox * based on it as well. Upshot: no need to check for 238355d6bb5Sswilcox * errors here. 239355d6bb5Sswilcox */ 2407c478bd9Sstevel@tonic-gate bp = getdirblk(idesc->id_blkno, blksiz); 241355d6bb5Sswilcox /* LINTED b_buf is aligned and id_loc/origloc was verified */ 2427c478bd9Sstevel@tonic-gate dp = (struct direct *)(bp->b_un.b_buf + origloc); 2437c478bd9Sstevel@tonic-gate 2447c478bd9Sstevel@tonic-gate /* 2457c478bd9Sstevel@tonic-gate * This is the current directory entry and since it is 2467c478bd9Sstevel@tonic-gate * corrupt we cannot trust the rest of the directory 2477c478bd9Sstevel@tonic-gate * block so change the current directory entry to 2487c478bd9Sstevel@tonic-gate * contain nothing and encompass the rest of the block. 2497c478bd9Sstevel@tonic-gate */ 2507c478bd9Sstevel@tonic-gate if (ndp == NULL) { 2517c478bd9Sstevel@tonic-gate dp->d_reclen = size; 2527c478bd9Sstevel@tonic-gate dp->d_ino = 0; 2537c478bd9Sstevel@tonic-gate dp->d_namlen = 0; 2547c478bd9Sstevel@tonic-gate dp->d_name[0] = '\0'; 2557c478bd9Sstevel@tonic-gate } 2567c478bd9Sstevel@tonic-gate /* 2577c478bd9Sstevel@tonic-gate * This is the next directory entry, i.e., we got here 258355d6bb5Sswilcox * via a "goto next_is_bad". That directory entry is 259355d6bb5Sswilcox * corrupt. However, the current directory entry is okay 260355d6bb5Sswilcox * so if we are in fix mode, just extend its record size 261355d6bb5Sswilcox * to encompass the rest of the block. 2627c478bd9Sstevel@tonic-gate */ 2637c478bd9Sstevel@tonic-gate else if (dofixret) { 2647c478bd9Sstevel@tonic-gate dp->d_reclen += size; 2657c478bd9Sstevel@tonic-gate } 2667c478bd9Sstevel@tonic-gate /* 2677c478bd9Sstevel@tonic-gate * If the user said to fix the directory corruption, then 2687c478bd9Sstevel@tonic-gate * mark the block as dirty. Otherwise, our "repairs" only 2697c478bd9Sstevel@tonic-gate * apply to the in-core copy so we don't hand back trash 2707c478bd9Sstevel@tonic-gate * to the caller. 2717c478bd9Sstevel@tonic-gate * 2727c478bd9Sstevel@tonic-gate * Note: It is possible that saying "no" to a change in 2737c478bd9Sstevel@tonic-gate * one part of the I/O buffer and "yes" to a later change 2747c478bd9Sstevel@tonic-gate * in the same I/O buffer may still flush the change to 275355d6bb5Sswilcox * which we said "no". This is the pathological case and 2767c478bd9Sstevel@tonic-gate * no fix is planned at this time. 2777c478bd9Sstevel@tonic-gate */ 278355d6bb5Sswilcox if (dofixret) { 2797c478bd9Sstevel@tonic-gate dirty(bp); 280355d6bb5Sswilcox if (preen && salvaged) 281355d6bb5Sswilcox (void) printf(" (SALVAGED)\n"); 282355d6bb5Sswilcox if (idesc->id_number == lfdir) 283355d6bb5Sswilcox lfdir = 0; 284355d6bb5Sswilcox } 285355d6bb5Sswilcox 286355d6bb5Sswilcox /* 287355d6bb5Sswilcox * dp points into bp, which will get re-used at some 288355d6bb5Sswilcox * arbitrary time in the future. We rely on the fact 289355d6bb5Sswilcox * that we're singled-threaded, and that we'll be done 290355d6bb5Sswilcox * with this directory entry by the time the next one 291355d6bb5Sswilcox * is needed. 292355d6bb5Sswilcox */ 2937c478bd9Sstevel@tonic-gate return (dp); 2947c478bd9Sstevel@tonic-gate } 2957c478bd9Sstevel@tonic-gate /* 2967c478bd9Sstevel@tonic-gate * The current directory entry checked out so advance past it. 2977c478bd9Sstevel@tonic-gate */ 2987c478bd9Sstevel@tonic-gate idesc->id_loc += dp->d_reclen; 2997c478bd9Sstevel@tonic-gate idesc->id_filesize -= (offset_t)dp->d_reclen; 3007c478bd9Sstevel@tonic-gate /* 3017c478bd9Sstevel@tonic-gate * If we are not at the directory block boundary, then peek 3027c478bd9Sstevel@tonic-gate * at the next directory entry and if it is bad we can add 3037c478bd9Sstevel@tonic-gate * its space to the current directory entry (compression). 3047c478bd9Sstevel@tonic-gate * Again, we sanity check the id_loc and id_filesize fields 3057c478bd9Sstevel@tonic-gate * since we modified them above. 3067c478bd9Sstevel@tonic-gate */ 307355d6bb5Sswilcox if ((idesc->id_loc & (DIRBLKSIZ - 1)) && /* not at start */ 308355d6bb5Sswilcox (idesc->id_loc < blksiz) && /* within block */ 309355d6bb5Sswilcox ((idesc->id_loc & 3) == 0) && /* properly aligned */ 310355d6bb5Sswilcox (idesc->id_filesize > 0)) { /* data follows */ 311355d6bb5Sswilcox /* LINTED b_buf is aligned and id_loc verified to be ok */ 3127c478bd9Sstevel@tonic-gate ndp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); 3137c478bd9Sstevel@tonic-gate if (dircheck(idesc, ndp) == 0) 3147c478bd9Sstevel@tonic-gate goto next_is_bad; 3157c478bd9Sstevel@tonic-gate } 3167c478bd9Sstevel@tonic-gate 317355d6bb5Sswilcox /* 318355d6bb5Sswilcox * See comment above about dp pointing into bp. 319355d6bb5Sswilcox */ 3207c478bd9Sstevel@tonic-gate return (dp); 3217c478bd9Sstevel@tonic-gate } 3227c478bd9Sstevel@tonic-gate 3237c478bd9Sstevel@tonic-gate /* 3247c478bd9Sstevel@tonic-gate * Verify that a directory entry is valid. 3257c478bd9Sstevel@tonic-gate * This is a superset of the checks made in the kernel. 3267c478bd9Sstevel@tonic-gate */ 327355d6bb5Sswilcox static int 328355d6bb5Sswilcox dircheck(struct inodesc *idesc, struct direct *dp) 3297c478bd9Sstevel@tonic-gate { 330355d6bb5Sswilcox size_t size; 3317c478bd9Sstevel@tonic-gate char *cp; 3327c478bd9Sstevel@tonic-gate int spaceleft; 3337c478bd9Sstevel@tonic-gate 334355d6bb5Sswilcox /* 335355d6bb5Sswilcox * Recall that id_filesize is the number of bytes left to 336355d6bb5Sswilcox * process in the directory. We check id_filesize >= size 337355d6bb5Sswilcox * instead of id_filesize >= d_reclen because all that the 338355d6bb5Sswilcox * directory is actually required to contain is the entry 339355d6bb5Sswilcox * itself (and it's how the kernel does the allocation). 340355d6bb5Sswilcox * 341355d6bb5Sswilcox * We indirectly check for d_reclen going past the end of 342355d6bb5Sswilcox * the allocated space by comparing it against spaceleft. 343355d6bb5Sswilcox */ 3447c478bd9Sstevel@tonic-gate size = DIRSIZ(dp); 3457c478bd9Sstevel@tonic-gate spaceleft = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ); 3467c478bd9Sstevel@tonic-gate if (dp->d_ino < maxino && 3477c478bd9Sstevel@tonic-gate dp->d_reclen != 0 && 3487c478bd9Sstevel@tonic-gate (int)dp->d_reclen <= spaceleft && 3497c478bd9Sstevel@tonic-gate (dp->d_reclen & 0x3) == 0 && 3507c478bd9Sstevel@tonic-gate (int)dp->d_reclen >= size && 3517c478bd9Sstevel@tonic-gate idesc->id_filesize >= (offset_t)size && 3527c478bd9Sstevel@tonic-gate dp->d_namlen <= MAXNAMLEN) { 3537c478bd9Sstevel@tonic-gate if (dp->d_ino == 0) 3547c478bd9Sstevel@tonic-gate return (1); 355355d6bb5Sswilcox for (cp = dp->d_name, size = 0; size < (size_t)dp->d_namlen; 3567c478bd9Sstevel@tonic-gate size++, cp++) 357355d6bb5Sswilcox if ((*cp == '\0') || (*cp == '/')) 358355d6bb5Sswilcox goto bad; 359355d6bb5Sswilcox if (*cp == '\0') 3607c478bd9Sstevel@tonic-gate return (1); 3617c478bd9Sstevel@tonic-gate } 362355d6bb5Sswilcox bad: 363355d6bb5Sswilcox if (debug) { 364355d6bb5Sswilcox (void) printf("Bad dir in inode %d at lbn %d, loc %d:\n", 365355d6bb5Sswilcox idesc->id_number, idesc->id_lbn, idesc->id_loc); 366355d6bb5Sswilcox (void) printf(" ino %d reclen %d namlen %d name `%s'\n", 367355d6bb5Sswilcox dp->d_ino, dp->d_reclen, dp->d_namlen, dp->d_name); 368355d6bb5Sswilcox } 3697c478bd9Sstevel@tonic-gate return (0); 3707c478bd9Sstevel@tonic-gate } 3717c478bd9Sstevel@tonic-gate 372355d6bb5Sswilcox void 373355d6bb5Sswilcox adjust(struct inodesc *idesc, int lcnt) 3747c478bd9Sstevel@tonic-gate { 3757c478bd9Sstevel@tonic-gate struct dinode *dp; 376355d6bb5Sswilcox caddr_t flow; 377355d6bb5Sswilcox int saveiscorrupt; 378355d6bb5Sswilcox struct inodesc lcidesc; 3797c478bd9Sstevel@tonic-gate 3807c478bd9Sstevel@tonic-gate dp = ginode(idesc->id_number); 3817c478bd9Sstevel@tonic-gate if (dp->di_nlink == lcnt) { 382355d6bb5Sswilcox /* 383355d6bb5Sswilcox * If we have not hit any unresolved problems, are running 384355d6bb5Sswilcox * in preen mode, and are on a file system using logging, 385355d6bb5Sswilcox * then just toss any partially allocated files, as they are 386355d6bb5Sswilcox * an expected occurrence. 387355d6bb5Sswilcox */ 388355d6bb5Sswilcox if (!iscorrupt && preen && islog) { 389355d6bb5Sswilcox clri(idesc, "UNREF", CLRI_VERBOSE, CLRI_NOP_OK); 390355d6bb5Sswilcox return; 391355d6bb5Sswilcox } else { 392355d6bb5Sswilcox /* 393355d6bb5Sswilcox * The file system can be considered clean even if 394355d6bb5Sswilcox * a file is not linked up, but is cleared. In 395355d6bb5Sswilcox * other words, the kernel won't panic over it. 396355d6bb5Sswilcox * Hence, iscorrupt should not be set when 397355d6bb5Sswilcox * linkup is answered no, but clri is answered yes. 398355d6bb5Sswilcox * 399355d6bb5Sswilcox * If neither is answered yes, then we have a 400355d6bb5Sswilcox * non-panic-inducing known corruption that the 401355d6bb5Sswilcox * user needs to be reminded of when we exit. 402355d6bb5Sswilcox */ 403355d6bb5Sswilcox saveiscorrupt = iscorrupt; 404355d6bb5Sswilcox if (linkup(idesc->id_number, (fsck_ino_t)0, 405355d6bb5Sswilcox NULL) == 0) { 406355d6bb5Sswilcox iscorrupt = saveiscorrupt; 407355d6bb5Sswilcox clri(idesc, "UNREF", CLRI_QUIET, CLRI_NOP_OK); 408355d6bb5Sswilcox if (statemap[idesc->id_number] != USTATE) 409355d6bb5Sswilcox iscorrupt = 1; 4107c478bd9Sstevel@tonic-gate return; 4117c478bd9Sstevel@tonic-gate } 412355d6bb5Sswilcox dp = ginode(idesc->id_number); 4137c478bd9Sstevel@tonic-gate } 414355d6bb5Sswilcox lcnt = lncntp[idesc->id_number]; 415355d6bb5Sswilcox } 416355d6bb5Sswilcox 417355d6bb5Sswilcox /* 418355d6bb5Sswilcox * It doesn't happen often, but it's possible to get a true 419355d6bb5Sswilcox * excess of links (especially if a lot of directories got 420355d6bb5Sswilcox * orphaned and reattached to lost+found). Instead of wrapping 421355d6bb5Sswilcox * around, do something semi-useful (i.e., give progress towards 422355d6bb5Sswilcox * a less-broken filesystem) when this happens. 423355d6bb5Sswilcox */ 424355d6bb5Sswilcox LINK_RANGE(flow, dp->di_nlink, -lcnt); 425355d6bb5Sswilcox if (flow != NULL) { 426355d6bb5Sswilcox LINK_CLEAR(flow, idesc->id_number, dp->di_mode, &lcidesc); 427355d6bb5Sswilcox if (statemap[idesc->id_number] == USTATE) 428355d6bb5Sswilcox return; 429355d6bb5Sswilcox } 430355d6bb5Sswilcox 431355d6bb5Sswilcox dp = ginode(idesc->id_number); 4327c478bd9Sstevel@tonic-gate if (lcnt && dp->di_nlink != lcnt) { 4337c478bd9Sstevel@tonic-gate pwarn("LINK COUNT %s", 434355d6bb5Sswilcox file_id(idesc->id_number, dp->di_mode)); 4357c478bd9Sstevel@tonic-gate pinode(idesc->id_number); 436355d6bb5Sswilcox dp = ginode(idesc->id_number); 437355d6bb5Sswilcox (void) printf(" COUNT %d SHOULD BE %d", 4387c478bd9Sstevel@tonic-gate dp->di_nlink, dp->di_nlink - lcnt); 439355d6bb5Sswilcox /* 440355d6bb5Sswilcox * Even lost+found is subject to this, as whenever 441355d6bb5Sswilcox * we modify it, we update both the in-memory and 442355d6bb5Sswilcox * on-disk counts. Thus, they should still be in 443355d6bb5Sswilcox * sync. 444355d6bb5Sswilcox */ 4457c478bd9Sstevel@tonic-gate if (preen) { 4467c478bd9Sstevel@tonic-gate if (lcnt < 0) { 447355d6bb5Sswilcox (void) printf("\n"); 4487c478bd9Sstevel@tonic-gate if ((dp->di_mode & IFMT) == IFSHAD) 4497c478bd9Sstevel@tonic-gate pwarn("LINK COUNT INCREASING"); 4507c478bd9Sstevel@tonic-gate else 4517c478bd9Sstevel@tonic-gate pfatal("LINK COUNT INCREASING"); 4527c478bd9Sstevel@tonic-gate } 4537c478bd9Sstevel@tonic-gate } 4547c478bd9Sstevel@tonic-gate if (preen || reply("ADJUST") == 1) { 4557c478bd9Sstevel@tonic-gate dp->di_nlink -= lcnt; 4567c478bd9Sstevel@tonic-gate inodirty(); 457355d6bb5Sswilcox if (preen) 458355d6bb5Sswilcox (void) printf(" (ADJUSTED)\n"); 459355d6bb5Sswilcox } else if (((dp->di_mode & IFMT) == IFDIR) || 460355d6bb5Sswilcox ((dp->di_mode & IFMT) == IFATTRDIR)) { 461355d6bb5Sswilcox /* 462355d6bb5Sswilcox * File counts can be off relatively harmlessly, 463355d6bb5Sswilcox * but a bad directory count can cause the 464355d6bb5Sswilcox * kernel to lose its mind. 465355d6bb5Sswilcox */ 466355d6bb5Sswilcox iscorrupt = 1; 4677c478bd9Sstevel@tonic-gate } 4687c478bd9Sstevel@tonic-gate } 4697c478bd9Sstevel@tonic-gate } 4707c478bd9Sstevel@tonic-gate 471355d6bb5Sswilcox static int 472355d6bb5Sswilcox mkentry(struct inodesc *idesc) 4737c478bd9Sstevel@tonic-gate { 4747c478bd9Sstevel@tonic-gate struct direct *dirp = idesc->id_dirp; 4757c478bd9Sstevel@tonic-gate struct direct newent; 4767c478bd9Sstevel@tonic-gate int newlen, oldlen; 4777c478bd9Sstevel@tonic-gate 4787c478bd9Sstevel@tonic-gate newent.d_namlen = strlen(idesc->id_name); 4797c478bd9Sstevel@tonic-gate newlen = DIRSIZ(&newent); 4807c478bd9Sstevel@tonic-gate if (dirp->d_ino != 0) 4817c478bd9Sstevel@tonic-gate oldlen = DIRSIZ(dirp); 4827c478bd9Sstevel@tonic-gate else 4837c478bd9Sstevel@tonic-gate oldlen = 0; 4847c478bd9Sstevel@tonic-gate if ((int)dirp->d_reclen - oldlen < newlen) 4857c478bd9Sstevel@tonic-gate return (KEEPON); 4867c478bd9Sstevel@tonic-gate newent.d_reclen = dirp->d_reclen - (ushort_t)oldlen; 4877c478bd9Sstevel@tonic-gate dirp->d_reclen = (ushort_t)oldlen; 488355d6bb5Sswilcox 489355d6bb5Sswilcox /* LINTED dirp is aligned and DIRSIZ() forces oldlen to be aligned */ 4907c478bd9Sstevel@tonic-gate dirp = (struct direct *)(((char *)dirp) + oldlen); 4917c478bd9Sstevel@tonic-gate dirp->d_ino = idesc->id_parent; /* ino to be entered is in id_parent */ 4927c478bd9Sstevel@tonic-gate dirp->d_reclen = newent.d_reclen; 4937c478bd9Sstevel@tonic-gate dirp->d_namlen = newent.d_namlen; 494355d6bb5Sswilcox (void) memmove(dirp->d_name, idesc->id_name, 495355d6bb5Sswilcox (size_t)newent.d_namlen + 1); 496355d6bb5Sswilcox 4977c478bd9Sstevel@tonic-gate return (ALTERED|STOP); 4987c478bd9Sstevel@tonic-gate } 4997c478bd9Sstevel@tonic-gate 500355d6bb5Sswilcox static int 501355d6bb5Sswilcox chgino(struct inodesc *idesc) 5027c478bd9Sstevel@tonic-gate { 5037c478bd9Sstevel@tonic-gate struct direct *dirp = idesc->id_dirp; 5047c478bd9Sstevel@tonic-gate 505355d6bb5Sswilcox if (memcmp(dirp->d_name, idesc->id_name, 506355d6bb5Sswilcox (size_t)dirp->d_namlen + 1) != 0) 5077c478bd9Sstevel@tonic-gate return (KEEPON); 5087c478bd9Sstevel@tonic-gate dirp->d_ino = idesc->id_parent; 5097c478bd9Sstevel@tonic-gate return (ALTERED|STOP); 5107c478bd9Sstevel@tonic-gate } 5117c478bd9Sstevel@tonic-gate 512355d6bb5Sswilcox int 513355d6bb5Sswilcox linkup(fsck_ino_t orphan, fsck_ino_t parentdir, caddr_t name) 5147c478bd9Sstevel@tonic-gate { 515355d6bb5Sswilcox int rval; 5167c478bd9Sstevel@tonic-gate struct dinode *dp; 5177c478bd9Sstevel@tonic-gate int lostdir; 5187c478bd9Sstevel@tonic-gate int lostshadow; 519355d6bb5Sswilcox fsck_ino_t oldlfdir; 520355d6bb5Sswilcox fsck_ino_t *intree; 5217c478bd9Sstevel@tonic-gate struct inodesc idesc; 5227c478bd9Sstevel@tonic-gate 523355d6bb5Sswilcox init_inodesc(&idesc); 5247c478bd9Sstevel@tonic-gate dp = ginode(orphan); 5257c478bd9Sstevel@tonic-gate lostdir = (((dp->di_mode & IFMT) == IFDIR) || 5267c478bd9Sstevel@tonic-gate ((dp->di_mode & IFMT) == IFATTRDIR)); 527355d6bb5Sswilcox if (debug && lostdir && dp->di_nlink <= 0 && lncntp[orphan] == -1) 528355d6bb5Sswilcox (void) printf( 529355d6bb5Sswilcox "old fsck would have left inode %d for reclaim thread\n", 530355d6bb5Sswilcox orphan); 5317c478bd9Sstevel@tonic-gate lostshadow = (dp->di_mode & IFMT) == IFSHAD; 532355d6bb5Sswilcox pwarn("UNREF %s ", file_id(orphan, dp->di_mode)); 5337c478bd9Sstevel@tonic-gate pinode(orphan); 5347c478bd9Sstevel@tonic-gate if (lostshadow || (dp->di_size == 0 && dp->di_oeftflag == 0)) 5357c478bd9Sstevel@tonic-gate return (0); 536355d6bb5Sswilcox if (!preen && (reply("RECONNECT") == 0)) 537355d6bb5Sswilcox goto noconnect; 538355d6bb5Sswilcox 5397c478bd9Sstevel@tonic-gate if (lfdir == 0) { 5407c478bd9Sstevel@tonic-gate dp = ginode(UFSROOTINO); 5417c478bd9Sstevel@tonic-gate idesc.id_name = lfname; 5427c478bd9Sstevel@tonic-gate idesc.id_type = DATA; 5437c478bd9Sstevel@tonic-gate idesc.id_func = findino; 5447c478bd9Sstevel@tonic-gate idesc.id_number = UFSROOTINO; 5457c478bd9Sstevel@tonic-gate idesc.id_fix = DONTKNOW; 546355d6bb5Sswilcox if ((ckinode(dp, &idesc, CKI_TRAVERSE) & FOUND) != 0) { 5477c478bd9Sstevel@tonic-gate lfdir = idesc.id_parent; 5487c478bd9Sstevel@tonic-gate } else { 549355d6bb5Sswilcox pwarn("NO %s DIRECTORY", lfname); 550355d6bb5Sswilcox if (preen || reply("CREATE") == 1) { 551355d6bb5Sswilcox lfdir = newdir(UFSROOTINO, (fsck_ino_t)0, 552355d6bb5Sswilcox lfmode, lfname); 5537c478bd9Sstevel@tonic-gate if (lfdir != 0) { 5547c478bd9Sstevel@tonic-gate if (preen) 555355d6bb5Sswilcox (void) printf(" (CREATED)\n"); 556355d6bb5Sswilcox else 557355d6bb5Sswilcox (void) printf("\n"); 558355d6bb5Sswilcox statemap[lfdir] |= INFOUND; 559355d6bb5Sswilcox /* 560355d6bb5Sswilcox * XXX What if we allocate an inode 561355d6bb5Sswilcox * that's already been scanned? Then 562355d6bb5Sswilcox * we need to leave lnctnp[] alone. 563355d6bb5Sswilcox */ 564355d6bb5Sswilcox TRACK_LNCNTP(UFSROOTINO, 565355d6bb5Sswilcox lncntp[UFSROOTINO]++); 5667c478bd9Sstevel@tonic-gate } 5677c478bd9Sstevel@tonic-gate } 5687c478bd9Sstevel@tonic-gate } 5697c478bd9Sstevel@tonic-gate if (lfdir == 0) { 570355d6bb5Sswilcox pfatal("SORRY. CANNOT CREATE %s DIRECTORY\n", lfname); 571355d6bb5Sswilcox pwarn("Could not reconnect inode %d\n", orphan); 572355d6bb5Sswilcox goto noconnect; 573355d6bb5Sswilcox } else { 574355d6bb5Sswilcox /* 575355d6bb5Sswilcox * We searched for it via the namespace, so by 576355d6bb5Sswilcox * definition it's been found. We have to do this 577355d6bb5Sswilcox * because it is possible that we're called before 578355d6bb5Sswilcox * the full namespace mapping is complete (especially 579355d6bb5Sswilcox * from pass 1, if it encounters a corrupt directory 580355d6bb5Sswilcox * that has to be cleared). 581355d6bb5Sswilcox */ 582355d6bb5Sswilcox statemap[lfdir] |= INFOUND; 5837c478bd9Sstevel@tonic-gate } 5847c478bd9Sstevel@tonic-gate } 5857c478bd9Sstevel@tonic-gate dp = ginode(lfdir); 5867c478bd9Sstevel@tonic-gate if ((dp->di_mode & IFMT) != IFDIR) { 587355d6bb5Sswilcox pfatal("%s IS NOT A DIRECTORY", lfname); 588355d6bb5Sswilcox if (reply("REALLOCATE") == 0) { 589355d6bb5Sswilcox iscorrupt = 1; 590355d6bb5Sswilcox goto noconnect; 5917c478bd9Sstevel@tonic-gate } 592355d6bb5Sswilcox oldlfdir = lfdir; 593355d6bb5Sswilcox lfdir = reallocdir(UFSROOTINO, (fsck_ino_t)0, lfmode, lfname); 594355d6bb5Sswilcox if (lfdir == 0) { 595355d6bb5Sswilcox iscorrupt = 1; 596355d6bb5Sswilcox pfatal("SORRY. CANNOT CREATE %s DIRECTORY\n\n", 597355d6bb5Sswilcox lfname); 598355d6bb5Sswilcox goto noconnect; 5997c478bd9Sstevel@tonic-gate } 6007c478bd9Sstevel@tonic-gate inodirty(); 601355d6bb5Sswilcox statemap[lfdir] |= INFOUND; 602355d6bb5Sswilcox freeino(oldlfdir, TI_PARENT); 6037c478bd9Sstevel@tonic-gate } 6047c478bd9Sstevel@tonic-gate if (statemap[lfdir] != DFOUND) { 605355d6bb5Sswilcox /* 606355d6bb5Sswilcox * Not a consistency problem of the sort that'll 607355d6bb5Sswilcox * cause the kernel heartburn, so don't set iscorrupt. 608355d6bb5Sswilcox */ 609355d6bb5Sswilcox if (debug) 610355d6bb5Sswilcox (void) printf("lfdir %d is in state 0x%x\n", 611355d6bb5Sswilcox lfdir, (int)statemap[lfdir]); 612355d6bb5Sswilcox lfdir = 0; 613355d6bb5Sswilcox pfatal("SORRY. %s DIRECTORY DISAPPEARED\n\n", lfname); 614355d6bb5Sswilcox pwarn("Could not reconnect inode %d\n", orphan); 615355d6bb5Sswilcox goto noconnect; 6167c478bd9Sstevel@tonic-gate } 617355d6bb5Sswilcox 618355d6bb5Sswilcox rval = do_reconnect(orphan, parentdir, name); 619355d6bb5Sswilcox 620355d6bb5Sswilcox return (rval); 621355d6bb5Sswilcox 622355d6bb5Sswilcox /* 623355d6bb5Sswilcox * Leaving things unconnected is harmless as far as trying to 624355d6bb5Sswilcox * use the filesystem later, so don't set iscorrupt yet (it's 625355d6bb5Sswilcox * just lost blocks and inodes, after all). 626355d6bb5Sswilcox * 627355d6bb5Sswilcox * Lost directories get noted for reporting after all checks 628355d6bb5Sswilcox * are done - they may get cleared later. 629355d6bb5Sswilcox */ 630355d6bb5Sswilcox noconnect: 6317c478bd9Sstevel@tonic-gate if (lostdir) { 632355d6bb5Sswilcox intree = tsearch((void *)orphan, &limbo_dirs, 633355d6bb5Sswilcox ino_t_cmp); 634355d6bb5Sswilcox if (intree == NULL) 635355d6bb5Sswilcox errexit("linkup: out of memory"); 636355d6bb5Sswilcox } 637355d6bb5Sswilcox return (0); 638355d6bb5Sswilcox } 639355d6bb5Sswilcox 640355d6bb5Sswilcox /* 641355d6bb5Sswilcox * Connect an orphaned inode to lost+found. 642355d6bb5Sswilcox * 643355d6bb5Sswilcox * Returns non-zero for success, zero for failure. 644355d6bb5Sswilcox */ 645355d6bb5Sswilcox static int 646355d6bb5Sswilcox do_reconnect(fsck_ino_t orphan, fsck_ino_t parentdir, caddr_t name) 647355d6bb5Sswilcox { 648355d6bb5Sswilcox caddr_t flow_msg; 649355d6bb5Sswilcox struct dinode *dp; 650355d6bb5Sswilcox int lostdir; 651355d6bb5Sswilcox mode_t mode; 652355d6bb5Sswilcox fsck_ino_t *intree; 653355d6bb5Sswilcox struct inodesc idesc; 654355d6bb5Sswilcox 655355d6bb5Sswilcox dp = ginode(orphan); 656355d6bb5Sswilcox mode = dp->di_mode & IFMT; 657355d6bb5Sswilcox lostdir = (mode == IFDIR) || (mode == IFATTRDIR); 658355d6bb5Sswilcox 659355d6bb5Sswilcox name = mkuniqname(name, lfname, lfdir, orphan); 660355d6bb5Sswilcox if (name == NULL) 661355d6bb5Sswilcox goto noconnect; 662355d6bb5Sswilcox if (makeentry(lfdir, orphan, name) == 0) { 663355d6bb5Sswilcox pfatal("SORRY. NO SPACE IN %s DIRECTORY\n", lfname); 664355d6bb5Sswilcox pwarn("Could not reconnect inode %d\n", orphan); 665355d6bb5Sswilcox goto noconnect; 666355d6bb5Sswilcox } 667355d6bb5Sswilcox 668355d6bb5Sswilcox dp = ginode(orphan); 669355d6bb5Sswilcox LINK_RANGE(flow_msg, lncntp[orphan], -1); 670355d6bb5Sswilcox if (flow_msg != NULL) { 671355d6bb5Sswilcox LINK_CLEAR(flow_msg, orphan, dp->di_mode, &idesc); 672355d6bb5Sswilcox if (statemap[orphan] == USTATE) 673355d6bb5Sswilcox goto noconnect; 674355d6bb5Sswilcox } 675355d6bb5Sswilcox TRACK_LNCNTP(orphan, lncntp[orphan]--); 676355d6bb5Sswilcox 677355d6bb5Sswilcox /* 678355d6bb5Sswilcox * Make sure that anything we put into the normal namespace 679355d6bb5Sswilcox * looks like it belongs there. Attributes can only be in 680355d6bb5Sswilcox * attribute directories, not the normal directory lost+found. 681355d6bb5Sswilcox */ 682355d6bb5Sswilcox maybe_convert_attrdir_to_dir(orphan); 683355d6bb5Sswilcox 684355d6bb5Sswilcox if (lostdir) { 685355d6bb5Sswilcox /* 686355d6bb5Sswilcox * Can't be creating a duplicate entry with makeentry(), 687355d6bb5Sswilcox * because changeino() will succeed if ".." already 688355d6bb5Sswilcox * exists. 689355d6bb5Sswilcox */ 6907c478bd9Sstevel@tonic-gate if ((changeino(orphan, "..", lfdir) & ALTERED) == 0 && 691355d6bb5Sswilcox parentdir != (fsck_ino_t)-1) 6927c478bd9Sstevel@tonic-gate (void) makeentry(orphan, lfdir, ".."); 6937c478bd9Sstevel@tonic-gate /* 6947c478bd9Sstevel@tonic-gate * If we were half-detached, don't try to get 6957c478bd9Sstevel@tonic-gate * inode 0 later on. 6967c478bd9Sstevel@tonic-gate */ 6977c478bd9Sstevel@tonic-gate if (parentdir == 0) 6987c478bd9Sstevel@tonic-gate parentdir = -1; 699355d6bb5Sswilcox /* 700355d6bb5Sswilcox * Fix up link counts. 701355d6bb5Sswilcox * 702355d6bb5Sswilcox * XXX This section is getting pretty byzantine, espcially 703355d6bb5Sswilcox * when combined with changeino()/chgino()'s link manipulation. 704355d6bb5Sswilcox */ 705355d6bb5Sswilcox LFDIR_LINK_RANGE_RVAL(flow_msg, lncntp[lfdir], 1, &idesc, 0); 706355d6bb5Sswilcox TRACK_LNCNTP(lfdir, lncntp[lfdir]--); 707355d6bb5Sswilcox pwarn("DIR I=%lu CONNECTED. ", (long)orphan); 708355d6bb5Sswilcox reattached_dir = 1; 709355d6bb5Sswilcox if (parentdir != (fsck_ino_t)-1) { 710355d6bb5Sswilcox /* 711355d6bb5Sswilcox * Have to clear the parent's reference. Otherwise, 712355d6bb5Sswilcox * if it's an orphan, then we may clear this orphan 713355d6bb5Sswilcox * in pass 4 even though we've reconnected it. 714355d6bb5Sswilcox * 715355d6bb5Sswilcox * We already have the reference count 716355d6bb5Sswilcox * allowing for a parent link, so undo the 717355d6bb5Sswilcox * adjustment done above. Otherwise we come 718355d6bb5Sswilcox * out high by one. 719355d6bb5Sswilcox */ 720355d6bb5Sswilcox (void) printf("PARENT WAS I=%lu\n", (long)parentdir); 721355d6bb5Sswilcox (void) cleardirentry(parentdir, orphan); 7227c478bd9Sstevel@tonic-gate } 723355d6bb5Sswilcox if (!preen) 724355d6bb5Sswilcox (void) printf("\n"); 725355d6bb5Sswilcox } else if (preen) { 726355d6bb5Sswilcox (void) printf(" (RECONNECTED)\n"); 7277c478bd9Sstevel@tonic-gate } 728355d6bb5Sswilcox 729355d6bb5Sswilcox statemap[orphan] &= ~INDELAYD; 7307c478bd9Sstevel@tonic-gate return (1); 731355d6bb5Sswilcox 732355d6bb5Sswilcox /* 733355d6bb5Sswilcox * Leaving things unconnected is harmless as far as trying to 734355d6bb5Sswilcox * use the filesystem later, so don't set iscorrupt yet (it's 735355d6bb5Sswilcox * just lost blocks and inodes, after all). 736355d6bb5Sswilcox * 737355d6bb5Sswilcox * Lost directories get noted for reporting after all checks 738355d6bb5Sswilcox * are done - they may get cleared later. 739355d6bb5Sswilcox */ 740355d6bb5Sswilcox noconnect: 741355d6bb5Sswilcox if (lostdir) { 742355d6bb5Sswilcox intree = tsearch((void *)orphan, &limbo_dirs, 743355d6bb5Sswilcox ino_t_cmp); 744355d6bb5Sswilcox if (intree == NULL) 745355d6bb5Sswilcox errexit("linkup: out of memory"); 746355d6bb5Sswilcox } 747355d6bb5Sswilcox return (0); 7487c478bd9Sstevel@tonic-gate } 7497c478bd9Sstevel@tonic-gate 7507c478bd9Sstevel@tonic-gate /* 7517c478bd9Sstevel@tonic-gate * fix an entry in a directory. 7527c478bd9Sstevel@tonic-gate */ 753355d6bb5Sswilcox int 754355d6bb5Sswilcox changeino(fsck_ino_t dir, char *name, fsck_ino_t newnum) 7557c478bd9Sstevel@tonic-gate { 7567c478bd9Sstevel@tonic-gate struct inodesc idesc; 7577c478bd9Sstevel@tonic-gate 758355d6bb5Sswilcox init_inodesc(&idesc); 7597c478bd9Sstevel@tonic-gate idesc.id_type = DATA; 7607c478bd9Sstevel@tonic-gate idesc.id_func = chgino; 7617c478bd9Sstevel@tonic-gate idesc.id_number = dir; 7627c478bd9Sstevel@tonic-gate idesc.id_fix = DONTKNOW; 7637c478bd9Sstevel@tonic-gate idesc.id_name = name; 7647c478bd9Sstevel@tonic-gate idesc.id_parent = newnum; /* new value for name */ 765355d6bb5Sswilcox return (ckinode(ginode(dir), &idesc, CKI_TRAVERSE)); 7667c478bd9Sstevel@tonic-gate } 7677c478bd9Sstevel@tonic-gate 7687c478bd9Sstevel@tonic-gate /* 7697c478bd9Sstevel@tonic-gate * make an entry in a directory 7707c478bd9Sstevel@tonic-gate */ 771355d6bb5Sswilcox int 772355d6bb5Sswilcox makeentry(fsck_ino_t parent, fsck_ino_t ino, char *name) 7737c478bd9Sstevel@tonic-gate { 774355d6bb5Sswilcox int repeat; 7757c478bd9Sstevel@tonic-gate struct dinode *dp; 776355d6bb5Sswilcox struct inoinfo *iip; 7777c478bd9Sstevel@tonic-gate struct inodesc idesc; 7787c478bd9Sstevel@tonic-gate char pathbuf[MAXPATHLEN + 1]; 7797c478bd9Sstevel@tonic-gate 7807c478bd9Sstevel@tonic-gate if (parent < UFSROOTINO || parent >= maxino || 7817c478bd9Sstevel@tonic-gate ino < UFSROOTINO || ino >= maxino) 7827c478bd9Sstevel@tonic-gate return (0); 783355d6bb5Sswilcox init_inodesc(&idesc); 7847c478bd9Sstevel@tonic-gate idesc.id_type = DATA; 7857c478bd9Sstevel@tonic-gate idesc.id_func = mkentry; 7867c478bd9Sstevel@tonic-gate idesc.id_number = parent; 7877c478bd9Sstevel@tonic-gate idesc.id_parent = ino; /* this is the inode to enter */ 7887c478bd9Sstevel@tonic-gate idesc.id_fix = DONTKNOW; 7897c478bd9Sstevel@tonic-gate idesc.id_name = name; 790355d6bb5Sswilcox 791355d6bb5Sswilcox repeat = 0; 792355d6bb5Sswilcox again: 7937c478bd9Sstevel@tonic-gate dp = ginode(parent); 794355d6bb5Sswilcox if ((dp->di_size % DIRBLKSIZ) != 0) { 7957c478bd9Sstevel@tonic-gate dp->di_size = roundup(dp->di_size, DIRBLKSIZ); 7967c478bd9Sstevel@tonic-gate inodirty(); 797355d6bb5Sswilcox 798355d6bb5Sswilcox iip = getinoinfo(ino); 799355d6bb5Sswilcox if (iip != NULL) 800355d6bb5Sswilcox iip->i_isize = dp->di_size; 8017c478bd9Sstevel@tonic-gate } 802355d6bb5Sswilcox 803355d6bb5Sswilcox if ((ckinode(dp, &idesc, CKI_TRAVERSE) & ALTERED) != 0) { 804355d6bb5Sswilcox iip = getinoinfo(ino); 805355d6bb5Sswilcox if (iip != NULL) 806355d6bb5Sswilcox iip->i_isize = dp->di_size; 807355d6bb5Sswilcox 8087c478bd9Sstevel@tonic-gate return (1); 809355d6bb5Sswilcox } 810355d6bb5Sswilcox 811355d6bb5Sswilcox if (repeat == 0) { 8127c478bd9Sstevel@tonic-gate getpathname(pathbuf, parent, parent); 813355d6bb5Sswilcox if (expanddir(parent, pathbuf) == 0) 8147c478bd9Sstevel@tonic-gate return (0); 815355d6bb5Sswilcox 816355d6bb5Sswilcox repeat = 1; 817355d6bb5Sswilcox goto again; 818355d6bb5Sswilcox } 819355d6bb5Sswilcox 820355d6bb5Sswilcox return (0); 8217c478bd9Sstevel@tonic-gate } 8227c478bd9Sstevel@tonic-gate 8237c478bd9Sstevel@tonic-gate /* 8247c478bd9Sstevel@tonic-gate * Attempt to expand the size of a directory 8257c478bd9Sstevel@tonic-gate */ 826355d6bb5Sswilcox static int 827355d6bb5Sswilcox expanddir(fsck_ino_t ino, char *name) 8287c478bd9Sstevel@tonic-gate { 8297c478bd9Sstevel@tonic-gate struct bufarea *bpback, *bp[2]; 8307c478bd9Sstevel@tonic-gate daddr32_t nxtibn, nxtbn; 8317c478bd9Sstevel@tonic-gate daddr32_t newblk[2]; 832355d6bb5Sswilcox struct dinode *dp; 8337c478bd9Sstevel@tonic-gate char *cp; 834355d6bb5Sswilcox int bc, f; 835355d6bb5Sswilcox int n; 8367c478bd9Sstevel@tonic-gate int allocIndir; 837355d6bb5Sswilcox int frag2blks; 8387c478bd9Sstevel@tonic-gate int lffragsz = 0; 8397c478bd9Sstevel@tonic-gate int c = 0; 840355d6bb5Sswilcox int retval = 0; 8417c478bd9Sstevel@tonic-gate 842355d6bb5Sswilcox bp[0] = bp[1] = NULL; 843355d6bb5Sswilcox 844355d6bb5Sswilcox dp = ginode(ino); 845355d6bb5Sswilcox if (dp->di_size == 0) { 846355d6bb5Sswilcox goto bail; 847355d6bb5Sswilcox } 8487c478bd9Sstevel@tonic-gate 8497c478bd9Sstevel@tonic-gate nxtbn = lblkno(&sblock, dp->di_size - 1) + 1; 8507c478bd9Sstevel@tonic-gate 8517c478bd9Sstevel@tonic-gate /* 852355d6bb5Sswilcox * Check that none of the nominally in-use direct block 853355d6bb5Sswilcox * addresses for the directory are bogus. 8547c478bd9Sstevel@tonic-gate */ 8557c478bd9Sstevel@tonic-gate for (bc = 0; ((nxtbn > 0) && (bc < nxtbn) && (bc < NDADDR)); bc++) { 856355d6bb5Sswilcox if (dp->di_db[bc] == 0) { 857355d6bb5Sswilcox goto bail; 858355d6bb5Sswilcox } 8597c478bd9Sstevel@tonic-gate } 8607c478bd9Sstevel@tonic-gate 8617c478bd9Sstevel@tonic-gate /* 8627c478bd9Sstevel@tonic-gate * Determine our data block allocation needs. We always need to 8637c478bd9Sstevel@tonic-gate * allocate at least one data block. We may need a second, the 8647c478bd9Sstevel@tonic-gate * indirect block itself. 8657c478bd9Sstevel@tonic-gate */ 8667c478bd9Sstevel@tonic-gate allocIndir = 0; 8677c478bd9Sstevel@tonic-gate nxtibn = -1; 868355d6bb5Sswilcox n = 0; 8697c478bd9Sstevel@tonic-gate 8707c478bd9Sstevel@tonic-gate if (nxtbn <= NDADDR) { 8717c478bd9Sstevel@tonic-gate /* 8727c478bd9Sstevel@tonic-gate * Still in direct blocks. Check for the unlikely 8737c478bd9Sstevel@tonic-gate * case where the last block is a frag rather than 8747c478bd9Sstevel@tonic-gate * a full block. This would only happen if someone had 8757c478bd9Sstevel@tonic-gate * created a file in lost+found, and then that caused 8767c478bd9Sstevel@tonic-gate * the dynamic directory shrinking capabilities of ufs 8777c478bd9Sstevel@tonic-gate * to kick in. 878355d6bb5Sswilcox * 879355d6bb5Sswilcox * Note that we test nxtbn <= NDADDR, as it's the 880355d6bb5Sswilcox * next block (i.e., one greater than the current/ 881355d6bb5Sswilcox * actual block being examined). 8827c478bd9Sstevel@tonic-gate */ 8837c478bd9Sstevel@tonic-gate lffragsz = dp->di_size % sblock.fs_bsize; 8847c478bd9Sstevel@tonic-gate } 8857c478bd9Sstevel@tonic-gate 8867c478bd9Sstevel@tonic-gate if (nxtbn >= NDADDR && !lffragsz) { 8877c478bd9Sstevel@tonic-gate n = sblock.fs_bsize / sizeof (daddr32_t); 8887c478bd9Sstevel@tonic-gate nxtibn = nxtbn - NDADDR; 8897c478bd9Sstevel@tonic-gate /* 8907c478bd9Sstevel@tonic-gate * Only go one level of indirection 8917c478bd9Sstevel@tonic-gate */ 892355d6bb5Sswilcox if (nxtibn >= n) { 893355d6bb5Sswilcox goto bail; 894355d6bb5Sswilcox } 895355d6bb5Sswilcox /* 896355d6bb5Sswilcox * First indirect block means we need to pick up 897355d6bb5Sswilcox * the actual indirect pointer block as well. 898355d6bb5Sswilcox */ 8997c478bd9Sstevel@tonic-gate if (nxtibn == 0) 9007c478bd9Sstevel@tonic-gate allocIndir++; 9017c478bd9Sstevel@tonic-gate } 9027c478bd9Sstevel@tonic-gate 9037c478bd9Sstevel@tonic-gate /* 9047c478bd9Sstevel@tonic-gate * Allocate all the new blocks we need. 9057c478bd9Sstevel@tonic-gate */ 906355d6bb5Sswilcox if ((newblk[0] = allocblk(sblock.fs_frag)) == 0) { 9077c478bd9Sstevel@tonic-gate goto bail; 908355d6bb5Sswilcox } 9097c478bd9Sstevel@tonic-gate c++; 9107c478bd9Sstevel@tonic-gate if (allocIndir) { 911355d6bb5Sswilcox if ((newblk[1] = allocblk(sblock.fs_frag)) == 0) { 9127c478bd9Sstevel@tonic-gate goto bail; 913355d6bb5Sswilcox } 9147c478bd9Sstevel@tonic-gate c++; 9157c478bd9Sstevel@tonic-gate } 9167c478bd9Sstevel@tonic-gate 9177c478bd9Sstevel@tonic-gate /* 9187c478bd9Sstevel@tonic-gate * Take care of the block that will hold new directory entries. 9197c478bd9Sstevel@tonic-gate * This one is always allocated. 9207c478bd9Sstevel@tonic-gate */ 921355d6bb5Sswilcox bp[0] = getdirblk(newblk[0], (size_t)sblock.fs_bsize); 922355d6bb5Sswilcox if (bp[0]->b_errs) { 9237c478bd9Sstevel@tonic-gate goto bail; 924355d6bb5Sswilcox } 925355d6bb5Sswilcox 9267c478bd9Sstevel@tonic-gate if (lffragsz) { 927355d6bb5Sswilcox /* 928355d6bb5Sswilcox * Preserve the partially-populated existing directory. 929355d6bb5Sswilcox */ 9307c478bd9Sstevel@tonic-gate bpback = getdirblk(dp->di_db[nxtbn - 1], 931355d6bb5Sswilcox (size_t)dblksize(&sblock, dp, nxtbn - 1)); 932355d6bb5Sswilcox if (!bpback->b_errs) { 933355d6bb5Sswilcox (void) memmove(bp[0]->b_un.b_buf, bpback->b_un.b_buf, 934355d6bb5Sswilcox (size_t)lffragsz); 935355d6bb5Sswilcox } 936355d6bb5Sswilcox } 937355d6bb5Sswilcox 938355d6bb5Sswilcox /* 939355d6bb5Sswilcox * Initialize the new fragments. lffragsz is zero if this 940355d6bb5Sswilcox * is a completely-new block. 941355d6bb5Sswilcox */ 9427c478bd9Sstevel@tonic-gate for (cp = &(bp[0]->b_un.b_buf[lffragsz]); 9437c478bd9Sstevel@tonic-gate cp < &(bp[0]->b_un.b_buf[sblock.fs_bsize]); 944355d6bb5Sswilcox cp += DIRBLKSIZ) { 945355d6bb5Sswilcox (void) memmove(cp, (char *)&emptydir, 946355d6bb5Sswilcox sizeof (emptydir)); 9477c478bd9Sstevel@tonic-gate } 9487c478bd9Sstevel@tonic-gate dirty(bp[0]); 9497c478bd9Sstevel@tonic-gate 9507c478bd9Sstevel@tonic-gate /* 9517c478bd9Sstevel@tonic-gate * If we allocated the indirect block, zero it out. Otherwise 9527c478bd9Sstevel@tonic-gate * read it in if we're using one. 9537c478bd9Sstevel@tonic-gate */ 9547c478bd9Sstevel@tonic-gate if (allocIndir) { 955355d6bb5Sswilcox bp[1] = getdatablk(newblk[1], (size_t)sblock.fs_bsize); 956355d6bb5Sswilcox if (bp[1]->b_errs) { 9577c478bd9Sstevel@tonic-gate goto bail; 958355d6bb5Sswilcox } 959355d6bb5Sswilcox (void) memset(bp[1]->b_un.b_buf, 0, sblock.fs_bsize); 9607c478bd9Sstevel@tonic-gate dirty(bp[1]); 9617c478bd9Sstevel@tonic-gate } else if (nxtibn >= 0) { 9627c478bd9Sstevel@tonic-gate /* Check that the indirect block pointer looks okay */ 963355d6bb5Sswilcox if (dp->di_ib[0] == 0) { 9647c478bd9Sstevel@tonic-gate goto bail; 965355d6bb5Sswilcox } 966355d6bb5Sswilcox bp[1] = getdatablk(dp->di_ib[0], (size_t)sblock.fs_bsize); 967355d6bb5Sswilcox if (bp[1]->b_errs) { 9687c478bd9Sstevel@tonic-gate goto bail; 969355d6bb5Sswilcox } 9707c478bd9Sstevel@tonic-gate 9717c478bd9Sstevel@tonic-gate for (bc = 0; ((bc < nxtibn) && (bc < n)); bc++) { 972355d6bb5Sswilcox /* LINTED pointer cast alignment */ 973355d6bb5Sswilcox if (((daddr32_t *)bp[1]->b_un.b_buf)[bc] == 0) { 9747c478bd9Sstevel@tonic-gate goto bail; 9757c478bd9Sstevel@tonic-gate } 9767c478bd9Sstevel@tonic-gate } 977355d6bb5Sswilcox } 9787c478bd9Sstevel@tonic-gate 979355d6bb5Sswilcox /* 980355d6bb5Sswilcox * Since the filesystem's consistency isn't affected by 981355d6bb5Sswilcox * whether or not we actually do the expansion, iscorrupt 982355d6bb5Sswilcox * is left alone for any of the approval paths. 983355d6bb5Sswilcox */ 9847c478bd9Sstevel@tonic-gate pwarn("NO SPACE LEFT IN %s", name); 985355d6bb5Sswilcox if (!preen && (reply("EXPAND") == 0)) 9867c478bd9Sstevel@tonic-gate goto bail; 9877c478bd9Sstevel@tonic-gate 9887c478bd9Sstevel@tonic-gate /* 989355d6bb5Sswilcox * Now that everything we need is gathered up and the 990355d6bb5Sswilcox * necessary approvals acquired, we can make our provisional 991355d6bb5Sswilcox * changes permanent. 9927c478bd9Sstevel@tonic-gate */ 9937c478bd9Sstevel@tonic-gate 9947c478bd9Sstevel@tonic-gate if (lffragsz) { 995355d6bb5Sswilcox /* 996355d6bb5Sswilcox * We've saved the data from the old end fragment(s) in 997355d6bb5Sswilcox * our new block, so we can just swap the new one in. 998355d6bb5Sswilcox * Make sure the size reflects the expansion of the 999355d6bb5Sswilcox * final fragments/block. 1000355d6bb5Sswilcox */ 10017c478bd9Sstevel@tonic-gate frag2blks = roundup(lffragsz, sblock.fs_fsize); 1002355d6bb5Sswilcox freeblk(ino, dp->di_db[nxtbn - 1], 10037c478bd9Sstevel@tonic-gate frag2blks / sblock.fs_fsize); 10047c478bd9Sstevel@tonic-gate frag2blks = btodb(frag2blks); 10057c478bd9Sstevel@tonic-gate dp->di_size -= (u_offset_t)lffragsz; 10067c478bd9Sstevel@tonic-gate dp->di_blocks = dp->di_blocks - frag2blks; 10077c478bd9Sstevel@tonic-gate dp->di_db[nxtbn - 1] = newblk[0]; 10087c478bd9Sstevel@tonic-gate dp->di_size += (u_offset_t)sblock.fs_bsize; 10097c478bd9Sstevel@tonic-gate dp->di_blocks += btodb(sblock.fs_bsize); 10107c478bd9Sstevel@tonic-gate inodirty(); 1011355d6bb5Sswilcox retval = 1; 1012355d6bb5Sswilcox goto done; 10137c478bd9Sstevel@tonic-gate } 10147c478bd9Sstevel@tonic-gate 1015355d6bb5Sswilcox /* 1016355d6bb5Sswilcox * Full-block addition's much easier. It's just an append. 1017355d6bb5Sswilcox */ 10187c478bd9Sstevel@tonic-gate dp->di_size += (u_offset_t)sblock.fs_bsize; 10197c478bd9Sstevel@tonic-gate dp->di_blocks += btodb(sblock.fs_bsize); 10207c478bd9Sstevel@tonic-gate if (allocIndir) { 10217c478bd9Sstevel@tonic-gate dp->di_blocks += btodb(sblock.fs_bsize); 10227c478bd9Sstevel@tonic-gate } 10237c478bd9Sstevel@tonic-gate 10247c478bd9Sstevel@tonic-gate inodirty(); 10257c478bd9Sstevel@tonic-gate if (nxtibn < 0) { 10267c478bd9Sstevel@tonic-gate /* 10277c478bd9Sstevel@tonic-gate * Still in direct blocks 10287c478bd9Sstevel@tonic-gate */ 10297c478bd9Sstevel@tonic-gate dp->di_db[nxtbn] = newblk[0]; 10307c478bd9Sstevel@tonic-gate } else { 10317c478bd9Sstevel@tonic-gate /* 10327c478bd9Sstevel@tonic-gate * Last indirect is always going to point at the 10337c478bd9Sstevel@tonic-gate * new directory buffer 10347c478bd9Sstevel@tonic-gate */ 10357c478bd9Sstevel@tonic-gate if (allocIndir) 10367c478bd9Sstevel@tonic-gate dp->di_ib[0] = newblk[1]; 1037355d6bb5Sswilcox /* LINTED pointer case alignment */ 10387c478bd9Sstevel@tonic-gate ((daddr32_t *)bp[1]->b_un.b_buf)[nxtibn] = newblk[0]; 10397c478bd9Sstevel@tonic-gate dirty(bp[1]); 10407c478bd9Sstevel@tonic-gate } 1041355d6bb5Sswilcox 1042355d6bb5Sswilcox if (preen) 1043355d6bb5Sswilcox (void) printf(" (EXPANDED)\n"); 1044355d6bb5Sswilcox 1045355d6bb5Sswilcox retval = 1; 1046355d6bb5Sswilcox goto done; 10477c478bd9Sstevel@tonic-gate 10487c478bd9Sstevel@tonic-gate bail: 10497c478bd9Sstevel@tonic-gate for (f = 0; f < c; f++) 1050355d6bb5Sswilcox freeblk(ino, newblk[f], sblock.fs_frag); 1051355d6bb5Sswilcox done: 1052355d6bb5Sswilcox /* 1053355d6bb5Sswilcox * bp[0] is handled by the directory cache's auto-release. 1054355d6bb5Sswilcox */ 1055355d6bb5Sswilcox if (bp[1] != NULL) 1056355d6bb5Sswilcox brelse(bp[1]); 1057355d6bb5Sswilcox 1058355d6bb5Sswilcox return (retval); 1059355d6bb5Sswilcox } 1060355d6bb5Sswilcox 1061355d6bb5Sswilcox static fsck_ino_t 1062355d6bb5Sswilcox newdir(fsck_ino_t parent, fsck_ino_t request, int mode, caddr_t name) 1063355d6bb5Sswilcox { 1064355d6bb5Sswilcox fsck_ino_t dino; 1065355d6bb5Sswilcox char pname[BUFSIZ]; 1066355d6bb5Sswilcox 1067355d6bb5Sswilcox /* 1068355d6bb5Sswilcox * This function creates a new directory and populates it with 1069355d6bb5Sswilcox * "." and "..", then links to it as NAME in PARENT. 1070355d6bb5Sswilcox */ 1071355d6bb5Sswilcox dino = allocdir(parent, request, mode, 1); 1072355d6bb5Sswilcox if (dino != 0) { 1073355d6bb5Sswilcox getpathname(pname, parent, parent); 1074355d6bb5Sswilcox name = mkuniqname(name, pname, parent, dino); 1075355d6bb5Sswilcox /* 1076355d6bb5Sswilcox * We don't touch numdirs, because it's just a cache of 1077355d6bb5Sswilcox * what the filesystem claimed originally and is used 1078355d6bb5Sswilcox * to calculate hash keys. 1079355d6bb5Sswilcox */ 1080355d6bb5Sswilcox if (makeentry(parent, dino, name) == 0) { 1081355d6bb5Sswilcox freedir(dino, parent); 1082355d6bb5Sswilcox dino = 0; 1083355d6bb5Sswilcox } 1084355d6bb5Sswilcox } 1085355d6bb5Sswilcox 1086355d6bb5Sswilcox return (dino); 1087355d6bb5Sswilcox } 1088355d6bb5Sswilcox 1089355d6bb5Sswilcox /* 1090355d6bb5Sswilcox * Replace whatever NAME refers to in PARENT with a new directory. 1091355d6bb5Sswilcox * Note that if the old inode REQUEST is a directory, all of its 1092355d6bb5Sswilcox * contents will be freed and reaped. 1093355d6bb5Sswilcox */ 1094355d6bb5Sswilcox static fsck_ino_t 1095355d6bb5Sswilcox reallocdir(fsck_ino_t parent, fsck_ino_t request, int mode, caddr_t name) 1096355d6bb5Sswilcox { 1097355d6bb5Sswilcox int retval; 1098355d6bb5Sswilcox fsck_ino_t newino; 1099355d6bb5Sswilcox 1100355d6bb5Sswilcox if ((request != 0) && (statemap[request] != USTATE)) 1101355d6bb5Sswilcox freeino(request, TI_PARENT); 1102355d6bb5Sswilcox 1103355d6bb5Sswilcox newino = allocdir(parent, request, mode, 0); 1104355d6bb5Sswilcox if (newino != 0) { 1105355d6bb5Sswilcox retval = changeino(parent, name, newino); 1106355d6bb5Sswilcox if ((retval & ALTERED) == 0) { 1107355d6bb5Sswilcox /* 1108355d6bb5Sswilcox * No change made, so name doesn't exist, so 1109355d6bb5Sswilcox * unwind allocation rather than leak it. 1110355d6bb5Sswilcox */ 1111355d6bb5Sswilcox freedir(newino, parent); 1112355d6bb5Sswilcox newino = 0; 1113355d6bb5Sswilcox } 1114355d6bb5Sswilcox } 1115355d6bb5Sswilcox 1116355d6bb5Sswilcox return (newino); 11177c478bd9Sstevel@tonic-gate } 11187c478bd9Sstevel@tonic-gate 11197c478bd9Sstevel@tonic-gate /* 11207c478bd9Sstevel@tonic-gate * allocate a new directory 11217c478bd9Sstevel@tonic-gate */ 1122355d6bb5Sswilcox fsck_ino_t 1123355d6bb5Sswilcox allocdir(fsck_ino_t parent, fsck_ino_t request, int mode, int update_parent) 11247c478bd9Sstevel@tonic-gate { 1125355d6bb5Sswilcox fsck_ino_t ino; 1126355d6bb5Sswilcox caddr_t cp; 1127355d6bb5Sswilcox caddr_t flow; 11287c478bd9Sstevel@tonic-gate struct dinode *dp; 11297c478bd9Sstevel@tonic-gate struct bufarea *bp; 1130355d6bb5Sswilcox struct inoinfo *inp; 1131355d6bb5Sswilcox struct inodesc idesc; 1132355d6bb5Sswilcox struct dirtemplate *dirp; 11337c478bd9Sstevel@tonic-gate 11347c478bd9Sstevel@tonic-gate ino = allocino(request, IFDIR|mode); 11357c478bd9Sstevel@tonic-gate if (ino == 0) 11367c478bd9Sstevel@tonic-gate return (0); 1137355d6bb5Sswilcox dirp = &dirhead; 1138355d6bb5Sswilcox dirp->dot_ino = ino; 1139355d6bb5Sswilcox dirp->dotdot_ino = parent; 11407c478bd9Sstevel@tonic-gate dp = ginode(ino); 1141355d6bb5Sswilcox bp = getdirblk(dp->di_db[0], (size_t)sblock.fs_fsize); 11427c478bd9Sstevel@tonic-gate if (bp->b_errs) { 1143355d6bb5Sswilcox freeino(ino, TI_PARENT); 11447c478bd9Sstevel@tonic-gate return (0); 11457c478bd9Sstevel@tonic-gate } 1146355d6bb5Sswilcox (void) memmove(bp->b_un.b_buf, (void *)dirp, 1147355d6bb5Sswilcox sizeof (struct dirtemplate)); 11487c478bd9Sstevel@tonic-gate for (cp = &bp->b_un.b_buf[DIRBLKSIZ]; 11497c478bd9Sstevel@tonic-gate cp < &bp->b_un.b_buf[sblock.fs_fsize]; 11507c478bd9Sstevel@tonic-gate cp += DIRBLKSIZ) 1151355d6bb5Sswilcox (void) memmove(cp, (void *)&emptydir, sizeof (emptydir)); 11527c478bd9Sstevel@tonic-gate dirty(bp); 11537c478bd9Sstevel@tonic-gate dp->di_nlink = 2; 11547c478bd9Sstevel@tonic-gate inodirty(); 11557c478bd9Sstevel@tonic-gate if (!inocached(ino)) { 11567c478bd9Sstevel@tonic-gate cacheino(dp, ino); 11577c478bd9Sstevel@tonic-gate } else { 11587c478bd9Sstevel@tonic-gate /* 11597c478bd9Sstevel@tonic-gate * re-using an old directory inode 11607c478bd9Sstevel@tonic-gate */ 11617c478bd9Sstevel@tonic-gate inp = getinoinfo(ino); 1162355d6bb5Sswilcox if (inp == NULL) { 1163355d6bb5Sswilcox if (debug) 1164355d6bb5Sswilcox errexit("allocdir got NULL from getinoinfo " 1165355d6bb5Sswilcox "for existing entry I=%d\n", 1166355d6bb5Sswilcox ino); 1167355d6bb5Sswilcox cacheino(dp, ino); 1168355d6bb5Sswilcox } else { 1169355d6bb5Sswilcox init_inoinfo(inp, dp, ino); 11707c478bd9Sstevel@tonic-gate inp->i_parent = parent; 1171355d6bb5Sswilcox inp->i_dotdot = parent; 11727c478bd9Sstevel@tonic-gate } 1173355d6bb5Sswilcox } 1174355d6bb5Sswilcox 1175355d6bb5Sswilcox /* 1176355d6bb5Sswilcox * Short-circuit all the dancing around below if it's the 1177355d6bb5Sswilcox * root inode. The net effect's the same. 1178355d6bb5Sswilcox */ 11797c478bd9Sstevel@tonic-gate if (ino == UFSROOTINO) { 1180355d6bb5Sswilcox TRACK_LNCNTP(ino, lncntp[ino] = dp->di_nlink); 11817c478bd9Sstevel@tonic-gate return (ino); 11827c478bd9Sstevel@tonic-gate } 1183355d6bb5Sswilcox 1184355d6bb5Sswilcox if (!update_parent) 1185355d6bb5Sswilcox return (ino); 1186355d6bb5Sswilcox 1187355d6bb5Sswilcox /* 1188355d6bb5Sswilcox * We never create attribute directories, which can have 1189355d6bb5Sswilcox * non-directory parents. So, the parent of the directory 1190355d6bb5Sswilcox * we're creating must itself be a directory. 1191355d6bb5Sswilcox */ 1192355d6bb5Sswilcox if (!INO_IS_DVALID(parent)) { 1193355d6bb5Sswilcox freeino(ino, TI_PARENT); 11947c478bd9Sstevel@tonic-gate return (0); 11957c478bd9Sstevel@tonic-gate } 1196355d6bb5Sswilcox 1197355d6bb5Sswilcox /* 1198355d6bb5Sswilcox * Make sure the parent can handle another link. 1199355d6bb5Sswilcox * Since we might only update one version of the 1200355d6bb5Sswilcox * count (disk versus in-memory), we have to check both. 1201355d6bb5Sswilcox */ 1202355d6bb5Sswilcox LINK_RANGE(flow, lncntp[parent], -1); 1203355d6bb5Sswilcox if (flow == NULL) 1204355d6bb5Sswilcox LINK_RANGE(flow, (int)dp->di_nlink, 1); 1205355d6bb5Sswilcox 1206355d6bb5Sswilcox if (flow != NULL) { 1207355d6bb5Sswilcox LINK_CLEAR(flow, parent, dp->di_mode, &idesc); 1208355d6bb5Sswilcox if (statemap[parent] == USTATE) { 1209355d6bb5Sswilcox /* 1210355d6bb5Sswilcox * No parent any more, so bail out. Callers 1211355d6bb5Sswilcox * are expected to handle this possibility. 1212355d6bb5Sswilcox * Since most just throw up their hands if 1213355d6bb5Sswilcox * we return 0, this just happens to work. 1214355d6bb5Sswilcox */ 1215355d6bb5Sswilcox freeino(ino, TI_PARENT); 1216355d6bb5Sswilcox return (0); 1217355d6bb5Sswilcox } 1218355d6bb5Sswilcox } 1219355d6bb5Sswilcox 1220355d6bb5Sswilcox /* 1221355d6bb5Sswilcox * We've created a directory with two entries, "." and "..", 1222355d6bb5Sswilcox * and a link count of two ("." and one from its parent). If 1223355d6bb5Sswilcox * the parent's not been scanned yet, which means this inode 1224355d6bb5Sswilcox * will get scanned later as well, then make our in-core count 1225355d6bb5Sswilcox * match what we pushed out to disk. Similarly, update the 1226355d6bb5Sswilcox * parent. On the other hand, if the parent's already been 1227355d6bb5Sswilcox * looked at (statemap[ino] == DFOUND), the discrepancy 1228355d6bb5Sswilcox * between lncntp[] and di_nlink will be noted later, with 1229*b9a41fd3Sswilcox * appropriate reporting and propagation, in pass2. 1230355d6bb5Sswilcox * 1231355d6bb5Sswilcox * We're explicitly skipping where the parent was DZLINK or 1232355d6bb5Sswilcox * DFOUND. If it has zero links, it can't be gotten to, so 1233*b9a41fd3Sswilcox * we want a discrepancy set up that will be caught in pass2. 1234355d6bb5Sswilcox * DFOUND was discussed above. 1235355d6bb5Sswilcox * 1236355d6bb5Sswilcox * Regarding the claim of a link from the parent: we've not 1237355d6bb5Sswilcox * done anything to create such a link here. We depend on the 1238355d6bb5Sswilcox * semantics of our callers attaching the inode we return to 1239355d6bb5Sswilcox * an existing entry in the directory or creating the entry 1240355d6bb5Sswilcox * themselves, but in either case, not modifying the link 1241355d6bb5Sswilcox * count. 1242355d6bb5Sswilcox * 1243355d6bb5Sswilcox * Note that setting lncntp[ino] to zero means that both claimed 1244355d6bb5Sswilcox * links have been ``found''. 1245355d6bb5Sswilcox */ 12467c478bd9Sstevel@tonic-gate statemap[ino] = statemap[parent]; 1247355d6bb5Sswilcox if (INO_IS_DVALID(parent)) { 1248355d6bb5Sswilcox TRACK_LNCNTP(ino, lncntp[ino] = 0); 1249355d6bb5Sswilcox TRACK_LNCNTP(parent, lncntp[parent]--); 12507c478bd9Sstevel@tonic-gate } 12517c478bd9Sstevel@tonic-gate dp = ginode(parent); 12527c478bd9Sstevel@tonic-gate dp->di_nlink++; 12537c478bd9Sstevel@tonic-gate inodirty(); 12547c478bd9Sstevel@tonic-gate return (ino); 12557c478bd9Sstevel@tonic-gate } 12567c478bd9Sstevel@tonic-gate 12577c478bd9Sstevel@tonic-gate /* 12587c478bd9Sstevel@tonic-gate * free a directory inode 12597c478bd9Sstevel@tonic-gate */ 1260355d6bb5Sswilcox static void 1261355d6bb5Sswilcox freedir(fsck_ino_t ino, fsck_ino_t parent) 12627c478bd9Sstevel@tonic-gate { 1263355d6bb5Sswilcox struct inoinfo *iip; 12647c478bd9Sstevel@tonic-gate 12657c478bd9Sstevel@tonic-gate if (ino != parent) { 1266355d6bb5Sswilcox /* 1267355d6bb5Sswilcox * Make sure that the desired parent gets a link 1268355d6bb5Sswilcox * count update from freeino()/truncino(). If 1269355d6bb5Sswilcox * we can't look it up, then it's not really a 1270355d6bb5Sswilcox * directory, so there's nothing to worry about. 1271355d6bb5Sswilcox */ 1272355d6bb5Sswilcox iip = getinoinfo(ino); 1273355d6bb5Sswilcox if (iip != NULL) 1274355d6bb5Sswilcox iip->i_parent = parent; 12757c478bd9Sstevel@tonic-gate } 1276355d6bb5Sswilcox freeino(ino, TI_PARENT); 12777c478bd9Sstevel@tonic-gate } 12787c478bd9Sstevel@tonic-gate 12797c478bd9Sstevel@tonic-gate /* 1280355d6bb5Sswilcox * generate a temporary name for use in the lost+found directory. 12817c478bd9Sstevel@tonic-gate */ 1282355d6bb5Sswilcox static void 1283355d6bb5Sswilcox lftempname(char *bufp, fsck_ino_t ino) 12847c478bd9Sstevel@tonic-gate { 1285355d6bb5Sswilcox fsck_ino_t in; 1286355d6bb5Sswilcox caddr_t cp; 12877c478bd9Sstevel@tonic-gate int namlen; 12887c478bd9Sstevel@tonic-gate 12897c478bd9Sstevel@tonic-gate cp = bufp + 2; 12907c478bd9Sstevel@tonic-gate for (in = maxino; in > 0; in /= 10) 12917c478bd9Sstevel@tonic-gate cp++; 1292355d6bb5Sswilcox *--cp = '\0'; 1293355d6bb5Sswilcox /* LINTED difference will not overflow an int */ 12947c478bd9Sstevel@tonic-gate namlen = cp - bufp; 1295355d6bb5Sswilcox if ((namlen > BUFSIZ) || (namlen > MAXPATHLEN)) { 1296355d6bb5Sswilcox errexit("buffer overflow in lftempname()\n"); 1297355d6bb5Sswilcox } 1298355d6bb5Sswilcox 12997c478bd9Sstevel@tonic-gate in = ino; 13007c478bd9Sstevel@tonic-gate while (cp > bufp) { 13017c478bd9Sstevel@tonic-gate *--cp = (in % 10) + '0'; 13027c478bd9Sstevel@tonic-gate in /= 10; 13037c478bd9Sstevel@tonic-gate } 13047c478bd9Sstevel@tonic-gate *cp = '#'; 13057c478bd9Sstevel@tonic-gate } 13067c478bd9Sstevel@tonic-gate 13077c478bd9Sstevel@tonic-gate /* 13087c478bd9Sstevel@tonic-gate * Get a directory block. 13097c478bd9Sstevel@tonic-gate * Insure that it is held until another is requested. 1310355d6bb5Sswilcox * 1311355d6bb5Sswilcox * Our callers are expected to check for errors and/or be 1312355d6bb5Sswilcox * prepared to handle blocks of zeros in the middle of a 1313355d6bb5Sswilcox * directory. 13147c478bd9Sstevel@tonic-gate */ 1315355d6bb5Sswilcox static struct bufarea * 1316355d6bb5Sswilcox getdirblk(daddr32_t blkno, size_t size) 13177c478bd9Sstevel@tonic-gate { 13187c478bd9Sstevel@tonic-gate if (pdirbp != 0) { 13197c478bd9Sstevel@tonic-gate brelse(pdirbp); 13207c478bd9Sstevel@tonic-gate } 13217c478bd9Sstevel@tonic-gate pdirbp = getdatablk(blkno, size); 13227c478bd9Sstevel@tonic-gate return (pdirbp); 13237c478bd9Sstevel@tonic-gate } 1324355d6bb5Sswilcox 1325355d6bb5Sswilcox /* 1326355d6bb5Sswilcox * Create a unique name for INODE to be created in directory PARENT. 1327355d6bb5Sswilcox * Use NAME if it is provided (non-NULL) and doesn't already exist. 1328355d6bb5Sswilcox * Returning NULL indicates no unique name could be generated. 1329355d6bb5Sswilcox * 1330355d6bb5Sswilcox * If we were given a name, and it conflicts with an existing 1331355d6bb5Sswilcox * entry, use our usual temp name instead. Without this check, 1332355d6bb5Sswilcox * we could end up creating duplicate entries for multiple 1333355d6bb5Sswilcox * orphaned directories in lost+found with the same name (but 1334355d6bb5Sswilcox * different parents). Of course, our usual name might already 1335355d6bb5Sswilcox * be in use as well, so be paranoid. 1336355d6bb5Sswilcox * 1337355d6bb5Sswilcox * We could do something like keep tacking something onto the 1338355d6bb5Sswilcox * end of tempname until we come up with something that's not 1339355d6bb5Sswilcox * in use, but that has liabilities as well. This is a 1340355d6bb5Sswilcox * sufficiently rare case that it's not worth going that 1341355d6bb5Sswilcox * overboard for. 1342355d6bb5Sswilcox */ 1343355d6bb5Sswilcox static caddr_t 1344355d6bb5Sswilcox mkuniqname(caddr_t name, caddr_t pname, fsck_ino_t parent, fsck_ino_t inode) 1345355d6bb5Sswilcox { 1346355d6bb5Sswilcox fsck_ino_t oldino; 1347355d6bb5Sswilcox struct dinode *dp; 1348355d6bb5Sswilcox caddr_t flow_msg; 1349355d6bb5Sswilcox struct inodesc idesc; 1350355d6bb5Sswilcox static char tempname[BUFSIZ]; 1351355d6bb5Sswilcox 1352355d6bb5Sswilcox lftempname(tempname, inode); 1353355d6bb5Sswilcox if ((name != NULL) && 1354355d6bb5Sswilcox (lookup_named_ino(parent, name) != 0)) { 1355355d6bb5Sswilcox name = NULL; 1356355d6bb5Sswilcox } 1357355d6bb5Sswilcox if (name == NULL) { 1358355d6bb5Sswilcox /* 1359355d6bb5Sswilcox * No name given, or it wasn't unique. 1360355d6bb5Sswilcox */ 1361355d6bb5Sswilcox name = tempname; 1362355d6bb5Sswilcox if ((oldino = lookup_named_ino(parent, name)) != 0) { 1363355d6bb5Sswilcox pfatal( 1364355d6bb5Sswilcox "Name ``%s'' for inode %d already exists in %s \n", 1365355d6bb5Sswilcox name, oldino, pname); 1366355d6bb5Sswilcox if (reply("REMOVE OLD ENTRY") == 0) { 1367355d6bb5Sswilcox if (parent == lfdir) 1368355d6bb5Sswilcox pwarn( 1369355d6bb5Sswilcox "Could not reconnect inode %d\n\n", 1370355d6bb5Sswilcox inode); 1371355d6bb5Sswilcox else 1372355d6bb5Sswilcox pwarn( 1373355d6bb5Sswilcox "Could not create entry for %d\n\n", 1374355d6bb5Sswilcox inode); 1375355d6bb5Sswilcox name = NULL; 1376355d6bb5Sswilcox goto noconnect; 1377355d6bb5Sswilcox } 1378355d6bb5Sswilcox (void) changeino(parent, name, inode); 1379355d6bb5Sswilcox LINK_RANGE(flow_msg, lncntp[oldino], 1); 1380355d6bb5Sswilcox if (flow_msg != NULL) { 1381355d6bb5Sswilcox /* 1382355d6bb5Sswilcox * Do a best-effort, but if we're not 1383355d6bb5Sswilcox * allowed to do the clear, the fs is 1384355d6bb5Sswilcox * corrupt in any case, so just carry on. 1385355d6bb5Sswilcox */ 1386355d6bb5Sswilcox dp = ginode(oldino); 1387355d6bb5Sswilcox LINK_CLEAR(flow_msg, oldino, dp->di_mode, 1388355d6bb5Sswilcox &idesc); 1389355d6bb5Sswilcox if (statemap[oldino] != USTATE) 1390355d6bb5Sswilcox iscorrupt = 1; 1391355d6bb5Sswilcox } else { 1392355d6bb5Sswilcox TRACK_LNCNTP(oldino, lncntp[oldino]++); 1393355d6bb5Sswilcox } 1394355d6bb5Sswilcox } 1395355d6bb5Sswilcox } 1396355d6bb5Sswilcox 1397355d6bb5Sswilcox noconnect: 1398355d6bb5Sswilcox return (name); 1399355d6bb5Sswilcox } 1400