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, 0, 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, 0, 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)
811 cm_buf_t *bufp = NULL;
815 if (scp->mountPointStringp[0])
818 #ifdef AFS_FREELANCE_CLIENT
819 /* File servers do not have data for freelance entries */
820 if (cm_freelanceEnabled &&
821 scp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
822 scp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
824 code = cm_FreelanceFetchMountPointString(scp);
826 #endif /* AFS_FREELANCE_CLIENT */
828 /* otherwise, we have to read it in */
829 lock_ReleaseWrite(&scp->rw);
831 thyper.LowPart = thyper.HighPart = 0;
832 code = buf_Get(scp, &thyper, &bufp);
834 lock_ObtainWrite(&scp->rw);
839 code = cm_SyncOp(scp, bufp, userp, reqp, 0,
840 CM_SCACHESYNC_READ | CM_SCACHESYNC_NEEDCALLBACK);
844 cm_SyncOpDone(scp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
846 if (cm_HaveBuffer(scp, bufp, 0))
849 /* otherwise load buffer */
850 code = cm_GetBuffer(scp, bufp, NULL, userp, reqp);
854 /* locked, has callback, has valid data in buffer */
855 if ((tlen = scp->length.LowPart) > MOUNTPOINTLEN - 1)
856 return CM_ERROR_TOOBIG;
858 code = CM_ERROR_INVAL;
862 /* someone else did the work while we were out */
863 if (scp->mountPointStringp[0]) {
868 /* otherwise, copy out the link */
869 memcpy(scp->mountPointStringp, bufp->datap, tlen);
871 /* now make it null-terminated. Note that the original contents of a
872 * link that is a mount point is "#volname." where "." is there just to
873 * be turned into a null. That is, we can trash the last char of the
874 * link without damaging the vol name. This is a stupid convention,
875 * but that's the protocol.
877 scp->mountPointStringp[tlen-1] = 0;
888 /* called with a locked scp and chases the mount point, yielding outScpp.
889 * scp remains write locked, just for simplicity of describing the interface.
891 long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
892 cm_req_t *reqp, cm_scache_t **outScpp)
894 fschar_t *cellNamep = NULL;
895 fschar_t *volNamep = NULL;
900 cm_volume_t *volp = NULL;
909 if (scp->mountRootFid.cell != 0 && scp->mountRootGen >= cm_data.mountRootGen) {
910 tfid = scp->mountRootFid;
911 lock_ReleaseWrite(&scp->rw);
912 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
913 lock_ObtainWrite(&scp->rw);
917 /* parse the volume name */
918 mpNamep = scp->mountPointStringp;
920 return CM_ERROR_NOSUCHPATH;
921 tlen = cm_FsStrLen(scp->mountPointStringp);
922 mtType = *scp->mountPointStringp;
924 cp = cm_FsStrChr(mpNamep, _FS(':'));
926 /* cellular mount point */
927 cellNamep = (fschar_t *)malloc((cp - mpNamep) * sizeof(fschar_t));
928 cm_FsStrCpyN(cellNamep, cp - mpNamep, mpNamep + 1, cp - mpNamep - 1);
929 volNamep = cm_FsStrDup(cp+1);
931 /* now look up the cell */
932 lock_ReleaseWrite(&scp->rw);
933 cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE);
934 lock_ObtainWrite(&scp->rw);
937 volNamep = cm_FsStrDup(mpNamep + 1);
939 cellp = cm_FindCellByID(scp->fid.cell, 0);
943 code = CM_ERROR_NOSUCHCELL;
947 vnLength = cm_FsStrLen(volNamep);
948 if (vnLength >= 8 && cm_FsStrCmp(volNamep + vnLength - 7, ".backup") == 0)
949 targetType = BACKVOL;
950 else if (vnLength >= 10
951 && cm_FsStrCmp(volNamep + vnLength - 9, ".readonly") == 0)
956 /* check for backups within backups */
957 if (targetType == BACKVOL
958 && (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
959 == CM_SCACHEFLAG_RO) {
960 code = CM_ERROR_NOSUCHVOLUME;
964 /* now we need to get the volume */
965 lock_ReleaseWrite(&scp->rw);
966 if (cm_VolNameIsID(volNamep)) {
967 code = cm_FindVolumeByID(cellp, atoi(volNamep), userp, reqp,
968 CM_GETVOL_FLAG_CREATE, &volp);
970 code = cm_FindVolumeByName(cellp, volNamep, userp, reqp,
971 CM_GETVOL_FLAG_CREATE, &volp);
973 lock_ObtainWrite(&scp->rw);
976 afs_uint32 cell, volume;
977 cm_vol_state_t *statep;
979 cell = cellp->cellID;
981 /* if the mt pt originates in a .backup volume (not a .readonly)
982 * and FollowBackupPath is active, and if there is a .backup
983 * volume for the target, then use the .backup of the target
984 * instead of the read-write.
986 if (cm_followBackupPath &&
987 volp->vol[BACKVOL].ID != 0 &&
988 (dscp->flags & (CM_SCACHEFLAG_RO|CM_SCACHEFLAG_PURERO)) == CM_SCACHEFLAG_RO &&
989 (targetType == RWVOL || targetType == ROVOL && volp->vol[ROVOL].ID == 0)
991 targetType = BACKVOL;
993 /* if the mt pt is in a read-only volume (not just a
994 * backup), and if there is a read-only volume for the
995 * target, and if this is a targetType '#' mount point, use
996 * the read-only, otherwise use the one specified.
998 else if (mtType == '#' && targetType == RWVOL &&
999 (scp->flags & CM_SCACHEFLAG_PURERO) &&
1000 volp->vol[ROVOL].ID != 0) {
1004 lock_ObtainWrite(&volp->rw);
1005 statep = cm_VolumeStateByType(volp, targetType);
1006 volume = statep->ID;
1007 statep->dotdotFid = dscp->fid;
1008 lock_ReleaseWrite(&volp->rw);
1010 /* the rest of the fid is a magic number */
1011 cm_SetFid(&scp->mountRootFid, cell, volume, 1, 1);
1012 scp->mountRootGen = cm_data.mountRootGen;
1014 tfid = scp->mountRootFid;
1015 lock_ReleaseWrite(&scp->rw);
1016 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
1017 lock_ObtainWrite(&scp->rw);
1030 long cm_LookupInternal(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_user_t *userp,
1031 cm_req_t *reqp, cm_scache_t **outScpp)
1034 int dnlcHit = 1; /* did we hit in the dnlc? yes, we did */
1035 cm_scache_t *tscp = NULL;
1036 cm_scache_t *mountedScp;
1037 cm_lookupSearch_t rock;
1039 normchar_t *nnamep = NULL;
1040 fschar_t *fnamep = NULL;
1044 memset(&rock, 0, sizeof(rock));
1046 if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
1047 && cm_ClientStrCmp(cnamep, _C("..")) == 0) {
1048 if (dscp->dotdotFid.volume == 0)
1049 return CM_ERROR_NOSUCHVOLUME;
1050 rock.fid = dscp->dotdotFid;
1052 } else if (cm_ClientStrCmp(cnamep, _C(".")) == 0) {
1053 rock.fid = dscp->fid;
1057 nnamep = cm_ClientStringToNormStringAlloc(cnamep, -1, NULL);
1059 code = CM_ERROR_NOSUCHFILE;
1062 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
1064 code = CM_ERROR_NOSUCHFILE;
1068 if (flags & CM_FLAG_NOMOUNTCHASE) {
1069 /* In this case, we should go and call cm_Dir* functions
1070 directly since the following cm_ApplyDir() function will
1078 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ, &dirop);
1081 code = cm_BPlusDirLookup(&dirop, nnamep, &rock.fid);
1086 code = cm_DirLookup(&dirop, fnamep, &rock.fid);
1088 cm_EndDirOp(&dirop);
1098 if (code == CM_ERROR_INEXACT_MATCH && (flags & CM_FLAG_CASEFOLD)) {
1105 return CM_ERROR_BPLUS_NOMATCH;
1110 rock.fid.cell = dscp->fid.cell;
1111 rock.fid.volume = dscp->fid.volume;
1112 rock.searchNamep = fnamep;
1113 rock.nsearchNamep = nnamep;
1114 rock.caseFold = (flags & CM_FLAG_CASEFOLD);
1115 rock.hasTilde = ((cm_ClientStrChr(cnamep, '~') != NULL) ? 1 : 0);
1117 /* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
1118 code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
1119 (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
1121 /* code == 0 means we fell off the end of the dir, while stopnow means
1122 * that we stopped early, probably because we found the entry we're
1123 * looking for. Any other non-zero code is an error.
1125 if (code && code != CM_ERROR_STOPNOW) {
1126 /* if the cm_scache_t we are searching in is not a directory
1127 * we must return path not found because the error
1128 * is to describe the final component not an intermediary
1130 if (code == CM_ERROR_NOTDIR) {
1131 if (flags & CM_FLAG_CHECKPATH)
1132 code = CM_ERROR_NOSUCHPATH;
1134 code = CM_ERROR_NOSUCHFILE;
1139 getroot = (dscp==cm_data.rootSCachep) ;
1141 if (!cm_freelanceEnabled || !getroot) {
1142 if (flags & CM_FLAG_CHECKPATH)
1143 code = CM_ERROR_NOSUCHPATH;
1145 code = CM_ERROR_NOSUCHFILE;
1148 else if (!cm_ClientStrChr(cnamep, '#') &&
1149 !cm_ClientStrChr(cnamep, '%') &&
1150 cm_ClientStrCmpI(cnamep, _C("srvsvc")) &&
1151 cm_ClientStrCmpI(cnamep, _C("wkssvc")) &&
1152 cm_ClientStrCmpI(cnamep, _C("ipc$")))
1154 /* nonexistent dir on freelance root, so add it */
1155 fschar_t fullname[CELL_MAXNAMELEN + 1] = "."; /* +1 so that when we skip the . the size is still CELL_MAXNAMELEN */
1158 osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %S",
1159 osi_LogSaveClientString(afsd_logp,cnamep));
1162 * There is an ugly behavior where a share name "foo" will be searched
1163 * for as "fo". If the searched for name differs by an already existing
1164 * symlink or mount point in the Freelance directory, do not add the
1165 * new value automatically.
1169 if (cnamep[0] == '.') {
1170 if (cm_GetCell_Gen(&fnamep[1], &fullname[1], CM_FLAG_CREATE)) {
1172 if (!cm_FreelanceMountPointExists(fullname, 0))
1173 code = cm_FreelanceAddMount(fullname, &fullname[1], "root.cell.",
1175 if ( cm_FsStrCmpI(&fnamep[1], &fullname[1]) &&
1176 !cm_FreelanceMountPointExists(fnamep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0) &&
1177 !cm_FreelanceSymlinkExists(fnamep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0))
1178 code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
1181 if (cm_GetCell_Gen(fnamep, fullname, CM_FLAG_CREATE)) {
1183 if (!cm_FreelanceMountPointExists(fullname, 0))
1184 code = cm_FreelanceAddMount(fullname, fullname, "root.cell.", 0, &rock.fid);
1185 if ( cm_FsStrCmpI(fnamep, fullname) &&
1186 !cm_FreelanceMountPointExists(fnamep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0) &&
1187 !cm_FreelanceSymlinkExists(fnamep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0))
1188 code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
1191 if (!found || code) { /* add mount point failed, so give up */
1192 if (flags & CM_FLAG_CHECKPATH)
1193 code = CM_ERROR_NOSUCHPATH;
1195 code = CM_ERROR_NOSUCHFILE;
1198 tscp = NULL; /* to force call of cm_GetSCache */
1200 if (flags & CM_FLAG_CHECKPATH)
1201 code = CM_ERROR_NOSUCHPATH;
1203 code = CM_ERROR_NOSUCHFILE;
1209 if ( !tscp ) /* we did not find it in the dnlc */
1212 code = cm_GetSCache(&rock.fid, &tscp, userp, reqp);
1216 /* tscp is now held */
1218 lock_ObtainWrite(&tscp->rw);
1219 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1220 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1222 lock_ReleaseWrite(&tscp->rw);
1223 cm_ReleaseSCache(tscp);
1226 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1227 /* tscp is now locked */
1229 if (!(flags & CM_FLAG_NOMOUNTCHASE)
1230 && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
1231 /* mount points are funny: they have a volume name to mount
1234 code = cm_ReadMountPoint(tscp, userp, reqp);
1236 code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
1238 lock_ReleaseWrite(&tscp->rw);
1239 cm_ReleaseSCache(tscp);
1246 lock_ReleaseWrite(&tscp->rw);
1249 /* copy back pointer */
1252 /* insert scache in dnlc */
1253 if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
1254 /* lock the directory entry to prevent racing callback revokes */
1255 lock_ObtainRead(&dscp->rw);
1256 if ( dscp->cbServerp != NULL && dscp->cbExpires > 0 ) {
1257 /* TODO: reuse nnamep from above */
1260 nnamep = cm_ClientStringToNormStringAlloc(cnamep, -1, NULL);
1262 cm_dnlcEnter(dscp, nnamep, tscp);
1264 lock_ReleaseRead(&dscp->rw);
1281 int cm_ExpandSysName(clientchar_t *inp, clientchar_t *outp, long outSizeCch, unsigned int index)
1286 tp = cm_ClientStrRChr(inp, '@');
1288 return 0; /* no @sys */
1290 if (cm_ClientStrCmp(tp, _C("@sys")) != 0)
1291 return 0; /* no @sys */
1293 /* caller just wants to know if this is a valid @sys type of name */
1297 if (index >= cm_sysNameCount)
1300 /* otherwise generate the properly expanded @sys name */
1301 prefixCount = (int)(tp - inp);
1303 cm_ClientStrCpyN(outp, outSizeCch, inp, prefixCount); /* copy out "a." from "a.@sys" */
1304 outp[prefixCount] = 0; /* null terminate the "a." */
1305 cm_ClientStrCat(outp, outSizeCch, cm_sysNameList[index]);/* append i386_nt40 */
1309 long cm_EvaluateVolumeReference(clientchar_t * namep, long flags, cm_user_t * userp,
1310 cm_req_t *reqp, cm_scache_t ** outScpp)
1312 afs_uint32 code = 0;
1313 fschar_t cellName[CELL_MAXNAMELEN];
1314 fschar_t volumeName[VL_MAXNAMELEN];
1318 fschar_t * fnamep = NULL;
1320 cm_cell_t * cellp = NULL;
1321 cm_volume_t * volp = NULL;
1325 int mountType = RWVOL;
1327 osi_Log1(afsd_logp, "cm_EvaluateVolumeReference for string [%S]",
1328 osi_LogSaveClientString(afsd_logp, namep));
1330 if (cm_ClientStrCmpNI(namep, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH) != 0) {
1331 goto _exit_invalid_path;
1334 /* namep is assumed to look like the following:
1336 @vol:<cellname>%<volume>\0
1338 @vol:<cellname>#<volume>\0
1342 fnamep = cm_ClientStringToFsStringAlloc(namep, -1, NULL);
1343 cp = fnamep + CM_PREFIX_VOL_CCH; /* cp points to cell name, hopefully */
1344 tp = cm_FsStrChr(cp, '%');
1346 tp = cm_FsStrChr(cp, '#');
1348 (len = tp - cp) == 0 ||
1349 len > CELL_MAXNAMELEN)
1350 goto _exit_invalid_path;
1351 cm_FsStrCpyN(cellName, lengthof(cellName), cp, len);
1356 cp = tp+1; /* cp now points to volume, supposedly */
1357 cm_FsStrCpy(volumeName, lengthof(volumeName), cp);
1359 /* OK, now we have the cell and the volume */
1360 osi_Log2(afsd_logp, " Found cell [%s] and volume [%s]",
1361 osi_LogSaveFsString(afsd_logp, cellName),
1362 osi_LogSaveFsString(afsd_logp, volumeName));
1364 cellp = cm_GetCell(cellName, CM_FLAG_CREATE);
1365 if (cellp == NULL) {
1366 goto _exit_invalid_path;
1369 len = cm_FsStrLen(volumeName);
1370 if (len >= 8 && cm_FsStrCmp(volumeName + len - 7, ".backup") == 0)
1372 else if (len >= 10 &&
1373 cm_FsStrCmp(volumeName + len - 9, ".readonly") == 0)
1378 if (cm_VolNameIsID(volumeName)) {
1379 code = cm_FindVolumeByID(cellp, atoi(volumeName), userp, reqp,
1380 CM_GETVOL_FLAG_CREATE, &volp);
1382 code = cm_FindVolumeByName(cellp, volumeName, userp, reqp,
1383 CM_GETVOL_FLAG_CREATE, &volp);
1389 if (volType == BACKVOL)
1390 volume = volp->vol[BACKVOL].ID;
1391 else if (volType == ROVOL ||
1392 (volType == RWVOL && mountType == ROVOL && volp->vol[ROVOL].ID != 0))
1393 volume = volp->vol[ROVOL].ID;
1395 volume = volp->vol[RWVOL].ID;
1397 cm_SetFid(&fid, cellp->cellID, volume, 1, 1);
1399 code = cm_GetSCache(&fid, outScpp, userp, reqp);
1412 if (flags & CM_FLAG_CHECKPATH)
1413 return CM_ERROR_NOSUCHPATH;
1415 return CM_ERROR_NOSUCHFILE;
1418 #ifdef DEBUG_REFCOUNT
1419 long cm_LookupDbg(cm_scache_t *dscp, clientchar_t *namep, long flags, cm_user_t *userp,
1420 cm_req_t *reqp, cm_scache_t **outScpp, char * file, long line)
1422 long cm_Lookup(cm_scache_t *dscp, clientchar_t *namep, long flags, cm_user_t *userp,
1423 cm_req_t *reqp, cm_scache_t **outScpp)
1427 clientchar_t tname[AFSPATHMAX];
1428 int sysNameIndex = 0;
1429 cm_scache_t *scp = NULL;
1431 #ifdef DEBUG_REFCOUNT
1432 afsi_log("%s:%d cm_Lookup dscp 0x%p ref %d", file, line, dscp, dscp->refCount, file, line);
1433 osi_Log2(afsd_logp, "cm_Lookup dscp 0x%p ref %d", dscp, dscp->refCount);
1436 if ( cm_ClientStrCmpI(namep,_C(SMB_IOCTL_FILENAME_NOSLASH)) == 0 ) {
1437 if (flags & CM_FLAG_CHECKPATH)
1438 return CM_ERROR_NOSUCHPATH;
1440 return CM_ERROR_NOSUCHFILE;
1443 if (dscp == cm_data.rootSCachep &&
1444 cm_ClientStrCmpNI(namep, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH) == 0) {
1445 return cm_EvaluateVolumeReference(namep, flags, userp, reqp, outScpp);
1448 if (cm_ExpandSysName(namep, NULL, 0, 0) > 0) {
1449 for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
1450 code = cm_ExpandSysName(namep, tname, lengthof(tname), sysNameIndex);
1452 code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
1453 #ifdef DEBUG_REFCOUNT
1454 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);
1455 osi_Log3(afsd_logp, "cm_LookupInternal (1) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1463 cm_ReleaseSCache(scp);
1467 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1468 #ifdef DEBUG_REFCOUNT
1469 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);
1470 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1477 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1478 #ifdef DEBUG_REFCOUNT
1479 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);
1480 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1486 /* None of the possible sysName expansions could be found */
1487 if (flags & CM_FLAG_CHECKPATH)
1488 return CM_ERROR_NOSUCHPATH;
1490 return CM_ERROR_NOSUCHFILE;
1493 /*! \brief Unlink a file name
1495 Encapsulates a call to RXAFS_RemoveFile().
1497 \param[in] dscp cm_scache_t pointing at the directory containing the
1498 name to be unlinked.
1500 \param[in] fnamep Original name to be unlinked. This is the
1501 name that will be passed into the RXAFS_RemoveFile() call.
1502 This parameter is optional. If not provided, the value will
1505 \param[in] came Client name to be unlinked. This name will be used
1506 to update the local directory caches.
1508 \param[in] userp cm_user_t for the request.
1510 \param[in] reqp Request tracker.
1513 long cm_Unlink(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t * cnamep,
1514 cm_user_t *userp, cm_req_t *reqp)
1520 AFSFetchStatus newDirStatus;
1522 struct rx_connection * rxconnp;
1524 cm_scache_t *scp = NULL;
1525 int free_fnamep = FALSE;
1527 if (fnamep == NULL) {
1530 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ, &dirop);
1532 code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
1535 cm_EndDirOp(&dirop);
1542 #ifdef AFS_FREELANCE_CLIENT
1543 if (cm_freelanceEnabled && dscp == cm_data.rootSCachep) {
1544 /* deleting a mount point from the root dir. */
1545 code = cm_FreelanceRemoveMount(fnamep);
1550 code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
1552 /* make sure we don't screw up the dir status during the merge */
1553 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
1555 lock_ObtainWrite(&dscp->rw);
1556 sflags = CM_SCACHESYNC_STOREDATA;
1557 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1558 lock_ReleaseWrite(&dscp->rw);
1560 cm_EndDirOp(&dirop);
1565 afsFid.Volume = dscp->fid.volume;
1566 afsFid.Vnode = dscp->fid.vnode;
1567 afsFid.Unique = dscp->fid.unique;
1569 osi_Log1(afsd_logp, "CALL RemoveFile scp 0x%p", dscp);
1571 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
1575 rxconnp = cm_GetRxConn(connp);
1576 code = RXAFS_RemoveFile(rxconnp, &afsFid, fnamep,
1577 &newDirStatus, &volSync);
1578 rx_PutConnection(rxconnp);
1580 } while (cm_Analyze(connp, userp, reqp, &dscp->fid, &volSync, NULL, NULL, code));
1581 code = cm_MapRPCError(code, reqp);
1584 osi_Log1(afsd_logp, "CALL RemoveFile FAILURE, code 0x%x", code);
1586 osi_Log0(afsd_logp, "CALL RemoveFile SUCCESS");
1589 lock_ObtainWrite(&dirop.scp->dirlock);
1590 dirop.lockType = CM_DIRLOCK_WRITE;
1592 lock_ObtainWrite(&dscp->rw);
1593 cm_dnlcRemove(dscp, cnamep);
1594 cm_SyncOpDone(dscp, NULL, sflags);
1596 cm_MergeStatus(NULL, dscp, &newDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
1597 } else if (code == CM_ERROR_NOSUCHFILE) {
1598 /* windows would not have allowed the request to delete the file
1599 * if it did not believe the file existed. therefore, we must
1600 * have an inconsistent view of the world.
1602 dscp->cbServerp = NULL;
1604 lock_ReleaseWrite(&dscp->rw);
1606 if (code == 0 && cm_CheckDirOpForSingleChange(&dirop) && cnamep) {
1607 cm_DirDeleteEntry(&dirop, fnamep);
1609 cm_BPlusDirDeleteEntry(&dirop, cnamep);
1612 cm_EndDirOp(&dirop);
1615 cm_ReleaseSCache(scp);
1617 lock_ObtainWrite(&scp->rw);
1618 scp->flags |= CM_SCACHEFLAG_DELETED;
1619 lock_ReleaseWrite(&scp->rw);
1630 /* called with a write locked vnode, and fills in the link info.
1631 * returns this the vnode still write locked.
1633 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1640 lock_AssertWrite(&linkScp->rw);
1641 if (!linkScp->mountPointStringp[0]) {
1643 #ifdef AFS_FREELANCE_CLIENT
1644 /* File servers do not have data for freelance entries */
1645 if (cm_freelanceEnabled &&
1646 linkScp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
1647 linkScp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
1649 code = cm_FreelanceFetchMountPointString(linkScp);
1651 #endif /* AFS_FREELANCE_CLIENT */
1653 /* read the link data from the file server*/
1654 lock_ReleaseWrite(&linkScp->rw);
1655 thyper.LowPart = thyper.HighPart = 0;
1656 code = buf_Get(linkScp, &thyper, &bufp);
1657 lock_ObtainWrite(&linkScp->rw);
1661 code = cm_SyncOp(linkScp, bufp, userp, reqp, 0,
1662 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1667 cm_SyncOpDone(linkScp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1669 if (cm_HaveBuffer(linkScp, bufp, 0))
1672 code = cm_GetBuffer(linkScp, bufp, NULL, userp, reqp);
1677 } /* while loop to get the data */
1679 /* now if we still have no link read in,
1680 * copy the data from the buffer */
1681 if ((temp = linkScp->length.LowPart) >= MOUNTPOINTLEN) {
1683 return CM_ERROR_TOOBIG;
1686 /* otherwise, it fits; make sure it is still null (could have
1687 * lost race with someone else referencing this link above),
1688 * and if so, copy in the data.
1690 if (!linkScp->mountPointStringp[0]) {
1691 strncpy(linkScp->mountPointStringp, bufp->datap, temp);
1692 linkScp->mountPointStringp[temp] = 0; /* null terminate */
1697 if ( !strnicmp(linkScp->mountPointStringp, "msdfs:", strlen("msdfs:")) )
1698 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1700 } /* don't have sym link contents cached */
1705 /* called with a held vnode and a path suffix, with the held vnode being a
1706 * symbolic link. Our goal is to generate a new path to interpret, and return
1707 * this new path in newSpaceBufferp. If the new vnode is relative to a dir
1708 * other than the directory containing the symbolic link, then the new root is
1709 * returned in *newRootScpp, otherwise a null is returned there.
1711 long cm_AssembleLink(cm_scache_t *linkScp, fschar_t *pathSuffixp,
1712 cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1713 cm_user_t *userp, cm_req_t *reqp)
1720 *newRootScpp = NULL;
1721 *newSpaceBufferp = NULL;
1723 lock_ObtainWrite(&linkScp->rw);
1724 code = cm_HandleLink(linkScp, userp, reqp);
1728 /* if we may overflow the buffer, bail out; buffer is signficantly
1729 * bigger than max path length, so we don't really have to worry about
1730 * being a little conservative here.
1732 if (cm_FsStrLen(linkScp->mountPointStringp) + cm_FsStrLen(pathSuffixp) + 2
1733 >= CM_UTILS_SPACESIZE) {
1734 code = CM_ERROR_TOOBIG;
1738 tsp = cm_GetSpace();
1739 linkp = linkScp->mountPointStringp;
1740 if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1741 if (strlen(linkp) > cm_mountRootLen)
1742 StringCbCopyA((char *) tsp->data, sizeof(tsp->data), linkp+cm_mountRootLen+1);
1745 *newRootScpp = cm_data.rootSCachep;
1746 cm_HoldSCache(cm_data.rootSCachep);
1747 } else if (linkp[0] == '\\' && linkp[1] == '\\') {
1748 if (!strnicmp(&linkp[2], cm_NetbiosName, (len = (long)strlen(cm_NetbiosName))))
1750 char * p = &linkp[len + 3];
1751 if (strnicmp(p, "all", 3) == 0)
1754 StringCbCopyA(tsp->data, sizeof(tsp->data), p);
1755 for (p = tsp->data; *p; p++) {
1759 *newRootScpp = cm_data.rootSCachep;
1760 cm_HoldSCache(cm_data.rootSCachep);
1762 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1763 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1764 code = CM_ERROR_PATH_NOT_COVERED;
1766 } else if ( linkScp->fileType == CM_SCACHETYPE_DFSLINK ||
1767 !strnicmp(linkp, "msdfs:", (len = (long)strlen("msdfs:"))) ) {
1768 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1769 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1770 code = CM_ERROR_PATH_NOT_COVERED;
1771 } else if (*linkp == '\\' || *linkp == '/') {
1773 /* formerly, this was considered to be from the AFS root,
1774 * but this seems to create problems. instead, we will just
1775 * reject the link */
1776 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp+1);
1777 *newRootScpp = cm_data.rootSCachep;
1778 cm_HoldSCache(cm_data.rootSCachep);
1780 /* we still copy the link data into the response so that
1781 * the user can see what the link points to
1783 linkScp->fileType = CM_SCACHETYPE_INVALID;
1784 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1785 code = CM_ERROR_NOSUCHPATH;
1788 /* a relative link */
1789 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1791 if (pathSuffixp[0] != 0) { /* if suffix string is non-null */
1792 StringCchCatA(tsp->data,lengthof(tsp->data), "\\");
1793 StringCchCatA(tsp->data,lengthof(tsp->data), pathSuffixp);
1797 clientchar_t * cpath = cm_FsStringToClientStringAlloc(tsp->data, -1, NULL);
1798 if (cpath != NULL) {
1799 cm_ClientStrCpy(tsp->wdata, lengthof(tsp->wdata), cpath);
1801 *newSpaceBufferp = tsp;
1803 code = CM_ERROR_NOSUCHPATH;
1810 if (code == CM_ERROR_PATH_NOT_COVERED && reqp->tidPathp && reqp->relPathp) {
1811 cm_VolStatus_Notify_DFS_Mapping(linkScp, reqp->tidPathp, reqp->relPathp);
1816 lock_ReleaseWrite(&linkScp->rw);
1819 #ifdef DEBUG_REFCOUNT
1820 long cm_NameIDbg(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
1821 cm_user_t *userp, clientchar_t *tidPathp, cm_req_t *reqp,
1822 cm_scache_t **outScpp,
1823 char * file, long line)
1825 long cm_NameI(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
1826 cm_user_t *userp, clientchar_t *tidPathp,
1827 cm_req_t *reqp, cm_scache_t **outScpp)
1831 clientchar_t *tp; /* ptr moving through input buffer */
1832 clientchar_t tc; /* temp char */
1833 int haveComponent; /* has new component started? */
1834 clientchar_t component[AFSPATHMAX]; /* this is the new component */
1835 clientchar_t *cp; /* component name being assembled */
1836 cm_scache_t *tscp; /* current location in the hierarchy */
1837 cm_scache_t *nscp; /* next dude down */
1838 cm_scache_t *dirScp; /* last dir we searched */
1839 cm_scache_t *linkScp; /* new root for the symlink we just
1841 cm_space_t *psp; /* space for current path, if we've hit
1843 cm_space_t *tempsp; /* temp vbl */
1844 clientchar_t *restp; /* rest of the pathname to interpret */
1845 int symlinkCount; /* count of # of symlinks traversed */
1846 int extraFlag; /* avoid chasing mt pts for dir cmd */
1847 int phase = 1; /* 1 = tidPathp, 2 = pathp */
1848 #define MAX_FID_COUNT 512
1849 cm_fid_t fids[MAX_FID_COUNT]; /* array of fids processed in this path walk */
1850 int fid_count = 0; /* number of fids processed in this path walk */
1855 #ifdef DEBUG_REFCOUNT
1856 afsi_log("%s:%d cm_NameI rootscp 0x%p ref %d", file, line, rootSCachep, rootSCachep->refCount);
1857 osi_Log4(afsd_logp,"cm_NameI rootscp 0x%p path %S tidpath %S flags 0x%x",
1858 rootSCachep, pathp ? pathp : L"<NULL>", tidPathp ? tidPathp : L"<NULL>",
1873 cm_HoldSCache(tscp);
1881 /* map Unix slashes into DOS ones so we can interpret Unix
1887 if (!haveComponent) {
1890 } else if (tc == 0) {
1904 /* we have a component here */
1905 if (tc == 0 || tc == '\\') {
1906 /* end of the component; we're at the last
1907 * component if tc == 0. However, if the last
1908 * is a symlink, we have more to do.
1910 *cp++ = 0; /* add null termination */
1912 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
1913 extraFlag = CM_FLAG_NOMOUNTCHASE;
1914 code = cm_Lookup(tscp, component,
1916 userp, reqp, &nscp);
1919 if (!cm_ClientStrCmp(component,_C("..")) ||
1920 !cm_ClientStrCmp(component,_C("."))) {
1922 * roll back the fid list until we find the
1923 * fid that matches where we are now. Its not
1924 * necessarily one or two fids because they
1925 * might have been symlinks or mount points or
1926 * both that were crossed.
1928 for ( i=fid_count-1; i>=0; i--) {
1929 if (!cm_FidCmp(&nscp->fid, &fids[i]))
1934 /* add the new fid to the list */
1935 if (fid_count == MAX_FID_COUNT) {
1936 code = CM_ERROR_TOO_MANY_SYMLINKS;
1937 cm_ReleaseSCache(nscp);
1941 fids[fid_count++] = nscp->fid;
1946 cm_ReleaseSCache(tscp);
1948 cm_ReleaseSCache(dirScp);
1951 if ((code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH) &&
1952 tscp->fileType == CM_SCACHETYPE_SYMLINK) {
1953 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_NOSUCHPATH");
1954 return CM_ERROR_NOSUCHPATH;
1956 osi_Log1(afsd_logp,"cm_NameI code 0x%x", code);
1961 haveComponent = 0; /* component done */
1963 cm_ReleaseSCache(dirScp);
1964 dirScp = tscp; /* for some symlinks */
1965 tscp = nscp; /* already held */
1967 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
1970 cm_ReleaseSCache(dirScp);
1976 /* now, if tscp is a symlink, we should follow it and
1977 * assemble the path again.
1979 lock_ObtainWrite(&tscp->rw);
1980 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1981 CM_SCACHESYNC_GETSTATUS
1982 | CM_SCACHESYNC_NEEDCALLBACK);
1984 lock_ReleaseWrite(&tscp->rw);
1985 cm_ReleaseSCache(tscp);
1988 cm_ReleaseSCache(dirScp);
1993 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1995 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
1996 /* this is a symlink; assemble a new buffer */
1997 lock_ReleaseWrite(&tscp->rw);
1998 if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
1999 cm_ReleaseSCache(tscp);
2002 cm_ReleaseSCache(dirScp);
2007 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_TOO_MANY_SYMLINKS");
2008 return CM_ERROR_TOO_MANY_SYMLINKS;
2018 /* TODO: make this better */
2019 frestp = cm_ClientStringToFsStringAlloc(restp, -1, NULL);
2020 code = cm_AssembleLink(tscp, frestp, &linkScp, &tempsp, userp, reqp);
2024 if (code == 0 && linkScp != NULL) {
2025 if (linkScp == cm_data.rootSCachep)
2028 for ( i=0; i<fid_count; i++) {
2029 if ( !cm_FidCmp(&linkScp->fid, &fids[i]) ) {
2030 code = CM_ERROR_TOO_MANY_SYMLINKS;
2031 cm_ReleaseSCache(linkScp);
2037 if (i == fid_count && fid_count < MAX_FID_COUNT) {
2038 fids[fid_count++] = linkScp->fid;
2043 /* something went wrong */
2044 cm_ReleaseSCache(tscp);
2047 cm_ReleaseSCache(dirScp);
2053 /* otherwise, tempsp has the new path,
2054 * and linkScp is the new root from
2055 * which to interpret that path.
2056 * Continue with the namei processing,
2057 * also doing the bookkeeping for the
2058 * space allocation and tracking the
2059 * vnode reference counts.
2065 cm_ReleaseSCache(tscp);
2070 * now, if linkScp is null, that's
2071 * AssembleLink's way of telling us that
2072 * the sym link is relative to the dir
2073 * containing the link. We have a ref
2074 * to it in dirScp, and we hold it now
2075 * and reuse it as the new spot in the
2083 /* not a symlink, we may be done */
2084 lock_ReleaseWrite(&tscp->rw);
2092 cm_ReleaseSCache(dirScp);
2100 cm_ReleaseSCache(dirScp);
2103 } /* end of a component */
2106 } /* we have a component */
2107 } /* big while loop over all components */
2111 cm_ReleaseSCache(dirScp);
2117 cm_ReleaseSCache(tscp);
2119 #ifdef DEBUG_REFCOUNT
2120 afsi_log("%s:%d cm_NameI code 0x%x outScpp 0x%p ref %d", file, line, code, *outScpp, (*outScpp) ? (*outScpp)->refCount : 0);
2122 osi_Log2(afsd_logp,"cm_NameI code 0x%x outScpp 0x%p", code, *outScpp);
2126 /* called with a dir, and a vnode within the dir that happens to be a symlink.
2127 * We chase the link, and return a held pointer to the target, if it exists,
2128 * in *outScpp. If we succeed, we return 0, otherwise we return an error code
2129 * and do not hold or return a target vnode.
2131 * This is very similar to calling cm_NameI with the last component of a name,
2132 * which happens to be a symlink, except that we've already passed by the name.
2134 * This function is typically called by the directory listing functions, which
2135 * encounter symlinks but need to return the proper file length so programs
2136 * like "more" work properly when they make use of the attributes retrieved from
2139 * The input vnode should not be locked when this function is called.
2141 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
2142 cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
2146 cm_scache_t *newRootScp;
2150 osi_Log1(afsd_logp, "Evaluating symlink scp 0x%p", linkScp);
2152 code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
2156 /* now, if newRootScp is NULL, we're really being told that the symlink
2157 * is relative to the current directory (dscp).
2159 if (newRootScp == NULL) {
2161 cm_HoldSCache(dscp);
2164 code = cm_NameI(newRootScp, spacep->wdata,
2165 CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
2166 userp, NULL, reqp, outScpp);
2168 if (code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH)
2169 code = CM_ERROR_NOSUCHPATH;
2171 /* this stuff is allocated no matter what happened on the namei call,
2173 cm_FreeSpace(spacep);
2174 cm_ReleaseSCache(newRootScp);
2176 if (linkScp == *outScpp) {
2177 cm_ReleaseSCache(*outScpp);
2179 code = CM_ERROR_NOSUCHPATH;
2185 /* for a given entry, make sure that it isn't in the stat cache, and then
2186 * add it to the list of file IDs to be obtained.
2188 * Don't bother adding it if we already have a vnode. Note that the dir
2189 * is locked, so we have to be careful checking the vnode we're thinking of
2190 * processing, to avoid deadlocks.
2192 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
2203 /* Don't overflow bsp. */
2204 if (bsp->counter >= CM_BULKMAX)
2205 return CM_ERROR_STOPNOW;
2207 thyper.LowPart = cm_data.buf_blockSize;
2208 thyper.HighPart = 0;
2209 thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
2211 /* thyper is now the first byte past the end of the record we're
2212 * interested in, and bsp->bufOffset is the first byte of the record
2213 * we're interested in.
2214 * Skip data in the others.
2217 if (LargeIntegerLessThan(*offp, bsp->bufOffset))
2219 if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
2220 return CM_ERROR_STOPNOW;
2221 if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
2224 cm_SetFid(&tfid, scp->fid.cell, scp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
2225 tscp = cm_FindSCache(&tfid);
2227 if (lock_TryWrite(&tscp->rw)) {
2228 /* we have an entry that we can look at */
2229 if (!(tscp->flags & CM_SCACHEFLAG_EACCESS) && cm_HaveCallback(tscp)) {
2230 /* we have a callback on it. Don't bother
2231 * fetching this stat entry, since we're happy
2232 * with the info we have.
2234 lock_ReleaseWrite(&tscp->rw);
2235 cm_ReleaseSCache(tscp);
2238 lock_ReleaseWrite(&tscp->rw);
2240 cm_ReleaseSCache(tscp);
2243 #ifdef AFS_FREELANCE_CLIENT
2244 // yj: if this is a mountpoint under root.afs then we don't want it
2245 // to be bulkstat-ed, instead, we call getSCache directly and under
2246 // getSCache, it is handled specially.
2247 if ( cm_freelanceEnabled &&
2248 tfid.cell==AFS_FAKE_ROOT_CELL_ID &&
2249 tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
2250 !(tfid.vnode==0x1 && tfid.unique==0x1) )
2252 osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
2253 return cm_GetSCache(&tfid, &tscp, NULL, NULL);
2255 #endif /* AFS_FREELANCE_CLIENT */
2258 bsp->fids[i].Volume = scp->fid.volume;
2259 bsp->fids[i].Vnode = tfid.vnode;
2260 bsp->fids[i].Unique = tfid.unique;
2265 cm_TryBulkStatRPC(cm_scache_t *dscp, cm_bulkStat_t *bbp, cm_user_t *userp, cm_req_t *reqp)
2268 AFSCBFids fidStruct;
2269 AFSBulkStats statStruct;
2271 AFSCBs callbackStruct;
2274 cm_callbackRequest_t cbReq;
2280 struct rx_connection * rxconnp;
2281 int inlinebulk = 0; /* Did we use InlineBulkStatus RPC or not? */
2283 /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
2284 * make the calls to create the entries. Handle AFSCBMAX files at a
2287 for (filex = 0; filex < bbp->counter; filex += filesThisCall) {
2288 filesThisCall = bbp->counter - filex;
2289 if (filesThisCall > AFSCBMAX)
2290 filesThisCall = AFSCBMAX;
2292 fidStruct.AFSCBFids_len = filesThisCall;
2293 fidStruct.AFSCBFids_val = &bbp->fids[filex];
2294 statStruct.AFSBulkStats_len = filesThisCall;
2295 statStruct.AFSBulkStats_val = &bbp->stats[filex];
2296 callbackStruct.AFSCBs_len = filesThisCall;
2297 callbackStruct.AFSCBs_val = &bbp->callbacks[filex];
2298 cm_StartCallbackGrantingCall(NULL, &cbReq);
2299 osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
2301 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2305 rxconnp = cm_GetRxConn(connp);
2306 if (!(connp->serverp->flags & CM_SERVERFLAG_NOINLINEBULK)) {
2307 code = RXAFS_InlineBulkStatus(rxconnp, &fidStruct,
2308 &statStruct, &callbackStruct, &volSync);
2309 if (code == RXGEN_OPCODE) {
2310 cm_SetServerNoInlineBulk(connp->serverp, 0);
2316 code = RXAFS_BulkStatus(rxconnp, &fidStruct,
2317 &statStruct, &callbackStruct, &volSync);
2319 rx_PutConnection(rxconnp);
2321 } while (cm_Analyze(connp, userp, reqp, &dscp->fid,
2322 &volSync, NULL, &cbReq, code));
2323 code = cm_MapRPCError(code, reqp);
2325 osi_Log2(afsd_logp, "CALL %sBulkStatus FAILURE code 0x%x",
2326 inlinebulk ? "Inline" : "", code);
2328 osi_Log1(afsd_logp, "CALL %sBulkStatus SUCCESS", inlinebulk ? "Inline" : "");
2330 /* may as well quit on an error, since we're not going to do
2331 * much better on the next immediate call, either.
2334 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2338 /* otherwise, we should do the merges */
2339 for (i = 0; i<filesThisCall; i++) {
2341 cm_SetFid(&tfid, dscp->fid.cell, bbp->fids[j].Volume, bbp->fids[j].Vnode, bbp->fids[j].Unique);
2342 code = cm_GetSCache(&tfid, &scp, userp, reqp);
2346 /* otherwise, if this entry has no callback info,
2349 lock_ObtainWrite(&scp->rw);
2350 /* now, we have to be extra paranoid on merging in this
2351 * information, since we didn't use cm_SyncOp before
2352 * starting the fetch to make sure that no bad races
2353 * were occurring. Specifically, we need to make sure
2354 * we don't obliterate any newer information in the
2355 * vnode than have here.
2357 * Right now, be pretty conservative: if there's a
2358 * callback or a pending call, skip it.
2360 if ((scp->cbServerp == NULL || (scp->flags & CM_SCACHEFLAG_EACCESS))
2362 (CM_SCACHEFLAG_FETCHING
2363 | CM_SCACHEFLAG_STORING
2364 | CM_SCACHEFLAG_SIZESTORING))) {
2365 cm_EndCallbackGrantingCall(scp, &cbReq,
2367 CM_CALLBACK_MAINTAINCOUNT);
2368 cm_MergeStatus(dscp, scp, &bbp->stats[j], &volSync, userp, 0);
2370 lock_ReleaseWrite(&scp->rw);
2371 cm_ReleaseSCache(scp);
2372 } /* all files in the response */
2373 /* now tell it to drop the count,
2374 * after doing the vnode processing above */
2375 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2376 } /* while there are still more files to process */
2378 /* If we did the InlineBulk RPC pull out the return code and log it */
2380 if ((&bbp->stats[0])->errorCode) {
2381 osi_Log1(afsd_logp, "cm_TryBulkStat bulk stat error: %d",
2382 (&bbp->stats[0])->errorCode);
2383 code = (&bbp->stats[0])->errorCode;
2390 /* called with a write locked scp and a pointer to a buffer. Make bulk stat
2391 * calls on all undeleted files in the page of the directory specified.
2394 cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
2400 osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%p", dscp);
2402 /* should be on a buffer boundary */
2403 osi_assertx((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0, "invalid offset");
2405 bbp = malloc(sizeof(cm_bulkStat_t));
2406 memset(bbp, 0, sizeof(cm_bulkStat_t));
2407 bbp->bufOffset = *offsetp;
2409 lock_ReleaseWrite(&dscp->rw);
2410 /* first, assemble the file IDs we need to stat */
2411 code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) bbp, offsetp, userp, reqp, NULL);
2413 /* if we failed, bail out early */
2414 if (code && code != CM_ERROR_STOPNOW) {
2416 lock_ObtainWrite(&dscp->rw);
2420 code = cm_TryBulkStatRPC(dscp, bbp, userp, reqp);
2421 osi_Log1(afsd_logp, "END cm_TryBulkStat code 0x%x", code);
2423 lock_ObtainWrite(&dscp->rw);
2428 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
2432 /* initialize store back mask as inexpensive local variable */
2434 memset(statusp, 0, sizeof(AFSStoreStatus));
2436 /* copy out queued info from scache first, if scp passed in */
2438 if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
2439 statusp->ClientModTime = scp->clientModTime;
2440 mask |= AFS_SETMODTIME;
2441 scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
2446 /* now add in our locally generated request */
2447 if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
2448 statusp->ClientModTime = attrp->clientModTime;
2449 mask |= AFS_SETMODTIME;
2451 if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
2452 statusp->UnixModeBits = attrp->unixModeBits;
2453 mask |= AFS_SETMODE;
2455 if (attrp->mask & CM_ATTRMASK_OWNER) {
2456 statusp->Owner = attrp->owner;
2457 mask |= AFS_SETOWNER;
2459 if (attrp->mask & CM_ATTRMASK_GROUP) {
2460 statusp->Group = attrp->group;
2461 mask |= AFS_SETGROUP;
2464 statusp->Mask = mask;
2467 /* set the file size, and make sure that all relevant buffers have been
2468 * truncated. Ensure that any partially truncated buffers have been zeroed
2469 * to the end of the buffer.
2471 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
2477 /* start by locking out buffer creation */
2478 lock_ObtainWrite(&scp->bufCreateLock);
2480 /* verify that this is a file, not a dir or a symlink */
2481 lock_ObtainWrite(&scp->rw);
2482 code = cm_SyncOp(scp, NULL, userp, reqp, 0,
2483 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2486 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2488 if (scp->fileType != CM_SCACHETYPE_FILE) {
2489 code = CM_ERROR_ISDIR;
2494 if (LargeIntegerLessThan(*sizep, scp->length))
2499 lock_ReleaseWrite(&scp->rw);
2501 /* can't hold scp->rw lock here, since we may wait for a storeback to
2502 * finish if the buffer package is cleaning a buffer by storing it to
2506 buf_Truncate(scp, userp, reqp, sizep);
2508 /* now ensure that file length is short enough, and update truncPos */
2509 lock_ObtainWrite(&scp->rw);
2511 /* make sure we have a callback (so we have the right value for the
2512 * length), and wait for it to be safe to do a truncate.
2514 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
2515 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2516 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2518 /* If we only have 'i' bits, then we should still be able to set
2519 the size of a file we created. */
2520 if (code == CM_ERROR_NOACCESS && scp->creator == userp) {
2521 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
2522 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2523 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2529 if (LargeIntegerLessThan(*sizep, scp->length)) {
2530 /* a real truncation. If truncPos is not set yet, or is bigger
2531 * than where we're truncating the file, set truncPos to this
2536 if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
2537 || LargeIntegerLessThan(*sizep, scp->length)) {
2539 scp->truncPos = *sizep;
2540 scp->mask |= CM_SCACHEMASK_TRUNCPOS;
2542 /* in either case, the new file size has been changed */
2543 scp->length = *sizep;
2544 scp->mask |= CM_SCACHEMASK_LENGTH;
2546 else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
2547 /* really extending the file */
2548 scp->length = *sizep;
2549 scp->mask |= CM_SCACHEMASK_LENGTH;
2552 /* done successfully */
2555 cm_SyncOpDone(scp, NULL,
2556 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2557 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2560 lock_ReleaseWrite(&scp->rw);
2561 lock_ReleaseWrite(&scp->bufCreateLock);
2566 /* set the file size or other attributes (but not both at once) */
2567 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
2571 AFSFetchStatus afsOutStatus;
2575 AFSStoreStatus afsInStatus;
2576 struct rx_connection * rxconnp;
2578 /* handle file length setting */
2579 if (attrp->mask & CM_ATTRMASK_LENGTH)
2580 return cm_SetLength(scp, &attrp->length, userp, reqp);
2582 lock_ObtainWrite(&scp->rw);
2583 /* otherwise, we have to make an RPC to get the status */
2584 code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
2586 lock_ReleaseWrite(&scp->rw);
2589 lock_ConvertWToR(&scp->rw);
2591 /* make the attr structure */
2592 cm_StatusFromAttr(&afsInStatus, scp, attrp);
2594 tfid.Volume = scp->fid.volume;
2595 tfid.Vnode = scp->fid.vnode;
2596 tfid.Unique = scp->fid.unique;
2597 lock_ReleaseRead(&scp->rw);
2599 /* now make the RPC */
2600 osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%p", scp);
2602 code = cm_ConnFromFID(&scp->fid, userp, reqp, &connp);
2606 rxconnp = cm_GetRxConn(connp);
2607 code = RXAFS_StoreStatus(rxconnp, &tfid,
2608 &afsInStatus, &afsOutStatus, &volSync);
2609 rx_PutConnection(rxconnp);
2611 } while (cm_Analyze(connp, userp, reqp,
2612 &scp->fid, &volSync, NULL, NULL, code));
2613 code = cm_MapRPCError(code, reqp);
2616 osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
2618 osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
2620 lock_ObtainWrite(&scp->rw);
2621 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
2623 cm_MergeStatus(NULL, scp, &afsOutStatus, &volSync, userp,
2624 CM_MERGEFLAG_FORCE|CM_MERGEFLAG_STOREDATA);
2626 /* if we're changing the mode bits, discard the ACL cache,
2627 * since we changed the mode bits.
2629 if (afsInStatus.Mask & AFS_SETMODE)
2630 cm_FreeAllACLEnts(scp);
2631 lock_ReleaseWrite(&scp->rw);
2635 long cm_Create(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
2636 cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
2641 cm_callbackRequest_t cbReq;
2644 cm_scache_t *scp = NULL;
2646 AFSStoreStatus inStatus;
2647 AFSFetchStatus updatedDirStatus;
2648 AFSFetchStatus newFileStatus;
2649 AFSCallBack newFileCallback;
2651 struct rx_connection * rxconnp;
2653 fschar_t * fnamep = NULL;
2655 /* can't create names with @sys in them; must expand it manually first.
2656 * return "invalid request" if they try.
2658 if (cm_ExpandSysName(cnamep, NULL, 0, 0)) {
2659 return CM_ERROR_ATSYS;
2662 #ifdef AFS_FREELANCE_CLIENT
2663 /* Freelance root volume does not hold files */
2664 if (cm_freelanceEnabled &&
2665 dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
2666 dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
2668 return CM_ERROR_NOACCESS;
2670 #endif /* AFS_FREELANCE_CLIENT */
2672 /* before starting the RPC, mark that we're changing the file data, so
2673 * that someone who does a chmod will know to wait until our call
2676 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2677 lock_ObtainWrite(&dscp->rw);
2678 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2679 lock_ReleaseWrite(&dscp->rw);
2681 cm_StartCallbackGrantingCall(NULL, &cbReq);
2683 cm_EndDirOp(&dirop);
2690 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
2692 cm_StatusFromAttr(&inStatus, NULL, attrp);
2694 /* try the RPC now */
2695 osi_Log1(afsd_logp, "CALL CreateFile scp 0x%p", dscp);
2697 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2701 dirAFSFid.Volume = dscp->fid.volume;
2702 dirAFSFid.Vnode = dscp->fid.vnode;
2703 dirAFSFid.Unique = dscp->fid.unique;
2705 rxconnp = cm_GetRxConn(connp);
2706 code = RXAFS_CreateFile(connp->rxconnp, &dirAFSFid, fnamep,
2707 &inStatus, &newAFSFid, &newFileStatus,
2708 &updatedDirStatus, &newFileCallback,
2710 rx_PutConnection(rxconnp);
2712 } while (cm_Analyze(connp, userp, reqp,
2713 &dscp->fid, &volSync, NULL, &cbReq, code));
2714 code = cm_MapRPCError(code, reqp);
2717 osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
2719 osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
2722 lock_ObtainWrite(&dirop.scp->dirlock);
2723 dirop.lockType = CM_DIRLOCK_WRITE;
2725 lock_ObtainWrite(&dscp->rw);
2726 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2728 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
2730 lock_ReleaseWrite(&dscp->rw);
2732 /* now try to create the file's entry, too, but be careful to
2733 * make sure that we don't merge in old info. Since we weren't locking
2734 * out any requests during the file's creation, we may have pretty old
2738 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
2739 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2741 lock_ObtainWrite(&scp->rw);
2742 scp->creator = userp; /* remember who created it */
2743 if (!cm_HaveCallback(scp)) {
2744 cm_MergeStatus(dscp, scp, &newFileStatus, &volSync,
2746 cm_EndCallbackGrantingCall(scp, &cbReq,
2747 &newFileCallback, 0);
2750 lock_ReleaseWrite(&scp->rw);
2754 /* make sure we end things properly */
2756 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2758 if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
2759 cm_DirCreateEntry(&dirop, fnamep, &newFid);
2761 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
2764 cm_EndDirOp(&dirop);
2773 cm_ReleaseSCache(scp);
2778 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
2782 code = buf_CleanVnode(scp, userp, reqp);
2784 lock_ObtainWrite(&scp->rw);
2786 if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
2787 | CM_SCACHEMASK_CLIENTMODTIME
2788 | CM_SCACHEMASK_LENGTH))
2789 code = cm_StoreMini(scp, userp, reqp);
2791 if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) {
2792 code = (scp->flags & CM_SCACHEFLAG_OVERQUOTA) ? CM_ERROR_QUOTA : CM_ERROR_SPACE;
2793 scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE);
2796 lock_ReleaseWrite(&scp->rw);
2801 long cm_MakeDir(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
2802 cm_user_t *userp, cm_req_t *reqp, cm_scache_t **scpp)
2807 cm_callbackRequest_t cbReq;
2810 cm_scache_t *scp = NULL;
2812 AFSStoreStatus inStatus;
2813 AFSFetchStatus updatedDirStatus;
2814 AFSFetchStatus newDirStatus;
2815 AFSCallBack newDirCallback;
2817 struct rx_connection * rxconnp;
2819 fschar_t * fnamep = NULL;
2821 /* can't create names with @sys in them; must expand it manually first.
2822 * return "invalid request" if they try.
2824 if (cm_ExpandSysName(cnamep, NULL, 0, 0)) {
2825 return CM_ERROR_ATSYS;
2828 #ifdef AFS_FREELANCE_CLIENT
2829 /* Freelance root volume does not hold subdirectories */
2830 if (cm_freelanceEnabled &&
2831 dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
2832 dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
2834 return CM_ERROR_NOACCESS;
2836 #endif /* AFS_FREELANCE_CLIENT */
2838 /* before starting the RPC, mark that we're changing the directory
2839 * data, so that someone who does a chmod on the dir will wait until
2840 * our call completes.
2842 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2843 lock_ObtainWrite(&dscp->rw);
2844 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2845 lock_ReleaseWrite(&dscp->rw);
2847 cm_StartCallbackGrantingCall(NULL, &cbReq);
2849 cm_EndDirOp(&dirop);
2856 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
2857 cm_StatusFromAttr(&inStatus, NULL, attrp);
2859 /* try the RPC now */
2860 osi_Log1(afsd_logp, "CALL MakeDir scp 0x%p", dscp);
2862 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2866 dirAFSFid.Volume = dscp->fid.volume;
2867 dirAFSFid.Vnode = dscp->fid.vnode;
2868 dirAFSFid.Unique = dscp->fid.unique;
2870 rxconnp = cm_GetRxConn(connp);
2871 code = RXAFS_MakeDir(connp->rxconnp, &dirAFSFid, fnamep,
2872 &inStatus, &newAFSFid, &newDirStatus,
2873 &updatedDirStatus, &newDirCallback,
2875 rx_PutConnection(rxconnp);
2877 } while (cm_Analyze(connp, userp, reqp,
2878 &dscp->fid, &volSync, NULL, &cbReq, code));
2879 code = cm_MapRPCError(code, reqp);
2882 osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
2884 osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
2887 lock_ObtainWrite(&dirop.scp->dirlock);
2888 dirop.lockType = CM_DIRLOCK_WRITE;
2890 lock_ObtainWrite(&dscp->rw);
2891 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2893 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
2895 lock_ReleaseWrite(&dscp->rw);
2897 /* now try to create the new dir's entry, too, but be careful to
2898 * make sure that we don't merge in old info. Since we weren't locking
2899 * out any requests during the file's creation, we may have pretty old
2903 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
2904 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2906 lock_ObtainWrite(&scp->rw);
2907 if (!cm_HaveCallback(scp)) {
2908 cm_MergeStatus(dscp, scp, &newDirStatus, &volSync,
2910 cm_EndCallbackGrantingCall(scp, &cbReq,
2911 &newDirCallback, 0);
2914 lock_ReleaseWrite(&scp->rw);
2918 /* make sure we end things properly */
2920 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2922 if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
2923 cm_DirCreateEntry(&dirop, fnamep, &newFid);
2925 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
2928 cm_EndDirOp(&dirop);
2936 cm_ReleaseSCache(scp);
2939 /* and return error code */
2943 long cm_Link(cm_scache_t *dscp, clientchar_t *cnamep, cm_scache_t *sscp, long flags,
2944 cm_user_t *userp, cm_req_t *reqp)
2949 AFSFid existingAFSFid;
2950 AFSFetchStatus updatedDirStatus;
2951 AFSFetchStatus newLinkStatus;
2953 struct rx_connection * rxconnp;
2955 fschar_t * fnamep = NULL;
2957 if (dscp->fid.cell != sscp->fid.cell ||
2958 dscp->fid.volume != sscp->fid.volume) {
2959 return CM_ERROR_CROSSDEVLINK;
2962 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2963 lock_ObtainWrite(&dscp->rw);
2964 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2965 lock_ReleaseWrite(&dscp->rw);
2967 cm_EndDirOp(&dirop);
2972 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
2974 /* try the RPC now */
2975 osi_Log1(afsd_logp, "CALL Link scp 0x%p", dscp);
2977 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2980 dirAFSFid.Volume = dscp->fid.volume;
2981 dirAFSFid.Vnode = dscp->fid.vnode;
2982 dirAFSFid.Unique = dscp->fid.unique;
2984 existingAFSFid.Volume = sscp->fid.volume;
2985 existingAFSFid.Vnode = sscp->fid.vnode;
2986 existingAFSFid.Unique = sscp->fid.unique;
2988 rxconnp = cm_GetRxConn(connp);
2989 code = RXAFS_Link(rxconnp, &dirAFSFid, fnamep, &existingAFSFid,
2990 &newLinkStatus, &updatedDirStatus, &volSync);
2991 rx_PutConnection(rxconnp);
2992 osi_Log1(afsd_logp," RXAFS_Link returns 0x%x", code);
2994 } while (cm_Analyze(connp, userp, reqp,
2995 &dscp->fid, &volSync, NULL, NULL, code));
2997 code = cm_MapRPCError(code, reqp);
3000 osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
3002 osi_Log0(afsd_logp, "CALL Link SUCCESS");
3005 lock_ObtainWrite(&dirop.scp->dirlock);
3006 dirop.lockType = CM_DIRLOCK_WRITE;
3008 lock_ObtainWrite(&dscp->rw);
3009 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3011 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
3013 lock_ReleaseWrite(&dscp->rw);
3016 if (cm_CheckDirOpForSingleChange(&dirop)) {
3017 cm_DirCreateEntry(&dirop, fnamep, &sscp->fid);
3019 cm_BPlusDirCreateEntry(&dirop, cnamep, &sscp->fid);
3023 cm_EndDirOp(&dirop);
3030 long cm_SymLink(cm_scache_t *dscp, clientchar_t *cnamep, fschar_t *contentsp, long flags,
3031 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
3039 AFSStoreStatus inStatus;
3040 AFSFetchStatus updatedDirStatus;
3041 AFSFetchStatus newLinkStatus;
3043 struct rx_connection * rxconnp;
3045 fschar_t *fnamep = NULL;
3047 /* before starting the RPC, mark that we're changing the directory data,
3048 * so that someone who does a chmod on the dir will wait until our
3051 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
3052 lock_ObtainWrite(&dscp->rw);
3053 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3054 lock_ReleaseWrite(&dscp->rw);
3056 cm_EndDirOp(&dirop);
3061 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3063 cm_StatusFromAttr(&inStatus, NULL, attrp);
3065 /* try the RPC now */
3066 osi_Log1(afsd_logp, "CALL Symlink scp 0x%p", dscp);
3068 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3072 dirAFSFid.Volume = dscp->fid.volume;
3073 dirAFSFid.Vnode = dscp->fid.vnode;
3074 dirAFSFid.Unique = dscp->fid.unique;
3076 rxconnp = cm_GetRxConn(connp);
3077 code = RXAFS_Symlink(rxconnp, &dirAFSFid, fnamep, contentsp,
3078 &inStatus, &newAFSFid, &newLinkStatus,
3079 &updatedDirStatus, &volSync);
3080 rx_PutConnection(rxconnp);
3082 } while (cm_Analyze(connp, userp, reqp,
3083 &dscp->fid, &volSync, NULL, NULL, code));
3084 code = cm_MapRPCError(code, reqp);
3087 osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
3089 osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
3092 lock_ObtainWrite(&dirop.scp->dirlock);
3093 dirop.lockType = CM_DIRLOCK_WRITE;
3095 lock_ObtainWrite(&dscp->rw);
3096 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3098 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
3100 lock_ReleaseWrite(&dscp->rw);
3103 if (cm_CheckDirOpForSingleChange(&dirop)) {
3104 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3106 cm_DirCreateEntry(&dirop, fnamep, &newFid);
3108 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
3112 cm_EndDirOp(&dirop);
3114 /* now try to create the new dir's entry, too, but be careful to
3115 * make sure that we don't merge in old info. Since we weren't locking
3116 * out any requests during the file's creation, we may have pretty old
3120 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3121 code = cm_GetSCache(&newFid, &scp, userp, reqp);
3123 lock_ObtainWrite(&scp->rw);
3124 if (!cm_HaveCallback(scp)) {
3125 cm_MergeStatus(dscp, scp, &newLinkStatus, &volSync,
3128 lock_ReleaseWrite(&scp->rw);
3129 cm_ReleaseSCache(scp);
3135 /* and return error code */
3139 /*! \brief Remove a directory
3141 Encapsulates a call to RXAFS_RemoveDir().
3143 \param[in] dscp cm_scache_t for the directory containing the
3144 directory to be removed.
3146 \param[in] fnamep This will be the original name of the directory
3147 as known to the file server. It will be passed in to RXAFS_RemoveDir().
3148 This parameter is optional. If it is not provided the value
3151 \param[in] cnamep Normalized name used to update the local
3154 \param[in] userp cm_user_t for the request.
3156 \param[in] reqp Request tracker.
3158 long cm_RemoveDir(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t *cnamep, cm_user_t *userp, cm_req_t *reqp)
3164 AFSFetchStatus updatedDirStatus;
3166 struct rx_connection * rxconnp;
3168 cm_scache_t *scp = NULL;
3169 int free_fnamep = FALSE;
3171 if (fnamep == NULL) {
3174 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ, &dirop);
3176 code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
3179 cm_EndDirOp(&dirop);
3186 code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
3190 /* before starting the RPC, mark that we're changing the directory data,
3191 * so that someone who does a chmod on the dir will wait until our
3194 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
3195 lock_ObtainWrite(&dscp->rw);
3196 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3197 lock_ReleaseWrite(&dscp->rw);
3199 cm_EndDirOp(&dirop);
3204 /* try the RPC now */
3205 osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%p", dscp);
3207 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3211 dirAFSFid.Volume = dscp->fid.volume;
3212 dirAFSFid.Vnode = dscp->fid.vnode;
3213 dirAFSFid.Unique = dscp->fid.unique;
3215 rxconnp = cm_GetRxConn(connp);
3216 code = RXAFS_RemoveDir(rxconnp, &dirAFSFid, fnamep,
3217 &updatedDirStatus, &volSync);
3218 rx_PutConnection(rxconnp);
3220 } while (cm_Analyze(connp, userp, reqp,
3221 &dscp->fid, &volSync, NULL, NULL, code));
3222 code = cm_MapRPCErrorRmdir(code, reqp);
3225 osi_Log1(afsd_logp, "CALL RemoveDir FAILURE, code 0x%x", code);
3227 osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
3230 lock_ObtainWrite(&dirop.scp->dirlock);
3231 dirop.lockType = CM_DIRLOCK_WRITE;
3233 lock_ObtainWrite(&dscp->rw);
3234 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3236 cm_dnlcRemove(dscp, cnamep);
3237 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
3239 lock_ReleaseWrite(&dscp->rw);
3242 if (cm_CheckDirOpForSingleChange(&dirop) && cnamep != NULL) {
3243 cm_DirDeleteEntry(&dirop, fnamep);
3245 cm_BPlusDirDeleteEntry(&dirop, cnamep);
3249 cm_EndDirOp(&dirop);
3252 cm_ReleaseSCache(scp);
3254 lock_ObtainWrite(&scp->rw);
3255 scp->flags |= CM_SCACHEFLAG_DELETED;
3256 lock_ReleaseWrite(&scp->rw);
3264 /* and return error code */
3268 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
3270 /* grab mutex on contents */
3271 lock_ObtainWrite(&scp->rw);
3273 /* reset the prefetch info */
3274 scp->prefetch.base.LowPart = 0; /* base */
3275 scp->prefetch.base.HighPart = 0;
3276 scp->prefetch.end.LowPart = 0; /* and end */
3277 scp->prefetch.end.HighPart = 0;
3279 /* release mutex on contents */
3280 lock_ReleaseWrite(&scp->rw);
3286 /*! \brief Rename a file or directory
3288 Encapsulates a RXAFS_Rename() call.
3290 \param[in] oldDscp cm_scache_t for the directory containing the old
3293 \param[in] oldNamep The original old name known to the file server.
3294 This is the name that will be passed into the RXAFS_Rename().
3295 If it is not provided, it will be looked up.
3297 \param[in] normalizedOldNamep Normalized old name. This is used for
3298 updating local directory caches.
3300 \param[in] newDscp cm_scache_t for the directory containing the new
3303 \param[in] newNamep New name. Normalized.
3305 \param[in] userp cm_user_t for the request.
3307 \param[in,out] reqp Request tracker.
3310 long cm_Rename(cm_scache_t *oldDscp, fschar_t *oldNamep, clientchar_t *cOldNamep,
3311 cm_scache_t *newDscp, clientchar_t *cNewNamep, cm_user_t *userp,
3316 AFSFid oldDirAFSFid;
3317 AFSFid newDirAFSFid;
3319 AFSFetchStatus updatedOldDirStatus;
3320 AFSFetchStatus updatedNewDirStatus;
3323 struct rx_connection * rxconnp;
3324 cm_dirOp_t oldDirOp;
3327 cm_dirOp_t newDirOp;
3328 fschar_t * newNamep = NULL;
3329 int free_oldNamep = FALSE;
3330 cm_scache_t *oldScp = NULL, *newScp = NULL;
3332 if (cOldNamep == NULL || cNewNamep == NULL ||
3333 cm_ClientStrLen(cOldNamep) == 0 ||
3334 cm_ClientStrLen(cNewNamep) == 0)
3335 return CM_ERROR_INVAL;
3338 * Before we permit the operation, make sure that we do not already have
3339 * an object in the destination directory that has a case-insensitive match
3340 * for this name UNLESS the matching object is the object we are renaming.
3342 code = cm_Lookup(oldDscp, cOldNamep, 0, userp, reqp, &oldScp);
3344 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S old name lookup failed",
3345 oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3349 code = cm_Lookup(newDscp, cNewNamep, CM_FLAG_CASEFOLD, userp, reqp, &newScp);
3351 /* found a matching object with the new name */
3352 if (cm_FidCmp(&oldScp->fid, &newScp->fid)) {
3353 /* and they don't match so return an error */
3354 osi_Log2(afsd_logp, "cm_Rename newDscp 0x%p cNewName %S new name already exists",
3355 newDscp, osi_LogSaveStringW(afsd_logp, cNewNamep));
3356 code = CM_ERROR_EXISTS;
3358 cm_ReleaseSCache(newScp);
3360 } else if (code == CM_ERROR_AMBIGUOUS_FILENAME) {
3361 code = CM_ERROR_EXISTS;
3368 if (oldNamep == NULL) {
3371 code = cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_READ, &oldDirOp);
3373 code = cm_BPlusDirLookupOriginalName(&oldDirOp, cOldNamep, &oldNamep);
3375 free_oldNamep = TRUE;
3376 cm_EndDirOp(&oldDirOp);
3380 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S Original Name lookup failed",
3381 oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3387 /* before starting the RPC, mark that we're changing the directory data,
3388 * so that someone who does a chmod on the dir will wait until our call
3389 * completes. We do this in vnode order so that we don't deadlock,
3390 * which makes the code a little verbose.
3392 if (oldDscp == newDscp) {
3393 /* check for identical names */
3394 if (cm_ClientStrCmp(cOldNamep, cNewNamep) == 0) {
3395 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_RENAME_IDENTICAL",
3397 code = CM_ERROR_RENAME_IDENTICAL;
3402 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3403 lock_ObtainWrite(&oldDscp->rw);
3404 cm_dnlcRemove(oldDscp, cOldNamep);
3405 cm_dnlcRemove(oldDscp, cNewNamep);
3406 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3407 CM_SCACHESYNC_STOREDATA);
3408 lock_ReleaseWrite(&oldDscp->rw);
3410 cm_EndDirOp(&oldDirOp);
3414 /* two distinct dir vnodes */
3416 if (oldDscp->fid.cell != newDscp->fid.cell ||
3417 oldDscp->fid.volume != newDscp->fid.volume) {
3418 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_CROSSDEVLINK",
3420 code = CM_ERROR_CROSSDEVLINK;
3424 /* shouldn't happen that we have distinct vnodes for two
3425 * different files, but could due to deliberate attack, or
3426 * stale info. Avoid deadlocks and quit now.
3428 if (oldDscp->fid.vnode == newDscp->fid.vnode) {
3429 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p vnode collision",
3431 code = CM_ERROR_CROSSDEVLINK;
3435 if (oldDscp->fid.vnode < newDscp->fid.vnode) {
3436 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3437 lock_ObtainWrite(&oldDscp->rw);
3438 cm_dnlcRemove(oldDscp, cOldNamep);
3439 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3440 CM_SCACHESYNC_STOREDATA);
3441 lock_ReleaseWrite(&oldDscp->rw);
3443 cm_EndDirOp(&oldDirOp);
3445 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE, &newDirOp);
3446 lock_ObtainWrite(&newDscp->rw);
3447 cm_dnlcRemove(newDscp, cNewNamep);
3448 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3449 CM_SCACHESYNC_STOREDATA);
3450 lock_ReleaseWrite(&newDscp->rw);
3452 cm_EndDirOp(&newDirOp);
3454 /* cleanup first one */
3455 lock_ObtainWrite(&oldDscp->rw);
3456 cm_SyncOpDone(oldDscp, NULL,
3457 CM_SCACHESYNC_STOREDATA);
3458 lock_ReleaseWrite(&oldDscp->rw);
3459 cm_EndDirOp(&oldDirOp);
3464 /* lock the new vnode entry first */
3465 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE, &newDirOp);
3466 lock_ObtainWrite(&newDscp->rw);
3467 cm_dnlcRemove(newDscp, cNewNamep);
3468 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3469 CM_SCACHESYNC_STOREDATA);
3470 lock_ReleaseWrite(&newDscp->rw);
3472 cm_EndDirOp(&newDirOp);
3474 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3475 lock_ObtainWrite(&oldDscp->rw);
3476 cm_dnlcRemove(oldDscp, cOldNamep);
3477 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3478 CM_SCACHESYNC_STOREDATA);
3479 lock_ReleaseWrite(&oldDscp->rw);
3481 cm_EndDirOp(&oldDirOp);
3483 /* cleanup first one */
3484 lock_ObtainWrite(&newDscp->rw);
3485 cm_SyncOpDone(newDscp, NULL,
3486 CM_SCACHESYNC_STOREDATA);
3487 lock_ReleaseWrite(&newDscp->rw);
3488 cm_EndDirOp(&newDirOp);
3492 } /* two distinct vnodes */
3499 newNamep = cm_ClientStringToFsStringAlloc(cNewNamep, -1, NULL);
3501 /* try the RPC now */
3502 osi_Log2(afsd_logp, "CALL Rename old scp 0x%p new scp 0x%p",
3505 code = cm_ConnFromFID(&oldDscp->fid, userp, reqp, &connp);
3509 oldDirAFSFid.Volume = oldDscp->fid.volume;
3510 oldDirAFSFid.Vnode = oldDscp->fid.vnode;
3511 oldDirAFSFid.Unique = oldDscp->fid.unique;
3512 newDirAFSFid.Volume = newDscp->fid.volume;
3513 newDirAFSFid.Vnode = newDscp->fid.vnode;
3514 newDirAFSFid.Unique = newDscp->fid.unique;
3516 rxconnp = cm_GetRxConn(connp);
3517 code = RXAFS_Rename(rxconnp, &oldDirAFSFid, oldNamep,
3518 &newDirAFSFid, newNamep,
3519 &updatedOldDirStatus, &updatedNewDirStatus,
3521 rx_PutConnection(rxconnp);
3523 } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
3524 &volSync, NULL, NULL, code));
3525 code = cm_MapRPCError(code, reqp);
3528 osi_Log1(afsd_logp, "CALL Rename FAILURE, code 0x%x", code);
3530 osi_Log0(afsd_logp, "CALL Rename SUCCESS");
3532 /* update the individual stat cache entries for the directories */
3534 lock_ObtainWrite(&oldDirOp.scp->dirlock);
3535 oldDirOp.lockType = CM_DIRLOCK_WRITE;
3537 lock_ObtainWrite(&oldDscp->rw);
3538 cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
3541 cm_MergeStatus(NULL, oldDscp, &updatedOldDirStatus, &volSync,
3542 userp, CM_MERGEFLAG_DIROP);
3543 lock_ReleaseWrite(&oldDscp->rw);
3545 if (code == 0 && cm_CheckDirOpForSingleChange(&oldDirOp)) {
3547 diropCode = cm_BPlusDirLookup(&oldDirOp, cOldNamep, &fileFid);
3548 if (diropCode == CM_ERROR_INEXACT_MATCH)
3550 else if (diropCode == EINVAL)
3552 diropCode = cm_DirLookup(&oldDirOp, oldNamep, &fileFid);
3554 if (diropCode == 0) {
3556 diropCode = cm_DirCreateEntry(&oldDirOp, newNamep, &fileFid);
3558 cm_BPlusDirCreateEntry(&oldDirOp, cNewNamep, &fileFid);
3562 if (diropCode == 0) {
3563 diropCode = cm_DirDeleteEntry(&oldDirOp, oldNamep);
3565 cm_BPlusDirDeleteEntry(&oldDirOp, cOldNamep);
3570 cm_EndDirOp(&oldDirOp);
3572 /* and update it for the new one, too, if necessary */
3575 lock_ObtainWrite(&newDirOp.scp->dirlock);
3576 newDirOp.lockType = CM_DIRLOCK_WRITE;
3578 lock_ObtainWrite(&newDscp->rw);
3579 cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
3581 cm_MergeStatus(NULL, newDscp, &updatedNewDirStatus, &volSync,
3582 userp, CM_MERGEFLAG_DIROP);
3583 lock_ReleaseWrite(&newDscp->rw);
3587 * The following optimization does not work.
3588 * When the file server processed a RXAFS_Rename() request the
3589 * FID of the object being moved between directories is not
3590 * preserved. The client does not know the new FID nor the
3591 * version number of the target. Not only can we not create
3592 * the directory entry in the new directory, but we can't
3593 * preserve the cached data for the file. It must be re-read
3594 * from the file server. - jaltman, 2009/02/20
3597 /* we only make the local change if we successfully made
3598 the change in the old directory AND there was only one
3599 change in the new directory */
3600 if (diropCode == 0 && cm_CheckDirOpForSingleChange(&newDirOp)) {
3601 cm_DirCreateEntry(&newDirOp, newNamep, &fileFid);
3603 cm_BPlusDirCreateEntry(&newDirOp, cNewNamep, &fileFid);
3608 cm_EndDirOp(&newDirOp);
3612 * After the rename the file server has invalidated the callbacks
3613 * on the file that was moved nor do we have a directory reference
3616 lock_ObtainWrite(&oldScp->rw);
3617 cm_DiscardSCache(oldScp);
3618 lock_ReleaseWrite(&oldScp->rw);
3622 cm_ReleaseSCache(oldScp);
3629 /* and return error code */
3633 /* Byte range locks:
3635 The OpenAFS Windows client has to fake byte range locks given no
3636 server side support for such locks. This is implemented as keyed
3637 byte range locks on the cache manager.
3639 Keyed byte range locks:
3641 Each cm_scache_t structure keeps track of a list of keyed locks.
3642 The key for a lock identifies an owner of a set of locks (referred
3643 to as a client). Each key is represented by a value. The set of
3644 key values used within a specific cm_scache_t structure form a
3645 namespace that has a scope of just that cm_scache_t structure. The
3646 same key value can be used with another cm_scache_t structure and
3647 correspond to a completely different client. However it is
3648 advantageous for the SMB or IFS layer to make sure that there is a
3649 1-1 mapping between client and keys over all cm_scache_t objects.
3651 Assume a client C has key Key(C) (although, since the scope of the
3652 key is a cm_scache_t, the key can be Key(C,S), where S is the
3653 cm_scache_t. But assume a 1-1 relation between keys and clients).
3654 A byte range (O,+L) denotes byte addresses (O) through (O+L-1)
3655 inclusive (a.k.a. [O,O+L-1]). The function Key(x) is implemented
3656 through cm_generateKey() function for both SMB and IFS.
3658 The list of locks for a cm_scache_t object S is maintained in
3659 S->fileLocks. The cache manager will set a lock on the AFS file
3660 server in order to assert the locks in S->fileLocks. If only
3661 shared locks are in place for S, then the cache manager will obtain
3662 a LockRead lock, while if there are any exclusive locks, it will
3663 obtain a LockWrite lock. If the exclusive locks are all released
3664 while the shared locks remain, then the cache manager will
3665 downgrade the lock from LockWrite to LockRead. Similarly, if an
3666 exclusive lock is obtained when only shared locks exist, then the
3667 cache manager will try to upgrade the lock from LockRead to
3670 Each lock L owned by client C maintains a key L->key such that
3671 L->key == Key(C), the effective range defined by L->LOffset and
3672 L->LLength such that the range of bytes affected by the lock is
3673 (L->LOffset, +L->LLength), a type maintained in L->LockType which
3674 is either exclusive or shared.
3678 A lock exists iff it is in S->fileLocks for some cm_scache_t
3679 S. Existing locks are in one of the following states: ACTIVE,
3680 WAITLOCK, WAITUNLOCK, LOST, DELETED.
3682 The following sections describe each lock and the associated
3685 1. ACTIVE: A lock L is ACTIVE iff the cache manager has asserted
3686 the lock with the AFS file server. This type of lock can be
3687 exercised by a client to read or write to the locked region (as
3690 1.1 ACTIVE->LOST: When the AFS file server fails to extend a
3691 server lock that was required to assert the lock. Before
3692 marking the lock as lost, the cache manager checks if the file
3693 has changed on the server. If the file has not changed, then
3694 the cache manager will attempt to obtain a new server lock
3695 that is sufficient to assert the client side locks for the
3696 file. If any of these fail, the lock is marked as LOST.
3697 Otherwise, it is left as ACTIVE.
3699 1.2 ACTIVE->DELETED: Lock is released.
3701 2. WAITLOCK: A lock is in a WAITLOCK state if the cache manager
3702 grants the lock but the lock is yet to be asserted with the AFS
3703 file server. Once the file server grants the lock, the state
3704 will transition to an ACTIVE lock.
3706 2.1 WAITLOCK->ACTIVE: The server granted the lock.
3708 2.2 WAITLOCK->DELETED: Lock is abandoned, or timed out during
3711 2.3 WAITLOCK->LOST: One or more locks from this client were
3712 marked as LOST. No further locks will be granted to this
3713 client until all lost locks are removed.
3715 3. WAITUNLOCK: A lock is in a WAITUNLOCK state if the cache manager
3716 receives a request for a lock that conflicts with an existing
3717 ACTIVE or WAITLOCK lock. The lock will be placed in the queue
3718 and will be granted at such time the conflicting locks are
3719 removed, at which point the state will transition to either
3722 3.1 WAITUNLOCK->ACTIVE: The conflicting lock was removed. The
3723 current serverLock is sufficient to assert this lock, or a
3724 sufficient serverLock is obtained.
3726 3.2 WAITUNLOCK->WAITLOCK: The conflicting lock was removed,
3727 however the required serverLock is yet to be asserted with the
3730 3.3 WAITUNLOCK->DELETED: The lock is abandoned, timed out or
3733 3.5 WAITUNLOCK->LOST: One or more locks from this client were
3734 marked as LOST. No further locks will be granted to this
3735 client until all lost locks are removed.
3737 4. LOST: A lock L is LOST if the server lock that was required to
3738 assert the lock could not be obtained or if it could not be
3739 extended, or if other locks by the same client were LOST.
3740 Essentially, once a lock is LOST, the contract between the cache
3741 manager and that specific client is no longer valid.
3743 The cache manager rechecks the server lock once every minute and
3744 extends it as appropriate. If this is not done for 5 minutes,
3745 the AFS file server will release the lock (the 5 minute timeout
3746 is based on current file server code and is fairly arbitrary).
3747 Once released, the lock cannot be re-obtained without verifying
3748 that the contents of the file hasn't been modified since the
3749 time the lock was released. Re-obtaining the lock without
3750 verifying this may lead to data corruption. If the lock can not
3751 be obtained safely, then all active locks for the cm_scache_t
3754 4.1 LOST->DELETED: The lock is released.
3756 5. DELETED: The lock is no longer relevant. Eventually, it will
3757 get removed from the cm_scache_t. In the meantime, it will be
3758 treated as if it does not exist.
3760 5.1 DELETED->not exist: The lock is removed from the
3763 The following are classifications of locks based on their state.
3765 6* A lock L is ACCEPTED if it is ACTIVE or WAITLOCK. These locks
3766 have been accepted by the cache manager, but may or may not have
3767 been granted back to the client.
3769 7* A lock L is QUEUED if it is ACTIVE, WAITLOCK or WAITUNLOCK.
3771 8* A lock L is WAITING if it is WAITLOCK or WAITUNLOCK.
3775 A client C can READ range (Offset,+Length) of a file represented by
3776 cm_scache_t S iff (1):
3778 1. for all _a_ in (Offset,+Length), all of the following is true:
3780 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3781 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3784 1.2 For each LOST lock L in S->fileLocks such that _a_ in
3785 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3788 (When locks are lost on an cm_scache_t, all locks are lost. By
3789 4.2 (below), if there is an exclusive LOST lock, then there
3790 can't be any overlapping ACTIVE locks.)
3792 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
3794 2. for all _a_ in (Offset,+Length), one of the following is true:
3796 2.1 Byte _a_ of S is unowned (as specified in 1.1) AND there
3797 does not exist a LOST lock L such that _a_ in
3798 (L->LOffset,+L->LLength).
3800 2.2 Byte _a_ of S is owned by C under lock L (as specified in
3801 1.2) AND L->LockType is exclusive.
3803 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
3805 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
3808 3.1 If L->LockType is exclusive then there does NOT exist a
3809 ACCEPTED lock M in S->fileLocks such that _a_ in
3810 (M->LOffset,+M->LLength).
3812 (If we count all QUEUED locks then we hit cases such as
3813 cascading waiting locks where the locks later on in the queue
3814 can be granted without compromising file integrity. On the
3815 other hand if only ACCEPTED locks are considered, then locks
3816 that were received earlier may end up waiting for locks that
3817 were received later to be unlocked. The choice of ACCEPTED
3818 locks was made to mimic the Windows byte range lock
3821 3.2 If L->LockType is shared then for each ACCEPTED lock M in
3822 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
3823 M->LockType is shared.
3825 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
3827 4.1 M->key != Key(C)
3829 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
3830 and (M->LOffset,+M->LLength) do not intersect.
3832 (Note: If a client loses a lock, it loses all locks.
3833 Subsequently, it will not be allowed to obtain any more locks
3834 until all existing LOST locks that belong to the client are
3835 released. Once all locks are released by a single client,
3836 there exists no further contract between the client and AFS
3837 about the contents of the file, hence the client can then
3838 proceed to obtain new locks and establish a new contract.
3840 This doesn't quite work as you think it should, because most
3841 applications aren't built to deal with losing locks they
3842 thought they once had. For now, we don't have a good
3843 solution to lost locks.
3845 Also, for consistency reasons, we have to hold off on
3846 granting locks that overlap exclusive LOST locks.)
3848 A client C can only unlock locks L in S->fileLocks which have
3851 The representation and invariants are as follows:
3853 - Each cm_scache_t structure keeps:
3855 - A queue of byte-range locks (cm_scache_t::fileLocks) which
3856 are of type cm_file_lock_t.
3858 - A record of the highest server-side lock that has been
3859 obtained for this object (cm_scache_t::serverLock), which is
3860 one of (-1), LockRead, LockWrite.
3862 - A count of ACCEPTED exclusive and shared locks that are in the
3863 queue (cm_scache_t::sharedLocks and
3864 cm_scache_t::exclusiveLocks)
3866 - Each cm_file_lock_t structure keeps:
3868 - The type of lock (cm_file_lock_t::LockType)
3870 - The key associated with the lock (cm_file_lock_t::key)
3872 - The offset and length of the lock (cm_file_lock_t::LOffset
3873 and cm_file_lock_t::LLength)
3875 - The state of the lock.
3877 - Time of issuance or last successful extension
3879 Semantic invariants:
3881 I1. The number of ACCEPTED locks in S->fileLocks are
3882 (S->sharedLocks + S->exclusiveLocks)
3884 External invariants:
3886 I3. S->serverLock is the lock that we have asserted with the
3887 AFS file server for this cm_scache_t.
3889 I4. S->serverLock == LockRead iff there is at least one ACTIVE
3890 shared lock, but no ACTIVE exclusive locks.
3892 I5. S->serverLock == LockWrite iff there is at least one ACTIVE
3895 I6. If L is a LOST lock, then for each lock M in S->fileLocks,
3896 M->key == L->key IMPLIES M is LOST or DELETED.
3901 #define IS_LOCK_ACTIVE(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == 0)
3903 #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)
3905 #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)
3907 #define IS_LOCK_LOST(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_LOST)
3909 #define IS_LOCK_DELETED(lockp) (((lockp)->flags & CM_FILELOCK_FLAG_DELETED) == CM_FILELOCK_FLAG_DELETED)
3912 #define IS_LOCK_ACCEPTED(lockp) (IS_LOCK_ACTIVE(lockp) || IS_LOCK_WAITLOCK(lockp))
3915 #define IS_LOCK_CLIENTONLY(lockp) ((((lockp)->scp->flags & CM_SCACHEFLAG_RO) == CM_SCACHEFLAG_RO) || (((lockp)->flags & CM_FILELOCK_FLAG_CLIENTONLY) == CM_FILELOCK_FLAG_CLIENTONLY))
3918 #define INTERSECT_RANGE(r1,r2) (((r2).offset+(r2).length) > (r1).offset && ((r1).offset +(r1).length) > (r2).offset)
3921 #define CONTAINS_RANGE(r1,r2) (((r2).offset+(r2).length) <= ((r1).offset+(r1).length) && (r1).offset <= (r2).offset)
3923 #if defined(VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS) && !defined(LOCK_TESTING)
3924 #define SCP_SUPPORTS_BRLOCKS(scp) ((scp)->cbServerp && ((scp)->cbServerp->capabilities & VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS))
3926 #define SCP_SUPPORTS_BRLOCKS(scp) (1)
3929 #define SERVERLOCKS_ENABLED(scp) (!((scp)->flags & CM_SCACHEFLAG_RO) && cm_enableServerLocks && SCP_SUPPORTS_BRLOCKS(scp))
3931 #if defined(VICED_CAPABILITY_WRITELOCKACL)
3932 #define SCP_SUPPORTS_WRITELOCKACL(scp) ((scp)->cbServerp && ((scp->cbServerp->capabilities & VICED_CAPABILITY_WRITELOCKACL)))
3934 #define SCP_SUPPORTS_WRITELOCKACL(scp) (0)
3936 /* This should really be defined in any build that this code is being
3938 #error VICED_CAPABILITY_WRITELOCKACL not defined.
3941 static void cm_LockRangeSubtract(cm_range_t * pos, const cm_range_t * neg)
3943 afs_int64 int_begin;
3946 int_begin = MAX(pos->offset, neg->offset);
3947 int_end = MIN(pos->offset+pos->length, neg->offset+neg->length);
3949 if (int_begin < int_end) {
3950 if (int_begin == pos->offset) {
3951 pos->length = pos->offset + pos->length - int_end;
3952 pos->offset = int_end;
3953 } else if (int_end == pos->offset + pos->length) {
3954 pos->length = int_begin - pos->offset;
3957 /* We only subtract ranges if the resulting range is
3958 contiguous. If we try to support non-contigous ranges, we
3959 aren't actually improving performance. */
3963 /* Called with scp->rw held. Returns 0 if all is clear to read the
3964 specified range by the client identified by key.
3966 long cm_LockCheckRead(cm_scache_t *scp,
3967 LARGE_INTEGER LOffset,
3968 LARGE_INTEGER LLength,
3971 #ifndef ADVISORY_LOCKS
3973 cm_file_lock_t *fileLock;
3977 int substract_ranges = FALSE;
3979 range.offset = LOffset.QuadPart;
3980 range.length = LLength.QuadPart;
3984 1. for all _a_ in (Offset,+Length), all of the following is true:
3986 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3987 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3990 1.2 For each LOST lock L in S->fileLocks such that _a_ in
3991 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3996 lock_ObtainRead(&cm_scacheLock);
3998 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
4000 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4002 if (INTERSECT_RANGE(range, fileLock->range)) {
4003 if (IS_LOCK_ACTIVE(fileLock)) {
4004 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4006 /* If there is an active lock for this client, it
4007 is safe to substract ranges.*/
4008 cm_LockRangeSubtract(&range, &fileLock->range);
4009 substract_ranges = TRUE;
4011 if (fileLock->lockType != LockRead) {
4012 code = CM_ERROR_LOCK_CONFLICT;
4016 /* even if the entire range is locked for reading,
4017 we still can't grant the lock at this point
4018 because the client may have lost locks. That
4019 is, unless we have already seen an active lock
4020 belonging to the client, in which case there
4021 can't be any lost locks for this client. */
4022 if (substract_ranges)
4023 cm_LockRangeSubtract(&range, &fileLock->range);
4025 } else if (IS_LOCK_LOST(fileLock) &&
4026 (cm_KeyEquals(&fileLock->key, &key, 0) || fileLock->lockType == LockWrite)) {
4027 code = CM_ERROR_BADFD;
4033 lock_ReleaseRead(&cm_scacheLock);
4035 osi_Log4(afsd_logp, "cm_LockCheckRead scp 0x%x offset %d length %d code 0x%x",
4036 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
4047 /* Called with scp->rw held. Returns 0 if all is clear to write the
4048 specified range by the client identified by key.
4050 long cm_LockCheckWrite(cm_scache_t *scp,
4051 LARGE_INTEGER LOffset,
4052 LARGE_INTEGER LLength,
4055 #ifndef ADVISORY_LOCKS
4057 cm_file_lock_t *fileLock;
4062 range.offset = LOffset.QuadPart;
4063 range.length = LLength.QuadPart;
4066 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
4068 2. for all _a_ in (Offset,+Length), one of the following is true:
4070 2.1 Byte _a_ of S is unowned AND there does not exist a LOST
4071 lock L such that _a_ in (L->LOffset,+L->LLength).
4073 2.2 Byte _a_ of S is owned by C under lock L AND L->LockType is
4077 lock_ObtainRead(&cm_scacheLock);
4079 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
4081 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4083 if (INTERSECT_RANGE(range, fileLock->range)) {
4084 if (IS_LOCK_ACTIVE(fileLock)) {
4085 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4086 if (fileLock->lockType == LockWrite) {
4088 /* if there is an active lock for this client, it
4089 is safe to substract ranges */
4090 cm_LockRangeSubtract(&range, &fileLock->range);
4092 code = CM_ERROR_LOCK_CONFLICT;
4096 code = CM_ERROR_LOCK_CONFLICT;
4099 } else if (IS_LOCK_LOST(fileLock)) {
4100 code = CM_ERROR_BADFD;
4106 lock_ReleaseRead(&cm_scacheLock);
4108 osi_Log4(afsd_logp, "cm_LockCheckWrite scp 0x%x offset %d length %d code 0x%x",
4109 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
4120 /* Called with cm_scacheLock write locked */
4121 static cm_file_lock_t * cm_GetFileLock(void) {
4124 l = (cm_file_lock_t *) cm_freeFileLocks;
4126 osi_QRemove(&cm_freeFileLocks, &l->q);
4128 l = malloc(sizeof(cm_file_lock_t));
4129 osi_assertx(l, "null cm_file_lock_t");
4132 memset(l, 0, sizeof(cm_file_lock_t));
4137 /* Called with cm_scacheLock write locked */
4138 static void cm_PutFileLock(cm_file_lock_t *l) {
4139 osi_QAdd(&cm_freeFileLocks, &l->q);
4142 /* called with scp->rw held. May release it during processing, but
4143 leaves it held on exit. */
4144 long cm_IntSetLock(cm_scache_t * scp, cm_user_t * userp, int lockType,
4150 struct rx_connection * rxconnp;
4152 afs_uint32 reqflags = reqp->flags;
4154 tfid.Volume = scp->fid.volume;
4155 tfid.Vnode = scp->fid.vnode;
4156 tfid.Unique = scp->fid.unique;
4159 osi_Log2(afsd_logp, "CALL SetLock scp 0x%p for lock %d", scp, lockType);
4161 reqp->flags |= CM_REQ_NORETRY;
4162 lock_ReleaseWrite(&scp->rw);
4165 code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4169 rxconnp = cm_GetRxConn(connp);
4170 code = RXAFS_SetLock(rxconnp, &tfid, lockType,
4172 rx_PutConnection(rxconnp);
4174 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
4177 code = cm_MapRPCError(code, reqp);
4179 osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
4181 osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
4184 lock_ObtainWrite(&scp->rw);
4185 reqp->flags = reqflags;
4189 /* called with scp->rw held. Releases it during processing */
4190 long cm_IntReleaseLock(cm_scache_t * scp, cm_user_t * userp,
4196 struct rx_connection * rxconnp;
4199 tfid.Volume = scp->fid.volume;
4200 tfid.Vnode = scp->fid.vnode;
4201 tfid.Unique = scp->fid.unique;
4204 lock_ReleaseWrite(&scp->rw);
4206 osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
4209 code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4213 rxconnp = cm_GetRxConn(connp);
4214 code = RXAFS_ReleaseLock(rxconnp, &tfid, &volSync);
4215 rx_PutConnection(rxconnp);
4217 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
4219 code = cm_MapRPCError(code, reqp);
4222 "CALL ReleaseLock FAILURE, code 0x%x", code);
4225 "CALL ReleaseLock SUCCESS");
4227 lock_ObtainWrite(&scp->rw);
4232 /* called with scp->rw held. May release it during processing, but
4233 will exit with lock held.
4237 - 0 if the user has permission to get the specified lock for the scp
4239 - CM_ERROR_NOACCESS if not
4241 Any other error from cm_SyncOp will be sent down untranslated.
4243 If CM_ERROR_NOACCESS is returned and lock_type is LockRead, then
4244 phas_insert (if non-NULL) will receive a boolean value indicating
4245 whether the user has INSERT permission or not.
4247 long cm_LockCheckPerms(cm_scache_t * scp,
4254 long code = 0, code2 = 0;
4256 /* lock permissions are slightly tricky because of the 'i' bit.
4257 If the user has PRSFS_LOCK, she can read-lock the file. If the
4258 user has PRSFS_WRITE, she can write-lock the file. However, if
4259 the user has PRSFS_INSERT, then she can write-lock new files,
4260 but not old ones. Since we don't have information about
4261 whether a file is new or not, we assume that if the user owns
4262 the scp, then she has the permissions that are granted by
4265 osi_Log3(afsd_logp, "cm_LockCheckPerms for scp[0x%p] type[%d] user[0x%p]",
4266 scp, lock_type, userp);
4268 if (lock_type == LockRead)
4269 rights |= PRSFS_LOCK;
4270 else if (lock_type == LockWrite)
4271 rights |= PRSFS_WRITE | PRSFS_LOCK;
4274 osi_assertx(FALSE, "invalid lock type");
4279 *phas_insert = FALSE;
4281 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
4282 CM_SCACHESYNC_GETSTATUS |
4283 CM_SCACHESYNC_NEEDCALLBACK);
4285 if (phas_insert && scp->creator == userp) {
4287 /* If this file was created by the user, then we check for
4288 PRSFS_INSERT. If the file server is recent enough, then
4289 this should be sufficient for her to get a write-lock (but
4290 not necessarily a read-lock). VICED_CAPABILITY_WRITELOCKACL
4291 indicates whether a file server supports getting write
4292 locks when the user only has PRSFS_INSERT.
4294 If the file was not created by the user we skip the check
4295 because the INSERT bit will not apply to this user even
4299 code2 = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
4300 CM_SCACHESYNC_GETSTATUS |
4301 CM_SCACHESYNC_NEEDCALLBACK);
4303 if (code2 == CM_ERROR_NOACCESS) {
4304 osi_Log0(afsd_logp, "cm_LockCheckPerms user has no INSERT bits");
4306 *phas_insert = TRUE;
4307 osi_Log0(afsd_logp, "cm_LockCheckPerms user has INSERT bits");
4311 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
4313 osi_Log1(afsd_logp, "cm_LockCheckPerms returning code %d", code);
4318 /* called with scp->rw held */
4319 long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
4320 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
4322 int allowWait, cm_user_t *userp, cm_req_t *reqp,
4323 cm_file_lock_t **lockpp)
4326 int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
4327 cm_file_lock_t *fileLock;
4330 int wait_unlock = FALSE;
4331 int force_client_lock = FALSE;
4333 osi_Log4(afsd_logp, "cm_Lock scp 0x%x type 0x%x offset %d length %d",
4334 scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
4335 osi_Log4(afsd_logp, "... allowWait %d key <0x%x, 0x%x, 0x%x>", allowWait,
4336 key.process_id, key.session_id, key.file_id);
4339 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
4341 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
4344 3.1 If L->LockType is exclusive then there does NOT exist a
4345 ACCEPTED lock M in S->fileLocks such that _a_ in
4346 (M->LOffset,+M->LLength).
4348 3.2 If L->LockType is shared then for each ACCEPTED lock M in
4349 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
4350 M->LockType is shared.
4352 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
4354 4.1 M->key != Key(C)
4356 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
4357 and (M->LOffset,+M->LLength) do not intersect.
4360 range.offset = LOffset.QuadPart;
4361 range.length = LLength.QuadPart;
4363 lock_ObtainRead(&cm_scacheLock);
4365 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4367 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4369 if (IS_LOCK_LOST(fileLock)) {
4370 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4371 code = CM_ERROR_BADFD;
4373 } else if (fileLock->lockType == LockWrite && INTERSECT_RANGE(range, fileLock->range)) {
4374 code = CM_ERROR_WOULDBLOCK;
4380 /* we don't need to check for deleted locks here since deleted
4381 locks are dequeued from scp->fileLocks */
4382 if (IS_LOCK_ACCEPTED(fileLock) &&
4383 INTERSECT_RANGE(range, fileLock->range)) {
4385 if ((sLockType & LOCKING_ANDX_SHARED_LOCK) == 0 ||
4386 fileLock->lockType != LockRead) {
4388 code = CM_ERROR_WOULDBLOCK;
4394 lock_ReleaseRead(&cm_scacheLock);
4396 if (code == 0 && SERVERLOCKS_ENABLED(scp)) {
4397 if (Which == scp->serverLock ||
4398 (Which == LockRead && scp->serverLock == LockWrite)) {
4402 /* we already have the lock we need */
4403 osi_Log3(afsd_logp, " we already have the correct lock. exclusives[%d], shared[%d], serverLock[%d]",
4404 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4406 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4408 /* special case: if we don't have permission to read-lock
4409 the file, then we force a clientside lock. This is to
4410 compensate for applications that obtain a read-lock for
4411 reading files off of directories that don't grant
4412 read-locks to the user. */
4413 if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4415 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4416 osi_Log0(afsd_logp, " User has no read-lock perms, but has INSERT perms.");
4419 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4420 force_client_lock = TRUE;
4424 } else if ((scp->exclusiveLocks > 0) ||
4425 (scp->sharedLocks > 0 && scp->serverLock != LockRead)) {
4428 /* We are already waiting for some other lock. We should
4429 wait for the daemon to catch up instead of generating a
4430 flood of SetLock calls. */
4431 osi_Log3(afsd_logp, " already waiting for other lock. exclusives[%d], shared[%d], serverLock[%d]",
4432 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4434 /* see if we have permission to create the lock in the
4436 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4438 code = CM_ERROR_WOULDBLOCK;
4439 else if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4441 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4443 " User has no read-lock perms, but has INSERT perms.");
4444 code = CM_ERROR_WOULDBLOCK;
4447 " User has no read-lock perms. Forcing client-side lock");
4448 force_client_lock = TRUE;
4452 /* leave any other codes as-is */
4456 int check_data_version = FALSE;
4459 /* first check if we have permission to elevate or obtain
4461 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4463 if (code == CM_ERROR_NOACCESS && Which == LockRead &&
4464 (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp))) {
4465 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4466 force_client_lock = TRUE;
4471 /* has_insert => (Which == LockRead, code == CM_ERROR_NOACCESS) */
4473 if (scp->serverLock == LockRead && Which == LockWrite) {
4475 /* We want to escalate the lock to a LockWrite.
4476 * Unfortunately that's not really possible without
4477 * letting go of the current lock. But for now we do
4481 " attempting to UPGRADE from LockRead to LockWrite.");
4483 " dataVersion on scp: %I64d", scp->dataVersion);
4485 /* we assume at this point (because scp->serverLock
4486 was valid) that we had a valid server lock. */
4487 scp->lockDataVersion = scp->dataVersion;
4488 check_data_version = TRUE;
4490 code = cm_IntReleaseLock(scp, userp, reqp);
4493 /* We couldn't release the lock */
4496 scp->serverLock = -1;
4500 /* We need to obtain a server lock of type Which in order
4501 * to assert this file lock */
4502 #ifndef AGGRESSIVE_LOCKS
4505 newLock = LockWrite;
4508 code = cm_IntSetLock(scp, userp, newLock, reqp);
4510 #ifdef AGGRESSIVE_LOCKS
4511 if ((code == CM_ERROR_WOULDBLOCK ||
4512 code == CM_ERROR_NOACCESS) && newLock != Which) {
4513 /* we wanted LockRead. We tried LockWrite. Now try
4518 osi_assertx(newLock == LockRead, "lock type not read");
4520 code = cm_IntSetLock(scp, userp, newLock, reqp);
4524 if (code == CM_ERROR_NOACCESS) {
4525 if (Which == LockRead) {
4526 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4528 /* We requested a read-lock, but we have permission to
4529 * get a write-lock. Try that */
4531 tcode = cm_LockCheckPerms(scp, LockWrite, userp, reqp, NULL);
4534 newLock = LockWrite;
4536 osi_Log0(afsd_logp, " User has 'i' perms and the request was for a LockRead. Trying to get a LockWrite instead");
4538 code = cm_IntSetLock(scp, userp, newLock, reqp);
4541 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4542 force_client_lock = TRUE;
4544 } else if (Which == LockWrite &&
4545 scp->creator == userp && !SCP_SUPPORTS_WRITELOCKACL(scp)) {
4548 /* Special case: if the lock request was for a
4549 * LockWrite and the user owns the file and we weren't
4550 * allowed to obtain the serverlock, we either lost a
4551 * race (the permissions changed from under us), or we
4552 * have 'i' bits, but we aren't allowed to lock the
4555 /* check if we lost a race... */
4556 tcode = cm_LockCheckPerms(scp, Which, userp, reqp, NULL);
4559 osi_Log0(afsd_logp, " User has 'i' perms but can't obtain write locks. Using client-side locks.");
4560 force_client_lock = TRUE;
4565 if (code == 0 && check_data_version &&
4566 scp->dataVersion != scp->lockDataVersion) {
4567 /* We lost a race. Although we successfully obtained
4568 * a lock, someone modified the file in between. The
4569 * locks have all been technically lost. */
4572 " Data version mismatch while upgrading lock.");
4574 " Data versions before=%I64d, after=%I64d",
4575 scp->lockDataVersion,
4578 " Releasing stale lock for scp 0x%x", scp);
4580 code = cm_IntReleaseLock(scp, userp, reqp);
4582 scp->serverLock = -1;
4584 code = CM_ERROR_INVAL;
4585 } else if (code == 0) {
4586 scp->serverLock = newLock;
4587 scp->lockDataVersion = scp->dataVersion;
4591 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4592 scp->serverLock == -1) {
4593 /* Oops. We lost the lock. */
4594 cm_LockMarkSCacheLost(scp);
4597 } else if (code == 0) { /* server locks not enabled */
4599 " Skipping server lock for scp");
4604 if (code != 0 && !force_client_lock) {
4605 /* Special case error translations
4607 Applications don't expect certain errors from a
4608 LockFile/UnlockFile call. We need to translate some error
4609 code to codes that apps expect and handle. */
4611 /* We shouldn't actually need to handle this case since we
4612 simulate locks for RO scps anyway. */
4613 if (code == CM_ERROR_READONLY) {
4614 osi_Log0(afsd_logp, " Reinterpreting CM_ERROR_READONLY as CM_ERROR_NOACCESS");
4615 code = CM_ERROR_NOACCESS;
4619 if (code == 0 || (code == CM_ERROR_WOULDBLOCK && allowWait) ||
4620 force_client_lock) {
4622 /* clear the error if we are forcing a client lock, so we
4623 don't get confused later. */
4624 if (force_client_lock && code != CM_ERROR_WOULDBLOCK)
4627 lock_ObtainWrite(&cm_scacheLock);
4628 fileLock = cm_GetFileLock();
4629 lock_ReleaseWrite(&cm_scacheLock);
4631 fileLock->fid = scp->fid;
4633 fileLock->key = key;
4634 fileLock->lockType = Which;
4636 fileLock->userp = userp;
4637 fileLock->range = range;
4638 fileLock->flags = (code == 0 ? 0 :
4640 CM_FILELOCK_FLAG_WAITUNLOCK :
4641 CM_FILELOCK_FLAG_WAITLOCK));
4643 if (force_client_lock || !SERVERLOCKS_ENABLED(scp))
4644 fileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
4646 fileLock->lastUpdate = (code == 0 && !force_client_lock) ? time(NULL) : 0;
4648 lock_ObtainWrite(&cm_scacheLock);
4649 osi_QAddT(&scp->fileLocksH, &scp->fileLocksT, &fileLock->fileq);
4650 cm_HoldSCacheNoLock(scp);
4651 fileLock->scp = scp;
4652 osi_QAdd(&cm_allFileLocks, &fileLock->q);
4653 lock_ReleaseWrite(&cm_scacheLock);
4659 if (IS_LOCK_CLIENTONLY(fileLock)) {
4661 } else if (IS_LOCK_ACCEPTED(fileLock)) {
4662 if (Which == LockRead)
4665 scp->exclusiveLocks++;
4669 "cm_Lock Lock added 0x%p flags 0x%x to scp [0x%p]",
4670 fileLock, fileLock->flags, scp);
4672 " exclusives[%d] shared[%d] client[%d] serverLock[%d]",
4673 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4674 (int)(signed char) scp->serverLock);
4677 "cm_Lock Rejecting lock (code = 0x%x)", code);
4683 /* Called with scp->rw held */
4684 long cm_UnlockByKey(cm_scache_t * scp,
4691 cm_file_lock_t *fileLock;
4692 osi_queue_t *q, *qn;
4695 osi_Log4(afsd_logp, "cm_UnlockByKey scp 0x%p key <0x%x,0x%x,0x%x",
4696 scp, key.process_id, key.session_id, key.file_id);
4697 osi_Log1(afsd_logp, " flags=0x%x", flags);
4699 lock_ObtainWrite(&cm_scacheLock);
4701 for (q = scp->fileLocksH; q; q = qn) {
4704 fileLock = (cm_file_lock_t *)
4705 ((char *) q - offsetof(cm_file_lock_t, fileq));
4708 osi_Log4(afsd_logp, " Checking lock[0x%x] range[%d,+%d] type[%d]",
4710 (unsigned long) fileLock->range.offset,
4711 (unsigned long) fileLock->range.length,
4712 fileLock->lockType);
4713 osi_Log4(afsd_logp, " key<0x%x, 0x%x, 0x%x> flags[0x%x]",
4714 fileLock->key.process_id, fileLock->key.session_id, fileLock->key.file_id,
4717 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
4718 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
4719 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4721 fileLock->fid.volume,
4722 fileLock->fid.vnode,
4723 fileLock->fid.unique);
4724 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4725 fileLock->scp->fid.cell,
4726 fileLock->scp->fid.volume,
4727 fileLock->scp->fid.vnode,
4728 fileLock->scp->fid.unique);
4729 osi_assertx(FALSE, "invalid fid value");
4733 if (!IS_LOCK_DELETED(fileLock) &&
4734 cm_KeyEquals(&fileLock->key, &key, flags)) {
4735 osi_Log3(afsd_logp, "...Unlock range [%d,+%d] type %d",
4736 fileLock->range.offset,
4737 fileLock->range.length,
4738 fileLock->lockType);
4740 if (scp->fileLocksT == q)
4741 scp->fileLocksT = osi_QPrev(q);
4742 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
4744 if (IS_LOCK_CLIENTONLY(fileLock)) {
4746 } else if (IS_LOCK_ACCEPTED(fileLock)) {
4747 if (fileLock->lockType == LockRead)
4750 scp->exclusiveLocks--;
4753 fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
4755 cm_ReleaseUser(fileLock->userp);
4756 cm_ReleaseSCacheNoLock(scp);
4758 fileLock->userp = NULL;
4759 fileLock->scp = NULL;
4765 lock_ReleaseWrite(&cm_scacheLock);
4767 if (n_unlocks == 0) {
4768 osi_Log0(afsd_logp, "cm_UnlockByKey no locks found");
4769 osi_Log3(afsd_logp, " Leaving scp with exclusives[%d], shared[%d], serverLock[%d]",
4770 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4775 osi_Log1(afsd_logp, "cm_UnlockByKey done with %d locks", n_unlocks);
4777 osi_assertx(scp->sharedLocks >= 0, "scp->sharedLocks < 0");
4778 osi_assertx(scp->exclusiveLocks >= 0, "scp->exclusiveLocks < 0");
4779 osi_assertx(scp->clientLocks >= 0, "scp->clientLocks < 0");
4781 if (!SERVERLOCKS_ENABLED(scp)) {
4782 osi_Log0(afsd_logp, " Skipping server lock for scp");
4786 /* Ideally we would go through the rest of the locks to determine
4787 * if one or more locks that were formerly in WAITUNLOCK can now
4788 * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
4789 * scp->sharedLocks accordingly. However, the retrying of locks
4790 * in that manner is done cm_RetryLock() manually.
4793 if (scp->serverLock == LockWrite &&
4794 scp->exclusiveLocks == 0 &&
4795 scp->sharedLocks > 0) {
4797 /* The serverLock should be downgraded to LockRead */
4798 osi_Log0(afsd_logp, " DOWNGRADE lock from LockWrite to LockRead");
4800 /* since scp->serverLock looked sane, we are going to assume
4801 that we have a valid server lock. */
4802 scp->lockDataVersion = scp->dataVersion;
4803 osi_Log1(afsd_logp, " dataVersion on scp = %I64d", scp->dataVersion);
4805 code = cm_IntReleaseLock(scp, userp, reqp);
4808 /* so we couldn't release it. Just let the lock be for now */
4812 scp->serverLock = -1;
4815 code = cm_IntSetLock(scp, userp, LockRead, reqp);
4817 if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
4818 scp->serverLock = LockRead;
4819 } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
4820 /* We lost a race condition. Although we have a valid
4821 lock on the file, the data has changed and essentially
4822 we have lost the lock we had during the transition. */
4824 osi_Log0(afsd_logp, "Data version mismatch during lock downgrade");
4825 osi_Log2(afsd_logp, " Data versions before=%I64d, after=%I64d",
4826 scp->lockDataVersion,
4829 code = cm_IntReleaseLock(scp, userp, reqp);
4831 code = CM_ERROR_INVAL;
4832 scp->serverLock = -1;
4836 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4837 (scp->serverLock == -1)) {
4839 cm_LockMarkSCacheLost(scp);
4842 /* failure here has no bearing on the return value of
4846 } else if (scp->serverLock != (-1) &&
4847 scp->exclusiveLocks == 0 &&
4848 scp->sharedLocks == 0) {
4849 /* The serverLock should be released entirely */
4851 code = cm_IntReleaseLock(scp, userp, reqp);
4854 scp->serverLock = (-1);
4859 osi_Log1(afsd_logp, "cm_UnlockByKey code 0x%x", code);
4860 osi_Log4(afsd_logp, " Leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
4861 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4862 (int)(signed char) scp->serverLock);
4867 long cm_Unlock(cm_scache_t *scp,
4868 unsigned char sLockType,
4869 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
4876 int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
4877 cm_file_lock_t *fileLock;
4879 int release_userp = FALSE;
4880 int exact_match = !(flags & CM_UNLOCK_FLAG_MATCH_RANGE);
4882 LARGE_INTEGER RangeEnd;
4884 osi_Log4(afsd_logp, "cm_Unlock scp 0x%p type 0x%x offset %d length %d",
4885 scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
4886 osi_Log4(afsd_logp, "... key <0x%x,0x%x,0x%x> flags 0x%x",
4887 key.process_id, key.session_id, key.file_id, flags);
4890 RangeEnd.QuadPart = LOffset.QuadPart + LLength.QuadPart;
4893 lock_ObtainRead(&cm_scacheLock);
4895 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4896 fileLock = (cm_file_lock_t *)
4897 ((char *) q - offsetof(cm_file_lock_t, fileq));
4900 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
4901 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
4902 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4904 fileLock->fid.volume,
4905 fileLock->fid.vnode,
4906 fileLock->fid.unique);
4907 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4908 fileLock->scp->fid.cell,
4909 fileLock->scp->fid.volume,
4910 fileLock->scp->fid.vnode,
4911 fileLock->scp->fid.unique);
4912 osi_assertx(FALSE, "invalid fid value");
4916 if (!IS_LOCK_DELETED(fileLock) &&
4917 cm_KeyEquals(&fileLock->key, &key, 0) &&
4918 fileLock->range.offset == LOffset.QuadPart &&
4919 fileLock->range.length == LLength.QuadPart) {
4925 if (!IS_LOCK_DELETED(fileLock) &&
4926 cm_KeyEquals(&fileLock->key, &key, 0) &&
4927 fileLock->range.offset >= LOffset.QuadPart &&
4928 fileLock->range.offset < RangeEnd.QuadPart &&
4929 (fileLock->range.offset + fileLock->range.length) <= RangeEnd.QuadPart) {
4937 lock_ReleaseRead(&cm_scacheLock);
4939 if (lock_found && !exact_match) {
4943 osi_Log0(afsd_logp, "cm_Unlock lock not found; failure");
4945 /* The lock didn't exist anyway. *shrug* */
4946 return CM_ERROR_RANGE_NOT_LOCKED;
4950 /* discard lock record */
4951 lock_ConvertRToW(&cm_scacheLock);
4952 if (scp->fileLocksT == q)
4953 scp->fileLocksT = osi_QPrev(q);
4954 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
4957 * Don't delete it here; let the daemon delete it, to simplify
4958 * the daemon's traversal of the list.
4961 if (IS_LOCK_CLIENTONLY(fileLock)) {
4963 } else if (IS_LOCK_ACCEPTED(fileLock)) {
4964 if (fileLock->lockType == LockRead)
4967 scp->exclusiveLocks--;
4970 fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
4971 if (userp != NULL) {
4972 cm_ReleaseUser(fileLock->userp);
4974 userp = fileLock->userp;
4975 release_userp = TRUE;
4977 fileLock->userp = NULL;
4978 cm_ReleaseSCacheNoLock(scp);
4979 fileLock->scp = NULL;
4980 lock_ReleaseWrite(&cm_scacheLock);
4982 if (!SERVERLOCKS_ENABLED(scp)) {
4983 osi_Log0(afsd_logp, " Skipping server locks for scp");
4987 /* Ideally we would go through the rest of the locks to determine
4988 * if one or more locks that were formerly in WAITUNLOCK can now
4989 * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
4990 * scp->sharedLocks accordingly. However, the retrying of locks
4991 * in that manner is done cm_RetryLock() manually.
4994 if (scp->serverLock == LockWrite &&
4995 scp->exclusiveLocks == 0 &&
4996 scp->sharedLocks > 0) {
4998 /* The serverLock should be downgraded to LockRead */
4999 osi_Log0(afsd_logp, " DOWNGRADE lock from LockWrite to LockRead");
5001 /* Since we already had a lock, we assume that there is a
5002 valid server lock. */
5003 scp->lockDataVersion = scp->dataVersion;
5004 osi_Log1(afsd_logp, " dataVersion on scp is %I64d", scp->dataVersion);
5006 /* before we downgrade, make sure that we have enough
5007 permissions to get the read lock. */
5008 code = cm_LockCheckPerms(scp, LockRead, userp, reqp, NULL);
5011 osi_Log0(afsd_logp, " SKIPPING downgrade because user doesn't have perms to get downgraded lock");
5017 code = cm_IntReleaseLock(scp, userp, reqp);
5020 /* so we couldn't release it. Just let the lock be for now */
5024 scp->serverLock = -1;
5027 code = cm_IntSetLock(scp, userp, LockRead, reqp);
5029 if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
5030 scp->serverLock = LockRead;
5031 } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
5032 /* Lost a race. We obtained a new lock, but that is
5033 meaningless since someone modified the file
5037 "Data version mismatch while downgrading lock");
5039 " Data versions before=%I64d, after=%I64d",
5040 scp->lockDataVersion,
5043 code = cm_IntReleaseLock(scp, userp, reqp);
5045 scp->serverLock = -1;
5046 code = CM_ERROR_INVAL;
5050 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
5051 (scp->serverLock == -1)) {
5053 cm_LockMarkSCacheLost(scp);
5056 /* failure here has no bearing on the return value of
5060 } else if (scp->serverLock != (-1) &&
5061 scp->exclusiveLocks == 0 &&
5062 scp->sharedLocks == 0) {
5063 /* The serverLock should be released entirely */
5065 code = cm_IntReleaseLock(scp, userp, reqp);
5068 scp->serverLock = (-1);
5072 if (release_userp) {
5073 cm_ReleaseUser(userp);
5074 release_userp = FALSE;
5078 goto try_again; /* might be more than one lock in the range */
5082 osi_Log1(afsd_logp, "cm_Unlock code 0x%x", code);
5083 osi_Log4(afsd_logp, " leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
5084 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
5085 (int)(signed char) scp->serverLock);
5090 /* called with scp->rw held */
5091 void cm_LockMarkSCacheLost(cm_scache_t * scp)
5093 cm_file_lock_t *fileLock;
5096 osi_Log1(afsd_logp, "cm_LockMarkSCacheLost scp 0x%x", scp);
5098 /* cm_scacheLock needed because we are modifying fileLock->flags */
5099 lock_ObtainWrite(&cm_scacheLock);
5101 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5103 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
5105 if (IS_LOCK_ACTIVE(fileLock) &&
5106 !IS_LOCK_CLIENTONLY(fileLock)) {
5107 if (fileLock->lockType == LockRead)
5110 scp->exclusiveLocks--;
5112 fileLock->flags |= CM_FILELOCK_FLAG_LOST;
5116 scp->serverLock = -1;
5117 scp->lockDataVersion = CM_SCACHE_VERSION_BAD;
5118 lock_ReleaseWrite(&cm_scacheLock);
5121 /* Called with no relevant locks held */
5122 void cm_CheckLocks()
5124 osi_queue_t *q, *nq;
5125 cm_file_lock_t *fileLock;
5131 struct rx_connection * rxconnp;
5136 lock_ObtainWrite(&cm_scacheLock);
5138 cm_lockRefreshCycle++;
5140 osi_Log1(afsd_logp, "cm_CheckLocks starting lock check cycle %d", cm_lockRefreshCycle);
5142 for (q = cm_allFileLocks; q; q = nq) {
5143 fileLock = (cm_file_lock_t *) q;
5147 if (IS_LOCK_DELETED(fileLock)) {
5149 osi_QRemove(&cm_allFileLocks, q);
5150 cm_PutFileLock(fileLock);
5152 } else if (IS_LOCK_ACTIVE(fileLock) && !IS_LOCK_CLIENTONLY(fileLock)) {
5154 /* Server locks must have been enabled for us to have
5155 received an active non-client-only lock. */
5156 osi_assertx(cm_enableServerLocks, "!cm_enableServerLocks");
5158 scp = fileLock->scp;
5159 osi_assertx(scp != NULL, "null cm_scache_t");
5161 cm_HoldSCacheNoLock(scp);
5164 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5165 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5166 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5168 fileLock->fid.volume,
5169 fileLock->fid.vnode,
5170 fileLock->fid.unique);
5171 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5172 fileLock->scp->fid.cell,
5173 fileLock->scp->fid.volume,
5174 fileLock->scp->fid.vnode,
5175 fileLock->scp->fid.unique);
5176 osi_assertx(FALSE, "invalid fid");
5179 /* Server locks are extended once per scp per refresh
5181 if (scp->lastRefreshCycle != cm_lockRefreshCycle) {
5183 int scp_done = FALSE;
5185 osi_Log1(afsd_logp, "cm_CheckLocks Updating scp 0x%x", scp);
5187 lock_ReleaseWrite(&cm_scacheLock);
5188 lock_ObtainWrite(&scp->rw);
5190 /* did the lock change while we weren't holding the lock? */
5191 if (!IS_LOCK_ACTIVE(fileLock))
5192 goto post_syncopdone;
5194 code = cm_SyncOp(scp, NULL, fileLock->userp, &req, 0,
5195 CM_SCACHESYNC_NEEDCALLBACK
5196 | CM_SCACHESYNC_GETSTATUS
5197 | CM_SCACHESYNC_LOCK);
5201 "cm_CheckLocks SyncOp failure code 0x%x", code);
5202 goto post_syncopdone;
5205 /* cm_SyncOp releases scp->rw during which the lock
5206 may get released. */
5207 if (!IS_LOCK_ACTIVE(fileLock))
5208 goto pre_syncopdone;
5210 if (scp->serverLock != -1 && !(scp->flags & CM_SCACHEFLAG_DELETED)) {
5214 tfid.Volume = scp->fid.volume;
5215 tfid.Vnode = scp->fid.vnode;
5216 tfid.Unique = scp->fid.unique;
5218 userp = fileLock->userp;
5220 osi_Log3(afsd_logp, "CALL ExtendLock lock 0x%p for scp=0x%p with lock %d",
5223 (int) scp->serverLock);
5225 lock_ReleaseWrite(&scp->rw);
5228 code = cm_ConnFromFID(&cfid, userp,
5233 rxconnp = cm_GetRxConn(connp);
5234 code = RXAFS_ExtendLock(rxconnp, &tfid,
5236 rx_PutConnection(rxconnp);
5238 osi_Log1(afsd_logp, " ExtendLock returns %d", code);
5240 } while (cm_Analyze(connp, userp, &req,
5241 &cfid, &volSync, NULL, NULL,
5244 code = cm_MapRPCError(code, &req);
5246 lock_ObtainWrite(&scp->rw);
5249 osi_Log1(afsd_logp, "CALL ExtendLock FAILURE, code 0x%x", code);
5251 osi_Log0(afsd_logp, "CALL ExtendLock SUCCESS");
5252 scp->lockDataVersion = scp->dataVersion;
5255 if ((code == EINVAL || code == CM_ERROR_INVAL) &&
5256 scp->lockDataVersion == scp->dataVersion) {
5260 (scp->exclusiveLocks > 0) ? LockWrite: LockRead;
5262 /* we might still have a chance to obtain a
5265 code = cm_IntSetLock(scp, userp, lockType, &req);
5268 code = CM_ERROR_INVAL;
5269 } else if (scp->lockDataVersion != scp->dataVersion) {
5271 /* now check if we still have the file at
5272 the right data version. */
5274 "Data version mismatch on scp 0x%p",
5277 " Data versions: before=%I64d, after=%I64d",
5278 scp->lockDataVersion,
5281 code = cm_IntReleaseLock(scp, userp, &req);
5283 code = CM_ERROR_INVAL;
5287 if (code == EINVAL || code == CM_ERROR_INVAL ||
5288 code == CM_ERROR_BADFD) {
5289 cm_LockMarkSCacheLost(scp);
5293 /* interestingly, we have found an active lock
5294 belonging to an scache that has no
5296 cm_LockMarkSCacheLost(scp);
5303 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5306 lock_ReleaseWrite(&scp->rw);
5308 lock_ObtainWrite(&cm_scacheLock);
5311 fileLock->lastUpdate = time(NULL);
5315 scp->lastRefreshCycle = cm_lockRefreshCycle;
5318 /* we have already refreshed the locks on this scp */
5319 fileLock->lastUpdate = time(NULL);
5322 cm_ReleaseSCacheNoLock(scp);
5324 } else if (IS_LOCK_ACTIVE(fileLock) && IS_LOCK_CLIENTONLY(fileLock)) {
5325 /* TODO: Check callbacks */
5329 lock_ReleaseWrite(&cm_scacheLock);
5330 osi_Log1(afsd_logp, "cm_CheckLocks completes lock check cycle %d", cm_lockRefreshCycle);
5333 /* NOT called with scp->rw held. */
5334 long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
5337 cm_scache_t *scp = NULL;
5338 cm_file_lock_t *fileLock;
5342 int force_client_lock = FALSE;
5343 int has_insert = FALSE;
5344 int check_data_version = FALSE;
5348 if (client_is_dead) {
5349 code = CM_ERROR_TIMEDOUT;
5353 lock_ObtainRead(&cm_scacheLock);
5355 osi_Log2(afsd_logp, "cm_RetryLock checking lock %p (scp=%p)", oldFileLock, oldFileLock->scp);
5356 osi_Log4(afsd_logp, " offset(%x:%x) length(%x:%x)",
5357 (unsigned)(oldFileLock->range.offset >> 32),
5358 (unsigned)(oldFileLock->range.offset & 0xffffffff),
5359 (unsigned)(oldFileLock->range.length >> 32),
5360 (unsigned)(oldFileLock->range.length & 0xffffffff));
5361 osi_Log4(afsd_logp, " key<0x%x,0x%x,0x%x> flags=%x",
5362 oldFileLock->key.process_id, oldFileLock->key.session_id, oldFileLock->key.file_id,
5363 (unsigned)(oldFileLock->flags));
5365 /* if the lock has already been granted, then we have nothing to do */
5366 if (IS_LOCK_ACTIVE(oldFileLock)) {
5367 lock_ReleaseRead(&cm_scacheLock);
5368 osi_Log0(afsd_logp, "cm_RetryLock lock already granted");
5372 /* we can't do anything with lost or deleted locks at the moment. */
5373 if (IS_LOCK_LOST(oldFileLock) || IS_LOCK_DELETED(oldFileLock)) {
5374 code = CM_ERROR_BADFD;
5375 osi_Log0(afsd_logp, "cm_RetryLock lock is lost or deleted");
5376 lock_ReleaseRead(&cm_scacheLock);
5380 scp = oldFileLock->scp;
5382 osi_assertx(scp != NULL, "null cm_scache_t");
5384 lock_ReleaseRead(&cm_scacheLock);
5385 lock_ObtainWrite(&scp->rw);
5387 code = cm_LockCheckPerms(scp, oldFileLock->lockType,
5391 if (code == CM_ERROR_NOACCESS && oldFileLock->lockType == LockRead) {
5392 if (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp)) {
5393 force_client_lock = TRUE;
5397 lock_ReleaseWrite(&scp->rw);
5401 lock_ObtainWrite(&cm_scacheLock);
5403 /* Check if we already have a sufficient server lock to allow this
5404 lock to go through. */
5405 if (IS_LOCK_WAITLOCK(oldFileLock) &&
5406 (!SERVERLOCKS_ENABLED(scp) ||
5407 scp->serverLock == oldFileLock->lockType ||
5408 scp->serverLock == LockWrite)) {
5410 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5412 if (SERVERLOCKS_ENABLED(scp)) {
5413 osi_Log1(afsd_logp, "cm_RetryLock Server lock (%d) is sufficient for lock. Granting",
5414 (int) scp->serverLock);
5416 osi_Log0(afsd_logp, "cm_RetryLock skipping server lock for scp");
5419 lock_ReleaseWrite(&cm_scacheLock);
5420 lock_ReleaseWrite(&scp->rw);
5425 if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5427 /* check if the conflicting locks have dissappeared already */
5428 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5430 fileLock = (cm_file_lock_t *)
5431 ((char *) q - offsetof(cm_file_lock_t, fileq));
5433 if (IS_LOCK_LOST(fileLock)) {
5434 if (cm_KeyEquals(&fileLock->key, &oldFileLock->key, 0)) {
5435 code = CM_ERROR_BADFD;
5436 oldFileLock->flags |= CM_FILELOCK_FLAG_LOST;
5437 osi_Log1(afsd_logp, " found lost lock %p for same key. Marking lock as lost",
5440 } else if (fileLock->lockType == LockWrite &&
5441 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5442 osi_Log1(afsd_logp, " found conflicting LOST lock %p", fileLock);
5443 code = CM_ERROR_WOULDBLOCK;
5448 if (IS_LOCK_ACCEPTED(fileLock) &&
5449 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5451 if (oldFileLock->lockType != LockRead ||
5452 fileLock->lockType != LockRead) {
5454 osi_Log1(afsd_logp, " found conflicting lock %p", fileLock);
5455 code = CM_ERROR_WOULDBLOCK;
5463 lock_ReleaseWrite(&cm_scacheLock);
5464 lock_ReleaseWrite(&scp->rw);
5469 /* when we get here, the lock is either a WAITUNLOCK or WAITLOCK.
5470 If it is WAITUNLOCK, then we didn't find any conflicting lock
5471 but we haven't verfied whether the serverLock is sufficient to
5472 assert it. If it is WAITLOCK, then the serverLock is
5473 insufficient to assert it. Eitherway, we are ready to accept
5474 the lock as either ACTIVE or WAITLOCK depending on the
5477 /* First, promote the WAITUNLOCK to a WAITLOCK */
5478 if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5479 if (oldFileLock->lockType == LockRead)
5482 scp->exclusiveLocks++;
5484 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITUNLOCK;
5485 oldFileLock->flags |= CM_FILELOCK_FLAG_WAITLOCK;
5488 osi_assertx(IS_LOCK_WAITLOCK(oldFileLock), "!IS_LOCK_WAITLOCK");
5490 if (force_client_lock ||
5491 !SERVERLOCKS_ENABLED(scp) ||
5492 scp->serverLock == oldFileLock->lockType ||
5493 (oldFileLock->lockType == LockRead &&
5494 scp->serverLock == LockWrite)) {
5496 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5498 if ((force_client_lock ||
5499 !SERVERLOCKS_ENABLED(scp)) &&
5500 !IS_LOCK_CLIENTONLY(oldFileLock)) {
5502 oldFileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
5504 if (oldFileLock->lockType == LockRead)
5507 scp->exclusiveLocks--;
5512 lock_ReleaseWrite(&cm_scacheLock);
5513 lock_ReleaseWrite(&scp->rw);
5520 code = cm_SyncOp(scp, NULL, oldFileLock->userp, &req, 0,
5521 CM_SCACHESYNC_NEEDCALLBACK
5522 | CM_SCACHESYNC_GETSTATUS
5523 | CM_SCACHESYNC_LOCK);
5525 osi_Log1(afsd_logp, "cm_RetryLock SyncOp failure code 0x%x", code);
5526 lock_ReleaseWrite(&cm_scacheLock);
5527 goto post_syncopdone;
5530 if (!IS_LOCK_WAITLOCK(oldFileLock))
5531 goto pre_syncopdone;
5533 userp = oldFileLock->userp;
5535 #ifndef AGGRESSIVE_LOCKS
5536 newLock = oldFileLock->lockType;
5538 newLock = LockWrite;
5542 /* if has_insert is non-zero, then:
5543 - the lock a LockRead
5544 - we don't have permission to get a LockRead
5545 - we do have permission to get a LockWrite
5546 - the server supports VICED_CAPABILITY_WRITELOCKACL
5549 newLock = LockWrite;
5552 lock_ReleaseWrite(&cm_scacheLock);
5554 /* when we get here, either we have a read-lock and want a
5555 write-lock or we don't have any locks and we want some
5558 if (scp->serverLock == LockRead) {
5560 osi_assertx(newLock == LockWrite, "!LockWrite");
5562 osi_Log0(afsd_logp, " Attempting to UPGRADE from LockRead to LockWrite");
5564 scp->lockDataVersion = scp->dataVersion;
5565 check_data_version = TRUE;
5567 code = cm_IntReleaseLock(scp, userp, &req);
5570 goto pre_syncopdone;
5572 scp->serverLock = -1;
5575 code = cm_IntSetLock(scp, userp, newLock, &req);
5578 if (scp->dataVersion != scp->lockDataVersion) {
5579 /* we lost a race. too bad */
5582 " Data version mismatch while upgrading lock.");
5584 " Data versions before=%I64d, after=%I64d",
5585 scp->lockDataVersion,
5588 " Releasing stale lock for scp 0x%x", scp);
5590 code = cm_IntReleaseLock(scp, userp, &req);
5592 scp->serverLock = -1;
5594 code = CM_ERROR_INVAL;
5596 cm_LockMarkSCacheLost(scp);
5598 scp->serverLock = newLock;
5603 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5609 if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
5610 lock_ObtainWrite(&cm_scacheLock);
5611 if (scp->fileLocksT == &oldFileLock->fileq)
5612 scp->fileLocksT = osi_QPrev(&oldFileLock->fileq);
5613 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, &oldFileLock->fileq);
5614 lock_ReleaseWrite(&cm_scacheLock);
5616 lock_ReleaseWrite(&scp->rw);
5619 lock_ObtainWrite(&cm_scacheLock);
5621 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5622 } else if (code != CM_ERROR_WOULDBLOCK) {
5623 oldFileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5624 cm_ReleaseUser(oldFileLock->userp);
5625 oldFileLock->userp = NULL;
5626 if (oldFileLock->scp) {
5627 cm_ReleaseSCacheNoLock(oldFileLock->scp);
5628 oldFileLock->scp = NULL;
5631 lock_ReleaseWrite(&cm_scacheLock);
5636 cm_key_t cm_GenerateKey(afs_uint16 session_id, afs_offs_t process_id, afs_uint16 file_id)
5640 key.process_id = process_id;
5641 key.session_id = session_id;
5642 key.file_id = file_id;
5647 int cm_KeyEquals(cm_key_t *k1, cm_key_t *k2, int flags)
5649 return (k1->session_id == k2->session_id) && (k1->file_id == k2->file_id) &&
5650 ((flags & CM_UNLOCK_BY_FID) || (k1->process_id == k2->process_id));
5653 void cm_ReleaseAllLocks(void)
5659 cm_file_lock_t *fileLock;
5662 for (i = 0; i < cm_data.scacheHashTableSize; i++)
5664 for ( scp = cm_data.scacheHashTablep[i]; scp; scp = scp->nextp ) {
5665 while (scp->fileLocksH != NULL) {
5666 lock_ObtainWrite(&scp->rw);
5667 lock_ObtainWrite(&cm_scacheLock);
5668 if (!scp->fileLocksH) {
5669 lock_ReleaseWrite(&cm_scacheLock);
5670 lock_ReleaseWrite(&scp->rw);
5673 fileLock = (cm_file_lock_t *)((char *) scp->fileLocksH - offsetof(cm_file_lock_t, fileq));
5674 userp = fileLock->userp;
5676 key = fileLock->key;
5677 cm_HoldSCacheNoLock(scp);
5678 lock_ReleaseWrite(&cm_scacheLock);
5679 cm_UnlockByKey(scp, key, 0, userp, &req);
5680 cm_ReleaseSCache(scp);
5681 cm_ReleaseUser(userp);
5682 lock_ReleaseWrite(&scp->rw);