#include <rx/rx.h>
+afs_int32 DErrno;
+
+/* Local static prototypes */
+static long
+cm_DirGetBlob(cm_dirOp_t * op,
+ unsigned int blobno, cm_buf_t ** bufferpp, cm_dirEntry_t ** blobpp);
+
+static long
+cm_DirFindItem(cm_dirOp_t * op,
+ char *ename,
+ cm_buf_t ** itembufpp, cm_dirEntry_t ** itempp,
+ cm_buf_t ** prevbufpp, unsigned short **previtempp);
+
+static long
+cm_DirOpAddBuffer(cm_dirOp_t * op, cm_buf_t * buffer);
+
+/* flags for cm_DirOpDelBuffer */
+#define DIROP_MODIFIED 1
+#define DIROP_SCPLOCKED 2
+
+static int
+cm_DirOpDelBuffer(cm_dirOp_t * op, cm_buf_t * buffer, int flags);
+
+static long
+cm_DirCheckStatus(cm_dirOp_t * op);
+
+static long
+cm_DirReleasePage(cm_dirOp_t * op, cm_buf_t ** bufferpp, int modified);
+
+static long
+cm_DirGetPage(cm_dirOp_t * op,
+ long index, cm_buf_t ** bufferpp, void ** datapp);
+
+static long
+cm_DirFindBlobs(cm_dirOp_t * op, int nblobs);
+
+static long
+cm_DirAddPage(cm_dirOp_t * op, int pageno);
+
+static long
+cm_DirFreeBlobs(cm_dirOp_t * op, int firstblob, int nblobs);
+
+
/* compute how many 32 byte entries an AFS 3 dir requires for storing
* the specified name.
*/
-long cm_NameEntries(char *namep, long *lenp)
+long
+cm_NameEntries(char *namep, long *lenp)
{
long i;
- i = (long)strlen(namep);
+ i = (long)strlen(namep) + 1;
if (lenp) *lenp = i;
- return 1+((i+16)>>5);
+ return 1 + ((i+15) >> 5);
+}
+
+/* Create an entry in a file. Dir is a file representation, while
+ entry is a string name.
+
+ On entry:
+ op->scp->mx is locked
+
+ On exit:
+ op->scp->mx is locked
+
+ None of the directory buffers for op->scp should be locked by the
+ calling thread.
+*/
+long
+cm_DirCreateEntry(cm_dirOp_t * op, char *entry, cm_fid_t * cfid)
+{
+ int blobs, firstelt;
+ int i;
+
+ cm_dirEntry_t *ep = NULL;
+ cm_buf_t *entrybuf = NULL;
+
+ unsigned short *pp = NULL;
+ cm_buf_t *prevptrbuf = NULL;
+
+ cm_dirHeader_t *dhp = NULL;
+ cm_buf_t *dhpbuf = NULL;
+
+ long code = 0;
+
+ /* check name quality */
+ if (*entry == 0)
+ return EINVAL;
+
+ osi_Log4(afsd_logp, "cm_DirCreateEntry for op 0x%p, name [%s] and fid[%d,%d]",
+ op, osi_LogSaveString(afsd_logp, entry), cfid->vnode, cfid->unique);
+
+ /* First check if file already exists. */
+ code = cm_DirFindItem(op,
+ entry,
+ &entrybuf, &ep,
+ &prevptrbuf, &pp);
+ if (code == 0) {
+ cm_DirReleasePage(op, &entrybuf, FALSE);
+ cm_DirReleasePage(op, &prevptrbuf, FALSE);
+ return EEXIST;
+ }
+
+ blobs = cm_NameEntries(entry, NULL); /* number of entries required */
+ firstelt = cm_DirFindBlobs(op, blobs);
+ if (firstelt < 0) {
+ osi_Log0(afsd_logp, "cm_DirCreateEntry returning EFBIG");
+ return EFBIG; /* directory is full */
+ }
+
+ /* First, we fill in the directory entry. */
+ code = cm_DirGetBlob(op, firstelt, &entrybuf, &ep);
+ if (code != 0)
+ return EIO;
+
+ ep->flag = CM_DIR_FFIRST;
+ ep->fid.vnode = htonl(cfid->vnode);
+ ep->fid.unique = htonl(cfid->unique);
+ strcpy(ep->name, entry);
+
+ /* Now we just have to thread it on the hash table list. */
+ code = cm_DirGetPage(op, 0, &dhpbuf, &dhp);
+ if (code != 0) {
+ cm_DirReleasePage(op, &entrybuf, TRUE);
+ return EIO;
+ }
+
+ i = cm_DirHash(entry);
+
+ ep->next = dhp->hashTable[i];
+ dhp->hashTable[i] = htons(firstelt);
+
+ cm_DirReleasePage(op, &dhpbuf, TRUE);
+ cm_DirReleasePage(op, &entrybuf, TRUE);
+
+ osi_Log0(afsd_logp, "cm_DirCreateEntry returning success");
+
+ return 0;
+}
+
+/* Return the length of a directory in pages
+
+ On entry:
+ op->scp->mx is locked
+
+ On exit:
+ op->scp->mx is locked
+
+ The first directory page for op->scp should not be locked by the
+ calling thread.
+*/
+int
+cm_DirLength(cm_dirOp_t * op)
+{
+ int i, ctr;
+ cm_dirHeader_t *dhp = NULL;
+ cm_buf_t *dhpbuf = NULL;
+
+ long code;
+
+ code = cm_DirGetPage(op, 0, &dhpbuf, &dhp);
+ if (code != 0)
+ return 0;
+
+ if (dhp->header.pgcount != 0)
+ ctr = ntohs(dhp->header.pgcount);
+ else {
+ /* old style, count the pages */
+ ctr = 0;
+ for (i = 0; i < CM_DIR_MAXPAGES; i++)
+ if (dhp->alloMap[i] != CM_DIR_EPP)
+ ctr++;
+ }
+ cm_DirReleasePage(op, &dhpbuf, FALSE);
+ return ctr * CM_DIR_PAGESIZE;
+}
+
+/* Delete a directory entry.
+
+ On entry:
+ op->scp->mx is locked
+
+ On exit:
+ op->scp->mx is locked
+
+ None of the directory buffers for op->scp should be locked by the
+ calling thread.
+ */
+int
+cm_DirDeleteEntry(cm_dirOp_t * op, char *entry)
+{
+ /* Delete an entry from a directory, including update of all free
+ entry descriptors. */
+
+ int nitems, index;
+ cm_dirEntry_t *firstitem = NULL;
+ cm_buf_t *itembuf = NULL;
+ unsigned short *previtem = NULL;
+ cm_buf_t *pibuf = NULL;
+ osi_hyper_t thyper;
+ unsigned long junk;
+
+ long code;
+
+ osi_Log2(afsd_logp, "cm_DirDeleteEntry for op 0x%p, entry [%s]",
+ op, osi_LogSaveString(afsd_logp, entry));
+
+ code = cm_DirFindItem(op, entry,
+ &itembuf, &firstitem,
+ &pibuf, &previtem);
+ if (code != 0) {
+ osi_Log0(afsd_logp, "cm_DirDeleteEntry returning ENOENT");
+ return ENOENT;
+ }
+
+ *previtem = firstitem->next;
+ cm_DirReleasePage(op, &pibuf, TRUE);
+
+ thyper = itembuf->offset;
+ thyper = LargeIntegerAdd(thyper,
+ ConvertLongToLargeInteger(((char *) firstitem) - itembuf->datap));
+ thyper = ExtendedLargeIntegerDivide(thyper, 32, &junk);
+
+ index = thyper.LowPart;
+ osi_assert(thyper.HighPart == 0);
+
+ nitems = cm_NameEntries(firstitem->name, NULL);
+ cm_DirReleasePage(op, &itembuf, FALSE);
+
+ cm_DirFreeBlobs(op, index, nitems);
+
+ osi_Log0(afsd_logp, "cm_DirDeleteEntry returning success");
+
+ return 0;
+}
+
+/* Find a bunch of contiguous entries; at least nblobs in a row.
+
+ Called with op->scp->mx */
+static long
+cm_DirFindBlobs(cm_dirOp_t * op, int nblobs)
+{
+ int i, j, k;
+ int failed = 0;
+
+ cm_dirHeader_t *dhp = NULL;
+ cm_buf_t *dhpbuf = NULL;
+ int dhpModified = FALSE;
+
+ cm_pageHeader_t *pp = NULL;
+ cm_buf_t *pagebuf = NULL;
+ int pageModified = FALSE;
+
+ int pgcount;
+
+ long code;
+
+ osi_Log2(afsd_logp, "cm_DirFindBlobs for op 0x%p, nblobs = %d",
+ op, nblobs);
+
+ code = cm_DirGetPage(op, 0, &dhpbuf, (void **) &dhp);
+ if (code)
+ return -1;
+
+ for (i = 0; i < CM_DIR_BIGMAXPAGES; i++) {
+ if (i >= CM_DIR_MAXPAGES || dhp->alloMap[i] >= nblobs) {
+ /* if page could contain enough entries */
+ /* If there are CM_DIR_EPP free entries, then the page is
+ not even allocated. */
+ if (i >= CM_DIR_MAXPAGES) {
+
+ /* this pages exists past the end of the old-style dir */
+ pgcount = ntohs(dhp->header.pgcount);
+ if (pgcount == 0) {
+ pgcount = CM_DIR_MAXPAGES;
+ dhp->header.pgcount = htons(pgcount);
+ dhpModified = TRUE;
+ }
+
+ if (i > pgcount - 1) {
+ /* this page is bigger than last allocated page */
+ cm_DirAddPage(op, i);
+ dhp->header.pgcount = htons(i + 1);
+ dhpModified = TRUE;
+ }
+ } else if (dhp->alloMap[i] == CM_DIR_EPP) {
+ /* Add the page to the directory. */
+ cm_DirAddPage(op, i);
+ dhp->alloMap[i] = CM_DIR_EPP - 1;
+ dhp->header.pgcount = htons(i + 1);
+ dhpModified = TRUE;
+ }
+
+ code = cm_DirGetPage(op, i, &pagebuf, &pp);
+ if (code) {
+ cm_DirReleasePage(op, &dhpbuf, dhpModified);
+ break;
+ }
+
+ for (j = 0; j <= CM_DIR_EPP - nblobs; j++) {
+ failed = 0;
+ for (k = 0; k < nblobs; k++)
+ if ((pp->freeBitmap[(j + k) >> 3] >> ((j + k) & 7)) & 1) {
+ failed = 1;
+ break;
+ }
+ if (!failed)
+ break;
+ failed = 1;
+ }
+
+ if (!failed) {
+ /* Here we have the first index in j. We update the allocation maps
+ * and free up any resources we've got allocated. */
+ if (i < CM_DIR_MAXPAGES) {
+ dhp->alloMap[i] -= nblobs;
+ dhpModified = TRUE;
+ }
+
+ cm_DirReleasePage(op, &dhpbuf, dhpModified);
+
+ for (k = 0; k < nblobs; k++)
+ pp->freeBitmap[(j + k) >> 3] |= 1 << ((j + k) & 7);
+
+ cm_DirReleasePage(op, &pagebuf, TRUE);
+
+ osi_Log0(afsd_logp, "cm_DirFindBlobs returning success");
+
+ return j + i * CM_DIR_EPP;
+ }
+ cm_DirReleasePage(op, &pagebuf, pageModified);
+ }
+ }
+
+ /* If we make it here, the directory is full. */
+ osi_Log0(afsd_logp, "cm_DirFindBlobs directory is full");
+ cm_DirReleasePage(op, &dhpbuf, dhpModified);
+ return -1;
+}
+
+/* Add a page to a directory.
+
+ Called with op->scp->mx
+*/
+static long
+cm_DirAddPage(cm_dirOp_t * op, int pageno)
+{
+ int i;
+ cm_pageHeader_t *pp = NULL;
+ cm_buf_t *pagebuf = NULL;
+ long code = 0;
+
+ osi_Log2(afsd_logp, "cm_DirAddPage for op 0x%p, pageno=%d", op, pageno);
+
+ code = cm_DirGetPage(op, pageno, &pagebuf, (void **) &pp);
+ if (code != 0)
+ return code;
+
+ pp->tag = htons(1234);
+ if (pageno > 0)
+ pp->pgcount = 0;
+ pp->freeCount = CM_DIR_EPP - 1; /* The first dude is already allocated */
+ pp->freeBitmap[0] = 0x01;
+ for (i = 1; i < CM_DIR_EPP / 8; i++) /* It's a constant */
+ pp->freeBitmap[i] = 0;
+
+ cm_DirReleasePage(op, &pagebuf, TRUE);
+
+ osi_Log0(afsd_logp, "cm_DirAddPage returning success");
+
+ return code;
+}
+
+/* Free a whole bunch of directory entries.
+
+ Called with op->scp->mx
+*/
+static long
+cm_DirFreeBlobs(cm_dirOp_t * op, int firstblob, int nblobs)
+{
+ int i;
+ int page;
+
+ cm_dirHeader_t *dhp = NULL;
+ cm_buf_t *dhpbuf = NULL;
+ int dhpmodified = FALSE;
+
+ cm_pageHeader_t *pp = NULL;
+ cm_buf_t *pagebuf = NULL;
+ long code = 0;
+
+ osi_Log3(afsd_logp, "cm_DirFreeBlobs for op 0x%p, firstblob=%d, nblobs=%d",
+ op, firstblob, nblobs);
+
+ page = firstblob / CM_DIR_EPP;
+ firstblob -= CM_DIR_EPP * page; /* convert to page-relative entry */
+
+ code = cm_DirGetPage(op, 0, &dhpbuf, &dhp);
+ if (code)
+ return code;
+
+ if (page < CM_DIR_MAXPAGES) {
+ dhp->alloMap[page] += nblobs;
+ dhpmodified = TRUE;
+ }
+
+ cm_DirReleasePage(op, &dhpbuf, dhpmodified);
+
+ code = cm_DirGetPage(op, page, &pagebuf, &pp);
+ if (code == 0) {
+ for (i = 0; i < nblobs; i++)
+ pp->freeBitmap[(firstblob + i) >> 3] &=
+ ~(1 << ((firstblob + i) & 7));
+ cm_DirReleasePage(op, &pagebuf, TRUE);
+ }
+
+ osi_Log1(afsd_logp, "cm_DirFreeBlobs returning code 0x%x", code);
+
+ return code;
+}
+
+/*
+ * Format an empty directory properly. Note that the first 13 entries in a
+ * directory header page are allocated, 1 to the page header, 4 to the
+ * allocation map and 8 to the hash table.
+ *
+ * Called with op->scp->mx
+ */
+int
+cm_DirMakeDir(cm_dirOp_t * op, cm_fid_t * me, cm_fid_t * parent)
+{
+ int i;
+ cm_dirHeader_t *dhp = NULL;
+ cm_buf_t *dhpbuf = NULL;
+
+ long code;
+
+ osi_Log3(afsd_logp, "cm_DirMakeDir for op 0x%p, directory fid[%d, %d]",
+ op, me->vnode, me->unique);
+ osi_Log2(afsd_logp, " parent[%d, %d]",
+ parent->vnode, parent->unique);
+
+ code = cm_DirGetPage(op, 0, &dhpbuf, &dhp);
+ if (code)
+ return 1;
+
+ dhp->header.pgcount = htons(1);
+ dhp->header.tag = htons(1234);
+ dhp->header.freeCount = (CM_DIR_EPP - CM_DIR_DHE - 1);
+ dhp->header.freeBitmap[0] = 0xff;
+ dhp->header.freeBitmap[1] = 0x1f;
+ for (i = 2; i < CM_DIR_EPP / 8; i++)
+ dhp->header.freeBitmap[i] = 0;
+ dhp->alloMap[0] = (CM_DIR_EPP - CM_DIR_DHE - 1);
+ for (i = 1; i < CM_DIR_MAXPAGES; i++)
+ dhp->alloMap[i] = CM_DIR_EPP;
+ for (i = 0; i < CM_DIR_NHASHENT; i++)
+ dhp->hashTable[i] = 0;
+
+ cm_DirReleasePage(op, &dhpbuf, TRUE);
+
+ cm_DirCreateEntry(op, ".", me);
+ cm_DirCreateEntry(op, "..", parent); /* Virtue is its own .. */
+
+ osi_Log0(afsd_logp, "cm_DirMakeDir returning success");
+
+ return 0;
+}
+
+/* Look up a file name in directory.
+
+ On entry:
+ op->scp->mx is locked
+
+ On exit:
+ op->scp->mx is locked
+
+ None of the directory buffers for op->scp should be locked by the
+ calling thread.
+*/
+int
+cm_DirLookup(cm_dirOp_t * op, char *entry, cm_fid_t * cfid)
+{
+ cm_dirEntry_t *firstitem = NULL;
+ cm_buf_t *itembuf = NULL;
+ unsigned short *previtem = NULL;
+ cm_buf_t *pibuf = NULL;
+
+ long code;
+
+ osi_Log2(afsd_logp, "cm_DirLookup for op 0x%p, entry[%s]",
+ op, osi_LogSaveString(afsd_logp, entry));
+
+ code = cm_DirFindItem(op, entry,
+ &itembuf, &firstitem,
+ &pibuf, &previtem);
+ if (code != 0) {
+ return ENOENT;
+ }
+
+ cm_DirReleasePage(op, &pibuf, FALSE);
+
+ cfid->cell = op->scp->fid.cell;
+ cfid->volume = op->scp->fid.volume;
+ cfid->vnode = ntohl(firstitem->fid.vnode);
+ cfid->unique = ntohl(firstitem->fid.unique);
+
+ cm_DirReleasePage(op, &itembuf, FALSE);
+
+ osi_Log2(afsd_logp, "cm_DirLookup returning fid[%d,%d]",
+ cfid->vnode, cfid->unique);
+
+ return 0;
+}
+
+/* Look up a file name in directory.
+
+ On entry:
+ op->scp->mx is locked
+
+ On exit:
+ op->scp->mx is locked
+
+ None of the directory buffers for op->scp should be locked by the
+ calling thread.
+*/
+int
+cm_DirLookupOffset(cm_dirOp_t * op, char *entry, cm_fid_t *cfid, osi_hyper_t *offsetp)
+{
+ cm_dirEntry_t *firstitem = NULL;
+ cm_buf_t *itembuf = NULL;
+ unsigned short *previtem = NULL;
+ cm_buf_t *pibuf = NULL;
+
+ long code;
+
+ osi_Log2(afsd_logp, "cm_DirLookupOffset for op 0x%p, entry[%s]",
+ op, osi_LogSaveString(afsd_logp, entry));
+
+ code = cm_DirFindItem(op, entry,
+ &itembuf, &firstitem,
+ &pibuf, &previtem);
+ if (code != 0)
+ return ENOENT;
+
+ cm_DirReleasePage(op, &pibuf, FALSE);
+
+ cfid->cell = op->scp->fid.cell;
+ cfid->volume = op->scp->fid.volume;
+ cfid->vnode = ntohl(firstitem->fid.vnode);
+ cfid->unique = ntohl(firstitem->fid.unique);
+ if (offsetp) {
+ osi_hyper_t thyper;
+
+ thyper = itembuf->offset;
+ thyper = LargeIntegerAdd(thyper,
+ ConvertLongToLargeInteger(((char *) firstitem) - itembuf->datap));
+
+ *offsetp = thyper;
+ }
+
+ cm_DirReleasePage(op, &itembuf, FALSE);
+
+ osi_Log2(afsd_logp, "cm_DirLookupOffset returning fid[%d,%d]",
+ cfid->vnode, cfid->unique);
+ if (offsetp) {
+ osi_Log2(afsd_logp, " offset [%x:%x]",
+ offsetp->HighPart, offsetp->LowPart);
+ }
+
+ return 0;
+}
+
+/* Apply a function to every directory entry in a directory.
+
+ On entry:
+ op->scp->mx is locked
+
+ On exit:
+ op->scp->mx is locked
+
+ None of the directory buffers for op->scp should be locked by the
+ calling thread.
+
+ The hook function cannot modify or lock any directory buffers.
+ */
+int
+cm_DirApply(cm_dirOp_t * op, int (*hookproc) (void *, char *, long, long), void *hook)
+{
+ /* Enumerate the contents of a directory. */
+ int i;
+ int num;
+
+ cm_dirHeader_t *dhp = NULL;
+ cm_buf_t *dhpbuf = NULL;
+
+ cm_dirEntry_t *ep = NULL;
+ cm_buf_t *epbuf = NULL;
+
+ long code = 0;
+
+ code = cm_DirGetPage(op, 0, &dhpbuf, &dhp);
+ if (code != 0)
+ return EIO;
+
+ for (i = 0; i < CM_DIR_NHASHENT; i++) {
+ /* For each hash chain, enumerate everyone on the list. */
+ num = ntohs(dhp->hashTable[i]);
+ while (num != 0) {
+ /* Walk down the hash table list. */
+ code = cm_DirGetBlob(op, num, &epbuf, &ep);
+ if (code != 0) {
+ cm_DirReleasePage(op, &dhpbuf, FALSE);
+ return code;
+ }
+
+ num = ntohs(ep->next);
+ (*hookproc) (hook, ep->name, ntohl(ep->fid.vnode),
+ ntohl(ep->fid.unique));
+
+ cm_DirReleasePage(op, &epbuf, FALSE);
+ }
+ }
+ cm_DirReleasePage(op, &dhpbuf, FALSE);
+
+ return 0;
+}
+
+/* Check if a directory is empty
+
+ On entry:
+ op->scp->mx is locked
+
+ On exit:
+ op->scp->mx is locked
+
+ None of the directory buffers for op->scp should be locked by the
+ calling thread.
+ */
+int
+cm_DirIsEmpty(cm_dirOp_t * op)
+{
+ /* Enumerate the contents of a directory. */
+ int i;
+ int num;
+
+ cm_dirHeader_t *dhp = NULL;
+ cm_buf_t *dhpbuf = NULL;
+
+ cm_dirEntry_t *ep = NULL;
+ cm_buf_t *epbuf = NULL;
+
+ long code = 0;
+
+ code = cm_DirGetPage(op, 0, &dhpbuf, &dhp);
+ if (code != 0)
+ return 0;
+
+ for (i = 0; i < CM_DIR_NHASHENT; i++) {
+ /* For each hash chain, enumerate everyone on the list. */
+ num = ntohs(dhp->hashTable[i]);
+
+ while (num != 0) {
+ /* Walk down the hash table list. */
+ code = cm_DirGetBlob(op, num, &epbuf, &ep);
+ if (code != 0)
+ break;
+
+ if (strcmp(ep->name, "..") && strcmp(ep->name, ".")) {
+ cm_DirReleasePage(op, &epbuf, FALSE);
+ cm_DirReleasePage(op, &dhpbuf, FALSE);
+ return 1;
+ }
+ num = ntohs(ep->next);
+ cm_DirReleasePage(op, &epbuf, FALSE);
+ }
+ }
+ cm_DirReleasePage(op, &dhpbuf, FALSE);
+ return 0;
+}
+
+/* Return a pointer to an entry, given its number.
+
+ On entry:
+ scp->mx locked
+ if *bufferpp != NULL, then *bufferpp->mx is locked
+
+ During:
+ scp->mx may be unlocked
+ *bufferpp may be released
+
+ On exit:
+ scp->mx locked
+ if *bufferpp != NULL, then *bufferpp->mx is locked
+
+ *bufferpp should be released via cm_DirReleasePage() or any other
+ *call that releases a directory buffer.
+*/
+static long
+cm_DirGetBlob(cm_dirOp_t * op,
+ unsigned int blobno, cm_buf_t ** bufferpp, cm_dirEntry_t ** blobpp)
+{
+ unsigned char * ep;
+ long code = 0;
+
+ osi_Log2(afsd_logp, "cm_DirGetBlob for op 0x%p, blobno=%d",
+ op, blobno);
+
+ code = cm_DirGetPage(op, blobno >> CM_DIR_LEPP,
+ bufferpp, (void **) &ep);
+ if (code != 0)
+ return code;
+
+ *blobpp = (cm_dirEntry_t *) (ep + 32 * (blobno & (CM_DIR_EPP - 1)));
+
+ return code;
}
+
+int
+cm_DirHash(char *string)
+{
+ /* Hash a string to a number between 0 and NHASHENT. */
+ register unsigned char tc;
+ register int hval;
+ register int tval;
+ hval = 0;
+ while ((tc = (*string++))) {
+ hval *= 173;
+ hval += tc;
+ }
+ tval = hval & (CM_DIR_NHASHENT - 1);
+ if (tval == 0)
+ return tval;
+ else if (hval < 0)
+ tval = CM_DIR_NHASHENT - tval;
+ return tval;
+}
+
+/* Find a directory entry, given its name. This entry returns a
+ * pointer to a locked buffer, and a pointer to a locked buffer (in
+ * previtem) referencing the found item (to aid the delete code). If
+ * no entry is found, however, no items are left locked, and a null
+ * pointer is returned instead.
+ *
+ * On entry:
+ * scp->mx locked
+ *
+ * On exit:
+ * scp->mx locked
+ */
+static long
+cm_DirFindItem(cm_dirOp_t * op,
+ char *ename,
+ cm_buf_t ** itembufpp, cm_dirEntry_t ** itempp,
+ cm_buf_t ** prevbufpp, unsigned short **previtempp)
+{
+ int i;
+ cm_dirHeader_t *dhp = NULL;
+ unsigned short *lp = NULL;
+ cm_dirEntry_t *tp = NULL;
+ cm_buf_t *hashbufp = NULL;
+ cm_buf_t *itembufp = NULL;
+ long code = 0;
+
+ osi_Log2(afsd_logp, "cm_DirFindItem for op 0x%p, entry[%s]",
+ op, osi_LogSaveString(afsd_logp, ename));
+
+ i = cm_DirHash(ename);
+
+ if (op->scp->fileType != CM_SCACHETYPE_DIRECTORY) {
+ osi_Log0(afsd_logp, "cm_DirFindItem: The scp is not a directory");
+ return CM_ERROR_INVAL;
+ }
+
+ code = cm_DirGetPage(op, 0, &hashbufp, (void **) &dhp);
+ if (code != 0) {
+ return code;
+ }
+
+ if (dhp->hashTable[i] == 0) {
+ /* no such entry */
+ osi_Log1(afsd_logp, "cm_DirFindItem: Hash bucket %d is empty", i);
+ cm_DirReleasePage(op, &hashbufp, FALSE);
+ return ENOENT;
+ }
+
+ code = cm_DirGetBlob(op,
+ (u_short) ntohs(dhp->hashTable[i]),
+ &itembufp, &tp);
+ if (code != 0) {
+ cm_DirReleasePage(op, &hashbufp, FALSE);
+ return code;
+ }
+
+ lp = &(dhp->hashTable[i]);
+
+ /* loop invariant:
+
+ lp : pointer to blob number of entry we are looking at
+ hashbufp : buffer containing lp
+ tp : pointer to entry we are looking at
+ itembufp : buffer containing tp
+ */
+ while (1) {
+ /* Look at each hash conflict entry. */
+ if (!strcmp(ename, tp->name)) {
+ osi_Log0(afsd_logp, "cm_DirFindItem: returning success");
+ /* Found our entry. */
+ *previtempp = lp;
+ *prevbufpp = hashbufp;
+ *itempp = tp;
+ *itembufpp = itembufp;
+ return 0;
+ }
+
+ lp = &(tp->next);
+ cm_DirReleasePage(op, &hashbufp, FALSE);
+ hashbufp = itembufp;
+
+ itembufp = NULL;
+ tp = NULL;
+
+ if (*lp == 0) {
+ /* The end of the line */
+ osi_Log0(afsd_logp, "cm_DirFindItem: returning ENOENT");
+ cm_DirReleasePage(op, &hashbufp, FALSE);
+ return ENOENT;
+ }
+
+ code = cm_DirGetBlob(op,
+ (u_short) ntohs(*lp),
+ &itembufp, &tp);
+
+ if (code != 0) {
+ cm_DirReleasePage(op, &hashbufp, FALSE);
+ return code;
+ }
+ }
+}
+
+/* Begin a sequence of directory operations. scp->mx should be
+ locked.
+*/
+long
+cm_BeginDirOp(cm_scache_t * scp, cm_user_t * userp, cm_req_t * reqp,
+ cm_dirOp_t * op)
+{
+ long code;
+ int i;
+
+ osi_Log3(afsd_logp, "Beginning dirOp[0x%p] for scp[0x%p], userp[0x%p]",
+ op, scp, userp);
+
+ memset(op, 0, sizeof(*op));
+
+ cm_HoldSCache(scp);
+ op->scp = scp;
+ cm_HoldUser(userp);
+ op->userp = userp;
+ cm_InitReq(&op->req);
+
+ op->dirtyBufCount = 0;
+ op->nBuffers = 0;
+
+ for (i=0; i < CM_DIROP_MAXBUFFERS; i++) {
+ op->buffers[i].flags = 0;
+ }
+
+ code = cm_DirCheckStatus(op);
+
+ if (code == 0) {
+ op->length = scp->length;
+ op->newLength = op->length;
+ op->dataVersion = scp->dataVersion;
+ op->newDataVersion = op->dataVersion;
+ } else {
+ cm_EndDirOp(op);
+ }
+
+ return code;
+}
+
+/* Check if it is safe for us to perform local directory updates.
+ Called with scp->mx held. */
+int
+cm_CheckDirOpForSingleChange(cm_dirOp_t * op)
+{
+ long code;
+
+ if (op->scp == NULL)
+ return 0;
+
+ code = cm_DirCheckStatus(op);
+
+ if (code == 0 &&
+ op->dataVersion == op->scp->dataVersion - 1) {
+ /* only one set of changes happened between cm_BeginDirOp()
+ and this function. It is safe for us to perform local
+ changes. */
+ op->newDataVersion = op->scp->dataVersion;
+ op->newLength = op->scp->serverLength;
+
+ osi_Log0(afsd_logp, "cm_CheckDirOpForSingleChange succeeded");
+
+ return 1;
+ }
+
+ osi_Log3(afsd_logp,
+ "cm_CheckDirOpForSingleChange failed. code=0x%x, old dv=%d, new dv=%d",
+ code, op->dataVersion, op->scp->dataVersion);
+ return 0;
+}
+
+/* End a sequence of directory operations. Called with op->scp->mx
+ locked.*/
+long
+cm_EndDirOp(cm_dirOp_t * op)
+{
+ long code = 0;
+
+ if (op->scp == NULL)
+ return 0;
+
+ osi_Log2(afsd_logp, "Ending dirOp 0x%p with %d dirty buffer releases",
+ op, op->dirtyBufCount);
+
+ if (op->dirtyBufCount > 0) {
+ /* we made changes. We should go through the list of buffers
+ and update the dataVersion for each. */
+
+ lock_ReleaseMutex(&op->scp->mx);
+ code = buf_ForceDataVersion(op->scp, op->dataVersion, op->newDataVersion);
+ lock_ObtainMutex(&op->scp->mx);
+ }
+
+ if (op->scp)
+ cm_ReleaseSCache(op->scp);
+ op->scp = NULL;
+
+ if (op->userp)
+ cm_ReleaseUser(op->userp);
+ op->userp = 0;
+
+ osi_assertx(op->nBuffers == 0, "Buffer leak after dirOp termination");
+
+ return code;
+}
+
+/* NOTE: Called without scp->mx and without bufferp->mx */
+static long
+cm_DirOpAddBuffer(cm_dirOp_t * op, cm_buf_t * bufferp)
+{
+ int i;
+ long code = 0;
+
+ osi_Log2(afsd_logp, "cm_DirOpAddBuffer for op 0x%p, buffer %p", op, bufferp);
+
+ if (bufferp == NULL)
+ return -1;
+
+ for (i=0; i < CM_DIROP_MAXBUFFERS; i++) {
+ if ((op->buffers[i].flags & CM_DIROPBUFF_INUSE) &&
+ op->buffers[i].bufferp == bufferp) {
+ break;
+ }
+ }
+
+ if (i < CM_DIROP_MAXBUFFERS) {
+ /* we already have this buffer on our list */
+
+ op->buffers[i].refcount++;
+ osi_Log0(afsd_logp,
+ "cm_DirOpAddBuffer: the buffer is already listed for the dirOp");
+ return 0;
+ } else {
+ /* we have to add a new buffer */
+ osi_assertx(op->nBuffers < CM_DIROP_MAXBUFFERS - 1,
+ "DirOp has exceeded CM_DIROP_MAXBUFFERS buffers");
+
+ for (i=0; i < CM_DIROP_MAXBUFFERS; i++) {
+ if (!(op->buffers[i].flags & CM_DIROPBUFF_INUSE))
+ break;
+ }
+
+ osi_assert(i < CM_DIROP_MAXBUFFERS);
+
+ lock_ObtainMutex(&bufferp->mx);
+ lock_ObtainMutex(&op->scp->mx);
+
+ /* Make sure we are synchronized. */
+ code = cm_SyncOp(op->scp, bufferp, op->userp, &op->req, PRSFS_LOOKUP,
+ CM_SCACHESYNC_NEEDCALLBACK |
+ CM_SCACHESYNC_WRITE |
+ CM_SCACHESYNC_BUFLOCKED);
+
+ if (code == 0 &&
+ bufferp->dataVersion != op->dataVersion) {
+
+ osi_Log2(afsd_logp, "cm_DirOpAddBuffer: buffer version mismatch. buf ver = %d. want %d", bufferp->dataVersion, op->dataVersion);
+
+ cm_SyncOpDone(op->scp, bufferp,
+ CM_SCACHESYNC_NEEDCALLBACK |
+ CM_SCACHESYNC_WRITE |
+ CM_SCACHESYNC_BUFLOCKED);
+
+ code = CM_ERROR_INVAL;
+ }
+
+ lock_ReleaseMutex(&op->scp->mx);
+ lock_ReleaseMutex(&bufferp->mx);
+
+ if (code) {
+ osi_Log1(afsd_logp, "cm_DirOpAddBuffer: failed to sync buffer. code=0x%x",
+ code);
+ return code;
+ }
+
+ buf_Hold(bufferp);
+ op->buffers[i].bufferp = bufferp;
+ op->buffers[i].refcount = 1; /* start with one ref */
+ op->buffers[i].flags = CM_DIROPBUFF_INUSE;
+
+ op->nBuffers++;
+
+ osi_Log0(afsd_logp, "cm_DirOpAddBuffer: returning success");
+
+ return 0;
+ }
+}
+
+/* Note: Called without op->scp->mx */
+static int
+cm_DirOpFindBuffer(cm_dirOp_t * op, osi_hyper_t offset, cm_buf_t ** bufferpp)
+{
+ int i;
+
+ for (i=0; i < CM_DIROP_MAXBUFFERS; i++) {
+ if ((op->buffers[i].flags & CM_DIROPBUFF_INUSE) &&
+ LargeIntegerEqualTo(op->buffers[i].bufferp->offset, offset))
+ break;
+ }
+
+ if (i < CM_DIROP_MAXBUFFERS) {
+ /* found it */
+ op->buffers[i].refcount++;
+ buf_Hold(op->buffers[i].bufferp);
+ *bufferpp = op->buffers[i].bufferp;
+
+ osi_Log2(afsd_logp, "cm_DirOpFindBuffer: found buffer for offset [%x:%x]",
+ offset.HighPart, offset.LowPart);
+ return 1;
+ }
+
+ osi_Log2(afsd_logp, "cm_DirOpFindBuffer: buffer not found for offset [%x:%x]",
+ offset.HighPart, offset.LowPart);
+ return 0;
+}
+
+
+/* NOTE: called with scp->mx NOT held */
+static int
+cm_DirOpDelBuffer(cm_dirOp_t * op, cm_buf_t * bufferp, int flags)
+{
+ int i;
+
+ osi_Log3(afsd_logp, "cm_DirOpDelBuffer for op 0x%p, buffer 0x%p, flags=%d",
+ op, bufferp, flags);
+
+ for (i=0; i < CM_DIROP_MAXBUFFERS; i++) {
+ if ((op->buffers[i].flags & CM_DIROPBUFF_INUSE) &&
+ op->buffers[i].bufferp == bufferp)
+ break;
+ }
+
+ if (i < CM_DIROP_MAXBUFFERS) {
+
+ if (flags & DIROP_MODIFIED)
+ op->dirtyBufCount++;
+
+ osi_assert(op->buffers[i].refcount > 0);
+ op->buffers[i].refcount --;
+
+ if (op->buffers[i].refcount == 0) {
+ /* this was the last reference we had */
+
+ osi_Log0(afsd_logp, "cm_DirOpDelBuffer: releasing buffer");
+
+ /* if this buffer was modified, then we update the data
+ version of the buffer with the data version of the
+ scp. */
+ if (!(flags & DIROP_SCPLOCKED)) {
+ lock_ObtainMutex(&op->scp->mx);
+ }
+
+ /* first make sure that the buffer is idle. It should
+ have been idle all along. */
+ osi_assertx((bufferp->cmFlags & (CM_BUF_CMFETCHING |
+ CM_BUF_CMSTORING)) == 0,
+ "Buffer is not idle while performing dirOp");
+
+ cm_SyncOpDone(op->scp, bufferp,
+ CM_SCACHESYNC_NEEDCALLBACK |
+ CM_SCACHESYNC_WRITE);
+
+#ifdef DEBUG
+ osi_assert(bufferp->dataVersion == op->dataVersion);
+#endif
+
+ lock_ReleaseMutex(&op->scp->mx);
+
+ lock_ObtainMutex(&bufferp->mx);
+
+ if (flags & DIROP_SCPLOCKED) {
+ lock_ObtainMutex(&op->scp->mx);
+ }
+
+ if (flags & DIROP_MODIFIED) {
+ /* We don't update the dataversion here. Instead we
+ wait until the dirOp is completed and then flip the
+ dataversion on all the buffers in one go.
+ Otherwise we won't know if the dataversion is
+ current because it was fetched from the server or
+ because we touched it during the dirOp. */
+
+ if (bufferp->userp != op->userp) {
+ if (bufferp->userp != NULL)
+ cm_ReleaseUser(bufferp->userp);
+ cm_HoldUser(op->userp);
+ bufferp->userp = op->userp;
+ }
+ }
+
+ lock_ReleaseMutex(&bufferp->mx);
+
+ op->buffers[i].bufferp = NULL;
+ buf_Release(bufferp);
+ op->buffers[i].flags = 0;
+
+ op->nBuffers--;
+
+ return 1;
+ } else {
+ /* we have other references to this buffer. so we have to
+ let it be */
+ return 0;
+ }
+
+ } else {
+ osi_Log0(afsd_logp, "cm_DirOpDelBuffer: buffer not found");
+ osi_assertx(FALSE, "Attempt to delete a non-existent buffer from a dirOp");
+ return -1;
+ }
+}
+
+/* Check if we have current status and a callback for the given scp.
+ This should be called before cm_DirGetPage() is called per scp.
+
+ On entry:
+ scp->mx locked
+
+ On exit:
+ scp->mx locked
+
+ During:
+ scp->mx may be released
+ */
+static long
+cm_DirCheckStatus(cm_dirOp_t * op)
+{
+ long code;
+
+ code = cm_SyncOp(op->scp, NULL, op->userp, &op->req, PRSFS_LOOKUP,
+ CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+
+ osi_Log2(afsd_logp, "cm_DirCheckStatus for op 0x%p returning code 0x%x",
+ op, code);
+
+ return code;
+}
+
+/* Release a directory buffer that was obtained via a call to
+ cm_DirGetPage() or any other function that returns a locked, held,
+ directory page buffer.
+
+ Called with scp->mx held
+ */
+static long
+cm_DirReleasePage(cm_dirOp_t * op, cm_buf_t ** bufferpp, int modified)
+{
+ long code = 0;
+
+ if (!*bufferpp)
+ return EINVAL;
+
+ cm_DirOpDelBuffer(op, *bufferpp,
+ ((modified ? DIROP_MODIFIED : 0) | DIROP_SCPLOCKED));
+ buf_Release(*bufferpp);
+ *bufferpp = NULL;
+
+ return code;
+}
+
+/*
+ Returns the index'th directory page from scp. The userp and reqp
+ will be used to fetch the buffer from the fileserver if necessary.
+ If the call is successful, a locked and held cm_buf_t is returned
+ via buferpp and a pointer to the directory page is returned via
+ datapp.
+
+ The returned buffer should be released via a call to
+ cm_DirReleasePage() or by passing it into a subsequent call to
+ cm_DirGetPage() for the *same* scp.
+
+ If a *locked* buffer for the *same* scp is passed in via bufferpp
+ to the function, it will check if the requested directory page is
+ located in the specified buffer. If not, the buffer will be
+ released and a new buffer returned that contains the requested
+ page.
+
+ Note: If a buffer is specified on entry via bufferpp, it is assumed
+ that the buffer is unmodified. If the buffer is modified, it
+ should be released via cm_DirReleasePage().
+
+ On entry:
+ scp->mx locked.
+ If *bufferpp is non-NULL, then *bufferpp->mx is locked.
+
+ On exit:
+ scp->mxlocked
+ If *bufferpp is non-NULL, then *bufferpp->mx is locked.
+
+ During:
+ scp->mx will be released
+
+ */
+static long
+cm_DirGetPage(cm_dirOp_t * op,
+ long index, cm_buf_t ** bufferpp, void ** datapp)
+{
+ osi_hyper_t pageOffset; /* offset of the dir page from the
+ start of the directory */
+ osi_hyper_t bufferOffset; /* offset of the buffer from the start
+ of the directory */
+ osi_hyper_t thyper;
+
+ cm_buf_t * bufferp = NULL;
+
+ void * datap = NULL;
+
+ long code = 0;
+
+ osi_Log2(afsd_logp, "cm_DirGetPage for op 0x%p, index %d", op, index);
+
+ pageOffset = ConvertLongToLargeInteger(index * CM_DIR_PAGESIZE);
+ bufferOffset.HighPart = pageOffset.HighPart;
+ bufferOffset.LowPart = pageOffset.LowPart & ~(cm_data.buf_blockSize - 1);
+
+ bufferp = *bufferpp;
+ if (bufferp != NULL) {
+ osi_assert(cm_FidCmp(&bufferp->fid, &op->scp->fid) == 0);
+
+ thyper = bufferp->offset;
+ }
+
+ lock_ReleaseMutex(&op->scp->mx);
+
+ if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
+ /* wrong buffer */
+
+ if (bufferp) {
+ buf_Release(bufferp);
+ cm_DirOpDelBuffer(op, bufferp, 0);
+ bufferp = NULL;
+ }
+
+ /* first check if we are already working with the buffer */
+ if (cm_DirOpFindBuffer(op, bufferOffset, &bufferp)) {
+ code = 0;
+ goto _has_buffer;
+ }
+
+ lock_ObtainRead(&op->scp->bufCreateLock);
+ code = buf_Get(op->scp, &bufferOffset, &bufferp);
+ lock_ReleaseRead(&op->scp->bufCreateLock);
+
+ if (code) {
+ osi_Log1(afsd_logp, " buf_Get returned code 0x%x", code);
+ bufferp = NULL;
+ goto _exit;
+ }
+
+ osi_assert(bufferp != NULL);
+
+ /* DirOpAddBuffer will obtain bufferp->mx if necessary */
+ code = cm_DirOpAddBuffer(op, bufferp);
+
+ if (code != 0) {
+ /* for some reason, the buffer was rejected. We can't use
+ this buffer, and since this is the only buffer we can
+ potentially use, there's no recourse.*/
+ buf_Release(bufferp);
+ bufferp = NULL;
+ goto _exit;
+ }
+
+#if 0
+ /* The code below is for making sure the buffer contains
+ current data. This is a bad idea, since the whole point of
+ doing directory updates locally is to avoid fetching all
+ the data from the server. */
+ while (1) {
+ lock_ObtainMutex(&op->scp->mx);
+ code = cm_SyncOp(op->scp, bufferp, op->userp, &op->req, PRSFS_LOOKUP,
+ CM_SCACHESYNC_NEEDCALLBACK |
+ CM_SCACHESYNC_READ |
+ CM_SCACHESYNC_BUFLOCKED);
+
+ if (code) {
+ lock_ReleaseMutex(&op->scp->mx);
+ break;
+ }
+
+ cm_SyncOpDone(op->scp, bufferp,
+ CM_SCACHESYNC_NEEDCALLBACK |
+ CM_SCACHESYNC_READ |
+ CM_SCACHESYNC_BUFLOCKED);
+
+ if (cm_HaveBuffer(op->scp, bufferp, 1)) {
+ lock_ReleaseMutex(&op->scp->mx);
+ break;
+ }
+
+ lock_ReleaseMutex(&bufferp->mx);
+ code = cm_GetBuffer(op->scp, bufferp, NULL, op->userp, &op->req);
+ lock_ReleaseMutex(&op->scp->mx);
+ lock_ObtainMutex(&bufferp->mx);
+
+ if (code)
+ break;
+ }
+
+ if (code) {
+ cm_DirOpDelBuffer(op, bufferp, 0);
+ buf_Release(bufferp);
+ bufferp = NULL;
+ goto _exit;
+ }
+#endif
+ }
+
+ _has_buffer:
+
+ /* now to figure out where the data is */
+ thyper = LargeIntegerSubtract(pageOffset, bufferOffset);
+
+ osi_assert(thyper.HighPart == 0);
+ osi_assert(cm_data.buf_blockSize > thyper.LowPart &&
+ cm_data.buf_blockSize - thyper.LowPart >= CM_DIR_PAGESIZE);
+
+ datap = (void *) (((char *)bufferp->datap) + thyper.LowPart);
+
+ if (datapp)
+ *datapp = datap;
+
+ /* also, if we are writing past EOF, we should make a note of the
+ new length */
+ thyper = LargeIntegerAdd(pageOffset,
+ ConvertLongToLargeInteger(CM_DIR_PAGESIZE));
+ if (LargeIntegerLessThan(op->newLength, thyper)) {
+ op->newLength = thyper;
+ }
+
+ _exit:
+
+ *bufferpp = bufferp;
+ if (op->scp)
+ lock_ObtainMutex(&op->scp->mx);
+
+ osi_Log1(afsd_logp, "cm_DirGetPage returning code 0x%x", code);
+
+ return code;
+}
+
+
+void
+cm_DirEntryListAdd(char * namep, cm_dirEntryList_t ** list)
+{
+ size_t len;
+ cm_dirEntryList_t * entry;
+
+ len = strlen(namep);
+ len += sizeof(cm_dirEntryList_t);
+
+ entry = malloc(len);
+ if (entry) {
+ entry->nextp = *list;
+ strcpy(entry->name, namep);
+ *list = entry;
+ }
+}
+
+void
+cm_DirEntryListFree(cm_dirEntryList_t ** list)
+{
+ cm_dirEntryList_t * entry;
+ cm_dirEntryList_t * next;
+
+ for (entry = *list; entry; entry = next) {
+ next = entry->nextp;
+ free(entry);
+ }
+
+ *list = NULL;
+}
+
{
cm_lookupSearch_t* sp = parmp;
+ if (
#ifdef AFS_FREELANCE_CLIENT
/* Freelance entries never end up in the DNLC because they
* do not have an associated cm_server_t
*/
- if ( !(cm_freelanceEnabled &&
+ !(cm_freelanceEnabled &&
sp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
- sp->fid.volume==AFS_FAKE_ROOT_VOL_ID ) )
-#endif /* AFS_FREELANCE_CLIENT */
- {
+ sp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
+#else /* !AFS_FREELANCE_CLIENT */
+ TRUE
+#endif
+ ) {
int casefold = sp->caseFold;
sp->caseFold = 0; /* we have a strong preference for exact matches */
if ( *retscp = cm_dnlcLookup(scp, sp)) /* dnlc hit */
return 0;
}
sp->caseFold = casefold;
+
+ /* see if we can find it using the directory hash tables.
+ we can only do exact matches, since the hash is case
+ sensitive. */
+ {
+ cm_dirOp_t dirop;
+
+ code = ENOENT;
+
+ code = cm_BeginDirOp(scp, userp, reqp, &dirop);
+ if (code == 0) {
+ code = cm_DirLookup(&dirop, sp->searchNamep, &sp->fid);
+ cm_EndDirOp(&dirop);
+ }
+
+ if (code == 0) {
+ /* found it */
+ sp->found = TRUE;
+ sp->ExactFound = TRUE;
+ lock_ReleaseMutex(&scp->mx);
+
+ *retscp = NULL; /* force caller to call cm_GetSCache() */
+
+ return 0;
+ }
+ }
}
}
cm_lookupSearch_t rock;
int getroot;
+ memset(&rock, 0, sizeof(rock));
+
if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
&& strcmp(namep, "..") == 0) {
if (dscp->dotdotFid.volume == 0)
goto haveFid;
}
- memset(&rock, 0, sizeof(rock));
+ if (flags & CM_FLAG_NOMOUNTCHASE) {
+ /* In this case, we should go and call cm_Dir* functions
+ directly since the following cm_ApplyDir() function will
+ not. */
+
+ cm_dirOp_t dirop;
+
+ lock_ObtainMutex(&dscp->mx);
+
+ code = cm_BeginDirOp(dscp, userp, reqp, &dirop);
+ if (code == 0) {
+ code = cm_DirLookup(&dirop, namep, &rock.fid);
+ cm_EndDirOp(&dirop);
+ }
+
+ lock_ReleaseMutex(&dscp->mx);
+
+ if (code == 0) {
+ /* found it */
+ rock.found = TRUE;
+ goto haveFid;
+ }
+ }
+
rock.fid.cell = dscp->fid.cell;
rock.fid.volume = dscp->fid.volume;
rock.searchNamep = namep;
AFSFetchStatus newDirStatus;
AFSVolSync volSync;
struct rx_connection * callp;
+ cm_dirOp_t dirop;
#ifdef AFS_FREELANCE_CLIENT
if (cm_freelanceEnabled && dscp == cm_data.rootSCachep) {
/* make sure we don't screw up the dir status during the merge */
lock_ObtainMutex(&dscp->mx);
+
+ code = cm_BeginDirOp(dscp, userp, reqp, &dirop);
+
sflags = CM_SCACHESYNC_STOREDATA;
code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
lock_ReleaseMutex(&dscp->mx);
- if (code)
+ if (code) {
+ cm_EndDirOp(&dirop);
return code;
+ }
/* make the RPC */
afsFid.Volume = dscp->fid.volume;
lock_ObtainMutex(&dscp->mx);
cm_dnlcRemove(dscp, namep);
cm_SyncOpDone(dscp, NULL, sflags);
- if (code == 0)
+ if (code == 0) {
cm_MergeStatus(NULL, dscp, &newDirStatus, &volSync, userp, 0);
- else if (code == CM_ERROR_NOSUCHFILE) {
+ if (cm_CheckDirOpForSingleChange(&dirop)) {
+ cm_DirDeleteEntry(&dirop, namep);
+ }
+ } else if (code == CM_ERROR_NOSUCHFILE) {
/* windows would not have allowed the request to delete the file
* if it did not believe the file existed. therefore, we must
* have an inconsistent view of the world.
*/
dscp->cbServerp = NULL;
}
+ cm_EndDirOp(&dirop);
lock_ReleaseMutex(&dscp->mx);
return code;
cm_callbackRequest_t cbReq;
AFSFid newAFSFid;
cm_fid_t newFid;
- cm_scache_t *scp;
+ cm_scache_t *scp = NULL;
int didEnd;
AFSStoreStatus inStatus;
AFSFetchStatus updatedDirStatus;
AFSCallBack newFileCallback;
AFSVolSync volSync;
struct rx_connection * callp;
+ cm_dirOp_t dirop;
/* can't create names with @sys in them; must expand it manually first.
* return "invalid request" if they try.
* completes.
*/
lock_ObtainMutex(&dscp->mx);
+ cm_BeginDirOp(dscp, userp, reqp, &dirop);
code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
if (code == 0) {
cm_StartCallbackGrantingCall(NULL, &cbReq);
+ } else {
+ cm_EndDirOp(&dirop);
}
lock_ReleaseMutex(&dscp->mx);
if (code) {
if (!didEnd)
cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
+ lock_ObtainMutex(&dscp->mx);
+ if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
+ cm_DirCreateEntry(&dirop, namep, &newFid);
+ }
+ cm_EndDirOp(&dirop);
+ lock_ReleaseMutex(&dscp->mx);
+
return code;
}
AFSCallBack newDirCallback;
AFSVolSync volSync;
struct rx_connection * callp;
+ cm_dirOp_t dirop;
/* can't create names with @sys in them; must expand it manually first.
* return "invalid request" if they try.
* our call completes.
*/
lock_ObtainMutex(&dscp->mx);
+ cm_BeginDirOp(dscp, userp, reqp, &dirop);
code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
if (code == 0) {
cm_StartCallbackGrantingCall(NULL, &cbReq);
+ } else {
+ cm_EndDirOp(&dirop);
}
lock_ReleaseMutex(&dscp->mx);
if (code) {
if (!didEnd)
cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
+ lock_ObtainMutex(&dscp->mx);
+ if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
+ cm_DirCreateEntry(&dirop, namep, &newFid);
+ }
+ cm_EndDirOp(&dirop);
+ lock_ReleaseMutex(&dscp->mx);
+
/* and return error code */
return code;
}
AFSFetchStatus newLinkStatus;
AFSVolSync volSync;
struct rx_connection * callp;
+ cm_dirOp_t dirop;
if (dscp->fid.cell != sscp->fid.cell ||
dscp->fid.volume != sscp->fid.volume) {
}
lock_ObtainMutex(&dscp->mx);
+ cm_BeginDirOp(dscp, userp, reqp, &dirop);
code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
+ if (code != 0)
+ cm_EndDirOp(&dirop);
lock_ReleaseMutex(&dscp->mx);
if (code)
cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
if (code == 0) {
cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, 0);
+ if (cm_CheckDirOpForSingleChange(&dirop)) {
+ cm_DirCreateEntry(&dirop, namep, &sscp->fid);
+ }
}
+ cm_EndDirOp(&dirop);
lock_ReleaseMutex(&dscp->mx);
return code;
AFSFetchStatus newLinkStatus;
AFSVolSync volSync;
struct rx_connection * callp;
+ cm_dirOp_t dirop;
/* before starting the RPC, mark that we're changing the directory data,
* so that someone who does a chmod on the dir will wait until our
* call completes.
*/
lock_ObtainMutex(&dscp->mx);
+ cm_BeginDirOp(dscp, userp, reqp, &dirop);
code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
+ if (code != 0)
+ cm_EndDirOp(&dirop);
lock_ReleaseMutex(&dscp->mx);
if (code) {
return code;
cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
if (code == 0) {
cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, 0);
+ if (cm_CheckDirOpForSingleChange(&dirop)) {
+ newFid.cell = dscp->fid.cell;
+ newFid.volume = dscp->fid.volume;
+ newFid.vnode = newAFSFid.Vnode;
+ newFid.unique = newAFSFid.Unique;
+
+ cm_DirCreateEntry(&dirop, namep, &newFid);
+ }
}
+ cm_EndDirOp(&dirop);
lock_ReleaseMutex(&dscp->mx);
/* now try to create the new dir's entry, too, but be careful to
AFSFetchStatus updatedDirStatus;
AFSVolSync volSync;
struct rx_connection * callp;
+ cm_dirOp_t dirOp;
/* before starting the RPC, mark that we're changing the directory data,
* so that someone who does a chmod on the dir will wait until our
* call completes.
*/
lock_ObtainMutex(&dscp->mx);
+ cm_BeginDirOp(dscp, userp, reqp, &dirOp);
code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
- lock_ReleaseMutex(&dscp->mx);
if (code) {
+ cm_EndDirOp(&dirOp);
+ lock_ReleaseMutex(&dscp->mx);
return code;
}
+ lock_ReleaseMutex(&dscp->mx);
didEnd = 0;
/* try the RPC now */
if (code == 0) {
cm_dnlcRemove(dscp, namep);
cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, 0);
+ if (cm_CheckDirOpForSingleChange(&dirOp)) {
+ cm_DirDeleteEntry(&dirOp, namep);
+ }
}
+ cm_EndDirOp(&dirOp);
lock_ReleaseMutex(&dscp->mx);
/* and return error code */
AFSVolSync volSync;
int oneDir;
struct rx_connection * callp;
+ cm_dirOp_t oldDirOp;
+ cm_fid_t fileFid;
+ int diropCode = -1;
+ cm_dirOp_t newDirOp;
/* before starting the RPC, mark that we're changing the directory data,
* so that someone who does a chmod on the dir will wait until our call
lock_ObtainMutex(&oldDscp->mx);
cm_dnlcRemove(oldDscp, oldNamep);
cm_dnlcRemove(oldDscp, newNamep);
+ cm_BeginDirOp(oldDscp, userp, reqp, &oldDirOp);
code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
CM_SCACHESYNC_STOREDATA);
+ if (code != 0) {
+ cm_EndDirOp(&oldDirOp);
+ }
lock_ReleaseMutex(&oldDscp->mx);
}
else {
if (oldDscp->fid.vnode < newDscp->fid.vnode) {
lock_ObtainMutex(&oldDscp->mx);
+ cm_BeginDirOp(oldDscp, userp, reqp, &oldDirOp);
cm_dnlcRemove(oldDscp, oldNamep);
code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
CM_SCACHESYNC_STOREDATA);
+ if (code != 0)
+ cm_EndDirOp(&oldDirOp);
lock_ReleaseMutex(&oldDscp->mx);
if (code == 0) {
lock_ObtainMutex(&newDscp->mx);
+ cm_BeginDirOp(newDscp, userp, reqp, &newDirOp);
cm_dnlcRemove(newDscp, newNamep);
code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
CM_SCACHESYNC_STOREDATA);
+ if (code != 0)
+ cm_EndDirOp(&newDirOp);
lock_ReleaseMutex(&newDscp->mx);
if (code) {
/* cleanup first one */
lock_ObtainMutex(&oldDscp->mx);
cm_SyncOpDone(oldDscp, NULL,
CM_SCACHESYNC_STOREDATA);
+ cm_EndDirOp(&oldDirOp);
lock_ReleaseMutex(&oldDscp->mx);
}
}
else {
/* lock the new vnode entry first */
lock_ObtainMutex(&newDscp->mx);
+ cm_BeginDirOp(newDscp, userp, reqp, &newDirOp);
cm_dnlcRemove(newDscp, newNamep);
code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
CM_SCACHESYNC_STOREDATA);
+ if (code != 0)
+ cm_EndDirOp(&newDirOp);
lock_ReleaseMutex(&newDscp->mx);
if (code == 0) {
lock_ObtainMutex(&oldDscp->mx);
+ cm_BeginDirOp(oldDscp, userp, reqp, &oldDirOp);
cm_dnlcRemove(oldDscp, oldNamep);
code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
CM_SCACHESYNC_STOREDATA);
+ if (code != 0)
+ cm_EndDirOp(&oldDirOp);
lock_ReleaseMutex(&oldDscp->mx);
if (code) {
/* cleanup first one */
lock_ObtainMutex(&newDscp->mx);
cm_SyncOpDone(newDscp, NULL,
CM_SCACHESYNC_STOREDATA);
+ cm_EndDirOp(&newDirOp);
lock_ReleaseMutex(&newDscp->mx);
}
}
/* update the individual stat cache entries for the directories */
lock_ObtainMutex(&oldDscp->mx);
cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
+
if (code == 0) {
cm_MergeStatus(NULL, oldDscp, &updatedOldDirStatus, &volSync,
userp, 0);
+
+ if (cm_CheckDirOpForSingleChange(&oldDirOp)) {
+
+ diropCode = cm_DirLookup(&oldDirOp, oldNamep, &fileFid);
+
+ if (diropCode == 0) {
+ if (oneDir) {
+ diropCode = cm_DirCreateEntry(&oldDirOp, newNamep, &fileFid);
+ }
+
+ if (diropCode == 0) {
+ diropCode = cm_DirDeleteEntry(&oldDirOp, oldNamep);
}
+ }
+ }
+ }
+ cm_EndDirOp(&oldDirOp);
lock_ReleaseMutex(&oldDscp->mx);
/* and update it for the new one, too, if necessary */
if (code == 0) {
cm_MergeStatus(NULL, newDscp, &updatedNewDirStatus, &volSync,
userp, 0);
+
+ /* we only make the local change if we successfully made
+ the change in the old directory AND there was only one
+ change in the new directory */
+ if (diropCode == 0 && cm_CheckDirOpForSingleChange(&newDirOp)) {
+ cm_DirCreateEntry(&newDirOp, newNamep, &fileFid);
+ }
}
+ cm_EndDirOp(&newDirOp);
lock_ReleaseMutex(&newDscp->mx);
}