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,
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.
16 #include <afsconfig.h>
17 #include <afs/param.h>
22 #include <sys/types.h>
27 #include <netinet/in.h>
33 /* Defined in vol/vol-salvage.c */
34 extern void Log(const char *format, ...);
35 /* Defined in vol/physio.c */
36 extern void Die(char *);
38 #define printf Log /* To make it work with volume salvager */
40 /* This routine is called with one parameter, the id (the same thing that is
41 * passed to physio or the buffer package) of a directory to check. It
42 * returns 1 if the directory looks good, and 0 otherwise. */
46 extern afs_int32 DErrno;
48 /* figure out how many pages in use in a directory, given ptr to its (locked)
51 ComputeUsedPages(register struct DirHeader *dhp)
53 register afs_int32 usedPages, i;
55 if (dhp->header.pgcount != 0) {
57 usedPages = ntohs(dhp->header.pgcount);
61 for (i = 0; i < MAXPAGES; i++) {
62 if (dhp->alloMap[i] == EPP) {
73 /* returns true if something went wrong checking, or if dir is fine. Returns
74 * false if we *know* that the dir is bad.
79 struct DirHeader *dhp;
80 struct PageHeader *pp;
83 int havedot = 0, havedotdot = 0;
84 int usedPages, count, entry;
85 char eaMap[BIGMAXPAGES * EPP / 8]; /* Change eaSize initialization below, too. */
87 afs_int32 entcount, maxents;
90 eaSize = BIGMAXPAGES * EPP / 8;
92 /* Read the directory header */
93 dhp = (struct DirHeader *)DRead(file, 0);
95 /* if DErrno is 0, then we know that the read worked, but was short,
96 * and the damage is permanent. Otherwise, we got an I/O or programming
97 * error. Claim the dir is OK, but log something.
100 printf("Could not read first page in directory (%d)\n", DErrno);
104 printf("First page in directory does not exist.\n");
108 /* Check magic number for first page */
109 if (dhp->header.tag != htons(1234)) {
110 printf("Bad first pageheader magic number.\n");
111 DRelease((struct buffer *)dhp, 0);
115 /* Verify that the number of free entries in each directory page
116 * is within range (0-EPP). Also ensure directory is contiguous:
117 * Once the first alloMap entry with EPP free entries is found,
118 * the rest should match.
120 up = 0; /* count of used pages if total pages < MAXPAGES */
121 k = 0; /* found last page */
122 for (i = 0; i < MAXPAGES; i++) {
125 /* Check if in range */
127 if ((j < 0) || (j > EPP - (13 + 2))) {
128 /* First page's dirheader uses 13 entries and at least
129 * two must exist for "." and ".."
131 printf("The dir header alloc map for page %d is bad.\n", i);
132 DRelease((struct buffer *)dhp, 0);
136 if ((j < 0) || (j > EPP)) {
137 printf("The dir header alloc map for page %d is bad.\n", i);
138 DRelease((struct buffer *)dhp, 0);
143 /* Check if contiguous */
144 if (k) { /* last page found */
145 if (j != EPP) { /* remaining entries must be EPP */
147 ("A partially-full page occurs in slot %d, after the dir end.\n",
149 DRelease((struct buffer *)dhp, 0);
152 } else if (j == EPP) { /* is this the last page */
154 } else { /* a used page */
155 up++; /* keep count */
159 /* Compute number of used directory pages and max entries in all
160 ** those pages, the value of 'up' must be less than pgcount. The above
161 ** loop only checks the first MAXPAGES in a directory. An alloMap does
162 ** not exists for pages between MAXPAGES and BIGMAXPAGES */
163 usedPages = ComputeUsedPages(dhp);
164 if (usedPages < up) {
166 ("Count of used directory pages does not match count in directory header\n");
167 DRelease((struct buffer *)dhp, 0);
171 /* For each directory page, check the magic number in each page
172 * header, and check that number of free entries (from freebitmap)
173 * matches the count in the alloMap from directory header.
175 for (i = 0; i < usedPages; i++) {
176 /* Read the page header */
177 pp = (struct PageHeader *)DRead(file, i);
179 DRelease((struct buffer *)dhp, 0);
181 /* couldn't read page, but not because it wasn't there permanently */
182 printf("Failed to read dir page %d (errno %d)\n", i, DErrno);
186 printf("Directory shorter than alloMap indicates (page %d)\n", i);
190 /* check the tag field */
191 if (pp->tag != htons(1234)) {
192 printf("Directory page %d has a bad magic number.\n", i);
193 DRelease((struct buffer *)pp, 0);
194 DRelease((struct buffer *)dhp, 0);
198 /* Count the number of entries allocated in this single
199 * directory page using the freebitmap in the page header.
202 for (j = 0; j < EPP / 8; j++) {
203 k = pp->freebitmap[j];
221 count = EPP - count; /* Change to count of free entries */
223 /* Now check that the count of free entries matches the count in the alloMap */
224 if ((i < MAXPAGES) && ((count & 0xff) != (dhp->alloMap[i] & 0xff))) {
226 ("Header alloMap count doesn't match count in freebitmap for page %d.\n",
228 DRelease((struct buffer *)pp, 0);
229 DRelease((struct buffer *)dhp, 0);
233 DRelease((struct buffer *)pp, 0);
236 /* Initialize the in-memory freebit map for all pages. */
237 for (i = 0; i < eaSize; i++) {
239 if (i < usedPages * (EPP / 8)) {
241 eaMap[i] = 0xff; /* A dir header uses first 13 entries */
243 eaMap[i] = 0x1f; /* A dir header uses first 13 entries */
244 } else if ((i % 8) == 0) {
245 eaMap[i] = 0x01; /* A page header uses only first entry */
249 maxents = usedPages * EPP;
251 /* Walk down all the hash lists, ensuring that each flag field has FFIRST
252 * in it. Mark the appropriate bits in the in-memory freebit map.
253 * Check that the name is in the right hash bucket.
254 * Also check for loops in the hash chain by counting the entries.
256 for (entcount = 0, i = 0; i < NHASHENT; i++) {
257 for (entry = ntohs(dhp->hashTable[i]); entry; entry = ne) {
258 /* Verify that the entry is within range */
259 if (entry < 0 || entry >= maxents) {
260 printf("Out-of-range hash id %d in chain %d.\n", entry, i);
261 DRelease((struct buffer *)dhp, 0);
265 /* Read the directory entry */
267 ep = GetBlob(file, entry);
270 /* something went wrong reading the page, but it wasn't
271 * really something wrong with the dir that we can fix.
273 printf("Could not get dir blob %d (errno %d)\n", entry,
275 DRelease((struct buffer *)dhp, 0);
278 printf("Invalid hash id %d in chain %d.\n", entry, i);
279 DRelease((struct buffer *)dhp, 0);
282 ne = ntohs(ep->next);
284 /* There can't be more than maxents entries */
285 if (++entcount >= maxents) {
286 printf("Directory's hash chain %d is circular.\n", i);
287 DRelease((struct buffer *)ep, 0);
288 DRelease((struct buffer *)dhp, 0);
292 /* A null name is no good */
293 if (ep->name[0] == '\000') {
294 printf("Dir entry %x in chain %d has bogus (null) name.\n",
296 DRelease((struct buffer *)ep, 0);
297 DRelease((struct buffer *)dhp, 0);
301 /* The entry flag better be FFIRST */
302 if (ep->flag != FFIRST) {
303 printf("Dir entry %x in chain %d has bogus flag field.\n", (int)ep,
305 DRelease((struct buffer *)ep, 0);
306 DRelease((struct buffer *)dhp, 0);
310 /* Check the size of the name */
311 j = strlen(ep->name);
312 if (j >= MAXENAME) { /* MAXENAME counts the null */
313 printf("Dir entry %x in chain %d has too-long name.\n", (int)ep,
315 DRelease((struct buffer *)ep, 0);
316 DRelease((struct buffer *)dhp, 0);
320 /* The name used up k directory entries, set the bit in our in-memory
321 * freebitmap for each entry used by the name.
323 k = NameBlobs(ep->name);
324 for (j = 0; j < k; j++) {
325 eaMap[(entry + j) >> 3] |= (1 << ((entry + j) & 7));
328 /* Hash the name and make sure it is in the correct name hash */
329 if ((j = DirHash(ep->name)) != i) {
331 ("Dir entry %x should be in hash bucket %d but IS in %d.\n",
333 DRelease((struct buffer *)ep, 0);
334 DRelease((struct buffer *)dhp, 0);
338 /* Check that if this is entry 13 (the 1st entry), then name must be "." */
340 if (strcmp(ep->name, ".") == 0) {
344 ("Dir entry %x, index 13 has name '%s' should be '.'\n",
346 DRelease((struct buffer *)ep, 0);
347 DRelease((struct buffer *)dhp, 0);
352 /* Check that if this is entry 14 (the 2nd entry), then name must be ".." */
354 if (strcmp(ep->name, "..") == 0) {
358 ("Dir entry %x, index 14 has name '%s' should be '..'\n",
360 DRelease((struct buffer *)ep, 0);
361 DRelease((struct buffer *)dhp, 0);
366 /* CHECK FOR DUPLICATE NAMES? */
368 DRelease((struct buffer *)ep, 0);
372 /* Verify that we found '.' and '..' in the correct place */
373 if (!havedot || !havedotdot) {
375 ("Directory entry '.' or '..' does not exist or is in the wrong index.\n");
376 DRelease((struct buffer *)dhp, 0);
380 /* The in-memory freebit map has been computed. Check that it
381 * matches the one in the page header.
382 * Note that if this matches, alloMap has already been checked against it.
384 for (i = 0; i < usedPages; i++) {
388 ("Failed on second attempt to read dir page %d (errno %d)\n",
390 DRelease((struct buffer *)dhp, 0);
391 /* if DErrno is 0, then the dir is really bad, and we return dir *not* OK.
392 * otherwise, we want to return true (1), meaning the dir isn't known
393 * to be bad (we can't tell, since I/Os are failing.
398 return 0; /* dir is really shorter */
401 count = i * (EPP / 8);
402 for (j = 0; j < EPP / 8; j++) {
403 if (eaMap[count + j] != pp->freebitmap[j]) {
405 ("Entry freebitmap error, page %d, map offset %d, %x should be %x.\n",
406 i, j, pp->freebitmap[j], eaMap[count + j]);
407 DRelease((struct buffer *)pp, 0);
408 DRelease((struct buffer *)dhp, 0);
413 DRelease((struct buffer *)pp, 0);
416 /* Finally cleanup and return. */
417 DRelease((struct buffer *)dhp, 0);
421 /* This routine is called with six parameters. The first is the id of
422 * the original, currently suspect, directory. The second is the file
423 * id of the place the salvager should place the new, fixed, directory.
424 * The third and the fourth parameters are the vnode number and the
425 * uniquifier of the currently suspect directory. The fifth and the
426 * sixth parameters are the vnode number and the uniquifier of the
430 DirSalvage(char *fromFile, char *toFile, afs_int32 vn, afs_int32 vu,
431 afs_int32 pvn, afs_int32 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",
484 DRelease((struct buffer *)dhp, 0);
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",
505 DRelease((struct buffer *)ep, 0);
509 DRelease((struct buffer *)ep, 0);
513 /* Clean up things. */
514 DRelease((struct buffer *)dhp, 0);