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
12 /* 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. */
14 #include <afs/param.h>
15 #include <sys/types.h>
20 #include <netinet/in.h>
24 #define printf Log /* To make it work with volume salvager */
26 /* 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. */
30 struct DirEntry *GetBlob();
31 struct PageHeader *DRead();
32 extern afs_int32 DErrno;
33 int NameBlobs(), Lookup(), Create(), MakeDir();
35 /* figure out how many pages in use in a directory, given ptr to its (locked) header */
36 static ComputeUsedPages(dhp)
37 register struct DirHeader *dhp; {
38 register afs_int32 usedPages, i;
40 if (dhp->header.pgcount != 0) {
42 usedPages = ntohs(dhp->header.pgcount);
47 for(i=0; i<MAXPAGES; i++) {
48 if (dhp->alloMap[i] == EPP) {
53 if (usedPages == 0) usedPages = MAXPAGES;
58 /* returns true if something went wrong checking, or if dir is fine. Returns
59 * false if we *know* that the dir is bad.
63 struct DirHeader *dhp;
64 struct PageHeader *pp;
67 int havedot = 0, havedotdot = 0;
68 int usedPages, count, entry;
69 char eaMap[BIGMAXPAGES*EPP/8]; /* Change eaSize initialization below, too. */
71 afs_int32 entcount, maxents;
74 eaSize = BIGMAXPAGES*EPP/8;
76 /* Read the directory header */
77 dhp = (struct DirHeader *) DRead (file, 0);
79 /* if DErrno is 0, then we know that the read worked, but was short,
80 * and the damage is permanent. Otherwise, we got an I/O or programming
81 * error. Claim the dir is OK, but log something.
84 printf("Could not read first page in directory (%d)\n", DErrno);
88 printf("First page in directory does not exist.\n");
92 /* Check magic number for first page */
93 if (dhp->header.tag != htons(1234)) {
94 printf("Bad first pageheader magic number.\n");
99 /* Verify that the number of free entries in each directory page
100 * is within range (0-EPP). Also ensure directory is contiguous:
101 * Once the first alloMap entry with EPP free entries is found,
102 * the rest should match.
104 up = 0; /* count of used pages if total pages < MAXPAGES */
105 k = 0; /* found last page */
106 for(i=0; i<MAXPAGES; i++) {
109 /* Check if in range */
111 if ((j < 0) || (j > EPP-(13+2))) {
112 /* First page's dirheader uses 13 entries and at least
113 * two must exist for "." and ".."
115 printf("The dir header alloc map for page %d is bad.\n", i);
120 if ((j < 0) || (j > EPP)) {
121 printf("The dir header alloc map for page %d is bad.\n", i);
127 /* Check if contiguous */
128 if (k) { /* last page found */
129 if (j != EPP) { /* remaining entries must be EPP */
130 printf("A partially-full page occurs in slot %d, after the dir end.\n", i);
134 } else if (j == EPP) { /* is this the last page */
136 } else { /* a used page */
137 up++; /* keep count */
141 /* Compute number of used directory pages and max entries in all
142 ** those pages, the value of 'up' must be less than pgcount. The above
143 ** loop only checks the first MAXPAGES in a directory. An alloMap does
144 ** not exists for pages between MAXPAGES and BIGMAXPAGES */
145 usedPages = ComputeUsedPages(dhp);
146 if (usedPages < up) {
147 printf("Count of used directory pages does not match count in directory header\n");
152 /* For each directory page, check the magic number in each page
153 * header, and check that number of free entries (from freebitmap)
154 * matches the count in the alloMap from directory header.
156 for (i=0; i<usedPages; i++) {
157 /* Read the page header */
158 pp = (struct PageHeader *) DRead(file, i);
162 /* couldn't read page, but not because it wasn't there permanently */
163 printf("Failed to read dir page %d (errno %d)\n", i, DErrno);
167 printf("Directory shorter than alloMap indicates (page %d)\n", i);
171 /* check the tag field */
172 if (pp->tag != htons(1234)) {
173 printf("Directory page %d has a bad magic number.\n", i);
179 /* Count the number of entries allocated in this single
180 * directory page using the freebitmap in the page header.
183 for(j=0; j<EPP/8; j++) {
184 k = pp->freebitmap[j];
185 if (k & 0x80) count++;
186 if (k & 0x40) count++;
187 if (k & 0x20) count++;
188 if (k & 0x10) count++;
189 if (k & 0x08) count++;
190 if (k & 0x04) count++;
191 if (k & 0x02) count++;
192 if (k & 0x01) count++;
194 count = EPP - count; /* Change to count of free entries */
196 /* Now check that the count of free entries matches the count in the alloMap */
197 if ( (i < MAXPAGES) && ((count & 0xff) != (dhp->alloMap[i] & 0xff)) ) {
198 printf("Header alloMap count doesn't match count in freebitmap for page %d.\n", i);
207 /* Initialize the in-memory freebit map for all pages. */
208 for (i=0; i<eaSize; i++) {
210 if (i < usedPages*(EPP/8)) {
212 eaMap[i] = 0xff; /* A dir header uses first 13 entries */
214 eaMap[i] = 0x1f; /* A dir header uses first 13 entries */
215 } else if ((i%8) == 0) {
216 eaMap[i] = 0x01; /* A page header uses only first entry */
220 maxents = usedPages * EPP;
222 /* Walk down all the hash lists, ensuring that each flag field has FFIRST
223 * in it. Mark the appropriate bits in the in-memory freebit map.
224 * Check that the name is in the right hash bucket.
225 * Also check for loops in the hash chain by counting the entries.
227 for (entcount=0,i=0; i<NHASHENT; i++) {
228 for (entry = ntohs(dhp->hashTable[i]); entry; entry = ne) {
229 /* Verify that the entry is within range */
230 if (entry < 0 || entry >= maxents) {
231 printf("Out-of-range hash id %d in chain %d.\n", entry, i);
236 /* Read the directory entry */
238 ep = GetBlob(file, entry);
241 /* something went wrong reading the page, but it wasn't
242 * really something wrong with the dir that we can fix.
244 printf("Could not get dir blob %d (errno %d)\n", entry, DErrno);
248 printf("Invalid hash id %d in chain %d.\n", entry, i);
252 ne = ntohs(ep->next);
254 /* There can't be more than maxents entries */
255 if (++entcount >= maxents) {
256 printf("Directory's hash chain %d is circular.\n", i);
262 /* A null name is no good */
263 if (ep->name[0] == '\000') {
264 printf("Dir entry %x in chain %d has bogus (null) name.\n", ep, i);
270 /* The entry flag better be FFIRST */
271 if (ep->flag != FFIRST) {
272 printf("Dir entry %x in chain %d has bogus flag field.\n", ep, i);
278 /* Check the size of the name */
279 j = strlen(ep->name);
280 if (j >= MAXENAME) { /* MAXENAME counts the null */
281 printf("Dir entry %x in chain %d has too-long name.\n", ep, i);
287 /* The name used up k directory entries, set the bit in our in-memory
288 * freebitmap for each entry used by the name.
290 k = NameBlobs(ep->name);
291 for (j=0; j<k; j++) {
292 eaMap[(entry+j)>>3] |= (1<<((entry+j)&7));
295 /* Hash the name and make sure it is in the correct name hash */
296 if ((j=DirHash(ep->name)) != i) {
297 printf("Dir entry %x should be in hash bucket %d but IS in %d.\n", ep, j, i);
303 /* Check that if this is entry 13 (the 1st entry), then name must be "." */
305 if (strcmp(ep->name,".") == 0) {
308 printf("Dir entry %x, index 13 has name '%s' should be '.'\n",
316 /* Check that if this is entry 14 (the 2nd entry), then name must be ".." */
318 if (strcmp(ep->name,"..") == 0) {
321 printf("Dir entry %x, index 14 has name '%s' should be '..'\n",
329 /* CHECK FOR DUPLICATE NAMES? */
335 /* Verify that we found '.' and '..' in the correct place */
336 if (!havedot || !havedotdot) {
337 printf("Directory entry '.' or '..' does not exist or is in the wrong index.\n");
342 /* The in-memory freebit map has been computed. Check that it
343 * matches the one in the page header.
344 * Note that if this matches, alloMap has already been checked against it.
346 for(i=0; i<usedPages; i++) {
349 printf("Failed on second attempt to read dir page %d (errno %d)\n", i, DErrno);
351 /* if DErrno is 0, then the dir is really bad, and we return dir *not* OK.
352 * otherwise, we want to return true (1), meaning the dir isn't known
353 * to be bad (we can't tell, since I/Os are failing.
355 if (DErrno != 0) Die("dirok4");
356 else return 0; /* dir is really shorter */
360 for (j=0; j<EPP/8; j++) {
361 if (eaMap[count+j] != pp->freebitmap[j]) {
362 printf("Entry freebitmap error, page %d, map offset %d, %x should be %x.\n",
363 i, j, pp->freebitmap[j], eaMap[count+j]);
373 /* Finally cleanup and return. */
378 /* This routine is called with six parameters. The first is the id of
379 * the original, currently suspect, directory. The second is the file
380 * id of the place the salvager should place the new, fixed, directory.
381 * The third and the fourth parameters are the vnode number and the
382 * uniquifier of the currently suspect directory. The fifth and the
383 * sixth parameters are the vnode number and the uniquifier of the
386 int DirSalvage (fromFile, toFile, vn, vu, pvn, pvu)
387 char *fromFile, *toFile;
388 afs_int32 vn, vu, pvn, pvu;
390 /* First do a MakeDir on the target. */
391 afs_int32 dot[3], dotdot[3], lfid[3], code, usedPages;
394 register char *tp, tc;
395 struct DirHeader *dhp;
399 bzero(dot, sizeof(dot));
400 bzero(dotdot, sizeof(dotdot));
406 MakeDir(toFile, dot, dotdot); /* Returns no error code. */
408 /* Find out how many pages are valid, using stupid heuristic since DRead
409 * never returns null.
411 dhp = (struct DirHeader *) DRead(fromFile, 0);
413 printf("Failed to read first page of fromDir!\n");
414 /* if DErrno != 0, then our call failed and we should let our
415 * caller know that there's something wrong with the new dir. If not,
416 * then we return here anyway, with an empty, but at least good, directory.
421 usedPages = ComputeUsedPages(dhp);
423 /* Finally, enumerate all the entries, doing a create on them. */
424 for(i=0; i<NHASHENT; i++) {
425 entry = ntohs(dhp->hashTable[i]);
428 if (entry < 0 || entry >= usedPages*EPP) {
429 printf("Warning: bogus hash table entry encountered, ignoring.\n");
433 ep = GetBlob(fromFile, entry);
436 printf("can't continue down hash chain (entry %d, errno %d)\n",
441 printf("Warning: bogus hash chain encountered, switching to next.\n");
444 strncpy(tname, ep->name, MAXENAME);
445 tname[MAXENAME-1] = '\000'; /* just in case */
448 entry = ntohs(ep->next);
450 if ((strcmp(tp,".") != 0) && (strcmp(tp,"..") != 0)) {
451 lfid[1] = ntohl(ep->fid.vnode);
452 lfid[2] = ntohl(ep->fid.vunique);
453 code = Create(toFile, tname, lfid);
455 printf("Create of %s returned code %d, skipping to next hash chain.\n", tname, code);
464 /* Clean up things. */