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 #include <afs/param.h>
26 afs_uint32 dir_lookup_hits = 0;
27 afs_uint32 dir_lookup_misses = 0;
28 afs_uint32 dir_create_entry = 0;
29 afs_uint32 dir_remove_entry = 0;
31 afs_uint64 dir_lookup_time = 0;
32 afs_uint64 dir_create_time = 0;
33 afs_uint64 dir_remove_time = 0;
35 afs_uint64 dir_enums = 0;
37 afs_int32 cm_BPlusTrees = 1;
39 int cm_MemDumpDirStats(FILE *outputFile, char *cookie, int lock)
44 sprintf(output, "%s - Dir Lookup Hits: %-8d\r\n", cookie, dir_lookup_hits);
45 WriteFile(outputFile, output, (DWORD)strlen(output), &zilch, NULL);
46 sprintf(output, "%s - Misses: %-8d\r\n", cookie, dir_lookup_misses);
47 WriteFile(outputFile, output, (DWORD)strlen(output), &zilch, NULL);
48 sprintf(output, "%s - Enums: %-8d\r\n", cookie, dir_enums);
49 WriteFile(outputFile, output, (DWORD)strlen(output), &zilch, NULL);
50 sprintf(output, "%s - Create: %-8d\r\n", cookie, dir_create_entry);
51 WriteFile(outputFile, output, (DWORD)strlen(output), &zilch, NULL);
52 sprintf(output, "%s - Remove: %-8d\r\n", cookie, dir_remove_entry);
53 WriteFile(outputFile, output, (DWORD)strlen(output), &zilch, NULL);
55 sprintf(output, "%s - Dir Times Lookup: %-16I64d\r\n", cookie, dir_lookup_time);
56 WriteFile(outputFile, output, (DWORD)strlen(output), &zilch, NULL);
57 sprintf(output, "%s - Create: %-16I64d\r\n", cookie, dir_create_time);
58 WriteFile(outputFile, output, (DWORD)strlen(output), &zilch, NULL);
59 sprintf(output, "%s - Remove: %-16I64d\r\n", cookie, dir_remove_time);
60 WriteFile(outputFile, output, (DWORD)strlen(output), &zilch, NULL);
65 void cm_DirDumpStats(void)
67 afsi_log("Dir Lookup Hits: %-8d", dir_lookup_hits);
68 afsi_log(" Misses: %-8d", dir_lookup_misses);
69 afsi_log(" Enums: %-8d", dir_enums);
70 afsi_log(" Create: %-8d", dir_create_entry);
71 afsi_log(" Remove: %-8d", dir_remove_entry);
73 afsi_log("Dir Times Lookup: %-16I64d", dir_lookup_time);
74 afsi_log(" Create: %-16I64d", dir_create_time);
75 afsi_log(" Remove: %-16I64d", dir_remove_time);
79 /* Local static prototypes */
81 cm_DirGetBlob(cm_dirOp_t * op,
82 unsigned int blobno, cm_buf_t ** bufferpp, cm_dirEntry_t ** blobpp);
85 cm_DirFindItem(cm_dirOp_t * op,
87 cm_buf_t ** itembufpp, cm_dirEntry_t ** itempp,
88 cm_buf_t ** prevbufpp, unsigned short **previtempp);
91 cm_DirOpAddBuffer(cm_dirOp_t * op, cm_buf_t * buffer);
93 /* flags for cm_DirOpDelBuffer */
94 #define DIROP_MODIFIED 1
95 #define DIROP_SCPLOCKED 2
98 cm_DirOpDelBuffer(cm_dirOp_t * op, cm_buf_t * buffer, int flags);
101 cm_DirCheckStatus(cm_dirOp_t * op);
104 cm_DirReleasePage(cm_dirOp_t * op, cm_buf_t ** bufferpp, int modified);
107 cm_DirGetPage(cm_dirOp_t * op,
108 long index, cm_buf_t ** bufferpp, void ** datapp);
111 cm_DirFindBlobs(cm_dirOp_t * op, int nblobs);
114 cm_DirAddPage(cm_dirOp_t * op, int pageno);
117 cm_DirFreeBlobs(cm_dirOp_t * op, int firstblob, int nblobs);
120 /* compute how many 32 byte entries an AFS 3 dir requires for storing
121 * the specified name.
124 cm_NameEntries(char *namep, long *lenp)
128 i = (long)strlen(namep) + 1;
130 return 1 + ((i+15) >> 5);
133 /* Create an entry in a file. Dir is a file representation, while
134 entry is a string name.
137 op->scp->mx is unlocked
140 op->scp->mx is unlocked
142 None of the directory buffers for op->scp should be locked by the
146 cm_DirCreateEntry(cm_dirOp_t * op, char *entry, cm_fid_t * cfid)
150 LARGE_INTEGER start, end;
152 cm_dirEntry_t *ep = NULL;
153 cm_buf_t *entrybuf = NULL;
155 unsigned short *pp = NULL;
156 cm_buf_t *prevptrbuf = NULL;
158 cm_dirHeader_t *dhp = NULL;
159 cm_buf_t *dhpbuf = NULL;
163 /* check name quality */
167 QueryPerformanceCounter(&start);
171 osi_Log4(afsd_logp, "cm_DirCreateEntry for op 0x%p, name [%s] and fid[%d,%d]",
172 op, osi_LogSaveString(afsd_logp, entry), cfid->vnode, cfid->unique);
174 /* First check if file already exists. */
175 code = cm_DirFindItem(op,
180 cm_DirReleasePage(op, &entrybuf, FALSE);
181 cm_DirReleasePage(op, &prevptrbuf, FALSE);
186 blobs = cm_NameEntries(entry, NULL); /* number of entries required */
187 firstelt = cm_DirFindBlobs(op, blobs);
189 osi_Log0(afsd_logp, "cm_DirCreateEntry returning EFBIG");
190 code = EFBIG; /* directory is full */
194 /* First, we fill in the directory entry. */
195 code = cm_DirGetBlob(op, firstelt, &entrybuf, &ep);
201 ep->flag = CM_DIR_FFIRST;
202 ep->fid.vnode = htonl(cfid->vnode);
203 ep->fid.unique = htonl(cfid->unique);
204 strcpy(ep->name, entry);
206 /* Now we just have to thread it on the hash table list. */
207 code = cm_DirGetPage(op, 0, &dhpbuf, &dhp);
209 cm_DirReleasePage(op, &entrybuf, TRUE);
214 i = cm_DirHash(entry);
216 ep->next = dhp->hashTable[i];
217 dhp->hashTable[i] = htons(firstelt);
219 cm_DirReleasePage(op, &dhpbuf, TRUE);
220 cm_DirReleasePage(op, &entrybuf, TRUE);
222 osi_Log0(afsd_logp, "cm_DirCreateEntry returning success");
226 QueryPerformanceCounter(&end);
228 dir_create_time += (end.QuadPart - start.QuadPart);
232 /* Return the length of a directory in pages
235 op->scp->mx is locked
238 op->scp->mx is locked
240 The first directory page for op->scp should not be locked by the
244 cm_DirLength(cm_dirOp_t * op)
247 cm_dirHeader_t *dhp = NULL;
248 cm_buf_t *dhpbuf = NULL;
252 code = cm_DirGetPage(op, 0, &dhpbuf, &dhp);
256 if (dhp->header.pgcount != 0)
257 ctr = ntohs(dhp->header.pgcount);
259 /* old style, count the pages */
261 for (i = 0; i < CM_DIR_MAXPAGES; i++)
262 if (dhp->alloMap[i] != CM_DIR_EPP)
265 cm_DirReleasePage(op, &dhpbuf, FALSE);
266 return ctr * CM_DIR_PAGESIZE;
269 /* Delete a directory entry.
272 op->scp->mx is unlocked
275 op->scp->mx is unlocked
277 None of the directory buffers for op->scp should be locked by the
281 cm_DirDeleteEntry(cm_dirOp_t * op, char *entry)
283 /* Delete an entry from a directory, including update of all free
284 entry descriptors. */
287 cm_dirEntry_t *firstitem = NULL;
288 cm_buf_t *itembuf = NULL;
289 unsigned short *previtem = NULL;
290 cm_buf_t *pibuf = NULL;
294 LARGE_INTEGER start, end;
296 QueryPerformanceCounter(&start);
298 osi_Log2(afsd_logp, "cm_DirDeleteEntry for op 0x%p, entry [%s]",
299 op, osi_LogSaveString(afsd_logp, entry));
301 code = cm_DirFindItem(op, entry,
302 &itembuf, &firstitem,
305 osi_Log0(afsd_logp, "cm_DirDeleteEntry returning ENOENT");
312 *previtem = firstitem->next;
313 cm_DirReleasePage(op, &pibuf, TRUE);
315 thyper = itembuf->offset;
316 thyper = LargeIntegerAdd(thyper,
317 ConvertLongToLargeInteger(((char *) firstitem) - itembuf->datap));
318 thyper = ExtendedLargeIntegerDivide(thyper, 32, &junk);
320 index = thyper.LowPart;
321 osi_assert(thyper.HighPart == 0);
323 nitems = cm_NameEntries(firstitem->name, NULL);
324 cm_DirReleasePage(op, &itembuf, FALSE);
326 cm_DirFreeBlobs(op, index, nitems);
328 osi_Log0(afsd_logp, "cm_DirDeleteEntry returning success");
332 QueryPerformanceCounter(&end);
334 dir_remove_time += (end.QuadPart - start.QuadPart);
339 /* Find a bunch of contiguous entries; at least nblobs in a row.
341 Called with op->scp->mx */
343 cm_DirFindBlobs(cm_dirOp_t * op, int nblobs)
348 cm_dirHeader_t *dhp = NULL;
349 cm_buf_t *dhpbuf = NULL;
350 int dhpModified = FALSE;
352 cm_pageHeader_t *pp = NULL;
353 cm_buf_t *pagebuf = NULL;
354 int pageModified = FALSE;
360 osi_Log2(afsd_logp, "cm_DirFindBlobs for op 0x%p, nblobs = %d",
363 code = cm_DirGetPage(op, 0, &dhpbuf, (void **) &dhp);
367 for (i = 0; i < CM_DIR_BIGMAXPAGES; i++) {
368 if (i >= CM_DIR_MAXPAGES || dhp->alloMap[i] >= nblobs) {
369 /* if page could contain enough entries */
370 /* If there are CM_DIR_EPP free entries, then the page is
371 not even allocated. */
372 if (i >= CM_DIR_MAXPAGES) {
374 /* this pages exists past the end of the old-style dir */
375 pgcount = ntohs(dhp->header.pgcount);
377 pgcount = CM_DIR_MAXPAGES;
378 dhp->header.pgcount = htons(pgcount);
382 if (i > pgcount - 1) {
383 /* this page is bigger than last allocated page */
384 cm_DirAddPage(op, i);
385 dhp->header.pgcount = htons(i + 1);
388 } else if (dhp->alloMap[i] == CM_DIR_EPP) {
389 /* Add the page to the directory. */
390 cm_DirAddPage(op, i);
391 dhp->alloMap[i] = CM_DIR_EPP - 1;
392 dhp->header.pgcount = htons(i + 1);
396 code = cm_DirGetPage(op, i, &pagebuf, &pp);
398 cm_DirReleasePage(op, &dhpbuf, dhpModified);
402 for (j = 0; j <= CM_DIR_EPP - nblobs; j++) {
404 for (k = 0; k < nblobs; k++)
405 if ((pp->freeBitmap[(j + k) >> 3] >> ((j + k) & 7)) & 1) {
415 /* Here we have the first index in j. We update the allocation maps
416 * and free up any resources we've got allocated. */
417 if (i < CM_DIR_MAXPAGES) {
418 dhp->alloMap[i] -= nblobs;
422 cm_DirReleasePage(op, &dhpbuf, dhpModified);
424 for (k = 0; k < nblobs; k++)
425 pp->freeBitmap[(j + k) >> 3] |= 1 << ((j + k) & 7);
427 cm_DirReleasePage(op, &pagebuf, TRUE);
429 osi_Log0(afsd_logp, "cm_DirFindBlobs returning success");
431 return j + i * CM_DIR_EPP;
433 cm_DirReleasePage(op, &pagebuf, pageModified);
437 /* If we make it here, the directory is full. */
438 osi_Log0(afsd_logp, "cm_DirFindBlobs directory is full");
439 cm_DirReleasePage(op, &dhpbuf, dhpModified);
443 /* Add a page to a directory.
445 Called with op->scp->mx
448 cm_DirAddPage(cm_dirOp_t * op, int pageno)
451 cm_pageHeader_t *pp = NULL;
452 cm_buf_t *pagebuf = NULL;
455 osi_Log2(afsd_logp, "cm_DirAddPage for op 0x%p, pageno=%d", op, pageno);
457 code = cm_DirGetPage(op, pageno, &pagebuf, (void **) &pp);
461 pp->tag = htons(1234);
464 pp->freeCount = CM_DIR_EPP - 1; /* The first dude is already allocated */
465 pp->freeBitmap[0] = 0x01;
466 for (i = 1; i < CM_DIR_EPP / 8; i++) /* It's a constant */
467 pp->freeBitmap[i] = 0;
469 cm_DirReleasePage(op, &pagebuf, TRUE);
471 osi_Log0(afsd_logp, "cm_DirAddPage returning success");
476 /* Free a whole bunch of directory entries.
478 Called with op->scp->mx
481 cm_DirFreeBlobs(cm_dirOp_t * op, int firstblob, int nblobs)
486 cm_dirHeader_t *dhp = NULL;
487 cm_buf_t *dhpbuf = NULL;
488 int dhpmodified = FALSE;
490 cm_pageHeader_t *pp = NULL;
491 cm_buf_t *pagebuf = NULL;
494 osi_Log3(afsd_logp, "cm_DirFreeBlobs for op 0x%p, firstblob=%d, nblobs=%d",
495 op, firstblob, nblobs);
497 page = firstblob / CM_DIR_EPP;
498 firstblob -= CM_DIR_EPP * page; /* convert to page-relative entry */
500 code = cm_DirGetPage(op, 0, &dhpbuf, &dhp);
504 if (page < CM_DIR_MAXPAGES) {
505 dhp->alloMap[page] += nblobs;
509 cm_DirReleasePage(op, &dhpbuf, dhpmodified);
511 code = cm_DirGetPage(op, page, &pagebuf, &pp);
513 for (i = 0; i < nblobs; i++)
514 pp->freeBitmap[(firstblob + i) >> 3] &=
515 ~(1 << ((firstblob + i) & 7));
516 cm_DirReleasePage(op, &pagebuf, TRUE);
519 osi_Log1(afsd_logp, "cm_DirFreeBlobs returning code 0x%x", code);
525 * Format an empty directory properly. Note that the first 13 entries in a
526 * directory header page are allocated, 1 to the page header, 4 to the
527 * allocation map and 8 to the hash table.
529 * Called with op->scp->mx unlocked
532 cm_DirMakeDir(cm_dirOp_t * op, cm_fid_t * me, cm_fid_t * parent)
535 cm_dirHeader_t *dhp = NULL;
536 cm_buf_t *dhpbuf = NULL;
540 osi_Log3(afsd_logp, "cm_DirMakeDir for op 0x%p, directory fid[%d, %d]",
541 op, me->vnode, me->unique);
542 osi_Log2(afsd_logp, " parent[%d, %d]",
543 parent->vnode, parent->unique);
545 code = cm_DirGetPage(op, 0, &dhpbuf, &dhp);
551 dhp->header.pgcount = htons(1);
552 dhp->header.tag = htons(1234);
553 dhp->header.freeCount = (CM_DIR_EPP - CM_DIR_DHE - 1);
554 dhp->header.freeBitmap[0] = 0xff;
555 dhp->header.freeBitmap[1] = 0x1f;
556 for (i = 2; i < CM_DIR_EPP / 8; i++)
557 dhp->header.freeBitmap[i] = 0;
558 dhp->alloMap[0] = (CM_DIR_EPP - CM_DIR_DHE - 1);
559 for (i = 1; i < CM_DIR_MAXPAGES; i++)
560 dhp->alloMap[i] = CM_DIR_EPP;
561 for (i = 0; i < CM_DIR_NHASHENT; i++)
562 dhp->hashTable[i] = 0;
564 cm_DirReleasePage(op, &dhpbuf, TRUE);
566 cm_DirCreateEntry(op, ".", me);
567 cm_DirCreateEntry(op, "..", parent); /* Virtue is its own .. */
569 osi_Log0(afsd_logp, "cm_DirMakeDir returning success");
575 /* Look up a file name in directory.
578 op->scp->mx is unlocked
581 op->scp->mx is unlocked
583 None of the directory buffers for op->scp should be locked by the
587 cm_DirLookup(cm_dirOp_t * op, char *entry, cm_fid_t * cfid)
589 cm_dirEntry_t *firstitem = NULL;
590 cm_buf_t *itembuf = NULL;
591 unsigned short *previtem = NULL;
592 cm_buf_t *pibuf = NULL;
597 QueryPerformanceCounter(&start);
599 osi_Log2(afsd_logp, "cm_DirLookup for op 0x%p, entry[%s]",
600 op, osi_LogSaveString(afsd_logp, entry));
602 code = cm_DirFindItem(op, entry,
603 &itembuf, &firstitem,
611 cm_DirReleasePage(op, &pibuf, FALSE);
613 cfid->cell = op->scp->fid.cell;
614 cfid->volume = op->scp->fid.volume;
615 cfid->vnode = ntohl(firstitem->fid.vnode);
616 cfid->unique = ntohl(firstitem->fid.unique);
618 cm_DirReleasePage(op, &itembuf, FALSE);
620 osi_Log2(afsd_logp, "cm_DirLookup returning fid[%d,%d]",
621 cfid->vnode, cfid->unique);
627 QueryPerformanceCounter(&end);
629 dir_lookup_time += (end.QuadPart - start.QuadPart);
634 /* Look up a file name in directory.
637 op->scp->mx is locked
640 op->scp->mx is locked
642 None of the directory buffers for op->scp should be locked by the
646 cm_DirLookupOffset(cm_dirOp_t * op, char *entry, cm_fid_t *cfid, osi_hyper_t *offsetp)
648 cm_dirEntry_t *firstitem = NULL;
649 cm_buf_t *itembuf = NULL;
650 unsigned short *previtem = NULL;
651 cm_buf_t *pibuf = NULL;
655 osi_Log2(afsd_logp, "cm_DirLookupOffset for op 0x%p, entry[%s]",
656 op, osi_LogSaveString(afsd_logp, entry));
658 code = cm_DirFindItem(op, entry,
659 &itembuf, &firstitem,
664 cm_DirReleasePage(op, &pibuf, FALSE);
666 cfid->cell = op->scp->fid.cell;
667 cfid->volume = op->scp->fid.volume;
668 cfid->vnode = ntohl(firstitem->fid.vnode);
669 cfid->unique = ntohl(firstitem->fid.unique);
673 thyper = itembuf->offset;
674 thyper = LargeIntegerAdd(thyper,
675 ConvertLongToLargeInteger(((char *) firstitem) - itembuf->datap));
680 cm_DirReleasePage(op, &itembuf, FALSE);
682 osi_Log2(afsd_logp, "cm_DirLookupOffset returning fid[%d,%d]",
683 cfid->vnode, cfid->unique);
685 osi_Log2(afsd_logp, " offset [%x:%x]",
686 offsetp->HighPart, offsetp->LowPart);
692 /* Apply a function to every directory entry in a directory.
695 op->scp->mx is locked
698 op->scp->mx is locked
700 None of the directory buffers for op->scp should be locked by the
703 The hook function cannot modify or lock any directory buffers.
706 cm_DirApply(cm_dirOp_t * op, int (*hookproc) (void *, char *, long, long), void *hook)
708 /* Enumerate the contents of a directory. */
712 cm_dirHeader_t *dhp = NULL;
713 cm_buf_t *dhpbuf = NULL;
715 cm_dirEntry_t *ep = NULL;
716 cm_buf_t *epbuf = NULL;
720 code = cm_DirGetPage(op, 0, &dhpbuf, &dhp);
724 for (i = 0; i < CM_DIR_NHASHENT; i++) {
725 /* For each hash chain, enumerate everyone on the list. */
726 num = ntohs(dhp->hashTable[i]);
728 /* Walk down the hash table list. */
729 code = cm_DirGetBlob(op, num, &epbuf, &ep);
731 cm_DirReleasePage(op, &dhpbuf, FALSE);
735 num = ntohs(ep->next);
736 (*hookproc) (hook, ep->name, ntohl(ep->fid.vnode),
737 ntohl(ep->fid.unique));
739 cm_DirReleasePage(op, &epbuf, FALSE);
742 cm_DirReleasePage(op, &dhpbuf, FALSE);
747 /* Check if a directory is empty
750 op->scp->mx is locked
753 op->scp->mx is locked
755 None of the directory buffers for op->scp should be locked by the
759 cm_DirIsEmpty(cm_dirOp_t * op)
761 /* Enumerate the contents of a directory. */
765 cm_dirHeader_t *dhp = NULL;
766 cm_buf_t *dhpbuf = NULL;
768 cm_dirEntry_t *ep = NULL;
769 cm_buf_t *epbuf = NULL;
773 code = cm_DirGetPage(op, 0, &dhpbuf, &dhp);
777 for (i = 0; i < CM_DIR_NHASHENT; i++) {
778 /* For each hash chain, enumerate everyone on the list. */
779 num = ntohs(dhp->hashTable[i]);
782 /* Walk down the hash table list. */
783 code = cm_DirGetBlob(op, num, &epbuf, &ep);
787 if (strcmp(ep->name, "..") && strcmp(ep->name, ".")) {
788 cm_DirReleasePage(op, &epbuf, FALSE);
789 cm_DirReleasePage(op, &dhpbuf, FALSE);
792 num = ntohs(ep->next);
793 cm_DirReleasePage(op, &epbuf, FALSE);
796 cm_DirReleasePage(op, &dhpbuf, FALSE);
800 /* Return a pointer to an entry, given its number.
804 if *bufferpp != NULL, then *bufferpp->mx is locked
807 scp->mx may be unlocked
808 *bufferpp may be released
812 if *bufferpp != NULL, then *bufferpp->mx is locked
814 *bufferpp should be released via cm_DirReleasePage() or any other
815 *call that releases a directory buffer.
818 cm_DirGetBlob(cm_dirOp_t * op,
819 unsigned int blobno, cm_buf_t ** bufferpp, cm_dirEntry_t ** blobpp)
824 osi_Log2(afsd_logp, "cm_DirGetBlob for op 0x%p, blobno=%d",
827 code = cm_DirGetPage(op, blobno >> CM_DIR_LEPP,
828 bufferpp, (void **) &ep);
832 *blobpp = (cm_dirEntry_t *) (ep + 32 * (blobno & (CM_DIR_EPP - 1)));
838 cm_DirHash(char *string)
840 /* Hash a string to a number between 0 and NHASHENT. */
841 register unsigned char tc;
845 while ((tc = (*string++))) {
849 tval = hval & (CM_DIR_NHASHENT - 1);
853 tval = CM_DIR_NHASHENT - tval;
857 /* Find a directory entry, given its name. This entry returns a
858 * pointer to a locked buffer, and a pointer to a locked buffer (in
859 * previtem) referencing the found item (to aid the delete code). If
860 * no entry is found, however, no items are left locked, and a null
861 * pointer is returned instead.
870 cm_DirFindItem(cm_dirOp_t * op,
872 cm_buf_t ** itembufpp, cm_dirEntry_t ** itempp,
873 cm_buf_t ** prevbufpp, unsigned short **previtempp)
876 cm_dirHeader_t *dhp = NULL;
877 unsigned short *lp = NULL;
878 cm_dirEntry_t *tp = NULL;
879 cm_buf_t *hashbufp = NULL;
880 cm_buf_t *itembufp = NULL;
883 osi_Log2(afsd_logp, "cm_DirFindItem for op 0x%p, entry[%s]",
884 op, osi_LogSaveString(afsd_logp, ename));
886 i = cm_DirHash(ename);
888 if (op->scp->fileType != CM_SCACHETYPE_DIRECTORY) {
889 osi_Log0(afsd_logp, "cm_DirFindItem: The scp is not a directory");
890 return CM_ERROR_INVAL;
893 code = cm_DirGetPage(op, 0, &hashbufp, (void **) &dhp);
898 if (dhp->hashTable[i] == 0) {
900 osi_Log1(afsd_logp, "cm_DirFindItem: Hash bucket %d is empty", i);
901 cm_DirReleasePage(op, &hashbufp, FALSE);
905 code = cm_DirGetBlob(op,
906 (u_short) ntohs(dhp->hashTable[i]),
909 cm_DirReleasePage(op, &hashbufp, FALSE);
913 lp = &(dhp->hashTable[i]);
917 lp : pointer to blob number of entry we are looking at
918 hashbufp : buffer containing lp
919 tp : pointer to entry we are looking at
920 itembufp : buffer containing tp
923 /* Look at each hash conflict entry. */
924 if (!strcmp(ename, tp->name)) {
925 osi_Log0(afsd_logp, "cm_DirFindItem: returning success");
926 /* Found our entry. */
928 *prevbufpp = hashbufp;
930 *itembufpp = itembufp;
935 cm_DirReleasePage(op, &hashbufp, FALSE);
942 /* The end of the line */
943 osi_Log0(afsd_logp, "cm_DirFindItem: returning ENOENT");
944 cm_DirReleasePage(op, &hashbufp, FALSE);
948 code = cm_DirGetBlob(op,
949 (u_short) ntohs(*lp),
953 cm_DirReleasePage(op, &hashbufp, FALSE);
959 /* Begin a sequence of directory operations. scp->mx should be
963 cm_BeginDirOp(cm_scache_t * scp, cm_user_t * userp, cm_req_t * reqp,
964 afs_uint32 lockType, cm_dirOp_t * op)
969 osi_Log3(afsd_logp, "Beginning dirOp[0x%p] for scp[0x%p], userp[0x%p]",
972 memset(op, 0, sizeof(*op));
978 cm_InitReq(&op->req);
980 op->dirtyBufCount = 0;
983 for (i=0; i < CM_DIROP_MAXBUFFERS; i++) {
984 op->buffers[i].flags = 0;
987 code = cm_DirCheckStatus(op);
990 op->length = scp->length;
991 op->newLength = op->length;
992 op->dataVersion = scp->dataVersion;
993 op->newDataVersion = op->dataVersion;
996 lock_ObtainRead(&scp->dirlock);
997 if (!cm_BPlusTrees ||
999 scp->dirDataVersion == scp->dataVersion))
1004 case CM_DIRLOCK_NONE:
1005 lock_ReleaseRead(&scp->dirlock);
1007 case CM_DIRLOCK_READ:
1008 /* got it already */
1010 case CM_DIRLOCK_WRITE:
1012 lock_ReleaseRead(&scp->dirlock);
1013 lock_ObtainWrite(&scp->dirlock);
1016 lock_ReleaseRead(&scp->dirlock);
1017 lock_ObtainWrite(&scp->dirlock);
1018 if (scp->dirBplus &&
1019 scp->dirDataVersion != scp->dataVersion)
1023 freeBtree(scp->dirBplus);
1024 scp->dirBplus = NULL;
1025 scp->dirDataVersion = -1;
1028 if (!scp->dirBplus) {
1029 cm_BPlusDirBuildTree(scp, userp, reqp);
1031 scp->dirDataVersion = scp->dataVersion;
1035 case CM_DIRLOCK_NONE:
1036 lock_ReleaseWrite(&scp->dirlock);
1038 case CM_DIRLOCK_READ:
1039 lock_ConvertWToR(&scp->dirlock);
1041 case CM_DIRLOCK_WRITE:
1043 /* got it already */;
1048 case CM_DIRLOCK_NONE:
1050 case CM_DIRLOCK_READ:
1051 lock_ObtainRead(&scp->dirlock);
1053 case CM_DIRLOCK_WRITE:
1055 lock_ObtainWrite(&scp->dirlock);
1058 op->lockType = lockType;
1067 /* Check if it is safe for us to perform local directory updates.
1068 Called with scp->mx unlocked. */
1070 cm_CheckDirOpForSingleChange(cm_dirOp_t * op)
1074 if (op->scp == NULL)
1077 code = cm_DirCheckStatus(op);
1080 op->dataVersion == op->scp->dataVersion - 1) {
1081 /* only one set of changes happened between cm_BeginDirOp()
1082 and this function. It is safe for us to perform local
1084 op->newDataVersion = op->scp->dataVersion;
1085 op->newLength = op->scp->serverLength;
1087 osi_Log0(afsd_logp, "cm_CheckDirOpForSingleChange succeeded");
1093 "cm_CheckDirOpForSingleChange failed. code=0x%x, old dv=%d, new dv=%d",
1094 code, op->dataVersion, op->scp->dataVersion);
1098 /* End a sequence of directory operations. Called with op->scp->mx
1101 cm_EndDirOp(cm_dirOp_t * op)
1105 if (op->scp == NULL)
1108 osi_Log2(afsd_logp, "Ending dirOp 0x%p with %d dirty buffer releases",
1109 op, op->dirtyBufCount);
1111 if (op->dirtyBufCount > 0) {
1112 /* we made changes. We should go through the list of buffers
1113 and update the dataVersion for each. */
1114 code = buf_ForceDataVersion(op->scp, op->dataVersion, op->newDataVersion);
1117 /* and update the data version on the B+ tree */
1118 if (op->scp->dirBplus &&
1119 op->scp->dirDataVersion == op->dataVersion) {
1121 switch (op->lockType) {
1122 case CM_DIRLOCK_READ:
1123 lock_ReleaseRead(&op->scp->dirlock);
1124 /* fall through ... */
1125 case CM_DIRLOCK_NONE:
1126 lock_ObtainWrite(&op->scp->dirlock);
1127 op->lockType = CM_DIRLOCK_WRITE;
1129 case CM_DIRLOCK_WRITE:
1131 /* already got it */;
1133 op->scp->dirDataVersion = op->newDataVersion;
1138 switch (op->lockType) {
1139 case CM_DIRLOCK_NONE:
1141 case CM_DIRLOCK_READ:
1142 lock_ReleaseRead(&op->scp->dirlock);
1144 case CM_DIRLOCK_WRITE:
1146 lock_ReleaseWrite(&op->scp->dirlock);
1150 cm_ReleaseSCache(op->scp);
1154 cm_ReleaseUser(op->userp);
1157 osi_assertx(op->nBuffers == 0, "Buffer leak after dirOp termination");
1162 /* NOTE: Called without scp->mx and without bufferp->mx */
1164 cm_DirOpAddBuffer(cm_dirOp_t * op, cm_buf_t * bufferp)
1169 osi_Log2(afsd_logp, "cm_DirOpAddBuffer for op 0x%p, buffer %p", op, bufferp);
1171 if (bufferp == NULL)
1174 for (i=0; i < CM_DIROP_MAXBUFFERS; i++) {
1175 if ((op->buffers[i].flags & CM_DIROPBUFF_INUSE) &&
1176 op->buffers[i].bufferp == bufferp) {
1181 if (i < CM_DIROP_MAXBUFFERS) {
1182 /* we already have this buffer on our list */
1184 op->buffers[i].refcount++;
1186 "cm_DirOpAddBuffer: the buffer is already listed for the dirOp");
1189 /* we have to add a new buffer */
1190 osi_assertx(op->nBuffers < CM_DIROP_MAXBUFFERS - 1,
1191 "DirOp has exceeded CM_DIROP_MAXBUFFERS buffers");
1193 for (i=0; i < CM_DIROP_MAXBUFFERS; i++) {
1194 if (!(op->buffers[i].flags & CM_DIROPBUFF_INUSE))
1198 osi_assert(i < CM_DIROP_MAXBUFFERS);
1200 lock_ObtainMutex(&bufferp->mx);
1201 lock_ObtainMutex(&op->scp->mx);
1203 /* Make sure we are synchronized. */
1204 code = cm_SyncOp(op->scp, bufferp, op->userp, &op->req, PRSFS_LOOKUP,
1205 CM_SCACHESYNC_NEEDCALLBACK |
1206 CM_SCACHESYNC_WRITE |
1207 CM_SCACHESYNC_BUFLOCKED);
1210 bufferp->dataVersion != op->dataVersion) {
1212 osi_Log2(afsd_logp, "cm_DirOpAddBuffer: buffer version mismatch. buf ver = %d. want %d", bufferp->dataVersion, op->dataVersion);
1214 cm_SyncOpDone(op->scp, bufferp,
1215 CM_SCACHESYNC_NEEDCALLBACK |
1216 CM_SCACHESYNC_WRITE |
1217 CM_SCACHESYNC_BUFLOCKED);
1219 code = CM_ERROR_INVAL;
1222 lock_ReleaseMutex(&op->scp->mx);
1223 lock_ReleaseMutex(&bufferp->mx);
1226 osi_Log1(afsd_logp, "cm_DirOpAddBuffer: failed to sync buffer. code=0x%x",
1232 op->buffers[i].bufferp = bufferp;
1233 op->buffers[i].refcount = 1; /* start with one ref */
1234 op->buffers[i].flags = CM_DIROPBUFF_INUSE;
1238 osi_Log0(afsd_logp, "cm_DirOpAddBuffer: returning success");
1244 /* Note: Called without op->scp->mx */
1246 cm_DirOpFindBuffer(cm_dirOp_t * op, osi_hyper_t offset, cm_buf_t ** bufferpp)
1250 for (i=0; i < CM_DIROP_MAXBUFFERS; i++) {
1251 if ((op->buffers[i].flags & CM_DIROPBUFF_INUSE) &&
1252 LargeIntegerEqualTo(op->buffers[i].bufferp->offset, offset))
1256 if (i < CM_DIROP_MAXBUFFERS) {
1258 op->buffers[i].refcount++;
1259 buf_Hold(op->buffers[i].bufferp);
1260 *bufferpp = op->buffers[i].bufferp;
1262 osi_Log2(afsd_logp, "cm_DirOpFindBuffer: found buffer for offset [%x:%x]",
1263 offset.HighPart, offset.LowPart);
1267 osi_Log2(afsd_logp, "cm_DirOpFindBuffer: buffer not found for offset [%x:%x]",
1268 offset.HighPart, offset.LowPart);
1273 /* NOTE: called with scp->mx held or not depending on the flags */
1275 cm_DirOpDelBuffer(cm_dirOp_t * op, cm_buf_t * bufferp, int flags)
1279 osi_Log3(afsd_logp, "cm_DirOpDelBuffer for op 0x%p, buffer 0x%p, flags=%d",
1280 op, bufferp, flags);
1282 for (i=0; i < CM_DIROP_MAXBUFFERS; i++) {
1283 if ((op->buffers[i].flags & CM_DIROPBUFF_INUSE) &&
1284 op->buffers[i].bufferp == bufferp)
1288 if (i < CM_DIROP_MAXBUFFERS) {
1290 if (flags & DIROP_MODIFIED)
1291 op->dirtyBufCount++;
1293 osi_assert(op->buffers[i].refcount > 0);
1294 op->buffers[i].refcount --;
1296 if (op->buffers[i].refcount == 0) {
1297 /* this was the last reference we had */
1299 osi_Log0(afsd_logp, "cm_DirOpDelBuffer: releasing buffer");
1301 /* if this buffer was modified, then we update the data
1302 version of the buffer with the data version of the
1304 if (!(flags & DIROP_SCPLOCKED)) {
1305 lock_ObtainMutex(&op->scp->mx);
1308 /* first make sure that the buffer is idle. It should
1309 have been idle all along. */
1310 osi_assertx((bufferp->cmFlags & (CM_BUF_CMFETCHING |
1311 CM_BUF_CMSTORING)) == 0,
1312 "Buffer is not idle while performing dirOp");
1314 cm_SyncOpDone(op->scp, bufferp,
1315 CM_SCACHESYNC_NEEDCALLBACK |
1316 CM_SCACHESYNC_WRITE);
1319 osi_assert(bufferp->dataVersion == op->dataVersion);
1322 lock_ReleaseMutex(&op->scp->mx);
1324 lock_ObtainMutex(&bufferp->mx);
1326 if (flags & DIROP_SCPLOCKED) {
1327 lock_ObtainMutex(&op->scp->mx);
1330 if (flags & DIROP_MODIFIED) {
1331 /* We don't update the dataversion here. Instead we
1332 wait until the dirOp is completed and then flip the
1333 dataversion on all the buffers in one go.
1334 Otherwise we won't know if the dataversion is
1335 current because it was fetched from the server or
1336 because we touched it during the dirOp. */
1338 if (bufferp->userp != op->userp) {
1339 if (bufferp->userp != NULL)
1340 cm_ReleaseUser(bufferp->userp);
1341 cm_HoldUser(op->userp);
1342 bufferp->userp = op->userp;
1346 lock_ReleaseMutex(&bufferp->mx);
1348 op->buffers[i].bufferp = NULL;
1349 buf_Release(bufferp);
1350 op->buffers[i].flags = 0;
1356 /* we have other references to this buffer. so we have to
1362 osi_Log0(afsd_logp, "cm_DirOpDelBuffer: buffer not found");
1363 osi_assertx(FALSE, "Attempt to delete a non-existent buffer from a dirOp");
1368 /* Check if we have current status and a callback for the given scp.
1369 This should be called before cm_DirGetPage() is called per scp.
1378 scp->mx may be released
1381 cm_DirCheckStatus(cm_dirOp_t * op)
1385 lock_ObtainMutex(&op->scp->mx);
1386 code = cm_SyncOp(op->scp, NULL, op->userp, &op->req, PRSFS_LOOKUP,
1387 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1388 lock_ReleaseMutex(&op->scp->mx);
1390 osi_Log2(afsd_logp, "cm_DirCheckStatus for op 0x%p returning code 0x%x",
1396 /* Release a directory buffer that was obtained via a call to
1397 cm_DirGetPage() or any other function that returns a locked, held,
1398 directory page buffer.
1400 Called with scp->mx unlocked
1403 cm_DirReleasePage(cm_dirOp_t * op, cm_buf_t ** bufferpp, int modified)
1410 cm_DirOpDelBuffer(op, *bufferpp,
1411 ((modified ? DIROP_MODIFIED : 0)));
1412 buf_Release(*bufferpp);
1419 Returns the index'th directory page from scp. The userp and reqp
1420 will be used to fetch the buffer from the fileserver if necessary.
1421 If the call is successful, a locked and held cm_buf_t is returned
1422 via buferpp and a pointer to the directory page is returned via
1425 The returned buffer should be released via a call to
1426 cm_DirReleasePage() or by passing it into a subsequent call to
1427 cm_DirGetPage() for the *same* scp.
1429 If a *locked* buffer for the *same* scp is passed in via bufferpp
1430 to the function, it will check if the requested directory page is
1431 located in the specified buffer. If not, the buffer will be
1432 released and a new buffer returned that contains the requested
1435 Note: If a buffer is specified on entry via bufferpp, it is assumed
1436 that the buffer is unmodified. If the buffer is modified, it
1437 should be released via cm_DirReleasePage().
1441 If *bufferpp is non-NULL, then *bufferpp->mx is locked.
1445 If *bufferpp is non-NULL, then *bufferpp->mx is locked.
1448 scp->mx will be obtained and released
1452 cm_DirGetPage(cm_dirOp_t * op,
1453 long index, cm_buf_t ** bufferpp, void ** datapp)
1455 osi_hyper_t pageOffset; /* offset of the dir page from the
1456 start of the directory */
1457 osi_hyper_t bufferOffset; /* offset of the buffer from the start
1461 cm_buf_t * bufferp = NULL;
1463 void * datap = NULL;
1467 osi_Log2(afsd_logp, "cm_DirGetPage for op 0x%p, index %d", op, index);
1469 pageOffset = ConvertLongToLargeInteger(index * CM_DIR_PAGESIZE);
1470 bufferOffset.HighPart = pageOffset.HighPart;
1471 bufferOffset.LowPart = pageOffset.LowPart & ~(cm_data.buf_blockSize - 1);
1473 bufferp = *bufferpp;
1474 if (bufferp != NULL) {
1475 osi_assert(cm_FidCmp(&bufferp->fid, &op->scp->fid) == 0);
1477 thyper = bufferp->offset;
1480 if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
1484 buf_Release(bufferp);
1485 cm_DirOpDelBuffer(op, bufferp, 0);
1489 /* first check if we are already working with the buffer */
1490 if (cm_DirOpFindBuffer(op, bufferOffset, &bufferp)) {
1495 lock_ObtainRead(&op->scp->bufCreateLock);
1496 code = buf_Get(op->scp, &bufferOffset, &bufferp);
1497 lock_ReleaseRead(&op->scp->bufCreateLock);
1500 osi_Log1(afsd_logp, " buf_Get returned code 0x%x", code);
1505 osi_assert(bufferp != NULL);
1507 /* DirOpAddBuffer will obtain bufferp->mx if necessary */
1508 code = cm_DirOpAddBuffer(op, bufferp);
1511 /* for some reason, the buffer was rejected. We can't use
1512 this buffer, and since this is the only buffer we can
1513 potentially use, there's no recourse.*/
1514 buf_Release(bufferp);
1520 /* The code below is for making sure the buffer contains
1521 current data. This is a bad idea, since the whole point of
1522 doing directory updates locally is to avoid fetching all
1523 the data from the server. */
1525 lock_ObtainMutex(&op->scp->mx);
1526 code = cm_SyncOp(op->scp, bufferp, op->userp, &op->req, PRSFS_LOOKUP,
1527 CM_SCACHESYNC_NEEDCALLBACK |
1528 CM_SCACHESYNC_READ |
1529 CM_SCACHESYNC_BUFLOCKED);
1532 lock_ReleaseMutex(&op->scp->mx);
1536 cm_SyncOpDone(op->scp, bufferp,
1537 CM_SCACHESYNC_NEEDCALLBACK |
1538 CM_SCACHESYNC_READ |
1539 CM_SCACHESYNC_BUFLOCKED);
1541 if (cm_HaveBuffer(op->scp, bufferp, 1)) {
1542 lock_ReleaseMutex(&op->scp->mx);
1546 lock_ReleaseMutex(&bufferp->mx);
1547 code = cm_GetBuffer(op->scp, bufferp, NULL, op->userp, &op->req);
1548 lock_ReleaseMutex(&op->scp->mx);
1549 lock_ObtainMutex(&bufferp->mx);
1556 cm_DirOpDelBuffer(op, bufferp, 0);
1557 buf_Release(bufferp);
1566 /* now to figure out where the data is */
1567 thyper = LargeIntegerSubtract(pageOffset, bufferOffset);
1569 osi_assert(thyper.HighPart == 0);
1570 osi_assert(cm_data.buf_blockSize > thyper.LowPart &&
1571 cm_data.buf_blockSize - thyper.LowPart >= CM_DIR_PAGESIZE);
1573 datap = (void *) (((char *)bufferp->datap) + thyper.LowPart);
1578 /* also, if we are writing past EOF, we should make a note of the
1580 thyper = LargeIntegerAdd(pageOffset,
1581 ConvertLongToLargeInteger(CM_DIR_PAGESIZE));
1582 if (LargeIntegerLessThan(op->newLength, thyper)) {
1583 op->newLength = thyper;
1588 *bufferpp = bufferp;
1590 osi_Log1(afsd_logp, "cm_DirGetPage returning code 0x%x", code);
1597 cm_DirEntryListAdd(char * namep, cm_dirEntryList_t ** list)
1600 cm_dirEntryList_t * entry;
1602 len = strlen(namep);
1603 len += sizeof(cm_dirEntryList_t);
1605 entry = malloc(len);
1607 entry->nextp = *list;
1608 strcpy(entry->name, namep);
1614 cm_DirEntryListFree(cm_dirEntryList_t ** list)
1616 cm_dirEntryList_t * entry;
1617 cm_dirEntryList_t * next;
1619 for (entry = *list; entry; entry = next) {
1620 next = entry->nextp;