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
propagate(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
dirscan(struct inodesc * idesc)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 *
fsck_readdir(struct inodesc * idesc)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
dircheck(struct inodesc * idesc,struct direct * dp)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
adjust(struct inodesc * idesc,int lcnt)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
mkentry(struct inodesc * idesc)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
chgino(struct inodesc * idesc)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
linkup(fsck_ino_t orphan,fsck_ino_t parentdir,caddr_t name)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
do_reconnect(fsck_ino_t orphan,fsck_ino_t parentdir,caddr_t name)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
changeino(fsck_ino_t dir,char * name,fsck_ino_t newnum)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
makeentry(fsck_ino_t parent,fsck_ino_t ino,char * name)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
expanddir(fsck_ino_t ino,char * name)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
newdir(fsck_ino_t parent,fsck_ino_t request,int mode,caddr_t name)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
reallocdir(fsck_ino_t parent,fsck_ino_t request,int mode,caddr_t name)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
allocdir(fsck_ino_t parent,fsck_ino_t request,int mode,int update_parent)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
freedir(fsck_ino_t ino,fsck_ino_t parent)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
lftempname(char * bufp,fsck_ino_t ino)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 *
getdirblk(daddr32_t blkno,size_t size)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
mkuniqname(caddr_t name,caddr_t pname,fsck_ino_t parent,fsck_ino_t inode)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