pass2.c revision 1.24 1 /* $NetBSD: pass2.c,v 1.24 2015/06/16 23:18:55 christos Exp $ */
2
3 /*
4 * Copyright (c) 1980, 1986, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32 #include <sys/types.h>
33 #include <sys/param.h>
34 #include <sys/time.h>
35 #include <sys/mount.h>
36 #include <sys/buf.h>
37
38 #include <ufs/lfs/lfs.h>
39 #include <ufs/lfs/lfs_inode.h>
40
41 #include <err.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45
46 #include "bufcache.h"
47 #include "vnode.h"
48 #include "lfs_user.h"
49
50 #include "fsck.h"
51 #include "fsutil.h"
52 #include "extern.h"
53
54 #define MINDIRSIZE (sizeof (struct lfs_dirtemplate))
55
56 static int pass2check(struct inodesc *);
57 static int blksort(const void *, const void *);
58
59 void
60 pass2(void)
61 {
62 struct ulfs1_dinode *dp;
63 struct uvnode *vp;
64 struct inoinfo **inpp, *inp;
65 struct inoinfo **inpend;
66 struct inodesc curino;
67 struct ulfs1_dinode dino;
68 char pathbuf[MAXPATHLEN + 1];
69
70 switch (statemap[ULFS_ROOTINO]) {
71
72 case USTATE:
73 pfatal("ROOT INODE UNALLOCATED");
74 if (reply("ALLOCATE") == 0)
75 err(EEXIT, "%s", "");
76 if (allocdir(ULFS_ROOTINO, ULFS_ROOTINO, 0755) != ULFS_ROOTINO)
77 err(EEXIT, "CANNOT ALLOCATE ROOT INODE\n");
78 break;
79
80 case DCLEAR:
81 pfatal("DUPS/BAD IN ROOT INODE");
82 if (reply("REALLOCATE")) {
83 freeino(ULFS_ROOTINO);
84 if (allocdir(ULFS_ROOTINO, ULFS_ROOTINO, 0755) != ULFS_ROOTINO)
85 err(EEXIT, "CANNOT ALLOCATE ROOT INODE");
86 break;
87 }
88 if (reply("CONTINUE") == 0)
89 err(EEXIT, "%s", "");
90 break;
91
92 case FSTATE:
93 case FCLEAR:
94 pfatal("ROOT INODE NOT DIRECTORY");
95 if (reply("REALLOCATE")) {
96 freeino(ULFS_ROOTINO);
97 if (allocdir(ULFS_ROOTINO, ULFS_ROOTINO, 0755) != ULFS_ROOTINO)
98 err(EEXIT, "CANNOT ALLOCATE ROOT INODE");
99 break;
100 }
101 if (reply("FIX") == 0)
102 errx(EEXIT, "%s", "");
103 vp = vget(fs, ULFS_ROOTINO);
104 dp = VTOD(vp);
105 dp->di_mode &= ~LFS_IFMT;
106 dp->di_mode |= LFS_IFDIR;
107 inodirty(VTOI(vp));
108 break;
109
110 case DSTATE:
111 break;
112
113 default:
114 errx(EEXIT, "BAD STATE %d FOR ROOT INODE", statemap[ULFS_ROOTINO]);
115 }
116 statemap[ULFS_WINO] = FSTATE;
117 typemap[ULFS_WINO] = LFS_DT_WHT;
118 /*
119 * Sort the directory list into disk block order.
120 */
121 qsort((char *) inpsort, (size_t) inplast, sizeof *inpsort, blksort);
122 /*
123 * Check the integrity of each directory.
124 */
125 memset(&curino, 0, sizeof(struct inodesc));
126 curino.id_type = DATA;
127 curino.id_func = pass2check;
128 inpend = &inpsort[inplast];
129 for (inpp = inpsort; inpp < inpend; inpp++) {
130 inp = *inpp;
131 if (inp->i_isize == 0)
132 continue;
133 if (inp->i_isize < MINDIRSIZE) {
134 direrror(inp->i_number, "DIRECTORY TOO SHORT");
135 inp->i_isize = roundup(MINDIRSIZE, LFS_DIRBLKSIZ);
136 if (reply("FIX") == 1) {
137 vp = vget(fs, inp->i_number);
138 dp = VTOD(vp);
139 dp->di_size = inp->i_isize;
140 inodirty(VTOI(vp));
141 }
142 } else if ((inp->i_isize & (LFS_DIRBLKSIZ - 1)) != 0) {
143 getpathname(pathbuf, sizeof(pathbuf), inp->i_number,
144 inp->i_number);
145 pwarn("DIRECTORY %s: LENGTH %lu NOT MULTIPLE OF %d",
146 pathbuf, (unsigned long) inp->i_isize, LFS_DIRBLKSIZ);
147 if (preen)
148 printf(" (ADJUSTED)\n");
149 inp->i_isize = roundup(inp->i_isize, LFS_DIRBLKSIZ);
150 if (preen || reply("ADJUST") == 1) {
151 vp = vget(fs, inp->i_number);
152 dp = VTOD(vp);
153 dp->di_size = inp->i_isize;
154 inodirty(VTOI(vp));
155 }
156 }
157 memset(&dino, 0, sizeof(struct ulfs1_dinode));
158 dino.di_mode = LFS_IFDIR;
159 dino.di_size = inp->i_isize;
160 memcpy(&dino.di_db[0], &inp->i_blks[0], (size_t) inp->i_numblks);
161 curino.id_number = inp->i_number;
162 curino.id_parent = inp->i_parent;
163 (void) ckinode(&dino, &curino);
164 }
165 /*
166 * Now that the parents of all directories have been found,
167 * make another pass to verify the value of `..'
168 */
169 for (inpp = inpsort; inpp < inpend; inpp++) {
170 inp = *inpp;
171 if (inp->i_parent == 0 || inp->i_isize == 0)
172 continue;
173 if (inp->i_dotdot == inp->i_parent ||
174 inp->i_dotdot == (ino_t) - 1)
175 continue;
176 if (inp->i_dotdot == 0) {
177 inp->i_dotdot = inp->i_parent;
178 fileerror(inp->i_parent, inp->i_number, "MISSING '..'");
179 if (reply("FIX") == 0)
180 continue;
181 (void) makeentry(inp->i_number, inp->i_parent, "..");
182 lncntp[inp->i_parent]--;
183 continue;
184 }
185 fileerror(inp->i_parent, inp->i_number,
186 "BAD INODE NUMBER FOR '..'");
187 if (reply("FIX") == 0)
188 continue;
189 lncntp[inp->i_dotdot]++;
190 lncntp[inp->i_parent]--;
191 inp->i_dotdot = inp->i_parent;
192 (void) changeino(inp->i_number, "..", inp->i_parent);
193 }
194 /*
195 * Mark all the directories that can be found from the root.
196 */
197 propagate();
198 }
199
200 static int
201 pass2check(struct inodesc * idesc)
202 {
203 struct lfs_direct *dirp = idesc->id_dirp;
204 struct inoinfo *inp;
205 int n, entrysize, ret = 0;
206 struct ulfs1_dinode *dp;
207 const char *errmsg;
208 struct lfs_direct proto;
209 char namebuf[MAXPATHLEN + 1];
210 char pathbuf[MAXPATHLEN + 1];
211
212 /*
213 * check for "."
214 */
215 if (idesc->id_entryno != 0)
216 goto chk1;
217 if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) {
218 if (dirp->d_ino != idesc->id_number) {
219 direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'");
220 dirp->d_ino = idesc->id_number;
221 if (reply("FIX") == 1)
222 ret |= ALTERED;
223 }
224 if (dirp->d_type != LFS_DT_DIR) {
225 direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'");
226 dirp->d_type = LFS_DT_DIR;
227 if (reply("FIX") == 1)
228 ret |= ALTERED;
229 }
230 goto chk1;
231 }
232 direrror(idesc->id_number, "MISSING '.'");
233 proto.d_ino = idesc->id_number;
234 proto.d_type = LFS_DT_DIR;
235 proto.d_namlen = 1;
236 (void) strlcpy(proto.d_name, ".", sizeof(proto.d_name));
237 entrysize = LFS_DIRSIZ(0, &proto, 0);
238 if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) {
239 pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n",
240 dirp->d_name);
241 } else if (dirp->d_reclen < entrysize) {
242 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n");
243 } else if (dirp->d_reclen < 2 * entrysize) {
244 proto.d_reclen = dirp->d_reclen;
245 memcpy(dirp, &proto, (size_t) entrysize);
246 if (reply("FIX") == 1)
247 ret |= ALTERED;
248 } else {
249 n = dirp->d_reclen - entrysize;
250 proto.d_reclen = entrysize;
251 memcpy(dirp, &proto, (size_t) entrysize);
252 idesc->id_entryno++;
253 lncntp[dirp->d_ino]--;
254 dirp = (struct lfs_direct *) ((char *) (dirp) + entrysize);
255 memset(dirp, 0, (size_t) n);
256 dirp->d_reclen = n;
257 if (reply("FIX") == 1)
258 ret |= ALTERED;
259 }
260 chk1:
261 if (idesc->id_entryno > 1)
262 goto chk2;
263 inp = getinoinfo(idesc->id_number);
264 proto.d_ino = inp->i_parent;
265 proto.d_type = LFS_DT_DIR;
266 proto.d_namlen = 2;
267 (void) strlcpy(proto.d_name, "..", sizeof(proto.d_name));
268 entrysize = LFS_DIRSIZ(0, &proto, 0);
269 if (idesc->id_entryno == 0) {
270 n = LFS_DIRSIZ(0, dirp, 0);
271 if (dirp->d_reclen < n + entrysize)
272 goto chk2;
273 proto.d_reclen = dirp->d_reclen - n;
274 dirp->d_reclen = n;
275 idesc->id_entryno++;
276 lncntp[dirp->d_ino]--;
277 dirp = (struct lfs_direct *) ((char *) (dirp) + n);
278 memset(dirp, 0, (size_t) proto.d_reclen);
279 dirp->d_reclen = proto.d_reclen;
280 }
281 if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) {
282 inp->i_dotdot = dirp->d_ino;
283 if (dirp->d_type != LFS_DT_DIR) {
284 direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'");
285 dirp->d_type = LFS_DT_DIR;
286 if (reply("FIX") == 1)
287 ret |= ALTERED;
288 }
289 goto chk2;
290 }
291 if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) {
292 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
293 pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
294 dirp->d_name);
295 inp->i_dotdot = (ino_t) - 1;
296 } else if (dirp->d_reclen < entrysize) {
297 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
298 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n");
299 inp->i_dotdot = (ino_t) - 1;
300 } else if (inp->i_parent != 0) {
301 /*
302 * We know the parent, so fix now.
303 */
304 inp->i_dotdot = inp->i_parent;
305 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
306 proto.d_reclen = dirp->d_reclen;
307 memcpy(dirp, &proto, (size_t) entrysize);
308 if (reply("FIX") == 1)
309 ret |= ALTERED;
310 }
311 idesc->id_entryno++;
312 if (dirp->d_ino != 0)
313 lncntp[dirp->d_ino]--;
314 return (ret | KEEPON);
315 chk2:
316 if (dirp->d_ino == 0)
317 return (ret | KEEPON);
318 if (dirp->d_namlen <= 2 &&
319 dirp->d_name[0] == '.' &&
320 idesc->id_entryno >= 2) {
321 if (dirp->d_namlen == 1) {
322 direrror(idesc->id_number, "EXTRA '.' ENTRY");
323 dirp->d_ino = 0;
324 if (reply("FIX") == 1)
325 ret |= ALTERED;
326 return (KEEPON | ret);
327 }
328 if (dirp->d_name[1] == '.') {
329 direrror(idesc->id_number, "EXTRA '..' ENTRY");
330 dirp->d_ino = 0;
331 if (reply("FIX") == 1)
332 ret |= ALTERED;
333 return (KEEPON | ret);
334 }
335 }
336 idesc->id_entryno++;
337 n = 0;
338 if (dirp->d_ino >= maxino) {
339 fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE");
340 n = reply("REMOVE");
341 } else if (dirp->d_ino == LFS_IFILE_INUM &&
342 idesc->id_number == ULFS_ROOTINO) {
343 if (dirp->d_type != LFS_DT_REG) {
344 fileerror(idesc->id_number, dirp->d_ino,
345 "BAD TYPE FOR IFILE");
346 dirp->d_type = LFS_DT_REG;
347 if (reply("FIX") == 1)
348 ret |= ALTERED;
349 }
350 } else if (((dirp->d_ino == ULFS_WINO && (dirp->d_type != LFS_DT_WHT)) ||
351 (dirp->d_ino != ULFS_WINO && dirp->d_type == LFS_DT_WHT))) {
352 fileerror(idesc->id_number, dirp->d_ino, "BAD WHITEOUT ENTRY");
353 dirp->d_ino = ULFS_WINO;
354 dirp->d_type = LFS_DT_WHT;
355 if (reply("FIX") == 1)
356 ret |= ALTERED;
357 } else {
358 again:
359 switch (statemap[dirp->d_ino]) {
360 case USTATE:
361 if (idesc->id_entryno <= 2)
362 break;
363 fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED");
364 n = reply("REMOVE");
365 break;
366
367 case DCLEAR:
368 case FCLEAR:
369 if (idesc->id_entryno <= 2)
370 break;
371 if (statemap[dirp->d_ino] == FCLEAR)
372 errmsg = "DUP/BAD";
373 else if (!preen)
374 errmsg = "ZERO LENGTH DIRECTORY";
375 else {
376 n = 1;
377 break;
378 }
379 fileerror(idesc->id_number, dirp->d_ino, errmsg);
380 if ((n = reply("REMOVE")) == 1)
381 break;
382 dp = ginode(dirp->d_ino);
383 statemap[dirp->d_ino] =
384 (dp->di_mode & LFS_IFMT) == LFS_IFDIR ? DSTATE : FSTATE;
385 lncntp[dirp->d_ino] = dp->di_nlink;
386 goto again;
387
388 case DSTATE:
389 case DFOUND:
390 inp = getinoinfo(dirp->d_ino);
391 if (inp->i_parent != 0 && idesc->id_entryno > 2) {
392 getpathname(pathbuf, sizeof(pathbuf),
393 idesc->id_number, idesc->id_number);
394 getpathname(namebuf, sizeof(namebuf),
395 dirp->d_ino, dirp->d_ino);
396 pwarn("%s %s %s\n", pathbuf,
397 "IS AN EXTRANEOUS HARD LINK TO DIRECTORY",
398 namebuf);
399 if (preen)
400 printf(" (IGNORED)\n");
401 else if ((n = reply("REMOVE")) == 1)
402 break;
403 }
404 if (idesc->id_entryno > 2)
405 inp->i_parent = idesc->id_number;
406 /* fall through */
407
408 case FSTATE:
409 if (dirp->d_type != typemap[dirp->d_ino]) {
410 fileerror(idesc->id_number, dirp->d_ino,
411 "BAD TYPE VALUE");
412 if (debug)
413 pwarn("dir has %d, typemap has %d\n",
414 dirp->d_type, typemap[dirp->d_ino]);
415 dirp->d_type = typemap[dirp->d_ino];
416 if (reply("FIX") == 1)
417 ret |= ALTERED;
418 }
419 lncntp[dirp->d_ino]--;
420 break;
421
422 default:
423 errx(EEXIT, "BAD STATE %d FOR INODE I=%d",
424 statemap[dirp->d_ino], dirp->d_ino);
425 }
426 }
427 if (n == 0)
428 return (ret | KEEPON);
429 dirp->d_ino = 0;
430 return (ret | KEEPON | ALTERED);
431 }
432 /*
433 * Routine to sort disk blocks.
434 */
435 static int
436 blksort(const void *inpp1, const void *inpp2)
437 {
438 return ((*(const struct inoinfo *const *) inpp1)->i_blks[0] -
439 (*(const struct inoinfo *const *) inpp2)->i_blks[0]);
440 }
441