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 <afs/param.h>
13 #include <afsconfig.h>
17 #include <sys/types.h>
22 #include <netinet/in.h>
26 #define printf Log /* To make it work with volume salvager */
28 /* 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. */
32 struct DirEntry *GetBlob();
33 struct PageHeader *DRead();
34 extern afs_int32 DErrno;
35 int NameBlobs(), Lookup(), Create(), MakeDir();
37 /* figure out how many pages in use in a directory, given ptr to its (locked) header */
38 static ComputeUsedPages(dhp)
39 register struct DirHeader *dhp; {
40 register afs_int32 usedPages, i;
42 if (dhp->header.pgcount != 0) {
44 usedPages = ntohs(dhp->header.pgcount);
49 for(i=0; i<MAXPAGES; i++) {
50 if (dhp->alloMap[i] == EPP) {
55 if (usedPages == 0) usedPages = MAXPAGES;
60 /* returns true if something went wrong checking, or if dir is fine. Returns
61 * false if we *know* that the dir is bad.
65 struct DirHeader *dhp;
66 struct PageHeader *pp;
69 int havedot = 0, havedotdot = 0;
70 int usedPages, count, entry;
71 char eaMap[BIGMAXPAGES*EPP/8]; /* Change eaSize initialization below, too. */
73 afs_int32 entcount, maxents;
76 eaSize = BIGMAXPAGES*EPP/8;
78 /* Read the directory header */
79 dhp = (struct DirHeader *) DRead (file, 0);
81 /* if DErrno is 0, then we know that the read worked, but was short,
82 * and the damage is permanent. Otherwise, we got an I/O or programming
83 * error. Claim the dir is OK, but log something.
86 printf("Could not read first page in directory (%d)\n", DErrno);
90 printf("First page in directory does not exist.\n");
94 /* Check magic number for first page */
95 if (dhp->header.tag != htons(1234)) {
96 printf("Bad first pageheader magic number.\n");
101 /* Verify that the number of free entries in each directory page
102 * is within range (0-EPP). Also ensure directory is contiguous:
103 * Once the first alloMap entry with EPP free entries is found,
104 * the rest should match.
106 up = 0; /* count of used pages if total pages < MAXPAGES */
107 k = 0; /* found last page */
108 for(i=0; i<MAXPAGES; i++) {
111 /* Check if in range */
113 if ((j < 0) || (j > EPP-(13+2))) {
114 /* First page's dirheader uses 13 entries and at least
115 * two must exist for "." and ".."
117 printf("The dir header alloc map for page %d is bad.\n", i);
122 if ((j < 0) || (j > EPP)) {
123 printf("The dir header alloc map for page %d is bad.\n", i);
129 /* Check if contiguous */
130 if (k) { /* last page found */
131 if (j != EPP) { /* remaining entries must be EPP */
132 printf("A partially-full page occurs in slot %d, after the dir end.\n", i);
136 } else if (j == EPP) { /* is this the last page */
138 } else { /* a used page */
139 up++; /* keep count */
143 /* Compute number of used directory pages and max entries in all
144 ** those pages, the value of 'up' must be less than pgcount. The above
145 ** loop only checks the first MAXPAGES in a directory. An alloMap does
146 ** not exists for pages between MAXPAGES and BIGMAXPAGES */
147 usedPages = ComputeUsedPages(dhp);
148 if (usedPages < up) {
149 printf("Count of used directory pages does not match count in directory header\n");
154 /* For each directory page, check the magic number in each page
155 * header, and check that number of free entries (from freebitmap)
156 * matches the count in the alloMap from directory header.
158 for (i=0; i<usedPages; i++) {
159 /* Read the page header */
160 pp = (struct PageHeader *) DRead(file, i);
164 /* couldn't read page, but not because it wasn't there permanently */
165 printf("Failed to read dir page %d (errno %d)\n", i, DErrno);
169 printf("Directory shorter than alloMap indicates (page %d)\n", i);
173 /* check the tag field */
174 if (pp->tag != htons(1234)) {
175 printf("Directory page %d has a bad magic number.\n", i);
181 /* Count the number of entries allocated in this single
182 * directory page using the freebitmap in the page header.
185 for(j=0; j<EPP/8; j++) {
186 k = pp->freebitmap[j];
187 if (k & 0x80) count++;
188 if (k & 0x40) count++;
189 if (k & 0x20) count++;
190 if (k & 0x10) count++;
191 if (k & 0x08) count++;
192 if (k & 0x04) count++;
193 if (k & 0x02) count++;
194 if (k & 0x01) count++;
196 count = EPP - count; /* Change to count of free entries */
198 /* Now check that the count of free entries matches the count in the alloMap */
199 if ( (i < MAXPAGES) && ((count & 0xff) != (dhp->alloMap[i] & 0xff)) ) {
200 printf("Header alloMap count doesn't match count in freebitmap for page %d.\n", i);
209 /* Initialize the in-memory freebit map for all pages. */
210 for (i=0; i<eaSize; i++) {
212 if (i < usedPages*(EPP/8)) {
214 eaMap[i] = 0xff; /* A dir header uses first 13 entries */
216 eaMap[i] = 0x1f; /* A dir header uses first 13 entries */
217 } else if ((i%8) == 0) {
218 eaMap[i] = 0x01; /* A page header uses only first entry */
222 maxents = usedPages * EPP;
224 /* Walk down all the hash lists, ensuring that each flag field has FFIRST
225 * in it. Mark the appropriate bits in the in-memory freebit map.
226 * Check that the name is in the right hash bucket.
227 * Also check for loops in the hash chain by counting the entries.
229 for (entcount=0,i=0; i<NHASHENT; i++) {
230 for (entry = ntohs(dhp->hashTable[i]); entry; entry = ne) {
231 /* Verify that the entry is within range */
232 if (entry < 0 || entry >= maxents) {
233 printf("Out-of-range hash id %d in chain %d.\n", entry, i);
238 /* Read the directory entry */
240 ep = GetBlob(file, entry);
243 /* something went wrong reading the page, but it wasn't
244 * really something wrong with the dir that we can fix.
246 printf("Could not get dir blob %d (errno %d)\n", entry, DErrno);
250 printf("Invalid hash id %d in chain %d.\n", entry, i);
254 ne = ntohs(ep->next);
256 /* There can't be more than maxents entries */
257 if (++entcount >= maxents) {
258 printf("Directory's hash chain %d is circular.\n", i);
264 /* A null name is no good */
265 if (ep->name[0] == '\000') {
266 printf("Dir entry %x in chain %d has bogus (null) name.\n", ep, i);
272 /* The entry flag better be FFIRST */
273 if (ep->flag != FFIRST) {
274 printf("Dir entry %x in chain %d has bogus flag field.\n", ep, i);
280 /* Check the size of the name */
281 j = strlen(ep->name);
282 if (j >= MAXENAME) { /* MAXENAME counts the null */
283 printf("Dir entry %x in chain %d has too-long name.\n", ep, i);
289 /* The name used up k directory entries, set the bit in our in-memory
290 * freebitmap for each entry used by the name.
292 k = NameBlobs(ep->name);
293 for (j=0; j<k; j++) {
294 eaMap[(entry+j)>>3] |= (1<<((entry+j)&7));
297 /* Hash the name and make sure it is in the correct name hash */
298 if ((j=DirHash(ep->name)) != i) {
299 printf("Dir entry %x should be in hash bucket %d but IS in %d.\n", ep, j, i);
305 /* Check that if this is entry 13 (the 1st entry), then name must be "." */
307 if (strcmp(ep->name,".") == 0) {
310 printf("Dir entry %x, index 13 has name '%s' should be '.'\n",
318 /* Check that if this is entry 14 (the 2nd entry), then name must be ".." */
320 if (strcmp(ep->name,"..") == 0) {
323 printf("Dir entry %x, index 14 has name '%s' should be '..'\n",
331 /* CHECK FOR DUPLICATE NAMES? */
337 /* Verify that we found '.' and '..' in the correct place */
338 if (!havedot || !havedotdot) {
339 printf("Directory entry '.' or '..' does not exist or is in the wrong index.\n");
344 /* The in-memory freebit map has been computed. Check that it
345 * matches the one in the page header.
346 * Note that if this matches, alloMap has already been checked against it.
348 for(i=0; i<usedPages; i++) {
351 printf("Failed on second attempt to read dir page %d (errno %d)\n", i, DErrno);
353 /* if DErrno is 0, then the dir is really bad, and we return dir *not* OK.
354 * otherwise, we want to return true (1), meaning the dir isn't known
355 * to be bad (we can't tell, since I/Os are failing.
357 if (DErrno != 0) Die("dirok4");
358 else return 0; /* dir is really shorter */
362 for (j=0; j<EPP/8; j++) {
363 if (eaMap[count+j] != pp->freebitmap[j]) {
364 printf("Entry freebitmap error, page %d, map offset %d, %x should be %x.\n",
365 i, j, pp->freebitmap[j], eaMap[count+j]);
375 /* Finally cleanup and return. */
380 /* This routine is called with six parameters. The first is the id of
381 * the original, currently suspect, directory. The second is the file
382 * id of the place the salvager should place the new, fixed, directory.
383 * The third and the fourth parameters are the vnode number and the
384 * uniquifier of the currently suspect directory. The fifth and the
385 * sixth parameters are the vnode number and the uniquifier of the
388 int DirSalvage (fromFile, toFile, vn, vu, pvn, pvu)
389 char *fromFile, *toFile;
390 afs_int32 vn, vu, pvn, pvu;
392 /* First do a MakeDir on the target. */
393 afs_int32 dot[3], dotdot[3], lfid[3], code, usedPages;
396 register char *tp, tc;
397 struct DirHeader *dhp;
401 bzero(dot, sizeof(dot));
402 bzero(dotdot, sizeof(dotdot));
408 MakeDir(toFile, dot, dotdot); /* Returns no error code. */
410 /* Find out how many pages are valid, using stupid heuristic since DRead
411 * never returns null.
413 dhp = (struct DirHeader *) DRead(fromFile, 0);
415 printf("Failed to read first page of fromDir!\n");
416 /* if DErrno != 0, then our call failed and we should let our
417 * caller know that there's something wrong with the new dir. If not,
418 * then we return here anyway, with an empty, but at least good, directory.
423 usedPages = ComputeUsedPages(dhp);
425 /* Finally, enumerate all the entries, doing a create on them. */
426 for(i=0; i<NHASHENT; i++) {
427 entry = ntohs(dhp->hashTable[i]);
430 if (entry < 0 || entry >= usedPages*EPP) {
431 printf("Warning: bogus hash table entry encountered, ignoring.\n");
435 ep = GetBlob(fromFile, entry);
438 printf("can't continue down hash chain (entry %d, errno %d)\n",
443 printf("Warning: bogus hash chain encountered, switching to next.\n");
446 strncpy(tname, ep->name, MAXENAME);
447 tname[MAXENAME-1] = '\000'; /* just in case */
450 entry = ntohs(ep->next);
452 if ((strcmp(tp,".") != 0) && (strcmp(tp,"..") != 0)) {
453 lfid[1] = ntohl(ep->fid.vnode);
454 lfid[2] = ntohl(ep->fid.vunique);
455 code = Create(toFile, tname, lfid);
457 printf("Create of %s returned code %d, skipping to next hash chain.\n", tname, code);
466 /* Clean up things. */