dffec6e053f62e997b9da9bedd0344bc80972068
[openafs.git] / src / dir / salvage.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 /* This is the directory salvager.  It consists of two routines.  The first, 
11  * DirOK, checks to see if the directory looks good.  If the directory does 
12  * NOT look good, the approved procedure is to then call Salvage, which 
13  * copies all the good entries from the damaged dir into a new directory. 
14  */
15
16 #include <afsconfig.h>
17 #include <afs/param.h>
18
19
20 #include <sys/types.h>
21 #include <errno.h>
22 #ifdef AFS_NT40_ENV
23 #include <winsock2.h>
24 #else
25 #include <netinet/in.h>
26 #endif
27
28 #include <string.h>
29
30 #include "dir.h"
31 /* Defined in vol/vol-salvage.c */
32 extern void Log(const char *format, ...);
33 /* Defined in vol/physio.c */
34 extern void Die(char *);
35     
36 #define printf  Log             /* To make it work with volume salvager */
37
38 /* This routine is called with one parameter, the id (the same thing that is 
39  * passed to physio or the buffer package) of a directory to check.  It 
40  * returns 1 if the directory looks good, and 0 otherwise. */
41
42 #define MAXENAME 256
43
44 extern afs_int32 DErrno;
45
46 /* figure out how many pages in use in a directory, given ptr to its (locked) 
47  * header */
48 static int
49 ComputeUsedPages(register struct DirHeader *dhp)
50 {
51     register afs_int32 usedPages, i;
52
53     if (dhp->header.pgcount != 0) {
54         /* new style */
55         usedPages = ntohs(dhp->header.pgcount);
56     } else {
57         /* old style */
58         usedPages = 0;
59         for (i = 0; i < MAXPAGES; i++) {
60             if (dhp->alloMap[i] == EPP) {
61                 usedPages = i;
62                 break;
63             }
64         }
65         if (usedPages == 0)
66             usedPages = MAXPAGES;
67     }
68     return usedPages;
69 }
70
71 /* returns true if something went wrong checking, or if dir is fine.  Returns
72  * false if we *know* that the dir is bad.
73  */
74 int
75 DirOK(void *file)
76 {
77     struct DirHeader *dhp;
78     struct PageHeader *pp;
79     struct DirEntry *ep;
80     int i, j, k, up;
81     int havedot = 0, havedotdot = 0;
82     int usedPages, count, entry;
83     char eaMap[BIGMAXPAGES * EPP / 8];  /* Change eaSize initialization below, too. */
84     int eaSize;
85     afs_int32 entcount, maxents;
86     unsigned short ne;
87
88     eaSize = BIGMAXPAGES * EPP / 8;
89
90     /* Read the directory header */
91     dhp = (struct DirHeader *)DRead(file, 0);
92     if (!dhp) {
93         /* if DErrno is 0, then we know that the read worked, but was short,
94          * and the damage is permanent.  Otherwise, we got an I/O or programming
95          * error.  Claim the dir is OK, but log something.
96          */
97         if (DErrno != 0) {
98             printf("Could not read first page in directory (%d)\n", DErrno);
99             Die("dirok1");
100             return 1;
101         }
102         printf("First page in directory does not exist.\n");
103         return 0;
104     }
105
106     /* Check magic number for first page */
107     if (dhp->header.tag != htons(1234)) {
108         printf("Bad first pageheader magic number.\n");
109         DRelease((struct buffer *)dhp, 0);
110         return 0;
111     }
112
113     /* Verify that the number of free entries in each directory page
114      * is within range (0-EPP). Also ensure directory is contiguous:
115      * Once the first alloMap entry with EPP free entries is found,
116      * the rest should match.
117      */
118     up = 0;                     /* count of used pages if total pages < MAXPAGES */
119     k = 0;                      /* found last page */
120     for (i = 0; i < MAXPAGES; i++) {
121         j = dhp->alloMap[i];
122
123         /* Check if in range */
124         if (i == 0) {
125             if ((j < 0) || (j > EPP - (13 + 2))) {
126                 /* First page's dirheader uses 13 entries and at least
127                  * two must exist for "." and ".."
128                  */
129                 printf("The dir header alloc map for page %d is bad.\n", i);
130                 DRelease((struct buffer *)dhp, 0);
131                 return 0;
132             }
133         } else {
134             if ((j < 0) || (j > EPP)) {
135                 printf("The dir header alloc map for page %d is bad.\n", i);
136                 DRelease((struct buffer *)dhp, 0);
137                 return 0;
138             }
139         }
140
141         /* Check if contiguous */
142         if (k) {                /* last page found */
143             if (j != EPP) {     /* remaining entries must be EPP */
144                 printf
145                     ("A partially-full page occurs in slot %d, after the dir end.\n",
146                      i);
147                 DRelease((struct buffer *)dhp, 0);
148                 return 0;
149             }
150         } else if (j == EPP) {  /* is this the last page */
151             k = 1;              /* yes */
152         } else {                /* a used page */
153             up++;               /* keep count */
154         }
155     }
156
157     /* Compute number of used directory pages and max entries in all 
158      ** those pages, the value of 'up' must be less than pgcount. The above
159      ** loop only checks the first MAXPAGES in a directory. An alloMap does
160      ** not exists for pages between MAXPAGES and BIGMAXPAGES */
161     usedPages = ComputeUsedPages(dhp);
162     if (usedPages < up) {
163         printf
164             ("Count of used directory pages does not match count in directory header\n");
165         DRelease((struct buffer *)dhp, 0);
166         return 0;
167     }
168
169     /* For each directory page, check the magic number in each page
170      * header, and check that number of free entries (from freebitmap)
171      * matches the count in the alloMap from directory header.
172      */
173     for (i = 0; i < usedPages; i++) {
174         /* Read the page header */
175         pp = (struct PageHeader *)DRead(file, i);
176         if (!pp) {
177             DRelease((struct buffer *)dhp, 0);
178             if (DErrno != 0) {
179                 /* couldn't read page, but not because it wasn't there permanently */
180                 printf("Failed to read dir page %d (errno %d)\n", i, DErrno);
181                 Die("dirok2");
182                 return 1;
183             }
184             printf("Directory shorter than alloMap indicates (page %d)\n", i);
185             return 0;
186         }
187
188         /* check the tag field */
189         if (pp->tag != htons(1234)) {
190             printf("Directory page %d has a bad magic number.\n", i);
191             DRelease((struct buffer *)pp, 0);
192             DRelease((struct buffer *)dhp, 0);
193             return 0;
194         }
195
196         /* Count the number of entries allocated in this single
197          * directory page using the freebitmap in the page header.
198          */
199         count = 0;
200         for (j = 0; j < EPP / 8; j++) {
201             k = pp->freebitmap[j];
202             if (k & 0x80)
203                 count++;
204             if (k & 0x40)
205                 count++;
206             if (k & 0x20)
207                 count++;
208             if (k & 0x10)
209                 count++;
210             if (k & 0x08)
211                 count++;
212             if (k & 0x04)
213                 count++;
214             if (k & 0x02)
215                 count++;
216             if (k & 0x01)
217                 count++;
218         }
219         count = EPP - count;    /* Change to count of free entries */
220
221         /* Now check that the count of free entries matches the count in the alloMap */
222         if ((i < MAXPAGES) && ((count & 0xff) != (dhp->alloMap[i] & 0xff))) {
223             printf
224                 ("Header alloMap count doesn't match count in freebitmap for page %d.\n",
225                  i);
226             DRelease((struct buffer *)pp, 0);
227             DRelease((struct buffer *)dhp, 0);
228             return 0;
229         }
230
231         DRelease((struct buffer *)pp, 0);
232     }
233
234     /* Initialize the in-memory freebit map for all pages. */
235     for (i = 0; i < eaSize; i++) {
236         eaMap[i] = 0;
237         if (i < usedPages * (EPP / 8)) {
238             if (i == 0) {
239                 eaMap[i] = 0xff;        /* A dir header uses first 13 entries */
240             } else if (i == 1) {
241                 eaMap[i] = 0x1f;        /* A dir header uses first 13 entries */
242             } else if ((i % 8) == 0) {
243                 eaMap[i] = 0x01;        /* A page header uses only first entry */
244             }
245         }
246     }
247     maxents = usedPages * EPP;
248
249     /* Walk down all the hash lists, ensuring that each flag field has FFIRST
250      * in it.  Mark the appropriate bits in the in-memory freebit map.
251      * Check that the name is in the right hash bucket.
252      * Also check for loops in the hash chain by counting the entries.
253      */
254     for (entcount = 0, i = 0; i < NHASHENT; i++) {
255         for (entry = ntohs(dhp->hashTable[i]); entry; entry = ne) {
256             /* Verify that the entry is within range */
257             if (entry < 0 || entry >= maxents) {
258                 printf("Out-of-range hash id %d in chain %d.\n", entry, i);
259                 DRelease((struct buffer *)dhp, 0);
260                 return 0;
261             }
262
263             /* Read the directory entry */
264             DErrno = 0;
265             ep = GetBlob(file, entry);
266             if (!ep) {
267                 if (DErrno != 0) {
268                     /* something went wrong reading the page, but it wasn't
269                      * really something wrong with the dir that we can fix.
270                      */
271                     printf("Could not get dir blob %d (errno %d)\n", entry,
272                            DErrno);
273                     DRelease((struct buffer *)dhp, 0);
274                     Die("dirok3");
275                 }
276                 printf("Invalid hash id %d in chain %d.\n", entry, i);
277                 DRelease((struct buffer *)dhp, 0);
278                 return 0;
279             }
280             ne = ntohs(ep->next);
281
282             /* There can't be more than maxents entries */
283             if (++entcount >= maxents) {
284                 printf("Directory's hash chain %d is circular.\n", i);
285                 DRelease((struct buffer *)ep, 0);
286                 DRelease((struct buffer *)dhp, 0);
287                 return 0;
288             }
289
290             /* A null name is no good */
291             if (ep->name[0] == '\000') {
292                 printf("Dir entry %x in chain %d has bogus (null) name.\n",
293                        (int)ep, i);
294                 DRelease((struct buffer *)ep, 0);
295                 DRelease((struct buffer *)dhp, 0);
296                 return 0;
297             }
298
299             /* The entry flag better be FFIRST */
300             if (ep->flag != FFIRST) {
301                 printf("Dir entry %x in chain %d has bogus flag field.\n", (int)ep,
302                        i);
303                 DRelease((struct buffer *)ep, 0);
304                 DRelease((struct buffer *)dhp, 0);
305                 return 0;
306             }
307
308             /* Check the size of the name */
309             j = strlen(ep->name);
310             if (j >= MAXENAME) {        /* MAXENAME counts the null */
311                 printf("Dir entry %x in chain %d has too-long name.\n", (int)ep,
312                        i);
313                 DRelease((struct buffer *)ep, 0);
314                 DRelease((struct buffer *)dhp, 0);
315                 return 0;
316             }
317
318             /* The name used up k directory entries, set the bit in our in-memory
319              * freebitmap for each entry used by the name.
320              */
321             k = NameBlobs(ep->name);
322             for (j = 0; j < k; j++) {
323                 eaMap[(entry + j) >> 3] |= (1 << ((entry + j) & 7));
324             }
325
326             /* Hash the name and make sure it is in the correct name hash */
327             if ((j = DirHash(ep->name)) != i) {
328                 printf
329                     ("Dir entry %x should be in hash bucket %d but IS in %d.\n",
330                      (int)ep, j, i);
331                 DRelease((struct buffer *)ep, 0);
332                 DRelease((struct buffer *)dhp, 0);
333                 return 0;
334             }
335
336             /* Check that if this is entry 13 (the 1st entry), then name must be "." */
337             if (entry == 13) {
338                 if (strcmp(ep->name, ".") == 0) {
339                     havedot = 1;
340                 } else {
341                     printf
342                         ("Dir entry %x, index 13 has name '%s' should be '.'\n",
343                          (int)ep, ep->name);
344                     DRelease((struct buffer *)ep, 0);
345                     DRelease((struct buffer *)dhp, 0);
346                     return 0;
347                 }
348             }
349
350             /* Check that if this is entry 14 (the 2nd entry), then name must be ".." */
351             if (entry == 14) {
352                 if (strcmp(ep->name, "..") == 0) {
353                     havedotdot = 1;
354                 } else {
355                     printf
356                         ("Dir entry %x, index 14 has name '%s' should be '..'\n",
357                          (int)ep, ep->name);
358                     DRelease((struct buffer *)ep, 0);
359                     DRelease((struct buffer *)dhp, 0);
360                     return 0;
361                 }
362             }
363
364             /* CHECK FOR DUPLICATE NAMES? */
365
366             DRelease((struct buffer *)ep, 0);
367         }
368     }
369
370     /* Verify that we found '.' and '..' in the correct place */
371     if (!havedot || !havedotdot) {
372         printf
373             ("Directory entry '.' or '..' does not exist or is in the wrong index.\n");
374         DRelease((struct buffer *)dhp, 0);
375         return 0;
376     }
377
378     /* The in-memory freebit map has been computed.  Check that it
379      * matches the one in the page header.
380      * Note that if this matches, alloMap has already been checked against it.
381      */
382     for (i = 0; i < usedPages; i++) {
383         pp = DRead(file, i);
384         if (!pp) {
385             printf
386                 ("Failed on second attempt to read dir page %d (errno %d)\n",
387                  i, DErrno);
388             DRelease((struct buffer *)dhp, 0);
389             /* if DErrno is 0, then the dir is really bad, and we return dir *not* OK.
390              * otherwise, we want to return true (1), meaning the dir isn't known
391              * to be bad (we can't tell, since I/Os are failing.
392              */
393             if (DErrno != 0)
394                 Die("dirok4");
395             else
396                 return 0;       /* dir is really shorter */
397         }
398
399         count = i * (EPP / 8);
400         for (j = 0; j < EPP / 8; j++) {
401             if (eaMap[count + j] != pp->freebitmap[j]) {
402                 printf
403                     ("Entry freebitmap error, page %d, map offset %d, %x should be %x.\n",
404                      i, j, pp->freebitmap[j], eaMap[count + j]);
405                 DRelease((struct buffer *)pp, 0);
406                 DRelease((struct buffer *)dhp, 0);
407                 return 0;
408             }
409         }
410
411         DRelease((struct buffer *)pp, 0);
412     }
413
414     /* Finally cleanup and return. */
415     DRelease((struct buffer *)dhp, 0);
416     return 1;
417 }
418
419 /* This routine is called with six parameters.  The first is the id of 
420  * the original, currently suspect, directory.  The second is the file 
421  * id of the place the salvager should place the new, fixed, directory. 
422  * The third and the fourth parameters are the vnode number and the
423  * uniquifier of the currently suspect directory. The fifth and the
424  * sixth parameters are the vnode number and the uniquifier of the
425  * parent directory.
426  */
427 int
428 DirSalvage(char *fromFile, char *toFile, afs_int32 vn, afs_int32 vu, 
429            afs_int32 pvn, afs_int32 pvu)
430 {
431     /* First do a MakeDir on the target. */
432     afs_int32 dot[3], dotdot[3], lfid[3], code, usedPages;
433     char tname[256];
434     register int i;
435     register char *tp;
436     struct DirHeader *dhp;
437     struct DirEntry *ep;
438     int entry;
439
440     memset(dot, 0, sizeof(dot));
441     memset(dotdot, 0, sizeof(dotdot));
442     dot[1] = vn;
443     dot[2] = vu;
444     dotdot[1] = pvn;
445     dotdot[2] = pvu;
446
447     MakeDir(toFile, dot, dotdot);       /* Returns no error code. */
448
449     /* Find out how many pages are valid, using stupid heuristic since DRead
450      * never returns null.
451      */
452     dhp = (struct DirHeader *)DRead(fromFile, 0);
453     if (!dhp) {
454         printf("Failed to read first page of fromDir!\n");
455         /* if DErrno != 0, then our call failed and we should let our
456          * caller know that there's something wrong with the new dir.  If not,
457          * then we return here anyway, with an empty, but at least good, directory.
458          */
459         return DErrno;
460     }
461
462     usedPages = ComputeUsedPages(dhp);
463
464     /* Finally, enumerate all the entries, doing a create on them. */
465     for (i = 0; i < NHASHENT; i++) {
466         entry = ntohs(dhp->hashTable[i]);
467         while (1) {
468             if (!entry)
469                 break;
470             if (entry < 0 || entry >= usedPages * EPP) {
471                 printf
472                     ("Warning: bogus hash table entry encountered, ignoring.\n");
473                 break;
474             }
475             DErrno = 0;
476             ep = GetBlob(fromFile, entry);
477             if (!ep) {
478                 if (DErrno) {
479                     printf
480                         ("can't continue down hash chain (entry %d, errno %d)\n",
481                          entry, DErrno);
482                     DRelease((struct buffer *)dhp, 0);
483                     return DErrno;
484                 }
485                 printf
486                     ("Warning: bogus hash chain encountered, switching to next.\n");
487                 break;
488             }
489             strncpy(tname, ep->name, MAXENAME);
490             tname[MAXENAME - 1] = '\000';       /* just in case */
491             tp = tname;
492
493             entry = ntohs(ep->next);
494
495             if ((strcmp(tp, ".") != 0) && (strcmp(tp, "..") != 0)) {
496                 lfid[1] = ntohl(ep->fid.vnode);
497                 lfid[2] = ntohl(ep->fid.vunique);
498                 code = Create(toFile, tname, lfid);
499                 if (code) {
500                     printf
501                         ("Create of %s returned code %d, skipping to next hash chain.\n",
502                          tname, code);
503                     DRelease((struct buffer *)ep, 0);
504                     break;
505                 }
506             }
507             DRelease((struct buffer *)ep, 0);
508         }
509     }
510
511     /* Clean up things. */
512     DRelease((struct buffer *)dhp, 0);
513     return 0;
514 }