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_ReleaseRead(&scp->dirlock);
1034 lock_ObtainWrite(&scp->dirlock);
1038 lock_ObtainMutex(&scp->mx);
1041 if (scp->dirBplus &&
1042 scp->dirDataVersion != scp->dataVersion)
1046 freeBtree(scp->dirBplus);
1047 scp->dirBplus = NULL;
1048 scp->dirDataVersion = -1;
1051 if (!scp->dirBplus) {
1053 lock_ReleaseMutex(&scp->mx);
1056 cm_BPlusDirBuildTree(scp, userp, reqp);
1058 lock_ObtainMutex(&scp->mx);
1061 if (op->dataVersion != scp->dataVersion) {
1062 /* We lost the race, therefore we must update the
1063 * dirop state and retry to build the tree.
1065 op->length = scp->length;
1066 op->newLength = op->length;
1067 op->dataVersion = scp->dataVersion;
1068 op->newDataVersion = op->dataVersion;
1073 scp->dirDataVersion = scp->dataVersion;
1078 case CM_DIRLOCK_NONE:
1079 lock_ReleaseWrite(&scp->dirlock);
1081 case CM_DIRLOCK_READ:
1082 lock_ConvertWToR(&scp->dirlock);
1084 case CM_DIRLOCK_WRITE:
1086 /* got it already */;
1091 /* we know that haveWrite matches lockType at this point */
1093 case CM_DIRLOCK_NONE:
1095 lock_ReleaseWrite(&scp->dirlock);
1097 lock_ReleaseRead(&scp->dirlock);
1099 case CM_DIRLOCK_READ:
1100 osi_assert(!haveWrite);
1102 case CM_DIRLOCK_WRITE:
1104 osi_assert(haveWrite);
1107 op->lockType = lockType;
1109 lock_ReleaseMutex(&scp->mx);
1112 lock_ReleaseWrite(&scp->dirlock);
1114 lock_ReleaseRead(&scp->dirlock);
1116 lock_ReleaseMutex(&scp->mx);
1123 /* Check if it is safe for us to perform local directory updates.
1124 Called with scp->mx unlocked. */
1126 cm_CheckDirOpForSingleChange(cm_dirOp_t * op)
1131 if (op->scp == NULL)
1134 lock_ObtainMutex(&op->scp->mx);
1135 code = cm_DirCheckStatus(op, 1);
1138 op->dataVersion == op->scp->dataVersion - 1) {
1139 /* only one set of changes happened between cm_BeginDirOp()
1140 and this function. It is safe for us to perform local
1142 op->newDataVersion = op->scp->dataVersion;
1143 op->newLength = op->scp->serverLength;
1147 lock_ReleaseMutex(&op->scp->mx);
1150 osi_Log0(afsd_logp, "cm_CheckDirOpForSingleChange succeeded");
1153 "cm_CheckDirOpForSingleChange failed. code=0x%x, old dv=%I64d, new dv=%I64d",
1154 code, op->dataVersion, op->scp->dataVersion);
1158 /* End a sequence of directory operations.
1159 * Called with op->scp->mx unlocked.*/
1161 cm_EndDirOp(cm_dirOp_t * op)
1165 if (op->scp == NULL)
1168 osi_Log2(afsd_logp, "Ending dirOp 0x%p with %d dirty buffer releases",
1169 op, op->dirtyBufCount);
1171 if (op->dirtyBufCount > 0) {
1173 /* update the data version on the B+ tree */
1174 if (op->scp->dirBplus &&
1175 op->scp->dirDataVersion == op->dataVersion) {
1177 switch (op->lockType) {
1178 case CM_DIRLOCK_READ:
1179 lock_ReleaseRead(&op->scp->dirlock);
1180 /* fall through ... */
1181 case CM_DIRLOCK_NONE:
1182 lock_ObtainWrite(&op->scp->dirlock);
1183 op->lockType = CM_DIRLOCK_WRITE;
1185 case CM_DIRLOCK_WRITE:
1187 /* already got it */;
1189 op->scp->dirDataVersion = op->newDataVersion;
1193 /* we made changes. We should go through the list of buffers
1194 * and update the dataVersion for each. */
1195 lock_ObtainMutex(&op->scp->mx);
1196 code = buf_ForceDataVersion(op->scp, op->dataVersion, op->newDataVersion);
1197 lock_ReleaseMutex(&op->scp->mx);
1200 switch (op->lockType) {
1201 case CM_DIRLOCK_NONE:
1203 case CM_DIRLOCK_READ:
1204 lock_ReleaseRead(&op->scp->dirlock);
1206 case CM_DIRLOCK_WRITE:
1208 lock_ReleaseWrite(&op->scp->dirlock);
1212 cm_ReleaseSCache(op->scp);
1216 cm_ReleaseUser(op->userp);
1219 osi_assertx(op->nBuffers == 0, "Buffer leak after dirOp termination");
1224 /* NOTE: Called without scp->mx and without bufferp->mx */
1226 cm_DirOpAddBuffer(cm_dirOp_t * op, cm_buf_t * bufferp)
1231 osi_Log2(afsd_logp, "cm_DirOpAddBuffer for op 0x%p, buffer %p", op, bufferp);
1233 if (bufferp == NULL)
1236 for (i=0; i < CM_DIROP_MAXBUFFERS; i++) {
1237 if ((op->buffers[i].flags & CM_DIROPBUFF_INUSE) &&
1238 op->buffers[i].bufferp == bufferp) {
1243 if (i < CM_DIROP_MAXBUFFERS) {
1244 /* we already have this buffer on our list */
1246 op->buffers[i].refcount++;
1248 "cm_DirOpAddBuffer: the buffer is already listed for the dirOp");
1251 /* we have to add a new buffer */
1252 osi_assertx(op->nBuffers < CM_DIROP_MAXBUFFERS - 1,
1253 "DirOp has exceeded CM_DIROP_MAXBUFFERS buffers");
1255 for (i=0; i < CM_DIROP_MAXBUFFERS; i++) {
1256 if (!(op->buffers[i].flags & CM_DIROPBUFF_INUSE))
1260 osi_assert(i < CM_DIROP_MAXBUFFERS);
1262 lock_ObtainMutex(&bufferp->mx);
1263 lock_ObtainMutex(&op->scp->mx);
1265 /* Make sure we are synchronized. */
1266 osi_assert(op->lockType != CM_DIRLOCK_NONE);
1268 code = cm_SyncOp(op->scp, bufferp, op->userp, &op->req, PRSFS_LOOKUP,
1269 CM_SCACHESYNC_NEEDCALLBACK |
1270 (op->lockType == CM_DIRLOCK_WRITE ? CM_SCACHESYNC_WRITE : CM_SCACHESYNC_READ) |
1271 CM_SCACHESYNC_BUFLOCKED);
1273 if (code == 0 && bufferp->dataVersion != op->dataVersion) {
1274 osi_Log2(afsd_logp, "cm_DirOpAddBuffer: buffer data version mismatch. buf dv = %I64d. needs %I64d",
1275 bufferp->dataVersion, op->dataVersion);
1277 cm_SyncOpDone(op->scp, bufferp,
1278 CM_SCACHESYNC_NEEDCALLBACK |
1279 (op->lockType == CM_DIRLOCK_WRITE ? CM_SCACHESYNC_WRITE : CM_SCACHESYNC_READ) |
1280 CM_SCACHESYNC_BUFLOCKED);
1282 code = CM_ERROR_INVAL;
1285 lock_ReleaseMutex(&op->scp->mx);
1286 lock_ReleaseMutex(&bufferp->mx);
1289 osi_Log1(afsd_logp, "cm_DirOpAddBuffer: failed to sync buffer. code=0x%x",
1295 op->buffers[i].bufferp = bufferp;
1296 op->buffers[i].refcount = 1; /* start with one ref */
1297 op->buffers[i].flags = CM_DIROPBUFF_INUSE;
1301 osi_Log0(afsd_logp, "cm_DirOpAddBuffer: returning success");
1307 /* Note: Called without op->scp->mx */
1309 cm_DirOpFindBuffer(cm_dirOp_t * op, osi_hyper_t offset, cm_buf_t ** bufferpp)
1313 for (i=0; i < CM_DIROP_MAXBUFFERS; i++) {
1314 if ((op->buffers[i].flags & CM_DIROPBUFF_INUSE) &&
1315 LargeIntegerEqualTo(op->buffers[i].bufferp->offset, offset))
1319 if (i < CM_DIROP_MAXBUFFERS) {
1321 op->buffers[i].refcount++;
1322 buf_Hold(op->buffers[i].bufferp);
1323 *bufferpp = op->buffers[i].bufferp;
1325 osi_Log2(afsd_logp, "cm_DirOpFindBuffer: found buffer for offset [%x:%x]",
1326 offset.HighPart, offset.LowPart);
1330 osi_Log2(afsd_logp, "cm_DirOpFindBuffer: buffer not found for offset [%x:%x]",
1331 offset.HighPart, offset.LowPart);
1336 /* NOTE: called with scp->mx held or not depending on the flags */
1338 cm_DirOpDelBuffer(cm_dirOp_t * op, cm_buf_t * bufferp, int flags)
1342 osi_Log3(afsd_logp, "cm_DirOpDelBuffer for op 0x%p, buffer 0x%p, flags=%d",
1343 op, bufferp, flags);
1345 for (i=0; i < CM_DIROP_MAXBUFFERS; i++) {
1346 if ((op->buffers[i].flags & CM_DIROPBUFF_INUSE) &&
1347 op->buffers[i].bufferp == bufferp)
1351 if (i < CM_DIROP_MAXBUFFERS) {
1353 if (flags & DIROP_MODIFIED)
1354 op->dirtyBufCount++;
1356 osi_assert(op->buffers[i].refcount > 0);
1357 op->buffers[i].refcount --;
1359 if (op->buffers[i].refcount == 0) {
1360 /* this was the last reference we had */
1362 osi_Log0(afsd_logp, "cm_DirOpDelBuffer: releasing buffer");
1364 /* if this buffer was modified, then we update the data
1365 version of the buffer with the data version of the
1367 if (!(flags & DIROP_SCPLOCKED)) {
1368 lock_ObtainMutex(&op->scp->mx);
1371 /* first make sure that the buffer is idle. It should
1372 have been idle all along. */
1373 osi_assertx((bufferp->cmFlags & (CM_BUF_CMFETCHING |
1374 CM_BUF_CMSTORING)) == 0,
1375 "Buffer is not idle while performing dirOp");
1377 cm_SyncOpDone(op->scp, bufferp,
1378 CM_SCACHESYNC_NEEDCALLBACK |
1379 (op->lockType == CM_DIRLOCK_WRITE ? CM_SCACHESYNC_WRITE : CM_SCACHESYNC_READ));
1382 osi_assert(bufferp->dataVersion == op->dataVersion);
1385 lock_ReleaseMutex(&op->scp->mx);
1387 lock_ObtainMutex(&bufferp->mx);
1389 if (flags & DIROP_SCPLOCKED) {
1390 lock_ObtainMutex(&op->scp->mx);
1393 if (flags & DIROP_MODIFIED) {
1394 /* We don't update the dataversion here. Instead we
1395 wait until the dirOp is completed and then flip the
1396 dataversion on all the buffers in one go.
1397 Otherwise we won't know if the dataversion is
1398 current because it was fetched from the server or
1399 because we touched it during the dirOp. */
1401 if (bufferp->userp != op->userp) {
1402 if (bufferp->userp != NULL)
1403 cm_ReleaseUser(bufferp->userp);
1404 cm_HoldUser(op->userp);
1405 bufferp->userp = op->userp;
1409 lock_ReleaseMutex(&bufferp->mx);
1411 op->buffers[i].bufferp = NULL;
1412 buf_Release(bufferp);
1413 op->buffers[i].flags = 0;
1419 /* we have other references to this buffer. so we have to
1425 osi_Log0(afsd_logp, "cm_DirOpDelBuffer: buffer not found");
1426 osi_assertx(FALSE, "Attempt to delete a non-existent buffer from a dirOp");
1431 /* Check if we have current status and a callback for the given scp.
1432 This should be called before cm_DirGetPage() is called per scp.
1435 scp->mx locked state indicated by parameter
1438 scp->mx same state as upon entry
1441 scp->mx may be released
1444 cm_DirCheckStatus(cm_dirOp_t * op, afs_uint32 locked)
1449 lock_ObtainMutex(&op->scp->mx);
1450 code = cm_SyncOp(op->scp, NULL, op->userp, &op->req, PRSFS_LOOKUP,
1451 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1453 lock_ReleaseMutex(&op->scp->mx);
1455 osi_Log2(afsd_logp, "cm_DirCheckStatus for op 0x%p returning code 0x%x",
1461 /* Release a directory buffer that was obtained via a call to
1462 cm_DirGetPage() or any other function that returns a locked, held,
1463 directory page buffer.
1465 Called with scp->mx unlocked
1468 cm_DirReleasePage(cm_dirOp_t * op, cm_buf_t ** bufferpp, int modified)
1475 cm_DirOpDelBuffer(op, *bufferpp,
1476 ((modified ? DIROP_MODIFIED : 0)));
1477 buf_Release(*bufferpp);
1484 Returns the index'th directory page from scp. The userp and reqp
1485 will be used to fetch the buffer from the fileserver if necessary.
1486 If the call is successful, a locked and held cm_buf_t is returned
1487 via buferpp and a pointer to the directory page is returned via
1490 The returned buffer should be released via a call to
1491 cm_DirReleasePage() or by passing it into a subsequent call to
1492 cm_DirGetPage() for the *same* scp.
1494 If a *locked* buffer for the *same* scp is passed in via bufferpp
1495 to the function, it will check if the requested directory page is
1496 located in the specified buffer. If not, the buffer will be
1497 released and a new buffer returned that contains the requested
1500 Note: If a buffer is specified on entry via bufferpp, it is assumed
1501 that the buffer is unmodified. If the buffer is modified, it
1502 should be released via cm_DirReleasePage().
1506 If *bufferpp is non-NULL, then *bufferpp->mx is locked.
1510 If *bufferpp is non-NULL, then *bufferpp->mx is locked.
1513 scp->mx will be obtained and released
1517 cm_DirGetPage(cm_dirOp_t * op,
1518 long index, cm_buf_t ** bufferpp, void ** datapp)
1520 osi_hyper_t pageOffset; /* offset of the dir page from the
1521 start of the directory */
1522 osi_hyper_t bufferOffset; /* offset of the buffer from the start
1526 cm_buf_t * bufferp = NULL;
1528 void * datap = NULL;
1532 osi_Log2(afsd_logp, "cm_DirGetPage for op 0x%p, index %d", op, index);
1534 pageOffset = ConvertLongToLargeInteger(index * CM_DIR_PAGESIZE);
1535 bufferOffset.HighPart = pageOffset.HighPart;
1536 bufferOffset.LowPart = pageOffset.LowPart & ~(cm_data.buf_blockSize - 1);
1538 bufferp = *bufferpp;
1539 if (bufferp != NULL) {
1540 osi_assert(cm_FidCmp(&bufferp->fid, &op->scp->fid) == 0);
1542 thyper = bufferp->offset;
1545 if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
1549 buf_Release(bufferp);
1550 cm_DirOpDelBuffer(op, bufferp, 0);
1554 /* first check if we are already working with the buffer */
1555 if (cm_DirOpFindBuffer(op, bufferOffset, &bufferp)) {
1560 code = buf_Get(op->scp, &bufferOffset, &bufferp);
1562 osi_Log1(afsd_logp, " buf_Get returned code 0x%x", code);
1567 osi_assert(bufferp != NULL);
1569 /* DirOpAddBuffer will obtain bufferp->mx if necessary */
1570 code = cm_DirOpAddBuffer(op, bufferp);
1573 /* for some reason, the buffer was rejected. We can't use
1574 this buffer, and since this is the only buffer we can
1575 potentially use, there's no recourse.*/
1576 buf_Release(bufferp);
1582 /* The code below is for making sure the buffer contains
1583 current data. This is a bad idea, since the whole point of
1584 doing directory updates locally is to avoid fetching all
1585 the data from the server. */
1587 lock_ObtainMutex(&op->scp->mx);
1588 code = cm_SyncOp(op->scp, bufferp, op->userp, &op->req, PRSFS_LOOKUP,
1589 CM_SCACHESYNC_NEEDCALLBACK |
1590 CM_SCACHESYNC_READ |
1591 CM_SCACHESYNC_BUFLOCKED);
1594 lock_ReleaseMutex(&op->scp->mx);
1598 cm_SyncOpDone(op->scp, bufferp,
1599 CM_SCACHESYNC_NEEDCALLBACK |
1600 CM_SCACHESYNC_READ |
1601 CM_SCACHESYNC_BUFLOCKED);
1603 if (cm_HaveBuffer(op->scp, bufferp, 1)) {
1604 lock_ReleaseMutex(&op->scp->mx);
1608 lock_ReleaseMutex(&bufferp->mx);
1609 code = cm_GetBuffer(op->scp, bufferp, NULL, op->userp, &op->req);
1610 lock_ReleaseMutex(&op->scp->mx);
1611 lock_ObtainMutex(&bufferp->mx);
1618 cm_DirOpDelBuffer(op, bufferp, 0);
1619 buf_Release(bufferp);
1628 /* now to figure out where the data is */
1629 thyper = LargeIntegerSubtract(pageOffset, bufferOffset);
1631 osi_assert(thyper.HighPart == 0);
1632 osi_assert(cm_data.buf_blockSize > thyper.LowPart &&
1633 cm_data.buf_blockSize - thyper.LowPart >= CM_DIR_PAGESIZE);
1635 datap = (void *) (((char *)bufferp->datap) + thyper.LowPart);
1640 /* also, if we are writing past EOF, we should make a note of the
1642 thyper = LargeIntegerAdd(pageOffset,
1643 ConvertLongToLargeInteger(CM_DIR_PAGESIZE));
1644 if (LargeIntegerLessThan(op->newLength, thyper)) {
1645 op->newLength = thyper;
1650 *bufferpp = bufferp;
1652 osi_Log1(afsd_logp, "cm_DirGetPage returning code 0x%x", code);
1659 cm_DirEntryListAdd(char * namep, cm_dirEntryList_t ** list)
1662 cm_dirEntryList_t * entry;
1664 len = strlen(namep);
1665 len += sizeof(cm_dirEntryList_t);
1667 entry = malloc(len);
1669 entry->nextp = *list;
1670 strcpy(entry->name, namep);
1676 cm_DirEntryListFree(cm_dirEntryList_t ** list)
1678 cm_dirEntryList_t * entry;
1679 cm_dirEntryList_t * next;
1681 for (entry = *list; entry; entry = next) {
1682 next = entry->nextp;