]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - sbin/fsck_ffs/pass2.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.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         case FZLINK:
95                 pfatal("ROOT INODE NOT DIRECTORY");
96                 if (reply("REALLOCATE")) {
97                         freeino(ROOTINO);
98                         if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
99                                 errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
100                         break;
101                 }
102                 if (reply("FIX") == 0) {
103                         ckfini(0);
104                         exit(EEXIT);
105                 }
106                 dp = ginode(ROOTINO);
107                 DIP_SET(dp, di_mode, DIP(dp, di_mode) & ~IFMT);
108                 DIP_SET(dp, di_mode, DIP(dp, di_mode) | IFDIR);
109                 inodirty();
110                 break;
111
112         case DSTATE:
113         case DZLINK:
114                 break;
115
116         default:
117                 errx(EEXIT, "BAD STATE %d FOR ROOT INODE",
118                     inoinfo(ROOTINO)->ino_state);
119         }
120         inoinfo(ROOTINO)->ino_state = DFOUND;
121         inoinfo(WINO)->ino_state = FSTATE;
122         inoinfo(WINO)->ino_type = DT_WHT;
123         /*
124          * Sort the directory list into disk block order.
125          */
126         qsort((char *)inpsort, (size_t)inplast, sizeof *inpsort, blksort);
127         /*
128          * Check the integrity of each directory.
129          */
130         memset(&curino, 0, sizeof(struct inodesc));
131         curino.id_type = DATA;
132         curino.id_func = pass2check;
133         inpend = &inpsort[inplast];
134         for (inpp = inpsort; inpp < inpend; inpp++) {
135                 if (got_siginfo) {
136                         printf("%s: phase 2: dir %td of %d (%d%%)\n", cdevname,
137                             inpp - inpsort, (int)inplast,
138                             (int)((inpp - inpsort) * 100 / inplast));
139                         got_siginfo = 0;
140                 }
141                 if (got_sigalarm) {
142                         setproctitle("%s p2 %d%%", cdevname,
143                             (int)((inpp - inpsort) * 100 / inplast));
144                         got_sigalarm = 0;
145                 }
146                 inp = *inpp;
147                 if (inp->i_isize == 0)
148                         continue;
149                 if (inp->i_isize < MINDIRSIZE) {
150                         direrror(inp->i_number, "DIRECTORY TOO SHORT");
151                         inp->i_isize = roundup(MINDIRSIZE, DIRBLKSIZ);
152                         if (reply("FIX") == 1) {
153                                 dp = ginode(inp->i_number);
154                                 DIP_SET(dp, di_size, inp->i_isize);
155                                 inodirty();
156                         }
157                 } else if ((inp->i_isize & (DIRBLKSIZ - 1)) != 0) {
158                         getpathname(pathbuf, inp->i_number, inp->i_number);
159                         if (usedsoftdep)
160                                 pfatal("%s %s: LENGTH %jd NOT MULTIPLE OF %d",
161                                         "DIRECTORY", pathbuf,
162                                         (intmax_t)inp->i_isize, DIRBLKSIZ);
163                         else
164                                 pwarn("%s %s: LENGTH %jd NOT MULTIPLE OF %d",
165                                         "DIRECTORY", pathbuf,
166                                         (intmax_t)inp->i_isize, DIRBLKSIZ);
167                         if (preen)
168                                 printf(" (ADJUSTED)\n");
169                         inp->i_isize = roundup(inp->i_isize, DIRBLKSIZ);
170                         if (preen || reply("ADJUST") == 1) {
171                                 dp = ginode(inp->i_number);
172                                 DIP_SET(dp, di_size,
173                                     roundup(inp->i_isize, DIRBLKSIZ));
174                                 inodirty();
175                         }
176                 }
177                 dp = &dino;
178                 memset(dp, 0, sizeof(struct ufs2_dinode));
179                 DIP_SET(dp, di_mode, IFDIR);
180                 DIP_SET(dp, di_size, inp->i_isize);
181                 for (i = 0;
182                      i < (inp->i_numblks<NDADDR ? inp->i_numblks : NDADDR);
183                      i++)
184                         DIP_SET(dp, di_db[i], inp->i_blks[i]);
185                 if (inp->i_numblks > NDADDR)
186                         for (i = 0; i < NIADDR; i++)
187                                 DIP_SET(dp, di_ib[i], inp->i_blks[NDADDR + i]);
188                 curino.id_number = inp->i_number;
189                 curino.id_parent = inp->i_parent;
190                 (void)ckinode(dp, &curino);
191         }
192         /*
193          * Now that the parents of all directories have been found,
194          * make another pass to verify the value of `..'
195          */
196         for (inpp = inpsort; inpp < inpend; inpp++) {
197                 inp = *inpp;
198                 if (inp->i_parent == 0 || inp->i_isize == 0)
199                         continue;
200                 if (inoinfo(inp->i_parent)->ino_state == DFOUND &&
201                     INO_IS_DUNFOUND(inp->i_number))
202                         inoinfo(inp->i_number)->ino_state = DFOUND;
203                 if (inp->i_dotdot == inp->i_parent ||
204                     inp->i_dotdot == (ino_t)-1)
205                         continue;
206                 if (inp->i_dotdot == 0) {
207                         inp->i_dotdot = inp->i_parent;
208                         fileerror(inp->i_parent, inp->i_number, "MISSING '..'");
209                         if (reply("FIX") == 0)
210                                 continue;
211                         (void)makeentry(inp->i_number, inp->i_parent, "..");
212                         inoinfo(inp->i_parent)->ino_linkcnt--;
213                         continue;
214                 }
215                 fileerror(inp->i_parent, inp->i_number,
216                     "BAD INODE NUMBER FOR '..'");
217                 if (reply("FIX") == 0)
218                         continue;
219                 inoinfo(inp->i_dotdot)->ino_linkcnt++;
220                 inoinfo(inp->i_parent)->ino_linkcnt--;
221                 inp->i_dotdot = inp->i_parent;
222                 (void)changeino(inp->i_number, "..", inp->i_parent);
223         }
224         /*
225          * Mark all the directories that can be found from the root.
226          */
227         propagate();
228 }
229
230 static int
231 pass2check(struct inodesc *idesc)
232 {
233         struct direct *dirp = idesc->id_dirp;
234         struct inoinfo *inp;
235         int n, entrysize, ret = 0;
236         union dinode *dp;
237         const char *errmsg;
238         struct direct proto;
239         char namebuf[MAXPATHLEN + 1];
240         char pathbuf[MAXPATHLEN + 1];
241
242         /*
243          * check for "."
244          */
245         if (dirp->d_ino > maxino)
246                 goto chk2;
247         if (idesc->id_entryno != 0)
248                 goto chk1;
249         if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) {
250                 if (dirp->d_ino != idesc->id_number) {
251                         direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'");
252                         dirp->d_ino = idesc->id_number;
253                         if (reply("FIX") == 1)
254                                 ret |= ALTERED;
255                 }
256                 if (dirp->d_type != DT_DIR) {
257                         direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'");
258                         dirp->d_type = DT_DIR;
259                         if (reply("FIX") == 1)
260                                 ret |= ALTERED;
261                 }
262                 goto chk1;
263         }
264         direrror(idesc->id_number, "MISSING '.'");
265         proto.d_ino = idesc->id_number;
266         proto.d_type = DT_DIR;
267         proto.d_namlen = 1;
268         (void)strcpy(proto.d_name, ".");
269         entrysize = DIRSIZ(0, &proto);
270         if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) {
271                 pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n",
272                         dirp->d_name);
273         } else if (dirp->d_reclen < entrysize) {
274                 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n");
275         } else if (dirp->d_reclen < 2 * entrysize) {
276                 proto.d_reclen = dirp->d_reclen;
277                 memmove(dirp, &proto, (size_t)entrysize);
278                 if (reply("FIX") == 1)
279                         ret |= ALTERED;
280         } else {
281                 n = dirp->d_reclen - entrysize;
282                 proto.d_reclen = entrysize;
283                 memmove(dirp, &proto, (size_t)entrysize);
284                 idesc->id_entryno++;
285                 inoinfo(dirp->d_ino)->ino_linkcnt--;
286                 dirp = (struct direct *)((char *)(dirp) + entrysize);
287                 memset(dirp, 0, (size_t)n);
288                 dirp->d_reclen = n;
289                 if (reply("FIX") == 1)
290                         ret |= ALTERED;
291         }
292 chk1:
293         if (idesc->id_entryno > 1)
294                 goto chk2;
295         inp = getinoinfo(idesc->id_number);
296         proto.d_ino = inp->i_parent;
297         proto.d_type = DT_DIR;
298         proto.d_namlen = 2;
299         (void)strcpy(proto.d_name, "..");
300         entrysize = DIRSIZ(0, &proto);
301         if (idesc->id_entryno == 0) {
302                 n = DIRSIZ(0, dirp);
303                 if (dirp->d_reclen < n + entrysize)
304                         goto chk2;
305                 proto.d_reclen = dirp->d_reclen - n;
306                 dirp->d_reclen = n;
307                 idesc->id_entryno++;
308                 inoinfo(dirp->d_ino)->ino_linkcnt--;
309                 dirp = (struct direct *)((char *)(dirp) + n);
310                 memset(dirp, 0, (size_t)proto.d_reclen);
311                 dirp->d_reclen = proto.d_reclen;
312         }
313         if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) {
314                 inp->i_dotdot = dirp->d_ino;
315                 if (dirp->d_type != DT_DIR) {
316                         direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'");
317                         dirp->d_type = DT_DIR;
318                         if (reply("FIX") == 1)
319                                 ret |= ALTERED;
320                 }
321                 goto chk2;
322         }
323         if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) {
324                 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
325                 pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
326                         dirp->d_name);
327                 inp->i_dotdot = (ino_t)-1;
328         } else if (dirp->d_reclen < entrysize) {
329                 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
330                 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n");
331                 inp->i_dotdot = (ino_t)-1;
332         } else if (inp->i_parent != 0) {
333                 /*
334                  * We know the parent, so fix now.
335                  */
336                 inp->i_dotdot = inp->i_parent;
337                 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
338                 proto.d_reclen = dirp->d_reclen;
339                 memmove(dirp, &proto, (size_t)entrysize);
340                 if (reply("FIX") == 1)
341                         ret |= ALTERED;
342         }
343         idesc->id_entryno++;
344         if (dirp->d_ino != 0)
345                 inoinfo(dirp->d_ino)->ino_linkcnt--;
346         return (ret|KEEPON);
347 chk2:
348         if (dirp->d_ino == 0)
349                 return (ret|KEEPON);
350         if (dirp->d_namlen <= 2 &&
351             dirp->d_name[0] == '.' &&
352             idesc->id_entryno >= 2) {
353                 if (dirp->d_namlen == 1) {
354                         direrror(idesc->id_number, "EXTRA '.' ENTRY");
355                         dirp->d_ino = 0;
356                         if (reply("FIX") == 1)
357                                 ret |= ALTERED;
358                         return (KEEPON | ret);
359                 }
360                 if (dirp->d_name[1] == '.') {
361                         direrror(idesc->id_number, "EXTRA '..' ENTRY");
362                         dirp->d_ino = 0;
363                         if (reply("FIX") == 1)
364                                 ret |= ALTERED;
365                         return (KEEPON | ret);
366                 }
367         }
368         idesc->id_entryno++;
369         n = 0;
370         if (dirp->d_ino > maxino) {
371                 fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE");
372                 n = reply("REMOVE");
373         } else if (((dirp->d_ino == WINO && dirp->d_type != DT_WHT) ||
374                     (dirp->d_ino != WINO && dirp->d_type == DT_WHT))) {
375                 fileerror(idesc->id_number, dirp->d_ino, "BAD WHITEOUT ENTRY");
376                 dirp->d_ino = WINO;
377                 dirp->d_type = DT_WHT;
378                 if (reply("FIX") == 1)
379                         ret |= ALTERED;
380         } else {
381 again:
382                 switch (inoinfo(dirp->d_ino)->ino_state) {
383                 case USTATE:
384                         if (idesc->id_entryno <= 2)
385                                 break;
386                         fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED");
387                         n = reply("REMOVE");
388                         break;
389
390                 case DCLEAR:
391                 case FCLEAR:
392                         if (idesc->id_entryno <= 2)
393                                 break;
394                         if (inoinfo(dirp->d_ino)->ino_state == FCLEAR)
395                                 errmsg = "DUP/BAD";
396                         else if (!preen && !usedsoftdep)
397                                 errmsg = "ZERO LENGTH DIRECTORY";
398                         else {
399                                 n = 1;
400                                 break;
401                         }
402                         fileerror(idesc->id_number, dirp->d_ino, errmsg);
403                         if ((n = reply("REMOVE")) == 1)
404                                 break;
405                         dp = ginode(dirp->d_ino);
406                         inoinfo(dirp->d_ino)->ino_state =
407                            (DIP(dp, di_mode) & IFMT) == IFDIR ? DSTATE : FSTATE;
408                         inoinfo(dirp->d_ino)->ino_linkcnt = DIP(dp, di_nlink);
409                         goto again;
410
411                 case DSTATE:
412                 case DZLINK:
413                         if (inoinfo(idesc->id_number)->ino_state == DFOUND)
414                                 inoinfo(dirp->d_ino)->ino_state = DFOUND;
415                         /* FALLTHROUGH */
416
417                 case DFOUND:
418                         inp = getinoinfo(dirp->d_ino);
419                         if (inp->i_parent != 0 && idesc->id_entryno > 2) {
420                                 getpathname(pathbuf, idesc->id_number,
421                                     idesc->id_number);
422                                 getpathname(namebuf, dirp->d_ino, dirp->d_ino);
423                                 pwarn("%s%s%s %s %s\n", pathbuf,
424                                     (strcmp(pathbuf, "/") == 0 ? "" : "/"),
425                                     dirp->d_name,
426                                     "IS AN EXTRANEOUS HARD LINK TO DIRECTORY",
427                                     namebuf);
428                                 if (cursnapshot != 0)
429                                         break;
430                                 if (preen) {
431                                         printf(" (REMOVED)\n");
432                                         n = 1;
433                                         break;
434                                 }
435                                 if ((n = reply("REMOVE")) == 1)
436                                         break;
437                         }
438                         if (idesc->id_entryno > 2)
439                                 inp->i_parent = idesc->id_number;
440                         /* FALLTHROUGH */
441
442                 case FSTATE:
443                 case FZLINK:
444                         if (dirp->d_type != inoinfo(dirp->d_ino)->ino_type) {
445                                 fileerror(idesc->id_number, dirp->d_ino,
446                                     "BAD TYPE VALUE");
447                                 dirp->d_type = inoinfo(dirp->d_ino)->ino_type;
448                                 if (reply("FIX") == 1)
449                                         ret |= ALTERED;
450                         }
451                         inoinfo(dirp->d_ino)->ino_linkcnt--;
452                         break;
453
454                 default:
455                         errx(EEXIT, "BAD STATE %d FOR INODE I=%d",
456                             inoinfo(dirp->d_ino)->ino_state, dirp->d_ino);
457                 }
458         }
459         if (n == 0)
460                 return (ret|KEEPON);
461         dirp->d_ino = 0;
462         return (ret|KEEPON|ALTERED);
463 }
464
465 /*
466  * Routine to sort disk blocks.
467  */
468 static int
469 blksort(const void *arg1, const void *arg2)
470 {
471
472         return ((*(struct inoinfo * const *)arg1)->i_blks[0] -
473                 (*(struct inoinfo * const *)arg2)->i_blks[0]);
474 }