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