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>
29 #define printf Log /* To make it work with volume salvager */
31 /* 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. */
35 extern afs_int32 DErrno;
37 /* figure out how many pages in use in a directory, given ptr to its (locked) header */
40 register struct DirHeader *dhp;
42 register afs_int32 usedPages, i;
44 if (dhp->header.pgcount != 0) {
46 usedPages = ntohs(dhp->header.pgcount);
50 for (i = 0; i < MAXPAGES; i++) {
51 if (dhp->alloMap[i] == EPP) {
62 /* returns true if something went wrong checking, or if dir is fine. Returns
63 * false if we *know* that the dir is bad.
69 struct DirHeader *dhp;
70 struct PageHeader *pp;
73 int havedot = 0, havedotdot = 0;
74 int usedPages, count, entry;
75 char eaMap[BIGMAXPAGES * EPP / 8]; /* Change eaSize initialization below, too. */
77 afs_int32 entcount, maxents;
80 eaSize = BIGMAXPAGES * EPP / 8;
82 /* Read the directory header */
83 dhp = (struct DirHeader *)DRead(file, 0);
85 /* if DErrno is 0, then we know that the read worked, but was short,
86 * and the damage is permanent. Otherwise, we got an I/O or programming
87 * error. Claim the dir is OK, but log something.
90 printf("Could not read first page in directory (%d)\n", DErrno);
94 printf("First page in directory does not exist.\n");
98 /* Check magic number for first page */
99 if (dhp->header.tag != htons(1234)) {
100 printf("Bad first pageheader magic number.\n");
105 /* Verify that the number of free entries in each directory page
106 * is within range (0-EPP). Also ensure directory is contiguous:
107 * Once the first alloMap entry with EPP free entries is found,
108 * the rest should match.
110 up = 0; /* count of used pages if total pages < MAXPAGES */
111 k = 0; /* found last page */
112 for (i = 0; i < MAXPAGES; i++) {
115 /* Check if in range */
117 if ((j < 0) || (j > EPP - (13 + 2))) {
118 /* First page's dirheader uses 13 entries and at least
119 * two must exist for "." and ".."
121 printf("The dir header alloc map for page %d is bad.\n", i);
126 if ((j < 0) || (j > EPP)) {
127 printf("The dir header alloc map for page %d is bad.\n", i);
133 /* Check if contiguous */
134 if (k) { /* last page found */
135 if (j != EPP) { /* remaining entries must be EPP */
137 ("A partially-full page occurs in slot %d, after the dir end.\n",
142 } else if (j == EPP) { /* is this the last page */
144 } else { /* a used page */
145 up++; /* keep count */
149 /* Compute number of used directory pages and max entries in all
150 ** those pages, the value of 'up' must be less than pgcount. The above
151 ** loop only checks the first MAXPAGES in a directory. An alloMap does
152 ** not exists for pages between MAXPAGES and BIGMAXPAGES */
153 usedPages = ComputeUsedPages(dhp);
154 if (usedPages < up) {
156 ("Count of used directory pages does not match count in directory header\n");
161 /* For each directory page, check the magic number in each page
162 * header, and check that number of free entries (from freebitmap)
163 * matches the count in the alloMap from directory header.
165 for (i = 0; i < usedPages; i++) {
166 /* Read the page header */
167 pp = (struct PageHeader *)DRead(file, i);
171 /* couldn't read page, but not because it wasn't there permanently */
172 printf("Failed to read dir page %d (errno %d)\n", i, DErrno);
176 printf("Directory shorter than alloMap indicates (page %d)\n", i);
180 /* check the tag field */
181 if (pp->tag != htons(1234)) {
182 printf("Directory page %d has a bad magic number.\n", i);
188 /* Count the number of entries allocated in this single
189 * directory page using the freebitmap in the page header.
192 for (j = 0; j < EPP / 8; j++) {
193 k = pp->freebitmap[j];
211 count = EPP - count; /* Change to count of free entries */
213 /* Now check that the count of free entries matches the count in the alloMap */
214 if ((i < MAXPAGES) && ((count & 0xff) != (dhp->alloMap[i] & 0xff))) {
216 ("Header alloMap count doesn't match count in freebitmap for page %d.\n",
226 /* Initialize the in-memory freebit map for all pages. */
227 for (i = 0; i < eaSize; i++) {
229 if (i < usedPages * (EPP / 8)) {
231 eaMap[i] = 0xff; /* A dir header uses first 13 entries */
233 eaMap[i] = 0x1f; /* A dir header uses first 13 entries */
234 } else if ((i % 8) == 0) {
235 eaMap[i] = 0x01; /* A page header uses only first entry */
239 maxents = usedPages * EPP;
241 /* Walk down all the hash lists, ensuring that each flag field has FFIRST
242 * in it. Mark the appropriate bits in the in-memory freebit map.
243 * Check that the name is in the right hash bucket.
244 * Also check for loops in the hash chain by counting the entries.
246 for (entcount = 0, i = 0; i < NHASHENT; i++) {
247 for (entry = ntohs(dhp->hashTable[i]); entry; entry = ne) {
248 /* Verify that the entry is within range */
249 if (entry < 0 || entry >= maxents) {
250 printf("Out-of-range hash id %d in chain %d.\n", entry, i);
255 /* Read the directory entry */
257 ep = GetBlob(file, entry);
260 /* something went wrong reading the page, but it wasn't
261 * really something wrong with the dir that we can fix.
263 printf("Could not get dir blob %d (errno %d)\n", entry,
268 printf("Invalid hash id %d in chain %d.\n", entry, i);
272 ne = ntohs(ep->next);
274 /* There can't be more than maxents entries */
275 if (++entcount >= maxents) {
276 printf("Directory's hash chain %d is circular.\n", i);
282 /* A null name is no good */
283 if (ep->name[0] == '\000') {
284 printf("Dir entry %x in chain %d has bogus (null) name.\n",
291 /* The entry flag better be FFIRST */
292 if (ep->flag != FFIRST) {
293 printf("Dir entry %x in chain %d has bogus flag field.\n", (int)ep,
300 /* Check the size of the name */
301 j = strlen(ep->name);
302 if (j >= MAXENAME) { /* MAXENAME counts the null */
303 printf("Dir entry %x in chain %d has too-long name.\n", (int)ep,
310 /* The name used up k directory entries, set the bit in our in-memory
311 * freebitmap for each entry used by the name.
313 k = NameBlobs(ep->name);
314 for (j = 0; j < k; j++) {
315 eaMap[(entry + j) >> 3] |= (1 << ((entry + j) & 7));
318 /* Hash the name and make sure it is in the correct name hash */
319 if ((j = DirHash(ep->name)) != i) {
321 ("Dir entry %x should be in hash bucket %d but IS in %d.\n",
328 /* Check that if this is entry 13 (the 1st entry), then name must be "." */
330 if (strcmp(ep->name, ".") == 0) {
334 ("Dir entry %x, index 13 has name '%s' should be '.'\n",
342 /* Check that if this is entry 14 (the 2nd entry), then name must be ".." */
344 if (strcmp(ep->name, "..") == 0) {
348 ("Dir entry %x, index 14 has name '%s' should be '..'\n",
356 /* CHECK FOR DUPLICATE NAMES? */
362 /* Verify that we found '.' and '..' in the correct place */
363 if (!havedot || !havedotdot) {
365 ("Directory entry '.' or '..' does not exist or is in the wrong index.\n");
370 /* The in-memory freebit map has been computed. Check that it
371 * matches the one in the page header.
372 * Note that if this matches, alloMap has already been checked against it.
374 for (i = 0; i < usedPages; i++) {
378 ("Failed on second attempt to read dir page %d (errno %d)\n",
381 /* if DErrno is 0, then the dir is really bad, and we return dir *not* OK.
382 * otherwise, we want to return true (1), meaning the dir isn't known
383 * to be bad (we can't tell, since I/Os are failing.
388 return 0; /* dir is really shorter */
391 count = i * (EPP / 8);
392 for (j = 0; j < EPP / 8; j++) {
393 if (eaMap[count + j] != pp->freebitmap[j]) {
395 ("Entry freebitmap error, page %d, map offset %d, %x should be %x.\n",
396 i, j, pp->freebitmap[j], eaMap[count + j]);
406 /* Finally cleanup and return. */
411 /* This routine is called with six parameters. The first is the id of
412 * the original, currently suspect, directory. The second is the file
413 * id of the place the salvager should place the new, fixed, directory.
414 * The third and the fourth parameters are the vnode number and the
415 * uniquifier of the currently suspect directory. The fifth and the
416 * sixth parameters are the vnode number and the uniquifier of the
420 DirSalvage(fromFile, toFile, vn, vu, pvn, pvu)
421 char *fromFile, *toFile;
422 afs_int32 vn, vu, pvn, pvu;
424 /* First do a MakeDir on the target. */
425 afs_int32 dot[3], dotdot[3], lfid[3], code, usedPages;
429 struct DirHeader *dhp;
433 memset(dot, 0, sizeof(dot));
434 memset(dotdot, 0, sizeof(dotdot));
440 MakeDir(toFile, dot, dotdot); /* Returns no error code. */
442 /* Find out how many pages are valid, using stupid heuristic since DRead
443 * never returns null.
445 dhp = (struct DirHeader *)DRead(fromFile, 0);
447 printf("Failed to read first page of fromDir!\n");
448 /* if DErrno != 0, then our call failed and we should let our
449 * caller know that there's something wrong with the new dir. If not,
450 * then we return here anyway, with an empty, but at least good, directory.
455 usedPages = ComputeUsedPages(dhp);
457 /* Finally, enumerate all the entries, doing a create on them. */
458 for (i = 0; i < NHASHENT; i++) {
459 entry = ntohs(dhp->hashTable[i]);
463 if (entry < 0 || entry >= usedPages * EPP) {
465 ("Warning: bogus hash table entry encountered, ignoring.\n");
469 ep = GetBlob(fromFile, entry);
473 ("can't continue down hash chain (entry %d, errno %d)\n",
479 ("Warning: bogus hash chain encountered, switching to next.\n");
482 strncpy(tname, ep->name, MAXENAME);
483 tname[MAXENAME - 1] = '\000'; /* just in case */
486 entry = ntohs(ep->next);
488 if ((strcmp(tp, ".") != 0) && (strcmp(tp, "..") != 0)) {
489 lfid[1] = ntohl(ep->fid.vnode);
490 lfid[2] = ntohl(ep->fid.vunique);
491 code = Create(toFile, tname, lfid);
494 ("Create of %s returned code %d, skipping to next hash chain.\n",
504 /* Clean up things. */