2858c92e701f0db53345bc58d85f57f09e035cfb
[openafs.git] / src / vfsck / pass2.c
1 /*
2  * Copyright (c) 1980, 1986 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that the above copyright notice and this paragraph are
7  * duplicated in all such forms and that any documentation,
8  * advertising materials, and other materials related to such
9  * distribution and use acknowledge that the software was developed
10  * by the University of California, Berkeley.  The name of the
11  * University may not be used to endorse or promote products derived
12  * from this software without specific prior written permission.
13  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16  */
17
18 #include <afsconfig.h>
19 #include <afs/param.h>
20
21 #ifdef AFS_HPUX_ENV
22 /* We need the old directory type headers (included below), so don't include
23  * the normal dirent.h, or it will conflict. */
24 # undef HAVE_DIRENT_H
25 # include <sys/inode.h>
26 # define        LONGFILENAMES   1
27 # include <sys/sysmacros.h>
28 # include <sys/ino.h>
29 # define        DIRSIZ_MACRO
30 # ifdef HAVE_USR_OLD_USR_INCLUDE_NDIR_H
31 #  include </usr/old/usr/include/ndir.h>
32 # else
33 #  include <ndir.h>
34 # endif
35 #endif
36
37 #include <roken.h>
38
39 #include <ctype.h>
40
41 #define VICE
42 #ifdef  AFS_OSF_ENV
43 #include <sys/vnode.h>
44 #include <sys/mount.h>
45 #include <ufs/inode.h>
46 #include <ufs/fs.h>
47 #define _BSD
48 #define _KERNEL
49 #include <ufs/dir.h>
50 #undef  _KERNEL
51 #undef  _BSD
52 #else /* AFS_OSF_ENV */
53 #ifdef AFS_VFSINCL_ENV
54 #include <sys/vnode.h>
55 #ifdef    AFS_SUN5_ENV
56 #include <sys/fs/ufs_inode.h>
57 #include <sys/fs/ufs_fs.h>
58 #define _KERNEL
59 #include <sys/fs/ufs_fsdir.h>
60 #undef _KERNEL
61 #include <sys/fs/ufs_mount.h>
62 #else
63 #include <ufs/inode.h>
64 #include <ufs/fs.h>
65 #include <ufs/fsdir.h>
66 #endif
67 #else /* AFS_VFSINCL_ENV */
68 #include <sys/inode.h>
69 #ifndef AFS_HPUX_ENV
70 #include <sys/dir.h>
71 #endif
72 #include <sys/fs.h>
73
74 #endif /* AFS_VFSINCL_ENV */
75 #endif /* AFS_OSF_ENV */
76 #include <afs/osi_inode.h>
77
78 #include "fsck.h"
79
80 int pass2check();
81
82 pass2()
83 {
84     struct dinode *dp;
85     struct inodesc rootdesc;
86
87     memset(&rootdesc, 0, sizeof(struct inodesc));
88     rootdesc.id_type = ADDR;
89     rootdesc.id_func = pass2check;
90     rootdesc.id_number = ROOTINO;
91     pathp = pathname;
92 #if defined(ACLS) && defined(AFS_HPUX_ENV)
93     switch (statemap[ROOTINO] & STATE) {
94 #else /* no ACLS */
95     switch (statemap[ROOTINO]) {
96 #endif /* ACLS */
97
98     case USTATE:
99         pfatal("ROOT INODE UNALLOCATED");
100         if (reply("ALLOCATE") == 0)
101             errexit("");
102         if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
103             errexit("CANNOT ALLOCATE ROOT INODE\n");
104         descend(&rootdesc, ROOTINO);
105         break;
106
107     case DCLEAR:
108         pfatal("DUPS/BAD IN ROOT INODE");
109         if (reply("REALLOCATE")) {
110             freeino(ROOTINO);
111             if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
112                 errexit("CANNOT ALLOCATE ROOT INODE\n");
113             descend(&rootdesc, ROOTINO);
114             break;
115         }
116         if (reply("CONTINUE") == 0)
117             errexit("");
118         statemap[ROOTINO] = DSTATE;
119         descend(&rootdesc, ROOTINO);
120         break;
121
122 #ifdef VICE
123     case VSTATE:
124 #endif /* VICE */
125     case FSTATE:
126     case FCLEAR:
127         pfatal("ROOT INODE NOT DIRECTORY");
128         if (reply("REALLOCATE")) {
129             freeino(ROOTINO);
130             if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
131                 errexit("CANNOT ALLOCATE ROOT INODE\n");
132             descend(&rootdesc, ROOTINO);
133             break;
134         }
135         if (reply("FIX") == 0)
136             errexit("");
137         dp = ginode(ROOTINO);
138         dp->di_mode &= ~IFMT;
139         dp->di_mode |= IFDIR;
140 #ifdef  AFS_SUN5_ENV
141         dp->di_smode = dp->di_mode;
142 #endif
143         inodirty();
144 #if defined(ACLS) && defined(AFS_HPUX_ENV)
145         /*
146          * Keep any info on associated continuation inode
147          */
148         if (statemap[ROOTINO] & HASCINODE)
149             statemap[ROOTINO] = DSTATE | HASCINODE;
150         else
151             statemap[ROOTINO] = DSTATE;
152 #else /* no ACLS */
153         statemap[ROOTINO] = DSTATE;
154 #endif /* ACLS */
155         /* fall into ... */
156
157     case DSTATE:
158         descend(&rootdesc, ROOTINO);
159         break;
160
161     default:
162         errexit("BAD STATE %d FOR ROOT INODE", statemap[ROOTINO]);
163     }
164 }
165
166 pass2check(idesc)
167      struct inodesc *idesc;
168 {
169     struct direct *dirp = idesc->id_dirp;
170     char *curpathloc;
171     int n, entrysize, ret = 0;
172     struct dinode *dp;
173     struct direct proto;
174     char namebuf[BUFSIZ];
175 #if defined(ACLS) && defined(AFS_HPUX_ENV)
176     int holdstate;
177 #endif /* ACLS */
178
179     /*
180      * check for "."
181      */
182     if (idesc->id_entryno != 0)
183         goto chk1;
184     if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) {
185         if (dirp->d_ino != idesc->id_number) {
186             direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'");
187             dirp->d_ino = idesc->id_number;
188             if (reply("FIX") == 1)
189                 ret |= ALTERED;
190         }
191         goto chk1;
192     }
193     direrror(idesc->id_number, "MISSING '.'");
194     proto.d_ino = idesc->id_number;
195     proto.d_namlen = 1;
196     (void)strcpy(proto.d_name, ".");
197     entrysize = DIRSIZ(&proto);
198     if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) {
199         pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n",
200                dirp->d_name);
201 #if     defined(AFS_SUN_ENV)
202         iscorrupt = 1;
203 #endif
204     } else if (dirp->d_reclen < entrysize) {
205         pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n");
206 #if     defined(AFS_SUN_ENV)
207         iscorrupt = 1;
208 #endif
209     } else if (dirp->d_reclen < 2 * entrysize) {
210         proto.d_reclen = dirp->d_reclen;
211         memcpy((char *)dirp, (char *)&proto, entrysize);
212         if (reply("FIX") == 1)
213             ret |= ALTERED;
214     } else {
215         n = dirp->d_reclen - entrysize;
216         proto.d_reclen = entrysize;
217         memcpy((char *)dirp, (char *)&proto, entrysize);
218         idesc->id_entryno++;
219         lncntp[dirp->d_ino]--;
220         dirp = (struct direct *)((char *)(dirp) + entrysize);
221         memset(dirp, 0, n);
222         dirp->d_reclen = n;
223         if (reply("FIX") == 1)
224             ret |= ALTERED;
225     }
226   chk1:
227     if (idesc->id_entryno > 1)
228         goto chk2;
229     proto.d_ino = idesc->id_parent;
230     proto.d_namlen = 2;
231     (void)strcpy(proto.d_name, "..");
232     entrysize = DIRSIZ(&proto);
233     if (idesc->id_entryno == 0) {
234         n = DIRSIZ(dirp);
235         if (dirp->d_reclen < n + entrysize)
236             goto chk2;
237         proto.d_reclen = dirp->d_reclen - n;
238         dirp->d_reclen = n;
239         idesc->id_entryno++;
240         lncntp[dirp->d_ino]--;
241         dirp = (struct direct *)((char *)(dirp) + n);
242         memset(dirp, 0, n);
243         dirp->d_reclen = n;
244     }
245     if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) {
246         if (dirp->d_ino != idesc->id_parent) {
247             direrror(idesc->id_number, "BAD INODE NUMBER FOR '..'");
248             dirp->d_ino = idesc->id_parent;
249             if (reply("FIX") == 1)
250                 ret |= ALTERED;
251         }
252         goto chk2;
253     }
254     direrror(idesc->id_number, "MISSING '..'");
255     if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) {
256         pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
257                dirp->d_name);
258 #if     defined(AFS_SUN_ENV)
259         iscorrupt = 1;
260 #endif
261     } else if (dirp->d_reclen < entrysize) {
262         pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n");
263 #if     defined(AFS_SUN_ENV)
264         iscorrupt = 1;
265 #endif
266     } else {
267         proto.d_reclen = dirp->d_reclen;
268         memcpy((char *)dirp, (char *)&proto, entrysize);
269         if (reply("FIX") == 1)
270             ret |= ALTERED;
271     }
272   chk2:
273     if (dirp->d_ino == 0)
274         return (ret | KEEPON);
275     if (dirp->d_namlen <= 2 && dirp->d_name[0] == '.'
276         && idesc->id_entryno >= 2) {
277         if (dirp->d_namlen == 1) {
278             direrror(idesc->id_number, "EXTRA '.' ENTRY");
279             dirp->d_ino = 0;
280             if (reply("FIX") == 1)
281                 ret |= ALTERED;
282             return (KEEPON | ret);
283         }
284         if (dirp->d_name[1] == '.') {
285             direrror(idesc->id_number, "EXTRA '..' ENTRY");
286             dirp->d_ino = 0;
287             if (reply("FIX") == 1)
288                 ret |= ALTERED;
289             return (KEEPON | ret);
290         }
291     }
292     curpathloc = pathp;
293     *pathp++ = '/';
294     if (pathp + dirp->d_namlen >= endpathname) {
295         *pathp = '\0';
296         errexit("NAME TOO LONG %s%s\n", pathname, dirp->d_name);
297     }
298     memcpy(pathp, dirp->d_name, (int)dirp->d_namlen + 1);
299     pathp += dirp->d_namlen;
300     idesc->id_entryno++;
301     n = 0;
302     if (dirp->d_ino > maxino || dirp->d_ino <= 0) {
303         direrror(dirp->d_ino, "I OUT OF RANGE");
304         n = reply("REMOVE");
305     } else {
306       again:
307 #if defined(ACLS) && defined(AFS_HPUX_ENV)
308         switch (statemap[dirp->d_ino] & STATE) {
309 #else /* no ACLS */
310         switch (statemap[dirp->d_ino]) {
311 #endif /* ACLS */
312         case USTATE:
313             direrror(dirp->d_ino, "UNALLOCATED");
314             n = reply("REMOVE");
315             break;
316
317         case DCLEAR:
318         case FCLEAR:
319             direrror(dirp->d_ino, "DUP/BAD");
320             if ((n = reply("REMOVE")) == 1)
321                 break;
322             dp = ginode(dirp->d_ino);
323 #ifdef VICE
324 #if defined(ACLS) && defined(AFS_HPUX_ENV)
325             holdstate =
326                 (dp->di_mode & IFMT) ==
327                 IFDIR ? DSTATE : (VICEINODE ? VSTATE : FSTATE);
328             if (statemap[dirp->d_ino] & HASCINODE)
329                 statemap[dirp->d_ino] = holdstate | HASCINODE;
330             else
331                 statemap[dirp->d_ino] = holdstate;
332 #else
333             statemap[dirp->d_ino] =
334                 (dp->di_mode & IFMT) ==
335                 IFDIR ? DSTATE : (VICEINODE ? VSTATE : FSTATE);
336 #endif
337 #else /* VICE */
338 #if defined(ACLS) && defined(AFS_HPUX_ENV)
339             holdstate = (dp->di_mode & IFMT) == IFDIR ? DSTATE : FSTATE;
340             if (statemap[dirp->d_ino] & HASCINODE)
341                 statemap[dirp->d_ino] = holdstate | HASCINODE;
342             else
343                 statemap[dirp->d_ino] = holdstate;
344 #else
345             statemap[dirp->d_ino] =
346                 (dp->di_mode & IFMT) == IFDIR ? DSTATE : FSTATE;
347 #endif
348 #endif /* VICE */
349             lncntp[dirp->d_ino] = dp->di_nlink;
350             goto again;
351
352         case DFOUND:
353             if (idesc->id_entryno > 2) {
354                 getpathname(namebuf, dirp->d_ino, dirp->d_ino);
355                 pwarn("%s %s %s\n", pathname,
356                       "IS AN EXTRANEOUS HARD LINK TO DIRECTORY", namebuf);
357                 if (preen)
358                     printf(" (IGNORED)\n");
359                 else if ((n = reply("REMOVE")) == 1)
360                     break;
361             }
362             /* fall through */
363
364         case FSTATE:
365 #ifdef VICE
366           filecase:
367 #endif /* VICE */
368             lncntp[dirp->d_ino]--;
369             break;
370
371 #ifdef VICE
372         case VSTATE:
373             direrror(dirp->d_ino, "VICE INODE REFERENCED BY DIRECTORY");
374             if (reply("CONVERT TO REGULAR FILE") != 1)
375                 break;
376             if ((dp = ginode(dirp->d_ino)) == NULL)
377                 break;
378             CLEAR_DVICEMAGIC(dp);
379             inodirty();
380             statemap[dirp->d_ino] = FSTATE;
381             ret |= ALTERED;
382             goto filecase;
383 #endif /* VICE */
384
385
386         case DSTATE:
387             descend(idesc, dirp->d_ino);
388             if (statemap[dirp->d_ino] == DFOUND) {
389                 lncntp[dirp->d_ino]--;
390             } else if (statemap[dirp->d_ino] == DCLEAR) {
391                 dirp->d_ino = 0;
392                 ret |= ALTERED;
393             } else
394                 errexit("BAD RETURN STATE %d FROM DESCEND",
395                         statemap[dirp->d_ino]);
396             break;
397
398 #if defined(ACLS) && defined(AFS_HPUX_ENV)
399             /* hpux has more dynamic states (CSTATE, CRSTATE) */
400         case CSTATE:
401             break;
402         case CRSTATE:
403             break;
404 #endif
405         default:
406             errexit("BAD STATE %d FOR INODE I=%d", statemap[dirp->d_ino],
407                     dirp->d_ino);
408         }
409     }
410     pathp = curpathloc;
411     *pathp = '\0';
412     if (n == 0)
413         return (ret | KEEPON);
414     dirp->d_ino = 0;
415     return (ret | KEEPON | ALTERED);
416 }