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, afs_uint32 locked);
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);
130 return 1 + ((i+16) >> 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.
960 * Called with scp->mx unlocked.
963 cm_BeginDirOp(cm_scache_t * scp, cm_user_t * userp, cm_req_t * reqp,
964 afs_uint32 lockType, cm_dirOp_t * op)
967 int i, mxheld = 0, haveWrite = 0;
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 if (lockType == CM_DIRLOCK_WRITE) {
988 lock_ObtainWrite(&scp->dirlock);
991 lock_ObtainRead(&scp->dirlock);
994 lock_ObtainMutex(&scp->mx);
996 code = cm_DirCheckStatus(op, 1);
998 op->length = scp->length;
999 op->newLength = op->length;
1000 op->dataVersion = scp->dataVersion;
1001 op->newDataVersion = op->dataVersion;
1004 if (!cm_BPlusTrees ||
1006 scp->dirDataVersion == scp->dataVersion))
1008 /* we know that haveWrite matches lockType at this point */
1010 case CM_DIRLOCK_NONE:
1012 lock_ReleaseWrite(&scp->dirlock);
1014 lock_ReleaseRead(&scp->dirlock);
1016 case CM_DIRLOCK_READ:
1017 osi_assert(!haveWrite);
1019 case CM_DIRLOCK_WRITE:
1021 osi_assert(haveWrite);
1024 if (!(scp->dirBplus &&
1025 scp->dirDataVersion == scp->dataVersion))
1030 lock_ReleaseMutex(&scp->mx);
1033 lock_ConvertRToW(&scp->dirlock);
1037 lock_ObtainMutex(&scp->mx);
1040 if (scp->dirBplus &&
1041 scp->dirDataVersion != scp->dataVersion)
1045 freeBtree(scp->dirBplus);
1046 scp->dirBplus = NULL;
1047 scp->dirDataVersion = -1;
1050 if (!scp->dirBplus) {
1052 lock_ReleaseMutex(&scp->mx);
1055 cm_BPlusDirBuildTree(scp, userp, reqp);
1057 lock_ObtainMutex(&scp->mx);
1060 if (op->dataVersion != scp->dataVersion) {
1061 /* We lost the race, therefore we must update the
1062 * dirop state and retry to build the tree.
1064 op->length = scp->length;
1065 op->newLength = op->length;
1066 op->dataVersion = scp->dataVersion;
1067 op->newDataVersion = op->dataVersion;
1072 scp->dirDataVersion = scp->dataVersion;
1077 case CM_DIRLOCK_NONE:
1078 lock_ReleaseWrite(&scp->dirlock);
1080 case CM_DIRLOCK_READ:
1081 lock_ConvertWToR(&scp->dirlock);
1083 case CM_DIRLOCK_WRITE:
1085 /* got it already */;
1090 /* we know that haveWrite matches lockType at this point */
1092 case CM_DIRLOCK_NONE:
1094 lock_ReleaseWrite(&scp->dirlock);
1096 lock_ReleaseRead(&scp->dirlock);
1098 case CM_DIRLOCK_READ:
1099 osi_assert(!haveWrite);
1101 case CM_DIRLOCK_WRITE:
1103 osi_assert(haveWrite);
1106 op->lockType = lockType;
1108 lock_ReleaseMutex(&scp->mx);
1111 lock_ReleaseWrite(&scp->dirlock);
1113 lock_ReleaseRead(&scp->dirlock);
1115 lock_ReleaseMutex(&scp->mx);
1122 /* Check if it is safe for us to perform local directory updates.
1123 Called with scp->mx unlocked. */
1125 cm_CheckDirOpForSingleChange(cm_dirOp_t * op)
1130 if (op->scp == NULL)
1133 lock_ObtainMutex(&op->scp->mx);
1134 code = cm_DirCheckStatus(op, 1);
1137 op->dataVersion == op->scp->dataVersion - 1) {
1138 /* only one set of changes happened between cm_BeginDirOp()
1139 and this function. It is safe for us to perform local
1141 op->newDataVersion = op->scp->dataVersion;
1142 op->newLength = op->scp->serverLength;
1146 lock_ReleaseMutex(&op->scp->mx);
1149 osi_Log0(afsd_logp, "cm_CheckDirOpForSingleChange succeeded");
1152 "cm_CheckDirOpForSingleChange failed. code=0x%x, old dv=%d, new dv=%d",
1153 code, op->dataVersion, op->scp->dataVersion);
1157 /* End a sequence of directory operations.
1158 * Called with op->scp->mx unlocked.*/
1160 cm_EndDirOp(cm_dirOp_t * op)
1164 if (op->scp == NULL)
1167 osi_Log2(afsd_logp, "Ending dirOp 0x%p with %d dirty buffer releases",
1168 op, op->dirtyBufCount);
1170 if (op->dirtyBufCount > 0) {
1172 /* update the data version on the B+ tree */
1173 if (op->scp->dirBplus &&
1174 op->scp->dirDataVersion == op->dataVersion) {
1176 switch (op->lockType) {
1177 case CM_DIRLOCK_READ:
1178 lock_ReleaseRead(&op->scp->dirlock);
1179 /* fall through ... */
1180 case CM_DIRLOCK_NONE:
1181 lock_ObtainWrite(&op->scp->dirlock);
1182 op->lockType = CM_DIRLOCK_WRITE;
1184 case CM_DIRLOCK_WRITE:
1186 /* already got it */;
1188 op->scp->dirDataVersion = op->newDataVersion;
1192 /* we made changes. We should go through the list of buffers
1193 * and update the dataVersion for each. */
1194 lock_ObtainMutex(&op->scp->mx);
1195 code = buf_ForceDataVersion(op->scp, op->dataVersion, op->newDataVersion);
1196 lock_ReleaseMutex(&op->scp->mx);
1199 switch (op->lockType) {
1200 case CM_DIRLOCK_NONE:
1202 case CM_DIRLOCK_READ:
1203 lock_ReleaseRead(&op->scp->dirlock);
1205 case CM_DIRLOCK_WRITE:
1207 lock_ReleaseWrite(&op->scp->dirlock);
1211 cm_ReleaseSCache(op->scp);
1215 cm_ReleaseUser(op->userp);
1218 osi_assertx(op->nBuffers == 0, "Buffer leak after dirOp termination");
1223 /* NOTE: Called without scp->mx and without bufferp->mx */
1225 cm_DirOpAddBuffer(cm_dirOp_t * op, cm_buf_t * bufferp)
1230 osi_Log2(afsd_logp, "cm_DirOpAddBuffer for op 0x%p, buffer %p", op, bufferp);
1232 if (bufferp == NULL)
1235 for (i=0; i < CM_DIROP_MAXBUFFERS; i++) {
1236 if ((op->buffers[i].flags & CM_DIROPBUFF_INUSE) &&
1237 op->buffers[i].bufferp == bufferp) {
1242 if (i < CM_DIROP_MAXBUFFERS) {
1243 /* we already have this buffer on our list */
1245 op->buffers[i].refcount++;
1247 "cm_DirOpAddBuffer: the buffer is already listed for the dirOp");
1250 /* we have to add a new buffer */
1251 osi_assertx(op->nBuffers < CM_DIROP_MAXBUFFERS - 1,
1252 "DirOp has exceeded CM_DIROP_MAXBUFFERS buffers");
1254 for (i=0; i < CM_DIROP_MAXBUFFERS; i++) {
1255 if (!(op->buffers[i].flags & CM_DIROPBUFF_INUSE))
1259 osi_assert(i < CM_DIROP_MAXBUFFERS);
1261 lock_ObtainMutex(&bufferp->mx);
1262 lock_ObtainMutex(&op->scp->mx);
1264 /* Make sure we are synchronized. */
1265 osi_assert(op->lockType != CM_DIRLOCK_NONE);
1267 code = cm_SyncOp(op->scp, bufferp, op->userp, &op->req, PRSFS_LOOKUP,
1268 CM_SCACHESYNC_NEEDCALLBACK |
1269 (op->lockType == CM_DIRLOCK_WRITE ? CM_SCACHESYNC_WRITE : CM_SCACHESYNC_READ) |
1270 CM_SCACHESYNC_BUFLOCKED);
1272 if (code == 0 && bufferp->dataVersion != op->dataVersion) {
1273 osi_Log2(afsd_logp, "cm_DirOpAddBuffer: buffer data version mismatch. buf dv = %d. needs %d",
1274 bufferp->dataVersion, op->dataVersion);
1276 cm_SyncOpDone(op->scp, bufferp,
1277 CM_SCACHESYNC_NEEDCALLBACK |
1278 (op->lockType == CM_DIRLOCK_WRITE ? CM_SCACHESYNC_WRITE : CM_SCACHESYNC_READ) |
1279 CM_SCACHESYNC_BUFLOCKED);
1281 code = CM_ERROR_INVAL;
1284 lock_ReleaseMutex(&op->scp->mx);
1285 lock_ReleaseMutex(&bufferp->mx);
1288 osi_Log1(afsd_logp, "cm_DirOpAddBuffer: failed to sync buffer. code=0x%x",
1294 op->buffers[i].bufferp = bufferp;
1295 op->buffers[i].refcount = 1; /* start with one ref */
1296 op->buffers[i].flags = CM_DIROPBUFF_INUSE;
1300 osi_Log0(afsd_logp, "cm_DirOpAddBuffer: returning success");
1306 /* Note: Called without op->scp->mx */
1308 cm_DirOpFindBuffer(cm_dirOp_t * op, osi_hyper_t offset, cm_buf_t ** bufferpp)
1312 for (i=0; i < CM_DIROP_MAXBUFFERS; i++) {
1313 if ((op->buffers[i].flags & CM_DIROPBUFF_INUSE) &&
1314 LargeIntegerEqualTo(op->buffers[i].bufferp->offset, offset))
1318 if (i < CM_DIROP_MAXBUFFERS) {
1320 op->buffers[i].refcount++;
1321 buf_Hold(op->buffers[i].bufferp);
1322 *bufferpp = op->buffers[i].bufferp;
1324 osi_Log2(afsd_logp, "cm_DirOpFindBuffer: found buffer for offset [%x:%x]",
1325 offset.HighPart, offset.LowPart);
1329 osi_Log2(afsd_logp, "cm_DirOpFindBuffer: buffer not found for offset [%x:%x]",
1330 offset.HighPart, offset.LowPart);
1335 /* NOTE: called with scp->mx held or not depending on the flags */
1337 cm_DirOpDelBuffer(cm_dirOp_t * op, cm_buf_t * bufferp, int flags)
1341 osi_Log3(afsd_logp, "cm_DirOpDelBuffer for op 0x%p, buffer 0x%p, flags=%d",
1342 op, bufferp, flags);
1344 for (i=0; i < CM_DIROP_MAXBUFFERS; i++) {
1345 if ((op->buffers[i].flags & CM_DIROPBUFF_INUSE) &&
1346 op->buffers[i].bufferp == bufferp)
1350 if (i < CM_DIROP_MAXBUFFERS) {
1352 if (flags & DIROP_MODIFIED)
1353 op->dirtyBufCount++;
1355 osi_assert(op->buffers[i].refcount > 0);
1356 op->buffers[i].refcount --;
1358 if (op->buffers[i].refcount == 0) {
1359 /* this was the last reference we had */
1361 osi_Log0(afsd_logp, "cm_DirOpDelBuffer: releasing buffer");
1363 /* if this buffer was modified, then we update the data
1364 version of the buffer with the data version of the
1366 if (!(flags & DIROP_SCPLOCKED)) {
1367 lock_ObtainMutex(&op->scp->mx);
1370 /* first make sure that the buffer is idle. It should
1371 have been idle all along. */
1372 osi_assertx((bufferp->cmFlags & (CM_BUF_CMFETCHING |
1373 CM_BUF_CMSTORING)) == 0,
1374 "Buffer is not idle while performing dirOp");
1376 cm_SyncOpDone(op->scp, bufferp,
1377 CM_SCACHESYNC_NEEDCALLBACK |
1378 (op->lockType == CM_DIRLOCK_WRITE ? CM_SCACHESYNC_WRITE : CM_SCACHESYNC_READ));
1381 osi_assert(bufferp->dataVersion == op->dataVersion);
1384 lock_ReleaseMutex(&op->scp->mx);
1386 lock_ObtainMutex(&bufferp->mx);
1388 if (flags & DIROP_SCPLOCKED) {
1389 lock_ObtainMutex(&op->scp->mx);
1392 if (flags & DIROP_MODIFIED) {
1393 /* We don't update the dataversion here. Instead we
1394 wait until the dirOp is completed and then flip the
1395 dataversion on all the buffers in one go.
1396 Otherwise we won't know if the dataversion is
1397 current because it was fetched from the server or
1398 because we touched it during the dirOp. */
1400 if (bufferp->userp != op->userp) {
1401 if (bufferp->userp != NULL)
1402 cm_ReleaseUser(bufferp->userp);
1403 cm_HoldUser(op->userp);
1404 bufferp->userp = op->userp;
1408 lock_ReleaseMutex(&bufferp->mx);
1410 op->buffers[i].bufferp = NULL;
1411 buf_Release(bufferp);
1412 op->buffers[i].flags = 0;
1418 /* we have other references to this buffer. so we have to
1424 osi_Log0(afsd_logp, "cm_DirOpDelBuffer: buffer not found");
1425 osi_assertx(FALSE, "Attempt to delete a non-existent buffer from a dirOp");
1430 /* Check if we have current status and a callback for the given scp.
1431 This should be called before cm_DirGetPage() is called per scp.
1434 scp->mx locked state indicated by parameter
1437 scp->mx same state as upon entry
1440 scp->mx may be released
1443 cm_DirCheckStatus(cm_dirOp_t * op, afs_uint32 locked)
1448 lock_ObtainMutex(&op->scp->mx);
1449 code = cm_SyncOp(op->scp, NULL, op->userp, &op->req, PRSFS_LOOKUP,
1450 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1452 lock_ReleaseMutex(&op->scp->mx);
1454 osi_Log2(afsd_logp, "cm_DirCheckStatus for op 0x%p returning code 0x%x",
1460 /* Release a directory buffer that was obtained via a call to
1461 cm_DirGetPage() or any other function that returns a locked, held,
1462 directory page buffer.
1464 Called with scp->mx unlocked
1467 cm_DirReleasePage(cm_dirOp_t * op, cm_buf_t ** bufferpp, int modified)
1474 cm_DirOpDelBuffer(op, *bufferpp,
1475 ((modified ? DIROP_MODIFIED : 0)));
1476 buf_Release(*bufferpp);
1483 Returns the index'th directory page from scp. The userp and reqp
1484 will be used to fetch the buffer from the fileserver if necessary.
1485 If the call is successful, a locked and held cm_buf_t is returned
1486 via buferpp and a pointer to the directory page is returned via
1489 The returned buffer should be released via a call to
1490 cm_DirReleasePage() or by passing it into a subsequent call to
1491 cm_DirGetPage() for the *same* scp.
1493 If a *locked* buffer for the *same* scp is passed in via bufferpp
1494 to the function, it will check if the requested directory page is
1495 located in the specified buffer. If not, the buffer will be
1496 released and a new buffer returned that contains the requested
1499 Note: If a buffer is specified on entry via bufferpp, it is assumed
1500 that the buffer is unmodified. If the buffer is modified, it
1501 should be released via cm_DirReleasePage().
1505 If *bufferpp is non-NULL, then *bufferpp->mx is locked.
1509 If *bufferpp is non-NULL, then *bufferpp->mx is locked.
1512 scp->mx will be obtained and released
1516 cm_DirGetPage(cm_dirOp_t * op,
1517 long index, cm_buf_t ** bufferpp, void ** datapp)
1519 osi_hyper_t pageOffset; /* offset of the dir page from the
1520 start of the directory */
1521 osi_hyper_t bufferOffset; /* offset of the buffer from the start
1525 cm_buf_t * bufferp = NULL;
1527 void * datap = NULL;
1531 osi_Log2(afsd_logp, "cm_DirGetPage for op 0x%p, index %d", op, index);
1533 pageOffset = ConvertLongToLargeInteger(index * CM_DIR_PAGESIZE);
1534 bufferOffset.HighPart = pageOffset.HighPart;
1535 bufferOffset.LowPart = pageOffset.LowPart & ~(cm_data.buf_blockSize - 1);
1537 bufferp = *bufferpp;
1538 if (bufferp != NULL) {
1539 osi_assert(cm_FidCmp(&bufferp->fid, &op->scp->fid) == 0);
1541 thyper = bufferp->offset;
1544 if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
1548 buf_Release(bufferp);
1549 cm_DirOpDelBuffer(op, bufferp, 0);
1553 /* first check if we are already working with the buffer */
1554 if (cm_DirOpFindBuffer(op, bufferOffset, &bufferp)) {
1559 code = buf_Get(op->scp, &bufferOffset, &bufferp);
1561 osi_Log1(afsd_logp, " buf_Get returned code 0x%x", code);
1566 osi_assert(bufferp != NULL);
1568 /* DirOpAddBuffer will obtain bufferp->mx if necessary */
1569 code = cm_DirOpAddBuffer(op, bufferp);
1572 /* for some reason, the buffer was rejected. We can't use
1573 this buffer, and since this is the only buffer we can
1574 potentially use, there's no recourse.*/
1575 buf_Release(bufferp);
1581 /* The code below is for making sure the buffer contains
1582 current data. This is a bad idea, since the whole point of
1583 doing directory updates locally is to avoid fetching all
1584 the data from the server. */
1586 lock_ObtainMutex(&op->scp->mx);
1587 code = cm_SyncOp(op->scp, bufferp, op->userp, &op->req, PRSFS_LOOKUP,
1588 CM_SCACHESYNC_NEEDCALLBACK |
1589 CM_SCACHESYNC_READ |
1590 CM_SCACHESYNC_BUFLOCKED);
1593 lock_ReleaseMutex(&op->scp->mx);
1597 cm_SyncOpDone(op->scp, bufferp,
1598 CM_SCACHESYNC_NEEDCALLBACK |
1599 CM_SCACHESYNC_READ |
1600 CM_SCACHESYNC_BUFLOCKED);
1602 if (cm_HaveBuffer(op->scp, bufferp, 1)) {
1603 lock_ReleaseMutex(&op->scp->mx);
1607 lock_ReleaseMutex(&bufferp->mx);
1608 code = cm_GetBuffer(op->scp, bufferp, NULL, op->userp, &op->req);
1609 lock_ReleaseMutex(&op->scp->mx);
1610 lock_ObtainMutex(&bufferp->mx);
1617 cm_DirOpDelBuffer(op, bufferp, 0);
1618 buf_Release(bufferp);
1627 /* now to figure out where the data is */
1628 thyper = LargeIntegerSubtract(pageOffset, bufferOffset);
1630 osi_assert(thyper.HighPart == 0);
1631 osi_assert(cm_data.buf_blockSize > thyper.LowPart &&
1632 cm_data.buf_blockSize - thyper.LowPart >= CM_DIR_PAGESIZE);
1634 datap = (void *) (((char *)bufferp->datap) + thyper.LowPart);
1639 /* also, if we are writing past EOF, we should make a note of the
1641 thyper = LargeIntegerAdd(pageOffset,
1642 ConvertLongToLargeInteger(CM_DIR_PAGESIZE));
1643 if (LargeIntegerLessThan(op->newLength, thyper)) {
1644 op->newLength = thyper;
1649 *bufferpp = bufferp;
1651 osi_Log1(afsd_logp, "cm_DirGetPage returning code 0x%x", code);
1658 cm_DirEntryListAdd(char * namep, cm_dirEntryList_t ** list)
1661 cm_dirEntryList_t * entry;
1663 len = strlen(namep);
1664 len += sizeof(cm_dirEntryList_t);
1666 entry = malloc(len);
1668 entry->nextp = *list;
1669 strcpy(entry->name, namep);
1675 cm_DirEntryListFree(cm_dirEntryList_t ** list)
1677 cm_dirEntryList_t * entry;
1678 cm_dirEntryList_t * next;
1680 for (entry = *list; entry; entry = next) {
1681 next = entry->nextp;