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_Log3(afsd_logp, "Beginning dirOp[0x%p] for scp[0x%p], userp[0x%p]",
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);
1040 if (!(scp->dirBplus &&
1041 scp->dirDataVersion == scp->dataVersion))
1046 lock_ReleaseWrite(&scp->rw);
1049 lock_ConvertRToW(&scp->dirlock);
1053 lock_ObtainWrite(&scp->rw);
1056 if (scp->dirBplus &&
1057 scp->dirDataVersion != scp->dataVersion)
1061 freeBtree(scp->dirBplus);
1062 scp->dirBplus = NULL;
1063 scp->dirDataVersion = CM_SCACHE_VERSION_BAD;
1066 if (!scp->dirBplus) {
1068 lock_ReleaseWrite(&scp->rw);
1071 cm_BPlusDirBuildTree(scp, userp, reqp);
1073 lock_ObtainWrite(&scp->rw);
1076 if (op->dataVersion != scp->dataVersion) {
1077 /* We lost the race, therefore we must update the
1078 * dirop state and retry to build the tree.
1080 op->length = scp->length;
1081 op->newLength = op->length;
1082 op->dataVersion = scp->dataVersion;
1083 op->newDataVersion = op->dataVersion;
1088 scp->dirDataVersion = scp->dataVersion;
1093 case CM_DIRLOCK_NONE:
1094 lock_ReleaseWrite(&scp->dirlock);
1096 case CM_DIRLOCK_READ:
1097 lock_ConvertWToR(&scp->dirlock);
1099 case CM_DIRLOCK_WRITE:
1101 /* got it already */;
1106 /* we know that haveWrite matches lockType at this point */
1108 case CM_DIRLOCK_NONE:
1110 lock_ReleaseWrite(&scp->dirlock);
1112 lock_ReleaseRead(&scp->dirlock);
1114 case CM_DIRLOCK_READ:
1115 osi_assert(!haveWrite);
1117 case CM_DIRLOCK_WRITE:
1119 osi_assert(haveWrite);
1122 op->lockType = lockType;
1124 lock_ReleaseWrite(&scp->rw);
1127 lock_ReleaseWrite(&scp->dirlock);
1129 lock_ReleaseRead(&scp->dirlock);
1131 lock_ReleaseWrite(&scp->rw);
1138 /* Check if it is safe for us to perform local directory updates.
1139 Called with op->scp->rw unlocked. */
1141 cm_CheckDirOpForSingleChange(cm_dirOp_t * op)
1146 if (op->scp == NULL)
1149 lock_ObtainWrite(&op->scp->rw);
1150 code = cm_DirCheckStatus(op, 1);
1153 op->dataVersion == op->scp->dataVersion - 1) {
1154 /* only one set of changes happened between cm_BeginDirOp()
1155 and this function. It is safe for us to perform local
1157 op->newDataVersion = op->scp->dataVersion;
1158 op->newLength = op->scp->serverLength;
1162 lock_ReleaseWrite(&op->scp->rw);
1165 osi_Log0(afsd_logp, "cm_CheckDirOpForSingleChange succeeded");
1168 "cm_CheckDirOpForSingleChange failed. code=0x%x, old dv=%d, new dv=%d",
1169 code, op->dataVersion, op->scp->dataVersion);
1173 /* End a sequence of directory operations.
1174 * Called with op->scp->rw unlocked.*/
1176 cm_EndDirOp(cm_dirOp_t * op)
1180 if (op->scp == NULL)
1183 osi_Log2(afsd_logp, "Ending dirOp 0x%p with %d dirty buffer releases",
1184 op, op->dirtyBufCount);
1186 if (op->dirtyBufCount > 0) {
1188 /* update the data version on the B+ tree */
1189 if (op->scp->dirBplus &&
1190 op->scp->dirDataVersion == op->dataVersion) {
1192 switch (op->lockType) {
1193 case CM_DIRLOCK_READ:
1194 lock_ReleaseRead(&op->scp->dirlock);
1195 /* fall through ... */
1196 case CM_DIRLOCK_NONE:
1197 lock_ObtainWrite(&op->scp->dirlock);
1198 op->lockType = CM_DIRLOCK_WRITE;
1200 case CM_DIRLOCK_WRITE:
1202 /* already got it */;
1204 op->scp->dirDataVersion = op->newDataVersion;
1208 /* we made changes. We should go through the list of buffers
1209 * and update the dataVersion for each. */
1210 lock_ObtainWrite(&op->scp->rw);
1211 code = buf_ForceDataVersion(op->scp, op->dataVersion, op->newDataVersion);
1212 op->scp->flags |= CM_SCACHEFLAG_LOCAL;
1213 lock_ReleaseWrite(&op->scp->rw);
1216 switch (op->lockType) {
1217 case CM_DIRLOCK_NONE:
1219 case CM_DIRLOCK_READ:
1220 lock_ReleaseRead(&op->scp->dirlock);
1222 case CM_DIRLOCK_WRITE:
1224 lock_ReleaseWrite(&op->scp->dirlock);
1228 cm_ReleaseSCache(op->scp);
1232 cm_ReleaseUser(op->userp);
1235 osi_assertx(op->nBuffers == 0, "Buffer leak after dirOp termination");
1240 /* NOTE: Called without scp->rw and without bufferp->mx */
1242 cm_DirOpAddBuffer(cm_dirOp_t * op, cm_buf_t * bufferp)
1247 osi_Log2(afsd_logp, "cm_DirOpAddBuffer for op 0x%p, buffer %p", op, bufferp);
1249 if (bufferp == NULL)
1252 for (i=0; i < CM_DIROP_MAXBUFFERS; i++) {
1253 if ((op->buffers[i].flags & CM_DIROPBUFF_INUSE) &&
1254 op->buffers[i].bufferp == bufferp) {
1259 if (i < CM_DIROP_MAXBUFFERS) {
1260 /* we already have this buffer on our list */
1262 op->buffers[i].refcount++;
1264 "cm_DirOpAddBuffer: the buffer is already listed for the dirOp");
1267 /* we have to add a new buffer */
1268 osi_assertx(op->nBuffers < CM_DIROP_MAXBUFFERS - 1,
1269 "DirOp has exceeded CM_DIROP_MAXBUFFERS buffers");
1271 for (i=0; i < CM_DIROP_MAXBUFFERS; i++) {
1272 if (!(op->buffers[i].flags & CM_DIROPBUFF_INUSE))
1276 osi_assert(i < CM_DIROP_MAXBUFFERS);
1278 lock_ObtainMutex(&bufferp->mx);
1279 lock_ObtainWrite(&op->scp->rw);
1281 /* Make sure we are synchronized. */
1282 osi_assert(op->lockType != CM_DIRLOCK_NONE);
1284 code = cm_SyncOp(op->scp, bufferp, op->userp, &op->req, PRSFS_LOOKUP,
1285 CM_SCACHESYNC_NEEDCALLBACK |
1286 (op->lockType == CM_DIRLOCK_WRITE ? CM_SCACHESYNC_WRITE : CM_SCACHESYNC_READ) |
1287 CM_SCACHESYNC_BUFLOCKED);
1289 if (code == 0 && bufferp->dataVersion != op->dataVersion) {
1291 "cm_DirOpAddBuffer: buffer data version mismatch. buf dv = %d. needs %d",
1292 bufferp->dataVersion, op->dataVersion);
1294 cm_SyncOpDone(op->scp, bufferp,
1295 CM_SCACHESYNC_NEEDCALLBACK |
1296 (op->lockType == CM_DIRLOCK_WRITE ? CM_SCACHESYNC_WRITE : CM_SCACHESYNC_READ) |
1297 CM_SCACHESYNC_BUFLOCKED);
1298 code = CM_ERROR_NOTINCACHE;
1301 lock_ReleaseWrite(&op->scp->rw);
1302 lock_ReleaseMutex(&bufferp->mx);
1305 osi_Log1(afsd_logp, "cm_DirOpAddBuffer: failed to sync buffer. code=0x%x",
1311 op->buffers[i].bufferp = bufferp;
1312 op->buffers[i].refcount = 1; /* start with one ref */
1313 op->buffers[i].flags = CM_DIROPBUFF_INUSE;
1317 osi_Log0(afsd_logp, "cm_DirOpAddBuffer: returning success");
1323 /* Note: Called without op->scp->rw */
1325 cm_DirOpFindBuffer(cm_dirOp_t * op, osi_hyper_t offset, cm_buf_t ** bufferpp)
1329 for (i=0; i < CM_DIROP_MAXBUFFERS; i++) {
1330 if ((op->buffers[i].flags & CM_DIROPBUFF_INUSE) &&
1331 LargeIntegerEqualTo(op->buffers[i].bufferp->offset, offset))
1335 if (i < CM_DIROP_MAXBUFFERS) {
1337 op->buffers[i].refcount++;
1338 buf_Hold(op->buffers[i].bufferp);
1339 *bufferpp = op->buffers[i].bufferp;
1341 osi_Log2(afsd_logp, "cm_DirOpFindBuffer: found buffer for offset [%x:%x]",
1342 offset.HighPart, offset.LowPart);
1346 osi_Log2(afsd_logp, "cm_DirOpFindBuffer: buffer not found for offset [%x:%x]",
1347 offset.HighPart, offset.LowPart);
1352 /* NOTE: called with scp->rw held or not depending on the flags */
1354 cm_DirOpDelBuffer(cm_dirOp_t * op, cm_buf_t * bufferp, int flags)
1358 osi_Log3(afsd_logp, "cm_DirOpDelBuffer for op 0x%p, buffer 0x%p, flags=%d",
1359 op, bufferp, flags);
1361 for (i=0; i < CM_DIROP_MAXBUFFERS; i++) {
1362 if ((op->buffers[i].flags & CM_DIROPBUFF_INUSE) &&
1363 op->buffers[i].bufferp == bufferp)
1367 if (i < CM_DIROP_MAXBUFFERS) {
1369 if (flags & DIROP_MODIFIED)
1370 op->dirtyBufCount++;
1372 osi_assert(op->buffers[i].refcount > 0);
1373 op->buffers[i].refcount --;
1375 if (op->buffers[i].refcount == 0) {
1376 /* this was the last reference we had */
1378 osi_Log0(afsd_logp, "cm_DirOpDelBuffer: releasing buffer");
1380 /* if this buffer was modified, then we update the data
1381 version of the buffer with the data version of the
1383 if (!(flags & DIROP_SCPLOCKED)) {
1384 lock_ObtainWrite(&op->scp->rw);
1387 /* first make sure that the buffer is idle. It should
1388 have been idle all along. */
1389 osi_assertx((bufferp->cmFlags & (CM_BUF_CMFETCHING |
1390 CM_BUF_CMSTORING)) == 0,
1391 "Buffer is not idle while performing dirOp");
1393 cm_SyncOpDone(op->scp, bufferp,
1394 CM_SCACHESYNC_NEEDCALLBACK |
1395 (op->lockType == CM_DIRLOCK_WRITE ? CM_SCACHESYNC_WRITE : CM_SCACHESYNC_READ));
1398 osi_assert(bufferp->dataVersion == op->dataVersion);
1401 lock_ReleaseWrite(&op->scp->rw);
1403 lock_ObtainMutex(&bufferp->mx);
1405 if (flags & DIROP_SCPLOCKED) {
1406 lock_ObtainWrite(&op->scp->rw);
1409 if (flags & DIROP_MODIFIED) {
1410 /* We don't update the dataversion here. Instead we
1411 wait until the dirOp is completed and then flip the
1412 dataversion on all the buffers in one go.
1413 Otherwise we won't know if the dataversion is
1414 current because it was fetched from the server or
1415 because we touched it during the dirOp. */
1417 if (bufferp->userp != op->userp) {
1418 if (bufferp->userp != NULL)
1419 cm_ReleaseUser(bufferp->userp);
1420 cm_HoldUser(op->userp);
1421 bufferp->userp = op->userp;
1425 lock_ReleaseMutex(&bufferp->mx);
1427 op->buffers[i].bufferp = NULL;
1428 buf_Release(bufferp);
1429 op->buffers[i].flags = 0;
1435 /* we have other references to this buffer. so we have to
1441 osi_Log0(afsd_logp, "cm_DirOpDelBuffer: buffer not found");
1442 osi_assertx(FALSE, "Attempt to delete a non-existent buffer from a dirOp");
1447 /* Check if we have current status and a callback for the given scp.
1448 This should be called before cm_DirGetPage() is called per scp.
1451 scp->rw locked state indicated by parameter
1454 scp->rw same state as upon entry
1457 scp->rw may be released
1460 cm_DirCheckStatus(cm_dirOp_t * op, int scp_locked)
1465 lock_ObtainWrite(&op->scp->rw);
1466 code = cm_SyncOp(op->scp, NULL, op->userp, &op->req, PRSFS_LOOKUP,
1467 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1469 lock_ReleaseWrite(&op->scp->rw);
1471 osi_Log2(afsd_logp, "cm_DirCheckStatus for op 0x%p returning code 0x%x",
1477 /* Attempt to prefetch all the buffers for this operation.
1479 Called with scp->rw unlocked
1482 cm_DirPrefetchBuffers(cm_dirOp_t * op)
1486 cm_buf_t *bufferp = NULL;
1488 osi_Log1(afsd_logp, "cm_DirPrefetchBuffers for op 0x%p", op);
1490 /* prefetching is only done on read operations where we don't
1491 expect the data version to change. */
1492 if (op->dataVersion != op->newDataVersion) {
1493 osi_Log0(afsd_logp, "Skipping prefetch for write operation.");
1494 return CM_ERROR_INVAL;
1497 lock_ObtainWrite(&op->scp->rw);
1499 /* When we are prefetching a file, we first flush out any of its
1500 contents just to make sure that we don't end up with buffers
1501 that was locally modified. */
1503 if (op->scp->flags & CM_SCACHEFLAG_LOCAL)
1504 op->scp->bufDataVersionLow = op->scp->dataVersion;
1506 offset = ConvertLongToLargeInteger(0);
1507 while (LargeIntegerLessThan(offset, op->scp->length)) {
1508 osi_Log2(afsd_logp, "Trying prefetch for offset %08x:%08x",
1509 offset.HighPart, offset.LowPart);
1510 lock_ReleaseWrite(&op->scp->rw);
1512 code = buf_Get(op->scp, &offset, &bufferp);
1514 lock_ObtainWrite(&op->scp->rw);
1521 code = cm_SyncOp(op->scp, bufferp, op->userp, &op->req, PRSFS_LOOKUP,
1522 CM_SCACHESYNC_NEEDCALLBACK |
1523 (op->lockType == CM_DIRLOCK_WRITE ? CM_SCACHESYNC_WRITE : CM_SCACHESYNC_READ));
1528 cm_SyncOpDone(op->scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK |
1529 (op->lockType == CM_DIRLOCK_WRITE ? CM_SCACHESYNC_WRITE : CM_SCACHESYNC_READ));
1531 if (cm_HaveBuffer(op->scp, bufferp, 0))
1534 code = cm_GetBuffer(op->scp, bufferp, NULL, op->userp, &op->req);
1543 buf_Release(bufferp);
1547 offset = LargeIntegerAdd(offset, ConvertLongToLargeInteger(cm_data.buf_blockSize));
1551 lock_ReleaseWrite(&op->scp->rw);
1553 osi_Log1(afsd_logp, "cm_DirPrefetchBuffers returning code 0x%x", code);
1558 /* Release a directory buffer that was obtained via a call to
1559 cm_DirGetPage() or any other function that returns a locked, held,
1560 directory page buffer.
1562 Called with scp->rw unlocked
1565 cm_DirReleasePage(cm_dirOp_t * op, cm_buf_t ** bufferpp, int modified)
1572 cm_DirOpDelBuffer(op, *bufferpp,
1573 ((modified ? DIROP_MODIFIED : 0)));
1574 buf_Release(*bufferpp);
1581 Returns the index'th directory page from scp. The userp and reqp
1582 will be used to fetch the buffer from the fileserver if necessary.
1583 If the call is successful, a locked and held cm_buf_t is returned
1584 via buferpp and a pointer to the directory page is returned via
1587 The returned buffer should be released via a call to
1588 cm_DirReleasePage() or by passing it into a subsequent call to
1589 cm_DirGetPage() for the *same* scp.
1591 If a *locked* buffer for the *same* scp is passed in via bufferpp
1592 to the function, it will check if the requested directory page is
1593 located in the specified buffer. If not, the buffer will be
1594 released and a new buffer returned that contains the requested
1597 If the specified page exists beyond the EOF for the scp, a new
1598 buffer will be allocated only if create is set to TRUE.
1600 Note: If a buffer is specified on entry via bufferpp, it is assumed
1601 that the buffer is unmodified. If the buffer is modified, it
1602 should be released via cm_DirReleasePage().
1606 If *bufferpp is non-NULL, then *bufferpp->mx is locked.
1610 If *bufferpp is non-NULL, then *bufferpp->mx is locked.
1613 scp->rw will be obtained and released
1617 cm_DirGetPage(cm_dirOp_t * op,
1618 long index, cm_buf_t ** bufferpp, void ** datapp)
1620 osi_hyper_t pageOffset; /* offset of the dir page from the
1621 start of the directory */
1622 osi_hyper_t bufferOffset; /* offset of the buffer from the start
1626 cm_buf_t * bufferp = NULL;
1628 void * datap = NULL;
1632 osi_Log2(afsd_logp, "cm_DirGetPage for op 0x%p, index %d", op, index);
1634 pageOffset = ConvertLongToLargeInteger(index * CM_DIR_PAGESIZE);
1635 bufferOffset.HighPart = pageOffset.HighPart;
1636 bufferOffset.LowPart = pageOffset.LowPart & ~(cm_data.buf_blockSize - 1);
1638 bufferp = *bufferpp;
1639 if (bufferp != NULL) {
1640 osi_assert(cm_FidCmp(&bufferp->fid, &op->scp->fid) == 0);
1642 thyper = bufferp->offset;
1645 if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
1649 buf_Release(bufferp);
1650 cm_DirOpDelBuffer(op, bufferp, 0);
1654 /* first check if we are already working with the buffer */
1655 if (cm_DirOpFindBuffer(op, bufferOffset, &bufferp)) {
1660 code = buf_Get(op->scp, &bufferOffset, &bufferp);
1662 osi_Log1(afsd_logp, " buf_Get returned code 0x%x", code);
1667 osi_assert(bufferp != NULL);
1669 /* DirOpAddBuffer will obtain bufferp->mx if necessary */
1670 code = cm_DirOpAddBuffer(op, bufferp);
1673 /* for some reason, the buffer was rejected. We can't use
1674 this buffer, and since this is the only buffer we can
1675 potentially use, there's no recourse.*/
1676 buf_Release(bufferp);
1684 /* now to figure out where the data is */
1685 thyper = LargeIntegerSubtract(pageOffset, bufferOffset);
1687 osi_assert(thyper.HighPart == 0);
1688 osi_assert(cm_data.buf_blockSize > thyper.LowPart &&
1689 cm_data.buf_blockSize - thyper.LowPart >= CM_DIR_PAGESIZE);
1691 datap = (void *) (((char *)bufferp->datap) + thyper.LowPart);
1696 /* also, if we are writing past EOF, we should make a note of the
1698 thyper = LargeIntegerAdd(pageOffset,
1699 ConvertLongToLargeInteger(CM_DIR_PAGESIZE));
1700 if (LargeIntegerLessThan(op->newLength, thyper)) {
1701 op->newLength = thyper;
1706 *bufferpp = bufferp;
1708 osi_Log1(afsd_logp, "cm_DirGetPage returning code 0x%x", code);
1715 cm_DirEntryListAdd(char * namep, cm_dirEntryList_t ** list)
1718 cm_dirEntryList_t * entry;
1720 len = strlen(namep);
1721 len += sizeof(cm_dirEntryList_t);
1723 entry = malloc(len);
1725 entry->nextp = *list;
1726 strcpy(entry->name, namep);
1732 cm_DirEntryListFree(cm_dirEntryList_t ** list)
1734 cm_dirEntryList_t * entry;
1735 cm_dirEntryList_t * next;
1737 for (entry = *list; entry; entry = next) {
1738 next = entry->nextp;