2 * Copyright 2000, International Business Machines Corporation and others.
5 * This software has been released under the terms of the IBM Public
6 * License. For details, see the LICENSE file in the top-level source
7 * directory or online at http://www.openafs.org/dl/license10.html
10 #include <afs/param.h>
30 extern void afsi_log(char *pattern, ...);
33 int cm_enableServerLocks = 1;
35 int cm_followBackupPath = 0;
38 * Case-folding array. This was constructed by inspecting of SMBtrace output.
39 * I do not know anything more about it.
41 unsigned char cm_foldUpper[256] = {
42 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
43 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
44 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
45 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
46 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
47 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
48 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
49 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
50 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
51 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
52 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
53 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
54 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
55 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
56 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
57 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
58 0x80, 0x9a, 0x90, 0x41, 0x8e, 0x41, 0x8f, 0x80,
59 0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x8e, 0x8f,
60 0x90, 0x92, 0x92, 0x4f, 0x99, 0x4f, 0x55, 0x55,
61 0x59, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
62 0x41, 0x49, 0x4f, 0x55, 0xa5, 0xa5, 0x56, 0xa7,
63 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
64 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
65 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
66 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
67 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
68 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
69 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
70 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
71 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
72 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
73 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
77 * Case-insensitive string comparison. We used to use stricmp, but it doesn't
78 * know about 8-bit characters (e.g. 129 is lowercase u-umlaut, 154 is
79 * upper-case u-umlaut).
81 int cm_stricmp(const char *str1, const char *str2)
93 c1 = (char) cm_foldUpper[(unsigned char)(*str1++)];
94 c2 = (char) cm_foldUpper[(unsigned char)(*str2++)];
104 /* return success if we can open this file in this mode */
105 long cm_CheckOpen(cm_scache_t *scp, int openMode, int trunc, cm_user_t *userp,
113 rights |= PRSFS_READ;
114 if (openMode == 1 || openMode == 2 || trunc)
115 rights |= PRSFS_WRITE;
117 lock_ObtainWrite(&scp->rw);
119 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
120 CM_SCACHESYNC_GETSTATUS
121 | CM_SCACHESYNC_NEEDCALLBACK
122 | CM_SCACHESYNC_LOCK);
125 ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
126 scp->fileType == CM_SCACHETYPE_FILE) {
129 unsigned int sLockType;
130 LARGE_INTEGER LOffset, LLength;
132 /* Check if there's some sort of lock on the file at the
135 key = cm_GenerateKey(CM_SESSION_CMINT,0,0);
137 if (rights & PRSFS_WRITE)
140 sLockType = LOCKING_ANDX_SHARED_LOCK;
142 LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
143 LOffset.LowPart = CM_FLSHARE_OFFSET_LOW;
144 LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
145 LLength.LowPart = CM_FLSHARE_LENGTH_LOW;
147 code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
150 cm_Unlock(scp, sLockType, LOffset, LLength, key, userp, reqp);
152 /* In this case, we allow the file open to go through even
153 though we can't enforce mandatory locking on the
155 if (code == CM_ERROR_NOACCESS &&
156 !(rights & PRSFS_WRITE))
160 case CM_ERROR_ALLOFFLINE:
161 case CM_ERROR_ALLDOWN:
162 case CM_ERROR_ALLBUSY:
163 case CM_ERROR_TIMEDOUT:
165 case CM_ERROR_WOULDBLOCK:
168 code = CM_ERROR_SHARING_VIOLATION;
173 } else if (code != 0) {
177 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
181 lock_ReleaseWrite(&scp->rw);
186 /* return success if we can open this file in this mode */
187 long cm_CheckNTOpen(cm_scache_t *scp, unsigned int desiredAccess,
188 unsigned int createDisp, cm_user_t *userp, cm_req_t *reqp,
189 cm_lock_data_t **ldpp)
194 osi_assertx(ldpp != NULL, "null cm_lock_data_t");
197 /* Always allow delete; the RPC will tell us if it's OK */
198 if (desiredAccess == DELETE)
203 if (desiredAccess & (AFS_ACCESS_READ|AFS_ACCESS_EXECUTE))
204 rights |= (scp->fileType == CM_SCACHETYPE_DIRECTORY ? PRSFS_LOOKUP : PRSFS_READ);
206 /* We used to require PRSFS_WRITE if createDisp was 4
207 (OPEN_ALWAYS) even if AFS_ACCESS_WRITE was not requested.
208 However, we don't need to do that since the existence of the
209 scp implies that we don't need to create it. */
210 if (desiredAccess & AFS_ACCESS_WRITE)
211 rights |= PRSFS_WRITE;
213 lock_ObtainWrite(&scp->rw);
215 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
216 CM_SCACHESYNC_GETSTATUS
217 | CM_SCACHESYNC_NEEDCALLBACK
218 | CM_SCACHESYNC_LOCK);
221 * If the open will fail because the volume is readonly, then we will
222 * return an access denied error instead. This is to help brain-dead
223 * apps run correctly on replicated volumes.
224 * See defect 10007 for more information.
226 if (code == CM_ERROR_READONLY)
227 code = CM_ERROR_NOACCESS;
230 ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
231 scp->fileType == CM_SCACHETYPE_FILE) {
233 unsigned int sLockType;
234 LARGE_INTEGER LOffset, LLength;
236 /* Check if there's some sort of lock on the file at the
239 key = cm_GenerateKey(CM_SESSION_CMINT,0,0);
240 if (rights & PRSFS_WRITE)
243 sLockType = LOCKING_ANDX_SHARED_LOCK;
245 /* single byte lock at offset 0x0100 0000 0000 0000 */
246 LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
247 LOffset.LowPart = CM_FLSHARE_OFFSET_LOW;
248 LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
249 LLength.LowPart = CM_FLSHARE_LENGTH_LOW;
251 code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
254 (*ldpp) = (cm_lock_data_t *)malloc(sizeof(cm_lock_data_t));
261 (*ldpp)->sLockType = sLockType;
262 (*ldpp)->LOffset.HighPart = LOffset.HighPart;
263 (*ldpp)->LOffset.LowPart = LOffset.LowPart;
264 (*ldpp)->LLength.HighPart = LLength.HighPart;
265 (*ldpp)->LLength.LowPart = LLength.LowPart;
267 /* In this case, we allow the file open to go through even
268 though we can't enforce mandatory locking on the
270 if (code == CM_ERROR_NOACCESS &&
271 !(rights & PRSFS_WRITE))
275 case CM_ERROR_ALLOFFLINE:
276 case CM_ERROR_ALLDOWN:
277 case CM_ERROR_ALLBUSY:
278 case CM_ERROR_TIMEDOUT:
280 case CM_ERROR_WOULDBLOCK:
283 code = CM_ERROR_SHARING_VIOLATION;
287 } else if (code != 0) {
292 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
295 lock_ReleaseWrite(&scp->rw);
297 osi_Log3(afsd_logp,"cm_CheckNTOpen scp 0x%p ldp 0x%p code 0x%x", scp, *ldpp, code);
301 extern long cm_CheckNTOpenDone(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp,
302 cm_lock_data_t ** ldpp)
304 osi_Log2(afsd_logp,"cm_CheckNTOpenDone scp 0x%p ldp 0x%p", scp, *ldpp);
306 lock_ObtainWrite(&scp->rw);
307 cm_Unlock(scp, (*ldpp)->sLockType, (*ldpp)->LOffset, (*ldpp)->LLength,
308 (*ldpp)->key, userp, reqp);
309 lock_ReleaseWrite(&scp->rw);
316 * When CAP_NT_SMBS has been negotiated, deletion (of files or directories) is
317 * done in three steps:
318 * (1) open for deletion (NT_CREATE_AND_X)
319 * (2) set for deletion on close (NT_TRANSACTION2, SET_FILE_INFO)
321 * We must not do the RPC until step 3. But if we are going to return an error
322 * code (e.g. directory not empty), we must return it by step 2, otherwise most
323 * clients will not notice it. So we do a preliminary check. For deleting
324 * files, this is almost free, since we have already done the RPC to get the
325 * parent directory's status bits. But for deleting directories, we must do an
326 * additional RPC to get the directory's data to check if it is empty. Sigh.
328 long cm_CheckNTDelete(cm_scache_t *dscp, cm_scache_t *scp, cm_user_t *userp,
334 cm_dirEntry_t *dep = 0;
335 unsigned short *hashTable;
337 int BeyondPage = 0, HaveDot = 0, HaveDotDot = 0;
340 /* First check permissions */
341 lock_ObtainWrite(&scp->rw);
342 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_DELETE,
343 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
344 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
345 lock_ReleaseWrite(&scp->rw);
349 /* If deleting directory, must be empty */
351 if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
354 thyper.HighPart = 0; thyper.LowPart = 0;
355 code = buf_Get(scp, &thyper, &bufferp);
359 lock_ObtainMutex(&bufferp->mx);
360 lock_ObtainWrite(&scp->rw);
363 code = cm_SyncOp(scp, bufferp, userp, reqp, 0,
364 CM_SCACHESYNC_NEEDCALLBACK
366 | CM_SCACHESYNC_BUFLOCKED);
370 if (cm_HaveBuffer(scp, bufferp, 1))
373 /* otherwise, load the buffer and try again */
374 lock_ReleaseMutex(&bufferp->mx);
375 code = cm_GetBuffer(scp, bufferp, NULL, userp, reqp);
376 lock_ReleaseWrite(&scp->rw);
377 lock_ObtainMutex(&bufferp->mx);
378 lock_ObtainWrite(&scp->rw);
379 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
384 lock_ReleaseWrite(&scp->rw);
387 /* We try to determine emptiness without looking beyond the first page,
388 * and without assuming "." and ".." are present and are on the first
389 * page (though these assumptions might, after all, be reasonable).
391 hashTable = (unsigned short *)(bufferp->datap + (32 * 5));
392 for (i=0; i<128; i++) {
393 idx = ntohs(hashTable[i]);
399 dep = (cm_dirEntry_t *)(bufferp->datap + (32 * idx));
400 if (strcmp(dep->name, ".") == 0)
402 else if (strcmp(dep->name, "..") == 0)
405 code = CM_ERROR_NOTEMPTY;
408 idx = ntohs(dep->next);
411 if (BeyondPage && HaveDot && HaveDotDot)
412 code = CM_ERROR_NOTEMPTY;
416 lock_ReleaseMutex(&bufferp->mx);
417 buf_Release(bufferp);
419 lock_ReleaseWrite(&scp->rw);
424 * Iterate through all entries in a directory.
425 * When the function funcp is called, the buffer is locked but the
426 * directory vnode is not.
428 * If the retscp parameter is not NULL, the parmp must be a
429 * cm_lookupSearch_t object.
431 long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
432 osi_hyper_t *startOffsetp, cm_user_t *userp, cm_req_t *reqp,
433 cm_scache_t **retscp)
437 cm_dirEntry_t *dep = 0;
440 osi_hyper_t dirLength;
441 osi_hyper_t bufferOffset;
442 osi_hyper_t curOffset;
446 cm_pageHeader_t *pageHeaderp;
448 long nextEntryCookie;
449 int numDirChunks; /* # of 32 byte dir chunks in this entry */
451 /* get the directory size */
452 lock_ObtainWrite(&scp->rw);
453 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_LOOKUP,
454 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
455 lock_ReleaseWrite(&scp->rw);
459 if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
460 return CM_ERROR_NOTDIR;
462 if (retscp) /* if this is a lookup call */
464 cm_lookupSearch_t* sp = parmp;
467 #ifdef AFS_FREELANCE_CLIENT
468 /* Freelance entries never end up in the DNLC because they
469 * do not have an associated cm_server_t
471 !(cm_freelanceEnabled &&
472 sp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
473 sp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
474 #else /* !AFS_FREELANCE_CLIENT */
479 int casefold = sp->caseFold;
480 sp->caseFold = 0; /* we have a strong preference for exact matches */
481 if ( *retscp = cm_dnlcLookup(scp, sp)) /* dnlc hit */
483 sp->caseFold = casefold;
486 sp->caseFold = casefold;
488 /* see if we can find it using the directory hash tables.
489 we can only do exact matches, since the hash is case
499 code = cm_BeginDirOp(scp, userp, reqp, CM_DIRLOCK_READ, &dirop);
503 code = cm_BPlusDirLookup(&dirop, sp->nsearchNamep, &sp->fid);
508 code = cm_DirLookup(&dirop, sp->searchNamep, &sp->fid);
516 sp->ExactFound = TRUE;
517 *retscp = NULL; /* force caller to call cm_GetSCache() */
522 if (sp->caseFold && code == CM_ERROR_INEXACT_MATCH) {
525 sp->ExactFound = FALSE;
526 *retscp = NULL; /* force caller to call cm_GetSCache() */
530 return CM_ERROR_BPLUS_NOMATCH;
538 * XXX We only get the length once. It might change when we drop the
541 dirLength = scp->length;
544 bufferOffset.LowPart = bufferOffset.HighPart = 0;
546 curOffset = *startOffsetp;
548 curOffset.HighPart = 0;
549 curOffset.LowPart = 0;
553 /* make sure that curOffset.LowPart doesn't point to the first
554 * 32 bytes in the 2nd through last dir page, and that it
555 * doesn't point at the first 13 32-byte chunks in the first
556 * dir page, since those are dir and page headers, and don't
557 * contain useful information.
559 temp = curOffset.LowPart & (2048-1);
560 if (curOffset.HighPart == 0 && curOffset.LowPart < 2048) {
561 /* we're in the first page */
562 if (temp < 13*32) temp = 13*32;
565 /* we're in a later dir page */
566 if (temp < 32) temp = 32;
569 /* make sure the low order 5 bits are zero */
572 /* now put temp bits back ito curOffset.LowPart */
573 curOffset.LowPart &= ~(2048-1);
574 curOffset.LowPart |= temp;
576 /* check if we've passed the dir's EOF */
577 if (LargeIntegerGreaterThanOrEqualTo(curOffset, dirLength))
580 /* see if we can use the bufferp we have now; compute in which
581 * page the current offset would be, and check whether that's
582 * the offset of the buffer we have. If not, get the buffer.
584 thyper.HighPart = curOffset.HighPart;
585 thyper.LowPart = curOffset.LowPart & ~(cm_data.buf_blockSize-1);
586 if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
589 lock_ReleaseMutex(&bufferp->mx);
590 buf_Release(bufferp);
594 code = buf_Get(scp, &thyper, &bufferp);
596 /* if buf_Get() fails we do not have a buffer object to lock */
601 lock_ObtainMutex(&bufferp->mx);
602 bufferOffset = thyper;
604 /* now get the data in the cache */
606 lock_ObtainWrite(&scp->rw);
607 code = cm_SyncOp(scp, bufferp, userp, reqp,
609 CM_SCACHESYNC_NEEDCALLBACK
611 | CM_SCACHESYNC_BUFLOCKED);
613 lock_ReleaseWrite(&scp->rw);
616 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
618 if (cm_HaveBuffer(scp, bufferp, 1)) {
619 lock_ReleaseWrite(&scp->rw);
623 /* otherwise, load the buffer and try again */
624 lock_ReleaseMutex(&bufferp->mx);
625 code = cm_GetBuffer(scp, bufferp, NULL, userp,
627 lock_ReleaseWrite(&scp->rw);
628 lock_ObtainMutex(&bufferp->mx);
633 lock_ReleaseMutex(&bufferp->mx);
634 buf_Release(bufferp);
638 } /* if (wrong buffer) ... */
640 /* now we have the buffer containing the entry we're interested
641 * in; copy it out if it represents a non-deleted entry.
643 entryInDir = curOffset.LowPart & (2048-1);
644 entryInBuffer = curOffset.LowPart & (cm_data.buf_blockSize - 1);
646 /* page header will help tell us which entries are free. Page
647 * header can change more often than once per buffer, since
648 * AFS 3 dir page size may be less than (but not more than) a
649 * buffer package buffer.
651 /* only look intra-buffer */
652 temp = curOffset.LowPart & (cm_data.buf_blockSize - 1);
653 temp &= ~(2048 - 1); /* turn off intra-page bits */
654 pageHeaderp = (cm_pageHeader_t *) (bufferp->datap + temp);
656 /* now determine which entry we're looking at in the page. If
657 * it is free (there's a free bitmap at the start of the dir),
658 * we should skip these 32 bytes.
660 slotInPage = (entryInDir & 0x7e0) >> 5;
661 if (!(pageHeaderp->freeBitmap[slotInPage>>3]
662 & (1 << (slotInPage & 0x7)))) {
663 /* this entry is free */
664 numDirChunks = 1; /* only skip this guy */
668 tp = bufferp->datap + entryInBuffer;
669 dep = (cm_dirEntry_t *) tp; /* now points to AFS3 dir entry */
671 /* while we're here, compute the next entry's location, too,
672 * since we'll need it when writing out the cookie into the
673 * dir listing stream.
675 numDirChunks = cm_NameEntries(dep->name, NULL);
677 /* compute the offset of the cookie representing the next entry */
678 nextEntryCookie = curOffset.LowPart
679 + (CM_DIR_CHUNKSIZE * numDirChunks);
681 if (dep->fid.vnode != 0) {
682 /* this is one of the entries to use: it is not deleted */
683 code = (*funcp)(scp, dep, parmp, &curOffset);
686 } /* if we're including this name */
689 /* and adjust curOffset to be where the new cookie is */
691 thyper.LowPart = CM_DIR_CHUNKSIZE * numDirChunks;
692 curOffset = LargeIntegerAdd(thyper, curOffset);
693 } /* while copying data for dir listing */
695 /* release the mutex */
697 lock_ReleaseMutex(&bufferp->mx);
698 buf_Release(bufferp);
703 int cm_NoneUpper(normchar_t *s)
707 if (c >= 'A' && c <= 'Z')
712 int cm_NoneLower(normchar_t *s)
716 if (c >= 'a' && c <= 'z')
721 long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
724 cm_lookupSearch_t *sp;
726 normchar_t matchName[MAX_PATH];
727 int looking_for_short_name = FALSE;
729 sp = (cm_lookupSearch_t *) rockp;
731 cm_FsStringToNormString(dep->name, -1, matchName, lengthof(matchName));
733 match = cm_NormStrCmpI(matchName, sp->nsearchNamep);
735 match = cm_NormStrCmp(matchName, sp->nsearchNamep);
739 && !cm_Is8Dot3(matchName)) {
741 cm_Gen8Dot3NameInt(dep->name, &dep->fid, matchName, NULL);
743 match = cm_NormStrCmpI(matchName, sp->nsearchNamep);
745 match = cm_NormStrCmp(matchName, sp->nsearchNamep);
746 looking_for_short_name = TRUE;
756 if (!sp->caseFold || looking_for_short_name) {
757 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
758 return CM_ERROR_STOPNOW;
762 * If we get here, we are doing a case-insensitive search, and we
763 * have found a match. Now we determine what kind of match it is:
764 * exact, lower-case, upper-case, or none of the above. This is done
765 * in order to choose among matches, if there are more than one.
768 /* Exact matches are the best. */
769 match = cm_NormStrCmp(matchName, sp->nsearchNamep);
772 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
773 return CM_ERROR_STOPNOW;
776 /* Lower-case matches are next. */
779 if (cm_NoneUpper(matchName)) {
784 /* Upper-case matches are next. */
787 if (cm_NoneLower(matchName)) {
792 /* General matches are last. */
798 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
802 /* read the contents of a mount point into the appropriate string.
803 * called with write locked scp, and returns with locked scp.
805 long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
812 if (scp->mountPointStringp[0])
815 /* otherwise, we have to read it in */
816 lock_ReleaseWrite(&scp->rw);
818 thyper.LowPart = thyper.HighPart = 0;
819 code = buf_Get(scp, &thyper, &bufp);
821 lock_ObtainWrite(&scp->rw);
826 code = cm_SyncOp(scp, bufp, userp, reqp, 0,
827 CM_SCACHESYNC_READ | CM_SCACHESYNC_NEEDCALLBACK);
831 cm_SyncOpDone(scp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
833 if (cm_HaveBuffer(scp, bufp, 0))
836 /* otherwise load buffer */
837 code = cm_GetBuffer(scp, bufp, NULL, userp, reqp);
841 /* locked, has callback, has valid data in buffer */
842 if ((tlen = scp->length.LowPart) > MOUNTPOINTLEN - 1)
843 return CM_ERROR_TOOBIG;
845 code = CM_ERROR_INVAL;
849 /* someone else did the work while we were out */
850 if (scp->mountPointStringp[0]) {
855 /* otherwise, copy out the link */
856 memcpy(scp->mountPointStringp, bufp->datap, tlen);
858 /* now make it null-terminated. Note that the original contents of a
859 * link that is a mount point is "#volname." where "." is there just to
860 * be turned into a null. That is, we can trash the last char of the
861 * link without damaging the vol name. This is a stupid convention,
862 * but that's the protocol.
864 scp->mountPointStringp[tlen-1] = 0;
874 /* called with a locked scp and chases the mount point, yielding outScpp.
875 * scp remains write locked, just for simplicity of describing the interface.
877 long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
878 cm_req_t *reqp, cm_scache_t **outScpp)
880 fschar_t *cellNamep = NULL;
881 fschar_t *volNamep = NULL;
886 cm_volume_t *volp = NULL;
893 if (scp->mountRootFid.cell != 0 && scp->mountRootGen >= cm_data.mountRootGen) {
894 tfid = scp->mountRootFid;
895 lock_ReleaseWrite(&scp->rw);
896 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
897 lock_ObtainWrite(&scp->rw);
901 /* parse the volume name */
902 mpNamep = scp->mountPointStringp;
904 return CM_ERROR_NOSUCHPATH;
905 tlen = cm_FsStrLen(scp->mountPointStringp);
906 mtType = *scp->mountPointStringp;
908 cp = cm_FsStrChr(mpNamep, _FS(':'));
910 /* cellular mount point */
911 cellNamep = (fschar_t *)malloc((cp - mpNamep) * sizeof(fschar_t));
912 cm_FsStrCpyN(cellNamep, cp - mpNamep, mpNamep + 1, cp - mpNamep - 1);
913 volNamep = cm_FsStrDup(cp+1);
915 /* now look up the cell */
916 lock_ReleaseWrite(&scp->rw);
917 cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE);
918 lock_ObtainWrite(&scp->rw);
921 volNamep = cm_FsStrDup(mpNamep + 1);
923 cellp = cm_FindCellByID(scp->fid.cell, 0);
927 code = CM_ERROR_NOSUCHCELL;
931 vnLength = cm_FsStrLen(volNamep);
932 if (vnLength >= 8 && cm_FsStrCmp(volNamep + vnLength - 7, ".backup") == 0)
933 targetType = BACKVOL;
934 else if (vnLength >= 10
935 && cm_FsStrCmp(volNamep + vnLength - 9, ".readonly") == 0)
940 /* check for backups within backups */
941 if (targetType == BACKVOL
942 && (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
943 == CM_SCACHEFLAG_RO) {
944 code = CM_ERROR_NOSUCHVOLUME;
948 /* now we need to get the volume */
949 lock_ReleaseWrite(&scp->rw);
950 if (cm_VolNameIsID(volNamep)) {
951 code = cm_FindVolumeByID(cellp, atoi(volNamep), userp, reqp,
952 CM_GETVOL_FLAG_CREATE, &volp);
954 code = cm_FindVolumeByName(cellp, volNamep, userp, reqp,
955 CM_GETVOL_FLAG_CREATE, &volp);
957 lock_ObtainWrite(&scp->rw);
960 afs_uint32 cell, volume;
961 cm_vol_state_t *statep;
963 cell = cellp->cellID;
965 /* if the mt pt originates in a .backup volume (not a .readonly)
966 * and FollowBackupPath is active, and if there is a .backup
967 * volume for the target, then use the .backup of the target
968 * instead of the read-write.
970 if (cm_followBackupPath &&
971 volp->vol[BACKVOL].ID != 0 &&
972 (dscp->flags & (CM_SCACHEFLAG_RO|CM_SCACHEFLAG_PURERO)) == CM_SCACHEFLAG_RO &&
973 (targetType == RWVOL || targetType == ROVOL && volp->vol[ROVOL].ID == 0)
975 targetType = BACKVOL;
977 /* if the mt pt is in a read-only volume (not just a
978 * backup), and if there is a read-only volume for the
979 * target, and if this is a targetType '#' mount point, use
980 * the read-only, otherwise use the one specified.
982 else if (mtType == '#' && targetType == RWVOL &&
983 (scp->flags & CM_SCACHEFLAG_PURERO) &&
984 volp->vol[ROVOL].ID != 0) {
988 lock_ObtainWrite(&volp->rw);
989 statep = cm_VolumeStateByType(volp, targetType);
991 statep->dotdotFid = dscp->fid;
992 lock_ReleaseWrite(&volp->rw);
994 /* the rest of the fid is a magic number */
995 cm_SetFid(&scp->mountRootFid, cell, volume, 1, 1);
996 scp->mountRootGen = cm_data.mountRootGen;
998 tfid = scp->mountRootFid;
999 lock_ReleaseWrite(&scp->rw);
1000 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
1001 lock_ObtainWrite(&scp->rw);
1014 long cm_LookupInternal(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_user_t *userp,
1015 cm_req_t *reqp, cm_scache_t **outpScpp)
1018 int dnlcHit = 1; /* did we hit in the dnlc? yes, we did */
1019 cm_scache_t *tscp = NULL;
1020 cm_scache_t *mountedScp;
1021 cm_lookupSearch_t rock;
1023 normchar_t *nnamep = NULL;
1024 fschar_t *fnamep = NULL;
1026 memset(&rock, 0, sizeof(rock));
1028 if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
1029 && cm_ClientStrCmp(cnamep, _C("..")) == 0) {
1030 if (dscp->dotdotFid.volume == 0)
1031 return CM_ERROR_NOSUCHVOLUME;
1032 rock.fid = dscp->dotdotFid;
1034 } else if (cm_ClientStrCmp(cnamep, _C(".")) == 0) {
1035 rock.fid = dscp->fid;
1039 nnamep = cm_ClientStringToNormStringAlloc(cnamep, -1, NULL);
1040 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
1042 if (flags & CM_FLAG_NOMOUNTCHASE) {
1043 /* In this case, we should go and call cm_Dir* functions
1044 directly since the following cm_ApplyDir() function will
1052 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ, &dirop);
1055 code = cm_BPlusDirLookup(&dirop, nnamep, &rock.fid);
1060 code = cm_DirLookup(&dirop, fnamep, &rock.fid);
1062 cm_EndDirOp(&dirop);
1072 if (code == CM_ERROR_INEXACT_MATCH && (flags & CM_FLAG_CASEFOLD)) {
1079 return CM_ERROR_BPLUS_NOMATCH;
1084 rock.fid.cell = dscp->fid.cell;
1085 rock.fid.volume = dscp->fid.volume;
1086 rock.searchNamep = fnamep;
1087 rock.nsearchNamep = nnamep;
1088 rock.caseFold = (flags & CM_FLAG_CASEFOLD);
1089 rock.hasTilde = ((cm_ClientStrChr(cnamep, '~') != NULL) ? 1 : 0);
1091 /* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
1092 code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
1093 (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
1095 /* code == 0 means we fell off the end of the dir, while stopnow means
1096 * that we stopped early, probably because we found the entry we're
1097 * looking for. Any other non-zero code is an error.
1099 if (code && code != CM_ERROR_STOPNOW) {
1100 /* if the cm_scache_t we are searching in is not a directory
1101 * we must return path not found because the error
1102 * is to describe the final component not an intermediary
1104 if (code == CM_ERROR_NOTDIR) {
1105 if (flags & CM_FLAG_CHECKPATH)
1106 code = CM_ERROR_NOSUCHPATH;
1108 code = CM_ERROR_NOSUCHFILE;
1113 getroot = (dscp==cm_data.rootSCachep) ;
1115 if (!cm_freelanceEnabled || !getroot) {
1116 if (flags & CM_FLAG_CHECKPATH)
1117 code = CM_ERROR_NOSUCHPATH;
1119 code = CM_ERROR_NOSUCHFILE;
1122 else if (!cm_ClientStrChr(cnamep, '#') &&
1123 !cm_ClientStrChr(cnamep, '%') &&
1124 cm_ClientStrCmpI(cnamep, _C("srvsvc")) &&
1125 cm_ClientStrCmpI(cnamep, _C("wkssvc")) &&
1126 cm_ClientStrCmpI(cnamep, _C("ipc$")))
1128 /* nonexistent dir on freelance root, so add it */
1129 fschar_t fullname[200] = ".";
1132 osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %S",
1133 osi_LogSaveClientString(afsd_logp,cnamep));
1136 * There is an ugly behavior where a share name "foo" will be searched
1137 * for as "fo". If the searched for name differs by an already existing
1138 * symlink or mount point in the Freelance directory, do not add the
1139 * new value automatically.
1143 if (cnamep[0] == '.') {
1144 if (cm_GetCell_Gen(&fnamep[1], &fullname[1], CM_FLAG_CREATE)) {
1146 if (!cm_FreelanceMountPointExists(fullname, 0))
1147 code = cm_FreelanceAddMount(fullname, &fullname[1], "root.cell.",
1149 if ( cm_FsStrCmpI(&fnamep[1], &fullname[1]) &&
1150 !cm_FreelanceMountPointExists(fnamep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0) &&
1151 !cm_FreelanceSymlinkExists(fnamep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0))
1152 code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
1155 if (cm_GetCell_Gen(fnamep, fullname, CM_FLAG_CREATE)) {
1157 if (!cm_FreelanceMountPointExists(fullname, 0))
1158 code = cm_FreelanceAddMount(fullname, fullname, "root.cell.", 0, &rock.fid);
1159 if ( cm_FsStrCmpI(fnamep, fullname) &&
1160 !cm_FreelanceMountPointExists(fnamep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0) &&
1161 !cm_FreelanceSymlinkExists(fnamep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0))
1162 code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
1165 if (!found || code < 0) { /* add mount point failed, so give up */
1166 if (flags & CM_FLAG_CHECKPATH)
1167 code = CM_ERROR_NOSUCHPATH;
1169 code = CM_ERROR_NOSUCHFILE;
1172 tscp = NULL; /* to force call of cm_GetSCache */
1174 if (flags & CM_FLAG_CHECKPATH)
1175 code = CM_ERROR_NOSUCHPATH;
1177 code = CM_ERROR_NOSUCHFILE;
1183 if ( !tscp ) /* we did not find it in the dnlc */
1186 code = cm_GetSCache(&rock.fid, &tscp, userp, reqp);
1190 /* tscp is now held */
1192 lock_ObtainWrite(&tscp->rw);
1193 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1194 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1196 lock_ReleaseWrite(&tscp->rw);
1197 cm_ReleaseSCache(tscp);
1200 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1201 /* tscp is now locked */
1203 if (!(flags & CM_FLAG_NOMOUNTCHASE)
1204 && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
1205 /* mount points are funny: they have a volume name to mount
1208 code = cm_ReadMountPoint(tscp, userp, reqp);
1210 code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
1212 lock_ReleaseWrite(&tscp->rw);
1213 cm_ReleaseSCache(tscp);
1220 lock_ReleaseWrite(&tscp->rw);
1223 /* copy back pointer */
1226 /* insert scache in dnlc */
1227 if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
1228 /* lock the directory entry to prevent racing callback revokes */
1229 lock_ObtainRead(&dscp->rw);
1230 if ( dscp->cbServerp != NULL && dscp->cbExpires > 0 ) {
1231 /* TODO: reuse nnamep from above */
1234 nnamep = cm_ClientStringToNormStringAlloc(cnamep, -1, NULL);
1235 cm_dnlcEnter(dscp, nnamep, tscp);
1237 lock_ReleaseRead(&dscp->rw);
1254 int cm_ExpandSysName(clientchar_t *inp, clientchar_t *outp, long outSizeCch, unsigned int index)
1259 tp = cm_ClientStrRChr(inp, '@');
1261 return 0; /* no @sys */
1263 if (cm_ClientStrCmp(tp, _C("@sys")) != 0)
1264 return 0; /* no @sys */
1266 /* caller just wants to know if this is a valid @sys type of name */
1270 if (index >= cm_sysNameCount)
1273 /* otherwise generate the properly expanded @sys name */
1274 prefixCount = (int)(tp - inp);
1276 cm_ClientStrCpyN(outp, outSizeCch, inp, prefixCount); /* copy out "a." from "a.@sys" */
1277 outp[prefixCount] = 0; /* null terminate the "a." */
1278 cm_ClientStrCat(outp, outSizeCch, cm_sysNameList[index]);/* append i386_nt40 */
1282 long cm_EvaluateVolumeReference(clientchar_t * namep, long flags, cm_user_t * userp,
1283 cm_req_t *reqp, cm_scache_t ** outpScpp)
1285 afs_uint32 code = 0;
1286 fschar_t cellName[CELL_MAXNAMELEN];
1287 fschar_t volumeName[VL_MAXNAMELEN];
1291 fschar_t * fnamep = NULL;
1293 cm_cell_t * cellp = NULL;
1294 cm_volume_t * volp = NULL;
1298 int mountType = RWVOL;
1300 osi_Log1(afsd_logp, "cm_EvaluateVolumeReference for string [%S]",
1301 osi_LogSaveClientString(afsd_logp, namep));
1303 if (cm_ClientStrCmpNI(namep, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH) != 0) {
1304 goto _exit_invalid_path;
1307 /* namep is assumed to look like the following:
1309 @vol:<cellname>%<volume>\0
1311 @vol:<cellname>#<volume>\0
1315 fnamep = cm_ClientStringToFsStringAlloc(namep, cm_ClientStrLen(namep), NULL);
1316 cp = fnamep + CM_PREFIX_VOL_CCH; /* cp points to cell name, hopefully */
1317 tp = cm_FsStrChr(cp, '%');
1319 tp = cm_FsStrChr(cp, '#');
1321 (len = tp - cp) == 0 ||
1322 len > CELL_MAXNAMELEN)
1323 goto _exit_invalid_path;
1324 cm_FsStrCpyN(cellName, lengthof(cellName), cp, len);
1329 cp = tp+1; /* cp now points to volume, supposedly */
1330 cm_FsStrCpy(volumeName, lengthof(volumeName), cp);
1332 /* OK, now we have the cell and the volume */
1333 osi_Log2(afsd_logp, " Found cell [%s] and volume [%s]",
1334 osi_LogSaveFsString(afsd_logp, cellName),
1335 osi_LogSaveFsString(afsd_logp, volumeName));
1337 cellp = cm_GetCell(cellName, CM_FLAG_CREATE);
1338 if (cellp == NULL) {
1339 goto _exit_invalid_path;
1342 len = cm_FsStrLen(volumeName);
1343 if (len >= 8 && cm_FsStrCmp(volumeName + len - 7, ".backup") == 0)
1345 else if (len >= 10 &&
1346 cm_FsStrCmp(volumeName + len - 9, ".readonly") == 0)
1351 if (cm_VolNameIsID(volumeName)) {
1352 code = cm_FindVolumeByID(cellp, atoi(volumeName), userp, reqp,
1353 CM_GETVOL_FLAG_CREATE, &volp);
1355 code = cm_FindVolumeByName(cellp, volumeName, userp, reqp,
1356 CM_GETVOL_FLAG_CREATE, &volp);
1362 if (volType == BACKVOL)
1363 volume = volp->vol[BACKVOL].ID;
1364 else if (volType == ROVOL ||
1365 (volType == RWVOL && mountType == ROVOL && volp->vol[ROVOL].ID != 0))
1366 volume = volp->vol[ROVOL].ID;
1368 volume = volp->vol[RWVOL].ID;
1370 cm_SetFid(&fid, cellp->cellID, volume, 1, 1);
1372 code = cm_GetSCache(&fid, outpScpp, userp, reqp);
1385 if (flags & CM_FLAG_CHECKPATH)
1386 return CM_ERROR_NOSUCHPATH;
1388 return CM_ERROR_NOSUCHFILE;
1391 #ifdef DEBUG_REFCOUNT
1392 long cm_LookupDbg(cm_scache_t *dscp, clientchar_t *namep, long flags, cm_user_t *userp,
1393 cm_req_t *reqp, cm_scache_t **outpScpp, char * file, long line)
1395 long cm_Lookup(cm_scache_t *dscp, clientchar_t *namep, long flags, cm_user_t *userp,
1396 cm_req_t *reqp, cm_scache_t **outpScpp)
1400 clientchar_t tname[AFSPATHMAX];
1401 int sysNameIndex = 0;
1402 cm_scache_t *scp = NULL;
1404 #ifdef DEBUG_REFCOUNT
1405 afsi_log("%s:%d cm_Lookup dscp 0x%p ref %d", file, line, dscp, dscp->refCount, file, line);
1406 osi_Log2(afsd_logp, "cm_Lookup dscp 0x%p ref %d", dscp, dscp->refCount);
1409 if ( cm_ClientStrCmpI(namep,_C(SMB_IOCTL_FILENAME_NOSLASH)) == 0 ) {
1410 if (flags & CM_FLAG_CHECKPATH)
1411 return CM_ERROR_NOSUCHPATH;
1413 return CM_ERROR_NOSUCHFILE;
1416 if (dscp == cm_data.rootSCachep &&
1417 cm_ClientStrCmpNI(namep, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH) == 0) {
1418 return cm_EvaluateVolumeReference(namep, flags, userp, reqp, outpScpp);
1421 if (cm_ExpandSysName(namep, NULL, 0, 0) > 0) {
1422 for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
1423 code = cm_ExpandSysName(namep, tname, lengthof(tname), sysNameIndex);
1425 code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
1426 #ifdef DEBUG_REFCOUNT
1427 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);
1428 osi_Log3(afsd_logp, "cm_LookupInternal (1) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1436 cm_ReleaseSCache(scp);
1440 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1441 #ifdef DEBUG_REFCOUNT
1442 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);
1443 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1450 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1451 #ifdef DEBUG_REFCOUNT
1452 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);
1453 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1459 /* None of the possible sysName expansions could be found */
1460 if (flags & CM_FLAG_CHECKPATH)
1461 return CM_ERROR_NOSUCHPATH;
1463 return CM_ERROR_NOSUCHFILE;
1466 /*! \brief Unlink a file name
1468 Encapsulates a call to RXAFS_RemoveFile().
1470 \param[in] dscp cm_scache_t pointing at the directory containing the
1471 name to be unlinked.
1473 \param[in] fnamep Original name to be unlinked. This is the
1474 name that will be passed into the RXAFS_RemoveFile() call.
1475 This parameter is optional. If not provided, the value will
1478 \param[in] came Client name to be unlinked. This name will be used
1479 to update the local directory caches.
1481 \param[in] userp cm_user_t for the request.
1483 \param[in] reqp Request tracker.
1486 long cm_Unlink(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t * cnamep,
1487 cm_user_t *userp, cm_req_t *reqp)
1493 AFSFetchStatus newDirStatus;
1495 struct rx_connection * callp;
1497 cm_scache_t *scp = NULL;
1498 int free_fnamep = FALSE;
1500 if (fnamep == NULL) {
1503 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ, &dirop);
1505 code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
1508 cm_EndDirOp(&dirop);
1515 #ifdef AFS_FREELANCE_CLIENT
1516 if (cm_freelanceEnabled && dscp == cm_data.rootSCachep) {
1517 /* deleting a mount point from the root dir. */
1518 code = cm_FreelanceRemoveMount(fnamep);
1523 code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
1525 /* make sure we don't screw up the dir status during the merge */
1526 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
1528 lock_ObtainWrite(&dscp->rw);
1529 sflags = CM_SCACHESYNC_STOREDATA;
1530 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1531 lock_ReleaseWrite(&dscp->rw);
1533 cm_EndDirOp(&dirop);
1538 afsFid.Volume = dscp->fid.volume;
1539 afsFid.Vnode = dscp->fid.vnode;
1540 afsFid.Unique = dscp->fid.unique;
1542 osi_Log1(afsd_logp, "CALL RemoveFile scp 0x%p", dscp);
1544 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
1548 callp = cm_GetRxConn(connp);
1549 code = RXAFS_RemoveFile(callp, &afsFid, fnamep,
1550 &newDirStatus, &volSync);
1551 rx_PutConnection(callp);
1553 } while (cm_Analyze(connp, userp, reqp, &dscp->fid, &volSync, NULL, NULL, code));
1554 code = cm_MapRPCError(code, reqp);
1557 osi_Log1(afsd_logp, "CALL RemoveFile FAILURE, code 0x%x", code);
1559 osi_Log0(afsd_logp, "CALL RemoveFile SUCCESS");
1562 lock_ObtainWrite(&dirop.scp->dirlock);
1563 dirop.lockType = CM_DIRLOCK_WRITE;
1565 lock_ObtainWrite(&dscp->rw);
1566 cm_dnlcRemove(dscp, cnamep);
1567 cm_SyncOpDone(dscp, NULL, sflags);
1569 cm_MergeStatus(NULL, dscp, &newDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
1570 } else if (code == CM_ERROR_NOSUCHFILE) {
1571 /* windows would not have allowed the request to delete the file
1572 * if it did not believe the file existed. therefore, we must
1573 * have an inconsistent view of the world.
1575 dscp->cbServerp = NULL;
1577 lock_ReleaseWrite(&dscp->rw);
1579 if (code == 0 && cm_CheckDirOpForSingleChange(&dirop) && cnamep) {
1580 cm_DirDeleteEntry(&dirop, fnamep);
1582 cm_BPlusDirDeleteEntry(&dirop, cnamep);
1585 cm_EndDirOp(&dirop);
1588 cm_ReleaseSCache(scp);
1590 lock_ObtainWrite(&scp->rw);
1591 scp->flags |= CM_SCACHEFLAG_DELETED;
1592 lock_ReleaseWrite(&scp->rw);
1603 /* called with a write locked vnode, and fills in the link info.
1604 * returns this the vnode still write locked.
1606 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1613 lock_AssertWrite(&linkScp->rw);
1614 if (!linkScp->mountPointStringp[0]) {
1615 /* read the link data */
1616 lock_ReleaseWrite(&linkScp->rw);
1617 thyper.LowPart = thyper.HighPart = 0;
1618 code = buf_Get(linkScp, &thyper, &bufp);
1619 lock_ObtainWrite(&linkScp->rw);
1623 code = cm_SyncOp(linkScp, bufp, userp, reqp, 0,
1624 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1629 cm_SyncOpDone(linkScp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1631 if (cm_HaveBuffer(linkScp, bufp, 0))
1634 code = cm_GetBuffer(linkScp, bufp, NULL, userp, reqp);
1639 } /* while loop to get the data */
1641 /* now if we still have no link read in,
1642 * copy the data from the buffer */
1643 if ((temp = linkScp->length.LowPart) >= MOUNTPOINTLEN) {
1645 return CM_ERROR_TOOBIG;
1648 /* otherwise, it fits; make sure it is still null (could have
1649 * lost race with someone else referencing this link above),
1650 * and if so, copy in the data.
1652 if (!linkScp->mountPointStringp[0]) {
1653 strncpy(linkScp->mountPointStringp, bufp->datap, temp);
1654 linkScp->mountPointStringp[temp] = 0; /* null terminate */
1656 if ( !strnicmp(linkScp->mountPointStringp, "msdfs:", strlen("msdfs:")) )
1657 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1660 } /* don't have sym link contents cached */
1665 /* called with a held vnode and a path suffix, with the held vnode being a
1666 * symbolic link. Our goal is to generate a new path to interpret, and return
1667 * this new path in newSpaceBufferp. If the new vnode is relative to a dir
1668 * other than the directory containing the symbolic link, then the new root is
1669 * returned in *newRootScpp, otherwise a null is returned there.
1671 long cm_AssembleLink(cm_scache_t *linkScp, fschar_t *pathSuffixp,
1672 cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1673 cm_user_t *userp, cm_req_t *reqp)
1680 *newRootScpp = NULL;
1681 *newSpaceBufferp = NULL;
1683 lock_ObtainWrite(&linkScp->rw);
1684 code = cm_HandleLink(linkScp, userp, reqp);
1688 /* if we may overflow the buffer, bail out; buffer is signficantly
1689 * bigger than max path length, so we don't really have to worry about
1690 * being a little conservative here.
1692 if (cm_FsStrLen(linkScp->mountPointStringp) + cm_FsStrLen(pathSuffixp) + 2
1693 >= CM_UTILS_SPACESIZE) {
1694 code = CM_ERROR_TOOBIG;
1698 tsp = cm_GetSpace();
1699 linkp = linkScp->mountPointStringp;
1700 if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1701 if (strlen(linkp) > cm_mountRootLen)
1702 StringCbCopyA((char *) tsp->data, sizeof(tsp->data), linkp+cm_mountRootLen+1);
1705 *newRootScpp = cm_data.rootSCachep;
1706 cm_HoldSCache(cm_data.rootSCachep);
1707 } else if (linkp[0] == '\\' && linkp[1] == '\\') {
1708 if (!strnicmp(&linkp[2], cm_NetbiosName, (len = (long)strlen(cm_NetbiosName))))
1710 char * p = &linkp[len + 3];
1711 if (strnicmp(p, "all", 3) == 0)
1714 StringCbCopyA(tsp->data, sizeof(tsp->data), p);
1715 for (p = tsp->data; *p; p++) {
1719 *newRootScpp = cm_data.rootSCachep;
1720 cm_HoldSCache(cm_data.rootSCachep);
1722 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1723 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1724 code = CM_ERROR_PATH_NOT_COVERED;
1726 } else if ( linkScp->fileType == CM_SCACHETYPE_DFSLINK ||
1727 !strnicmp(linkp, "msdfs:", (len = (long)strlen("msdfs:"))) ) {
1728 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1729 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1730 code = CM_ERROR_PATH_NOT_COVERED;
1731 } else if (*linkp == '\\' || *linkp == '/') {
1733 /* formerly, this was considered to be from the AFS root,
1734 * but this seems to create problems. instead, we will just
1735 * reject the link */
1736 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp+1);
1737 *newRootScpp = cm_data.rootSCachep;
1738 cm_HoldSCache(cm_data.rootSCachep);
1740 /* we still copy the link data into the response so that
1741 * the user can see what the link points to
1743 linkScp->fileType = CM_SCACHETYPE_INVALID;
1744 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1745 code = CM_ERROR_NOSUCHPATH;
1748 /* a relative link */
1749 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1751 if (pathSuffixp[0] != 0) { /* if suffix string is non-null */
1752 StringCchCatA(tsp->data,lengthof(tsp->data), "\\");
1753 StringCchCatA(tsp->data,lengthof(tsp->data), pathSuffixp);
1756 clientchar_t * cpath = cm_FsStringToClientStringAlloc(tsp->data, -1, NULL);
1757 cm_ClientStrCpy(tsp->wdata, lengthof(tsp->wdata), cpath);
1759 *newSpaceBufferp = tsp;
1763 if (code == CM_ERROR_PATH_NOT_COVERED && reqp->tidPathp && reqp->relPathp) {
1764 cm_VolStatus_Notify_DFS_Mapping(linkScp, reqp->tidPathp, reqp->relPathp);
1769 lock_ReleaseWrite(&linkScp->rw);
1772 #ifdef DEBUG_REFCOUNT
1773 long cm_NameIDbg(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
1774 cm_user_t *userp, clientchar_t *tidPathp, cm_req_t *reqp,
1775 cm_scache_t **outScpp,
1776 char * file, long line)
1778 long cm_NameI(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
1779 cm_user_t *userp, clientchar_t *tidPathp,
1780 cm_req_t *reqp, cm_scache_t **outScpp)
1784 clientchar_t *tp; /* ptr moving through input buffer */
1785 clientchar_t tc; /* temp char */
1786 int haveComponent; /* has new component started? */
1787 clientchar_t component[AFSPATHMAX]; /* this is the new component */
1788 clientchar_t *cp; /* component name being assembled */
1789 cm_scache_t *tscp; /* current location in the hierarchy */
1790 cm_scache_t *nscp; /* next dude down */
1791 cm_scache_t *dirScp; /* last dir we searched */
1792 cm_scache_t *linkScp; /* new root for the symlink we just
1794 cm_space_t *psp; /* space for current path, if we've hit
1796 cm_space_t *tempsp; /* temp vbl */
1797 clientchar_t *restp; /* rest of the pathname to interpret */
1798 int symlinkCount; /* count of # of symlinks traversed */
1799 int extraFlag; /* avoid chasing mt pts for dir cmd */
1800 int phase = 1; /* 1 = tidPathp, 2 = pathp */
1801 #define MAX_FID_COUNT 512
1802 cm_fid_t fids[MAX_FID_COUNT]; /* array of fids processed in this path walk */
1803 int fid_count = 0; /* number of fids processed in this path walk */
1806 #ifdef DEBUG_REFCOUNT
1807 afsi_log("%s:%d cm_NameI rootscp 0x%p ref %d", file, line, rootSCachep, rootSCachep->refCount);
1808 osi_Log4(afsd_logp,"cm_NameI rootscp 0x%p path %S tidpath %S flags 0x%x",
1809 rootSCachep, pathp ? pathp : "<NULL>", tidPathp ? tidPathp : "<NULL>",
1824 cm_HoldSCache(tscp);
1832 /* map Unix slashes into DOS ones so we can interpret Unix
1838 if (!haveComponent) {
1841 } else if (tc == 0) {
1855 /* we have a component here */
1856 if (tc == 0 || tc == '\\') {
1857 /* end of the component; we're at the last
1858 * component if tc == 0. However, if the last
1859 * is a symlink, we have more to do.
1861 *cp++ = 0; /* add null termination */
1863 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
1864 extraFlag = CM_FLAG_NOMOUNTCHASE;
1865 code = cm_Lookup(tscp, component,
1867 userp, reqp, &nscp);
1870 if (!cm_ClientStrCmp(component,_C("..")) ||
1871 !cm_ClientStrCmp(component,_C("."))) {
1873 * roll back the fid list until we find the
1874 * fid that matches where we are now. Its not
1875 * necessarily one or two fids because they
1876 * might have been symlinks or mount points or
1877 * both that were crossed.
1879 for ( i=fid_count-1; i>=0; i--) {
1880 if (!cm_FidCmp(&nscp->fid, &fids[i]))
1885 /* add the new fid to the list */
1886 for ( i=0; i<fid_count; i++) {
1887 if ( !cm_FidCmp(&nscp->fid, &fids[i]) ) {
1888 code = CM_ERROR_TOO_MANY_SYMLINKS;
1889 cm_ReleaseSCache(nscp);
1894 if (i == fid_count && fid_count < MAX_FID_COUNT) {
1895 fids[fid_count++] = nscp->fid;
1901 cm_ReleaseSCache(tscp);
1903 cm_ReleaseSCache(dirScp);
1906 if ((code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH) &&
1907 tscp->fileType == CM_SCACHETYPE_SYMLINK) {
1908 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_NOSUCHPATH");
1909 return CM_ERROR_NOSUCHPATH;
1911 osi_Log1(afsd_logp,"cm_NameI code 0x%x", code);
1916 haveComponent = 0; /* component done */
1918 cm_ReleaseSCache(dirScp);
1919 dirScp = tscp; /* for some symlinks */
1920 tscp = nscp; /* already held */
1922 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
1925 cm_ReleaseSCache(dirScp);
1931 /* now, if tscp is a symlink, we should follow it and
1932 * assemble the path again.
1934 lock_ObtainWrite(&tscp->rw);
1935 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1936 CM_SCACHESYNC_GETSTATUS
1937 | CM_SCACHESYNC_NEEDCALLBACK);
1939 lock_ReleaseWrite(&tscp->rw);
1940 cm_ReleaseSCache(tscp);
1943 cm_ReleaseSCache(dirScp);
1948 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1950 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
1951 /* this is a symlink; assemble a new buffer */
1952 lock_ReleaseWrite(&tscp->rw);
1953 if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
1954 cm_ReleaseSCache(tscp);
1957 cm_ReleaseSCache(dirScp);
1962 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_TOO_MANY_SYMLINKS");
1963 return CM_ERROR_TOO_MANY_SYMLINKS;
1973 /* TODO: make this better */
1974 frestp = cm_ClientStringToFsStringAlloc(restp, -1, NULL);
1975 code = cm_AssembleLink(tscp, frestp, &linkScp, &tempsp, userp, reqp);
1979 if (code == 0 && linkScp != NULL) {
1980 if (linkScp == cm_data.rootSCachep)
1983 for ( i=0; i<fid_count; i++) {
1984 if ( !cm_FidCmp(&linkScp->fid, &fids[i]) ) {
1985 code = CM_ERROR_TOO_MANY_SYMLINKS;
1986 cm_ReleaseSCache(linkScp);
1992 if (i == fid_count && fid_count < MAX_FID_COUNT) {
1993 fids[fid_count++] = linkScp->fid;
1998 /* something went wrong */
1999 cm_ReleaseSCache(tscp);
2002 cm_ReleaseSCache(dirScp);
2008 /* otherwise, tempsp has the new path,
2009 * and linkScp is the new root from
2010 * which to interpret that path.
2011 * Continue with the namei processing,
2012 * also doing the bookkeeping for the
2013 * space allocation and tracking the
2014 * vnode reference counts.
2020 cm_ReleaseSCache(tscp);
2025 * now, if linkScp is null, that's
2026 * AssembleLink's way of telling us that
2027 * the sym link is relative to the dir
2028 * containing the link. We have a ref
2029 * to it in dirScp, and we hold it now
2030 * and reuse it as the new spot in the
2038 /* not a symlink, we may be done */
2039 lock_ReleaseWrite(&tscp->rw);
2047 cm_ReleaseSCache(dirScp);
2055 cm_ReleaseSCache(dirScp);
2058 } /* end of a component */
2061 } /* we have a component */
2062 } /* big while loop over all components */
2066 cm_ReleaseSCache(dirScp);
2072 cm_ReleaseSCache(tscp);
2074 #ifdef DEBUG_REFCOUNT
2075 afsi_log("%s:%d cm_NameI code 0x%x outScpp 0x%p ref %d", file, line, code, *outScpp, (*outScpp)->refCount);
2077 osi_Log2(afsd_logp,"cm_NameI code 0x%x outScpp 0x%p", code, *outScpp);
2081 /* called with a dir, and a vnode within the dir that happens to be a symlink.
2082 * We chase the link, and return a held pointer to the target, if it exists,
2083 * in *outScpp. If we succeed, we return 0, otherwise we return an error code
2084 * and do not hold or return a target vnode.
2086 * This is very similar to calling cm_NameI with the last component of a name,
2087 * which happens to be a symlink, except that we've already passed by the name.
2089 * This function is typically called by the directory listing functions, which
2090 * encounter symlinks but need to return the proper file length so programs
2091 * like "more" work properly when they make use of the attributes retrieved from
2094 * The input vnode should not be locked when this function is called.
2096 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
2097 cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
2101 cm_scache_t *newRootScp;
2103 osi_Log1(afsd_logp, "Evaluating symlink scp 0x%p", linkScp);
2105 code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
2109 /* now, if newRootScp is NULL, we're really being told that the symlink
2110 * is relative to the current directory (dscp).
2112 if (newRootScp == NULL) {
2114 cm_HoldSCache(dscp);
2117 code = cm_NameI(newRootScp, spacep->wdata,
2118 CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
2119 userp, NULL, reqp, outScpp);
2121 if (code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH)
2122 code = CM_ERROR_NOSUCHPATH;
2124 /* this stuff is allocated no matter what happened on the namei call,
2126 cm_FreeSpace(spacep);
2127 cm_ReleaseSCache(newRootScp);
2129 if (linkScp == *outScpp) {
2130 cm_ReleaseSCache(*outScpp);
2132 code = CM_ERROR_NOSUCHPATH;
2138 /* for a given entry, make sure that it isn't in the stat cache, and then
2139 * add it to the list of file IDs to be obtained.
2141 * Don't bother adding it if we already have a vnode. Note that the dir
2142 * is locked, so we have to be careful checking the vnode we're thinking of
2143 * processing, to avoid deadlocks.
2145 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
2156 /* Don't overflow bsp. */
2157 if (bsp->counter >= CM_BULKMAX)
2158 return CM_ERROR_STOPNOW;
2160 thyper.LowPart = cm_data.buf_blockSize;
2161 thyper.HighPart = 0;
2162 thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
2164 /* thyper is now the first byte past the end of the record we're
2165 * interested in, and bsp->bufOffset is the first byte of the record
2166 * we're interested in.
2167 * Skip data in the others.
2170 if (LargeIntegerLessThan(*offp, bsp->bufOffset))
2172 if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
2173 return CM_ERROR_STOPNOW;
2174 if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
2177 cm_SetFid(&tfid, scp->fid.cell, scp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
2178 tscp = cm_FindSCache(&tfid);
2180 if (lock_TryWrite(&tscp->rw)) {
2181 /* we have an entry that we can look at */
2182 if (!(tscp->flags & CM_SCACHEFLAG_EACCESS) && cm_HaveCallback(tscp)) {
2183 /* we have a callback on it. Don't bother
2184 * fetching this stat entry, since we're happy
2185 * with the info we have.
2187 lock_ReleaseWrite(&tscp->rw);
2188 cm_ReleaseSCache(tscp);
2191 lock_ReleaseWrite(&tscp->rw);
2193 cm_ReleaseSCache(tscp);
2196 #ifdef AFS_FREELANCE_CLIENT
2197 // yj: if this is a mountpoint under root.afs then we don't want it
2198 // to be bulkstat-ed, instead, we call getSCache directly and under
2199 // getSCache, it is handled specially.
2200 if ( cm_freelanceEnabled &&
2201 tfid.cell==AFS_FAKE_ROOT_CELL_ID &&
2202 tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
2203 !(tfid.vnode==0x1 && tfid.unique==0x1) )
2205 osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
2206 return cm_GetSCache(&tfid, &tscp, NULL, NULL);
2208 #endif /* AFS_FREELANCE_CLIENT */
2211 bsp->fids[i].Volume = scp->fid.volume;
2212 bsp->fids[i].Vnode = tfid.vnode;
2213 bsp->fids[i].Unique = tfid.unique;
2218 cm_TryBulkStatRPC(cm_scache_t *dscp, cm_bulkStat_t *bbp, cm_user_t *userp, cm_req_t *reqp)
2221 AFSCBFids fidStruct;
2222 AFSBulkStats statStruct;
2224 AFSCBs callbackStruct;
2227 cm_callbackRequest_t cbReq;
2233 struct rx_connection * callp;
2234 int inlinebulk = 0; /* Did we use InlineBulkStatus RPC or not? */
2236 /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
2237 * make the calls to create the entries. Handle AFSCBMAX files at a
2240 for (filex = 0; filex < bbp->counter; filex += filesThisCall) {
2241 filesThisCall = bbp->counter - filex;
2242 if (filesThisCall > AFSCBMAX)
2243 filesThisCall = AFSCBMAX;
2245 fidStruct.AFSCBFids_len = filesThisCall;
2246 fidStruct.AFSCBFids_val = &bbp->fids[filex];
2247 statStruct.AFSBulkStats_len = filesThisCall;
2248 statStruct.AFSBulkStats_val = &bbp->stats[filex];
2249 callbackStruct.AFSCBs_len = filesThisCall;
2250 callbackStruct.AFSCBs_val = &bbp->callbacks[filex];
2251 cm_StartCallbackGrantingCall(NULL, &cbReq);
2252 osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
2254 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2258 callp = cm_GetRxConn(connp);
2259 if (!(connp->serverp->flags & CM_SERVERFLAG_NOINLINEBULK)) {
2260 code = RXAFS_InlineBulkStatus(callp, &fidStruct,
2261 &statStruct, &callbackStruct, &volSync);
2262 if (code == RXGEN_OPCODE) {
2263 cm_SetServerNoInlineBulk(connp->serverp, 0);
2269 code = RXAFS_BulkStatus(callp, &fidStruct,
2270 &statStruct, &callbackStruct, &volSync);
2272 rx_PutConnection(callp);
2274 } while (cm_Analyze(connp, userp, reqp, &dscp->fid,
2275 &volSync, NULL, &cbReq, code));
2276 code = cm_MapRPCError(code, reqp);
2278 osi_Log2(afsd_logp, "CALL %sBulkStatus FAILURE code 0x%x",
2279 inlinebulk ? "Inline" : "", code);
2281 osi_Log1(afsd_logp, "CALL %sBulkStatus SUCCESS", inlinebulk ? "Inline" : "");
2283 /* may as well quit on an error, since we're not going to do
2284 * much better on the next immediate call, either.
2287 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2291 /* otherwise, we should do the merges */
2292 for (i = 0; i<filesThisCall; i++) {
2294 cm_SetFid(&tfid, dscp->fid.cell, bbp->fids[j].Volume, bbp->fids[j].Vnode, bbp->fids[j].Unique);
2295 code = cm_GetSCache(&tfid, &scp, userp, reqp);
2299 /* otherwise, if this entry has no callback info,
2302 lock_ObtainWrite(&scp->rw);
2303 /* now, we have to be extra paranoid on merging in this
2304 * information, since we didn't use cm_SyncOp before
2305 * starting the fetch to make sure that no bad races
2306 * were occurring. Specifically, we need to make sure
2307 * we don't obliterate any newer information in the
2308 * vnode than have here.
2310 * Right now, be pretty conservative: if there's a
2311 * callback or a pending call, skip it.
2313 if ((scp->cbServerp == NULL || (scp->flags & CM_SCACHEFLAG_EACCESS))
2315 (CM_SCACHEFLAG_FETCHING
2316 | CM_SCACHEFLAG_STORING
2317 | CM_SCACHEFLAG_SIZESTORING))) {
2318 cm_EndCallbackGrantingCall(scp, &cbReq,
2320 CM_CALLBACK_MAINTAINCOUNT);
2321 cm_MergeStatus(dscp, scp, &bbp->stats[j], &volSync, userp, 0);
2323 lock_ReleaseWrite(&scp->rw);
2324 cm_ReleaseSCache(scp);
2325 } /* all files in the response */
2326 /* now tell it to drop the count,
2327 * after doing the vnode processing above */
2328 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2329 } /* while there are still more files to process */
2331 /* If we did the InlineBulk RPC pull out the return code and log it */
2333 if ((&bbp->stats[0])->errorCode) {
2334 osi_Log1(afsd_logp, "cm_TryBulkStat bulk stat error: %d",
2335 (&bbp->stats[0])->errorCode);
2336 code = (&bbp->stats[0])->errorCode;
2343 /* called with a write locked scp and a pointer to a buffer. Make bulk stat
2344 * calls on all undeleted files in the page of the directory specified.
2347 cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
2353 osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%p", dscp);
2355 /* should be on a buffer boundary */
2356 osi_assertx((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0, "invalid offset");
2358 bbp = malloc(sizeof(cm_bulkStat_t));
2359 memset(bbp, 0, sizeof(cm_bulkStat_t));
2360 bbp->bufOffset = *offsetp;
2362 lock_ReleaseWrite(&dscp->rw);
2363 /* first, assemble the file IDs we need to stat */
2364 code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) bbp, offsetp, userp, reqp, NULL);
2366 /* if we failed, bail out early */
2367 if (code && code != CM_ERROR_STOPNOW) {
2369 lock_ObtainWrite(&dscp->rw);
2373 code = cm_TryBulkStatRPC(dscp, bbp, userp, reqp);
2374 osi_Log1(afsd_logp, "END cm_TryBulkStat code 0x%x", code);
2376 lock_ObtainWrite(&dscp->rw);
2381 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
2385 /* initialize store back mask as inexpensive local variable */
2387 memset(statusp, 0, sizeof(AFSStoreStatus));
2389 /* copy out queued info from scache first, if scp passed in */
2391 if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
2392 statusp->ClientModTime = scp->clientModTime;
2393 mask |= AFS_SETMODTIME;
2394 scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
2399 /* now add in our locally generated request */
2400 if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
2401 statusp->ClientModTime = attrp->clientModTime;
2402 mask |= AFS_SETMODTIME;
2404 if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
2405 statusp->UnixModeBits = attrp->unixModeBits;
2406 mask |= AFS_SETMODE;
2408 if (attrp->mask & CM_ATTRMASK_OWNER) {
2409 statusp->Owner = attrp->owner;
2410 mask |= AFS_SETOWNER;
2412 if (attrp->mask & CM_ATTRMASK_GROUP) {
2413 statusp->Group = attrp->group;
2414 mask |= AFS_SETGROUP;
2417 statusp->Mask = mask;
2420 /* set the file size, and make sure that all relevant buffers have been
2421 * truncated. Ensure that any partially truncated buffers have been zeroed
2422 * to the end of the buffer.
2424 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
2430 /* start by locking out buffer creation */
2431 lock_ObtainWrite(&scp->bufCreateLock);
2433 /* verify that this is a file, not a dir or a symlink */
2434 lock_ObtainWrite(&scp->rw);
2435 code = cm_SyncOp(scp, NULL, userp, reqp, 0,
2436 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2439 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2441 if (scp->fileType != CM_SCACHETYPE_FILE) {
2442 code = CM_ERROR_ISDIR;
2447 if (LargeIntegerLessThan(*sizep, scp->length))
2452 lock_ReleaseWrite(&scp->rw);
2454 /* can't hold scp->rw lock here, since we may wait for a storeback to
2455 * finish if the buffer package is cleaning a buffer by storing it to
2459 buf_Truncate(scp, userp, reqp, sizep);
2461 /* now ensure that file length is short enough, and update truncPos */
2462 lock_ObtainWrite(&scp->rw);
2464 /* make sure we have a callback (so we have the right value for the
2465 * length), and wait for it to be safe to do a truncate.
2467 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
2468 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2469 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2471 /* If we only have 'i' bits, then we should still be able to set
2472 the size of a file we created. */
2473 if (code == CM_ERROR_NOACCESS && scp->creator == userp) {
2474 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
2475 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2476 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2482 if (LargeIntegerLessThan(*sizep, scp->length)) {
2483 /* a real truncation. If truncPos is not set yet, or is bigger
2484 * than where we're truncating the file, set truncPos to this
2489 if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
2490 || LargeIntegerLessThan(*sizep, scp->length)) {
2492 scp->truncPos = *sizep;
2493 scp->mask |= CM_SCACHEMASK_TRUNCPOS;
2495 /* in either case, the new file size has been changed */
2496 scp->length = *sizep;
2497 scp->mask |= CM_SCACHEMASK_LENGTH;
2499 else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
2500 /* really extending the file */
2501 scp->length = *sizep;
2502 scp->mask |= CM_SCACHEMASK_LENGTH;
2505 /* done successfully */
2508 cm_SyncOpDone(scp, NULL,
2509 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2510 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2513 lock_ReleaseWrite(&scp->rw);
2514 lock_ReleaseWrite(&scp->bufCreateLock);
2519 /* set the file size or other attributes (but not both at once) */
2520 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
2524 AFSFetchStatus afsOutStatus;
2528 AFSStoreStatus afsInStatus;
2529 struct rx_connection * callp;
2531 /* handle file length setting */
2532 if (attrp->mask & CM_ATTRMASK_LENGTH)
2533 return cm_SetLength(scp, &attrp->length, userp, reqp);
2535 lock_ObtainWrite(&scp->rw);
2536 /* otherwise, we have to make an RPC to get the status */
2537 code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
2539 lock_ReleaseWrite(&scp->rw);
2542 lock_ConvertWToR(&scp->rw);
2544 /* make the attr structure */
2545 cm_StatusFromAttr(&afsInStatus, scp, attrp);
2547 tfid.Volume = scp->fid.volume;
2548 tfid.Vnode = scp->fid.vnode;
2549 tfid.Unique = scp->fid.unique;
2550 lock_ReleaseRead(&scp->rw);
2552 /* now make the RPC */
2553 osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%p", scp);
2555 code = cm_ConnFromFID(&scp->fid, userp, reqp, &connp);
2559 callp = cm_GetRxConn(connp);
2560 code = RXAFS_StoreStatus(callp, &tfid,
2561 &afsInStatus, &afsOutStatus, &volSync);
2562 rx_PutConnection(callp);
2564 } while (cm_Analyze(connp, userp, reqp,
2565 &scp->fid, &volSync, NULL, NULL, code));
2566 code = cm_MapRPCError(code, reqp);
2569 osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
2571 osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
2573 lock_ObtainWrite(&scp->rw);
2574 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
2576 cm_MergeStatus(NULL, scp, &afsOutStatus, &volSync, userp,
2577 CM_MERGEFLAG_FORCE|CM_MERGEFLAG_STOREDATA);
2579 /* if we're changing the mode bits, discard the ACL cache,
2580 * since we changed the mode bits.
2582 if (afsInStatus.Mask & AFS_SETMODE)
2583 cm_FreeAllACLEnts(scp);
2584 lock_ReleaseWrite(&scp->rw);
2588 long cm_Create(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
2589 cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
2594 cm_callbackRequest_t cbReq;
2597 cm_scache_t *scp = NULL;
2599 AFSStoreStatus inStatus;
2600 AFSFetchStatus updatedDirStatus;
2601 AFSFetchStatus newFileStatus;
2602 AFSCallBack newFileCallback;
2604 struct rx_connection * callp;
2606 fschar_t * fnamep = NULL;
2608 /* can't create names with @sys in them; must expand it manually first.
2609 * return "invalid request" if they try.
2611 if (cm_ExpandSysName(cnamep, NULL, 0, 0)) {
2612 return CM_ERROR_ATSYS;
2615 #ifdef AFS_FREELANCE_CLIENT
2616 /* Freelance root volume does not hold files */
2617 if (cm_freelanceEnabled &&
2618 dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
2619 dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
2621 return CM_ERROR_NOACCESS;
2623 #endif /* AFS_FREELANCE_CLIENT */
2625 /* before starting the RPC, mark that we're changing the file data, so
2626 * that someone who does a chmod will know to wait until our call
2629 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2630 lock_ObtainWrite(&dscp->rw);
2631 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2632 lock_ReleaseWrite(&dscp->rw);
2634 cm_StartCallbackGrantingCall(NULL, &cbReq);
2636 cm_EndDirOp(&dirop);
2643 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
2645 cm_StatusFromAttr(&inStatus, NULL, attrp);
2647 /* try the RPC now */
2648 osi_Log1(afsd_logp, "CALL CreateFile scp 0x%p", dscp);
2650 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2654 dirAFSFid.Volume = dscp->fid.volume;
2655 dirAFSFid.Vnode = dscp->fid.vnode;
2656 dirAFSFid.Unique = dscp->fid.unique;
2658 callp = cm_GetRxConn(connp);
2659 code = RXAFS_CreateFile(connp->callp, &dirAFSFid, fnamep,
2660 &inStatus, &newAFSFid, &newFileStatus,
2661 &updatedDirStatus, &newFileCallback,
2663 rx_PutConnection(callp);
2665 } while (cm_Analyze(connp, userp, reqp,
2666 &dscp->fid, &volSync, NULL, &cbReq, code));
2667 code = cm_MapRPCError(code, reqp);
2670 osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
2672 osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
2675 lock_ObtainWrite(&dirop.scp->dirlock);
2676 dirop.lockType = CM_DIRLOCK_WRITE;
2678 lock_ObtainWrite(&dscp->rw);
2679 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2681 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
2683 lock_ReleaseWrite(&dscp->rw);
2685 /* now try to create the file's entry, too, but be careful to
2686 * make sure that we don't merge in old info. Since we weren't locking
2687 * out any requests during the file's creation, we may have pretty old
2691 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
2692 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2694 lock_ObtainWrite(&scp->rw);
2695 scp->creator = userp; /* remember who created it */
2696 if (!cm_HaveCallback(scp)) {
2697 cm_MergeStatus(dscp, scp, &newFileStatus, &volSync,
2699 cm_EndCallbackGrantingCall(scp, &cbReq,
2700 &newFileCallback, 0);
2703 lock_ReleaseWrite(&scp->rw);
2708 /* make sure we end things properly */
2710 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2712 if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
2713 cm_DirCreateEntry(&dirop, fnamep, &newFid);
2715 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
2718 cm_EndDirOp(&dirop);
2726 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
2730 code = buf_CleanVnode(scp, userp, reqp);
2732 lock_ObtainWrite(&scp->rw);
2734 if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
2735 | CM_SCACHEMASK_CLIENTMODTIME
2736 | CM_SCACHEMASK_LENGTH))
2737 code = cm_StoreMini(scp, userp, reqp);
2739 if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) {
2740 code = (scp->flags & CM_SCACHEFLAG_OVERQUOTA) ? CM_ERROR_QUOTA : CM_ERROR_SPACE;
2741 scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE);
2744 lock_ReleaseWrite(&scp->rw);
2749 long cm_MakeDir(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
2750 cm_user_t *userp, cm_req_t *reqp)
2755 cm_callbackRequest_t cbReq;
2758 cm_scache_t *scp = NULL;
2760 AFSStoreStatus inStatus;
2761 AFSFetchStatus updatedDirStatus;
2762 AFSFetchStatus newDirStatus;
2763 AFSCallBack newDirCallback;
2765 struct rx_connection * callp;
2767 fschar_t * fnamep = NULL;
2769 /* can't create names with @sys in them; must expand it manually first.
2770 * return "invalid request" if they try.
2772 if (cm_ExpandSysName(cnamep, NULL, 0, 0)) {
2773 return CM_ERROR_ATSYS;
2776 #ifdef AFS_FREELANCE_CLIENT
2777 /* Freelance root volume does not hold subdirectories */
2778 if (cm_freelanceEnabled &&
2779 dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
2780 dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
2782 return CM_ERROR_NOACCESS;
2784 #endif /* AFS_FREELANCE_CLIENT */
2786 /* before starting the RPC, mark that we're changing the directory
2787 * data, so that someone who does a chmod on the dir will wait until
2788 * our call completes.
2790 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2791 lock_ObtainWrite(&dscp->rw);
2792 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2793 lock_ReleaseWrite(&dscp->rw);
2795 cm_StartCallbackGrantingCall(NULL, &cbReq);
2797 cm_EndDirOp(&dirop);
2804 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
2805 cm_StatusFromAttr(&inStatus, NULL, attrp);
2807 /* try the RPC now */
2808 osi_Log1(afsd_logp, "CALL MakeDir scp 0x%p", dscp);
2810 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2814 dirAFSFid.Volume = dscp->fid.volume;
2815 dirAFSFid.Vnode = dscp->fid.vnode;
2816 dirAFSFid.Unique = dscp->fid.unique;
2818 callp = cm_GetRxConn(connp);
2819 code = RXAFS_MakeDir(connp->callp, &dirAFSFid, fnamep,
2820 &inStatus, &newAFSFid, &newDirStatus,
2821 &updatedDirStatus, &newDirCallback,
2823 rx_PutConnection(callp);
2825 } while (cm_Analyze(connp, userp, reqp,
2826 &dscp->fid, &volSync, NULL, &cbReq, code));
2827 code = cm_MapRPCError(code, reqp);
2830 osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
2832 osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
2835 lock_ObtainWrite(&dirop.scp->dirlock);
2836 dirop.lockType = CM_DIRLOCK_WRITE;
2838 lock_ObtainWrite(&dscp->rw);
2839 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2841 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
2843 lock_ReleaseWrite(&dscp->rw);
2845 /* now try to create the new dir's entry, too, but be careful to
2846 * make sure that we don't merge in old info. Since we weren't locking
2847 * out any requests during the file's creation, we may have pretty old
2851 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
2852 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2854 lock_ObtainWrite(&scp->rw);
2855 if (!cm_HaveCallback(scp)) {
2856 cm_MergeStatus(dscp, scp, &newDirStatus, &volSync,
2858 cm_EndCallbackGrantingCall(scp, &cbReq,
2859 &newDirCallback, 0);
2862 lock_ReleaseWrite(&scp->rw);
2863 cm_ReleaseSCache(scp);
2867 /* make sure we end things properly */
2869 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2871 if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
2872 cm_DirCreateEntry(&dirop, fnamep, &newFid);
2874 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
2877 cm_EndDirOp(&dirop);
2881 /* and return error code */
2885 long cm_Link(cm_scache_t *dscp, clientchar_t *cnamep, cm_scache_t *sscp, long flags,
2886 cm_user_t *userp, cm_req_t *reqp)
2891 AFSFid existingAFSFid;
2892 AFSFetchStatus updatedDirStatus;
2893 AFSFetchStatus newLinkStatus;
2895 struct rx_connection * callp;
2897 fschar_t * fnamep = NULL;
2899 if (dscp->fid.cell != sscp->fid.cell ||
2900 dscp->fid.volume != sscp->fid.volume) {
2901 return CM_ERROR_CROSSDEVLINK;
2904 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2905 lock_ObtainWrite(&dscp->rw);
2906 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2907 lock_ReleaseWrite(&dscp->rw);
2909 cm_EndDirOp(&dirop);
2914 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
2916 /* try the RPC now */
2917 osi_Log1(afsd_logp, "CALL Link scp 0x%p", dscp);
2919 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2922 dirAFSFid.Volume = dscp->fid.volume;
2923 dirAFSFid.Vnode = dscp->fid.vnode;
2924 dirAFSFid.Unique = dscp->fid.unique;
2926 existingAFSFid.Volume = sscp->fid.volume;
2927 existingAFSFid.Vnode = sscp->fid.vnode;
2928 existingAFSFid.Unique = sscp->fid.unique;
2930 callp = cm_GetRxConn(connp);
2931 code = RXAFS_Link(callp, &dirAFSFid, fnamep, &existingAFSFid,
2932 &newLinkStatus, &updatedDirStatus, &volSync);
2933 rx_PutConnection(callp);
2934 osi_Log1(afsd_logp," RXAFS_Link returns 0x%x", code);
2936 } while (cm_Analyze(connp, userp, reqp,
2937 &dscp->fid, &volSync, NULL, NULL, code));
2939 code = cm_MapRPCError(code, reqp);
2942 osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
2944 osi_Log0(afsd_logp, "CALL Link SUCCESS");
2947 lock_ObtainWrite(&dirop.scp->dirlock);
2948 dirop.lockType = CM_DIRLOCK_WRITE;
2950 lock_ObtainWrite(&dscp->rw);
2951 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2953 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
2955 lock_ReleaseWrite(&dscp->rw);
2958 if (cm_CheckDirOpForSingleChange(&dirop)) {
2959 cm_DirCreateEntry(&dirop, fnamep, &sscp->fid);
2961 cm_BPlusDirCreateEntry(&dirop, cnamep, &sscp->fid);
2965 cm_EndDirOp(&dirop);
2972 long cm_SymLink(cm_scache_t *dscp, clientchar_t *cnamep, fschar_t *contentsp, long flags,
2973 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
2981 AFSStoreStatus inStatus;
2982 AFSFetchStatus updatedDirStatus;
2983 AFSFetchStatus newLinkStatus;
2985 struct rx_connection * callp;
2987 fschar_t *fnamep = NULL;
2989 /* before starting the RPC, mark that we're changing the directory data,
2990 * so that someone who does a chmod on the dir will wait until our
2993 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2994 lock_ObtainWrite(&dscp->rw);
2995 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2996 lock_ReleaseWrite(&dscp->rw);
2998 cm_EndDirOp(&dirop);
3003 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3005 cm_StatusFromAttr(&inStatus, NULL, attrp);
3007 /* try the RPC now */
3008 osi_Log1(afsd_logp, "CALL Symlink scp 0x%p", dscp);
3010 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3014 dirAFSFid.Volume = dscp->fid.volume;
3015 dirAFSFid.Vnode = dscp->fid.vnode;
3016 dirAFSFid.Unique = dscp->fid.unique;
3018 callp = cm_GetRxConn(connp);
3019 code = RXAFS_Symlink(callp, &dirAFSFid, fnamep, contentsp,
3020 &inStatus, &newAFSFid, &newLinkStatus,
3021 &updatedDirStatus, &volSync);
3022 rx_PutConnection(callp);
3024 } while (cm_Analyze(connp, userp, reqp,
3025 &dscp->fid, &volSync, NULL, NULL, code));
3026 code = cm_MapRPCError(code, reqp);
3029 osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
3031 osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
3034 lock_ObtainWrite(&dirop.scp->dirlock);
3035 dirop.lockType = CM_DIRLOCK_WRITE;
3037 lock_ObtainWrite(&dscp->rw);
3038 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3040 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
3042 lock_ReleaseWrite(&dscp->rw);
3045 if (cm_CheckDirOpForSingleChange(&dirop)) {
3046 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3048 cm_DirCreateEntry(&dirop, fnamep, &newFid);
3050 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
3054 cm_EndDirOp(&dirop);
3056 /* now try to create the new dir's entry, too, but be careful to
3057 * make sure that we don't merge in old info. Since we weren't locking
3058 * out any requests during the file's creation, we may have pretty old
3062 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3063 code = cm_GetSCache(&newFid, &scp, userp, reqp);
3065 lock_ObtainWrite(&scp->rw);
3066 if (!cm_HaveCallback(scp)) {
3067 cm_MergeStatus(dscp, scp, &newLinkStatus, &volSync,
3070 lock_ReleaseWrite(&scp->rw);
3071 cm_ReleaseSCache(scp);
3077 /* and return error code */
3081 /*! \brief Remove a directory
3083 Encapsulates a call to RXAFS_RemoveDir().
3085 \param[in] dscp cm_scache_t for the directory containing the
3086 directory to be removed.
3088 \param[in] fnamep This will be the original name of the directory
3089 as known to the file server. It will be passed in to RXAFS_RemoveDir().
3090 This parameter is optional. If it is not provided the value
3093 \param[in] cnamep Normalized name used to update the local
3096 \param[in] userp cm_user_t for the request.
3098 \param[in] reqp Request tracker.
3100 long cm_RemoveDir(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t *cnamep, cm_user_t *userp, cm_req_t *reqp)
3106 AFSFetchStatus updatedDirStatus;
3108 struct rx_connection * callp;
3110 cm_scache_t *scp = NULL;
3111 int free_fnamep = FALSE;
3113 if (fnamep == NULL) {
3116 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ, &dirop);
3118 code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
3121 cm_EndDirOp(&dirop);
3128 code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
3132 /* before starting the RPC, mark that we're changing the directory data,
3133 * so that someone who does a chmod on the dir will wait until our
3136 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
3137 lock_ObtainWrite(&dscp->rw);
3138 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3139 lock_ReleaseWrite(&dscp->rw);
3141 cm_EndDirOp(&dirop);
3146 /* try the RPC now */
3147 osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%p", dscp);
3149 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3153 dirAFSFid.Volume = dscp->fid.volume;
3154 dirAFSFid.Vnode = dscp->fid.vnode;
3155 dirAFSFid.Unique = dscp->fid.unique;
3157 callp = cm_GetRxConn(connp);
3158 code = RXAFS_RemoveDir(callp, &dirAFSFid, fnamep,
3159 &updatedDirStatus, &volSync);
3160 rx_PutConnection(callp);
3162 } while (cm_Analyze(connp, userp, reqp,
3163 &dscp->fid, &volSync, NULL, NULL, code));
3164 code = cm_MapRPCErrorRmdir(code, reqp);
3167 osi_Log1(afsd_logp, "CALL RemoveDir FAILURE, code 0x%x", code);
3169 osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
3172 lock_ObtainWrite(&dirop.scp->dirlock);
3173 dirop.lockType = CM_DIRLOCK_WRITE;
3175 lock_ObtainWrite(&dscp->rw);
3176 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3178 cm_dnlcRemove(dscp, cnamep);
3179 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
3181 lock_ReleaseWrite(&dscp->rw);
3184 if (cm_CheckDirOpForSingleChange(&dirop) && cnamep != NULL) {
3185 cm_DirDeleteEntry(&dirop, fnamep);
3187 cm_BPlusDirDeleteEntry(&dirop, cnamep);
3191 cm_EndDirOp(&dirop);
3194 cm_ReleaseSCache(scp);
3196 lock_ObtainWrite(&scp->rw);
3197 scp->flags |= CM_SCACHEFLAG_DELETED;
3198 lock_ReleaseWrite(&scp->rw);
3206 /* and return error code */
3210 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
3212 /* grab mutex on contents */
3213 lock_ObtainWrite(&scp->rw);
3215 /* reset the prefetch info */
3216 scp->prefetch.base.LowPart = 0; /* base */
3217 scp->prefetch.base.HighPart = 0;
3218 scp->prefetch.end.LowPart = 0; /* and end */
3219 scp->prefetch.end.HighPart = 0;
3221 /* release mutex on contents */
3222 lock_ReleaseWrite(&scp->rw);
3228 /*! \brief Rename a file or directory
3230 Encapsulates a RXAFS_Rename() call.
3232 \param[in] oldDscp cm_scache_t for the directory containing the old
3235 \param[in] oldNamep The original old name known to the file server.
3236 This is the name that will be passed into the RXAFS_Rename().
3237 If it is not provided, it will be looked up.
3239 \param[in] normalizedOldNamep Normalized old name. This is used for
3240 updating local directory caches.
3242 \param[in] newDscp cm_scache_t for the directory containing the new
3245 \param[in] newNamep New name. Normalized.
3247 \param[in] userp cm_user_t for the request.
3249 \param[in,out] reqp Request tracker.
3252 long cm_Rename(cm_scache_t *oldDscp, fschar_t *oldNamep, clientchar_t *cOldNamep,
3253 cm_scache_t *newDscp, clientchar_t *cNewNamep, cm_user_t *userp,
3258 AFSFid oldDirAFSFid;
3259 AFSFid newDirAFSFid;
3261 AFSFetchStatus updatedOldDirStatus;
3262 AFSFetchStatus updatedNewDirStatus;
3265 struct rx_connection * callp;
3266 cm_dirOp_t oldDirOp;
3269 cm_dirOp_t newDirOp;
3270 fschar_t * newNamep = NULL;
3271 int free_oldNamep = FALSE;
3273 if (oldNamep == NULL) {
3276 code = cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_READ, &oldDirOp);
3278 code = cm_BPlusDirLookupOriginalName(&oldDirOp, cOldNamep, &oldNamep);
3280 free_oldNamep = TRUE;
3281 cm_EndDirOp(&oldDirOp);
3289 /* before starting the RPC, mark that we're changing the directory data,
3290 * so that someone who does a chmod on the dir will wait until our call
3291 * completes. We do this in vnode order so that we don't deadlock,
3292 * which makes the code a little verbose.
3294 if (oldDscp == newDscp) {
3295 /* check for identical names */
3296 if (cm_ClientStrCmp(cOldNamep, cNewNamep) == 0) {
3297 code = CM_ERROR_RENAME_IDENTICAL;
3302 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3303 lock_ObtainWrite(&oldDscp->rw);
3304 cm_dnlcRemove(oldDscp, cOldNamep);
3305 cm_dnlcRemove(oldDscp, cNewNamep);
3306 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3307 CM_SCACHESYNC_STOREDATA);
3308 lock_ReleaseWrite(&oldDscp->rw);
3310 cm_EndDirOp(&oldDirOp);
3314 /* two distinct dir vnodes */
3316 if (oldDscp->fid.cell != newDscp->fid.cell ||
3317 oldDscp->fid.volume != newDscp->fid.volume) {
3318 code = CM_ERROR_CROSSDEVLINK;
3322 /* shouldn't happen that we have distinct vnodes for two
3323 * different files, but could due to deliberate attack, or
3324 * stale info. Avoid deadlocks and quit now.
3326 if (oldDscp->fid.vnode == newDscp->fid.vnode) {
3327 code = CM_ERROR_CROSSDEVLINK;
3331 if (oldDscp->fid.vnode < newDscp->fid.vnode) {
3332 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3333 lock_ObtainWrite(&oldDscp->rw);
3334 cm_dnlcRemove(oldDscp, cOldNamep);
3335 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3336 CM_SCACHESYNC_STOREDATA);
3337 lock_ReleaseWrite(&oldDscp->rw);
3339 cm_EndDirOp(&oldDirOp);
3341 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE, &newDirOp);
3342 lock_ObtainWrite(&newDscp->rw);
3343 cm_dnlcRemove(newDscp, cNewNamep);
3344 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3345 CM_SCACHESYNC_STOREDATA);
3346 lock_ReleaseWrite(&newDscp->rw);
3348 cm_EndDirOp(&newDirOp);
3350 /* cleanup first one */
3351 lock_ObtainWrite(&oldDscp->rw);
3352 cm_SyncOpDone(oldDscp, NULL,
3353 CM_SCACHESYNC_STOREDATA);
3354 lock_ReleaseWrite(&oldDscp->rw);
3355 cm_EndDirOp(&oldDirOp);
3360 /* lock the new vnode entry first */
3361 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE, &newDirOp);
3362 lock_ObtainWrite(&newDscp->rw);
3363 cm_dnlcRemove(newDscp, cNewNamep);
3364 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3365 CM_SCACHESYNC_STOREDATA);
3366 lock_ReleaseWrite(&newDscp->rw);
3368 cm_EndDirOp(&newDirOp);
3370 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3371 lock_ObtainWrite(&oldDscp->rw);
3372 cm_dnlcRemove(oldDscp, cOldNamep);
3373 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3374 CM_SCACHESYNC_STOREDATA);
3375 lock_ReleaseWrite(&oldDscp->rw);
3377 cm_EndDirOp(&oldDirOp);
3379 /* cleanup first one */
3380 lock_ObtainWrite(&newDscp->rw);
3381 cm_SyncOpDone(newDscp, NULL,
3382 CM_SCACHESYNC_STOREDATA);
3383 lock_ReleaseWrite(&newDscp->rw);
3384 cm_EndDirOp(&newDirOp);
3388 } /* two distinct vnodes */
3395 newNamep = cm_ClientStringToFsStringAlloc(cNewNamep, -1, NULL);
3397 /* try the RPC now */
3398 osi_Log2(afsd_logp, "CALL Rename old scp 0x%p new scp 0x%p",
3401 code = cm_ConnFromFID(&oldDscp->fid, userp, reqp, &connp);
3405 oldDirAFSFid.Volume = oldDscp->fid.volume;
3406 oldDirAFSFid.Vnode = oldDscp->fid.vnode;
3407 oldDirAFSFid.Unique = oldDscp->fid.unique;
3408 newDirAFSFid.Volume = newDscp->fid.volume;
3409 newDirAFSFid.Vnode = newDscp->fid.vnode;
3410 newDirAFSFid.Unique = newDscp->fid.unique;
3412 callp = cm_GetRxConn(connp);
3413 code = RXAFS_Rename(callp, &oldDirAFSFid, oldNamep,
3414 &newDirAFSFid, newNamep,
3415 &updatedOldDirStatus, &updatedNewDirStatus,
3417 rx_PutConnection(callp);
3419 } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
3420 &volSync, NULL, NULL, code));
3421 code = cm_MapRPCError(code, reqp);
3424 osi_Log1(afsd_logp, "CALL Rename FAILURE, code 0x%x", code);
3426 osi_Log0(afsd_logp, "CALL Rename SUCCESS");
3428 /* update the individual stat cache entries for the directories */
3430 lock_ObtainWrite(&oldDirOp.scp->dirlock);
3431 oldDirOp.lockType = CM_DIRLOCK_WRITE;
3433 lock_ObtainWrite(&oldDscp->rw);
3434 cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
3437 cm_MergeStatus(NULL, oldDscp, &updatedOldDirStatus, &volSync,
3438 userp, CM_MERGEFLAG_DIROP);
3439 lock_ReleaseWrite(&oldDscp->rw);
3442 if (cm_CheckDirOpForSingleChange(&oldDirOp)) {
3445 diropCode = cm_BPlusDirLookup(&oldDirOp, cOldNamep, &fileFid);
3446 if (diropCode == CM_ERROR_INEXACT_MATCH)
3448 else if (diropCode == EINVAL)
3450 diropCode = cm_DirLookup(&oldDirOp, oldNamep, &fileFid);
3452 if (diropCode == 0) {
3454 diropCode = cm_DirCreateEntry(&oldDirOp, newNamep, &fileFid);
3456 cm_BPlusDirCreateEntry(&oldDirOp, cNewNamep, &fileFid);
3460 if (diropCode == 0) {
3461 diropCode = cm_DirDeleteEntry(&oldDirOp, oldNamep);
3463 cm_BPlusDirDeleteEntry(&oldDirOp, cOldNamep);
3469 cm_EndDirOp(&oldDirOp);
3471 /* and update it for the new one, too, if necessary */
3474 lock_ObtainWrite(&newDirOp.scp->dirlock);
3475 newDirOp.lockType = CM_DIRLOCK_WRITE;
3477 lock_ObtainWrite(&newDscp->rw);
3478 cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
3480 cm_MergeStatus(NULL, newDscp, &updatedNewDirStatus, &volSync,
3481 userp, CM_MERGEFLAG_DIROP);
3482 lock_ReleaseWrite(&newDscp->rw);
3485 /* we only make the local change if we successfully made
3486 the change in the old directory AND there was only one
3487 change in the new directory */
3488 if (diropCode == 0 && cm_CheckDirOpForSingleChange(&newDirOp)) {
3489 cm_DirCreateEntry(&newDirOp, newNamep, &fileFid);
3491 cm_BPlusDirCreateEntry(&newDirOp, cNewNamep, &fileFid);
3495 cm_EndDirOp(&newDirOp);
3504 /* and return error code */
3508 /* Byte range locks:
3510 The OpenAFS Windows client has to fake byte range locks given no
3511 server side support for such locks. This is implemented as keyed
3512 byte range locks on the cache manager.
3514 Keyed byte range locks:
3516 Each cm_scache_t structure keeps track of a list of keyed locks.
3517 The key for a lock identifies an owner of a set of locks (referred
3518 to as a client). Each key is represented by a value. The set of
3519 key values used within a specific cm_scache_t structure form a
3520 namespace that has a scope of just that cm_scache_t structure. The
3521 same key value can be used with another cm_scache_t structure and
3522 correspond to a completely different client. However it is
3523 advantageous for the SMB or IFS layer to make sure that there is a
3524 1-1 mapping between client and keys over all cm_scache_t objects.
3526 Assume a client C has key Key(C) (although, since the scope of the
3527 key is a cm_scache_t, the key can be Key(C,S), where S is the
3528 cm_scache_t. But assume a 1-1 relation between keys and clients).
3529 A byte range (O,+L) denotes byte addresses (O) through (O+L-1)
3530 inclusive (a.k.a. [O,O+L-1]). The function Key(x) is implemented
3531 through cm_generateKey() function for both SMB and IFS.
3533 The list of locks for a cm_scache_t object S is maintained in
3534 S->fileLocks. The cache manager will set a lock on the AFS file
3535 server in order to assert the locks in S->fileLocks. If only
3536 shared locks are in place for S, then the cache manager will obtain
3537 a LockRead lock, while if there are any exclusive locks, it will
3538 obtain a LockWrite lock. If the exclusive locks are all released
3539 while the shared locks remain, then the cache manager will
3540 downgrade the lock from LockWrite to LockRead. Similarly, if an
3541 exclusive lock is obtained when only shared locks exist, then the
3542 cache manager will try to upgrade the lock from LockRead to
3545 Each lock L owned by client C maintains a key L->key such that
3546 L->key == Key(C), the effective range defined by L->LOffset and
3547 L->LLength such that the range of bytes affected by the lock is
3548 (L->LOffset, +L->LLength), a type maintained in L->LockType which
3549 is either exclusive or shared.
3553 A lock exists iff it is in S->fileLocks for some cm_scache_t
3554 S. Existing locks are in one of the following states: ACTIVE,
3555 WAITLOCK, WAITUNLOCK, LOST, DELETED.
3557 The following sections describe each lock and the associated
3560 1. ACTIVE: A lock L is ACTIVE iff the cache manager has asserted
3561 the lock with the AFS file server. This type of lock can be
3562 exercised by a client to read or write to the locked region (as
3565 1.1 ACTIVE->LOST: When the AFS file server fails to extend a
3566 server lock that was required to assert the lock. Before
3567 marking the lock as lost, the cache manager checks if the file
3568 has changed on the server. If the file has not changed, then
3569 the cache manager will attempt to obtain a new server lock
3570 that is sufficient to assert the client side locks for the
3571 file. If any of these fail, the lock is marked as LOST.
3572 Otherwise, it is left as ACTIVE.
3574 1.2 ACTIVE->DELETED: Lock is released.
3576 2. WAITLOCK: A lock is in a WAITLOCK state if the cache manager
3577 grants the lock but the lock is yet to be asserted with the AFS
3578 file server. Once the file server grants the lock, the state
3579 will transition to an ACTIVE lock.
3581 2.1 WAITLOCK->ACTIVE: The server granted the lock.
3583 2.2 WAITLOCK->DELETED: Lock is abandoned, or timed out during
3586 2.3 WAITLOCK->LOST: One or more locks from this client were
3587 marked as LOST. No further locks will be granted to this
3588 client until all lost locks are removed.
3590 3. WAITUNLOCK: A lock is in a WAITUNLOCK state if the cache manager
3591 receives a request for a lock that conflicts with an existing
3592 ACTIVE or WAITLOCK lock. The lock will be placed in the queue
3593 and will be granted at such time the conflicting locks are
3594 removed, at which point the state will transition to either
3597 3.1 WAITUNLOCK->ACTIVE: The conflicting lock was removed. The
3598 current serverLock is sufficient to assert this lock, or a
3599 sufficient serverLock is obtained.
3601 3.2 WAITUNLOCK->WAITLOCK: The conflicting lock was removed,
3602 however the required serverLock is yet to be asserted with the
3605 3.3 WAITUNLOCK->DELETED: The lock is abandoned, timed out or
3608 3.5 WAITUNLOCK->LOST: One or more locks from this client were
3609 marked as LOST. No further locks will be granted to this
3610 client until all lost locks are removed.
3612 4. LOST: A lock L is LOST if the server lock that was required to
3613 assert the lock could not be obtained or if it could not be
3614 extended, or if other locks by the same client were LOST.
3615 Essentially, once a lock is LOST, the contract between the cache
3616 manager and that specific client is no longer valid.
3618 The cache manager rechecks the server lock once every minute and
3619 extends it as appropriate. If this is not done for 5 minutes,
3620 the AFS file server will release the lock (the 5 minute timeout
3621 is based on current file server code and is fairly arbitrary).
3622 Once released, the lock cannot be re-obtained without verifying
3623 that the contents of the file hasn't been modified since the
3624 time the lock was released. Re-obtaining the lock without
3625 verifying this may lead to data corruption. If the lock can not
3626 be obtained safely, then all active locks for the cm_scache_t
3629 4.1 LOST->DELETED: The lock is released.
3631 5. DELETED: The lock is no longer relevant. Eventually, it will
3632 get removed from the cm_scache_t. In the meantime, it will be
3633 treated as if it does not exist.
3635 5.1 DELETED->not exist: The lock is removed from the
3638 The following are classifications of locks based on their state.
3640 6* A lock L is ACCEPTED if it is ACTIVE or WAITLOCK. These locks
3641 have been accepted by the cache manager, but may or may not have
3642 been granted back to the client.
3644 7* A lock L is QUEUED if it is ACTIVE, WAITLOCK or WAITUNLOCK.
3646 8* A lock L is WAITING if it is WAITLOCK or WAITUNLOCK.
3650 A client C can READ range (Offset,+Length) of a file represented by
3651 cm_scache_t S iff (1):
3653 1. for all _a_ in (Offset,+Length), all of the following is true:
3655 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3656 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3659 1.2 For each LOST lock L in S->fileLocks such that _a_ in
3660 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3663 (When locks are lost on an cm_scache_t, all locks are lost. By
3664 4.2 (below), if there is an exclusive LOST lock, then there
3665 can't be any overlapping ACTIVE locks.)
3667 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
3669 2. for all _a_ in (Offset,+Length), one of the following is true:
3671 2.1 Byte _a_ of S is unowned (as specified in 1.1) AND there
3672 does not exist a LOST lock L such that _a_ in
3673 (L->LOffset,+L->LLength).
3675 2.2 Byte _a_ of S is owned by C under lock L (as specified in
3676 1.2) AND L->LockType is exclusive.
3678 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
3680 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
3683 3.1 If L->LockType is exclusive then there does NOT exist a
3684 ACCEPTED lock M in S->fileLocks such that _a_ in
3685 (M->LOffset,+M->LLength).
3687 (If we count all QUEUED locks then we hit cases such as
3688 cascading waiting locks where the locks later on in the queue
3689 can be granted without compromising file integrity. On the
3690 other hand if only ACCEPTED locks are considered, then locks
3691 that were received earlier may end up waiting for locks that
3692 were received later to be unlocked. The choice of ACCEPTED
3693 locks was made to mimic the Windows byte range lock
3696 3.2 If L->LockType is shared then for each ACCEPTED lock M in
3697 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
3698 M->LockType is shared.
3700 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
3702 4.1 M->key != Key(C)
3704 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
3705 and (M->LOffset,+M->LLength) do not intersect.
3707 (Note: If a client loses a lock, it loses all locks.
3708 Subsequently, it will not be allowed to obtain any more locks
3709 until all existing LOST locks that belong to the client are
3710 released. Once all locks are released by a single client,
3711 there exists no further contract between the client and AFS
3712 about the contents of the file, hence the client can then
3713 proceed to obtain new locks and establish a new contract.
3715 This doesn't quite work as you think it should, because most
3716 applications aren't built to deal with losing locks they
3717 thought they once had. For now, we don't have a good
3718 solution to lost locks.
3720 Also, for consistency reasons, we have to hold off on
3721 granting locks that overlap exclusive LOST locks.)
3723 A client C can only unlock locks L in S->fileLocks which have
3726 The representation and invariants are as follows:
3728 - Each cm_scache_t structure keeps:
3730 - A queue of byte-range locks (cm_scache_t::fileLocks) which
3731 are of type cm_file_lock_t.
3733 - A record of the highest server-side lock that has been
3734 obtained for this object (cm_scache_t::serverLock), which is
3735 one of (-1), LockRead, LockWrite.
3737 - A count of ACCEPTED exclusive and shared locks that are in the
3738 queue (cm_scache_t::sharedLocks and
3739 cm_scache_t::exclusiveLocks)
3741 - Each cm_file_lock_t structure keeps:
3743 - The type of lock (cm_file_lock_t::LockType)
3745 - The key associated with the lock (cm_file_lock_t::key)
3747 - The offset and length of the lock (cm_file_lock_t::LOffset
3748 and cm_file_lock_t::LLength)
3750 - The state of the lock.
3752 - Time of issuance or last successful extension
3754 Semantic invariants:
3756 I1. The number of ACCEPTED locks in S->fileLocks are
3757 (S->sharedLocks + S->exclusiveLocks)
3759 External invariants:
3761 I3. S->serverLock is the lock that we have asserted with the
3762 AFS file server for this cm_scache_t.
3764 I4. S->serverLock == LockRead iff there is at least one ACTIVE
3765 shared lock, but no ACTIVE exclusive locks.
3767 I5. S->serverLock == LockWrite iff there is at least one ACTIVE
3770 I6. If L is a LOST lock, then for each lock M in S->fileLocks,
3771 M->key == L->key IMPLIES M is LOST or DELETED.
3776 #define IS_LOCK_ACTIVE(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == 0)
3778 #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)
3780 #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)
3782 #define IS_LOCK_LOST(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_LOST)
3784 #define IS_LOCK_DELETED(lockp) (((lockp)->flags & CM_FILELOCK_FLAG_DELETED) == CM_FILELOCK_FLAG_DELETED)
3787 #define IS_LOCK_ACCEPTED(lockp) (IS_LOCK_ACTIVE(lockp) || IS_LOCK_WAITLOCK(lockp))
3790 #define IS_LOCK_CLIENTONLY(lockp) ((((lockp)->scp->flags & CM_SCACHEFLAG_RO) == CM_SCACHEFLAG_RO) || (((lockp)->flags & CM_FILELOCK_FLAG_CLIENTONLY) == CM_FILELOCK_FLAG_CLIENTONLY))
3793 #define INTERSECT_RANGE(r1,r2) (((r2).offset+(r2).length) > (r1).offset && ((r1).offset +(r1).length) > (r2).offset)
3796 #define CONTAINS_RANGE(r1,r2) (((r2).offset+(r2).length) <= ((r1).offset+(r1).length) && (r1).offset <= (r2).offset)
3798 #if defined(VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS) && !defined(LOCK_TESTING)
3799 #define SCP_SUPPORTS_BRLOCKS(scp) ((scp)->cbServerp && ((scp)->cbServerp->capabilities & VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS))
3801 #define SCP_SUPPORTS_BRLOCKS(scp) (1)
3804 #define SERVERLOCKS_ENABLED(scp) (!((scp)->flags & CM_SCACHEFLAG_RO) && cm_enableServerLocks && SCP_SUPPORTS_BRLOCKS(scp))
3806 #if defined(VICED_CAPABILITY_WRITELOCKACL)
3807 #define SCP_SUPPORTS_WRITELOCKACL(scp) ((scp)->cbServerp && ((scp->cbServerp->capabilities & VICED_CAPABILITY_WRITELOCKACL)))
3809 #define SCP_SUPPORTS_WRITELOCKACL(scp) (0)
3811 /* This should really be defined in any build that this code is being
3813 #error VICED_CAPABILITY_WRITELOCKACL not defined.
3816 static void cm_LockRangeSubtract(cm_range_t * pos, const cm_range_t * neg)
3818 afs_int64 int_begin;
3821 int_begin = MAX(pos->offset, neg->offset);
3822 int_end = MIN(pos->offset+pos->length, neg->offset+neg->length);
3824 if (int_begin < int_end) {
3825 if (int_begin == pos->offset) {
3826 pos->length = pos->offset + pos->length - int_end;
3827 pos->offset = int_end;
3828 } else if (int_end == pos->offset + pos->length) {
3829 pos->length = int_begin - pos->offset;
3832 /* We only subtract ranges if the resulting range is
3833 contiguous. If we try to support non-contigous ranges, we
3834 aren't actually improving performance. */
3838 /* Called with scp->rw held. Returns 0 if all is clear to read the
3839 specified range by the client identified by key.
3841 long cm_LockCheckRead(cm_scache_t *scp,
3842 LARGE_INTEGER LOffset,
3843 LARGE_INTEGER LLength,
3846 #ifndef ADVISORY_LOCKS
3848 cm_file_lock_t *fileLock;
3852 int substract_ranges = FALSE;
3854 range.offset = LOffset.QuadPart;
3855 range.length = LLength.QuadPart;
3859 1. for all _a_ in (Offset,+Length), all of the following is true:
3861 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3862 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3865 1.2 For each LOST lock L in S->fileLocks such that _a_ in
3866 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3871 lock_ObtainRead(&cm_scacheLock);
3873 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
3875 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
3877 if (INTERSECT_RANGE(range, fileLock->range)) {
3878 if (IS_LOCK_ACTIVE(fileLock)) {
3879 if (fileLock->key == key) {
3881 /* If there is an active lock for this client, it
3882 is safe to substract ranges.*/
3883 cm_LockRangeSubtract(&range, &fileLock->range);
3884 substract_ranges = TRUE;
3886 if (fileLock->lockType != LockRead) {
3887 code = CM_ERROR_LOCK_CONFLICT;
3891 /* even if the entire range is locked for reading,
3892 we still can't grant the lock at this point
3893 because the client may have lost locks. That
3894 is, unless we have already seen an active lock
3895 belonging to the client, in which case there
3896 can't be any lost locks for this client. */
3897 if (substract_ranges)
3898 cm_LockRangeSubtract(&range, &fileLock->range);
3900 } else if (IS_LOCK_LOST(fileLock) &&
3901 (fileLock->key == key || fileLock->lockType == LockWrite)) {
3902 code = CM_ERROR_BADFD;
3908 lock_ReleaseRead(&cm_scacheLock);
3910 osi_Log4(afsd_logp, "cm_LockCheckRead scp 0x%x offset %d length %d code 0x%x",
3911 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
3922 /* Called with scp->rw held. Returns 0 if all is clear to write the
3923 specified range by the client identified by key.
3925 long cm_LockCheckWrite(cm_scache_t *scp,
3926 LARGE_INTEGER LOffset,
3927 LARGE_INTEGER LLength,
3930 #ifndef ADVISORY_LOCKS
3932 cm_file_lock_t *fileLock;
3937 range.offset = LOffset.QuadPart;
3938 range.length = LLength.QuadPart;
3941 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
3943 2. for all _a_ in (Offset,+Length), one of the following is true:
3945 2.1 Byte _a_ of S is unowned AND there does not exist a LOST
3946 lock L such that _a_ in (L->LOffset,+L->LLength).
3948 2.2 Byte _a_ of S is owned by C under lock L AND L->LockType is
3952 lock_ObtainRead(&cm_scacheLock);
3954 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
3956 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
3958 if (INTERSECT_RANGE(range, fileLock->range)) {
3959 if (IS_LOCK_ACTIVE(fileLock)) {
3960 if (fileLock->key == key) {
3961 if (fileLock->lockType == LockWrite) {
3963 /* if there is an active lock for this client, it
3964 is safe to substract ranges */
3965 cm_LockRangeSubtract(&range, &fileLock->range);
3967 code = CM_ERROR_LOCK_CONFLICT;
3971 code = CM_ERROR_LOCK_CONFLICT;
3974 } else if (IS_LOCK_LOST(fileLock)) {
3975 code = CM_ERROR_BADFD;
3981 lock_ReleaseRead(&cm_scacheLock);
3983 osi_Log4(afsd_logp, "cm_LockCheckWrite scp 0x%x offset %d length %d code 0x%x",
3984 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
3995 /* Called with cm_scacheLock write locked */
3996 static cm_file_lock_t * cm_GetFileLock(void) {
3999 l = (cm_file_lock_t *) cm_freeFileLocks;
4001 osi_QRemove(&cm_freeFileLocks, &l->q);
4003 l = malloc(sizeof(cm_file_lock_t));
4004 osi_assertx(l, "null cm_file_lock_t");
4007 memset(l, 0, sizeof(cm_file_lock_t));
4012 /* Called with cm_scacheLock write locked */
4013 static void cm_PutFileLock(cm_file_lock_t *l) {
4014 osi_QAdd(&cm_freeFileLocks, &l->q);
4017 /* called with scp->rw held. May release it during processing, but
4018 leaves it held on exit. */
4019 long cm_IntSetLock(cm_scache_t * scp, cm_user_t * userp, int lockType,
4025 struct rx_connection * callp;
4027 afs_uint32 reqflags = reqp->flags;
4029 tfid.Volume = scp->fid.volume;
4030 tfid.Vnode = scp->fid.vnode;
4031 tfid.Unique = scp->fid.unique;
4034 osi_Log2(afsd_logp, "CALL SetLock scp 0x%p for lock %d", scp, lockType);
4036 reqp->flags |= CM_REQ_NORETRY;
4037 lock_ReleaseWrite(&scp->rw);
4040 code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4044 callp = cm_GetRxConn(connp);
4045 code = RXAFS_SetLock(callp, &tfid, lockType,
4047 rx_PutConnection(callp);
4049 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
4052 code = cm_MapRPCError(code, reqp);
4054 osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
4056 osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
4059 lock_ObtainWrite(&scp->rw);
4060 reqp->flags = reqflags;
4064 /* called with scp->rw held. Releases it during processing */
4065 long cm_IntReleaseLock(cm_scache_t * scp, cm_user_t * userp,
4071 struct rx_connection * callp;
4074 tfid.Volume = scp->fid.volume;
4075 tfid.Vnode = scp->fid.vnode;
4076 tfid.Unique = scp->fid.unique;
4079 lock_ReleaseWrite(&scp->rw);
4081 osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
4084 code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4088 callp = cm_GetRxConn(connp);
4089 code = RXAFS_ReleaseLock(callp, &tfid, &volSync);
4090 rx_PutConnection(callp);
4092 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
4094 code = cm_MapRPCError(code, reqp);
4097 "CALL ReleaseLock FAILURE, code 0x%x", code);
4100 "CALL ReleaseLock SUCCESS");
4102 lock_ObtainWrite(&scp->rw);
4107 /* called with scp->rw held. May release it during processing, but
4108 will exit with lock held.
4112 - 0 if the user has permission to get the specified lock for the scp
4114 - CM_ERROR_NOACCESS if not
4116 Any other error from cm_SyncOp will be sent down untranslated.
4118 If CM_ERROR_NOACCESS is returned and lock_type is LockRead, then
4119 phas_insert (if non-NULL) will receive a boolean value indicating
4120 whether the user has INSERT permission or not.
4122 long cm_LockCheckPerms(cm_scache_t * scp,
4129 long code = 0, code2 = 0;
4131 /* lock permissions are slightly tricky because of the 'i' bit.
4132 If the user has PRSFS_LOCK, she can read-lock the file. If the
4133 user has PRSFS_WRITE, she can write-lock the file. However, if
4134 the user has PRSFS_INSERT, then she can write-lock new files,
4135 but not old ones. Since we don't have information about
4136 whether a file is new or not, we assume that if the user owns
4137 the scp, then she has the permissions that are granted by
4140 osi_Log3(afsd_logp, "cm_LockCheckPerms for scp[0x%p] type[%d] user[0x%p]",
4141 scp, lock_type, userp);
4143 if (lock_type == LockRead)
4144 rights |= PRSFS_LOCK;
4145 else if (lock_type == LockWrite)
4146 rights |= PRSFS_WRITE | PRSFS_LOCK;
4149 osi_assertx(FALSE, "invalid lock type");
4154 *phas_insert = FALSE;
4156 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
4157 CM_SCACHESYNC_GETSTATUS |
4158 CM_SCACHESYNC_NEEDCALLBACK);
4160 if (phas_insert && scp->creator == userp) {
4162 /* If this file was created by the user, then we check for
4163 PRSFS_INSERT. If the file server is recent enough, then
4164 this should be sufficient for her to get a write-lock (but
4165 not necessarily a read-lock). VICED_CAPABILITY_WRITELOCKACL
4166 indicates whether a file server supports getting write
4167 locks when the user only has PRSFS_INSERT.
4169 If the file was not created by the user we skip the check
4170 because the INSERT bit will not apply to this user even
4174 code2 = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
4175 CM_SCACHESYNC_GETSTATUS |
4176 CM_SCACHESYNC_NEEDCALLBACK);
4178 if (code2 == CM_ERROR_NOACCESS) {
4179 osi_Log0(afsd_logp, "cm_LockCheckPerms user has no INSERT bits");
4181 *phas_insert = TRUE;
4182 osi_Log0(afsd_logp, "cm_LockCheckPerms user has INSERT bits");
4186 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
4188 osi_Log1(afsd_logp, "cm_LockCheckPerms returning code %d", code);
4193 /* called with scp->rw held */
4194 long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
4195 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
4197 int allowWait, cm_user_t *userp, cm_req_t *reqp,
4198 cm_file_lock_t **lockpp)
4201 int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
4202 cm_file_lock_t *fileLock;
4205 int wait_unlock = FALSE;
4206 int force_client_lock = FALSE;
4208 osi_Log4(afsd_logp, "cm_Lock scp 0x%x type 0x%x offset %d length %d",
4209 scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
4210 osi_Log3(afsd_logp, "... allowWait %d key 0x%x:%x", allowWait,
4211 (unsigned long)(key >> 32), (unsigned long)(key & 0xffffffff));
4214 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
4216 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
4219 3.1 If L->LockType is exclusive then there does NOT exist a
4220 ACCEPTED lock M in S->fileLocks such that _a_ in
4221 (M->LOffset,+M->LLength).
4223 3.2 If L->LockType is shared then for each ACCEPTED lock M in
4224 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
4225 M->LockType is shared.
4227 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
4229 4.1 M->key != Key(C)
4231 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
4232 and (M->LOffset,+M->LLength) do not intersect.
4235 range.offset = LOffset.QuadPart;
4236 range.length = LLength.QuadPart;
4238 lock_ObtainRead(&cm_scacheLock);
4240 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4242 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4244 if (IS_LOCK_LOST(fileLock)) {
4245 if (fileLock->key == key) {
4246 code = CM_ERROR_BADFD;
4248 } else if (fileLock->lockType == LockWrite && INTERSECT_RANGE(range, fileLock->range)) {
4249 code = CM_ERROR_WOULDBLOCK;
4255 /* we don't need to check for deleted locks here since deleted
4256 locks are dequeued from scp->fileLocks */
4257 if (IS_LOCK_ACCEPTED(fileLock) &&
4258 INTERSECT_RANGE(range, fileLock->range)) {
4260 if ((sLockType & LOCKING_ANDX_SHARED_LOCK) == 0 ||
4261 fileLock->lockType != LockRead) {
4263 code = CM_ERROR_WOULDBLOCK;
4269 lock_ReleaseRead(&cm_scacheLock);
4271 if (code == 0 && SERVERLOCKS_ENABLED(scp)) {
4272 if (Which == scp->serverLock ||
4273 (Which == LockRead && scp->serverLock == LockWrite)) {
4277 /* we already have the lock we need */
4278 osi_Log3(afsd_logp, " we already have the correct lock. exclusives[%d], shared[%d], serverLock[%d]",
4279 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4281 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4283 /* special case: if we don't have permission to read-lock
4284 the file, then we force a clientside lock. This is to
4285 compensate for applications that obtain a read-lock for
4286 reading files off of directories that don't grant
4287 read-locks to the user. */
4288 if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4290 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4291 osi_Log0(afsd_logp, " User has no read-lock perms, but has INSERT perms.");
4294 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4295 force_client_lock = TRUE;
4299 } else if ((scp->exclusiveLocks > 0) ||
4300 (scp->sharedLocks > 0 && scp->serverLock != LockRead)) {
4303 /* We are already waiting for some other lock. We should
4304 wait for the daemon to catch up instead of generating a
4305 flood of SetLock calls. */
4306 osi_Log3(afsd_logp, " already waiting for other lock. exclusives[%d], shared[%d], serverLock[%d]",
4307 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4309 /* see if we have permission to create the lock in the
4311 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4313 code = CM_ERROR_WOULDBLOCK;
4314 else if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4316 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4318 " User has no read-lock perms, but has INSERT perms.");
4319 code = CM_ERROR_WOULDBLOCK;
4322 " User has no read-lock perms. Forcing client-side lock");
4323 force_client_lock = TRUE;
4327 /* leave any other codes as-is */
4331 int check_data_version = FALSE;
4334 /* first check if we have permission to elevate or obtain
4336 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4338 if (code == CM_ERROR_NOACCESS && Which == LockRead &&
4339 (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp))) {
4340 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4341 force_client_lock = TRUE;
4346 /* has_insert => (Which == LockRead, code == CM_ERROR_NOACCESS) */
4348 if (scp->serverLock == LockRead && Which == LockWrite) {
4350 /* We want to escalate the lock to a LockWrite.
4351 * Unfortunately that's not really possible without
4352 * letting go of the current lock. But for now we do
4356 " attempting to UPGRADE from LockRead to LockWrite.");
4358 " dataVersion on scp: %I64d", scp->dataVersion);
4360 /* we assume at this point (because scp->serverLock
4361 was valid) that we had a valid server lock. */
4362 scp->lockDataVersion = scp->dataVersion;
4363 check_data_version = TRUE;
4365 code = cm_IntReleaseLock(scp, userp, reqp);
4368 /* We couldn't release the lock */
4371 scp->serverLock = -1;
4375 /* We need to obtain a server lock of type Which in order
4376 * to assert this file lock */
4377 #ifndef AGGRESSIVE_LOCKS
4380 newLock = LockWrite;
4383 code = cm_IntSetLock(scp, userp, newLock, reqp);
4385 #ifdef AGGRESSIVE_LOCKS
4386 if ((code == CM_ERROR_WOULDBLOCK ||
4387 code == CM_ERROR_NOACCESS) && newLock != Which) {
4388 /* we wanted LockRead. We tried LockWrite. Now try
4393 osi_assertx(newLock == LockRead, "lock type not read");
4395 code = cm_IntSetLock(scp, userp, newLock, reqp);
4399 if (code == CM_ERROR_NOACCESS) {
4400 if (Which == LockRead) {
4401 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4403 /* We requested a read-lock, but we have permission to
4404 * get a write-lock. Try that */
4406 tcode = cm_LockCheckPerms(scp, LockWrite, userp, reqp, NULL);
4409 newLock = LockWrite;
4411 osi_Log0(afsd_logp, " User has 'i' perms and the request was for a LockRead. Trying to get a LockWrite instead");
4413 code = cm_IntSetLock(scp, userp, newLock, reqp);
4416 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4417 force_client_lock = TRUE;
4419 } else if (Which == LockWrite &&
4420 scp->creator == userp && !SCP_SUPPORTS_WRITELOCKACL(scp)) {
4423 /* Special case: if the lock request was for a
4424 * LockWrite and the user owns the file and we weren't
4425 * allowed to obtain the serverlock, we either lost a
4426 * race (the permissions changed from under us), or we
4427 * have 'i' bits, but we aren't allowed to lock the
4430 /* check if we lost a race... */
4431 tcode = cm_LockCheckPerms(scp, Which, userp, reqp, NULL);
4434 osi_Log0(afsd_logp, " User has 'i' perms but can't obtain write locks. Using client-side locks.");
4435 force_client_lock = TRUE;
4440 if (code == 0 && check_data_version &&
4441 scp->dataVersion != scp->lockDataVersion) {
4442 /* We lost a race. Although we successfully obtained
4443 * a lock, someone modified the file in between. The
4444 * locks have all been technically lost. */
4447 " Data version mismatch while upgrading lock.");
4449 " Data versions before=%I64d, after=%I64d",
4450 scp->lockDataVersion,
4453 " Releasing stale lock for scp 0x%x", scp);
4455 code = cm_IntReleaseLock(scp, userp, reqp);
4457 scp->serverLock = -1;
4459 code = CM_ERROR_INVAL;
4460 } else if (code == 0) {
4461 scp->serverLock = newLock;
4462 scp->lockDataVersion = scp->dataVersion;
4466 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4467 scp->serverLock == -1) {
4468 /* Oops. We lost the lock. */
4469 cm_LockMarkSCacheLost(scp);
4472 } else if (code == 0) { /* server locks not enabled */
4474 " Skipping server lock for scp");
4479 if (code != 0 && !force_client_lock) {
4480 /* Special case error translations
4482 Applications don't expect certain errors from a
4483 LockFile/UnlockFile call. We need to translate some error
4484 code to codes that apps expect and handle. */
4486 /* We shouldn't actually need to handle this case since we
4487 simulate locks for RO scps anyway. */
4488 if (code == CM_ERROR_READONLY) {
4489 osi_Log0(afsd_logp, " Reinterpreting CM_ERROR_READONLY as CM_ERROR_NOACCESS");
4490 code = CM_ERROR_NOACCESS;
4494 if (code == 0 || (code == CM_ERROR_WOULDBLOCK && allowWait) ||
4495 force_client_lock) {
4497 /* clear the error if we are forcing a client lock, so we
4498 don't get confused later. */
4499 if (force_client_lock && code != CM_ERROR_WOULDBLOCK)
4502 lock_ObtainWrite(&cm_scacheLock);
4503 fileLock = cm_GetFileLock();
4504 lock_ReleaseWrite(&cm_scacheLock);
4506 fileLock->fid = scp->fid;
4508 fileLock->key = key;
4509 fileLock->lockType = Which;
4511 fileLock->userp = userp;
4512 fileLock->range = range;
4513 fileLock->flags = (code == 0 ? 0 :
4515 CM_FILELOCK_FLAG_WAITUNLOCK :
4516 CM_FILELOCK_FLAG_WAITLOCK));
4518 if (force_client_lock || !SERVERLOCKS_ENABLED(scp))
4519 fileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
4521 fileLock->lastUpdate = (code == 0 && !force_client_lock) ? time(NULL) : 0;
4523 lock_ObtainWrite(&cm_scacheLock);
4524 osi_QAddT(&scp->fileLocksH, &scp->fileLocksT, &fileLock->fileq);
4525 cm_HoldSCacheNoLock(scp);
4526 fileLock->scp = scp;
4527 osi_QAdd(&cm_allFileLocks, &fileLock->q);
4528 lock_ReleaseWrite(&cm_scacheLock);
4534 if (IS_LOCK_CLIENTONLY(fileLock)) {
4536 } else if (IS_LOCK_ACCEPTED(fileLock)) {
4537 if (Which == LockRead)
4540 scp->exclusiveLocks++;
4544 "cm_Lock Lock added 0x%p flags 0x%x to scp [0x%p]",
4545 fileLock, fileLock->flags, scp);
4547 " exclusives[%d] shared[%d] client[%d] serverLock[%d]",
4548 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4549 (int)(signed char) scp->serverLock);
4552 "cm_Lock Rejecting lock (code = 0x%x)", code);
4558 static int cm_KeyEquals(cm_key_t k1, cm_key_t k2, int flags);
4560 /* Called with scp->rw held */
4561 long cm_UnlockByKey(cm_scache_t * scp,
4568 cm_file_lock_t *fileLock;
4569 osi_queue_t *q, *qn;
4572 osi_Log4(afsd_logp, "cm_UnlockByKey scp 0x%p key 0x%x:%x flags=0x%x",
4574 (unsigned long)(key >> 32),
4575 (unsigned long)(key & 0xffffffff),
4578 lock_ObtainWrite(&cm_scacheLock);
4580 for (q = scp->fileLocksH; q; q = qn) {
4583 fileLock = (cm_file_lock_t *)
4584 ((char *) q - offsetof(cm_file_lock_t, fileq));
4587 osi_Log4(afsd_logp, " Checking lock[0x%x] range[%d,+%d] type[%d]",
4589 (unsigned long) fileLock->range.offset,
4590 (unsigned long) fileLock->range.length,
4591 fileLock->lockType);
4592 osi_Log3(afsd_logp, " key[0x%x:%x] flags[0x%x]",
4593 (unsigned long)(fileLock->key >> 32),
4594 (unsigned long)(fileLock->key & 0xffffffff),
4597 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
4598 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
4599 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4601 fileLock->fid.volume,
4602 fileLock->fid.vnode,
4603 fileLock->fid.unique);
4604 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4605 fileLock->scp->fid.cell,
4606 fileLock->scp->fid.volume,
4607 fileLock->scp->fid.vnode,
4608 fileLock->scp->fid.unique);
4609 osi_assertx(FALSE, "invalid fid value");
4613 if (!IS_LOCK_DELETED(fileLock) &&
4614 cm_KeyEquals(fileLock->key, key, flags)) {
4615 osi_Log3(afsd_logp, "...Unlock range [%d,+%d] type %d",
4616 fileLock->range.offset,
4617 fileLock->range.length,
4618 fileLock->lockType);
4620 if (scp->fileLocksT == q)
4621 scp->fileLocksT = osi_QPrev(q);
4622 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
4624 if (IS_LOCK_CLIENTONLY(fileLock)) {
4626 } else if (IS_LOCK_ACCEPTED(fileLock)) {
4627 if (fileLock->lockType == LockRead)
4630 scp->exclusiveLocks--;
4633 fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
4635 cm_ReleaseUser(fileLock->userp);
4636 cm_ReleaseSCacheNoLock(scp);
4638 fileLock->userp = NULL;
4639 fileLock->scp = NULL;
4645 lock_ReleaseWrite(&cm_scacheLock);
4647 if (n_unlocks == 0) {
4648 osi_Log0(afsd_logp, "cm_UnlockByKey no locks found");
4649 osi_Log3(afsd_logp, " Leaving scp with exclusives[%d], shared[%d], serverLock[%d]",
4650 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4655 osi_Log1(afsd_logp, "cm_UnlockByKey done with %d locks", n_unlocks);
4657 osi_assertx(scp->sharedLocks >= 0, "scp->sharedLocks < 0");
4658 osi_assertx(scp->exclusiveLocks >= 0, "scp->exclusiveLocks < 0");
4659 osi_assertx(scp->clientLocks >= 0, "scp->clientLocks < 0");
4661 if (!SERVERLOCKS_ENABLED(scp)) {
4662 osi_Log0(afsd_logp, " Skipping server lock for scp");
4666 /* Ideally we would go through the rest of the locks to determine
4667 * if one or more locks that were formerly in WAITUNLOCK can now
4668 * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
4669 * scp->sharedLocks accordingly. However, the retrying of locks
4670 * in that manner is done cm_RetryLock() manually.
4673 if (scp->serverLock == LockWrite &&
4674 scp->exclusiveLocks == 0 &&
4675 scp->sharedLocks > 0) {
4677 /* The serverLock should be downgraded to LockRead */
4678 osi_Log0(afsd_logp, " DOWNGRADE lock from LockWrite to LockRead");
4680 /* since scp->serverLock looked sane, we are going to assume
4681 that we have a valid server lock. */
4682 scp->lockDataVersion = scp->dataVersion;
4683 osi_Log1(afsd_logp, " dataVersion on scp = %I64d", scp->dataVersion);
4685 code = cm_IntReleaseLock(scp, userp, reqp);
4688 /* so we couldn't release it. Just let the lock be for now */
4692 scp->serverLock = -1;
4695 code = cm_IntSetLock(scp, userp, LockRead, reqp);
4697 if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
4698 scp->serverLock = LockRead;
4699 } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
4700 /* We lost a race condition. Although we have a valid
4701 lock on the file, the data has changed and essentially
4702 we have lost the lock we had during the transition. */
4704 osi_Log0(afsd_logp, "Data version mismatch during lock downgrade");
4705 osi_Log2(afsd_logp, " Data versions before=%I64d, after=%I64d",
4706 scp->lockDataVersion,
4709 code = cm_IntReleaseLock(scp, userp, reqp);
4711 code = CM_ERROR_INVAL;
4712 scp->serverLock = -1;
4716 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4717 (scp->serverLock == -1)) {
4719 cm_LockMarkSCacheLost(scp);
4722 /* failure here has no bearing on the return value of
4726 } else if (scp->serverLock != (-1) &&
4727 scp->exclusiveLocks == 0 &&
4728 scp->sharedLocks == 0) {
4729 /* The serverLock should be released entirely */
4731 code = cm_IntReleaseLock(scp, userp, reqp);
4734 scp->serverLock = (-1);
4739 osi_Log1(afsd_logp, "cm_UnlockByKey code 0x%x", code);
4740 osi_Log4(afsd_logp, " Leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
4741 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4742 (int)(signed char) scp->serverLock);
4747 long cm_Unlock(cm_scache_t *scp,
4748 unsigned char sLockType,
4749 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
4755 int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
4756 cm_file_lock_t *fileLock;
4758 int release_userp = FALSE;
4760 osi_Log4(afsd_logp, "cm_Unlock scp 0x%p type 0x%x offset %d length %d",
4761 scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
4762 osi_Log2(afsd_logp, "... key 0x%x:%x",
4763 (unsigned long) (key >> 32), (unsigned long) (key & 0xffffffff));
4765 lock_ObtainRead(&cm_scacheLock);
4767 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4768 fileLock = (cm_file_lock_t *)
4769 ((char *) q - offsetof(cm_file_lock_t, fileq));
4772 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
4773 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
4774 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4776 fileLock->fid.volume,
4777 fileLock->fid.vnode,
4778 fileLock->fid.unique);
4779 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4780 fileLock->scp->fid.cell,
4781 fileLock->scp->fid.volume,
4782 fileLock->scp->fid.vnode,
4783 fileLock->scp->fid.unique);
4784 osi_assertx(FALSE, "invalid fid value");
4787 if (!IS_LOCK_DELETED(fileLock) &&
4788 fileLock->key == key &&
4789 fileLock->range.offset == LOffset.QuadPart &&
4790 fileLock->range.length == LLength.QuadPart) {
4796 osi_Log0(afsd_logp, "cm_Unlock lock not found; failure");
4798 lock_ReleaseRead(&cm_scacheLock);
4800 /* The lock didn't exist anyway. *shrug* */
4801 return CM_ERROR_RANGE_NOT_LOCKED;
4804 /* discard lock record */
4805 lock_ConvertRToW(&cm_scacheLock);
4806 if (scp->fileLocksT == q)
4807 scp->fileLocksT = osi_QPrev(q);
4808 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
4811 * Don't delete it here; let the daemon delete it, to simplify
4812 * the daemon's traversal of the list.
4815 if (IS_LOCK_CLIENTONLY(fileLock)) {
4817 } else if (IS_LOCK_ACCEPTED(fileLock)) {
4818 if (fileLock->lockType == LockRead)
4821 scp->exclusiveLocks--;
4824 fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
4825 if (userp != NULL) {
4826 cm_ReleaseUser(fileLock->userp);
4828 userp = fileLock->userp;
4829 release_userp = TRUE;
4831 fileLock->userp = NULL;
4832 cm_ReleaseSCacheNoLock(scp);
4833 fileLock->scp = NULL;
4834 lock_ReleaseWrite(&cm_scacheLock);
4836 if (!SERVERLOCKS_ENABLED(scp)) {
4837 osi_Log0(afsd_logp, " Skipping server locks for scp");
4841 /* Ideally we would go through the rest of the locks to determine
4842 * if one or more locks that were formerly in WAITUNLOCK can now
4843 * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
4844 * scp->sharedLocks accordingly. However, the retrying of locks
4845 * in that manner is done cm_RetryLock() manually.
4848 if (scp->serverLock == LockWrite &&
4849 scp->exclusiveLocks == 0 &&
4850 scp->sharedLocks > 0) {
4852 /* The serverLock should be downgraded to LockRead */
4853 osi_Log0(afsd_logp, " DOWNGRADE lock from LockWrite to LockRead");
4855 /* Since we already had a lock, we assume that there is a
4856 valid server lock. */
4857 scp->lockDataVersion = scp->dataVersion;
4858 osi_Log1(afsd_logp, " dataVersion on scp is %I64d", scp->dataVersion);
4860 /* before we downgrade, make sure that we have enough
4861 permissions to get the read lock. */
4862 code = cm_LockCheckPerms(scp, LockRead, userp, reqp, NULL);
4865 osi_Log0(afsd_logp, " SKIPPING downgrade because user doesn't have perms to get downgraded lock");
4871 code = cm_IntReleaseLock(scp, userp, reqp);
4874 /* so we couldn't release it. Just let the lock be for now */
4878 scp->serverLock = -1;
4881 code = cm_IntSetLock(scp, userp, LockRead, reqp);
4883 if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
4884 scp->serverLock = LockRead;
4885 } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
4886 /* Lost a race. We obtained a new lock, but that is
4887 meaningless since someone modified the file
4891 "Data version mismatch while downgrading lock");
4893 " Data versions before=%I64d, after=%I64d",
4894 scp->lockDataVersion,
4897 code = cm_IntReleaseLock(scp, userp, reqp);
4899 scp->serverLock = -1;
4900 code = CM_ERROR_INVAL;
4904 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4905 (scp->serverLock == -1)) {
4907 cm_LockMarkSCacheLost(scp);
4910 /* failure here has no bearing on the return value of
4914 } else if (scp->serverLock != (-1) &&
4915 scp->exclusiveLocks == 0 &&
4916 scp->sharedLocks == 0) {
4917 /* The serverLock should be released entirely */
4919 code = cm_IntReleaseLock(scp, userp, reqp);
4922 scp->serverLock = (-1);
4927 cm_ReleaseUser(userp);
4931 osi_Log1(afsd_logp, "cm_Unlock code 0x%x", code);
4932 osi_Log4(afsd_logp, " leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
4933 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4934 (int)(signed char) scp->serverLock);
4939 /* called with scp->rw held */
4940 void cm_LockMarkSCacheLost(cm_scache_t * scp)
4942 cm_file_lock_t *fileLock;
4945 osi_Log1(afsd_logp, "cm_LockMarkSCacheLost scp 0x%x", scp);
4947 /* cm_scacheLock needed because we are modifying fileLock->flags */
4948 lock_ObtainWrite(&cm_scacheLock);
4950 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4952 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4954 if (IS_LOCK_ACTIVE(fileLock) &&
4955 !IS_LOCK_CLIENTONLY(fileLock)) {
4956 if (fileLock->lockType == LockRead)
4959 scp->exclusiveLocks--;
4961 fileLock->flags |= CM_FILELOCK_FLAG_LOST;
4965 scp->serverLock = -1;
4966 scp->lockDataVersion = -1;
4967 lock_ReleaseWrite(&cm_scacheLock);
4970 /* Called with no relevant locks held */
4971 void cm_CheckLocks()
4973 osi_queue_t *q, *nq;
4974 cm_file_lock_t *fileLock;
4980 struct rx_connection * callp;
4985 lock_ObtainWrite(&cm_scacheLock);
4987 cm_lockRefreshCycle++;
4989 osi_Log1(afsd_logp, "cm_CheckLocks starting lock check cycle %d", cm_lockRefreshCycle);
4991 for (q = cm_allFileLocks; q; q = nq) {
4992 fileLock = (cm_file_lock_t *) q;
4996 if (IS_LOCK_DELETED(fileLock)) {
4998 osi_QRemove(&cm_allFileLocks, q);
4999 cm_PutFileLock(fileLock);
5001 } else if (IS_LOCK_ACTIVE(fileLock) && !IS_LOCK_CLIENTONLY(fileLock)) {
5003 /* Server locks must have been enabled for us to have
5004 received an active non-client-only lock. */
5005 osi_assertx(cm_enableServerLocks, "!cm_enableServerLocks");
5007 scp = fileLock->scp;
5008 osi_assertx(scp != NULL, "null cm_scache_t");
5010 cm_HoldSCacheNoLock(scp);
5013 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5014 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5015 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5017 fileLock->fid.volume,
5018 fileLock->fid.vnode,
5019 fileLock->fid.unique);
5020 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5021 fileLock->scp->fid.cell,
5022 fileLock->scp->fid.volume,
5023 fileLock->scp->fid.vnode,
5024 fileLock->scp->fid.unique);
5025 osi_assertx(FALSE, "invalid fid");
5028 /* Server locks are extended once per scp per refresh
5030 if (scp->lastRefreshCycle != cm_lockRefreshCycle) {
5032 int scp_done = FALSE;
5034 osi_Log1(afsd_logp, "cm_CheckLocks Updating scp 0x%x", scp);
5036 lock_ReleaseWrite(&cm_scacheLock);
5037 lock_ObtainWrite(&scp->rw);
5039 /* did the lock change while we weren't holding the lock? */
5040 if (!IS_LOCK_ACTIVE(fileLock))
5041 goto post_syncopdone;
5043 code = cm_SyncOp(scp, NULL, fileLock->userp, &req, 0,
5044 CM_SCACHESYNC_NEEDCALLBACK
5045 | CM_SCACHESYNC_GETSTATUS
5046 | CM_SCACHESYNC_LOCK);
5050 "cm_CheckLocks SyncOp failure code 0x%x", code);
5051 goto post_syncopdone;
5054 /* cm_SyncOp releases scp->rw during which the lock
5055 may get released. */
5056 if (!IS_LOCK_ACTIVE(fileLock))
5057 goto pre_syncopdone;
5059 if (scp->serverLock != -1 && !(scp->flags & CM_SCACHEFLAG_DELETED)) {
5063 tfid.Volume = scp->fid.volume;
5064 tfid.Vnode = scp->fid.vnode;
5065 tfid.Unique = scp->fid.unique;
5067 userp = fileLock->userp;
5069 osi_Log3(afsd_logp, "CALL ExtendLock lock 0x%p for scp=0x%p with lock %d",
5072 (int) scp->serverLock);
5074 lock_ReleaseWrite(&scp->rw);
5077 code = cm_ConnFromFID(&cfid, userp,
5082 callp = cm_GetRxConn(connp);
5083 code = RXAFS_ExtendLock(callp, &tfid,
5085 rx_PutConnection(callp);
5087 osi_Log1(afsd_logp, " ExtendLock returns %d", code);
5089 } while (cm_Analyze(connp, userp, &req,
5090 &cfid, &volSync, NULL, NULL,
5093 code = cm_MapRPCError(code, &req);
5095 lock_ObtainWrite(&scp->rw);
5098 osi_Log1(afsd_logp, "CALL ExtendLock FAILURE, code 0x%x", code);
5100 osi_Log0(afsd_logp, "CALL ExtendLock SUCCESS");
5101 scp->lockDataVersion = scp->dataVersion;
5104 if ((code == EINVAL || code == CM_ERROR_INVAL) &&
5105 scp->lockDataVersion == scp->dataVersion) {
5109 (scp->exclusiveLocks > 0) ? LockWrite: LockRead;
5111 /* we might still have a chance to obtain a
5114 code = cm_IntSetLock(scp, userp, lockType, &req);
5117 code = CM_ERROR_INVAL;
5118 } else if (scp->lockDataVersion != scp->dataVersion) {
5120 /* now check if we still have the file at
5121 the right data version. */
5123 "Data version mismatch on scp 0x%p",
5126 " Data versions: before=%I64d, after=%I64d",
5127 scp->lockDataVersion,
5130 code = cm_IntReleaseLock(scp, userp, &req);
5132 code = CM_ERROR_INVAL;
5136 if (code == EINVAL || code == CM_ERROR_INVAL ||
5137 code == CM_ERROR_BADFD) {
5138 cm_LockMarkSCacheLost(scp);
5142 /* interestingly, we have found an active lock
5143 belonging to an scache that has no
5145 cm_LockMarkSCacheLost(scp);
5152 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5155 lock_ReleaseWrite(&scp->rw);
5157 lock_ObtainWrite(&cm_scacheLock);
5160 fileLock->lastUpdate = time(NULL);
5164 scp->lastRefreshCycle = cm_lockRefreshCycle;
5167 /* we have already refreshed the locks on this scp */
5168 fileLock->lastUpdate = time(NULL);
5171 cm_ReleaseSCacheNoLock(scp);
5173 } else if (IS_LOCK_ACTIVE(fileLock) && IS_LOCK_CLIENTONLY(fileLock)) {
5174 /* TODO: Check callbacks */
5178 lock_ReleaseWrite(&cm_scacheLock);
5179 osi_Log1(afsd_logp, "cm_CheckLocks completes lock check cycle %d", cm_lockRefreshCycle);
5182 /* NOT called with scp->rw held. */
5183 long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
5186 cm_scache_t *scp = NULL;
5187 cm_file_lock_t *fileLock;
5191 int force_client_lock = FALSE;
5192 int has_insert = FALSE;
5193 int check_data_version = FALSE;
5197 if (client_is_dead) {
5198 code = CM_ERROR_TIMEDOUT;
5202 lock_ObtainRead(&cm_scacheLock);
5204 osi_Log2(afsd_logp, "cm_RetryLock checking lock %p (scp=%p)", oldFileLock, oldFileLock->scp);
5205 osi_Log4(afsd_logp, " offset(%x:%x) length(%x:%x)",
5206 (unsigned)(oldFileLock->range.offset >> 32),
5207 (unsigned)(oldFileLock->range.offset & 0xffffffff),
5208 (unsigned)(oldFileLock->range.length >> 32),
5209 (unsigned)(oldFileLock->range.length & 0xffffffff));
5210 osi_Log3(afsd_logp, " key(%x:%x) flags=%x",
5211 (unsigned)(oldFileLock->key >> 32),
5212 (unsigned)(oldFileLock->key & 0xffffffff),
5213 (unsigned)(oldFileLock->flags));
5215 /* if the lock has already been granted, then we have nothing to do */
5216 if (IS_LOCK_ACTIVE(oldFileLock)) {
5217 lock_ReleaseRead(&cm_scacheLock);
5218 osi_Log0(afsd_logp, "cm_RetryLock lock already granted");
5222 /* we can't do anything with lost or deleted locks at the moment. */
5223 if (IS_LOCK_LOST(oldFileLock) || IS_LOCK_DELETED(oldFileLock)) {
5224 code = CM_ERROR_BADFD;
5225 osi_Log0(afsd_logp, "cm_RetryLock lock is lost or deleted");
5226 lock_ReleaseRead(&cm_scacheLock);
5230 scp = oldFileLock->scp;
5232 osi_assertx(scp != NULL, "null cm_scache_t");
5234 lock_ReleaseRead(&cm_scacheLock);
5235 lock_ObtainWrite(&scp->rw);
5237 code = cm_LockCheckPerms(scp, oldFileLock->lockType,
5241 if (code == CM_ERROR_NOACCESS && oldFileLock->lockType == LockRead) {
5242 if (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp)) {
5243 force_client_lock = TRUE;
5247 lock_ReleaseWrite(&scp->rw);
5251 lock_ObtainWrite(&cm_scacheLock);
5253 /* Check if we already have a sufficient server lock to allow this
5254 lock to go through. */
5255 if (IS_LOCK_WAITLOCK(oldFileLock) &&
5256 (!SERVERLOCKS_ENABLED(scp) ||
5257 scp->serverLock == oldFileLock->lockType ||
5258 scp->serverLock == LockWrite)) {
5260 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5262 if (SERVERLOCKS_ENABLED(scp)) {
5263 osi_Log1(afsd_logp, "cm_RetryLock Server lock (%d) is sufficient for lock. Granting",
5264 (int) scp->serverLock);
5266 osi_Log0(afsd_logp, "cm_RetryLock skipping server lock for scp");
5269 lock_ReleaseWrite(&cm_scacheLock);
5270 lock_ReleaseWrite(&scp->rw);
5275 if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5277 /* check if the conflicting locks have dissappeared already */
5278 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5280 fileLock = (cm_file_lock_t *)
5281 ((char *) q - offsetof(cm_file_lock_t, fileq));
5283 if (IS_LOCK_LOST(fileLock)) {
5284 if (fileLock->key == oldFileLock->key) {
5285 code = CM_ERROR_BADFD;
5286 oldFileLock->flags |= CM_FILELOCK_FLAG_LOST;
5287 osi_Log1(afsd_logp, " found lost lock %p for same key. Marking lock as lost",
5290 } else if (fileLock->lockType == LockWrite &&
5291 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5292 osi_Log1(afsd_logp, " found conflicting LOST lock %p", fileLock);
5293 code = CM_ERROR_WOULDBLOCK;
5298 if (IS_LOCK_ACCEPTED(fileLock) &&
5299 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5301 if (oldFileLock->lockType != LockRead ||
5302 fileLock->lockType != LockRead) {
5304 osi_Log1(afsd_logp, " found conflicting lock %p", fileLock);
5305 code = CM_ERROR_WOULDBLOCK;
5313 lock_ReleaseWrite(&cm_scacheLock);
5314 lock_ReleaseWrite(&scp->rw);
5319 /* when we get here, the lock is either a WAITUNLOCK or WAITLOCK.
5320 If it is WAITUNLOCK, then we didn't find any conflicting lock
5321 but we haven't verfied whether the serverLock is sufficient to
5322 assert it. If it is WAITLOCK, then the serverLock is
5323 insufficient to assert it. Eitherway, we are ready to accept
5324 the lock as either ACTIVE or WAITLOCK depending on the
5327 /* First, promote the WAITUNLOCK to a WAITLOCK */
5328 if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5329 if (oldFileLock->lockType == LockRead)
5332 scp->exclusiveLocks++;
5334 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITUNLOCK;
5335 oldFileLock->flags |= CM_FILELOCK_FLAG_WAITLOCK;
5338 osi_assertx(IS_LOCK_WAITLOCK(oldFileLock), "!IS_LOCK_WAITLOCK");
5340 if (force_client_lock ||
5341 !SERVERLOCKS_ENABLED(scp) ||
5342 scp->serverLock == oldFileLock->lockType ||
5343 (oldFileLock->lockType == LockRead &&
5344 scp->serverLock == LockWrite)) {
5346 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5348 if ((force_client_lock ||
5349 !SERVERLOCKS_ENABLED(scp)) &&
5350 !IS_LOCK_CLIENTONLY(oldFileLock)) {
5352 oldFileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
5354 if (oldFileLock->lockType == LockRead)
5357 scp->exclusiveLocks--;
5362 lock_ReleaseWrite(&cm_scacheLock);
5363 lock_ReleaseWrite(&scp->rw);
5370 code = cm_SyncOp(scp, NULL, oldFileLock->userp, &req, 0,
5371 CM_SCACHESYNC_NEEDCALLBACK
5372 | CM_SCACHESYNC_GETSTATUS
5373 | CM_SCACHESYNC_LOCK);
5375 osi_Log1(afsd_logp, "cm_RetryLock SyncOp failure code 0x%x", code);
5376 lock_ReleaseWrite(&cm_scacheLock);
5377 goto post_syncopdone;
5380 if (!IS_LOCK_WAITLOCK(oldFileLock))
5381 goto pre_syncopdone;
5383 userp = oldFileLock->userp;
5385 #ifndef AGGRESSIVE_LOCKS
5386 newLock = oldFileLock->lockType;
5388 newLock = LockWrite;
5392 /* if has_insert is non-zero, then:
5393 - the lock a LockRead
5394 - we don't have permission to get a LockRead
5395 - we do have permission to get a LockWrite
5396 - the server supports VICED_CAPABILITY_WRITELOCKACL
5399 newLock = LockWrite;
5402 lock_ReleaseWrite(&cm_scacheLock);
5404 /* when we get here, either we have a read-lock and want a
5405 write-lock or we don't have any locks and we want some
5408 if (scp->serverLock == LockRead) {
5410 osi_assertx(newLock == LockWrite, "!LockWrite");
5412 osi_Log0(afsd_logp, " Attempting to UPGRADE from LockRead to LockWrite");
5414 scp->lockDataVersion = scp->dataVersion;
5415 check_data_version = TRUE;
5417 code = cm_IntReleaseLock(scp, userp, &req);
5420 goto pre_syncopdone;
5422 scp->serverLock = -1;
5425 code = cm_IntSetLock(scp, userp, newLock, &req);
5428 if (scp->dataVersion != scp->lockDataVersion) {
5429 /* we lost a race. too bad */
5432 " Data version mismatch while upgrading lock.");
5434 " Data versions before=%I64d, after=%I64d",
5435 scp->lockDataVersion,
5438 " Releasing stale lock for scp 0x%x", scp);
5440 code = cm_IntReleaseLock(scp, userp, &req);
5442 scp->serverLock = -1;
5444 code = CM_ERROR_INVAL;
5446 cm_LockMarkSCacheLost(scp);
5448 scp->serverLock = newLock;
5453 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5459 if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
5460 lock_ObtainWrite(&cm_scacheLock);
5461 if (scp->fileLocksT == &oldFileLock->fileq)
5462 scp->fileLocksT = osi_QPrev(&oldFileLock->fileq);
5463 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, &oldFileLock->fileq);
5464 lock_ReleaseWrite(&cm_scacheLock);
5466 lock_ReleaseWrite(&scp->rw);
5469 lock_ObtainWrite(&cm_scacheLock);
5471 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5472 } else if (code != CM_ERROR_WOULDBLOCK) {
5473 oldFileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5474 cm_ReleaseUser(oldFileLock->userp);
5475 oldFileLock->userp = NULL;
5476 if (oldFileLock->scp) {
5477 cm_ReleaseSCacheNoLock(oldFileLock->scp);
5478 oldFileLock->scp = NULL;
5481 lock_ReleaseWrite(&cm_scacheLock);
5486 cm_key_t cm_GenerateKey(unsigned int session_id, unsigned long process_id, unsigned int file_id)
5489 osi_assertx((process_id & 0xffffffff) == process_id, "unexpected process_id");
5490 osi_assertx((session_id & 0xffff) == session_id, "unexpected session_id");
5491 osi_assertx((file_id & 0xffff) == file_id, "unexpected file_id");
5495 (((cm_key_t) (process_id & 0xffffffff)) << 32) |
5496 (((cm_key_t) (session_id & 0xffff)) << 16) |
5497 (((cm_key_t) (file_id & 0xffff)));
5500 static int cm_KeyEquals(cm_key_t k1, cm_key_t k2, int flags)
5502 if (flags & CM_UNLOCK_BY_FID) {
5503 return ((k1 & 0xffffffff) == (k2 & 0xffffffff));
5509 void cm_ReleaseAllLocks(void)
5515 cm_file_lock_t *fileLock;
5518 for (i = 0; i < cm_data.scacheHashTableSize; i++)
5520 for ( scp = cm_data.scacheHashTablep[i]; scp; scp = scp->nextp ) {
5521 while (scp->fileLocksH != NULL) {
5522 lock_ObtainWrite(&scp->rw);
5523 lock_ObtainWrite(&cm_scacheLock);
5524 if (!scp->fileLocksH) {
5525 lock_ReleaseWrite(&cm_scacheLock);
5526 lock_ReleaseWrite(&scp->rw);
5529 fileLock = (cm_file_lock_t *)((char *) scp->fileLocksH - offsetof(cm_file_lock_t, fileq));
5530 userp = fileLock->userp;
5532 key = fileLock->key;
5533 cm_HoldSCacheNoLock(scp);
5534 lock_ReleaseWrite(&cm_scacheLock);
5535 cm_UnlockByKey(scp, key, 0, userp, &req);
5536 cm_ReleaseSCache(scp);
5537 cm_ReleaseUser(userp);
5538 lock_ReleaseWrite(&scp->rw);