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_Gen8Dot3Name(cm_dirEntry_t *dep, char *shortName, char **shortNameEndp)
184 int vnode = ntohl(dep->fid.vnode);
186 int validExtension = 0;
187 char tc, *temp, *name;
189 /* Unparse the file's vnode number to get a "uniquifier" */
191 number[nsize] = cm_8Dot3Mapping[vnode % cm_8Dot3MapSize];
193 vnode /= cm_8Dot3MapSize;
197 * Look for valid extension. There has to be a dot, and
198 * at least one of the characters following has to be legal.
200 lastDot = strrchr(dep->name, '.');
202 temp = lastDot; temp++;
204 if (cm_LegalChars[tc])
210 /* Copy name characters */
212 for (i = 0, name = dep->name;
213 i < (7 - nsize) && name != lastDot; ) {
218 if (!cm_LegalChars[tc])
221 *shortName++ = toupper(tc);
227 /* Copy uniquifier characters */
228 memcpy(shortName, number, nsize);
231 if (validExtension) {
232 /* Copy extension characters */
233 *shortName++ = *lastDot++; /* copy dot */
234 for (i = 0, tc = *lastDot++;
237 if (cm_LegalChars[tc]) {
239 *shortName++ = toupper(tc);
248 *shortNameEndp = shortName;
251 /* return success if we can open this file in this mode */
252 long cm_CheckOpen(cm_scache_t *scp, int openMode, int trunc, cm_user_t *userp,
259 if (openMode != 1) rights |= PRSFS_READ;
260 if (openMode == 1 || openMode == 2 || trunc) rights |= PRSFS_WRITE;
262 lock_ObtainMutex(&scp->mx);
264 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
265 CM_SCACHESYNC_GETSTATUS
266 | CM_SCACHESYNC_NEEDCALLBACK
267 | CM_SCACHESYNC_LOCK);
270 ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
271 scp->fileType == CM_SCACHETYPE_FILE) {
274 unsigned int sLockType;
275 LARGE_INTEGER LOffset, LLength;
277 /* Check if there's some sort of lock on the file at the
280 key = cm_GenerateKey(CM_SESSION_CMINT,0,0);
282 if (rights & PRSFS_WRITE)
285 sLockType = LOCKING_ANDX_SHARED_LOCK;
287 LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
288 LOffset.LowPart = CM_FLSHARE_OFFSET_LOW;
289 LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
290 LLength.LowPart = CM_FLSHARE_LENGTH_LOW;
292 code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
295 cm_Unlock(scp, sLockType, LOffset, LLength, key, userp, reqp);
297 /* In this case, we allow the file open to go through even
298 though we can't enforce mandatory locking on the
300 if (code == CM_ERROR_NOACCESS &&
301 !(rights & PRSFS_WRITE))
305 case CM_ERROR_ALLOFFLINE:
306 case CM_ERROR_ALLDOWN:
307 case CM_ERROR_ALLBUSY:
308 case CM_ERROR_TIMEDOUT:
310 case CM_ERROR_WOULDBLOCK:
313 code = CM_ERROR_SHARING_VIOLATION;
318 } else if (code != 0) {
322 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
326 lock_ReleaseMutex(&scp->mx);
331 /* return success if we can open this file in this mode */
332 long cm_CheckNTOpen(cm_scache_t *scp, unsigned int desiredAccess,
333 unsigned int createDisp, cm_user_t *userp, cm_req_t *reqp)
338 /* Always allow delete; the RPC will tell us if it's OK */
339 if (desiredAccess == DELETE)
344 if (desiredAccess & AFS_ACCESS_READ)
345 rights |= PRSFS_READ;
347 if ((desiredAccess & AFS_ACCESS_WRITE)
349 rights |= PRSFS_WRITE;
351 lock_ObtainMutex(&scp->mx);
353 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
354 CM_SCACHESYNC_GETSTATUS
355 | CM_SCACHESYNC_NEEDCALLBACK
356 | CM_SCACHESYNC_LOCK);
359 * If the open will fail because the volume is readonly, then we will
360 * return an access denied error instead. This is to help brain-dead
361 * apps run correctly on replicated volumes.
362 * See defect 10007 for more information.
364 if (code == CM_ERROR_READONLY)
365 code = CM_ERROR_NOACCESS;
368 ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
369 scp->fileType == CM_SCACHETYPE_FILE) {
371 unsigned int sLockType;
372 LARGE_INTEGER LOffset, LLength;
374 /* Check if there's some sort of lock on the file at the
377 key = cm_GenerateKey(CM_SESSION_CMINT,0,0);
378 if (rights & PRSFS_WRITE)
381 sLockType = LOCKING_ANDX_SHARED_LOCK;
383 /* single byte lock at offset 0x0100 0000 0000 0000 */
384 LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
385 LOffset.LowPart = CM_FLSHARE_OFFSET_LOW;
386 LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
387 LLength.LowPart = CM_FLSHARE_LENGTH_LOW;
389 code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
392 cm_Unlock(scp, sLockType, LOffset, LLength, key, userp, reqp);
394 /* In this case, we allow the file open to go through even
395 though we can't enforce mandatory locking on the
397 if (code == CM_ERROR_NOACCESS &&
398 !(rights & PRSFS_WRITE))
402 case CM_ERROR_ALLOFFLINE:
403 case CM_ERROR_ALLDOWN:
404 case CM_ERROR_ALLBUSY:
405 case CM_ERROR_TIMEDOUT:
407 case CM_ERROR_WOULDBLOCK:
410 code = CM_ERROR_SHARING_VIOLATION;
414 } else if (code != 0) {
418 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
421 lock_ReleaseMutex(&scp->mx);
427 * When CAP_NT_SMBS has been negotiated, deletion (of files or directories) is
428 * done in three steps:
429 * (1) open for deletion (NT_CREATE_AND_X)
430 * (2) set for deletion on close (NT_TRANSACTION2, SET_FILE_INFO)
432 * We must not do the RPC until step 3. But if we are going to return an error
433 * code (e.g. directory not empty), we must return it by step 2, otherwise most
434 * clients will not notice it. So we do a preliminary check. For deleting
435 * files, this is almost free, since we have already done the RPC to get the
436 * parent directory's status bits. But for deleting directories, we must do an
437 * additional RPC to get the directory's data to check if it is empty. Sigh.
439 long cm_CheckNTDelete(cm_scache_t *dscp, cm_scache_t *scp, cm_user_t *userp,
446 unsigned short *hashTable;
448 int BeyondPage = 0, HaveDot = 0, HaveDotDot = 0;
450 /* First check permissions */
451 lock_ObtainMutex(&dscp->mx);
452 code = cm_SyncOp(dscp, NULL, userp, reqp, PRSFS_DELETE,
453 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
454 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
455 lock_ReleaseMutex(&dscp->mx);
459 /* If deleting directory, must be empty */
461 if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
464 thyper.HighPart = 0; thyper.LowPart = 0;
465 lock_ObtainRead(&scp->bufCreateLock);
466 code = buf_Get(scp, &thyper, &bufferp);
467 lock_ReleaseRead(&scp->bufCreateLock);
471 lock_ObtainMutex(&bufferp->mx);
472 lock_ObtainMutex(&scp->mx);
474 code = cm_SyncOp(scp, bufferp, userp, reqp, 0,
475 CM_SCACHESYNC_NEEDCALLBACK
477 | CM_SCACHESYNC_BUFLOCKED);
481 if (cm_HaveBuffer(scp, bufferp, 1))
484 /* otherwise, load the buffer and try again */
485 lock_ReleaseMutex(&bufferp->mx);
486 code = cm_GetBuffer(scp, bufferp, NULL, userp, reqp);
487 lock_ReleaseMutex(&scp->mx);
488 lock_ObtainMutex(&bufferp->mx);
489 lock_ObtainMutex(&scp->mx);
490 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
495 /* We try to determine emptiness without looking beyond the first page,
496 * and without assuming "." and ".." are present and are on the first
497 * page (though these assumptions might, after all, be reasonable).
499 hashTable = (unsigned short *)(bufferp->datap + (32 * 5));
500 for (i=0; i<128; i++) {
501 idx = ntohs(hashTable[i]);
507 dep = (cm_dirEntry_t *)(bufferp->datap + (32 * idx));
508 if (strcmp(dep->name, ".") == 0)
510 else if (strcmp(dep->name, "..") == 0)
513 code = CM_ERROR_NOTEMPTY;
516 idx = ntohs(dep->next);
519 if (BeyondPage && HaveDot && HaveDotDot)
520 code = CM_ERROR_NOTEMPTY;
524 lock_ReleaseMutex(&bufferp->mx);
525 buf_Release(bufferp);
526 lock_ReleaseMutex(&scp->mx);
531 * Iterate through all entries in a directory.
532 * When the function funcp is called, the buffer is locked but the
533 * directory vnode is not.
535 * If the retscp parameter is not NULL, the parmp must be a
536 * cm_lookupSearch_t object.
538 long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
539 osi_hyper_t *startOffsetp, cm_user_t *userp, cm_req_t *reqp,
540 cm_scache_t **retscp)
547 osi_hyper_t dirLength;
548 osi_hyper_t bufferOffset;
549 osi_hyper_t curOffset;
553 cm_pageHeader_t *pageHeaderp;
555 long nextEntryCookie;
556 int numDirChunks; /* # of 32 byte dir chunks in this entry */
558 /* get the directory size */
559 lock_ObtainMutex(&scp->mx);
560 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_LOOKUP,
561 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
563 lock_ReleaseMutex(&scp->mx);
567 if (scp->fileType != CM_SCACHETYPE_DIRECTORY) {
568 lock_ReleaseMutex(&scp->mx);
569 return CM_ERROR_NOTDIR;
572 if (retscp) /* if this is a lookup call */
574 cm_lookupSearch_t* sp = parmp;
576 #ifdef AFS_FREELANCE_CLIENT
577 /* Freelance entries never end up in the DNLC because they
578 * do not have an associated cm_server_t
580 if ( !(cm_freelanceEnabled &&
581 sp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
582 sp->fid.volume==AFS_FAKE_ROOT_VOL_ID ) )
583 #endif /* AFS_FREELANCE_CLIENT */
585 int casefold = sp->caseFold;
586 sp->caseFold = 0; /* we have a strong preference for exact matches */
587 if ( *retscp = cm_dnlcLookup(scp, sp)) /* dnlc hit */
589 sp->caseFold = casefold;
590 lock_ReleaseMutex(&scp->mx);
593 sp->caseFold = casefold;
598 * XXX We only get the length once. It might change when we drop the
601 dirLength = scp->length;
603 lock_ReleaseMutex(&scp->mx);
606 bufferOffset.LowPart = bufferOffset.HighPart = 0;
608 curOffset = *startOffsetp;
610 curOffset.HighPart = 0;
611 curOffset.LowPart = 0;
615 /* make sure that curOffset.LowPart doesn't point to the first
616 * 32 bytes in the 2nd through last dir page, and that it
617 * doesn't point at the first 13 32-byte chunks in the first
618 * dir page, since those are dir and page headers, and don't
619 * contain useful information.
621 temp = curOffset.LowPart & (2048-1);
622 if (curOffset.HighPart == 0 && curOffset.LowPart < 2048) {
623 /* we're in the first page */
624 if (temp < 13*32) temp = 13*32;
627 /* we're in a later dir page */
628 if (temp < 32) temp = 32;
631 /* make sure the low order 5 bits are zero */
634 /* now put temp bits back ito curOffset.LowPart */
635 curOffset.LowPart &= ~(2048-1);
636 curOffset.LowPart |= temp;
638 /* check if we've passed the dir's EOF */
639 if (LargeIntegerGreaterThanOrEqualTo(curOffset, dirLength))
642 /* see if we can use the bufferp we have now; compute in which
643 * page the current offset would be, and check whether that's
644 * the offset of the buffer we have. If not, get the buffer.
646 thyper.HighPart = curOffset.HighPart;
647 thyper.LowPart = curOffset.LowPart & ~(cm_data.buf_blockSize-1);
648 if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
651 lock_ReleaseMutex(&bufferp->mx);
652 buf_Release(bufferp);
656 lock_ObtainRead(&scp->bufCreateLock);
657 code = buf_Get(scp, &thyper, &bufferp);
658 lock_ReleaseRead(&scp->bufCreateLock);
660 /* if buf_Get() fails we do not have a buffer object to lock */
666 /* for the IFS version, we bulkstat the dirents because this
667 routine is used in place of smb_ReceiveCoreSearchDir. our
668 other option is to modify smb_ReceiveCoreSearchDir itself,
669 but this seems to be the proper use for cm_ApplyDir. */
670 lock_ObtainMutex(&scp->mx);
671 if ((scp->flags & CM_SCACHEFLAG_BULKSTATTING) == 0
672 && (scp->bulkStatProgress.QuadPart <= thyper.QuadPart))
674 scp->flags |= CM_SCACHEFLAG_BULKSTATTING;
675 code = cm_TryBulkStat(scp, &thyper, userp, reqp);
676 scp->flags &= ~CM_SCACHEFLAG_BULKSTATTING;
677 scp->bulkStatProgress = thyper;
679 lock_ReleaseMutex(&scp->mx);
682 lock_ObtainMutex(&bufferp->mx);
683 bufferOffset = thyper;
685 /* now get the data in the cache */
687 lock_ObtainMutex(&scp->mx);
688 code = cm_SyncOp(scp, bufferp, userp, reqp,
690 CM_SCACHESYNC_NEEDCALLBACK
692 | CM_SCACHESYNC_BUFLOCKED);
694 lock_ReleaseMutex(&scp->mx);
697 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
699 if (cm_HaveBuffer(scp, bufferp, 1)) {
700 lock_ReleaseMutex(&scp->mx);
704 /* otherwise, load the buffer and try again */
705 lock_ReleaseMutex(&bufferp->mx);
706 code = cm_GetBuffer(scp, bufferp, NULL, userp,
708 lock_ReleaseMutex(&scp->mx);
709 lock_ObtainMutex(&bufferp->mx);
714 lock_ReleaseMutex(&bufferp->mx);
715 buf_Release(bufferp);
719 } /* if (wrong buffer) ... */
721 /* now we have the buffer containing the entry we're interested
722 * in; copy it out if it represents a non-deleted entry.
724 entryInDir = curOffset.LowPart & (2048-1);
725 entryInBuffer = curOffset.LowPart & (cm_data.buf_blockSize - 1);
727 /* page header will help tell us which entries are free. Page
728 * header can change more often than once per buffer, since
729 * AFS 3 dir page size may be less than (but not more than) a
730 * buffer package buffer.
732 /* only look intra-buffer */
733 temp = curOffset.LowPart & (cm_data.buf_blockSize - 1);
734 temp &= ~(2048 - 1); /* turn off intra-page bits */
735 pageHeaderp = (cm_pageHeader_t *) (bufferp->datap + temp);
737 /* now determine which entry we're looking at in the page. If
738 * it is free (there's a free bitmap at the start of the dir),
739 * we should skip these 32 bytes.
741 slotInPage = (entryInDir & 0x7e0) >> 5;
742 if (!(pageHeaderp->freeBitmap[slotInPage>>3]
743 & (1 << (slotInPage & 0x7)))) {
744 /* this entry is free */
745 numDirChunks = 1; /* only skip this guy */
749 tp = bufferp->datap + entryInBuffer;
750 dep = (cm_dirEntry_t *) tp; /* now points to AFS3 dir entry */
752 /* while we're here, compute the next entry's location, too,
753 * since we'll need it when writing out the cookie into the
754 * dir listing stream.
756 numDirChunks = cm_NameEntries(dep->name, NULL);
758 /* compute the offset of the cookie representing the next entry */
759 nextEntryCookie = curOffset.LowPart
760 + (CM_DIR_CHUNKSIZE * numDirChunks);
762 if (dep->fid.vnode != 0) {
763 /* this is one of the entries to use: it is not deleted */
764 code = (*funcp)(scp, dep, parmp, &curOffset);
767 } /* if we're including this name */
770 /* and adjust curOffset to be where the new cookie is */
772 thyper.LowPart = CM_DIR_CHUNKSIZE * numDirChunks;
773 curOffset = LargeIntegerAdd(thyper, curOffset);
774 } /* while copying data for dir listing */
776 /* release the mutex */
778 lock_ReleaseMutex(&bufferp->mx);
779 buf_Release(bufferp);
784 int cm_NoneUpper(char *s)
788 if (c >= 'A' && c <= 'Z')
793 int cm_NoneLower(char *s)
797 if (c >= 'a' && c <= 'z')
802 long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
805 cm_lookupSearch_t *sp;
810 sp = (cm_lookupSearch_t *) rockp;
812 matchName = dep->name;
814 match = cm_stricmp(matchName, sp->searchNamep);
816 match = strcmp(matchName, sp->searchNamep);
820 && !cm_Is8Dot3(dep->name)) {
821 matchName = shortName;
822 cm_Gen8Dot3Name(dep, shortName, NULL);
824 match = cm_stricmp(matchName, sp->searchNamep);
826 match = strcmp(matchName, sp->searchNamep);
836 if (!sp->caseFold || matchName == shortName) {
837 sp->fid.vnode = ntohl(dep->fid.vnode);
838 sp->fid.unique = ntohl(dep->fid.unique);
839 return CM_ERROR_STOPNOW;
843 * If we get here, we are doing a case-insensitive search, and we
844 * have found a match. Now we determine what kind of match it is:
845 * exact, lower-case, upper-case, or none of the above. This is done
846 * in order to choose among matches, if there are more than one.
849 /* Exact matches are the best. */
850 match = strcmp(matchName, sp->searchNamep);
853 sp->fid.vnode = ntohl(dep->fid.vnode);
854 sp->fid.unique = ntohl(dep->fid.unique);
855 return CM_ERROR_STOPNOW;
858 /* Lower-case matches are next. */
861 if (cm_NoneUpper(matchName)) {
866 /* Upper-case matches are next. */
869 if (cm_NoneLower(matchName)) {
874 /* General matches are last. */
880 sp->fid.vnode = ntohl(dep->fid.vnode);
881 sp->fid.unique = ntohl(dep->fid.unique);
885 /* read the contents of a mount point into the appropriate string.
886 * called with locked scp, and returns with locked scp.
888 long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
895 if (scp->mountPointStringp[0])
898 /* otherwise, we have to read it in */
899 lock_ReleaseMutex(&scp->mx);
901 lock_ObtainRead(&scp->bufCreateLock);
902 thyper.LowPart = thyper.HighPart = 0;
903 code = buf_Get(scp, &thyper, &bufp);
904 lock_ReleaseRead(&scp->bufCreateLock);
906 lock_ObtainMutex(&scp->mx);
911 code = cm_SyncOp(scp, bufp, userp, reqp, 0,
912 CM_SCACHESYNC_READ | CM_SCACHESYNC_NEEDCALLBACK);
916 cm_SyncOpDone(scp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
919 if (cm_HaveBuffer(scp, bufp, 0))
922 /* otherwise load buffer */
923 code = cm_GetBuffer(scp, bufp, NULL, userp, reqp);
928 /* locked, has callback, has valid data in buffer */
929 if ((tlen = scp->length.LowPart) > 1000)
930 return CM_ERROR_TOOBIG;
932 code = CM_ERROR_INVAL;
936 /* someone else did the work while we were out */
937 if (scp->mountPointStringp[0]) {
942 /* otherwise, copy out the link */
943 memcpy(scp->mountPointStringp, bufp->datap, tlen);
945 /* now make it null-terminated. Note that the original contents of a
946 * link that is a mount point is "#volname." where "." is there just to
947 * be turned into a null. That is, we can trash the last char of the
948 * link without damaging the vol name. This is a stupid convention,
949 * but that's the protocol.
951 scp->mountPointStringp[tlen-1] = 0;
960 /* called with a locked scp and chases the mount point, yielding outScpp.
961 * scp remains locked, just for simplicity of describing the interface.
963 long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
964 cm_req_t *reqp, cm_scache_t **outScpp)
979 if (scp->mountRootFid.cell != 0 && scp->mountRootGen >= cm_data.mountRootGen) {
980 tfid = scp->mountRootFid;
981 lock_ReleaseMutex(&scp->mx);
982 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
983 lock_ObtainMutex(&scp->mx);
987 /* parse the volume name */
988 mpNamep = scp->mountPointStringp;
990 return CM_ERROR_NOSUCHPATH;
991 tlen = (int)strlen(scp->mountPointStringp);
992 mtType = *scp->mountPointStringp;
993 cellNamep = malloc(tlen);
994 volNamep = malloc(tlen);
996 cp = strrchr(mpNamep, ':');
998 /* cellular mount point */
999 memset(cellNamep, 0, tlen);
1000 strncpy(cellNamep, mpNamep+1, cp - mpNamep - 1);
1001 strcpy(volNamep, cp+1);
1002 /* now look up the cell */
1003 cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE);
1007 strcpy(volNamep, mpNamep+1);
1009 cellp = cm_FindCellByID(scp->fid.cell);
1013 code = CM_ERROR_NOSUCHCELL;
1017 vnLength = strlen(volNamep);
1018 if (vnLength >= 8 && strcmp(volNamep + vnLength - 7, ".backup") == 0)
1020 else if (vnLength >= 10
1021 && strcmp(volNamep + vnLength - 9, ".readonly") == 0)
1026 /* check for backups within backups */
1028 && (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
1029 == CM_SCACHEFLAG_RO) {
1030 code = CM_ERROR_NOSUCHVOLUME;
1034 /* now we need to get the volume */
1035 lock_ReleaseMutex(&scp->mx);
1036 code = cm_GetVolumeByName(cellp, volNamep, userp, reqp, 0, &volp);
1037 lock_ObtainMutex(&scp->mx);
1040 /* save the parent of the volume root for this is the
1041 * place where the volume is mounted and we must remember
1042 * this in the volume structure rather than just in the
1043 * scache entry lest the scache entry gets recycled
1046 lock_ObtainMutex(&volp->mx);
1047 volp->dotdotFid = dscp->fid;
1048 lock_ReleaseMutex(&volp->mx);
1050 scp->mountRootFid.cell = cellp->cellID;
1051 /* if the mt pt is in a read-only volume (not just a
1052 * backup), and if there is a read-only volume for the
1053 * target, and if this is a type '#' mount point, use
1054 * the read-only, otherwise use the one specified.
1056 if (mtType == '#' && (scp->flags & CM_SCACHEFLAG_PURERO)
1057 && volp->roID != 0 && type == RWVOL)
1060 scp->mountRootFid.volume = volp->roID;
1061 else if (type == BACKVOL)
1062 scp->mountRootFid.volume = volp->bkID;
1064 scp->mountRootFid.volume = volp->rwID;
1066 /* the rest of the fid is a magic number */
1067 scp->mountRootFid.vnode = 1;
1068 scp->mountRootFid.unique = 1;
1069 scp->mountRootGen = cm_data.mountRootGen;
1071 tfid = scp->mountRootFid;
1072 lock_ReleaseMutex(&scp->mx);
1073 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
1074 lock_ObtainMutex(&scp->mx);
1083 long cm_LookupInternal(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
1084 cm_req_t *reqp, cm_scache_t **outpScpp)
1087 int dnlcHit = 1; /* did we hit in the dnlc? yes, we did */
1088 cm_scache_t *tscp = NULL;
1089 cm_scache_t *mountedScp;
1090 cm_lookupSearch_t rock;
1093 if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
1094 && strcmp(namep, "..") == 0) {
1095 if (dscp->dotdotFid.volume == 0)
1096 return CM_ERROR_NOSUCHVOLUME;
1097 rock.fid = dscp->dotdotFid;
1099 } else if (strcmp(namep, ".") == 0) {
1100 rock.fid = dscp->fid;
1104 memset(&rock, 0, sizeof(rock));
1105 rock.fid.cell = dscp->fid.cell;
1106 rock.fid.volume = dscp->fid.volume;
1107 rock.searchNamep = namep;
1108 rock.caseFold = (flags & CM_FLAG_CASEFOLD);
1109 rock.hasTilde = ((strchr(namep, '~') != NULL) ? 1 : 0);
1111 /* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
1112 code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
1113 (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
1115 /* code == 0 means we fell off the end of the dir, while stopnow means
1116 * that we stopped early, probably because we found the entry we're
1117 * looking for. Any other non-zero code is an error.
1119 if (code && code != CM_ERROR_STOPNOW) {
1120 /* if the cm_scache_t we are searching in is not a directory
1121 * we must return path not found because the error
1122 * is to describe the final component not an intermediary
1124 if (code == CM_ERROR_NOTDIR) {
1125 if (flags & CM_FLAG_CHECKPATH)
1126 return CM_ERROR_NOSUCHPATH;
1128 return CM_ERROR_NOSUCHFILE;
1133 getroot = (dscp==cm_data.rootSCachep) ;
1135 if (!cm_freelanceEnabled || !getroot) {
1136 if (flags & CM_FLAG_CHECKPATH)
1137 return CM_ERROR_NOSUCHPATH;
1139 return CM_ERROR_NOSUCHFILE;
1141 else { /* nonexistent dir on freelance root, so add it */
1142 char fullname[200] = ".";
1145 osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %s",
1146 osi_LogSaveString(afsd_logp,namep));
1147 if (namep[0] == '.') {
1148 if (cm_GetCell_Gen(&namep[1], &fullname[1], CM_FLAG_CREATE)) {
1150 if ( stricmp(&namep[1], &fullname[1]) )
1151 code = cm_FreelanceAddSymlink(namep, fullname, &rock.fid);
1153 code = cm_FreelanceAddMount(namep, &fullname[1], "root.cell.", 1, &rock.fid);
1156 if (cm_GetCell_Gen(namep, fullname, CM_FLAG_CREATE)) {
1158 if ( stricmp(namep, fullname) )
1159 code = cm_FreelanceAddSymlink(namep, fullname, &rock.fid);
1161 code = cm_FreelanceAddMount(namep, fullname, "root.cell.", 0, &rock.fid);
1164 if (!found || code < 0) { /* add mount point failed, so give up */
1165 if (flags & CM_FLAG_CHECKPATH)
1166 return CM_ERROR_NOSUCHPATH;
1168 return CM_ERROR_NOSUCHFILE;
1170 tscp = NULL; /* to force call of cm_GetSCache */
1175 if ( !tscp ) /* we did not find it in the dnlc */
1178 code = cm_GetSCache(&rock.fid, &tscp, userp, reqp);
1182 /* tscp is now held */
1184 lock_ObtainMutex(&tscp->mx);
1185 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1186 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1188 lock_ReleaseMutex(&tscp->mx);
1189 cm_ReleaseSCache(tscp);
1192 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1193 /* tscp is now locked */
1195 if (!(flags & CM_FLAG_NOMOUNTCHASE)
1196 && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
1197 /* mount points are funny: they have a volume name to mount
1200 code = cm_ReadMountPoint(tscp, userp, reqp);
1202 code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
1204 lock_ReleaseMutex(&tscp->mx);
1205 cm_ReleaseSCache(tscp);
1212 lock_ReleaseMutex(&tscp->mx);
1215 /* copy back pointer */
1218 /* insert scache in dnlc */
1219 if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
1220 /* lock the directory entry to prevent racing callback revokes */
1221 lock_ObtainMutex(&dscp->mx);
1222 if ( dscp->cbServerp != NULL && dscp->cbExpires > 0 )
1223 cm_dnlcEnter(dscp, namep, tscp);
1224 lock_ReleaseMutex(&dscp->mx);
1231 int cm_ExpandSysName(char *inp, char *outp, long outSize, unsigned int index)
1236 tp = strrchr(inp, '@');
1238 return 0; /* no @sys */
1240 if (strcmp(tp, "@sys") != 0)
1241 return 0; /* no @sys */
1243 /* caller just wants to know if this is a valid @sys type of name */
1247 if (index >= MAXNUMSYSNAMES)
1250 /* otherwise generate the properly expanded @sys name */
1251 prefixCount = (int)(tp - inp);
1253 strncpy(outp, inp, prefixCount); /* copy out "a." from "a.@sys" */
1254 outp[prefixCount] = 0; /* null terminate the "a." */
1255 strcat(outp, cm_sysNameList[index]);/* append i386_nt40 */
1259 long cm_Lookup(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
1260 cm_req_t *reqp, cm_scache_t **outpScpp)
1264 int sysNameIndex = 0;
1265 cm_scache_t *scp = NULL;
1267 if ( stricmp(namep,SMB_IOCTL_FILENAME_NOSLASH) == 0 ) {
1268 if (flags & CM_FLAG_CHECKPATH)
1269 return CM_ERROR_NOSUCHPATH;
1271 return CM_ERROR_NOSUCHFILE;
1274 for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
1275 code = cm_ExpandSysName(namep, tname, sizeof(tname), sysNameIndex);
1277 code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
1283 cm_ReleaseSCache(scp);
1287 return cm_LookupInternal(dscp, namep, flags, userp, reqp, outpScpp);
1291 /* None of the possible sysName expansions could be found */
1292 if (flags & CM_FLAG_CHECKPATH)
1293 return CM_ERROR_NOSUCHPATH;
1295 return CM_ERROR_NOSUCHFILE;
1298 long cm_Unlink(cm_scache_t *dscp, char *namep, cm_user_t *userp, cm_req_t *reqp)
1304 AFSFetchStatus newDirStatus;
1306 struct rx_connection * callp;
1308 #ifdef AFS_FREELANCE_CLIENT
1309 if (cm_freelanceEnabled && dscp == cm_data.rootSCachep) {
1310 /* deleting a mount point from the root dir. */
1311 code = cm_FreelanceRemoveMount(namep);
1316 /* make sure we don't screw up the dir status during the merge */
1317 lock_ObtainMutex(&dscp->mx);
1318 sflags = CM_SCACHESYNC_STOREDATA;
1319 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1320 lock_ReleaseMutex(&dscp->mx);
1325 afsFid.Volume = dscp->fid.volume;
1326 afsFid.Vnode = dscp->fid.vnode;
1327 afsFid.Unique = dscp->fid.unique;
1329 osi_Log1(afsd_logp, "CALL RemoveFile scp 0x%p", dscp);
1331 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1335 callp = cm_GetRxConn(connp);
1336 code = RXAFS_RemoveFile(callp, &afsFid, namep,
1337 &newDirStatus, &volSync);
1338 rx_PutConnection(callp);
1340 } while (cm_Analyze(connp, userp, reqp, &dscp->fid, &volSync, NULL, NULL, code));
1341 code = cm_MapRPCError(code, reqp);
1344 osi_Log1(afsd_logp, "CALL RemoveFile FAILURE, code 0x%x", code);
1346 osi_Log0(afsd_logp, "CALL RemoveFile SUCCESS");
1348 lock_ObtainMutex(&dscp->mx);
1349 cm_dnlcRemove(dscp, namep);
1350 cm_SyncOpDone(dscp, NULL, sflags);
1352 cm_MergeStatus(dscp, &newDirStatus, &volSync, userp, 0);
1353 else if (code == CM_ERROR_NOSUCHFILE) {
1354 /* windows would not have allowed the request to delete the file
1355 * if it did not believe the file existed. therefore, we must
1356 * have an inconsistent view of the world.
1358 dscp->cbServerp = NULL;
1360 lock_ReleaseMutex(&dscp->mx);
1365 /* called with a locked vnode, and fills in the link info.
1366 * returns this the vnode still locked.
1368 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1375 lock_AssertMutex(&linkScp->mx);
1376 if (!linkScp->mountPointStringp[0]) {
1377 /* read the link data */
1378 lock_ReleaseMutex(&linkScp->mx);
1379 thyper.LowPart = thyper.HighPart = 0;
1380 code = buf_Get(linkScp, &thyper, &bufp);
1381 lock_ObtainMutex(&linkScp->mx);
1385 code = cm_SyncOp(linkScp, bufp, userp, reqp, 0,
1386 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1391 cm_SyncOpDone(linkScp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1393 if (cm_HaveBuffer(linkScp, bufp, 0))
1396 code = cm_GetBuffer(linkScp, bufp, NULL, userp, reqp);
1401 } /* while loop to get the data */
1403 /* now if we still have no link read in,
1404 * copy the data from the buffer */
1405 if ((temp = linkScp->length.LowPart) >= MOUNTPOINTLEN) {
1407 return CM_ERROR_TOOBIG;
1410 /* otherwise, it fits; make sure it is still null (could have
1411 * lost race with someone else referencing this link above),
1412 * and if so, copy in the data.
1414 if (!linkScp->mountPointStringp[0]) {
1415 strncpy(linkScp->mountPointStringp, bufp->datap, temp);
1416 linkScp->mountPointStringp[temp] = 0; /* null terminate */
1419 } /* don't have sym link contents cached */
1424 /* called with a held vnode and a path suffix, with the held vnode being a
1425 * symbolic link. Our goal is to generate a new path to interpret, and return
1426 * this new path in newSpaceBufferp. If the new vnode is relative to a dir
1427 * other than the directory containing the symbolic link, then the new root is
1428 * returned in *newRootScpp, otherwise a null is returned there.
1430 long cm_AssembleLink(cm_scache_t *linkScp, char *pathSuffixp,
1431 cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1432 cm_user_t *userp, cm_req_t *reqp)
1439 lock_ObtainMutex(&linkScp->mx);
1440 code = cm_HandleLink(linkScp, userp, reqp);
1444 /* if we may overflow the buffer, bail out; buffer is signficantly
1445 * bigger than max path length, so we don't really have to worry about
1446 * being a little conservative here.
1448 if (strlen(linkScp->mountPointStringp) + strlen(pathSuffixp) + 2
1449 >= CM_UTILS_SPACESIZE)
1450 return CM_ERROR_TOOBIG;
1452 tsp = cm_GetSpace();
1453 linkp = linkScp->mountPointStringp;
1454 if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1455 if (strlen(linkp) > cm_mountRootLen)
1456 strcpy(tsp->data, linkp+cm_mountRootLen+1);
1459 *newRootScpp = cm_data.rootSCachep;
1460 cm_HoldSCache(cm_data.rootSCachep);
1461 } else if (linkp[0] == '\\' && linkp[1] == '\\') {
1462 if (!strnicmp(&linkp[2], cm_NetbiosName, (len = (long)strlen(cm_NetbiosName))))
1464 char * p = &linkp[len + 3];
1465 if (strnicmp(p, "all", 3) == 0)
1468 strcpy(tsp->data, p);
1469 for (p = tsp->data; *p; p++) {
1473 *newRootScpp = cm_data.rootSCachep;
1474 cm_HoldSCache(cm_data.rootSCachep);
1476 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1477 strcpy(tsp->data, linkp);
1478 *newRootScpp = NULL;
1479 code = CM_ERROR_PATH_NOT_COVERED;
1481 } else if ( !strnicmp(linkp, "msdfs:", (len = (long)strlen("msdfs:"))) ) {
1482 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1483 strcpy(tsp->data, linkp);
1484 *newRootScpp = NULL;
1485 code = CM_ERROR_PATH_NOT_COVERED;
1486 } else if (*linkp == '\\' || *linkp == '/') {
1488 /* formerly, this was considered to be from the AFS root,
1489 * but this seems to create problems. instead, we will just
1490 * reject the link */
1491 strcpy(tsp->data, linkp+1);
1492 *newRootScpp = cm_data.rootSCachep;
1493 cm_HoldSCache(cm_data.rootSCachep);
1495 /* we still copy the link data into the response so that
1496 * the user can see what the link points to
1498 linkScp->fileType = CM_SCACHETYPE_INVALID;
1499 strcpy(tsp->data, linkp);
1500 *newRootScpp = NULL;
1501 code = CM_ERROR_NOSUCHPATH;
1504 /* a relative link */
1505 strcpy(tsp->data, linkp);
1506 *newRootScpp = NULL;
1508 if (pathSuffixp[0] != 0) { /* if suffix string is non-null */
1509 strcat(tsp->data, "\\");
1510 strcat(tsp->data, pathSuffixp);
1512 *newSpaceBufferp = tsp;
1515 lock_ReleaseMutex(&linkScp->mx);
1519 long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
1520 cm_user_t *userp, char *tidPathp, cm_req_t *reqp, cm_scache_t **outScpp)
1523 char *tp; /* ptr moving through input buffer */
1524 char tc; /* temp char */
1525 int haveComponent; /* has new component started? */
1526 char component[256]; /* this is the new component */
1527 char *cp; /* component name being assembled */
1528 cm_scache_t *tscp; /* current location in the hierarchy */
1529 cm_scache_t *nscp; /* next dude down */
1530 cm_scache_t *dirScp; /* last dir we searched */
1531 cm_scache_t *linkScp; /* new root for the symlink we just
1533 cm_space_t *psp; /* space for current path, if we've hit
1535 cm_space_t *tempsp; /* temp vbl */
1536 char *restp; /* rest of the pathname to interpret */
1537 int symlinkCount; /* count of # of symlinks traversed */
1538 int extraFlag; /* avoid chasing mt pts for dir cmd */
1539 int phase = 1; /* 1 = tidPathp, 2 = pathp */
1552 cm_HoldSCache(tscp);
1559 /* map Unix slashes into DOS ones so we can interpret Unix
1565 if (!haveComponent) {
1568 } else if (tc == 0) {
1582 /* we have a component here */
1583 if (tc == 0 || tc == '\\') {
1584 /* end of the component; we're at the last
1585 * component if tc == 0. However, if the last
1586 * is a symlink, we have more to do.
1588 *cp++ = 0; /* add null termination */
1589 if (!strcmp(".",component)) {
1592 cm_ReleaseSCache(dirScp);
1598 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
1599 extraFlag = CM_FLAG_NOMOUNTCHASE;
1600 code = cm_Lookup(tscp, component,
1602 userp, reqp, &nscp);
1604 cm_ReleaseSCache(tscp);
1606 cm_ReleaseSCache(dirScp);
1609 if (code == CM_ERROR_NOSUCHFILE && tscp->fileType == CM_SCACHETYPE_SYMLINK)
1610 return CM_ERROR_NOSUCHPATH;
1614 haveComponent = 0; /* component done */
1616 cm_ReleaseSCache(dirScp);
1617 dirScp = tscp; /* for some symlinks */
1618 tscp = nscp; /* already held */
1620 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
1623 cm_ReleaseSCache(dirScp);
1629 /* now, if tscp is a symlink, we should follow
1630 * it and assemble the path again.
1632 lock_ObtainMutex(&tscp->mx);
1633 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1634 CM_SCACHESYNC_GETSTATUS
1635 | CM_SCACHESYNC_NEEDCALLBACK);
1637 lock_ReleaseMutex(&tscp->mx);
1638 cm_ReleaseSCache(tscp);
1641 cm_ReleaseSCache(dirScp);
1646 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1648 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
1649 /* this is a symlink; assemble a new buffer */
1650 lock_ReleaseMutex(&tscp->mx);
1651 if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
1652 cm_ReleaseSCache(tscp);
1655 cm_ReleaseSCache(dirScp);
1660 return CM_ERROR_TOO_MANY_SYMLINKS;
1666 code = cm_AssembleLink(tscp, restp, &linkScp, &tempsp, userp, reqp);
1668 /* something went wrong */
1669 cm_ReleaseSCache(tscp);
1672 cm_ReleaseSCache(dirScp);
1678 /* otherwise, tempsp has the new path,
1679 * and linkScp is the new root from
1680 * which to interpret that path.
1681 * Continue with the namei processing,
1682 * also doing the bookkeeping for the
1683 * space allocation and tracking the
1684 * vnode reference counts.
1690 cm_ReleaseSCache(tscp);
1695 * now, if linkScp is null, that's
1696 * AssembleLink's way of telling us that
1697 * the sym link is relative to the dir
1698 * containing the link. We have a ref
1699 * to it in dirScp, and we hold it now
1700 * and reuse it as the new spot in the
1708 /* not a symlink, we may be done */
1709 lock_ReleaseMutex(&tscp->mx);
1717 cm_ReleaseSCache(dirScp);
1725 cm_ReleaseSCache(dirScp);
1728 } /* end of a component */
1731 } /* we have a component */
1732 } /* big while loop over all components */
1736 cm_ReleaseSCache(dirScp);
1742 cm_ReleaseSCache(tscp);
1746 /* called with a dir, and a vnode within the dir that happens to be a symlink.
1747 * We chase the link, and return a held pointer to the target, if it exists,
1748 * in *outScpp. If we succeed, we return 0, otherwise we return an error code
1749 * and do not hold or return a target vnode.
1751 * This is very similar to calling cm_NameI with the last component of a name,
1752 * which happens to be a symlink, except that we've already passed by the name.
1754 * This function is typically called by the directory listing functions, which
1755 * encounter symlinks but need to return the proper file length so programs
1756 * like "more" work properly when they make use of the attributes retrieved from
1759 * The input vnode should not be locked when this function is called.
1761 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
1762 cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
1766 cm_scache_t *newRootScp;
1768 osi_Log1(afsd_logp, "Evaluating symlink scp 0x%p", linkScp);
1770 code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
1774 /* now, if newRootScp is NULL, we're really being told that the symlink
1775 * is relative to the current directory (dscp).
1777 if (newRootScp == NULL) {
1779 cm_HoldSCache(dscp);
1782 code = cm_NameI(newRootScp, spacep->data,
1783 CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
1784 userp, NULL, reqp, outScpp);
1786 if (code == CM_ERROR_NOSUCHFILE)
1787 code = CM_ERROR_NOSUCHPATH;
1789 /* this stuff is allocated no matter what happened on the namei call,
1791 cm_FreeSpace(spacep);
1792 cm_ReleaseSCache(newRootScp);
1797 /* make this big enough so that one buffer of dir pages won't overflow. We'll
1798 * check anyway, but we want to minimize the chance that we have to leave stuff
1801 #define CM_BULKMAX (3 * AFSCBMAX)
1803 /* rock for bulk stat calls */
1804 typedef struct cm_bulkStat {
1805 osi_hyper_t bufOffset; /* only do it for things in this buffer page */
1807 /* info for the actual call */
1808 int counter; /* next free slot */
1809 AFSFid fids[CM_BULKMAX];
1810 AFSFetchStatus stats[CM_BULKMAX];
1811 AFSCallBack callbacks[CM_BULKMAX];
1814 /* for a given entry, make sure that it isn't in the stat cache, and then
1815 * add it to the list of file IDs to be obtained.
1817 * Don't bother adding it if we already have a vnode. Note that the dir
1818 * is locked, so we have to be careful checking the vnode we're thinking of
1819 * processing, to avoid deadlocks.
1821 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
1832 /* Don't overflow bsp. */
1833 if (bsp->counter >= CM_BULKMAX)
1834 return CM_ERROR_STOPNOW;
1836 thyper.LowPart = cm_data.buf_blockSize;
1837 thyper.HighPart = 0;
1838 thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
1840 /* thyper is now the first byte past the end of the record we're
1841 * interested in, and bsp->bufOffset is the first byte of the record
1842 * we're interested in.
1843 * Skip data in the others.
1846 if (LargeIntegerLessThan(*offp, bsp->bufOffset))
1848 if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
1849 return CM_ERROR_STOPNOW;
1850 if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
1853 tfid.cell = scp->fid.cell;
1854 tfid.volume = scp->fid.volume;
1855 tfid.vnode = ntohl(dep->fid.vnode);
1856 tfid.unique = ntohl(dep->fid.unique);
1857 tscp = cm_FindSCache(&tfid);
1859 if (lock_TryMutex(&tscp->mx)) {
1860 /* we have an entry that we can look at */
1861 if (!(tscp->flags & CM_SCACHEFLAG_EACCESS) && cm_HaveCallback(tscp)) {
1862 /* we have a callback on it. Don't bother
1863 * fetching this stat entry, since we're happy
1864 * with the info we have.
1866 lock_ReleaseMutex(&tscp->mx);
1867 cm_ReleaseSCache(tscp);
1870 lock_ReleaseMutex(&tscp->mx);
1872 cm_ReleaseSCache(tscp);
1875 #ifdef AFS_FREELANCE_CLIENT
1876 // yj: if this is a mountpoint under root.afs then we don't want it
1877 // to be bulkstat-ed, instead, we call getSCache directly and under
1878 // getSCache, it is handled specially.
1879 if ( cm_freelanceEnabled &&
1880 tfid.cell==AFS_FAKE_ROOT_CELL_ID &&
1881 tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
1882 !(tfid.vnode==0x1 && tfid.unique==0x1) )
1884 osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
1885 return cm_GetSCache(&tfid, &tscp, NULL, NULL);
1887 #endif /* AFS_FREELANCE_CLIENT */
1890 bsp->fids[i].Volume = scp->fid.volume;
1891 bsp->fids[i].Vnode = tfid.vnode;
1892 bsp->fids[i].Unique = tfid.unique;
1896 /* called with a locked scp and a pointer to a buffer. Make bulk stat
1897 * calls on all undeleted files in the page of the directory specified.
1900 cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
1904 cm_bulkStat_t bb; /* this is *BIG*, probably 16K or so;
1905 * watch for stack problems */
1906 AFSCBFids fidStruct;
1907 AFSBulkStats statStruct;
1909 AFSCBs callbackStruct;
1912 cm_callbackRequest_t cbReq;
1918 struct rx_connection * callp;
1919 int inlinebulk = 0; /* Did we use InlineBulkStatus RPC or not? */
1921 osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%p", dscp);
1923 /* should be on a buffer boundary */
1924 osi_assert((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0);
1926 memset(&bb, 0, sizeof(bb));
1927 bb.bufOffset = *offsetp;
1929 lock_ReleaseMutex(&dscp->mx);
1930 /* first, assemble the file IDs we need to stat */
1931 code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) &bb, offsetp, userp, reqp, NULL);
1933 /* if we failed, bail out early */
1934 if (code && code != CM_ERROR_STOPNOW) {
1935 lock_ObtainMutex(&dscp->mx);
1939 /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
1940 * make the calls to create the entries. Handle AFSCBMAX files at a
1944 while (filex < bb.counter) {
1945 filesThisCall = bb.counter - filex;
1946 if (filesThisCall > AFSCBMAX)
1947 filesThisCall = AFSCBMAX;
1949 fidStruct.AFSCBFids_len = filesThisCall;
1950 fidStruct.AFSCBFids_val = &bb.fids[filex];
1951 statStruct.AFSBulkStats_len = filesThisCall;
1952 statStruct.AFSBulkStats_val = &bb.stats[filex];
1953 callbackStruct.AFSCBs_len = filesThisCall;
1954 callbackStruct.AFSCBs_val = &bb.callbacks[filex];
1955 cm_StartCallbackGrantingCall(NULL, &cbReq);
1956 osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
1958 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1962 callp = cm_GetRxConn(connp);
1963 if (!(connp->serverp->flags & CM_SERVERFLAG_NOINLINEBULK)) {
1964 code = RXAFS_InlineBulkStatus(callp, &fidStruct,
1965 &statStruct, &callbackStruct, &volSync);
1966 if (code == RXGEN_OPCODE) {
1967 cm_SetServerNoInlineBulk(connp->serverp, 0);
1973 code = RXAFS_BulkStatus(callp, &fidStruct,
1974 &statStruct, &callbackStruct, &volSync);
1976 rx_PutConnection(callp);
1978 } while (cm_Analyze(connp, userp, reqp, &dscp->fid,
1979 &volSync, NULL, &cbReq, code));
1980 code = cm_MapRPCError(code, reqp);
1982 osi_Log2(afsd_logp, "CALL %sBulkStatus FAILURE code 0x%x",
1983 inlinebulk ? "Inline" : "", code);
1985 osi_Log1(afsd_logp, "CALL %sBulkStatus SUCCESS", inlinebulk ? "Inline" : "");
1987 /* may as well quit on an error, since we're not going to do
1988 * much better on the next immediate call, either.
1991 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
1995 /* otherwise, we should do the merges */
1996 for (i = 0; i<filesThisCall; i++) {
1998 tfid.cell = dscp->fid.cell;
1999 tfid.volume = bb.fids[j].Volume;
2000 tfid.vnode = bb.fids[j].Vnode;
2001 tfid.unique = bb.fids[j].Unique;
2002 code = cm_GetSCache(&tfid, &scp, userp, reqp);
2006 /* otherwise, if this entry has no callback info,
2009 lock_ObtainMutex(&scp->mx);
2010 /* now, we have to be extra paranoid on merging in this
2011 * information, since we didn't use cm_SyncOp before
2012 * starting the fetch to make sure that no bad races
2013 * were occurring. Specifically, we need to make sure
2014 * we don't obliterate any newer information in the
2015 * vnode than have here.
2017 * Right now, be pretty conservative: if there's a
2018 * callback or a pending call, skip it.
2020 if ((scp->cbServerp == NULL || (scp->flags & CM_SCACHEFLAG_EACCESS))
2022 (CM_SCACHEFLAG_FETCHING
2023 | CM_SCACHEFLAG_STORING
2024 | CM_SCACHEFLAG_SIZESTORING))) {
2025 cm_EndCallbackGrantingCall(scp, &cbReq,
2027 CM_CALLBACK_MAINTAINCOUNT);
2028 cm_MergeStatus(scp, &bb.stats[j], &volSync, userp, 0);
2030 lock_ReleaseMutex(&scp->mx);
2031 cm_ReleaseSCache(scp);
2032 } /* all files in the response */
2033 /* now tell it to drop the count,
2034 * after doing the vnode processing above */
2035 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2037 filex += filesThisCall;
2038 } /* while there are still more files to process */
2039 lock_ObtainMutex(&dscp->mx);
2042 /* If we did the InlineBulk RPC pull out the return code */
2044 if ((&bb.stats[0])->errorCode) {
2045 cm_Analyze(NULL /*connp was released by the previous cm_Analyze */,
2046 userp, reqp, &dscp->fid, &volSync, NULL, NULL, (&bb.stats[0])->errorCode);
2047 code = cm_MapRPCError((&bb.stats[0])->errorCode, reqp);
2055 osi_Log1(afsd_logp, "END cm_TryBulkStat code = 0x%x", code);
2059 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
2063 /* initialize store back mask as inexpensive local variable */
2065 memset(statusp, 0, sizeof(AFSStoreStatus));
2067 /* copy out queued info from scache first, if scp passed in */
2069 if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
2070 statusp->ClientModTime = scp->clientModTime;
2071 mask |= AFS_SETMODTIME;
2072 scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
2077 /* now add in our locally generated request */
2078 if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
2079 statusp->ClientModTime = attrp->clientModTime;
2080 mask |= AFS_SETMODTIME;
2082 if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
2083 statusp->UnixModeBits = attrp->unixModeBits;
2084 mask |= AFS_SETMODE;
2086 if (attrp->mask & CM_ATTRMASK_OWNER) {
2087 statusp->Owner = attrp->owner;
2088 mask |= AFS_SETOWNER;
2090 if (attrp->mask & CM_ATTRMASK_GROUP) {
2091 statusp->Group = attrp->group;
2092 mask |= AFS_SETGROUP;
2095 statusp->Mask = mask;
2098 /* set the file size, and make sure that all relevant buffers have been
2099 * truncated. Ensure that any partially truncated buffers have been zeroed
2100 * to the end of the buffer.
2102 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
2108 /* start by locking out buffer creation */
2109 lock_ObtainWrite(&scp->bufCreateLock);
2111 /* verify that this is a file, not a dir or a symlink */
2112 lock_ObtainMutex(&scp->mx);
2113 code = cm_SyncOp(scp, NULL, userp, reqp, 0,
2114 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2117 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2119 if (scp->fileType != CM_SCACHETYPE_FILE) {
2120 code = CM_ERROR_ISDIR;
2125 if (LargeIntegerLessThan(*sizep, scp->length))
2130 lock_ReleaseMutex(&scp->mx);
2132 /* can't hold scp->mx lock here, since we may wait for a storeback to
2133 * finish if the buffer package is cleaning a buffer by storing it to
2137 buf_Truncate(scp, userp, reqp, sizep);
2139 /* now ensure that file length is short enough, and update truncPos */
2140 lock_ObtainMutex(&scp->mx);
2142 /* make sure we have a callback (so we have the right value for the
2143 * length), and wait for it to be safe to do a truncate.
2145 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
2146 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2147 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2151 if (LargeIntegerLessThan(*sizep, scp->length)) {
2152 /* a real truncation. If truncPos is not set yet, or is bigger
2153 * than where we're truncating the file, set truncPos to this
2158 if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
2159 || LargeIntegerLessThan(*sizep, scp->length)) {
2161 scp->truncPos = *sizep;
2162 scp->mask |= CM_SCACHEMASK_TRUNCPOS;
2164 /* in either case, the new file size has been changed */
2165 scp->length = *sizep;
2166 scp->mask |= CM_SCACHEMASK_LENGTH;
2168 else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
2169 /* really extending the file */
2170 scp->length = *sizep;
2171 scp->mask |= CM_SCACHEMASK_LENGTH;
2174 /* done successfully */
2177 cm_SyncOpDone(scp, NULL,
2178 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2179 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2182 lock_ReleaseMutex(&scp->mx);
2183 lock_ReleaseWrite(&scp->bufCreateLock);
2188 /* set the file size or other attributes (but not both at once) */
2189 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
2193 AFSFetchStatus afsOutStatus;
2197 AFSStoreStatus afsInStatus;
2198 struct rx_connection * callp;
2200 /* handle file length setting */
2201 if (attrp->mask & CM_ATTRMASK_LENGTH)
2202 return cm_SetLength(scp, &attrp->length, userp, reqp);
2204 lock_ObtainMutex(&scp->mx);
2205 /* otherwise, we have to make an RPC to get the status */
2206 code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
2208 lock_ReleaseMutex(&scp->mx);
2212 /* make the attr structure */
2213 cm_StatusFromAttr(&afsInStatus, scp, attrp);
2215 tfid.Volume = scp->fid.volume;
2216 tfid.Vnode = scp->fid.vnode;
2217 tfid.Unique = scp->fid.unique;
2218 lock_ReleaseMutex(&scp->mx);
2220 /* now make the RPC */
2221 osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%p", scp);
2223 code = cm_Conn(&scp->fid, userp, reqp, &connp);
2227 callp = cm_GetRxConn(connp);
2228 code = RXAFS_StoreStatus(callp, &tfid,
2229 &afsInStatus, &afsOutStatus, &volSync);
2230 rx_PutConnection(callp);
2232 } while (cm_Analyze(connp, userp, reqp,
2233 &scp->fid, &volSync, NULL, NULL, code));
2234 code = cm_MapRPCError(code, reqp);
2237 osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
2239 osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
2241 lock_ObtainMutex(&scp->mx);
2242 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
2244 cm_MergeStatus(scp, &afsOutStatus, &volSync, userp,
2245 CM_MERGEFLAG_FORCE);
2247 /* if we're changing the mode bits, discard the ACL cache,
2248 * since we changed the mode bits.
2250 if (afsInStatus.Mask & AFS_SETMODE) cm_FreeAllACLEnts(scp);
2251 lock_ReleaseMutex(&scp->mx);
2255 long cm_Create(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2256 cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
2261 cm_callbackRequest_t cbReq;
2266 AFSStoreStatus inStatus;
2267 AFSFetchStatus updatedDirStatus;
2268 AFSFetchStatus newFileStatus;
2269 AFSCallBack newFileCallback;
2271 struct rx_connection * callp;
2273 /* can't create names with @sys in them; must expand it manually first.
2274 * return "invalid request" if they try.
2276 if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2277 return CM_ERROR_ATSYS;
2280 /* before starting the RPC, mark that we're changing the file data, so
2281 * that someone who does a chmod will know to wait until our call
2284 lock_ObtainMutex(&dscp->mx);
2285 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2287 cm_StartCallbackGrantingCall(NULL, &cbReq);
2289 lock_ReleaseMutex(&dscp->mx);
2295 cm_StatusFromAttr(&inStatus, NULL, attrp);
2297 /* try the RPC now */
2298 osi_Log1(afsd_logp, "CALL CreateFile scp 0x%p", dscp);
2300 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2304 dirAFSFid.Volume = dscp->fid.volume;
2305 dirAFSFid.Vnode = dscp->fid.vnode;
2306 dirAFSFid.Unique = dscp->fid.unique;
2308 callp = cm_GetRxConn(connp);
2309 code = RXAFS_CreateFile(connp->callp, &dirAFSFid, namep,
2310 &inStatus, &newAFSFid, &newFileStatus,
2311 &updatedDirStatus, &newFileCallback,
2313 rx_PutConnection(callp);
2315 } while (cm_Analyze(connp, userp, reqp,
2316 &dscp->fid, &volSync, NULL, &cbReq, code));
2317 code = cm_MapRPCError(code, reqp);
2320 osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
2322 osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
2324 lock_ObtainMutex(&dscp->mx);
2325 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2327 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2329 lock_ReleaseMutex(&dscp->mx);
2331 /* now try to create the file's entry, too, but be careful to
2332 * make sure that we don't merge in old info. Since we weren't locking
2333 * out any requests during the file's creation, we may have pretty old
2337 newFid.cell = dscp->fid.cell;
2338 newFid.volume = dscp->fid.volume;
2339 newFid.vnode = newAFSFid.Vnode;
2340 newFid.unique = newAFSFid.Unique;
2341 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2343 lock_ObtainMutex(&scp->mx);
2344 scp->creator = userp; /* remember who created it */
2345 if (!cm_HaveCallback(scp)) {
2346 cm_MergeStatus(scp, &newFileStatus, &volSync,
2348 cm_EndCallbackGrantingCall(scp, &cbReq,
2349 &newFileCallback, 0);
2352 lock_ReleaseMutex(&scp->mx);
2357 /* make sure we end things properly */
2359 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2364 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
2368 lock_ObtainWrite(&scp->bufCreateLock);
2369 code = buf_CleanVnode(scp, userp, reqp);
2370 lock_ReleaseWrite(&scp->bufCreateLock);
2372 lock_ObtainMutex(&scp->mx);
2374 if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
2375 | CM_SCACHEMASK_CLIENTMODTIME
2376 | CM_SCACHEMASK_LENGTH))
2377 code = cm_StoreMini(scp, userp, reqp);
2379 if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) {
2380 code = (scp->flags & CM_SCACHEFLAG_OVERQUOTA) ? CM_ERROR_QUOTA : CM_ERROR_SPACE;
2381 scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE);
2384 lock_ReleaseMutex(&scp->mx);
2389 long cm_MakeDir(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2390 cm_user_t *userp, cm_req_t *reqp)
2395 cm_callbackRequest_t cbReq;
2400 AFSStoreStatus inStatus;
2401 AFSFetchStatus updatedDirStatus;
2402 AFSFetchStatus newDirStatus;
2403 AFSCallBack newDirCallback;
2405 struct rx_connection * callp;
2407 /* can't create names with @sys in them; must expand it manually first.
2408 * return "invalid request" if they try.
2410 if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2411 return CM_ERROR_ATSYS;
2414 /* before starting the RPC, mark that we're changing the directory
2415 * data, so that someone who does a chmod on the dir will wait until
2416 * our call completes.
2418 lock_ObtainMutex(&dscp->mx);
2419 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2421 cm_StartCallbackGrantingCall(NULL, &cbReq);
2423 lock_ReleaseMutex(&dscp->mx);
2429 cm_StatusFromAttr(&inStatus, NULL, attrp);
2431 /* try the RPC now */
2432 osi_Log1(afsd_logp, "CALL MakeDir scp 0x%p", dscp);
2434 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2438 dirAFSFid.Volume = dscp->fid.volume;
2439 dirAFSFid.Vnode = dscp->fid.vnode;
2440 dirAFSFid.Unique = dscp->fid.unique;
2442 callp = cm_GetRxConn(connp);
2443 code = RXAFS_MakeDir(connp->callp, &dirAFSFid, namep,
2444 &inStatus, &newAFSFid, &newDirStatus,
2445 &updatedDirStatus, &newDirCallback,
2447 rx_PutConnection(callp);
2449 } while (cm_Analyze(connp, userp, reqp,
2450 &dscp->fid, &volSync, NULL, &cbReq, code));
2451 code = cm_MapRPCError(code, reqp);
2454 osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
2456 osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
2458 lock_ObtainMutex(&dscp->mx);
2459 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2461 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2463 lock_ReleaseMutex(&dscp->mx);
2465 /* now try to create the new dir's entry, too, but be careful to
2466 * make sure that we don't merge in old info. Since we weren't locking
2467 * out any requests during the file's creation, we may have pretty old
2471 newFid.cell = dscp->fid.cell;
2472 newFid.volume = dscp->fid.volume;
2473 newFid.vnode = newAFSFid.Vnode;
2474 newFid.unique = newAFSFid.Unique;
2475 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2477 lock_ObtainMutex(&scp->mx);
2478 if (!cm_HaveCallback(scp)) {
2479 cm_MergeStatus(scp, &newDirStatus, &volSync,
2481 cm_EndCallbackGrantingCall(scp, &cbReq,
2482 &newDirCallback, 0);
2485 lock_ReleaseMutex(&scp->mx);
2486 cm_ReleaseSCache(scp);
2490 /* make sure we end things properly */
2492 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2494 /* and return error code */
2498 long cm_Link(cm_scache_t *dscp, char *namep, cm_scache_t *sscp, long flags,
2499 cm_user_t *userp, cm_req_t *reqp)
2504 AFSFid existingAFSFid;
2505 AFSFetchStatus updatedDirStatus;
2506 AFSFetchStatus newLinkStatus;
2508 struct rx_connection * callp;
2510 if (dscp->fid.cell != sscp->fid.cell ||
2511 dscp->fid.volume != sscp->fid.volume) {
2512 return CM_ERROR_CROSSDEVLINK;
2515 lock_ObtainMutex(&dscp->mx);
2516 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2517 lock_ReleaseMutex(&dscp->mx);
2522 /* try the RPC now */
2523 osi_Log1(afsd_logp, "CALL Link scp 0x%p", dscp);
2525 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2528 dirAFSFid.Volume = dscp->fid.volume;
2529 dirAFSFid.Vnode = dscp->fid.vnode;
2530 dirAFSFid.Unique = dscp->fid.unique;
2532 existingAFSFid.Volume = sscp->fid.volume;
2533 existingAFSFid.Vnode = sscp->fid.vnode;
2534 existingAFSFid.Unique = sscp->fid.unique;
2536 callp = cm_GetRxConn(connp);
2537 code = RXAFS_Link(callp, &dirAFSFid, namep, &existingAFSFid,
2538 &newLinkStatus, &updatedDirStatus, &volSync);
2539 rx_PutConnection(callp);
2540 osi_Log1(smb_logp," RXAFS_Link returns 0x%x", code);
2542 } while (cm_Analyze(connp, userp, reqp,
2543 &dscp->fid, &volSync, NULL, NULL, code));
2545 code = cm_MapRPCError(code, reqp);
2548 osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
2550 osi_Log0(afsd_logp, "CALL Link SUCCESS");
2552 lock_ObtainMutex(&dscp->mx);
2553 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2555 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2557 lock_ReleaseMutex(&dscp->mx);
2562 long cm_SymLink(cm_scache_t *dscp, char *namep, char *contentsp, long flags,
2563 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
2571 AFSStoreStatus inStatus;
2572 AFSFetchStatus updatedDirStatus;
2573 AFSFetchStatus newLinkStatus;
2575 struct rx_connection * callp;
2577 /* before starting the RPC, mark that we're changing the directory data,
2578 * so that someone who does a chmod on the dir will wait until our
2581 lock_ObtainMutex(&dscp->mx);
2582 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2583 lock_ReleaseMutex(&dscp->mx);
2588 cm_StatusFromAttr(&inStatus, NULL, attrp);
2590 /* try the RPC now */
2591 osi_Log1(afsd_logp, "CALL Symlink scp 0x%p", dscp);
2593 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2597 dirAFSFid.Volume = dscp->fid.volume;
2598 dirAFSFid.Vnode = dscp->fid.vnode;
2599 dirAFSFid.Unique = dscp->fid.unique;
2601 callp = cm_GetRxConn(connp);
2602 code = RXAFS_Symlink(callp, &dirAFSFid, namep, contentsp,
2603 &inStatus, &newAFSFid, &newLinkStatus,
2604 &updatedDirStatus, &volSync);
2605 rx_PutConnection(callp);
2607 } while (cm_Analyze(connp, userp, reqp,
2608 &dscp->fid, &volSync, NULL, NULL, code));
2609 code = cm_MapRPCError(code, reqp);
2612 osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
2614 osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
2616 lock_ObtainMutex(&dscp->mx);
2617 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2619 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2621 lock_ReleaseMutex(&dscp->mx);
2623 /* now try to create the new dir's entry, too, but be careful to
2624 * make sure that we don't merge in old info. Since we weren't locking
2625 * out any requests during the file's creation, we may have pretty old
2629 newFid.cell = dscp->fid.cell;
2630 newFid.volume = dscp->fid.volume;
2631 newFid.vnode = newAFSFid.Vnode;
2632 newFid.unique = newAFSFid.Unique;
2633 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2635 lock_ObtainMutex(&scp->mx);
2636 if (!cm_HaveCallback(scp)) {
2637 cm_MergeStatus(scp, &newLinkStatus, &volSync,
2640 lock_ReleaseMutex(&scp->mx);
2641 cm_ReleaseSCache(scp);
2645 /* and return error code */
2649 long cm_RemoveDir(cm_scache_t *dscp, char *namep, cm_user_t *userp,
2656 AFSFetchStatus updatedDirStatus;
2658 struct rx_connection * callp;
2660 /* before starting the RPC, mark that we're changing the directory data,
2661 * so that someone who does a chmod on the dir will wait until our
2664 lock_ObtainMutex(&dscp->mx);
2665 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2666 lock_ReleaseMutex(&dscp->mx);
2672 /* try the RPC now */
2673 osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%p", dscp);
2675 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2679 dirAFSFid.Volume = dscp->fid.volume;
2680 dirAFSFid.Vnode = dscp->fid.vnode;
2681 dirAFSFid.Unique = dscp->fid.unique;
2683 callp = cm_GetRxConn(connp);
2684 code = RXAFS_RemoveDir(callp, &dirAFSFid, namep,
2685 &updatedDirStatus, &volSync);
2686 rx_PutConnection(callp);
2688 } while (cm_Analyze(connp, userp, reqp,
2689 &dscp->fid, &volSync, NULL, NULL, code));
2690 code = cm_MapRPCErrorRmdir(code, reqp);
2693 osi_Log1(afsd_logp, "CALL RemoveDir FAILURE, code 0x%x", code);
2695 osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
2697 lock_ObtainMutex(&dscp->mx);
2698 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2700 cm_dnlcRemove(dscp, namep);
2701 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2703 lock_ReleaseMutex(&dscp->mx);
2705 /* and return error code */
2709 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
2711 /* grab mutex on contents */
2712 lock_ObtainMutex(&scp->mx);
2714 /* reset the prefetch info */
2715 scp->prefetch.base.LowPart = 0; /* base */
2716 scp->prefetch.base.HighPart = 0;
2717 scp->prefetch.end.LowPart = 0; /* and end */
2718 scp->prefetch.end.HighPart = 0;
2720 /* release mutex on contents */
2721 lock_ReleaseMutex(&scp->mx);
2727 long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
2728 char *newNamep, cm_user_t *userp, cm_req_t *reqp)
2732 AFSFid oldDirAFSFid;
2733 AFSFid newDirAFSFid;
2735 AFSFetchStatus updatedOldDirStatus;
2736 AFSFetchStatus updatedNewDirStatus;
2739 struct rx_connection * callp;
2741 /* before starting the RPC, mark that we're changing the directory data,
2742 * so that someone who does a chmod on the dir will wait until our call
2743 * completes. We do this in vnode order so that we don't deadlock,
2744 * which makes the code a little verbose.
2746 if (oldDscp == newDscp) {
2747 /* check for identical names */
2748 if (strcmp(oldNamep, newNamep) == 0)
2749 return CM_ERROR_RENAME_IDENTICAL;
2752 lock_ObtainMutex(&oldDscp->mx);
2753 cm_dnlcRemove(oldDscp, oldNamep);
2754 cm_dnlcRemove(oldDscp, newNamep);
2755 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2756 CM_SCACHESYNC_STOREDATA);
2757 lock_ReleaseMutex(&oldDscp->mx);
2760 /* two distinct dir vnodes */
2762 if (oldDscp->fid.cell != newDscp->fid.cell ||
2763 oldDscp->fid.volume != newDscp->fid.volume)
2764 return CM_ERROR_CROSSDEVLINK;
2766 /* shouldn't happen that we have distinct vnodes for two
2767 * different files, but could due to deliberate attack, or
2768 * stale info. Avoid deadlocks and quit now.
2770 if (oldDscp->fid.vnode == newDscp->fid.vnode)
2771 return CM_ERROR_CROSSDEVLINK;
2773 if (oldDscp->fid.vnode < newDscp->fid.vnode) {
2774 lock_ObtainMutex(&oldDscp->mx);
2775 cm_dnlcRemove(oldDscp, oldNamep);
2776 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2777 CM_SCACHESYNC_STOREDATA);
2778 lock_ReleaseMutex(&oldDscp->mx);
2780 lock_ObtainMutex(&newDscp->mx);
2781 cm_dnlcRemove(newDscp, newNamep);
2782 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2783 CM_SCACHESYNC_STOREDATA);
2784 lock_ReleaseMutex(&newDscp->mx);
2786 /* cleanup first one */
2787 lock_ObtainMutex(&oldDscp->mx);
2788 cm_SyncOpDone(oldDscp, NULL,
2789 CM_SCACHESYNC_STOREDATA);
2790 lock_ReleaseMutex(&oldDscp->mx);
2795 /* lock the new vnode entry first */
2796 lock_ObtainMutex(&newDscp->mx);
2797 cm_dnlcRemove(newDscp, newNamep);
2798 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2799 CM_SCACHESYNC_STOREDATA);
2800 lock_ReleaseMutex(&newDscp->mx);
2802 lock_ObtainMutex(&oldDscp->mx);
2803 cm_dnlcRemove(oldDscp, oldNamep);
2804 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2805 CM_SCACHESYNC_STOREDATA);
2806 lock_ReleaseMutex(&oldDscp->mx);
2808 /* cleanup first one */
2809 lock_ObtainMutex(&newDscp->mx);
2810 cm_SyncOpDone(newDscp, NULL,
2811 CM_SCACHESYNC_STOREDATA);
2812 lock_ReleaseMutex(&newDscp->mx);
2816 } /* two distinct vnodes */
2823 /* try the RPC now */
2824 osi_Log2(afsd_logp, "CALL Rename old scp 0x%p new scp 0x%p",
2827 code = cm_Conn(&oldDscp->fid, userp, reqp, &connp);
2831 oldDirAFSFid.Volume = oldDscp->fid.volume;
2832 oldDirAFSFid.Vnode = oldDscp->fid.vnode;
2833 oldDirAFSFid.Unique = oldDscp->fid.unique;
2834 newDirAFSFid.Volume = newDscp->fid.volume;
2835 newDirAFSFid.Vnode = newDscp->fid.vnode;
2836 newDirAFSFid.Unique = newDscp->fid.unique;
2838 callp = cm_GetRxConn(connp);
2839 code = RXAFS_Rename(callp, &oldDirAFSFid, oldNamep,
2840 &newDirAFSFid, newNamep,
2841 &updatedOldDirStatus, &updatedNewDirStatus,
2843 rx_PutConnection(callp);
2845 } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
2846 &volSync, NULL, NULL, code));
2847 code = cm_MapRPCError(code, reqp);
2850 osi_Log1(afsd_logp, "CALL Rename FAILURE, code 0x%x", code);
2852 osi_Log0(afsd_logp, "CALL Rename SUCCESS");
2854 /* update the individual stat cache entries for the directories */
2855 lock_ObtainMutex(&oldDscp->mx);
2856 cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
2858 cm_MergeStatus(oldDscp, &updatedOldDirStatus, &volSync,
2861 lock_ReleaseMutex(&oldDscp->mx);
2863 /* and update it for the new one, too, if necessary */
2865 lock_ObtainMutex(&newDscp->mx);
2866 cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
2868 cm_MergeStatus(newDscp, &updatedNewDirStatus, &volSync,
2871 lock_ReleaseMutex(&newDscp->mx);
2874 /* and return error code */
2878 /* Byte range locks:
2880 The OpenAFS Windows client has to fake byte range locks given no
2881 server side support for such locks. This is implemented as keyed
2882 byte range locks on the cache manager.
2884 Keyed byte range locks:
2886 Each cm_scache_t structure keeps track of a list of keyed locks.
2887 The key for a lock identifies an owner of a set of locks (referred
2888 to as a client). Each key is represented by a value. The set of
2889 key values used within a specific cm_scache_t structure form a
2890 namespace that has a scope of just that cm_scache_t structure. The
2891 same key value can be used with another cm_scache_t structure and
2892 correspond to a completely different client. However it is
2893 advantageous for the SMB or IFS layer to make sure that there is a
2894 1-1 mapping between client and keys over all cm_scache_t objects.
2896 Assume a client C has key Key(C) (although, since the scope of the
2897 key is a cm_scache_t, the key can be Key(C,S), where S is the
2898 cm_scache_t. But assume a 1-1 relation between keys and clients).
2899 A byte range (O,+L) denotes byte addresses (O) through (O+L-1)
2900 inclusive (a.k.a. [O,O+L-1]). The function Key(x) is implemented
2901 through cm_generateKey() function for both SMB and IFS.
2903 The list of locks for a cm_scache_t object S is maintained in
2904 S->fileLocks. The cache manager will set a lock on the AFS file
2905 server in order to assert the locks in S->fileLocks. If only
2906 shared locks are in place for S, then the cache manager will obtain
2907 a LockRead lock, while if there are any exclusive locks, it will
2908 obtain a LockWrite lock. If the exclusive locks are all released
2909 while the shared locks remain, then the cache manager will
2910 downgrade the lock from LockWrite to LockRead. Similarly, if an
2911 exclusive lock is obtained when only shared locks exist, then the
2912 cache manager will try to upgrade the lock from LockRead to
2915 Each lock L owned by client C maintains a key L->key such that
2916 L->key == Key(C), the effective range defined by L->LOffset and
2917 L->LLength such that the range of bytes affected by the lock is
2918 (L->LOffset, +L->LLength), a type maintained in L->LockType which
2919 is either exclusive or shared.
2923 A lock exists iff it is in S->fileLocks for some cm_scache_t
2924 S. Existing locks are in one of the following states: ACTIVE,
2925 WAITLOCK, WAITUNLOCK, LOST, DELETED.
2927 The following sections describe each lock and the associated
2930 1. ACTIVE: A lock L is ACTIVE iff the cache manager has asserted
2931 the lock with the AFS file server. This type of lock can be
2932 exercised by a client to read or write to the locked region (as
2935 1.1 ACTIVE->LOST: When the AFS file server fails to extend a
2936 server lock that was required to assert the lock. Before
2937 marking the lock as lost, the cache manager checks if the file
2938 has changed on the server. If the file has not changed, then
2939 the cache manager will attempt to obtain a new server lock
2940 that is sufficient to assert the client side locks for the
2941 file. If any of these fail, the lock is marked as LOST.
2942 Otherwise, it is left as ACTIVE.
2944 1.2 ACTIVE->DELETED: Lock is released.
2946 2. WAITLOCK: A lock is in a WAITLOCK state if the cache manager
2947 grants the lock but the lock is yet to be asserted with the AFS
2948 file server. Once the file server grants the lock, the state
2949 will transition to an ACTIVE lock.
2951 2.1 WAITLOCK->ACTIVE: The server granted the lock.
2953 2.2 WAITLOCK->DELETED: Lock is abandoned, or timed out during
2956 2.3 WAITLOCK->LOST: One or more locks from this client were
2957 marked as LOST. No further locks will be granted to this
2958 client until all lost locks are removed.
2960 3. WAITUNLOCK: A lock is in a WAITUNLOCK state if the cache manager
2961 receives a request for a lock that conflicts with an existing
2962 ACTIVE or WAITLOCK lock. The lock will be placed in the queue
2963 and will be granted at such time the conflicting locks are
2964 removed, at which point the state will transition to either
2967 3.1 WAITUNLOCK->ACTIVE: The conflicting lock was removed. The
2968 current serverLock is sufficient to assert this lock, or a
2969 sufficient serverLock is obtained.
2971 3.2 WAITUNLOCK->WAITLOCK: The conflicting lock was removed,
2972 however the required serverLock is yet to be asserted with the
2975 3.3 WAITUNLOCK->DELETED: The lock is abandoned, timed out or
2978 3.5 WAITUNLOCK->LOST: One or more locks from this client were
2979 marked as LOST. No further locks will be granted to this
2980 client until all lost locks are removed.
2982 4. LOST: A lock L is LOST if the server lock that was required to
2983 assert the lock could not be obtained or if it could not be
2984 extended, or if other locks by the same client were LOST.
2985 Essentially, once a lock is LOST, the contract between the cache
2986 manager and that specific client is no longer valid.
2988 The cache manager rechecks the server lock once every minute and
2989 extends it as appropriate. If this is not done for 5 minutes,
2990 the AFS file server will release the lock (the 5 minute timeout
2991 is based on current file server code and is fairly arbitrary).
2992 Once released, the lock cannot be re-obtained without verifying
2993 that the contents of the file hasn't been modified since the
2994 time the lock was released. Re-obtaining the lock without
2995 verifying this may lead to data corruption. If the lock can not
2996 be obtained safely, then all active locks for the cm_scache_t
2999 4.1 LOST->DELETED: The lock is released.
3001 5. DELETED: The lock is no longer relevant. Eventually, it will
3002 get removed from the cm_scache_t. In the meantime, it will be
3003 treated as if it does not exist.
3005 5.1 DELETED->not exist: The lock is removed from the
3008 The following are classifications of locks based on their state.
3010 6* A lock L is ACCEPTED if it is ACTIVE or WAITLOCK. These locks
3011 have been accepted by the cache manager, but may or may not have
3012 been granted back to the client.
3014 7* A lock L is QUEUED if it is ACTIVE, WAITLOCK or WAITUNLOCK.
3016 8* A lock L is WAITING if it is WAITLOCK or WAITUNLOCK.
3020 A client C can READ range (Offset,+Length) of a file represented by
3021 cm_scache_t S iff (1):
3023 1. for all _a_ in (Offset,+Length), all of the following is true:
3025 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3026 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3029 1.2 For each LOST lock L in S->fileLocks such that _a_ in
3030 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3033 (When locks are lost on an cm_scache_t, all locks are lost. By
3034 4.2 (below), if there is an exclusive LOST lock, then there
3035 can't be any overlapping ACTIVE locks.)
3037 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
3039 2. for all _a_ in (Offset,+Length), one of the following is true:
3041 2.1 Byte _a_ of S is unowned (as specified in 1.1) AND there
3042 does not exist a LOST lock L such that _a_ in
3043 (L->LOffset,+L->LLength).
3045 2.2 Byte _a_ of S is owned by C under lock L (as specified in
3046 1.2) AND L->LockType is exclusive.
3048 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
3050 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
3053 3.1 If L->LockType is exclusive then there does NOT exist a
3054 ACCEPTED lock M in S->fileLocks such that _a_ in
3055 (M->LOffset,+M->LLength).
3057 (If we count all QUEUED locks then we hit cases such as
3058 cascading waiting locks where the locks later on in the queue
3059 can be granted without compromising file integrity. On the
3060 other hand if only ACCEPTED locks are considered, then locks
3061 that were received earlier may end up waiting for locks that
3062 were received later to be unlocked. The choice of ACCEPTED
3063 locks was made to mimic the Windows byte range lock
3066 3.2 If L->LockType is shared then for each ACCEPTED lock M in
3067 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
3068 M->LockType is shared.
3070 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
3072 4.1 M->key != Key(C)
3074 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
3075 and (M->LOffset,+M->LLength) do not intersect.
3077 (Note: If a client loses a lock, it loses all locks.
3078 Subsequently, it will not be allowed to obtain any more locks
3079 until all existing LOST locks that belong to the client are
3080 released. Once all locks are released by a single client,
3081 there exists no further contract between the client and AFS
3082 about the contents of the file, hence the client can then
3083 proceed to obtain new locks and establish a new contract.
3085 This doesn't quite work as you think it should, because most
3086 applications aren't built to deal with losing locks they
3087 thought they once had. For now, we don't have a good
3088 solution to lost locks.
3090 Also, for consistency reasons, we have to hold off on
3091 granting locks that overlap exclusive LOST locks.)
3093 A client C can only unlock locks L in S->fileLocks which have
3096 The representation and invariants are as follows:
3098 - Each cm_scache_t structure keeps:
3100 - A queue of byte-range locks (cm_scache_t::fileLocks) which
3101 are of type cm_file_lock_t.
3103 - A record of the highest server-side lock that has been
3104 obtained for this object (cm_scache_t::serverLock), which is
3105 one of (-1), LockRead, LockWrite.
3107 - A count of ACCEPTED exclusive and shared locks that are in the
3108 queue (cm_scache_t::sharedLocks and
3109 cm_scache_t::exclusiveLocks)
3111 - Each cm_file_lock_t structure keeps:
3113 - The type of lock (cm_file_lock_t::LockType)
3115 - The key associated with the lock (cm_file_lock_t::key)
3117 - The offset and length of the lock (cm_file_lock_t::LOffset
3118 and cm_file_lock_t::LLength)
3120 - The state of the lock.
3122 - Time of issuance or last successful extension
3124 Semantic invariants:
3126 I1. The number of ACCEPTED locks in S->fileLocks are
3127 (S->sharedLocks + S->exclusiveLocks)
3129 External invariants:
3131 I3. S->serverLock is the lock that we have asserted with the
3132 AFS file server for this cm_scache_t.
3134 I4. S->serverLock == LockRead iff there is at least one ACTIVE
3135 shared lock, but no ACTIVE exclusive locks.
3137 I5. S->serverLock == LockWrite iff there is at least one ACTIVE
3140 I6. If L is a LOST lock, then for each lock M in S->fileLocks,
3141 M->key == L->key IMPLIES M is LOST or DELETED.
3146 #define IS_LOCK_ACTIVE(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == 0)
3148 #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)
3150 #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)
3152 #define IS_LOCK_LOST(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_LOST)
3154 #define IS_LOCK_DELETED(lockp) (((lockp)->flags & CM_FILELOCK_FLAG_DELETED) == CM_FILELOCK_FLAG_DELETED)
3157 #define IS_LOCK_ACCEPTED(lockp) (IS_LOCK_ACTIVE(lockp) || IS_LOCK_WAITLOCK(lockp))
3160 #define IS_LOCK_CLIENTONLY(lockp) ((((lockp)->scp->flags & CM_SCACHEFLAG_RO) == CM_SCACHEFLAG_RO) || (((lockp)->flags & CM_FILELOCK_FLAG_CLIENTONLY) == CM_FILELOCK_FLAG_CLIENTONLY))
3163 #define INTERSECT_RANGE(r1,r2) (((r2).offset+(r2).length) > (r1).offset && ((r1).offset +(r1).length) > (r2).offset)
3166 #define CONTAINS_RANGE(r1,r2) (((r2).offset+(r2).length) <= ((r1).offset+(r1).length) && (r1).offset <= (r2).offset)
3168 #if defined(VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS) && !defined(LOCK_TESTING)
3169 #define SCP_SUPPORTS_BRLOCKS(scp) ((scp)->cbServerp && ((scp)->cbServerp->capabilities & VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS))
3171 #define SCP_SUPPORTS_BRLOCKS(scp) (1)
3174 #define SERVERLOCKS_ENABLED(scp) (!((scp)->flags & CM_SCACHEFLAG_RO) && cm_enableServerLocks && SCP_SUPPORTS_BRLOCKS(scp))
3176 static void cm_LockRangeSubtract(cm_range_t * pos, const cm_range_t * neg)
3178 afs_int64 int_begin;
3181 int_begin = MAX(pos->offset, neg->offset);
3182 int_end = MIN(pos->offset+pos->length, neg->offset+neg->length);
3184 if (int_begin < int_end) {
3185 if (int_begin == pos->offset) {
3186 pos->length = pos->offset + pos->length - int_end;
3187 pos->offset = int_end;
3188 } else if (int_end == pos->offset + pos->length) {
3189 pos->length = int_begin - pos->offset;
3192 /* We only subtract ranges if the resulting range is
3193 contiguous. If we try to support non-contigous ranges, we
3194 aren't actually improving performance. */
3198 /* Called with scp->mx held. Returns 0 if all is clear to read the
3199 specified range by the client identified by key.
3201 long cm_LockCheckRead(cm_scache_t *scp,
3202 LARGE_INTEGER LOffset,
3203 LARGE_INTEGER LLength,
3206 #ifndef ADVISORY_LOCKS
3208 cm_file_lock_t *fileLock;
3212 int substract_ranges = FALSE;
3214 range.offset = LOffset.QuadPart;
3215 range.length = LLength.QuadPart;
3219 1. for all _a_ in (Offset,+Length), all of the following is true:
3221 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3222 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3225 1.2 For each LOST lock L in S->fileLocks such that _a_ in
3226 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3231 lock_ObtainRead(&cm_scacheLock);
3233 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
3235 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
3237 if (INTERSECT_RANGE(range, fileLock->range)) {
3238 if (IS_LOCK_ACTIVE(fileLock)) {
3239 if (fileLock->key == key) {
3241 /* If there is an active lock for this client, it
3242 is safe to substract ranges.*/
3243 cm_LockRangeSubtract(&range, &fileLock->range);
3244 substract_ranges = TRUE;
3246 if (fileLock->lockType != LockRead) {
3247 code = CM_ERROR_LOCK_CONFLICT;
3251 /* even if the entire range is locked for reading,
3252 we still can't grant the lock at this point
3253 because the client may have lost locks. That
3254 is, unless we have already seen an active lock
3255 belonging to the client, in which case there
3256 can't be any lost locks for this client. */
3257 if (substract_ranges)
3258 cm_LockRangeSubtract(&range, &fileLock->range);
3260 } else if (IS_LOCK_LOST(fileLock) &&
3261 (fileLock->key == key || fileLock->lockType == LockWrite)) {
3262 code = CM_ERROR_BADFD;
3268 lock_ReleaseRead(&cm_scacheLock);
3270 osi_Log4(afsd_logp, "cm_LockCheckRead scp 0x%x offset %d length %d code 0x%x",
3271 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
3282 /* Called with scp->mx held. Returns 0 if all is clear to write the
3283 specified range by the client identified by key.
3285 long cm_LockCheckWrite(cm_scache_t *scp,
3286 LARGE_INTEGER LOffset,
3287 LARGE_INTEGER LLength,
3290 #ifndef ADVISORY_LOCKS
3292 cm_file_lock_t *fileLock;
3297 range.offset = LOffset.QuadPart;
3298 range.length = LLength.QuadPart;
3301 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
3303 2. for all _a_ in (Offset,+Length), one of the following is true:
3305 2.1 Byte _a_ of S is unowned AND there does not exist a LOST
3306 lock L such that _a_ in (L->LOffset,+L->LLength).
3308 2.2 Byte _a_ of S is owned by C under lock L AND L->LockType is
3312 lock_ObtainRead(&cm_scacheLock);
3314 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
3316 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
3318 if (INTERSECT_RANGE(range, fileLock->range)) {
3319 if (IS_LOCK_ACTIVE(fileLock)) {
3320 if (fileLock->key == key) {
3321 if (fileLock->lockType == LockWrite) {
3323 /* if there is an active lock for this client, it
3324 is safe to substract ranges */
3325 cm_LockRangeSubtract(&range, &fileLock->range);
3327 code = CM_ERROR_LOCK_CONFLICT;
3331 code = CM_ERROR_LOCK_CONFLICT;
3334 } else if (IS_LOCK_LOST(fileLock)) {
3335 code = CM_ERROR_BADFD;
3341 lock_ReleaseRead(&cm_scacheLock);
3343 osi_Log4(afsd_logp, "cm_LockCheckWrite scp 0x%x offset %d length %d code 0x%x",
3344 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
3356 static void cm_LockMarkSCacheLost(cm_scache_t * scp);
3358 /* Called with cm_scacheLock write locked */
3359 static cm_file_lock_t * cm_GetFileLock(void) {
3362 l = (cm_file_lock_t *) cm_freeFileLocks;
3364 osi_QRemove(&cm_freeFileLocks, &l->q);
3366 l = malloc(sizeof(cm_file_lock_t));
3370 memset(l, 0, sizeof(cm_file_lock_t));
3375 /* Called with cm_scacheLock write locked */
3376 static void cm_PutFileLock(cm_file_lock_t *l) {
3377 osi_QAdd(&cm_freeFileLocks, &l->q);
3380 /* called with scp->mx held. May release it during processing, but
3381 leaves it held on exit. */
3382 long cm_IntSetLock(cm_scache_t * scp, cm_user_t * userp, int lockType,
3388 struct rx_connection * callp;
3391 tfid.Volume = scp->fid.volume;
3392 tfid.Vnode = scp->fid.vnode;
3393 tfid.Unique = scp->fid.unique;
3396 osi_Log2(afsd_logp, "CALL SetLock scp 0x%p for lock %d", scp, lockType);
3398 lock_ReleaseMutex(&scp->mx);
3401 code = cm_Conn(&cfid, userp, reqp, &connp);
3405 callp = cm_GetRxConn(connp);
3406 code = RXAFS_SetLock(callp, &tfid, lockType,
3408 rx_PutConnection(callp);
3410 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
3413 code = cm_MapRPCError(code, reqp);
3415 osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
3417 osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
3420 lock_ObtainMutex(&scp->mx);
3425 /* called with scp->mx held. Releases it during processing */
3426 long cm_IntReleaseLock(cm_scache_t * scp, cm_user_t * userp,
3432 struct rx_connection * callp;
3435 tfid.Volume = scp->fid.volume;
3436 tfid.Vnode = scp->fid.vnode;
3437 tfid.Unique = scp->fid.unique;
3440 lock_ReleaseMutex(&scp->mx);
3442 osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
3445 code = cm_Conn(&cfid, userp, reqp, &connp);
3449 callp = cm_GetRxConn(connp);
3450 code = RXAFS_ReleaseLock(callp, &tfid, &volSync);
3451 rx_PutConnection(callp);
3453 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
3455 code = cm_MapRPCError(code, reqp);
3458 "CALL ReleaseLock FAILURE, code 0x%x", code);
3461 "CALL ReleaseLock SUCCESS");
3463 lock_ObtainMutex(&scp->mx);
3468 /* called with scp->mx held. May release it during processing, but
3469 will exit with lock held.
3473 - 0 if the user has permission to get the specified lock for the scp
3475 - CM_ERROR_NOACCESS if not
3477 Any other error from cm_SyncOp will be sent down untranslated.
3479 long cm_LockCheckPerms(cm_scache_t * scp,
3487 /* lock permissions are slightly tricky because of the 'i' bit.
3488 If the user has PRSFS_LOCK, she can read-lock the file. If the
3489 user has PRSFS_WRITE, she can write-lock the file. However, if
3490 the user has PRSFS_INSERT, then she can write-lock new files,
3491 but not old ones. Since we don't have information about
3492 whether a file is new or not, we assume that if the user owns
3493 the scp, then she has the permissions that are granted by
3496 osi_Log3(afsd_logp, "cm_LockCheckPerms for scp[0x%p] type[%d] user[0x%p]",
3497 scp, lock_type, userp);
3499 if (lock_type == LockRead)
3500 rights |= PRSFS_LOCK;
3501 else if (lock_type == LockWrite)
3502 rights |= PRSFS_WRITE;
3509 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
3510 CM_SCACHESYNC_GETSTATUS |
3511 CM_SCACHESYNC_NEEDCALLBACK);
3513 if (code == CM_ERROR_NOACCESS &&
3514 lock_type == LockWrite &&
3515 scp->creator == userp) {
3516 /* check for PRSFS_INSERT. */
3518 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
3519 CM_SCACHESYNC_GETSTATUS |
3520 CM_SCACHESYNC_NEEDCALLBACK);
3522 if (code == CM_ERROR_NOACCESS)
3523 osi_Log0(afsd_logp, "cm_LockCheckPerms user is creator but has no INSERT bits for scp");
3526 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
3528 osi_Log1(afsd_logp, "cm_LockCheckPerms returning code %d", code);
3533 /* called with scp->mx held */
3534 long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
3535 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
3537 int allowWait, cm_user_t *userp, cm_req_t *reqp,
3538 cm_file_lock_t **lockpp)
3541 int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
3542 cm_file_lock_t *fileLock;
3545 int wait_unlock = FALSE;
3546 int force_client_lock = FALSE;
3548 osi_Log4(afsd_logp, "cm_Lock scp 0x%x type 0x%x offset %d length %d",
3549 scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
3550 osi_Log3(afsd_logp, "... allowWait %d key 0x%x:%x", allowWait,
3551 (unsigned long)(key >> 32), (unsigned long)(key & 0xffffffff));
3554 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
3556 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
3559 3.1 If L->LockType is exclusive then there does NOT exist a
3560 ACCEPTED lock M in S->fileLocks such that _a_ in
3561 (M->LOffset,+M->LLength).
3563 3.2 If L->LockType is shared then for each ACCEPTED lock M in
3564 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
3565 M->LockType is shared.
3567 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
3569 4.1 M->key != Key(C)
3571 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
3572 and (M->LOffset,+M->LLength) do not intersect.
3575 range.offset = LOffset.QuadPart;
3576 range.length = LLength.QuadPart;
3578 lock_ObtainRead(&cm_scacheLock);
3580 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
3582 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
3584 if (IS_LOCK_LOST(fileLock)) {
3585 if (fileLock->key == key) {
3586 code = CM_ERROR_BADFD;
3588 } else if (fileLock->lockType == LockWrite && INTERSECT_RANGE(range, fileLock->range)) {
3589 code = CM_ERROR_WOULDBLOCK;
3595 /* we don't need to check for deleted locks here since deleted
3596 locks are dequeued from scp->fileLocks */
3597 if (IS_LOCK_ACCEPTED(fileLock) &&
3598 INTERSECT_RANGE(range, fileLock->range)) {
3600 if ((sLockType & LOCKING_ANDX_SHARED_LOCK) == 0 ||
3601 fileLock->lockType != LockRead) {
3603 code = CM_ERROR_WOULDBLOCK;
3609 lock_ReleaseRead(&cm_scacheLock);
3611 if (code == 0 && SERVERLOCKS_ENABLED(scp)) {
3612 if (Which == scp->serverLock ||
3613 (Which == LockRead && scp->serverLock == LockWrite)) {
3615 /* we already have the lock we need */
3616 osi_Log3(afsd_logp, " we already have the correct lock. exclusives[%d], shared[%d], serverLock[%d]",
3617 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
3619 code = cm_LockCheckPerms(scp, Which, userp, reqp);
3621 /* special case: if we don't have permission to read-lock
3622 the file, then we force a clientside lock. This is to
3623 compensate for applications that obtain a read-lock for
3624 reading files off of directories that don't grant
3625 read-locks to the user. */
3626 if (code == CM_ERROR_NOACCESS && Which == LockRead) {
3627 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
3628 force_client_lock = TRUE;
3631 } else if ((scp->exclusiveLocks > 0) ||
3632 (scp->sharedLocks > 0 && scp->serverLock != LockRead)) {
3634 /* We are already waiting for some other lock. We should
3635 wait for the daemon to catch up instead of generating a
3636 flood of SetLock calls. */
3637 osi_Log3(afsd_logp, " already waiting for other lock. exclusives[%d], shared[%d], serverLock[%d]",
3638 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
3640 /* see if we have permission to create the lock in the
3642 code = cm_LockCheckPerms(scp, Which, userp, reqp);
3644 code = CM_ERROR_WOULDBLOCK;
3645 else if (code == CM_ERROR_NOACCESS && Which == LockRead) {
3646 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
3647 force_client_lock = TRUE;
3650 /* leave any other codes as-is */
3654 int check_data_version = FALSE;
3656 /* first check if we have permission to elevate or obtain
3658 code = cm_LockCheckPerms(scp, Which, userp, reqp);
3660 if (code == CM_ERROR_NOACCESS && Which == LockRead) {
3661 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
3662 force_client_lock = TRUE;
3667 if (scp->serverLock == LockRead && Which == LockWrite) {
3669 /* We want to escalate the lock to a LockWrite.
3670 Unfortunately that's not really possible without
3671 letting go of the current lock. But for now we do
3675 " attempting to UPGRADE from LockRead to LockWrite.");
3677 " dataVersion on scp: %d", scp->dataVersion);
3679 /* we assume at this point (because scp->serverLock
3680 was valid) that we had a valid server lock. */
3681 scp->lockDataVersion = scp->dataVersion;
3682 check_data_version = TRUE;
3684 code = cm_IntReleaseLock(scp, userp, reqp);
3687 /* We couldn't release the lock */
3690 scp->serverLock = -1;
3694 /* We need to obtain a server lock of type Which in order
3695 to assert this file lock */
3696 #ifndef AGGRESSIVE_LOCKS
3699 newLock = LockWrite;
3701 code = cm_IntSetLock(scp, userp, newLock, reqp);
3703 if (code == CM_ERROR_WOULDBLOCK && newLock != Which) {
3704 /* we wanted LockRead. We tried LockWrite. Now try
3709 osi_assert(newLock == LockRead);
3711 code = cm_IntSetLock(scp, userp, newLock, reqp);
3714 if (code == 0 && check_data_version &&
3715 scp->dataVersion != scp->lockDataVersion) {
3716 /* We lost a race. Although we successfully obtained
3717 a lock, someone modified the file in between. The
3718 locks have all been technically lost. */
3721 " Data version mismatch while upgrading lock.");
3723 " Data versions before=%d, after=%d",
3724 scp->lockDataVersion,
3727 " Releasing stale lock for scp 0x%x", scp);
3729 code = cm_IntReleaseLock(scp, userp, reqp);
3731 scp->serverLock = -1;
3733 code = CM_ERROR_INVAL;
3734 } else if (code == 0) {
3735 scp->serverLock = newLock;
3736 scp->lockDataVersion = scp->dataVersion;
3740 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
3741 scp->serverLock == -1) {
3742 /* Oops. We lost the lock. */
3743 cm_LockMarkSCacheLost(scp);
3746 } else if (code == 0) { /* server locks not enabled */
3748 " Skipping server lock for scp");
3753 if (code != 0 && !force_client_lock) {
3754 /* Special case error translations
3756 Applications don't expect certain errors from a
3757 LockFile/UnlockFile call. We need to translate some error
3758 code to codes that apps expect and handle. */
3760 /* We shouldn't actually need to handle this case since we
3761 simulate locks for RO scps anyway. */
3762 if (code == CM_ERROR_READONLY) {
3763 osi_Log0(afsd_logp, " Reinterpreting CM_ERROR_READONLY as CM_ERROR_NOACCESS");
3764 code = CM_ERROR_NOACCESS;
3768 if (code == 0 || (code == CM_ERROR_WOULDBLOCK && allowWait) ||
3769 force_client_lock) {
3771 /* clear the error if we are forcing a client lock, so we
3772 don't get confused later. */
3773 if (force_client_lock && code != CM_ERROR_WOULDBLOCK)
3776 lock_ObtainWrite(&cm_scacheLock);
3777 fileLock = cm_GetFileLock();
3778 lock_ReleaseWrite(&cm_scacheLock);
3780 fileLock->fid = scp->fid;
3782 fileLock->key = key;
3783 fileLock->lockType = Which;
3785 fileLock->userp = userp;
3786 fileLock->range = range;
3787 fileLock->flags = (code == 0 ? 0 :
3789 CM_FILELOCK_FLAG_WAITUNLOCK :
3790 CM_FILELOCK_FLAG_WAITLOCK));
3792 if (force_client_lock || !SERVERLOCKS_ENABLED(scp))
3793 fileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
3795 fileLock->lastUpdate = (code == 0 && !force_client_lock) ? time(NULL) : 0;
3797 lock_ObtainWrite(&cm_scacheLock);
3798 osi_QAddT(&scp->fileLocksH, &scp->fileLocksT, &fileLock->fileq);
3799 cm_HoldSCacheNoLock(scp);
3800 fileLock->scp = scp;
3801 osi_QAdd(&cm_allFileLocks, &fileLock->q);
3802 lock_ReleaseWrite(&cm_scacheLock);
3808 if (IS_LOCK_CLIENTONLY(fileLock)) {
3810 } else if (IS_LOCK_ACCEPTED(fileLock)) {
3811 if (Which == LockRead)
3814 scp->exclusiveLocks++;
3818 "cm_Lock Lock added 0x%p flags 0x%x to scp [0x%p]",
3819 fileLock, fileLock->flags, scp);
3821 " exclusives[%d] shared[%d] client[%d] serverLock[%d]",
3822 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
3823 (int)(signed char) scp->serverLock);
3826 "cm_Lock Rejecting lock (code = 0x%x)", code);
3832 static int cm_KeyEquals(cm_key_t k1, cm_key_t k2, int flags);
3834 /* Called with scp->mx held */
3835 long cm_UnlockByKey(cm_scache_t * scp,
3842 cm_file_lock_t *fileLock;
3843 osi_queue_t *q, *qn;
3846 osi_Log4(afsd_logp, "cm_UnlockByKey scp 0x%p key 0x%x:%x flags=0x%x",
3848 (unsigned long)(key >> 32),
3849 (unsigned long)(key & 0xffffffff),
3852 lock_ObtainWrite(&cm_scacheLock);
3854 for (q = scp->fileLocksH; q; q = qn) {
3857 fileLock = (cm_file_lock_t *)
3858 ((char *) q - offsetof(cm_file_lock_t, fileq));
3861 osi_Log4(afsd_logp, " Checking lock[0x%x] range[%d,+%d] type[%d]",
3863 (unsigned long) fileLock->range.offset,
3864 (unsigned long) fileLock->range.length,
3865 fileLock->lockType);
3866 osi_Log3(afsd_logp, " key[0x%x:%x] flags[0x%x]",
3867 (unsigned long)(fileLock->key >> 32),
3868 (unsigned long)(fileLock->key & 0xffffffff),
3871 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
3872 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
3873 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
3875 fileLock->fid.volume,
3876 fileLock->fid.vnode,
3877 fileLock->fid.unique);
3878 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
3879 fileLock->scp->fid.cell,
3880 fileLock->scp->fid.volume,
3881 fileLock->scp->fid.vnode,
3882 fileLock->scp->fid.unique);
3887 if (!IS_LOCK_DELETED(fileLock) &&
3888 cm_KeyEquals(fileLock->key, key, flags)) {
3889 osi_Log3(afsd_logp, "...Unlock range [%d,+%d] type %d",
3890 fileLock->range.offset,
3891 fileLock->range.length,
3892 fileLock->lockType);
3894 if (scp->fileLocksT == q)
3895 scp->fileLocksT = osi_QPrev(q);
3896 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
3898 if (IS_LOCK_CLIENTONLY(fileLock)) {
3900 } else if (IS_LOCK_ACCEPTED(fileLock)) {
3901 if (fileLock->lockType == LockRead)
3904 scp->exclusiveLocks--;
3907 fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
3909 cm_ReleaseUser(fileLock->userp);
3910 cm_ReleaseSCacheNoLock(scp);
3912 fileLock->userp = NULL;
3913 fileLock->scp = NULL;
3919 lock_ReleaseWrite(&cm_scacheLock);
3921 if (n_unlocks == 0) {
3922 osi_Log0(afsd_logp, "cm_UnlockByKey no locks found");
3923 osi_Log3(afsd_logp, " Leaving scp with exclusives[%d], shared[%d], serverLock[%d]",
3924 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
3929 osi_Log1(afsd_logp, "cm_UnlockByKey done with %d locks", n_unlocks);
3931 osi_assertx(scp->sharedLocks >= 0, "scp->sharedLocks < 0");
3932 osi_assertx(scp->exclusiveLocks >= 0, "scp->exclusiveLocks < 0");
3933 osi_assertx(scp->clientLocks >= 0, "scp->clientLocks < 0");
3935 if (!SERVERLOCKS_ENABLED(scp)) {
3936 osi_Log0(afsd_logp, " Skipping server lock for scp");
3940 /* Ideally we would go through the rest of the locks to determine
3941 * if one or more locks that were formerly in WAITUNLOCK can now
3942 * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
3943 * scp->sharedLocks accordingly. However, the retrying of locks
3944 * in that manner is done cm_RetryLock() manually.
3947 if (scp->serverLock == LockWrite &&
3948 scp->exclusiveLocks == 0 &&
3949 scp->sharedLocks > 0) {
3951 /* The serverLock should be downgraded to LockRead */
3952 osi_Log0(afsd_logp, " DOWNGRADE lock from LockWrite to LockRead");
3954 /* since scp->serverLock looked sane, we are going to assume
3955 that we have a valid server lock. */
3956 scp->lockDataVersion = scp->dataVersion;
3957 osi_Log1(afsd_logp, " dataVersion on scp = %d", scp->dataVersion);
3959 code = cm_IntReleaseLock(scp, userp, reqp);
3962 /* so we couldn't release it. Just let the lock be for now */
3966 scp->serverLock = -1;
3969 code = cm_IntSetLock(scp, userp, LockRead, reqp);
3971 if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
3972 scp->serverLock = LockRead;
3973 } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
3974 /* We lost a race condition. Although we have a valid
3975 lock on the file, the data has changed and essentially
3976 we have lost the lock we had during the transition. */
3978 osi_Log0(afsd_logp, "Data version mismatch during lock downgrade");
3979 osi_Log2(afsd_logp, " Data versions before=%d, after=%d",
3980 scp->lockDataVersion,
3983 code = cm_IntReleaseLock(scp, userp, reqp);
3985 code = CM_ERROR_INVAL;
3986 scp->serverLock = -1;
3990 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
3991 (scp->serverLock == -1)) {
3993 cm_LockMarkSCacheLost(scp);
3996 /* failure here has no bearing on the return value of
4000 } else if (scp->serverLock != (-1) &&
4001 scp->exclusiveLocks == 0 &&
4002 scp->sharedLocks == 0) {
4003 /* The serverLock should be released entirely */
4005 code = cm_IntReleaseLock(scp, userp, reqp);
4008 scp->serverLock = (-1);
4013 osi_Log1(afsd_logp, "cm_UnlockByKey code 0x%x", code);
4014 osi_Log4(afsd_logp, " Leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
4015 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4016 (int)(signed char) scp->serverLock);
4021 long cm_Unlock(cm_scache_t *scp,
4022 unsigned char sLockType,
4023 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
4029 int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
4030 cm_file_lock_t *fileLock;
4032 int release_userp = FALSE;
4034 osi_Log4(afsd_logp, "cm_Unlock scp 0x%p type 0x%x offset %d length %d",
4035 scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
4036 osi_Log2(afsd_logp, "... key 0x%x:%x",
4037 (unsigned long) (key >> 32), (unsigned long) (key & 0xffffffff));
4039 lock_ObtainRead(&cm_scacheLock);
4041 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4042 fileLock = (cm_file_lock_t *)
4043 ((char *) q - offsetof(cm_file_lock_t, fileq));
4046 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
4047 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
4048 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4050 fileLock->fid.volume,
4051 fileLock->fid.vnode,
4052 fileLock->fid.unique);
4053 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4054 fileLock->scp->fid.cell,
4055 fileLock->scp->fid.volume,
4056 fileLock->scp->fid.vnode,
4057 fileLock->scp->fid.unique);
4061 if (!IS_LOCK_DELETED(fileLock) &&
4062 fileLock->key == key &&
4063 fileLock->range.offset == LOffset.QuadPart &&
4064 fileLock->range.length == LLength.QuadPart) {
4070 osi_Log0(afsd_logp, "cm_Unlock lock not found; failure");
4072 lock_ReleaseRead(&cm_scacheLock);
4074 /* The lock didn't exist anyway. *shrug* */
4078 lock_ReleaseRead(&cm_scacheLock);
4080 /* discard lock record */
4081 lock_ObtainWrite(&cm_scacheLock);
4082 if (scp->fileLocksT == q)
4083 scp->fileLocksT = osi_QPrev(q);
4084 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
4087 * Don't delete it here; let the daemon delete it, to simplify
4088 * the daemon's traversal of the list.
4091 if (IS_LOCK_CLIENTONLY(fileLock)) {
4093 } else if (IS_LOCK_ACCEPTED(fileLock)) {
4094 if (fileLock->lockType == LockRead)
4097 scp->exclusiveLocks--;
4100 fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
4101 if (userp != NULL) {
4102 cm_ReleaseUser(fileLock->userp);
4104 userp = fileLock->userp;
4105 release_userp = TRUE;
4107 fileLock->userp = NULL;
4108 cm_ReleaseSCacheNoLock(scp);
4109 fileLock->scp = NULL;
4110 lock_ReleaseWrite(&cm_scacheLock);
4112 if (!SERVERLOCKS_ENABLED(scp)) {
4113 osi_Log0(afsd_logp, " Skipping server locks for scp");
4117 /* Ideally we would go through the rest of the locks to determine
4118 * if one or more locks that were formerly in WAITUNLOCK can now
4119 * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
4120 * scp->sharedLocks accordingly. However, the retrying of locks
4121 * in that manner is done cm_RetryLock() manually.
4124 if (scp->serverLock == LockWrite &&
4125 scp->exclusiveLocks == 0 &&
4126 scp->sharedLocks > 0) {
4128 /* The serverLock should be downgraded to LockRead */
4129 osi_Log0(afsd_logp, " DOWNGRADE lock from LockWrite to LockRead");
4131 /* Since we already had a lock, we assume that there is a
4132 valid server lock. */
4133 scp->lockDataVersion = scp->dataVersion;
4134 osi_Log1(afsd_logp, " dataVersion on scp is %d", scp->dataVersion);
4136 code = cm_IntReleaseLock(scp, userp, reqp);
4139 /* so we couldn't release it. Just let the lock be for now */
4143 scp->serverLock = -1;
4146 code = cm_IntSetLock(scp, userp, LockRead, reqp);
4148 if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
4149 scp->serverLock = LockRead;
4150 } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
4151 /* Lost a race. We obtained a new lock, but that is
4152 meaningless since someone modified the file
4156 "Data version mismatch while downgrading lock");
4158 " Data versions before=%d, after=%d",
4159 scp->lockDataVersion,
4162 code = cm_IntReleaseLock(scp, userp, reqp);
4164 scp->serverLock = -1;
4165 code = CM_ERROR_INVAL;
4169 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4170 (scp->serverLock == -1)) {
4172 cm_LockMarkSCacheLost(scp);
4175 /* failure here has no bearing on the return value of
4179 } else if (scp->serverLock != (-1) &&
4180 scp->exclusiveLocks == 0 &&
4181 scp->sharedLocks == 0) {
4182 /* The serverLock should be released entirely */
4184 code = cm_IntReleaseLock(scp, userp, reqp);
4187 scp->serverLock = (-1);
4192 cm_ReleaseUser(userp);
4196 osi_Log1(afsd_logp, "cm_Unlock code 0x%x", code);
4197 osi_Log4(afsd_logp, " leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
4198 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4199 (int)(signed char) scp->serverLock);
4204 /* called with scp->mx held */
4205 static void cm_LockMarkSCacheLost(cm_scache_t * scp)
4207 cm_file_lock_t *fileLock;
4210 osi_Log1(afsd_logp, "cm_LockMarkSCacheLost scp 0x%x", scp);
4213 /* With the current code, we can't lose a lock on a RO scp */
4214 osi_assert(!(scp->flags & CM_SCACHEFLAG_RO));
4217 /* cm_scacheLock needed because we are modifying fileLock->flags */
4218 lock_ObtainWrite(&cm_scacheLock);
4220 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4222 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4224 if (IS_LOCK_ACTIVE(fileLock) &&
4225 !IS_LOCK_CLIENTONLY(fileLock)) {
4226 if (fileLock->lockType == LockRead)
4229 scp->exclusiveLocks--;
4231 fileLock->flags |= CM_FILELOCK_FLAG_LOST;
4235 scp->serverLock = -1;
4236 scp->lockDataVersion = -1;
4237 lock_ReleaseWrite(&cm_scacheLock);
4240 /* Called with no relevant locks held */
4241 void cm_CheckLocks()
4243 osi_queue_t *q, *nq;
4244 cm_file_lock_t *fileLock;
4250 struct rx_connection * callp;
4255 lock_ObtainWrite(&cm_scacheLock);
4257 cm_lockRefreshCycle++;
4259 osi_Log1(afsd_logp, "cm_CheckLocks starting lock check cycle %d", cm_lockRefreshCycle);
4261 for (q = cm_allFileLocks; q; q = nq) {
4262 fileLock = (cm_file_lock_t *) q;
4266 if (IS_LOCK_DELETED(fileLock)) {
4268 osi_QRemove(&cm_allFileLocks, q);
4269 cm_PutFileLock(fileLock);
4271 } else if (IS_LOCK_ACTIVE(fileLock) && !IS_LOCK_CLIENTONLY(fileLock)) {
4273 /* Server locks must have been enabled for us to have
4274 received an active non-client-only lock. */
4275 osi_assert(cm_enableServerLocks);
4277 scp = fileLock->scp;
4278 osi_assert(scp != NULL);
4280 cm_HoldSCacheNoLock(scp);
4283 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
4284 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
4285 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4287 fileLock->fid.volume,
4288 fileLock->fid.vnode,
4289 fileLock->fid.unique);
4290 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4291 fileLock->scp->fid.cell,
4292 fileLock->scp->fid.volume,
4293 fileLock->scp->fid.vnode,
4294 fileLock->scp->fid.unique);
4298 /* Server locks are extended once per scp per refresh
4300 if (scp->lastRefreshCycle != cm_lockRefreshCycle) {
4302 int scp_done = FALSE;
4304 osi_Log1(afsd_logp, "cm_CheckLocks Updating scp 0x%x", scp);
4306 lock_ReleaseWrite(&cm_scacheLock);
4307 lock_ObtainMutex(&scp->mx);
4309 /* did the lock change while we weren't holding the lock? */
4310 if (!IS_LOCK_ACTIVE(fileLock))
4311 goto post_syncopdone;
4313 code = cm_SyncOp(scp, NULL, fileLock->userp, &req, 0,
4314 CM_SCACHESYNC_NEEDCALLBACK
4315 | CM_SCACHESYNC_GETSTATUS
4316 | CM_SCACHESYNC_LOCK);
4320 "cm_CheckLocks SyncOp failure code 0x%x", code);
4321 goto post_syncopdone;
4324 /* cm_SyncOp releases scp->mx during which the lock
4325 may get released. */
4326 if (!IS_LOCK_ACTIVE(fileLock))
4327 goto pre_syncopdone;
4329 if (scp->serverLock != -1) {
4333 tfid.Volume = scp->fid.volume;
4334 tfid.Vnode = scp->fid.vnode;
4335 tfid.Unique = scp->fid.unique;
4337 userp = fileLock->userp;
4339 osi_Log3(afsd_logp, "CALL ExtendLock lock 0x%p for scp=0x%p with lock %d",
4342 (int) scp->serverLock);
4344 lock_ReleaseMutex(&scp->mx);
4347 code = cm_Conn(&cfid, userp,
4352 callp = cm_GetRxConn(connp);
4353 code = RXAFS_ExtendLock(callp, &tfid,
4355 rx_PutConnection(callp);
4357 osi_Log1(afsd_logp, " ExtendLock returns %d", code);
4359 } while (cm_Analyze(connp, userp, &req,
4360 &cfid, &volSync, NULL, NULL,
4363 code = cm_MapRPCError(code, &req);
4365 lock_ObtainMutex(&scp->mx);
4368 osi_Log1(afsd_logp, "CALL ExtendLock FAILURE, code 0x%x", code);
4370 osi_Log0(afsd_logp, "CALL ExtendLock SUCCESS");
4371 scp->lockDataVersion = scp->dataVersion;
4374 if ((code == EINVAL || code == CM_ERROR_INVAL) &&
4375 scp->lockDataVersion == scp->dataVersion) {
4379 (scp->exclusiveLocks > 0) ? LockWrite: LockRead;
4381 /* we might still have a chance to obtain a
4384 code = cm_IntSetLock(scp, userp, lockType, &req);
4387 code = CM_ERROR_INVAL;
4388 } else if (scp->lockDataVersion != scp->dataVersion) {
4390 /* now check if we still have the file at
4391 the right data version. */
4393 "Data version mismatch on scp 0x%p",
4396 " Data versions: before=%d, after=%d",
4397 scp->lockDataVersion,
4400 code = cm_IntReleaseLock(scp, userp, &req);
4402 code = CM_ERROR_INVAL;
4406 if (code == EINVAL || code == CM_ERROR_INVAL) {
4407 cm_LockMarkSCacheLost(scp);
4411 /* interestingly, we have found an active lock
4412 belonging to an scache that has no
4414 cm_LockMarkSCacheLost(scp);
4421 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
4424 lock_ReleaseMutex(&scp->mx);
4426 lock_ObtainWrite(&cm_scacheLock);
4429 fileLock->lastUpdate = time(NULL);
4433 scp->lastRefreshCycle = cm_lockRefreshCycle;
4436 /* we have already refreshed the locks on this scp */
4437 fileLock->lastUpdate = time(NULL);
4440 cm_ReleaseSCacheNoLock(scp);
4442 } else if (IS_LOCK_ACTIVE(fileLock) && IS_LOCK_CLIENTONLY(fileLock)) {
4443 /* TODO: Check callbacks */
4447 lock_ReleaseWrite(&cm_scacheLock);
4448 osi_Log1(afsd_logp, "cm_CheckLocks completes lock check cycle %d", cm_lockRefreshCycle);
4451 /* NOT called with scp->mx held. */
4452 long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
4455 cm_scache_t *scp = NULL;
4456 cm_file_lock_t *fileLock;
4460 int force_client_lock = FALSE;
4464 if (client_is_dead) {
4465 code = CM_ERROR_TIMEDOUT;
4469 lock_ObtainRead(&cm_scacheLock);
4471 osi_Log2(afsd_logp, "cm_RetryLock checking lock %p (scp=%p)", oldFileLock, oldFileLock->scp);
4472 osi_Log4(afsd_logp, " offset(%x:%x) length(%x:%x)",
4473 (unsigned)(oldFileLock->range.offset >> 32),
4474 (unsigned)(oldFileLock->range.offset & 0xffffffff),
4475 (unsigned)(oldFileLock->range.length >> 32),
4476 (unsigned)(oldFileLock->range.length & 0xffffffff));
4477 osi_Log3(afsd_logp, " key(%x:%x) flags=%x",
4478 (unsigned)(oldFileLock->key >> 32),
4479 (unsigned)(oldFileLock->key & 0xffffffff),
4480 (unsigned)(oldFileLock->flags));
4482 /* if the lock has already been granted, then we have nothing to do */
4483 if (IS_LOCK_ACTIVE(oldFileLock)) {
4484 lock_ReleaseRead(&cm_scacheLock);
4485 osi_Log0(afsd_logp, "cm_RetryLock lock already granted");
4489 /* we can't do anything with lost or deleted locks at the moment. */
4490 if (IS_LOCK_LOST(oldFileLock) || IS_LOCK_DELETED(oldFileLock)) {
4491 code = CM_ERROR_BADFD;
4492 osi_Log0(afsd_logp, "cm_RetryLock lock is lost or deleted");
4493 lock_ReleaseRead(&cm_scacheLock);
4497 scp = oldFileLock->scp;
4499 osi_assert(scp != NULL);
4501 lock_ReleaseRead(&cm_scacheLock);
4502 lock_ObtainMutex(&scp->mx);
4504 code = cm_LockCheckPerms(scp, oldFileLock->lockType,
4508 if (code == CM_ERROR_NOACCESS && oldFileLock->lockType == LockRead) {
4509 force_client_lock = TRUE;
4512 lock_ReleaseMutex(&scp->mx);
4516 lock_ObtainWrite(&cm_scacheLock);
4518 /* Check if we already have a sufficient server lock to allow this
4519 lock to go through. */
4520 if (IS_LOCK_WAITLOCK(oldFileLock) &&
4521 (!SERVERLOCKS_ENABLED(scp) ||
4522 scp->serverLock == oldFileLock->lockType ||
4523 scp->serverLock == LockWrite)) {
4525 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
4527 if (SERVERLOCKS_ENABLED(scp)) {
4528 osi_Log1(afsd_logp, "cm_RetryLock Server lock (%d) is sufficient for lock. Granting",
4529 (int) scp->serverLock);
4531 osi_Log0(afsd_logp, "cm_RetryLock skipping server lock for scp");
4534 lock_ReleaseWrite(&cm_scacheLock);
4535 lock_ReleaseMutex(&scp->mx);
4540 if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
4542 /* check if the conflicting locks have dissappeared already */
4543 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4545 fileLock = (cm_file_lock_t *)
4546 ((char *) q - offsetof(cm_file_lock_t, fileq));
4548 if (IS_LOCK_LOST(fileLock)) {
4549 if (fileLock->key == oldFileLock->key) {
4550 code = CM_ERROR_BADFD;
4551 oldFileLock->flags |= CM_FILELOCK_FLAG_LOST;
4552 osi_Log1(afsd_logp, " found lost lock %p for same key. Marking lock as lost",
4555 } else if (fileLock->lockType == LockWrite &&
4556 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
4557 osi_Log1(afsd_logp, " found conflicting LOST lock %p", fileLock);
4558 code = CM_ERROR_WOULDBLOCK;
4563 if (IS_LOCK_ACCEPTED(fileLock) &&
4564 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
4566 if (oldFileLock->lockType != LockRead ||
4567 fileLock->lockType != LockRead) {
4569 osi_Log1(afsd_logp, " found conflicting lock %p", fileLock);
4570 code = CM_ERROR_WOULDBLOCK;
4578 lock_ReleaseWrite(&cm_scacheLock);
4579 lock_ReleaseMutex(&scp->mx);
4584 /* when we get here, the lock is either a WAITUNLOCK or WAITLOCK.
4585 If it is WAITUNLOCK, then we didn't find any conflicting lock
4586 but we haven't verfied whether the serverLock is sufficient to
4587 assert it. If it is WAITLOCK, then the serverLock is
4588 insufficient to assert it. Eitherway, we are ready to accept
4589 the lock as either ACTIVE or WAITLOCK depending on the
4592 /* First, promote the WAITUNLOCK to a WAITLOCK */
4593 if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
4594 if (oldFileLock->lockType == LockRead)
4597 scp->exclusiveLocks++;
4599 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITUNLOCK;
4600 oldFileLock->flags |= CM_FILELOCK_FLAG_WAITLOCK;
4603 if (force_client_lock ||
4604 !SERVERLOCKS_ENABLED(scp) ||
4605 scp->serverLock == oldFileLock->lockType ||
4606 (oldFileLock->lockType == LockRead &&
4607 scp->serverLock == LockWrite)) {
4609 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
4611 if ((force_client_lock ||
4612 !SERVERLOCKS_ENABLED(scp)) &&
4613 !IS_LOCK_CLIENTONLY(oldFileLock)) {
4615 oldFileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
4617 if (oldFileLock->lockType == LockRead)
4620 scp->exclusiveLocks--;
4625 lock_ReleaseWrite(&cm_scacheLock);
4626 lock_ReleaseMutex(&scp->mx);
4633 code = cm_SyncOp(scp, NULL, oldFileLock->userp, &req, 0,
4634 CM_SCACHESYNC_NEEDCALLBACK
4635 | CM_SCACHESYNC_GETSTATUS
4636 | CM_SCACHESYNC_LOCK);
4638 osi_Log1(smb_logp, "cm_RetryLock SyncOp failure code 0x%x", code);
4639 lock_ReleaseWrite(&cm_scacheLock);
4640 goto post_syncopdone;
4643 if (!IS_LOCK_WAITLOCK(oldFileLock))
4644 goto pre_syncopdone;
4646 userp = oldFileLock->userp;
4648 #ifndef AGGRESSIVE_LOCKS
4649 newLock = oldFileLock->lockType;
4651 newLock = LockWrite;
4654 lock_ReleaseWrite(&cm_scacheLock);
4656 code = cm_IntSetLock(scp, userp, newLock, &req);
4659 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
4665 if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
4666 lock_ObtainWrite(&cm_scacheLock);
4667 if (scp->fileLocksT == &oldFileLock->fileq)
4668 scp->fileLocksT = osi_QPrev(&oldFileLock->fileq);
4669 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, &oldFileLock->fileq);
4670 lock_ReleaseWrite(&cm_scacheLock);
4671 } else if (code == 0 && IS_LOCK_WAITLOCK(oldFileLock)) {
4672 scp->serverLock = newLock;
4674 lock_ReleaseMutex(&scp->mx);
4677 lock_ObtainWrite(&cm_scacheLock);
4679 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
4680 } else if (code != CM_ERROR_WOULDBLOCK) {
4681 oldFileLock->flags |= CM_FILELOCK_FLAG_DELETED;
4682 cm_ReleaseUser(oldFileLock->userp);
4683 oldFileLock->userp = NULL;
4684 if (oldFileLock->scp) {
4685 cm_ReleaseSCacheNoLock(oldFileLock->scp);
4686 oldFileLock->scp = NULL;
4689 lock_ReleaseWrite(&cm_scacheLock);
4694 cm_key_t cm_GenerateKey(unsigned int session_id, unsigned long process_id, unsigned int file_id)
4697 osi_assert((process_id & 0xffffffff) == process_id);
4698 osi_assert((session_id & 0xffff) == session_id);
4699 osi_assert((file_id & 0xffff) == file_id);
4703 (((cm_key_t) (process_id & 0xffffffff)) << 32) |
4704 (((cm_key_t) (session_id & 0xffff)) << 16) |
4705 (((cm_key_t) (file_id & 0xffff)));
4708 static int cm_KeyEquals(cm_key_t k1, cm_key_t k2, int flags)
4710 if (flags & CM_UNLOCK_BY_FID) {
4711 return ((k1 & 0xffffffff) == (k2 & 0xffffffff));
4717 void cm_ReleaseAllLocks(void)
4723 cm_file_lock_t *fileLock;
4726 for (i = 0; i < cm_data.hashTableSize; i++)
4728 for ( scp = cm_data.hashTablep[i]; scp; scp = scp->nextp ) {
4729 while (scp->fileLocksH != NULL) {
4730 lock_ObtainMutex(&scp->mx);
4731 lock_ObtainWrite(&cm_scacheLock);
4732 if (!scp->fileLocksH) {
4733 lock_ReleaseWrite(&cm_scacheLock);
4734 lock_ReleaseMutex(&scp->mx);
4737 fileLock = (cm_file_lock_t *)((char *) scp->fileLocksH - offsetof(cm_file_lock_t, fileq));
4738 userp = fileLock->userp;
4740 key = fileLock->key;
4741 cm_HoldSCacheNoLock(scp);
4742 lock_ReleaseWrite(&cm_scacheLock);
4743 cm_UnlockByKey(scp, key, 0, userp, &req);
4744 cm_ReleaseSCache(scp);
4745 cm_ReleaseUser(userp);
4746 lock_ReleaseMutex(&scp->mx);