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>
27 /* Used by cm_FollowMountPoint */
33 extern void afsi_log(char *pattern, ...);
36 int cm_enableServerLocks = 0;
39 * Case-folding array. This was constructed by inspecting of SMBtrace output.
40 * I do not know anything more about it.
42 unsigned char cm_foldUpper[256] = {
43 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
44 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
45 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
46 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
47 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
48 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
49 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
50 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
51 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
52 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
53 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
54 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
55 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
56 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
57 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
58 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
59 0x80, 0x9a, 0x90, 0x41, 0x8e, 0x41, 0x8f, 0x80,
60 0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x8e, 0x8f,
61 0x90, 0x92, 0x92, 0x4f, 0x99, 0x4f, 0x55, 0x55,
62 0x59, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
63 0x41, 0x49, 0x4f, 0x55, 0xa5, 0xa5, 0x56, 0xa7,
64 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
65 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
66 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
67 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
68 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
69 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
70 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
71 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
72 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
73 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
74 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
78 * Case-insensitive string comparison. We used to use stricmp, but it doesn't
79 * know about 8-bit characters (e.g. 129 is lowercase u-umlaut, 154 is
80 * upper-case u-umlaut).
82 int cm_stricmp(const char *str1, const char *str2)
94 c1 = (char) cm_foldUpper[(unsigned char)(*str1++)];
95 c2 = (char) cm_foldUpper[(unsigned char)(*str2++)];
103 /* characters that are legal in an 8.3 name */
105 * We used to have 1's for all characters from 128 to 254. But
106 * the NT client behaves better if we create an 8.3 name for any
107 * name that has a character with the high bit on, and if we
108 * delete those characters from 8.3 names. In particular, see
109 * Sybase defect 10859.
111 char cm_LegalChars[256] = {
112 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
113 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
114 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0,
115 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
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, 0, 0, 0, 1, 1,
118 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
119 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1,
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,
126 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
127 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
130 /* return true iff component is a valid 8.3 name */
131 int cm_Is8Dot3(char *namep)
138 * can't have a leading dot;
139 * special case for . and ..
141 if (namep[0] == '.') {
144 if (namep[1] == '.' && namep[2] == 0)
148 while (tc = *namep++) {
150 /* saw another dot */
151 if (sawDot) return 0; /* second dot */
156 if (cm_LegalChars[tc] == 0)
159 if (!sawDot && charCount > 8)
160 /* more than 8 chars in name */
162 if (sawDot && charCount > 3)
163 /* more than 3 chars in extension */
170 * Number unparsing map for generating 8.3 names;
171 * The version taken from DFS was on drugs.
172 * You can't include '&' and '@' in a file name.
174 char cm_8Dot3Mapping[42] =
175 {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
176 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K',
177 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U',
178 'V', 'W', 'X', 'Y', 'Z', '_', '-', '$', '#', '!', '+', '='
180 int cm_8Dot3MapSize = sizeof(cm_8Dot3Mapping);
182 void cm_Gen8Dot3Name(cm_dirEntry_t *dep, char *shortName, char **shortNameEndp)
186 int vnode = ntohl(dep->fid.vnode);
188 int validExtension = 0;
189 char tc, *temp, *name;
191 /* Unparse the file's vnode number to get a "uniquifier" */
193 number[nsize] = cm_8Dot3Mapping[vnode % cm_8Dot3MapSize];
195 vnode /= cm_8Dot3MapSize;
199 * Look for valid extension. There has to be a dot, and
200 * at least one of the characters following has to be legal.
202 lastDot = strrchr(dep->name, '.');
204 temp = lastDot; temp++;
206 if (cm_LegalChars[tc])
212 /* Copy name characters */
214 for (i = 0, name = dep->name;
215 i < (7 - nsize) && name != lastDot; ) {
220 if (!cm_LegalChars[tc])
223 *shortName++ = toupper(tc);
229 /* Copy uniquifier characters */
230 memcpy(shortName, number, nsize);
233 if (validExtension) {
234 /* Copy extension characters */
235 *shortName++ = *lastDot++; /* copy dot */
236 for (i = 0, tc = *lastDot++;
239 if (cm_LegalChars[tc]) {
241 *shortName++ = toupper(tc);
250 *shortNameEndp = shortName;
253 /* return success if we can open this file in this mode */
254 long cm_CheckOpen(cm_scache_t *scp, int openMode, int trunc, cm_user_t *userp,
261 if (openMode != 1) rights |= PRSFS_READ;
262 if (openMode == 1 || openMode == 2 || trunc) rights |= PRSFS_WRITE;
264 lock_ObtainMutex(&scp->mx);
266 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
267 CM_SCACHESYNC_GETSTATUS
268 | CM_SCACHESYNC_NEEDCALLBACK);
271 ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
272 scp->fileType == CM_SCACHETYPE_FILE) {
275 unsigned int sLockType;
276 LARGE_INTEGER LOffset, LLength;
278 /* Check if there's some sort of lock on the file at the
281 key = cm_GenerateKey(CM_SESSION_CMINT,0,0);
283 if (rights & PRSFS_WRITE)
286 sLockType = LOCKING_ANDX_SHARED_LOCK;
288 LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
289 LOffset.LowPart = CM_FLSHARE_OFFSET_LOW;
290 LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
291 LLength.LowPart = CM_FLSHARE_LENGTH_LOW;
293 code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
296 cm_Unlock(scp, sLockType, LOffset, LLength, key, userp, reqp);
298 /* In this case, we allow the file open to go through even
299 though we can't enforce mandatory locking on the
301 if (code == CM_ERROR_NOACCESS &&
302 !(rights & PRSFS_WRITE))
306 case CM_ERROR_ALLOFFLINE:
307 case CM_ERROR_ALLDOWN:
308 case CM_ERROR_ALLBUSY:
309 case CM_ERROR_TIMEDOUT:
311 case CM_ERROR_WOULDBLOCK:
314 code = CM_ERROR_SHARING_VIOLATION;
320 lock_ReleaseMutex(&scp->mx);
325 /* return success if we can open this file in this mode */
326 long cm_CheckNTOpen(cm_scache_t *scp, unsigned int desiredAccess,
327 unsigned int createDisp, cm_user_t *userp, cm_req_t *reqp)
332 /* Always allow delete; the RPC will tell us if it's OK */
333 if (desiredAccess == DELETE)
338 if (desiredAccess & AFS_ACCESS_READ)
339 rights |= PRSFS_READ;
341 if ((desiredAccess & AFS_ACCESS_WRITE)
343 rights |= PRSFS_WRITE;
345 lock_ObtainMutex(&scp->mx);
347 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
348 CM_SCACHESYNC_GETSTATUS
349 | CM_SCACHESYNC_NEEDCALLBACK);
352 * If the open will fail because the volume is readonly, then we will
353 * return an access denied error instead. This is to help brain-dead
354 * apps run correctly on replicated volumes.
355 * See defect 10007 for more information.
357 if (code == CM_ERROR_READONLY)
358 code = CM_ERROR_NOACCESS;
359 else if (code == 0 &&
360 ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
361 scp->fileType == CM_SCACHETYPE_FILE) {
363 unsigned int sLockType;
364 LARGE_INTEGER LOffset, LLength;
366 /* Check if there's some sort of lock on the file at the
369 key = cm_GenerateKey(CM_SESSION_CMINT,0,0);
370 if (rights & PRSFS_WRITE)
373 sLockType = LOCKING_ANDX_SHARED_LOCK;
375 /* single byte lock at offset 0x0100 0000 0000 0000 */
376 LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
377 LOffset.LowPart = CM_FLSHARE_OFFSET_LOW;
378 LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
379 LLength.LowPart = CM_FLSHARE_LENGTH_LOW;
381 code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
384 cm_Unlock(scp, sLockType, LOffset, LLength, key, userp, reqp);
386 /* In this case, we allow the file open to go through even
387 though we can't enforce mandatory locking on the
389 if (code == CM_ERROR_NOACCESS &&
390 !(rights & PRSFS_WRITE))
394 case CM_ERROR_ALLOFFLINE:
395 case CM_ERROR_ALLDOWN:
396 case CM_ERROR_ALLBUSY:
397 case CM_ERROR_TIMEDOUT:
399 case CM_ERROR_WOULDBLOCK:
402 code = CM_ERROR_SHARING_VIOLATION;
408 lock_ReleaseMutex(&scp->mx);
414 * When CAP_NT_SMBS has been negotiated, deletion (of files or directories) is
415 * done in three steps:
416 * (1) open for deletion (NT_CREATE_AND_X)
417 * (2) set for deletion on close (NT_TRANSACTION2, SET_FILE_INFO)
419 * We must not do the RPC until step 3. But if we are going to return an error
420 * code (e.g. directory not empty), we must return it by step 2, otherwise most
421 * clients will not notice it. So we do a preliminary check. For deleting
422 * files, this is almost free, since we have already done the RPC to get the
423 * parent directory's status bits. But for deleting directories, we must do an
424 * additional RPC to get the directory's data to check if it is empty. Sigh.
426 long cm_CheckNTDelete(cm_scache_t *dscp, cm_scache_t *scp, cm_user_t *userp,
433 unsigned short *hashTable;
435 int BeyondPage = 0, HaveDot = 0, HaveDotDot = 0;
437 /* First check permissions */
438 lock_ObtainMutex(&dscp->mx);
439 code = cm_SyncOp(dscp, NULL, userp, reqp, PRSFS_DELETE,
440 CM_SCACHESYNC_GETSTATUS
441 | CM_SCACHESYNC_NEEDCALLBACK);
442 lock_ReleaseMutex(&dscp->mx);
446 /* If deleting directory, must be empty */
448 if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
451 thyper.HighPart = 0; thyper.LowPart = 0;
452 lock_ObtainRead(&scp->bufCreateLock);
453 code = buf_Get(scp, &thyper, &bufferp);
454 lock_ReleaseRead(&scp->bufCreateLock);
458 lock_ObtainMutex(&bufferp->mx);
459 lock_ObtainMutex(&scp->mx);
461 code = cm_SyncOp(scp, bufferp, userp, reqp, 0,
462 CM_SCACHESYNC_NEEDCALLBACK
464 | CM_SCACHESYNC_BUFLOCKED);
468 if (cm_HaveBuffer(scp, bufferp, 1))
471 /* otherwise, load the buffer and try again */
472 lock_ReleaseMutex(&bufferp->mx);
473 code = cm_GetBuffer(scp, bufferp, NULL, userp, reqp);
474 lock_ReleaseMutex(&scp->mx);
475 lock_ObtainMutex(&bufferp->mx);
476 lock_ObtainMutex(&scp->mx);
481 /* We try to determine emptiness without looking beyond the first page,
482 * and without assuming "." and ".." are present and are on the first
483 * page (though these assumptions might, after all, be reasonable).
485 hashTable = (unsigned short *)(bufferp->datap + (32 * 5));
486 for (i=0; i<128; i++) {
487 idx = ntohs(hashTable[i]);
493 dep = (cm_dirEntry_t *)(bufferp->datap + (32 * idx));
494 if (strcmp(dep->name, ".") == 0)
496 else if (strcmp(dep->name, "..") == 0)
499 code = CM_ERROR_NOTEMPTY;
502 idx = ntohs(dep->next);
505 if (BeyondPage && HaveDot && HaveDotDot)
506 code = CM_ERROR_NOTEMPTY;
510 lock_ReleaseMutex(&bufferp->mx);
511 buf_Release(bufferp);
512 lock_ReleaseMutex(&scp->mx);
517 * Iterate through all entries in a directory.
518 * When the function funcp is called, the buffer is locked but the
519 * directory vnode is not.
521 * If the retscp parameter is not NULL, the parmp must be a
522 * cm_lookupSearch_t object.
524 long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
525 osi_hyper_t *startOffsetp, cm_user_t *userp, cm_req_t *reqp,
526 cm_scache_t **retscp)
533 osi_hyper_t dirLength;
534 osi_hyper_t bufferOffset;
535 osi_hyper_t curOffset;
539 cm_pageHeader_t *pageHeaderp;
541 long nextEntryCookie;
542 int numDirChunks; /* # of 32 byte dir chunks in this entry */
544 /* get the directory size */
545 lock_ObtainMutex(&scp->mx);
546 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_LOOKUP,
547 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
549 lock_ReleaseMutex(&scp->mx);
553 if (scp->fileType != CM_SCACHETYPE_DIRECTORY) {
554 lock_ReleaseMutex(&scp->mx);
555 return CM_ERROR_NOTDIR;
558 if (retscp) /* if this is a lookup call */
560 cm_lookupSearch_t* sp = parmp;
562 #ifdef AFS_FREELANCE_CLIENT
563 /* Freelance entries never end up in the DNLC because they
564 * do not have an associated cm_server_t
566 if ( !(cm_freelanceEnabled &&
567 sp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
568 sp->fid.volume==AFS_FAKE_ROOT_VOL_ID ) )
569 #endif /* AFS_FREELANCE_CLIENT */
571 int casefold = sp->caseFold;
572 sp->caseFold = 0; /* we have a strong preference for exact matches */
573 if ( *retscp = cm_dnlcLookup(scp, sp)) /* dnlc hit */
575 sp->caseFold = casefold;
576 lock_ReleaseMutex(&scp->mx);
579 sp->caseFold = casefold;
584 * XXX We only get the length once. It might change when we drop the
587 dirLength = scp->length;
589 lock_ReleaseMutex(&scp->mx);
592 bufferOffset.LowPart = bufferOffset.HighPart = 0;
594 curOffset = *startOffsetp;
596 curOffset.HighPart = 0;
597 curOffset.LowPart = 0;
601 /* make sure that curOffset.LowPart doesn't point to the first
602 * 32 bytes in the 2nd through last dir page, and that it
603 * doesn't point at the first 13 32-byte chunks in the first
604 * dir page, since those are dir and page headers, and don't
605 * contain useful information.
607 temp = curOffset.LowPart & (2048-1);
608 if (curOffset.HighPart == 0 && curOffset.LowPart < 2048) {
609 /* we're in the first page */
610 if (temp < 13*32) temp = 13*32;
613 /* we're in a later dir page */
614 if (temp < 32) temp = 32;
617 /* make sure the low order 5 bits are zero */
620 /* now put temp bits back ito curOffset.LowPart */
621 curOffset.LowPart &= ~(2048-1);
622 curOffset.LowPart |= temp;
624 /* check if we've passed the dir's EOF */
625 if (LargeIntegerGreaterThanOrEqualTo(curOffset, dirLength))
628 /* see if we can use the bufferp we have now; compute in which
629 * page the current offset would be, and check whether that's
630 * the offset of the buffer we have. If not, get the buffer.
632 thyper.HighPart = curOffset.HighPart;
633 thyper.LowPart = curOffset.LowPart & ~(cm_data.buf_blockSize-1);
634 if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
637 lock_ReleaseMutex(&bufferp->mx);
638 buf_Release(bufferp);
642 lock_ObtainRead(&scp->bufCreateLock);
643 code = buf_Get(scp, &thyper, &bufferp);
644 lock_ReleaseRead(&scp->bufCreateLock);
646 /* if buf_Get() fails we do not have a buffer object to lock */
652 /* for the IFS version, we bulkstat the dirents because this
653 routine is used in place of smb_ReceiveCoreSearchDir. our
654 other option is to modify smb_ReceiveCoreSearchDir itself,
655 but this seems to be the proper use for cm_ApplyDir. */
656 lock_ObtainMutex(&scp->mx);
657 if ((scp->flags & CM_SCACHEFLAG_BULKSTATTING) == 0
658 && (scp->bulkStatProgress.QuadPart <= thyper.QuadPart))
660 scp->flags |= CM_SCACHEFLAG_BULKSTATTING;
661 cm_TryBulkStat(scp, &thyper, userp, reqp);
662 scp->flags &= ~CM_SCACHEFLAG_BULKSTATTING;
663 scp->bulkStatProgress = thyper;
665 lock_ReleaseMutex(&scp->mx);
668 lock_ObtainMutex(&bufferp->mx);
669 bufferOffset = thyper;
671 /* now get the data in the cache */
673 lock_ObtainMutex(&scp->mx);
674 code = cm_SyncOp(scp, bufferp, userp, reqp,
676 CM_SCACHESYNC_NEEDCALLBACK
678 | CM_SCACHESYNC_BUFLOCKED);
680 lock_ReleaseMutex(&scp->mx);
684 if (cm_HaveBuffer(scp, bufferp, 1)) {
685 lock_ReleaseMutex(&scp->mx);
689 /* otherwise, load the buffer and try again */
690 lock_ReleaseMutex(&bufferp->mx);
691 code = cm_GetBuffer(scp, bufferp, NULL, userp,
693 lock_ReleaseMutex(&scp->mx);
694 lock_ObtainMutex(&bufferp->mx);
699 lock_ReleaseMutex(&bufferp->mx);
700 buf_Release(bufferp);
704 } /* if (wrong buffer) ... */
706 /* now we have the buffer containing the entry we're interested
707 * in; copy it out if it represents a non-deleted entry.
709 entryInDir = curOffset.LowPart & (2048-1);
710 entryInBuffer = curOffset.LowPart & (cm_data.buf_blockSize - 1);
712 /* page header will help tell us which entries are free. Page
713 * header can change more often than once per buffer, since
714 * AFS 3 dir page size may be less than (but not more than) a
715 * buffer package buffer.
717 /* only look intra-buffer */
718 temp = curOffset.LowPart & (cm_data.buf_blockSize - 1);
719 temp &= ~(2048 - 1); /* turn off intra-page bits */
720 pageHeaderp = (cm_pageHeader_t *) (bufferp->datap + temp);
722 /* now determine which entry we're looking at in the page. If
723 * it is free (there's a free bitmap at the start of the dir),
724 * we should skip these 32 bytes.
726 slotInPage = (entryInDir & 0x7e0) >> 5;
727 if (!(pageHeaderp->freeBitmap[slotInPage>>3]
728 & (1 << (slotInPage & 0x7)))) {
729 /* this entry is free */
730 numDirChunks = 1; /* only skip this guy */
734 tp = bufferp->datap + entryInBuffer;
735 dep = (cm_dirEntry_t *) tp; /* now points to AFS3 dir entry */
737 /* while we're here, compute the next entry's location, too,
738 * since we'll need it when writing out the cookie into the
739 * dir listing stream.
741 numDirChunks = cm_NameEntries(dep->name, NULL);
743 /* compute the offset of the cookie representing the next entry */
744 nextEntryCookie = curOffset.LowPart
745 + (CM_DIR_CHUNKSIZE * numDirChunks);
747 if (dep->fid.vnode != 0) {
748 /* this is one of the entries to use: it is not deleted */
749 code = (*funcp)(scp, dep, parmp, &curOffset);
752 } /* if we're including this name */
755 /* and adjust curOffset to be where the new cookie is */
757 thyper.LowPart = CM_DIR_CHUNKSIZE * numDirChunks;
758 curOffset = LargeIntegerAdd(thyper, curOffset);
759 } /* while copying data for dir listing */
761 /* release the mutex */
763 lock_ReleaseMutex(&bufferp->mx);
764 buf_Release(bufferp);
769 int cm_NoneUpper(char *s)
773 if (c >= 'A' && c <= 'Z')
778 int cm_NoneLower(char *s)
782 if (c >= 'a' && c <= 'z')
787 long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
790 cm_lookupSearch_t *sp;
795 sp = (cm_lookupSearch_t *) rockp;
797 matchName = dep->name;
799 match = cm_stricmp(matchName, sp->searchNamep);
801 match = strcmp(matchName, sp->searchNamep);
805 && !cm_Is8Dot3(dep->name)) {
806 matchName = shortName;
807 cm_Gen8Dot3Name(dep, shortName, NULL);
809 match = cm_stricmp(matchName, sp->searchNamep);
811 match = strcmp(matchName, sp->searchNamep);
821 if (!sp->caseFold || matchName == shortName) {
822 sp->fid.vnode = ntohl(dep->fid.vnode);
823 sp->fid.unique = ntohl(dep->fid.unique);
824 return CM_ERROR_STOPNOW;
828 * If we get here, we are doing a case-insensitive search, and we
829 * have found a match. Now we determine what kind of match it is:
830 * exact, lower-case, upper-case, or none of the above. This is done
831 * in order to choose among matches, if there are more than one.
834 /* Exact matches are the best. */
835 match = strcmp(matchName, sp->searchNamep);
838 sp->fid.vnode = ntohl(dep->fid.vnode);
839 sp->fid.unique = ntohl(dep->fid.unique);
840 return CM_ERROR_STOPNOW;
843 /* Lower-case matches are next. */
846 if (cm_NoneUpper(matchName)) {
851 /* Upper-case matches are next. */
854 if (cm_NoneLower(matchName)) {
859 /* General matches are last. */
865 sp->fid.vnode = ntohl(dep->fid.vnode);
866 sp->fid.unique = ntohl(dep->fid.unique);
870 /* read the contents of a mount point into the appropriate string.
871 * called with locked scp, and returns with locked scp.
873 long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
880 if (scp->mountPointStringp[0])
883 /* otherwise, we have to read it in */
884 lock_ReleaseMutex(&scp->mx);
886 lock_ObtainRead(&scp->bufCreateLock);
887 thyper.LowPart = thyper.HighPart = 0;
888 code = buf_Get(scp, &thyper, &bufp);
889 lock_ReleaseRead(&scp->bufCreateLock);
891 lock_ObtainMutex(&scp->mx);
896 code = cm_SyncOp(scp, bufp, userp, reqp, 0,
897 CM_SCACHESYNC_READ | CM_SCACHESYNC_NEEDCALLBACK);
902 if (cm_HaveBuffer(scp, bufp, 0))
905 /* otherwise load buffer */
906 code = cm_GetBuffer(scp, bufp, NULL, userp, reqp);
911 /* locked, has callback, has valid data in buffer */
912 if ((tlen = scp->length.LowPart) > 1000)
913 return CM_ERROR_TOOBIG;
915 code = CM_ERROR_INVAL;
919 /* someone else did the work while we were out */
920 if (scp->mountPointStringp[0]) {
925 /* otherwise, copy out the link */
926 memcpy(scp->mountPointStringp, bufp->datap, tlen);
928 /* now make it null-terminated. Note that the original contents of a
929 * link that is a mount point is "#volname." where "." is there just to
930 * be turned into a null. That is, we can trash the last char of the
931 * link without damaging the vol name. This is a stupid convention,
932 * but that's the protocol.
934 scp->mountPointStringp[tlen-1] = 0;
943 /* called with a locked scp and chases the mount point, yielding outScpp.
944 * scp remains locked, just for simplicity of describing the interface.
946 long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
947 cm_req_t *reqp, cm_scache_t **outScpp)
962 if (scp->mountRootFid.cell != 0 && scp->mountRootGen >= cm_data.mountRootGen) {
963 tfid = scp->mountRootFid;
964 lock_ReleaseMutex(&scp->mx);
965 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
966 lock_ObtainMutex(&scp->mx);
970 /* parse the volume name */
971 mpNamep = scp->mountPointStringp;
972 osi_assert(mpNamep[0]);
973 tlen = (int)strlen(scp->mountPointStringp);
974 mtType = *scp->mountPointStringp;
975 cellNamep = malloc(tlen);
976 volNamep = malloc(tlen);
978 cp = strrchr(mpNamep, ':');
980 /* cellular mount point */
981 memset(cellNamep, 0, tlen);
982 strncpy(cellNamep, mpNamep+1, cp - mpNamep - 1);
983 strcpy(volNamep, cp+1);
984 /* now look up the cell */
985 cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE);
989 strcpy(volNamep, mpNamep+1);
991 cellp = cm_FindCellByID(scp->fid.cell);
995 code = CM_ERROR_NOSUCHCELL;
999 vnLength = strlen(volNamep);
1000 if (vnLength >= 8 && strcmp(volNamep + vnLength - 7, ".backup") == 0)
1002 else if (vnLength >= 10
1003 && strcmp(volNamep + vnLength - 9, ".readonly") == 0)
1008 /* check for backups within backups */
1010 && (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
1011 == CM_SCACHEFLAG_RO) {
1012 code = CM_ERROR_NOSUCHVOLUME;
1016 /* now we need to get the volume */
1017 lock_ReleaseMutex(&scp->mx);
1018 code = cm_GetVolumeByName(cellp, volNamep, userp, reqp, 0, &volp);
1019 lock_ObtainMutex(&scp->mx);
1022 /* save the parent of the volume root for this is the
1023 * place where the volume is mounted and we must remember
1024 * this in the volume structure rather than just in the
1025 * scache entry lest the scache entry gets recycled
1028 lock_ObtainMutex(&volp->mx);
1029 volp->dotdotFid = dscp->fid;
1030 lock_ReleaseMutex(&volp->mx);
1032 scp->mountRootFid.cell = cellp->cellID;
1033 /* if the mt pt is in a read-only volume (not just a
1034 * backup), and if there is a read-only volume for the
1035 * target, and if this is a type '#' mount point, use
1036 * the read-only, otherwise use the one specified.
1038 if (mtType == '#' && (scp->flags & CM_SCACHEFLAG_PURERO)
1039 && volp->roID != 0 && type == RWVOL)
1042 scp->mountRootFid.volume = volp->roID;
1043 else if (type == BACKVOL)
1044 scp->mountRootFid.volume = volp->bkID;
1046 scp->mountRootFid.volume = volp->rwID;
1048 /* the rest of the fid is a magic number */
1049 scp->mountRootFid.vnode = 1;
1050 scp->mountRootFid.unique = 1;
1051 scp->mountRootGen = cm_data.mountRootGen;
1053 tfid = scp->mountRootFid;
1054 lock_ReleaseMutex(&scp->mx);
1055 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
1056 lock_ObtainMutex(&scp->mx);
1065 long cm_LookupInternal(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
1066 cm_req_t *reqp, cm_scache_t **outpScpp)
1069 int dnlcHit = 1; /* did we hit in the dnlc? yes, we did */
1070 cm_scache_t *tscp = NULL;
1071 cm_scache_t *mountedScp;
1072 cm_lookupSearch_t rock;
1075 if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
1076 && strcmp(namep, "..") == 0) {
1077 if (dscp->dotdotFid.volume == 0)
1078 return CM_ERROR_NOSUCHVOLUME;
1079 rock.fid = dscp->dotdotFid;
1083 memset(&rock, 0, sizeof(rock));
1084 rock.fid.cell = dscp->fid.cell;
1085 rock.fid.volume = dscp->fid.volume;
1086 rock.searchNamep = namep;
1087 rock.caseFold = (flags & CM_FLAG_CASEFOLD);
1088 rock.hasTilde = ((strchr(namep, '~') != NULL) ? 1 : 0);
1090 /* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
1091 code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
1092 (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
1094 /* code == 0 means we fell off the end of the dir, while stopnow means
1095 * that we stopped early, probably because we found the entry we're
1096 * looking for. Any other non-zero code is an error.
1098 if (code && code != CM_ERROR_STOPNOW) {
1099 /* if the cm_scache_t we are searching in is not a directory
1100 * we must return path not found because the error
1101 * is to describe the final component not an intermediary
1103 if (code == CM_ERROR_NOTDIR) {
1104 if (flags & CM_FLAG_CHECKPATH)
1105 return CM_ERROR_NOSUCHPATH;
1107 return CM_ERROR_NOSUCHFILE;
1112 getroot = (dscp==cm_data.rootSCachep) ;
1114 if (!cm_freelanceEnabled || !getroot) {
1115 if (flags & CM_FLAG_CHECKPATH)
1116 return CM_ERROR_NOSUCHPATH;
1118 return CM_ERROR_NOSUCHFILE;
1120 else { /* nonexistent dir on freelance root, so add it */
1121 char fullname[200] = ".";
1124 osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %s",
1125 osi_LogSaveString(afsd_logp,namep));
1126 if (namep[0] == '.') {
1127 if (cm_GetCell_Gen(&namep[1], &fullname[1], CM_FLAG_CREATE)) {
1129 if ( stricmp(&namep[1], &fullname[1]) )
1130 code = cm_FreelanceAddSymlink(namep, fullname, &rock.fid);
1132 code = cm_FreelanceAddMount(namep, &fullname[1], "root.cell.", 1, &rock.fid);
1135 if (cm_GetCell_Gen(namep, fullname, CM_FLAG_CREATE)) {
1137 if ( stricmp(namep, fullname) )
1138 code = cm_FreelanceAddSymlink(namep, fullname, &rock.fid);
1140 code = cm_FreelanceAddMount(namep, fullname, "root.cell.", 0, &rock.fid);
1143 if (!found || code < 0) { /* add mount point failed, so give up */
1144 if (flags & CM_FLAG_CHECKPATH)
1145 return CM_ERROR_NOSUCHPATH;
1147 return CM_ERROR_NOSUCHFILE;
1149 tscp = NULL; /* to force call of cm_GetSCache */
1154 if ( !tscp ) /* we did not find it in the dnlc */
1157 code = cm_GetSCache(&rock.fid, &tscp, userp, reqp);
1161 /* tscp is now held */
1163 lock_ObtainMutex(&tscp->mx);
1164 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1165 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1167 lock_ReleaseMutex(&tscp->mx);
1168 cm_ReleaseSCache(tscp);
1171 /* tscp is now locked */
1173 if (!(flags & CM_FLAG_NOMOUNTCHASE)
1174 && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
1175 /* mount points are funny: they have a volume name to mount
1178 code = cm_ReadMountPoint(tscp, userp, reqp);
1180 code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
1182 lock_ReleaseMutex(&tscp->mx);
1183 cm_ReleaseSCache(tscp);
1190 lock_ReleaseMutex(&tscp->mx);
1193 /* copy back pointer */
1196 /* insert scache in dnlc */
1197 if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
1198 /* lock the directory entry to prevent racing callback revokes */
1199 lock_ObtainMutex(&dscp->mx);
1200 if ( dscp->cbServerp && dscp->cbExpires )
1201 cm_dnlcEnter(dscp, namep, tscp);
1202 lock_ReleaseMutex(&dscp->mx);
1209 int cm_ExpandSysName(char *inp, char *outp, long outSize, unsigned int index)
1214 tp = strrchr(inp, '@');
1216 return 0; /* no @sys */
1218 if (strcmp(tp, "@sys") != 0)
1219 return 0; /* no @sys */
1221 /* caller just wants to know if this is a valid @sys type of name */
1225 if (index >= MAXNUMSYSNAMES)
1228 /* otherwise generate the properly expanded @sys name */
1229 prefixCount = (int)(tp - inp);
1231 strncpy(outp, inp, prefixCount); /* copy out "a." from "a.@sys" */
1232 outp[prefixCount] = 0; /* null terminate the "a." */
1233 strcat(outp, cm_sysNameList[index]);/* append i386_nt40 */
1237 long cm_Lookup(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
1238 cm_req_t *reqp, cm_scache_t **outpScpp)
1242 int sysNameIndex = 0;
1243 cm_scache_t *scp = 0;
1245 if ( stricmp(namep,SMB_IOCTL_FILENAME_NOSLASH) == 0 ) {
1246 if (flags & CM_FLAG_CHECKPATH)
1247 return CM_ERROR_NOSUCHPATH;
1249 return CM_ERROR_NOSUCHFILE;
1252 for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
1253 code = cm_ExpandSysName(namep, tname, sizeof(tname), sysNameIndex);
1255 code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
1261 cm_ReleaseSCache(scp);
1265 return cm_LookupInternal(dscp, namep, flags, userp, reqp, outpScpp);
1269 /* None of the possible sysName expansions could be found */
1270 if (flags & CM_FLAG_CHECKPATH)
1271 return CM_ERROR_NOSUCHPATH;
1273 return CM_ERROR_NOSUCHFILE;
1276 long cm_Unlink(cm_scache_t *dscp, char *namep, cm_user_t *userp, cm_req_t *reqp)
1282 AFSFetchStatus newDirStatus;
1284 struct rx_connection * callp;
1286 #ifdef AFS_FREELANCE_CLIENT
1287 if (cm_freelanceEnabled && dscp == cm_data.rootSCachep) {
1288 /* deleting a mount point from the root dir. */
1289 code = cm_FreelanceRemoveMount(namep);
1294 /* make sure we don't screw up the dir status during the merge */
1295 lock_ObtainMutex(&dscp->mx);
1296 sflags = CM_SCACHESYNC_STOREDATA;
1297 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1298 lock_ReleaseMutex(&dscp->mx);
1303 afsFid.Volume = dscp->fid.volume;
1304 afsFid.Vnode = dscp->fid.vnode;
1305 afsFid.Unique = dscp->fid.unique;
1307 osi_Log1(afsd_logp, "CALL RemoveFile scp 0x%p", dscp);
1309 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1313 callp = cm_GetRxConn(connp);
1314 code = RXAFS_RemoveFile(callp, &afsFid, namep,
1315 &newDirStatus, &volSync);
1316 rx_PutConnection(callp);
1318 } while (cm_Analyze(connp, userp, reqp, &dscp->fid, &volSync, NULL, NULL, code));
1319 code = cm_MapRPCError(code, reqp);
1322 osi_Log1(afsd_logp, "CALL RemoveFile FAILURE, code 0x%x", code);
1324 osi_Log0(afsd_logp, "CALL RemoveFile SUCCESS");
1326 lock_ObtainMutex(&dscp->mx);
1327 cm_dnlcRemove(dscp, namep);
1328 cm_SyncOpDone(dscp, NULL, sflags);
1330 cm_MergeStatus(dscp, &newDirStatus, &volSync, userp, 0);
1331 lock_ReleaseMutex(&dscp->mx);
1336 /* called with a locked vnode, and fills in the link info.
1337 * returns this the vnode still locked.
1339 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1346 lock_AssertMutex(&linkScp->mx);
1347 if (!linkScp->mountPointStringp[0]) {
1348 /* read the link data */
1349 lock_ReleaseMutex(&linkScp->mx);
1350 thyper.LowPart = thyper.HighPart = 0;
1351 code = buf_Get(linkScp, &thyper, &bufp);
1352 lock_ObtainMutex(&linkScp->mx);
1356 code = cm_SyncOp(linkScp, bufp, userp, reqp, 0,
1357 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1362 if (cm_HaveBuffer(linkScp, bufp, 0))
1365 code = cm_GetBuffer(linkScp, bufp, NULL, userp, reqp);
1370 } /* while loop to get the data */
1372 /* now if we still have no link read in,
1373 * copy the data from the buffer */
1374 if ((temp = linkScp->length.LowPart) >= MOUNTPOINTLEN) {
1376 return CM_ERROR_TOOBIG;
1379 /* otherwise, it fits; make sure it is still null (could have
1380 * lost race with someone else referencing this link above),
1381 * and if so, copy in the data.
1383 if (!linkScp->mountPointStringp[0]) {
1384 strncpy(linkScp->mountPointStringp, bufp->datap, temp);
1385 linkScp->mountPointStringp[temp] = 0; /* null terminate */
1388 } /* don't have sym link contents cached */
1393 /* called with a held vnode and a path suffix, with the held vnode being a
1394 * symbolic link. Our goal is to generate a new path to interpret, and return
1395 * this new path in newSpaceBufferp. If the new vnode is relative to a dir
1396 * other than the directory containing the symbolic link, then the new root is
1397 * returned in *newRootScpp, otherwise a null is returned there.
1399 long cm_AssembleLink(cm_scache_t *linkScp, char *pathSuffixp,
1400 cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1401 cm_user_t *userp, cm_req_t *reqp)
1408 lock_ObtainMutex(&linkScp->mx);
1409 code = cm_HandleLink(linkScp, userp, reqp);
1413 /* if we may overflow the buffer, bail out; buffer is signficantly
1414 * bigger than max path length, so we don't really have to worry about
1415 * being a little conservative here.
1417 if (strlen(linkScp->mountPointStringp) + strlen(pathSuffixp) + 2
1418 >= CM_UTILS_SPACESIZE)
1419 return CM_ERROR_TOOBIG;
1421 tsp = cm_GetSpace();
1422 linkp = linkScp->mountPointStringp;
1423 if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1424 if (strlen(linkp) > cm_mountRootLen)
1425 strcpy(tsp->data, linkp+cm_mountRootLen+1);
1428 *newRootScpp = cm_data.rootSCachep;
1429 cm_HoldSCache(cm_data.rootSCachep);
1430 } else if (linkp[0] == '\\' && linkp[1] == '\\') {
1431 if (!strnicmp(&linkp[2], cm_NetbiosName, (len = (long)strlen(cm_NetbiosName))))
1433 char * p = &linkp[len + 3];
1434 if (strnicmp(p, "all", 3) == 0)
1437 strcpy(tsp->data, p);
1438 for (p = tsp->data; *p; p++) {
1442 *newRootScpp = cm_data.rootSCachep;
1443 cm_HoldSCache(cm_data.rootSCachep);
1445 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1446 strcpy(tsp->data, linkp);
1447 *newRootScpp = NULL;
1448 code = CM_ERROR_PATH_NOT_COVERED;
1450 } else if ( !strnicmp(linkp, "msdfs:", (len = (long)strlen("msdfs:"))) ) {
1451 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1452 strcpy(tsp->data, linkp);
1453 *newRootScpp = NULL;
1454 code = CM_ERROR_PATH_NOT_COVERED;
1455 } else if (*linkp == '\\' || *linkp == '/') {
1457 /* formerly, this was considered to be from the AFS root,
1458 * but this seems to create problems. instead, we will just
1459 * reject the link */
1460 strcpy(tsp->data, linkp+1);
1461 *newRootScpp = cm_data.rootSCachep;
1462 cm_HoldSCache(cm_data.rootSCachep);
1464 /* we still copy the link data into the response so that
1465 * the user can see what the link points to
1467 linkScp->fileType = CM_SCACHETYPE_INVALID;
1468 strcpy(tsp->data, linkp);
1469 *newRootScpp = NULL;
1470 code = CM_ERROR_NOSUCHPATH;
1473 /* a relative link */
1474 strcpy(tsp->data, linkp);
1475 *newRootScpp = NULL;
1477 if (pathSuffixp[0] != 0) { /* if suffix string is non-null */
1478 strcat(tsp->data, "\\");
1479 strcat(tsp->data, pathSuffixp);
1481 *newSpaceBufferp = tsp;
1484 lock_ReleaseMutex(&linkScp->mx);
1488 long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
1489 cm_user_t *userp, char *tidPathp, cm_req_t *reqp, cm_scache_t **outScpp)
1492 char *tp; /* ptr moving through input buffer */
1493 char tc; /* temp char */
1494 int haveComponent; /* has new component started? */
1495 char component[256]; /* this is the new component */
1496 char *cp; /* component name being assembled */
1497 cm_scache_t *tscp; /* current location in the hierarchy */
1498 cm_scache_t *nscp; /* next dude down */
1499 cm_scache_t *dirScp; /* last dir we searched */
1500 cm_scache_t *linkScp; /* new root for the symlink we just
1502 cm_space_t *psp; /* space for current path, if we've hit
1504 cm_space_t *tempsp; /* temp vbl */
1505 char *restp; /* rest of the pathname to interpret */
1506 int symlinkCount; /* count of # of symlinks traversed */
1507 int extraFlag; /* avoid chasing mt pts for dir cmd */
1508 int phase = 1; /* 1 = tidPathp, 2 = pathp */
1521 cm_HoldSCache(tscp);
1528 /* map Unix slashes into DOS ones so we can interpret Unix
1534 if (!haveComponent) {
1537 } else if (tc == 0) {
1551 /* we have a component here */
1552 if (tc == 0 || tc == '\\') {
1553 /* end of the component; we're at the last
1554 * component if tc == 0. However, if the last
1555 * is a symlink, we have more to do.
1557 *cp++ = 0; /* add null termination */
1559 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
1560 extraFlag = CM_FLAG_NOMOUNTCHASE;
1561 code = cm_Lookup(tscp, component,
1563 userp, reqp, &nscp);
1565 cm_ReleaseSCache(tscp);
1567 cm_ReleaseSCache(dirScp);
1570 if (code == CM_ERROR_NOSUCHFILE && tscp->fileType == CM_SCACHETYPE_SYMLINK)
1571 return CM_ERROR_NOSUCHPATH;
1575 haveComponent = 0; /* component done */
1577 cm_ReleaseSCache(dirScp);
1578 dirScp = tscp; /* for some symlinks */
1579 tscp = nscp; /* already held */
1581 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
1584 cm_ReleaseSCache(dirScp);
1590 /* now, if tscp is a symlink, we should follow
1591 * it and assemble the path again.
1593 lock_ObtainMutex(&tscp->mx);
1594 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1595 CM_SCACHESYNC_GETSTATUS
1596 | CM_SCACHESYNC_NEEDCALLBACK);
1598 lock_ReleaseMutex(&tscp->mx);
1599 cm_ReleaseSCache(tscp);
1602 cm_ReleaseSCache(dirScp);
1607 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
1608 /* this is a symlink; assemble a new buffer */
1609 lock_ReleaseMutex(&tscp->mx);
1610 if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
1611 cm_ReleaseSCache(tscp);
1614 cm_ReleaseSCache(dirScp);
1619 return CM_ERROR_TOO_MANY_SYMLINKS;
1625 code = cm_AssembleLink(tscp, restp, &linkScp, &tempsp, userp, reqp);
1627 /* something went wrong */
1628 cm_ReleaseSCache(tscp);
1631 cm_ReleaseSCache(dirScp);
1637 /* otherwise, tempsp has the new path,
1638 * and linkScp is the new root from
1639 * which to interpret that path.
1640 * Continue with the namei processing,
1641 * also doing the bookkeeping for the
1642 * space allocation and tracking the
1643 * vnode reference counts.
1649 cm_ReleaseSCache(tscp);
1654 * now, if linkScp is null, that's
1655 * AssembleLink's way of telling us that
1656 * the sym link is relative to the dir
1657 * containing the link. We have a ref
1658 * to it in dirScp, and we hold it now
1659 * and reuse it as the new spot in the
1667 /* not a symlink, we may be done */
1668 lock_ReleaseMutex(&tscp->mx);
1676 cm_ReleaseSCache(dirScp);
1684 cm_ReleaseSCache(dirScp);
1687 } /* end of a component */
1690 } /* we have a component */
1691 } /* big while loop over all components */
1695 cm_ReleaseSCache(dirScp);
1701 cm_ReleaseSCache(tscp);
1705 /* called with a dir, and a vnode within the dir that happens to be a symlink.
1706 * We chase the link, and return a held pointer to the target, if it exists,
1707 * in *outScpp. If we succeed, we return 0, otherwise we return an error code
1708 * and do not hold or return a target vnode.
1710 * This is very similar to calling cm_NameI with the last component of a name,
1711 * which happens to be a symlink, except that we've already passed by the name.
1713 * This function is typically called by the directory listing functions, which
1714 * encounter symlinks but need to return the proper file length so programs
1715 * like "more" work properly when they make use of the attributes retrieved from
1718 * The input vnode should not be locked when this function is called.
1720 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
1721 cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
1725 cm_scache_t *newRootScp;
1727 osi_Log1(afsd_logp, "Evaluating symlink scp 0x%p", linkScp);
1729 code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
1733 /* now, if newRootScp is NULL, we're really being told that the symlink
1734 * is relative to the current directory (dscp).
1736 if (newRootScp == NULL) {
1738 cm_HoldSCache(dscp);
1741 code = cm_NameI(newRootScp, spacep->data,
1742 CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
1743 userp, NULL, reqp, outScpp);
1745 if (code == CM_ERROR_NOSUCHFILE)
1746 code = CM_ERROR_NOSUCHPATH;
1748 /* this stuff is allocated no matter what happened on the namei call,
1750 cm_FreeSpace(spacep);
1751 cm_ReleaseSCache(newRootScp);
1756 /* make this big enough so that one buffer of dir pages won't overflow. We'll
1757 * check anyway, but we want to minimize the chance that we have to leave stuff
1760 #define CM_BULKMAX 128
1762 /* rock for bulk stat calls */
1763 typedef struct cm_bulkStat {
1764 osi_hyper_t bufOffset; /* only do it for things in this buffer page */
1766 /* info for the actual call */
1767 int counter; /* next free slot */
1768 AFSFid fids[CM_BULKMAX];
1769 AFSFetchStatus stats[CM_BULKMAX];
1770 AFSCallBack callbacks[CM_BULKMAX];
1773 /* for a given entry, make sure that it isn't in the stat cache, and then
1774 * add it to the list of file IDs to be obtained.
1776 * Don't bother adding it if we already have a vnode. Note that the dir
1777 * is locked, so we have to be careful checking the vnode we're thinking of
1778 * processing, to avoid deadlocks.
1780 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
1791 /* Don't overflow bsp. */
1792 if (bsp->counter >= CM_BULKMAX)
1793 return CM_ERROR_STOPNOW;
1795 thyper.LowPart = cm_data.buf_blockSize;
1796 thyper.HighPart = 0;
1797 thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
1799 /* thyper is now the first byte past the end of the record we're
1800 * interested in, and bsp->bufOffset is the first byte of the record
1801 * we're interested in.
1802 * Skip data in the others.
1805 if (LargeIntegerLessThan(*offp, bsp->bufOffset))
1807 if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
1808 return CM_ERROR_STOPNOW;
1809 if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
1812 tfid.cell = scp->fid.cell;
1813 tfid.volume = scp->fid.volume;
1814 tfid.vnode = ntohl(dep->fid.vnode);
1815 tfid.unique = ntohl(dep->fid.unique);
1816 tscp = cm_FindSCache(&tfid);
1818 if (lock_TryMutex(&tscp->mx)) {
1819 /* we have an entry that we can look at */
1820 if (cm_HaveCallback(tscp)) {
1821 /* we have a callback on it. Don't bother
1822 * fetching this stat entry, since we're happy
1823 * with the info we have.
1825 lock_ReleaseMutex(&tscp->mx);
1826 cm_ReleaseSCache(tscp);
1829 lock_ReleaseMutex(&tscp->mx);
1831 cm_ReleaseSCache(tscp);
1834 #ifdef AFS_FREELANCE_CLIENT
1835 // yj: if this is a mountpoint under root.afs then we don't want it
1836 // to be bulkstat-ed, instead, we call getSCache directly and under
1837 // getSCache, it is handled specially.
1838 if ( cm_freelanceEnabled &&
1839 tfid.cell==AFS_FAKE_ROOT_CELL_ID &&
1840 tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
1841 !(tfid.vnode==0x1 && tfid.unique==0x1) )
1843 osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
1844 return cm_GetSCache(&tfid, &tscp, NULL, NULL);
1846 #endif /* AFS_FREELANCE_CLIENT */
1849 bsp->fids[i].Volume = scp->fid.volume;
1850 bsp->fids[i].Vnode = tfid.vnode;
1851 bsp->fids[i].Unique = tfid.unique;
1855 /* called with a locked scp and a pointer to a buffer. Make bulk stat
1856 * calls on all undeleted files in the page of the directory specified.
1858 void cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
1862 cm_bulkStat_t bb; /* this is *BIG*, probably 12K or so;
1863 * watch for stack problems */
1864 AFSCBFids fidStruct;
1865 AFSBulkStats statStruct;
1867 AFSCBs callbackStruct;
1870 cm_callbackRequest_t cbReq;
1876 struct rx_connection * callp;
1878 osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%p", dscp);
1880 /* should be on a buffer boundary */
1881 osi_assert((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0);
1884 bb.bufOffset = *offsetp;
1886 lock_ReleaseMutex(&dscp->mx);
1887 /* first, assemble the file IDs we need to stat */
1888 code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) &bb, offsetp, userp, reqp, NULL);
1890 /* if we failed, bail out early */
1891 if (code && code != CM_ERROR_STOPNOW) {
1892 lock_ObtainMutex(&dscp->mx);
1896 /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
1897 * make the calls to create the entries. Handle AFSCBMAX files at a
1901 while (filex < bb.counter) {
1902 filesThisCall = bb.counter - filex;
1903 if (filesThisCall > AFSCBMAX)
1904 filesThisCall = AFSCBMAX;
1906 fidStruct.AFSCBFids_len = filesThisCall;
1907 fidStruct.AFSCBFids_val = &bb.fids[filex];
1908 statStruct.AFSBulkStats_len = filesThisCall;
1909 statStruct.AFSBulkStats_val = &bb.stats[filex];
1910 callbackStruct.AFSCBs_len = filesThisCall;
1911 callbackStruct.AFSCBs_val = &bb.callbacks[filex];
1912 cm_StartCallbackGrantingCall(NULL, &cbReq);
1913 osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
1915 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1919 callp = cm_GetRxConn(connp);
1920 code = RXAFS_BulkStatus(callp, &fidStruct,
1921 &statStruct, &callbackStruct, &volSync);
1922 rx_PutConnection(callp);
1924 } while (cm_Analyze(connp, userp, reqp, &dscp->fid,
1925 &volSync, NULL, &cbReq, code));
1926 code = cm_MapRPCError(code, reqp);
1929 osi_Log1(afsd_logp, "CALL BulkStatus FAILURE code 0x%x", code);
1931 osi_Log0(afsd_logp, "CALL BulkStatus SUCCESS");
1933 /* may as well quit on an error, since we're not going to do
1934 * much better on the next immediate call, either.
1937 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
1941 /* otherwise, we should do the merges */
1942 for (i = 0; i<filesThisCall; i++) {
1944 tfid.cell = dscp->fid.cell;
1945 tfid.volume = bb.fids[j].Volume;
1946 tfid.vnode = bb.fids[j].Vnode;
1947 tfid.unique = bb.fids[j].Unique;
1948 code = cm_GetSCache(&tfid, &scp, userp, reqp);
1952 /* otherwise, if this entry has no callback info,
1955 lock_ObtainMutex(&scp->mx);
1956 /* now, we have to be extra paranoid on merging in this
1957 * information, since we didn't use cm_SyncOp before
1958 * starting the fetch to make sure that no bad races
1959 * were occurring. Specifically, we need to make sure
1960 * we don't obliterate any newer information in the
1961 * vnode than have here.
1963 * Right now, be pretty conservative: if there's a
1964 * callback or a pending call, skip it.
1966 if (scp->cbServerp == NULL
1968 (CM_SCACHEFLAG_FETCHING
1969 | CM_SCACHEFLAG_STORING
1970 | CM_SCACHEFLAG_SIZESTORING))) {
1971 cm_EndCallbackGrantingCall(scp, &cbReq,
1973 CM_CALLBACK_MAINTAINCOUNT);
1974 cm_MergeStatus(scp, &bb.stats[j], &volSync,
1977 lock_ReleaseMutex(&scp->mx);
1978 cm_ReleaseSCache(scp);
1979 } /* all files in the response */
1980 /* now tell it to drop the count,
1981 * after doing the vnode processing above */
1982 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
1984 filex += filesThisCall;
1985 } /* while there are still more files to process */
1986 lock_ObtainMutex(&dscp->mx);
1987 osi_Log0(afsd_logp, "END cm_TryBulkStat");
1990 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
1994 /* initialize store back mask as inexpensive local variable */
1996 memset(statusp, 0, sizeof(AFSStoreStatus));
1998 /* copy out queued info from scache first, if scp passed in */
2000 if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
2001 statusp->ClientModTime = scp->clientModTime;
2002 mask |= AFS_SETMODTIME;
2003 scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
2008 /* now add in our locally generated request */
2009 if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
2010 statusp->ClientModTime = attrp->clientModTime;
2011 mask |= AFS_SETMODTIME;
2013 if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
2014 statusp->UnixModeBits = attrp->unixModeBits;
2015 mask |= AFS_SETMODE;
2017 if (attrp->mask & CM_ATTRMASK_OWNER) {
2018 statusp->Owner = attrp->owner;
2019 mask |= AFS_SETOWNER;
2021 if (attrp->mask & CM_ATTRMASK_GROUP) {
2022 statusp->Group = attrp->group;
2023 mask |= AFS_SETGROUP;
2026 statusp->Mask = mask;
2029 /* set the file size, and make sure that all relevant buffers have been
2030 * truncated. Ensure that any partially truncated buffers have been zeroed
2031 * to the end of the buffer.
2033 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
2039 /* start by locking out buffer creation */
2040 lock_ObtainWrite(&scp->bufCreateLock);
2042 /* verify that this is a file, not a dir or a symlink */
2043 lock_ObtainMutex(&scp->mx);
2044 code = cm_SyncOp(scp, NULL, userp, reqp, 0,
2045 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2049 if (scp->fileType != CM_SCACHETYPE_FILE) {
2050 code = CM_ERROR_ISDIR;
2055 if (LargeIntegerLessThan(*sizep, scp->length))
2060 lock_ReleaseMutex(&scp->mx);
2062 /* can't hold scp->mx lock here, since we may wait for a storeback to
2063 * finish if the buffer package is cleaning a buffer by storing it to
2067 buf_Truncate(scp, userp, reqp, sizep);
2069 /* now ensure that file length is short enough, and update truncPos */
2070 lock_ObtainMutex(&scp->mx);
2072 /* make sure we have a callback (so we have the right value for the
2073 * length), and wait for it to be safe to do a truncate.
2075 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
2076 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2077 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2081 if (LargeIntegerLessThan(*sizep, scp->length)) {
2082 /* a real truncation. If truncPos is not set yet, or is bigger
2083 * than where we're truncating the file, set truncPos to this
2088 if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
2089 || LargeIntegerLessThan(*sizep, scp->length)) {
2091 scp->truncPos = *sizep;
2092 scp->mask |= CM_SCACHEMASK_TRUNCPOS;
2094 /* in either case, the new file size has been changed */
2095 scp->length = *sizep;
2096 scp->mask |= CM_SCACHEMASK_LENGTH;
2098 else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
2099 /* really extending the file */
2100 scp->length = *sizep;
2101 scp->mask |= CM_SCACHEMASK_LENGTH;
2104 /* done successfully */
2108 lock_ReleaseMutex(&scp->mx);
2109 lock_ReleaseWrite(&scp->bufCreateLock);
2114 /* set the file size or other attributes (but not both at once) */
2115 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
2120 AFSFetchStatus afsOutStatus;
2124 AFSStoreStatus afsInStatus;
2125 struct rx_connection * callp;
2127 /* handle file length setting */
2128 if (attrp->mask & CM_ATTRMASK_LENGTH)
2129 return cm_SetLength(scp, &attrp->length, userp, reqp);
2131 flags = CM_SCACHESYNC_STORESTATUS;
2133 lock_ObtainMutex(&scp->mx);
2134 /* otherwise, we have to make an RPC to get the status */
2135 code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
2137 /* make the attr structure */
2138 cm_StatusFromAttr(&afsInStatus, scp, attrp);
2140 tfid.Volume = scp->fid.volume;
2141 tfid.Vnode = scp->fid.vnode;
2142 tfid.Unique = scp->fid.unique;
2144 lock_ReleaseMutex(&scp->mx);
2148 /* now make the RPC */
2149 osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%p", scp);
2151 code = cm_Conn(&scp->fid, userp, reqp, &connp);
2155 callp = cm_GetRxConn(connp);
2156 code = RXAFS_StoreStatus(callp, &tfid,
2157 &afsInStatus, &afsOutStatus, &volSync);
2158 rx_PutConnection(callp);
2160 } while (cm_Analyze(connp, userp, reqp,
2161 &scp->fid, &volSync, NULL, NULL, code));
2162 code = cm_MapRPCError(code, reqp);
2165 osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
2167 osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
2169 lock_ObtainMutex(&scp->mx);
2170 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
2172 cm_MergeStatus(scp, &afsOutStatus, &volSync, userp,
2173 CM_MERGEFLAG_FORCE);
2175 /* if we're changing the mode bits, discard the ACL cache,
2176 * since we changed the mode bits.
2178 if (afsInStatus.Mask & AFS_SETMODE) cm_FreeAllACLEnts(scp);
2179 lock_ReleaseMutex(&scp->mx);
2183 long cm_Create(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2184 cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
2189 cm_callbackRequest_t cbReq;
2194 AFSStoreStatus inStatus;
2195 AFSFetchStatus updatedDirStatus;
2196 AFSFetchStatus newFileStatus;
2197 AFSCallBack newFileCallback;
2199 struct rx_connection * callp;
2201 /* can't create names with @sys in them; must expand it manually first.
2202 * return "invalid request" if they try.
2204 if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2205 return CM_ERROR_ATSYS;
2208 /* before starting the RPC, mark that we're changing the file data, so
2209 * that someone who does a chmod will know to wait until our call
2212 lock_ObtainMutex(&dscp->mx);
2213 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2215 cm_StartCallbackGrantingCall(NULL, &cbReq);
2217 lock_ReleaseMutex(&dscp->mx);
2223 cm_StatusFromAttr(&inStatus, NULL, attrp);
2225 /* try the RPC now */
2226 osi_Log1(afsd_logp, "CALL CreateFile scp 0x%p", dscp);
2228 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2232 dirAFSFid.Volume = dscp->fid.volume;
2233 dirAFSFid.Vnode = dscp->fid.vnode;
2234 dirAFSFid.Unique = dscp->fid.unique;
2236 callp = cm_GetRxConn(connp);
2237 code = RXAFS_CreateFile(connp->callp, &dirAFSFid, namep,
2238 &inStatus, &newAFSFid, &newFileStatus,
2239 &updatedDirStatus, &newFileCallback,
2241 rx_PutConnection(callp);
2243 } while (cm_Analyze(connp, userp, reqp,
2244 &dscp->fid, &volSync, NULL, &cbReq, code));
2245 code = cm_MapRPCError(code, reqp);
2248 osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
2250 osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
2252 lock_ObtainMutex(&dscp->mx);
2253 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2255 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2257 lock_ReleaseMutex(&dscp->mx);
2259 /* now try to create the file's entry, too, but be careful to
2260 * make sure that we don't merge in old info. Since we weren't locking
2261 * out any requests during the file's creation, we may have pretty old
2265 newFid.cell = dscp->fid.cell;
2266 newFid.volume = dscp->fid.volume;
2267 newFid.vnode = newAFSFid.Vnode;
2268 newFid.unique = newAFSFid.Unique;
2269 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2271 lock_ObtainMutex(&scp->mx);
2272 if (!cm_HaveCallback(scp)) {
2273 cm_MergeStatus(scp, &newFileStatus, &volSync,
2275 cm_EndCallbackGrantingCall(scp, &cbReq,
2276 &newFileCallback, 0);
2279 lock_ReleaseMutex(&scp->mx);
2284 /* make sure we end things properly */
2286 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2291 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
2295 lock_ObtainWrite(&scp->bufCreateLock);
2296 code = buf_CleanVnode(scp, userp, reqp);
2297 lock_ReleaseWrite(&scp->bufCreateLock);
2299 lock_ObtainMutex(&scp->mx);
2300 scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA
2301 | CM_SCACHEFLAG_OUTOFSPACE);
2302 if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
2303 | CM_SCACHEMASK_CLIENTMODTIME
2304 | CM_SCACHEMASK_LENGTH))
2305 code = cm_StoreMini(scp, userp, reqp);
2306 lock_ReleaseMutex(&scp->mx);
2311 long cm_MakeDir(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2312 cm_user_t *userp, cm_req_t *reqp)
2317 cm_callbackRequest_t cbReq;
2322 AFSStoreStatus inStatus;
2323 AFSFetchStatus updatedDirStatus;
2324 AFSFetchStatus newDirStatus;
2325 AFSCallBack newDirCallback;
2327 struct rx_connection * callp;
2329 /* can't create names with @sys in them; must expand it manually first.
2330 * return "invalid request" if they try.
2332 if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2333 return CM_ERROR_ATSYS;
2336 /* before starting the RPC, mark that we're changing the directory
2337 * data, so that someone who does a chmod on the dir will wait until
2338 * our call completes.
2340 lock_ObtainMutex(&dscp->mx);
2341 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2343 cm_StartCallbackGrantingCall(NULL, &cbReq);
2345 lock_ReleaseMutex(&dscp->mx);
2351 cm_StatusFromAttr(&inStatus, NULL, attrp);
2353 /* try the RPC now */
2354 osi_Log1(afsd_logp, "CALL MakeDir scp 0x%p", dscp);
2356 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2360 dirAFSFid.Volume = dscp->fid.volume;
2361 dirAFSFid.Vnode = dscp->fid.vnode;
2362 dirAFSFid.Unique = dscp->fid.unique;
2364 callp = cm_GetRxConn(connp);
2365 code = RXAFS_MakeDir(connp->callp, &dirAFSFid, namep,
2366 &inStatus, &newAFSFid, &newDirStatus,
2367 &updatedDirStatus, &newDirCallback,
2369 rx_PutConnection(callp);
2371 } while (cm_Analyze(connp, userp, reqp,
2372 &dscp->fid, &volSync, NULL, &cbReq, code));
2373 code = cm_MapRPCError(code, reqp);
2376 osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
2378 osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
2380 lock_ObtainMutex(&dscp->mx);
2381 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2383 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2385 lock_ReleaseMutex(&dscp->mx);
2387 /* now try to create the new dir's entry, too, but be careful to
2388 * make sure that we don't merge in old info. Since we weren't locking
2389 * out any requests during the file's creation, we may have pretty old
2393 newFid.cell = dscp->fid.cell;
2394 newFid.volume = dscp->fid.volume;
2395 newFid.vnode = newAFSFid.Vnode;
2396 newFid.unique = newAFSFid.Unique;
2397 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2399 lock_ObtainMutex(&scp->mx);
2400 if (!cm_HaveCallback(scp)) {
2401 cm_MergeStatus(scp, &newDirStatus, &volSync,
2403 cm_EndCallbackGrantingCall(scp, &cbReq,
2404 &newDirCallback, 0);
2407 lock_ReleaseMutex(&scp->mx);
2408 cm_ReleaseSCache(scp);
2412 /* make sure we end things properly */
2414 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2416 /* and return error code */
2420 long cm_Link(cm_scache_t *dscp, char *namep, cm_scache_t *sscp, long flags,
2421 cm_user_t *userp, cm_req_t *reqp)
2426 AFSFid existingAFSFid;
2427 AFSFetchStatus updatedDirStatus;
2428 AFSFetchStatus newLinkStatus;
2430 struct rx_connection * callp;
2432 if (dscp->fid.cell != sscp->fid.cell ||
2433 dscp->fid.volume != sscp->fid.volume) {
2434 return CM_ERROR_CROSSDEVLINK;
2437 lock_ObtainMutex(&dscp->mx);
2438 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2439 lock_ReleaseMutex(&dscp->mx);
2444 /* try the RPC now */
2445 osi_Log1(afsd_logp, "CALL Link scp 0x%p", dscp);
2447 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2450 dirAFSFid.Volume = dscp->fid.volume;
2451 dirAFSFid.Vnode = dscp->fid.vnode;
2452 dirAFSFid.Unique = dscp->fid.unique;
2454 existingAFSFid.Volume = sscp->fid.volume;
2455 existingAFSFid.Vnode = sscp->fid.vnode;
2456 existingAFSFid.Unique = sscp->fid.unique;
2458 callp = cm_GetRxConn(connp);
2459 code = RXAFS_Link(callp, &dirAFSFid, namep, &existingAFSFid,
2460 &newLinkStatus, &updatedDirStatus, &volSync);
2461 rx_PutConnection(callp);
2462 osi_Log1(smb_logp," RXAFS_Link returns 0x%x", code);
2464 } while (cm_Analyze(connp, userp, reqp,
2465 &dscp->fid, &volSync, NULL, NULL, code));
2467 code = cm_MapRPCError(code, reqp);
2470 osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
2472 osi_Log0(afsd_logp, "CALL Link SUCCESS");
2474 lock_ObtainMutex(&dscp->mx);
2475 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2477 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2479 lock_ReleaseMutex(&dscp->mx);
2484 long cm_SymLink(cm_scache_t *dscp, char *namep, char *contentsp, long flags,
2485 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
2493 AFSStoreStatus inStatus;
2494 AFSFetchStatus updatedDirStatus;
2495 AFSFetchStatus newLinkStatus;
2497 struct rx_connection * callp;
2499 /* before starting the RPC, mark that we're changing the directory data,
2500 * so that someone who does a chmod on the dir will wait until our
2503 lock_ObtainMutex(&dscp->mx);
2504 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2505 lock_ReleaseMutex(&dscp->mx);
2510 cm_StatusFromAttr(&inStatus, NULL, attrp);
2512 /* try the RPC now */
2513 osi_Log1(afsd_logp, "CALL Symlink scp 0x%p", dscp);
2515 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2519 dirAFSFid.Volume = dscp->fid.volume;
2520 dirAFSFid.Vnode = dscp->fid.vnode;
2521 dirAFSFid.Unique = dscp->fid.unique;
2523 callp = cm_GetRxConn(connp);
2524 code = RXAFS_Symlink(callp, &dirAFSFid, namep, contentsp,
2525 &inStatus, &newAFSFid, &newLinkStatus,
2526 &updatedDirStatus, &volSync);
2527 rx_PutConnection(callp);
2529 } while (cm_Analyze(connp, userp, reqp,
2530 &dscp->fid, &volSync, NULL, NULL, code));
2531 code = cm_MapRPCError(code, reqp);
2534 osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
2536 osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
2538 lock_ObtainMutex(&dscp->mx);
2539 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2541 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2543 lock_ReleaseMutex(&dscp->mx);
2545 /* now try to create the new dir's entry, too, but be careful to
2546 * make sure that we don't merge in old info. Since we weren't locking
2547 * out any requests during the file's creation, we may have pretty old
2551 newFid.cell = dscp->fid.cell;
2552 newFid.volume = dscp->fid.volume;
2553 newFid.vnode = newAFSFid.Vnode;
2554 newFid.unique = newAFSFid.Unique;
2555 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2557 lock_ObtainMutex(&scp->mx);
2558 if (!cm_HaveCallback(scp)) {
2559 cm_MergeStatus(scp, &newLinkStatus, &volSync,
2562 lock_ReleaseMutex(&scp->mx);
2563 cm_ReleaseSCache(scp);
2567 /* and return error code */
2571 long cm_RemoveDir(cm_scache_t *dscp, char *namep, cm_user_t *userp,
2578 AFSFetchStatus updatedDirStatus;
2580 struct rx_connection * callp;
2582 /* before starting the RPC, mark that we're changing the directory data,
2583 * so that someone who does a chmod on the dir will wait until our
2586 lock_ObtainMutex(&dscp->mx);
2587 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2588 lock_ReleaseMutex(&dscp->mx);
2594 /* try the RPC now */
2595 osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%p", dscp);
2597 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2601 dirAFSFid.Volume = dscp->fid.volume;
2602 dirAFSFid.Vnode = dscp->fid.vnode;
2603 dirAFSFid.Unique = dscp->fid.unique;
2605 callp = cm_GetRxConn(connp);
2606 code = RXAFS_RemoveDir(callp, &dirAFSFid, namep,
2607 &updatedDirStatus, &volSync);
2608 rx_PutConnection(callp);
2610 } while (cm_Analyze(connp, userp, reqp,
2611 &dscp->fid, &volSync, NULL, NULL, code));
2612 code = cm_MapRPCErrorRmdir(code, reqp);
2615 osi_Log1(afsd_logp, "CALL RemoveDir FAILURE, code 0x%x", code);
2617 osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
2619 lock_ObtainMutex(&dscp->mx);
2620 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2622 cm_dnlcRemove(dscp, namep);
2623 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2625 lock_ReleaseMutex(&dscp->mx);
2627 /* and return error code */
2631 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
2633 /* grab mutex on contents */
2634 lock_ObtainMutex(&scp->mx);
2636 /* reset the prefetch info */
2637 scp->prefetch.base.LowPart = 0; /* base */
2638 scp->prefetch.base.HighPart = 0;
2639 scp->prefetch.end.LowPart = 0; /* and end */
2640 scp->prefetch.end.HighPart = 0;
2642 /* release mutex on contents */
2643 lock_ReleaseMutex(&scp->mx);
2649 long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
2650 char *newNamep, cm_user_t *userp, cm_req_t *reqp)
2654 AFSFid oldDirAFSFid;
2655 AFSFid newDirAFSFid;
2657 AFSFetchStatus updatedOldDirStatus;
2658 AFSFetchStatus updatedNewDirStatus;
2661 struct rx_connection * callp;
2663 /* before starting the RPC, mark that we're changing the directory data,
2664 * so that someone who does a chmod on the dir will wait until our call
2665 * completes. We do this in vnode order so that we don't deadlock,
2666 * which makes the code a little verbose.
2668 if (oldDscp == newDscp) {
2669 /* check for identical names */
2670 if (strcmp(oldNamep, newNamep) == 0)
2671 return CM_ERROR_RENAME_IDENTICAL;
2674 lock_ObtainMutex(&oldDscp->mx);
2675 cm_dnlcRemove(oldDscp, oldNamep);
2676 cm_dnlcRemove(oldDscp, newNamep);
2677 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2678 CM_SCACHESYNC_STOREDATA);
2679 lock_ReleaseMutex(&oldDscp->mx);
2682 /* two distinct dir vnodes */
2684 if (oldDscp->fid.cell != newDscp->fid.cell ||
2685 oldDscp->fid.volume != newDscp->fid.volume)
2686 return CM_ERROR_CROSSDEVLINK;
2688 /* shouldn't happen that we have distinct vnodes for two
2689 * different files, but could due to deliberate attack, or
2690 * stale info. Avoid deadlocks and quit now.
2692 if (oldDscp->fid.vnode == newDscp->fid.vnode)
2693 return CM_ERROR_CROSSDEVLINK;
2695 if (oldDscp->fid.vnode < newDscp->fid.vnode) {
2696 lock_ObtainMutex(&oldDscp->mx);
2697 cm_dnlcRemove(oldDscp, oldNamep);
2698 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2699 CM_SCACHESYNC_STOREDATA);
2700 lock_ReleaseMutex(&oldDscp->mx);
2702 lock_ObtainMutex(&newDscp->mx);
2703 cm_dnlcRemove(newDscp, newNamep);
2704 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2705 CM_SCACHESYNC_STOREDATA);
2706 lock_ReleaseMutex(&newDscp->mx);
2708 /* cleanup first one */
2709 lock_ObtainMutex(&newDscp->mx);
2710 cm_SyncOpDone(oldDscp, NULL,
2711 CM_SCACHESYNC_STOREDATA);
2712 lock_ReleaseMutex(&oldDscp->mx);
2717 /* lock the new vnode entry first */
2718 lock_ObtainMutex(&newDscp->mx);
2719 cm_dnlcRemove(newDscp, newNamep);
2720 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2721 CM_SCACHESYNC_STOREDATA);
2722 lock_ReleaseMutex(&newDscp->mx);
2724 lock_ObtainMutex(&oldDscp->mx);
2725 cm_dnlcRemove(oldDscp, oldNamep);
2726 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2727 CM_SCACHESYNC_STOREDATA);
2728 lock_ReleaseMutex(&oldDscp->mx);
2730 /* cleanup first one */
2731 lock_ObtainMutex(&newDscp->mx);
2732 cm_SyncOpDone(newDscp, NULL,
2733 CM_SCACHESYNC_STOREDATA);
2734 lock_ReleaseMutex(&newDscp->mx);
2738 } /* two distinct vnodes */
2745 /* try the RPC now */
2746 osi_Log2(afsd_logp, "CALL Rename old scp 0x%p new scp 0x%p",
2749 code = cm_Conn(&oldDscp->fid, userp, reqp, &connp);
2753 oldDirAFSFid.Volume = oldDscp->fid.volume;
2754 oldDirAFSFid.Vnode = oldDscp->fid.vnode;
2755 oldDirAFSFid.Unique = oldDscp->fid.unique;
2756 newDirAFSFid.Volume = newDscp->fid.volume;
2757 newDirAFSFid.Vnode = newDscp->fid.vnode;
2758 newDirAFSFid.Unique = newDscp->fid.unique;
2760 callp = cm_GetRxConn(connp);
2761 code = RXAFS_Rename(callp, &oldDirAFSFid, oldNamep,
2762 &newDirAFSFid, newNamep,
2763 &updatedOldDirStatus, &updatedNewDirStatus,
2765 rx_PutConnection(callp);
2767 } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
2768 &volSync, NULL, NULL, code));
2769 code = cm_MapRPCError(code, reqp);
2772 osi_Log1(afsd_logp, "CALL Rename FAILURE, code 0x%x", code);
2774 osi_Log0(afsd_logp, "CALL Rename SUCCESS");
2776 /* update the individual stat cache entries for the directories */
2777 lock_ObtainMutex(&oldDscp->mx);
2778 cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
2780 cm_MergeStatus(oldDscp, &updatedOldDirStatus, &volSync,
2783 lock_ReleaseMutex(&oldDscp->mx);
2785 /* and update it for the new one, too, if necessary */
2787 lock_ObtainMutex(&newDscp->mx);
2788 cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
2790 cm_MergeStatus(newDscp, &updatedNewDirStatus, &volSync,
2793 lock_ReleaseMutex(&newDscp->mx);
2796 /* and return error code */
2800 /* Byte range locks:
2802 The OpenAFS Windows client has to fake byte range locks given no
2803 server side support for such locks. This is implemented as keyed
2804 byte range locks on the cache manager.
2806 Keyed byte range locks:
2808 Each cm_scache_t structure keeps track of a list of keyed locks.
2809 The key for a lock identifies an owner of a set of locks (referred
2810 to as a client). Each key is represented by a value. The set of
2811 key values used within a specific cm_scache_t structure form a
2812 namespace that has a scope of just that cm_scache_t structure. The
2813 same key value can be used with another cm_scache_t structure and
2814 correspond to a completely different client. However it is
2815 advantageous for the SMB or IFS layer to make sure that there is a
2816 1-1 mapping between client and keys over all cm_scache_t objects.
2818 Assume a client C has key Key(C) (although, since the scope of the
2819 key is a cm_scache_t, the key can be Key(C,S), where S is the
2820 cm_scache_t. But assume a 1-1 relation between keys and clients).
2821 A byte range (O,+L) denotes byte addresses (O) through (O+L-1)
2822 inclusive (a.k.a. [O,O+L-1]). The function Key(x) is implemented
2823 through cm_generateKey() function for both SMB and IFS.
2825 The list of locks for a cm_scache_t object S is maintained in
2826 S->fileLocks. The cache manager will set a lock on the AFS file
2827 server in order to assert the locks in S->fileLocks. If only
2828 shared locks are in place for S, then the cache manager will obtain
2829 a LockRead lock, while if there are any exclusive locks, it will
2830 obtain a LockWrite lock. If the exclusive locks are all released
2831 while the shared locks remain, then the cache manager will
2832 downgrade the lock from LockWrite to LockRead. Similarly, if an
2833 exclusive lock is obtained when only shared locks exist, then the
2834 cache manager will try to upgrade the lock from LockRead to
2837 Each lock L owned by client C maintains a key L->key such that
2838 L->key == Key(C), the effective range defined by L->LOffset and
2839 L->LLength such that the range of bytes affected by the lock is
2840 (L->LOffset, +L->LLength), a type maintained in L->LockType which
2841 is either exclusive or shared.
2845 A lock exists iff it is in S->fileLocks for some cm_scache_t
2846 S. Existing locks are in one of the following states: ACTIVE,
2847 WAITLOCK, WAITUNLOCK, LOST, DELETED.
2849 The following sections describe each lock and the associated
2852 1. ACTIVE: A lock L is ACTIVE iff the cache manager has asserted
2853 the lock with the AFS file server. This type of lock can be
2854 exercised by a client to read or write to the locked region (as
2857 1.1 ACTIVE->LOST: When the AFS file server fails to extend a
2858 server lock that was required to assert the lock. Before
2859 marking the lock as lost, the cache manager checks if the file
2860 has changed on the server. If the file has not changed, then
2861 the cache manager will attempt to obtain a new server lock
2862 that is sufficient to assert the client side locks for the
2863 file. If any of these fail, the lock is marked as LOST.
2864 Otherwise, it is left as ACTIVE.
2866 1.2 ACTIVE->DELETED: Lock is released.
2868 2. WAITLOCK: A lock is in a WAITLOCK state if the cache manager
2869 grants the lock but the lock is yet to be asserted with the AFS
2870 file server. Once the file server grants the lock, the state
2871 will transition to an ACTIVE lock.
2873 2.1 WAITLOCK->ACTIVE: The server granted the lock.
2875 2.2 WAITLOCK->DELETED: Lock is abandoned, or timed out during
2878 2.3 WAITLOCK->LOST: One or more locks from this client were
2879 marked as LOST. No further locks will be granted to this
2880 client until all lost locks are removed.
2882 3. WAITUNLOCK: A lock is in a WAITUNLOCK state if the cache manager
2883 receives a request for a lock that conflicts with an existing
2884 ACTIVE or WAITLOCK lock. The lock will be placed in the queue
2885 and will be granted at such time the conflicting locks are
2886 removed, at which point the state will transition to either
2889 3.1 WAITUNLOCK->ACTIVE: The conflicting lock was removed. The
2890 current serverLock is sufficient to assert this lock, or a
2891 sufficient serverLock is obtained.
2893 3.2 WAITUNLOCK->WAITLOCK: The conflicting lock was removed,
2894 however the required serverLock is yet to be asserted with the
2897 3.3 WAITUNLOCK->DELETED: The lock is abandoned, timed out or
2900 3.5 WAITUNLOCK->LOST: One or more locks from this client were
2901 marked as LOST. No further locks will be granted to this
2902 client until all lost locks are removed.
2904 4. LOST: A lock L is LOST if the server lock that was required to
2905 assert the lock could not be obtained or if it could not be
2906 extended, or if other locks by the same client were LOST.
2907 Essentially, once a lock is LOST, the contract between the cache
2908 manager and that specific client is no longer valid.
2910 The cache manager rechecks the server lock once every minute and
2911 extends it as appropriate. If this is not done for 5 minutes,
2912 the AFS file server will release the lock (the 5 minute timeout
2913 is based on current file server code and is fairly arbitrary).
2914 Once released, the lock cannot be re-obtained without verifying
2915 that the contents of the file hasn't been modified since the
2916 time the lock was released. Re-obtaining the lock without
2917 verifying this may lead to data corruption. If the lock can not
2918 be obtained safely, then all active locks for the cm_scache_t
2921 4.1 LOST->DELETED: The lock is released.
2923 5. DELETED: The lock is no longer relevant. Eventually, it will
2924 get removed from the cm_scache_t. In the meantime, it will be
2925 treated as if it does not exist.
2927 5.1 DELETED->not exist: The lock is removed from the
2930 The following are classifications of locks based on their state.
2932 6* A lock L is ACCEPTED if it is ACTIVE or WAITLOCK. These locks
2933 have been accepted by the cache manager, but may or may not have
2934 been granted back to the client.
2936 7* A lock L is QUEUED if it is ACTIVE, WAITLOCK or WAITUNLOCK.
2938 8* A lock L is WAITING if it is WAITLOCK or WAITUNLOCK.
2942 A client C can READ range (Offset,+Length) of a file represented by
2943 cm_scache_t S iff (1):
2945 1. for all _a_ in (Offset,+Length), all of the following is true:
2947 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
2948 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
2951 1.2 For each LOST lock L in S->fileLocks such that _a_ in
2952 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
2955 (When locks are lost on an cm_scache_t, all locks are lost. By
2956 4.2 (below), if there is an exclusive LOST lock, then there
2957 can't be any overlapping ACTIVE locks.)
2959 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
2961 2. for all _a_ in (Offset,+Length), one of the following is true:
2963 2.1 Byte _a_ of S is unowned (as specified in 1.1) AND there
2964 does not exist a LOST lock L such that _a_ in
2965 (L->LOffset,+L->LLength).
2967 2.2 Byte _a_ of S is owned by C under lock L (as specified in
2968 1.2) AND L->LockType is exclusive.
2970 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
2972 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
2975 3.1 If L->LockType is exclusive then there does NOT exist a
2976 ACCEPTED lock M in S->fileLocks such that _a_ in
2977 (M->LOffset,+M->LLength).
2979 (If we count all QUEUED locks then we hit cases such as
2980 cascading waiting locks where the locks later on in the queue
2981 can be granted without compromising file integrity. On the
2982 other hand if only ACCEPTED locks are considered, then locks
2983 that were received earlier may end up waiting for locks that
2984 were received later to be unlocked. The choice of ACCEPTED
2985 locks was made to mimic the Windows byte range lock
2988 3.2 If L->LockType is shared then for each ACCEPTED lock M in
2989 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
2990 M->LockType is shared.
2992 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
2994 4.1 M->key != Key(C)
2996 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
2997 and (M->LOffset,+M->LLength) do not intersect.
2999 (Note: If a client loses a lock, it loses all locks.
3000 Subsequently, it will not be allowed to obtain any more locks
3001 until all existing LOST locks that belong to the client are
3002 released. Once all locks are released by a single client,
3003 there exists no further contract between the client and AFS
3004 about the contents of the file, hence the client can then
3005 proceed to obtain new locks and establish a new contract.
3007 This doesn't quite work as you think it should, because most
3008 applications aren't built to deal with losing locks they
3009 thought they once had. For now, we don't have a good
3010 solution to lost locks.
3012 Also, for consistency reasons, we have to hold off on
3013 granting locks that overlap exclusive LOST locks.)
3015 A client C can only unlock locks L in S->fileLocks which have
3018 The representation and invariants are as follows:
3020 - Each cm_scache_t structure keeps:
3022 - A queue of byte-range locks (cm_scache_t::fileLocks) which
3023 are of type cm_file_lock_t.
3025 - A record of the highest server-side lock that has been
3026 obtained for this object (cm_scache_t::serverLock), which is
3027 one of (-1), LockRead, LockWrite.
3029 - A count of ACCEPTED exclusive and shared locks that are in the
3030 queue (cm_scache_t::sharedLocks and
3031 cm_scache_t::exclusiveLocks)
3033 - Each cm_file_lock_t structure keeps:
3035 - The type of lock (cm_file_lock_t::LockType)
3037 - The key associated with the lock (cm_file_lock_t::key)
3039 - The offset and length of the lock (cm_file_lock_t::LOffset
3040 and cm_file_lock_t::LLength)
3042 - The state of the lock.
3044 - Time of issuance or last successful extension
3046 Semantic invariants:
3048 I1. The number of ACCEPTED locks in S->fileLocks are
3049 (S->sharedLocks + S->exclusiveLocks)
3051 External invariants:
3053 I3. S->serverLock is the lock that we have asserted with the
3054 AFS file server for this cm_scache_t.
3056 I4. S->serverLock == LockRead iff there is at least one ACTIVE
3057 shared lock, but no ACTIVE exclusive locks.
3059 I5. S->serverLock == LockWrite iff there is at least one ACTIVE
3062 I6. If L is a LOST lock, then for each lock M in S->fileLocks,
3063 M->key == L->key IMPLIES M is LOST or DELETED.
3068 #define IS_LOCK_ACTIVE(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == 0)
3070 #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)
3072 #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)
3074 #define IS_LOCK_LOST(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_LOST)
3076 #define IS_LOCK_DELETED(lockp) (((lockp)->flags & CM_FILELOCK_FLAG_DELETED) == CM_FILELOCK_FLAG_DELETED)
3079 #define IS_LOCK_ACCEPTED(lockp) (IS_LOCK_ACTIVE(lockp) || IS_LOCK_WAITLOCK(lockp))
3082 #define IS_LOCK_CLIENTONLY(lockp) ((((lockp)->scp->flags & CM_SCACHEFLAG_RO) == CM_SCACHEFLAG_RO) || (((lockp)->flags & CM_FILELOCK_FLAG_CLIENTONLY) == CM_FILELOCK_FLAG_CLIENTONLY))
3085 #define INTERSECT_RANGE(r1,r2) (((r2).offset+(r2).length) > (r1).offset && ((r1).offset +(r1).length) > (r2).offset)
3088 #define CONTAINS_RANGE(r1,r2) (((r2).offset+(r2).length) <= ((r1).offset+(r1).length) && (r1).offset <= (r2).offset)
3090 #define SERVERLOCKS_ENABLED(scp) (!(((scp)->flags & CM_SCACHEFLAG_RO) && cm_enableServerLocks))
3092 static void cm_LockRangeSubtract(cm_range_t * pos, const cm_range_t * neg)
3094 afs_int64 int_begin;
3097 int_begin = MAX(pos->offset, neg->offset);
3098 int_end = MIN(pos->offset+pos->length, neg->offset+neg->length);
3100 if(int_begin < int_end) {
3101 if(int_begin == pos->offset) {
3102 pos->length = pos->offset + pos->length - int_end;
3103 pos->offset = int_end;
3104 } else if(int_end == pos->offset + pos->length) {
3105 pos->length = int_begin - pos->offset;
3108 /* We only subtract ranges if the resulting range is
3109 contiguous. If we try to support non-contigous ranges, we
3110 aren't actually improving performance. */
3114 /* Called with scp->mx held. Returns 0 if all is clear to read the
3115 specified range by the client identified by key.
3117 long cm_LockCheckRead(cm_scache_t *scp,
3118 LARGE_INTEGER LOffset,
3119 LARGE_INTEGER LLength,
3122 #ifndef ADVISORY_LOCKS
3124 cm_file_lock_t *fileLock;
3128 int substract_ranges = FALSE;
3130 range.offset = LOffset.QuadPart;
3131 range.length = LLength.QuadPart;
3135 1. for all _a_ in (Offset,+Length), all of the following is true:
3137 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3138 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3141 1.2 For each LOST lock L in S->fileLocks such that _a_ in
3142 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3147 lock_ObtainRead(&cm_scacheLock);
3149 for(q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
3151 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
3153 if (INTERSECT_RANGE(range, fileLock->range)) {
3154 if(IS_LOCK_ACTIVE(fileLock)) {
3155 if(fileLock->key == key) {
3157 /* If there is an active lock for this client, it
3158 is safe to substract ranges.*/
3159 cm_LockRangeSubtract(&range, &fileLock->range);
3160 substract_ranges = TRUE;
3162 if(fileLock->lockType != LockRead) {
3163 code = CM_ERROR_LOCK_CONFLICT;
3167 /* even if the entire range is locked for reading,
3168 we still can't grant the lock at this point
3169 because the client may have lost locks. That
3170 is, unless we have already seen an active lock
3171 belonging to the client, in which case there
3172 can't be any lost locks for this client. */
3173 if(substract_ranges)
3174 cm_LockRangeSubtract(&range, &fileLock->range);
3176 } else if(IS_LOCK_LOST(fileLock) &&
3177 (fileLock->key == key || fileLock->lockType == LockWrite)) {
3178 code = CM_ERROR_BADFD;
3184 lock_ReleaseRead(&cm_scacheLock);
3186 osi_Log4(afsd_logp, "cm_LockCheckRead scp 0x%x offset %d length %d code 0x%x",
3187 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
3198 /* Called with scp->mx held. Returns 0 if all is clear to write the
3199 specified range by the client identified by key.
3201 long cm_LockCheckWrite(cm_scache_t *scp,
3202 LARGE_INTEGER LOffset,
3203 LARGE_INTEGER LLength,
3206 #ifndef ADVISORY_LOCKS
3208 cm_file_lock_t *fileLock;
3213 range.offset = LOffset.QuadPart;
3214 range.length = LLength.QuadPart;
3217 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
3219 2. for all _a_ in (Offset,+Length), one of the following is true:
3221 2.1 Byte _a_ of S is unowned AND there does not exist a LOST
3222 lock L such that _a_ in (L->LOffset,+L->LLength).
3224 2.2 Byte _a_ of S is owned by C under lock L AND L->LockType is
3228 lock_ObtainRead(&cm_scacheLock);
3230 for(q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
3232 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
3234 if(INTERSECT_RANGE(range, fileLock->range)) {
3235 if(IS_LOCK_ACTIVE(fileLock)) {
3236 if(fileLock->key == key) {
3237 if(fileLock->lockType == LockWrite) {
3239 /* if there is an active lock for this client, it
3240 is safe to substract ranges */
3241 cm_LockRangeSubtract(&range, &fileLock->range);
3243 code = CM_ERROR_LOCK_CONFLICT;
3247 code = CM_ERROR_LOCK_CONFLICT;
3250 } else if(IS_LOCK_LOST(fileLock)) {
3251 code = CM_ERROR_BADFD;
3257 lock_ReleaseRead(&cm_scacheLock);
3259 osi_Log4(afsd_logp, "cm_LockCheckWrite scp 0x%x offset %d length %d code 0x%x",
3260 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
3272 static void cm_LockMarkSCacheLost(cm_scache_t * scp);
3274 /* Called with cm_scacheLock write locked */
3275 static cm_file_lock_t * cm_GetFileLock(void) {
3278 l = (cm_file_lock_t *) cm_freeFileLocks;
3280 osi_QRemove(&cm_freeFileLocks, &l->q);
3282 l = malloc(sizeof(cm_file_lock_t));
3286 memset(l, 0, sizeof(cm_file_lock_t));
3291 /* Called with cm_scacheLock write locked */
3292 static void cm_PutFileLock(cm_file_lock_t *l) {
3293 osi_QAdd(&cm_freeFileLocks, &l->q);
3296 /* called with scp->mx held */
3297 long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
3298 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
3300 int allowWait, cm_user_t *userp, cm_req_t *reqp,
3301 cm_file_lock_t **lockpp)
3304 int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
3308 cm_file_lock_t *fileLock;
3310 struct rx_connection * callp;
3312 int wait_unlock = FALSE;
3314 osi_Log4(afsd_logp, "cm_Lock scp 0x%x type 0x%x offset %d length %d",
3315 scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
3316 osi_Log3(afsd_logp, "... allowWait %d key 0x%x:%x", allowWait,
3317 (unsigned long)(key >> 32), (unsigned long)(key & 0xffffffff));
3320 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
3322 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
3325 3.1 If L->LockType is exclusive then there does NOT exist a
3326 ACCEPTED lock M in S->fileLocks such that _a_ in
3327 (M->LOffset,+M->LLength).
3329 3.2 If L->LockType is shared then for each ACCEPTED lock M in
3330 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
3331 M->LockType is shared.
3333 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
3335 4.1 M->key != Key(C)
3337 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
3338 and (M->LOffset,+M->LLength) do not intersect.
3341 range.offset = LOffset.QuadPart;
3342 range.length = LLength.QuadPart;
3344 lock_ObtainRead(&cm_scacheLock);
3346 for(q = scp->fileLocksH; q; q = osi_QNext(q)) {
3348 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
3350 if(IS_LOCK_LOST(fileLock)) {
3351 if (fileLock->key == key) {
3352 code = CM_ERROR_BADFD;
3354 } else if (fileLock->lockType == LockWrite && INTERSECT_RANGE(range, fileLock->range)) {
3355 code = CM_ERROR_WOULDBLOCK;
3361 /* we don't need to check for deleted locks here since deleted
3362 locks are dequeued from scp->fileLocks */
3363 if(IS_LOCK_ACCEPTED(fileLock) &&
3364 INTERSECT_RANGE(range, fileLock->range)) {
3366 if((sLockType & LOCKING_ANDX_SHARED_LOCK) == 0 ||
3367 fileLock->lockType != LockRead) {
3369 code = CM_ERROR_WOULDBLOCK;
3375 lock_ReleaseRead(&cm_scacheLock);
3377 if(code == 0 && SERVERLOCKS_ENABLED(scp)) {
3378 if(Which == scp->serverLock ||
3379 (Which == LockRead && scp->serverLock == LockWrite)) {
3381 /* we already have the lock we need */
3382 osi_Log3(afsd_logp, " we already have the correct lock. exclusives[%d], shared[%d], serverLock[%d]",
3383 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
3384 code = 0; /* redundant */
3386 } else if((scp->exclusiveLocks > 0) ||
3387 (scp->sharedLocks > 0 && scp->serverLock != LockRead)) {
3389 /* We are already waiting for some other lock. We should
3390 wait for the daemon to catch up instead of generating a
3391 flood of SetLock calls. */
3392 osi_Log3(afsd_logp, " already waiting for other lock. exclusives[%d], shared[%d], serverLock[%d]",
3393 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
3394 code = CM_ERROR_WOULDBLOCK;
3400 if (scp->serverLock == LockRead && Which == LockWrite) {
3402 /* We want to escalate the lock to a LockWrite.
3403 Unfortunately that's not really possible without
3404 letting go of the current lock. But for now we do
3407 osi_Log0(afsd_logp, " attempting to UPGRADE from LockRead to LockWrite.");
3409 tfid.Volume = scp->fid.volume;
3410 tfid.Vnode = scp->fid.vnode;
3411 tfid.Unique = scp->fid.unique;
3414 lock_ReleaseMutex(&scp->mx);
3416 osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
3419 code = cm_Conn(&cfid, userp, reqp, &connp);
3423 callp = cm_GetRxConn(connp);
3424 code = RXAFS_ReleaseLock(callp, &tfid, &volSync);
3425 rx_PutConnection(callp);
3427 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
3429 code = cm_MapRPCError(code, reqp);
3432 osi_Log1(afsd_logp, "CALL ReleaseLock FAILURE, code 0x%x", code);
3434 osi_Log0(afsd_logp, "CALL ReleaseLock SUCCESS");
3436 lock_ObtainMutex(&scp->mx);
3439 /* We couldn't release the lock */
3442 scp->serverLock = -1;
3446 /* We need to obtain a server lock of type Which in order
3447 to assert this file lock */
3448 tfid.Volume = scp->fid.volume;
3449 tfid.Vnode = scp->fid.vnode;
3450 tfid.Unique = scp->fid.unique;
3453 #ifndef AGGRESSIVE_LOCKS
3456 newLock = LockWrite;
3458 osi_Log3(afsd_logp, "CALL SetLock scp 0x%p from %d to %d", scp, (int) scp->serverLock, newLock);
3460 lock_ReleaseMutex(&scp->mx);
3463 code = cm_Conn(&cfid, userp, reqp, &connp);
3467 callp = cm_GetRxConn(connp);
3468 code = RXAFS_SetLock(callp, &tfid, newLock,
3470 rx_PutConnection(callp);
3472 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
3475 code = cm_MapRPCError(code, reqp);
3478 osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
3480 osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
3483 if (code == CM_ERROR_WOULDBLOCK && newLock != Which) {
3484 /* we wanted LockRead. We tried LockWrite. Now try LockRead again */
3488 osi_assert(newLock == LockRead);
3490 osi_Log3(afsd_logp, "CALL SetLock AGAIN scp 0x%p from %d to %d",
3491 scp, (int) scp->serverLock, newLock);
3494 code = cm_Conn(&cfid, userp, reqp, &connp);
3498 callp = cm_GetRxConn(connp);
3499 code = RXAFS_SetLock(callp, &tfid, newLock,
3501 rx_PutConnection(callp);
3503 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
3506 code = cm_MapRPCError(code, reqp);
3509 osi_Log1(afsd_logp, "CALL SetLock FAILURE AGAIN, code 0x%x", code);
3511 osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
3515 lock_ObtainMutex(&scp->mx);
3518 scp->serverLock = newLock;
3520 if ((scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
3521 scp->serverLock == -1) {
3522 /* Oops. We lost the lock. */
3523 cm_LockMarkSCacheLost(scp);
3527 } else if (code == 0) { /* server locks not enabled */
3529 " Skipping server lock for scp");
3535 /* Special case error translations
3537 Applications don't expect certain errors from a
3538 LockFile/UnlockFile call. We need to translate some error
3539 code to codes that apps expect and handle. */
3541 /* We shouldn't actually need to handle this case since we
3542 simulate locks for RO scps anyway. */
3543 if (code == CM_ERROR_READONLY) {
3544 osi_Log0(afsd_logp, " Reinterpreting CM_ERROR_READONLY as CM_ERROR_NOACCESS");
3545 code = CM_ERROR_NOACCESS;
3549 if (code == 0 || (code == CM_ERROR_WOULDBLOCK && allowWait)) {
3551 lock_ObtainWrite(&cm_scacheLock);
3552 fileLock = cm_GetFileLock();
3553 lock_ReleaseWrite(&cm_scacheLock);
3555 fileLock->fid = scp->fid;
3557 fileLock->key = key;
3558 fileLock->lockType = Which;
3560 fileLock->userp = userp;
3561 fileLock->range = range;
3562 fileLock->flags = (code == 0 ? 0 :
3564 CM_FILELOCK_FLAG_WAITUNLOCK :
3565 CM_FILELOCK_FLAG_WAITLOCK));
3567 if (!SERVERLOCKS_ENABLED(scp))
3568 fileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
3570 fileLock->lastUpdate = (code == 0) ? time(NULL) : 0;
3572 osi_QAddT(&scp->fileLocksH, &scp->fileLocksT, &fileLock->fileq);
3574 lock_ObtainWrite(&cm_scacheLock);
3575 cm_HoldSCacheNoLock(scp);
3576 fileLock->scp = scp;
3577 osi_QAdd(&cm_allFileLocks, &fileLock->q);
3578 lock_ReleaseWrite(&cm_scacheLock);
3584 if (IS_LOCK_ACCEPTED(fileLock)) {
3585 if(Which == LockRead)
3588 scp->exclusiveLocks++;
3591 osi_Log2(afsd_logp, "cm_Lock Lock added 0x%p flags 0x%x", fileLock, fileLock->flags);
3592 osi_Log4(afsd_logp, " scp[0x%p] exclusives[%d] shared[%d] serverLock[%d]",
3593 scp, scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
3599 static int cm_KeyEquals(cm_key_t k1, cm_key_t k2, int flags);
3601 /* Called with scp->mx held */
3602 long cm_UnlockByKey(cm_scache_t * scp,
3612 cm_file_lock_t *fileLock;
3613 osi_queue_t *q, *qn;
3614 struct rx_connection * callp;
3617 osi_Log3(afsd_logp, "cm_UnlockByKey scp 0x%p key 0x%x:%x",
3618 scp, (unsigned long)(key >> 32), (unsigned long)(key & 0xffffffff));
3620 lock_ObtainWrite(&cm_scacheLock);
3622 for(q = scp->fileLocksH; q; q = qn) {
3625 fileLock = (cm_file_lock_t *)
3626 ((char *) q - offsetof(cm_file_lock_t, fileq));
3629 osi_Log4(afsd_logp, " Checking lock[0x%x] range[%d,+%d] type[%d]",
3630 fileLock, (unsigned long) fileLock->range.offset, (unsigned long) fileLock->range.length,
3631 fileLock->lockType);
3632 osi_Log3(afsd_logp, " key[0x%x:%x] flags[0x%x]",
3633 (unsigned long)(fileLock->key >> 32),
3634 (unsigned long)(fileLock->key & 0xffffffff),
3637 if(cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
3638 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
3639 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
3641 fileLock->fid.volume,
3642 fileLock->fid.vnode,
3643 fileLock->fid.unique);
3644 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
3645 fileLock->scp->fid.cell,
3646 fileLock->scp->fid.volume,
3647 fileLock->scp->fid.vnode,
3648 fileLock->scp->fid.unique);
3653 if (!IS_LOCK_DELETED(fileLock) &&
3654 cm_KeyEquals(fileLock->key, key, flags)) {
3655 osi_Log3(afsd_logp, "...Unlock range [%d,+%d] type %d",
3656 fileLock->range.offset,
3657 fileLock->range.length,
3658 fileLock->lockType);
3660 if (scp->fileLocksT == q)
3661 scp->fileLocksT = osi_QPrev(q);
3662 osi_QRemove(&scp->fileLocksH,q);
3664 if(IS_LOCK_ACCEPTED(fileLock)) {
3665 if(fileLock->lockType == LockRead)
3668 scp->exclusiveLocks--;
3671 fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
3673 cm_ReleaseUser(fileLock->userp);
3674 cm_ReleaseSCacheNoLock(scp);
3676 fileLock->userp = NULL;
3677 fileLock->scp = NULL;
3683 lock_ReleaseWrite(&cm_scacheLock);
3685 if(n_unlocks == 0) {
3686 osi_Log0(afsd_logp, "cm_UnlockByKey no locks found");
3687 osi_Log3(afsd_logp, " Leaving scp with exclusives[%d], shared[%d], serverLock[%d]",
3688 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
3693 osi_Log1(afsd_logp, "cm_UnlockByKey done with %d locks", n_unlocks);
3695 osi_assertx(scp->sharedLocks >= 0, "scp->sharedLocks < 0");
3696 osi_assertx(scp->exclusiveLocks >= 0, "scp->exclusiveLocks < 0");
3698 if (!SERVERLOCKS_ENABLED(scp)) {
3699 osi_Log0(afsd_logp, " Skipping server lock for scp");
3703 /* Ideally we would go through the rest of the locks to determine
3704 * if one or more locks that were formerly in WAITUNLOCK can now
3705 * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
3706 * scp->sharedLocks accordingly. However, the retrying of locks
3707 * in that manner is done cm_RetryLock() manually.
3710 if (scp->serverLock == LockWrite && scp->exclusiveLocks == 0 && scp->sharedLocks > 0) {
3714 /* The serverLock should be downgraded to LockRead */
3715 osi_Log0(afsd_logp, " DOWNGRADE lock from LockWrite to LockRead");
3717 tfid.Volume = scp->fid.volume;
3718 tfid.Vnode = scp->fid.vnode;
3719 tfid.Unique = scp->fid.unique;
3722 lock_ReleaseMutex(&scp->mx);
3724 osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
3727 code = cm_Conn(&cfid, userp, reqp, &connp);
3731 callp = cm_GetRxConn(connp);
3732 code = RXAFS_ReleaseLock(callp, &tfid, &volSync);
3733 rx_PutConnection(callp);
3735 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
3737 code = cm_MapRPCError(code, reqp);
3740 osi_Log1(afsd_logp, "CALL ReleaseLock FAILURE, code 0x%x", code);
3742 osi_Log0(afsd_logp, "CALL ReleaseLock SUCCESS");
3744 lock_ObtainMutex(&scp->mx);
3747 /* so we couldn't release it. Just let the lock be for now */
3751 scp->serverLock = -1;
3754 tfid.Volume = scp->fid.volume;
3755 tfid.Vnode = scp->fid.vnode;
3756 tfid.Unique = scp->fid.unique;
3759 osi_Log3(afsd_logp, "CALL SetLock scp 0x%p from %d to %d", scp, (int) scp->serverLock, LockRead);
3761 lock_ReleaseMutex(&scp->mx);
3765 code = cm_Conn(&cfid, userp, reqp, &connp);
3769 callp = cm_GetRxConn(connp);
3770 code = RXAFS_SetLock(callp, &tfid, LockRead,
3773 rx_PutConnection(callp);
3775 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
3779 osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
3781 osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
3784 lock_ObtainMutex(&scp->mx);
3787 scp->serverLock = LockRead;
3789 if ((scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
3790 (scp->serverLock == -1)) {
3792 cm_LockMarkSCacheLost(scp);
3796 /* failure here has no bearing on the return value of
3800 } else if(scp->serverLock != (-1) && scp->exclusiveLocks == 0 && scp->sharedLocks == 0) {
3803 /* The serverLock should be released entirely */
3805 tfid.Volume = scp->fid.volume;
3806 tfid.Vnode = scp->fid.vnode;
3807 tfid.Unique = scp->fid.unique;
3810 lock_ReleaseMutex(&scp->mx);
3812 osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
3815 code = cm_Conn(&cfid, userp, reqp, &connp);
3819 callp = cm_GetRxConn(connp);
3820 code = RXAFS_ReleaseLock(callp, &tfid, &volSync);
3821 rx_PutConnection(callp);
3823 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
3825 code = cm_MapRPCError(code, reqp);
3828 osi_Log1(afsd_logp, "CALL ReleaseLock FAILURE, code 0x%x", code);
3830 osi_Log0(afsd_logp, "CALL ReleaseLock SUCCESS");
3832 lock_ObtainMutex(&scp->mx);
3835 scp->serverLock = (-1);
3840 osi_Log1(afsd_logp, "cm_UnlockByKey code 0x%x", code);
3841 osi_Log3(afsd_logp, " Leaving scp with exclusives[%d], shared[%d], serverLock[%d]",
3842 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
3847 long cm_Unlock(cm_scache_t *scp,
3848 unsigned char sLockType,
3849 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
3855 int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
3859 cm_file_lock_t *fileLock;
3861 int release_userp = FALSE;
3862 struct rx_connection * callp;
3864 osi_Log4(afsd_logp, "cm_Unlock scp 0x%p type 0x%x offset %d length %d",
3865 scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
3866 osi_Log2(afsd_logp, "... key 0x%x:%x",
3867 (unsigned long) (key >> 32), (unsigned long) (key & 0xffffffff));
3869 lock_ObtainRead(&cm_scacheLock);
3871 for(q = scp->fileLocksH; q; q = osi_QNext(q)) {
3872 fileLock = (cm_file_lock_t *)
3873 ((char *) q - offsetof(cm_file_lock_t, fileq));
3876 if(cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
3877 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
3878 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
3880 fileLock->fid.volume,
3881 fileLock->fid.vnode,
3882 fileLock->fid.unique);
3883 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
3884 fileLock->scp->fid.cell,
3885 fileLock->scp->fid.volume,
3886 fileLock->scp->fid.vnode,
3887 fileLock->scp->fid.unique);
3891 if (!IS_LOCK_DELETED(fileLock) &&
3892 fileLock->key == key &&
3893 fileLock->range.offset == LOffset.QuadPart &&
3894 fileLock->range.length == LLength.QuadPart) {
3900 osi_Log0(afsd_logp, "cm_Unlock lock not found; failure");
3902 lock_ReleaseRead(&cm_scacheLock);
3904 /* The lock didn't exist anyway. *shrug* */
3908 /* discard lock record */
3909 if (scp->fileLocksT == q)
3910 scp->fileLocksT = osi_QPrev(q);
3911 osi_QRemove(&scp->fileLocksH, q);
3913 lock_ReleaseRead(&cm_scacheLock);
3916 * Don't delete it here; let the daemon delete it, to simplify
3917 * the daemon's traversal of the list.
3920 lock_ObtainWrite(&cm_scacheLock);
3922 if(IS_LOCK_ACCEPTED(fileLock)) {
3923 if(fileLock->lockType == LockRead)
3926 scp->exclusiveLocks--;
3929 fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
3930 if (userp != NULL) {
3931 cm_ReleaseUser(fileLock->userp);
3933 userp = fileLock->userp;
3934 release_userp = TRUE;
3936 fileLock->userp = NULL;
3937 cm_ReleaseSCacheNoLock(scp);
3938 fileLock->scp = NULL;
3939 lock_ReleaseWrite(&cm_scacheLock);
3941 if (!SERVERLOCKS_ENABLED(scp)) {
3942 osi_Log0(afsd_logp, " Skipping server locks for scp");
3946 /* Ideally we would go through the rest of the locks to determine
3947 * if one or more locks that were formerly in WAITUNLOCK can now
3948 * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
3949 * scp->sharedLocks accordingly. However, the retrying of locks
3950 * in that manner is done cm_RetryLock() manually.
3953 if (scp->serverLock == LockWrite && scp->exclusiveLocks == 0 && scp->sharedLocks > 0) {
3957 /* The serverLock should be downgraded to LockRead */
3958 osi_Log0(afsd_logp, " DOWNGRADE lock from LockWrite to LockRead");
3960 tfid.Volume = scp->fid.volume;
3961 tfid.Vnode = scp->fid.vnode;
3962 tfid.Unique = scp->fid.unique;
3965 lock_ReleaseMutex(&scp->mx);
3967 osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
3970 code = cm_Conn(&cfid, userp, reqp, &connp);
3974 callp = cm_GetRxConn(connp);
3975 code = RXAFS_ReleaseLock(callp, &tfid, &volSync);
3976 rx_PutConnection(callp);
3978 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
3981 code = cm_MapRPCError(code, reqp);
3984 osi_Log1(afsd_logp, "CALL ReleaseLock FAILURE, code 0x%x", code);
3986 osi_Log0(afsd_logp, "CALL ReleaseLock SUCCESS");
3988 lock_ObtainMutex(&scp->mx);
3991 /* so we couldn't release it. Just let the lock be for now */
3995 scp->serverLock = -1;
3998 tfid.Volume = scp->fid.volume;
3999 tfid.Vnode = scp->fid.vnode;
4000 tfid.Unique = scp->fid.unique;
4003 osi_Log3(afsd_logp, "CALL SetLock scp 0x%p from %d to %d", scp, (int) scp->serverLock, LockRead);
4005 lock_ReleaseMutex(&scp->mx);
4009 code = cm_Conn(&cfid, userp, reqp, &connp);
4013 callp = cm_GetRxConn(connp);
4014 code = RXAFS_SetLock(callp, &tfid, LockRead,
4017 rx_PutConnection(callp);
4019 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
4023 osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
4025 osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
4028 lock_ObtainMutex(&scp->mx);
4031 scp->serverLock = LockRead;
4033 if ((scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4034 (scp->serverLock == -1)) {
4036 cm_LockMarkSCacheLost(scp);
4040 /* failure here has no bearing on the return value of
4044 } else if(scp->serverLock != (-1) && scp->exclusiveLocks == 0 && scp->sharedLocks == 0) {
4047 /* The serverLock should be released entirely */
4049 tfid.Volume = scp->fid.volume;
4050 tfid.Vnode = scp->fid.vnode;
4051 tfid.Unique = scp->fid.unique;
4054 lock_ReleaseMutex(&scp->mx);
4056 osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
4059 code = cm_Conn(&cfid, userp, reqp, &connp);
4063 callp = cm_GetRxConn(connp);
4064 code = RXAFS_ReleaseLock(callp, &tfid, &volSync);
4065 rx_PutConnection(callp);
4067 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
4069 code = cm_MapRPCError(code, reqp);
4072 osi_Log1(afsd_logp, "CALL ReleaseLock FAILURE, code 0x%x", code);
4074 osi_Log0(afsd_logp, "CALL ReleaseLock SUCCESS");
4076 lock_ObtainMutex(&scp->mx);
4079 scp->serverLock = (-1);
4084 cm_ReleaseUser(userp);
4088 osi_Log4(afsd_logp, "cm_Unlock code 0x%x leaving scp with exclusives[%d], shared[%d], serverLock[%d]",
4089 code, scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4094 /* called with scp->mx held */
4095 static void cm_LockMarkSCacheLost(cm_scache_t * scp)
4097 cm_file_lock_t *fileLock;
4100 osi_Log1(afsd_logp, "cm_LockMarkSCacheLost scp 0x%x", scp);
4103 /* With the current code, we can't lose a lock on a RO scp */
4104 osi_assert(!(scp->flags & CM_SCACHEFLAG_RO));
4107 /* cm_scacheLock needed because we are modifying fileLock->flags */
4108 lock_ObtainWrite(&cm_scacheLock);
4110 for(q = scp->fileLocksH; q; q = osi_QNext(q)) {
4112 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4114 if(IS_LOCK_ACTIVE(fileLock)) {
4115 if (fileLock->lockType == LockRead)
4118 scp->exclusiveLocks--;
4120 fileLock->flags |= CM_FILELOCK_FLAG_LOST;
4124 scp->serverLock = -1;
4125 lock_ReleaseWrite(&cm_scacheLock);
4128 /* Called with no relevant locks held */
4129 void cm_CheckLocks()
4131 osi_queue_t *q, *nq;
4132 cm_file_lock_t *fileLock;
4138 struct rx_connection * callp;
4143 lock_ObtainWrite(&cm_scacheLock);
4145 cm_lockRefreshCycle++;
4147 osi_Log1(afsd_logp, "cm_CheckLocks starting lock check cycle %d", cm_lockRefreshCycle);
4149 for(q = cm_allFileLocks; q; q = nq) {
4150 fileLock = (cm_file_lock_t *) q;
4154 if (IS_LOCK_DELETED(fileLock)) {
4156 osi_QRemove(&cm_allFileLocks, q);
4157 cm_PutFileLock(fileLock);
4159 } else if (IS_LOCK_ACTIVE(fileLock) && !IS_LOCK_CLIENTONLY(fileLock)) {
4161 /* Server locks must have been enabled for us to have
4162 received an active non-client-only lock. */
4163 //osi_assert(cm_enableServerLocks);
4165 scp = fileLock->scp;
4166 osi_assert(scp != NULL);
4168 cm_HoldSCacheNoLock(scp);
4171 if(cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
4172 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
4173 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4175 fileLock->fid.volume,
4176 fileLock->fid.vnode,
4177 fileLock->fid.unique);
4178 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4179 fileLock->scp->fid.cell,
4180 fileLock->scp->fid.volume,
4181 fileLock->scp->fid.vnode,
4182 fileLock->scp->fid.unique);
4186 /* Server locks are extended once per scp per refresh
4188 if (scp->lastRefreshCycle != cm_lockRefreshCycle) {
4190 int scp_done = FALSE;
4192 osi_Log1(afsd_logp, "cm_CheckLocks Updating scp 0x%x", scp);
4194 lock_ReleaseWrite(&cm_scacheLock);
4195 lock_ObtainMutex(&scp->mx);
4197 /* did the lock change while we weren't holding the lock? */
4198 if (!IS_LOCK_ACTIVE(fileLock))
4199 goto post_syncopdone;
4201 code = cm_SyncOp(scp, NULL, fileLock->userp, &req, 0,
4202 CM_SCACHESYNC_NEEDCALLBACK
4203 | CM_SCACHESYNC_GETSTATUS
4204 | CM_SCACHESYNC_LOCK);
4207 osi_Log1(smb_logp, "cm_CheckLocks SyncOp failure code 0x%x", code);
4208 goto post_syncopdone;
4211 /* cm_SyncOp releases scp->mx during which the lock
4212 may get released. */
4213 if (!IS_LOCK_ACTIVE(fileLock))
4214 goto pre_syncopdone;
4216 if(scp->serverLock != -1) {
4220 tfid.Volume = scp->fid.volume;
4221 tfid.Vnode = scp->fid.vnode;
4222 tfid.Unique = scp->fid.unique;
4224 userp = fileLock->userp;
4226 osi_Log3(afsd_logp, "CALL ExtendLock lock 0x%p for scp=0x%p with lock %d",
4229 (int) scp->serverLock);
4231 lock_ReleaseMutex(&scp->mx);
4234 code = cm_Conn(&cfid, userp,
4239 callp = cm_GetRxConn(connp);
4240 code = RXAFS_ExtendLock(callp, &tfid,
4242 rx_PutConnection(callp);
4244 osi_Log1(afsd_logp, " ExtendLock returns %d", code);
4246 } while (cm_Analyze(connp, userp, &req,
4247 &cfid, &volSync, NULL, NULL,
4250 code = cm_MapRPCError(code, &req);
4252 lock_ObtainMutex(&scp->mx);
4255 osi_Log1(afsd_logp, "CALL ExtendLock FAILURE, code 0x%x", code);
4256 if (code == EINVAL || code == CM_ERROR_INVAL)
4257 cm_LockMarkSCacheLost(scp);
4259 osi_Log0(afsd_logp, "CALL ExtendLock SUCCESS");
4262 /* interestingly, we have found an active lock
4263 belonging to an scache that has no
4265 cm_LockMarkSCacheLost(scp);
4272 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
4275 lock_ReleaseMutex(&scp->mx);
4277 lock_ObtainWrite(&cm_scacheLock);
4280 fileLock->lastUpdate = time(NULL);
4284 scp->lastRefreshCycle = cm_lockRefreshCycle;
4287 /* we have already refreshed the locks on this scp */
4288 fileLock->lastUpdate = time(NULL);
4291 cm_ReleaseSCacheNoLock(scp);
4293 } else if (IS_LOCK_ACTIVE(fileLock) && IS_LOCK_CLIENTONLY(fileLock)) {
4294 /* TODO: Check callbacks */
4298 lock_ReleaseWrite(&cm_scacheLock);
4299 osi_Log1(afsd_logp, "cm_CheckLocks completes lock check cycle %d", cm_lockRefreshCycle);
4302 /* NOT called with scp->mx held. */
4303 long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
4306 cm_scache_t *scp = NULL;
4310 cm_file_lock_t *fileLock;
4313 struct rx_connection * callp;
4318 if (client_is_dead) {
4319 code = CM_ERROR_TIMEDOUT;
4323 lock_ObtainRead(&cm_scacheLock);
4325 osi_Log2(afsd_logp, "cm_RetryLock checking lock %p (scp=%p)", oldFileLock, oldFileLock->scp);
4326 osi_Log4(afsd_logp, " offset(%x:%x) length(%x:%x)",
4327 (unsigned)(oldFileLock->range.offset >> 32),
4328 (unsigned)(oldFileLock->range.offset & 0xffffffff),
4329 (unsigned)(oldFileLock->range.length >> 32),
4330 (unsigned)(oldFileLock->range.length & 0xffffffff));
4331 osi_Log3(afsd_logp, " key(%x:%x) flags=%x",
4332 (unsigned)(oldFileLock->key >> 32),
4333 (unsigned)(oldFileLock->key & 0xffffffff),
4334 (unsigned)(oldFileLock->flags));
4336 /* if the lock has already been granted, then we have nothing to do */
4337 if(IS_LOCK_ACTIVE(oldFileLock)) {
4338 lock_ReleaseRead(&cm_scacheLock);
4339 osi_Log0(afsd_logp, "cm_RetryLock lock already granted");
4343 /* we can't do anything with lost or deleted locks at the moment. */
4344 if(IS_LOCK_LOST(oldFileLock) || IS_LOCK_DELETED(oldFileLock)) {
4345 code = CM_ERROR_BADFD;
4346 osi_Log0(afsd_logp, "cm_RetryLock lock is lost or deleted");
4347 lock_ReleaseRead(&cm_scacheLock);
4351 scp = oldFileLock->scp;
4353 osi_assert(scp != NULL);
4355 lock_ReleaseRead(&cm_scacheLock);
4356 lock_ObtainMutex(&scp->mx);
4357 lock_ObtainWrite(&cm_scacheLock);
4359 /* Check if we already have a sufficient server lock to allow this
4360 lock to go through */
4361 if(IS_LOCK_WAITLOCK(oldFileLock) &&
4362 (!SERVERLOCKS_ENABLED(scp) ||
4363 scp->serverLock == oldFileLock->lockType ||
4364 scp->serverLock == LockWrite)) {
4366 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
4368 if (SERVERLOCKS_ENABLED(scp)) {
4369 osi_Log1(afsd_logp, "cm_RetryLock Server lock (%d) is sufficient for lock. Granting",
4370 (int) scp->serverLock);
4372 osi_Log0(afsd_logp, "cm_RetryLock skipping server lock for scp");
4375 lock_ReleaseWrite(&cm_scacheLock);
4376 lock_ReleaseMutex(&scp->mx);
4381 if(IS_LOCK_WAITUNLOCK(oldFileLock)) {
4383 /* check if the conflicting locks have dissappeared already */
4385 for(q = scp->fileLocksH; q; q = osi_QNext(q)) {
4387 fileLock = (cm_file_lock_t *)
4388 ((char *) q - offsetof(cm_file_lock_t, fileq));
4390 if(IS_LOCK_LOST(fileLock)) {
4391 if (fileLock->key == oldFileLock->key) {
4392 code = CM_ERROR_BADFD;
4393 oldFileLock->flags |= CM_FILELOCK_FLAG_LOST;
4394 osi_Log1(afsd_logp, " found lost lock %p for same key. Marking lock as lost",
4397 } else if (fileLock->lockType == LockWrite &&
4398 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
4399 osi_Log1(afsd_logp, " found conflicting LOST lock %p", fileLock);
4400 code = CM_ERROR_WOULDBLOCK;
4405 if(IS_LOCK_ACCEPTED(fileLock) &&
4406 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
4408 if(oldFileLock->lockType != LockRead ||
4409 fileLock->lockType != LockRead) {
4411 osi_Log1(afsd_logp, " found conflicting lock %p", fileLock);
4412 code = CM_ERROR_WOULDBLOCK;
4420 lock_ReleaseWrite(&cm_scacheLock);
4421 lock_ReleaseMutex(&scp->mx);
4426 /* when we get here, the lock is either a WAITUNLOCK or WAITLOCK.
4427 If it is WAITUNLOCK, then we didn't find any conflicting lock
4428 but we haven't verfied whether the serverLock is sufficient to
4429 assert it. If it is WAITLOCK, then the serverLock is
4430 insufficient to assert it. Eitherway, we are ready to accept
4431 the lock as either ACTIVE or WAITLOCK depending on the
4434 /* First, promote the WAITUNLOCK to a WAITLOCK */
4435 if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
4436 if (oldFileLock->lockType == LockRead)
4439 scp->exclusiveLocks++;
4441 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITUNLOCK;
4442 oldFileLock->flags |= CM_FILELOCK_FLAG_WAITLOCK;
4445 if (!SERVERLOCKS_ENABLED(scp) ||
4446 scp->serverLock == oldFileLock->lockType ||
4447 (oldFileLock->lockType == LockRead &&
4448 scp->serverLock == LockWrite)) {
4450 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
4452 lock_ReleaseWrite(&cm_scacheLock);
4453 lock_ReleaseMutex(&scp->mx);
4461 code = cm_SyncOp(scp, NULL, oldFileLock->userp, &req, 0,
4462 CM_SCACHESYNC_NEEDCALLBACK
4463 | CM_SCACHESYNC_GETSTATUS
4464 | CM_SCACHESYNC_LOCK);
4466 osi_Log1(smb_logp, "cm_RetryLock SyncOp failure code 0x%x", code);
4467 lock_ReleaseWrite(&cm_scacheLock);
4468 goto post_syncopdone;
4471 if(!IS_LOCK_WAITLOCK(oldFileLock))
4472 goto pre_syncopdone;
4474 tfid.Volume = scp->fid.volume;
4475 tfid.Vnode = scp->fid.vnode;
4476 tfid.Unique = scp->fid.unique;
4478 userp = oldFileLock->userp;
4480 #ifndef AGGRESSIVE_LOCKS
4481 newLock = oldFileLock->lockType;
4483 newLock = LockWrite;
4486 osi_Log1(afsd_logp, "CALL SetLock lock 0x%p", oldFileLock);
4488 lock_ReleaseWrite(&cm_scacheLock);
4489 lock_ReleaseMutex(&scp->mx);
4492 code = cm_Conn(&cfid, userp, &req, &connp);
4496 callp = cm_GetRxConn(connp);
4497 code = RXAFS_SetLock(callp, &tfid, newLock,
4499 rx_PutConnection(callp);
4501 } while (cm_Analyze(connp, userp, &req,
4504 code = cm_MapRPCError(code, &req);
4507 osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
4509 osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
4512 lock_ObtainMutex(&scp->mx);
4514 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
4520 if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
4521 if (scp->fileLocksT == &oldFileLock->fileq)
4522 scp->fileLocksT = osi_QPrev(&oldFileLock->fileq);
4523 osi_QRemove(&scp->fileLocksH, &oldFileLock->fileq);
4524 } else if (code == 0 && IS_LOCK_WAITLOCK(oldFileLock)) {
4525 scp->serverLock = newLock;
4527 lock_ReleaseMutex(&scp->mx);
4530 lock_ObtainWrite(&cm_scacheLock);
4532 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
4533 } else if (code != CM_ERROR_WOULDBLOCK) {
4534 oldFileLock->flags |= CM_FILELOCK_FLAG_DELETED;
4535 cm_ReleaseUser(oldFileLock->userp);
4536 oldFileLock->userp = NULL;
4537 if (oldFileLock->scp) {
4538 cm_ReleaseSCacheNoLock(oldFileLock->scp);
4539 oldFileLock->scp = NULL;
4542 lock_ReleaseWrite(&cm_scacheLock);
4547 cm_key_t cm_GenerateKey(unsigned int session_id, unsigned long process_id, unsigned int file_id)
4549 return (((cm_key_t) process_id) << 32) |
4550 (((cm_key_t) session_id) << 16) |
4551 (((cm_key_t) file_id));
4554 static int cm_KeyEquals(cm_key_t k1, cm_key_t k2, int flags)
4556 if (flags & CM_UNLOCK_BY_FID) {
4557 return ((k1 & 0xffffffff) == (k2 & 0xffffffff));
4563 void cm_ReleaseAllLocks(void)
4569 cm_file_lock_t *fileLock;
4572 for (i = 0; i < cm_data.hashTableSize; i++)
4574 for ( scp = cm_data.hashTablep[i]; scp; scp = scp->nextp ) {
4575 while (scp->fileLocksH != NULL) {
4576 lock_ObtainMutex(&scp->mx);
4577 lock_ObtainWrite(&cm_scacheLock);
4578 if (!scp->fileLocksH) {
4579 lock_ReleaseWrite(&cm_scacheLock);
4580 lock_ReleaseMutex(&scp->mx);
4583 fileLock = (cm_file_lock_t *)((char *) scp->fileLocksH - offsetof(cm_file_lock_t, fileq));
4584 userp = fileLock->userp;
4586 key = fileLock->key;
4587 cm_HoldSCacheNoLock(scp);
4588 lock_ReleaseWrite(&cm_scacheLock);
4589 cm_UnlockByKey(scp, key, 0, userp, &req);
4590 cm_ReleaseSCache(scp);
4591 cm_ReleaseUser(userp);
4592 lock_ReleaseMutex(&scp->mx);