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