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