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>
30 extern void afsi_log(char *pattern, ...);
33 int cm_enableServerLocks = 1;
35 int cm_followBackupPath = 0;
38 * Case-folding array. This was constructed by inspecting of SMBtrace output.
39 * I do not know anything more about it.
41 unsigned char cm_foldUpper[256] = {
42 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
43 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
44 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
45 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
46 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
47 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
48 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
49 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
50 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
51 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
52 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
53 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
54 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
55 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
56 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
57 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
58 0x80, 0x9a, 0x90, 0x41, 0x8e, 0x41, 0x8f, 0x80,
59 0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x8e, 0x8f,
60 0x90, 0x92, 0x92, 0x4f, 0x99, 0x4f, 0x55, 0x55,
61 0x59, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
62 0x41, 0x49, 0x4f, 0x55, 0xa5, 0xa5, 0x56, 0xa7,
63 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
64 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
65 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
66 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
67 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
68 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
69 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
70 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
71 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
72 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
73 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
77 * Case-insensitive string comparison. We used to use stricmp, but it doesn't
78 * know about 8-bit characters (e.g. 129 is lowercase u-umlaut, 154 is
79 * upper-case u-umlaut).
81 int cm_stricmp(const char *str1, const char *str2)
93 c1 = (char) cm_foldUpper[(unsigned char)(*str1++)];
94 c2 = (char) cm_foldUpper[(unsigned char)(*str2++)];
104 /* return success if we can open this file in this mode */
105 long cm_CheckOpen(cm_scache_t *scp, int openMode, int trunc, cm_user_t *userp,
113 rights |= PRSFS_READ;
114 if (openMode == 1 || openMode == 2 || trunc)
115 rights |= PRSFS_WRITE;
117 lock_ObtainWrite(&scp->rw);
119 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
120 CM_SCACHESYNC_GETSTATUS
121 | CM_SCACHESYNC_NEEDCALLBACK
122 | CM_SCACHESYNC_LOCK);
125 ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
126 scp->fileType == CM_SCACHETYPE_FILE) {
129 unsigned int sLockType;
130 LARGE_INTEGER LOffset, LLength;
132 /* Check if there's some sort of lock on the file at the
135 key = cm_GenerateKey(CM_SESSION_CMINT,0,0);
137 if (rights & PRSFS_WRITE)
140 sLockType = LOCKING_ANDX_SHARED_LOCK;
142 LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
143 LOffset.LowPart = CM_FLSHARE_OFFSET_LOW;
144 LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
145 LLength.LowPart = CM_FLSHARE_LENGTH_LOW;
147 code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
150 cm_Unlock(scp, sLockType, LOffset, LLength, key, userp, reqp);
152 /* In this case, we allow the file open to go through even
153 though we can't enforce mandatory locking on the
155 if (code == CM_ERROR_NOACCESS &&
156 !(rights & PRSFS_WRITE))
160 case CM_ERROR_ALLOFFLINE:
161 case CM_ERROR_ALLDOWN:
162 case CM_ERROR_ALLBUSY:
163 case CM_ERROR_TIMEDOUT:
165 case CM_ERROR_WOULDBLOCK:
168 code = CM_ERROR_SHARING_VIOLATION;
173 } else if (code != 0) {
177 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
181 lock_ReleaseWrite(&scp->rw);
186 /* return success if we can open this file in this mode */
187 long cm_CheckNTOpen(cm_scache_t *scp, unsigned int desiredAccess,
188 unsigned int createDisp, cm_user_t *userp, cm_req_t *reqp,
189 cm_lock_data_t **ldpp)
194 osi_assertx(ldpp != NULL, "null cm_lock_data_t");
197 /* Always allow delete; the RPC will tell us if it's OK */
198 if (desiredAccess == DELETE)
203 if (desiredAccess & (AFS_ACCESS_READ|AFS_ACCESS_EXECUTE))
204 rights |= (scp->fileType == CM_SCACHETYPE_DIRECTORY ? PRSFS_LOOKUP : PRSFS_READ);
206 /* We used to require PRSFS_WRITE if createDisp was 4
207 (OPEN_ALWAYS) even if AFS_ACCESS_WRITE was not requested.
208 However, we don't need to do that since the existence of the
209 scp implies that we don't need to create it. */
210 if (desiredAccess & AFS_ACCESS_WRITE)
211 rights |= PRSFS_WRITE;
213 lock_ObtainWrite(&scp->rw);
215 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
216 CM_SCACHESYNC_GETSTATUS
217 | CM_SCACHESYNC_NEEDCALLBACK
218 | CM_SCACHESYNC_LOCK);
221 * If the open will fail because the volume is readonly, then we will
222 * return an access denied error instead. This is to help brain-dead
223 * apps run correctly on replicated volumes.
224 * See defect 10007 for more information.
226 if (code == CM_ERROR_READONLY)
227 code = CM_ERROR_NOACCESS;
230 ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
231 scp->fileType == CM_SCACHETYPE_FILE) {
233 unsigned int sLockType;
234 LARGE_INTEGER LOffset, LLength;
236 /* Check if there's some sort of lock on the file at the
239 key = cm_GenerateKey(CM_SESSION_CMINT,0,0);
240 if (rights & PRSFS_WRITE)
243 sLockType = LOCKING_ANDX_SHARED_LOCK;
245 /* single byte lock at offset 0x0100 0000 0000 0000 */
246 LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
247 LOffset.LowPart = CM_FLSHARE_OFFSET_LOW;
248 LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
249 LLength.LowPart = CM_FLSHARE_LENGTH_LOW;
251 code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
254 (*ldpp) = (cm_lock_data_t *)malloc(sizeof(cm_lock_data_t));
261 (*ldpp)->sLockType = sLockType;
262 (*ldpp)->LOffset.HighPart = LOffset.HighPart;
263 (*ldpp)->LOffset.LowPart = LOffset.LowPart;
264 (*ldpp)->LLength.HighPart = LLength.HighPart;
265 (*ldpp)->LLength.LowPart = LLength.LowPart;
267 /* In this case, we allow the file open to go through even
268 though we can't enforce mandatory locking on the
270 if (code == CM_ERROR_NOACCESS &&
271 !(rights & PRSFS_WRITE))
275 case CM_ERROR_ALLOFFLINE:
276 case CM_ERROR_ALLDOWN:
277 case CM_ERROR_ALLBUSY:
278 case CM_ERROR_TIMEDOUT:
280 case CM_ERROR_WOULDBLOCK:
283 code = CM_ERROR_SHARING_VIOLATION;
287 } else if (code != 0) {
292 lock_ReleaseWrite(&scp->rw);
294 osi_Log3(afsd_logp,"cm_CheckNTOpen scp 0x%p ldp 0x%p code 0x%x", scp, *ldpp, code);
298 extern long cm_CheckNTOpenDone(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp,
299 cm_lock_data_t ** ldpp)
301 osi_Log2(afsd_logp,"cm_CheckNTOpenDone scp 0x%p ldp 0x%p", scp, *ldpp);
302 lock_ObtainWrite(&scp->rw);
304 cm_Unlock(scp, (*ldpp)->sLockType, (*ldpp)->LOffset, (*ldpp)->LLength,
305 (*ldpp)->key, userp, reqp);
309 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
310 lock_ReleaseWrite(&scp->rw);
314 * When CAP_NT_SMBS has been negotiated, deletion (of files or directories) is
315 * done in three steps:
316 * (1) open for deletion (NT_CREATE_AND_X)
317 * (2) set for deletion on close (NT_TRANSACTION2, SET_FILE_INFO)
319 * We must not do the RPC until step 3. But if we are going to return an error
320 * code (e.g. directory not empty), we must return it by step 2, otherwise most
321 * clients will not notice it. So we do a preliminary check. For deleting
322 * files, this is almost free, since we have already done the RPC to get the
323 * parent directory's status bits. But for deleting directories, we must do an
324 * additional RPC to get the directory's data to check if it is empty. Sigh.
326 long cm_CheckNTDelete(cm_scache_t *dscp, cm_scache_t *scp, cm_user_t *userp,
332 cm_dirEntry_t *dep = 0;
333 unsigned short *hashTable;
335 int BeyondPage = 0, HaveDot = 0, HaveDotDot = 0;
338 /* First check permissions */
339 lock_ObtainWrite(&scp->rw);
340 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_DELETE,
341 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
343 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
344 lock_ReleaseWrite(&scp->rw);
348 /* If deleting directory, must be empty */
350 if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
353 thyper.HighPart = 0; thyper.LowPart = 0;
354 code = buf_Get(scp, &thyper, &bufferp);
358 lock_ObtainMutex(&bufferp->mx);
359 lock_ObtainWrite(&scp->rw);
362 code = cm_SyncOp(scp, bufferp, userp, reqp, 0,
363 CM_SCACHESYNC_NEEDCALLBACK
365 | CM_SCACHESYNC_BUFLOCKED);
369 if (cm_HaveBuffer(scp, bufferp, 1))
372 /* otherwise, load the buffer and try again */
373 lock_ReleaseMutex(&bufferp->mx);
374 code = cm_GetBuffer(scp, bufferp, NULL, userp, reqp);
375 lock_ReleaseWrite(&scp->rw);
376 lock_ObtainMutex(&bufferp->mx);
377 lock_ObtainWrite(&scp->rw);
378 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
383 lock_ReleaseWrite(&scp->rw);
386 /* We try to determine emptiness without looking beyond the first page,
387 * and without assuming "." and ".." are present and are on the first
388 * page (though these assumptions might, after all, be reasonable).
390 hashTable = (unsigned short *)(bufferp->datap + (32 * 5));
391 for (i=0; i<128; i++) {
392 idx = ntohs(hashTable[i]);
398 dep = (cm_dirEntry_t *)(bufferp->datap + (32 * idx));
399 if (strcmp(dep->name, ".") == 0)
401 else if (strcmp(dep->name, "..") == 0)
404 code = CM_ERROR_NOTEMPTY;
407 idx = ntohs(dep->next);
410 if (BeyondPage && HaveDot && HaveDotDot)
411 code = CM_ERROR_NOTEMPTY;
415 lock_ReleaseMutex(&bufferp->mx);
416 buf_Release(bufferp);
418 lock_ReleaseWrite(&scp->rw);
423 * Iterate through all entries in a directory.
424 * When the function funcp is called, the buffer is locked but the
425 * directory vnode is not.
427 * If the retscp parameter is not NULL, the parmp must be a
428 * cm_lookupSearch_t object.
430 long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
431 osi_hyper_t *startOffsetp, cm_user_t *userp, cm_req_t *reqp,
432 cm_scache_t **retscp)
436 cm_dirEntry_t *dep = 0;
439 osi_hyper_t dirLength;
440 osi_hyper_t bufferOffset;
441 osi_hyper_t curOffset;
445 cm_pageHeader_t *pageHeaderp;
447 long nextEntryCookie;
448 int numDirChunks; /* # of 32 byte dir chunks in this entry */
450 /* get the directory size */
451 lock_ObtainWrite(&scp->rw);
452 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_LOOKUP,
453 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
454 lock_ReleaseWrite(&scp->rw);
458 if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
459 return CM_ERROR_NOTDIR;
461 if (retscp) /* if this is a lookup call */
463 cm_lookupSearch_t* sp = parmp;
466 #ifdef AFS_FREELANCE_CLIENT
467 /* Freelance entries never end up in the DNLC because they
468 * do not have an associated cm_server_t
470 !(cm_freelanceEnabled &&
471 sp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
472 sp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
473 #else /* !AFS_FREELANCE_CLIENT */
478 int casefold = sp->caseFold;
479 sp->caseFold = 0; /* we have a strong preference for exact matches */
480 if ( *retscp = cm_dnlcLookup(scp, sp)) /* dnlc hit */
482 sp->caseFold = casefold;
485 sp->caseFold = casefold;
487 /* see if we can find it using the directory hash tables.
488 we can only do exact matches, since the hash is case
498 code = cm_BeginDirOp(scp, userp, reqp, CM_DIRLOCK_READ, &dirop);
502 code = cm_BPlusDirLookup(&dirop, sp->nsearchNamep, &sp->fid);
507 code = cm_DirLookup(&dirop, sp->searchNamep, &sp->fid);
515 sp->ExactFound = TRUE;
516 *retscp = NULL; /* force caller to call cm_GetSCache() */
521 if (sp->caseFold && code == CM_ERROR_INEXACT_MATCH) {
524 sp->ExactFound = FALSE;
525 *retscp = NULL; /* force caller to call cm_GetSCache() */
529 return CM_ERROR_BPLUS_NOMATCH;
537 * XXX We only get the length once. It might change when we drop the
540 dirLength = scp->length;
543 bufferOffset.LowPart = bufferOffset.HighPart = 0;
545 curOffset = *startOffsetp;
547 curOffset.HighPart = 0;
548 curOffset.LowPart = 0;
552 /* make sure that curOffset.LowPart doesn't point to the first
553 * 32 bytes in the 2nd through last dir page, and that it
554 * doesn't point at the first 13 32-byte chunks in the first
555 * dir page, since those are dir and page headers, and don't
556 * contain useful information.
558 temp = curOffset.LowPart & (2048-1);
559 if (curOffset.HighPart == 0 && curOffset.LowPart < 2048) {
560 /* we're in the first page */
561 if (temp < 13*32) temp = 13*32;
564 /* we're in a later dir page */
565 if (temp < 32) temp = 32;
568 /* make sure the low order 5 bits are zero */
571 /* now put temp bits back ito curOffset.LowPart */
572 curOffset.LowPart &= ~(2048-1);
573 curOffset.LowPart |= temp;
575 /* check if we've passed the dir's EOF */
576 if (LargeIntegerGreaterThanOrEqualTo(curOffset, dirLength))
579 /* see if we can use the bufferp we have now; compute in which
580 * page the current offset would be, and check whether that's
581 * the offset of the buffer we have. If not, get the buffer.
583 thyper.HighPart = curOffset.HighPart;
584 thyper.LowPart = curOffset.LowPart & ~(cm_data.buf_blockSize-1);
585 if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
588 lock_ReleaseMutex(&bufferp->mx);
589 buf_Release(bufferp);
593 code = buf_Get(scp, &thyper, &bufferp);
595 /* if buf_Get() fails we do not have a buffer object to lock */
600 lock_ObtainMutex(&bufferp->mx);
601 bufferOffset = thyper;
603 /* now get the data in the cache */
605 lock_ObtainWrite(&scp->rw);
606 code = cm_SyncOp(scp, bufferp, userp, reqp,
608 CM_SCACHESYNC_NEEDCALLBACK
610 | CM_SCACHESYNC_BUFLOCKED);
612 lock_ReleaseWrite(&scp->rw);
615 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
617 if (cm_HaveBuffer(scp, bufferp, 1)) {
618 lock_ReleaseWrite(&scp->rw);
622 /* otherwise, load the buffer and try again */
623 lock_ReleaseMutex(&bufferp->mx);
624 code = cm_GetBuffer(scp, bufferp, NULL, userp,
626 lock_ReleaseWrite(&scp->rw);
627 lock_ObtainMutex(&bufferp->mx);
632 lock_ReleaseMutex(&bufferp->mx);
633 buf_Release(bufferp);
637 } /* if (wrong buffer) ... */
639 /* now we have the buffer containing the entry we're interested
640 * in; copy it out if it represents a non-deleted entry.
642 entryInDir = curOffset.LowPart & (2048-1);
643 entryInBuffer = curOffset.LowPart & (cm_data.buf_blockSize - 1);
645 /* page header will help tell us which entries are free. Page
646 * header can change more often than once per buffer, since
647 * AFS 3 dir page size may be less than (but not more than) a
648 * buffer package buffer.
650 /* only look intra-buffer */
651 temp = curOffset.LowPart & (cm_data.buf_blockSize - 1);
652 temp &= ~(2048 - 1); /* turn off intra-page bits */
653 pageHeaderp = (cm_pageHeader_t *) (bufferp->datap + temp);
655 /* now determine which entry we're looking at in the page. If
656 * it is free (there's a free bitmap at the start of the dir),
657 * we should skip these 32 bytes.
659 slotInPage = (entryInDir & 0x7e0) >> 5;
660 if (!(pageHeaderp->freeBitmap[slotInPage>>3]
661 & (1 << (slotInPage & 0x7)))) {
662 /* this entry is free */
663 numDirChunks = 1; /* only skip this guy */
667 tp = bufferp->datap + entryInBuffer;
668 dep = (cm_dirEntry_t *) tp; /* now points to AFS3 dir entry */
670 /* while we're here, compute the next entry's location, too,
671 * since we'll need it when writing out the cookie into the
672 * dir listing stream.
674 numDirChunks = cm_NameEntries(dep->name, NULL);
676 /* compute the offset of the cookie representing the next entry */
677 nextEntryCookie = curOffset.LowPart
678 + (CM_DIR_CHUNKSIZE * numDirChunks);
680 if (dep->fid.vnode != 0) {
681 /* this is one of the entries to use: it is not deleted */
682 code = (*funcp)(scp, dep, parmp, &curOffset);
685 } /* if we're including this name */
688 /* and adjust curOffset to be where the new cookie is */
690 thyper.LowPart = CM_DIR_CHUNKSIZE * numDirChunks;
691 curOffset = LargeIntegerAdd(thyper, curOffset);
692 } /* while copying data for dir listing */
694 /* release the mutex */
696 lock_ReleaseMutex(&bufferp->mx);
697 buf_Release(bufferp);
702 int cm_NoneUpper(normchar_t *s)
706 if (c >= 'A' && c <= 'Z')
711 int cm_NoneLower(normchar_t *s)
715 if (c >= 'a' && c <= 'z')
720 long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
723 cm_lookupSearch_t *sp;
725 normchar_t matchName[MAX_PATH];
726 int looking_for_short_name = FALSE;
728 sp = (cm_lookupSearch_t *) rockp;
730 if (cm_FsStringToNormString(dep->name, -1, matchName, lengthof(matchName)) == 0) {
731 /* Can't normalize FS string. */
736 match = cm_NormStrCmpI(matchName, sp->nsearchNamep);
738 match = cm_NormStrCmp(matchName, sp->nsearchNamep);
742 && !cm_Is8Dot3(matchName)) {
744 cm_Gen8Dot3NameInt(dep->name, &dep->fid, matchName, NULL);
746 match = cm_NormStrCmpI(matchName, sp->nsearchNamep);
748 match = cm_NormStrCmp(matchName, sp->nsearchNamep);
749 looking_for_short_name = TRUE;
759 if (!sp->caseFold || looking_for_short_name) {
760 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
761 return CM_ERROR_STOPNOW;
765 * If we get here, we are doing a case-insensitive search, and we
766 * have found a match. Now we determine what kind of match it is:
767 * exact, lower-case, upper-case, or none of the above. This is done
768 * in order to choose among matches, if there are more than one.
771 /* Exact matches are the best. */
772 match = cm_NormStrCmp(matchName, sp->nsearchNamep);
775 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
776 return CM_ERROR_STOPNOW;
779 /* Lower-case matches are next. */
782 if (cm_NoneUpper(matchName)) {
787 /* Upper-case matches are next. */
790 if (cm_NoneLower(matchName)) {
795 /* General matches are last. */
801 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
805 /* read the contents of a mount point into the appropriate string.
806 * called with write locked scp, and returns with locked scp.
808 long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
815 if (scp->mountPointStringp[0])
818 /* otherwise, we have to read it in */
819 lock_ReleaseWrite(&scp->rw);
821 thyper.LowPart = thyper.HighPart = 0;
822 code = buf_Get(scp, &thyper, &bufp);
824 lock_ObtainWrite(&scp->rw);
829 code = cm_SyncOp(scp, bufp, userp, reqp, 0,
830 CM_SCACHESYNC_READ | CM_SCACHESYNC_NEEDCALLBACK);
834 cm_SyncOpDone(scp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
836 if (cm_HaveBuffer(scp, bufp, 0))
839 /* otherwise load buffer */
840 code = cm_GetBuffer(scp, bufp, NULL, userp, reqp);
844 /* locked, has callback, has valid data in buffer */
845 if ((tlen = scp->length.LowPart) > MOUNTPOINTLEN - 1)
846 return CM_ERROR_TOOBIG;
848 code = CM_ERROR_INVAL;
852 /* someone else did the work while we were out */
853 if (scp->mountPointStringp[0]) {
858 /* otherwise, copy out the link */
859 memcpy(scp->mountPointStringp, bufp->datap, tlen);
861 /* now make it null-terminated. Note that the original contents of a
862 * link that is a mount point is "#volname." where "." is there just to
863 * be turned into a null. That is, we can trash the last char of the
864 * link without damaging the vol name. This is a stupid convention,
865 * but that's the protocol.
867 scp->mountPointStringp[tlen-1] = 0;
877 /* called with a locked scp and chases the mount point, yielding outScpp.
878 * scp remains write locked, just for simplicity of describing the interface.
880 long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
881 cm_req_t *reqp, cm_scache_t **outScpp)
883 fschar_t *cellNamep = NULL;
884 fschar_t *volNamep = NULL;
889 cm_volume_t *volp = NULL;
898 if (scp->mountRootFid.cell != 0 && scp->mountRootGen >= cm_data.mountRootGen) {
899 tfid = scp->mountRootFid;
900 lock_ReleaseWrite(&scp->rw);
901 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
902 lock_ObtainWrite(&scp->rw);
906 /* parse the volume name */
907 mpNamep = scp->mountPointStringp;
909 return CM_ERROR_NOSUCHPATH;
910 tlen = cm_FsStrLen(scp->mountPointStringp);
911 mtType = *scp->mountPointStringp;
913 cp = cm_FsStrChr(mpNamep, _FS(':'));
915 /* cellular mount point */
916 cellNamep = (fschar_t *)malloc((cp - mpNamep) * sizeof(fschar_t));
917 cm_FsStrCpyN(cellNamep, cp - mpNamep, mpNamep + 1, cp - mpNamep - 1);
918 volNamep = cm_FsStrDup(cp+1);
920 /* now look up the cell */
921 lock_ReleaseWrite(&scp->rw);
922 cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE);
923 lock_ObtainWrite(&scp->rw);
926 volNamep = cm_FsStrDup(mpNamep + 1);
928 cellp = cm_FindCellByID(scp->fid.cell, 0);
932 code = CM_ERROR_NOSUCHCELL;
936 vnLength = cm_FsStrLen(volNamep);
937 if (vnLength >= 8 && cm_FsStrCmp(volNamep + vnLength - 7, ".backup") == 0)
938 targetType = BACKVOL;
939 else if (vnLength >= 10
940 && cm_FsStrCmp(volNamep + vnLength - 9, ".readonly") == 0)
945 /* check for backups within backups */
946 if (targetType == BACKVOL
947 && (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
948 == CM_SCACHEFLAG_RO) {
949 code = CM_ERROR_NOSUCHVOLUME;
953 /* now we need to get the volume */
954 lock_ReleaseWrite(&scp->rw);
955 if (cm_VolNameIsID(volNamep)) {
956 code = cm_FindVolumeByID(cellp, atoi(volNamep), userp, reqp,
957 CM_GETVOL_FLAG_CREATE, &volp);
959 code = cm_FindVolumeByName(cellp, volNamep, userp, reqp,
960 CM_GETVOL_FLAG_CREATE, &volp);
962 lock_ObtainWrite(&scp->rw);
965 afs_uint32 cell, volume;
966 cm_vol_state_t *statep;
968 cell = cellp->cellID;
970 /* if the mt pt originates in a .backup volume (not a .readonly)
971 * and FollowBackupPath is active, and if there is a .backup
972 * volume for the target, then use the .backup of the target
973 * instead of the read-write.
975 if (cm_followBackupPath &&
976 volp->vol[BACKVOL].ID != 0 &&
977 (dscp->flags & (CM_SCACHEFLAG_RO|CM_SCACHEFLAG_PURERO)) == CM_SCACHEFLAG_RO &&
978 (targetType == RWVOL || targetType == ROVOL && volp->vol[ROVOL].ID == 0)
980 targetType = BACKVOL;
982 /* if the mt pt is in a read-only volume (not just a
983 * backup), and if there is a read-only volume for the
984 * target, and if this is a targetType '#' mount point, use
985 * the read-only, otherwise use the one specified.
987 else if (mtType == '#' && targetType == RWVOL &&
988 (scp->flags & CM_SCACHEFLAG_PURERO) &&
989 volp->vol[ROVOL].ID != 0) {
993 lock_ObtainWrite(&volp->rw);
994 statep = cm_VolumeStateByType(volp, targetType);
996 statep->dotdotFid = dscp->fid;
997 lock_ReleaseWrite(&volp->rw);
999 /* the rest of the fid is a magic number */
1000 cm_SetFid(&scp->mountRootFid, cell, volume, 1, 1);
1001 scp->mountRootGen = cm_data.mountRootGen;
1003 tfid = scp->mountRootFid;
1004 lock_ReleaseWrite(&scp->rw);
1005 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
1006 lock_ObtainWrite(&scp->rw);
1019 long cm_LookupInternal(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_user_t *userp,
1020 cm_req_t *reqp, cm_scache_t **outScpp)
1023 int dnlcHit = 1; /* did we hit in the dnlc? yes, we did */
1024 cm_scache_t *tscp = NULL;
1025 cm_scache_t *mountedScp;
1026 cm_lookupSearch_t rock;
1028 normchar_t *nnamep = NULL;
1029 fschar_t *fnamep = NULL;
1033 memset(&rock, 0, sizeof(rock));
1035 if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
1036 && cm_ClientStrCmp(cnamep, _C("..")) == 0) {
1037 if (dscp->dotdotFid.volume == 0)
1038 return CM_ERROR_NOSUCHVOLUME;
1039 rock.fid = dscp->dotdotFid;
1041 } else if (cm_ClientStrCmp(cnamep, _C(".")) == 0) {
1042 rock.fid = dscp->fid;
1046 nnamep = cm_ClientStringToNormStringAlloc(cnamep, -1, NULL);
1048 code = CM_ERROR_NOSUCHFILE;
1051 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
1053 code = CM_ERROR_NOSUCHFILE;
1057 if (flags & CM_FLAG_NOMOUNTCHASE) {
1058 /* In this case, we should go and call cm_Dir* functions
1059 directly since the following cm_ApplyDir() function will
1067 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ, &dirop);
1070 code = cm_BPlusDirLookup(&dirop, nnamep, &rock.fid);
1075 code = cm_DirLookup(&dirop, fnamep, &rock.fid);
1077 cm_EndDirOp(&dirop);
1087 if (code == CM_ERROR_INEXACT_MATCH && (flags & CM_FLAG_CASEFOLD)) {
1094 return CM_ERROR_BPLUS_NOMATCH;
1099 rock.fid.cell = dscp->fid.cell;
1100 rock.fid.volume = dscp->fid.volume;
1101 rock.searchNamep = fnamep;
1102 rock.nsearchNamep = nnamep;
1103 rock.caseFold = (flags & CM_FLAG_CASEFOLD);
1104 rock.hasTilde = ((cm_ClientStrChr(cnamep, '~') != NULL) ? 1 : 0);
1106 /* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
1107 code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
1108 (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
1110 /* code == 0 means we fell off the end of the dir, while stopnow means
1111 * that we stopped early, probably because we found the entry we're
1112 * looking for. Any other non-zero code is an error.
1114 if (code && code != CM_ERROR_STOPNOW) {
1115 /* if the cm_scache_t we are searching in is not a directory
1116 * we must return path not found because the error
1117 * is to describe the final component not an intermediary
1119 if (code == CM_ERROR_NOTDIR) {
1120 if (flags & CM_FLAG_CHECKPATH)
1121 code = CM_ERROR_NOSUCHPATH;
1123 code = CM_ERROR_NOSUCHFILE;
1128 getroot = (dscp==cm_data.rootSCachep) ;
1130 if (!cm_freelanceEnabled || !getroot) {
1131 if (flags & CM_FLAG_CHECKPATH)
1132 code = CM_ERROR_NOSUCHPATH;
1134 code = CM_ERROR_NOSUCHFILE;
1137 else if (!cm_ClientStrChr(cnamep, '#') &&
1138 !cm_ClientStrChr(cnamep, '%') &&
1139 cm_ClientStrCmpI(cnamep, _C("srvsvc")) &&
1140 cm_ClientStrCmpI(cnamep, _C("wkssvc")) &&
1141 cm_ClientStrCmpI(cnamep, _C("ipc$")))
1143 /* nonexistent dir on freelance root, so add it */
1144 fschar_t fullname[CELL_MAXNAMELEN + 1] = "."; /* +1 so that when we skip the . the size is still CELL_MAXNAMELEN */
1147 osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %S",
1148 osi_LogSaveClientString(afsd_logp,cnamep));
1151 * There is an ugly behavior where a share name "foo" will be searched
1152 * for as "fo". If the searched for name differs by an already existing
1153 * symlink or mount point in the Freelance directory, do not add the
1154 * new value automatically.
1158 if (cnamep[0] == '.') {
1159 if (cm_GetCell_Gen(&fnamep[1], &fullname[1], CM_FLAG_CREATE)) {
1161 if (!cm_FreelanceMountPointExists(fullname, 0))
1162 code = cm_FreelanceAddMount(fullname, &fullname[1], "root.cell.",
1164 if ( cm_FsStrCmpI(&fnamep[1], &fullname[1]) &&
1165 !cm_FreelanceMountPointExists(fnamep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0) &&
1166 !cm_FreelanceSymlinkExists(fnamep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0))
1167 code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
1170 if (cm_GetCell_Gen(fnamep, fullname, CM_FLAG_CREATE)) {
1172 if (!cm_FreelanceMountPointExists(fullname, 0))
1173 code = cm_FreelanceAddMount(fullname, fullname, "root.cell.", 0, &rock.fid);
1174 if ( cm_FsStrCmpI(fnamep, fullname) &&
1175 !cm_FreelanceMountPointExists(fnamep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0) &&
1176 !cm_FreelanceSymlinkExists(fnamep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0))
1177 code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
1180 if (!found || code < 0) { /* add mount point failed, so give up */
1181 if (flags & CM_FLAG_CHECKPATH)
1182 code = CM_ERROR_NOSUCHPATH;
1184 code = CM_ERROR_NOSUCHFILE;
1187 tscp = NULL; /* to force call of cm_GetSCache */
1189 if (flags & CM_FLAG_CHECKPATH)
1190 code = CM_ERROR_NOSUCHPATH;
1192 code = CM_ERROR_NOSUCHFILE;
1198 if ( !tscp ) /* we did not find it in the dnlc */
1201 code = cm_GetSCache(&rock.fid, &tscp, userp, reqp);
1205 /* tscp is now held */
1207 lock_ObtainWrite(&tscp->rw);
1208 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1209 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1211 lock_ReleaseWrite(&tscp->rw);
1212 cm_ReleaseSCache(tscp);
1215 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1216 /* tscp is now locked */
1218 if (!(flags & CM_FLAG_NOMOUNTCHASE)
1219 && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
1220 /* mount points are funny: they have a volume name to mount
1223 code = cm_ReadMountPoint(tscp, userp, reqp);
1225 code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
1227 lock_ReleaseWrite(&tscp->rw);
1228 cm_ReleaseSCache(tscp);
1235 lock_ReleaseWrite(&tscp->rw);
1238 /* copy back pointer */
1241 /* insert scache in dnlc */
1242 if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
1243 /* lock the directory entry to prevent racing callback revokes */
1244 lock_ObtainRead(&dscp->rw);
1245 if ( dscp->cbServerp != NULL && dscp->cbExpires > 0 ) {
1246 /* TODO: reuse nnamep from above */
1249 nnamep = cm_ClientStringToNormStringAlloc(cnamep, -1, NULL);
1251 cm_dnlcEnter(dscp, nnamep, tscp);
1253 lock_ReleaseRead(&dscp->rw);
1270 int cm_ExpandSysName(clientchar_t *inp, clientchar_t *outp, long outSizeCch, unsigned int index)
1275 tp = cm_ClientStrRChr(inp, '@');
1277 return 0; /* no @sys */
1279 if (cm_ClientStrCmp(tp, _C("@sys")) != 0)
1280 return 0; /* no @sys */
1282 /* caller just wants to know if this is a valid @sys type of name */
1286 if (index >= cm_sysNameCount)
1289 /* otherwise generate the properly expanded @sys name */
1290 prefixCount = (int)(tp - inp);
1292 cm_ClientStrCpyN(outp, outSizeCch, inp, prefixCount); /* copy out "a." from "a.@sys" */
1293 outp[prefixCount] = 0; /* null terminate the "a." */
1294 cm_ClientStrCat(outp, outSizeCch, cm_sysNameList[index]);/* append i386_nt40 */
1298 long cm_EvaluateVolumeReference(clientchar_t * namep, long flags, cm_user_t * userp,
1299 cm_req_t *reqp, cm_scache_t ** outScpp)
1301 afs_uint32 code = 0;
1302 fschar_t cellName[CELL_MAXNAMELEN];
1303 fschar_t volumeName[VL_MAXNAMELEN];
1307 fschar_t * fnamep = NULL;
1309 cm_cell_t * cellp = NULL;
1310 cm_volume_t * volp = NULL;
1314 int mountType = RWVOL;
1316 osi_Log1(afsd_logp, "cm_EvaluateVolumeReference for string [%S]",
1317 osi_LogSaveClientString(afsd_logp, namep));
1319 if (cm_ClientStrCmpNI(namep, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH) != 0) {
1320 goto _exit_invalid_path;
1323 /* namep is assumed to look like the following:
1325 @vol:<cellname>%<volume>\0
1327 @vol:<cellname>#<volume>\0
1331 fnamep = cm_ClientStringToFsStringAlloc(namep, -1, NULL);
1332 cp = fnamep + CM_PREFIX_VOL_CCH; /* cp points to cell name, hopefully */
1333 tp = cm_FsStrChr(cp, '%');
1335 tp = cm_FsStrChr(cp, '#');
1337 (len = tp - cp) == 0 ||
1338 len > CELL_MAXNAMELEN)
1339 goto _exit_invalid_path;
1340 cm_FsStrCpyN(cellName, lengthof(cellName), cp, len);
1345 cp = tp+1; /* cp now points to volume, supposedly */
1346 cm_FsStrCpy(volumeName, lengthof(volumeName), cp);
1348 /* OK, now we have the cell and the volume */
1349 osi_Log2(afsd_logp, " Found cell [%s] and volume [%s]",
1350 osi_LogSaveFsString(afsd_logp, cellName),
1351 osi_LogSaveFsString(afsd_logp, volumeName));
1353 cellp = cm_GetCell(cellName, CM_FLAG_CREATE);
1354 if (cellp == NULL) {
1355 goto _exit_invalid_path;
1358 len = cm_FsStrLen(volumeName);
1359 if (len >= 8 && cm_FsStrCmp(volumeName + len - 7, ".backup") == 0)
1361 else if (len >= 10 &&
1362 cm_FsStrCmp(volumeName + len - 9, ".readonly") == 0)
1367 if (cm_VolNameIsID(volumeName)) {
1368 code = cm_FindVolumeByID(cellp, atoi(volumeName), userp, reqp,
1369 CM_GETVOL_FLAG_CREATE, &volp);
1371 code = cm_FindVolumeByName(cellp, volumeName, userp, reqp,
1372 CM_GETVOL_FLAG_CREATE, &volp);
1378 if (volType == BACKVOL)
1379 volume = volp->vol[BACKVOL].ID;
1380 else if (volType == ROVOL ||
1381 (volType == RWVOL && mountType == ROVOL && volp->vol[ROVOL].ID != 0))
1382 volume = volp->vol[ROVOL].ID;
1384 volume = volp->vol[RWVOL].ID;
1386 cm_SetFid(&fid, cellp->cellID, volume, 1, 1);
1388 code = cm_GetSCache(&fid, outScpp, userp, reqp);
1401 if (flags & CM_FLAG_CHECKPATH)
1402 return CM_ERROR_NOSUCHPATH;
1404 return CM_ERROR_NOSUCHFILE;
1407 #ifdef DEBUG_REFCOUNT
1408 long cm_LookupDbg(cm_scache_t *dscp, clientchar_t *namep, long flags, cm_user_t *userp,
1409 cm_req_t *reqp, cm_scache_t **outScpp, char * file, long line)
1411 long cm_Lookup(cm_scache_t *dscp, clientchar_t *namep, long flags, cm_user_t *userp,
1412 cm_req_t *reqp, cm_scache_t **outScpp)
1416 clientchar_t tname[AFSPATHMAX];
1417 int sysNameIndex = 0;
1418 cm_scache_t *scp = NULL;
1420 #ifdef DEBUG_REFCOUNT
1421 afsi_log("%s:%d cm_Lookup dscp 0x%p ref %d", file, line, dscp, dscp->refCount, file, line);
1422 osi_Log2(afsd_logp, "cm_Lookup dscp 0x%p ref %d", dscp, dscp->refCount);
1425 if ( cm_ClientStrCmpI(namep,_C(SMB_IOCTL_FILENAME_NOSLASH)) == 0 ) {
1426 if (flags & CM_FLAG_CHECKPATH)
1427 return CM_ERROR_NOSUCHPATH;
1429 return CM_ERROR_NOSUCHFILE;
1432 if (dscp == cm_data.rootSCachep &&
1433 cm_ClientStrCmpNI(namep, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH) == 0) {
1434 return cm_EvaluateVolumeReference(namep, flags, userp, reqp, outScpp);
1437 if (cm_ExpandSysName(namep, NULL, 0, 0) > 0) {
1438 for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
1439 code = cm_ExpandSysName(namep, tname, lengthof(tname), sysNameIndex);
1441 code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
1442 #ifdef DEBUG_REFCOUNT
1443 afsi_log("%s:%d cm_LookupInternal (1) code 0x%x dscp 0x%p ref %d scp 0x%p ref %d", file, line, code, dscp, dscp->refCount, scp, scp ? scp->refCount : 0);
1444 osi_Log3(afsd_logp, "cm_LookupInternal (1) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1452 cm_ReleaseSCache(scp);
1456 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1457 #ifdef DEBUG_REFCOUNT
1458 afsi_log("%s:%d cm_LookupInternal (2) code 0x%x dscp 0x%p ref %d scp 0x%p ref %d", file, line, code, dscp, dscp->refCount, scp, scp ? scp->refCount : 0);
1459 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1466 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1467 #ifdef DEBUG_REFCOUNT
1468 afsi_log("%s:%d cm_LookupInternal (2) code 0x%x dscp 0x%p ref %d scp 0x%p ref %d", file, line, code, dscp, dscp->refCount, scp, scp ? scp->refCount : 0);
1469 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1475 /* None of the possible sysName expansions could be found */
1476 if (flags & CM_FLAG_CHECKPATH)
1477 return CM_ERROR_NOSUCHPATH;
1479 return CM_ERROR_NOSUCHFILE;
1482 /*! \brief Unlink a file name
1484 Encapsulates a call to RXAFS_RemoveFile().
1486 \param[in] dscp cm_scache_t pointing at the directory containing the
1487 name to be unlinked.
1489 \param[in] fnamep Original name to be unlinked. This is the
1490 name that will be passed into the RXAFS_RemoveFile() call.
1491 This parameter is optional. If not provided, the value will
1494 \param[in] came Client name to be unlinked. This name will be used
1495 to update the local directory caches.
1497 \param[in] userp cm_user_t for the request.
1499 \param[in] reqp Request tracker.
1502 long cm_Unlink(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t * cnamep,
1503 cm_user_t *userp, cm_req_t *reqp)
1509 AFSFetchStatus newDirStatus;
1511 struct rx_connection * rxconnp;
1513 cm_scache_t *scp = NULL;
1514 int free_fnamep = FALSE;
1516 if (fnamep == NULL) {
1519 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ, &dirop);
1521 code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
1524 cm_EndDirOp(&dirop);
1531 #ifdef AFS_FREELANCE_CLIENT
1532 if (cm_freelanceEnabled && dscp == cm_data.rootSCachep) {
1533 /* deleting a mount point from the root dir. */
1534 code = cm_FreelanceRemoveMount(fnamep);
1539 code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
1541 /* make sure we don't screw up the dir status during the merge */
1542 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
1544 lock_ObtainWrite(&dscp->rw);
1545 sflags = CM_SCACHESYNC_STOREDATA;
1546 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1547 lock_ReleaseWrite(&dscp->rw);
1549 cm_EndDirOp(&dirop);
1554 afsFid.Volume = dscp->fid.volume;
1555 afsFid.Vnode = dscp->fid.vnode;
1556 afsFid.Unique = dscp->fid.unique;
1558 osi_Log1(afsd_logp, "CALL RemoveFile scp 0x%p", dscp);
1560 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
1564 rxconnp = cm_GetRxConn(connp);
1565 code = RXAFS_RemoveFile(rxconnp, &afsFid, fnamep,
1566 &newDirStatus, &volSync);
1567 rx_PutConnection(rxconnp);
1569 } while (cm_Analyze(connp, userp, reqp, &dscp->fid, &volSync, NULL, NULL, code));
1570 code = cm_MapRPCError(code, reqp);
1573 osi_Log1(afsd_logp, "CALL RemoveFile FAILURE, code 0x%x", code);
1575 osi_Log0(afsd_logp, "CALL RemoveFile SUCCESS");
1578 lock_ObtainWrite(&dirop.scp->dirlock);
1579 dirop.lockType = CM_DIRLOCK_WRITE;
1581 lock_ObtainWrite(&dscp->rw);
1582 cm_dnlcRemove(dscp, cnamep);
1583 cm_SyncOpDone(dscp, NULL, sflags);
1585 cm_MergeStatus(NULL, dscp, &newDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
1586 } else if (code == CM_ERROR_NOSUCHFILE) {
1587 /* windows would not have allowed the request to delete the file
1588 * if it did not believe the file existed. therefore, we must
1589 * have an inconsistent view of the world.
1591 dscp->cbServerp = NULL;
1593 lock_ReleaseWrite(&dscp->rw);
1595 if (code == 0 && cm_CheckDirOpForSingleChange(&dirop) && cnamep) {
1596 cm_DirDeleteEntry(&dirop, fnamep);
1598 cm_BPlusDirDeleteEntry(&dirop, cnamep);
1601 cm_EndDirOp(&dirop);
1604 cm_ReleaseSCache(scp);
1606 lock_ObtainWrite(&scp->rw);
1607 scp->flags |= CM_SCACHEFLAG_DELETED;
1608 lock_ReleaseWrite(&scp->rw);
1619 /* called with a write locked vnode, and fills in the link info.
1620 * returns this the vnode still write locked.
1622 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1629 lock_AssertWrite(&linkScp->rw);
1630 if (!linkScp->mountPointStringp[0]) {
1631 /* read the link data */
1632 lock_ReleaseWrite(&linkScp->rw);
1633 thyper.LowPart = thyper.HighPart = 0;
1634 code = buf_Get(linkScp, &thyper, &bufp);
1635 lock_ObtainWrite(&linkScp->rw);
1639 code = cm_SyncOp(linkScp, bufp, userp, reqp, 0,
1640 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1645 cm_SyncOpDone(linkScp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1647 if (cm_HaveBuffer(linkScp, bufp, 0))
1650 code = cm_GetBuffer(linkScp, bufp, NULL, userp, reqp);
1655 } /* while loop to get the data */
1657 /* now if we still have no link read in,
1658 * copy the data from the buffer */
1659 if ((temp = linkScp->length.LowPart) >= MOUNTPOINTLEN) {
1661 return CM_ERROR_TOOBIG;
1664 /* otherwise, it fits; make sure it is still null (could have
1665 * lost race with someone else referencing this link above),
1666 * and if so, copy in the data.
1668 if (!linkScp->mountPointStringp[0]) {
1669 strncpy(linkScp->mountPointStringp, bufp->datap, temp);
1670 linkScp->mountPointStringp[temp] = 0; /* null terminate */
1672 if ( !strnicmp(linkScp->mountPointStringp, "msdfs:", strlen("msdfs:")) )
1673 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1676 } /* don't have sym link contents cached */
1681 /* called with a held vnode and a path suffix, with the held vnode being a
1682 * symbolic link. Our goal is to generate a new path to interpret, and return
1683 * this new path in newSpaceBufferp. If the new vnode is relative to a dir
1684 * other than the directory containing the symbolic link, then the new root is
1685 * returned in *newRootScpp, otherwise a null is returned there.
1687 long cm_AssembleLink(cm_scache_t *linkScp, fschar_t *pathSuffixp,
1688 cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1689 cm_user_t *userp, cm_req_t *reqp)
1696 *newRootScpp = NULL;
1697 *newSpaceBufferp = NULL;
1699 lock_ObtainWrite(&linkScp->rw);
1700 code = cm_HandleLink(linkScp, userp, reqp);
1704 /* if we may overflow the buffer, bail out; buffer is signficantly
1705 * bigger than max path length, so we don't really have to worry about
1706 * being a little conservative here.
1708 if (cm_FsStrLen(linkScp->mountPointStringp) + cm_FsStrLen(pathSuffixp) + 2
1709 >= CM_UTILS_SPACESIZE) {
1710 code = CM_ERROR_TOOBIG;
1714 tsp = cm_GetSpace();
1715 linkp = linkScp->mountPointStringp;
1716 if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1717 if (strlen(linkp) > cm_mountRootLen)
1718 StringCbCopyA((char *) tsp->data, sizeof(tsp->data), linkp+cm_mountRootLen+1);
1721 *newRootScpp = cm_data.rootSCachep;
1722 cm_HoldSCache(cm_data.rootSCachep);
1723 } else if (linkp[0] == '\\' && linkp[1] == '\\') {
1724 if (!strnicmp(&linkp[2], cm_NetbiosName, (len = (long)strlen(cm_NetbiosName))))
1726 char * p = &linkp[len + 3];
1727 if (strnicmp(p, "all", 3) == 0)
1730 StringCbCopyA(tsp->data, sizeof(tsp->data), p);
1731 for (p = tsp->data; *p; p++) {
1735 *newRootScpp = cm_data.rootSCachep;
1736 cm_HoldSCache(cm_data.rootSCachep);
1738 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1739 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1740 code = CM_ERROR_PATH_NOT_COVERED;
1742 } else if ( linkScp->fileType == CM_SCACHETYPE_DFSLINK ||
1743 !strnicmp(linkp, "msdfs:", (len = (long)strlen("msdfs:"))) ) {
1744 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1745 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1746 code = CM_ERROR_PATH_NOT_COVERED;
1747 } else if (*linkp == '\\' || *linkp == '/') {
1749 /* formerly, this was considered to be from the AFS root,
1750 * but this seems to create problems. instead, we will just
1751 * reject the link */
1752 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp+1);
1753 *newRootScpp = cm_data.rootSCachep;
1754 cm_HoldSCache(cm_data.rootSCachep);
1756 /* we still copy the link data into the response so that
1757 * the user can see what the link points to
1759 linkScp->fileType = CM_SCACHETYPE_INVALID;
1760 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1761 code = CM_ERROR_NOSUCHPATH;
1764 /* a relative link */
1765 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1767 if (pathSuffixp[0] != 0) { /* if suffix string is non-null */
1768 StringCchCatA(tsp->data,lengthof(tsp->data), "\\");
1769 StringCchCatA(tsp->data,lengthof(tsp->data), pathSuffixp);
1773 clientchar_t * cpath = cm_FsStringToClientStringAlloc(tsp->data, -1, NULL);
1774 if (cpath != NULL) {
1775 cm_ClientStrCpy(tsp->wdata, lengthof(tsp->wdata), cpath);
1777 *newSpaceBufferp = tsp;
1779 code = CM_ERROR_NOSUCHPATH;
1786 if (code == CM_ERROR_PATH_NOT_COVERED && reqp->tidPathp && reqp->relPathp) {
1787 cm_VolStatus_Notify_DFS_Mapping(linkScp, reqp->tidPathp, reqp->relPathp);
1792 lock_ReleaseWrite(&linkScp->rw);
1795 #ifdef DEBUG_REFCOUNT
1796 long cm_NameIDbg(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
1797 cm_user_t *userp, clientchar_t *tidPathp, cm_req_t *reqp,
1798 cm_scache_t **outScpp,
1799 char * file, long line)
1801 long cm_NameI(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
1802 cm_user_t *userp, clientchar_t *tidPathp,
1803 cm_req_t *reqp, cm_scache_t **outScpp)
1807 clientchar_t *tp; /* ptr moving through input buffer */
1808 clientchar_t tc; /* temp char */
1809 int haveComponent; /* has new component started? */
1810 clientchar_t component[AFSPATHMAX]; /* this is the new component */
1811 clientchar_t *cp; /* component name being assembled */
1812 cm_scache_t *tscp; /* current location in the hierarchy */
1813 cm_scache_t *nscp; /* next dude down */
1814 cm_scache_t *dirScp; /* last dir we searched */
1815 cm_scache_t *linkScp; /* new root for the symlink we just
1817 cm_space_t *psp; /* space for current path, if we've hit
1819 cm_space_t *tempsp; /* temp vbl */
1820 clientchar_t *restp; /* rest of the pathname to interpret */
1821 int symlinkCount; /* count of # of symlinks traversed */
1822 int extraFlag; /* avoid chasing mt pts for dir cmd */
1823 int phase = 1; /* 1 = tidPathp, 2 = pathp */
1824 #define MAX_FID_COUNT 512
1825 cm_fid_t fids[MAX_FID_COUNT]; /* array of fids processed in this path walk */
1826 int fid_count = 0; /* number of fids processed in this path walk */
1831 #ifdef DEBUG_REFCOUNT
1832 afsi_log("%s:%d cm_NameI rootscp 0x%p ref %d", file, line, rootSCachep, rootSCachep->refCount);
1833 osi_Log4(afsd_logp,"cm_NameI rootscp 0x%p path %S tidpath %S flags 0x%x",
1834 rootSCachep, pathp ? pathp : L"<NULL>", tidPathp ? tidPathp : L"<NULL>",
1849 cm_HoldSCache(tscp);
1857 /* map Unix slashes into DOS ones so we can interpret Unix
1863 if (!haveComponent) {
1866 } else if (tc == 0) {
1880 /* we have a component here */
1881 if (tc == 0 || tc == '\\') {
1882 /* end of the component; we're at the last
1883 * component if tc == 0. However, if the last
1884 * is a symlink, we have more to do.
1886 *cp++ = 0; /* add null termination */
1888 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
1889 extraFlag = CM_FLAG_NOMOUNTCHASE;
1890 code = cm_Lookup(tscp, component,
1892 userp, reqp, &nscp);
1895 if (!cm_ClientStrCmp(component,_C("..")) ||
1896 !cm_ClientStrCmp(component,_C("."))) {
1898 * roll back the fid list until we find the
1899 * fid that matches where we are now. Its not
1900 * necessarily one or two fids because they
1901 * might have been symlinks or mount points or
1902 * both that were crossed.
1904 for ( i=fid_count-1; i>=0; i--) {
1905 if (!cm_FidCmp(&nscp->fid, &fids[i]))
1910 /* add the new fid to the list */
1911 for ( i=0; i<fid_count; i++) {
1912 if ( !cm_FidCmp(&nscp->fid, &fids[i]) ) {
1913 code = CM_ERROR_TOO_MANY_SYMLINKS;
1914 cm_ReleaseSCache(nscp);
1919 if (i == fid_count && fid_count < MAX_FID_COUNT) {
1920 fids[fid_count++] = nscp->fid;
1926 cm_ReleaseSCache(tscp);
1928 cm_ReleaseSCache(dirScp);
1931 if ((code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH) &&
1932 tscp->fileType == CM_SCACHETYPE_SYMLINK) {
1933 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_NOSUCHPATH");
1934 return CM_ERROR_NOSUCHPATH;
1936 osi_Log1(afsd_logp,"cm_NameI code 0x%x", code);
1941 haveComponent = 0; /* component done */
1943 cm_ReleaseSCache(dirScp);
1944 dirScp = tscp; /* for some symlinks */
1945 tscp = nscp; /* already held */
1947 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
1950 cm_ReleaseSCache(dirScp);
1956 /* now, if tscp is a symlink, we should follow it and
1957 * assemble the path again.
1959 lock_ObtainWrite(&tscp->rw);
1960 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1961 CM_SCACHESYNC_GETSTATUS
1962 | CM_SCACHESYNC_NEEDCALLBACK);
1964 lock_ReleaseWrite(&tscp->rw);
1965 cm_ReleaseSCache(tscp);
1968 cm_ReleaseSCache(dirScp);
1973 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1975 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
1976 /* this is a symlink; assemble a new buffer */
1977 lock_ReleaseWrite(&tscp->rw);
1978 if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
1979 cm_ReleaseSCache(tscp);
1982 cm_ReleaseSCache(dirScp);
1987 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_TOO_MANY_SYMLINKS");
1988 return CM_ERROR_TOO_MANY_SYMLINKS;
1998 /* TODO: make this better */
1999 frestp = cm_ClientStringToFsStringAlloc(restp, -1, NULL);
2000 code = cm_AssembleLink(tscp, frestp, &linkScp, &tempsp, userp, reqp);
2004 if (code == 0 && linkScp != NULL) {
2005 if (linkScp == cm_data.rootSCachep)
2008 for ( i=0; i<fid_count; i++) {
2009 if ( !cm_FidCmp(&linkScp->fid, &fids[i]) ) {
2010 code = CM_ERROR_TOO_MANY_SYMLINKS;
2011 cm_ReleaseSCache(linkScp);
2017 if (i == fid_count && fid_count < MAX_FID_COUNT) {
2018 fids[fid_count++] = linkScp->fid;
2023 /* something went wrong */
2024 cm_ReleaseSCache(tscp);
2027 cm_ReleaseSCache(dirScp);
2033 /* otherwise, tempsp has the new path,
2034 * and linkScp is the new root from
2035 * which to interpret that path.
2036 * Continue with the namei processing,
2037 * also doing the bookkeeping for the
2038 * space allocation and tracking the
2039 * vnode reference counts.
2045 cm_ReleaseSCache(tscp);
2050 * now, if linkScp is null, that's
2051 * AssembleLink's way of telling us that
2052 * the sym link is relative to the dir
2053 * containing the link. We have a ref
2054 * to it in dirScp, and we hold it now
2055 * and reuse it as the new spot in the
2063 /* not a symlink, we may be done */
2064 lock_ReleaseWrite(&tscp->rw);
2072 cm_ReleaseSCache(dirScp);
2080 cm_ReleaseSCache(dirScp);
2083 } /* end of a component */
2086 } /* we have a component */
2087 } /* big while loop over all components */
2091 cm_ReleaseSCache(dirScp);
2097 cm_ReleaseSCache(tscp);
2099 #ifdef DEBUG_REFCOUNT
2100 afsi_log("%s:%d cm_NameI code 0x%x outScpp 0x%p ref %d", file, line, code, *outScpp, (*outScpp) ? (*outScpp)->refCount : 0);
2102 osi_Log2(afsd_logp,"cm_NameI code 0x%x outScpp 0x%p", code, *outScpp);
2106 /* called with a dir, and a vnode within the dir that happens to be a symlink.
2107 * We chase the link, and return a held pointer to the target, if it exists,
2108 * in *outScpp. If we succeed, we return 0, otherwise we return an error code
2109 * and do not hold or return a target vnode.
2111 * This is very similar to calling cm_NameI with the last component of a name,
2112 * which happens to be a symlink, except that we've already passed by the name.
2114 * This function is typically called by the directory listing functions, which
2115 * encounter symlinks but need to return the proper file length so programs
2116 * like "more" work properly when they make use of the attributes retrieved from
2119 * The input vnode should not be locked when this function is called.
2121 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
2122 cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
2126 cm_scache_t *newRootScp;
2130 osi_Log1(afsd_logp, "Evaluating symlink scp 0x%p", linkScp);
2132 code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
2136 /* now, if newRootScp is NULL, we're really being told that the symlink
2137 * is relative to the current directory (dscp).
2139 if (newRootScp == NULL) {
2141 cm_HoldSCache(dscp);
2144 code = cm_NameI(newRootScp, spacep->wdata,
2145 CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
2146 userp, NULL, reqp, outScpp);
2148 if (code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH)
2149 code = CM_ERROR_NOSUCHPATH;
2151 /* this stuff is allocated no matter what happened on the namei call,
2153 cm_FreeSpace(spacep);
2154 cm_ReleaseSCache(newRootScp);
2156 if (linkScp == *outScpp) {
2157 cm_ReleaseSCache(*outScpp);
2159 code = CM_ERROR_NOSUCHPATH;
2165 /* for a given entry, make sure that it isn't in the stat cache, and then
2166 * add it to the list of file IDs to be obtained.
2168 * Don't bother adding it if we already have a vnode. Note that the dir
2169 * is locked, so we have to be careful checking the vnode we're thinking of
2170 * processing, to avoid deadlocks.
2172 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
2183 /* Don't overflow bsp. */
2184 if (bsp->counter >= CM_BULKMAX)
2185 return CM_ERROR_STOPNOW;
2187 thyper.LowPart = cm_data.buf_blockSize;
2188 thyper.HighPart = 0;
2189 thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
2191 /* thyper is now the first byte past the end of the record we're
2192 * interested in, and bsp->bufOffset is the first byte of the record
2193 * we're interested in.
2194 * Skip data in the others.
2197 if (LargeIntegerLessThan(*offp, bsp->bufOffset))
2199 if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
2200 return CM_ERROR_STOPNOW;
2201 if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
2204 cm_SetFid(&tfid, scp->fid.cell, scp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
2205 tscp = cm_FindSCache(&tfid);
2207 if (lock_TryWrite(&tscp->rw)) {
2208 /* we have an entry that we can look at */
2209 if (!(tscp->flags & CM_SCACHEFLAG_EACCESS) && cm_HaveCallback(tscp)) {
2210 /* we have a callback on it. Don't bother
2211 * fetching this stat entry, since we're happy
2212 * with the info we have.
2214 lock_ReleaseWrite(&tscp->rw);
2215 cm_ReleaseSCache(tscp);
2218 lock_ReleaseWrite(&tscp->rw);
2220 cm_ReleaseSCache(tscp);
2223 #ifdef AFS_FREELANCE_CLIENT
2224 // yj: if this is a mountpoint under root.afs then we don't want it
2225 // to be bulkstat-ed, instead, we call getSCache directly and under
2226 // getSCache, it is handled specially.
2227 if ( cm_freelanceEnabled &&
2228 tfid.cell==AFS_FAKE_ROOT_CELL_ID &&
2229 tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
2230 !(tfid.vnode==0x1 && tfid.unique==0x1) )
2232 osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
2233 return cm_GetSCache(&tfid, &tscp, NULL, NULL);
2235 #endif /* AFS_FREELANCE_CLIENT */
2238 bsp->fids[i].Volume = scp->fid.volume;
2239 bsp->fids[i].Vnode = tfid.vnode;
2240 bsp->fids[i].Unique = tfid.unique;
2245 cm_TryBulkStatRPC(cm_scache_t *dscp, cm_bulkStat_t *bbp, cm_user_t *userp, cm_req_t *reqp)
2248 AFSCBFids fidStruct;
2249 AFSBulkStats statStruct;
2251 AFSCBs callbackStruct;
2254 cm_callbackRequest_t cbReq;
2260 struct rx_connection * rxconnp;
2261 int inlinebulk = 0; /* Did we use InlineBulkStatus RPC or not? */
2263 /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
2264 * make the calls to create the entries. Handle AFSCBMAX files at a
2267 for (filex = 0; filex < bbp->counter; filex += filesThisCall) {
2268 filesThisCall = bbp->counter - filex;
2269 if (filesThisCall > AFSCBMAX)
2270 filesThisCall = AFSCBMAX;
2272 fidStruct.AFSCBFids_len = filesThisCall;
2273 fidStruct.AFSCBFids_val = &bbp->fids[filex];
2274 statStruct.AFSBulkStats_len = filesThisCall;
2275 statStruct.AFSBulkStats_val = &bbp->stats[filex];
2276 callbackStruct.AFSCBs_len = filesThisCall;
2277 callbackStruct.AFSCBs_val = &bbp->callbacks[filex];
2278 cm_StartCallbackGrantingCall(NULL, &cbReq);
2279 osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
2281 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2285 rxconnp = cm_GetRxConn(connp);
2286 if (!(connp->serverp->flags & CM_SERVERFLAG_NOINLINEBULK)) {
2287 code = RXAFS_InlineBulkStatus(rxconnp, &fidStruct,
2288 &statStruct, &callbackStruct, &volSync);
2289 if (code == RXGEN_OPCODE) {
2290 cm_SetServerNoInlineBulk(connp->serverp, 0);
2296 code = RXAFS_BulkStatus(rxconnp, &fidStruct,
2297 &statStruct, &callbackStruct, &volSync);
2299 rx_PutConnection(rxconnp);
2301 } while (cm_Analyze(connp, userp, reqp, &dscp->fid,
2302 &volSync, NULL, &cbReq, code));
2303 code = cm_MapRPCError(code, reqp);
2305 osi_Log2(afsd_logp, "CALL %sBulkStatus FAILURE code 0x%x",
2306 inlinebulk ? "Inline" : "", code);
2308 osi_Log1(afsd_logp, "CALL %sBulkStatus SUCCESS", inlinebulk ? "Inline" : "");
2310 /* may as well quit on an error, since we're not going to do
2311 * much better on the next immediate call, either.
2314 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2318 /* otherwise, we should do the merges */
2319 for (i = 0; i<filesThisCall; i++) {
2321 cm_SetFid(&tfid, dscp->fid.cell, bbp->fids[j].Volume, bbp->fids[j].Vnode, bbp->fids[j].Unique);
2322 code = cm_GetSCache(&tfid, &scp, userp, reqp);
2326 /* otherwise, if this entry has no callback info,
2329 lock_ObtainWrite(&scp->rw);
2330 /* now, we have to be extra paranoid on merging in this
2331 * information, since we didn't use cm_SyncOp before
2332 * starting the fetch to make sure that no bad races
2333 * were occurring. Specifically, we need to make sure
2334 * we don't obliterate any newer information in the
2335 * vnode than have here.
2337 * Right now, be pretty conservative: if there's a
2338 * callback or a pending call, skip it.
2340 if ((scp->cbServerp == NULL || (scp->flags & CM_SCACHEFLAG_EACCESS))
2342 (CM_SCACHEFLAG_FETCHING
2343 | CM_SCACHEFLAG_STORING
2344 | CM_SCACHEFLAG_SIZESTORING))) {
2345 cm_EndCallbackGrantingCall(scp, &cbReq,
2347 CM_CALLBACK_MAINTAINCOUNT);
2348 cm_MergeStatus(dscp, scp, &bbp->stats[j], &volSync, userp, 0);
2350 lock_ReleaseWrite(&scp->rw);
2351 cm_ReleaseSCache(scp);
2352 } /* all files in the response */
2353 /* now tell it to drop the count,
2354 * after doing the vnode processing above */
2355 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2356 } /* while there are still more files to process */
2358 /* If we did the InlineBulk RPC pull out the return code and log it */
2360 if ((&bbp->stats[0])->errorCode) {
2361 osi_Log1(afsd_logp, "cm_TryBulkStat bulk stat error: %d",
2362 (&bbp->stats[0])->errorCode);
2363 code = (&bbp->stats[0])->errorCode;
2370 /* called with a write locked scp and a pointer to a buffer. Make bulk stat
2371 * calls on all undeleted files in the page of the directory specified.
2374 cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
2380 osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%p", dscp);
2382 /* should be on a buffer boundary */
2383 osi_assertx((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0, "invalid offset");
2385 bbp = malloc(sizeof(cm_bulkStat_t));
2386 memset(bbp, 0, sizeof(cm_bulkStat_t));
2387 bbp->bufOffset = *offsetp;
2389 lock_ReleaseWrite(&dscp->rw);
2390 /* first, assemble the file IDs we need to stat */
2391 code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) bbp, offsetp, userp, reqp, NULL);
2393 /* if we failed, bail out early */
2394 if (code && code != CM_ERROR_STOPNOW) {
2396 lock_ObtainWrite(&dscp->rw);
2400 code = cm_TryBulkStatRPC(dscp, bbp, userp, reqp);
2401 osi_Log1(afsd_logp, "END cm_TryBulkStat code 0x%x", code);
2403 lock_ObtainWrite(&dscp->rw);
2408 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
2412 /* initialize store back mask as inexpensive local variable */
2414 memset(statusp, 0, sizeof(AFSStoreStatus));
2416 /* copy out queued info from scache first, if scp passed in */
2418 if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
2419 statusp->ClientModTime = scp->clientModTime;
2420 mask |= AFS_SETMODTIME;
2421 scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
2426 /* now add in our locally generated request */
2427 if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
2428 statusp->ClientModTime = attrp->clientModTime;
2429 mask |= AFS_SETMODTIME;
2431 if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
2432 statusp->UnixModeBits = attrp->unixModeBits;
2433 mask |= AFS_SETMODE;
2435 if (attrp->mask & CM_ATTRMASK_OWNER) {
2436 statusp->Owner = attrp->owner;
2437 mask |= AFS_SETOWNER;
2439 if (attrp->mask & CM_ATTRMASK_GROUP) {
2440 statusp->Group = attrp->group;
2441 mask |= AFS_SETGROUP;
2444 statusp->Mask = mask;
2447 /* set the file size, and make sure that all relevant buffers have been
2448 * truncated. Ensure that any partially truncated buffers have been zeroed
2449 * to the end of the buffer.
2451 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
2457 /* start by locking out buffer creation */
2458 lock_ObtainWrite(&scp->bufCreateLock);
2460 /* verify that this is a file, not a dir or a symlink */
2461 lock_ObtainWrite(&scp->rw);
2462 code = cm_SyncOp(scp, NULL, userp, reqp, 0,
2463 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2466 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2468 if (scp->fileType != CM_SCACHETYPE_FILE) {
2469 code = CM_ERROR_ISDIR;
2474 if (LargeIntegerLessThan(*sizep, scp->length))
2479 lock_ReleaseWrite(&scp->rw);
2481 /* can't hold scp->rw lock here, since we may wait for a storeback to
2482 * finish if the buffer package is cleaning a buffer by storing it to
2486 buf_Truncate(scp, userp, reqp, sizep);
2488 /* now ensure that file length is short enough, and update truncPos */
2489 lock_ObtainWrite(&scp->rw);
2491 /* make sure we have a callback (so we have the right value for the
2492 * length), and wait for it to be safe to do a truncate.
2494 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
2495 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2496 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2498 /* If we only have 'i' bits, then we should still be able to set
2499 the size of a file we created. */
2500 if (code == CM_ERROR_NOACCESS && scp->creator == userp) {
2501 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
2502 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2503 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2509 if (LargeIntegerLessThan(*sizep, scp->length)) {
2510 /* a real truncation. If truncPos is not set yet, or is bigger
2511 * than where we're truncating the file, set truncPos to this
2516 if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
2517 || LargeIntegerLessThan(*sizep, scp->length)) {
2519 scp->truncPos = *sizep;
2520 scp->mask |= CM_SCACHEMASK_TRUNCPOS;
2522 /* in either case, the new file size has been changed */
2523 scp->length = *sizep;
2524 scp->mask |= CM_SCACHEMASK_LENGTH;
2526 else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
2527 /* really extending the file */
2528 scp->length = *sizep;
2529 scp->mask |= CM_SCACHEMASK_LENGTH;
2532 /* done successfully */
2535 cm_SyncOpDone(scp, NULL,
2536 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2537 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2540 lock_ReleaseWrite(&scp->rw);
2541 lock_ReleaseWrite(&scp->bufCreateLock);
2546 /* set the file size or other attributes (but not both at once) */
2547 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
2551 AFSFetchStatus afsOutStatus;
2555 AFSStoreStatus afsInStatus;
2556 struct rx_connection * rxconnp;
2558 /* handle file length setting */
2559 if (attrp->mask & CM_ATTRMASK_LENGTH)
2560 return cm_SetLength(scp, &attrp->length, userp, reqp);
2562 lock_ObtainWrite(&scp->rw);
2563 /* otherwise, we have to make an RPC to get the status */
2564 code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
2566 lock_ReleaseWrite(&scp->rw);
2569 lock_ConvertWToR(&scp->rw);
2571 /* make the attr structure */
2572 cm_StatusFromAttr(&afsInStatus, scp, attrp);
2574 tfid.Volume = scp->fid.volume;
2575 tfid.Vnode = scp->fid.vnode;
2576 tfid.Unique = scp->fid.unique;
2577 lock_ReleaseRead(&scp->rw);
2579 /* now make the RPC */
2580 osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%p", scp);
2582 code = cm_ConnFromFID(&scp->fid, userp, reqp, &connp);
2586 rxconnp = cm_GetRxConn(connp);
2587 code = RXAFS_StoreStatus(rxconnp, &tfid,
2588 &afsInStatus, &afsOutStatus, &volSync);
2589 rx_PutConnection(rxconnp);
2591 } while (cm_Analyze(connp, userp, reqp,
2592 &scp->fid, &volSync, NULL, NULL, code));
2593 code = cm_MapRPCError(code, reqp);
2596 osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
2598 osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
2600 lock_ObtainWrite(&scp->rw);
2601 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
2603 cm_MergeStatus(NULL, scp, &afsOutStatus, &volSync, userp,
2604 CM_MERGEFLAG_FORCE|CM_MERGEFLAG_STOREDATA);
2606 /* if we're changing the mode bits, discard the ACL cache,
2607 * since we changed the mode bits.
2609 if (afsInStatus.Mask & AFS_SETMODE)
2610 cm_FreeAllACLEnts(scp);
2611 lock_ReleaseWrite(&scp->rw);
2615 long cm_Create(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
2616 cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
2621 cm_callbackRequest_t cbReq;
2624 cm_scache_t *scp = NULL;
2626 AFSStoreStatus inStatus;
2627 AFSFetchStatus updatedDirStatus;
2628 AFSFetchStatus newFileStatus;
2629 AFSCallBack newFileCallback;
2631 struct rx_connection * rxconnp;
2633 fschar_t * fnamep = NULL;
2635 /* can't create names with @sys in them; must expand it manually first.
2636 * return "invalid request" if they try.
2638 if (cm_ExpandSysName(cnamep, NULL, 0, 0)) {
2639 return CM_ERROR_ATSYS;
2642 #ifdef AFS_FREELANCE_CLIENT
2643 /* Freelance root volume does not hold files */
2644 if (cm_freelanceEnabled &&
2645 dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
2646 dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
2648 return CM_ERROR_NOACCESS;
2650 #endif /* AFS_FREELANCE_CLIENT */
2652 /* before starting the RPC, mark that we're changing the file data, so
2653 * that someone who does a chmod will know to wait until our call
2656 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2657 lock_ObtainWrite(&dscp->rw);
2658 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2659 lock_ReleaseWrite(&dscp->rw);
2661 cm_StartCallbackGrantingCall(NULL, &cbReq);
2663 cm_EndDirOp(&dirop);
2670 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
2672 cm_StatusFromAttr(&inStatus, NULL, attrp);
2674 /* try the RPC now */
2675 osi_Log1(afsd_logp, "CALL CreateFile scp 0x%p", dscp);
2677 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2681 dirAFSFid.Volume = dscp->fid.volume;
2682 dirAFSFid.Vnode = dscp->fid.vnode;
2683 dirAFSFid.Unique = dscp->fid.unique;
2685 rxconnp = cm_GetRxConn(connp);
2686 code = RXAFS_CreateFile(connp->rxconnp, &dirAFSFid, fnamep,
2687 &inStatus, &newAFSFid, &newFileStatus,
2688 &updatedDirStatus, &newFileCallback,
2690 rx_PutConnection(rxconnp);
2692 } while (cm_Analyze(connp, userp, reqp,
2693 &dscp->fid, &volSync, NULL, &cbReq, code));
2694 code = cm_MapRPCError(code, reqp);
2697 osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
2699 osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
2702 lock_ObtainWrite(&dirop.scp->dirlock);
2703 dirop.lockType = CM_DIRLOCK_WRITE;
2705 lock_ObtainWrite(&dscp->rw);
2706 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2708 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
2710 lock_ReleaseWrite(&dscp->rw);
2712 /* now try to create the file's entry, too, but be careful to
2713 * make sure that we don't merge in old info. Since we weren't locking
2714 * out any requests during the file's creation, we may have pretty old
2718 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
2719 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2721 lock_ObtainWrite(&scp->rw);
2722 scp->creator = userp; /* remember who created it */
2723 if (!cm_HaveCallback(scp)) {
2724 cm_MergeStatus(dscp, scp, &newFileStatus, &volSync,
2726 cm_EndCallbackGrantingCall(scp, &cbReq,
2727 &newFileCallback, 0);
2730 lock_ReleaseWrite(&scp->rw);
2734 /* make sure we end things properly */
2736 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2738 if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
2739 cm_DirCreateEntry(&dirop, fnamep, &newFid);
2741 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
2744 cm_EndDirOp(&dirop);
2753 cm_ReleaseSCache(scp);
2758 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
2762 code = buf_CleanVnode(scp, userp, reqp);
2764 lock_ObtainWrite(&scp->rw);
2766 if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
2767 | CM_SCACHEMASK_CLIENTMODTIME
2768 | CM_SCACHEMASK_LENGTH))
2769 code = cm_StoreMini(scp, userp, reqp);
2771 if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) {
2772 code = (scp->flags & CM_SCACHEFLAG_OVERQUOTA) ? CM_ERROR_QUOTA : CM_ERROR_SPACE;
2773 scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE);
2776 lock_ReleaseWrite(&scp->rw);
2781 long cm_MakeDir(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
2782 cm_user_t *userp, cm_req_t *reqp, cm_scache_t **scpp)
2787 cm_callbackRequest_t cbReq;
2790 cm_scache_t *scp = NULL;
2792 AFSStoreStatus inStatus;
2793 AFSFetchStatus updatedDirStatus;
2794 AFSFetchStatus newDirStatus;
2795 AFSCallBack newDirCallback;
2797 struct rx_connection * rxconnp;
2799 fschar_t * fnamep = NULL;
2801 /* can't create names with @sys in them; must expand it manually first.
2802 * return "invalid request" if they try.
2804 if (cm_ExpandSysName(cnamep, NULL, 0, 0)) {
2805 return CM_ERROR_ATSYS;
2808 #ifdef AFS_FREELANCE_CLIENT
2809 /* Freelance root volume does not hold subdirectories */
2810 if (cm_freelanceEnabled &&
2811 dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
2812 dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
2814 return CM_ERROR_NOACCESS;
2816 #endif /* AFS_FREELANCE_CLIENT */
2818 /* before starting the RPC, mark that we're changing the directory
2819 * data, so that someone who does a chmod on the dir will wait until
2820 * our call completes.
2822 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2823 lock_ObtainWrite(&dscp->rw);
2824 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2825 lock_ReleaseWrite(&dscp->rw);
2827 cm_StartCallbackGrantingCall(NULL, &cbReq);
2829 cm_EndDirOp(&dirop);
2836 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
2837 cm_StatusFromAttr(&inStatus, NULL, attrp);
2839 /* try the RPC now */
2840 osi_Log1(afsd_logp, "CALL MakeDir scp 0x%p", dscp);
2842 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2846 dirAFSFid.Volume = dscp->fid.volume;
2847 dirAFSFid.Vnode = dscp->fid.vnode;
2848 dirAFSFid.Unique = dscp->fid.unique;
2850 rxconnp = cm_GetRxConn(connp);
2851 code = RXAFS_MakeDir(connp->rxconnp, &dirAFSFid, fnamep,
2852 &inStatus, &newAFSFid, &newDirStatus,
2853 &updatedDirStatus, &newDirCallback,
2855 rx_PutConnection(rxconnp);
2857 } while (cm_Analyze(connp, userp, reqp,
2858 &dscp->fid, &volSync, NULL, &cbReq, code));
2859 code = cm_MapRPCError(code, reqp);
2862 osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
2864 osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
2867 lock_ObtainWrite(&dirop.scp->dirlock);
2868 dirop.lockType = CM_DIRLOCK_WRITE;
2870 lock_ObtainWrite(&dscp->rw);
2871 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2873 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
2875 lock_ReleaseWrite(&dscp->rw);
2877 /* now try to create the new dir's entry, too, but be careful to
2878 * make sure that we don't merge in old info. Since we weren't locking
2879 * out any requests during the file's creation, we may have pretty old
2883 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
2884 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2886 lock_ObtainWrite(&scp->rw);
2887 if (!cm_HaveCallback(scp)) {
2888 cm_MergeStatus(dscp, scp, &newDirStatus, &volSync,
2890 cm_EndCallbackGrantingCall(scp, &cbReq,
2891 &newDirCallback, 0);
2894 lock_ReleaseWrite(&scp->rw);
2898 /* make sure we end things properly */
2900 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2902 if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
2903 cm_DirCreateEntry(&dirop, fnamep, &newFid);
2905 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
2908 cm_EndDirOp(&dirop);
2916 cm_ReleaseSCache(scp);
2919 /* and return error code */
2923 long cm_Link(cm_scache_t *dscp, clientchar_t *cnamep, cm_scache_t *sscp, long flags,
2924 cm_user_t *userp, cm_req_t *reqp)
2929 AFSFid existingAFSFid;
2930 AFSFetchStatus updatedDirStatus;
2931 AFSFetchStatus newLinkStatus;
2933 struct rx_connection * rxconnp;
2935 fschar_t * fnamep = NULL;
2937 if (dscp->fid.cell != sscp->fid.cell ||
2938 dscp->fid.volume != sscp->fid.volume) {
2939 return CM_ERROR_CROSSDEVLINK;
2942 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2943 lock_ObtainWrite(&dscp->rw);
2944 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2945 lock_ReleaseWrite(&dscp->rw);
2947 cm_EndDirOp(&dirop);
2952 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
2954 /* try the RPC now */
2955 osi_Log1(afsd_logp, "CALL Link scp 0x%p", dscp);
2957 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2960 dirAFSFid.Volume = dscp->fid.volume;
2961 dirAFSFid.Vnode = dscp->fid.vnode;
2962 dirAFSFid.Unique = dscp->fid.unique;
2964 existingAFSFid.Volume = sscp->fid.volume;
2965 existingAFSFid.Vnode = sscp->fid.vnode;
2966 existingAFSFid.Unique = sscp->fid.unique;
2968 rxconnp = cm_GetRxConn(connp);
2969 code = RXAFS_Link(rxconnp, &dirAFSFid, fnamep, &existingAFSFid,
2970 &newLinkStatus, &updatedDirStatus, &volSync);
2971 rx_PutConnection(rxconnp);
2972 osi_Log1(afsd_logp," RXAFS_Link returns 0x%x", code);
2974 } while (cm_Analyze(connp, userp, reqp,
2975 &dscp->fid, &volSync, NULL, NULL, code));
2977 code = cm_MapRPCError(code, reqp);
2980 osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
2982 osi_Log0(afsd_logp, "CALL Link SUCCESS");
2985 lock_ObtainWrite(&dirop.scp->dirlock);
2986 dirop.lockType = CM_DIRLOCK_WRITE;
2988 lock_ObtainWrite(&dscp->rw);
2989 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2991 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
2993 lock_ReleaseWrite(&dscp->rw);
2996 if (cm_CheckDirOpForSingleChange(&dirop)) {
2997 cm_DirCreateEntry(&dirop, fnamep, &sscp->fid);
2999 cm_BPlusDirCreateEntry(&dirop, cnamep, &sscp->fid);
3003 cm_EndDirOp(&dirop);
3010 long cm_SymLink(cm_scache_t *dscp, clientchar_t *cnamep, fschar_t *contentsp, long flags,
3011 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
3019 AFSStoreStatus inStatus;
3020 AFSFetchStatus updatedDirStatus;
3021 AFSFetchStatus newLinkStatus;
3023 struct rx_connection * rxconnp;
3025 fschar_t *fnamep = NULL;
3027 /* before starting the RPC, mark that we're changing the directory data,
3028 * so that someone who does a chmod on the dir will wait until our
3031 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
3032 lock_ObtainWrite(&dscp->rw);
3033 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3034 lock_ReleaseWrite(&dscp->rw);
3036 cm_EndDirOp(&dirop);
3041 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3043 cm_StatusFromAttr(&inStatus, NULL, attrp);
3045 /* try the RPC now */
3046 osi_Log1(afsd_logp, "CALL Symlink scp 0x%p", dscp);
3048 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3052 dirAFSFid.Volume = dscp->fid.volume;
3053 dirAFSFid.Vnode = dscp->fid.vnode;
3054 dirAFSFid.Unique = dscp->fid.unique;
3056 rxconnp = cm_GetRxConn(connp);
3057 code = RXAFS_Symlink(rxconnp, &dirAFSFid, fnamep, contentsp,
3058 &inStatus, &newAFSFid, &newLinkStatus,
3059 &updatedDirStatus, &volSync);
3060 rx_PutConnection(rxconnp);
3062 } while (cm_Analyze(connp, userp, reqp,
3063 &dscp->fid, &volSync, NULL, NULL, code));
3064 code = cm_MapRPCError(code, reqp);
3067 osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
3069 osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
3072 lock_ObtainWrite(&dirop.scp->dirlock);
3073 dirop.lockType = CM_DIRLOCK_WRITE;
3075 lock_ObtainWrite(&dscp->rw);
3076 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3078 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
3080 lock_ReleaseWrite(&dscp->rw);
3083 if (cm_CheckDirOpForSingleChange(&dirop)) {
3084 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3086 cm_DirCreateEntry(&dirop, fnamep, &newFid);
3088 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
3092 cm_EndDirOp(&dirop);
3094 /* now try to create the new dir's entry, too, but be careful to
3095 * make sure that we don't merge in old info. Since we weren't locking
3096 * out any requests during the file's creation, we may have pretty old
3100 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3101 code = cm_GetSCache(&newFid, &scp, userp, reqp);
3103 lock_ObtainWrite(&scp->rw);
3104 if (!cm_HaveCallback(scp)) {
3105 cm_MergeStatus(dscp, scp, &newLinkStatus, &volSync,
3108 lock_ReleaseWrite(&scp->rw);
3109 cm_ReleaseSCache(scp);
3115 /* and return error code */
3119 /*! \brief Remove a directory
3121 Encapsulates a call to RXAFS_RemoveDir().
3123 \param[in] dscp cm_scache_t for the directory containing the
3124 directory to be removed.
3126 \param[in] fnamep This will be the original name of the directory
3127 as known to the file server. It will be passed in to RXAFS_RemoveDir().
3128 This parameter is optional. If it is not provided the value
3131 \param[in] cnamep Normalized name used to update the local
3134 \param[in] userp cm_user_t for the request.
3136 \param[in] reqp Request tracker.
3138 long cm_RemoveDir(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t *cnamep, cm_user_t *userp, cm_req_t *reqp)
3144 AFSFetchStatus updatedDirStatus;
3146 struct rx_connection * rxconnp;
3148 cm_scache_t *scp = NULL;
3149 int free_fnamep = FALSE;
3151 if (fnamep == NULL) {
3154 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ, &dirop);
3156 code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
3159 cm_EndDirOp(&dirop);
3166 code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
3170 /* before starting the RPC, mark that we're changing the directory data,
3171 * so that someone who does a chmod on the dir will wait until our
3174 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
3175 lock_ObtainWrite(&dscp->rw);
3176 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3177 lock_ReleaseWrite(&dscp->rw);
3179 cm_EndDirOp(&dirop);
3184 /* try the RPC now */
3185 osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%p", dscp);
3187 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3191 dirAFSFid.Volume = dscp->fid.volume;
3192 dirAFSFid.Vnode = dscp->fid.vnode;
3193 dirAFSFid.Unique = dscp->fid.unique;
3195 rxconnp = cm_GetRxConn(connp);
3196 code = RXAFS_RemoveDir(rxconnp, &dirAFSFid, fnamep,
3197 &updatedDirStatus, &volSync);
3198 rx_PutConnection(rxconnp);
3200 } while (cm_Analyze(connp, userp, reqp,
3201 &dscp->fid, &volSync, NULL, NULL, code));
3202 code = cm_MapRPCErrorRmdir(code, reqp);
3205 osi_Log1(afsd_logp, "CALL RemoveDir FAILURE, code 0x%x", code);
3207 osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
3210 lock_ObtainWrite(&dirop.scp->dirlock);
3211 dirop.lockType = CM_DIRLOCK_WRITE;
3213 lock_ObtainWrite(&dscp->rw);
3214 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3216 cm_dnlcRemove(dscp, cnamep);
3217 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
3219 lock_ReleaseWrite(&dscp->rw);
3222 if (cm_CheckDirOpForSingleChange(&dirop) && cnamep != NULL) {
3223 cm_DirDeleteEntry(&dirop, fnamep);
3225 cm_BPlusDirDeleteEntry(&dirop, cnamep);
3229 cm_EndDirOp(&dirop);
3232 cm_ReleaseSCache(scp);
3234 lock_ObtainWrite(&scp->rw);
3235 scp->flags |= CM_SCACHEFLAG_DELETED;
3236 lock_ReleaseWrite(&scp->rw);
3244 /* and return error code */
3248 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
3250 /* grab mutex on contents */
3251 lock_ObtainWrite(&scp->rw);
3253 /* reset the prefetch info */
3254 scp->prefetch.base.LowPart = 0; /* base */
3255 scp->prefetch.base.HighPart = 0;
3256 scp->prefetch.end.LowPart = 0; /* and end */
3257 scp->prefetch.end.HighPart = 0;
3259 /* release mutex on contents */
3260 lock_ReleaseWrite(&scp->rw);
3266 /*! \brief Rename a file or directory
3268 Encapsulates a RXAFS_Rename() call.
3270 \param[in] oldDscp cm_scache_t for the directory containing the old
3273 \param[in] oldNamep The original old name known to the file server.
3274 This is the name that will be passed into the RXAFS_Rename().
3275 If it is not provided, it will be looked up.
3277 \param[in] normalizedOldNamep Normalized old name. This is used for
3278 updating local directory caches.
3280 \param[in] newDscp cm_scache_t for the directory containing the new
3283 \param[in] newNamep New name. Normalized.
3285 \param[in] userp cm_user_t for the request.
3287 \param[in,out] reqp Request tracker.
3290 long cm_Rename(cm_scache_t *oldDscp, fschar_t *oldNamep, clientchar_t *cOldNamep,
3291 cm_scache_t *newDscp, clientchar_t *cNewNamep, cm_user_t *userp,
3296 AFSFid oldDirAFSFid;
3297 AFSFid newDirAFSFid;
3299 AFSFetchStatus updatedOldDirStatus;
3300 AFSFetchStatus updatedNewDirStatus;
3303 struct rx_connection * rxconnp;
3304 cm_dirOp_t oldDirOp;
3307 cm_dirOp_t newDirOp;
3308 fschar_t * newNamep = NULL;
3309 int free_oldNamep = FALSE;
3310 cm_scache_t *oldScp = NULL, *newScp = NULL;
3312 if (cOldNamep == NULL || cNewNamep == NULL ||
3313 cm_ClientStrLen(cOldNamep) == 0 ||
3314 cm_ClientStrLen(cNewNamep) == 0)
3315 return CM_ERROR_INVAL;
3318 * Before we permit the operation, make sure that we do not already have
3319 * an object in the destination directory that has a case-insensitive match
3320 * for this name UNLESS the matching object is the object we are renaming.
3322 code = cm_Lookup(oldDscp, cOldNamep, 0, userp, reqp, &oldScp);
3324 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S old name lookup failed",
3325 oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3329 code = cm_Lookup(newDscp, cNewNamep, CM_FLAG_CASEFOLD, userp, reqp, &newScp);
3331 /* found a matching object with the new name */
3332 if (cm_FidCmp(&oldScp->fid, &newScp->fid)) {
3333 /* and they don't match so return an error */
3334 osi_Log2(afsd_logp, "cm_Rename newDscp 0x%p cNewName %S new name already exists",
3335 newDscp, osi_LogSaveStringW(afsd_logp, cNewNamep));
3336 code = CM_ERROR_EXISTS;
3338 cm_ReleaseSCache(newScp);
3340 } else if (code == CM_ERROR_AMBIGUOUS_FILENAME) {
3341 code = CM_ERROR_EXISTS;
3345 cm_ReleaseSCache(oldScp);
3351 if (oldNamep == NULL) {
3354 code = cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_READ, &oldDirOp);
3356 code = cm_BPlusDirLookupOriginalName(&oldDirOp, cOldNamep, &oldNamep);
3358 free_oldNamep = TRUE;
3359 cm_EndDirOp(&oldDirOp);
3363 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S Original Name lookup failed",
3364 oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3370 /* before starting the RPC, mark that we're changing the directory data,
3371 * so that someone who does a chmod on the dir will wait until our call
3372 * completes. We do this in vnode order so that we don't deadlock,
3373 * which makes the code a little verbose.
3375 if (oldDscp == newDscp) {
3376 /* check for identical names */
3377 if (cm_ClientStrCmp(cOldNamep, cNewNamep) == 0) {
3378 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_RENAME_IDENTICAL",
3380 code = CM_ERROR_RENAME_IDENTICAL;
3385 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3386 lock_ObtainWrite(&oldDscp->rw);
3387 cm_dnlcRemove(oldDscp, cOldNamep);
3388 cm_dnlcRemove(oldDscp, cNewNamep);
3389 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3390 CM_SCACHESYNC_STOREDATA);
3391 lock_ReleaseWrite(&oldDscp->rw);
3393 cm_EndDirOp(&oldDirOp);
3397 /* two distinct dir vnodes */
3399 if (oldDscp->fid.cell != newDscp->fid.cell ||
3400 oldDscp->fid.volume != newDscp->fid.volume) {
3401 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_CROSSDEVLINK",
3403 code = CM_ERROR_CROSSDEVLINK;
3407 /* shouldn't happen that we have distinct vnodes for two
3408 * different files, but could due to deliberate attack, or
3409 * stale info. Avoid deadlocks and quit now.
3411 if (oldDscp->fid.vnode == newDscp->fid.vnode) {
3412 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p vnode collision",
3414 code = CM_ERROR_CROSSDEVLINK;
3418 if (oldDscp->fid.vnode < newDscp->fid.vnode) {
3419 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3420 lock_ObtainWrite(&oldDscp->rw);
3421 cm_dnlcRemove(oldDscp, cOldNamep);
3422 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3423 CM_SCACHESYNC_STOREDATA);
3424 lock_ReleaseWrite(&oldDscp->rw);
3426 cm_EndDirOp(&oldDirOp);
3428 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE, &newDirOp);
3429 lock_ObtainWrite(&newDscp->rw);
3430 cm_dnlcRemove(newDscp, cNewNamep);
3431 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3432 CM_SCACHESYNC_STOREDATA);
3433 lock_ReleaseWrite(&newDscp->rw);
3435 cm_EndDirOp(&newDirOp);
3437 /* cleanup first one */
3438 lock_ObtainWrite(&oldDscp->rw);
3439 cm_SyncOpDone(oldDscp, NULL,
3440 CM_SCACHESYNC_STOREDATA);
3441 lock_ReleaseWrite(&oldDscp->rw);
3442 cm_EndDirOp(&oldDirOp);
3447 /* lock the new vnode entry first */
3448 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE, &newDirOp);
3449 lock_ObtainWrite(&newDscp->rw);
3450 cm_dnlcRemove(newDscp, cNewNamep);
3451 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3452 CM_SCACHESYNC_STOREDATA);
3453 lock_ReleaseWrite(&newDscp->rw);
3455 cm_EndDirOp(&newDirOp);
3457 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3458 lock_ObtainWrite(&oldDscp->rw);
3459 cm_dnlcRemove(oldDscp, cOldNamep);
3460 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3461 CM_SCACHESYNC_STOREDATA);
3462 lock_ReleaseWrite(&oldDscp->rw);
3464 cm_EndDirOp(&oldDirOp);
3466 /* cleanup first one */
3467 lock_ObtainWrite(&newDscp->rw);
3468 cm_SyncOpDone(newDscp, NULL,
3469 CM_SCACHESYNC_STOREDATA);
3470 lock_ReleaseWrite(&newDscp->rw);
3471 cm_EndDirOp(&newDirOp);
3475 } /* two distinct vnodes */
3482 newNamep = cm_ClientStringToFsStringAlloc(cNewNamep, -1, NULL);
3484 /* try the RPC now */
3485 osi_Log2(afsd_logp, "CALL Rename old scp 0x%p new scp 0x%p",
3488 code = cm_ConnFromFID(&oldDscp->fid, userp, reqp, &connp);
3492 oldDirAFSFid.Volume = oldDscp->fid.volume;
3493 oldDirAFSFid.Vnode = oldDscp->fid.vnode;
3494 oldDirAFSFid.Unique = oldDscp->fid.unique;
3495 newDirAFSFid.Volume = newDscp->fid.volume;
3496 newDirAFSFid.Vnode = newDscp->fid.vnode;
3497 newDirAFSFid.Unique = newDscp->fid.unique;
3499 rxconnp = cm_GetRxConn(connp);
3500 code = RXAFS_Rename(rxconnp, &oldDirAFSFid, oldNamep,
3501 &newDirAFSFid, newNamep,
3502 &updatedOldDirStatus, &updatedNewDirStatus,
3504 rx_PutConnection(rxconnp);
3506 } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
3507 &volSync, NULL, NULL, code));
3508 code = cm_MapRPCError(code, reqp);
3511 osi_Log1(afsd_logp, "CALL Rename FAILURE, code 0x%x", code);
3513 osi_Log0(afsd_logp, "CALL Rename SUCCESS");
3515 /* update the individual stat cache entries for the directories */
3517 lock_ObtainWrite(&oldDirOp.scp->dirlock);
3518 oldDirOp.lockType = CM_DIRLOCK_WRITE;
3520 lock_ObtainWrite(&oldDscp->rw);
3521 cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
3524 cm_MergeStatus(NULL, oldDscp, &updatedOldDirStatus, &volSync,
3525 userp, CM_MERGEFLAG_DIROP);
3526 lock_ReleaseWrite(&oldDscp->rw);
3529 if (cm_CheckDirOpForSingleChange(&oldDirOp)) {
3532 diropCode = cm_BPlusDirLookup(&oldDirOp, cOldNamep, &fileFid);
3533 if (diropCode == CM_ERROR_INEXACT_MATCH)
3535 else if (diropCode == EINVAL)
3537 diropCode = cm_DirLookup(&oldDirOp, oldNamep, &fileFid);
3539 if (diropCode == 0) {
3541 diropCode = cm_DirCreateEntry(&oldDirOp, newNamep, &fileFid);
3543 cm_BPlusDirCreateEntry(&oldDirOp, cNewNamep, &fileFid);
3547 if (diropCode == 0) {
3548 diropCode = cm_DirDeleteEntry(&oldDirOp, oldNamep);
3550 cm_BPlusDirDeleteEntry(&oldDirOp, cOldNamep);
3556 cm_EndDirOp(&oldDirOp);
3558 /* and update it for the new one, too, if necessary */
3561 lock_ObtainWrite(&newDirOp.scp->dirlock);
3562 newDirOp.lockType = CM_DIRLOCK_WRITE;
3564 lock_ObtainWrite(&newDscp->rw);
3565 cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
3567 cm_MergeStatus(NULL, newDscp, &updatedNewDirStatus, &volSync,
3568 userp, CM_MERGEFLAG_DIROP);
3569 lock_ReleaseWrite(&newDscp->rw);
3572 /* we only make the local change if we successfully made
3573 the change in the old directory AND there was only one
3574 change in the new directory */
3575 if (diropCode == 0 && cm_CheckDirOpForSingleChange(&newDirOp)) {
3576 cm_DirCreateEntry(&newDirOp, newNamep, &fileFid);
3578 cm_BPlusDirCreateEntry(&newDirOp, cNewNamep, &fileFid);
3582 cm_EndDirOp(&newDirOp);
3591 /* and return error code */
3595 /* Byte range locks:
3597 The OpenAFS Windows client has to fake byte range locks given no
3598 server side support for such locks. This is implemented as keyed
3599 byte range locks on the cache manager.
3601 Keyed byte range locks:
3603 Each cm_scache_t structure keeps track of a list of keyed locks.
3604 The key for a lock identifies an owner of a set of locks (referred
3605 to as a client). Each key is represented by a value. The set of
3606 key values used within a specific cm_scache_t structure form a
3607 namespace that has a scope of just that cm_scache_t structure. The
3608 same key value can be used with another cm_scache_t structure and
3609 correspond to a completely different client. However it is
3610 advantageous for the SMB or IFS layer to make sure that there is a
3611 1-1 mapping between client and keys over all cm_scache_t objects.
3613 Assume a client C has key Key(C) (although, since the scope of the
3614 key is a cm_scache_t, the key can be Key(C,S), where S is the
3615 cm_scache_t. But assume a 1-1 relation between keys and clients).
3616 A byte range (O,+L) denotes byte addresses (O) through (O+L-1)
3617 inclusive (a.k.a. [O,O+L-1]). The function Key(x) is implemented
3618 through cm_generateKey() function for both SMB and IFS.
3620 The list of locks for a cm_scache_t object S is maintained in
3621 S->fileLocks. The cache manager will set a lock on the AFS file
3622 server in order to assert the locks in S->fileLocks. If only
3623 shared locks are in place for S, then the cache manager will obtain
3624 a LockRead lock, while if there are any exclusive locks, it will
3625 obtain a LockWrite lock. If the exclusive locks are all released
3626 while the shared locks remain, then the cache manager will
3627 downgrade the lock from LockWrite to LockRead. Similarly, if an
3628 exclusive lock is obtained when only shared locks exist, then the
3629 cache manager will try to upgrade the lock from LockRead to
3632 Each lock L owned by client C maintains a key L->key such that
3633 L->key == Key(C), the effective range defined by L->LOffset and
3634 L->LLength such that the range of bytes affected by the lock is
3635 (L->LOffset, +L->LLength), a type maintained in L->LockType which
3636 is either exclusive or shared.
3640 A lock exists iff it is in S->fileLocks for some cm_scache_t
3641 S. Existing locks are in one of the following states: ACTIVE,
3642 WAITLOCK, WAITUNLOCK, LOST, DELETED.
3644 The following sections describe each lock and the associated
3647 1. ACTIVE: A lock L is ACTIVE iff the cache manager has asserted
3648 the lock with the AFS file server. This type of lock can be
3649 exercised by a client to read or write to the locked region (as
3652 1.1 ACTIVE->LOST: When the AFS file server fails to extend a
3653 server lock that was required to assert the lock. Before
3654 marking the lock as lost, the cache manager checks if the file
3655 has changed on the server. If the file has not changed, then
3656 the cache manager will attempt to obtain a new server lock
3657 that is sufficient to assert the client side locks for the
3658 file. If any of these fail, the lock is marked as LOST.
3659 Otherwise, it is left as ACTIVE.
3661 1.2 ACTIVE->DELETED: Lock is released.
3663 2. WAITLOCK: A lock is in a WAITLOCK state if the cache manager
3664 grants the lock but the lock is yet to be asserted with the AFS
3665 file server. Once the file server grants the lock, the state
3666 will transition to an ACTIVE lock.
3668 2.1 WAITLOCK->ACTIVE: The server granted the lock.
3670 2.2 WAITLOCK->DELETED: Lock is abandoned, or timed out during
3673 2.3 WAITLOCK->LOST: One or more locks from this client were
3674 marked as LOST. No further locks will be granted to this
3675 client until all lost locks are removed.
3677 3. WAITUNLOCK: A lock is in a WAITUNLOCK state if the cache manager
3678 receives a request for a lock that conflicts with an existing
3679 ACTIVE or WAITLOCK lock. The lock will be placed in the queue
3680 and will be granted at such time the conflicting locks are
3681 removed, at which point the state will transition to either
3684 3.1 WAITUNLOCK->ACTIVE: The conflicting lock was removed. The
3685 current serverLock is sufficient to assert this lock, or a
3686 sufficient serverLock is obtained.
3688 3.2 WAITUNLOCK->WAITLOCK: The conflicting lock was removed,
3689 however the required serverLock is yet to be asserted with the
3692 3.3 WAITUNLOCK->DELETED: The lock is abandoned, timed out or
3695 3.5 WAITUNLOCK->LOST: One or more locks from this client were
3696 marked as LOST. No further locks will be granted to this
3697 client until all lost locks are removed.
3699 4. LOST: A lock L is LOST if the server lock that was required to
3700 assert the lock could not be obtained or if it could not be
3701 extended, or if other locks by the same client were LOST.
3702 Essentially, once a lock is LOST, the contract between the cache
3703 manager and that specific client is no longer valid.
3705 The cache manager rechecks the server lock once every minute and
3706 extends it as appropriate. If this is not done for 5 minutes,
3707 the AFS file server will release the lock (the 5 minute timeout
3708 is based on current file server code and is fairly arbitrary).
3709 Once released, the lock cannot be re-obtained without verifying
3710 that the contents of the file hasn't been modified since the
3711 time the lock was released. Re-obtaining the lock without
3712 verifying this may lead to data corruption. If the lock can not
3713 be obtained safely, then all active locks for the cm_scache_t
3716 4.1 LOST->DELETED: The lock is released.
3718 5. DELETED: The lock is no longer relevant. Eventually, it will
3719 get removed from the cm_scache_t. In the meantime, it will be
3720 treated as if it does not exist.
3722 5.1 DELETED->not exist: The lock is removed from the
3725 The following are classifications of locks based on their state.
3727 6* A lock L is ACCEPTED if it is ACTIVE or WAITLOCK. These locks
3728 have been accepted by the cache manager, but may or may not have
3729 been granted back to the client.
3731 7* A lock L is QUEUED if it is ACTIVE, WAITLOCK or WAITUNLOCK.
3733 8* A lock L is WAITING if it is WAITLOCK or WAITUNLOCK.
3737 A client C can READ range (Offset,+Length) of a file represented by
3738 cm_scache_t S iff (1):
3740 1. for all _a_ in (Offset,+Length), all of the following is true:
3742 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3743 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3746 1.2 For each LOST lock L in S->fileLocks such that _a_ in
3747 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3750 (When locks are lost on an cm_scache_t, all locks are lost. By
3751 4.2 (below), if there is an exclusive LOST lock, then there
3752 can't be any overlapping ACTIVE locks.)
3754 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
3756 2. for all _a_ in (Offset,+Length), one of the following is true:
3758 2.1 Byte _a_ of S is unowned (as specified in 1.1) AND there
3759 does not exist a LOST lock L such that _a_ in
3760 (L->LOffset,+L->LLength).
3762 2.2 Byte _a_ of S is owned by C under lock L (as specified in
3763 1.2) AND L->LockType is exclusive.
3765 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
3767 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
3770 3.1 If L->LockType is exclusive then there does NOT exist a
3771 ACCEPTED lock M in S->fileLocks such that _a_ in
3772 (M->LOffset,+M->LLength).
3774 (If we count all QUEUED locks then we hit cases such as
3775 cascading waiting locks where the locks later on in the queue
3776 can be granted without compromising file integrity. On the
3777 other hand if only ACCEPTED locks are considered, then locks
3778 that were received earlier may end up waiting for locks that
3779 were received later to be unlocked. The choice of ACCEPTED
3780 locks was made to mimic the Windows byte range lock
3783 3.2 If L->LockType is shared then for each ACCEPTED lock M in
3784 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
3785 M->LockType is shared.
3787 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
3789 4.1 M->key != Key(C)
3791 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
3792 and (M->LOffset,+M->LLength) do not intersect.
3794 (Note: If a client loses a lock, it loses all locks.
3795 Subsequently, it will not be allowed to obtain any more locks
3796 until all existing LOST locks that belong to the client are
3797 released. Once all locks are released by a single client,
3798 there exists no further contract between the client and AFS
3799 about the contents of the file, hence the client can then
3800 proceed to obtain new locks and establish a new contract.
3802 This doesn't quite work as you think it should, because most
3803 applications aren't built to deal with losing locks they
3804 thought they once had. For now, we don't have a good
3805 solution to lost locks.
3807 Also, for consistency reasons, we have to hold off on
3808 granting locks that overlap exclusive LOST locks.)
3810 A client C can only unlock locks L in S->fileLocks which have
3813 The representation and invariants are as follows:
3815 - Each cm_scache_t structure keeps:
3817 - A queue of byte-range locks (cm_scache_t::fileLocks) which
3818 are of type cm_file_lock_t.
3820 - A record of the highest server-side lock that has been
3821 obtained for this object (cm_scache_t::serverLock), which is
3822 one of (-1), LockRead, LockWrite.
3824 - A count of ACCEPTED exclusive and shared locks that are in the
3825 queue (cm_scache_t::sharedLocks and
3826 cm_scache_t::exclusiveLocks)
3828 - Each cm_file_lock_t structure keeps:
3830 - The type of lock (cm_file_lock_t::LockType)
3832 - The key associated with the lock (cm_file_lock_t::key)
3834 - The offset and length of the lock (cm_file_lock_t::LOffset
3835 and cm_file_lock_t::LLength)
3837 - The state of the lock.
3839 - Time of issuance or last successful extension
3841 Semantic invariants:
3843 I1. The number of ACCEPTED locks in S->fileLocks are
3844 (S->sharedLocks + S->exclusiveLocks)
3846 External invariants:
3848 I3. S->serverLock is the lock that we have asserted with the
3849 AFS file server for this cm_scache_t.
3851 I4. S->serverLock == LockRead iff there is at least one ACTIVE
3852 shared lock, but no ACTIVE exclusive locks.
3854 I5. S->serverLock == LockWrite iff there is at least one ACTIVE
3857 I6. If L is a LOST lock, then for each lock M in S->fileLocks,
3858 M->key == L->key IMPLIES M is LOST or DELETED.
3863 #define IS_LOCK_ACTIVE(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == 0)
3865 #define IS_LOCK_WAITLOCK(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_WAITLOCK)
3867 #define IS_LOCK_WAITUNLOCK(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_WAITUNLOCK)
3869 #define IS_LOCK_LOST(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_LOST)
3871 #define IS_LOCK_DELETED(lockp) (((lockp)->flags & CM_FILELOCK_FLAG_DELETED) == CM_FILELOCK_FLAG_DELETED)
3874 #define IS_LOCK_ACCEPTED(lockp) (IS_LOCK_ACTIVE(lockp) || IS_LOCK_WAITLOCK(lockp))
3877 #define IS_LOCK_CLIENTONLY(lockp) ((((lockp)->scp->flags & CM_SCACHEFLAG_RO) == CM_SCACHEFLAG_RO) || (((lockp)->flags & CM_FILELOCK_FLAG_CLIENTONLY) == CM_FILELOCK_FLAG_CLIENTONLY))
3880 #define INTERSECT_RANGE(r1,r2) (((r2).offset+(r2).length) > (r1).offset && ((r1).offset +(r1).length) > (r2).offset)
3883 #define CONTAINS_RANGE(r1,r2) (((r2).offset+(r2).length) <= ((r1).offset+(r1).length) && (r1).offset <= (r2).offset)
3885 #if defined(VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS) && !defined(LOCK_TESTING)
3886 #define SCP_SUPPORTS_BRLOCKS(scp) ((scp)->cbServerp && ((scp)->cbServerp->capabilities & VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS))
3888 #define SCP_SUPPORTS_BRLOCKS(scp) (1)
3891 #define SERVERLOCKS_ENABLED(scp) (!((scp)->flags & CM_SCACHEFLAG_RO) && cm_enableServerLocks && SCP_SUPPORTS_BRLOCKS(scp))
3893 #if defined(VICED_CAPABILITY_WRITELOCKACL)
3894 #define SCP_SUPPORTS_WRITELOCKACL(scp) ((scp)->cbServerp && ((scp->cbServerp->capabilities & VICED_CAPABILITY_WRITELOCKACL)))
3896 #define SCP_SUPPORTS_WRITELOCKACL(scp) (0)
3898 /* This should really be defined in any build that this code is being
3900 #error VICED_CAPABILITY_WRITELOCKACL not defined.
3903 static void cm_LockRangeSubtract(cm_range_t * pos, const cm_range_t * neg)
3905 afs_int64 int_begin;
3908 int_begin = MAX(pos->offset, neg->offset);
3909 int_end = MIN(pos->offset+pos->length, neg->offset+neg->length);
3911 if (int_begin < int_end) {
3912 if (int_begin == pos->offset) {
3913 pos->length = pos->offset + pos->length - int_end;
3914 pos->offset = int_end;
3915 } else if (int_end == pos->offset + pos->length) {
3916 pos->length = int_begin - pos->offset;
3919 /* We only subtract ranges if the resulting range is
3920 contiguous. If we try to support non-contigous ranges, we
3921 aren't actually improving performance. */
3925 /* Called with scp->rw held. Returns 0 if all is clear to read the
3926 specified range by the client identified by key.
3928 long cm_LockCheckRead(cm_scache_t *scp,
3929 LARGE_INTEGER LOffset,
3930 LARGE_INTEGER LLength,
3933 #ifndef ADVISORY_LOCKS
3935 cm_file_lock_t *fileLock;
3939 int substract_ranges = FALSE;
3941 range.offset = LOffset.QuadPart;
3942 range.length = LLength.QuadPart;
3946 1. for all _a_ in (Offset,+Length), all of the following is true:
3948 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3949 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3952 1.2 For each LOST lock L in S->fileLocks such that _a_ in
3953 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3958 lock_ObtainRead(&cm_scacheLock);
3960 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
3962 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
3964 if (INTERSECT_RANGE(range, fileLock->range)) {
3965 if (IS_LOCK_ACTIVE(fileLock)) {
3966 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
3968 /* If there is an active lock for this client, it
3969 is safe to substract ranges.*/
3970 cm_LockRangeSubtract(&range, &fileLock->range);
3971 substract_ranges = TRUE;
3973 if (fileLock->lockType != LockRead) {
3974 code = CM_ERROR_LOCK_CONFLICT;
3978 /* even if the entire range is locked for reading,
3979 we still can't grant the lock at this point
3980 because the client may have lost locks. That
3981 is, unless we have already seen an active lock
3982 belonging to the client, in which case there
3983 can't be any lost locks for this client. */
3984 if (substract_ranges)
3985 cm_LockRangeSubtract(&range, &fileLock->range);
3987 } else if (IS_LOCK_LOST(fileLock) &&
3988 (cm_KeyEquals(&fileLock->key, &key, 0) || fileLock->lockType == LockWrite)) {
3989 code = CM_ERROR_BADFD;
3995 lock_ReleaseRead(&cm_scacheLock);
3997 osi_Log4(afsd_logp, "cm_LockCheckRead scp 0x%x offset %d length %d code 0x%x",
3998 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
4009 /* Called with scp->rw held. Returns 0 if all is clear to write the
4010 specified range by the client identified by key.
4012 long cm_LockCheckWrite(cm_scache_t *scp,
4013 LARGE_INTEGER LOffset,
4014 LARGE_INTEGER LLength,
4017 #ifndef ADVISORY_LOCKS
4019 cm_file_lock_t *fileLock;
4024 range.offset = LOffset.QuadPart;
4025 range.length = LLength.QuadPart;
4028 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
4030 2. for all _a_ in (Offset,+Length), one of the following is true:
4032 2.1 Byte _a_ of S is unowned AND there does not exist a LOST
4033 lock L such that _a_ in (L->LOffset,+L->LLength).
4035 2.2 Byte _a_ of S is owned by C under lock L AND L->LockType is
4039 lock_ObtainRead(&cm_scacheLock);
4041 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
4043 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4045 if (INTERSECT_RANGE(range, fileLock->range)) {
4046 if (IS_LOCK_ACTIVE(fileLock)) {
4047 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4048 if (fileLock->lockType == LockWrite) {
4050 /* if there is an active lock for this client, it
4051 is safe to substract ranges */
4052 cm_LockRangeSubtract(&range, &fileLock->range);
4054 code = CM_ERROR_LOCK_CONFLICT;
4058 code = CM_ERROR_LOCK_CONFLICT;
4061 } else if (IS_LOCK_LOST(fileLock)) {
4062 code = CM_ERROR_BADFD;
4068 lock_ReleaseRead(&cm_scacheLock);
4070 osi_Log4(afsd_logp, "cm_LockCheckWrite scp 0x%x offset %d length %d code 0x%x",
4071 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
4082 /* Called with cm_scacheLock write locked */
4083 static cm_file_lock_t * cm_GetFileLock(void) {
4086 l = (cm_file_lock_t *) cm_freeFileLocks;
4088 osi_QRemove(&cm_freeFileLocks, &l->q);
4090 l = malloc(sizeof(cm_file_lock_t));
4091 osi_assertx(l, "null cm_file_lock_t");
4094 memset(l, 0, sizeof(cm_file_lock_t));
4099 /* Called with cm_scacheLock write locked */
4100 static void cm_PutFileLock(cm_file_lock_t *l) {
4101 osi_QAdd(&cm_freeFileLocks, &l->q);
4104 /* called with scp->rw held. May release it during processing, but
4105 leaves it held on exit. */
4106 long cm_IntSetLock(cm_scache_t * scp, cm_user_t * userp, int lockType,
4112 struct rx_connection * rxconnp;
4114 afs_uint32 reqflags = reqp->flags;
4116 tfid.Volume = scp->fid.volume;
4117 tfid.Vnode = scp->fid.vnode;
4118 tfid.Unique = scp->fid.unique;
4121 osi_Log2(afsd_logp, "CALL SetLock scp 0x%p for lock %d", scp, lockType);
4123 reqp->flags |= CM_REQ_NORETRY;
4124 lock_ReleaseWrite(&scp->rw);
4127 code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4131 rxconnp = cm_GetRxConn(connp);
4132 code = RXAFS_SetLock(rxconnp, &tfid, lockType,
4134 rx_PutConnection(rxconnp);
4136 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
4139 code = cm_MapRPCError(code, reqp);
4141 osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
4143 osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
4146 lock_ObtainWrite(&scp->rw);
4147 reqp->flags = reqflags;
4151 /* called with scp->rw held. Releases it during processing */
4152 long cm_IntReleaseLock(cm_scache_t * scp, cm_user_t * userp,
4158 struct rx_connection * rxconnp;
4161 tfid.Volume = scp->fid.volume;
4162 tfid.Vnode = scp->fid.vnode;
4163 tfid.Unique = scp->fid.unique;
4166 lock_ReleaseWrite(&scp->rw);
4168 osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
4171 code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4175 rxconnp = cm_GetRxConn(connp);
4176 code = RXAFS_ReleaseLock(rxconnp, &tfid, &volSync);
4177 rx_PutConnection(rxconnp);
4179 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
4181 code = cm_MapRPCError(code, reqp);
4184 "CALL ReleaseLock FAILURE, code 0x%x", code);
4187 "CALL ReleaseLock SUCCESS");
4189 lock_ObtainWrite(&scp->rw);
4194 /* called with scp->rw held. May release it during processing, but
4195 will exit with lock held.
4199 - 0 if the user has permission to get the specified lock for the scp
4201 - CM_ERROR_NOACCESS if not
4203 Any other error from cm_SyncOp will be sent down untranslated.
4205 If CM_ERROR_NOACCESS is returned and lock_type is LockRead, then
4206 phas_insert (if non-NULL) will receive a boolean value indicating
4207 whether the user has INSERT permission or not.
4209 long cm_LockCheckPerms(cm_scache_t * scp,
4216 long code = 0, code2 = 0;
4218 /* lock permissions are slightly tricky because of the 'i' bit.
4219 If the user has PRSFS_LOCK, she can read-lock the file. If the
4220 user has PRSFS_WRITE, she can write-lock the file. However, if
4221 the user has PRSFS_INSERT, then she can write-lock new files,
4222 but not old ones. Since we don't have information about
4223 whether a file is new or not, we assume that if the user owns
4224 the scp, then she has the permissions that are granted by
4227 osi_Log3(afsd_logp, "cm_LockCheckPerms for scp[0x%p] type[%d] user[0x%p]",
4228 scp, lock_type, userp);
4230 if (lock_type == LockRead)
4231 rights |= PRSFS_LOCK;
4232 else if (lock_type == LockWrite)
4233 rights |= PRSFS_WRITE | PRSFS_LOCK;
4236 osi_assertx(FALSE, "invalid lock type");
4241 *phas_insert = FALSE;
4243 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
4244 CM_SCACHESYNC_GETSTATUS |
4245 CM_SCACHESYNC_NEEDCALLBACK);
4247 if (phas_insert && scp->creator == userp) {
4249 /* If this file was created by the user, then we check for
4250 PRSFS_INSERT. If the file server is recent enough, then
4251 this should be sufficient for her to get a write-lock (but
4252 not necessarily a read-lock). VICED_CAPABILITY_WRITELOCKACL
4253 indicates whether a file server supports getting write
4254 locks when the user only has PRSFS_INSERT.
4256 If the file was not created by the user we skip the check
4257 because the INSERT bit will not apply to this user even
4261 code2 = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
4262 CM_SCACHESYNC_GETSTATUS |
4263 CM_SCACHESYNC_NEEDCALLBACK);
4265 if (code2 == CM_ERROR_NOACCESS) {
4266 osi_Log0(afsd_logp, "cm_LockCheckPerms user has no INSERT bits");
4268 *phas_insert = TRUE;
4269 osi_Log0(afsd_logp, "cm_LockCheckPerms user has INSERT bits");
4273 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
4275 osi_Log1(afsd_logp, "cm_LockCheckPerms returning code %d", code);
4280 /* called with scp->rw held */
4281 long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
4282 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
4284 int allowWait, cm_user_t *userp, cm_req_t *reqp,
4285 cm_file_lock_t **lockpp)
4288 int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
4289 cm_file_lock_t *fileLock;
4292 int wait_unlock = FALSE;
4293 int force_client_lock = FALSE;
4295 osi_Log4(afsd_logp, "cm_Lock scp 0x%x type 0x%x offset %d length %d",
4296 scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
4297 osi_Log4(afsd_logp, "... allowWait %d key <0x%x, 0x%x, 0x%x>", allowWait,
4298 key.process_id, key.session_id, key.file_id);
4301 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
4303 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
4306 3.1 If L->LockType is exclusive then there does NOT exist a
4307 ACCEPTED lock M in S->fileLocks such that _a_ in
4308 (M->LOffset,+M->LLength).
4310 3.2 If L->LockType is shared then for each ACCEPTED lock M in
4311 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
4312 M->LockType is shared.
4314 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
4316 4.1 M->key != Key(C)
4318 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
4319 and (M->LOffset,+M->LLength) do not intersect.
4322 range.offset = LOffset.QuadPart;
4323 range.length = LLength.QuadPart;
4325 lock_ObtainRead(&cm_scacheLock);
4327 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4329 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4331 if (IS_LOCK_LOST(fileLock)) {
4332 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4333 code = CM_ERROR_BADFD;
4335 } else if (fileLock->lockType == LockWrite && INTERSECT_RANGE(range, fileLock->range)) {
4336 code = CM_ERROR_WOULDBLOCK;
4342 /* we don't need to check for deleted locks here since deleted
4343 locks are dequeued from scp->fileLocks */
4344 if (IS_LOCK_ACCEPTED(fileLock) &&
4345 INTERSECT_RANGE(range, fileLock->range)) {
4347 if ((sLockType & LOCKING_ANDX_SHARED_LOCK) == 0 ||
4348 fileLock->lockType != LockRead) {
4350 code = CM_ERROR_WOULDBLOCK;
4356 lock_ReleaseRead(&cm_scacheLock);
4358 if (code == 0 && SERVERLOCKS_ENABLED(scp)) {
4359 if (Which == scp->serverLock ||
4360 (Which == LockRead && scp->serverLock == LockWrite)) {
4364 /* we already have the lock we need */
4365 osi_Log3(afsd_logp, " we already have the correct lock. exclusives[%d], shared[%d], serverLock[%d]",
4366 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4368 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4370 /* special case: if we don't have permission to read-lock
4371 the file, then we force a clientside lock. This is to
4372 compensate for applications that obtain a read-lock for
4373 reading files off of directories that don't grant
4374 read-locks to the user. */
4375 if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4377 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4378 osi_Log0(afsd_logp, " User has no read-lock perms, but has INSERT perms.");
4381 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4382 force_client_lock = TRUE;
4386 } else if ((scp->exclusiveLocks > 0) ||
4387 (scp->sharedLocks > 0 && scp->serverLock != LockRead)) {
4390 /* We are already waiting for some other lock. We should
4391 wait for the daemon to catch up instead of generating a
4392 flood of SetLock calls. */
4393 osi_Log3(afsd_logp, " already waiting for other lock. exclusives[%d], shared[%d], serverLock[%d]",
4394 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4396 /* see if we have permission to create the lock in the
4398 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4400 code = CM_ERROR_WOULDBLOCK;
4401 else if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4403 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4405 " User has no read-lock perms, but has INSERT perms.");
4406 code = CM_ERROR_WOULDBLOCK;
4409 " User has no read-lock perms. Forcing client-side lock");
4410 force_client_lock = TRUE;
4414 /* leave any other codes as-is */
4418 int check_data_version = FALSE;
4421 /* first check if we have permission to elevate or obtain
4423 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4425 if (code == CM_ERROR_NOACCESS && Which == LockRead &&
4426 (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp))) {
4427 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4428 force_client_lock = TRUE;
4433 /* has_insert => (Which == LockRead, code == CM_ERROR_NOACCESS) */
4435 if (scp->serverLock == LockRead && Which == LockWrite) {
4437 /* We want to escalate the lock to a LockWrite.
4438 * Unfortunately that's not really possible without
4439 * letting go of the current lock. But for now we do
4443 " attempting to UPGRADE from LockRead to LockWrite.");
4445 " dataVersion on scp: %I64d", scp->dataVersion);
4447 /* we assume at this point (because scp->serverLock
4448 was valid) that we had a valid server lock. */
4449 scp->lockDataVersion = scp->dataVersion;
4450 check_data_version = TRUE;
4452 code = cm_IntReleaseLock(scp, userp, reqp);
4455 /* We couldn't release the lock */
4458 scp->serverLock = -1;
4462 /* We need to obtain a server lock of type Which in order
4463 * to assert this file lock */
4464 #ifndef AGGRESSIVE_LOCKS
4467 newLock = LockWrite;
4470 code = cm_IntSetLock(scp, userp, newLock, reqp);
4472 #ifdef AGGRESSIVE_LOCKS
4473 if ((code == CM_ERROR_WOULDBLOCK ||
4474 code == CM_ERROR_NOACCESS) && newLock != Which) {
4475 /* we wanted LockRead. We tried LockWrite. Now try
4480 osi_assertx(newLock == LockRead, "lock type not read");
4482 code = cm_IntSetLock(scp, userp, newLock, reqp);
4486 if (code == CM_ERROR_NOACCESS) {
4487 if (Which == LockRead) {
4488 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4490 /* We requested a read-lock, but we have permission to
4491 * get a write-lock. Try that */
4493 tcode = cm_LockCheckPerms(scp, LockWrite, userp, reqp, NULL);
4496 newLock = LockWrite;
4498 osi_Log0(afsd_logp, " User has 'i' perms and the request was for a LockRead. Trying to get a LockWrite instead");
4500 code = cm_IntSetLock(scp, userp, newLock, reqp);
4503 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4504 force_client_lock = TRUE;
4506 } else if (Which == LockWrite &&
4507 scp->creator == userp && !SCP_SUPPORTS_WRITELOCKACL(scp)) {
4510 /* Special case: if the lock request was for a
4511 * LockWrite and the user owns the file and we weren't
4512 * allowed to obtain the serverlock, we either lost a
4513 * race (the permissions changed from under us), or we
4514 * have 'i' bits, but we aren't allowed to lock the
4517 /* check if we lost a race... */
4518 tcode = cm_LockCheckPerms(scp, Which, userp, reqp, NULL);
4521 osi_Log0(afsd_logp, " User has 'i' perms but can't obtain write locks. Using client-side locks.");
4522 force_client_lock = TRUE;
4527 if (code == 0 && check_data_version &&
4528 scp->dataVersion != scp->lockDataVersion) {
4529 /* We lost a race. Although we successfully obtained
4530 * a lock, someone modified the file in between. The
4531 * locks have all been technically lost. */
4534 " Data version mismatch while upgrading lock.");
4536 " Data versions before=%I64d, after=%I64d",
4537 scp->lockDataVersion,
4540 " Releasing stale lock for scp 0x%x", scp);
4542 code = cm_IntReleaseLock(scp, userp, reqp);
4544 scp->serverLock = -1;
4546 code = CM_ERROR_INVAL;
4547 } else if (code == 0) {
4548 scp->serverLock = newLock;
4549 scp->lockDataVersion = scp->dataVersion;
4553 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4554 scp->serverLock == -1) {
4555 /* Oops. We lost the lock. */
4556 cm_LockMarkSCacheLost(scp);
4559 } else if (code == 0) { /* server locks not enabled */
4561 " Skipping server lock for scp");
4566 if (code != 0 && !force_client_lock) {
4567 /* Special case error translations
4569 Applications don't expect certain errors from a
4570 LockFile/UnlockFile call. We need to translate some error
4571 code to codes that apps expect and handle. */
4573 /* We shouldn't actually need to handle this case since we
4574 simulate locks for RO scps anyway. */
4575 if (code == CM_ERROR_READONLY) {
4576 osi_Log0(afsd_logp, " Reinterpreting CM_ERROR_READONLY as CM_ERROR_NOACCESS");
4577 code = CM_ERROR_NOACCESS;
4581 if (code == 0 || (code == CM_ERROR_WOULDBLOCK && allowWait) ||
4582 force_client_lock) {
4584 /* clear the error if we are forcing a client lock, so we
4585 don't get confused later. */
4586 if (force_client_lock && code != CM_ERROR_WOULDBLOCK)
4589 lock_ObtainWrite(&cm_scacheLock);
4590 fileLock = cm_GetFileLock();
4591 lock_ReleaseWrite(&cm_scacheLock);
4593 fileLock->fid = scp->fid;
4595 fileLock->key = key;
4596 fileLock->lockType = Which;
4598 fileLock->userp = userp;
4599 fileLock->range = range;
4600 fileLock->flags = (code == 0 ? 0 :
4602 CM_FILELOCK_FLAG_WAITUNLOCK :
4603 CM_FILELOCK_FLAG_WAITLOCK));
4605 if (force_client_lock || !SERVERLOCKS_ENABLED(scp))
4606 fileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
4608 fileLock->lastUpdate = (code == 0 && !force_client_lock) ? time(NULL) : 0;
4610 lock_ObtainWrite(&cm_scacheLock);
4611 osi_QAddT(&scp->fileLocksH, &scp->fileLocksT, &fileLock->fileq);
4612 cm_HoldSCacheNoLock(scp);
4613 fileLock->scp = scp;
4614 osi_QAdd(&cm_allFileLocks, &fileLock->q);
4615 lock_ReleaseWrite(&cm_scacheLock);
4621 if (IS_LOCK_CLIENTONLY(fileLock)) {
4623 } else if (IS_LOCK_ACCEPTED(fileLock)) {
4624 if (Which == LockRead)
4627 scp->exclusiveLocks++;
4631 "cm_Lock Lock added 0x%p flags 0x%x to scp [0x%p]",
4632 fileLock, fileLock->flags, scp);
4634 " exclusives[%d] shared[%d] client[%d] serverLock[%d]",
4635 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4636 (int)(signed char) scp->serverLock);
4639 "cm_Lock Rejecting lock (code = 0x%x)", code);
4645 /* Called with scp->rw held */
4646 long cm_UnlockByKey(cm_scache_t * scp,
4653 cm_file_lock_t *fileLock;
4654 osi_queue_t *q, *qn;
4657 osi_Log4(afsd_logp, "cm_UnlockByKey scp 0x%p key <0x%x,0x%x,0x%x",
4658 scp, key.process_id, key.session_id, key.file_id);
4659 osi_Log1(afsd_logp, " flags=0x%x", flags);
4661 lock_ObtainWrite(&cm_scacheLock);
4663 for (q = scp->fileLocksH; q; q = qn) {
4666 fileLock = (cm_file_lock_t *)
4667 ((char *) q - offsetof(cm_file_lock_t, fileq));
4670 osi_Log4(afsd_logp, " Checking lock[0x%x] range[%d,+%d] type[%d]",
4672 (unsigned long) fileLock->range.offset,
4673 (unsigned long) fileLock->range.length,
4674 fileLock->lockType);
4675 osi_Log4(afsd_logp, " key<0x%x, 0x%x, 0x%x> flags[0x%x]",
4676 fileLock->key.process_id, fileLock->key.session_id, fileLock->key.file_id,
4679 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
4680 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
4681 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4683 fileLock->fid.volume,
4684 fileLock->fid.vnode,
4685 fileLock->fid.unique);
4686 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4687 fileLock->scp->fid.cell,
4688 fileLock->scp->fid.volume,
4689 fileLock->scp->fid.vnode,
4690 fileLock->scp->fid.unique);
4691 osi_assertx(FALSE, "invalid fid value");
4695 if (!IS_LOCK_DELETED(fileLock) &&
4696 cm_KeyEquals(&fileLock->key, &key, flags)) {
4697 osi_Log3(afsd_logp, "...Unlock range [%d,+%d] type %d",
4698 fileLock->range.offset,
4699 fileLock->range.length,
4700 fileLock->lockType);
4702 if (scp->fileLocksT == q)
4703 scp->fileLocksT = osi_QPrev(q);
4704 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
4706 if (IS_LOCK_CLIENTONLY(fileLock)) {
4708 } else if (IS_LOCK_ACCEPTED(fileLock)) {
4709 if (fileLock->lockType == LockRead)
4712 scp->exclusiveLocks--;
4715 fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
4717 cm_ReleaseUser(fileLock->userp);
4718 cm_ReleaseSCacheNoLock(scp);
4720 fileLock->userp = NULL;
4721 fileLock->scp = NULL;
4727 lock_ReleaseWrite(&cm_scacheLock);
4729 if (n_unlocks == 0) {
4730 osi_Log0(afsd_logp, "cm_UnlockByKey no locks found");
4731 osi_Log3(afsd_logp, " Leaving scp with exclusives[%d], shared[%d], serverLock[%d]",
4732 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4737 osi_Log1(afsd_logp, "cm_UnlockByKey done with %d locks", n_unlocks);
4739 osi_assertx(scp->sharedLocks >= 0, "scp->sharedLocks < 0");
4740 osi_assertx(scp->exclusiveLocks >= 0, "scp->exclusiveLocks < 0");
4741 osi_assertx(scp->clientLocks >= 0, "scp->clientLocks < 0");
4743 if (!SERVERLOCKS_ENABLED(scp)) {
4744 osi_Log0(afsd_logp, " Skipping server lock for scp");
4748 /* Ideally we would go through the rest of the locks to determine
4749 * if one or more locks that were formerly in WAITUNLOCK can now
4750 * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
4751 * scp->sharedLocks accordingly. However, the retrying of locks
4752 * in that manner is done cm_RetryLock() manually.
4755 if (scp->serverLock == LockWrite &&
4756 scp->exclusiveLocks == 0 &&
4757 scp->sharedLocks > 0) {
4759 /* The serverLock should be downgraded to LockRead */
4760 osi_Log0(afsd_logp, " DOWNGRADE lock from LockWrite to LockRead");
4762 /* since scp->serverLock looked sane, we are going to assume
4763 that we have a valid server lock. */
4764 scp->lockDataVersion = scp->dataVersion;
4765 osi_Log1(afsd_logp, " dataVersion on scp = %I64d", scp->dataVersion);
4767 code = cm_IntReleaseLock(scp, userp, reqp);
4770 /* so we couldn't release it. Just let the lock be for now */
4774 scp->serverLock = -1;
4777 code = cm_IntSetLock(scp, userp, LockRead, reqp);
4779 if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
4780 scp->serverLock = LockRead;
4781 } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
4782 /* We lost a race condition. Although we have a valid
4783 lock on the file, the data has changed and essentially
4784 we have lost the lock we had during the transition. */
4786 osi_Log0(afsd_logp, "Data version mismatch during lock downgrade");
4787 osi_Log2(afsd_logp, " Data versions before=%I64d, after=%I64d",
4788 scp->lockDataVersion,
4791 code = cm_IntReleaseLock(scp, userp, reqp);
4793 code = CM_ERROR_INVAL;
4794 scp->serverLock = -1;
4798 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4799 (scp->serverLock == -1)) {
4801 cm_LockMarkSCacheLost(scp);
4804 /* failure here has no bearing on the return value of
4808 } else if (scp->serverLock != (-1) &&
4809 scp->exclusiveLocks == 0 &&
4810 scp->sharedLocks == 0) {
4811 /* The serverLock should be released entirely */
4813 code = cm_IntReleaseLock(scp, userp, reqp);
4816 scp->serverLock = (-1);
4821 osi_Log1(afsd_logp, "cm_UnlockByKey code 0x%x", code);
4822 osi_Log4(afsd_logp, " Leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
4823 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4824 (int)(signed char) scp->serverLock);
4829 long cm_Unlock(cm_scache_t *scp,
4830 unsigned char sLockType,
4831 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
4837 int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
4838 cm_file_lock_t *fileLock;
4840 int release_userp = FALSE;
4842 osi_Log4(afsd_logp, "cm_Unlock scp 0x%p type 0x%x offset %d length %d",
4843 scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
4844 osi_Log3(afsd_logp, "... key <0x%x,0x%x,0x%x>",
4845 key.process_id, key.session_id, key.file_id);
4847 lock_ObtainRead(&cm_scacheLock);
4849 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4850 fileLock = (cm_file_lock_t *)
4851 ((char *) q - offsetof(cm_file_lock_t, fileq));
4854 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
4855 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
4856 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4858 fileLock->fid.volume,
4859 fileLock->fid.vnode,
4860 fileLock->fid.unique);
4861 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4862 fileLock->scp->fid.cell,
4863 fileLock->scp->fid.volume,
4864 fileLock->scp->fid.vnode,
4865 fileLock->scp->fid.unique);
4866 osi_assertx(FALSE, "invalid fid value");
4869 if (!IS_LOCK_DELETED(fileLock) &&
4870 cm_KeyEquals(&fileLock->key, &key, 0) &&
4871 fileLock->range.offset == LOffset.QuadPart &&
4872 fileLock->range.length == LLength.QuadPart) {
4878 osi_Log0(afsd_logp, "cm_Unlock lock not found; failure");
4880 lock_ReleaseRead(&cm_scacheLock);
4882 /* The lock didn't exist anyway. *shrug* */
4883 return CM_ERROR_RANGE_NOT_LOCKED;
4886 /* discard lock record */
4887 lock_ConvertRToW(&cm_scacheLock);
4888 if (scp->fileLocksT == q)
4889 scp->fileLocksT = osi_QPrev(q);
4890 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
4893 * Don't delete it here; let the daemon delete it, to simplify
4894 * the daemon's traversal of the list.
4897 if (IS_LOCK_CLIENTONLY(fileLock)) {
4899 } else if (IS_LOCK_ACCEPTED(fileLock)) {
4900 if (fileLock->lockType == LockRead)
4903 scp->exclusiveLocks--;
4906 fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
4907 if (userp != NULL) {
4908 cm_ReleaseUser(fileLock->userp);
4910 userp = fileLock->userp;
4911 release_userp = TRUE;
4913 fileLock->userp = NULL;
4914 cm_ReleaseSCacheNoLock(scp);
4915 fileLock->scp = NULL;
4916 lock_ReleaseWrite(&cm_scacheLock);
4918 if (!SERVERLOCKS_ENABLED(scp)) {
4919 osi_Log0(afsd_logp, " Skipping server locks for scp");
4923 /* Ideally we would go through the rest of the locks to determine
4924 * if one or more locks that were formerly in WAITUNLOCK can now
4925 * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
4926 * scp->sharedLocks accordingly. However, the retrying of locks
4927 * in that manner is done cm_RetryLock() manually.
4930 if (scp->serverLock == LockWrite &&
4931 scp->exclusiveLocks == 0 &&
4932 scp->sharedLocks > 0) {
4934 /* The serverLock should be downgraded to LockRead */
4935 osi_Log0(afsd_logp, " DOWNGRADE lock from LockWrite to LockRead");
4937 /* Since we already had a lock, we assume that there is a
4938 valid server lock. */
4939 scp->lockDataVersion = scp->dataVersion;
4940 osi_Log1(afsd_logp, " dataVersion on scp is %I64d", scp->dataVersion);
4942 /* before we downgrade, make sure that we have enough
4943 permissions to get the read lock. */
4944 code = cm_LockCheckPerms(scp, LockRead, userp, reqp, NULL);
4947 osi_Log0(afsd_logp, " SKIPPING downgrade because user doesn't have perms to get downgraded lock");
4953 code = cm_IntReleaseLock(scp, userp, reqp);
4956 /* so we couldn't release it. Just let the lock be for now */
4960 scp->serverLock = -1;
4963 code = cm_IntSetLock(scp, userp, LockRead, reqp);
4965 if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
4966 scp->serverLock = LockRead;
4967 } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
4968 /* Lost a race. We obtained a new lock, but that is
4969 meaningless since someone modified the file
4973 "Data version mismatch while downgrading lock");
4975 " Data versions before=%I64d, after=%I64d",
4976 scp->lockDataVersion,
4979 code = cm_IntReleaseLock(scp, userp, reqp);
4981 scp->serverLock = -1;
4982 code = CM_ERROR_INVAL;
4986 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4987 (scp->serverLock == -1)) {
4989 cm_LockMarkSCacheLost(scp);
4992 /* failure here has no bearing on the return value of
4996 } else if (scp->serverLock != (-1) &&
4997 scp->exclusiveLocks == 0 &&
4998 scp->sharedLocks == 0) {
4999 /* The serverLock should be released entirely */
5001 code = cm_IntReleaseLock(scp, userp, reqp);
5004 scp->serverLock = (-1);
5009 cm_ReleaseUser(userp);
5013 osi_Log1(afsd_logp, "cm_Unlock code 0x%x", code);
5014 osi_Log4(afsd_logp, " leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
5015 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
5016 (int)(signed char) scp->serverLock);
5021 /* called with scp->rw held */
5022 void cm_LockMarkSCacheLost(cm_scache_t * scp)
5024 cm_file_lock_t *fileLock;
5027 osi_Log1(afsd_logp, "cm_LockMarkSCacheLost scp 0x%x", scp);
5029 /* cm_scacheLock needed because we are modifying fileLock->flags */
5030 lock_ObtainWrite(&cm_scacheLock);
5032 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5034 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
5036 if (IS_LOCK_ACTIVE(fileLock) &&
5037 !IS_LOCK_CLIENTONLY(fileLock)) {
5038 if (fileLock->lockType == LockRead)
5041 scp->exclusiveLocks--;
5043 fileLock->flags |= CM_FILELOCK_FLAG_LOST;
5047 scp->serverLock = -1;
5048 scp->lockDataVersion = -1;
5049 lock_ReleaseWrite(&cm_scacheLock);
5052 /* Called with no relevant locks held */
5053 void cm_CheckLocks()
5055 osi_queue_t *q, *nq;
5056 cm_file_lock_t *fileLock;
5062 struct rx_connection * rxconnp;
5067 lock_ObtainWrite(&cm_scacheLock);
5069 cm_lockRefreshCycle++;
5071 osi_Log1(afsd_logp, "cm_CheckLocks starting lock check cycle %d", cm_lockRefreshCycle);
5073 for (q = cm_allFileLocks; q; q = nq) {
5074 fileLock = (cm_file_lock_t *) q;
5078 if (IS_LOCK_DELETED(fileLock)) {
5080 osi_QRemove(&cm_allFileLocks, q);
5081 cm_PutFileLock(fileLock);
5083 } else if (IS_LOCK_ACTIVE(fileLock) && !IS_LOCK_CLIENTONLY(fileLock)) {
5085 /* Server locks must have been enabled for us to have
5086 received an active non-client-only lock. */
5087 osi_assertx(cm_enableServerLocks, "!cm_enableServerLocks");
5089 scp = fileLock->scp;
5090 osi_assertx(scp != NULL, "null cm_scache_t");
5092 cm_HoldSCacheNoLock(scp);
5095 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5096 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5097 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5099 fileLock->fid.volume,
5100 fileLock->fid.vnode,
5101 fileLock->fid.unique);
5102 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5103 fileLock->scp->fid.cell,
5104 fileLock->scp->fid.volume,
5105 fileLock->scp->fid.vnode,
5106 fileLock->scp->fid.unique);
5107 osi_assertx(FALSE, "invalid fid");
5110 /* Server locks are extended once per scp per refresh
5112 if (scp->lastRefreshCycle != cm_lockRefreshCycle) {
5114 int scp_done = FALSE;
5116 osi_Log1(afsd_logp, "cm_CheckLocks Updating scp 0x%x", scp);
5118 lock_ReleaseWrite(&cm_scacheLock);
5119 lock_ObtainWrite(&scp->rw);
5121 /* did the lock change while we weren't holding the lock? */
5122 if (!IS_LOCK_ACTIVE(fileLock))
5123 goto post_syncopdone;
5125 code = cm_SyncOp(scp, NULL, fileLock->userp, &req, 0,
5126 CM_SCACHESYNC_NEEDCALLBACK
5127 | CM_SCACHESYNC_GETSTATUS
5128 | CM_SCACHESYNC_LOCK);
5132 "cm_CheckLocks SyncOp failure code 0x%x", code);
5133 goto post_syncopdone;
5136 /* cm_SyncOp releases scp->rw during which the lock
5137 may get released. */
5138 if (!IS_LOCK_ACTIVE(fileLock))
5139 goto pre_syncopdone;
5141 if (scp->serverLock != -1 && !(scp->flags & CM_SCACHEFLAG_DELETED)) {
5145 tfid.Volume = scp->fid.volume;
5146 tfid.Vnode = scp->fid.vnode;
5147 tfid.Unique = scp->fid.unique;
5149 userp = fileLock->userp;
5151 osi_Log3(afsd_logp, "CALL ExtendLock lock 0x%p for scp=0x%p with lock %d",
5154 (int) scp->serverLock);
5156 lock_ReleaseWrite(&scp->rw);
5159 code = cm_ConnFromFID(&cfid, userp,
5164 rxconnp = cm_GetRxConn(connp);
5165 code = RXAFS_ExtendLock(rxconnp, &tfid,
5167 rx_PutConnection(rxconnp);
5169 osi_Log1(afsd_logp, " ExtendLock returns %d", code);
5171 } while (cm_Analyze(connp, userp, &req,
5172 &cfid, &volSync, NULL, NULL,
5175 code = cm_MapRPCError(code, &req);
5177 lock_ObtainWrite(&scp->rw);
5180 osi_Log1(afsd_logp, "CALL ExtendLock FAILURE, code 0x%x", code);
5182 osi_Log0(afsd_logp, "CALL ExtendLock SUCCESS");
5183 scp->lockDataVersion = scp->dataVersion;
5186 if ((code == EINVAL || code == CM_ERROR_INVAL) &&
5187 scp->lockDataVersion == scp->dataVersion) {
5191 (scp->exclusiveLocks > 0) ? LockWrite: LockRead;
5193 /* we might still have a chance to obtain a
5196 code = cm_IntSetLock(scp, userp, lockType, &req);
5199 code = CM_ERROR_INVAL;
5200 } else if (scp->lockDataVersion != scp->dataVersion) {
5202 /* now check if we still have the file at
5203 the right data version. */
5205 "Data version mismatch on scp 0x%p",
5208 " Data versions: before=%I64d, after=%I64d",
5209 scp->lockDataVersion,
5212 code = cm_IntReleaseLock(scp, userp, &req);
5214 code = CM_ERROR_INVAL;
5218 if (code == EINVAL || code == CM_ERROR_INVAL ||
5219 code == CM_ERROR_BADFD) {
5220 cm_LockMarkSCacheLost(scp);
5224 /* interestingly, we have found an active lock
5225 belonging to an scache that has no
5227 cm_LockMarkSCacheLost(scp);
5234 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5237 lock_ReleaseWrite(&scp->rw);
5239 lock_ObtainWrite(&cm_scacheLock);
5242 fileLock->lastUpdate = time(NULL);
5246 scp->lastRefreshCycle = cm_lockRefreshCycle;
5249 /* we have already refreshed the locks on this scp */
5250 fileLock->lastUpdate = time(NULL);
5253 cm_ReleaseSCacheNoLock(scp);
5255 } else if (IS_LOCK_ACTIVE(fileLock) && IS_LOCK_CLIENTONLY(fileLock)) {
5256 /* TODO: Check callbacks */
5260 lock_ReleaseWrite(&cm_scacheLock);
5261 osi_Log1(afsd_logp, "cm_CheckLocks completes lock check cycle %d", cm_lockRefreshCycle);
5264 /* NOT called with scp->rw held. */
5265 long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
5268 cm_scache_t *scp = NULL;
5269 cm_file_lock_t *fileLock;
5273 int force_client_lock = FALSE;
5274 int has_insert = FALSE;
5275 int check_data_version = FALSE;
5279 if (client_is_dead) {
5280 code = CM_ERROR_TIMEDOUT;
5284 lock_ObtainRead(&cm_scacheLock);
5286 osi_Log2(afsd_logp, "cm_RetryLock checking lock %p (scp=%p)", oldFileLock, oldFileLock->scp);
5287 osi_Log4(afsd_logp, " offset(%x:%x) length(%x:%x)",
5288 (unsigned)(oldFileLock->range.offset >> 32),
5289 (unsigned)(oldFileLock->range.offset & 0xffffffff),
5290 (unsigned)(oldFileLock->range.length >> 32),
5291 (unsigned)(oldFileLock->range.length & 0xffffffff));
5292 osi_Log4(afsd_logp, " key<0x%x,0x%x,0x%x> flags=%x",
5293 oldFileLock->key.process_id, oldFileLock->key.session_id, oldFileLock->key.file_id,
5294 (unsigned)(oldFileLock->flags));
5296 /* if the lock has already been granted, then we have nothing to do */
5297 if (IS_LOCK_ACTIVE(oldFileLock)) {
5298 lock_ReleaseRead(&cm_scacheLock);
5299 osi_Log0(afsd_logp, "cm_RetryLock lock already granted");
5303 /* we can't do anything with lost or deleted locks at the moment. */
5304 if (IS_LOCK_LOST(oldFileLock) || IS_LOCK_DELETED(oldFileLock)) {
5305 code = CM_ERROR_BADFD;
5306 osi_Log0(afsd_logp, "cm_RetryLock lock is lost or deleted");
5307 lock_ReleaseRead(&cm_scacheLock);
5311 scp = oldFileLock->scp;
5313 osi_assertx(scp != NULL, "null cm_scache_t");
5315 lock_ReleaseRead(&cm_scacheLock);
5316 lock_ObtainWrite(&scp->rw);
5318 code = cm_LockCheckPerms(scp, oldFileLock->lockType,
5322 if (code == CM_ERROR_NOACCESS && oldFileLock->lockType == LockRead) {
5323 if (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp)) {
5324 force_client_lock = TRUE;
5328 lock_ReleaseWrite(&scp->rw);
5332 lock_ObtainWrite(&cm_scacheLock);
5334 /* Check if we already have a sufficient server lock to allow this
5335 lock to go through. */
5336 if (IS_LOCK_WAITLOCK(oldFileLock) &&
5337 (!SERVERLOCKS_ENABLED(scp) ||
5338 scp->serverLock == oldFileLock->lockType ||
5339 scp->serverLock == LockWrite)) {
5341 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5343 if (SERVERLOCKS_ENABLED(scp)) {
5344 osi_Log1(afsd_logp, "cm_RetryLock Server lock (%d) is sufficient for lock. Granting",
5345 (int) scp->serverLock);
5347 osi_Log0(afsd_logp, "cm_RetryLock skipping server lock for scp");
5350 lock_ReleaseWrite(&cm_scacheLock);
5351 lock_ReleaseWrite(&scp->rw);
5356 if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5358 /* check if the conflicting locks have dissappeared already */
5359 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5361 fileLock = (cm_file_lock_t *)
5362 ((char *) q - offsetof(cm_file_lock_t, fileq));
5364 if (IS_LOCK_LOST(fileLock)) {
5365 if (cm_KeyEquals(&fileLock->key, &oldFileLock->key, 0)) {
5366 code = CM_ERROR_BADFD;
5367 oldFileLock->flags |= CM_FILELOCK_FLAG_LOST;
5368 osi_Log1(afsd_logp, " found lost lock %p for same key. Marking lock as lost",
5371 } else if (fileLock->lockType == LockWrite &&
5372 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5373 osi_Log1(afsd_logp, " found conflicting LOST lock %p", fileLock);
5374 code = CM_ERROR_WOULDBLOCK;
5379 if (IS_LOCK_ACCEPTED(fileLock) &&
5380 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5382 if (oldFileLock->lockType != LockRead ||
5383 fileLock->lockType != LockRead) {
5385 osi_Log1(afsd_logp, " found conflicting lock %p", fileLock);
5386 code = CM_ERROR_WOULDBLOCK;
5394 lock_ReleaseWrite(&cm_scacheLock);
5395 lock_ReleaseWrite(&scp->rw);
5400 /* when we get here, the lock is either a WAITUNLOCK or WAITLOCK.
5401 If it is WAITUNLOCK, then we didn't find any conflicting lock
5402 but we haven't verfied whether the serverLock is sufficient to
5403 assert it. If it is WAITLOCK, then the serverLock is
5404 insufficient to assert it. Eitherway, we are ready to accept
5405 the lock as either ACTIVE or WAITLOCK depending on the
5408 /* First, promote the WAITUNLOCK to a WAITLOCK */
5409 if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5410 if (oldFileLock->lockType == LockRead)
5413 scp->exclusiveLocks++;
5415 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITUNLOCK;
5416 oldFileLock->flags |= CM_FILELOCK_FLAG_WAITLOCK;
5419 osi_assertx(IS_LOCK_WAITLOCK(oldFileLock), "!IS_LOCK_WAITLOCK");
5421 if (force_client_lock ||
5422 !SERVERLOCKS_ENABLED(scp) ||
5423 scp->serverLock == oldFileLock->lockType ||
5424 (oldFileLock->lockType == LockRead &&
5425 scp->serverLock == LockWrite)) {
5427 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5429 if ((force_client_lock ||
5430 !SERVERLOCKS_ENABLED(scp)) &&
5431 !IS_LOCK_CLIENTONLY(oldFileLock)) {
5433 oldFileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
5435 if (oldFileLock->lockType == LockRead)
5438 scp->exclusiveLocks--;
5443 lock_ReleaseWrite(&cm_scacheLock);
5444 lock_ReleaseWrite(&scp->rw);
5451 code = cm_SyncOp(scp, NULL, oldFileLock->userp, &req, 0,
5452 CM_SCACHESYNC_NEEDCALLBACK
5453 | CM_SCACHESYNC_GETSTATUS
5454 | CM_SCACHESYNC_LOCK);
5456 osi_Log1(afsd_logp, "cm_RetryLock SyncOp failure code 0x%x", code);
5457 lock_ReleaseWrite(&cm_scacheLock);
5458 goto post_syncopdone;
5461 if (!IS_LOCK_WAITLOCK(oldFileLock))
5462 goto pre_syncopdone;
5464 userp = oldFileLock->userp;
5466 #ifndef AGGRESSIVE_LOCKS
5467 newLock = oldFileLock->lockType;
5469 newLock = LockWrite;
5473 /* if has_insert is non-zero, then:
5474 - the lock a LockRead
5475 - we don't have permission to get a LockRead
5476 - we do have permission to get a LockWrite
5477 - the server supports VICED_CAPABILITY_WRITELOCKACL
5480 newLock = LockWrite;
5483 lock_ReleaseWrite(&cm_scacheLock);
5485 /* when we get here, either we have a read-lock and want a
5486 write-lock or we don't have any locks and we want some
5489 if (scp->serverLock == LockRead) {
5491 osi_assertx(newLock == LockWrite, "!LockWrite");
5493 osi_Log0(afsd_logp, " Attempting to UPGRADE from LockRead to LockWrite");
5495 scp->lockDataVersion = scp->dataVersion;
5496 check_data_version = TRUE;
5498 code = cm_IntReleaseLock(scp, userp, &req);
5501 goto pre_syncopdone;
5503 scp->serverLock = -1;
5506 code = cm_IntSetLock(scp, userp, newLock, &req);
5509 if (scp->dataVersion != scp->lockDataVersion) {
5510 /* we lost a race. too bad */
5513 " Data version mismatch while upgrading lock.");
5515 " Data versions before=%I64d, after=%I64d",
5516 scp->lockDataVersion,
5519 " Releasing stale lock for scp 0x%x", scp);
5521 code = cm_IntReleaseLock(scp, userp, &req);
5523 scp->serverLock = -1;
5525 code = CM_ERROR_INVAL;
5527 cm_LockMarkSCacheLost(scp);
5529 scp->serverLock = newLock;
5534 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5540 if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
5541 lock_ObtainWrite(&cm_scacheLock);
5542 if (scp->fileLocksT == &oldFileLock->fileq)
5543 scp->fileLocksT = osi_QPrev(&oldFileLock->fileq);
5544 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, &oldFileLock->fileq);
5545 lock_ReleaseWrite(&cm_scacheLock);
5547 lock_ReleaseWrite(&scp->rw);
5550 lock_ObtainWrite(&cm_scacheLock);
5552 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5553 } else if (code != CM_ERROR_WOULDBLOCK) {
5554 oldFileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5555 cm_ReleaseUser(oldFileLock->userp);
5556 oldFileLock->userp = NULL;
5557 if (oldFileLock->scp) {
5558 cm_ReleaseSCacheNoLock(oldFileLock->scp);
5559 oldFileLock->scp = NULL;
5562 lock_ReleaseWrite(&cm_scacheLock);
5567 cm_key_t cm_GenerateKey(afs_uint16 session_id, afs_offs_t process_id, afs_uint16 file_id)
5571 key.process_id = process_id;
5572 key.session_id = session_id;
5573 key.file_id = file_id;
5578 int cm_KeyEquals(cm_key_t *k1, cm_key_t *k2, int flags)
5580 return (k1->session_id == k2->session_id) && (k1->file_id == k2->file_id) &&
5581 ((flags & CM_UNLOCK_BY_FID) || (k1->process_id == k2->process_id));
5584 void cm_ReleaseAllLocks(void)
5590 cm_file_lock_t *fileLock;
5593 for (i = 0; i < cm_data.scacheHashTableSize; i++)
5595 for ( scp = cm_data.scacheHashTablep[i]; scp; scp = scp->nextp ) {
5596 while (scp->fileLocksH != NULL) {
5597 lock_ObtainWrite(&scp->rw);
5598 lock_ObtainWrite(&cm_scacheLock);
5599 if (!scp->fileLocksH) {
5600 lock_ReleaseWrite(&cm_scacheLock);
5601 lock_ReleaseWrite(&scp->rw);
5604 fileLock = (cm_file_lock_t *)((char *) scp->fileLocksH - offsetof(cm_file_lock_t, fileq));
5605 userp = fileLock->userp;
5607 key = fileLock->key;
5608 cm_HoldSCacheNoLock(scp);
5609 lock_ReleaseWrite(&cm_scacheLock);
5610 cm_UnlockByKey(scp, key, 0, userp, &req);
5611 cm_ReleaseSCache(scp);
5612 cm_ReleaseUser(userp);
5613 lock_ReleaseWrite(&scp->rw);