new copyright notice
[unix-history] / usr / src / sbin / fsck / pass2.c
CommitLineData
76797561 1/*
fe32782c
KM
2 * Copyright (c) 1980, 1986 The Regents of the University of California.
3 * All rights reserved.
4 *
70ab3c27 5 * %sccs.include.redist.c%
76797561
DF
6 */
7
ba8f3d8f 8#ifndef lint
70ab3c27 9static char sccsid[] = "@(#)pass2.c 5.13 (Berkeley) %G%";
fe32782c 10#endif /* not lint */
ba8f3d8f
KM
11
12#include <sys/param.h>
72e5286b 13#include <ufs/dinode.h>
4d7f4685 14#include <ufs/fs.h>
987e77a3 15#define KERNEL
4d7f4685 16#include <ufs/dir.h>
987e77a3 17#undef KERNEL
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 */
83 qsort((char *)inpsort, (int)inplast, sizeof *inpsort, blksort);
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;
90 dp = &dino;
91 inpend = &inpsort[inplast];
92 for (inpp = inpsort; inpp < inpend; inpp++) {
93 inp = *inpp;
f13c3b57
KM
94 if (inp->i_isize == 0)
95 continue;
987e77a3
KM
96 if (inp->i_isize < MINDIRSIZE) {
97 direrror(inp->i_number, "DIRECTORY TOO SHORT");
98 inp->i_isize = MINDIRSIZE;
99 if (reply("FIX") == 1) {
100 dp = ginode(inp->i_number);
101 dp->di_size = MINDIRSIZE;
102 inodirty();
103 dp = &dino;
104 }
105 }
106 if ((inp->i_isize & (DIRBLKSIZ - 1)) != 0) {
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],
122 (int)inp->i_numblks);
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;
146 makeentry(inp->i_number, inp->i_parent, "..");
147 lncntp[inp->i_parent]--;
148 continue;
149 }
150 fileerror(inp->i_parent, inp->i_number,
151 "BAD INODE NUMBER FOR '..'");
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
KM
176
177 /*
178 * check for "."
179 */
180 if (idesc->id_entryno != 0)
181 goto chk1;
182 if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) {
183 if (dirp->d_ino != idesc->id_number) {
569ec282 184 direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'");
ba8f3d8f
KM
185 dirp->d_ino = idesc->id_number;
186 if (reply("FIX") == 1)
187 ret |= ALTERED;
188 }
189 goto chk1;
190 }
569ec282 191 direrror(idesc->id_number, "MISSING '.'");
ba8f3d8f
KM
192 proto.d_ino = idesc->id_number;
193 proto.d_namlen = 1;
194 (void)strcpy(proto.d_name, ".");
195 entrysize = DIRSIZ(&proto);
196 if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) {
197 pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n",
198 dirp->d_name);
199 } else if (dirp->d_reclen < entrysize) {
200 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n");
201 } else if (dirp->d_reclen < 2 * entrysize) {
202 proto.d_reclen = dirp->d_reclen;
203 bcopy((char *)&proto, (char *)dirp, entrysize);
204 if (reply("FIX") == 1)
205 ret |= ALTERED;
206 } else {
207 n = dirp->d_reclen - entrysize;
208 proto.d_reclen = entrysize;
209 bcopy((char *)&proto, (char *)dirp, entrysize);
210 idesc->id_entryno++;
211 lncntp[dirp->d_ino]--;
569ec282 212 dirp = (struct direct *)((char *)(dirp) + entrysize);
ba8f3d8f
KM
213 bzero((char *)dirp, n);
214 dirp->d_reclen = n;
215 if (reply("FIX") == 1)
216 ret |= ALTERED;
217 }
218chk1:
219 if (idesc->id_entryno > 1)
220 goto chk2;
987e77a3
KM
221 inp = getinoinfo(idesc->id_number);
222 proto.d_ino = inp->i_parent;
ba8f3d8f
KM
223 proto.d_namlen = 2;
224 (void)strcpy(proto.d_name, "..");
225 entrysize = DIRSIZ(&proto);
226 if (idesc->id_entryno == 0) {
227 n = DIRSIZ(dirp);
228 if (dirp->d_reclen < n + entrysize)
229 goto chk2;
230 proto.d_reclen = dirp->d_reclen - n;
231 dirp->d_reclen = n;
232 idesc->id_entryno++;
233 lncntp[dirp->d_ino]--;
569ec282 234 dirp = (struct direct *)((char *)(dirp) + n);
987e77a3
KM
235 bzero((char *)dirp, (int)proto.d_reclen);
236 dirp->d_reclen = proto.d_reclen;
ba8f3d8f
KM
237 }
238 if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) {
987e77a3 239 inp->i_dotdot = dirp->d_ino;
ba8f3d8f
KM
240 goto chk2;
241 }
ba8f3d8f 242 if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) {
987e77a3 243 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
ba8f3d8f
KM
244 pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
245 dirp->d_name);
987e77a3 246 inp->i_dotdot = (ino_t)-1;
ba8f3d8f 247 } else if (dirp->d_reclen < entrysize) {
987e77a3 248 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
ba8f3d8f 249 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n");
987e77a3
KM
250 inp->i_dotdot = (ino_t)-1;
251 } else if (inp->i_parent != 0) {
252 /*
253 * We know the parent, so fix now.
254 */
255 inp->i_dotdot = inp->i_parent;
256 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
ba8f3d8f
KM
257 proto.d_reclen = dirp->d_reclen;
258 bcopy((char *)&proto, (char *)dirp, entrysize);
259 if (reply("FIX") == 1)
260 ret |= ALTERED;
261 }
987e77a3
KM
262 idesc->id_entryno++;
263 if (dirp->d_ino != 0)
264 lncntp[dirp->d_ino]--;
265 return (ret|KEEPON);
ba8f3d8f
KM
266chk2:
267 if (dirp->d_ino == 0)
268 return (ret|KEEPON);
269 if (dirp->d_namlen <= 2 &&
270 dirp->d_name[0] == '.' &&
271 idesc->id_entryno >= 2) {
272 if (dirp->d_namlen == 1) {
569ec282 273 direrror(idesc->id_number, "EXTRA '.' ENTRY");
ba8f3d8f
KM
274 dirp->d_ino = 0;
275 if (reply("FIX") == 1)
276 ret |= ALTERED;
277 return (KEEPON | ret);
278 }
279 if (dirp->d_name[1] == '.') {
569ec282 280 direrror(idesc->id_number, "EXTRA '..' ENTRY");
ba8f3d8f
KM
281 dirp->d_ino = 0;
282 if (reply("FIX") == 1)
283 ret |= ALTERED;
284 return (KEEPON | ret);
285 }
286 }
ba8f3d8f
KM
287 idesc->id_entryno++;
288 n = 0;
569ec282 289 if (dirp->d_ino > maxino || dirp->d_ino <= 0) {
987e77a3 290 fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE");
ba8f3d8f
KM
291 n = reply("REMOVE");
292 } else {
293again:
294 switch (statemap[dirp->d_ino]) {
295 case USTATE:
8dc4ce37
KM
296 if (idesc->id_entryno <= 2)
297 break;
987e77a3 298 fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED");
ba8f3d8f
KM
299 n = reply("REMOVE");
300 break;
301
993a756c
KM
302 case DCLEAR:
303 case FCLEAR:
8dc4ce37
KM
304 if (idesc->id_entryno <= 2)
305 break;
f13c3b57
KM
306 if (statemap[dirp->d_ino] == DCLEAR)
307 errmsg = "ZERO LENGTH DIRECTORY";
308 else
309 errmsg = "DUP/BAD";
310 fileerror(idesc->id_number, dirp->d_ino, errmsg);
ba8f3d8f
KM
311 if ((n = reply("REMOVE")) == 1)
312 break;
39c18287 313 dp = ginode(dirp->d_ino);
569ec282
KM
314 statemap[dirp->d_ino] =
315 (dp->di_mode & IFMT) == IFDIR ? DSTATE : FSTATE;
77d16a71 316 lncntp[dirp->d_ino] = dp->di_nlink;
ba8f3d8f
KM
317 goto again;
318
987e77a3
KM
319 case DSTATE:
320 if (statemap[idesc->id_number] == DFOUND)
321 statemap[dirp->d_ino] = DFOUND;
322 /* fall through */
323
993a756c 324 case DFOUND:
987e77a3 325 inp = getinoinfo(dirp->d_ino);
987e77a3
KM
326 if (inp->i_parent != 0 && idesc->id_entryno > 2) {
327 getpathname(pathbuf, idesc->id_number,
328 idesc->id_number);
3ad2f081 329 getpathname(namebuf, dirp->d_ino, dirp->d_ino);
987e77a3 330 pwarn("%s %s %s\n", pathbuf,
3ad2f081
KM
331 "IS AN EXTRANEOUS HARD LINK TO DIRECTORY",
332 namebuf);
333 if (preen)
334 printf(" (IGNORED)\n");
335 else if ((n = reply("REMOVE")) == 1)
336 break;
337 }
987e77a3
KM
338 if (idesc->id_entryno > 2)
339 inp->i_parent = idesc->id_number;
993a756c
KM
340 /* fall through */
341
ba8f3d8f
KM
342 case FSTATE:
343 lncntp[dirp->d_ino]--;
344 break;
345
ea4448dc
KM
346 default:
347 errexit("BAD STATE %d FOR INODE I=%d",
348 statemap[dirp->d_ino], dirp->d_ino);
ba8f3d8f
KM
349 }
350 }
ba8f3d8f
KM
351 if (n == 0)
352 return (ret|KEEPON);
353 dirp->d_ino = 0;
354 return (ret|KEEPON|ALTERED);
355}
987e77a3
KM
356
357/*
358 * Routine to sort disk blocks.
359 */
360blksort(inpp1, inpp2)
361 struct inoinfo **inpp1, **inpp2;
362{
363
364 return ((*inpp1)->i_blks[0] - (*inpp2)->i_blks[0]);
365}