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>
25 /* Used by cm_FollowMountPoint */
31 extern void afsi_log(char *pattern, ...);
34 int cm_enableServerLocks = 1;
37 * Case-folding array. This was constructed by inspecting of SMBtrace output.
38 * I do not know anything more about it.
40 unsigned char cm_foldUpper[256] = {
41 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
42 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
43 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
44 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
45 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
46 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
47 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
48 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
49 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
50 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
51 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
52 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
53 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
54 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
55 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
56 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
57 0x80, 0x9a, 0x90, 0x41, 0x8e, 0x41, 0x8f, 0x80,
58 0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x8e, 0x8f,
59 0x90, 0x92, 0x92, 0x4f, 0x99, 0x4f, 0x55, 0x55,
60 0x59, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
61 0x41, 0x49, 0x4f, 0x55, 0xa5, 0xa5, 0x56, 0xa7,
62 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
63 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
64 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
65 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
66 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
67 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
68 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
69 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
70 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
71 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
72 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
76 * Case-insensitive string comparison. We used to use stricmp, but it doesn't
77 * know about 8-bit characters (e.g. 129 is lowercase u-umlaut, 154 is
78 * upper-case u-umlaut).
80 int cm_stricmp(const char *str1, const char *str2)
92 c1 = (char) cm_foldUpper[(unsigned char)(*str1++)];
93 c2 = (char) cm_foldUpper[(unsigned char)(*str2++)];
101 /* characters that are legal in an 8.3 name */
103 * We used to have 1's for all characters from 128 to 254. But
104 * the NT client behaves better if we create an 8.3 name for any
105 * name that has a character with the high bit on, and if we
106 * delete those characters from 8.3 names. In particular, see
107 * Sybase defect 10859.
109 char cm_LegalChars[256] = {
110 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
111 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
112 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0,
113 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
114 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
115 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1,
116 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
117 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1,
118 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
119 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
120 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
121 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
122 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
123 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
124 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
125 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
128 /* return true iff component is a valid 8.3 name */
129 int cm_Is8Dot3(char *namep)
136 * can't have a leading dot;
137 * special case for . and ..
139 if (namep[0] == '.') {
142 if (namep[1] == '.' && namep[2] == 0)
146 while (tc = *namep++) {
148 /* saw another dot */
149 if (sawDot) return 0; /* second dot */
154 if (cm_LegalChars[tc] == 0)
157 if (!sawDot && charCount > 8)
158 /* more than 8 chars in name */
160 if (sawDot && charCount > 3)
161 /* more than 3 chars in extension */
168 * Number unparsing map for generating 8.3 names;
169 * The version taken from DFS was on drugs.
170 * You can't include '&' and '@' in a file name.
172 char cm_8Dot3Mapping[42] =
173 {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
174 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K',
175 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U',
176 'V', 'W', 'X', 'Y', 'Z', '_', '-', '$', '#', '!', '+', '='
178 int cm_8Dot3MapSize = sizeof(cm_8Dot3Mapping);
180 void cm_Gen8Dot3NameInt(const char * longname, cm_dirFid_t * pfid,
181 char *shortName, char **shortNameEndp)
185 int vnode = ntohl(pfid->vnode);
187 int validExtension = 0;
191 /* Unparse the file's vnode number to get a "uniquifier" */
193 number[nsize] = cm_8Dot3Mapping[vnode % cm_8Dot3MapSize];
195 vnode /= cm_8Dot3MapSize;
199 * Look for valid extension. There has to be a dot, and
200 * at least one of the characters following has to be legal.
202 lastDot = strrchr(longname, '.');
204 temp = lastDot; temp++;
206 if (cm_LegalChars[tc])
212 /* Copy name characters */
213 for (i = 0, name = longname;
214 i < (7 - nsize) && name != lastDot; ) {
219 if (!cm_LegalChars[tc])
222 *shortName++ = toupper(tc);
228 /* Copy uniquifier characters */
229 memcpy(shortName, number, nsize);
232 if (validExtension) {
233 /* Copy extension characters */
234 *shortName++ = *lastDot++; /* copy dot */
235 for (i = 0, tc = *lastDot++;
238 if (cm_LegalChars[tc]) {
240 *shortName++ = toupper(tc);
249 *shortNameEndp = shortName;
252 /* return success if we can open this file in this mode */
253 long cm_CheckOpen(cm_scache_t *scp, int openMode, int trunc, cm_user_t *userp,
261 rights |= PRSFS_READ;
262 if (openMode == 1 || openMode == 2 || trunc)
263 rights |= PRSFS_WRITE;
265 lock_ObtainMutex(&scp->mx);
267 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
268 CM_SCACHESYNC_GETSTATUS
269 | CM_SCACHESYNC_NEEDCALLBACK
270 | CM_SCACHESYNC_LOCK);
273 ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
274 scp->fileType == CM_SCACHETYPE_FILE) {
277 unsigned int sLockType;
278 LARGE_INTEGER LOffset, LLength;
280 /* Check if there's some sort of lock on the file at the
283 key = cm_GenerateKey(CM_SESSION_CMINT,0,0);
285 if (rights & PRSFS_WRITE)
288 sLockType = LOCKING_ANDX_SHARED_LOCK;
290 LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
291 LOffset.LowPart = CM_FLSHARE_OFFSET_LOW;
292 LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
293 LLength.LowPart = CM_FLSHARE_LENGTH_LOW;
295 code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
298 cm_Unlock(scp, sLockType, LOffset, LLength, key, userp, reqp);
300 /* In this case, we allow the file open to go through even
301 though we can't enforce mandatory locking on the
303 if (code == CM_ERROR_NOACCESS &&
304 !(rights & PRSFS_WRITE))
308 case CM_ERROR_ALLOFFLINE:
309 case CM_ERROR_ALLDOWN:
310 case CM_ERROR_ALLBUSY:
311 case CM_ERROR_TIMEDOUT:
313 case CM_ERROR_WOULDBLOCK:
316 code = CM_ERROR_SHARING_VIOLATION;
321 } else if (code != 0) {
325 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
329 lock_ReleaseMutex(&scp->mx);
334 /* return success if we can open this file in this mode */
335 long cm_CheckNTOpen(cm_scache_t *scp, unsigned int desiredAccess,
336 unsigned int createDisp, cm_user_t *userp, cm_req_t *reqp,
337 cm_lock_data_t **ldpp)
342 osi_assert(ldpp != NULL);
345 /* Always allow delete; the RPC will tell us if it's OK */
346 if (desiredAccess == DELETE)
351 if (desiredAccess & AFS_ACCESS_READ)
352 rights |= (scp->fileType == CM_SCACHETYPE_DIRECTORY ? PRSFS_LOOKUP : PRSFS_READ);
354 /* We used to require PRSFS_WRITE if createDisp was 4
355 (OPEN_ALWAYS) even if AFS_ACCESS_WRITE was not requested.
356 However, we don't need to do that since the existence of the
357 scp implies that we don't need to create it. */
358 if (desiredAccess & AFS_ACCESS_WRITE)
359 rights |= PRSFS_WRITE;
361 lock_ObtainMutex(&scp->mx);
363 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
364 CM_SCACHESYNC_GETSTATUS
365 | CM_SCACHESYNC_NEEDCALLBACK
366 | CM_SCACHESYNC_LOCK);
369 * If the open will fail because the volume is readonly, then we will
370 * return an access denied error instead. This is to help brain-dead
371 * apps run correctly on replicated volumes.
372 * See defect 10007 for more information.
374 if (code == CM_ERROR_READONLY)
375 code = CM_ERROR_NOACCESS;
378 ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
379 scp->fileType == CM_SCACHETYPE_FILE) {
381 unsigned int sLockType;
382 LARGE_INTEGER LOffset, LLength;
384 /* Check if there's some sort of lock on the file at the
387 key = cm_GenerateKey(CM_SESSION_CMINT,0,0);
388 if (rights & PRSFS_WRITE)
391 sLockType = LOCKING_ANDX_SHARED_LOCK;
393 /* single byte lock at offset 0x0100 0000 0000 0000 */
394 LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
395 LOffset.LowPart = CM_FLSHARE_OFFSET_LOW;
396 LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
397 LLength.LowPart = CM_FLSHARE_LENGTH_LOW;
399 code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
402 (*ldpp) = (cm_lock_data_t *)malloc(sizeof(cm_lock_data_t));
409 (*ldpp)->sLockType = sLockType;
410 (*ldpp)->LOffset.HighPart = LOffset.HighPart;
411 (*ldpp)->LOffset.LowPart = LOffset.LowPart;
412 (*ldpp)->LLength.HighPart = LLength.HighPart;
413 (*ldpp)->LLength.LowPart = LLength.LowPart;
415 /* In this case, we allow the file open to go through even
416 though we can't enforce mandatory locking on the
418 if (code == CM_ERROR_NOACCESS &&
419 !(rights & PRSFS_WRITE))
423 case CM_ERROR_ALLOFFLINE:
424 case CM_ERROR_ALLDOWN:
425 case CM_ERROR_ALLBUSY:
426 case CM_ERROR_TIMEDOUT:
428 case CM_ERROR_WOULDBLOCK:
431 code = CM_ERROR_SHARING_VIOLATION;
435 } else if (code != 0) {
439 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
442 lock_ReleaseMutex(&scp->mx);
447 extern long cm_CheckNTOpenDone(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp,
448 cm_lock_data_t ** ldpp)
451 lock_ObtainMutex(&scp->mx);
452 cm_Unlock(scp, (*ldpp)->sLockType, (*ldpp)->LOffset, (*ldpp)->LLength,
453 (*ldpp)->key, userp, reqp);
454 lock_ReleaseMutex(&scp->mx);
461 * When CAP_NT_SMBS has been negotiated, deletion (of files or directories) is
462 * done in three steps:
463 * (1) open for deletion (NT_CREATE_AND_X)
464 * (2) set for deletion on close (NT_TRANSACTION2, SET_FILE_INFO)
466 * We must not do the RPC until step 3. But if we are going to return an error
467 * code (e.g. directory not empty), we must return it by step 2, otherwise most
468 * clients will not notice it. So we do a preliminary check. For deleting
469 * files, this is almost free, since we have already done the RPC to get the
470 * parent directory's status bits. But for deleting directories, we must do an
471 * additional RPC to get the directory's data to check if it is empty. Sigh.
473 long cm_CheckNTDelete(cm_scache_t *dscp, cm_scache_t *scp, cm_user_t *userp,
480 unsigned short *hashTable;
482 int BeyondPage = 0, HaveDot = 0, HaveDotDot = 0;
484 /* First check permissions */
485 lock_ObtainMutex(&scp->mx);
486 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_DELETE,
487 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
488 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
489 lock_ReleaseMutex(&scp->mx);
493 /* If deleting directory, must be empty */
495 if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
498 thyper.HighPart = 0; thyper.LowPart = 0;
499 lock_ObtainRead(&scp->bufCreateLock);
500 code = buf_Get(scp, &thyper, &bufferp);
501 lock_ReleaseRead(&scp->bufCreateLock);
505 lock_ObtainMutex(&bufferp->mx);
506 lock_ObtainMutex(&scp->mx);
508 code = cm_SyncOp(scp, bufferp, userp, reqp, 0,
509 CM_SCACHESYNC_NEEDCALLBACK
511 | CM_SCACHESYNC_BUFLOCKED);
515 if (cm_HaveBuffer(scp, bufferp, 1))
518 /* otherwise, load the buffer and try again */
519 lock_ReleaseMutex(&bufferp->mx);
520 code = cm_GetBuffer(scp, bufferp, NULL, userp, reqp);
521 lock_ReleaseMutex(&scp->mx);
522 lock_ObtainMutex(&bufferp->mx);
523 lock_ObtainMutex(&scp->mx);
524 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
529 /* We try to determine emptiness without looking beyond the first page,
530 * and without assuming "." and ".." are present and are on the first
531 * page (though these assumptions might, after all, be reasonable).
533 hashTable = (unsigned short *)(bufferp->datap + (32 * 5));
534 for (i=0; i<128; i++) {
535 idx = ntohs(hashTable[i]);
541 dep = (cm_dirEntry_t *)(bufferp->datap + (32 * idx));
542 if (strcmp(dep->name, ".") == 0)
544 else if (strcmp(dep->name, "..") == 0)
547 code = CM_ERROR_NOTEMPTY;
550 idx = ntohs(dep->next);
553 if (BeyondPage && HaveDot && HaveDotDot)
554 code = CM_ERROR_NOTEMPTY;
558 lock_ReleaseMutex(&bufferp->mx);
559 buf_Release(bufferp);
560 lock_ReleaseMutex(&scp->mx);
565 * Iterate through all entries in a directory.
566 * When the function funcp is called, the buffer is locked but the
567 * directory vnode is not.
569 * If the retscp parameter is not NULL, the parmp must be a
570 * cm_lookupSearch_t object.
572 long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
573 osi_hyper_t *startOffsetp, cm_user_t *userp, cm_req_t *reqp,
574 cm_scache_t **retscp)
581 osi_hyper_t dirLength;
582 osi_hyper_t bufferOffset;
583 osi_hyper_t curOffset;
587 cm_pageHeader_t *pageHeaderp;
589 long nextEntryCookie;
590 int numDirChunks; /* # of 32 byte dir chunks in this entry */
592 /* get the directory size */
593 lock_ObtainMutex(&scp->mx);
594 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_LOOKUP,
595 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
597 lock_ReleaseMutex(&scp->mx);
601 if (scp->fileType != CM_SCACHETYPE_DIRECTORY) {
602 lock_ReleaseMutex(&scp->mx);
603 return CM_ERROR_NOTDIR;
606 if (retscp) /* if this is a lookup call */
608 cm_lookupSearch_t* sp = parmp;
610 #ifdef AFS_FREELANCE_CLIENT
611 /* Freelance entries never end up in the DNLC because they
612 * do not have an associated cm_server_t
614 if ( !(cm_freelanceEnabled &&
615 sp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
616 sp->fid.volume==AFS_FAKE_ROOT_VOL_ID ) )
617 #endif /* AFS_FREELANCE_CLIENT */
619 int casefold = sp->caseFold;
620 sp->caseFold = 0; /* we have a strong preference for exact matches */
621 if ( *retscp = cm_dnlcLookup(scp, sp)) /* dnlc hit */
623 sp->caseFold = casefold;
624 lock_ReleaseMutex(&scp->mx);
627 sp->caseFold = casefold;
632 * XXX We only get the length once. It might change when we drop the
635 dirLength = scp->length;
637 lock_ReleaseMutex(&scp->mx);
640 bufferOffset.LowPart = bufferOffset.HighPart = 0;
642 curOffset = *startOffsetp;
644 curOffset.HighPart = 0;
645 curOffset.LowPart = 0;
649 /* make sure that curOffset.LowPart doesn't point to the first
650 * 32 bytes in the 2nd through last dir page, and that it
651 * doesn't point at the first 13 32-byte chunks in the first
652 * dir page, since those are dir and page headers, and don't
653 * contain useful information.
655 temp = curOffset.LowPart & (2048-1);
656 if (curOffset.HighPart == 0 && curOffset.LowPart < 2048) {
657 /* we're in the first page */
658 if (temp < 13*32) temp = 13*32;
661 /* we're in a later dir page */
662 if (temp < 32) temp = 32;
665 /* make sure the low order 5 bits are zero */
668 /* now put temp bits back ito curOffset.LowPart */
669 curOffset.LowPart &= ~(2048-1);
670 curOffset.LowPart |= temp;
672 /* check if we've passed the dir's EOF */
673 if (LargeIntegerGreaterThanOrEqualTo(curOffset, dirLength))
676 /* see if we can use the bufferp we have now; compute in which
677 * page the current offset would be, and check whether that's
678 * the offset of the buffer we have. If not, get the buffer.
680 thyper.HighPart = curOffset.HighPart;
681 thyper.LowPart = curOffset.LowPart & ~(cm_data.buf_blockSize-1);
682 if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
685 lock_ReleaseMutex(&bufferp->mx);
686 buf_Release(bufferp);
690 lock_ObtainRead(&scp->bufCreateLock);
691 code = buf_Get(scp, &thyper, &bufferp);
692 lock_ReleaseRead(&scp->bufCreateLock);
694 /* if buf_Get() fails we do not have a buffer object to lock */
700 /* for the IFS version, we bulkstat the dirents because this
701 routine is used in place of smb_ReceiveCoreSearchDir. our
702 other option is to modify smb_ReceiveCoreSearchDir itself,
703 but this seems to be the proper use for cm_ApplyDir. */
704 lock_ObtainMutex(&scp->mx);
705 if ((scp->flags & CM_SCACHEFLAG_BULKSTATTING) == 0
706 && (scp->bulkStatProgress.QuadPart <= thyper.QuadPart))
708 scp->flags |= CM_SCACHEFLAG_BULKSTATTING;
709 code = cm_TryBulkStat(scp, &thyper, userp, reqp);
710 scp->flags &= ~CM_SCACHEFLAG_BULKSTATTING;
711 scp->bulkStatProgress = thyper;
713 lock_ReleaseMutex(&scp->mx);
716 lock_ObtainMutex(&bufferp->mx);
717 bufferOffset = thyper;
719 /* now get the data in the cache */
721 lock_ObtainMutex(&scp->mx);
722 code = cm_SyncOp(scp, bufferp, userp, reqp,
724 CM_SCACHESYNC_NEEDCALLBACK
726 | CM_SCACHESYNC_BUFLOCKED);
728 lock_ReleaseMutex(&scp->mx);
731 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
733 if (cm_HaveBuffer(scp, bufferp, 1)) {
734 lock_ReleaseMutex(&scp->mx);
738 /* otherwise, load the buffer and try again */
739 lock_ReleaseMutex(&bufferp->mx);
740 code = cm_GetBuffer(scp, bufferp, NULL, userp,
742 lock_ReleaseMutex(&scp->mx);
743 lock_ObtainMutex(&bufferp->mx);
748 lock_ReleaseMutex(&bufferp->mx);
749 buf_Release(bufferp);
753 } /* if (wrong buffer) ... */
755 /* now we have the buffer containing the entry we're interested
756 * in; copy it out if it represents a non-deleted entry.
758 entryInDir = curOffset.LowPart & (2048-1);
759 entryInBuffer = curOffset.LowPart & (cm_data.buf_blockSize - 1);
761 /* page header will help tell us which entries are free. Page
762 * header can change more often than once per buffer, since
763 * AFS 3 dir page size may be less than (but not more than) a
764 * buffer package buffer.
766 /* only look intra-buffer */
767 temp = curOffset.LowPart & (cm_data.buf_blockSize - 1);
768 temp &= ~(2048 - 1); /* turn off intra-page bits */
769 pageHeaderp = (cm_pageHeader_t *) (bufferp->datap + temp);
771 /* now determine which entry we're looking at in the page. If
772 * it is free (there's a free bitmap at the start of the dir),
773 * we should skip these 32 bytes.
775 slotInPage = (entryInDir & 0x7e0) >> 5;
776 if (!(pageHeaderp->freeBitmap[slotInPage>>3]
777 & (1 << (slotInPage & 0x7)))) {
778 /* this entry is free */
779 numDirChunks = 1; /* only skip this guy */
783 tp = bufferp->datap + entryInBuffer;
784 dep = (cm_dirEntry_t *) tp; /* now points to AFS3 dir entry */
786 /* while we're here, compute the next entry's location, too,
787 * since we'll need it when writing out the cookie into the
788 * dir listing stream.
790 numDirChunks = cm_NameEntries(dep->name, NULL);
792 /* compute the offset of the cookie representing the next entry */
793 nextEntryCookie = curOffset.LowPart
794 + (CM_DIR_CHUNKSIZE * numDirChunks);
796 if (dep->fid.vnode != 0) {
797 /* this is one of the entries to use: it is not deleted */
798 code = (*funcp)(scp, dep, parmp, &curOffset);
801 } /* if we're including this name */
804 /* and adjust curOffset to be where the new cookie is */
806 thyper.LowPart = CM_DIR_CHUNKSIZE * numDirChunks;
807 curOffset = LargeIntegerAdd(thyper, curOffset);
808 } /* while copying data for dir listing */
810 /* release the mutex */
812 lock_ReleaseMutex(&bufferp->mx);
813 buf_Release(bufferp);
818 int cm_NoneUpper(char *s)
822 if (c >= 'A' && c <= 'Z')
827 int cm_NoneLower(char *s)
831 if (c >= 'a' && c <= 'z')
836 long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
839 cm_lookupSearch_t *sp;
844 sp = (cm_lookupSearch_t *) rockp;
846 matchName = dep->name;
848 match = cm_stricmp(matchName, sp->searchNamep);
850 match = strcmp(matchName, sp->searchNamep);
854 && !cm_Is8Dot3(dep->name)) {
855 matchName = shortName;
856 cm_Gen8Dot3Name(dep, shortName, NULL);
858 match = cm_stricmp(matchName, sp->searchNamep);
860 match = strcmp(matchName, sp->searchNamep);
870 if (!sp->caseFold || matchName == shortName) {
871 sp->fid.vnode = ntohl(dep->fid.vnode);
872 sp->fid.unique = ntohl(dep->fid.unique);
873 return CM_ERROR_STOPNOW;
877 * If we get here, we are doing a case-insensitive search, and we
878 * have found a match. Now we determine what kind of match it is:
879 * exact, lower-case, upper-case, or none of the above. This is done
880 * in order to choose among matches, if there are more than one.
883 /* Exact matches are the best. */
884 match = strcmp(matchName, sp->searchNamep);
887 sp->fid.vnode = ntohl(dep->fid.vnode);
888 sp->fid.unique = ntohl(dep->fid.unique);
889 return CM_ERROR_STOPNOW;
892 /* Lower-case matches are next. */
895 if (cm_NoneUpper(matchName)) {
900 /* Upper-case matches are next. */
903 if (cm_NoneLower(matchName)) {
908 /* General matches are last. */
914 sp->fid.vnode = ntohl(dep->fid.vnode);
915 sp->fid.unique = ntohl(dep->fid.unique);
919 /* read the contents of a mount point into the appropriate string.
920 * called with locked scp, and returns with locked scp.
922 long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
929 if (scp->mountPointStringp[0])
932 /* otherwise, we have to read it in */
933 lock_ReleaseMutex(&scp->mx);
935 lock_ObtainRead(&scp->bufCreateLock);
936 thyper.LowPart = thyper.HighPart = 0;
937 code = buf_Get(scp, &thyper, &bufp);
938 lock_ReleaseRead(&scp->bufCreateLock);
940 lock_ObtainMutex(&scp->mx);
945 code = cm_SyncOp(scp, bufp, userp, reqp, 0,
946 CM_SCACHESYNC_READ | CM_SCACHESYNC_NEEDCALLBACK);
950 cm_SyncOpDone(scp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
953 if (cm_HaveBuffer(scp, bufp, 0))
956 /* otherwise load buffer */
957 code = cm_GetBuffer(scp, bufp, NULL, userp, reqp);
961 /* locked, has callback, has valid data in buffer */
962 if ((tlen = scp->length.LowPart) > 1000)
963 return CM_ERROR_TOOBIG;
965 code = CM_ERROR_INVAL;
969 /* someone else did the work while we were out */
970 if (scp->mountPointStringp[0]) {
975 /* otherwise, copy out the link */
976 memcpy(scp->mountPointStringp, bufp->datap, tlen);
978 /* now make it null-terminated. Note that the original contents of a
979 * link that is a mount point is "#volname." where "." is there just to
980 * be turned into a null. That is, we can trash the last char of the
981 * link without damaging the vol name. This is a stupid convention,
982 * but that's the protocol.
984 scp->mountPointStringp[tlen-1] = 0;
993 /* called with a locked scp and chases the mount point, yielding outScpp.
994 * scp remains locked, just for simplicity of describing the interface.
996 long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
997 cm_req_t *reqp, cm_scache_t **outScpp)
1005 cm_volume_t *volp = NULL;
1012 if (scp->mountRootFid.cell != 0 && scp->mountRootGen >= cm_data.mountRootGen) {
1013 tfid = scp->mountRootFid;
1014 lock_ReleaseMutex(&scp->mx);
1015 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
1016 lock_ObtainMutex(&scp->mx);
1020 /* parse the volume name */
1021 mpNamep = scp->mountPointStringp;
1023 return CM_ERROR_NOSUCHPATH;
1024 tlen = (int)strlen(scp->mountPointStringp);
1025 mtType = *scp->mountPointStringp;
1026 cellNamep = malloc(tlen);
1027 volNamep = malloc(tlen);
1029 cp = strrchr(mpNamep, ':');
1031 /* cellular mount point */
1032 memset(cellNamep, 0, tlen);
1033 strncpy(cellNamep, mpNamep+1, cp - mpNamep - 1);
1034 strcpy(volNamep, cp+1);
1035 /* now look up the cell */
1036 cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE);
1040 strcpy(volNamep, mpNamep+1);
1042 cellp = cm_FindCellByID(scp->fid.cell);
1046 code = CM_ERROR_NOSUCHCELL;
1050 vnLength = strlen(volNamep);
1051 if (vnLength >= 8 && strcmp(volNamep + vnLength - 7, ".backup") == 0)
1053 else if (vnLength >= 10
1054 && strcmp(volNamep + vnLength - 9, ".readonly") == 0)
1059 /* check for backups within backups */
1061 && (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
1062 == CM_SCACHEFLAG_RO) {
1063 code = CM_ERROR_NOSUCHVOLUME;
1067 /* now we need to get the volume */
1068 lock_ReleaseMutex(&scp->mx);
1069 code = cm_GetVolumeByName(cellp, volNamep, userp, reqp, 0, &volp);
1070 lock_ObtainMutex(&scp->mx);
1073 /* save the parent of the volume root for this is the
1074 * place where the volume is mounted and we must remember
1075 * this in the volume structure rather than just in the
1076 * scache entry lest the scache entry gets recycled
1079 lock_ObtainMutex(&volp->mx);
1080 volp->dotdotFid = dscp->fid;
1081 lock_ReleaseMutex(&volp->mx);
1083 scp->mountRootFid.cell = cellp->cellID;
1084 /* if the mt pt is in a read-only volume (not just a
1085 * backup), and if there is a read-only volume for the
1086 * target, and if this is a type '#' mount point, use
1087 * the read-only, otherwise use the one specified.
1089 if (mtType == '#' && (scp->flags & CM_SCACHEFLAG_PURERO)
1090 && volp->roID != 0 && type == RWVOL)
1093 scp->mountRootFid.volume = volp->roID;
1094 else if (type == BACKVOL)
1095 scp->mountRootFid.volume = volp->bkID;
1097 scp->mountRootFid.volume = volp->rwID;
1099 /* the rest of the fid is a magic number */
1100 scp->mountRootFid.vnode = 1;
1101 scp->mountRootFid.unique = 1;
1102 scp->mountRootGen = cm_data.mountRootGen;
1104 tfid = scp->mountRootFid;
1105 lock_ReleaseMutex(&scp->mx);
1106 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
1107 lock_ObtainMutex(&scp->mx);
1118 long cm_LookupInternal(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
1119 cm_req_t *reqp, cm_scache_t **outpScpp)
1122 int dnlcHit = 1; /* did we hit in the dnlc? yes, we did */
1123 cm_scache_t *tscp = NULL;
1124 cm_scache_t *mountedScp;
1125 cm_lookupSearch_t rock;
1128 if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
1129 && strcmp(namep, "..") == 0) {
1130 if (dscp->dotdotFid.volume == 0)
1131 return CM_ERROR_NOSUCHVOLUME;
1132 rock.fid = dscp->dotdotFid;
1134 } else if (strcmp(namep, ".") == 0) {
1135 rock.fid = dscp->fid;
1139 memset(&rock, 0, sizeof(rock));
1140 rock.fid.cell = dscp->fid.cell;
1141 rock.fid.volume = dscp->fid.volume;
1142 rock.searchNamep = namep;
1143 rock.caseFold = (flags & CM_FLAG_CASEFOLD);
1144 rock.hasTilde = ((strchr(namep, '~') != NULL) ? 1 : 0);
1146 /* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
1147 code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
1148 (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
1150 /* code == 0 means we fell off the end of the dir, while stopnow means
1151 * that we stopped early, probably because we found the entry we're
1152 * looking for. Any other non-zero code is an error.
1154 if (code && code != CM_ERROR_STOPNOW) {
1155 /* if the cm_scache_t we are searching in is not a directory
1156 * we must return path not found because the error
1157 * is to describe the final component not an intermediary
1159 if (code == CM_ERROR_NOTDIR) {
1160 if (flags & CM_FLAG_CHECKPATH)
1161 return CM_ERROR_NOSUCHPATH;
1163 return CM_ERROR_NOSUCHFILE;
1168 getroot = (dscp==cm_data.rootSCachep) ;
1170 if (!cm_freelanceEnabled || !getroot) {
1171 if (flags & CM_FLAG_CHECKPATH)
1172 return CM_ERROR_NOSUCHPATH;
1174 return CM_ERROR_NOSUCHFILE;
1176 else { /* nonexistent dir on freelance root, so add it */
1177 char fullname[200] = ".";
1180 osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %s",
1181 osi_LogSaveString(afsd_logp,namep));
1182 if (namep[0] == '.') {
1183 if (cm_GetCell_Gen(&namep[1], &fullname[1], CM_FLAG_CREATE)) {
1185 if ( stricmp(&namep[1], &fullname[1]) )
1186 code = cm_FreelanceAddSymlink(namep, fullname, &rock.fid);
1188 code = cm_FreelanceAddMount(namep, &fullname[1], "root.cell.", 1, &rock.fid);
1191 if (cm_GetCell_Gen(namep, fullname, CM_FLAG_CREATE)) {
1193 if ( stricmp(namep, fullname) )
1194 code = cm_FreelanceAddSymlink(namep, fullname, &rock.fid);
1196 code = cm_FreelanceAddMount(namep, fullname, "root.cell.", 0, &rock.fid);
1199 if (!found || code < 0) { /* add mount point failed, so give up */
1200 if (flags & CM_FLAG_CHECKPATH)
1201 return CM_ERROR_NOSUCHPATH;
1203 return CM_ERROR_NOSUCHFILE;
1205 tscp = NULL; /* to force call of cm_GetSCache */
1210 if ( !tscp ) /* we did not find it in the dnlc */
1213 code = cm_GetSCache(&rock.fid, &tscp, userp, reqp);
1217 /* tscp is now held */
1219 lock_ObtainMutex(&tscp->mx);
1220 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1221 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1223 lock_ReleaseMutex(&tscp->mx);
1224 cm_ReleaseSCache(tscp);
1227 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1228 /* tscp is now locked */
1230 if (!(flags & CM_FLAG_NOMOUNTCHASE)
1231 && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
1232 /* mount points are funny: they have a volume name to mount
1235 code = cm_ReadMountPoint(tscp, userp, reqp);
1237 code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
1239 lock_ReleaseMutex(&tscp->mx);
1240 cm_ReleaseSCache(tscp);
1247 lock_ReleaseMutex(&tscp->mx);
1250 /* copy back pointer */
1253 /* insert scache in dnlc */
1254 if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
1255 /* lock the directory entry to prevent racing callback revokes */
1256 lock_ObtainMutex(&dscp->mx);
1257 if ( dscp->cbServerp != NULL && dscp->cbExpires > 0 )
1258 cm_dnlcEnter(dscp, namep, tscp);
1259 lock_ReleaseMutex(&dscp->mx);
1266 int cm_ExpandSysName(char *inp, char *outp, long outSize, unsigned int index)
1271 tp = strrchr(inp, '@');
1273 return 0; /* no @sys */
1275 if (strcmp(tp, "@sys") != 0)
1276 return 0; /* no @sys */
1278 /* caller just wants to know if this is a valid @sys type of name */
1282 if (index >= MAXNUMSYSNAMES)
1285 /* otherwise generate the properly expanded @sys name */
1286 prefixCount = (int)(tp - inp);
1288 strncpy(outp, inp, prefixCount); /* copy out "a." from "a.@sys" */
1289 outp[prefixCount] = 0; /* null terminate the "a." */
1290 strcat(outp, cm_sysNameList[index]);/* append i386_nt40 */
1294 #ifdef DEBUG_REFCOUNT
1295 long cm_LookupDbg(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
1296 cm_req_t *reqp, cm_scache_t **outpScpp, char * file, long line)
1298 long cm_Lookup(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
1299 cm_req_t *reqp, cm_scache_t **outpScpp)
1304 int sysNameIndex = 0;
1305 cm_scache_t *scp = NULL;
1307 #ifdef DEBUG_REFCOUNT
1308 afsi_log("%s:%d cm_Lookup dscp 0x%p ref %d", file, line, dscp, dscp->refCount, file, line);
1309 osi_Log2(afsd_logp, "cm_Lookup dscp 0x%p ref %d", dscp, dscp->refCount);
1312 if ( stricmp(namep,SMB_IOCTL_FILENAME_NOSLASH) == 0 ) {
1313 if (flags & CM_FLAG_CHECKPATH)
1314 return CM_ERROR_NOSUCHPATH;
1316 return CM_ERROR_NOSUCHFILE;
1319 for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
1320 code = cm_ExpandSysName(namep, tname, sizeof(tname), sysNameIndex);
1322 code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
1323 #ifdef DEBUG_REFCOUNT
1324 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);
1325 osi_Log3(afsd_logp, "cm_LookupInternal (1) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1333 cm_ReleaseSCache(scp);
1337 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1338 #ifdef DEBUG_REFCOUNT
1339 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);
1340 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1347 /* None of the possible sysName expansions could be found */
1348 if (flags & CM_FLAG_CHECKPATH)
1349 return CM_ERROR_NOSUCHPATH;
1351 return CM_ERROR_NOSUCHFILE;
1354 long cm_Unlink(cm_scache_t *dscp, char *namep, cm_user_t *userp, cm_req_t *reqp)
1360 AFSFetchStatus newDirStatus;
1362 struct rx_connection * callp;
1364 #ifdef AFS_FREELANCE_CLIENT
1365 if (cm_freelanceEnabled && dscp == cm_data.rootSCachep) {
1366 /* deleting a mount point from the root dir. */
1367 code = cm_FreelanceRemoveMount(namep);
1372 /* make sure we don't screw up the dir status during the merge */
1373 lock_ObtainMutex(&dscp->mx);
1374 sflags = CM_SCACHESYNC_STOREDATA;
1375 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1376 lock_ReleaseMutex(&dscp->mx);
1381 afsFid.Volume = dscp->fid.volume;
1382 afsFid.Vnode = dscp->fid.vnode;
1383 afsFid.Unique = dscp->fid.unique;
1385 osi_Log1(afsd_logp, "CALL RemoveFile scp 0x%p", dscp);
1387 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1391 callp = cm_GetRxConn(connp);
1392 code = RXAFS_RemoveFile(callp, &afsFid, namep,
1393 &newDirStatus, &volSync);
1394 rx_PutConnection(callp);
1396 } while (cm_Analyze(connp, userp, reqp, &dscp->fid, &volSync, NULL, NULL, code));
1397 code = cm_MapRPCError(code, reqp);
1400 osi_Log1(afsd_logp, "CALL RemoveFile FAILURE, code 0x%x", code);
1402 osi_Log0(afsd_logp, "CALL RemoveFile SUCCESS");
1404 lock_ObtainMutex(&dscp->mx);
1405 cm_dnlcRemove(dscp, namep);
1406 cm_SyncOpDone(dscp, NULL, sflags);
1408 cm_MergeStatus(NULL, dscp, &newDirStatus, &volSync, userp, 0);
1409 else if (code == CM_ERROR_NOSUCHFILE) {
1410 /* windows would not have allowed the request to delete the file
1411 * if it did not believe the file existed. therefore, we must
1412 * have an inconsistent view of the world.
1414 dscp->cbServerp = NULL;
1416 lock_ReleaseMutex(&dscp->mx);
1421 /* called with a locked vnode, and fills in the link info.
1422 * returns this the vnode still locked.
1424 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1431 lock_AssertMutex(&linkScp->mx);
1432 if (!linkScp->mountPointStringp[0]) {
1433 /* read the link data */
1434 lock_ReleaseMutex(&linkScp->mx);
1435 thyper.LowPart = thyper.HighPart = 0;
1436 code = buf_Get(linkScp, &thyper, &bufp);
1437 lock_ObtainMutex(&linkScp->mx);
1441 code = cm_SyncOp(linkScp, bufp, userp, reqp, 0,
1442 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1447 cm_SyncOpDone(linkScp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1449 if (cm_HaveBuffer(linkScp, bufp, 0))
1452 code = cm_GetBuffer(linkScp, bufp, NULL, userp, reqp);
1457 } /* while loop to get the data */
1459 /* now if we still have no link read in,
1460 * copy the data from the buffer */
1461 if ((temp = linkScp->length.LowPart) >= MOUNTPOINTLEN) {
1463 return CM_ERROR_TOOBIG;
1466 /* otherwise, it fits; make sure it is still null (could have
1467 * lost race with someone else referencing this link above),
1468 * and if so, copy in the data.
1470 if (!linkScp->mountPointStringp[0]) {
1471 strncpy(linkScp->mountPointStringp, bufp->datap, temp);
1472 linkScp->mountPointStringp[temp] = 0; /* null terminate */
1475 } /* don't have sym link contents cached */
1480 /* called with a held vnode and a path suffix, with the held vnode being a
1481 * symbolic link. Our goal is to generate a new path to interpret, and return
1482 * this new path in newSpaceBufferp. If the new vnode is relative to a dir
1483 * other than the directory containing the symbolic link, then the new root is
1484 * returned in *newRootScpp, otherwise a null is returned there.
1486 long cm_AssembleLink(cm_scache_t *linkScp, char *pathSuffixp,
1487 cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1488 cm_user_t *userp, cm_req_t *reqp)
1495 lock_ObtainMutex(&linkScp->mx);
1496 code = cm_HandleLink(linkScp, userp, reqp);
1500 /* if we may overflow the buffer, bail out; buffer is signficantly
1501 * bigger than max path length, so we don't really have to worry about
1502 * being a little conservative here.
1504 if (strlen(linkScp->mountPointStringp) + strlen(pathSuffixp) + 2
1505 >= CM_UTILS_SPACESIZE)
1506 return CM_ERROR_TOOBIG;
1508 tsp = cm_GetSpace();
1509 linkp = linkScp->mountPointStringp;
1510 if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1511 if (strlen(linkp) > cm_mountRootLen)
1512 strcpy(tsp->data, linkp+cm_mountRootLen+1);
1515 *newRootScpp = cm_data.rootSCachep;
1516 cm_HoldSCache(cm_data.rootSCachep);
1517 } else if (linkp[0] == '\\' && linkp[1] == '\\') {
1518 if (!strnicmp(&linkp[2], cm_NetbiosName, (len = (long)strlen(cm_NetbiosName))))
1520 char * p = &linkp[len + 3];
1521 if (strnicmp(p, "all", 3) == 0)
1524 strcpy(tsp->data, p);
1525 for (p = tsp->data; *p; p++) {
1529 *newRootScpp = cm_data.rootSCachep;
1530 cm_HoldSCache(cm_data.rootSCachep);
1532 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1533 strcpy(tsp->data, linkp);
1534 *newRootScpp = NULL;
1535 code = CM_ERROR_PATH_NOT_COVERED;
1537 } else if ( !strnicmp(linkp, "msdfs:", (len = (long)strlen("msdfs:"))) ) {
1538 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1539 strcpy(tsp->data, linkp);
1540 *newRootScpp = NULL;
1541 code = CM_ERROR_PATH_NOT_COVERED;
1542 } else if (*linkp == '\\' || *linkp == '/') {
1544 /* formerly, this was considered to be from the AFS root,
1545 * but this seems to create problems. instead, we will just
1546 * reject the link */
1547 strcpy(tsp->data, linkp+1);
1548 *newRootScpp = cm_data.rootSCachep;
1549 cm_HoldSCache(cm_data.rootSCachep);
1551 /* we still copy the link data into the response so that
1552 * the user can see what the link points to
1554 linkScp->fileType = CM_SCACHETYPE_INVALID;
1555 strcpy(tsp->data, linkp);
1556 *newRootScpp = NULL;
1557 code = CM_ERROR_NOSUCHPATH;
1560 /* a relative link */
1561 strcpy(tsp->data, linkp);
1562 *newRootScpp = NULL;
1564 if (pathSuffixp[0] != 0) { /* if suffix string is non-null */
1565 strcat(tsp->data, "\\");
1566 strcat(tsp->data, pathSuffixp);
1568 *newSpaceBufferp = tsp;
1571 lock_ReleaseMutex(&linkScp->mx);
1574 #ifdef DEBUG_REFCOUNT
1575 long cm_NameIDbg(cm_scache_t *rootSCachep, char *pathp, long flags,
1576 cm_user_t *userp, char *tidPathp, cm_req_t *reqp, cm_scache_t **outScpp,
1577 char * file, long line)
1579 long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
1580 cm_user_t *userp, char *tidPathp, cm_req_t *reqp, cm_scache_t **outScpp)
1584 char *tp; /* ptr moving through input buffer */
1585 char tc; /* temp char */
1586 int haveComponent; /* has new component started? */
1587 char component[256]; /* this is the new component */
1588 char *cp; /* component name being assembled */
1589 cm_scache_t *tscp; /* current location in the hierarchy */
1590 cm_scache_t *nscp; /* next dude down */
1591 cm_scache_t *dirScp; /* last dir we searched */
1592 cm_scache_t *linkScp; /* new root for the symlink we just
1594 cm_space_t *psp; /* space for current path, if we've hit
1596 cm_space_t *tempsp; /* temp vbl */
1597 char *restp; /* rest of the pathname to interpret */
1598 int symlinkCount; /* count of # of symlinks traversed */
1599 int extraFlag; /* avoid chasing mt pts for dir cmd */
1600 int phase = 1; /* 1 = tidPathp, 2 = pathp */
1602 #ifdef DEBUG_REFCOUNT
1603 afsi_log("%s:%d cm_NameI rootscp 0x%p ref %d", file, line, rootSCachep, rootSCachep->refCount);
1604 osi_Log4(afsd_logp,"cm_NameI rootscp 0x%p path %s tidpath %s flags 0x%x",
1605 rootSCachep, pathp ? pathp : "<NULL>", tidPathp ? tidPathp : "<NULL>",
1620 cm_HoldSCache(tscp);
1628 /* map Unix slashes into DOS ones so we can interpret Unix
1634 if (!haveComponent) {
1637 } else if (tc == 0) {
1651 /* we have a component here */
1652 if (tc == 0 || tc == '\\') {
1653 /* end of the component; we're at the last
1654 * component if tc == 0. However, if the last
1655 * is a symlink, we have more to do.
1657 *cp++ = 0; /* add null termination */
1659 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
1660 extraFlag = CM_FLAG_NOMOUNTCHASE;
1661 code = cm_Lookup(tscp, component,
1663 userp, reqp, &nscp);
1665 cm_ReleaseSCache(tscp);
1667 cm_ReleaseSCache(dirScp);
1670 if (code == CM_ERROR_NOSUCHFILE && tscp->fileType == CM_SCACHETYPE_SYMLINK) {
1671 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_NOSUCHPATH");
1672 return CM_ERROR_NOSUCHPATH;
1674 osi_Log1(afsd_logp,"cm_NameI code 0x%x", code);
1678 haveComponent = 0; /* component done */
1680 cm_ReleaseSCache(dirScp);
1681 dirScp = tscp; /* for some symlinks */
1682 tscp = nscp; /* already held */
1684 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
1687 cm_ReleaseSCache(dirScp);
1693 /* now, if tscp is a symlink, we should follow
1694 * it and assemble the path again.
1696 lock_ObtainMutex(&tscp->mx);
1697 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1698 CM_SCACHESYNC_GETSTATUS
1699 | CM_SCACHESYNC_NEEDCALLBACK);
1701 lock_ReleaseMutex(&tscp->mx);
1702 cm_ReleaseSCache(tscp);
1705 cm_ReleaseSCache(dirScp);
1710 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1712 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
1713 /* this is a symlink; assemble a new buffer */
1714 lock_ReleaseMutex(&tscp->mx);
1715 if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
1716 cm_ReleaseSCache(tscp);
1719 cm_ReleaseSCache(dirScp);
1724 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_TOO_MANY_SYMLINKS");
1725 return CM_ERROR_TOO_MANY_SYMLINKS;
1731 code = cm_AssembleLink(tscp, restp, &linkScp, &tempsp, userp, reqp);
1733 /* something went wrong */
1734 cm_ReleaseSCache(tscp);
1737 cm_ReleaseSCache(dirScp);
1743 /* otherwise, tempsp has the new path,
1744 * and linkScp is the new root from
1745 * which to interpret that path.
1746 * Continue with the namei processing,
1747 * also doing the bookkeeping for the
1748 * space allocation and tracking the
1749 * vnode reference counts.
1755 cm_ReleaseSCache(tscp);
1760 * now, if linkScp is null, that's
1761 * AssembleLink's way of telling us that
1762 * the sym link is relative to the dir
1763 * containing the link. We have a ref
1764 * to it in dirScp, and we hold it now
1765 * and reuse it as the new spot in the
1773 /* not a symlink, we may be done */
1774 lock_ReleaseMutex(&tscp->mx);
1782 cm_ReleaseSCache(dirScp);
1790 cm_ReleaseSCache(dirScp);
1793 } /* end of a component */
1796 } /* we have a component */
1797 } /* big while loop over all components */
1801 cm_ReleaseSCache(dirScp);
1807 cm_ReleaseSCache(tscp);
1809 #ifdef DEBUG_REFCOUNT
1810 afsi_log("%s:%d cm_NameI code 0x%x outScpp 0x%p ref %d", file, line, code, *outScpp, (*outScpp)->refCount);
1812 osi_Log2(afsd_logp,"cm_NameI code 0x%x outScpp 0x%p", code, *outScpp);
1816 /* called with a dir, and a vnode within the dir that happens to be a symlink.
1817 * We chase the link, and return a held pointer to the target, if it exists,
1818 * in *outScpp. If we succeed, we return 0, otherwise we return an error code
1819 * and do not hold or return a target vnode.
1821 * This is very similar to calling cm_NameI with the last component of a name,
1822 * which happens to be a symlink, except that we've already passed by the name.
1824 * This function is typically called by the directory listing functions, which
1825 * encounter symlinks but need to return the proper file length so programs
1826 * like "more" work properly when they make use of the attributes retrieved from
1829 * The input vnode should not be locked when this function is called.
1831 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
1832 cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
1836 cm_scache_t *newRootScp;
1838 osi_Log1(afsd_logp, "Evaluating symlink scp 0x%p", linkScp);
1840 code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
1844 /* now, if newRootScp is NULL, we're really being told that the symlink
1845 * is relative to the current directory (dscp).
1847 if (newRootScp == NULL) {
1849 cm_HoldSCache(dscp);
1852 code = cm_NameI(newRootScp, spacep->data,
1853 CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
1854 userp, NULL, reqp, outScpp);
1856 if (code == CM_ERROR_NOSUCHFILE)
1857 code = CM_ERROR_NOSUCHPATH;
1859 /* this stuff is allocated no matter what happened on the namei call,
1861 cm_FreeSpace(spacep);
1862 cm_ReleaseSCache(newRootScp);
1867 /* make this big enough so that one buffer of dir pages won't overflow. We'll
1868 * check anyway, but we want to minimize the chance that we have to leave stuff
1871 #define CM_BULKMAX (3 * AFSCBMAX)
1873 /* rock for bulk stat calls */
1874 typedef struct cm_bulkStat {
1875 osi_hyper_t bufOffset; /* only do it for things in this buffer page */
1877 /* info for the actual call */
1878 int counter; /* next free slot */
1879 AFSFid fids[CM_BULKMAX];
1880 AFSFetchStatus stats[CM_BULKMAX];
1881 AFSCallBack callbacks[CM_BULKMAX];
1884 /* for a given entry, make sure that it isn't in the stat cache, and then
1885 * add it to the list of file IDs to be obtained.
1887 * Don't bother adding it if we already have a vnode. Note that the dir
1888 * is locked, so we have to be careful checking the vnode we're thinking of
1889 * processing, to avoid deadlocks.
1891 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
1902 /* Don't overflow bsp. */
1903 if (bsp->counter >= CM_BULKMAX)
1904 return CM_ERROR_STOPNOW;
1906 thyper.LowPart = cm_data.buf_blockSize;
1907 thyper.HighPart = 0;
1908 thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
1910 /* thyper is now the first byte past the end of the record we're
1911 * interested in, and bsp->bufOffset is the first byte of the record
1912 * we're interested in.
1913 * Skip data in the others.
1916 if (LargeIntegerLessThan(*offp, bsp->bufOffset))
1918 if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
1919 return CM_ERROR_STOPNOW;
1920 if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
1923 tfid.cell = scp->fid.cell;
1924 tfid.volume = scp->fid.volume;
1925 tfid.vnode = ntohl(dep->fid.vnode);
1926 tfid.unique = ntohl(dep->fid.unique);
1927 tscp = cm_FindSCache(&tfid);
1929 if (lock_TryMutex(&tscp->mx)) {
1930 /* we have an entry that we can look at */
1931 if (!(tscp->flags & CM_SCACHEFLAG_EACCESS) && cm_HaveCallback(tscp)) {
1932 /* we have a callback on it. Don't bother
1933 * fetching this stat entry, since we're happy
1934 * with the info we have.
1936 lock_ReleaseMutex(&tscp->mx);
1937 cm_ReleaseSCache(tscp);
1940 lock_ReleaseMutex(&tscp->mx);
1942 cm_ReleaseSCache(tscp);
1945 #ifdef AFS_FREELANCE_CLIENT
1946 // yj: if this is a mountpoint under root.afs then we don't want it
1947 // to be bulkstat-ed, instead, we call getSCache directly and under
1948 // getSCache, it is handled specially.
1949 if ( cm_freelanceEnabled &&
1950 tfid.cell==AFS_FAKE_ROOT_CELL_ID &&
1951 tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
1952 !(tfid.vnode==0x1 && tfid.unique==0x1) )
1954 osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
1955 return cm_GetSCache(&tfid, &tscp, NULL, NULL);
1957 #endif /* AFS_FREELANCE_CLIENT */
1960 bsp->fids[i].Volume = scp->fid.volume;
1961 bsp->fids[i].Vnode = tfid.vnode;
1962 bsp->fids[i].Unique = tfid.unique;
1966 /* called with a locked scp and a pointer to a buffer. Make bulk stat
1967 * calls on all undeleted files in the page of the directory specified.
1970 cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
1974 cm_bulkStat_t bb; /* this is *BIG*, probably 16K or so;
1975 * watch for stack problems */
1976 AFSCBFids fidStruct;
1977 AFSBulkStats statStruct;
1979 AFSCBs callbackStruct;
1982 cm_callbackRequest_t cbReq;
1988 struct rx_connection * callp;
1989 int inlinebulk = 0; /* Did we use InlineBulkStatus RPC or not? */
1991 osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%p", dscp);
1993 /* should be on a buffer boundary */
1994 osi_assert((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0);
1996 memset(&bb, 0, sizeof(bb));
1997 bb.bufOffset = *offsetp;
1999 lock_ReleaseMutex(&dscp->mx);
2000 /* first, assemble the file IDs we need to stat */
2001 code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) &bb, offsetp, userp, reqp, NULL);
2003 /* if we failed, bail out early */
2004 if (code && code != CM_ERROR_STOPNOW) {
2005 lock_ObtainMutex(&dscp->mx);
2009 /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
2010 * make the calls to create the entries. Handle AFSCBMAX files at a
2014 while (filex < bb.counter) {
2015 filesThisCall = bb.counter - filex;
2016 if (filesThisCall > AFSCBMAX)
2017 filesThisCall = AFSCBMAX;
2019 fidStruct.AFSCBFids_len = filesThisCall;
2020 fidStruct.AFSCBFids_val = &bb.fids[filex];
2021 statStruct.AFSBulkStats_len = filesThisCall;
2022 statStruct.AFSBulkStats_val = &bb.stats[filex];
2023 callbackStruct.AFSCBs_len = filesThisCall;
2024 callbackStruct.AFSCBs_val = &bb.callbacks[filex];
2025 cm_StartCallbackGrantingCall(NULL, &cbReq);
2026 osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
2028 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2032 callp = cm_GetRxConn(connp);
2033 if (!(connp->serverp->flags & CM_SERVERFLAG_NOINLINEBULK)) {
2034 code = RXAFS_InlineBulkStatus(callp, &fidStruct,
2035 &statStruct, &callbackStruct, &volSync);
2036 if (code == RXGEN_OPCODE) {
2037 cm_SetServerNoInlineBulk(connp->serverp, 0);
2043 code = RXAFS_BulkStatus(callp, &fidStruct,
2044 &statStruct, &callbackStruct, &volSync);
2046 rx_PutConnection(callp);
2048 } while (cm_Analyze(connp, userp, reqp, &dscp->fid,
2049 &volSync, NULL, &cbReq, code));
2050 code = cm_MapRPCError(code, reqp);
2052 osi_Log2(afsd_logp, "CALL %sBulkStatus FAILURE code 0x%x",
2053 inlinebulk ? "Inline" : "", code);
2055 osi_Log1(afsd_logp, "CALL %sBulkStatus SUCCESS", inlinebulk ? "Inline" : "");
2057 /* may as well quit on an error, since we're not going to do
2058 * much better on the next immediate call, either.
2061 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2065 /* otherwise, we should do the merges */
2066 for (i = 0; i<filesThisCall; i++) {
2068 tfid.cell = dscp->fid.cell;
2069 tfid.volume = bb.fids[j].Volume;
2070 tfid.vnode = bb.fids[j].Vnode;
2071 tfid.unique = bb.fids[j].Unique;
2072 code = cm_GetSCache(&tfid, &scp, userp, reqp);
2076 /* otherwise, if this entry has no callback info,
2079 lock_ObtainMutex(&scp->mx);
2080 /* now, we have to be extra paranoid on merging in this
2081 * information, since we didn't use cm_SyncOp before
2082 * starting the fetch to make sure that no bad races
2083 * were occurring. Specifically, we need to make sure
2084 * we don't obliterate any newer information in the
2085 * vnode than have here.
2087 * Right now, be pretty conservative: if there's a
2088 * callback or a pending call, skip it.
2090 if ((scp->cbServerp == NULL || (scp->flags & CM_SCACHEFLAG_EACCESS))
2092 (CM_SCACHEFLAG_FETCHING
2093 | CM_SCACHEFLAG_STORING
2094 | CM_SCACHEFLAG_SIZESTORING))) {
2095 cm_EndCallbackGrantingCall(scp, &cbReq,
2097 CM_CALLBACK_MAINTAINCOUNT);
2098 cm_MergeStatus(dscp, scp, &bb.stats[j], &volSync, userp, 0);
2100 lock_ReleaseMutex(&scp->mx);
2101 cm_ReleaseSCache(scp);
2102 } /* all files in the response */
2103 /* now tell it to drop the count,
2104 * after doing the vnode processing above */
2105 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2107 filex += filesThisCall;
2108 } /* while there are still more files to process */
2109 lock_ObtainMutex(&dscp->mx);
2111 /* If we did the InlineBulk RPC pull out the return code and log it */
2113 if ((&bb.stats[0])->errorCode) {
2114 osi_Log1(afsd_logp, "cm_TryBulkStat bulk stat error: %d",
2115 (&bb.stats[0])->errorCode);
2119 osi_Log0(afsd_logp, "END cm_TryBulkStat");
2123 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
2127 /* initialize store back mask as inexpensive local variable */
2129 memset(statusp, 0, sizeof(AFSStoreStatus));
2131 /* copy out queued info from scache first, if scp passed in */
2133 if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
2134 statusp->ClientModTime = scp->clientModTime;
2135 mask |= AFS_SETMODTIME;
2136 scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
2141 /* now add in our locally generated request */
2142 if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
2143 statusp->ClientModTime = attrp->clientModTime;
2144 mask |= AFS_SETMODTIME;
2146 if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
2147 statusp->UnixModeBits = attrp->unixModeBits;
2148 mask |= AFS_SETMODE;
2150 if (attrp->mask & CM_ATTRMASK_OWNER) {
2151 statusp->Owner = attrp->owner;
2152 mask |= AFS_SETOWNER;
2154 if (attrp->mask & CM_ATTRMASK_GROUP) {
2155 statusp->Group = attrp->group;
2156 mask |= AFS_SETGROUP;
2159 statusp->Mask = mask;
2162 /* set the file size, and make sure that all relevant buffers have been
2163 * truncated. Ensure that any partially truncated buffers have been zeroed
2164 * to the end of the buffer.
2166 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
2172 /* start by locking out buffer creation */
2173 lock_ObtainWrite(&scp->bufCreateLock);
2175 /* verify that this is a file, not a dir or a symlink */
2176 lock_ObtainMutex(&scp->mx);
2177 code = cm_SyncOp(scp, NULL, userp, reqp, 0,
2178 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2181 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2183 if (scp->fileType != CM_SCACHETYPE_FILE) {
2184 code = CM_ERROR_ISDIR;
2189 if (LargeIntegerLessThan(*sizep, scp->length))
2194 lock_ReleaseMutex(&scp->mx);
2196 /* can't hold scp->mx lock here, since we may wait for a storeback to
2197 * finish if the buffer package is cleaning a buffer by storing it to
2201 buf_Truncate(scp, userp, reqp, sizep);
2203 /* now ensure that file length is short enough, and update truncPos */
2204 lock_ObtainMutex(&scp->mx);
2206 /* make sure we have a callback (so we have the right value for the
2207 * length), and wait for it to be safe to do a truncate.
2209 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
2210 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2211 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2215 if (LargeIntegerLessThan(*sizep, scp->length)) {
2216 /* a real truncation. If truncPos is not set yet, or is bigger
2217 * than where we're truncating the file, set truncPos to this
2222 if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
2223 || LargeIntegerLessThan(*sizep, scp->length)) {
2225 scp->truncPos = *sizep;
2226 scp->mask |= CM_SCACHEMASK_TRUNCPOS;
2228 /* in either case, the new file size has been changed */
2229 scp->length = *sizep;
2230 scp->mask |= CM_SCACHEMASK_LENGTH;
2232 else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
2233 /* really extending the file */
2234 scp->length = *sizep;
2235 scp->mask |= CM_SCACHEMASK_LENGTH;
2238 /* done successfully */
2241 cm_SyncOpDone(scp, NULL,
2242 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2243 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2246 lock_ReleaseMutex(&scp->mx);
2247 lock_ReleaseWrite(&scp->bufCreateLock);
2252 /* set the file size or other attributes (but not both at once) */
2253 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
2257 AFSFetchStatus afsOutStatus;
2261 AFSStoreStatus afsInStatus;
2262 struct rx_connection * callp;
2264 /* handle file length setting */
2265 if (attrp->mask & CM_ATTRMASK_LENGTH)
2266 return cm_SetLength(scp, &attrp->length, userp, reqp);
2268 lock_ObtainMutex(&scp->mx);
2269 /* otherwise, we have to make an RPC to get the status */
2270 code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
2272 lock_ReleaseMutex(&scp->mx);
2276 /* make the attr structure */
2277 cm_StatusFromAttr(&afsInStatus, scp, attrp);
2279 tfid.Volume = scp->fid.volume;
2280 tfid.Vnode = scp->fid.vnode;
2281 tfid.Unique = scp->fid.unique;
2282 lock_ReleaseMutex(&scp->mx);
2284 /* now make the RPC */
2285 osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%p", scp);
2287 code = cm_Conn(&scp->fid, userp, reqp, &connp);
2291 callp = cm_GetRxConn(connp);
2292 code = RXAFS_StoreStatus(callp, &tfid,
2293 &afsInStatus, &afsOutStatus, &volSync);
2294 rx_PutConnection(callp);
2296 } while (cm_Analyze(connp, userp, reqp,
2297 &scp->fid, &volSync, NULL, NULL, code));
2298 code = cm_MapRPCError(code, reqp);
2301 osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
2303 osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
2305 lock_ObtainMutex(&scp->mx);
2306 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
2308 cm_MergeStatus(NULL, scp, &afsOutStatus, &volSync, userp,
2309 CM_MERGEFLAG_FORCE|CM_MERGEFLAG_STOREDATA);
2311 /* if we're changing the mode bits, discard the ACL cache,
2312 * since we changed the mode bits.
2314 if (afsInStatus.Mask & AFS_SETMODE)
2315 cm_FreeAllACLEnts(scp);
2316 lock_ReleaseMutex(&scp->mx);
2320 long cm_Create(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2321 cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
2326 cm_callbackRequest_t cbReq;
2331 AFSStoreStatus inStatus;
2332 AFSFetchStatus updatedDirStatus;
2333 AFSFetchStatus newFileStatus;
2334 AFSCallBack newFileCallback;
2336 struct rx_connection * callp;
2338 /* can't create names with @sys in them; must expand it manually first.
2339 * return "invalid request" if they try.
2341 if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2342 return CM_ERROR_ATSYS;
2345 /* before starting the RPC, mark that we're changing the file data, so
2346 * that someone who does a chmod will know to wait until our call
2349 lock_ObtainMutex(&dscp->mx);
2350 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2352 cm_StartCallbackGrantingCall(NULL, &cbReq);
2354 lock_ReleaseMutex(&dscp->mx);
2360 cm_StatusFromAttr(&inStatus, NULL, attrp);
2362 /* try the RPC now */
2363 osi_Log1(afsd_logp, "CALL CreateFile scp 0x%p", dscp);
2365 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2369 dirAFSFid.Volume = dscp->fid.volume;
2370 dirAFSFid.Vnode = dscp->fid.vnode;
2371 dirAFSFid.Unique = dscp->fid.unique;
2373 callp = cm_GetRxConn(connp);
2374 code = RXAFS_CreateFile(connp->callp, &dirAFSFid, namep,
2375 &inStatus, &newAFSFid, &newFileStatus,
2376 &updatedDirStatus, &newFileCallback,
2378 rx_PutConnection(callp);
2380 } while (cm_Analyze(connp, userp, reqp,
2381 &dscp->fid, &volSync, NULL, &cbReq, code));
2382 code = cm_MapRPCError(code, reqp);
2385 osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
2387 osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
2389 lock_ObtainMutex(&dscp->mx);
2390 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2392 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, 0);
2394 lock_ReleaseMutex(&dscp->mx);
2396 /* now try to create the file's entry, too, but be careful to
2397 * make sure that we don't merge in old info. Since we weren't locking
2398 * out any requests during the file's creation, we may have pretty old
2402 newFid.cell = dscp->fid.cell;
2403 newFid.volume = dscp->fid.volume;
2404 newFid.vnode = newAFSFid.Vnode;
2405 newFid.unique = newAFSFid.Unique;
2406 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2408 lock_ObtainMutex(&scp->mx);
2409 scp->creator = userp; /* remember who created it */
2410 if (!cm_HaveCallback(scp)) {
2411 cm_MergeStatus(dscp, scp, &newFileStatus, &volSync,
2413 cm_EndCallbackGrantingCall(scp, &cbReq,
2414 &newFileCallback, 0);
2417 lock_ReleaseMutex(&scp->mx);
2422 /* make sure we end things properly */
2424 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2429 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
2433 lock_ObtainWrite(&scp->bufCreateLock);
2434 code = buf_CleanVnode(scp, userp, reqp);
2435 lock_ReleaseWrite(&scp->bufCreateLock);
2437 lock_ObtainMutex(&scp->mx);
2439 if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
2440 | CM_SCACHEMASK_CLIENTMODTIME
2441 | CM_SCACHEMASK_LENGTH))
2442 code = cm_StoreMini(scp, userp, reqp);
2444 if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) {
2445 code = (scp->flags & CM_SCACHEFLAG_OVERQUOTA) ? CM_ERROR_QUOTA : CM_ERROR_SPACE;
2446 scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE);
2449 lock_ReleaseMutex(&scp->mx);
2454 long cm_MakeDir(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2455 cm_user_t *userp, cm_req_t *reqp)
2460 cm_callbackRequest_t cbReq;
2465 AFSStoreStatus inStatus;
2466 AFSFetchStatus updatedDirStatus;
2467 AFSFetchStatus newDirStatus;
2468 AFSCallBack newDirCallback;
2470 struct rx_connection * callp;
2472 /* can't create names with @sys in them; must expand it manually first.
2473 * return "invalid request" if they try.
2475 if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2476 return CM_ERROR_ATSYS;
2479 /* before starting the RPC, mark that we're changing the directory
2480 * data, so that someone who does a chmod on the dir will wait until
2481 * our call completes.
2483 lock_ObtainMutex(&dscp->mx);
2484 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2486 cm_StartCallbackGrantingCall(NULL, &cbReq);
2488 lock_ReleaseMutex(&dscp->mx);
2494 cm_StatusFromAttr(&inStatus, NULL, attrp);
2496 /* try the RPC now */
2497 osi_Log1(afsd_logp, "CALL MakeDir scp 0x%p", dscp);
2499 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2503 dirAFSFid.Volume = dscp->fid.volume;
2504 dirAFSFid.Vnode = dscp->fid.vnode;
2505 dirAFSFid.Unique = dscp->fid.unique;
2507 callp = cm_GetRxConn(connp);
2508 code = RXAFS_MakeDir(connp->callp, &dirAFSFid, namep,
2509 &inStatus, &newAFSFid, &newDirStatus,
2510 &updatedDirStatus, &newDirCallback,
2512 rx_PutConnection(callp);
2514 } while (cm_Analyze(connp, userp, reqp,
2515 &dscp->fid, &volSync, NULL, &cbReq, code));
2516 code = cm_MapRPCError(code, reqp);
2519 osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
2521 osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
2523 lock_ObtainMutex(&dscp->mx);
2524 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2526 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, 0);
2528 lock_ReleaseMutex(&dscp->mx);
2530 /* now try to create the new dir's entry, too, but be careful to
2531 * make sure that we don't merge in old info. Since we weren't locking
2532 * out any requests during the file's creation, we may have pretty old
2536 newFid.cell = dscp->fid.cell;
2537 newFid.volume = dscp->fid.volume;
2538 newFid.vnode = newAFSFid.Vnode;
2539 newFid.unique = newAFSFid.Unique;
2540 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2542 lock_ObtainMutex(&scp->mx);
2543 if (!cm_HaveCallback(scp)) {
2544 cm_MergeStatus(dscp, scp, &newDirStatus, &volSync,
2546 cm_EndCallbackGrantingCall(scp, &cbReq,
2547 &newDirCallback, 0);
2550 lock_ReleaseMutex(&scp->mx);
2551 cm_ReleaseSCache(scp);
2555 /* make sure we end things properly */
2557 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2559 /* and return error code */
2563 long cm_Link(cm_scache_t *dscp, char *namep, cm_scache_t *sscp, long flags,
2564 cm_user_t *userp, cm_req_t *reqp)
2569 AFSFid existingAFSFid;
2570 AFSFetchStatus updatedDirStatus;
2571 AFSFetchStatus newLinkStatus;
2573 struct rx_connection * callp;
2575 if (dscp->fid.cell != sscp->fid.cell ||
2576 dscp->fid.volume != sscp->fid.volume) {
2577 return CM_ERROR_CROSSDEVLINK;
2580 lock_ObtainMutex(&dscp->mx);
2581 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2582 lock_ReleaseMutex(&dscp->mx);
2587 /* try the RPC now */
2588 osi_Log1(afsd_logp, "CALL Link scp 0x%p", dscp);
2590 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2593 dirAFSFid.Volume = dscp->fid.volume;
2594 dirAFSFid.Vnode = dscp->fid.vnode;
2595 dirAFSFid.Unique = dscp->fid.unique;
2597 existingAFSFid.Volume = sscp->fid.volume;
2598 existingAFSFid.Vnode = sscp->fid.vnode;
2599 existingAFSFid.Unique = sscp->fid.unique;
2601 callp = cm_GetRxConn(connp);
2602 code = RXAFS_Link(callp, &dirAFSFid, namep, &existingAFSFid,
2603 &newLinkStatus, &updatedDirStatus, &volSync);
2604 rx_PutConnection(callp);
2605 osi_Log1(smb_logp," RXAFS_Link returns 0x%x", code);
2607 } while (cm_Analyze(connp, userp, reqp,
2608 &dscp->fid, &volSync, NULL, NULL, code));
2610 code = cm_MapRPCError(code, reqp);
2613 osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
2615 osi_Log0(afsd_logp, "CALL Link SUCCESS");
2617 lock_ObtainMutex(&dscp->mx);
2618 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2620 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, 0);
2622 lock_ReleaseMutex(&dscp->mx);
2627 long cm_SymLink(cm_scache_t *dscp, char *namep, char *contentsp, long flags,
2628 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
2636 AFSStoreStatus inStatus;
2637 AFSFetchStatus updatedDirStatus;
2638 AFSFetchStatus newLinkStatus;
2640 struct rx_connection * callp;
2642 /* before starting the RPC, mark that we're changing the directory data,
2643 * so that someone who does a chmod on the dir will wait until our
2646 lock_ObtainMutex(&dscp->mx);
2647 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2648 lock_ReleaseMutex(&dscp->mx);
2653 cm_StatusFromAttr(&inStatus, NULL, attrp);
2655 /* try the RPC now */
2656 osi_Log1(afsd_logp, "CALL Symlink scp 0x%p", dscp);
2658 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2662 dirAFSFid.Volume = dscp->fid.volume;
2663 dirAFSFid.Vnode = dscp->fid.vnode;
2664 dirAFSFid.Unique = dscp->fid.unique;
2666 callp = cm_GetRxConn(connp);
2667 code = RXAFS_Symlink(callp, &dirAFSFid, namep, contentsp,
2668 &inStatus, &newAFSFid, &newLinkStatus,
2669 &updatedDirStatus, &volSync);
2670 rx_PutConnection(callp);
2672 } while (cm_Analyze(connp, userp, reqp,
2673 &dscp->fid, &volSync, NULL, NULL, code));
2674 code = cm_MapRPCError(code, reqp);
2677 osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
2679 osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
2681 lock_ObtainMutex(&dscp->mx);
2682 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2684 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, 0);
2686 lock_ReleaseMutex(&dscp->mx);
2688 /* now try to create the new dir's entry, too, but be careful to
2689 * make sure that we don't merge in old info. Since we weren't locking
2690 * out any requests during the file's creation, we may have pretty old
2694 newFid.cell = dscp->fid.cell;
2695 newFid.volume = dscp->fid.volume;
2696 newFid.vnode = newAFSFid.Vnode;
2697 newFid.unique = newAFSFid.Unique;
2698 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2700 lock_ObtainMutex(&scp->mx);
2701 if (!cm_HaveCallback(scp)) {
2702 cm_MergeStatus(dscp, scp, &newLinkStatus, &volSync,
2705 lock_ReleaseMutex(&scp->mx);
2706 cm_ReleaseSCache(scp);
2710 /* and return error code */
2714 long cm_RemoveDir(cm_scache_t *dscp, char *namep, cm_user_t *userp,
2721 AFSFetchStatus updatedDirStatus;
2723 struct rx_connection * callp;
2725 /* before starting the RPC, mark that we're changing the directory data,
2726 * so that someone who does a chmod on the dir will wait until our
2729 lock_ObtainMutex(&dscp->mx);
2730 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2731 lock_ReleaseMutex(&dscp->mx);
2737 /* try the RPC now */
2738 osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%p", dscp);
2740 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2744 dirAFSFid.Volume = dscp->fid.volume;
2745 dirAFSFid.Vnode = dscp->fid.vnode;
2746 dirAFSFid.Unique = dscp->fid.unique;
2748 callp = cm_GetRxConn(connp);
2749 code = RXAFS_RemoveDir(callp, &dirAFSFid, namep,
2750 &updatedDirStatus, &volSync);
2751 rx_PutConnection(callp);
2753 } while (cm_Analyze(connp, userp, reqp,
2754 &dscp->fid, &volSync, NULL, NULL, code));
2755 code = cm_MapRPCErrorRmdir(code, reqp);
2758 osi_Log1(afsd_logp, "CALL RemoveDir FAILURE, code 0x%x", code);
2760 osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
2762 lock_ObtainMutex(&dscp->mx);
2763 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2765 cm_dnlcRemove(dscp, namep);
2766 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, 0);
2768 lock_ReleaseMutex(&dscp->mx);
2770 /* and return error code */
2774 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
2776 /* grab mutex on contents */
2777 lock_ObtainMutex(&scp->mx);
2779 /* reset the prefetch info */
2780 scp->prefetch.base.LowPart = 0; /* base */
2781 scp->prefetch.base.HighPart = 0;
2782 scp->prefetch.end.LowPart = 0; /* and end */
2783 scp->prefetch.end.HighPart = 0;
2785 /* release mutex on contents */
2786 lock_ReleaseMutex(&scp->mx);
2792 long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
2793 char *newNamep, cm_user_t *userp, cm_req_t *reqp)
2797 AFSFid oldDirAFSFid;
2798 AFSFid newDirAFSFid;
2800 AFSFetchStatus updatedOldDirStatus;
2801 AFSFetchStatus updatedNewDirStatus;
2804 struct rx_connection * callp;
2806 /* before starting the RPC, mark that we're changing the directory data,
2807 * so that someone who does a chmod on the dir will wait until our call
2808 * completes. We do this in vnode order so that we don't deadlock,
2809 * which makes the code a little verbose.
2811 if (oldDscp == newDscp) {
2812 /* check for identical names */
2813 if (strcmp(oldNamep, newNamep) == 0)
2814 return CM_ERROR_RENAME_IDENTICAL;
2817 lock_ObtainMutex(&oldDscp->mx);
2818 cm_dnlcRemove(oldDscp, oldNamep);
2819 cm_dnlcRemove(oldDscp, newNamep);
2820 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2821 CM_SCACHESYNC_STOREDATA);
2822 lock_ReleaseMutex(&oldDscp->mx);
2825 /* two distinct dir vnodes */
2827 if (oldDscp->fid.cell != newDscp->fid.cell ||
2828 oldDscp->fid.volume != newDscp->fid.volume)
2829 return CM_ERROR_CROSSDEVLINK;
2831 /* shouldn't happen that we have distinct vnodes for two
2832 * different files, but could due to deliberate attack, or
2833 * stale info. Avoid deadlocks and quit now.
2835 if (oldDscp->fid.vnode == newDscp->fid.vnode)
2836 return CM_ERROR_CROSSDEVLINK;
2838 if (oldDscp->fid.vnode < newDscp->fid.vnode) {
2839 lock_ObtainMutex(&oldDscp->mx);
2840 cm_dnlcRemove(oldDscp, oldNamep);
2841 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2842 CM_SCACHESYNC_STOREDATA);
2843 lock_ReleaseMutex(&oldDscp->mx);
2845 lock_ObtainMutex(&newDscp->mx);
2846 cm_dnlcRemove(newDscp, newNamep);
2847 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2848 CM_SCACHESYNC_STOREDATA);
2849 lock_ReleaseMutex(&newDscp->mx);
2851 /* cleanup first one */
2852 lock_ObtainMutex(&oldDscp->mx);
2853 cm_SyncOpDone(oldDscp, NULL,
2854 CM_SCACHESYNC_STOREDATA);
2855 lock_ReleaseMutex(&oldDscp->mx);
2860 /* lock the new vnode entry first */
2861 lock_ObtainMutex(&newDscp->mx);
2862 cm_dnlcRemove(newDscp, newNamep);
2863 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2864 CM_SCACHESYNC_STOREDATA);
2865 lock_ReleaseMutex(&newDscp->mx);
2867 lock_ObtainMutex(&oldDscp->mx);
2868 cm_dnlcRemove(oldDscp, oldNamep);
2869 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2870 CM_SCACHESYNC_STOREDATA);
2871 lock_ReleaseMutex(&oldDscp->mx);
2873 /* cleanup first one */
2874 lock_ObtainMutex(&newDscp->mx);
2875 cm_SyncOpDone(newDscp, NULL,
2876 CM_SCACHESYNC_STOREDATA);
2877 lock_ReleaseMutex(&newDscp->mx);
2881 } /* two distinct vnodes */
2888 /* try the RPC now */
2889 osi_Log2(afsd_logp, "CALL Rename old scp 0x%p new scp 0x%p",
2892 code = cm_Conn(&oldDscp->fid, userp, reqp, &connp);
2896 oldDirAFSFid.Volume = oldDscp->fid.volume;
2897 oldDirAFSFid.Vnode = oldDscp->fid.vnode;
2898 oldDirAFSFid.Unique = oldDscp->fid.unique;
2899 newDirAFSFid.Volume = newDscp->fid.volume;
2900 newDirAFSFid.Vnode = newDscp->fid.vnode;
2901 newDirAFSFid.Unique = newDscp->fid.unique;
2903 callp = cm_GetRxConn(connp);
2904 code = RXAFS_Rename(callp, &oldDirAFSFid, oldNamep,
2905 &newDirAFSFid, newNamep,
2906 &updatedOldDirStatus, &updatedNewDirStatus,
2908 rx_PutConnection(callp);
2910 } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
2911 &volSync, NULL, NULL, code));
2912 code = cm_MapRPCError(code, reqp);
2915 osi_Log1(afsd_logp, "CALL Rename FAILURE, code 0x%x", code);
2917 osi_Log0(afsd_logp, "CALL Rename SUCCESS");
2919 /* update the individual stat cache entries for the directories */
2920 lock_ObtainMutex(&oldDscp->mx);
2921 cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
2923 cm_MergeStatus(NULL, oldDscp, &updatedOldDirStatus, &volSync,
2926 lock_ReleaseMutex(&oldDscp->mx);
2928 /* and update it for the new one, too, if necessary */
2930 lock_ObtainMutex(&newDscp->mx);
2931 cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
2933 cm_MergeStatus(NULL, newDscp, &updatedNewDirStatus, &volSync,
2936 lock_ReleaseMutex(&newDscp->mx);
2939 /* and return error code */
2943 /* Byte range locks:
2945 The OpenAFS Windows client has to fake byte range locks given no
2946 server side support for such locks. This is implemented as keyed
2947 byte range locks on the cache manager.
2949 Keyed byte range locks:
2951 Each cm_scache_t structure keeps track of a list of keyed locks.
2952 The key for a lock identifies an owner of a set of locks (referred
2953 to as a client). Each key is represented by a value. The set of
2954 key values used within a specific cm_scache_t structure form a
2955 namespace that has a scope of just that cm_scache_t structure. The
2956 same key value can be used with another cm_scache_t structure and
2957 correspond to a completely different client. However it is
2958 advantageous for the SMB or IFS layer to make sure that there is a
2959 1-1 mapping between client and keys over all cm_scache_t objects.
2961 Assume a client C has key Key(C) (although, since the scope of the
2962 key is a cm_scache_t, the key can be Key(C,S), where S is the
2963 cm_scache_t. But assume a 1-1 relation between keys and clients).
2964 A byte range (O,+L) denotes byte addresses (O) through (O+L-1)
2965 inclusive (a.k.a. [O,O+L-1]). The function Key(x) is implemented
2966 through cm_generateKey() function for both SMB and IFS.
2968 The list of locks for a cm_scache_t object S is maintained in
2969 S->fileLocks. The cache manager will set a lock on the AFS file
2970 server in order to assert the locks in S->fileLocks. If only
2971 shared locks are in place for S, then the cache manager will obtain
2972 a LockRead lock, while if there are any exclusive locks, it will
2973 obtain a LockWrite lock. If the exclusive locks are all released
2974 while the shared locks remain, then the cache manager will
2975 downgrade the lock from LockWrite to LockRead. Similarly, if an
2976 exclusive lock is obtained when only shared locks exist, then the
2977 cache manager will try to upgrade the lock from LockRead to
2980 Each lock L owned by client C maintains a key L->key such that
2981 L->key == Key(C), the effective range defined by L->LOffset and
2982 L->LLength such that the range of bytes affected by the lock is
2983 (L->LOffset, +L->LLength), a type maintained in L->LockType which
2984 is either exclusive or shared.
2988 A lock exists iff it is in S->fileLocks for some cm_scache_t
2989 S. Existing locks are in one of the following states: ACTIVE,
2990 WAITLOCK, WAITUNLOCK, LOST, DELETED.
2992 The following sections describe each lock and the associated
2995 1. ACTIVE: A lock L is ACTIVE iff the cache manager has asserted
2996 the lock with the AFS file server. This type of lock can be
2997 exercised by a client to read or write to the locked region (as
3000 1.1 ACTIVE->LOST: When the AFS file server fails to extend a
3001 server lock that was required to assert the lock. Before
3002 marking the lock as lost, the cache manager checks if the file
3003 has changed on the server. If the file has not changed, then
3004 the cache manager will attempt to obtain a new server lock
3005 that is sufficient to assert the client side locks for the
3006 file. If any of these fail, the lock is marked as LOST.
3007 Otherwise, it is left as ACTIVE.
3009 1.2 ACTIVE->DELETED: Lock is released.
3011 2. WAITLOCK: A lock is in a WAITLOCK state if the cache manager
3012 grants the lock but the lock is yet to be asserted with the AFS
3013 file server. Once the file server grants the lock, the state
3014 will transition to an ACTIVE lock.
3016 2.1 WAITLOCK->ACTIVE: The server granted the lock.
3018 2.2 WAITLOCK->DELETED: Lock is abandoned, or timed out during
3021 2.3 WAITLOCK->LOST: One or more locks from this client were
3022 marked as LOST. No further locks will be granted to this
3023 client until all lost locks are removed.
3025 3. WAITUNLOCK: A lock is in a WAITUNLOCK state if the cache manager
3026 receives a request for a lock that conflicts with an existing
3027 ACTIVE or WAITLOCK lock. The lock will be placed in the queue
3028 and will be granted at such time the conflicting locks are
3029 removed, at which point the state will transition to either
3032 3.1 WAITUNLOCK->ACTIVE: The conflicting lock was removed. The
3033 current serverLock is sufficient to assert this lock, or a
3034 sufficient serverLock is obtained.
3036 3.2 WAITUNLOCK->WAITLOCK: The conflicting lock was removed,
3037 however the required serverLock is yet to be asserted with the
3040 3.3 WAITUNLOCK->DELETED: The lock is abandoned, timed out or
3043 3.5 WAITUNLOCK->LOST: One or more locks from this client were
3044 marked as LOST. No further locks will be granted to this
3045 client until all lost locks are removed.
3047 4. LOST: A lock L is LOST if the server lock that was required to
3048 assert the lock could not be obtained or if it could not be
3049 extended, or if other locks by the same client were LOST.
3050 Essentially, once a lock is LOST, the contract between the cache
3051 manager and that specific client is no longer valid.
3053 The cache manager rechecks the server lock once every minute and
3054 extends it as appropriate. If this is not done for 5 minutes,
3055 the AFS file server will release the lock (the 5 minute timeout
3056 is based on current file server code and is fairly arbitrary).
3057 Once released, the lock cannot be re-obtained without verifying
3058 that the contents of the file hasn't been modified since the
3059 time the lock was released. Re-obtaining the lock without
3060 verifying this may lead to data corruption. If the lock can not
3061 be obtained safely, then all active locks for the cm_scache_t
3064 4.1 LOST->DELETED: The lock is released.
3066 5. DELETED: The lock is no longer relevant. Eventually, it will
3067 get removed from the cm_scache_t. In the meantime, it will be
3068 treated as if it does not exist.
3070 5.1 DELETED->not exist: The lock is removed from the
3073 The following are classifications of locks based on their state.
3075 6* A lock L is ACCEPTED if it is ACTIVE or WAITLOCK. These locks
3076 have been accepted by the cache manager, but may or may not have
3077 been granted back to the client.
3079 7* A lock L is QUEUED if it is ACTIVE, WAITLOCK or WAITUNLOCK.
3081 8* A lock L is WAITING if it is WAITLOCK or WAITUNLOCK.
3085 A client C can READ range (Offset,+Length) of a file represented by
3086 cm_scache_t S iff (1):
3088 1. for all _a_ in (Offset,+Length), all of the following is true:
3090 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3091 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3094 1.2 For each LOST lock L in S->fileLocks such that _a_ in
3095 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3098 (When locks are lost on an cm_scache_t, all locks are lost. By
3099 4.2 (below), if there is an exclusive LOST lock, then there
3100 can't be any overlapping ACTIVE locks.)
3102 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
3104 2. for all _a_ in (Offset,+Length), one of the following is true:
3106 2.1 Byte _a_ of S is unowned (as specified in 1.1) AND there
3107 does not exist a LOST lock L such that _a_ in
3108 (L->LOffset,+L->LLength).
3110 2.2 Byte _a_ of S is owned by C under lock L (as specified in
3111 1.2) AND L->LockType is exclusive.
3113 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
3115 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
3118 3.1 If L->LockType is exclusive then there does NOT exist a
3119 ACCEPTED lock M in S->fileLocks such that _a_ in
3120 (M->LOffset,+M->LLength).
3122 (If we count all QUEUED locks then we hit cases such as
3123 cascading waiting locks where the locks later on in the queue
3124 can be granted without compromising file integrity. On the
3125 other hand if only ACCEPTED locks are considered, then locks
3126 that were received earlier may end up waiting for locks that
3127 were received later to be unlocked. The choice of ACCEPTED
3128 locks was made to mimic the Windows byte range lock
3131 3.2 If L->LockType is shared then for each ACCEPTED lock M in
3132 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
3133 M->LockType is shared.
3135 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
3137 4.1 M->key != Key(C)
3139 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
3140 and (M->LOffset,+M->LLength) do not intersect.
3142 (Note: If a client loses a lock, it loses all locks.
3143 Subsequently, it will not be allowed to obtain any more locks
3144 until all existing LOST locks that belong to the client are
3145 released. Once all locks are released by a single client,
3146 there exists no further contract between the client and AFS
3147 about the contents of the file, hence the client can then
3148 proceed to obtain new locks and establish a new contract.
3150 This doesn't quite work as you think it should, because most
3151 applications aren't built to deal with losing locks they
3152 thought they once had. For now, we don't have a good
3153 solution to lost locks.
3155 Also, for consistency reasons, we have to hold off on
3156 granting locks that overlap exclusive LOST locks.)
3158 A client C can only unlock locks L in S->fileLocks which have
3161 The representation and invariants are as follows:
3163 - Each cm_scache_t structure keeps:
3165 - A queue of byte-range locks (cm_scache_t::fileLocks) which
3166 are of type cm_file_lock_t.
3168 - A record of the highest server-side lock that has been
3169 obtained for this object (cm_scache_t::serverLock), which is
3170 one of (-1), LockRead, LockWrite.
3172 - A count of ACCEPTED exclusive and shared locks that are in the
3173 queue (cm_scache_t::sharedLocks and
3174 cm_scache_t::exclusiveLocks)
3176 - Each cm_file_lock_t structure keeps:
3178 - The type of lock (cm_file_lock_t::LockType)
3180 - The key associated with the lock (cm_file_lock_t::key)
3182 - The offset and length of the lock (cm_file_lock_t::LOffset
3183 and cm_file_lock_t::LLength)
3185 - The state of the lock.
3187 - Time of issuance or last successful extension
3189 Semantic invariants:
3191 I1. The number of ACCEPTED locks in S->fileLocks are
3192 (S->sharedLocks + S->exclusiveLocks)
3194 External invariants:
3196 I3. S->serverLock is the lock that we have asserted with the
3197 AFS file server for this cm_scache_t.
3199 I4. S->serverLock == LockRead iff there is at least one ACTIVE
3200 shared lock, but no ACTIVE exclusive locks.
3202 I5. S->serverLock == LockWrite iff there is at least one ACTIVE
3205 I6. If L is a LOST lock, then for each lock M in S->fileLocks,
3206 M->key == L->key IMPLIES M is LOST or DELETED.
3211 #define IS_LOCK_ACTIVE(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == 0)
3213 #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)
3215 #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)
3217 #define IS_LOCK_LOST(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_LOST)
3219 #define IS_LOCK_DELETED(lockp) (((lockp)->flags & CM_FILELOCK_FLAG_DELETED) == CM_FILELOCK_FLAG_DELETED)
3222 #define IS_LOCK_ACCEPTED(lockp) (IS_LOCK_ACTIVE(lockp) || IS_LOCK_WAITLOCK(lockp))
3225 #define IS_LOCK_CLIENTONLY(lockp) ((((lockp)->scp->flags & CM_SCACHEFLAG_RO) == CM_SCACHEFLAG_RO) || (((lockp)->flags & CM_FILELOCK_FLAG_CLIENTONLY) == CM_FILELOCK_FLAG_CLIENTONLY))
3228 #define INTERSECT_RANGE(r1,r2) (((r2).offset+(r2).length) > (r1).offset && ((r1).offset +(r1).length) > (r2).offset)
3231 #define CONTAINS_RANGE(r1,r2) (((r2).offset+(r2).length) <= ((r1).offset+(r1).length) && (r1).offset <= (r2).offset)
3233 #if defined(VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS) && !defined(LOCK_TESTING)
3234 #define SCP_SUPPORTS_BRLOCKS(scp) ((scp)->cbServerp && ((scp)->cbServerp->capabilities & VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS))
3236 #define SCP_SUPPORTS_BRLOCKS(scp) (1)
3239 #define SERVERLOCKS_ENABLED(scp) (!((scp)->flags & CM_SCACHEFLAG_RO) && cm_enableServerLocks && SCP_SUPPORTS_BRLOCKS(scp))
3241 static void cm_LockRangeSubtract(cm_range_t * pos, const cm_range_t * neg)
3243 afs_int64 int_begin;
3246 int_begin = MAX(pos->offset, neg->offset);
3247 int_end = MIN(pos->offset+pos->length, neg->offset+neg->length);
3249 if (int_begin < int_end) {
3250 if (int_begin == pos->offset) {
3251 pos->length = pos->offset + pos->length - int_end;
3252 pos->offset = int_end;
3253 } else if (int_end == pos->offset + pos->length) {
3254 pos->length = int_begin - pos->offset;
3257 /* We only subtract ranges if the resulting range is
3258 contiguous. If we try to support non-contigous ranges, we
3259 aren't actually improving performance. */
3263 /* Called with scp->mx held. Returns 0 if all is clear to read the
3264 specified range by the client identified by key.
3266 long cm_LockCheckRead(cm_scache_t *scp,
3267 LARGE_INTEGER LOffset,
3268 LARGE_INTEGER LLength,
3271 #ifndef ADVISORY_LOCKS
3273 cm_file_lock_t *fileLock;
3277 int substract_ranges = FALSE;
3279 range.offset = LOffset.QuadPart;
3280 range.length = LLength.QuadPart;
3284 1. for all _a_ in (Offset,+Length), all of the following is true:
3286 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3287 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3290 1.2 For each LOST lock L in S->fileLocks such that _a_ in
3291 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3296 lock_ObtainRead(&cm_scacheLock);
3298 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
3300 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
3302 if (INTERSECT_RANGE(range, fileLock->range)) {
3303 if (IS_LOCK_ACTIVE(fileLock)) {
3304 if (fileLock->key == key) {
3306 /* If there is an active lock for this client, it
3307 is safe to substract ranges.*/
3308 cm_LockRangeSubtract(&range, &fileLock->range);
3309 substract_ranges = TRUE;
3311 if (fileLock->lockType != LockRead) {
3312 code = CM_ERROR_LOCK_CONFLICT;
3316 /* even if the entire range is locked for reading,
3317 we still can't grant the lock at this point
3318 because the client may have lost locks. That
3319 is, unless we have already seen an active lock
3320 belonging to the client, in which case there
3321 can't be any lost locks for this client. */
3322 if (substract_ranges)
3323 cm_LockRangeSubtract(&range, &fileLock->range);
3325 } else if (IS_LOCK_LOST(fileLock) &&
3326 (fileLock->key == key || fileLock->lockType == LockWrite)) {
3327 code = CM_ERROR_BADFD;
3333 lock_ReleaseRead(&cm_scacheLock);
3335 osi_Log4(afsd_logp, "cm_LockCheckRead scp 0x%x offset %d length %d code 0x%x",
3336 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
3347 /* Called with scp->mx held. Returns 0 if all is clear to write the
3348 specified range by the client identified by key.
3350 long cm_LockCheckWrite(cm_scache_t *scp,
3351 LARGE_INTEGER LOffset,
3352 LARGE_INTEGER LLength,
3355 #ifndef ADVISORY_LOCKS
3357 cm_file_lock_t *fileLock;
3362 range.offset = LOffset.QuadPart;
3363 range.length = LLength.QuadPart;
3366 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
3368 2. for all _a_ in (Offset,+Length), one of the following is true:
3370 2.1 Byte _a_ of S is unowned AND there does not exist a LOST
3371 lock L such that _a_ in (L->LOffset,+L->LLength).
3373 2.2 Byte _a_ of S is owned by C under lock L AND L->LockType is
3377 lock_ObtainRead(&cm_scacheLock);
3379 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
3381 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
3383 if (INTERSECT_RANGE(range, fileLock->range)) {
3384 if (IS_LOCK_ACTIVE(fileLock)) {
3385 if (fileLock->key == key) {
3386 if (fileLock->lockType == LockWrite) {
3388 /* if there is an active lock for this client, it
3389 is safe to substract ranges */
3390 cm_LockRangeSubtract(&range, &fileLock->range);
3392 code = CM_ERROR_LOCK_CONFLICT;
3396 code = CM_ERROR_LOCK_CONFLICT;
3399 } else if (IS_LOCK_LOST(fileLock)) {
3400 code = CM_ERROR_BADFD;
3406 lock_ReleaseRead(&cm_scacheLock);
3408 osi_Log4(afsd_logp, "cm_LockCheckWrite scp 0x%x offset %d length %d code 0x%x",
3409 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
3421 static void cm_LockMarkSCacheLost(cm_scache_t * scp);
3423 /* Called with cm_scacheLock write locked */
3424 static cm_file_lock_t * cm_GetFileLock(void) {
3427 l = (cm_file_lock_t *) cm_freeFileLocks;
3429 osi_QRemove(&cm_freeFileLocks, &l->q);
3431 l = malloc(sizeof(cm_file_lock_t));
3435 memset(l, 0, sizeof(cm_file_lock_t));
3440 /* Called with cm_scacheLock write locked */
3441 static void cm_PutFileLock(cm_file_lock_t *l) {
3442 osi_QAdd(&cm_freeFileLocks, &l->q);
3445 /* called with scp->mx held. May release it during processing, but
3446 leaves it held on exit. */
3447 long cm_IntSetLock(cm_scache_t * scp, cm_user_t * userp, int lockType,
3453 struct rx_connection * callp;
3456 tfid.Volume = scp->fid.volume;
3457 tfid.Vnode = scp->fid.vnode;
3458 tfid.Unique = scp->fid.unique;
3461 osi_Log2(afsd_logp, "CALL SetLock scp 0x%p for lock %d", scp, lockType);
3463 lock_ReleaseMutex(&scp->mx);
3466 code = cm_Conn(&cfid, userp, reqp, &connp);
3470 callp = cm_GetRxConn(connp);
3471 code = RXAFS_SetLock(callp, &tfid, lockType,
3473 rx_PutConnection(callp);
3475 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
3478 code = cm_MapRPCError(code, reqp);
3480 osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
3482 osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
3485 lock_ObtainMutex(&scp->mx);
3490 /* called with scp->mx held. Releases it during processing */
3491 long cm_IntReleaseLock(cm_scache_t * scp, cm_user_t * userp,
3497 struct rx_connection * callp;
3500 tfid.Volume = scp->fid.volume;
3501 tfid.Vnode = scp->fid.vnode;
3502 tfid.Unique = scp->fid.unique;
3505 lock_ReleaseMutex(&scp->mx);
3507 osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
3510 code = cm_Conn(&cfid, userp, reqp, &connp);
3514 callp = cm_GetRxConn(connp);
3515 code = RXAFS_ReleaseLock(callp, &tfid, &volSync);
3516 rx_PutConnection(callp);
3518 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
3520 code = cm_MapRPCError(code, reqp);
3523 "CALL ReleaseLock FAILURE, code 0x%x", code);
3526 "CALL ReleaseLock SUCCESS");
3528 lock_ObtainMutex(&scp->mx);
3533 /* called with scp->mx held. May release it during processing, but
3534 will exit with lock held.
3538 - 0 if the user has permission to get the specified lock for the scp
3540 - CM_ERROR_NOACCESS if not
3542 Any other error from cm_SyncOp will be sent down untranslated.
3544 long cm_LockCheckPerms(cm_scache_t * scp,
3552 /* lock permissions are slightly tricky because of the 'i' bit.
3553 If the user has PRSFS_LOCK, she can read-lock the file. If the
3554 user has PRSFS_WRITE, she can write-lock the file. However, if
3555 the user has PRSFS_INSERT, then she can write-lock new files,
3556 but not old ones. Since we don't have information about
3557 whether a file is new or not, we assume that if the user owns
3558 the scp, then she has the permissions that are granted by
3561 osi_Log3(afsd_logp, "cm_LockCheckPerms for scp[0x%p] type[%d] user[0x%p]",
3562 scp, lock_type, userp);
3564 if (lock_type == LockRead)
3565 rights |= PRSFS_LOCK;
3566 else if (lock_type == LockWrite)
3567 rights |= PRSFS_WRITE | PRSFS_LOCK;
3574 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
3575 CM_SCACHESYNC_GETSTATUS |
3576 CM_SCACHESYNC_NEEDCALLBACK);
3578 if (code == CM_ERROR_NOACCESS &&
3579 lock_type == LockWrite &&
3580 scp->creator == userp) {
3581 /* check for PRSFS_INSERT. */
3583 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
3584 CM_SCACHESYNC_GETSTATUS |
3585 CM_SCACHESYNC_NEEDCALLBACK);
3587 if (code == CM_ERROR_NOACCESS)
3588 osi_Log0(afsd_logp, "cm_LockCheckPerms user is creator but has no INSERT bits for scp");
3591 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
3593 osi_Log1(afsd_logp, "cm_LockCheckPerms returning code %d", code);
3598 /* called with scp->mx held */
3599 long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
3600 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
3602 int allowWait, cm_user_t *userp, cm_req_t *reqp,
3603 cm_file_lock_t **lockpp)
3606 int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
3607 cm_file_lock_t *fileLock;
3610 int wait_unlock = FALSE;
3611 int force_client_lock = FALSE;
3613 osi_Log4(afsd_logp, "cm_Lock scp 0x%x type 0x%x offset %d length %d",
3614 scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
3615 osi_Log3(afsd_logp, "... allowWait %d key 0x%x:%x", allowWait,
3616 (unsigned long)(key >> 32), (unsigned long)(key & 0xffffffff));
3619 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
3621 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
3624 3.1 If L->LockType is exclusive then there does NOT exist a
3625 ACCEPTED lock M in S->fileLocks such that _a_ in
3626 (M->LOffset,+M->LLength).
3628 3.2 If L->LockType is shared then for each ACCEPTED lock M in
3629 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
3630 M->LockType is shared.
3632 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
3634 4.1 M->key != Key(C)
3636 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
3637 and (M->LOffset,+M->LLength) do not intersect.
3640 range.offset = LOffset.QuadPart;
3641 range.length = LLength.QuadPart;
3643 lock_ObtainRead(&cm_scacheLock);
3645 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
3647 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
3649 if (IS_LOCK_LOST(fileLock)) {
3650 if (fileLock->key == key) {
3651 code = CM_ERROR_BADFD;
3653 } else if (fileLock->lockType == LockWrite && INTERSECT_RANGE(range, fileLock->range)) {
3654 code = CM_ERROR_WOULDBLOCK;
3660 /* we don't need to check for deleted locks here since deleted
3661 locks are dequeued from scp->fileLocks */
3662 if (IS_LOCK_ACCEPTED(fileLock) &&
3663 INTERSECT_RANGE(range, fileLock->range)) {
3665 if ((sLockType & LOCKING_ANDX_SHARED_LOCK) == 0 ||
3666 fileLock->lockType != LockRead) {
3668 code = CM_ERROR_WOULDBLOCK;
3674 lock_ReleaseRead(&cm_scacheLock);
3676 if (code == 0 && SERVERLOCKS_ENABLED(scp)) {
3677 if (Which == scp->serverLock ||
3678 (Which == LockRead && scp->serverLock == LockWrite)) {
3680 /* we already have the lock we need */
3681 osi_Log3(afsd_logp, " we already have the correct lock. exclusives[%d], shared[%d], serverLock[%d]",
3682 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
3684 code = cm_LockCheckPerms(scp, Which, userp, reqp);
3686 /* special case: if we don't have permission to read-lock
3687 the file, then we force a clientside lock. This is to
3688 compensate for applications that obtain a read-lock for
3689 reading files off of directories that don't grant
3690 read-locks to the user. */
3691 if (code == CM_ERROR_NOACCESS && Which == LockRead) {
3692 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
3693 force_client_lock = TRUE;
3696 } else if ((scp->exclusiveLocks > 0) ||
3697 (scp->sharedLocks > 0 && scp->serverLock != LockRead)) {
3699 /* We are already waiting for some other lock. We should
3700 wait for the daemon to catch up instead of generating a
3701 flood of SetLock calls. */
3702 osi_Log3(afsd_logp, " already waiting for other lock. exclusives[%d], shared[%d], serverLock[%d]",
3703 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
3705 /* see if we have permission to create the lock in the
3707 code = cm_LockCheckPerms(scp, Which, userp, reqp);
3709 code = CM_ERROR_WOULDBLOCK;
3710 else if (code == CM_ERROR_NOACCESS && Which == LockRead) {
3711 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
3712 force_client_lock = TRUE;
3715 /* leave any other codes as-is */
3719 int check_data_version = FALSE;
3721 /* first check if we have permission to elevate or obtain
3723 code = cm_LockCheckPerms(scp, Which, userp, reqp);
3725 if (code == CM_ERROR_NOACCESS && Which == LockRead) {
3726 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
3727 force_client_lock = TRUE;
3732 if (scp->serverLock == LockRead && Which == LockWrite) {
3734 /* We want to escalate the lock to a LockWrite.
3735 Unfortunately that's not really possible without
3736 letting go of the current lock. But for now we do
3740 " attempting to UPGRADE from LockRead to LockWrite.");
3742 " dataVersion on scp: %d", scp->dataVersion);
3744 /* we assume at this point (because scp->serverLock
3745 was valid) that we had a valid server lock. */
3746 scp->lockDataVersion = scp->dataVersion;
3747 check_data_version = TRUE;
3749 code = cm_IntReleaseLock(scp, userp, reqp);
3752 /* We couldn't release the lock */
3755 scp->serverLock = -1;
3759 /* We need to obtain a server lock of type Which in order
3760 to assert this file lock */
3761 #ifndef AGGRESSIVE_LOCKS
3764 newLock = LockWrite;
3766 code = cm_IntSetLock(scp, userp, newLock, reqp);
3768 if (code == CM_ERROR_WOULDBLOCK && newLock != Which) {
3769 /* we wanted LockRead. We tried LockWrite. Now try
3774 osi_assert(newLock == LockRead);
3776 code = cm_IntSetLock(scp, userp, newLock, reqp);
3779 if (code == 0 && check_data_version &&
3780 scp->dataVersion != scp->lockDataVersion) {
3781 /* We lost a race. Although we successfully obtained
3782 a lock, someone modified the file in between. The
3783 locks have all been technically lost. */
3786 " Data version mismatch while upgrading lock.");
3788 " Data versions before=%d, after=%d",
3789 scp->lockDataVersion,
3792 " Releasing stale lock for scp 0x%x", scp);
3794 code = cm_IntReleaseLock(scp, userp, reqp);
3796 scp->serverLock = -1;
3798 code = CM_ERROR_INVAL;
3799 } else if (code == 0) {
3800 scp->serverLock = newLock;
3801 scp->lockDataVersion = scp->dataVersion;
3805 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
3806 scp->serverLock == -1) {
3807 /* Oops. We lost the lock. */
3808 cm_LockMarkSCacheLost(scp);
3811 } else if (code == 0) { /* server locks not enabled */
3813 " Skipping server lock for scp");
3818 if (code != 0 && !force_client_lock) {
3819 /* Special case error translations
3821 Applications don't expect certain errors from a
3822 LockFile/UnlockFile call. We need to translate some error
3823 code to codes that apps expect and handle. */
3825 /* We shouldn't actually need to handle this case since we
3826 simulate locks for RO scps anyway. */
3827 if (code == CM_ERROR_READONLY) {
3828 osi_Log0(afsd_logp, " Reinterpreting CM_ERROR_READONLY as CM_ERROR_NOACCESS");
3829 code = CM_ERROR_NOACCESS;
3833 if (code == 0 || (code == CM_ERROR_WOULDBLOCK && allowWait) ||
3834 force_client_lock) {
3836 /* clear the error if we are forcing a client lock, so we
3837 don't get confused later. */
3838 if (force_client_lock && code != CM_ERROR_WOULDBLOCK)
3841 lock_ObtainWrite(&cm_scacheLock);
3842 fileLock = cm_GetFileLock();
3843 lock_ReleaseWrite(&cm_scacheLock);
3845 fileLock->fid = scp->fid;
3847 fileLock->key = key;
3848 fileLock->lockType = Which;
3850 fileLock->userp = userp;
3851 fileLock->range = range;
3852 fileLock->flags = (code == 0 ? 0 :
3854 CM_FILELOCK_FLAG_WAITUNLOCK :
3855 CM_FILELOCK_FLAG_WAITLOCK));
3857 if (force_client_lock || !SERVERLOCKS_ENABLED(scp))
3858 fileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
3860 fileLock->lastUpdate = (code == 0 && !force_client_lock) ? time(NULL) : 0;
3862 lock_ObtainWrite(&cm_scacheLock);
3863 osi_QAddT(&scp->fileLocksH, &scp->fileLocksT, &fileLock->fileq);
3864 cm_HoldSCacheNoLock(scp);
3865 fileLock->scp = scp;
3866 osi_QAdd(&cm_allFileLocks, &fileLock->q);
3867 lock_ReleaseWrite(&cm_scacheLock);
3873 if (IS_LOCK_CLIENTONLY(fileLock)) {
3875 } else if (IS_LOCK_ACCEPTED(fileLock)) {
3876 if (Which == LockRead)
3879 scp->exclusiveLocks++;
3883 "cm_Lock Lock added 0x%p flags 0x%x to scp [0x%p]",
3884 fileLock, fileLock->flags, scp);
3886 " exclusives[%d] shared[%d] client[%d] serverLock[%d]",
3887 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
3888 (int)(signed char) scp->serverLock);
3891 "cm_Lock Rejecting lock (code = 0x%x)", code);
3897 static int cm_KeyEquals(cm_key_t k1, cm_key_t k2, int flags);
3899 /* Called with scp->mx held */
3900 long cm_UnlockByKey(cm_scache_t * scp,
3907 cm_file_lock_t *fileLock;
3908 osi_queue_t *q, *qn;
3911 osi_Log4(afsd_logp, "cm_UnlockByKey scp 0x%p key 0x%x:%x flags=0x%x",
3913 (unsigned long)(key >> 32),
3914 (unsigned long)(key & 0xffffffff),
3917 lock_ObtainWrite(&cm_scacheLock);
3919 for (q = scp->fileLocksH; q; q = qn) {
3922 fileLock = (cm_file_lock_t *)
3923 ((char *) q - offsetof(cm_file_lock_t, fileq));
3926 osi_Log4(afsd_logp, " Checking lock[0x%x] range[%d,+%d] type[%d]",
3928 (unsigned long) fileLock->range.offset,
3929 (unsigned long) fileLock->range.length,
3930 fileLock->lockType);
3931 osi_Log3(afsd_logp, " key[0x%x:%x] flags[0x%x]",
3932 (unsigned long)(fileLock->key >> 32),
3933 (unsigned long)(fileLock->key & 0xffffffff),
3936 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
3937 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
3938 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
3940 fileLock->fid.volume,
3941 fileLock->fid.vnode,
3942 fileLock->fid.unique);
3943 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
3944 fileLock->scp->fid.cell,
3945 fileLock->scp->fid.volume,
3946 fileLock->scp->fid.vnode,
3947 fileLock->scp->fid.unique);
3952 if (!IS_LOCK_DELETED(fileLock) &&
3953 cm_KeyEquals(fileLock->key, key, flags)) {
3954 osi_Log3(afsd_logp, "...Unlock range [%d,+%d] type %d",
3955 fileLock->range.offset,
3956 fileLock->range.length,
3957 fileLock->lockType);
3959 if (scp->fileLocksT == q)
3960 scp->fileLocksT = osi_QPrev(q);
3961 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
3963 if (IS_LOCK_CLIENTONLY(fileLock)) {
3965 } else if (IS_LOCK_ACCEPTED(fileLock)) {
3966 if (fileLock->lockType == LockRead)
3969 scp->exclusiveLocks--;
3972 fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
3974 cm_ReleaseUser(fileLock->userp);
3975 cm_ReleaseSCacheNoLock(scp);
3977 fileLock->userp = NULL;
3978 fileLock->scp = NULL;
3984 lock_ReleaseWrite(&cm_scacheLock);
3986 if (n_unlocks == 0) {
3987 osi_Log0(afsd_logp, "cm_UnlockByKey no locks found");
3988 osi_Log3(afsd_logp, " Leaving scp with exclusives[%d], shared[%d], serverLock[%d]",
3989 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
3994 osi_Log1(afsd_logp, "cm_UnlockByKey done with %d locks", n_unlocks);
3996 osi_assertx(scp->sharedLocks >= 0, "scp->sharedLocks < 0");
3997 osi_assertx(scp->exclusiveLocks >= 0, "scp->exclusiveLocks < 0");
3998 osi_assertx(scp->clientLocks >= 0, "scp->clientLocks < 0");
4000 if (!SERVERLOCKS_ENABLED(scp)) {
4001 osi_Log0(afsd_logp, " Skipping server lock for scp");
4005 /* Ideally we would go through the rest of the locks to determine
4006 * if one or more locks that were formerly in WAITUNLOCK can now
4007 * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
4008 * scp->sharedLocks accordingly. However, the retrying of locks
4009 * in that manner is done cm_RetryLock() manually.
4012 if (scp->serverLock == LockWrite &&
4013 scp->exclusiveLocks == 0 &&
4014 scp->sharedLocks > 0) {
4016 /* The serverLock should be downgraded to LockRead */
4017 osi_Log0(afsd_logp, " DOWNGRADE lock from LockWrite to LockRead");
4019 /* since scp->serverLock looked sane, we are going to assume
4020 that we have a valid server lock. */
4021 scp->lockDataVersion = scp->dataVersion;
4022 osi_Log1(afsd_logp, " dataVersion on scp = %d", scp->dataVersion);
4024 code = cm_IntReleaseLock(scp, userp, reqp);
4027 /* so we couldn't release it. Just let the lock be for now */
4031 scp->serverLock = -1;
4034 code = cm_IntSetLock(scp, userp, LockRead, reqp);
4036 if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
4037 scp->serverLock = LockRead;
4038 } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
4039 /* We lost a race condition. Although we have a valid
4040 lock on the file, the data has changed and essentially
4041 we have lost the lock we had during the transition. */
4043 osi_Log0(afsd_logp, "Data version mismatch during lock downgrade");
4044 osi_Log2(afsd_logp, " Data versions before=%d, after=%d",
4045 scp->lockDataVersion,
4048 code = cm_IntReleaseLock(scp, userp, reqp);
4050 code = CM_ERROR_INVAL;
4051 scp->serverLock = -1;
4055 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4056 (scp->serverLock == -1)) {
4058 cm_LockMarkSCacheLost(scp);
4061 /* failure here has no bearing on the return value of
4065 } else if (scp->serverLock != (-1) &&
4066 scp->exclusiveLocks == 0 &&
4067 scp->sharedLocks == 0) {
4068 /* The serverLock should be released entirely */
4070 code = cm_IntReleaseLock(scp, userp, reqp);
4073 scp->serverLock = (-1);
4078 osi_Log1(afsd_logp, "cm_UnlockByKey code 0x%x", code);
4079 osi_Log4(afsd_logp, " Leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
4080 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4081 (int)(signed char) scp->serverLock);
4086 long cm_Unlock(cm_scache_t *scp,
4087 unsigned char sLockType,
4088 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
4094 int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
4095 cm_file_lock_t *fileLock;
4097 int release_userp = FALSE;
4099 osi_Log4(afsd_logp, "cm_Unlock scp 0x%p type 0x%x offset %d length %d",
4100 scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
4101 osi_Log2(afsd_logp, "... key 0x%x:%x",
4102 (unsigned long) (key >> 32), (unsigned long) (key & 0xffffffff));
4104 lock_ObtainRead(&cm_scacheLock);
4106 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4107 fileLock = (cm_file_lock_t *)
4108 ((char *) q - offsetof(cm_file_lock_t, fileq));
4111 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
4112 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
4113 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4115 fileLock->fid.volume,
4116 fileLock->fid.vnode,
4117 fileLock->fid.unique);
4118 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4119 fileLock->scp->fid.cell,
4120 fileLock->scp->fid.volume,
4121 fileLock->scp->fid.vnode,
4122 fileLock->scp->fid.unique);
4126 if (!IS_LOCK_DELETED(fileLock) &&
4127 fileLock->key == key &&
4128 fileLock->range.offset == LOffset.QuadPart &&
4129 fileLock->range.length == LLength.QuadPart) {
4135 osi_Log0(afsd_logp, "cm_Unlock lock not found; failure");
4137 lock_ReleaseRead(&cm_scacheLock);
4139 /* The lock didn't exist anyway. *shrug* */
4143 lock_ReleaseRead(&cm_scacheLock);
4145 /* discard lock record */
4146 lock_ObtainWrite(&cm_scacheLock);
4147 if (scp->fileLocksT == q)
4148 scp->fileLocksT = osi_QPrev(q);
4149 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
4152 * Don't delete it here; let the daemon delete it, to simplify
4153 * the daemon's traversal of the list.
4156 if (IS_LOCK_CLIENTONLY(fileLock)) {
4158 } else if (IS_LOCK_ACCEPTED(fileLock)) {
4159 if (fileLock->lockType == LockRead)
4162 scp->exclusiveLocks--;
4165 fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
4166 if (userp != NULL) {
4167 cm_ReleaseUser(fileLock->userp);
4169 userp = fileLock->userp;
4170 release_userp = TRUE;
4172 fileLock->userp = NULL;
4173 cm_ReleaseSCacheNoLock(scp);
4174 fileLock->scp = NULL;
4175 lock_ReleaseWrite(&cm_scacheLock);
4177 if (!SERVERLOCKS_ENABLED(scp)) {
4178 osi_Log0(afsd_logp, " Skipping server locks for scp");
4182 /* Ideally we would go through the rest of the locks to determine
4183 * if one or more locks that were formerly in WAITUNLOCK can now
4184 * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
4185 * scp->sharedLocks accordingly. However, the retrying of locks
4186 * in that manner is done cm_RetryLock() manually.
4189 if (scp->serverLock == LockWrite &&
4190 scp->exclusiveLocks == 0 &&
4191 scp->sharedLocks > 0) {
4193 /* The serverLock should be downgraded to LockRead */
4194 osi_Log0(afsd_logp, " DOWNGRADE lock from LockWrite to LockRead");
4196 /* Since we already had a lock, we assume that there is a
4197 valid server lock. */
4198 scp->lockDataVersion = scp->dataVersion;
4199 osi_Log1(afsd_logp, " dataVersion on scp is %d", scp->dataVersion);
4201 code = cm_IntReleaseLock(scp, userp, reqp);
4204 /* so we couldn't release it. Just let the lock be for now */
4208 scp->serverLock = -1;
4211 code = cm_IntSetLock(scp, userp, LockRead, reqp);
4213 if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
4214 scp->serverLock = LockRead;
4215 } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
4216 /* Lost a race. We obtained a new lock, but that is
4217 meaningless since someone modified the file
4221 "Data version mismatch while downgrading lock");
4223 " Data versions before=%d, after=%d",
4224 scp->lockDataVersion,
4227 code = cm_IntReleaseLock(scp, userp, reqp);
4229 scp->serverLock = -1;
4230 code = CM_ERROR_INVAL;
4234 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4235 (scp->serverLock == -1)) {
4237 cm_LockMarkSCacheLost(scp);
4240 /* failure here has no bearing on the return value of
4244 } else if (scp->serverLock != (-1) &&
4245 scp->exclusiveLocks == 0 &&
4246 scp->sharedLocks == 0) {
4247 /* The serverLock should be released entirely */
4249 code = cm_IntReleaseLock(scp, userp, reqp);
4252 scp->serverLock = (-1);
4257 cm_ReleaseUser(userp);
4261 osi_Log1(afsd_logp, "cm_Unlock code 0x%x", code);
4262 osi_Log4(afsd_logp, " leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
4263 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4264 (int)(signed char) scp->serverLock);
4269 /* called with scp->mx held */
4270 static void cm_LockMarkSCacheLost(cm_scache_t * scp)
4272 cm_file_lock_t *fileLock;
4275 osi_Log1(afsd_logp, "cm_LockMarkSCacheLost scp 0x%x", scp);
4278 /* With the current code, we can't lose a lock on a RO scp */
4279 osi_assert(!(scp->flags & CM_SCACHEFLAG_RO));
4282 /* cm_scacheLock needed because we are modifying fileLock->flags */
4283 lock_ObtainWrite(&cm_scacheLock);
4285 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4287 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4289 if (IS_LOCK_ACTIVE(fileLock) &&
4290 !IS_LOCK_CLIENTONLY(fileLock)) {
4291 if (fileLock->lockType == LockRead)
4294 scp->exclusiveLocks--;
4296 fileLock->flags |= CM_FILELOCK_FLAG_LOST;
4300 scp->serverLock = -1;
4301 scp->lockDataVersion = -1;
4302 lock_ReleaseWrite(&cm_scacheLock);
4305 /* Called with no relevant locks held */
4306 void cm_CheckLocks()
4308 osi_queue_t *q, *nq;
4309 cm_file_lock_t *fileLock;
4315 struct rx_connection * callp;
4320 lock_ObtainWrite(&cm_scacheLock);
4322 cm_lockRefreshCycle++;
4324 osi_Log1(afsd_logp, "cm_CheckLocks starting lock check cycle %d", cm_lockRefreshCycle);
4326 for (q = cm_allFileLocks; q; q = nq) {
4327 fileLock = (cm_file_lock_t *) q;
4331 if (IS_LOCK_DELETED(fileLock)) {
4333 osi_QRemove(&cm_allFileLocks, q);
4334 cm_PutFileLock(fileLock);
4336 } else if (IS_LOCK_ACTIVE(fileLock) && !IS_LOCK_CLIENTONLY(fileLock)) {
4338 /* Server locks must have been enabled for us to have
4339 received an active non-client-only lock. */
4340 osi_assert(cm_enableServerLocks);
4342 scp = fileLock->scp;
4343 osi_assert(scp != NULL);
4345 cm_HoldSCacheNoLock(scp);
4348 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
4349 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
4350 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4352 fileLock->fid.volume,
4353 fileLock->fid.vnode,
4354 fileLock->fid.unique);
4355 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4356 fileLock->scp->fid.cell,
4357 fileLock->scp->fid.volume,
4358 fileLock->scp->fid.vnode,
4359 fileLock->scp->fid.unique);
4363 /* Server locks are extended once per scp per refresh
4365 if (scp->lastRefreshCycle != cm_lockRefreshCycle) {
4367 int scp_done = FALSE;
4369 osi_Log1(afsd_logp, "cm_CheckLocks Updating scp 0x%x", scp);
4371 lock_ReleaseWrite(&cm_scacheLock);
4372 lock_ObtainMutex(&scp->mx);
4374 /* did the lock change while we weren't holding the lock? */
4375 if (!IS_LOCK_ACTIVE(fileLock))
4376 goto post_syncopdone;
4378 code = cm_SyncOp(scp, NULL, fileLock->userp, &req, 0,
4379 CM_SCACHESYNC_NEEDCALLBACK
4380 | CM_SCACHESYNC_GETSTATUS
4381 | CM_SCACHESYNC_LOCK);
4385 "cm_CheckLocks SyncOp failure code 0x%x", code);
4386 goto post_syncopdone;
4389 /* cm_SyncOp releases scp->mx during which the lock
4390 may get released. */
4391 if (!IS_LOCK_ACTIVE(fileLock))
4392 goto pre_syncopdone;
4394 if (scp->serverLock != -1) {
4398 tfid.Volume = scp->fid.volume;
4399 tfid.Vnode = scp->fid.vnode;
4400 tfid.Unique = scp->fid.unique;
4402 userp = fileLock->userp;
4404 osi_Log3(afsd_logp, "CALL ExtendLock lock 0x%p for scp=0x%p with lock %d",
4407 (int) scp->serverLock);
4409 lock_ReleaseMutex(&scp->mx);
4412 code = cm_Conn(&cfid, userp,
4417 callp = cm_GetRxConn(connp);
4418 code = RXAFS_ExtendLock(callp, &tfid,
4420 rx_PutConnection(callp);
4422 osi_Log1(afsd_logp, " ExtendLock returns %d", code);
4424 } while (cm_Analyze(connp, userp, &req,
4425 &cfid, &volSync, NULL, NULL,
4428 code = cm_MapRPCError(code, &req);
4430 lock_ObtainMutex(&scp->mx);
4433 osi_Log1(afsd_logp, "CALL ExtendLock FAILURE, code 0x%x", code);
4435 osi_Log0(afsd_logp, "CALL ExtendLock SUCCESS");
4436 scp->lockDataVersion = scp->dataVersion;
4439 if ((code == EINVAL || code == CM_ERROR_INVAL) &&
4440 scp->lockDataVersion == scp->dataVersion) {
4444 (scp->exclusiveLocks > 0) ? LockWrite: LockRead;
4446 /* we might still have a chance to obtain a
4449 code = cm_IntSetLock(scp, userp, lockType, &req);
4452 code = CM_ERROR_INVAL;
4453 } else if (scp->lockDataVersion != scp->dataVersion) {
4455 /* now check if we still have the file at
4456 the right data version. */
4458 "Data version mismatch on scp 0x%p",
4461 " Data versions: before=%d, after=%d",
4462 scp->lockDataVersion,
4465 code = cm_IntReleaseLock(scp, userp, &req);
4467 code = CM_ERROR_INVAL;
4471 if (code == EINVAL || code == CM_ERROR_INVAL) {
4472 cm_LockMarkSCacheLost(scp);
4476 /* interestingly, we have found an active lock
4477 belonging to an scache that has no
4479 cm_LockMarkSCacheLost(scp);
4486 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
4489 lock_ReleaseMutex(&scp->mx);
4491 lock_ObtainWrite(&cm_scacheLock);
4494 fileLock->lastUpdate = time(NULL);
4498 scp->lastRefreshCycle = cm_lockRefreshCycle;
4501 /* we have already refreshed the locks on this scp */
4502 fileLock->lastUpdate = time(NULL);
4505 cm_ReleaseSCacheNoLock(scp);
4507 } else if (IS_LOCK_ACTIVE(fileLock) && IS_LOCK_CLIENTONLY(fileLock)) {
4508 /* TODO: Check callbacks */
4512 lock_ReleaseWrite(&cm_scacheLock);
4513 osi_Log1(afsd_logp, "cm_CheckLocks completes lock check cycle %d", cm_lockRefreshCycle);
4516 /* NOT called with scp->mx held. */
4517 long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
4520 cm_scache_t *scp = NULL;
4521 cm_file_lock_t *fileLock;
4525 int force_client_lock = FALSE;
4529 if (client_is_dead) {
4530 code = CM_ERROR_TIMEDOUT;
4534 lock_ObtainRead(&cm_scacheLock);
4536 osi_Log2(afsd_logp, "cm_RetryLock checking lock %p (scp=%p)", oldFileLock, oldFileLock->scp);
4537 osi_Log4(afsd_logp, " offset(%x:%x) length(%x:%x)",
4538 (unsigned)(oldFileLock->range.offset >> 32),
4539 (unsigned)(oldFileLock->range.offset & 0xffffffff),
4540 (unsigned)(oldFileLock->range.length >> 32),
4541 (unsigned)(oldFileLock->range.length & 0xffffffff));
4542 osi_Log3(afsd_logp, " key(%x:%x) flags=%x",
4543 (unsigned)(oldFileLock->key >> 32),
4544 (unsigned)(oldFileLock->key & 0xffffffff),
4545 (unsigned)(oldFileLock->flags));
4547 /* if the lock has already been granted, then we have nothing to do */
4548 if (IS_LOCK_ACTIVE(oldFileLock)) {
4549 lock_ReleaseRead(&cm_scacheLock);
4550 osi_Log0(afsd_logp, "cm_RetryLock lock already granted");
4554 /* we can't do anything with lost or deleted locks at the moment. */
4555 if (IS_LOCK_LOST(oldFileLock) || IS_LOCK_DELETED(oldFileLock)) {
4556 code = CM_ERROR_BADFD;
4557 osi_Log0(afsd_logp, "cm_RetryLock lock is lost or deleted");
4558 lock_ReleaseRead(&cm_scacheLock);
4562 scp = oldFileLock->scp;
4564 osi_assert(scp != NULL);
4566 lock_ReleaseRead(&cm_scacheLock);
4567 lock_ObtainMutex(&scp->mx);
4569 code = cm_LockCheckPerms(scp, oldFileLock->lockType,
4573 if (code == CM_ERROR_NOACCESS && oldFileLock->lockType == LockRead) {
4574 force_client_lock = TRUE;
4577 lock_ReleaseMutex(&scp->mx);
4581 lock_ObtainWrite(&cm_scacheLock);
4583 /* Check if we already have a sufficient server lock to allow this
4584 lock to go through. */
4585 if (IS_LOCK_WAITLOCK(oldFileLock) &&
4586 (!SERVERLOCKS_ENABLED(scp) ||
4587 scp->serverLock == oldFileLock->lockType ||
4588 scp->serverLock == LockWrite)) {
4590 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
4592 if (SERVERLOCKS_ENABLED(scp)) {
4593 osi_Log1(afsd_logp, "cm_RetryLock Server lock (%d) is sufficient for lock. Granting",
4594 (int) scp->serverLock);
4596 osi_Log0(afsd_logp, "cm_RetryLock skipping server lock for scp");
4599 lock_ReleaseWrite(&cm_scacheLock);
4600 lock_ReleaseMutex(&scp->mx);
4605 if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
4607 /* check if the conflicting locks have dissappeared already */
4608 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4610 fileLock = (cm_file_lock_t *)
4611 ((char *) q - offsetof(cm_file_lock_t, fileq));
4613 if (IS_LOCK_LOST(fileLock)) {
4614 if (fileLock->key == oldFileLock->key) {
4615 code = CM_ERROR_BADFD;
4616 oldFileLock->flags |= CM_FILELOCK_FLAG_LOST;
4617 osi_Log1(afsd_logp, " found lost lock %p for same key. Marking lock as lost",
4620 } else if (fileLock->lockType == LockWrite &&
4621 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
4622 osi_Log1(afsd_logp, " found conflicting LOST lock %p", fileLock);
4623 code = CM_ERROR_WOULDBLOCK;
4628 if (IS_LOCK_ACCEPTED(fileLock) &&
4629 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
4631 if (oldFileLock->lockType != LockRead ||
4632 fileLock->lockType != LockRead) {
4634 osi_Log1(afsd_logp, " found conflicting lock %p", fileLock);
4635 code = CM_ERROR_WOULDBLOCK;
4643 lock_ReleaseWrite(&cm_scacheLock);
4644 lock_ReleaseMutex(&scp->mx);
4649 /* when we get here, the lock is either a WAITUNLOCK or WAITLOCK.
4650 If it is WAITUNLOCK, then we didn't find any conflicting lock
4651 but we haven't verfied whether the serverLock is sufficient to
4652 assert it. If it is WAITLOCK, then the serverLock is
4653 insufficient to assert it. Eitherway, we are ready to accept
4654 the lock as either ACTIVE or WAITLOCK depending on the
4657 /* First, promote the WAITUNLOCK to a WAITLOCK */
4658 if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
4659 if (oldFileLock->lockType == LockRead)
4662 scp->exclusiveLocks++;
4664 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITUNLOCK;
4665 oldFileLock->flags |= CM_FILELOCK_FLAG_WAITLOCK;
4668 if (force_client_lock ||
4669 !SERVERLOCKS_ENABLED(scp) ||
4670 scp->serverLock == oldFileLock->lockType ||
4671 (oldFileLock->lockType == LockRead &&
4672 scp->serverLock == LockWrite)) {
4674 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
4676 if ((force_client_lock ||
4677 !SERVERLOCKS_ENABLED(scp)) &&
4678 !IS_LOCK_CLIENTONLY(oldFileLock)) {
4680 oldFileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
4682 if (oldFileLock->lockType == LockRead)
4685 scp->exclusiveLocks--;
4690 lock_ReleaseWrite(&cm_scacheLock);
4691 lock_ReleaseMutex(&scp->mx);
4698 code = cm_SyncOp(scp, NULL, oldFileLock->userp, &req, 0,
4699 CM_SCACHESYNC_NEEDCALLBACK
4700 | CM_SCACHESYNC_GETSTATUS
4701 | CM_SCACHESYNC_LOCK);
4703 osi_Log1(smb_logp, "cm_RetryLock SyncOp failure code 0x%x", code);
4704 lock_ReleaseWrite(&cm_scacheLock);
4705 goto post_syncopdone;
4708 if (!IS_LOCK_WAITLOCK(oldFileLock))
4709 goto pre_syncopdone;
4711 userp = oldFileLock->userp;
4713 #ifndef AGGRESSIVE_LOCKS
4714 newLock = oldFileLock->lockType;
4716 newLock = LockWrite;
4719 lock_ReleaseWrite(&cm_scacheLock);
4721 code = cm_IntSetLock(scp, userp, newLock, &req);
4724 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
4730 if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
4731 lock_ObtainWrite(&cm_scacheLock);
4732 if (scp->fileLocksT == &oldFileLock->fileq)
4733 scp->fileLocksT = osi_QPrev(&oldFileLock->fileq);
4734 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, &oldFileLock->fileq);
4735 lock_ReleaseWrite(&cm_scacheLock);
4736 } else if (code == 0 && IS_LOCK_WAITLOCK(oldFileLock)) {
4737 scp->serverLock = newLock;
4739 lock_ReleaseMutex(&scp->mx);
4742 lock_ObtainWrite(&cm_scacheLock);
4744 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
4745 } else if (code != CM_ERROR_WOULDBLOCK) {
4746 oldFileLock->flags |= CM_FILELOCK_FLAG_DELETED;
4747 cm_ReleaseUser(oldFileLock->userp);
4748 oldFileLock->userp = NULL;
4749 if (oldFileLock->scp) {
4750 cm_ReleaseSCacheNoLock(oldFileLock->scp);
4751 oldFileLock->scp = NULL;
4754 lock_ReleaseWrite(&cm_scacheLock);
4759 cm_key_t cm_GenerateKey(unsigned int session_id, unsigned long process_id, unsigned int file_id)
4762 osi_assert((process_id & 0xffffffff) == process_id);
4763 osi_assert((session_id & 0xffff) == session_id);
4764 osi_assert((file_id & 0xffff) == file_id);
4768 (((cm_key_t) (process_id & 0xffffffff)) << 32) |
4769 (((cm_key_t) (session_id & 0xffff)) << 16) |
4770 (((cm_key_t) (file_id & 0xffff)));
4773 static int cm_KeyEquals(cm_key_t k1, cm_key_t k2, int flags)
4775 if (flags & CM_UNLOCK_BY_FID) {
4776 return ((k1 & 0xffffffff) == (k2 & 0xffffffff));
4782 void cm_ReleaseAllLocks(void)
4788 cm_file_lock_t *fileLock;
4791 for (i = 0; i < cm_data.hashTableSize; i++)
4793 for ( scp = cm_data.hashTablep[i]; scp; scp = scp->nextp ) {
4794 while (scp->fileLocksH != NULL) {
4795 lock_ObtainMutex(&scp->mx);
4796 lock_ObtainWrite(&cm_scacheLock);
4797 if (!scp->fileLocksH) {
4798 lock_ReleaseWrite(&cm_scacheLock);
4799 lock_ReleaseMutex(&scp->mx);
4802 fileLock = (cm_file_lock_t *)((char *) scp->fileLocksH - offsetof(cm_file_lock_t, fileq));
4803 userp = fileLock->userp;
4805 key = fileLock->key;
4806 cm_HoldSCacheNoLock(scp);
4807 lock_ReleaseWrite(&cm_scacheLock);
4808 cm_UnlockByKey(scp, key, 0, userp, &req);
4809 cm_ReleaseSCache(scp);
4810 cm_ReleaseUser(userp);
4811 lock_ReleaseMutex(&scp->mx);