4.4BSD snapshot (revision 8.1)
[unix-history] / usr / src / sbin / fsck / pass2.c
CommitLineData
76797561 1/*
21e5ab9a
KB
2 * Copyright (c) 1980, 1986, 1993
3 * The Regents of the University of California. All rights reserved.
fe32782c 4 *
70ab3c27 5 * %sccs.include.redist.c%
76797561
DF
6 */
7
ba8f3d8f 8#ifndef lint
21e5ab9a 9static char sccsid[] = "@(#)pass2.c 8.1 (Berkeley) %G%";
fe32782c 10#endif /* not lint */
ba8f3d8f
KM
11
12#include <sys/param.h>
b82067db 13#include <sys/time.h>
558b3a30 14#include <ufs/ufs/dinode.h>
558b3a30 15#include <ufs/ufs/dir.h>
558b3a30 16#include <ufs/ffs/fs.h>
d72e970b 17#include <stdlib.h>
38dde0cd 18#include <string.h>
ba8f3d8f
KM
19#include "fsck.h"
20
987e77a3
KM
21#define MINDIRSIZE (sizeof (struct dirtemplate))
22
23int pass2check(), blksort();
ba8f3d8f
KM
24
25pass2()
26{
569ec282 27 register struct dinode *dp;
987e77a3
KM
28 register struct inoinfo **inpp, *inp;
29 struct inoinfo **inpend;
30 struct inodesc curino;
31 struct dinode dino;
32 char pathbuf[MAXPATHLEN + 1];
ba8f3d8f 33
ba8f3d8f
KM
34 switch (statemap[ROOTINO]) {
35
36 case USTATE:
7e1e49f2
KM
37 pfatal("ROOT INODE UNALLOCATED");
38 if (reply("ALLOCATE") == 0)
39 errexit("");
c689da7b 40 if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
7e1e49f2 41 errexit("CANNOT ALLOCATE ROOT INODE\n");
7e1e49f2
KM
42 break;
43
44 case DCLEAR:
45 pfatal("DUPS/BAD IN ROOT INODE");
46 if (reply("REALLOCATE")) {
47 freeino(ROOTINO);
c689da7b 48 if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
7e1e49f2 49 errexit("CANNOT ALLOCATE ROOT INODE\n");
7e1e49f2
KM
50 break;
51 }
52 if (reply("CONTINUE") == 0)
53 errexit("");
7e1e49f2 54 break;
ba8f3d8f
KM
55
56 case FSTATE:
993a756c 57 case FCLEAR:
ba8f3d8f 58 pfatal("ROOT INODE NOT DIRECTORY");
7e1e49f2
KM
59 if (reply("REALLOCATE")) {
60 freeino(ROOTINO);
c689da7b 61 if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
7e1e49f2 62 errexit("CANNOT ALLOCATE ROOT INODE\n");
7e1e49f2
KM
63 break;
64 }
39c18287 65 if (reply("FIX") == 0)
ba8f3d8f 66 errexit("");
39c18287 67 dp = ginode(ROOTINO);
ba8f3d8f
KM
68 dp->di_mode &= ~IFMT;
69 dp->di_mode |= IFDIR;
70 inodirty();
987e77a3 71 break;
ba8f3d8f
KM
72
73 case DSTATE:
ba8f3d8f
KM
74 break;
75
993a756c
KM
76 default:
77 errexit("BAD STATE %d FOR ROOT INODE", statemap[ROOTINO]);
ba8f3d8f 78 }
987e77a3
KM
79 statemap[ROOTINO] = DFOUND;
80 /*
81 * Sort the directory list into disk block order.
82 */
d72e970b 83 qsort((char *)inpsort, (size_t)inplast, sizeof *inpsort, blksort);
987e77a3
KM
84 /*
85 * Check the integrity of each directory.
86 */
87 bzero((char *)&curino, sizeof(struct inodesc));
88 curino.id_type = DATA;
89 curino.id_func = pass2check;
7a11042a 90 dino.di_mode = IFDIR;
987e77a3
KM
91 dp = &dino;
92 inpend = &inpsort[inplast];
93 for (inpp = inpsort; inpp < inpend; inpp++) {
94 inp = *inpp;
f13c3b57
KM
95 if (inp->i_isize == 0)
96 continue;
987e77a3
KM
97 if (inp->i_isize < MINDIRSIZE) {
98 direrror(inp->i_number, "DIRECTORY TOO SHORT");
3017a9c8 99 inp->i_isize = roundup(MINDIRSIZE, DIRBLKSIZ);
987e77a3
KM
100 if (reply("FIX") == 1) {
101 dp = ginode(inp->i_number);
3017a9c8 102 dp->di_size = inp->i_isize;
987e77a3
KM
103 inodirty();
104 dp = &dino;
105 }
3017a9c8 106 } else if ((inp->i_isize & (DIRBLKSIZ - 1)) != 0) {
987e77a3
KM
107 getpathname(pathbuf, inp->i_number, inp->i_number);
108 pwarn("DIRECTORY %s: LENGTH %d NOT MULTIPLE OF %d",
109 pathbuf, inp->i_isize, DIRBLKSIZ);
110 if (preen)
111 printf(" (ADJUSTED)\n");
112 inp->i_isize = roundup(inp->i_isize, DIRBLKSIZ);
113 if (preen || reply("ADJUST") == 1) {
114 dp = ginode(inp->i_number);
115 dp->di_size = roundup(inp->i_isize, DIRBLKSIZ);
116 inodirty();
117 dp = &dino;
118 }
119 }
120 dp->di_size = inp->i_isize;
121 bcopy((char *)&inp->i_blks[0], (char *)&dp->di_db[0],
d72e970b 122 (size_t)inp->i_numblks);
987e77a3
KM
123 curino.id_number = inp->i_number;
124 curino.id_parent = inp->i_parent;
125 (void)ckinode(dp, &curino);
126 }
127 /*
128 * Now that the parents of all directories have been found,
129 * make another pass to verify the value of `..'
130 */
131 for (inpp = inpsort; inpp < inpend; inpp++) {
132 inp = *inpp;
f13c3b57 133 if (inp->i_parent == 0 || inp->i_isize == 0)
987e77a3
KM
134 continue;
135 if (statemap[inp->i_parent] == DFOUND &&
136 statemap[inp->i_number] == DSTATE)
137 statemap[inp->i_number] = DFOUND;
138 if (inp->i_dotdot == inp->i_parent ||
139 inp->i_dotdot == (ino_t)-1)
140 continue;
141 if (inp->i_dotdot == 0) {
142 inp->i_dotdot = inp->i_parent;
143 fileerror(inp->i_parent, inp->i_number, "MISSING '..'");
144 if (reply("FIX") == 0)
145 continue;
d72e970b 146 (void)makeentry(inp->i_number, inp->i_parent, "..");
987e77a3
KM
147 lncntp[inp->i_parent]--;
148 continue;
149 }
150 fileerror(inp->i_parent, inp->i_number,
3735ff63 151 "BAD INODE NUMBER FOR '..'");
987e77a3
KM
152 if (reply("FIX") == 0)
153 continue;
154 lncntp[inp->i_dotdot]++;
155 lncntp[inp->i_parent]--;
156 inp->i_dotdot = inp->i_parent;
157 (void)changeino(inp->i_number, "..", inp->i_parent);
158 }
159 /*
160 * Mark all the directories that can be found from the root.
161 */
162 propagate();
ba8f3d8f
KM
163}
164
165pass2check(idesc)
166 struct inodesc *idesc;
167{
569ec282 168 register struct direct *dirp = idesc->id_dirp;
987e77a3 169 register struct inoinfo *inp;
ba8f3d8f 170 int n, entrysize, ret = 0;
569ec282 171 struct dinode *dp;
f13c3b57 172 char *errmsg;
569ec282 173 struct direct proto;
987e77a3
KM
174 char namebuf[MAXPATHLEN + 1];
175 char pathbuf[MAXPATHLEN + 1];
ba8f3d8f 176
3735ff63
KM
177 /*
178 * If converting, set directory entry type.
179 */
180 if (doinglevel2 && dirp->d_ino > 0 && dirp->d_ino < maxino) {
181 dirp->d_type = typemap[dirp->d_ino];
182 ret |= ALTERED;
183 }
ba8f3d8f
KM
184 /*
185 * check for "."
186 */
187 if (idesc->id_entryno != 0)
188 goto chk1;
189 if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) {
190 if (dirp->d_ino != idesc->id_number) {
569ec282 191 direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'");
ba8f3d8f
KM
192 dirp->d_ino = idesc->id_number;
193 if (reply("FIX") == 1)
194 ret |= ALTERED;
195 }
3735ff63
KM
196 if (newinofmt && dirp->d_type != DT_DIR) {
197 direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'");
198 dirp->d_type = DT_DIR;
199 if (reply("FIX") == 1)
200 ret |= ALTERED;
201 }
ba8f3d8f
KM
202 goto chk1;
203 }
569ec282 204 direrror(idesc->id_number, "MISSING '.'");
ba8f3d8f 205 proto.d_ino = idesc->id_number;
3735ff63
KM
206 if (newinofmt)
207 proto.d_type = DT_DIR;
5ea8a7e3
KM
208 else
209 proto.d_type = 0;
ba8f3d8f
KM
210 proto.d_namlen = 1;
211 (void)strcpy(proto.d_name, ".");
3735ff63 212 entrysize = DIRSIZ(0, &proto);
ba8f3d8f
KM
213 if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) {
214 pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n",
215 dirp->d_name);
216 } else if (dirp->d_reclen < entrysize) {
217 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n");
218 } else if (dirp->d_reclen < 2 * entrysize) {
219 proto.d_reclen = dirp->d_reclen;
d72e970b 220 bcopy((char *)&proto, (char *)dirp, (size_t)entrysize);
ba8f3d8f
KM
221 if (reply("FIX") == 1)
222 ret |= ALTERED;
223 } else {
224 n = dirp->d_reclen - entrysize;
225 proto.d_reclen = entrysize;
d72e970b 226 bcopy((char *)&proto, (char *)dirp, (size_t)entrysize);
ba8f3d8f
KM
227 idesc->id_entryno++;
228 lncntp[dirp->d_ino]--;
569ec282 229 dirp = (struct direct *)((char *)(dirp) + entrysize);
d72e970b 230 bzero((char *)dirp, (size_t)n);
ba8f3d8f
KM
231 dirp->d_reclen = n;
232 if (reply("FIX") == 1)
233 ret |= ALTERED;
234 }
235chk1:
236 if (idesc->id_entryno > 1)
237 goto chk2;
987e77a3
KM
238 inp = getinoinfo(idesc->id_number);
239 proto.d_ino = inp->i_parent;
3735ff63
KM
240 if (newinofmt)
241 proto.d_type = DT_DIR;
5ea8a7e3
KM
242 else
243 proto.d_type = 0;
ba8f3d8f
KM
244 proto.d_namlen = 2;
245 (void)strcpy(proto.d_name, "..");
3735ff63 246 entrysize = DIRSIZ(0, &proto);
ba8f3d8f 247 if (idesc->id_entryno == 0) {
3735ff63 248 n = DIRSIZ(0, dirp);
ba8f3d8f
KM
249 if (dirp->d_reclen < n + entrysize)
250 goto chk2;
251 proto.d_reclen = dirp->d_reclen - n;
252 dirp->d_reclen = n;
253 idesc->id_entryno++;
254 lncntp[dirp->d_ino]--;
569ec282 255 dirp = (struct direct *)((char *)(dirp) + n);
d72e970b 256 bzero((char *)dirp, (size_t)proto.d_reclen);
987e77a3 257 dirp->d_reclen = proto.d_reclen;
ba8f3d8f
KM
258 }
259 if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) {
987e77a3 260 inp->i_dotdot = dirp->d_ino;
3735ff63
KM
261 if (newinofmt && dirp->d_type != DT_DIR) {
262 direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'");
263 dirp->d_type = DT_DIR;
264 if (reply("FIX") == 1)
265 ret |= ALTERED;
266 }
ba8f3d8f
KM
267 goto chk2;
268 }
ba8f3d8f 269 if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) {
987e77a3 270 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
ba8f3d8f
KM
271 pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
272 dirp->d_name);
987e77a3 273 inp->i_dotdot = (ino_t)-1;
ba8f3d8f 274 } else if (dirp->d_reclen < entrysize) {
987e77a3 275 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
ba8f3d8f 276 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n");
987e77a3
KM
277 inp->i_dotdot = (ino_t)-1;
278 } else if (inp->i_parent != 0) {
279 /*
280 * We know the parent, so fix now.
281 */
282 inp->i_dotdot = inp->i_parent;
283 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
ba8f3d8f 284 proto.d_reclen = dirp->d_reclen;
d72e970b 285 bcopy((char *)&proto, (char *)dirp, (size_t)entrysize);
ba8f3d8f
KM
286 if (reply("FIX") == 1)
287 ret |= ALTERED;
288 }
987e77a3
KM
289 idesc->id_entryno++;
290 if (dirp->d_ino != 0)
291 lncntp[dirp->d_ino]--;
292 return (ret|KEEPON);
ba8f3d8f
KM
293chk2:
294 if (dirp->d_ino == 0)
295 return (ret|KEEPON);
296 if (dirp->d_namlen <= 2 &&
297 dirp->d_name[0] == '.' &&
298 idesc->id_entryno >= 2) {
299 if (dirp->d_namlen == 1) {
569ec282 300 direrror(idesc->id_number, "EXTRA '.' ENTRY");
ba8f3d8f
KM
301 dirp->d_ino = 0;
302 if (reply("FIX") == 1)
303 ret |= ALTERED;
304 return (KEEPON | ret);
305 }
306 if (dirp->d_name[1] == '.') {
569ec282 307 direrror(idesc->id_number, "EXTRA '..' ENTRY");
ba8f3d8f
KM
308 dirp->d_ino = 0;
309 if (reply("FIX") == 1)
310 ret |= ALTERED;
311 return (KEEPON | ret);
312 }
313 }
ba8f3d8f
KM
314 idesc->id_entryno++;
315 n = 0;
fca9f60c 316 if (dirp->d_ino > maxino) {
987e77a3 317 fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE");
ba8f3d8f
KM
318 n = reply("REMOVE");
319 } else {
320again:
321 switch (statemap[dirp->d_ino]) {
322 case USTATE:
8dc4ce37
KM
323 if (idesc->id_entryno <= 2)
324 break;
987e77a3 325 fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED");
ba8f3d8f
KM
326 n = reply("REMOVE");
327 break;
328
993a756c
KM
329 case DCLEAR:
330 case FCLEAR:
8dc4ce37
KM
331 if (idesc->id_entryno <= 2)
332 break;
e965843f 333 if (statemap[dirp->d_ino] == FCLEAR)
f13c3b57 334 errmsg = "DUP/BAD";
e965843f
KM
335 else if (!preen)
336 errmsg = "ZERO LENGTH DIRECTORY";
337 else {
338 n = 1;
339 break;
340 }
f13c3b57 341 fileerror(idesc->id_number, dirp->d_ino, errmsg);
ba8f3d8f
KM
342 if ((n = reply("REMOVE")) == 1)
343 break;
39c18287 344 dp = ginode(dirp->d_ino);
569ec282
KM
345 statemap[dirp->d_ino] =
346 (dp->di_mode & IFMT) == IFDIR ? DSTATE : FSTATE;
77d16a71 347 lncntp[dirp->d_ino] = dp->di_nlink;
ba8f3d8f
KM
348 goto again;
349
987e77a3
KM
350 case DSTATE:
351 if (statemap[idesc->id_number] == DFOUND)
352 statemap[dirp->d_ino] = DFOUND;
353 /* fall through */
354
993a756c 355 case DFOUND:
987e77a3 356 inp = getinoinfo(dirp->d_ino);
987e77a3
KM
357 if (inp->i_parent != 0 && idesc->id_entryno > 2) {
358 getpathname(pathbuf, idesc->id_number,
359 idesc->id_number);
3ad2f081 360 getpathname(namebuf, dirp->d_ino, dirp->d_ino);
987e77a3 361 pwarn("%s %s %s\n", pathbuf,
3ad2f081
KM
362 "IS AN EXTRANEOUS HARD LINK TO DIRECTORY",
363 namebuf);
364 if (preen)
365 printf(" (IGNORED)\n");
366 else if ((n = reply("REMOVE")) == 1)
367 break;
368 }
987e77a3
KM
369 if (idesc->id_entryno > 2)
370 inp->i_parent = idesc->id_number;
993a756c
KM
371 /* fall through */
372
ba8f3d8f 373 case FSTATE:
3735ff63
KM
374 if (newinofmt && dirp->d_type != typemap[dirp->d_ino]) {
375 fileerror(idesc->id_number, dirp->d_ino,
376 "BAD TYPE VALUE");
377 dirp->d_type = typemap[dirp->d_ino];
378 if (reply("FIX") == 1)
379 ret |= ALTERED;
380 }
ba8f3d8f
KM
381 lncntp[dirp->d_ino]--;
382 break;
383
ea4448dc
KM
384 default:
385 errexit("BAD STATE %d FOR INODE I=%d",
386 statemap[dirp->d_ino], dirp->d_ino);
ba8f3d8f
KM
387 }
388 }
ba8f3d8f
KM
389 if (n == 0)
390 return (ret|KEEPON);
391 dirp->d_ino = 0;
392 return (ret|KEEPON|ALTERED);
393}
987e77a3
KM
394
395/*
396 * Routine to sort disk blocks.
397 */
398blksort(inpp1, inpp2)
399 struct inoinfo **inpp1, **inpp2;
400{
401
402 return ((*inpp1)->i_blks[0] - (*inpp2)->i_blks[0]);
403}