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 struct DirEntry *GetBlob();
42 struct PageHeader *DRead();
43 extern afs_int32 DErrno;
44 int NameBlobs(), Lookup(), Create(), MakeDir();
46 /* figure out how many pages in use in a directory, given ptr to its (locked) header */
49 register struct DirHeader *dhp;
51 register afs_int32 usedPages, i;
53 if (dhp->header.pgcount != 0) {
55 usedPages = ntohs(dhp->header.pgcount);
59 for (i = 0; i < MAXPAGES; i++) {
60 if (dhp->alloMap[i] == EPP) {
71 /* returns true if something went wrong checking, or if dir is fine. Returns
72 * false if we *know* that the dir is bad.
78 struct DirHeader *dhp;
79 struct PageHeader *pp;
82 int havedot = 0, havedotdot = 0;
83 int usedPages, count, entry;
84 char eaMap[BIGMAXPAGES * EPP / 8]; /* Change eaSize initialization below, too. */
86 afs_int32 entcount, maxents;
89 eaSize = BIGMAXPAGES * EPP / 8;
91 /* Read the directory header */
92 dhp = (struct DirHeader *)DRead(file, 0);
94 /* if DErrno is 0, then we know that the read worked, but was short,
95 * and the damage is permanent. Otherwise, we got an I/O or programming
96 * error. Claim the dir is OK, but log something.
99 printf("Could not read first page in directory (%d)\n", DErrno);
103 printf("First page in directory does not exist.\n");
107 /* Check magic number for first page */
108 if (dhp->header.tag != htons(1234)) {
109 printf("Bad first pageheader magic number.\n");
114 /* Verify that the number of free entries in each directory page
115 * is within range (0-EPP). Also ensure directory is contiguous:
116 * Once the first alloMap entry with EPP free entries is found,
117 * the rest should match.
119 up = 0; /* count of used pages if total pages < MAXPAGES */
120 k = 0; /* found last page */
121 for (i = 0; i < MAXPAGES; i++) {
124 /* Check if in range */
126 if ((j < 0) || (j > EPP - (13 + 2))) {
127 /* First page's dirheader uses 13 entries and at least
128 * two must exist for "." and ".."
130 printf("The dir header alloc map for page %d is bad.\n", i);
135 if ((j < 0) || (j > EPP)) {
136 printf("The dir header alloc map for page %d is bad.\n", i);
142 /* Check if contiguous */
143 if (k) { /* last page found */
144 if (j != EPP) { /* remaining entries must be EPP */
146 ("A partially-full page occurs in slot %d, after the dir end.\n",
151 } else if (j == EPP) { /* is this the last page */
153 } else { /* a used page */
154 up++; /* keep count */
158 /* Compute number of used directory pages and max entries in all
159 ** those pages, the value of 'up' must be less than pgcount. The above
160 ** loop only checks the first MAXPAGES in a directory. An alloMap does
161 ** not exists for pages between MAXPAGES and BIGMAXPAGES */
162 usedPages = ComputeUsedPages(dhp);
163 if (usedPages < up) {
165 ("Count of used directory pages does not match count in directory header\n");
170 /* For each directory page, check the magic number in each page
171 * header, and check that number of free entries (from freebitmap)
172 * matches the count in the alloMap from directory header.
174 for (i = 0; i < usedPages; i++) {
175 /* Read the page header */
176 pp = (struct PageHeader *)DRead(file, i);
180 /* couldn't read page, but not because it wasn't there permanently */
181 printf("Failed to read dir page %d (errno %d)\n", i, DErrno);
185 printf("Directory shorter than alloMap indicates (page %d)\n", i);
189 /* check the tag field */
190 if (pp->tag != htons(1234)) {
191 printf("Directory page %d has a bad magic number.\n", i);
197 /* Count the number of entries allocated in this single
198 * directory page using the freebitmap in the page header.
201 for (j = 0; j < EPP / 8; j++) {
202 k = pp->freebitmap[j];
220 count = EPP - count; /* Change to count of free entries */
222 /* Now check that the count of free entries matches the count in the alloMap */
223 if ((i < MAXPAGES) && ((count & 0xff) != (dhp->alloMap[i] & 0xff))) {
225 ("Header alloMap count doesn't match count in freebitmap for page %d.\n",
235 /* Initialize the in-memory freebit map for all pages. */
236 for (i = 0; i < eaSize; i++) {
238 if (i < usedPages * (EPP / 8)) {
240 eaMap[i] = 0xff; /* A dir header uses first 13 entries */
242 eaMap[i] = 0x1f; /* A dir header uses first 13 entries */
243 } else if ((i % 8) == 0) {
244 eaMap[i] = 0x01; /* A page header uses only first entry */
248 maxents = usedPages * EPP;
250 /* Walk down all the hash lists, ensuring that each flag field has FFIRST
251 * in it. Mark the appropriate bits in the in-memory freebit map.
252 * Check that the name is in the right hash bucket.
253 * Also check for loops in the hash chain by counting the entries.
255 for (entcount = 0, i = 0; i < NHASHENT; i++) {
256 for (entry = ntohs(dhp->hashTable[i]); entry; entry = ne) {
257 /* Verify that the entry is within range */
258 if (entry < 0 || entry >= maxents) {
259 printf("Out-of-range hash id %d in chain %d.\n", entry, i);
264 /* Read the directory entry */
266 ep = GetBlob(file, entry);
269 /* something went wrong reading the page, but it wasn't
270 * really something wrong with the dir that we can fix.
272 printf("Could not get dir blob %d (errno %d)\n", entry,
277 printf("Invalid hash id %d in chain %d.\n", entry, i);
281 ne = ntohs(ep->next);
283 /* There can't be more than maxents entries */
284 if (++entcount >= maxents) {
285 printf("Directory's hash chain %d is circular.\n", i);
291 /* A null name is no good */
292 if (ep->name[0] == '\000') {
293 printf("Dir entry %x in chain %d has bogus (null) name.\n",
300 /* The entry flag better be FFIRST */
301 if (ep->flag != FFIRST) {
302 printf("Dir entry %x in chain %d has bogus flag field.\n", ep,
309 /* Check the size of the name */
310 j = strlen(ep->name);
311 if (j >= MAXENAME) { /* MAXENAME counts the null */
312 printf("Dir entry %x in chain %d has too-long name.\n", ep,
319 /* The name used up k directory entries, set the bit in our in-memory
320 * freebitmap for each entry used by the name.
322 k = NameBlobs(ep->name);
323 for (j = 0; j < k; j++) {
324 eaMap[(entry + j) >> 3] |= (1 << ((entry + j) & 7));
327 /* Hash the name and make sure it is in the correct name hash */
328 if ((j = DirHash(ep->name)) != i) {
330 ("Dir entry %x should be in hash bucket %d but IS in %d.\n",
337 /* Check that if this is entry 13 (the 1st entry), then name must be "." */
339 if (strcmp(ep->name, ".") == 0) {
343 ("Dir entry %x, index 13 has name '%s' should be '.'\n",
351 /* Check that if this is entry 14 (the 2nd entry), then name must be ".." */
353 if (strcmp(ep->name, "..") == 0) {
357 ("Dir entry %x, index 14 has name '%s' should be '..'\n",
365 /* CHECK FOR DUPLICATE NAMES? */
371 /* Verify that we found '.' and '..' in the correct place */
372 if (!havedot || !havedotdot) {
374 ("Directory entry '.' or '..' does not exist or is in the wrong index.\n");
379 /* The in-memory freebit map has been computed. Check that it
380 * matches the one in the page header.
381 * Note that if this matches, alloMap has already been checked against it.
383 for (i = 0; i < usedPages; i++) {
387 ("Failed on second attempt to read dir page %d (errno %d)\n",
390 /* if DErrno is 0, then the dir is really bad, and we return dir *not* OK.
391 * otherwise, we want to return true (1), meaning the dir isn't known
392 * to be bad (we can't tell, since I/Os are failing.
397 return 0; /* dir is really shorter */
400 count = i * (EPP / 8);
401 for (j = 0; j < EPP / 8; j++) {
402 if (eaMap[count + j] != pp->freebitmap[j]) {
404 ("Entry freebitmap error, page %d, map offset %d, %x should be %x.\n",
405 i, j, pp->freebitmap[j], eaMap[count + j]);
415 /* Finally cleanup and return. */
420 /* This routine is called with six parameters. The first is the id of
421 * the original, currently suspect, directory. The second is the file
422 * id of the place the salvager should place the new, fixed, directory.
423 * The third and the fourth parameters are the vnode number and the
424 * uniquifier of the currently suspect directory. The fifth and the
425 * sixth parameters are the vnode number and the uniquifier of the
429 DirSalvage(fromFile, toFile, vn, vu, pvn, pvu)
430 char *fromFile, *toFile;
431 afs_int32 vn, vu, pvn, pvu;
433 /* First do a MakeDir on the target. */
434 afs_int32 dot[3], dotdot[3], lfid[3], code, usedPages;
438 struct DirHeader *dhp;
442 memset(dot, 0, sizeof(dot));
443 memset(dotdot, 0, sizeof(dotdot));
449 MakeDir(toFile, dot, dotdot); /* Returns no error code. */
451 /* Find out how many pages are valid, using stupid heuristic since DRead
452 * never returns null.
454 dhp = (struct DirHeader *)DRead(fromFile, 0);
456 printf("Failed to read first page of fromDir!\n");
457 /* if DErrno != 0, then our call failed and we should let our
458 * caller know that there's something wrong with the new dir. If not,
459 * then we return here anyway, with an empty, but at least good, directory.
464 usedPages = ComputeUsedPages(dhp);
466 /* Finally, enumerate all the entries, doing a create on them. */
467 for (i = 0; i < NHASHENT; i++) {
468 entry = ntohs(dhp->hashTable[i]);
472 if (entry < 0 || entry >= usedPages * EPP) {
474 ("Warning: bogus hash table entry encountered, ignoring.\n");
478 ep = GetBlob(fromFile, entry);
482 ("can't continue down hash chain (entry %d, errno %d)\n",
488 ("Warning: bogus hash chain encountered, switching to next.\n");
491 strncpy(tname, ep->name, MAXENAME);
492 tname[MAXENAME - 1] = '\000'; /* just in case */
495 entry = ntohs(ep->next);
497 if ((strcmp(tp, ".") != 0) && (strcmp(tp, "..") != 0)) {
498 lfid[1] = ntohl(ep->fid.vnode);
499 lfid[2] = ntohl(ep->fid.vunique);
500 code = Create(toFile, tname, lfid);
503 ("Create of %s returned code %d, skipping to next hash chain.\n",
513 /* Clean up things. */