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_int32 cm_BPlusTrees = 1;
37 void cm_DirDumpStats(void)
39 afsi_log("Dir Lookup Hits: %-8d", dir_lookup_hits);
40 afsi_log(" Misses: %-8d", dir_lookup_misses);
41 afsi_log(" Create: %-8d", dir_create_entry);
42 afsi_log(" Remove: %-8d", dir_remove_entry);
44 afsi_log("Dir Times Lookup: %-16I64d", dir_lookup_time);
45 afsi_log(" Create: %-16I64d", dir_create_time);
46 afsi_log(" Remove: %-16I64d", dir_remove_time);
50 /* Local static prototypes */
52 cm_DirGetBlob(cm_dirOp_t * op,
53 unsigned int blobno, cm_buf_t ** bufferpp, cm_dirEntry_t ** blobpp);
56 cm_DirFindItem(cm_dirOp_t * op,
58 cm_buf_t ** itembufpp, cm_dirEntry_t ** itempp,
59 cm_buf_t ** prevbufpp, unsigned short **previtempp);
62 cm_DirOpAddBuffer(cm_dirOp_t * op, cm_buf_t * buffer);
64 /* flags for cm_DirOpDelBuffer */
65 #define DIROP_MODIFIED 1
66 #define DIROP_SCPLOCKED 2
69 cm_DirOpDelBuffer(cm_dirOp_t * op, cm_buf_t * buffer, int flags);
72 cm_DirCheckStatus(cm_dirOp_t * op);
75 cm_DirReleasePage(cm_dirOp_t * op, cm_buf_t ** bufferpp, int modified);
78 cm_DirGetPage(cm_dirOp_t * op,
79 long index, cm_buf_t ** bufferpp, void ** datapp);
82 cm_DirFindBlobs(cm_dirOp_t * op, int nblobs);
85 cm_DirAddPage(cm_dirOp_t * op, int pageno);
88 cm_DirFreeBlobs(cm_dirOp_t * op, int firstblob, int nblobs);
91 /* compute how many 32 byte entries an AFS 3 dir requires for storing
95 cm_NameEntries(char *namep, long *lenp)
99 i = (long)strlen(namep) + 1;
101 return 1 + ((i+15) >> 5);
104 /* Create an entry in a file. Dir is a file representation, while
105 entry is a string name.
108 op->scp->mx is unlocked
111 op->scp->mx is unlocked
113 None of the directory buffers for op->scp should be locked by the
117 cm_DirCreateEntry(cm_dirOp_t * op, char *entry, cm_fid_t * cfid)
121 LARGE_INTEGER start, end;
123 cm_dirEntry_t *ep = NULL;
124 cm_buf_t *entrybuf = NULL;
126 unsigned short *pp = NULL;
127 cm_buf_t *prevptrbuf = NULL;
129 cm_dirHeader_t *dhp = NULL;
130 cm_buf_t *dhpbuf = NULL;
134 /* check name quality */
138 QueryPerformanceCounter(&start);
142 osi_Log4(afsd_logp, "cm_DirCreateEntry for op 0x%p, name [%s] and fid[%d,%d]",
143 op, osi_LogSaveString(afsd_logp, entry), cfid->vnode, cfid->unique);
145 /* First check if file already exists. */
146 code = cm_DirFindItem(op,
151 cm_DirReleasePage(op, &entrybuf, FALSE);
152 cm_DirReleasePage(op, &prevptrbuf, FALSE);
157 blobs = cm_NameEntries(entry, NULL); /* number of entries required */
158 firstelt = cm_DirFindBlobs(op, blobs);
160 osi_Log0(afsd_logp, "cm_DirCreateEntry returning EFBIG");
161 code = EFBIG; /* directory is full */
165 /* First, we fill in the directory entry. */
166 code = cm_DirGetBlob(op, firstelt, &entrybuf, &ep);
172 ep->flag = CM_DIR_FFIRST;
173 ep->fid.vnode = htonl(cfid->vnode);
174 ep->fid.unique = htonl(cfid->unique);
175 strcpy(ep->name, entry);
177 /* Now we just have to thread it on the hash table list. */
178 code = cm_DirGetPage(op, 0, &dhpbuf, &dhp);
180 cm_DirReleasePage(op, &entrybuf, TRUE);
185 i = cm_DirHash(entry);
187 ep->next = dhp->hashTable[i];
188 dhp->hashTable[i] = htons(firstelt);
190 cm_DirReleasePage(op, &dhpbuf, TRUE);
191 cm_DirReleasePage(op, &entrybuf, TRUE);
193 osi_Log0(afsd_logp, "cm_DirCreateEntry returning success");
197 QueryPerformanceCounter(&end);
199 dir_create_time += (end.QuadPart - start.QuadPart);
203 /* Return the length of a directory in pages
206 op->scp->mx is locked
209 op->scp->mx is locked
211 The first directory page for op->scp should not be locked by the
215 cm_DirLength(cm_dirOp_t * op)
218 cm_dirHeader_t *dhp = NULL;
219 cm_buf_t *dhpbuf = NULL;
223 code = cm_DirGetPage(op, 0, &dhpbuf, &dhp);
227 if (dhp->header.pgcount != 0)
228 ctr = ntohs(dhp->header.pgcount);
230 /* old style, count the pages */
232 for (i = 0; i < CM_DIR_MAXPAGES; i++)
233 if (dhp->alloMap[i] != CM_DIR_EPP)
236 cm_DirReleasePage(op, &dhpbuf, FALSE);
237 return ctr * CM_DIR_PAGESIZE;
240 /* Delete a directory entry.
243 op->scp->mx is unlocked
246 op->scp->mx is unlocked
248 None of the directory buffers for op->scp should be locked by the
252 cm_DirDeleteEntry(cm_dirOp_t * op, char *entry)
254 /* Delete an entry from a directory, including update of all free
255 entry descriptors. */
258 cm_dirEntry_t *firstitem = NULL;
259 cm_buf_t *itembuf = NULL;
260 unsigned short *previtem = NULL;
261 cm_buf_t *pibuf = NULL;
265 LARGE_INTEGER start, end;
267 QueryPerformanceCounter(&start);
269 osi_Log2(afsd_logp, "cm_DirDeleteEntry for op 0x%p, entry [%s]",
270 op, osi_LogSaveString(afsd_logp, entry));
272 code = cm_DirFindItem(op, entry,
273 &itembuf, &firstitem,
276 osi_Log0(afsd_logp, "cm_DirDeleteEntry returning ENOENT");
283 *previtem = firstitem->next;
284 cm_DirReleasePage(op, &pibuf, TRUE);
286 thyper = itembuf->offset;
287 thyper = LargeIntegerAdd(thyper,
288 ConvertLongToLargeInteger(((char *) firstitem) - itembuf->datap));
289 thyper = ExtendedLargeIntegerDivide(thyper, 32, &junk);
291 index = thyper.LowPart;
292 osi_assert(thyper.HighPart == 0);
294 nitems = cm_NameEntries(firstitem->name, NULL);
295 cm_DirReleasePage(op, &itembuf, FALSE);
297 cm_DirFreeBlobs(op, index, nitems);
299 osi_Log0(afsd_logp, "cm_DirDeleteEntry returning success");
303 QueryPerformanceCounter(&end);
305 dir_remove_time += (end.QuadPart - start.QuadPart);
310 /* Find a bunch of contiguous entries; at least nblobs in a row.
312 Called with op->scp->mx */
314 cm_DirFindBlobs(cm_dirOp_t * op, int nblobs)
319 cm_dirHeader_t *dhp = NULL;
320 cm_buf_t *dhpbuf = NULL;
321 int dhpModified = FALSE;
323 cm_pageHeader_t *pp = NULL;
324 cm_buf_t *pagebuf = NULL;
325 int pageModified = FALSE;
331 osi_Log2(afsd_logp, "cm_DirFindBlobs for op 0x%p, nblobs = %d",
334 code = cm_DirGetPage(op, 0, &dhpbuf, (void **) &dhp);
338 for (i = 0; i < CM_DIR_BIGMAXPAGES; i++) {
339 if (i >= CM_DIR_MAXPAGES || dhp->alloMap[i] >= nblobs) {
340 /* if page could contain enough entries */
341 /* If there are CM_DIR_EPP free entries, then the page is
342 not even allocated. */
343 if (i >= CM_DIR_MAXPAGES) {
345 /* this pages exists past the end of the old-style dir */
346 pgcount = ntohs(dhp->header.pgcount);
348 pgcount = CM_DIR_MAXPAGES;
349 dhp->header.pgcount = htons(pgcount);
353 if (i > pgcount - 1) {
354 /* this page is bigger than last allocated page */
355 cm_DirAddPage(op, i);
356 dhp->header.pgcount = htons(i + 1);
359 } else if (dhp->alloMap[i] == CM_DIR_EPP) {
360 /* Add the page to the directory. */
361 cm_DirAddPage(op, i);
362 dhp->alloMap[i] = CM_DIR_EPP - 1;
363 dhp->header.pgcount = htons(i + 1);
367 code = cm_DirGetPage(op, i, &pagebuf, &pp);
369 cm_DirReleasePage(op, &dhpbuf, dhpModified);
373 for (j = 0; j <= CM_DIR_EPP - nblobs; j++) {
375 for (k = 0; k < nblobs; k++)
376 if ((pp->freeBitmap[(j + k) >> 3] >> ((j + k) & 7)) & 1) {
386 /* Here we have the first index in j. We update the allocation maps
387 * and free up any resources we've got allocated. */
388 if (i < CM_DIR_MAXPAGES) {
389 dhp->alloMap[i] -= nblobs;
393 cm_DirReleasePage(op, &dhpbuf, dhpModified);
395 for (k = 0; k < nblobs; k++)
396 pp->freeBitmap[(j + k) >> 3] |= 1 << ((j + k) & 7);
398 cm_DirReleasePage(op, &pagebuf, TRUE);
400 osi_Log0(afsd_logp, "cm_DirFindBlobs returning success");
402 return j + i * CM_DIR_EPP;
404 cm_DirReleasePage(op, &pagebuf, pageModified);
408 /* If we make it here, the directory is full. */
409 osi_Log0(afsd_logp, "cm_DirFindBlobs directory is full");
410 cm_DirReleasePage(op, &dhpbuf, dhpModified);
414 /* Add a page to a directory.
416 Called with op->scp->mx
419 cm_DirAddPage(cm_dirOp_t * op, int pageno)
422 cm_pageHeader_t *pp = NULL;
423 cm_buf_t *pagebuf = NULL;
426 osi_Log2(afsd_logp, "cm_DirAddPage for op 0x%p, pageno=%d", op, pageno);
428 code = cm_DirGetPage(op, pageno, &pagebuf, (void **) &pp);
432 pp->tag = htons(1234);
435 pp->freeCount = CM_DIR_EPP - 1; /* The first dude is already allocated */
436 pp->freeBitmap[0] = 0x01;
437 for (i = 1; i < CM_DIR_EPP / 8; i++) /* It's a constant */
438 pp->freeBitmap[i] = 0;
440 cm_DirReleasePage(op, &pagebuf, TRUE);
442 osi_Log0(afsd_logp, "cm_DirAddPage returning success");
447 /* Free a whole bunch of directory entries.
449 Called with op->scp->mx
452 cm_DirFreeBlobs(cm_dirOp_t * op, int firstblob, int nblobs)
457 cm_dirHeader_t *dhp = NULL;
458 cm_buf_t *dhpbuf = NULL;
459 int dhpmodified = FALSE;
461 cm_pageHeader_t *pp = NULL;
462 cm_buf_t *pagebuf = NULL;
465 osi_Log3(afsd_logp, "cm_DirFreeBlobs for op 0x%p, firstblob=%d, nblobs=%d",
466 op, firstblob, nblobs);
468 page = firstblob / CM_DIR_EPP;
469 firstblob -= CM_DIR_EPP * page; /* convert to page-relative entry */
471 code = cm_DirGetPage(op, 0, &dhpbuf, &dhp);
475 if (page < CM_DIR_MAXPAGES) {
476 dhp->alloMap[page] += nblobs;
480 cm_DirReleasePage(op, &dhpbuf, dhpmodified);
482 code = cm_DirGetPage(op, page, &pagebuf, &pp);
484 for (i = 0; i < nblobs; i++)
485 pp->freeBitmap[(firstblob + i) >> 3] &=
486 ~(1 << ((firstblob + i) & 7));
487 cm_DirReleasePage(op, &pagebuf, TRUE);
490 osi_Log1(afsd_logp, "cm_DirFreeBlobs returning code 0x%x", code);
496 * Format an empty directory properly. Note that the first 13 entries in a
497 * directory header page are allocated, 1 to the page header, 4 to the
498 * allocation map and 8 to the hash table.
500 * Called with op->scp->mx unlocked
503 cm_DirMakeDir(cm_dirOp_t * op, cm_fid_t * me, cm_fid_t * parent)
506 cm_dirHeader_t *dhp = NULL;
507 cm_buf_t *dhpbuf = NULL;
511 osi_Log3(afsd_logp, "cm_DirMakeDir for op 0x%p, directory fid[%d, %d]",
512 op, me->vnode, me->unique);
513 osi_Log2(afsd_logp, " parent[%d, %d]",
514 parent->vnode, parent->unique);
516 code = cm_DirGetPage(op, 0, &dhpbuf, &dhp);
522 dhp->header.pgcount = htons(1);
523 dhp->header.tag = htons(1234);
524 dhp->header.freeCount = (CM_DIR_EPP - CM_DIR_DHE - 1);
525 dhp->header.freeBitmap[0] = 0xff;
526 dhp->header.freeBitmap[1] = 0x1f;
527 for (i = 2; i < CM_DIR_EPP / 8; i++)
528 dhp->header.freeBitmap[i] = 0;
529 dhp->alloMap[0] = (CM_DIR_EPP - CM_DIR_DHE - 1);
530 for (i = 1; i < CM_DIR_MAXPAGES; i++)
531 dhp->alloMap[i] = CM_DIR_EPP;
532 for (i = 0; i < CM_DIR_NHASHENT; i++)
533 dhp->hashTable[i] = 0;
535 cm_DirReleasePage(op, &dhpbuf, TRUE);
537 cm_DirCreateEntry(op, ".", me);
538 cm_DirCreateEntry(op, "..", parent); /* Virtue is its own .. */
540 osi_Log0(afsd_logp, "cm_DirMakeDir returning success");
546 /* Look up a file name in directory.
549 op->scp->mx is unlocked
552 op->scp->mx is unlocked
554 None of the directory buffers for op->scp should be locked by the
558 cm_DirLookup(cm_dirOp_t * op, char *entry, cm_fid_t * cfid)
560 cm_dirEntry_t *firstitem = NULL;
561 cm_buf_t *itembuf = NULL;
562 unsigned short *previtem = NULL;
563 cm_buf_t *pibuf = NULL;
568 QueryPerformanceCounter(&start);
570 osi_Log2(afsd_logp, "cm_DirLookup for op 0x%p, entry[%s]",
571 op, osi_LogSaveString(afsd_logp, entry));
573 code = cm_DirFindItem(op, entry,
574 &itembuf, &firstitem,
582 cm_DirReleasePage(op, &pibuf, FALSE);
584 cfid->cell = op->scp->fid.cell;
585 cfid->volume = op->scp->fid.volume;
586 cfid->vnode = ntohl(firstitem->fid.vnode);
587 cfid->unique = ntohl(firstitem->fid.unique);
589 cm_DirReleasePage(op, &itembuf, FALSE);
591 osi_Log2(afsd_logp, "cm_DirLookup returning fid[%d,%d]",
592 cfid->vnode, cfid->unique);
598 QueryPerformanceCounter(&end);
600 dir_lookup_time += (end.QuadPart - start.QuadPart);
605 /* Look up a file name in directory.
608 op->scp->mx is locked
611 op->scp->mx is locked
613 None of the directory buffers for op->scp should be locked by the
617 cm_DirLookupOffset(cm_dirOp_t * op, char *entry, cm_fid_t *cfid, osi_hyper_t *offsetp)
619 cm_dirEntry_t *firstitem = NULL;
620 cm_buf_t *itembuf = NULL;
621 unsigned short *previtem = NULL;
622 cm_buf_t *pibuf = NULL;
626 osi_Log2(afsd_logp, "cm_DirLookupOffset for op 0x%p, entry[%s]",
627 op, osi_LogSaveString(afsd_logp, entry));
629 code = cm_DirFindItem(op, entry,
630 &itembuf, &firstitem,
635 cm_DirReleasePage(op, &pibuf, FALSE);
637 cfid->cell = op->scp->fid.cell;
638 cfid->volume = op->scp->fid.volume;
639 cfid->vnode = ntohl(firstitem->fid.vnode);
640 cfid->unique = ntohl(firstitem->fid.unique);
644 thyper = itembuf->offset;
645 thyper = LargeIntegerAdd(thyper,
646 ConvertLongToLargeInteger(((char *) firstitem) - itembuf->datap));
651 cm_DirReleasePage(op, &itembuf, FALSE);
653 osi_Log2(afsd_logp, "cm_DirLookupOffset returning fid[%d,%d]",
654 cfid->vnode, cfid->unique);
656 osi_Log2(afsd_logp, " offset [%x:%x]",
657 offsetp->HighPart, offsetp->LowPart);
663 /* Apply a function to every directory entry in a directory.
666 op->scp->mx is locked
669 op->scp->mx is locked
671 None of the directory buffers for op->scp should be locked by the
674 The hook function cannot modify or lock any directory buffers.
677 cm_DirApply(cm_dirOp_t * op, int (*hookproc) (void *, char *, long, long), void *hook)
679 /* Enumerate the contents of a directory. */
683 cm_dirHeader_t *dhp = NULL;
684 cm_buf_t *dhpbuf = NULL;
686 cm_dirEntry_t *ep = NULL;
687 cm_buf_t *epbuf = NULL;
691 code = cm_DirGetPage(op, 0, &dhpbuf, &dhp);
695 for (i = 0; i < CM_DIR_NHASHENT; i++) {
696 /* For each hash chain, enumerate everyone on the list. */
697 num = ntohs(dhp->hashTable[i]);
699 /* Walk down the hash table list. */
700 code = cm_DirGetBlob(op, num, &epbuf, &ep);
702 cm_DirReleasePage(op, &dhpbuf, FALSE);
706 num = ntohs(ep->next);
707 (*hookproc) (hook, ep->name, ntohl(ep->fid.vnode),
708 ntohl(ep->fid.unique));
710 cm_DirReleasePage(op, &epbuf, FALSE);
713 cm_DirReleasePage(op, &dhpbuf, FALSE);
718 /* Check if a directory is empty
721 op->scp->mx is locked
724 op->scp->mx is locked
726 None of the directory buffers for op->scp should be locked by the
730 cm_DirIsEmpty(cm_dirOp_t * op)
732 /* Enumerate the contents of a directory. */
736 cm_dirHeader_t *dhp = NULL;
737 cm_buf_t *dhpbuf = NULL;
739 cm_dirEntry_t *ep = NULL;
740 cm_buf_t *epbuf = NULL;
744 code = cm_DirGetPage(op, 0, &dhpbuf, &dhp);
748 for (i = 0; i < CM_DIR_NHASHENT; i++) {
749 /* For each hash chain, enumerate everyone on the list. */
750 num = ntohs(dhp->hashTable[i]);
753 /* Walk down the hash table list. */
754 code = cm_DirGetBlob(op, num, &epbuf, &ep);
758 if (strcmp(ep->name, "..") && strcmp(ep->name, ".")) {
759 cm_DirReleasePage(op, &epbuf, FALSE);
760 cm_DirReleasePage(op, &dhpbuf, FALSE);
763 num = ntohs(ep->next);
764 cm_DirReleasePage(op, &epbuf, FALSE);
767 cm_DirReleasePage(op, &dhpbuf, FALSE);
771 /* Return a pointer to an entry, given its number.
775 if *bufferpp != NULL, then *bufferpp->mx is locked
778 scp->mx may be unlocked
779 *bufferpp may be released
783 if *bufferpp != NULL, then *bufferpp->mx is locked
785 *bufferpp should be released via cm_DirReleasePage() or any other
786 *call that releases a directory buffer.
789 cm_DirGetBlob(cm_dirOp_t * op,
790 unsigned int blobno, cm_buf_t ** bufferpp, cm_dirEntry_t ** blobpp)
795 osi_Log2(afsd_logp, "cm_DirGetBlob for op 0x%p, blobno=%d",
798 code = cm_DirGetPage(op, blobno >> CM_DIR_LEPP,
799 bufferpp, (void **) &ep);
803 *blobpp = (cm_dirEntry_t *) (ep + 32 * (blobno & (CM_DIR_EPP - 1)));
809 cm_DirHash(char *string)
811 /* Hash a string to a number between 0 and NHASHENT. */
812 register unsigned char tc;
816 while ((tc = (*string++))) {
820 tval = hval & (CM_DIR_NHASHENT - 1);
824 tval = CM_DIR_NHASHENT - tval;
828 /* Find a directory entry, given its name. This entry returns a
829 * pointer to a locked buffer, and a pointer to a locked buffer (in
830 * previtem) referencing the found item (to aid the delete code). If
831 * no entry is found, however, no items are left locked, and a null
832 * pointer is returned instead.
841 cm_DirFindItem(cm_dirOp_t * op,
843 cm_buf_t ** itembufpp, cm_dirEntry_t ** itempp,
844 cm_buf_t ** prevbufpp, unsigned short **previtempp)
847 cm_dirHeader_t *dhp = NULL;
848 unsigned short *lp = NULL;
849 cm_dirEntry_t *tp = NULL;
850 cm_buf_t *hashbufp = NULL;
851 cm_buf_t *itembufp = NULL;
854 osi_Log2(afsd_logp, "cm_DirFindItem for op 0x%p, entry[%s]",
855 op, osi_LogSaveString(afsd_logp, ename));
857 i = cm_DirHash(ename);
859 if (op->scp->fileType != CM_SCACHETYPE_DIRECTORY) {
860 osi_Log0(afsd_logp, "cm_DirFindItem: The scp is not a directory");
861 return CM_ERROR_INVAL;
864 code = cm_DirGetPage(op, 0, &hashbufp, (void **) &dhp);
869 if (dhp->hashTable[i] == 0) {
871 osi_Log1(afsd_logp, "cm_DirFindItem: Hash bucket %d is empty", i);
872 cm_DirReleasePage(op, &hashbufp, FALSE);
876 code = cm_DirGetBlob(op,
877 (u_short) ntohs(dhp->hashTable[i]),
880 cm_DirReleasePage(op, &hashbufp, FALSE);
884 lp = &(dhp->hashTable[i]);
888 lp : pointer to blob number of entry we are looking at
889 hashbufp : buffer containing lp
890 tp : pointer to entry we are looking at
891 itembufp : buffer containing tp
894 /* Look at each hash conflict entry. */
895 if (!strcmp(ename, tp->name)) {
896 osi_Log0(afsd_logp, "cm_DirFindItem: returning success");
897 /* Found our entry. */
899 *prevbufpp = hashbufp;
901 *itembufpp = itembufp;
906 cm_DirReleasePage(op, &hashbufp, FALSE);
913 /* The end of the line */
914 osi_Log0(afsd_logp, "cm_DirFindItem: returning ENOENT");
915 cm_DirReleasePage(op, &hashbufp, FALSE);
919 code = cm_DirGetBlob(op,
920 (u_short) ntohs(*lp),
924 cm_DirReleasePage(op, &hashbufp, FALSE);
930 /* Begin a sequence of directory operations. scp->mx should be
934 cm_BeginDirOp(cm_scache_t * scp, cm_user_t * userp, cm_req_t * reqp,
935 afs_uint32 lockType, cm_dirOp_t * op)
940 osi_Log3(afsd_logp, "Beginning dirOp[0x%p] for scp[0x%p], userp[0x%p]",
943 memset(op, 0, sizeof(*op));
949 cm_InitReq(&op->req);
951 op->dirtyBufCount = 0;
954 for (i=0; i < CM_DIROP_MAXBUFFERS; i++) {
955 op->buffers[i].flags = 0;
958 code = cm_DirCheckStatus(op);
961 op->length = scp->length;
962 op->newLength = op->length;
963 op->dataVersion = scp->dataVersion;
964 op->newDataVersion = op->dataVersion;
967 lock_ObtainRead(&scp->dirlock);
968 if (!cm_BPlusTrees ||
970 scp->dirDataVersion == scp->dataVersion))
975 case CM_DIRLOCK_NONE:
976 lock_ReleaseRead(&scp->dirlock);
978 case CM_DIRLOCK_READ:
981 case CM_DIRLOCK_WRITE:
983 lock_ReleaseRead(&scp->dirlock);
984 lock_ObtainWrite(&scp->dirlock);
987 lock_ReleaseRead(&scp->dirlock);
988 lock_ObtainWrite(&scp->dirlock);
990 scp->dirDataVersion != scp->dataVersion)
994 freeBtree(scp->dirBplus);
995 scp->dirBplus = NULL;
996 scp->dirDataVersion = -1;
999 if (!scp->dirBplus) {
1000 cm_BPlusDirBuildTree(scp, userp, reqp);
1002 scp->dirDataVersion = scp->dataVersion;
1006 case CM_DIRLOCK_NONE:
1007 lock_ReleaseWrite(&scp->dirlock);
1009 case CM_DIRLOCK_READ:
1010 lock_ConvertWToR(&scp->dirlock);
1012 case CM_DIRLOCK_WRITE:
1014 /* got it already */;
1019 case CM_DIRLOCK_NONE:
1021 case CM_DIRLOCK_READ:
1022 lock_ObtainRead(&scp->dirlock);
1024 case CM_DIRLOCK_WRITE:
1026 lock_ObtainWrite(&scp->dirlock);
1029 op->lockType = lockType;
1038 /* Check if it is safe for us to perform local directory updates.
1039 Called with scp->mx unlocked. */
1041 cm_CheckDirOpForSingleChange(cm_dirOp_t * op)
1045 if (op->scp == NULL)
1048 code = cm_DirCheckStatus(op);
1051 op->dataVersion == op->scp->dataVersion - 1) {
1052 /* only one set of changes happened between cm_BeginDirOp()
1053 and this function. It is safe for us to perform local
1055 op->newDataVersion = op->scp->dataVersion;
1056 op->newLength = op->scp->serverLength;
1058 osi_Log0(afsd_logp, "cm_CheckDirOpForSingleChange succeeded");
1064 "cm_CheckDirOpForSingleChange failed. code=0x%x, old dv=%d, new dv=%d",
1065 code, op->dataVersion, op->scp->dataVersion);
1069 /* End a sequence of directory operations. Called with op->scp->mx
1072 cm_EndDirOp(cm_dirOp_t * op)
1076 if (op->scp == NULL)
1079 osi_Log2(afsd_logp, "Ending dirOp 0x%p with %d dirty buffer releases",
1080 op, op->dirtyBufCount);
1082 if (op->dirtyBufCount > 0) {
1083 /* we made changes. We should go through the list of buffers
1084 and update the dataVersion for each. */
1085 code = buf_ForceDataVersion(op->scp, op->dataVersion, op->newDataVersion);
1088 /* and update the data version on the B+ tree */
1089 if (op->scp->dirBplus &&
1090 op->scp->dirDataVersion == op->dataVersion) {
1092 switch (op->lockType) {
1093 case CM_DIRLOCK_READ:
1094 lock_ReleaseRead(&op->scp->dirlock);
1095 /* fall through ... */
1096 case CM_DIRLOCK_NONE:
1097 lock_ObtainWrite(&op->scp->dirlock);
1098 op->lockType = CM_DIRLOCK_WRITE;
1100 case CM_DIRLOCK_WRITE:
1102 /* already got it */;
1104 op->scp->dirDataVersion = op->newDataVersion;
1109 switch (op->lockType) {
1110 case CM_DIRLOCK_NONE:
1112 case CM_DIRLOCK_READ:
1113 lock_ReleaseRead(&op->scp->dirlock);
1115 case CM_DIRLOCK_WRITE:
1117 lock_ReleaseWrite(&op->scp->dirlock);
1121 cm_ReleaseSCache(op->scp);
1125 cm_ReleaseUser(op->userp);
1128 osi_assertx(op->nBuffers == 0, "Buffer leak after dirOp termination");
1133 /* NOTE: Called without scp->mx and without bufferp->mx */
1135 cm_DirOpAddBuffer(cm_dirOp_t * op, cm_buf_t * bufferp)
1140 osi_Log2(afsd_logp, "cm_DirOpAddBuffer for op 0x%p, buffer %p", op, bufferp);
1142 if (bufferp == NULL)
1145 for (i=0; i < CM_DIROP_MAXBUFFERS; i++) {
1146 if ((op->buffers[i].flags & CM_DIROPBUFF_INUSE) &&
1147 op->buffers[i].bufferp == bufferp) {
1152 if (i < CM_DIROP_MAXBUFFERS) {
1153 /* we already have this buffer on our list */
1155 op->buffers[i].refcount++;
1157 "cm_DirOpAddBuffer: the buffer is already listed for the dirOp");
1160 /* we have to add a new buffer */
1161 osi_assertx(op->nBuffers < CM_DIROP_MAXBUFFERS - 1,
1162 "DirOp has exceeded CM_DIROP_MAXBUFFERS buffers");
1164 for (i=0; i < CM_DIROP_MAXBUFFERS; i++) {
1165 if (!(op->buffers[i].flags & CM_DIROPBUFF_INUSE))
1169 osi_assert(i < CM_DIROP_MAXBUFFERS);
1171 lock_ObtainMutex(&bufferp->mx);
1172 lock_ObtainMutex(&op->scp->mx);
1174 /* Make sure we are synchronized. */
1175 code = cm_SyncOp(op->scp, bufferp, op->userp, &op->req, PRSFS_LOOKUP,
1176 CM_SCACHESYNC_NEEDCALLBACK |
1177 CM_SCACHESYNC_WRITE |
1178 CM_SCACHESYNC_BUFLOCKED);
1181 bufferp->dataVersion != op->dataVersion) {
1183 osi_Log2(afsd_logp, "cm_DirOpAddBuffer: buffer version mismatch. buf ver = %d. want %d", bufferp->dataVersion, op->dataVersion);
1185 cm_SyncOpDone(op->scp, bufferp,
1186 CM_SCACHESYNC_NEEDCALLBACK |
1187 CM_SCACHESYNC_WRITE |
1188 CM_SCACHESYNC_BUFLOCKED);
1190 code = CM_ERROR_INVAL;
1193 lock_ReleaseMutex(&op->scp->mx);
1194 lock_ReleaseMutex(&bufferp->mx);
1197 osi_Log1(afsd_logp, "cm_DirOpAddBuffer: failed to sync buffer. code=0x%x",
1203 op->buffers[i].bufferp = bufferp;
1204 op->buffers[i].refcount = 1; /* start with one ref */
1205 op->buffers[i].flags = CM_DIROPBUFF_INUSE;
1209 osi_Log0(afsd_logp, "cm_DirOpAddBuffer: returning success");
1215 /* Note: Called without op->scp->mx */
1217 cm_DirOpFindBuffer(cm_dirOp_t * op, osi_hyper_t offset, cm_buf_t ** bufferpp)
1221 for (i=0; i < CM_DIROP_MAXBUFFERS; i++) {
1222 if ((op->buffers[i].flags & CM_DIROPBUFF_INUSE) &&
1223 LargeIntegerEqualTo(op->buffers[i].bufferp->offset, offset))
1227 if (i < CM_DIROP_MAXBUFFERS) {
1229 op->buffers[i].refcount++;
1230 buf_Hold(op->buffers[i].bufferp);
1231 *bufferpp = op->buffers[i].bufferp;
1233 osi_Log2(afsd_logp, "cm_DirOpFindBuffer: found buffer for offset [%x:%x]",
1234 offset.HighPart, offset.LowPart);
1238 osi_Log2(afsd_logp, "cm_DirOpFindBuffer: buffer not found for offset [%x:%x]",
1239 offset.HighPart, offset.LowPart);
1244 /* NOTE: called with scp->mx held or not depending on the flags */
1246 cm_DirOpDelBuffer(cm_dirOp_t * op, cm_buf_t * bufferp, int flags)
1250 osi_Log3(afsd_logp, "cm_DirOpDelBuffer for op 0x%p, buffer 0x%p, flags=%d",
1251 op, bufferp, flags);
1253 for (i=0; i < CM_DIROP_MAXBUFFERS; i++) {
1254 if ((op->buffers[i].flags & CM_DIROPBUFF_INUSE) &&
1255 op->buffers[i].bufferp == bufferp)
1259 if (i < CM_DIROP_MAXBUFFERS) {
1261 if (flags & DIROP_MODIFIED)
1262 op->dirtyBufCount++;
1264 osi_assert(op->buffers[i].refcount > 0);
1265 op->buffers[i].refcount --;
1267 if (op->buffers[i].refcount == 0) {
1268 /* this was the last reference we had */
1270 osi_Log0(afsd_logp, "cm_DirOpDelBuffer: releasing buffer");
1272 /* if this buffer was modified, then we update the data
1273 version of the buffer with the data version of the
1275 if (!(flags & DIROP_SCPLOCKED)) {
1276 lock_ObtainMutex(&op->scp->mx);
1279 /* first make sure that the buffer is idle. It should
1280 have been idle all along. */
1281 osi_assertx((bufferp->cmFlags & (CM_BUF_CMFETCHING |
1282 CM_BUF_CMSTORING)) == 0,
1283 "Buffer is not idle while performing dirOp");
1285 cm_SyncOpDone(op->scp, bufferp,
1286 CM_SCACHESYNC_NEEDCALLBACK |
1287 CM_SCACHESYNC_WRITE);
1290 osi_assert(bufferp->dataVersion == op->dataVersion);
1293 lock_ReleaseMutex(&op->scp->mx);
1295 lock_ObtainMutex(&bufferp->mx);
1297 if (flags & DIROP_SCPLOCKED) {
1298 lock_ObtainMutex(&op->scp->mx);
1301 if (flags & DIROP_MODIFIED) {
1302 /* We don't update the dataversion here. Instead we
1303 wait until the dirOp is completed and then flip the
1304 dataversion on all the buffers in one go.
1305 Otherwise we won't know if the dataversion is
1306 current because it was fetched from the server or
1307 because we touched it during the dirOp. */
1309 if (bufferp->userp != op->userp) {
1310 if (bufferp->userp != NULL)
1311 cm_ReleaseUser(bufferp->userp);
1312 cm_HoldUser(op->userp);
1313 bufferp->userp = op->userp;
1317 lock_ReleaseMutex(&bufferp->mx);
1319 op->buffers[i].bufferp = NULL;
1320 buf_Release(bufferp);
1321 op->buffers[i].flags = 0;
1327 /* we have other references to this buffer. so we have to
1333 osi_Log0(afsd_logp, "cm_DirOpDelBuffer: buffer not found");
1334 osi_assertx(FALSE, "Attempt to delete a non-existent buffer from a dirOp");
1339 /* Check if we have current status and a callback for the given scp.
1340 This should be called before cm_DirGetPage() is called per scp.
1349 scp->mx may be released
1352 cm_DirCheckStatus(cm_dirOp_t * op)
1356 lock_ObtainMutex(&op->scp->mx);
1357 code = cm_SyncOp(op->scp, NULL, op->userp, &op->req, PRSFS_LOOKUP,
1358 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1359 lock_ReleaseMutex(&op->scp->mx);
1361 osi_Log2(afsd_logp, "cm_DirCheckStatus for op 0x%p returning code 0x%x",
1367 /* Release a directory buffer that was obtained via a call to
1368 cm_DirGetPage() or any other function that returns a locked, held,
1369 directory page buffer.
1371 Called with scp->mx unlocked
1374 cm_DirReleasePage(cm_dirOp_t * op, cm_buf_t ** bufferpp, int modified)
1381 cm_DirOpDelBuffer(op, *bufferpp,
1382 ((modified ? DIROP_MODIFIED : 0)));
1383 buf_Release(*bufferpp);
1390 Returns the index'th directory page from scp. The userp and reqp
1391 will be used to fetch the buffer from the fileserver if necessary.
1392 If the call is successful, a locked and held cm_buf_t is returned
1393 via buferpp and a pointer to the directory page is returned via
1396 The returned buffer should be released via a call to
1397 cm_DirReleasePage() or by passing it into a subsequent call to
1398 cm_DirGetPage() for the *same* scp.
1400 If a *locked* buffer for the *same* scp is passed in via bufferpp
1401 to the function, it will check if the requested directory page is
1402 located in the specified buffer. If not, the buffer will be
1403 released and a new buffer returned that contains the requested
1406 Note: If a buffer is specified on entry via bufferpp, it is assumed
1407 that the buffer is unmodified. If the buffer is modified, it
1408 should be released via cm_DirReleasePage().
1412 If *bufferpp is non-NULL, then *bufferpp->mx is locked.
1416 If *bufferpp is non-NULL, then *bufferpp->mx is locked.
1419 scp->mx will be obtained and released
1423 cm_DirGetPage(cm_dirOp_t * op,
1424 long index, cm_buf_t ** bufferpp, void ** datapp)
1426 osi_hyper_t pageOffset; /* offset of the dir page from the
1427 start of the directory */
1428 osi_hyper_t bufferOffset; /* offset of the buffer from the start
1432 cm_buf_t * bufferp = NULL;
1434 void * datap = NULL;
1438 osi_Log2(afsd_logp, "cm_DirGetPage for op 0x%p, index %d", op, index);
1440 pageOffset = ConvertLongToLargeInteger(index * CM_DIR_PAGESIZE);
1441 bufferOffset.HighPart = pageOffset.HighPart;
1442 bufferOffset.LowPart = pageOffset.LowPart & ~(cm_data.buf_blockSize - 1);
1444 bufferp = *bufferpp;
1445 if (bufferp != NULL) {
1446 osi_assert(cm_FidCmp(&bufferp->fid, &op->scp->fid) == 0);
1448 thyper = bufferp->offset;
1451 if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
1455 buf_Release(bufferp);
1456 cm_DirOpDelBuffer(op, bufferp, 0);
1460 /* first check if we are already working with the buffer */
1461 if (cm_DirOpFindBuffer(op, bufferOffset, &bufferp)) {
1466 lock_ObtainRead(&op->scp->bufCreateLock);
1467 code = buf_Get(op->scp, &bufferOffset, &bufferp);
1468 lock_ReleaseRead(&op->scp->bufCreateLock);
1471 osi_Log1(afsd_logp, " buf_Get returned code 0x%x", code);
1476 osi_assert(bufferp != NULL);
1478 /* DirOpAddBuffer will obtain bufferp->mx if necessary */
1479 code = cm_DirOpAddBuffer(op, bufferp);
1482 /* for some reason, the buffer was rejected. We can't use
1483 this buffer, and since this is the only buffer we can
1484 potentially use, there's no recourse.*/
1485 buf_Release(bufferp);
1491 /* The code below is for making sure the buffer contains
1492 current data. This is a bad idea, since the whole point of
1493 doing directory updates locally is to avoid fetching all
1494 the data from the server. */
1496 lock_ObtainMutex(&op->scp->mx);
1497 code = cm_SyncOp(op->scp, bufferp, op->userp, &op->req, PRSFS_LOOKUP,
1498 CM_SCACHESYNC_NEEDCALLBACK |
1499 CM_SCACHESYNC_READ |
1500 CM_SCACHESYNC_BUFLOCKED);
1503 lock_ReleaseMutex(&op->scp->mx);
1507 cm_SyncOpDone(op->scp, bufferp,
1508 CM_SCACHESYNC_NEEDCALLBACK |
1509 CM_SCACHESYNC_READ |
1510 CM_SCACHESYNC_BUFLOCKED);
1512 if (cm_HaveBuffer(op->scp, bufferp, 1)) {
1513 lock_ReleaseMutex(&op->scp->mx);
1517 lock_ReleaseMutex(&bufferp->mx);
1518 code = cm_GetBuffer(op->scp, bufferp, NULL, op->userp, &op->req);
1519 lock_ReleaseMutex(&op->scp->mx);
1520 lock_ObtainMutex(&bufferp->mx);
1527 cm_DirOpDelBuffer(op, bufferp, 0);
1528 buf_Release(bufferp);
1537 /* now to figure out where the data is */
1538 thyper = LargeIntegerSubtract(pageOffset, bufferOffset);
1540 osi_assert(thyper.HighPart == 0);
1541 osi_assert(cm_data.buf_blockSize > thyper.LowPart &&
1542 cm_data.buf_blockSize - thyper.LowPart >= CM_DIR_PAGESIZE);
1544 datap = (void *) (((char *)bufferp->datap) + thyper.LowPart);
1549 /* also, if we are writing past EOF, we should make a note of the
1551 thyper = LargeIntegerAdd(pageOffset,
1552 ConvertLongToLargeInteger(CM_DIR_PAGESIZE));
1553 if (LargeIntegerLessThan(op->newLength, thyper)) {
1554 op->newLength = thyper;
1559 *bufferpp = bufferp;
1561 osi_Log1(afsd_logp, "cm_DirGetPage returning code 0x%x", code);
1568 cm_DirEntryListAdd(char * namep, cm_dirEntryList_t ** list)
1571 cm_dirEntryList_t * entry;
1573 len = strlen(namep);
1574 len += sizeof(cm_dirEntryList_t);
1576 entry = malloc(len);
1578 entry->nextp = *list;
1579 strcpy(entry->name, namep);
1585 cm_DirEntryListFree(cm_dirEntryList_t ** list)
1587 cm_dirEntryList_t * entry;
1588 cm_dirEntryList_t * next;
1590 for (entry = *list; entry; entry = next) {
1591 next = entry->nextp;