]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - sbin/fsck_ffs/pass2.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.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 (idesc->id_entryno != 0)
246                 goto chk1;
247         if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) {
248                 if (dirp->d_ino != idesc->id_number) {
249                         direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'");
250                         dirp->d_ino = idesc->id_number;
251                         if (reply("FIX") == 1)
252                                 ret |= ALTERED;
253                 }
254                 if (dirp->d_type != DT_DIR) {
255                         direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'");
256                         dirp->d_type = DT_DIR;
257                         if (reply("FIX") == 1)
258                                 ret |= ALTERED;
259                 }
260                 goto chk1;
261         }
262         direrror(idesc->id_number, "MISSING '.'");
263         proto.d_ino = idesc->id_number;
264         proto.d_type = DT_DIR;
265         proto.d_namlen = 1;
266         (void)strcpy(proto.d_name, ".");
267         entrysize = DIRSIZ(0, &proto);
268         if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) {
269                 pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n",
270                         dirp->d_name);
271         } else if (dirp->d_reclen < entrysize) {
272                 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n");
273         } else if (dirp->d_reclen < 2 * entrysize) {
274                 proto.d_reclen = dirp->d_reclen;
275                 memmove(dirp, &proto, (size_t)entrysize);
276                 if (reply("FIX") == 1)
277                         ret |= ALTERED;
278         } else {
279                 n = dirp->d_reclen - entrysize;
280                 proto.d_reclen = entrysize;
281                 memmove(dirp, &proto, (size_t)entrysize);
282                 idesc->id_entryno++;
283                 inoinfo(dirp->d_ino)->ino_linkcnt--;
284                 dirp = (struct direct *)((char *)(dirp) + entrysize);
285                 memset(dirp, 0, (size_t)n);
286                 dirp->d_reclen = n;
287                 if (reply("FIX") == 1)
288                         ret |= ALTERED;
289         }
290 chk1:
291         if (idesc->id_entryno > 1)
292                 goto chk2;
293         inp = getinoinfo(idesc->id_number);
294         proto.d_ino = inp->i_parent;
295         proto.d_type = DT_DIR;
296         proto.d_namlen = 2;
297         (void)strcpy(proto.d_name, "..");
298         entrysize = DIRSIZ(0, &proto);
299         if (idesc->id_entryno == 0) {
300                 n = DIRSIZ(0, dirp);
301                 if (dirp->d_reclen < n + entrysize)
302                         goto chk2;
303                 proto.d_reclen = dirp->d_reclen - n;
304                 dirp->d_reclen = n;
305                 idesc->id_entryno++;
306                 inoinfo(dirp->d_ino)->ino_linkcnt--;
307                 dirp = (struct direct *)((char *)(dirp) + n);
308                 memset(dirp, 0, (size_t)proto.d_reclen);
309                 dirp->d_reclen = proto.d_reclen;
310         }
311         if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) {
312                 inp->i_dotdot = dirp->d_ino;
313                 if (dirp->d_type != DT_DIR) {
314                         direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'");
315                         dirp->d_type = DT_DIR;
316                         if (reply("FIX") == 1)
317                                 ret |= ALTERED;
318                 }
319                 goto chk2;
320         }
321         if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) {
322                 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
323                 pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
324                         dirp->d_name);
325                 inp->i_dotdot = (ino_t)-1;
326         } else if (dirp->d_reclen < entrysize) {
327                 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
328                 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n");
329                 inp->i_dotdot = (ino_t)-1;
330         } else if (inp->i_parent != 0) {
331                 /*
332                  * We know the parent, so fix now.
333                  */
334                 inp->i_dotdot = inp->i_parent;
335                 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
336                 proto.d_reclen = dirp->d_reclen;
337                 memmove(dirp, &proto, (size_t)entrysize);
338                 if (reply("FIX") == 1)
339                         ret |= ALTERED;
340         }
341         idesc->id_entryno++;
342         if (dirp->d_ino != 0)
343                 inoinfo(dirp->d_ino)->ino_linkcnt--;
344         return (ret|KEEPON);
345 chk2:
346         if (dirp->d_ino == 0)
347                 return (ret|KEEPON);
348         if (dirp->d_namlen <= 2 &&
349             dirp->d_name[0] == '.' &&
350             idesc->id_entryno >= 2) {
351                 if (dirp->d_namlen == 1) {
352                         direrror(idesc->id_number, "EXTRA '.' ENTRY");
353                         dirp->d_ino = 0;
354                         if (reply("FIX") == 1)
355                                 ret |= ALTERED;
356                         return (KEEPON | ret);
357                 }
358                 if (dirp->d_name[1] == '.') {
359                         direrror(idesc->id_number, "EXTRA '..' ENTRY");
360                         dirp->d_ino = 0;
361                         if (reply("FIX") == 1)
362                                 ret |= ALTERED;
363                         return (KEEPON | ret);
364                 }
365         }
366         idesc->id_entryno++;
367         n = 0;
368         if (dirp->d_ino > maxino) {
369                 fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE");
370                 n = reply("REMOVE");
371         } else if (((dirp->d_ino == WINO && dirp->d_type != DT_WHT) ||
372                     (dirp->d_ino != WINO && dirp->d_type == DT_WHT))) {
373                 fileerror(idesc->id_number, dirp->d_ino, "BAD WHITEOUT ENTRY");
374                 dirp->d_ino = WINO;
375                 dirp->d_type = DT_WHT;
376                 if (reply("FIX") == 1)
377                         ret |= ALTERED;
378         } else {
379 again:
380                 switch (inoinfo(dirp->d_ino)->ino_state) {
381                 case USTATE:
382                         if (idesc->id_entryno <= 2)
383                                 break;
384                         fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED");
385                         n = reply("REMOVE");
386                         break;
387
388                 case DCLEAR:
389                 case FCLEAR:
390                         if (idesc->id_entryno <= 2)
391                                 break;
392                         if (inoinfo(dirp->d_ino)->ino_state == FCLEAR)
393                                 errmsg = "DUP/BAD";
394                         else if (!preen && !usedsoftdep)
395                                 errmsg = "ZERO LENGTH DIRECTORY";
396                         else {
397                                 n = 1;
398                                 break;
399                         }
400                         fileerror(idesc->id_number, dirp->d_ino, errmsg);
401                         if ((n = reply("REMOVE")) == 1)
402                                 break;
403                         dp = ginode(dirp->d_ino);
404                         inoinfo(dirp->d_ino)->ino_state =
405                            (DIP(dp, di_mode) & IFMT) == IFDIR ? DSTATE : FSTATE;
406                         inoinfo(dirp->d_ino)->ino_linkcnt = DIP(dp, di_nlink);
407                         goto again;
408
409                 case DSTATE:
410                 case DZLINK:
411                         if (inoinfo(idesc->id_number)->ino_state == DFOUND)
412                                 inoinfo(dirp->d_ino)->ino_state = DFOUND;
413                         /* FALLTHROUGH */
414
415                 case DFOUND:
416                         inp = getinoinfo(dirp->d_ino);
417                         if (inp->i_parent != 0 && idesc->id_entryno > 2) {
418                                 getpathname(pathbuf, idesc->id_number,
419                                     idesc->id_number);
420                                 getpathname(namebuf, dirp->d_ino, dirp->d_ino);
421                                 pwarn("%s%s%s %s %s\n", pathbuf,
422                                     (strcmp(pathbuf, "/") == 0 ? "" : "/"),
423                                     dirp->d_name,
424                                     "IS AN EXTRANEOUS HARD LINK TO DIRECTORY",
425                                     namebuf);
426                                 if (cursnapshot != 0)
427                                         break;
428                                 if (preen) {
429                                         printf(" (REMOVED)\n");
430                                         n = 1;
431                                         break;
432                                 }
433                                 if ((n = reply("REMOVE")) == 1)
434                                         break;
435                         }
436                         if (idesc->id_entryno > 2)
437                                 inp->i_parent = idesc->id_number;
438                         /* FALLTHROUGH */
439
440                 case FSTATE:
441                 case FZLINK:
442                         if (dirp->d_type != inoinfo(dirp->d_ino)->ino_type) {
443                                 fileerror(idesc->id_number, dirp->d_ino,
444                                     "BAD TYPE VALUE");
445                                 dirp->d_type = inoinfo(dirp->d_ino)->ino_type;
446                                 if (reply("FIX") == 1)
447                                         ret |= ALTERED;
448                         }
449                         inoinfo(dirp->d_ino)->ino_linkcnt--;
450                         break;
451
452                 default:
453                         errx(EEXIT, "BAD STATE %d FOR INODE I=%d",
454                             inoinfo(dirp->d_ino)->ino_state, dirp->d_ino);
455                 }
456         }
457         if (n == 0)
458                 return (ret|KEEPON);
459         dirp->d_ino = 0;
460         return (ret|KEEPON|ALTERED);
461 }
462
463 /*
464  * Routine to sort disk blocks.
465  */
466 static int
467 blksort(const void *arg1, const void *arg2)
468 {
469
470         return ((*(struct inoinfo * const *)arg1)->i_blks[0] -
471                 (*(struct inoinfo * const *)arg2)->i_blks[0]);
472 }