10Sstevel@tonic-gate /*
2392Sswilcox * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
30Sstevel@tonic-gate * Use is subject to license terms.
40Sstevel@tonic-gate */
50Sstevel@tonic-gate
60Sstevel@tonic-gate /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
70Sstevel@tonic-gate /* All Rights Reserved */
80Sstevel@tonic-gate
90Sstevel@tonic-gate /*
100Sstevel@tonic-gate * Copyright (c) 1980, 1986, 1990 The Regents of the University of California.
110Sstevel@tonic-gate * All rights reserved.
120Sstevel@tonic-gate *
130Sstevel@tonic-gate * Redistribution and use in source and binary forms are permitted
140Sstevel@tonic-gate * provided that: (1) source distributions retain this entire copyright
150Sstevel@tonic-gate * notice and comment, and (2) distributions including binaries display
160Sstevel@tonic-gate * the following acknowledgement: ``This product includes software
170Sstevel@tonic-gate * developed by the University of California, Berkeley and its contributors''
180Sstevel@tonic-gate * in the documentation or other materials provided with the distribution
190Sstevel@tonic-gate * and in all advertising materials mentioning features or use of this
200Sstevel@tonic-gate * software. Neither the name of the University nor the names of its
210Sstevel@tonic-gate * contributors may be used to endorse or promote products derived
220Sstevel@tonic-gate * from this software without specific prior written permission.
230Sstevel@tonic-gate * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
240Sstevel@tonic-gate * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
250Sstevel@tonic-gate * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
260Sstevel@tonic-gate */
270Sstevel@tonic-gate
280Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI"
290Sstevel@tonic-gate
300Sstevel@tonic-gate #include <sys/param.h>
310Sstevel@tonic-gate #include <sys/types.h>
320Sstevel@tonic-gate #include <sys/sysmacros.h>
330Sstevel@tonic-gate #include <sys/mntent.h>
34392Sswilcox #include <string.h>
35392Sswilcox #include <stdarg.h>
360Sstevel@tonic-gate #include <sys/fs/ufs_fs.h>
370Sstevel@tonic-gate #include <sys/vnode.h>
380Sstevel@tonic-gate #include <sys/fs/ufs_inode.h>
390Sstevel@tonic-gate #define _KERNEL
400Sstevel@tonic-gate #include <sys/fs/ufs_fsdir.h>
410Sstevel@tonic-gate #undef _KERNEL
420Sstevel@tonic-gate #include "fsck.h"
430Sstevel@tonic-gate
44392Sswilcox struct rc_queue {
45392Sswilcox struct rc_queue *rc_next;
46392Sswilcox fsck_ino_t rc_orphan;
47392Sswilcox fsck_ino_t rc_parent;
48392Sswilcox caddr_t rc_name;
49392Sswilcox };
500Sstevel@tonic-gate
51392Sswilcox caddr_t lfname = "lost+found"; /* name to use for l+f dir */
52392Sswilcox static int lfmode = 01700; /* mode to use when creating l+f dir */
53392Sswilcox static struct dirtemplate emptydir = { 0, DIRBLKSIZ };
54392Sswilcox static struct dirtemplate dirhead = {
55392Sswilcox 0, 12, 1, ".", 0, DIRBLKSIZ - 12, 2, ".."
56392Sswilcox };
57392Sswilcox
58392Sswilcox static void lftempname(char *, fsck_ino_t);
59392Sswilcox static int do_reconnect(fsck_ino_t, fsck_ino_t, caddr_t);
60392Sswilcox static caddr_t mkuniqname(caddr_t, caddr_t, fsck_ino_t, fsck_ino_t);
61392Sswilcox static int chgino(struct inodesc *);
62392Sswilcox static int dircheck(struct inodesc *, struct direct *);
63392Sswilcox static int expanddir(fsck_ino_t, char *);
64392Sswilcox static void freedir(fsck_ino_t, fsck_ino_t);
65392Sswilcox static struct direct *fsck_readdir(struct inodesc *);
66392Sswilcox static struct bufarea *getdirblk(daddr32_t, size_t);
67392Sswilcox static int mkentry(struct inodesc *);
68392Sswilcox static fsck_ino_t newdir(fsck_ino_t, fsck_ino_t, int, caddr_t);
69392Sswilcox static fsck_ino_t reallocdir(fsck_ino_t, fsck_ino_t, int, caddr_t);
700Sstevel@tonic-gate
710Sstevel@tonic-gate /*
720Sstevel@tonic-gate * Propagate connected state through the tree.
730Sstevel@tonic-gate */
74392Sswilcox void
propagate(void)75392Sswilcox propagate(void)
760Sstevel@tonic-gate {
770Sstevel@tonic-gate struct inoinfo **inpp, *inp;
780Sstevel@tonic-gate struct inoinfo **inpend;
79392Sswilcox int change, inorphan;
800Sstevel@tonic-gate
810Sstevel@tonic-gate inpend = &inpsort[inplast];
820Sstevel@tonic-gate do {
830Sstevel@tonic-gate change = 0;
840Sstevel@tonic-gate for (inpp = inpsort; inpp < inpend; inpp++) {
850Sstevel@tonic-gate inp = *inpp;
860Sstevel@tonic-gate if (inp->i_parent == 0)
870Sstevel@tonic-gate continue;
880Sstevel@tonic-gate if (statemap[inp->i_parent] == DFOUND &&
89392Sswilcox INO_IS_DUNFOUND(inp->i_number)) {
90392Sswilcox inorphan = statemap[inp->i_number] & INORPHAN;
91392Sswilcox statemap[inp->i_number] = DFOUND | inorphan;
920Sstevel@tonic-gate change++;
930Sstevel@tonic-gate }
940Sstevel@tonic-gate }
950Sstevel@tonic-gate } while (change > 0);
960Sstevel@tonic-gate }
970Sstevel@tonic-gate
980Sstevel@tonic-gate /*
990Sstevel@tonic-gate * Scan each entry in a directory block.
1000Sstevel@tonic-gate */
101392Sswilcox int
dirscan(struct inodesc * idesc)102392Sswilcox dirscan(struct inodesc *idesc)
1030Sstevel@tonic-gate {
1040Sstevel@tonic-gate struct direct *dp;
1050Sstevel@tonic-gate struct bufarea *bp;
106392Sswilcox uint_t dsize, n;
107392Sswilcox size_t blksiz;
108392Sswilcox union { /* keep lint happy about alignment */
109392Sswilcox char dbuf[DIRBLKSIZ];
110392Sswilcox struct direct dir;
111392Sswilcox } u;
1120Sstevel@tonic-gate
1130Sstevel@tonic-gate if (idesc->id_type != DATA)
1140Sstevel@tonic-gate errexit("wrong type to dirscan %d\n", idesc->id_type);
1150Sstevel@tonic-gate if (idesc->id_entryno == 0 &&
1160Sstevel@tonic-gate (idesc->id_filesize & (DIRBLKSIZ - 1)) != 0)
1170Sstevel@tonic-gate idesc->id_filesize = roundup(idesc->id_filesize, DIRBLKSIZ);
1180Sstevel@tonic-gate blksiz = idesc->id_numfrags * sblock.fs_fsize;
1190Sstevel@tonic-gate if (chkrange(idesc->id_blkno, idesc->id_numfrags)) {
1200Sstevel@tonic-gate idesc->id_filesize -= (offset_t)blksiz;
1210Sstevel@tonic-gate return (SKIP);
1220Sstevel@tonic-gate }
1230Sstevel@tonic-gate idesc->id_loc = 0;
1240Sstevel@tonic-gate for (dp = fsck_readdir(idesc); dp != NULL; dp = fsck_readdir(idesc)) {
1250Sstevel@tonic-gate /*
1260Sstevel@tonic-gate * If we were just passed a corrupt directory entry with
127392Sswilcox * d_reclen > DIRBLKSIZ, we don't want to memmove() all over
128392Sswilcox * our stack. This directory gets cleaned up later.
1290Sstevel@tonic-gate */
130392Sswilcox dsize = MIN(dp->d_reclen, sizeof (u.dbuf));
131392Sswilcox (void) memmove((void *)u.dbuf, (void *)dp, (size_t)dsize);
132392Sswilcox idesc->id_dirp = &u.dir;
1330Sstevel@tonic-gate if ((n = (*idesc->id_func)(idesc)) & ALTERED) {
134392Sswilcox /*
135392Sswilcox * We can ignore errors from getdirblk() here,
136392Sswilcox * as the block is still in memory thanks to
137392Sswilcox * buffering and fsck_readdir(). If there was
138392Sswilcox * an error reading it before, then all decisions
139392Sswilcox * leading to getting us here were based on the
140392Sswilcox * resulting zeros. As such, we have nothing
141392Sswilcox * to worry about at this point.
142392Sswilcox */
1430Sstevel@tonic-gate bp = getdirblk(idesc->id_blkno, blksiz);
144392Sswilcox (void) memmove((void *)(bp->b_un.b_buf +
145392Sswilcox idesc->id_loc - dsize),
146392Sswilcox (void *)u.dbuf, (size_t)dsize);
1470Sstevel@tonic-gate dirty(bp);
1480Sstevel@tonic-gate sbdirty();
1490Sstevel@tonic-gate }
1500Sstevel@tonic-gate if (n & STOP)
1510Sstevel@tonic-gate return (n);
1520Sstevel@tonic-gate }
1530Sstevel@tonic-gate return (idesc->id_filesize > 0 ? KEEPON : STOP);
1540Sstevel@tonic-gate }
1550Sstevel@tonic-gate
1560Sstevel@tonic-gate /*
1570Sstevel@tonic-gate * Get current entry in a directory (and peek at the next entry).
1580Sstevel@tonic-gate */
159392Sswilcox static struct direct *
fsck_readdir(struct inodesc * idesc)160392Sswilcox fsck_readdir(struct inodesc *idesc)
1610Sstevel@tonic-gate {
1620Sstevel@tonic-gate struct direct *dp, *ndp = 0;
1630Sstevel@tonic-gate struct bufarea *bp;
164392Sswilcox ushort_t size; /* of directory entry */
165392Sswilcox size_t blksiz;
1660Sstevel@tonic-gate int dofixret;
167392Sswilcox int salvaged; /* when to report SALVAGED in preen mode */
1680Sstevel@tonic-gate int origloc = idesc->id_loc;
1690Sstevel@tonic-gate
1700Sstevel@tonic-gate blksiz = idesc->id_numfrags * sblock.fs_fsize;
1710Sstevel@tonic-gate /*
172392Sswilcox * Sanity check id_filesize and id_loc fields. The latter
173392Sswilcox * has to be within the block we're looking at, as well as
174392Sswilcox * aligned to a four-byte boundary. The alignment is due to
175392Sswilcox * a struct direct containing four-byte integers. It's
176392Sswilcox * unfortunate that the four is a magic number, but there's
177392Sswilcox * really no good way to derive it from the ufs header files.
1780Sstevel@tonic-gate */
179392Sswilcox if ((idesc->id_filesize <= 0) || (idesc->id_loc >= blksiz) ||
180392Sswilcox ((idesc->id_loc & 3) != 0))
1810Sstevel@tonic-gate return (NULL);
182392Sswilcox /*
183392Sswilcox * We don't have to worry about holes in the directory's
184392Sswilcox * block list, because that was checked for when the
185392Sswilcox * inode was first encountered during pass1. We never
186392Sswilcox * scan a directory until after we've vetted its block list.
187392Sswilcox */
188392Sswilcox /*
189392Sswilcox * We can ignore errors from getdirblk() here, as dircheck()
190392Sswilcox * will reject any entries that would have been in the bad
191392Sswilcox * sectors (fsck_bread() fills in zeros on failures). The main
192392Sswilcox * reject keys are that d_reclen would be zero and/or that it
193392Sswilcox * is less than the minimal size of a directory entry. Since
194392Sswilcox * entries can't span sectors, there's no worry about having
195392Sswilcox * a good beginning in one sector and the rest in the next,
196392Sswilcox * where that second sector was unreadable and therefore
197392Sswilcox * replaced with zeros.
198392Sswilcox */
1990Sstevel@tonic-gate bp = getdirblk(idesc->id_blkno, blksiz);
200392Sswilcox /* LINTED b_buf is aligned and id_loc was verified above */
2010Sstevel@tonic-gate dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
2020Sstevel@tonic-gate
2030Sstevel@tonic-gate /*
2040Sstevel@tonic-gate * Check the current entry in the directory.
2050Sstevel@tonic-gate */
2060Sstevel@tonic-gate if (dircheck(idesc, dp) == 0) {
2070Sstevel@tonic-gate /*
2080Sstevel@tonic-gate * If we are in here, then either the current directory
2090Sstevel@tonic-gate * entry is bad or the next directory entry is bad.
2100Sstevel@tonic-gate */
2110Sstevel@tonic-gate next_is_bad:
2120Sstevel@tonic-gate /*
2130Sstevel@tonic-gate * Find the amount of space left to the end of the
2140Sstevel@tonic-gate * directory block for either directory entry.
2150Sstevel@tonic-gate */
2160Sstevel@tonic-gate size = DIRBLKSIZ - (idesc->id_loc & (DIRBLKSIZ - 1));
2170Sstevel@tonic-gate
2180Sstevel@tonic-gate /*
2190Sstevel@tonic-gate * Advance to the end of the directory block.
2200Sstevel@tonic-gate */
2210Sstevel@tonic-gate idesc->id_loc += size;
2220Sstevel@tonic-gate idesc->id_filesize -= (offset_t)size;
2230Sstevel@tonic-gate
2240Sstevel@tonic-gate /*
2250Sstevel@tonic-gate * Ask the question before we fix the in-core directory
2260Sstevel@tonic-gate * block because dofix() may reuse the buffer.
2270Sstevel@tonic-gate */
228392Sswilcox salvaged = (idesc->id_fix == DONTKNOW);
2290Sstevel@tonic-gate dofixret = dofix(idesc, "DIRECTORY CORRUPTED");
230392Sswilcox
231392Sswilcox /*
232392Sswilcox * If there was an error reading the block, then that
233392Sswilcox * same error can reasonably be expected to have occurred
234392Sswilcox * when it was read previously. As such, the decision
235392Sswilcox * to come here was based on the results of that partially-
236392Sswilcox * zerod block, and so anything we change should be
237392Sswilcox * based on it as well. Upshot: no need to check for
238392Sswilcox * errors here.
239392Sswilcox */
2400Sstevel@tonic-gate bp = getdirblk(idesc->id_blkno, blksiz);
241392Sswilcox /* LINTED b_buf is aligned and id_loc/origloc was verified */
2420Sstevel@tonic-gate dp = (struct direct *)(bp->b_un.b_buf + origloc);
2430Sstevel@tonic-gate
2440Sstevel@tonic-gate /*
2450Sstevel@tonic-gate * This is the current directory entry and since it is
2460Sstevel@tonic-gate * corrupt we cannot trust the rest of the directory
2470Sstevel@tonic-gate * block so change the current directory entry to
2480Sstevel@tonic-gate * contain nothing and encompass the rest of the block.
2490Sstevel@tonic-gate */
2500Sstevel@tonic-gate if (ndp == NULL) {
2510Sstevel@tonic-gate dp->d_reclen = size;
2520Sstevel@tonic-gate dp->d_ino = 0;
2530Sstevel@tonic-gate dp->d_namlen = 0;
2540Sstevel@tonic-gate dp->d_name[0] = '\0';
2550Sstevel@tonic-gate }
2560Sstevel@tonic-gate /*
2570Sstevel@tonic-gate * This is the next directory entry, i.e., we got here
258392Sswilcox * via a "goto next_is_bad". That directory entry is
259392Sswilcox * corrupt. However, the current directory entry is okay
260392Sswilcox * so if we are in fix mode, just extend its record size
261392Sswilcox * to encompass the rest of the block.
2620Sstevel@tonic-gate */
2630Sstevel@tonic-gate else if (dofixret) {
2640Sstevel@tonic-gate dp->d_reclen += size;
2650Sstevel@tonic-gate }
2660Sstevel@tonic-gate /*
2670Sstevel@tonic-gate * If the user said to fix the directory corruption, then
2680Sstevel@tonic-gate * mark the block as dirty. Otherwise, our "repairs" only
2690Sstevel@tonic-gate * apply to the in-core copy so we don't hand back trash
2700Sstevel@tonic-gate * to the caller.
2710Sstevel@tonic-gate *
2720Sstevel@tonic-gate * Note: It is possible that saying "no" to a change in
2730Sstevel@tonic-gate * one part of the I/O buffer and "yes" to a later change
2740Sstevel@tonic-gate * in the same I/O buffer may still flush the change to
275392Sswilcox * which we said "no". This is the pathological case and
2760Sstevel@tonic-gate * no fix is planned at this time.
2770Sstevel@tonic-gate */
278392Sswilcox if (dofixret) {
2790Sstevel@tonic-gate dirty(bp);
280392Sswilcox if (preen && salvaged)
281392Sswilcox (void) printf(" (SALVAGED)\n");
282392Sswilcox if (idesc->id_number == lfdir)
283392Sswilcox lfdir = 0;
284392Sswilcox }
285392Sswilcox
286392Sswilcox /*
287392Sswilcox * dp points into bp, which will get re-used at some
288392Sswilcox * arbitrary time in the future. We rely on the fact
289392Sswilcox * that we're singled-threaded, and that we'll be done
290392Sswilcox * with this directory entry by the time the next one
291392Sswilcox * is needed.
292392Sswilcox */
2930Sstevel@tonic-gate return (dp);
2940Sstevel@tonic-gate }
2950Sstevel@tonic-gate /*
2960Sstevel@tonic-gate * The current directory entry checked out so advance past it.
2970Sstevel@tonic-gate */
2980Sstevel@tonic-gate idesc->id_loc += dp->d_reclen;
2990Sstevel@tonic-gate idesc->id_filesize -= (offset_t)dp->d_reclen;
3000Sstevel@tonic-gate /*
3010Sstevel@tonic-gate * If we are not at the directory block boundary, then peek
3020Sstevel@tonic-gate * at the next directory entry and if it is bad we can add
3030Sstevel@tonic-gate * its space to the current directory entry (compression).
3040Sstevel@tonic-gate * Again, we sanity check the id_loc and id_filesize fields
3050Sstevel@tonic-gate * since we modified them above.
3060Sstevel@tonic-gate */
307392Sswilcox if ((idesc->id_loc & (DIRBLKSIZ - 1)) && /* not at start */
308392Sswilcox (idesc->id_loc < blksiz) && /* within block */
309392Sswilcox ((idesc->id_loc & 3) == 0) && /* properly aligned */
310392Sswilcox (idesc->id_filesize > 0)) { /* data follows */
311392Sswilcox /* LINTED b_buf is aligned and id_loc verified to be ok */
3120Sstevel@tonic-gate ndp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
3130Sstevel@tonic-gate if (dircheck(idesc, ndp) == 0)
3140Sstevel@tonic-gate goto next_is_bad;
3150Sstevel@tonic-gate }
3160Sstevel@tonic-gate
317392Sswilcox /*
318392Sswilcox * See comment above about dp pointing into bp.
319392Sswilcox */
3200Sstevel@tonic-gate return (dp);
3210Sstevel@tonic-gate }
3220Sstevel@tonic-gate
3230Sstevel@tonic-gate /*
3240Sstevel@tonic-gate * Verify that a directory entry is valid.
3250Sstevel@tonic-gate * This is a superset of the checks made in the kernel.
3260Sstevel@tonic-gate */
327392Sswilcox static int
dircheck(struct inodesc * idesc,struct direct * dp)328392Sswilcox dircheck(struct inodesc *idesc, struct direct *dp)
3290Sstevel@tonic-gate {
330392Sswilcox size_t size;
3310Sstevel@tonic-gate char *cp;
3320Sstevel@tonic-gate int spaceleft;
3330Sstevel@tonic-gate
334392Sswilcox /*
335392Sswilcox * Recall that id_filesize is the number of bytes left to
336392Sswilcox * process in the directory. We check id_filesize >= size
337392Sswilcox * instead of id_filesize >= d_reclen because all that the
338392Sswilcox * directory is actually required to contain is the entry
339392Sswilcox * itself (and it's how the kernel does the allocation).
340392Sswilcox *
341392Sswilcox * We indirectly check for d_reclen going past the end of
342392Sswilcox * the allocated space by comparing it against spaceleft.
343392Sswilcox */
3440Sstevel@tonic-gate size = DIRSIZ(dp);
3450Sstevel@tonic-gate spaceleft = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ);
3460Sstevel@tonic-gate if (dp->d_ino < maxino &&
3470Sstevel@tonic-gate dp->d_reclen != 0 &&
3480Sstevel@tonic-gate (int)dp->d_reclen <= spaceleft &&
3490Sstevel@tonic-gate (dp->d_reclen & 0x3) == 0 &&
3500Sstevel@tonic-gate (int)dp->d_reclen >= size &&
3510Sstevel@tonic-gate idesc->id_filesize >= (offset_t)size &&
3520Sstevel@tonic-gate dp->d_namlen <= MAXNAMLEN) {
3530Sstevel@tonic-gate if (dp->d_ino == 0)
3540Sstevel@tonic-gate return (1);
355392Sswilcox for (cp = dp->d_name, size = 0; size < (size_t)dp->d_namlen;
3560Sstevel@tonic-gate size++, cp++)
357392Sswilcox if ((*cp == '\0') || (*cp == '/'))
358392Sswilcox goto bad;
359392Sswilcox if (*cp == '\0')
3600Sstevel@tonic-gate return (1);
3610Sstevel@tonic-gate }
362392Sswilcox bad:
363392Sswilcox if (debug) {
364392Sswilcox (void) printf("Bad dir in inode %d at lbn %d, loc %d:\n",
365392Sswilcox idesc->id_number, idesc->id_lbn, idesc->id_loc);
366392Sswilcox (void) printf(" ino %d reclen %d namlen %d name `%s'\n",
367392Sswilcox dp->d_ino, dp->d_reclen, dp->d_namlen, dp->d_name);
368392Sswilcox }
3690Sstevel@tonic-gate return (0);
3700Sstevel@tonic-gate }
3710Sstevel@tonic-gate
372392Sswilcox void
adjust(struct inodesc * idesc,int lcnt)373392Sswilcox adjust(struct inodesc *idesc, int lcnt)
3740Sstevel@tonic-gate {
3750Sstevel@tonic-gate struct dinode *dp;
376392Sswilcox caddr_t flow;
377392Sswilcox int saveiscorrupt;
378392Sswilcox struct inodesc lcidesc;
3790Sstevel@tonic-gate
3800Sstevel@tonic-gate dp = ginode(idesc->id_number);
3810Sstevel@tonic-gate if (dp->di_nlink == lcnt) {
382392Sswilcox /*
383392Sswilcox * If we have not hit any unresolved problems, are running
384392Sswilcox * in preen mode, and are on a file system using logging,
385392Sswilcox * then just toss any partially allocated files, as they are
386392Sswilcox * an expected occurrence.
387392Sswilcox */
388392Sswilcox if (!iscorrupt && preen && islog) {
389392Sswilcox clri(idesc, "UNREF", CLRI_VERBOSE, CLRI_NOP_OK);
3900Sstevel@tonic-gate return;
391392Sswilcox } else {
392392Sswilcox /*
393392Sswilcox * The file system can be considered clean even if
394392Sswilcox * a file is not linked up, but is cleared. In
395392Sswilcox * other words, the kernel won't panic over it.
396392Sswilcox * Hence, iscorrupt should not be set when
397392Sswilcox * linkup is answered no, but clri is answered yes.
398392Sswilcox *
399392Sswilcox * If neither is answered yes, then we have a
400392Sswilcox * non-panic-inducing known corruption that the
401392Sswilcox * user needs to be reminded of when we exit.
402392Sswilcox */
403392Sswilcox saveiscorrupt = iscorrupt;
404392Sswilcox if (linkup(idesc->id_number, (fsck_ino_t)0,
405392Sswilcox NULL) == 0) {
406392Sswilcox iscorrupt = saveiscorrupt;
407392Sswilcox clri(idesc, "UNREF", CLRI_QUIET, CLRI_NOP_OK);
408392Sswilcox if (statemap[idesc->id_number] != USTATE)
409392Sswilcox iscorrupt = 1;
410392Sswilcox return;
411392Sswilcox }
412392Sswilcox dp = ginode(idesc->id_number);
4130Sstevel@tonic-gate }
414392Sswilcox lcnt = lncntp[idesc->id_number];
4150Sstevel@tonic-gate }
416392Sswilcox
417392Sswilcox /*
418392Sswilcox * It doesn't happen often, but it's possible to get a true
419392Sswilcox * excess of links (especially if a lot of directories got
420392Sswilcox * orphaned and reattached to lost+found). Instead of wrapping
421392Sswilcox * around, do something semi-useful (i.e., give progress towards
422392Sswilcox * a less-broken filesystem) when this happens.
423392Sswilcox */
424392Sswilcox LINK_RANGE(flow, dp->di_nlink, -lcnt);
425392Sswilcox if (flow != NULL) {
426392Sswilcox LINK_CLEAR(flow, idesc->id_number, dp->di_mode, &lcidesc);
427392Sswilcox if (statemap[idesc->id_number] == USTATE)
428392Sswilcox return;
429392Sswilcox }
430392Sswilcox
431392Sswilcox dp = ginode(idesc->id_number);
4320Sstevel@tonic-gate if (lcnt && dp->di_nlink != lcnt) {
4330Sstevel@tonic-gate pwarn("LINK COUNT %s",
434392Sswilcox file_id(idesc->id_number, dp->di_mode));
4350Sstevel@tonic-gate pinode(idesc->id_number);
436392Sswilcox dp = ginode(idesc->id_number);
437392Sswilcox (void) printf(" COUNT %d SHOULD BE %d",
4380Sstevel@tonic-gate dp->di_nlink, dp->di_nlink - lcnt);
439392Sswilcox /*
440392Sswilcox * Even lost+found is subject to this, as whenever
441392Sswilcox * we modify it, we update both the in-memory and
442392Sswilcox * on-disk counts. Thus, they should still be in
443392Sswilcox * sync.
444392Sswilcox */
4450Sstevel@tonic-gate if (preen) {
4460Sstevel@tonic-gate if (lcnt < 0) {
447392Sswilcox (void) printf("\n");
4480Sstevel@tonic-gate if ((dp->di_mode & IFMT) == IFSHAD)
4490Sstevel@tonic-gate pwarn("LINK COUNT INCREASING");
4500Sstevel@tonic-gate else
4510Sstevel@tonic-gate pfatal("LINK COUNT INCREASING");
4520Sstevel@tonic-gate }
4530Sstevel@tonic-gate }
4540Sstevel@tonic-gate if (preen || reply("ADJUST") == 1) {
4550Sstevel@tonic-gate dp->di_nlink -= lcnt;
4560Sstevel@tonic-gate inodirty();
457392Sswilcox if (preen)
458392Sswilcox (void) printf(" (ADJUSTED)\n");
459392Sswilcox } else if (((dp->di_mode & IFMT) == IFDIR) ||
460392Sswilcox ((dp->di_mode & IFMT) == IFATTRDIR)) {
461392Sswilcox /*
462392Sswilcox * File counts can be off relatively harmlessly,
463392Sswilcox * but a bad directory count can cause the
464392Sswilcox * kernel to lose its mind.
465392Sswilcox */
466392Sswilcox iscorrupt = 1;
4670Sstevel@tonic-gate }
4680Sstevel@tonic-gate }
4690Sstevel@tonic-gate }
4700Sstevel@tonic-gate
471392Sswilcox static int
mkentry(struct inodesc * idesc)472392Sswilcox mkentry(struct inodesc *idesc)
4730Sstevel@tonic-gate {
4740Sstevel@tonic-gate struct direct *dirp = idesc->id_dirp;
4750Sstevel@tonic-gate struct direct newent;
4760Sstevel@tonic-gate int newlen, oldlen;
4770Sstevel@tonic-gate
4780Sstevel@tonic-gate newent.d_namlen = strlen(idesc->id_name);
4790Sstevel@tonic-gate newlen = DIRSIZ(&newent);
4800Sstevel@tonic-gate if (dirp->d_ino != 0)
4810Sstevel@tonic-gate oldlen = DIRSIZ(dirp);
4820Sstevel@tonic-gate else
4830Sstevel@tonic-gate oldlen = 0;
4840Sstevel@tonic-gate if ((int)dirp->d_reclen - oldlen < newlen)
4850Sstevel@tonic-gate return (KEEPON);
4860Sstevel@tonic-gate newent.d_reclen = dirp->d_reclen - (ushort_t)oldlen;
4870Sstevel@tonic-gate dirp->d_reclen = (ushort_t)oldlen;
488392Sswilcox
489392Sswilcox /* LINTED dirp is aligned and DIRSIZ() forces oldlen to be aligned */
4900Sstevel@tonic-gate dirp = (struct direct *)(((char *)dirp) + oldlen);
4910Sstevel@tonic-gate dirp->d_ino = idesc->id_parent; /* ino to be entered is in id_parent */
4920Sstevel@tonic-gate dirp->d_reclen = newent.d_reclen;
4930Sstevel@tonic-gate dirp->d_namlen = newent.d_namlen;
494392Sswilcox (void) memmove(dirp->d_name, idesc->id_name,
495392Sswilcox (size_t)newent.d_namlen + 1);
496392Sswilcox
4970Sstevel@tonic-gate return (ALTERED|STOP);
4980Sstevel@tonic-gate }
4990Sstevel@tonic-gate
500392Sswilcox static int
chgino(struct inodesc * idesc)501392Sswilcox chgino(struct inodesc *idesc)
5020Sstevel@tonic-gate {
5030Sstevel@tonic-gate struct direct *dirp = idesc->id_dirp;
5040Sstevel@tonic-gate
505392Sswilcox if (memcmp(dirp->d_name, idesc->id_name,
506392Sswilcox (size_t)dirp->d_namlen + 1) != 0)
5070Sstevel@tonic-gate return (KEEPON);
5080Sstevel@tonic-gate dirp->d_ino = idesc->id_parent;
5090Sstevel@tonic-gate return (ALTERED|STOP);
5100Sstevel@tonic-gate }
5110Sstevel@tonic-gate
512392Sswilcox int
linkup(fsck_ino_t orphan,fsck_ino_t parentdir,caddr_t name)513392Sswilcox linkup(fsck_ino_t orphan, fsck_ino_t parentdir, caddr_t name)
5140Sstevel@tonic-gate {
515392Sswilcox int rval;
5160Sstevel@tonic-gate struct dinode *dp;
5170Sstevel@tonic-gate int lostdir;
5180Sstevel@tonic-gate int lostshadow;
519392Sswilcox fsck_ino_t oldlfdir;
520392Sswilcox fsck_ino_t *intree;
5210Sstevel@tonic-gate struct inodesc idesc;
5220Sstevel@tonic-gate
523392Sswilcox init_inodesc(&idesc);
5240Sstevel@tonic-gate dp = ginode(orphan);
5250Sstevel@tonic-gate lostdir = (((dp->di_mode & IFMT) == IFDIR) ||
5260Sstevel@tonic-gate ((dp->di_mode & IFMT) == IFATTRDIR));
527392Sswilcox if (debug && lostdir && dp->di_nlink <= 0 && lncntp[orphan] == -1)
528392Sswilcox (void) printf(
529392Sswilcox "old fsck would have left inode %d for reclaim thread\n",
530392Sswilcox orphan);
5310Sstevel@tonic-gate lostshadow = (dp->di_mode & IFMT) == IFSHAD;
532392Sswilcox pwarn("UNREF %s ", file_id(orphan, dp->di_mode));
5330Sstevel@tonic-gate pinode(orphan);
5340Sstevel@tonic-gate if (lostshadow || (dp->di_size == 0 && dp->di_oeftflag == 0))
5350Sstevel@tonic-gate return (0);
536392Sswilcox if (!preen && (reply("RECONNECT") == 0))
537392Sswilcox goto noconnect;
538392Sswilcox
5390Sstevel@tonic-gate if (lfdir == 0) {
5400Sstevel@tonic-gate dp = ginode(UFSROOTINO);
5410Sstevel@tonic-gate idesc.id_name = lfname;
5420Sstevel@tonic-gate idesc.id_type = DATA;
5430Sstevel@tonic-gate idesc.id_func = findino;
5440Sstevel@tonic-gate idesc.id_number = UFSROOTINO;
5450Sstevel@tonic-gate idesc.id_fix = DONTKNOW;
546392Sswilcox if ((ckinode(dp, &idesc, CKI_TRAVERSE) & FOUND) != 0) {
5470Sstevel@tonic-gate lfdir = idesc.id_parent;
5480Sstevel@tonic-gate } else {
549392Sswilcox pwarn("NO %s DIRECTORY", lfname);
550392Sswilcox if (preen || reply("CREATE") == 1) {
551392Sswilcox lfdir = newdir(UFSROOTINO, (fsck_ino_t)0,
552392Sswilcox lfmode, lfname);
5530Sstevel@tonic-gate if (lfdir != 0) {
554392Sswilcox if (preen)
555392Sswilcox (void) printf(" (CREATED)\n");
556392Sswilcox else
557392Sswilcox (void) printf("\n");
558392Sswilcox statemap[lfdir] |= INFOUND;
559392Sswilcox /*
560392Sswilcox * XXX What if we allocate an inode
561392Sswilcox * that's already been scanned? Then
562392Sswilcox * we need to leave lnctnp[] alone.
563392Sswilcox */
564392Sswilcox TRACK_LNCNTP(UFSROOTINO,
565392Sswilcox lncntp[UFSROOTINO]++);
5660Sstevel@tonic-gate }
5670Sstevel@tonic-gate }
5680Sstevel@tonic-gate }
5690Sstevel@tonic-gate if (lfdir == 0) {
570392Sswilcox pfatal("SORRY. CANNOT CREATE %s DIRECTORY\n", lfname);
571392Sswilcox pwarn("Could not reconnect inode %d\n", orphan);
572392Sswilcox goto noconnect;
573392Sswilcox } else {
574392Sswilcox /*
575392Sswilcox * We searched for it via the namespace, so by
576392Sswilcox * definition it's been found. We have to do this
577392Sswilcox * because it is possible that we're called before
578392Sswilcox * the full namespace mapping is complete (especially
579392Sswilcox * from pass 1, if it encounters a corrupt directory
580392Sswilcox * that has to be cleared).
581392Sswilcox */
582392Sswilcox statemap[lfdir] |= INFOUND;
5830Sstevel@tonic-gate }
5840Sstevel@tonic-gate }
5850Sstevel@tonic-gate dp = ginode(lfdir);
5860Sstevel@tonic-gate if ((dp->di_mode & IFMT) != IFDIR) {
587392Sswilcox pfatal("%s IS NOT A DIRECTORY", lfname);
588392Sswilcox if (reply("REALLOCATE") == 0) {
589392Sswilcox iscorrupt = 1;
590392Sswilcox goto noconnect;
591392Sswilcox }
5920Sstevel@tonic-gate oldlfdir = lfdir;
593392Sswilcox lfdir = reallocdir(UFSROOTINO, (fsck_ino_t)0, lfmode, lfname);
594392Sswilcox if (lfdir == 0) {
595392Sswilcox iscorrupt = 1;
596392Sswilcox pfatal("SORRY. CANNOT CREATE %s DIRECTORY\n\n",
597392Sswilcox lfname);
598392Sswilcox goto noconnect;
5990Sstevel@tonic-gate }
6000Sstevel@tonic-gate inodirty();
601392Sswilcox statemap[lfdir] |= INFOUND;
602392Sswilcox freeino(oldlfdir, TI_PARENT);
6030Sstevel@tonic-gate }
6040Sstevel@tonic-gate if (statemap[lfdir] != DFOUND) {
605392Sswilcox /*
606392Sswilcox * Not a consistency problem of the sort that'll
607392Sswilcox * cause the kernel heartburn, so don't set iscorrupt.
608392Sswilcox */
609392Sswilcox if (debug)
610392Sswilcox (void) printf("lfdir %d is in state 0x%x\n",
611392Sswilcox lfdir, (int)statemap[lfdir]);
612392Sswilcox lfdir = 0;
613392Sswilcox pfatal("SORRY. %s DIRECTORY DISAPPEARED\n\n", lfname);
614392Sswilcox pwarn("Could not reconnect inode %d\n", orphan);
615392Sswilcox goto noconnect;
616392Sswilcox }
617392Sswilcox
618392Sswilcox rval = do_reconnect(orphan, parentdir, name);
619392Sswilcox
620392Sswilcox return (rval);
621392Sswilcox
622392Sswilcox /*
623392Sswilcox * Leaving things unconnected is harmless as far as trying to
624392Sswilcox * use the filesystem later, so don't set iscorrupt yet (it's
625392Sswilcox * just lost blocks and inodes, after all).
626392Sswilcox *
627392Sswilcox * Lost directories get noted for reporting after all checks
628392Sswilcox * are done - they may get cleared later.
629392Sswilcox */
630392Sswilcox noconnect:
631392Sswilcox if (lostdir) {
632392Sswilcox intree = tsearch((void *)orphan, &limbo_dirs,
633392Sswilcox ino_t_cmp);
634392Sswilcox if (intree == NULL)
635392Sswilcox errexit("linkup: out of memory");
6360Sstevel@tonic-gate }
637392Sswilcox return (0);
638392Sswilcox }
639392Sswilcox
640392Sswilcox /*
641392Sswilcox * Connect an orphaned inode to lost+found.
642392Sswilcox *
643392Sswilcox * Returns non-zero for success, zero for failure.
644392Sswilcox */
645392Sswilcox static int
do_reconnect(fsck_ino_t orphan,fsck_ino_t parentdir,caddr_t name)646392Sswilcox do_reconnect(fsck_ino_t orphan, fsck_ino_t parentdir, caddr_t name)
647392Sswilcox {
648392Sswilcox caddr_t flow_msg;
649392Sswilcox struct dinode *dp;
650392Sswilcox int lostdir;
651392Sswilcox mode_t mode;
652392Sswilcox fsck_ino_t *intree;
653392Sswilcox struct inodesc idesc;
654392Sswilcox
655392Sswilcox dp = ginode(orphan);
656392Sswilcox mode = dp->di_mode & IFMT;
657392Sswilcox lostdir = (mode == IFDIR) || (mode == IFATTRDIR);
658392Sswilcox
659392Sswilcox name = mkuniqname(name, lfname, lfdir, orphan);
660392Sswilcox if (name == NULL)
661392Sswilcox goto noconnect;
662392Sswilcox if (makeentry(lfdir, orphan, name) == 0) {
663392Sswilcox pfatal("SORRY. NO SPACE IN %s DIRECTORY\n", lfname);
664392Sswilcox pwarn("Could not reconnect inode %d\n", orphan);
665392Sswilcox goto noconnect;
6660Sstevel@tonic-gate }
667392Sswilcox
668392Sswilcox dp = ginode(orphan);
669392Sswilcox LINK_RANGE(flow_msg, lncntp[orphan], -1);
670392Sswilcox if (flow_msg != NULL) {
671392Sswilcox LINK_CLEAR(flow_msg, orphan, dp->di_mode, &idesc);
672392Sswilcox if (statemap[orphan] == USTATE)
673392Sswilcox goto noconnect;
674392Sswilcox }
675392Sswilcox TRACK_LNCNTP(orphan, lncntp[orphan]--);
676392Sswilcox
677392Sswilcox /*
678392Sswilcox * Make sure that anything we put into the normal namespace
679392Sswilcox * looks like it belongs there. Attributes can only be in
680392Sswilcox * attribute directories, not the normal directory lost+found.
681392Sswilcox */
682392Sswilcox maybe_convert_attrdir_to_dir(orphan);
683392Sswilcox
6840Sstevel@tonic-gate if (lostdir) {
685392Sswilcox /*
686392Sswilcox * Can't be creating a duplicate entry with makeentry(),
687392Sswilcox * because changeino() will succeed if ".." already
688392Sswilcox * exists.
689392Sswilcox */
6900Sstevel@tonic-gate if ((changeino(orphan, "..", lfdir) & ALTERED) == 0 &&
691392Sswilcox parentdir != (fsck_ino_t)-1)
6920Sstevel@tonic-gate (void) makeentry(orphan, lfdir, "..");
6930Sstevel@tonic-gate /*
6940Sstevel@tonic-gate * If we were half-detached, don't try to get
6950Sstevel@tonic-gate * inode 0 later on.
6960Sstevel@tonic-gate */
6970Sstevel@tonic-gate if (parentdir == 0)
6980Sstevel@tonic-gate parentdir = -1;
699392Sswilcox /*
700392Sswilcox * Fix up link counts.
701392Sswilcox *
702392Sswilcox * XXX This section is getting pretty byzantine, espcially
703392Sswilcox * when combined with changeino()/chgino()'s link manipulation.
704392Sswilcox */
705392Sswilcox LFDIR_LINK_RANGE_RVAL(flow_msg, lncntp[lfdir], 1, &idesc, 0);
706392Sswilcox TRACK_LNCNTP(lfdir, lncntp[lfdir]--);
707392Sswilcox pwarn("DIR I=%lu CONNECTED. ", (long)orphan);
708392Sswilcox reattached_dir = 1;
709392Sswilcox if (parentdir != (fsck_ino_t)-1) {
710392Sswilcox /*
711392Sswilcox * Have to clear the parent's reference. Otherwise,
712392Sswilcox * if it's an orphan, then we may clear this orphan
713392Sswilcox * in pass 4 even though we've reconnected it.
714392Sswilcox *
715392Sswilcox * We already have the reference count
716392Sswilcox * allowing for a parent link, so undo the
717392Sswilcox * adjustment done above. Otherwise we come
718392Sswilcox * out high by one.
719392Sswilcox */
720392Sswilcox (void) printf("PARENT WAS I=%lu\n", (long)parentdir);
721392Sswilcox (void) cleardirentry(parentdir, orphan);
7220Sstevel@tonic-gate }
723392Sswilcox if (!preen)
724392Sswilcox (void) printf("\n");
725392Sswilcox } else if (preen) {
726392Sswilcox (void) printf(" (RECONNECTED)\n");
7270Sstevel@tonic-gate }
728392Sswilcox
729392Sswilcox statemap[orphan] &= ~INDELAYD;
7300Sstevel@tonic-gate return (1);
731392Sswilcox
732392Sswilcox /*
733392Sswilcox * Leaving things unconnected is harmless as far as trying to
734392Sswilcox * use the filesystem later, so don't set iscorrupt yet (it's
735392Sswilcox * just lost blocks and inodes, after all).
736392Sswilcox *
737392Sswilcox * Lost directories get noted for reporting after all checks
738392Sswilcox * are done - they may get cleared later.
739392Sswilcox */
740392Sswilcox noconnect:
741392Sswilcox if (lostdir) {
742392Sswilcox intree = tsearch((void *)orphan, &limbo_dirs,
743392Sswilcox ino_t_cmp);
744392Sswilcox if (intree == NULL)
745392Sswilcox errexit("linkup: out of memory");
746392Sswilcox }
747392Sswilcox return (0);
7480Sstevel@tonic-gate }
7490Sstevel@tonic-gate
7500Sstevel@tonic-gate /*
7510Sstevel@tonic-gate * fix an entry in a directory.
7520Sstevel@tonic-gate */
753392Sswilcox int
changeino(fsck_ino_t dir,char * name,fsck_ino_t newnum)754392Sswilcox changeino(fsck_ino_t dir, char *name, fsck_ino_t newnum)
7550Sstevel@tonic-gate {
7560Sstevel@tonic-gate struct inodesc idesc;
7570Sstevel@tonic-gate
758392Sswilcox init_inodesc(&idesc);
7590Sstevel@tonic-gate idesc.id_type = DATA;
7600Sstevel@tonic-gate idesc.id_func = chgino;
7610Sstevel@tonic-gate idesc.id_number = dir;
7620Sstevel@tonic-gate idesc.id_fix = DONTKNOW;
7630Sstevel@tonic-gate idesc.id_name = name;
7640Sstevel@tonic-gate idesc.id_parent = newnum; /* new value for name */
765392Sswilcox return (ckinode(ginode(dir), &idesc, CKI_TRAVERSE));
7660Sstevel@tonic-gate }
7670Sstevel@tonic-gate
7680Sstevel@tonic-gate /*
7690Sstevel@tonic-gate * make an entry in a directory
7700Sstevel@tonic-gate */
771392Sswilcox int
makeentry(fsck_ino_t parent,fsck_ino_t ino,char * name)772392Sswilcox makeentry(fsck_ino_t parent, fsck_ino_t ino, char *name)
7730Sstevel@tonic-gate {
774392Sswilcox int repeat;
7750Sstevel@tonic-gate struct dinode *dp;
776392Sswilcox struct inoinfo *iip;
7770Sstevel@tonic-gate struct inodesc idesc;
7780Sstevel@tonic-gate char pathbuf[MAXPATHLEN + 1];
7790Sstevel@tonic-gate
7800Sstevel@tonic-gate if (parent < UFSROOTINO || parent >= maxino ||
7810Sstevel@tonic-gate ino < UFSROOTINO || ino >= maxino)
7820Sstevel@tonic-gate return (0);
783392Sswilcox init_inodesc(&idesc);
7840Sstevel@tonic-gate idesc.id_type = DATA;
7850Sstevel@tonic-gate idesc.id_func = mkentry;
7860Sstevel@tonic-gate idesc.id_number = parent;
7870Sstevel@tonic-gate idesc.id_parent = ino; /* this is the inode to enter */
7880Sstevel@tonic-gate idesc.id_fix = DONTKNOW;
7890Sstevel@tonic-gate idesc.id_name = name;
790392Sswilcox
791392Sswilcox repeat = 0;
792392Sswilcox again:
7930Sstevel@tonic-gate dp = ginode(parent);
794392Sswilcox if ((dp->di_size % DIRBLKSIZ) != 0) {
7950Sstevel@tonic-gate dp->di_size = roundup(dp->di_size, DIRBLKSIZ);
7960Sstevel@tonic-gate inodirty();
797392Sswilcox
798392Sswilcox iip = getinoinfo(ino);
799392Sswilcox if (iip != NULL)
800392Sswilcox iip->i_isize = dp->di_size;
8010Sstevel@tonic-gate }
802392Sswilcox
803392Sswilcox if ((ckinode(dp, &idesc, CKI_TRAVERSE) & ALTERED) != 0) {
804392Sswilcox iip = getinoinfo(ino);
805392Sswilcox if (iip != NULL)
806392Sswilcox iip->i_isize = dp->di_size;
807392Sswilcox
8080Sstevel@tonic-gate return (1);
809392Sswilcox }
810392Sswilcox
811392Sswilcox if (repeat == 0) {
812392Sswilcox getpathname(pathbuf, parent, parent);
813392Sswilcox if (expanddir(parent, pathbuf) == 0)
814392Sswilcox return (0);
815392Sswilcox
816392Sswilcox repeat = 1;
817392Sswilcox goto again;
818392Sswilcox }
819392Sswilcox
820392Sswilcox return (0);
8210Sstevel@tonic-gate }
8220Sstevel@tonic-gate
8230Sstevel@tonic-gate /*
8240Sstevel@tonic-gate * Attempt to expand the size of a directory
8250Sstevel@tonic-gate */
826392Sswilcox static int
expanddir(fsck_ino_t ino,char * name)827392Sswilcox expanddir(fsck_ino_t ino, char *name)
8280Sstevel@tonic-gate {
8290Sstevel@tonic-gate struct bufarea *bpback, *bp[2];
8300Sstevel@tonic-gate daddr32_t nxtibn, nxtbn;
8310Sstevel@tonic-gate daddr32_t newblk[2];
832392Sswilcox struct dinode *dp;
8330Sstevel@tonic-gate char *cp;
834392Sswilcox int bc, f;
835392Sswilcox int n;
8360Sstevel@tonic-gate int allocIndir;
837392Sswilcox int frag2blks;
8380Sstevel@tonic-gate int lffragsz = 0;
8390Sstevel@tonic-gate int c = 0;
840392Sswilcox int retval = 0;
8410Sstevel@tonic-gate
842392Sswilcox bp[0] = bp[1] = NULL;
843392Sswilcox
844392Sswilcox dp = ginode(ino);
845392Sswilcox if (dp->di_size == 0) {
846392Sswilcox goto bail;
847392Sswilcox }
8480Sstevel@tonic-gate
8490Sstevel@tonic-gate nxtbn = lblkno(&sblock, dp->di_size - 1) + 1;
8500Sstevel@tonic-gate
8510Sstevel@tonic-gate /*
852392Sswilcox * Check that none of the nominally in-use direct block
853392Sswilcox * addresses for the directory are bogus.
8540Sstevel@tonic-gate */
8550Sstevel@tonic-gate for (bc = 0; ((nxtbn > 0) && (bc < nxtbn) && (bc < NDADDR)); bc++) {
856392Sswilcox if (dp->di_db[bc] == 0) {
857392Sswilcox goto bail;
858392Sswilcox }
8590Sstevel@tonic-gate }
8600Sstevel@tonic-gate
8610Sstevel@tonic-gate /*
8620Sstevel@tonic-gate * Determine our data block allocation needs. We always need to
8630Sstevel@tonic-gate * allocate at least one data block. We may need a second, the
8640Sstevel@tonic-gate * indirect block itself.
8650Sstevel@tonic-gate */
8660Sstevel@tonic-gate allocIndir = 0;
8670Sstevel@tonic-gate nxtibn = -1;
868392Sswilcox n = 0;
8690Sstevel@tonic-gate
8700Sstevel@tonic-gate if (nxtbn <= NDADDR) {
8710Sstevel@tonic-gate /*
8720Sstevel@tonic-gate * Still in direct blocks. Check for the unlikely
8730Sstevel@tonic-gate * case where the last block is a frag rather than
8740Sstevel@tonic-gate * a full block. This would only happen if someone had
8750Sstevel@tonic-gate * created a file in lost+found, and then that caused
8760Sstevel@tonic-gate * the dynamic directory shrinking capabilities of ufs
8770Sstevel@tonic-gate * to kick in.
878392Sswilcox *
879392Sswilcox * Note that we test nxtbn <= NDADDR, as it's the
880392Sswilcox * next block (i.e., one greater than the current/
881392Sswilcox * actual block being examined).
8820Sstevel@tonic-gate */
8830Sstevel@tonic-gate lffragsz = dp->di_size % sblock.fs_bsize;
8840Sstevel@tonic-gate }
8850Sstevel@tonic-gate
8860Sstevel@tonic-gate if (nxtbn >= NDADDR && !lffragsz) {
887392Sswilcox n = sblock.fs_bsize / sizeof (daddr32_t);
8880Sstevel@tonic-gate nxtibn = nxtbn - NDADDR;
8890Sstevel@tonic-gate /*
8900Sstevel@tonic-gate * Only go one level of indirection
8910Sstevel@tonic-gate */
892392Sswilcox if (nxtibn >= n) {
893392Sswilcox goto bail;
894392Sswilcox }
895392Sswilcox /*
896392Sswilcox * First indirect block means we need to pick up
897392Sswilcox * the actual indirect pointer block as well.
898392Sswilcox */
8990Sstevel@tonic-gate if (nxtibn == 0)
9000Sstevel@tonic-gate allocIndir++;
9010Sstevel@tonic-gate }
9020Sstevel@tonic-gate
9030Sstevel@tonic-gate /*
9040Sstevel@tonic-gate * Allocate all the new blocks we need.
9050Sstevel@tonic-gate */
906392Sswilcox if ((newblk[0] = allocblk(sblock.fs_frag)) == 0) {
9070Sstevel@tonic-gate goto bail;
908392Sswilcox }
9090Sstevel@tonic-gate c++;
9100Sstevel@tonic-gate if (allocIndir) {
911392Sswilcox if ((newblk[1] = allocblk(sblock.fs_frag)) == 0) {
9120Sstevel@tonic-gate goto bail;
913392Sswilcox }
9140Sstevel@tonic-gate c++;
9150Sstevel@tonic-gate }
9160Sstevel@tonic-gate
9170Sstevel@tonic-gate /*
9180Sstevel@tonic-gate * Take care of the block that will hold new directory entries.
9190Sstevel@tonic-gate * This one is always allocated.
9200Sstevel@tonic-gate */
921392Sswilcox bp[0] = getdirblk(newblk[0], (size_t)sblock.fs_bsize);
922392Sswilcox if (bp[0]->b_errs) {
9230Sstevel@tonic-gate goto bail;
924392Sswilcox }
925392Sswilcox
9260Sstevel@tonic-gate if (lffragsz) {
927392Sswilcox /*
928392Sswilcox * Preserve the partially-populated existing directory.
929392Sswilcox */
9300Sstevel@tonic-gate bpback = getdirblk(dp->di_db[nxtbn - 1],
931392Sswilcox (size_t)dblksize(&sblock, dp, nxtbn - 1));
932392Sswilcox if (!bpback->b_errs) {
933392Sswilcox (void) memmove(bp[0]->b_un.b_buf, bpback->b_un.b_buf,
934392Sswilcox (size_t)lffragsz);
935392Sswilcox }
936392Sswilcox }
937392Sswilcox
938392Sswilcox /*
939392Sswilcox * Initialize the new fragments. lffragsz is zero if this
940392Sswilcox * is a completely-new block.
941392Sswilcox */
942392Sswilcox for (cp = &(bp[0]->b_un.b_buf[lffragsz]);
943392Sswilcox cp < &(bp[0]->b_un.b_buf[sblock.fs_bsize]);
944392Sswilcox cp += DIRBLKSIZ) {
945392Sswilcox (void) memmove(cp, (char *)&emptydir,
946392Sswilcox sizeof (emptydir));
9470Sstevel@tonic-gate }
9480Sstevel@tonic-gate dirty(bp[0]);
9490Sstevel@tonic-gate
9500Sstevel@tonic-gate /*
9510Sstevel@tonic-gate * If we allocated the indirect block, zero it out. Otherwise
9520Sstevel@tonic-gate * read it in if we're using one.
9530Sstevel@tonic-gate */
9540Sstevel@tonic-gate if (allocIndir) {
955392Sswilcox bp[1] = getdatablk(newblk[1], (size_t)sblock.fs_bsize);
956392Sswilcox if (bp[1]->b_errs) {
9570Sstevel@tonic-gate goto bail;
958392Sswilcox }
959392Sswilcox (void) memset(bp[1]->b_un.b_buf, 0, sblock.fs_bsize);
9600Sstevel@tonic-gate dirty(bp[1]);
9610Sstevel@tonic-gate } else if (nxtibn >= 0) {
9620Sstevel@tonic-gate /* Check that the indirect block pointer looks okay */
963392Sswilcox if (dp->di_ib[0] == 0) {
9640Sstevel@tonic-gate goto bail;
965392Sswilcox }
966392Sswilcox bp[1] = getdatablk(dp->di_ib[0], (size_t)sblock.fs_bsize);
967392Sswilcox if (bp[1]->b_errs) {
9680Sstevel@tonic-gate goto bail;
969392Sswilcox }
9700Sstevel@tonic-gate
9710Sstevel@tonic-gate for (bc = 0; ((bc < nxtibn) && (bc < n)); bc++) {
972392Sswilcox /* LINTED pointer cast alignment */
973392Sswilcox if (((daddr32_t *)bp[1]->b_un.b_buf)[bc] == 0) {
9740Sstevel@tonic-gate goto bail;
975392Sswilcox }
9760Sstevel@tonic-gate }
9770Sstevel@tonic-gate }
9780Sstevel@tonic-gate
979392Sswilcox /*
980392Sswilcox * Since the filesystem's consistency isn't affected by
981392Sswilcox * whether or not we actually do the expansion, iscorrupt
982392Sswilcox * is left alone for any of the approval paths.
983392Sswilcox */
9840Sstevel@tonic-gate pwarn("NO SPACE LEFT IN %s", name);
985392Sswilcox if (!preen && (reply("EXPAND") == 0))
9860Sstevel@tonic-gate goto bail;
9870Sstevel@tonic-gate
9880Sstevel@tonic-gate /*
989392Sswilcox * Now that everything we need is gathered up and the
990392Sswilcox * necessary approvals acquired, we can make our provisional
991392Sswilcox * changes permanent.
9920Sstevel@tonic-gate */
9930Sstevel@tonic-gate
9940Sstevel@tonic-gate if (lffragsz) {
995392Sswilcox /*
996392Sswilcox * We've saved the data from the old end fragment(s) in
997392Sswilcox * our new block, so we can just swap the new one in.
998392Sswilcox * Make sure the size reflects the expansion of the
999392Sswilcox * final fragments/block.
1000392Sswilcox */
10010Sstevel@tonic-gate frag2blks = roundup(lffragsz, sblock.fs_fsize);
1002392Sswilcox freeblk(ino, dp->di_db[nxtbn - 1],
10030Sstevel@tonic-gate frag2blks / sblock.fs_fsize);
10040Sstevel@tonic-gate frag2blks = btodb(frag2blks);
10050Sstevel@tonic-gate dp->di_size -= (u_offset_t)lffragsz;
10060Sstevel@tonic-gate dp->di_blocks = dp->di_blocks - frag2blks;
10070Sstevel@tonic-gate dp->di_db[nxtbn - 1] = newblk[0];
10080Sstevel@tonic-gate dp->di_size += (u_offset_t)sblock.fs_bsize;
10090Sstevel@tonic-gate dp->di_blocks += btodb(sblock.fs_bsize);
10100Sstevel@tonic-gate inodirty();
1011392Sswilcox retval = 1;
1012392Sswilcox goto done;
10130Sstevel@tonic-gate }
10140Sstevel@tonic-gate
1015392Sswilcox /*
1016392Sswilcox * Full-block addition's much easier. It's just an append.
1017392Sswilcox */
10180Sstevel@tonic-gate dp->di_size += (u_offset_t)sblock.fs_bsize;
10190Sstevel@tonic-gate dp->di_blocks += btodb(sblock.fs_bsize);
10200Sstevel@tonic-gate if (allocIndir) {
10210Sstevel@tonic-gate dp->di_blocks += btodb(sblock.fs_bsize);
10220Sstevel@tonic-gate }
10230Sstevel@tonic-gate
10240Sstevel@tonic-gate inodirty();
10250Sstevel@tonic-gate if (nxtibn < 0) {
10260Sstevel@tonic-gate /*
10270Sstevel@tonic-gate * Still in direct blocks
10280Sstevel@tonic-gate */
10290Sstevel@tonic-gate dp->di_db[nxtbn] = newblk[0];
10300Sstevel@tonic-gate } else {
10310Sstevel@tonic-gate /*
10320Sstevel@tonic-gate * Last indirect is always going to point at the
10330Sstevel@tonic-gate * new directory buffer
10340Sstevel@tonic-gate */
10350Sstevel@tonic-gate if (allocIndir)
10360Sstevel@tonic-gate dp->di_ib[0] = newblk[1];
1037392Sswilcox /* LINTED pointer case alignment */
10380Sstevel@tonic-gate ((daddr32_t *)bp[1]->b_un.b_buf)[nxtibn] = newblk[0];
10390Sstevel@tonic-gate dirty(bp[1]);
10400Sstevel@tonic-gate }
1041392Sswilcox
1042392Sswilcox if (preen)
1043392Sswilcox (void) printf(" (EXPANDED)\n");
1044392Sswilcox
1045392Sswilcox retval = 1;
1046392Sswilcox goto done;
10470Sstevel@tonic-gate
10480Sstevel@tonic-gate bail:
10490Sstevel@tonic-gate for (f = 0; f < c; f++)
1050392Sswilcox freeblk(ino, newblk[f], sblock.fs_frag);
1051392Sswilcox done:
1052392Sswilcox /*
1053392Sswilcox * bp[0] is handled by the directory cache's auto-release.
1054392Sswilcox */
1055392Sswilcox if (bp[1] != NULL)
1056392Sswilcox brelse(bp[1]);
1057392Sswilcox
1058392Sswilcox return (retval);
1059392Sswilcox }
1060392Sswilcox
1061392Sswilcox static fsck_ino_t
newdir(fsck_ino_t parent,fsck_ino_t request,int mode,caddr_t name)1062392Sswilcox newdir(fsck_ino_t parent, fsck_ino_t request, int mode, caddr_t name)
1063392Sswilcox {
1064392Sswilcox fsck_ino_t dino;
1065392Sswilcox char pname[BUFSIZ];
1066392Sswilcox
1067392Sswilcox /*
1068392Sswilcox * This function creates a new directory and populates it with
1069392Sswilcox * "." and "..", then links to it as NAME in PARENT.
1070392Sswilcox */
1071392Sswilcox dino = allocdir(parent, request, mode, 1);
1072392Sswilcox if (dino != 0) {
1073392Sswilcox getpathname(pname, parent, parent);
1074392Sswilcox name = mkuniqname(name, pname, parent, dino);
1075392Sswilcox /*
1076392Sswilcox * We don't touch numdirs, because it's just a cache of
1077392Sswilcox * what the filesystem claimed originally and is used
1078392Sswilcox * to calculate hash keys.
1079392Sswilcox */
1080392Sswilcox if (makeentry(parent, dino, name) == 0) {
1081392Sswilcox freedir(dino, parent);
1082392Sswilcox dino = 0;
1083392Sswilcox }
1084392Sswilcox }
1085392Sswilcox
1086392Sswilcox return (dino);
1087392Sswilcox }
1088392Sswilcox
1089392Sswilcox /*
1090392Sswilcox * Replace whatever NAME refers to in PARENT with a new directory.
1091392Sswilcox * Note that if the old inode REQUEST is a directory, all of its
1092392Sswilcox * contents will be freed and reaped.
1093392Sswilcox */
1094392Sswilcox static fsck_ino_t
reallocdir(fsck_ino_t parent,fsck_ino_t request,int mode,caddr_t name)1095392Sswilcox reallocdir(fsck_ino_t parent, fsck_ino_t request, int mode, caddr_t name)
1096392Sswilcox {
1097392Sswilcox int retval;
1098392Sswilcox fsck_ino_t newino;
1099392Sswilcox
1100392Sswilcox if ((request != 0) && (statemap[request] != USTATE))
1101392Sswilcox freeino(request, TI_PARENT);
1102392Sswilcox
1103392Sswilcox newino = allocdir(parent, request, mode, 0);
1104392Sswilcox if (newino != 0) {
1105392Sswilcox retval = changeino(parent, name, newino);
1106392Sswilcox if ((retval & ALTERED) == 0) {
1107392Sswilcox /*
1108392Sswilcox * No change made, so name doesn't exist, so
1109392Sswilcox * unwind allocation rather than leak it.
1110392Sswilcox */
1111392Sswilcox freedir(newino, parent);
1112392Sswilcox newino = 0;
1113392Sswilcox }
1114392Sswilcox }
1115392Sswilcox
1116392Sswilcox return (newino);
11170Sstevel@tonic-gate }
11180Sstevel@tonic-gate
11190Sstevel@tonic-gate /*
11200Sstevel@tonic-gate * allocate a new directory
11210Sstevel@tonic-gate */
1122392Sswilcox fsck_ino_t
allocdir(fsck_ino_t parent,fsck_ino_t request,int mode,int update_parent)1123392Sswilcox allocdir(fsck_ino_t parent, fsck_ino_t request, int mode, int update_parent)
11240Sstevel@tonic-gate {
1125392Sswilcox fsck_ino_t ino;
1126392Sswilcox caddr_t cp;
1127392Sswilcox caddr_t flow;
11280Sstevel@tonic-gate struct dinode *dp;
1129392Sswilcox struct bufarea *bp;
11300Sstevel@tonic-gate struct inoinfo *inp;
1131392Sswilcox struct inodesc idesc;
1132392Sswilcox struct dirtemplate *dirp;
11330Sstevel@tonic-gate
11340Sstevel@tonic-gate ino = allocino(request, IFDIR|mode);
11350Sstevel@tonic-gate if (ino == 0)
11360Sstevel@tonic-gate return (0);
1137392Sswilcox dirp = &dirhead;
1138392Sswilcox dirp->dot_ino = ino;
1139392Sswilcox dirp->dotdot_ino = parent;
11400Sstevel@tonic-gate dp = ginode(ino);
1141392Sswilcox bp = getdirblk(dp->di_db[0], (size_t)sblock.fs_fsize);
11420Sstevel@tonic-gate if (bp->b_errs) {
1143392Sswilcox freeino(ino, TI_PARENT);
11440Sstevel@tonic-gate return (0);
11450Sstevel@tonic-gate }
1146392Sswilcox (void) memmove(bp->b_un.b_buf, (void *)dirp,
1147392Sswilcox sizeof (struct dirtemplate));
11480Sstevel@tonic-gate for (cp = &bp->b_un.b_buf[DIRBLKSIZ];
11490Sstevel@tonic-gate cp < &bp->b_un.b_buf[sblock.fs_fsize];
11500Sstevel@tonic-gate cp += DIRBLKSIZ)
1151392Sswilcox (void) memmove(cp, (void *)&emptydir, sizeof (emptydir));
11520Sstevel@tonic-gate dirty(bp);
11530Sstevel@tonic-gate dp->di_nlink = 2;
11540Sstevel@tonic-gate inodirty();
11550Sstevel@tonic-gate if (!inocached(ino)) {
11560Sstevel@tonic-gate cacheino(dp, ino);
11570Sstevel@tonic-gate } else {
11580Sstevel@tonic-gate /*
11590Sstevel@tonic-gate * re-using an old directory inode
11600Sstevel@tonic-gate */
11610Sstevel@tonic-gate inp = getinoinfo(ino);
1162392Sswilcox if (inp == NULL) {
1163392Sswilcox if (debug)
1164392Sswilcox errexit("allocdir got NULL from getinoinfo "
1165392Sswilcox "for existing entry I=%d\n",
1166392Sswilcox ino);
1167392Sswilcox cacheino(dp, ino);
1168392Sswilcox } else {
1169392Sswilcox init_inoinfo(inp, dp, ino);
1170392Sswilcox inp->i_parent = parent;
1171392Sswilcox inp->i_dotdot = parent;
1172392Sswilcox }
11730Sstevel@tonic-gate }
1174392Sswilcox
1175392Sswilcox /*
1176392Sswilcox * Short-circuit all the dancing around below if it's the
1177392Sswilcox * root inode. The net effect's the same.
1178392Sswilcox */
11790Sstevel@tonic-gate if (ino == UFSROOTINO) {
1180392Sswilcox TRACK_LNCNTP(ino, lncntp[ino] = dp->di_nlink);
11810Sstevel@tonic-gate return (ino);
11820Sstevel@tonic-gate }
1183392Sswilcox
1184392Sswilcox if (!update_parent)
1185392Sswilcox return (ino);
1186392Sswilcox
1187392Sswilcox /*
1188392Sswilcox * We never create attribute directories, which can have
1189392Sswilcox * non-directory parents. So, the parent of the directory
1190392Sswilcox * we're creating must itself be a directory.
1191392Sswilcox */
1192392Sswilcox if (!INO_IS_DVALID(parent)) {
1193392Sswilcox freeino(ino, TI_PARENT);
11940Sstevel@tonic-gate return (0);
11950Sstevel@tonic-gate }
1196392Sswilcox
1197392Sswilcox /*
1198392Sswilcox * Make sure the parent can handle another link.
1199392Sswilcox * Since we might only update one version of the
1200392Sswilcox * count (disk versus in-memory), we have to check both.
1201392Sswilcox */
1202392Sswilcox LINK_RANGE(flow, lncntp[parent], -1);
1203392Sswilcox if (flow == NULL)
1204392Sswilcox LINK_RANGE(flow, (int)dp->di_nlink, 1);
1205392Sswilcox
1206392Sswilcox if (flow != NULL) {
1207392Sswilcox LINK_CLEAR(flow, parent, dp->di_mode, &idesc);
1208392Sswilcox if (statemap[parent] == USTATE) {
1209392Sswilcox /*
1210392Sswilcox * No parent any more, so bail out. Callers
1211392Sswilcox * are expected to handle this possibility.
1212392Sswilcox * Since most just throw up their hands if
1213392Sswilcox * we return 0, this just happens to work.
1214392Sswilcox */
1215392Sswilcox freeino(ino, TI_PARENT);
1216392Sswilcox return (0);
1217392Sswilcox }
1218392Sswilcox }
1219392Sswilcox
1220392Sswilcox /*
1221392Sswilcox * We've created a directory with two entries, "." and "..",
1222392Sswilcox * and a link count of two ("." and one from its parent). If
1223392Sswilcox * the parent's not been scanned yet, which means this inode
1224392Sswilcox * will get scanned later as well, then make our in-core count
1225392Sswilcox * match what we pushed out to disk. Similarly, update the
1226392Sswilcox * parent. On the other hand, if the parent's already been
1227392Sswilcox * looked at (statemap[ino] == DFOUND), the discrepancy
1228392Sswilcox * between lncntp[] and di_nlink will be noted later, with
1229*504Sswilcox * appropriate reporting and propagation, in pass2.
1230392Sswilcox *
1231392Sswilcox * We're explicitly skipping where the parent was DZLINK or
1232392Sswilcox * DFOUND. If it has zero links, it can't be gotten to, so
1233*504Sswilcox * we want a discrepancy set up that will be caught in pass2.
1234392Sswilcox * DFOUND was discussed above.
1235392Sswilcox *
1236392Sswilcox * Regarding the claim of a link from the parent: we've not
1237392Sswilcox * done anything to create such a link here. We depend on the
1238392Sswilcox * semantics of our callers attaching the inode we return to
1239392Sswilcox * an existing entry in the directory or creating the entry
1240392Sswilcox * themselves, but in either case, not modifying the link
1241392Sswilcox * count.
1242392Sswilcox *
1243392Sswilcox * Note that setting lncntp[ino] to zero means that both claimed
1244392Sswilcox * links have been ``found''.
1245392Sswilcox */
12460Sstevel@tonic-gate statemap[ino] = statemap[parent];
1247392Sswilcox if (INO_IS_DVALID(parent)) {
1248392Sswilcox TRACK_LNCNTP(ino, lncntp[ino] = 0);
1249392Sswilcox TRACK_LNCNTP(parent, lncntp[parent]--);
12500Sstevel@tonic-gate }
12510Sstevel@tonic-gate dp = ginode(parent);
12520Sstevel@tonic-gate dp->di_nlink++;
12530Sstevel@tonic-gate inodirty();
12540Sstevel@tonic-gate return (ino);
12550Sstevel@tonic-gate }
12560Sstevel@tonic-gate
12570Sstevel@tonic-gate /*
12580Sstevel@tonic-gate * free a directory inode
12590Sstevel@tonic-gate */
1260392Sswilcox static void
freedir(fsck_ino_t ino,fsck_ino_t parent)1261392Sswilcox freedir(fsck_ino_t ino, fsck_ino_t parent)
12620Sstevel@tonic-gate {
1263392Sswilcox struct inoinfo *iip;
12640Sstevel@tonic-gate
12650Sstevel@tonic-gate if (ino != parent) {
1266392Sswilcox /*
1267392Sswilcox * Make sure that the desired parent gets a link
1268392Sswilcox * count update from freeino()/truncino(). If
1269392Sswilcox * we can't look it up, then it's not really a
1270392Sswilcox * directory, so there's nothing to worry about.
1271392Sswilcox */
1272392Sswilcox iip = getinoinfo(ino);
1273392Sswilcox if (iip != NULL)
1274392Sswilcox iip->i_parent = parent;
12750Sstevel@tonic-gate }
1276392Sswilcox freeino(ino, TI_PARENT);
12770Sstevel@tonic-gate }
12780Sstevel@tonic-gate
12790Sstevel@tonic-gate /*
1280392Sswilcox * generate a temporary name for use in the lost+found directory.
12810Sstevel@tonic-gate */
1282392Sswilcox static void
lftempname(char * bufp,fsck_ino_t ino)1283392Sswilcox lftempname(char *bufp, fsck_ino_t ino)
12840Sstevel@tonic-gate {
1285392Sswilcox fsck_ino_t in;
1286392Sswilcox caddr_t cp;
12870Sstevel@tonic-gate int namlen;
12880Sstevel@tonic-gate
12890Sstevel@tonic-gate cp = bufp + 2;
12900Sstevel@tonic-gate for (in = maxino; in > 0; in /= 10)
12910Sstevel@tonic-gate cp++;
1292392Sswilcox *--cp = '\0';
1293392Sswilcox /* LINTED difference will not overflow an int */
12940Sstevel@tonic-gate namlen = cp - bufp;
1295392Sswilcox if ((namlen > BUFSIZ) || (namlen > MAXPATHLEN)) {
1296392Sswilcox errexit("buffer overflow in lftempname()\n");
1297392Sswilcox }
1298392Sswilcox
12990Sstevel@tonic-gate in = ino;
13000Sstevel@tonic-gate while (cp > bufp) {
13010Sstevel@tonic-gate *--cp = (in % 10) + '0';
13020Sstevel@tonic-gate in /= 10;
13030Sstevel@tonic-gate }
13040Sstevel@tonic-gate *cp = '#';
13050Sstevel@tonic-gate }
13060Sstevel@tonic-gate
13070Sstevel@tonic-gate /*
13080Sstevel@tonic-gate * Get a directory block.
13090Sstevel@tonic-gate * Insure that it is held until another is requested.
1310392Sswilcox *
1311392Sswilcox * Our callers are expected to check for errors and/or be
1312392Sswilcox * prepared to handle blocks of zeros in the middle of a
1313392Sswilcox * directory.
13140Sstevel@tonic-gate */
1315392Sswilcox static struct bufarea *
getdirblk(daddr32_t blkno,size_t size)1316392Sswilcox getdirblk(daddr32_t blkno, size_t size)
13170Sstevel@tonic-gate {
13180Sstevel@tonic-gate if (pdirbp != 0) {
13190Sstevel@tonic-gate brelse(pdirbp);
13200Sstevel@tonic-gate }
13210Sstevel@tonic-gate pdirbp = getdatablk(blkno, size);
13220Sstevel@tonic-gate return (pdirbp);
13230Sstevel@tonic-gate }
1324392Sswilcox
1325392Sswilcox /*
1326392Sswilcox * Create a unique name for INODE to be created in directory PARENT.
1327392Sswilcox * Use NAME if it is provided (non-NULL) and doesn't already exist.
1328392Sswilcox * Returning NULL indicates no unique name could be generated.
1329392Sswilcox *
1330392Sswilcox * If we were given a name, and it conflicts with an existing
1331392Sswilcox * entry, use our usual temp name instead. Without this check,
1332392Sswilcox * we could end up creating duplicate entries for multiple
1333392Sswilcox * orphaned directories in lost+found with the same name (but
1334392Sswilcox * different parents). Of course, our usual name might already
1335392Sswilcox * be in use as well, so be paranoid.
1336392Sswilcox *
1337392Sswilcox * We could do something like keep tacking something onto the
1338392Sswilcox * end of tempname until we come up with something that's not
1339392Sswilcox * in use, but that has liabilities as well. This is a
1340392Sswilcox * sufficiently rare case that it's not worth going that
1341392Sswilcox * overboard for.
1342392Sswilcox */
1343392Sswilcox static caddr_t
mkuniqname(caddr_t name,caddr_t pname,fsck_ino_t parent,fsck_ino_t inode)1344392Sswilcox mkuniqname(caddr_t name, caddr_t pname, fsck_ino_t parent, fsck_ino_t inode)
1345392Sswilcox {
1346392Sswilcox fsck_ino_t oldino;
1347392Sswilcox struct dinode *dp;
1348392Sswilcox caddr_t flow_msg;
1349392Sswilcox struct inodesc idesc;
1350392Sswilcox static char tempname[BUFSIZ];
1351392Sswilcox
1352392Sswilcox lftempname(tempname, inode);
1353392Sswilcox if ((name != NULL) &&
1354392Sswilcox (lookup_named_ino(parent, name) != 0)) {
1355392Sswilcox name = NULL;
1356392Sswilcox }
1357392Sswilcox if (name == NULL) {
1358392Sswilcox /*
1359392Sswilcox * No name given, or it wasn't unique.
1360392Sswilcox */
1361392Sswilcox name = tempname;
1362392Sswilcox if ((oldino = lookup_named_ino(parent, name)) != 0) {
1363392Sswilcox pfatal(
1364392Sswilcox "Name ``%s'' for inode %d already exists in %s \n",
1365392Sswilcox name, oldino, pname);
1366392Sswilcox if (reply("REMOVE OLD ENTRY") == 0) {
1367392Sswilcox if (parent == lfdir)
1368392Sswilcox pwarn(
1369392Sswilcox "Could not reconnect inode %d\n\n",
1370392Sswilcox inode);
1371392Sswilcox else
1372392Sswilcox pwarn(
1373392Sswilcox "Could not create entry for %d\n\n",
1374392Sswilcox inode);
1375392Sswilcox name = NULL;
1376392Sswilcox goto noconnect;
1377392Sswilcox }
1378392Sswilcox (void) changeino(parent, name, inode);
1379392Sswilcox LINK_RANGE(flow_msg, lncntp[oldino], 1);
1380392Sswilcox if (flow_msg != NULL) {
1381392Sswilcox /*
1382392Sswilcox * Do a best-effort, but if we're not
1383392Sswilcox * allowed to do the clear, the fs is
1384392Sswilcox * corrupt in any case, so just carry on.
1385392Sswilcox */
1386392Sswilcox dp = ginode(oldino);
1387392Sswilcox LINK_CLEAR(flow_msg, oldino, dp->di_mode,
1388392Sswilcox &idesc);
1389392Sswilcox if (statemap[oldino] != USTATE)
1390392Sswilcox iscorrupt = 1;
1391392Sswilcox } else {
1392392Sswilcox TRACK_LNCNTP(oldino, lncntp[oldino]++);
1393392Sswilcox }
1394392Sswilcox }
1395392Sswilcox }
1396392Sswilcox
1397392Sswilcox noconnect:
1398392Sswilcox return (name);
1399392Sswilcox }
1400