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>
23 /* Local static prototypes */
25 cm_DirGetBlob(cm_dirOp_t * op,
26 unsigned int blobno, cm_buf_t ** bufferpp, cm_dirEntry_t ** blobpp);
29 cm_DirFindItem(cm_dirOp_t * op,
31 cm_buf_t ** itembufpp, cm_dirEntry_t ** itempp,
32 cm_buf_t ** prevbufpp, unsigned short **previtempp);
35 cm_DirOpAddBuffer(cm_dirOp_t * op, cm_buf_t * buffer);
37 /* flags for cm_DirOpDelBuffer */
38 #define DIROP_MODIFIED 1
39 #define DIROP_SCPLOCKED 2
42 cm_DirOpDelBuffer(cm_dirOp_t * op, cm_buf_t * buffer, int flags);
45 cm_DirCheckStatus(cm_dirOp_t * op);
48 cm_DirReleasePage(cm_dirOp_t * op, cm_buf_t ** bufferpp, int modified);
51 cm_DirGetPage(cm_dirOp_t * op,
52 long index, cm_buf_t ** bufferpp, void ** datapp);
55 cm_DirFindBlobs(cm_dirOp_t * op, int nblobs);
58 cm_DirAddPage(cm_dirOp_t * op, int pageno);
61 cm_DirFreeBlobs(cm_dirOp_t * op, int firstblob, int nblobs);
64 /* compute how many 32 byte entries an AFS 3 dir requires for storing
68 cm_NameEntries(char *namep, long *lenp)
72 i = (long)strlen(namep) + 1;
74 return 1 + ((i+15) >> 5);
77 /* Create an entry in a file. Dir is a file representation, while
78 entry is a string name.
86 None of the directory buffers for op->scp should be locked by the
90 cm_DirCreateEntry(cm_dirOp_t * op, char *entry, cm_fid_t * cfid)
95 cm_dirEntry_t *ep = NULL;
96 cm_buf_t *entrybuf = NULL;
98 unsigned short *pp = NULL;
99 cm_buf_t *prevptrbuf = NULL;
101 cm_dirHeader_t *dhp = NULL;
102 cm_buf_t *dhpbuf = NULL;
106 /* check name quality */
110 osi_Log4(afsd_logp, "cm_DirCreateEntry for op 0x%p, name [%s] and fid[%d,%d]",
111 op, osi_LogSaveString(afsd_logp, entry), cfid->vnode, cfid->unique);
113 /* First check if file already exists. */
114 code = cm_DirFindItem(op,
119 cm_DirReleasePage(op, &entrybuf, FALSE);
120 cm_DirReleasePage(op, &prevptrbuf, FALSE);
124 blobs = cm_NameEntries(entry, NULL); /* number of entries required */
125 firstelt = cm_DirFindBlobs(op, blobs);
127 osi_Log0(afsd_logp, "cm_DirCreateEntry returning EFBIG");
128 return EFBIG; /* directory is full */
131 /* First, we fill in the directory entry. */
132 code = cm_DirGetBlob(op, firstelt, &entrybuf, &ep);
136 ep->flag = CM_DIR_FFIRST;
137 ep->fid.vnode = htonl(cfid->vnode);
138 ep->fid.unique = htonl(cfid->unique);
139 strcpy(ep->name, entry);
141 /* Now we just have to thread it on the hash table list. */
142 code = cm_DirGetPage(op, 0, &dhpbuf, &dhp);
144 cm_DirReleasePage(op, &entrybuf, TRUE);
148 i = cm_DirHash(entry);
150 ep->next = dhp->hashTable[i];
151 dhp->hashTable[i] = htons(firstelt);
153 cm_DirReleasePage(op, &dhpbuf, TRUE);
154 cm_DirReleasePage(op, &entrybuf, TRUE);
156 osi_Log0(afsd_logp, "cm_DirCreateEntry returning success");
161 /* Return the length of a directory in pages
164 op->scp->mx is locked
167 op->scp->mx is locked
169 The first directory page for op->scp should not be locked by the
173 cm_DirLength(cm_dirOp_t * op)
176 cm_dirHeader_t *dhp = NULL;
177 cm_buf_t *dhpbuf = NULL;
181 code = cm_DirGetPage(op, 0, &dhpbuf, &dhp);
185 if (dhp->header.pgcount != 0)
186 ctr = ntohs(dhp->header.pgcount);
188 /* old style, count the pages */
190 for (i = 0; i < CM_DIR_MAXPAGES; i++)
191 if (dhp->alloMap[i] != CM_DIR_EPP)
194 cm_DirReleasePage(op, &dhpbuf, FALSE);
195 return ctr * CM_DIR_PAGESIZE;
198 /* Delete a directory entry.
201 op->scp->mx is locked
204 op->scp->mx is locked
206 None of the directory buffers for op->scp should be locked by the
210 cm_DirDeleteEntry(cm_dirOp_t * op, char *entry)
212 /* Delete an entry from a directory, including update of all free
213 entry descriptors. */
216 cm_dirEntry_t *firstitem = NULL;
217 cm_buf_t *itembuf = NULL;
218 unsigned short *previtem = NULL;
219 cm_buf_t *pibuf = NULL;
225 osi_Log2(afsd_logp, "cm_DirDeleteEntry for op 0x%p, entry [%s]",
226 op, osi_LogSaveString(afsd_logp, entry));
228 code = cm_DirFindItem(op, entry,
229 &itembuf, &firstitem,
232 osi_Log0(afsd_logp, "cm_DirDeleteEntry returning ENOENT");
236 *previtem = firstitem->next;
237 cm_DirReleasePage(op, &pibuf, TRUE);
239 thyper = itembuf->offset;
240 thyper = LargeIntegerAdd(thyper,
241 ConvertLongToLargeInteger(((char *) firstitem) - itembuf->datap));
242 thyper = ExtendedLargeIntegerDivide(thyper, 32, &junk);
244 index = thyper.LowPart;
245 osi_assert(thyper.HighPart == 0);
247 nitems = cm_NameEntries(firstitem->name, NULL);
248 cm_DirReleasePage(op, &itembuf, FALSE);
250 cm_DirFreeBlobs(op, index, nitems);
252 osi_Log0(afsd_logp, "cm_DirDeleteEntry returning success");
257 /* Find a bunch of contiguous entries; at least nblobs in a row.
259 Called with op->scp->mx */
261 cm_DirFindBlobs(cm_dirOp_t * op, int nblobs)
266 cm_dirHeader_t *dhp = NULL;
267 cm_buf_t *dhpbuf = NULL;
268 int dhpModified = FALSE;
270 cm_pageHeader_t *pp = NULL;
271 cm_buf_t *pagebuf = NULL;
272 int pageModified = FALSE;
278 osi_Log2(afsd_logp, "cm_DirFindBlobs for op 0x%p, nblobs = %d",
281 code = cm_DirGetPage(op, 0, &dhpbuf, (void **) &dhp);
285 for (i = 0; i < CM_DIR_BIGMAXPAGES; i++) {
286 if (i >= CM_DIR_MAXPAGES || dhp->alloMap[i] >= nblobs) {
287 /* if page could contain enough entries */
288 /* If there are CM_DIR_EPP free entries, then the page is
289 not even allocated. */
290 if (i >= CM_DIR_MAXPAGES) {
292 /* this pages exists past the end of the old-style dir */
293 pgcount = ntohs(dhp->header.pgcount);
295 pgcount = CM_DIR_MAXPAGES;
296 dhp->header.pgcount = htons(pgcount);
300 if (i > pgcount - 1) {
301 /* this page is bigger than last allocated page */
302 cm_DirAddPage(op, i);
303 dhp->header.pgcount = htons(i + 1);
306 } else if (dhp->alloMap[i] == CM_DIR_EPP) {
307 /* Add the page to the directory. */
308 cm_DirAddPage(op, i);
309 dhp->alloMap[i] = CM_DIR_EPP - 1;
310 dhp->header.pgcount = htons(i + 1);
314 code = cm_DirGetPage(op, i, &pagebuf, &pp);
316 cm_DirReleasePage(op, &dhpbuf, dhpModified);
320 for (j = 0; j <= CM_DIR_EPP - nblobs; j++) {
322 for (k = 0; k < nblobs; k++)
323 if ((pp->freeBitmap[(j + k) >> 3] >> ((j + k) & 7)) & 1) {
333 /* Here we have the first index in j. We update the allocation maps
334 * and free up any resources we've got allocated. */
335 if (i < CM_DIR_MAXPAGES) {
336 dhp->alloMap[i] -= nblobs;
340 cm_DirReleasePage(op, &dhpbuf, dhpModified);
342 for (k = 0; k < nblobs; k++)
343 pp->freeBitmap[(j + k) >> 3] |= 1 << ((j + k) & 7);
345 cm_DirReleasePage(op, &pagebuf, TRUE);
347 osi_Log0(afsd_logp, "cm_DirFindBlobs returning success");
349 return j + i * CM_DIR_EPP;
351 cm_DirReleasePage(op, &pagebuf, pageModified);
355 /* If we make it here, the directory is full. */
356 osi_Log0(afsd_logp, "cm_DirFindBlobs directory is full");
357 cm_DirReleasePage(op, &dhpbuf, dhpModified);
361 /* Add a page to a directory.
363 Called with op->scp->mx
366 cm_DirAddPage(cm_dirOp_t * op, int pageno)
369 cm_pageHeader_t *pp = NULL;
370 cm_buf_t *pagebuf = NULL;
373 osi_Log2(afsd_logp, "cm_DirAddPage for op 0x%p, pageno=%d", op, pageno);
375 code = cm_DirGetPage(op, pageno, &pagebuf, (void **) &pp);
379 pp->tag = htons(1234);
382 pp->freeCount = CM_DIR_EPP - 1; /* The first dude is already allocated */
383 pp->freeBitmap[0] = 0x01;
384 for (i = 1; i < CM_DIR_EPP / 8; i++) /* It's a constant */
385 pp->freeBitmap[i] = 0;
387 cm_DirReleasePage(op, &pagebuf, TRUE);
389 osi_Log0(afsd_logp, "cm_DirAddPage returning success");
394 /* Free a whole bunch of directory entries.
396 Called with op->scp->mx
399 cm_DirFreeBlobs(cm_dirOp_t * op, int firstblob, int nblobs)
404 cm_dirHeader_t *dhp = NULL;
405 cm_buf_t *dhpbuf = NULL;
406 int dhpmodified = FALSE;
408 cm_pageHeader_t *pp = NULL;
409 cm_buf_t *pagebuf = NULL;
412 osi_Log3(afsd_logp, "cm_DirFreeBlobs for op 0x%p, firstblob=%d, nblobs=%d",
413 op, firstblob, nblobs);
415 page = firstblob / CM_DIR_EPP;
416 firstblob -= CM_DIR_EPP * page; /* convert to page-relative entry */
418 code = cm_DirGetPage(op, 0, &dhpbuf, &dhp);
422 if (page < CM_DIR_MAXPAGES) {
423 dhp->alloMap[page] += nblobs;
427 cm_DirReleasePage(op, &dhpbuf, dhpmodified);
429 code = cm_DirGetPage(op, page, &pagebuf, &pp);
431 for (i = 0; i < nblobs; i++)
432 pp->freeBitmap[(firstblob + i) >> 3] &=
433 ~(1 << ((firstblob + i) & 7));
434 cm_DirReleasePage(op, &pagebuf, TRUE);
437 osi_Log1(afsd_logp, "cm_DirFreeBlobs returning code 0x%x", code);
443 * Format an empty directory properly. Note that the first 13 entries in a
444 * directory header page are allocated, 1 to the page header, 4 to the
445 * allocation map and 8 to the hash table.
447 * Called with op->scp->mx
450 cm_DirMakeDir(cm_dirOp_t * op, cm_fid_t * me, cm_fid_t * parent)
453 cm_dirHeader_t *dhp = NULL;
454 cm_buf_t *dhpbuf = NULL;
458 osi_Log3(afsd_logp, "cm_DirMakeDir for op 0x%p, directory fid[%d, %d]",
459 op, me->vnode, me->unique);
460 osi_Log2(afsd_logp, " parent[%d, %d]",
461 parent->vnode, parent->unique);
463 code = cm_DirGetPage(op, 0, &dhpbuf, &dhp);
467 dhp->header.pgcount = htons(1);
468 dhp->header.tag = htons(1234);
469 dhp->header.freeCount = (CM_DIR_EPP - CM_DIR_DHE - 1);
470 dhp->header.freeBitmap[0] = 0xff;
471 dhp->header.freeBitmap[1] = 0x1f;
472 for (i = 2; i < CM_DIR_EPP / 8; i++)
473 dhp->header.freeBitmap[i] = 0;
474 dhp->alloMap[0] = (CM_DIR_EPP - CM_DIR_DHE - 1);
475 for (i = 1; i < CM_DIR_MAXPAGES; i++)
476 dhp->alloMap[i] = CM_DIR_EPP;
477 for (i = 0; i < CM_DIR_NHASHENT; i++)
478 dhp->hashTable[i] = 0;
480 cm_DirReleasePage(op, &dhpbuf, TRUE);
482 cm_DirCreateEntry(op, ".", me);
483 cm_DirCreateEntry(op, "..", parent); /* Virtue is its own .. */
485 osi_Log0(afsd_logp, "cm_DirMakeDir returning success");
490 /* Look up a file name in directory.
493 op->scp->mx is locked
496 op->scp->mx is locked
498 None of the directory buffers for op->scp should be locked by the
502 cm_DirLookup(cm_dirOp_t * op, char *entry, cm_fid_t * cfid)
504 cm_dirEntry_t *firstitem = NULL;
505 cm_buf_t *itembuf = NULL;
506 unsigned short *previtem = NULL;
507 cm_buf_t *pibuf = NULL;
511 osi_Log2(afsd_logp, "cm_DirLookup for op 0x%p, entry[%s]",
512 op, osi_LogSaveString(afsd_logp, entry));
514 code = cm_DirFindItem(op, entry,
515 &itembuf, &firstitem,
521 cm_DirReleasePage(op, &pibuf, FALSE);
523 cfid->cell = op->scp->fid.cell;
524 cfid->volume = op->scp->fid.volume;
525 cfid->vnode = ntohl(firstitem->fid.vnode);
526 cfid->unique = ntohl(firstitem->fid.unique);
528 cm_DirReleasePage(op, &itembuf, FALSE);
530 osi_Log2(afsd_logp, "cm_DirLookup returning fid[%d,%d]",
531 cfid->vnode, cfid->unique);
536 /* Look up a file name in directory.
539 op->scp->mx is locked
542 op->scp->mx is locked
544 None of the directory buffers for op->scp should be locked by the
548 cm_DirLookupOffset(cm_dirOp_t * op, char *entry, cm_fid_t *cfid, osi_hyper_t *offsetp)
550 cm_dirEntry_t *firstitem = NULL;
551 cm_buf_t *itembuf = NULL;
552 unsigned short *previtem = NULL;
553 cm_buf_t *pibuf = NULL;
557 osi_Log2(afsd_logp, "cm_DirLookupOffset for op 0x%p, entry[%s]",
558 op, osi_LogSaveString(afsd_logp, entry));
560 code = cm_DirFindItem(op, entry,
561 &itembuf, &firstitem,
566 cm_DirReleasePage(op, &pibuf, FALSE);
568 cfid->cell = op->scp->fid.cell;
569 cfid->volume = op->scp->fid.volume;
570 cfid->vnode = ntohl(firstitem->fid.vnode);
571 cfid->unique = ntohl(firstitem->fid.unique);
575 thyper = itembuf->offset;
576 thyper = LargeIntegerAdd(thyper,
577 ConvertLongToLargeInteger(((char *) firstitem) - itembuf->datap));
582 cm_DirReleasePage(op, &itembuf, FALSE);
584 osi_Log2(afsd_logp, "cm_DirLookupOffset returning fid[%d,%d]",
585 cfid->vnode, cfid->unique);
587 osi_Log2(afsd_logp, " offset [%x:%x]",
588 offsetp->HighPart, offsetp->LowPart);
594 /* Apply a function to every directory entry in a directory.
597 op->scp->mx is locked
600 op->scp->mx is locked
602 None of the directory buffers for op->scp should be locked by the
605 The hook function cannot modify or lock any directory buffers.
608 cm_DirApply(cm_dirOp_t * op, int (*hookproc) (void *, char *, long, long), void *hook)
610 /* Enumerate the contents of a directory. */
614 cm_dirHeader_t *dhp = NULL;
615 cm_buf_t *dhpbuf = NULL;
617 cm_dirEntry_t *ep = NULL;
618 cm_buf_t *epbuf = NULL;
622 code = cm_DirGetPage(op, 0, &dhpbuf, &dhp);
626 for (i = 0; i < CM_DIR_NHASHENT; i++) {
627 /* For each hash chain, enumerate everyone on the list. */
628 num = ntohs(dhp->hashTable[i]);
630 /* Walk down the hash table list. */
631 code = cm_DirGetBlob(op, num, &epbuf, &ep);
633 cm_DirReleasePage(op, &dhpbuf, FALSE);
637 num = ntohs(ep->next);
638 (*hookproc) (hook, ep->name, ntohl(ep->fid.vnode),
639 ntohl(ep->fid.unique));
641 cm_DirReleasePage(op, &epbuf, FALSE);
644 cm_DirReleasePage(op, &dhpbuf, FALSE);
649 /* Check if a directory is empty
652 op->scp->mx is locked
655 op->scp->mx is locked
657 None of the directory buffers for op->scp should be locked by the
661 cm_DirIsEmpty(cm_dirOp_t * op)
663 /* Enumerate the contents of a directory. */
667 cm_dirHeader_t *dhp = NULL;
668 cm_buf_t *dhpbuf = NULL;
670 cm_dirEntry_t *ep = NULL;
671 cm_buf_t *epbuf = NULL;
675 code = cm_DirGetPage(op, 0, &dhpbuf, &dhp);
679 for (i = 0; i < CM_DIR_NHASHENT; i++) {
680 /* For each hash chain, enumerate everyone on the list. */
681 num = ntohs(dhp->hashTable[i]);
684 /* Walk down the hash table list. */
685 code = cm_DirGetBlob(op, num, &epbuf, &ep);
689 if (strcmp(ep->name, "..") && strcmp(ep->name, ".")) {
690 cm_DirReleasePage(op, &epbuf, FALSE);
691 cm_DirReleasePage(op, &dhpbuf, FALSE);
694 num = ntohs(ep->next);
695 cm_DirReleasePage(op, &epbuf, FALSE);
698 cm_DirReleasePage(op, &dhpbuf, FALSE);
702 /* Return a pointer to an entry, given its number.
706 if *bufferpp != NULL, then *bufferpp->mx is locked
709 scp->mx may be unlocked
710 *bufferpp may be released
714 if *bufferpp != NULL, then *bufferpp->mx is locked
716 *bufferpp should be released via cm_DirReleasePage() or any other
717 *call that releases a directory buffer.
720 cm_DirGetBlob(cm_dirOp_t * op,
721 unsigned int blobno, cm_buf_t ** bufferpp, cm_dirEntry_t ** blobpp)
726 osi_Log2(afsd_logp, "cm_DirGetBlob for op 0x%p, blobno=%d",
729 code = cm_DirGetPage(op, blobno >> CM_DIR_LEPP,
730 bufferpp, (void **) &ep);
734 *blobpp = (cm_dirEntry_t *) (ep + 32 * (blobno & (CM_DIR_EPP - 1)));
740 cm_DirHash(char *string)
742 /* Hash a string to a number between 0 and NHASHENT. */
743 register unsigned char tc;
747 while ((tc = (*string++))) {
751 tval = hval & (CM_DIR_NHASHENT - 1);
755 tval = CM_DIR_NHASHENT - tval;
759 /* Find a directory entry, given its name. This entry returns a
760 * pointer to a locked buffer, and a pointer to a locked buffer (in
761 * previtem) referencing the found item (to aid the delete code). If
762 * no entry is found, however, no items are left locked, and a null
763 * pointer is returned instead.
772 cm_DirFindItem(cm_dirOp_t * op,
774 cm_buf_t ** itembufpp, cm_dirEntry_t ** itempp,
775 cm_buf_t ** prevbufpp, unsigned short **previtempp)
778 cm_dirHeader_t *dhp = NULL;
779 unsigned short *lp = NULL;
780 cm_dirEntry_t *tp = NULL;
781 cm_buf_t *hashbufp = NULL;
782 cm_buf_t *itembufp = NULL;
785 osi_Log2(afsd_logp, "cm_DirFindItem for op 0x%p, entry[%s]",
786 op, osi_LogSaveString(afsd_logp, ename));
788 i = cm_DirHash(ename);
790 if (op->scp->fileType != CM_SCACHETYPE_DIRECTORY) {
791 osi_Log0(afsd_logp, "cm_DirFindItem: The scp is not a directory");
792 return CM_ERROR_INVAL;
795 code = cm_DirGetPage(op, 0, &hashbufp, (void **) &dhp);
800 if (dhp->hashTable[i] == 0) {
802 osi_Log1(afsd_logp, "cm_DirFindItem: Hash bucket %d is empty", i);
803 cm_DirReleasePage(op, &hashbufp, FALSE);
807 code = cm_DirGetBlob(op,
808 (u_short) ntohs(dhp->hashTable[i]),
811 cm_DirReleasePage(op, &hashbufp, FALSE);
815 lp = &(dhp->hashTable[i]);
819 lp : pointer to blob number of entry we are looking at
820 hashbufp : buffer containing lp
821 tp : pointer to entry we are looking at
822 itembufp : buffer containing tp
825 /* Look at each hash conflict entry. */
826 if (!strcmp(ename, tp->name)) {
827 osi_Log0(afsd_logp, "cm_DirFindItem: returning success");
828 /* Found our entry. */
830 *prevbufpp = hashbufp;
832 *itembufpp = itembufp;
837 cm_DirReleasePage(op, &hashbufp, FALSE);
844 /* The end of the line */
845 osi_Log0(afsd_logp, "cm_DirFindItem: returning ENOENT");
846 cm_DirReleasePage(op, &hashbufp, FALSE);
850 code = cm_DirGetBlob(op,
851 (u_short) ntohs(*lp),
855 cm_DirReleasePage(op, &hashbufp, FALSE);
861 /* Begin a sequence of directory operations. scp->mx should be
865 cm_BeginDirOp(cm_scache_t * scp, cm_user_t * userp, cm_req_t * reqp,
871 osi_Log3(afsd_logp, "Beginning dirOp[0x%p] for scp[0x%p], userp[0x%p]",
874 memset(op, 0, sizeof(*op));
880 cm_InitReq(&op->req);
882 op->dirtyBufCount = 0;
885 for (i=0; i < CM_DIROP_MAXBUFFERS; i++) {
886 op->buffers[i].flags = 0;
889 code = cm_DirCheckStatus(op);
892 op->length = scp->length;
893 op->newLength = op->length;
894 op->dataVersion = scp->dataVersion;
895 op->newDataVersion = op->dataVersion;
903 /* Check if it is safe for us to perform local directory updates.
904 Called with scp->mx held. */
906 cm_CheckDirOpForSingleChange(cm_dirOp_t * op)
913 code = cm_DirCheckStatus(op);
916 op->dataVersion == op->scp->dataVersion - 1) {
917 /* only one set of changes happened between cm_BeginDirOp()
918 and this function. It is safe for us to perform local
920 op->newDataVersion = op->scp->dataVersion;
921 op->newLength = op->scp->serverLength;
923 osi_Log0(afsd_logp, "cm_CheckDirOpForSingleChange succeeded");
929 "cm_CheckDirOpForSingleChange failed. code=0x%x, old dv=%d, new dv=%d",
930 code, op->dataVersion, op->scp->dataVersion);
934 /* End a sequence of directory operations. Called with op->scp->mx
937 cm_EndDirOp(cm_dirOp_t * op)
944 osi_Log2(afsd_logp, "Ending dirOp 0x%p with %d dirty buffer releases",
945 op, op->dirtyBufCount);
947 if (op->dirtyBufCount > 0) {
948 /* we made changes. We should go through the list of buffers
949 and update the dataVersion for each. */
951 lock_ReleaseMutex(&op->scp->mx);
952 code = buf_ForceDataVersion(op->scp, op->dataVersion, op->newDataVersion);
953 lock_ObtainMutex(&op->scp->mx);
957 cm_ReleaseSCache(op->scp);
961 cm_ReleaseUser(op->userp);
964 osi_assertx(op->nBuffers == 0, "Buffer leak after dirOp termination");
969 /* NOTE: Called without scp->mx and without bufferp->mx */
971 cm_DirOpAddBuffer(cm_dirOp_t * op, cm_buf_t * bufferp)
976 osi_Log2(afsd_logp, "cm_DirOpAddBuffer for op 0x%p, buffer %p", op, bufferp);
981 for (i=0; i < CM_DIROP_MAXBUFFERS; i++) {
982 if ((op->buffers[i].flags & CM_DIROPBUFF_INUSE) &&
983 op->buffers[i].bufferp == bufferp) {
988 if (i < CM_DIROP_MAXBUFFERS) {
989 /* we already have this buffer on our list */
991 op->buffers[i].refcount++;
993 "cm_DirOpAddBuffer: the buffer is already listed for the dirOp");
996 /* we have to add a new buffer */
997 osi_assertx(op->nBuffers < CM_DIROP_MAXBUFFERS - 1,
998 "DirOp has exceeded CM_DIROP_MAXBUFFERS buffers");
1000 for (i=0; i < CM_DIROP_MAXBUFFERS; i++) {
1001 if (!(op->buffers[i].flags & CM_DIROPBUFF_INUSE))
1005 osi_assert(i < CM_DIROP_MAXBUFFERS);
1007 lock_ObtainMutex(&bufferp->mx);
1008 lock_ObtainMutex(&op->scp->mx);
1010 /* Make sure we are synchronized. */
1011 code = cm_SyncOp(op->scp, bufferp, op->userp, &op->req, PRSFS_LOOKUP,
1012 CM_SCACHESYNC_NEEDCALLBACK |
1013 CM_SCACHESYNC_WRITE |
1014 CM_SCACHESYNC_BUFLOCKED);
1017 bufferp->dataVersion != op->dataVersion) {
1019 osi_Log2(afsd_logp, "cm_DirOpAddBuffer: buffer version mismatch. buf ver = %d. want %d", bufferp->dataVersion, op->dataVersion);
1021 cm_SyncOpDone(op->scp, bufferp,
1022 CM_SCACHESYNC_NEEDCALLBACK |
1023 CM_SCACHESYNC_WRITE |
1024 CM_SCACHESYNC_BUFLOCKED);
1026 code = CM_ERROR_INVAL;
1029 lock_ReleaseMutex(&op->scp->mx);
1030 lock_ReleaseMutex(&bufferp->mx);
1033 osi_Log1(afsd_logp, "cm_DirOpAddBuffer: failed to sync buffer. code=0x%x",
1039 op->buffers[i].bufferp = bufferp;
1040 op->buffers[i].refcount = 1; /* start with one ref */
1041 op->buffers[i].flags = CM_DIROPBUFF_INUSE;
1045 osi_Log0(afsd_logp, "cm_DirOpAddBuffer: returning success");
1051 /* Note: Called without op->scp->mx */
1053 cm_DirOpFindBuffer(cm_dirOp_t * op, osi_hyper_t offset, cm_buf_t ** bufferpp)
1057 for (i=0; i < CM_DIROP_MAXBUFFERS; i++) {
1058 if ((op->buffers[i].flags & CM_DIROPBUFF_INUSE) &&
1059 LargeIntegerEqualTo(op->buffers[i].bufferp->offset, offset))
1063 if (i < CM_DIROP_MAXBUFFERS) {
1065 op->buffers[i].refcount++;
1066 buf_Hold(op->buffers[i].bufferp);
1067 *bufferpp = op->buffers[i].bufferp;
1069 osi_Log2(afsd_logp, "cm_DirOpFindBuffer: found buffer for offset [%x:%x]",
1070 offset.HighPart, offset.LowPart);
1074 osi_Log2(afsd_logp, "cm_DirOpFindBuffer: buffer not found for offset [%x:%x]",
1075 offset.HighPart, offset.LowPart);
1080 /* NOTE: called with scp->mx NOT held */
1082 cm_DirOpDelBuffer(cm_dirOp_t * op, cm_buf_t * bufferp, int flags)
1086 osi_Log3(afsd_logp, "cm_DirOpDelBuffer for op 0x%p, buffer 0x%p, flags=%d",
1087 op, bufferp, flags);
1089 for (i=0; i < CM_DIROP_MAXBUFFERS; i++) {
1090 if ((op->buffers[i].flags & CM_DIROPBUFF_INUSE) &&
1091 op->buffers[i].bufferp == bufferp)
1095 if (i < CM_DIROP_MAXBUFFERS) {
1097 if (flags & DIROP_MODIFIED)
1098 op->dirtyBufCount++;
1100 osi_assert(op->buffers[i].refcount > 0);
1101 op->buffers[i].refcount --;
1103 if (op->buffers[i].refcount == 0) {
1104 /* this was the last reference we had */
1106 osi_Log0(afsd_logp, "cm_DirOpDelBuffer: releasing buffer");
1108 /* if this buffer was modified, then we update the data
1109 version of the buffer with the data version of the
1111 if (!(flags & DIROP_SCPLOCKED)) {
1112 lock_ObtainMutex(&op->scp->mx);
1115 /* first make sure that the buffer is idle. It should
1116 have been idle all along. */
1117 osi_assertx((bufferp->cmFlags & (CM_BUF_CMFETCHING |
1118 CM_BUF_CMSTORING)) == 0,
1119 "Buffer is not idle while performing dirOp");
1121 cm_SyncOpDone(op->scp, bufferp,
1122 CM_SCACHESYNC_NEEDCALLBACK |
1123 CM_SCACHESYNC_WRITE);
1126 osi_assert(bufferp->dataVersion == op->dataVersion);
1129 lock_ReleaseMutex(&op->scp->mx);
1131 lock_ObtainMutex(&bufferp->mx);
1133 if (flags & DIROP_SCPLOCKED) {
1134 lock_ObtainMutex(&op->scp->mx);
1137 if (flags & DIROP_MODIFIED) {
1138 /* We don't update the dataversion here. Instead we
1139 wait until the dirOp is completed and then flip the
1140 dataversion on all the buffers in one go.
1141 Otherwise we won't know if the dataversion is
1142 current because it was fetched from the server or
1143 because we touched it during the dirOp. */
1145 if (bufferp->userp != op->userp) {
1146 if (bufferp->userp != NULL)
1147 cm_ReleaseUser(bufferp->userp);
1148 cm_HoldUser(op->userp);
1149 bufferp->userp = op->userp;
1153 lock_ReleaseMutex(&bufferp->mx);
1155 op->buffers[i].bufferp = NULL;
1156 buf_Release(bufferp);
1157 op->buffers[i].flags = 0;
1163 /* we have other references to this buffer. so we have to
1169 osi_Log0(afsd_logp, "cm_DirOpDelBuffer: buffer not found");
1170 osi_assertx(FALSE, "Attempt to delete a non-existent buffer from a dirOp");
1175 /* Check if we have current status and a callback for the given scp.
1176 This should be called before cm_DirGetPage() is called per scp.
1185 scp->mx may be released
1188 cm_DirCheckStatus(cm_dirOp_t * op)
1192 code = cm_SyncOp(op->scp, NULL, op->userp, &op->req, PRSFS_LOOKUP,
1193 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1195 osi_Log2(afsd_logp, "cm_DirCheckStatus for op 0x%p returning code 0x%x",
1201 /* Release a directory buffer that was obtained via a call to
1202 cm_DirGetPage() or any other function that returns a locked, held,
1203 directory page buffer.
1205 Called with scp->mx held
1208 cm_DirReleasePage(cm_dirOp_t * op, cm_buf_t ** bufferpp, int modified)
1215 cm_DirOpDelBuffer(op, *bufferpp,
1216 ((modified ? DIROP_MODIFIED : 0) | DIROP_SCPLOCKED));
1217 buf_Release(*bufferpp);
1224 Returns the index'th directory page from scp. The userp and reqp
1225 will be used to fetch the buffer from the fileserver if necessary.
1226 If the call is successful, a locked and held cm_buf_t is returned
1227 via buferpp and a pointer to the directory page is returned via
1230 The returned buffer should be released via a call to
1231 cm_DirReleasePage() or by passing it into a subsequent call to
1232 cm_DirGetPage() for the *same* scp.
1234 If a *locked* buffer for the *same* scp is passed in via bufferpp
1235 to the function, it will check if the requested directory page is
1236 located in the specified buffer. If not, the buffer will be
1237 released and a new buffer returned that contains the requested
1240 Note: If a buffer is specified on entry via bufferpp, it is assumed
1241 that the buffer is unmodified. If the buffer is modified, it
1242 should be released via cm_DirReleasePage().
1246 If *bufferpp is non-NULL, then *bufferpp->mx is locked.
1250 If *bufferpp is non-NULL, then *bufferpp->mx is locked.
1253 scp->mx will be released
1257 cm_DirGetPage(cm_dirOp_t * op,
1258 long index, cm_buf_t ** bufferpp, void ** datapp)
1260 osi_hyper_t pageOffset; /* offset of the dir page from the
1261 start of the directory */
1262 osi_hyper_t bufferOffset; /* offset of the buffer from the start
1266 cm_buf_t * bufferp = NULL;
1268 void * datap = NULL;
1272 osi_Log2(afsd_logp, "cm_DirGetPage for op 0x%p, index %d", op, index);
1274 pageOffset = ConvertLongToLargeInteger(index * CM_DIR_PAGESIZE);
1275 bufferOffset.HighPart = pageOffset.HighPart;
1276 bufferOffset.LowPart = pageOffset.LowPart & ~(cm_data.buf_blockSize - 1);
1278 bufferp = *bufferpp;
1279 if (bufferp != NULL) {
1280 osi_assert(cm_FidCmp(&bufferp->fid, &op->scp->fid) == 0);
1282 thyper = bufferp->offset;
1285 lock_ReleaseMutex(&op->scp->mx);
1287 if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
1291 buf_Release(bufferp);
1292 cm_DirOpDelBuffer(op, bufferp, 0);
1296 /* first check if we are already working with the buffer */
1297 if (cm_DirOpFindBuffer(op, bufferOffset, &bufferp)) {
1302 lock_ObtainRead(&op->scp->bufCreateLock);
1303 code = buf_Get(op->scp, &bufferOffset, &bufferp);
1304 lock_ReleaseRead(&op->scp->bufCreateLock);
1307 osi_Log1(afsd_logp, " buf_Get returned code 0x%x", code);
1312 osi_assert(bufferp != NULL);
1314 /* DirOpAddBuffer will obtain bufferp->mx if necessary */
1315 code = cm_DirOpAddBuffer(op, bufferp);
1318 /* for some reason, the buffer was rejected. We can't use
1319 this buffer, and since this is the only buffer we can
1320 potentially use, there's no recourse.*/
1321 buf_Release(bufferp);
1327 /* The code below is for making sure the buffer contains
1328 current data. This is a bad idea, since the whole point of
1329 doing directory updates locally is to avoid fetching all
1330 the data from the server. */
1332 lock_ObtainMutex(&op->scp->mx);
1333 code = cm_SyncOp(op->scp, bufferp, op->userp, &op->req, PRSFS_LOOKUP,
1334 CM_SCACHESYNC_NEEDCALLBACK |
1335 CM_SCACHESYNC_READ |
1336 CM_SCACHESYNC_BUFLOCKED);
1339 lock_ReleaseMutex(&op->scp->mx);
1343 cm_SyncOpDone(op->scp, bufferp,
1344 CM_SCACHESYNC_NEEDCALLBACK |
1345 CM_SCACHESYNC_READ |
1346 CM_SCACHESYNC_BUFLOCKED);
1348 if (cm_HaveBuffer(op->scp, bufferp, 1)) {
1349 lock_ReleaseMutex(&op->scp->mx);
1353 lock_ReleaseMutex(&bufferp->mx);
1354 code = cm_GetBuffer(op->scp, bufferp, NULL, op->userp, &op->req);
1355 lock_ReleaseMutex(&op->scp->mx);
1356 lock_ObtainMutex(&bufferp->mx);
1363 cm_DirOpDelBuffer(op, bufferp, 0);
1364 buf_Release(bufferp);
1373 /* now to figure out where the data is */
1374 thyper = LargeIntegerSubtract(pageOffset, bufferOffset);
1376 osi_assert(thyper.HighPart == 0);
1377 osi_assert(cm_data.buf_blockSize > thyper.LowPart &&
1378 cm_data.buf_blockSize - thyper.LowPart >= CM_DIR_PAGESIZE);
1380 datap = (void *) (((char *)bufferp->datap) + thyper.LowPart);
1385 /* also, if we are writing past EOF, we should make a note of the
1387 thyper = LargeIntegerAdd(pageOffset,
1388 ConvertLongToLargeInteger(CM_DIR_PAGESIZE));
1389 if (LargeIntegerLessThan(op->newLength, thyper)) {
1390 op->newLength = thyper;
1395 *bufferpp = bufferp;
1397 lock_ObtainMutex(&op->scp->mx);
1399 osi_Log1(afsd_logp, "cm_DirGetPage returning code 0x%x", code);
1406 cm_DirEntryListAdd(char * namep, cm_dirEntryList_t ** list)
1409 cm_dirEntryList_t * entry;
1411 len = strlen(namep);
1412 len += sizeof(cm_dirEntryList_t);
1414 entry = malloc(len);
1416 entry->nextp = *list;
1417 strcpy(entry->name, namep);
1423 cm_DirEntryListFree(cm_dirEntryList_t ** list)
1425 cm_dirEntryList_t * entry;
1426 cm_dirEntryList_t * next;
1428 for (entry = *list; entry; entry = next) {
1429 next = entry->nextp;