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, afs_uint32 flags, 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) &&
1068 (!(flags & CM_DIROP_FLAG_NOBUILDTREE))) {
1070 lock_ReleaseWrite(&scp->rw);
1073 code = cm_BPlusDirBuildTree(scp, userp, reqp);
1074 osi_Log1(afsd_logp, "cm_BeginDirOp cm_BPlusDirBuildTree code 0x%x", code);
1076 lock_ObtainWrite(&scp->rw);
1081 freeBtree(scp->dirBplus);
1082 scp->dirBplus = NULL;
1083 scp->dirDataVersion = CM_SCACHE_VERSION_BAD;
1085 if (op->dataVersion != scp->dataVersion) {
1086 /* We lost the race, therefore we must update the
1087 * dirop state and retry to build the tree.
1089 op->length = scp->length;
1090 op->newLength = op->length;
1091 op->dataVersion = scp->dataVersion;
1092 op->newDataVersion = op->dataVersion;
1097 scp->dirDataVersion = scp->dataVersion;
1104 case CM_DIRLOCK_NONE:
1105 lock_ReleaseWrite(&scp->dirlock);
1107 case CM_DIRLOCK_READ:
1108 lock_ConvertWToR(&scp->dirlock);
1110 case CM_DIRLOCK_WRITE:
1112 /* got it already */;
1114 op->lockType = lockType;
1118 /* we know that haveWrite matches lockType at this point */
1120 case CM_DIRLOCK_NONE:
1122 lock_ReleaseWrite(&scp->dirlock);
1124 lock_ReleaseRead(&scp->dirlock);
1126 case CM_DIRLOCK_READ:
1127 osi_assert(!haveWrite);
1129 case CM_DIRLOCK_WRITE:
1131 osi_assert(haveWrite);
1133 op->lockType = lockType;
1138 lock_ReleaseWrite(&scp->rw);
1142 lock_ReleaseWrite(&scp->dirlock);
1144 lock_ReleaseRead(&scp->dirlock);
1147 osi_Log1(afsd_logp, "cm_BeginDirOp return code 0x%x", code);
1153 /* Check if it is safe for us to perform local directory updates.
1154 Called with op->scp->rw unlocked. */
1156 cm_CheckDirOpForSingleChange(cm_dirOp_t * op)
1161 if (op->scp == NULL)
1164 lock_ObtainWrite(&op->scp->rw);
1165 code = cm_DirCheckStatus(op, 1);
1168 op->dataVersion == op->scp->dataVersion - 1) {
1169 /* only one set of changes happened between cm_BeginDirOp()
1170 and this function. It is safe for us to perform local
1172 op->newDataVersion = op->scp->dataVersion;
1173 op->newLength = op->scp->serverLength;
1177 lock_ReleaseWrite(&op->scp->rw);
1180 osi_Log0(afsd_logp, "cm_CheckDirOpForSingleChange succeeded");
1183 "cm_CheckDirOpForSingleChange failed. code=0x%x, old dv=%d, new dv=%d",
1184 code, op->dataVersion, op->scp->dataVersion);
1188 /* End a sequence of directory operations.
1189 * Called with op->scp->rw unlocked.*/
1191 cm_EndDirOp(cm_dirOp_t * op)
1195 osi_Log4(afsd_logp, "Ending dirOp[0x%p] scp[0x%p] lockType[0x%x] with %d dirty buffer releases",
1196 op, op->scp, op->lockType, op->dirtyBufCount);
1198 if (op->scp == NULL)
1201 if (op->dirtyBufCount > 0) {
1203 /* update the data version on the B+ tree */
1204 if (op->scp->dirBplus &&
1205 op->scp->dirDataVersion == op->dataVersion) {
1207 switch (op->lockType) {
1208 case CM_DIRLOCK_READ:
1209 lock_ReleaseRead(&op->scp->dirlock);
1210 /* fall through ... */
1211 case CM_DIRLOCK_NONE:
1212 lock_ObtainWrite(&op->scp->dirlock);
1213 op->lockType = CM_DIRLOCK_WRITE;
1215 case CM_DIRLOCK_WRITE:
1217 /* already got it */;
1219 op->scp->dirDataVersion = op->newDataVersion;
1223 /* we made changes. We should go through the list of buffers
1224 * and update the dataVersion for each. */
1225 lock_ObtainWrite(&op->scp->rw);
1226 code = buf_ForceDataVersion(op->scp, op->dataVersion, op->newDataVersion);
1227 op->scp->flags |= CM_SCACHEFLAG_LOCAL;
1228 lock_ReleaseWrite(&op->scp->rw);
1231 switch (op->lockType) {
1232 case CM_DIRLOCK_NONE:
1234 case CM_DIRLOCK_READ:
1235 lock_ReleaseRead(&op->scp->dirlock);
1237 case CM_DIRLOCK_WRITE:
1239 lock_ReleaseWrite(&op->scp->dirlock);
1243 cm_ReleaseSCache(op->scp);
1247 cm_ReleaseUser(op->userp);
1250 osi_assertx(op->nBuffers == 0, "Buffer leak after dirOp termination");
1253 osi_Log1(afsd_logp, "cm_EndDirOp return code 0x%x", code);
1258 /* NOTE: Called without scp->rw and without bufferp->mx */
1260 cm_DirOpAddBuffer(cm_dirOp_t * op, cm_buf_t * bufferp)
1265 osi_Log2(afsd_logp, "cm_DirOpAddBuffer for op 0x%p, buffer %p", op, bufferp);
1267 if (bufferp == NULL)
1270 for (i=0; i < CM_DIROP_MAXBUFFERS; i++) {
1271 if ((op->buffers[i].flags & CM_DIROPBUFF_INUSE) &&
1272 op->buffers[i].bufferp == bufferp) {
1277 if (i < CM_DIROP_MAXBUFFERS) {
1278 /* we already have this buffer on our list */
1280 op->buffers[i].refcount++;
1282 "cm_DirOpAddBuffer: the buffer is already listed for the dirOp");
1285 /* we have to add a new buffer */
1286 osi_assertx(op->nBuffers < CM_DIROP_MAXBUFFERS - 1,
1287 "DirOp has exceeded CM_DIROP_MAXBUFFERS buffers");
1289 for (i=0; i < CM_DIROP_MAXBUFFERS; i++) {
1290 if (!(op->buffers[i].flags & CM_DIROPBUFF_INUSE))
1294 osi_assert(i < CM_DIROP_MAXBUFFERS);
1296 lock_ObtainMutex(&bufferp->mx);
1297 lock_ObtainWrite(&op->scp->rw);
1299 /* Make sure we are synchronized. */
1300 osi_assert(op->lockType != CM_DIRLOCK_NONE);
1302 code = cm_SyncOp(op->scp, bufferp, op->userp, &op->req, PRSFS_LOOKUP,
1303 CM_SCACHESYNC_NEEDCALLBACK |
1304 (op->lockType == CM_DIRLOCK_WRITE ? CM_SCACHESYNC_WRITE : CM_SCACHESYNC_READ) |
1305 CM_SCACHESYNC_BUFLOCKED);
1307 if (code == 0 && bufferp->dataVersion != op->dataVersion) {
1309 "cm_DirOpAddBuffer: buffer data version mismatch. buf dv = %d. needs %d",
1310 bufferp->dataVersion, op->dataVersion);
1312 cm_SyncOpDone(op->scp, bufferp,
1313 CM_SCACHESYNC_NEEDCALLBACK |
1314 (op->lockType == CM_DIRLOCK_WRITE ? CM_SCACHESYNC_WRITE : CM_SCACHESYNC_READ) |
1315 CM_SCACHESYNC_BUFLOCKED);
1316 code = CM_ERROR_NOTINCACHE;
1319 lock_ReleaseWrite(&op->scp->rw);
1320 lock_ReleaseMutex(&bufferp->mx);
1323 osi_Log1(afsd_logp, "cm_DirOpAddBuffer: failed to sync buffer. code=0x%x",
1329 op->buffers[i].bufferp = bufferp;
1330 op->buffers[i].refcount = 1; /* start with one ref */
1331 op->buffers[i].flags = CM_DIROPBUFF_INUSE;
1335 osi_Log0(afsd_logp, "cm_DirOpAddBuffer: returning success");
1341 /* Note: Called without op->scp->rw */
1343 cm_DirOpFindBuffer(cm_dirOp_t * op, osi_hyper_t offset, cm_buf_t ** bufferpp)
1347 for (i=0; i < CM_DIROP_MAXBUFFERS; i++) {
1348 if ((op->buffers[i].flags & CM_DIROPBUFF_INUSE) &&
1349 LargeIntegerEqualTo(op->buffers[i].bufferp->offset, offset))
1353 if (i < CM_DIROP_MAXBUFFERS) {
1355 op->buffers[i].refcount++;
1356 buf_Hold(op->buffers[i].bufferp);
1357 *bufferpp = op->buffers[i].bufferp;
1359 osi_Log2(afsd_logp, "cm_DirOpFindBuffer: found buffer for offset [%x:%x]",
1360 offset.HighPart, offset.LowPart);
1364 osi_Log2(afsd_logp, "cm_DirOpFindBuffer: buffer not found for offset [%x:%x]",
1365 offset.HighPart, offset.LowPart);
1370 /* NOTE: called with scp->rw held or not depending on the flags */
1372 cm_DirOpDelBuffer(cm_dirOp_t * op, cm_buf_t * bufferp, int flags)
1376 osi_Log3(afsd_logp, "cm_DirOpDelBuffer for op 0x%p, buffer 0x%p, flags=%d",
1377 op, bufferp, flags);
1379 for (i=0; i < CM_DIROP_MAXBUFFERS; i++) {
1380 if ((op->buffers[i].flags & CM_DIROPBUFF_INUSE) &&
1381 op->buffers[i].bufferp == bufferp)
1385 if (i < CM_DIROP_MAXBUFFERS) {
1387 if (flags & DIROP_MODIFIED)
1388 op->dirtyBufCount++;
1390 osi_assert(op->buffers[i].refcount > 0);
1391 op->buffers[i].refcount --;
1393 if (op->buffers[i].refcount == 0) {
1394 /* this was the last reference we had */
1396 osi_Log0(afsd_logp, "cm_DirOpDelBuffer: releasing buffer");
1398 /* if this buffer was modified, then we update the data
1399 version of the buffer with the data version of the
1401 if (!(flags & DIROP_SCPLOCKED)) {
1402 lock_ObtainWrite(&op->scp->rw);
1405 /* first make sure that the buffer is idle. It should
1406 have been idle all along. */
1407 osi_assertx((bufferp->cmFlags & (CM_BUF_CMFETCHING |
1408 CM_BUF_CMSTORING)) == 0,
1409 "Buffer is not idle while performing dirOp");
1411 cm_SyncOpDone(op->scp, bufferp,
1412 CM_SCACHESYNC_NEEDCALLBACK |
1413 (op->lockType == CM_DIRLOCK_WRITE ? CM_SCACHESYNC_WRITE : CM_SCACHESYNC_READ));
1416 osi_assert(bufferp->dataVersion == op->dataVersion);
1419 lock_ReleaseWrite(&op->scp->rw);
1421 lock_ObtainMutex(&bufferp->mx);
1423 if (flags & DIROP_SCPLOCKED) {
1424 lock_ObtainWrite(&op->scp->rw);
1427 if (flags & DIROP_MODIFIED) {
1428 /* We don't update the dataversion here. Instead we
1429 wait until the dirOp is completed and then flip the
1430 dataversion on all the buffers in one go.
1431 Otherwise we won't know if the dataversion is
1432 current because it was fetched from the server or
1433 because we touched it during the dirOp. */
1435 if (bufferp->userp != op->userp) {
1436 if (bufferp->userp != NULL)
1437 cm_ReleaseUser(bufferp->userp);
1438 cm_HoldUser(op->userp);
1439 bufferp->userp = op->userp;
1443 lock_ReleaseMutex(&bufferp->mx);
1445 op->buffers[i].bufferp = NULL;
1446 buf_Release(bufferp);
1447 op->buffers[i].flags = 0;
1453 /* we have other references to this buffer. so we have to
1459 osi_Log0(afsd_logp, "cm_DirOpDelBuffer: buffer not found");
1460 osi_assertx(FALSE, "Attempt to delete a non-existent buffer from a dirOp");
1465 /* Check if we have current status and a callback for the given scp.
1466 This should be called before cm_DirGetPage() is called per scp.
1469 scp->rw locked state indicated by parameter
1472 scp->rw same state as upon entry
1475 scp->rw may be released
1478 cm_DirCheckStatus(cm_dirOp_t * op, int scp_locked)
1483 lock_ObtainWrite(&op->scp->rw);
1484 code = cm_SyncOp(op->scp, NULL, op->userp, &op->req, PRSFS_LOOKUP,
1485 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1487 lock_ReleaseWrite(&op->scp->rw);
1489 osi_Log2(afsd_logp, "cm_DirCheckStatus for op 0x%p returning code 0x%x",
1495 /* Attempt to prefetch all the buffers for this operation.
1497 Called with scp->rw unlocked
1500 cm_DirPrefetchBuffers(cm_dirOp_t * op)
1504 cm_buf_t *bufferp = NULL;
1506 osi_Log1(afsd_logp, "cm_DirPrefetchBuffers for op 0x%p", op);
1508 /* prefetching is only done on read operations where we don't
1509 expect the data version to change. */
1510 if (op->dataVersion != op->newDataVersion) {
1511 osi_Log0(afsd_logp, "Skipping prefetch for write operation.");
1512 return CM_ERROR_INVAL;
1515 lock_ObtainWrite(&op->scp->rw);
1517 /* When we are prefetching a file, we first flush out any of its
1518 contents just to make sure that we don't end up with buffers
1519 that was locally modified. */
1521 if (op->scp->flags & CM_SCACHEFLAG_LOCAL)
1522 op->scp->bufDataVersionLow = op->scp->dataVersion;
1524 offset = ConvertLongToLargeInteger(0);
1525 while (LargeIntegerLessThan(offset, op->scp->length)) {
1526 osi_Log2(afsd_logp, "Trying prefetch for offset %08x:%08x",
1527 offset.HighPart, offset.LowPart);
1528 lock_ReleaseWrite(&op->scp->rw);
1530 code = buf_Get(op->scp, &offset, &op->req, &bufferp);
1532 lock_ObtainWrite(&op->scp->rw);
1539 code = cm_SyncOp(op->scp, bufferp, op->userp, &op->req, PRSFS_LOOKUP,
1540 CM_SCACHESYNC_NEEDCALLBACK |
1541 (op->lockType == CM_DIRLOCK_WRITE ? CM_SCACHESYNC_WRITE : CM_SCACHESYNC_READ));
1546 cm_SyncOpDone(op->scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK |
1547 (op->lockType == CM_DIRLOCK_WRITE ? CM_SCACHESYNC_WRITE : CM_SCACHESYNC_READ));
1549 if (cm_HaveBuffer(op->scp, bufferp, 0))
1552 code = cm_GetBuffer(op->scp, bufferp, NULL, op->userp, &op->req);
1561 buf_Release(bufferp);
1565 offset = LargeIntegerAdd(offset, ConvertLongToLargeInteger(cm_data.buf_blockSize));
1568 lock_ReleaseWrite(&op->scp->rw);
1570 osi_Log1(afsd_logp, "cm_DirPrefetchBuffers returning code 0x%x", code);
1575 /* Release a directory buffer that was obtained via a call to
1576 cm_DirGetPage() or any other function that returns a locked, held,
1577 directory page buffer.
1579 Called with scp->rw unlocked
1582 cm_DirReleasePage(cm_dirOp_t * op, cm_buf_t ** bufferpp, int modified)
1589 cm_DirOpDelBuffer(op, *bufferpp,
1590 ((modified ? DIROP_MODIFIED : 0)));
1591 buf_Release(*bufferpp);
1598 Returns the index'th directory page from scp. The userp and reqp
1599 will be used to fetch the buffer from the fileserver if necessary.
1600 If the call is successful, a locked and held cm_buf_t is returned
1601 via buferpp and a pointer to the directory page is returned via
1604 The returned buffer should be released via a call to
1605 cm_DirReleasePage() or by passing it into a subsequent call to
1606 cm_DirGetPage() for the *same* scp.
1608 If a *locked* buffer for the *same* scp is passed in via bufferpp
1609 to the function, it will check if the requested directory page is
1610 located in the specified buffer. If not, the buffer will be
1611 released and a new buffer returned that contains the requested
1614 If the specified page exists beyond the EOF for the scp, a new
1615 buffer will be allocated only if create is set to TRUE.
1617 Note: If a buffer is specified on entry via bufferpp, it is assumed
1618 that the buffer is unmodified. If the buffer is modified, it
1619 should be released via cm_DirReleasePage().
1623 If *bufferpp is non-NULL, then *bufferpp->mx is locked.
1627 If *bufferpp is non-NULL, then *bufferpp->mx is locked.
1630 scp->rw will be obtained and released
1634 cm_DirGetPage(cm_dirOp_t * op,
1635 long index, cm_buf_t ** bufferpp, void ** datapp)
1637 osi_hyper_t pageOffset; /* offset of the dir page from the
1638 start of the directory */
1639 osi_hyper_t bufferOffset; /* offset of the buffer from the start
1643 cm_buf_t * bufferp = NULL;
1645 void * datap = NULL;
1649 osi_Log2(afsd_logp, "cm_DirGetPage for op 0x%p, index %d", op, index);
1651 pageOffset = ConvertLongToLargeInteger(index * CM_DIR_PAGESIZE);
1652 bufferOffset.HighPart = pageOffset.HighPart;
1653 bufferOffset.LowPart = pageOffset.LowPart & ~(cm_data.buf_blockSize - 1);
1655 bufferp = *bufferpp;
1656 if (bufferp != NULL) {
1657 osi_assert(cm_FidCmp(&bufferp->fid, &op->scp->fid) == 0);
1659 thyper = bufferp->offset;
1662 if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
1666 buf_Release(bufferp);
1667 cm_DirOpDelBuffer(op, bufferp, 0);
1671 /* first check if we are already working with the buffer */
1672 if (cm_DirOpFindBuffer(op, bufferOffset, &bufferp)) {
1677 code = buf_Get(op->scp, &bufferOffset, &op->req, &bufferp);
1679 osi_Log1(afsd_logp, " buf_Get returned code 0x%x", code);
1684 osi_assert(bufferp != NULL);
1686 /* DirOpAddBuffer will obtain bufferp->mx if necessary */
1687 code = cm_DirOpAddBuffer(op, bufferp);
1690 /* for some reason, the buffer was rejected. We can't use
1691 this buffer, and since this is the only buffer we can
1692 potentially use, there's no recourse.*/
1693 buf_Release(bufferp);
1701 /* now to figure out where the data is */
1702 thyper = LargeIntegerSubtract(pageOffset, bufferOffset);
1704 osi_assert(thyper.HighPart == 0);
1705 osi_assert(cm_data.buf_blockSize > thyper.LowPart &&
1706 cm_data.buf_blockSize - thyper.LowPart >= CM_DIR_PAGESIZE);
1708 datap = (void *) (((char *)bufferp->datap) + thyper.LowPart);
1713 /* also, if we are writing past EOF, we should make a note of the
1715 thyper = LargeIntegerAdd(pageOffset,
1716 ConvertLongToLargeInteger(CM_DIR_PAGESIZE));
1717 if (LargeIntegerLessThan(op->newLength, thyper)) {
1718 op->newLength = thyper;
1723 *bufferpp = bufferp;
1725 osi_Log1(afsd_logp, "cm_DirGetPage returning code 0x%x", code);
1732 cm_DirEntryListAdd(char * namep, cm_dirEntryList_t ** list)
1735 cm_dirEntryList_t * entry;
1737 len = strlen(namep);
1738 len += sizeof(cm_dirEntryList_t);
1740 entry = malloc(len);
1742 entry->nextp = *list;
1743 strcpy(entry->name, namep);
1749 cm_DirEntryListFree(cm_dirEntryList_t ** list)
1751 cm_dirEntryList_t * entry;
1752 cm_dirEntryList_t * next;
1754 for (entry = *list; entry; entry = next) {
1755 next = entry->nextp;