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, size_t *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->rw is unlocked
140 op->scp->rw 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->rw is locked
238 op->scp->rw 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->rw is unlocked
275 op->scp->rw 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->rw */
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->rw
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->rw
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->rw 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");
576 /* Look up a file name in directory.
579 op->scp->rw is unlocked
582 op->scp->rw is unlocked
584 None of the directory buffers for op->scp should be locked by the
588 cm_DirLookup(cm_dirOp_t * op, char *entry, cm_fid_t * cfid)
590 cm_dirEntry_t *firstitem = NULL;
591 cm_buf_t *itembuf = NULL;
592 unsigned short *previtem = NULL;
593 cm_buf_t *pibuf = NULL;
598 QueryPerformanceCounter(&start);
600 osi_Log2(afsd_logp, "cm_DirLookup for op 0x%p, entry[%s]",
601 op, osi_LogSaveString(afsd_logp, entry));
603 code = cm_DirFindItem(op, entry,
604 &itembuf, &firstitem,
612 cm_DirReleasePage(op, &pibuf, FALSE);
614 cfid->cell = op->scp->fid.cell;
615 cfid->volume = op->scp->fid.volume;
616 cfid->vnode = ntohl(firstitem->fid.vnode);
617 cfid->unique = ntohl(firstitem->fid.unique);
619 cm_DirReleasePage(op, &itembuf, FALSE);
621 osi_Log2(afsd_logp, "cm_DirLookup returning fid[%d,%d]",
622 cfid->vnode, cfid->unique);
628 QueryPerformanceCounter(&end);
630 dir_lookup_time += (end.QuadPart - start.QuadPart);
635 /* Look up a file name in directory.
638 op->scp->rw is locked
641 op->scp->rw is locked
643 None of the directory buffers for op->scp should be locked by the
647 cm_DirLookupOffset(cm_dirOp_t * op, char *entry, cm_fid_t *cfid, osi_hyper_t *offsetp)
649 cm_dirEntry_t *firstitem = NULL;
650 cm_buf_t *itembuf = NULL;
651 unsigned short *previtem = NULL;
652 cm_buf_t *pibuf = NULL;
656 osi_Log2(afsd_logp, "cm_DirLookupOffset for op 0x%p, entry[%s]",
657 op, osi_LogSaveString(afsd_logp, entry));
659 code = cm_DirFindItem(op, entry,
660 &itembuf, &firstitem,
665 cm_DirReleasePage(op, &pibuf, FALSE);
667 cfid->cell = op->scp->fid.cell;
668 cfid->volume = op->scp->fid.volume;
669 cfid->vnode = ntohl(firstitem->fid.vnode);
670 cfid->unique = ntohl(firstitem->fid.unique);
674 thyper = itembuf->offset;
675 thyper = LargeIntegerAdd(thyper,
676 ConvertLongToLargeInteger(((char *) firstitem) - itembuf->datap));
681 cm_DirReleasePage(op, &itembuf, FALSE);
683 osi_Log2(afsd_logp, "cm_DirLookupOffset returning fid[%d,%d]",
684 cfid->vnode, cfid->unique);
686 osi_Log2(afsd_logp, " offset [%x:%x]",
687 offsetp->HighPart, offsetp->LowPart);
693 /* Apply a function to every directory entry in a directory.
696 op->scp->rw is locked
699 op->scp->rw is locked
701 None of the directory buffers for op->scp should be locked by the
704 The hook function cannot modify or lock any directory buffers.
707 cm_DirApply(cm_dirOp_t * op, int (*hookproc) (void *, char *, long, long), void *hook)
709 /* Enumerate the contents of a directory. */
713 cm_dirHeader_t *dhp = NULL;
714 cm_buf_t *dhpbuf = NULL;
716 cm_dirEntry_t *ep = NULL;
717 cm_buf_t *epbuf = NULL;
721 code = cm_DirGetPage(op, 0, &dhpbuf, &dhp);
725 for (i = 0; i < CM_DIR_NHASHENT; i++) {
726 /* For each hash chain, enumerate everyone on the list. */
727 num = ntohs(dhp->hashTable[i]);
729 /* Walk down the hash table list. */
730 code = cm_DirGetBlob(op, num, &epbuf, &ep);
732 cm_DirReleasePage(op, &dhpbuf, FALSE);
736 num = ntohs(ep->next);
737 (*hookproc) (hook, ep->name, ntohl(ep->fid.vnode),
738 ntohl(ep->fid.unique));
740 cm_DirReleasePage(op, &epbuf, FALSE);
743 cm_DirReleasePage(op, &dhpbuf, FALSE);
748 /* Check if a directory is empty
751 op->scp->rw is locked
754 op->scp->rw is locked
756 None of the directory buffers for op->scp should be locked by the
760 cm_DirIsEmpty(cm_dirOp_t * op)
762 /* Enumerate the contents of a directory. */
766 cm_dirHeader_t *dhp = NULL;
767 cm_buf_t *dhpbuf = NULL;
769 cm_dirEntry_t *ep = NULL;
770 cm_buf_t *epbuf = NULL;
774 code = cm_DirGetPage(op, 0, &dhpbuf, &dhp);
778 for (i = 0; i < CM_DIR_NHASHENT; i++) {
779 /* For each hash chain, enumerate everyone on the list. */
780 num = ntohs(dhp->hashTable[i]);
783 /* Walk down the hash table list. */
784 code = cm_DirGetBlob(op, num, &epbuf, &ep);
788 if (strcmp(ep->name, "..") && strcmp(ep->name, ".")) {
789 cm_DirReleasePage(op, &epbuf, FALSE);
790 cm_DirReleasePage(op, &dhpbuf, FALSE);
793 num = ntohs(ep->next);
794 cm_DirReleasePage(op, &epbuf, FALSE);
797 cm_DirReleasePage(op, &dhpbuf, FALSE);
801 /* Return a pointer to an entry, given its number.
805 if *bufferpp != NULL, then *bufferpp->mx is locked
808 scp->rw may be unlocked
809 *bufferpp may be released
813 if *bufferpp != NULL, then *bufferpp->mx is locked
815 *bufferpp should be released via cm_DirReleasePage() or any other
816 *call that releases a directory buffer.
819 cm_DirGetBlob(cm_dirOp_t * op,
820 unsigned int blobno, cm_buf_t ** bufferpp, cm_dirEntry_t ** blobpp)
825 osi_Log2(afsd_logp, "cm_DirGetBlob for op 0x%p, blobno=%d",
828 code = cm_DirGetPage(op, blobno >> CM_DIR_LEPP,
829 bufferpp, (void **) &ep);
833 *blobpp = (cm_dirEntry_t *) (ep + 32 * (blobno & (CM_DIR_EPP - 1)));
839 cm_DirHash(char *string)
841 /* Hash a string to a number between 0 and NHASHENT. */
842 register unsigned char tc;
846 while ((tc = (*string++))) {
850 tval = hval & (CM_DIR_NHASHENT - 1);
854 tval = CM_DIR_NHASHENT - tval;
858 /* Find a directory entry, given its name. This entry returns a
859 * pointer to a locked buffer, and a pointer to a locked buffer (in
860 * previtem) referencing the found item (to aid the delete code). If
861 * no entry is found, however, no items are left locked, and a null
862 * pointer is returned instead.
871 cm_DirFindItem(cm_dirOp_t * op,
873 cm_buf_t ** itembufpp, cm_dirEntry_t ** itempp,
874 cm_buf_t ** prevbufpp, unsigned short **previtempp)
877 cm_dirHeader_t *dhp = NULL;
878 unsigned short *lp = NULL;
879 cm_dirEntry_t *tp = NULL;
880 cm_buf_t *hashbufp = NULL;
881 cm_buf_t *itembufp = NULL;
884 osi_Log2(afsd_logp, "cm_DirFindItem for op 0x%p, entry[%s]",
885 op, osi_LogSaveString(afsd_logp, ename));
887 i = cm_DirHash(ename);
889 if (op->scp->fileType != CM_SCACHETYPE_DIRECTORY) {
890 osi_Log0(afsd_logp, "cm_DirFindItem: The scp is not a directory");
891 return CM_ERROR_INVAL;
894 code = cm_DirGetPage(op, 0, &hashbufp, (void **) &dhp);
899 if (dhp->hashTable[i] == 0) {
901 osi_Log1(afsd_logp, "cm_DirFindItem: Hash bucket %d is empty", i);
902 cm_DirReleasePage(op, &hashbufp, FALSE);
906 code = cm_DirGetBlob(op,
907 (u_short) ntohs(dhp->hashTable[i]),
910 cm_DirReleasePage(op, &hashbufp, FALSE);
914 lp = &(dhp->hashTable[i]);
918 lp : pointer to blob number of entry we are looking at
919 hashbufp : buffer containing lp
920 tp : pointer to entry we are looking at
921 itembufp : buffer containing tp
924 /* Look at each hash conflict entry. */
925 if (!strcmp(ename, tp->name)) {
926 osi_Log0(afsd_logp, "cm_DirFindItem: returning success");
927 /* Found our entry. */
929 *prevbufpp = hashbufp;
931 *itembufpp = itembufp;
936 cm_DirReleasePage(op, &hashbufp, FALSE);
943 /* The end of the line */
944 osi_Log0(afsd_logp, "cm_DirFindItem: returning ENOENT");
945 cm_DirReleasePage(op, &hashbufp, FALSE);
949 code = cm_DirGetBlob(op,
950 (u_short) ntohs(*lp),
954 cm_DirReleasePage(op, &hashbufp, FALSE);
960 /* Begin a sequence of directory operations.
961 * Called with scp->rw unlocked.
964 cm_BeginDirOp(cm_scache_t * scp, cm_user_t * userp, cm_req_t * reqp,
965 afs_uint32 lockType, cm_dirOp_t * op)
968 int i, mxheld = 0, haveWrite = 0;
970 osi_Log3(afsd_logp, "Beginning dirOp[0x%p] for scp[0x%p], userp[0x%p]",
973 memset(op, 0, sizeof(*op));
979 cm_InitReq(&op->req);
981 op->dirtyBufCount = 0;
984 for (i=0; i < CM_DIROP_MAXBUFFERS; i++) {
985 op->buffers[i].flags = 0;
988 if (lockType == CM_DIRLOCK_WRITE) {
989 lock_ObtainWrite(&scp->dirlock);
992 lock_ObtainRead(&scp->dirlock);
995 lock_ObtainWrite(&scp->rw);
997 code = cm_DirCheckStatus(op, 1);
999 op->length = scp->length;
1000 op->newLength = op->length;
1001 op->dataVersion = scp->dataVersion;
1002 op->newDataVersion = op->dataVersion;
1005 if (!cm_BPlusTrees ||
1007 scp->dirDataVersion == scp->dataVersion))
1009 /* we know that haveWrite matches lockType at this point */
1011 case CM_DIRLOCK_NONE:
1013 lock_ReleaseWrite(&scp->dirlock);
1015 lock_ReleaseRead(&scp->dirlock);
1017 case CM_DIRLOCK_READ:
1018 osi_assert(!haveWrite);
1020 case CM_DIRLOCK_WRITE:
1022 osi_assert(haveWrite);
1025 if (!(scp->dirBplus &&
1026 scp->dirDataVersion == scp->dataVersion))
1031 lock_ReleaseWrite(&scp->rw);
1034 lock_ConvertRToW(&scp->dirlock);
1038 lock_ObtainWrite(&scp->rw);
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_ReleaseWrite(&scp->rw);
1056 cm_BPlusDirBuildTree(scp, userp, reqp);
1058 lock_ObtainWrite(&scp->rw);
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_ReleaseWrite(&scp->rw);
1112 lock_ReleaseWrite(&scp->dirlock);
1114 lock_ReleaseRead(&scp->dirlock);
1116 lock_ReleaseWrite(&scp->rw);
1123 /* Check if it is safe for us to perform local directory updates.
1124 Called with scp->rw unlocked. */
1126 cm_CheckDirOpForSingleChange(cm_dirOp_t * op)
1131 if (op->scp == NULL)
1134 lock_ObtainWrite(&op->scp->rw);
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_ReleaseWrite(&op->scp->rw);
1150 osi_Log0(afsd_logp, "cm_CheckDirOpForSingleChange succeeded");
1153 "cm_CheckDirOpForSingleChange failed. code=0x%x, old dv=%d, new dv=%d",
1154 code, op->dataVersion, op->scp->dataVersion);
1158 /* End a sequence of directory operations.
1159 * Called with op->scp->rw 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_ObtainWrite(&op->scp->rw);
1196 code = buf_ForceDataVersion(op->scp, op->dataVersion, op->newDataVersion);
1197 lock_ReleaseWrite(&op->scp->rw);
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->rw 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_ObtainWrite(&op->scp->rw);
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 = %d. needs %d",
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_ReleaseWrite(&op->scp->rw);
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->rw */
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->rw 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_ObtainWrite(&op->scp->rw);
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_ReleaseWrite(&op->scp->rw);
1387 lock_ObtainMutex(&bufferp->mx);
1389 if (flags & DIROP_SCPLOCKED) {
1390 lock_ObtainWrite(&op->scp->rw);
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->rw locked state indicated by parameter
1438 scp->rw same state as upon entry
1441 scp->rw may be released
1444 cm_DirCheckStatus(cm_dirOp_t * op, afs_uint32 locked)
1449 lock_ObtainWrite(&op->scp->rw);
1450 code = cm_SyncOp(op->scp, NULL, op->userp, &op->req, PRSFS_LOOKUP,
1451 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1453 lock_ReleaseWrite(&op->scp->rw);
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->rw 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->rw 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_ObtainWrite(&op->scp->rw);
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_ReleaseWrite(&op->scp->rw);
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_ReleaseWrite(&op->scp->rw);
1608 lock_ReleaseMutex(&bufferp->mx);
1609 code = cm_GetBuffer(op->scp, bufferp, NULL, op->userp, &op->req);
1610 lock_ReleaseWrite(&op->scp->rw);
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;