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, int 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 cm_DirPrefetchBuffers(cm_dirOp_t * op);
122 /* compute how many 32 byte entries an AFS 3 dir requires for storing
123 * the specified name.
126 cm_NameEntries(char *namep, size_t *lenp)
130 i = (long)strlen(namep);
132 return 1 + ((i+16) >> 5);
135 /* Create an entry in a file. Dir is a file representation, while
136 entry is a string name.
139 op->scp->rw is unlocked
142 op->scp->rw is unlocked
144 None of the directory buffers for op->scp should be locked by the
148 cm_DirCreateEntry(cm_dirOp_t * op, char *entry, cm_fid_t * cfid)
152 LARGE_INTEGER start, end;
154 cm_dirEntry_t *ep = NULL;
155 cm_buf_t *entrybuf = NULL;
157 unsigned short *pp = NULL;
158 cm_buf_t *prevptrbuf = NULL;
160 cm_dirHeader_t *dhp = NULL;
161 cm_buf_t *dhpbuf = NULL;
165 /* check name quality */
169 QueryPerformanceCounter(&start);
173 osi_Log4(afsd_logp, "cm_DirCreateEntry for op 0x%p, name [%s] and fid[%d,%d]",
174 op, osi_LogSaveString(afsd_logp, entry), cfid->vnode, cfid->unique);
176 /* First check if file already exists. */
177 code = cm_DirFindItem(op,
182 cm_DirReleasePage(op, &entrybuf, FALSE);
183 cm_DirReleasePage(op, &prevptrbuf, FALSE);
188 blobs = cm_NameEntries(entry, NULL); /* number of entries required */
189 firstelt = cm_DirFindBlobs(op, blobs);
191 osi_Log0(afsd_logp, "cm_DirCreateEntry returning EFBIG");
192 code = EFBIG; /* directory is full */
196 /* First, we fill in the directory entry. */
197 code = cm_DirGetBlob(op, firstelt, &entrybuf, &ep);
203 ep->flag = CM_DIR_FFIRST;
204 ep->fid.vnode = htonl(cfid->vnode);
205 ep->fid.unique = htonl(cfid->unique);
206 strcpy(ep->name, entry);
208 /* Now we just have to thread it on the hash table list. */
209 code = cm_DirGetPage(op, 0, &dhpbuf, &dhp);
211 cm_DirReleasePage(op, &entrybuf, TRUE);
216 i = cm_DirHash(entry);
218 ep->next = dhp->hashTable[i];
219 dhp->hashTable[i] = htons(firstelt);
221 cm_DirReleasePage(op, &dhpbuf, TRUE);
222 cm_DirReleasePage(op, &entrybuf, TRUE);
224 osi_Log0(afsd_logp, "cm_DirCreateEntry returning success");
228 QueryPerformanceCounter(&end);
230 dir_create_time += (end.QuadPart - start.QuadPart);
234 /* Return the length of a directory in pages
237 op->scp->rw is locked
240 op->scp->rw is locked
242 The first directory page for op->scp should not be locked by the
246 cm_DirLength(cm_dirOp_t * op)
249 cm_dirHeader_t *dhp = NULL;
250 cm_buf_t *dhpbuf = NULL;
254 code = cm_DirGetPage(op, 0, &dhpbuf, &dhp);
258 if (dhp->header.pgcount != 0)
259 ctr = ntohs(dhp->header.pgcount);
261 /* old style, count the pages */
263 for (i = 0; i < CM_DIR_MAXPAGES; i++)
264 if (dhp->alloMap[i] != CM_DIR_EPP)
267 cm_DirReleasePage(op, &dhpbuf, FALSE);
268 return ctr * CM_DIR_PAGESIZE;
271 /* Delete a directory entry.
274 op->scp->rw is unlocked
277 op->scp->rw is unlocked
279 None of the directory buffers for op->scp should be locked by the
283 cm_DirDeleteEntry(cm_dirOp_t * op, char *entry)
285 /* Delete an entry from a directory, including update of all free
286 entry descriptors. */
289 cm_dirEntry_t *firstitem = NULL;
290 cm_buf_t *itembuf = NULL;
291 unsigned short *previtem = NULL;
292 cm_buf_t *pibuf = NULL;
296 LARGE_INTEGER start, end;
298 QueryPerformanceCounter(&start);
300 osi_Log2(afsd_logp, "cm_DirDeleteEntry for op 0x%p, entry [%s]",
301 op, osi_LogSaveString(afsd_logp, entry));
303 code = cm_DirFindItem(op, entry,
304 &itembuf, &firstitem,
307 osi_Log0(afsd_logp, "cm_DirDeleteEntry returning ENOENT");
314 *previtem = firstitem->next;
315 cm_DirReleasePage(op, &pibuf, TRUE);
317 thyper = itembuf->offset;
318 thyper = LargeIntegerAdd(thyper,
319 ConvertLongToLargeInteger(((char *) firstitem) - itembuf->datap));
320 thyper = ExtendedLargeIntegerDivide(thyper, 32, &junk);
322 index = thyper.LowPart;
323 osi_assert(thyper.HighPart == 0);
325 nitems = cm_NameEntries(firstitem->name, NULL);
326 cm_DirReleasePage(op, &itembuf, FALSE);
328 cm_DirFreeBlobs(op, index, nitems);
330 osi_Log0(afsd_logp, "cm_DirDeleteEntry returning success");
334 QueryPerformanceCounter(&end);
336 dir_remove_time += (end.QuadPart - start.QuadPart);
341 /* Find a bunch of contiguous entries; at least nblobs in a row.
343 Called with op->scp->rw */
345 cm_DirFindBlobs(cm_dirOp_t * op, int nblobs)
350 cm_dirHeader_t *dhp = NULL;
351 cm_buf_t *dhpbuf = NULL;
352 int dhpModified = FALSE;
354 cm_pageHeader_t *pp = NULL;
355 cm_buf_t *pagebuf = NULL;
356 int pageModified = FALSE;
362 osi_Log2(afsd_logp, "cm_DirFindBlobs for op 0x%p, nblobs = %d",
365 code = cm_DirGetPage(op, 0, &dhpbuf, (void **) &dhp);
369 for (i = 0; i < CM_DIR_BIGMAXPAGES; i++) {
370 if (i >= CM_DIR_MAXPAGES || dhp->alloMap[i] >= nblobs) {
371 /* if page could contain enough entries */
372 /* If there are CM_DIR_EPP free entries, then the page is
373 not even allocated. */
374 if (i >= CM_DIR_MAXPAGES) {
376 /* this pages exists past the end of the old-style dir */
377 pgcount = ntohs(dhp->header.pgcount);
379 pgcount = CM_DIR_MAXPAGES;
380 dhp->header.pgcount = htons(pgcount);
384 if (i > pgcount - 1) {
385 /* this page is bigger than last allocated page */
386 cm_DirAddPage(op, i);
387 dhp->header.pgcount = htons(i + 1);
390 } else if (dhp->alloMap[i] == CM_DIR_EPP) {
391 /* Add the page to the directory. */
392 cm_DirAddPage(op, i);
393 dhp->alloMap[i] = CM_DIR_EPP - 1;
394 dhp->header.pgcount = htons(i + 1);
398 /* the create flag is not set for the GetPage call below
399 since the page should have been added if necessary
401 code = cm_DirGetPage(op, i, &pagebuf, &pp);
403 cm_DirReleasePage(op, &dhpbuf, dhpModified);
407 for (j = 0; j <= CM_DIR_EPP - nblobs; j++) {
409 for (k = 0; k < nblobs; k++)
410 if ((pp->freeBitmap[(j + k) >> 3] >> ((j + k) & 7)) & 1) {
420 /* Here we have the first index in j. We update the allocation maps
421 * and free up any resources we've got allocated. */
422 if (i < CM_DIR_MAXPAGES) {
423 dhp->alloMap[i] -= nblobs;
427 cm_DirReleasePage(op, &dhpbuf, dhpModified);
429 for (k = 0; k < nblobs; k++)
430 pp->freeBitmap[(j + k) >> 3] |= 1 << ((j + k) & 7);
432 cm_DirReleasePage(op, &pagebuf, TRUE);
434 osi_Log0(afsd_logp, "cm_DirFindBlobs returning success");
436 return j + i * CM_DIR_EPP;
438 cm_DirReleasePage(op, &pagebuf, pageModified);
442 /* If we make it here, the directory is full. */
443 osi_Log0(afsd_logp, "cm_DirFindBlobs directory is full");
444 cm_DirReleasePage(op, &dhpbuf, dhpModified);
448 /* Add a page to a directory.
450 Called with op->scp->rw
453 cm_DirAddPage(cm_dirOp_t * op, int pageno)
456 cm_pageHeader_t *pp = NULL;
457 cm_buf_t *pagebuf = NULL;
460 osi_Log2(afsd_logp, "cm_DirAddPage for op 0x%p, pageno=%d", op, pageno);
462 code = cm_DirGetPage(op, pageno, &pagebuf, (void **) &pp);
466 pp->tag = htons(1234);
469 pp->freeCount = CM_DIR_EPP - 1; /* The first dude is already allocated */
470 pp->freeBitmap[0] = 0x01;
471 for (i = 1; i < CM_DIR_EPP / 8; i++) /* It's a constant */
472 pp->freeBitmap[i] = 0;
474 cm_DirReleasePage(op, &pagebuf, TRUE);
476 osi_Log0(afsd_logp, "cm_DirAddPage returning success");
481 /* Free a whole bunch of directory entries.
483 Called with op->scp->rw
486 cm_DirFreeBlobs(cm_dirOp_t * op, int firstblob, int nblobs)
491 cm_dirHeader_t *dhp = NULL;
492 cm_buf_t *dhpbuf = NULL;
493 int dhpmodified = FALSE;
495 cm_pageHeader_t *pp = NULL;
496 cm_buf_t *pagebuf = NULL;
499 osi_Log3(afsd_logp, "cm_DirFreeBlobs for op 0x%p, firstblob=%d, nblobs=%d",
500 op, firstblob, nblobs);
502 page = firstblob / CM_DIR_EPP;
503 firstblob -= CM_DIR_EPP * page; /* convert to page-relative entry */
505 code = cm_DirGetPage(op, 0, &dhpbuf, &dhp);
509 if (page < CM_DIR_MAXPAGES) {
510 dhp->alloMap[page] += nblobs;
514 cm_DirReleasePage(op, &dhpbuf, dhpmodified);
516 code = cm_DirGetPage(op, page, &pagebuf, &pp);
518 for (i = 0; i < nblobs; i++)
519 pp->freeBitmap[(firstblob + i) >> 3] &=
520 ~(1 << ((firstblob + i) & 7));
521 cm_DirReleasePage(op, &pagebuf, TRUE);
524 osi_Log1(afsd_logp, "cm_DirFreeBlobs returning code 0x%x", code);
530 * Format an empty directory properly. Note that the first 13 entries in a
531 * directory header page are allocated, 1 to the page header, 4 to the
532 * allocation map and 8 to the hash table.
534 * Called with op->scp->rw unlocked
537 cm_DirMakeDir(cm_dirOp_t * op, cm_fid_t * me, cm_fid_t * parent)
540 cm_dirHeader_t *dhp = NULL;
541 cm_buf_t *dhpbuf = NULL;
545 osi_Log3(afsd_logp, "cm_DirMakeDir for op 0x%p, directory fid[%d, %d]",
546 op, me->vnode, me->unique);
547 osi_Log2(afsd_logp, " parent[%d, %d]",
548 parent->vnode, parent->unique);
550 code = cm_DirGetPage(op, 0, &dhpbuf, &dhp);
556 dhp->header.pgcount = htons(1);
557 dhp->header.tag = htons(1234);
558 dhp->header.freeCount = (CM_DIR_EPP - CM_DIR_DHE - 1);
559 dhp->header.freeBitmap[0] = 0xff;
560 dhp->header.freeBitmap[1] = 0x1f;
561 for (i = 2; i < CM_DIR_EPP / 8; i++)
562 dhp->header.freeBitmap[i] = 0;
563 dhp->alloMap[0] = (CM_DIR_EPP - CM_DIR_DHE - 1);
564 for (i = 1; i < CM_DIR_MAXPAGES; i++)
565 dhp->alloMap[i] = CM_DIR_EPP;
566 for (i = 0; i < CM_DIR_NHASHENT; i++)
567 dhp->hashTable[i] = 0;
569 cm_DirReleasePage(op, &dhpbuf, TRUE);
571 cm_DirCreateEntry(op, ".", me);
572 cm_DirCreateEntry(op, "..", parent); /* Virtue is its own .. */
574 osi_Log0(afsd_logp, "cm_DirMakeDir returning success");
581 /* Look up a file name in directory.
584 op->scp->rw is unlocked
587 op->scp->rw is unlocked
589 None of the directory buffers for op->scp should be locked by the
593 cm_DirLookup(cm_dirOp_t * op, char *entry, cm_fid_t * cfid)
595 cm_dirEntry_t *firstitem = NULL;
596 cm_buf_t *itembuf = NULL;
597 unsigned short *previtem = NULL;
598 cm_buf_t *pibuf = NULL;
603 lock_AssertNone(&op->scp->rw);
605 QueryPerformanceCounter(&start);
607 osi_Log2(afsd_logp, "cm_DirLookup for op 0x%p, entry[%s]",
608 op, osi_LogSaveString(afsd_logp, entry));
610 code = cm_DirFindItem(op, entry,
611 &itembuf, &firstitem,
614 if (code == CM_ERROR_NOTINCACHE) {
615 code = cm_DirPrefetchBuffers(op);
617 code = cm_DirFindItem(op, entry, &itembuf, &firstitem,
627 cm_DirReleasePage(op, &pibuf, FALSE);
629 cfid->cell = op->scp->fid.cell;
630 cfid->volume = op->scp->fid.volume;
631 cfid->vnode = ntohl(firstitem->fid.vnode);
632 cfid->unique = ntohl(firstitem->fid.unique);
634 cm_DirReleasePage(op, &itembuf, FALSE);
636 osi_Log2(afsd_logp, "cm_DirLookup returning fid[%d,%d]",
637 cfid->vnode, cfid->unique);
643 QueryPerformanceCounter(&end);
645 dir_lookup_time += (end.QuadPart - start.QuadPart);
650 /* Look up a file name in directory.
653 op->scp->rw is locked
656 op->scp->rw is locked
658 None of the directory buffers for op->scp should be locked by the
662 cm_DirLookupOffset(cm_dirOp_t * op, char *entry, cm_fid_t *cfid, osi_hyper_t *offsetp)
664 cm_dirEntry_t *firstitem = NULL;
665 cm_buf_t *itembuf = NULL;
666 unsigned short *previtem = NULL;
667 cm_buf_t *pibuf = NULL;
671 osi_Log2(afsd_logp, "cm_DirLookupOffset for op 0x%p, entry[%s]",
672 op, osi_LogSaveString(afsd_logp, entry));
674 code = cm_DirFindItem(op, entry,
675 &itembuf, &firstitem,
680 cm_DirReleasePage(op, &pibuf, FALSE);
682 cfid->cell = op->scp->fid.cell;
683 cfid->volume = op->scp->fid.volume;
684 cfid->vnode = ntohl(firstitem->fid.vnode);
685 cfid->unique = ntohl(firstitem->fid.unique);
689 thyper = itembuf->offset;
690 thyper = LargeIntegerAdd(thyper,
691 ConvertLongToLargeInteger(((char *) firstitem) - itembuf->datap));
696 cm_DirReleasePage(op, &itembuf, FALSE);
698 osi_Log2(afsd_logp, "cm_DirLookupOffset returning fid[%d,%d]",
699 cfid->vnode, cfid->unique);
701 osi_Log2(afsd_logp, " offset [%x:%x]",
702 offsetp->HighPart, offsetp->LowPart);
708 /* Apply a function to every directory entry in a directory.
711 op->scp->rw is locked
714 op->scp->rw is locked
716 None of the directory buffers for op->scp should be locked by the
719 The hook function cannot modify or lock any directory buffers.
722 cm_DirApply(cm_dirOp_t * op, int (*hookproc) (void *, char *, long, long), void *hook)
724 /* Enumerate the contents of a directory. */
728 cm_dirHeader_t *dhp = NULL;
729 cm_buf_t *dhpbuf = NULL;
731 cm_dirEntry_t *ep = NULL;
732 cm_buf_t *epbuf = NULL;
736 code = cm_DirGetPage(op, 0, &dhpbuf, &dhp);
740 for (i = 0; i < CM_DIR_NHASHENT; i++) {
741 /* For each hash chain, enumerate everyone on the list. */
742 num = ntohs(dhp->hashTable[i]);
744 /* Walk down the hash table list. */
745 code = cm_DirGetBlob(op, num, &epbuf, &ep);
747 cm_DirReleasePage(op, &dhpbuf, FALSE);
751 num = ntohs(ep->next);
752 (*hookproc) (hook, ep->name, ntohl(ep->fid.vnode),
753 ntohl(ep->fid.unique));
755 cm_DirReleasePage(op, &epbuf, FALSE);
758 cm_DirReleasePage(op, &dhpbuf, FALSE);
763 /* Check if a directory is empty
766 op->scp->rw is locked
769 op->scp->rw is locked
771 None of the directory buffers for op->scp should be locked by the
775 cm_DirIsEmpty(cm_dirOp_t * op)
777 /* Enumerate the contents of a directory. */
781 cm_dirHeader_t *dhp = NULL;
782 cm_buf_t *dhpbuf = NULL;
784 cm_dirEntry_t *ep = NULL;
785 cm_buf_t *epbuf = NULL;
789 code = cm_DirGetPage(op, 0, &dhpbuf, &dhp);
793 for (i = 0; i < CM_DIR_NHASHENT; i++) {
794 /* For each hash chain, enumerate everyone on the list. */
795 num = ntohs(dhp->hashTable[i]);
798 /* Walk down the hash table list. */
799 code = cm_DirGetBlob(op, num, &epbuf, &ep);
803 if (strcmp(ep->name, "..") && strcmp(ep->name, ".")) {
804 cm_DirReleasePage(op, &epbuf, FALSE);
805 cm_DirReleasePage(op, &dhpbuf, FALSE);
808 num = ntohs(ep->next);
809 cm_DirReleasePage(op, &epbuf, FALSE);
812 cm_DirReleasePage(op, &dhpbuf, FALSE);
816 /* Return a pointer to an entry, given its number.
820 if *bufferpp != NULL, then *bufferpp->mx is locked
823 scp->rw may be unlocked
824 *bufferpp may be released
828 if *bufferpp != NULL, then *bufferpp->mx is locked
830 *bufferpp should be released via cm_DirReleasePage() or any other
831 *call that releases a directory buffer.
834 cm_DirGetBlob(cm_dirOp_t * op,
835 unsigned int blobno, cm_buf_t ** bufferpp, cm_dirEntry_t ** blobpp)
840 osi_Log2(afsd_logp, "cm_DirGetBlob for op 0x%p, blobno=%d",
843 code = cm_DirGetPage(op, blobno >> CM_DIR_LEPP,
844 bufferpp, (void **) &ep);
848 *blobpp = (cm_dirEntry_t *) (ep + 32 * (blobno & (CM_DIR_EPP - 1)));
854 cm_DirHash(char *string)
856 /* Hash a string to a number between 0 and NHASHENT. */
861 while ((tc = (*string++))) {
865 tval = hval & (CM_DIR_NHASHENT - 1);
869 tval = CM_DIR_NHASHENT - tval;
873 /* Find a directory entry, given its name. This entry returns a
874 * pointer to a locked buffer, and a pointer to a locked buffer (in
875 * previtem) referencing the found item (to aid the delete code). If
876 * no entry is found, however, no items are left locked, and a null
877 * pointer is returned instead.
886 cm_DirFindItem(cm_dirOp_t * op,
888 cm_buf_t ** itembufpp, cm_dirEntry_t ** itempp,
889 cm_buf_t ** prevbufpp, unsigned short **previtempp)
892 cm_dirHeader_t *dhp = NULL;
893 unsigned short *lp = NULL;
894 cm_dirEntry_t *tp = NULL;
895 cm_buf_t *hashbufp = NULL;
896 cm_buf_t *itembufp = NULL;
899 osi_Log2(afsd_logp, "cm_DirFindItem for op 0x%p, entry[%s]",
900 op, osi_LogSaveString(afsd_logp, ename));
902 i = cm_DirHash(ename);
904 if (op->scp->fileType != CM_SCACHETYPE_DIRECTORY) {
905 osi_Log0(afsd_logp, "cm_DirFindItem: The scp is not a directory");
906 return CM_ERROR_INVAL;
909 code = cm_DirGetPage(op, 0, &hashbufp, (void **) &dhp);
914 if (dhp->hashTable[i] == 0) {
916 osi_Log1(afsd_logp, "cm_DirFindItem: Hash bucket %d is empty", i);
917 cm_DirReleasePage(op, &hashbufp, FALSE);
921 code = cm_DirGetBlob(op,
922 (u_short) ntohs(dhp->hashTable[i]),
925 cm_DirReleasePage(op, &hashbufp, FALSE);
929 lp = &(dhp->hashTable[i]);
933 lp : pointer to blob number of entry we are looking at
934 hashbufp : buffer containing lp
935 tp : pointer to entry we are looking at
936 itembufp : buffer containing tp
939 /* Look at each hash conflict entry. */
940 if (!strcmp(ename, tp->name)) {
941 osi_Log0(afsd_logp, "cm_DirFindItem: returning success");
942 /* Found our entry. */
944 *prevbufpp = hashbufp;
946 *itembufpp = itembufp;
951 cm_DirReleasePage(op, &hashbufp, FALSE);
958 /* The end of the line */
959 osi_Log0(afsd_logp, "cm_DirFindItem: returning ENOENT");
960 cm_DirReleasePage(op, &hashbufp, FALSE);
964 code = cm_DirGetBlob(op,
965 (u_short) ntohs(*lp),
969 cm_DirReleasePage(op, &hashbufp, FALSE);
975 /* Begin a sequence of directory operations.
976 * Called with scp->rw unlocked.
979 cm_BeginDirOp(cm_scache_t * scp, cm_user_t * userp, cm_req_t * reqp,
980 afs_uint32 lockType, cm_dirOp_t * op)
983 int i, mxheld = 0, haveWrite = 0;
985 osi_Log4(afsd_logp, "Beginning dirOp[0x%p] for scp[0x%p], userp[0x%p] lockType[0x%x]",
986 op, scp, userp, lockType);
988 memset(op, 0, sizeof(*op));
994 op->req = *reqp; /* copy the values from the input */
996 op->dirtyBufCount = 0;
999 for (i=0; i < CM_DIROP_MAXBUFFERS; i++) {
1000 op->buffers[i].flags = 0;
1003 if (lockType == CM_DIRLOCK_WRITE) {
1004 lock_ObtainWrite(&scp->dirlock);
1007 lock_ObtainRead(&scp->dirlock);
1010 lock_ObtainWrite(&scp->rw);
1012 code = cm_DirCheckStatus(op, 1);
1014 op->length = scp->length;
1015 op->newLength = op->length;
1016 op->dataVersion = scp->dataVersion;
1017 op->newDataVersion = op->dataVersion;
1020 if (!cm_BPlusTrees ||
1022 scp->dirDataVersion == scp->dataVersion))
1024 /* we know that haveWrite matches lockType at this point */
1026 case CM_DIRLOCK_NONE:
1028 lock_ReleaseWrite(&scp->dirlock);
1030 lock_ReleaseRead(&scp->dirlock);
1032 case CM_DIRLOCK_READ:
1033 osi_assert(!haveWrite);
1035 case CM_DIRLOCK_WRITE:
1037 osi_assert(haveWrite);
1039 op->lockType = lockType;
1041 if (!(scp->dirBplus &&
1042 scp->dirDataVersion == scp->dataVersion))
1047 lock_ReleaseWrite(&scp->rw);
1050 lock_ConvertRToW(&scp->dirlock);
1054 lock_ObtainWrite(&scp->rw);
1057 if (scp->dirBplus &&
1058 scp->dirDataVersion != scp->dataVersion)
1062 freeBtree(scp->dirBplus);
1063 scp->dirBplus = NULL;
1064 scp->dirDataVersion = CM_SCACHE_VERSION_BAD;
1067 if (!scp->dirBplus) {
1069 lock_ReleaseWrite(&scp->rw);
1072 code = cm_BPlusDirBuildTree(scp, userp, reqp);
1073 osi_Log1(afsd_logp, "cm_BeginDirOp cm_BPlusDirBuildTree code 0x%x", code);
1075 lock_ObtainWrite(&scp->rw);
1080 freeBtree(scp->dirBplus);
1081 scp->dirBplus = NULL;
1082 scp->dirDataVersion = CM_SCACHE_VERSION_BAD;
1084 if (op->dataVersion != scp->dataVersion) {
1085 /* We lost the race, therefore we must update the
1086 * dirop state and retry to build the tree.
1088 op->length = scp->length;
1089 op->newLength = op->length;
1090 op->dataVersion = scp->dataVersion;
1091 op->newDataVersion = op->dataVersion;
1096 scp->dirDataVersion = scp->dataVersion;
1103 case CM_DIRLOCK_NONE:
1104 lock_ReleaseWrite(&scp->dirlock);
1106 case CM_DIRLOCK_READ:
1107 lock_ConvertWToR(&scp->dirlock);
1109 case CM_DIRLOCK_WRITE:
1111 /* got it already */;
1113 op->lockType = lockType;
1117 /* we know that haveWrite matches lockType at this point */
1119 case CM_DIRLOCK_NONE:
1121 lock_ReleaseWrite(&scp->dirlock);
1123 lock_ReleaseRead(&scp->dirlock);
1125 case CM_DIRLOCK_READ:
1126 osi_assert(!haveWrite);
1128 case CM_DIRLOCK_WRITE:
1130 osi_assert(haveWrite);
1132 op->lockType = lockType;
1137 lock_ReleaseWrite(&scp->rw);
1141 lock_ReleaseWrite(&scp->dirlock);
1143 lock_ReleaseRead(&scp->dirlock);
1146 osi_Log1(afsd_logp, "cm_BeginDirOp return code 0x%x", code);
1152 /* Check if it is safe for us to perform local directory updates.
1153 Called with op->scp->rw unlocked. */
1155 cm_CheckDirOpForSingleChange(cm_dirOp_t * op)
1160 if (op->scp == NULL)
1163 lock_ObtainWrite(&op->scp->rw);
1164 code = cm_DirCheckStatus(op, 1);
1167 op->dataVersion == op->scp->dataVersion - 1) {
1168 /* only one set of changes happened between cm_BeginDirOp()
1169 and this function. It is safe for us to perform local
1171 op->newDataVersion = op->scp->dataVersion;
1172 op->newLength = op->scp->serverLength;
1176 lock_ReleaseWrite(&op->scp->rw);
1179 osi_Log0(afsd_logp, "cm_CheckDirOpForSingleChange succeeded");
1182 "cm_CheckDirOpForSingleChange failed. code=0x%x, old dv=%d, new dv=%d",
1183 code, op->dataVersion, op->scp->dataVersion);
1187 /* End a sequence of directory operations.
1188 * Called with op->scp->rw unlocked.*/
1190 cm_EndDirOp(cm_dirOp_t * op)
1194 osi_Log4(afsd_logp, "Ending dirOp[0x%p] scp[0x%p] lockType[0x%x] with %d dirty buffer releases",
1195 op, op->scp, op->lockType, op->dirtyBufCount);
1197 if (op->scp == NULL)
1200 if (op->dirtyBufCount > 0) {
1202 /* update the data version on the B+ tree */
1203 if (op->scp->dirBplus &&
1204 op->scp->dirDataVersion == op->dataVersion) {
1206 switch (op->lockType) {
1207 case CM_DIRLOCK_READ:
1208 lock_ReleaseRead(&op->scp->dirlock);
1209 /* fall through ... */
1210 case CM_DIRLOCK_NONE:
1211 lock_ObtainWrite(&op->scp->dirlock);
1212 op->lockType = CM_DIRLOCK_WRITE;
1214 case CM_DIRLOCK_WRITE:
1216 /* already got it */;
1218 op->scp->dirDataVersion = op->newDataVersion;
1222 /* we made changes. We should go through the list of buffers
1223 * and update the dataVersion for each. */
1224 lock_ObtainWrite(&op->scp->rw);
1225 code = buf_ForceDataVersion(op->scp, op->dataVersion, op->newDataVersion);
1226 op->scp->flags |= CM_SCACHEFLAG_LOCAL;
1227 lock_ReleaseWrite(&op->scp->rw);
1230 switch (op->lockType) {
1231 case CM_DIRLOCK_NONE:
1233 case CM_DIRLOCK_READ:
1234 lock_ReleaseRead(&op->scp->dirlock);
1236 case CM_DIRLOCK_WRITE:
1238 lock_ReleaseWrite(&op->scp->dirlock);
1242 cm_ReleaseSCache(op->scp);
1246 cm_ReleaseUser(op->userp);
1249 osi_assertx(op->nBuffers == 0, "Buffer leak after dirOp termination");
1252 osi_Log1(afsd_logp, "cm_EndDirOp return code 0x%x", code);
1257 /* NOTE: Called without scp->rw and without bufferp->mx */
1259 cm_DirOpAddBuffer(cm_dirOp_t * op, cm_buf_t * bufferp)
1264 osi_Log2(afsd_logp, "cm_DirOpAddBuffer for op 0x%p, buffer %p", op, bufferp);
1266 if (bufferp == NULL)
1269 for (i=0; i < CM_DIROP_MAXBUFFERS; i++) {
1270 if ((op->buffers[i].flags & CM_DIROPBUFF_INUSE) &&
1271 op->buffers[i].bufferp == bufferp) {
1276 if (i < CM_DIROP_MAXBUFFERS) {
1277 /* we already have this buffer on our list */
1279 op->buffers[i].refcount++;
1281 "cm_DirOpAddBuffer: the buffer is already listed for the dirOp");
1284 /* we have to add a new buffer */
1285 osi_assertx(op->nBuffers < CM_DIROP_MAXBUFFERS - 1,
1286 "DirOp has exceeded CM_DIROP_MAXBUFFERS buffers");
1288 for (i=0; i < CM_DIROP_MAXBUFFERS; i++) {
1289 if (!(op->buffers[i].flags & CM_DIROPBUFF_INUSE))
1293 osi_assert(i < CM_DIROP_MAXBUFFERS);
1295 lock_ObtainMutex(&bufferp->mx);
1296 lock_ObtainWrite(&op->scp->rw);
1298 /* Make sure we are synchronized. */
1299 osi_assert(op->lockType != CM_DIRLOCK_NONE);
1301 code = cm_SyncOp(op->scp, bufferp, op->userp, &op->req, PRSFS_LOOKUP,
1302 CM_SCACHESYNC_NEEDCALLBACK |
1303 (op->lockType == CM_DIRLOCK_WRITE ? CM_SCACHESYNC_WRITE : CM_SCACHESYNC_READ) |
1304 CM_SCACHESYNC_BUFLOCKED);
1306 if (code == 0 && bufferp->dataVersion != op->dataVersion) {
1308 "cm_DirOpAddBuffer: buffer data version mismatch. buf dv = %d. needs %d",
1309 bufferp->dataVersion, op->dataVersion);
1311 cm_SyncOpDone(op->scp, bufferp,
1312 CM_SCACHESYNC_NEEDCALLBACK |
1313 (op->lockType == CM_DIRLOCK_WRITE ? CM_SCACHESYNC_WRITE : CM_SCACHESYNC_READ) |
1314 CM_SCACHESYNC_BUFLOCKED);
1315 code = CM_ERROR_NOTINCACHE;
1318 lock_ReleaseWrite(&op->scp->rw);
1319 lock_ReleaseMutex(&bufferp->mx);
1322 osi_Log1(afsd_logp, "cm_DirOpAddBuffer: failed to sync buffer. code=0x%x",
1328 op->buffers[i].bufferp = bufferp;
1329 op->buffers[i].refcount = 1; /* start with one ref */
1330 op->buffers[i].flags = CM_DIROPBUFF_INUSE;
1334 osi_Log0(afsd_logp, "cm_DirOpAddBuffer: returning success");
1340 /* Note: Called without op->scp->rw */
1342 cm_DirOpFindBuffer(cm_dirOp_t * op, osi_hyper_t offset, cm_buf_t ** bufferpp)
1346 for (i=0; i < CM_DIROP_MAXBUFFERS; i++) {
1347 if ((op->buffers[i].flags & CM_DIROPBUFF_INUSE) &&
1348 LargeIntegerEqualTo(op->buffers[i].bufferp->offset, offset))
1352 if (i < CM_DIROP_MAXBUFFERS) {
1354 op->buffers[i].refcount++;
1355 buf_Hold(op->buffers[i].bufferp);
1356 *bufferpp = op->buffers[i].bufferp;
1358 osi_Log2(afsd_logp, "cm_DirOpFindBuffer: found buffer for offset [%x:%x]",
1359 offset.HighPart, offset.LowPart);
1363 osi_Log2(afsd_logp, "cm_DirOpFindBuffer: buffer not found for offset [%x:%x]",
1364 offset.HighPart, offset.LowPart);
1369 /* NOTE: called with scp->rw held or not depending on the flags */
1371 cm_DirOpDelBuffer(cm_dirOp_t * op, cm_buf_t * bufferp, int flags)
1375 osi_Log3(afsd_logp, "cm_DirOpDelBuffer for op 0x%p, buffer 0x%p, flags=%d",
1376 op, bufferp, flags);
1378 for (i=0; i < CM_DIROP_MAXBUFFERS; i++) {
1379 if ((op->buffers[i].flags & CM_DIROPBUFF_INUSE) &&
1380 op->buffers[i].bufferp == bufferp)
1384 if (i < CM_DIROP_MAXBUFFERS) {
1386 if (flags & DIROP_MODIFIED)
1387 op->dirtyBufCount++;
1389 osi_assert(op->buffers[i].refcount > 0);
1390 op->buffers[i].refcount --;
1392 if (op->buffers[i].refcount == 0) {
1393 /* this was the last reference we had */
1395 osi_Log0(afsd_logp, "cm_DirOpDelBuffer: releasing buffer");
1397 /* if this buffer was modified, then we update the data
1398 version of the buffer with the data version of the
1400 if (!(flags & DIROP_SCPLOCKED)) {
1401 lock_ObtainWrite(&op->scp->rw);
1404 /* first make sure that the buffer is idle. It should
1405 have been idle all along. */
1406 osi_assertx((bufferp->cmFlags & (CM_BUF_CMFETCHING |
1407 CM_BUF_CMSTORING)) == 0,
1408 "Buffer is not idle while performing dirOp");
1410 cm_SyncOpDone(op->scp, bufferp,
1411 CM_SCACHESYNC_NEEDCALLBACK |
1412 (op->lockType == CM_DIRLOCK_WRITE ? CM_SCACHESYNC_WRITE : CM_SCACHESYNC_READ));
1415 osi_assert(bufferp->dataVersion == op->dataVersion);
1418 lock_ReleaseWrite(&op->scp->rw);
1420 lock_ObtainMutex(&bufferp->mx);
1422 if (flags & DIROP_SCPLOCKED) {
1423 lock_ObtainWrite(&op->scp->rw);
1426 if (flags & DIROP_MODIFIED) {
1427 /* We don't update the dataversion here. Instead we
1428 wait until the dirOp is completed and then flip the
1429 dataversion on all the buffers in one go.
1430 Otherwise we won't know if the dataversion is
1431 current because it was fetched from the server or
1432 because we touched it during the dirOp. */
1434 if (bufferp->userp != op->userp) {
1435 if (bufferp->userp != NULL)
1436 cm_ReleaseUser(bufferp->userp);
1437 cm_HoldUser(op->userp);
1438 bufferp->userp = op->userp;
1442 lock_ReleaseMutex(&bufferp->mx);
1444 op->buffers[i].bufferp = NULL;
1445 buf_Release(bufferp);
1446 op->buffers[i].flags = 0;
1452 /* we have other references to this buffer. so we have to
1458 osi_Log0(afsd_logp, "cm_DirOpDelBuffer: buffer not found");
1459 osi_assertx(FALSE, "Attempt to delete a non-existent buffer from a dirOp");
1464 /* Check if we have current status and a callback for the given scp.
1465 This should be called before cm_DirGetPage() is called per scp.
1468 scp->rw locked state indicated by parameter
1471 scp->rw same state as upon entry
1474 scp->rw may be released
1477 cm_DirCheckStatus(cm_dirOp_t * op, int scp_locked)
1482 lock_ObtainWrite(&op->scp->rw);
1483 code = cm_SyncOp(op->scp, NULL, op->userp, &op->req, PRSFS_LOOKUP,
1484 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1486 lock_ReleaseWrite(&op->scp->rw);
1488 osi_Log2(afsd_logp, "cm_DirCheckStatus for op 0x%p returning code 0x%x",
1494 /* Attempt to prefetch all the buffers for this operation.
1496 Called with scp->rw unlocked
1499 cm_DirPrefetchBuffers(cm_dirOp_t * op)
1503 cm_buf_t *bufferp = NULL;
1505 osi_Log1(afsd_logp, "cm_DirPrefetchBuffers for op 0x%p", op);
1507 /* prefetching is only done on read operations where we don't
1508 expect the data version to change. */
1509 if (op->dataVersion != op->newDataVersion) {
1510 osi_Log0(afsd_logp, "Skipping prefetch for write operation.");
1511 return CM_ERROR_INVAL;
1514 lock_ObtainWrite(&op->scp->rw);
1516 /* When we are prefetching a file, we first flush out any of its
1517 contents just to make sure that we don't end up with buffers
1518 that was locally modified. */
1520 if (op->scp->flags & CM_SCACHEFLAG_LOCAL)
1521 op->scp->bufDataVersionLow = op->scp->dataVersion;
1523 offset = ConvertLongToLargeInteger(0);
1524 while (LargeIntegerLessThan(offset, op->scp->length)) {
1525 osi_Log2(afsd_logp, "Trying prefetch for offset %08x:%08x",
1526 offset.HighPart, offset.LowPart);
1527 lock_ReleaseWrite(&op->scp->rw);
1529 code = buf_Get(op->scp, &offset, &op->req, &bufferp);
1531 lock_ObtainWrite(&op->scp->rw);
1538 code = cm_SyncOp(op->scp, bufferp, op->userp, &op->req, PRSFS_LOOKUP,
1539 CM_SCACHESYNC_NEEDCALLBACK |
1540 (op->lockType == CM_DIRLOCK_WRITE ? CM_SCACHESYNC_WRITE : CM_SCACHESYNC_READ));
1545 cm_SyncOpDone(op->scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK |
1546 (op->lockType == CM_DIRLOCK_WRITE ? CM_SCACHESYNC_WRITE : CM_SCACHESYNC_READ));
1548 if (cm_HaveBuffer(op->scp, bufferp, 0))
1551 code = cm_GetBuffer(op->scp, bufferp, NULL, op->userp, &op->req);
1560 buf_Release(bufferp);
1564 offset = LargeIntegerAdd(offset, ConvertLongToLargeInteger(cm_data.buf_blockSize));
1567 lock_ReleaseWrite(&op->scp->rw);
1569 osi_Log1(afsd_logp, "cm_DirPrefetchBuffers returning code 0x%x", code);
1574 /* Release a directory buffer that was obtained via a call to
1575 cm_DirGetPage() or any other function that returns a locked, held,
1576 directory page buffer.
1578 Called with scp->rw unlocked
1581 cm_DirReleasePage(cm_dirOp_t * op, cm_buf_t ** bufferpp, int modified)
1588 cm_DirOpDelBuffer(op, *bufferpp,
1589 ((modified ? DIROP_MODIFIED : 0)));
1590 buf_Release(*bufferpp);
1597 Returns the index'th directory page from scp. The userp and reqp
1598 will be used to fetch the buffer from the fileserver if necessary.
1599 If the call is successful, a locked and held cm_buf_t is returned
1600 via buferpp and a pointer to the directory page is returned via
1603 The returned buffer should be released via a call to
1604 cm_DirReleasePage() or by passing it into a subsequent call to
1605 cm_DirGetPage() for the *same* scp.
1607 If a *locked* buffer for the *same* scp is passed in via bufferpp
1608 to the function, it will check if the requested directory page is
1609 located in the specified buffer. If not, the buffer will be
1610 released and a new buffer returned that contains the requested
1613 If the specified page exists beyond the EOF for the scp, a new
1614 buffer will be allocated only if create is set to TRUE.
1616 Note: If a buffer is specified on entry via bufferpp, it is assumed
1617 that the buffer is unmodified. If the buffer is modified, it
1618 should be released via cm_DirReleasePage().
1622 If *bufferpp is non-NULL, then *bufferpp->mx is locked.
1626 If *bufferpp is non-NULL, then *bufferpp->mx is locked.
1629 scp->rw will be obtained and released
1633 cm_DirGetPage(cm_dirOp_t * op,
1634 long index, cm_buf_t ** bufferpp, void ** datapp)
1636 osi_hyper_t pageOffset; /* offset of the dir page from the
1637 start of the directory */
1638 osi_hyper_t bufferOffset; /* offset of the buffer from the start
1642 cm_buf_t * bufferp = NULL;
1644 void * datap = NULL;
1648 osi_Log2(afsd_logp, "cm_DirGetPage for op 0x%p, index %d", op, index);
1650 pageOffset = ConvertLongToLargeInteger(index * CM_DIR_PAGESIZE);
1651 bufferOffset.HighPart = pageOffset.HighPart;
1652 bufferOffset.LowPart = pageOffset.LowPart & ~(cm_data.buf_blockSize - 1);
1654 bufferp = *bufferpp;
1655 if (bufferp != NULL) {
1656 osi_assert(cm_FidCmp(&bufferp->fid, &op->scp->fid) == 0);
1658 thyper = bufferp->offset;
1661 if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
1665 buf_Release(bufferp);
1666 cm_DirOpDelBuffer(op, bufferp, 0);
1670 /* first check if we are already working with the buffer */
1671 if (cm_DirOpFindBuffer(op, bufferOffset, &bufferp)) {
1676 code = buf_Get(op->scp, &bufferOffset, &op->req, &bufferp);
1678 osi_Log1(afsd_logp, " buf_Get returned code 0x%x", code);
1683 osi_assert(bufferp != NULL);
1685 /* DirOpAddBuffer will obtain bufferp->mx if necessary */
1686 code = cm_DirOpAddBuffer(op, bufferp);
1689 /* for some reason, the buffer was rejected. We can't use
1690 this buffer, and since this is the only buffer we can
1691 potentially use, there's no recourse.*/
1692 buf_Release(bufferp);
1700 /* now to figure out where the data is */
1701 thyper = LargeIntegerSubtract(pageOffset, bufferOffset);
1703 osi_assert(thyper.HighPart == 0);
1704 osi_assert(cm_data.buf_blockSize > thyper.LowPart &&
1705 cm_data.buf_blockSize - thyper.LowPart >= CM_DIR_PAGESIZE);
1707 datap = (void *) (((char *)bufferp->datap) + thyper.LowPart);
1712 /* also, if we are writing past EOF, we should make a note of the
1714 thyper = LargeIntegerAdd(pageOffset,
1715 ConvertLongToLargeInteger(CM_DIR_PAGESIZE));
1716 if (LargeIntegerLessThan(op->newLength, thyper)) {
1717 op->newLength = thyper;
1722 *bufferpp = bufferp;
1724 osi_Log1(afsd_logp, "cm_DirGetPage returning code 0x%x", code);
1731 cm_DirEntryListAdd(char * namep, cm_dirEntryList_t ** list)
1734 cm_dirEntryList_t * entry;
1736 len = strlen(namep);
1737 len += sizeof(cm_dirEntryList_t);
1739 entry = malloc(len);
1741 entry->nextp = *list;
1742 strcpy(entry->name, namep);
1748 cm_DirEntryListFree(cm_dirEntryList_t ** list)
1750 cm_dirEntryList_t * entry;
1751 cm_dirEntryList_t * next;
1753 for (entry = *list; entry; entry = next) {
1754 next = entry->nextp;