2 * Copyright 2000, International Business Machines Corporation and others.
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
10 /* This is the directory salvager. It consists of two routines. The first, DirOK, checks to see if the directory looks good. If the directory does NOT look good, the approved procedure is to then call Salvage, which copies all the good entries from the damaged dir into a new directory. */
12 #include <afsconfig.h>
13 #include <afs/param.h>
18 #include <sys/types.h>
23 #include <netinet/in.h>
35 #define printf Log /* To make it work with volume salvager */
37 /* This routine is called with one parameter, the id (the same thing that is passed to physio or the buffer package) of a directory to check. It returns 1 if the directory looks good, and 0 otherwise. */
41 extern afs_int32 DErrno;
43 /* figure out how many pages in use in a directory, given ptr to its (locked) header */
46 register struct DirHeader *dhp;
48 register afs_int32 usedPages, i;
50 if (dhp->header.pgcount != 0) {
52 usedPages = ntohs(dhp->header.pgcount);
56 for (i = 0; i < MAXPAGES; i++) {
57 if (dhp->alloMap[i] == EPP) {
68 /* returns true if something went wrong checking, or if dir is fine. Returns
69 * false if we *know* that the dir is bad.
75 struct DirHeader *dhp;
76 struct PageHeader *pp;
79 int havedot = 0, havedotdot = 0;
80 int usedPages, count, entry;
81 char eaMap[BIGMAXPAGES * EPP / 8]; /* Change eaSize initialization below, too. */
83 afs_int32 entcount, maxents;
86 eaSize = BIGMAXPAGES * EPP / 8;
88 /* Read the directory header */
89 dhp = (struct DirHeader *)DRead(file, 0);
91 /* if DErrno is 0, then we know that the read worked, but was short,
92 * and the damage is permanent. Otherwise, we got an I/O or programming
93 * error. Claim the dir is OK, but log something.
96 printf("Could not read first page in directory (%d)\n", DErrno);
100 printf("First page in directory does not exist.\n");
104 /* Check magic number for first page */
105 if (dhp->header.tag != htons(1234)) {
106 printf("Bad first pageheader magic number.\n");
111 /* Verify that the number of free entries in each directory page
112 * is within range (0-EPP). Also ensure directory is contiguous:
113 * Once the first alloMap entry with EPP free entries is found,
114 * the rest should match.
116 up = 0; /* count of used pages if total pages < MAXPAGES */
117 k = 0; /* found last page */
118 for (i = 0; i < MAXPAGES; i++) {
121 /* Check if in range */
123 if ((j < 0) || (j > EPP - (13 + 2))) {
124 /* First page's dirheader uses 13 entries and at least
125 * two must exist for "." and ".."
127 printf("The dir header alloc map for page %d is bad.\n", i);
132 if ((j < 0) || (j > EPP)) {
133 printf("The dir header alloc map for page %d is bad.\n", i);
139 /* Check if contiguous */
140 if (k) { /* last page found */
141 if (j != EPP) { /* remaining entries must be EPP */
143 ("A partially-full page occurs in slot %d, after the dir end.\n",
148 } else if (j == EPP) { /* is this the last page */
150 } else { /* a used page */
151 up++; /* keep count */
155 /* Compute number of used directory pages and max entries in all
156 ** those pages, the value of 'up' must be less than pgcount. The above
157 ** loop only checks the first MAXPAGES in a directory. An alloMap does
158 ** not exists for pages between MAXPAGES and BIGMAXPAGES */
159 usedPages = ComputeUsedPages(dhp);
160 if (usedPages < up) {
162 ("Count of used directory pages does not match count in directory header\n");
167 /* For each directory page, check the magic number in each page
168 * header, and check that number of free entries (from freebitmap)
169 * matches the count in the alloMap from directory header.
171 for (i = 0; i < usedPages; i++) {
172 /* Read the page header */
173 pp = (struct PageHeader *)DRead(file, i);
177 /* couldn't read page, but not because it wasn't there permanently */
178 printf("Failed to read dir page %d (errno %d)\n", i, DErrno);
182 printf("Directory shorter than alloMap indicates (page %d)\n", i);
186 /* check the tag field */
187 if (pp->tag != htons(1234)) {
188 printf("Directory page %d has a bad magic number.\n", i);
194 /* Count the number of entries allocated in this single
195 * directory page using the freebitmap in the page header.
198 for (j = 0; j < EPP / 8; j++) {
199 k = pp->freebitmap[j];
217 count = EPP - count; /* Change to count of free entries */
219 /* Now check that the count of free entries matches the count in the alloMap */
220 if ((i < MAXPAGES) && ((count & 0xff) != (dhp->alloMap[i] & 0xff))) {
222 ("Header alloMap count doesn't match count in freebitmap for page %d.\n",
232 /* Initialize the in-memory freebit map for all pages. */
233 for (i = 0; i < eaSize; i++) {
235 if (i < usedPages * (EPP / 8)) {
237 eaMap[i] = 0xff; /* A dir header uses first 13 entries */
239 eaMap[i] = 0x1f; /* A dir header uses first 13 entries */
240 } else if ((i % 8) == 0) {
241 eaMap[i] = 0x01; /* A page header uses only first entry */
245 maxents = usedPages * EPP;
247 /* Walk down all the hash lists, ensuring that each flag field has FFIRST
248 * in it. Mark the appropriate bits in the in-memory freebit map.
249 * Check that the name is in the right hash bucket.
250 * Also check for loops in the hash chain by counting the entries.
252 for (entcount = 0, i = 0; i < NHASHENT; i++) {
253 for (entry = ntohs(dhp->hashTable[i]); entry; entry = ne) {
254 /* Verify that the entry is within range */
255 if (entry < 0 || entry >= maxents) {
256 printf("Out-of-range hash id %d in chain %d.\n", entry, i);
261 /* Read the directory entry */
263 ep = GetBlob(file, entry);
266 /* something went wrong reading the page, but it wasn't
267 * really something wrong with the dir that we can fix.
269 printf("Could not get dir blob %d (errno %d)\n", entry,
274 printf("Invalid hash id %d in chain %d.\n", entry, i);
278 ne = ntohs(ep->next);
280 /* There can't be more than maxents entries */
281 if (++entcount >= maxents) {
282 printf("Directory's hash chain %d is circular.\n", i);
288 /* A null name is no good */
289 if (ep->name[0] == '\000') {
290 printf("Dir entry %x in chain %d has bogus (null) name.\n",
297 /* The entry flag better be FFIRST */
298 if (ep->flag != FFIRST) {
299 printf("Dir entry %x in chain %d has bogus flag field.\n", (int)ep,
306 /* Check the size of the name */
307 j = strlen(ep->name);
308 if (j >= MAXENAME) { /* MAXENAME counts the null */
309 printf("Dir entry %x in chain %d has too-long name.\n", (int)ep,
316 /* The name used up k directory entries, set the bit in our in-memory
317 * freebitmap for each entry used by the name.
319 k = NameBlobs(ep->name);
320 for (j = 0; j < k; j++) {
321 eaMap[(entry + j) >> 3] |= (1 << ((entry + j) & 7));
324 /* Hash the name and make sure it is in the correct name hash */
325 if ((j = DirHash(ep->name)) != i) {
327 ("Dir entry %x should be in hash bucket %d but IS in %d.\n",
334 /* Check that if this is entry 13 (the 1st entry), then name must be "." */
336 if (strcmp(ep->name, ".") == 0) {
340 ("Dir entry %x, index 13 has name '%s' should be '.'\n",
348 /* Check that if this is entry 14 (the 2nd entry), then name must be ".." */
350 if (strcmp(ep->name, "..") == 0) {
354 ("Dir entry %x, index 14 has name '%s' should be '..'\n",
362 /* CHECK FOR DUPLICATE NAMES? */
368 /* Verify that we found '.' and '..' in the correct place */
369 if (!havedot || !havedotdot) {
371 ("Directory entry '.' or '..' does not exist or is in the wrong index.\n");
376 /* The in-memory freebit map has been computed. Check that it
377 * matches the one in the page header.
378 * Note that if this matches, alloMap has already been checked against it.
380 for (i = 0; i < usedPages; i++) {
384 ("Failed on second attempt to read dir page %d (errno %d)\n",
387 /* if DErrno is 0, then the dir is really bad, and we return dir *not* OK.
388 * otherwise, we want to return true (1), meaning the dir isn't known
389 * to be bad (we can't tell, since I/Os are failing.
394 return 0; /* dir is really shorter */
397 count = i * (EPP / 8);
398 for (j = 0; j < EPP / 8; j++) {
399 if (eaMap[count + j] != pp->freebitmap[j]) {
401 ("Entry freebitmap error, page %d, map offset %d, %x should be %x.\n",
402 i, j, pp->freebitmap[j], eaMap[count + j]);
412 /* Finally cleanup and return. */
417 /* This routine is called with six parameters. The first is the id of
418 * the original, currently suspect, directory. The second is the file
419 * id of the place the salvager should place the new, fixed, directory.
420 * The third and the fourth parameters are the vnode number and the
421 * uniquifier of the currently suspect directory. The fifth and the
422 * sixth parameters are the vnode number and the uniquifier of the
426 DirSalvage(fromFile, toFile, vn, vu, pvn, pvu)
427 char *fromFile, *toFile;
428 afs_int32 vn, vu, pvn, pvu;
430 /* First do a MakeDir on the target. */
431 afs_int32 dot[3], dotdot[3], lfid[3], code, usedPages;
435 struct DirHeader *dhp;
439 memset(dot, 0, sizeof(dot));
440 memset(dotdot, 0, sizeof(dotdot));
446 MakeDir(toFile, dot, dotdot); /* Returns no error code. */
448 /* Find out how many pages are valid, using stupid heuristic since DRead
449 * never returns null.
451 dhp = (struct DirHeader *)DRead(fromFile, 0);
453 printf("Failed to read first page of fromDir!\n");
454 /* if DErrno != 0, then our call failed and we should let our
455 * caller know that there's something wrong with the new dir. If not,
456 * then we return here anyway, with an empty, but at least good, directory.
461 usedPages = ComputeUsedPages(dhp);
463 /* Finally, enumerate all the entries, doing a create on them. */
464 for (i = 0; i < NHASHENT; i++) {
465 entry = ntohs(dhp->hashTable[i]);
469 if (entry < 0 || entry >= usedPages * EPP) {
471 ("Warning: bogus hash table entry encountered, ignoring.\n");
475 ep = GetBlob(fromFile, entry);
479 ("can't continue down hash chain (entry %d, errno %d)\n",
485 ("Warning: bogus hash chain encountered, switching to next.\n");
488 strncpy(tname, ep->name, MAXENAME);
489 tname[MAXENAME - 1] = '\000'; /* just in case */
492 entry = ntohs(ep->next);
494 if ((strcmp(tp, ".") != 0) && (strcmp(tp, "..") != 0)) {
495 lfid[1] = ntohl(ep->fid.vnode);
496 lfid[2] = ntohl(ep->fid.vunique);
497 code = Create(toFile, tname, lfid);
500 ("Create of %s returned code %d, skipping to next hash chain.\n",
510 /* Clean up things. */