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 extern void afsi_log(char *pattern, ...);
30 int cm_enableServerLocks = 1;
33 * Case-folding array. This was constructed by inspecting of SMBtrace output.
34 * I do not know anything more about it.
36 unsigned char cm_foldUpper[256] = {
37 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
38 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
39 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
40 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
41 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
42 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
43 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
44 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
45 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
46 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
47 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
48 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
49 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
50 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
51 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
52 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
53 0x80, 0x9a, 0x90, 0x41, 0x8e, 0x41, 0x8f, 0x80,
54 0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x8e, 0x8f,
55 0x90, 0x92, 0x92, 0x4f, 0x99, 0x4f, 0x55, 0x55,
56 0x59, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
57 0x41, 0x49, 0x4f, 0x55, 0xa5, 0xa5, 0x56, 0xa7,
58 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
59 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
60 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
61 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
62 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
63 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
64 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
65 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
66 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
67 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
68 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
72 * Case-insensitive string comparison. We used to use stricmp, but it doesn't
73 * know about 8-bit characters (e.g. 129 is lowercase u-umlaut, 154 is
74 * upper-case u-umlaut).
76 int cm_stricmp(const char *str1, const char *str2)
88 c1 = (char) cm_foldUpper[(unsigned char)(*str1++)];
89 c2 = (char) cm_foldUpper[(unsigned char)(*str2++)];
97 /* characters that are legal in an 8.3 name */
99 * We used to have 1's for all characters from 128 to 254. But
100 * the NT client behaves better if we create an 8.3 name for any
101 * name that has a character with the high bit on, and if we
102 * delete those characters from 8.3 names. In particular, see
103 * Sybase defect 10859.
105 char cm_LegalChars[256] = {
106 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
107 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
108 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0,
109 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
110 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
111 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1,
112 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
113 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1,
114 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
115 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
116 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
117 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
118 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
119 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
120 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
121 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
124 /* return true iff component is a valid 8.3 name */
125 int cm_Is8Dot3(char *namep)
132 * can't have a leading dot;
133 * special case for . and ..
135 if (namep[0] == '.') {
138 if (namep[1] == '.' && namep[2] == 0)
142 while (tc = *namep++) {
144 /* saw another dot */
145 if (sawDot) return 0; /* second dot */
150 if (cm_LegalChars[tc] == 0)
153 if (!sawDot && charCount > 8)
154 /* more than 8 chars in name */
156 if (sawDot && charCount > 3)
157 /* more than 3 chars in extension */
164 * Number unparsing map for generating 8.3 names;
165 * The version taken from DFS was on drugs.
166 * You can't include '&' and '@' in a file name.
168 char cm_8Dot3Mapping[42] =
169 {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
170 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K',
171 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U',
172 'V', 'W', 'X', 'Y', 'Z', '_', '-', '$', '#', '!', '+', '='
174 int cm_8Dot3MapSize = sizeof(cm_8Dot3Mapping);
176 void cm_Gen8Dot3NameInt(const char * longname, cm_dirFid_t * pfid,
177 char *shortName, char **shortNameEndp)
181 int vnode = ntohl(pfid->vnode);
183 int validExtension = 0;
187 /* Unparse the file's vnode number to get a "uniquifier" */
189 number[nsize] = cm_8Dot3Mapping[vnode % cm_8Dot3MapSize];
191 vnode /= cm_8Dot3MapSize;
195 * Look for valid extension. There has to be a dot, and
196 * at least one of the characters following has to be legal.
198 lastDot = strrchr(longname, '.');
200 temp = lastDot; temp++;
202 if (cm_LegalChars[tc])
208 /* Copy name characters */
209 for (i = 0, name = longname;
210 i < (7 - nsize) && name != lastDot; ) {
215 if (!cm_LegalChars[tc])
218 *shortName++ = toupper(tc);
224 /* Copy uniquifier characters */
225 memcpy(shortName, number, nsize);
228 if (validExtension) {
229 /* Copy extension characters */
230 *shortName++ = *lastDot++; /* copy dot */
231 for (i = 0, tc = *lastDot++;
234 if (cm_LegalChars[tc]) {
236 *shortName++ = toupper(tc);
245 *shortNameEndp = shortName;
248 /* return success if we can open this file in this mode */
249 long cm_CheckOpen(cm_scache_t *scp, int openMode, int trunc, cm_user_t *userp,
257 rights |= PRSFS_READ;
258 if (openMode == 1 || openMode == 2 || trunc)
259 rights |= PRSFS_WRITE;
261 lock_ObtainMutex(&scp->mx);
263 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
264 CM_SCACHESYNC_GETSTATUS
265 | CM_SCACHESYNC_NEEDCALLBACK
266 | CM_SCACHESYNC_LOCK);
269 ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
270 scp->fileType == CM_SCACHETYPE_FILE) {
273 unsigned int sLockType;
274 LARGE_INTEGER LOffset, LLength;
276 /* Check if there's some sort of lock on the file at the
279 key = cm_GenerateKey(CM_SESSION_CMINT,0,0);
281 if (rights & PRSFS_WRITE)
284 sLockType = LOCKING_ANDX_SHARED_LOCK;
286 LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
287 LOffset.LowPart = CM_FLSHARE_OFFSET_LOW;
288 LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
289 LLength.LowPart = CM_FLSHARE_LENGTH_LOW;
291 code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
294 cm_Unlock(scp, sLockType, LOffset, LLength, key, userp, reqp);
296 /* In this case, we allow the file open to go through even
297 though we can't enforce mandatory locking on the
299 if (code == CM_ERROR_NOACCESS &&
300 !(rights & PRSFS_WRITE))
304 case CM_ERROR_ALLOFFLINE:
305 case CM_ERROR_ALLDOWN:
306 case CM_ERROR_ALLBUSY:
307 case CM_ERROR_TIMEDOUT:
309 case CM_ERROR_WOULDBLOCK:
312 code = CM_ERROR_SHARING_VIOLATION;
317 } else if (code != 0) {
321 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
325 lock_ReleaseMutex(&scp->mx);
330 /* return success if we can open this file in this mode */
331 long cm_CheckNTOpen(cm_scache_t *scp, unsigned int desiredAccess,
332 unsigned int createDisp, cm_user_t *userp, cm_req_t *reqp,
333 cm_lock_data_t **ldpp)
338 osi_assert(ldpp != NULL);
341 /* Always allow delete; the RPC will tell us if it's OK */
342 if (desiredAccess == DELETE)
347 if (desiredAccess & AFS_ACCESS_READ)
348 rights |= (scp->fileType == CM_SCACHETYPE_DIRECTORY ? PRSFS_LOOKUP : PRSFS_READ);
350 /* We used to require PRSFS_WRITE if createDisp was 4
351 (OPEN_ALWAYS) even if AFS_ACCESS_WRITE was not requested.
352 However, we don't need to do that since the existence of the
353 scp implies that we don't need to create it. */
354 if (desiredAccess & AFS_ACCESS_WRITE)
355 rights |= PRSFS_WRITE;
357 lock_ObtainMutex(&scp->mx);
359 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
360 CM_SCACHESYNC_GETSTATUS
361 | CM_SCACHESYNC_NEEDCALLBACK
362 | CM_SCACHESYNC_LOCK);
365 * If the open will fail because the volume is readonly, then we will
366 * return an access denied error instead. This is to help brain-dead
367 * apps run correctly on replicated volumes.
368 * See defect 10007 for more information.
370 if (code == CM_ERROR_READONLY)
371 code = CM_ERROR_NOACCESS;
374 ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
375 scp->fileType == CM_SCACHETYPE_FILE) {
377 unsigned int sLockType;
378 LARGE_INTEGER LOffset, LLength;
380 /* Check if there's some sort of lock on the file at the
383 key = cm_GenerateKey(CM_SESSION_CMINT,0,0);
384 if (rights & PRSFS_WRITE)
387 sLockType = LOCKING_ANDX_SHARED_LOCK;
389 /* single byte lock at offset 0x0100 0000 0000 0000 */
390 LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
391 LOffset.LowPart = CM_FLSHARE_OFFSET_LOW;
392 LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
393 LLength.LowPart = CM_FLSHARE_LENGTH_LOW;
395 code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
398 (*ldpp) = (cm_lock_data_t *)malloc(sizeof(cm_lock_data_t));
405 (*ldpp)->sLockType = sLockType;
406 (*ldpp)->LOffset.HighPart = LOffset.HighPart;
407 (*ldpp)->LOffset.LowPart = LOffset.LowPart;
408 (*ldpp)->LLength.HighPart = LLength.HighPart;
409 (*ldpp)->LLength.LowPart = LLength.LowPart;
411 /* In this case, we allow the file open to go through even
412 though we can't enforce mandatory locking on the
414 if (code == CM_ERROR_NOACCESS &&
415 !(rights & PRSFS_WRITE))
419 case CM_ERROR_ALLOFFLINE:
420 case CM_ERROR_ALLDOWN:
421 case CM_ERROR_ALLBUSY:
422 case CM_ERROR_TIMEDOUT:
424 case CM_ERROR_WOULDBLOCK:
427 code = CM_ERROR_SHARING_VIOLATION;
431 } else if (code != 0) {
435 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
438 lock_ReleaseMutex(&scp->mx);
443 extern long cm_CheckNTOpenDone(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp,
444 cm_lock_data_t ** ldpp)
447 lock_ObtainMutex(&scp->mx);
448 cm_Unlock(scp, (*ldpp)->sLockType, (*ldpp)->LOffset, (*ldpp)->LLength,
449 (*ldpp)->key, userp, reqp);
450 lock_ReleaseMutex(&scp->mx);
457 * When CAP_NT_SMBS has been negotiated, deletion (of files or directories) is
458 * done in three steps:
459 * (1) open for deletion (NT_CREATE_AND_X)
460 * (2) set for deletion on close (NT_TRANSACTION2, SET_FILE_INFO)
462 * We must not do the RPC until step 3. But if we are going to return an error
463 * code (e.g. directory not empty), we must return it by step 2, otherwise most
464 * clients will not notice it. So we do a preliminary check. For deleting
465 * files, this is almost free, since we have already done the RPC to get the
466 * parent directory's status bits. But for deleting directories, we must do an
467 * additional RPC to get the directory's data to check if it is empty. Sigh.
469 long cm_CheckNTDelete(cm_scache_t *dscp, cm_scache_t *scp, cm_user_t *userp,
476 unsigned short *hashTable;
478 int BeyondPage = 0, HaveDot = 0, HaveDotDot = 0;
480 /* First check permissions */
481 lock_ObtainMutex(&scp->mx);
482 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_DELETE,
483 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
484 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
485 lock_ReleaseMutex(&scp->mx);
489 /* If deleting directory, must be empty */
491 if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
494 thyper.HighPart = 0; thyper.LowPart = 0;
495 lock_ObtainRead(&scp->bufCreateLock);
496 code = buf_Get(scp, &thyper, &bufferp);
497 lock_ReleaseRead(&scp->bufCreateLock);
501 lock_ObtainMutex(&bufferp->mx);
502 lock_ObtainMutex(&scp->mx);
504 code = cm_SyncOp(scp, bufferp, userp, reqp, 0,
505 CM_SCACHESYNC_NEEDCALLBACK
507 | CM_SCACHESYNC_BUFLOCKED);
511 if (cm_HaveBuffer(scp, bufferp, 1))
514 /* otherwise, load the buffer and try again */
515 lock_ReleaseMutex(&bufferp->mx);
516 code = cm_GetBuffer(scp, bufferp, NULL, userp, reqp);
517 lock_ReleaseMutex(&scp->mx);
518 lock_ObtainMutex(&bufferp->mx);
519 lock_ObtainMutex(&scp->mx);
520 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
525 /* We try to determine emptiness without looking beyond the first page,
526 * and without assuming "." and ".." are present and are on the first
527 * page (though these assumptions might, after all, be reasonable).
529 hashTable = (unsigned short *)(bufferp->datap + (32 * 5));
530 for (i=0; i<128; i++) {
531 idx = ntohs(hashTable[i]);
537 dep = (cm_dirEntry_t *)(bufferp->datap + (32 * idx));
538 if (strcmp(dep->name, ".") == 0)
540 else if (strcmp(dep->name, "..") == 0)
543 code = CM_ERROR_NOTEMPTY;
546 idx = ntohs(dep->next);
549 if (BeyondPage && HaveDot && HaveDotDot)
550 code = CM_ERROR_NOTEMPTY;
554 lock_ReleaseMutex(&bufferp->mx);
555 buf_Release(bufferp);
556 lock_ReleaseMutex(&scp->mx);
561 * Iterate through all entries in a directory.
562 * When the function funcp is called, the buffer is locked but the
563 * directory vnode is not.
565 * If the retscp parameter is not NULL, the parmp must be a
566 * cm_lookupSearch_t object.
568 long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
569 osi_hyper_t *startOffsetp, cm_user_t *userp, cm_req_t *reqp,
570 cm_scache_t **retscp)
577 osi_hyper_t dirLength;
578 osi_hyper_t bufferOffset;
579 osi_hyper_t curOffset;
583 cm_pageHeader_t *pageHeaderp;
585 long nextEntryCookie;
586 int numDirChunks; /* # of 32 byte dir chunks in this entry */
588 /* get the directory size */
589 lock_ObtainMutex(&scp->mx);
590 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_LOOKUP,
591 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
592 lock_ReleaseMutex(&scp->mx);
596 if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
597 return CM_ERROR_NOTDIR;
599 if (retscp) /* if this is a lookup call */
601 cm_lookupSearch_t* sp = parmp;
604 #ifdef AFS_FREELANCE_CLIENT
605 /* Freelance entries never end up in the DNLC because they
606 * do not have an associated cm_server_t
608 !(cm_freelanceEnabled &&
609 sp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
610 sp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
611 #else /* !AFS_FREELANCE_CLIENT */
616 int casefold = sp->caseFold;
617 sp->caseFold = 0; /* we have a strong preference for exact matches */
618 if ( *retscp = cm_dnlcLookup(scp, sp)) /* dnlc hit */
620 sp->caseFold = casefold;
623 sp->caseFold = casefold;
625 /* see if we can find it using the directory hash tables.
626 we can only do exact matches, since the hash is case
636 code = cm_BeginDirOp(scp, userp, reqp, CM_DIRLOCK_READ, &dirop);
640 code = cm_BPlusDirLookup(&dirop, sp->searchNamep, &sp->fid);
645 code = cm_DirLookup(&dirop, sp->searchNamep, &sp->fid);
653 sp->ExactFound = TRUE;
654 *retscp = NULL; /* force caller to call cm_GetSCache() */
659 if (sp->caseFold && code == CM_ERROR_INEXACT_MATCH) {
662 sp->ExactFound = FALSE;
663 *retscp = NULL; /* force caller to call cm_GetSCache() */
667 return CM_ERROR_BPLUS_NOMATCH;
675 * XXX We only get the length once. It might change when we drop the
678 dirLength = scp->length;
681 bufferOffset.LowPart = bufferOffset.HighPart = 0;
683 curOffset = *startOffsetp;
685 curOffset.HighPart = 0;
686 curOffset.LowPart = 0;
690 /* make sure that curOffset.LowPart doesn't point to the first
691 * 32 bytes in the 2nd through last dir page, and that it
692 * doesn't point at the first 13 32-byte chunks in the first
693 * dir page, since those are dir and page headers, and don't
694 * contain useful information.
696 temp = curOffset.LowPart & (2048-1);
697 if (curOffset.HighPart == 0 && curOffset.LowPart < 2048) {
698 /* we're in the first page */
699 if (temp < 13*32) temp = 13*32;
702 /* we're in a later dir page */
703 if (temp < 32) temp = 32;
706 /* make sure the low order 5 bits are zero */
709 /* now put temp bits back ito curOffset.LowPart */
710 curOffset.LowPart &= ~(2048-1);
711 curOffset.LowPart |= temp;
713 /* check if we've passed the dir's EOF */
714 if (LargeIntegerGreaterThanOrEqualTo(curOffset, dirLength))
717 /* see if we can use the bufferp we have now; compute in which
718 * page the current offset would be, and check whether that's
719 * the offset of the buffer we have. If not, get the buffer.
721 thyper.HighPart = curOffset.HighPart;
722 thyper.LowPart = curOffset.LowPart & ~(cm_data.buf_blockSize-1);
723 if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
726 lock_ReleaseMutex(&bufferp->mx);
727 buf_Release(bufferp);
731 lock_ObtainRead(&scp->bufCreateLock);
732 code = buf_Get(scp, &thyper, &bufferp);
733 lock_ReleaseRead(&scp->bufCreateLock);
735 /* if buf_Get() fails we do not have a buffer object to lock */
741 /* for the IFS version, we bulkstat the dirents because this
742 routine is used in place of smb_ReceiveCoreSearchDir. our
743 other option is to modify smb_ReceiveCoreSearchDir itself,
744 but this seems to be the proper use for cm_ApplyDir. */
745 lock_ObtainMutex(&scp->mx);
746 if ((scp->flags & CM_SCACHEFLAG_BULKSTATTING) == 0
747 && (scp->bulkStatProgress.QuadPart <= thyper.QuadPart))
749 scp->flags |= CM_SCACHEFLAG_BULKSTATTING;
750 code = cm_TryBulkStat(scp, &thyper, userp, reqp);
751 scp->flags &= ~CM_SCACHEFLAG_BULKSTATTING;
752 scp->bulkStatProgress = thyper;
754 lock_ReleaseMutex(&scp->mx);
757 lock_ObtainMutex(&bufferp->mx);
758 bufferOffset = thyper;
760 /* now get the data in the cache */
762 lock_ObtainMutex(&scp->mx);
763 code = cm_SyncOp(scp, bufferp, userp, reqp,
765 CM_SCACHESYNC_NEEDCALLBACK
767 | CM_SCACHESYNC_BUFLOCKED);
769 lock_ReleaseMutex(&scp->mx);
772 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
774 if (cm_HaveBuffer(scp, bufferp, 1)) {
775 lock_ReleaseMutex(&scp->mx);
779 /* otherwise, load the buffer and try again */
780 lock_ReleaseMutex(&bufferp->mx);
781 code = cm_GetBuffer(scp, bufferp, NULL, userp,
783 lock_ReleaseMutex(&scp->mx);
784 lock_ObtainMutex(&bufferp->mx);
789 lock_ReleaseMutex(&bufferp->mx);
790 buf_Release(bufferp);
794 } /* if (wrong buffer) ... */
796 /* now we have the buffer containing the entry we're interested
797 * in; copy it out if it represents a non-deleted entry.
799 entryInDir = curOffset.LowPart & (2048-1);
800 entryInBuffer = curOffset.LowPart & (cm_data.buf_blockSize - 1);
802 /* page header will help tell us which entries are free. Page
803 * header can change more often than once per buffer, since
804 * AFS 3 dir page size may be less than (but not more than) a
805 * buffer package buffer.
807 /* only look intra-buffer */
808 temp = curOffset.LowPart & (cm_data.buf_blockSize - 1);
809 temp &= ~(2048 - 1); /* turn off intra-page bits */
810 pageHeaderp = (cm_pageHeader_t *) (bufferp->datap + temp);
812 /* now determine which entry we're looking at in the page. If
813 * it is free (there's a free bitmap at the start of the dir),
814 * we should skip these 32 bytes.
816 slotInPage = (entryInDir & 0x7e0) >> 5;
817 if (!(pageHeaderp->freeBitmap[slotInPage>>3]
818 & (1 << (slotInPage & 0x7)))) {
819 /* this entry is free */
820 numDirChunks = 1; /* only skip this guy */
824 tp = bufferp->datap + entryInBuffer;
825 dep = (cm_dirEntry_t *) tp; /* now points to AFS3 dir entry */
827 /* while we're here, compute the next entry's location, too,
828 * since we'll need it when writing out the cookie into the
829 * dir listing stream.
831 numDirChunks = cm_NameEntries(dep->name, NULL);
833 /* compute the offset of the cookie representing the next entry */
834 nextEntryCookie = curOffset.LowPart
835 + (CM_DIR_CHUNKSIZE * numDirChunks);
837 if (dep->fid.vnode != 0) {
838 /* this is one of the entries to use: it is not deleted */
839 code = (*funcp)(scp, dep, parmp, &curOffset);
842 } /* if we're including this name */
845 /* and adjust curOffset to be where the new cookie is */
847 thyper.LowPart = CM_DIR_CHUNKSIZE * numDirChunks;
848 curOffset = LargeIntegerAdd(thyper, curOffset);
849 } /* while copying data for dir listing */
851 /* release the mutex */
853 lock_ReleaseMutex(&bufferp->mx);
854 buf_Release(bufferp);
859 int cm_NoneUpper(char *s)
863 if (c >= 'A' && c <= 'Z')
868 int cm_NoneLower(char *s)
872 if (c >= 'a' && c <= 'z')
877 long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
880 cm_lookupSearch_t *sp;
885 sp = (cm_lookupSearch_t *) rockp;
887 matchName = dep->name;
889 match = cm_stricmp(matchName, sp->searchNamep);
891 match = strcmp(matchName, sp->searchNamep);
895 && !cm_Is8Dot3(dep->name)) {
896 matchName = shortName;
897 cm_Gen8Dot3Name(dep, shortName, NULL);
899 match = cm_stricmp(matchName, sp->searchNamep);
901 match = strcmp(matchName, sp->searchNamep);
911 if (!sp->caseFold || matchName == shortName) {
912 sp->fid.vnode = ntohl(dep->fid.vnode);
913 sp->fid.unique = ntohl(dep->fid.unique);
914 return CM_ERROR_STOPNOW;
918 * If we get here, we are doing a case-insensitive search, and we
919 * have found a match. Now we determine what kind of match it is:
920 * exact, lower-case, upper-case, or none of the above. This is done
921 * in order to choose among matches, if there are more than one.
924 /* Exact matches are the best. */
925 match = strcmp(matchName, sp->searchNamep);
928 sp->fid.vnode = ntohl(dep->fid.vnode);
929 sp->fid.unique = ntohl(dep->fid.unique);
930 return CM_ERROR_STOPNOW;
933 /* Lower-case matches are next. */
936 if (cm_NoneUpper(matchName)) {
941 /* Upper-case matches are next. */
944 if (cm_NoneLower(matchName)) {
949 /* General matches are last. */
955 sp->fid.vnode = ntohl(dep->fid.vnode);
956 sp->fid.unique = ntohl(dep->fid.unique);
960 /* read the contents of a mount point into the appropriate string.
961 * called with locked scp, and returns with locked scp.
963 long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
970 if (scp->mountPointStringp[0])
973 /* otherwise, we have to read it in */
974 lock_ReleaseMutex(&scp->mx);
976 lock_ObtainRead(&scp->bufCreateLock);
977 thyper.LowPart = thyper.HighPart = 0;
978 code = buf_Get(scp, &thyper, &bufp);
979 lock_ReleaseRead(&scp->bufCreateLock);
981 lock_ObtainMutex(&scp->mx);
986 code = cm_SyncOp(scp, bufp, userp, reqp, 0,
987 CM_SCACHESYNC_READ | CM_SCACHESYNC_NEEDCALLBACK);
991 cm_SyncOpDone(scp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
993 if (cm_HaveBuffer(scp, bufp, 0))
996 /* otherwise load buffer */
997 code = cm_GetBuffer(scp, bufp, NULL, userp, reqp);
1001 /* locked, has callback, has valid data in buffer */
1002 if ((tlen = scp->length.LowPart) > MOUNTPOINTLEN - 1)
1003 return CM_ERROR_TOOBIG;
1005 code = CM_ERROR_INVAL;
1009 /* someone else did the work while we were out */
1010 if (scp->mountPointStringp[0]) {
1015 /* otherwise, copy out the link */
1016 memcpy(scp->mountPointStringp, bufp->datap, tlen);
1018 /* now make it null-terminated. Note that the original contents of a
1019 * link that is a mount point is "#volname." where "." is there just to
1020 * be turned into a null. That is, we can trash the last char of the
1021 * link without damaging the vol name. This is a stupid convention,
1022 * but that's the protocol.
1024 scp->mountPointStringp[tlen-1] = 0;
1034 /* called with a locked scp and chases the mount point, yielding outScpp.
1035 * scp remains locked, just for simplicity of describing the interface.
1037 long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
1038 cm_req_t *reqp, cm_scache_t **outScpp)
1046 cm_volume_t *volp = NULL;
1053 if (scp->mountRootFid.cell != 0 && scp->mountRootGen >= cm_data.mountRootGen) {
1054 tfid = scp->mountRootFid;
1055 lock_ReleaseMutex(&scp->mx);
1056 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
1057 lock_ObtainMutex(&scp->mx);
1061 /* parse the volume name */
1062 mpNamep = scp->mountPointStringp;
1064 return CM_ERROR_NOSUCHPATH;
1065 tlen = (int)strlen(scp->mountPointStringp);
1066 mtType = *scp->mountPointStringp;
1067 cellNamep = malloc(tlen);
1068 volNamep = malloc(tlen);
1070 cp = strrchr(mpNamep, ':');
1072 /* cellular mount point */
1073 memset(cellNamep, 0, tlen);
1074 strncpy(cellNamep, mpNamep+1, cp - mpNamep - 1);
1075 strcpy(volNamep, cp+1);
1076 /* now look up the cell */
1077 cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE);
1081 strcpy(volNamep, mpNamep+1);
1083 cellp = cm_FindCellByID(scp->fid.cell);
1087 code = CM_ERROR_NOSUCHCELL;
1091 vnLength = strlen(volNamep);
1092 if (vnLength >= 8 && strcmp(volNamep + vnLength - 7, ".backup") == 0)
1094 else if (vnLength >= 10
1095 && strcmp(volNamep + vnLength - 9, ".readonly") == 0)
1100 /* check for backups within backups */
1102 && (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
1103 == CM_SCACHEFLAG_RO) {
1104 code = CM_ERROR_NOSUCHVOLUME;
1108 /* now we need to get the volume */
1109 lock_ReleaseMutex(&scp->mx);
1110 if (cm_VolNameIsID(volNamep)) {
1111 code = cm_GetVolumeByID(cellp, atoi(volNamep), userp, reqp,
1112 CM_GETVOL_FLAG_CREATE, &volp);
1114 code = cm_GetVolumeByName(cellp, volNamep, userp, reqp,
1115 CM_GETVOL_FLAG_CREATE, &volp);
1117 lock_ObtainMutex(&scp->mx);
1120 /* save the parent of the volume root for this is the
1121 * place where the volume is mounted and we must remember
1122 * this in the volume structure rather than just in the
1123 * scache entry lest the scache entry gets recycled
1126 lock_ObtainMutex(&volp->mx);
1127 volp->dotdotFid = dscp->fid;
1128 lock_ReleaseMutex(&volp->mx);
1130 scp->mountRootFid.cell = cellp->cellID;
1131 /* if the mt pt is in a read-only volume (not just a
1132 * backup), and if there is a read-only volume for the
1133 * target, and if this is a type '#' mount point, use
1134 * the read-only, otherwise use the one specified.
1136 if (mtType == '#' && (scp->flags & CM_SCACHEFLAG_PURERO)
1137 && volp->ro.ID != 0 && type == RWVOL)
1140 scp->mountRootFid.volume = volp->ro.ID;
1141 else if (type == BACKVOL)
1142 scp->mountRootFid.volume = volp->bk.ID;
1144 scp->mountRootFid.volume = volp->rw.ID;
1146 /* the rest of the fid is a magic number */
1147 scp->mountRootFid.vnode = 1;
1148 scp->mountRootFid.unique = 1;
1149 scp->mountRootGen = cm_data.mountRootGen;
1151 tfid = scp->mountRootFid;
1152 lock_ReleaseMutex(&scp->mx);
1153 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
1154 lock_ObtainMutex(&scp->mx);
1165 long cm_LookupInternal(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
1166 cm_req_t *reqp, cm_scache_t **outpScpp)
1169 int dnlcHit = 1; /* did we hit in the dnlc? yes, we did */
1170 cm_scache_t *tscp = NULL;
1171 cm_scache_t *mountedScp;
1172 cm_lookupSearch_t rock;
1175 memset(&rock, 0, sizeof(rock));
1177 if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
1178 && strcmp(namep, "..") == 0) {
1179 if (dscp->dotdotFid.volume == 0)
1180 return CM_ERROR_NOSUCHVOLUME;
1181 rock.fid = dscp->dotdotFid;
1183 } else if (strcmp(namep, ".") == 0) {
1184 rock.fid = dscp->fid;
1188 if (flags & CM_FLAG_NOMOUNTCHASE) {
1189 /* In this case, we should go and call cm_Dir* functions
1190 directly since the following cm_ApplyDir() function will
1198 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ, &dirop);
1201 code = cm_BPlusDirLookup(&dirop, namep, &rock.fid);
1206 code = cm_DirLookup(&dirop, namep, &rock.fid);
1208 cm_EndDirOp(&dirop);
1218 if (code == CM_ERROR_INEXACT_MATCH && (flags & CM_FLAG_CASEFOLD)) {
1225 return CM_ERROR_BPLUS_NOMATCH;
1230 rock.fid.cell = dscp->fid.cell;
1231 rock.fid.volume = dscp->fid.volume;
1232 rock.searchNamep = namep;
1233 rock.caseFold = (flags & CM_FLAG_CASEFOLD);
1234 rock.hasTilde = ((strchr(namep, '~') != NULL) ? 1 : 0);
1236 /* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
1237 code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
1238 (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
1240 /* code == 0 means we fell off the end of the dir, while stopnow means
1241 * that we stopped early, probably because we found the entry we're
1242 * looking for. Any other non-zero code is an error.
1244 if (code && code != CM_ERROR_STOPNOW) {
1245 /* if the cm_scache_t we are searching in is not a directory
1246 * we must return path not found because the error
1247 * is to describe the final component not an intermediary
1249 if (code == CM_ERROR_NOTDIR) {
1250 if (flags & CM_FLAG_CHECKPATH)
1251 return CM_ERROR_NOSUCHPATH;
1253 return CM_ERROR_NOSUCHFILE;
1258 getroot = (dscp==cm_data.rootSCachep) ;
1260 if (!cm_freelanceEnabled || !getroot) {
1261 if (flags & CM_FLAG_CHECKPATH)
1262 return CM_ERROR_NOSUCHPATH;
1264 return CM_ERROR_NOSUCHFILE;
1266 else { /* nonexistent dir on freelance root, so add it */
1267 char fullname[200] = ".";
1270 osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %s",
1271 osi_LogSaveString(afsd_logp,namep));
1272 if (namep[0] == '.') {
1273 if (cm_GetCell_Gen(&namep[1], &fullname[1], CM_FLAG_CREATE)) {
1275 if ( stricmp(&namep[1], &fullname[1]) )
1276 code = cm_FreelanceAddSymlink(namep, fullname, &rock.fid);
1278 code = cm_FreelanceAddMount(namep, &fullname[1], "root.cell.", 1, &rock.fid);
1281 if (cm_GetCell_Gen(namep, fullname, CM_FLAG_CREATE)) {
1283 if ( stricmp(namep, fullname) )
1284 code = cm_FreelanceAddSymlink(namep, fullname, &rock.fid);
1286 code = cm_FreelanceAddMount(namep, fullname, "root.cell.", 0, &rock.fid);
1289 if (!found || code < 0) { /* add mount point failed, so give up */
1290 if (flags & CM_FLAG_CHECKPATH)
1291 return CM_ERROR_NOSUCHPATH;
1293 return CM_ERROR_NOSUCHFILE;
1295 tscp = NULL; /* to force call of cm_GetSCache */
1300 if ( !tscp ) /* we did not find it in the dnlc */
1303 code = cm_GetSCache(&rock.fid, &tscp, userp, reqp);
1307 /* tscp is now held */
1309 lock_ObtainMutex(&tscp->mx);
1310 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1311 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1313 lock_ReleaseMutex(&tscp->mx);
1314 cm_ReleaseSCache(tscp);
1317 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1318 /* tscp is now locked */
1320 if (!(flags & CM_FLAG_NOMOUNTCHASE)
1321 && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
1322 /* mount points are funny: they have a volume name to mount
1325 code = cm_ReadMountPoint(tscp, userp, reqp);
1327 code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
1329 lock_ReleaseMutex(&tscp->mx);
1330 cm_ReleaseSCache(tscp);
1337 lock_ReleaseMutex(&tscp->mx);
1340 /* copy back pointer */
1343 /* insert scache in dnlc */
1344 if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
1345 /* lock the directory entry to prevent racing callback revokes */
1346 lock_ObtainMutex(&dscp->mx);
1347 if ( dscp->cbServerp != NULL && dscp->cbExpires > 0 )
1348 cm_dnlcEnter(dscp, namep, tscp);
1349 lock_ReleaseMutex(&dscp->mx);
1356 int cm_ExpandSysName(char *inp, char *outp, long outSize, unsigned int index)
1361 tp = strrchr(inp, '@');
1363 return 0; /* no @sys */
1365 if (strcmp(tp, "@sys") != 0)
1366 return 0; /* no @sys */
1368 /* caller just wants to know if this is a valid @sys type of name */
1372 if (index >= MAXNUMSYSNAMES)
1375 /* otherwise generate the properly expanded @sys name */
1376 prefixCount = (int)(tp - inp);
1378 strncpy(outp, inp, prefixCount); /* copy out "a." from "a.@sys" */
1379 outp[prefixCount] = 0; /* null terminate the "a." */
1380 strcat(outp, cm_sysNameList[index]);/* append i386_nt40 */
1384 long cm_EvaluateVolumeReference(char * namep, long flags, cm_user_t * userp,
1385 cm_req_t *reqp, cm_scache_t ** outpScpp)
1388 char cellName[CELL_MAXNAMELEN];
1389 char volumeName[VL_MAXNAMELEN];
1394 cm_cell_t * cellp = NULL;
1395 cm_volume_t * volp = NULL;
1398 int mountType = RWVOL;
1400 osi_Log1(afsd_logp, "cm_EvaluateVolumeReference for string [%s]",
1401 osi_LogSaveString(afsd_logp, namep));
1403 if (strnicmp(namep, CM_PREFIX_VOL, CM_PREFIX_VOL_CCH) != 0) {
1404 goto _exit_invalid_path;
1407 /* namep is assumed to look like the following:
1409 @vol:<cellname>%<volume>\0
1411 @vol:<cellname>#<volume>\0
1415 cp = namep + CM_PREFIX_VOL_CCH; /* cp points to cell name, hopefully */
1416 tp = strchr(cp, '%');
1418 tp = strchr(cp, '#');
1420 (len = tp - cp) == 0 ||
1421 len > CELL_MAXNAMELEN)
1422 goto _exit_invalid_path;
1423 strncpy(cellName, cp, len);
1424 cellName[len] = '\0';
1429 cp = tp+1; /* cp now points to volume, supposedly */
1430 strncpy(volumeName, cp, VL_MAXNAMELEN-1);
1431 volumeName[VL_MAXNAMELEN - 1] = 0;
1433 /* OK, now we have the cell and the volume */
1434 osi_Log2(afsd_logp, " Found cell [%s] and volume [%s]",
1435 osi_LogSaveString(afsd_logp, cellName),
1436 osi_LogSaveString(afsd_logp, volumeName));
1438 cellp = cm_GetCell(cellName, CM_FLAG_CREATE);
1439 if (cellp == NULL) {
1440 goto _exit_invalid_path;
1443 len = strlen(volumeName);
1444 if (len >= 8 && strcmp(volumeName + len - 7, ".backup") == 0)
1446 else if (len >= 10 &&
1447 strcmp(volumeName + len - 9, ".readonly") == 0)
1452 if (cm_VolNameIsID(volumeName)) {
1453 code = cm_GetVolumeByID(cellp, atoi(volumeName), userp, reqp,
1454 CM_GETVOL_FLAG_CREATE, &volp);
1456 code = cm_GetVolumeByName(cellp, volumeName, userp, reqp,
1457 CM_GETVOL_FLAG_CREATE, &volp);
1463 fid.cell = cellp->cellID;
1465 if (volType == BACKVOL)
1466 fid.volume = volp->bk.ID;
1467 else if (volType == ROVOL ||
1468 (volType == RWVOL && mountType == ROVOL && volp->ro.ID != 0))
1469 fid.volume = volp->ro.ID;
1471 fid.volume = volp->rw.ID;
1476 code = cm_GetSCache(&fid, outpScpp, userp, reqp);
1486 if (flags & CM_FLAG_CHECKPATH)
1487 return CM_ERROR_NOSUCHPATH;
1489 return CM_ERROR_NOSUCHFILE;
1492 #ifdef DEBUG_REFCOUNT
1493 long cm_LookupDbg(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
1494 cm_req_t *reqp, cm_scache_t **outpScpp, char * file, long line)
1496 long cm_Lookup(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
1497 cm_req_t *reqp, cm_scache_t **outpScpp)
1502 int sysNameIndex = 0;
1503 cm_scache_t *scp = NULL;
1505 #ifdef DEBUG_REFCOUNT
1506 afsi_log("%s:%d cm_Lookup dscp 0x%p ref %d", file, line, dscp, dscp->refCount, file, line);
1507 osi_Log2(afsd_logp, "cm_Lookup dscp 0x%p ref %d", dscp, dscp->refCount);
1510 if ( stricmp(namep,SMB_IOCTL_FILENAME_NOSLASH) == 0 ) {
1511 if (flags & CM_FLAG_CHECKPATH)
1512 return CM_ERROR_NOSUCHPATH;
1514 return CM_ERROR_NOSUCHFILE;
1517 if (dscp == cm_data.rootSCachep &&
1518 strnicmp(namep, CM_PREFIX_VOL, CM_PREFIX_VOL_CCH) == 0) {
1519 return cm_EvaluateVolumeReference(namep, flags, userp, reqp, outpScpp);
1522 if (cm_ExpandSysName(namep, NULL, 0, 0) > 0) {
1523 for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
1524 code = cm_ExpandSysName(namep, tname, sizeof(tname), sysNameIndex);
1526 code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
1527 #ifdef DEBUG_REFCOUNT
1528 afsi_log("%s:%d cm_LookupInternal (1) code 0x%x dscp 0x%p ref %d scp 0x%p ref %d", file, line, code, dscp, dscp->refCount, scp, scp ? scp->refCount : 0);
1529 osi_Log3(afsd_logp, "cm_LookupInternal (1) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1537 cm_ReleaseSCache(scp);
1541 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1542 #ifdef DEBUG_REFCOUNT
1543 afsi_log("%s:%d cm_LookupInternal (2) code 0x%x dscp 0x%p ref %d scp 0x%p ref %d", file, line, code, dscp, dscp->refCount, scp, scp ? scp->refCount : 0);
1544 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1551 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1552 #ifdef DEBUG_REFCOUNT
1553 afsi_log("%s:%d cm_LookupInternal (2) code 0x%x dscp 0x%p ref %d scp 0x%p ref %d", file, line, code, dscp, dscp->refCount, scp, scp ? scp->refCount : 0);
1554 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1560 /* None of the possible sysName expansions could be found */
1561 if (flags & CM_FLAG_CHECKPATH)
1562 return CM_ERROR_NOSUCHPATH;
1564 return CM_ERROR_NOSUCHFILE;
1567 long cm_Unlink(cm_scache_t *dscp, char *namep, cm_user_t *userp, cm_req_t *reqp)
1573 AFSFetchStatus newDirStatus;
1575 struct rx_connection * callp;
1578 #ifdef AFS_FREELANCE_CLIENT
1579 if (cm_freelanceEnabled && dscp == cm_data.rootSCachep) {
1580 /* deleting a mount point from the root dir. */
1581 code = cm_FreelanceRemoveMount(namep);
1586 /* make sure we don't screw up the dir status during the merge */
1587 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
1589 lock_ObtainMutex(&dscp->mx);
1590 sflags = CM_SCACHESYNC_STOREDATA;
1591 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1592 lock_ReleaseMutex(&dscp->mx);
1594 cm_EndDirOp(&dirop);
1599 afsFid.Volume = dscp->fid.volume;
1600 afsFid.Vnode = dscp->fid.vnode;
1601 afsFid.Unique = dscp->fid.unique;
1603 osi_Log1(afsd_logp, "CALL RemoveFile scp 0x%p", dscp);
1605 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
1609 callp = cm_GetRxConn(connp);
1610 code = RXAFS_RemoveFile(callp, &afsFid, namep,
1611 &newDirStatus, &volSync);
1612 rx_PutConnection(callp);
1614 } while (cm_Analyze(connp, userp, reqp, &dscp->fid, &volSync, NULL, NULL, code));
1615 code = cm_MapRPCError(code, reqp);
1618 osi_Log1(afsd_logp, "CALL RemoveFile FAILURE, code 0x%x", code);
1620 osi_Log0(afsd_logp, "CALL RemoveFile SUCCESS");
1623 lock_ObtainWrite(&dirop.scp->dirlock);
1624 dirop.lockType = CM_DIRLOCK_WRITE;
1626 lock_ObtainMutex(&dscp->mx);
1627 cm_dnlcRemove(dscp, namep);
1628 cm_SyncOpDone(dscp, NULL, sflags);
1630 cm_MergeStatus(NULL, dscp, &newDirStatus, &volSync, userp, 0);
1631 } else if (code == CM_ERROR_NOSUCHFILE) {
1632 /* windows would not have allowed the request to delete the file
1633 * if it did not believe the file existed. therefore, we must
1634 * have an inconsistent view of the world.
1636 dscp->cbServerp = NULL;
1638 lock_ReleaseMutex(&dscp->mx);
1640 if (code == 0 && cm_CheckDirOpForSingleChange(&dirop)) {
1641 cm_DirDeleteEntry(&dirop, namep);
1643 cm_BPlusDirDeleteEntry(&dirop, namep);
1646 cm_EndDirOp(&dirop);
1651 /* called with a locked vnode, and fills in the link info.
1652 * returns this the vnode still locked.
1654 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1661 lock_AssertMutex(&linkScp->mx);
1662 if (!linkScp->mountPointStringp[0]) {
1663 /* read the link data */
1664 lock_ReleaseMutex(&linkScp->mx);
1665 thyper.LowPart = thyper.HighPart = 0;
1666 code = buf_Get(linkScp, &thyper, &bufp);
1667 lock_ObtainMutex(&linkScp->mx);
1671 code = cm_SyncOp(linkScp, bufp, userp, reqp, 0,
1672 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1677 cm_SyncOpDone(linkScp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1679 if (cm_HaveBuffer(linkScp, bufp, 0))
1682 code = cm_GetBuffer(linkScp, bufp, NULL, userp, reqp);
1687 } /* while loop to get the data */
1689 /* now if we still have no link read in,
1690 * copy the data from the buffer */
1691 if ((temp = linkScp->length.LowPart) >= MOUNTPOINTLEN) {
1693 return CM_ERROR_TOOBIG;
1696 /* otherwise, it fits; make sure it is still null (could have
1697 * lost race with someone else referencing this link above),
1698 * and if so, copy in the data.
1700 if (!linkScp->mountPointStringp[0]) {
1701 strncpy(linkScp->mountPointStringp, bufp->datap, temp);
1702 linkScp->mountPointStringp[temp] = 0; /* null terminate */
1705 } /* don't have sym link contents cached */
1710 /* called with a held vnode and a path suffix, with the held vnode being a
1711 * symbolic link. Our goal is to generate a new path to interpret, and return
1712 * this new path in newSpaceBufferp. If the new vnode is relative to a dir
1713 * other than the directory containing the symbolic link, then the new root is
1714 * returned in *newRootScpp, otherwise a null is returned there.
1716 long cm_AssembleLink(cm_scache_t *linkScp, char *pathSuffixp,
1717 cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1718 cm_user_t *userp, cm_req_t *reqp)
1725 lock_ObtainMutex(&linkScp->mx);
1726 code = cm_HandleLink(linkScp, userp, reqp);
1730 /* if we may overflow the buffer, bail out; buffer is signficantly
1731 * bigger than max path length, so we don't really have to worry about
1732 * being a little conservative here.
1734 if (strlen(linkScp->mountPointStringp) + strlen(pathSuffixp) + 2
1735 >= CM_UTILS_SPACESIZE)
1736 return CM_ERROR_TOOBIG;
1738 tsp = cm_GetSpace();
1739 linkp = linkScp->mountPointStringp;
1740 if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1741 if (strlen(linkp) > cm_mountRootLen)
1742 strcpy(tsp->data, linkp+cm_mountRootLen+1);
1745 *newRootScpp = cm_data.rootSCachep;
1746 cm_HoldSCache(cm_data.rootSCachep);
1747 } else if (linkp[0] == '\\' && linkp[1] == '\\') {
1748 if (!strnicmp(&linkp[2], cm_NetbiosName, (len = (long)strlen(cm_NetbiosName))))
1750 char * p = &linkp[len + 3];
1751 if (strnicmp(p, "all", 3) == 0)
1754 strcpy(tsp->data, p);
1755 for (p = tsp->data; *p; p++) {
1759 *newRootScpp = cm_data.rootSCachep;
1760 cm_HoldSCache(cm_data.rootSCachep);
1762 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1763 strcpy(tsp->data, linkp);
1764 *newRootScpp = NULL;
1765 code = CM_ERROR_PATH_NOT_COVERED;
1767 } else if ( !strnicmp(linkp, "msdfs:", (len = (long)strlen("msdfs:"))) ) {
1768 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1769 strcpy(tsp->data, linkp);
1770 *newRootScpp = NULL;
1771 code = CM_ERROR_PATH_NOT_COVERED;
1772 } else if (*linkp == '\\' || *linkp == '/') {
1774 /* formerly, this was considered to be from the AFS root,
1775 * but this seems to create problems. instead, we will just
1776 * reject the link */
1777 strcpy(tsp->data, linkp+1);
1778 *newRootScpp = cm_data.rootSCachep;
1779 cm_HoldSCache(cm_data.rootSCachep);
1781 /* we still copy the link data into the response so that
1782 * the user can see what the link points to
1784 linkScp->fileType = CM_SCACHETYPE_INVALID;
1785 strcpy(tsp->data, linkp);
1786 *newRootScpp = NULL;
1787 code = CM_ERROR_NOSUCHPATH;
1790 /* a relative link */
1791 strcpy(tsp->data, linkp);
1792 *newRootScpp = NULL;
1794 if (pathSuffixp[0] != 0) { /* if suffix string is non-null */
1795 strcat(tsp->data, "\\");
1796 strcat(tsp->data, pathSuffixp);
1798 *newSpaceBufferp = tsp;
1801 lock_ReleaseMutex(&linkScp->mx);
1804 #ifdef DEBUG_REFCOUNT
1805 long cm_NameIDbg(cm_scache_t *rootSCachep, char *pathp, long flags,
1806 cm_user_t *userp, char *tidPathp, cm_req_t *reqp, cm_scache_t **outScpp,
1807 char * file, long line)
1809 long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
1810 cm_user_t *userp, char *tidPathp, cm_req_t *reqp, cm_scache_t **outScpp)
1814 char *tp; /* ptr moving through input buffer */
1815 char tc; /* temp char */
1816 int haveComponent; /* has new component started? */
1817 char component[256]; /* this is the new component */
1818 char *cp; /* component name being assembled */
1819 cm_scache_t *tscp; /* current location in the hierarchy */
1820 cm_scache_t *nscp; /* next dude down */
1821 cm_scache_t *dirScp; /* last dir we searched */
1822 cm_scache_t *linkScp; /* new root for the symlink we just
1824 cm_space_t *psp; /* space for current path, if we've hit
1826 cm_space_t *tempsp; /* temp vbl */
1827 char *restp; /* rest of the pathname to interpret */
1828 int symlinkCount; /* count of # of symlinks traversed */
1829 int extraFlag; /* avoid chasing mt pts for dir cmd */
1830 int phase = 1; /* 1 = tidPathp, 2 = pathp */
1831 #define MAX_FID_COUNT 512
1832 cm_fid_t fids[MAX_FID_COUNT]; /* array of fids processed in this path walk */
1833 int fid_count = 0; /* number of fids processed in this path walk */
1836 #ifdef DEBUG_REFCOUNT
1837 afsi_log("%s:%d cm_NameI rootscp 0x%p ref %d", file, line, rootSCachep, rootSCachep->refCount);
1838 osi_Log4(afsd_logp,"cm_NameI rootscp 0x%p path %s tidpath %s flags 0x%x",
1839 rootSCachep, pathp ? pathp : "<NULL>", tidPathp ? tidPathp : "<NULL>",
1854 cm_HoldSCache(tscp);
1862 /* map Unix slashes into DOS ones so we can interpret Unix
1868 if (!haveComponent) {
1871 } else if (tc == 0) {
1885 /* we have a component here */
1886 if (tc == 0 || tc == '\\') {
1887 /* end of the component; we're at the last
1888 * component if tc == 0. However, if the last
1889 * is a symlink, we have more to do.
1891 *cp++ = 0; /* add null termination */
1893 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
1894 extraFlag = CM_FLAG_NOMOUNTCHASE;
1895 code = cm_Lookup(tscp, component,
1897 userp, reqp, &nscp);
1900 if (!strcmp(component,"..") || !strcmp(component,".")) {
1902 * roll back the fid list until we find the fid
1903 * that matches where we are now. Its not necessarily
1904 * one or two fids because they might have been
1905 * symlinks or mount points or both that were crossed.
1907 for ( i=fid_count-1; i>=0; i--) {
1908 if (!cm_FidCmp(&nscp->fid, &fids[i]))
1912 /* add the new fid to the list */
1913 for ( i=0; i<fid_count; i++) {
1914 if ( !cm_FidCmp(&nscp->fid, &fids[i]) ) {
1915 code = CM_ERROR_TOO_MANY_SYMLINKS;
1916 cm_ReleaseSCache(nscp);
1921 if (i == fid_count && fid_count < MAX_FID_COUNT) {
1922 fids[fid_count++] = nscp->fid;
1928 cm_ReleaseSCache(tscp);
1930 cm_ReleaseSCache(dirScp);
1933 if ((code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH) &&
1934 tscp->fileType == CM_SCACHETYPE_SYMLINK)
1936 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_NOSUCHPATH");
1937 return CM_ERROR_NOSUCHPATH;
1939 osi_Log1(afsd_logp,"cm_NameI code 0x%x", code);
1944 haveComponent = 0; /* component done */
1946 cm_ReleaseSCache(dirScp);
1947 dirScp = tscp; /* for some symlinks */
1948 tscp = nscp; /* already held */
1950 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
1953 cm_ReleaseSCache(dirScp);
1959 /* now, if tscp is a symlink, we should follow
1960 * it and assemble the path again.
1962 lock_ObtainMutex(&tscp->mx);
1963 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1964 CM_SCACHESYNC_GETSTATUS
1965 | CM_SCACHESYNC_NEEDCALLBACK);
1967 lock_ReleaseMutex(&tscp->mx);
1968 cm_ReleaseSCache(tscp);
1971 cm_ReleaseSCache(dirScp);
1976 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1978 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
1979 /* this is a symlink; assemble a new buffer */
1980 lock_ReleaseMutex(&tscp->mx);
1981 if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
1982 cm_ReleaseSCache(tscp);
1985 cm_ReleaseSCache(dirScp);
1990 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_TOO_MANY_SYMLINKS");
1991 return CM_ERROR_TOO_MANY_SYMLINKS;
1997 code = cm_AssembleLink(tscp, restp, &linkScp, &tempsp, userp, reqp);
1999 if (code == 0 && linkScp != NULL) {
2000 if (linkScp == cm_data.rootSCachep)
2003 for ( i=0; i<fid_count; i++) {
2004 if ( !cm_FidCmp(&linkScp->fid, &fids[i]) ) {
2005 code = CM_ERROR_TOO_MANY_SYMLINKS;
2006 cm_ReleaseSCache(linkScp);
2012 if (i == fid_count && fid_count < MAX_FID_COUNT) {
2013 fids[fid_count++] = linkScp->fid;
2018 /* something went wrong */
2019 cm_ReleaseSCache(tscp);
2022 cm_ReleaseSCache(dirScp);
2028 /* otherwise, tempsp has the new path,
2029 * and linkScp is the new root from
2030 * which to interpret that path.
2031 * Continue with the namei processing,
2032 * also doing the bookkeeping for the
2033 * space allocation and tracking the
2034 * vnode reference counts.
2040 cm_ReleaseSCache(tscp);
2045 * now, if linkScp is null, that's
2046 * AssembleLink's way of telling us that
2047 * the sym link is relative to the dir
2048 * containing the link. We have a ref
2049 * to it in dirScp, and we hold it now
2050 * and reuse it as the new spot in the
2058 /* not a symlink, we may be done */
2059 lock_ReleaseMutex(&tscp->mx);
2067 cm_ReleaseSCache(dirScp);
2075 cm_ReleaseSCache(dirScp);
2078 } /* end of a component */
2081 } /* we have a component */
2082 } /* big while loop over all components */
2086 cm_ReleaseSCache(dirScp);
2092 cm_ReleaseSCache(tscp);
2094 #ifdef DEBUG_REFCOUNT
2095 afsi_log("%s:%d cm_NameI code 0x%x outScpp 0x%p ref %d", file, line, code, *outScpp, (*outScpp)->refCount);
2097 osi_Log2(afsd_logp,"cm_NameI code 0x%x outScpp 0x%p", code, *outScpp);
2101 /* called with a dir, and a vnode within the dir that happens to be a symlink.
2102 * We chase the link, and return a held pointer to the target, if it exists,
2103 * in *outScpp. If we succeed, we return 0, otherwise we return an error code
2104 * and do not hold or return a target vnode.
2106 * This is very similar to calling cm_NameI with the last component of a name,
2107 * which happens to be a symlink, except that we've already passed by the name.
2109 * This function is typically called by the directory listing functions, which
2110 * encounter symlinks but need to return the proper file length so programs
2111 * like "more" work properly when they make use of the attributes retrieved from
2114 * The input vnode should not be locked when this function is called.
2116 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
2117 cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
2121 cm_scache_t *newRootScp;
2123 osi_Log1(afsd_logp, "Evaluating symlink scp 0x%p", linkScp);
2125 code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
2129 /* now, if newRootScp is NULL, we're really being told that the symlink
2130 * is relative to the current directory (dscp).
2132 if (newRootScp == NULL) {
2134 cm_HoldSCache(dscp);
2137 code = cm_NameI(newRootScp, spacep->data,
2138 CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
2139 userp, NULL, reqp, outScpp);
2141 if (code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH)
2142 code = CM_ERROR_NOSUCHPATH;
2144 /* this stuff is allocated no matter what happened on the namei call,
2146 cm_FreeSpace(spacep);
2147 cm_ReleaseSCache(newRootScp);
2149 if (linkScp == *outScpp) {
2150 cm_ReleaseSCache(*outScpp);
2152 code = CM_ERROR_NOSUCHPATH;
2158 /* make this big enough so that one buffer of dir pages won't overflow. We'll
2159 * check anyway, but we want to minimize the chance that we have to leave stuff
2162 #define CM_BULKMAX (3 * AFSCBMAX)
2164 /* rock for bulk stat calls */
2165 typedef struct cm_bulkStat {
2166 osi_hyper_t bufOffset; /* only do it for things in this buffer page */
2168 /* info for the actual call */
2169 int counter; /* next free slot */
2170 AFSFid fids[CM_BULKMAX];
2171 AFSFetchStatus stats[CM_BULKMAX];
2172 AFSCallBack callbacks[CM_BULKMAX];
2175 /* for a given entry, make sure that it isn't in the stat cache, and then
2176 * add it to the list of file IDs to be obtained.
2178 * Don't bother adding it if we already have a vnode. Note that the dir
2179 * is locked, so we have to be careful checking the vnode we're thinking of
2180 * processing, to avoid deadlocks.
2182 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
2193 /* Don't overflow bsp. */
2194 if (bsp->counter >= CM_BULKMAX)
2195 return CM_ERROR_STOPNOW;
2197 thyper.LowPart = cm_data.buf_blockSize;
2198 thyper.HighPart = 0;
2199 thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
2201 /* thyper is now the first byte past the end of the record we're
2202 * interested in, and bsp->bufOffset is the first byte of the record
2203 * we're interested in.
2204 * Skip data in the others.
2207 if (LargeIntegerLessThan(*offp, bsp->bufOffset))
2209 if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
2210 return CM_ERROR_STOPNOW;
2211 if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
2214 tfid.cell = scp->fid.cell;
2215 tfid.volume = scp->fid.volume;
2216 tfid.vnode = ntohl(dep->fid.vnode);
2217 tfid.unique = ntohl(dep->fid.unique);
2218 tscp = cm_FindSCache(&tfid);
2220 if (lock_TryMutex(&tscp->mx)) {
2221 /* we have an entry that we can look at */
2222 if (!(tscp->flags & CM_SCACHEFLAG_EACCESS) && cm_HaveCallback(tscp)) {
2223 /* we have a callback on it. Don't bother
2224 * fetching this stat entry, since we're happy
2225 * with the info we have.
2227 lock_ReleaseMutex(&tscp->mx);
2228 cm_ReleaseSCache(tscp);
2231 lock_ReleaseMutex(&tscp->mx);
2233 cm_ReleaseSCache(tscp);
2236 #ifdef AFS_FREELANCE_CLIENT
2237 // yj: if this is a mountpoint under root.afs then we don't want it
2238 // to be bulkstat-ed, instead, we call getSCache directly and under
2239 // getSCache, it is handled specially.
2240 if ( cm_freelanceEnabled &&
2241 tfid.cell==AFS_FAKE_ROOT_CELL_ID &&
2242 tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
2243 !(tfid.vnode==0x1 && tfid.unique==0x1) )
2245 osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
2246 return cm_GetSCache(&tfid, &tscp, NULL, NULL);
2248 #endif /* AFS_FREELANCE_CLIENT */
2251 bsp->fids[i].Volume = scp->fid.volume;
2252 bsp->fids[i].Vnode = tfid.vnode;
2253 bsp->fids[i].Unique = tfid.unique;
2257 /* called with a locked scp and a pointer to a buffer. Make bulk stat
2258 * calls on all undeleted files in the page of the directory specified.
2261 cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
2265 cm_bulkStat_t bb; /* this is *BIG*, probably 16K or so;
2266 * watch for stack problems */
2267 AFSCBFids fidStruct;
2268 AFSBulkStats statStruct;
2270 AFSCBs callbackStruct;
2273 cm_callbackRequest_t cbReq;
2279 struct rx_connection * callp;
2280 int inlinebulk = 0; /* Did we use InlineBulkStatus RPC or not? */
2282 osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%p", dscp);
2284 /* should be on a buffer boundary */
2285 osi_assert((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0);
2287 memset(&bb, 0, sizeof(bb));
2288 bb.bufOffset = *offsetp;
2290 lock_ReleaseMutex(&dscp->mx);
2291 /* first, assemble the file IDs we need to stat */
2292 code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) &bb, offsetp, userp, reqp, NULL);
2294 /* if we failed, bail out early */
2295 if (code && code != CM_ERROR_STOPNOW) {
2296 lock_ObtainMutex(&dscp->mx);
2300 /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
2301 * make the calls to create the entries. Handle AFSCBMAX files at a
2305 while (filex < bb.counter) {
2306 filesThisCall = bb.counter - filex;
2307 if (filesThisCall > AFSCBMAX)
2308 filesThisCall = AFSCBMAX;
2310 fidStruct.AFSCBFids_len = filesThisCall;
2311 fidStruct.AFSCBFids_val = &bb.fids[filex];
2312 statStruct.AFSBulkStats_len = filesThisCall;
2313 statStruct.AFSBulkStats_val = &bb.stats[filex];
2314 callbackStruct.AFSCBs_len = filesThisCall;
2315 callbackStruct.AFSCBs_val = &bb.callbacks[filex];
2316 cm_StartCallbackGrantingCall(NULL, &cbReq);
2317 osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
2319 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2323 callp = cm_GetRxConn(connp);
2324 if (!(connp->serverp->flags & CM_SERVERFLAG_NOINLINEBULK)) {
2325 code = RXAFS_InlineBulkStatus(callp, &fidStruct,
2326 &statStruct, &callbackStruct, &volSync);
2327 if (code == RXGEN_OPCODE) {
2328 cm_SetServerNoInlineBulk(connp->serverp, 0);
2334 code = RXAFS_BulkStatus(callp, &fidStruct,
2335 &statStruct, &callbackStruct, &volSync);
2337 rx_PutConnection(callp);
2339 } while (cm_Analyze(connp, userp, reqp, &dscp->fid,
2340 &volSync, NULL, &cbReq, code));
2341 code = cm_MapRPCError(code, reqp);
2343 osi_Log2(afsd_logp, "CALL %sBulkStatus FAILURE code 0x%x",
2344 inlinebulk ? "Inline" : "", code);
2346 osi_Log1(afsd_logp, "CALL %sBulkStatus SUCCESS", inlinebulk ? "Inline" : "");
2348 /* may as well quit on an error, since we're not going to do
2349 * much better on the next immediate call, either.
2352 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2356 /* otherwise, we should do the merges */
2357 for (i = 0; i<filesThisCall; i++) {
2359 tfid.cell = dscp->fid.cell;
2360 tfid.volume = bb.fids[j].Volume;
2361 tfid.vnode = bb.fids[j].Vnode;
2362 tfid.unique = bb.fids[j].Unique;
2363 code = cm_GetSCache(&tfid, &scp, userp, reqp);
2367 /* otherwise, if this entry has no callback info,
2370 lock_ObtainMutex(&scp->mx);
2371 /* now, we have to be extra paranoid on merging in this
2372 * information, since we didn't use cm_SyncOp before
2373 * starting the fetch to make sure that no bad races
2374 * were occurring. Specifically, we need to make sure
2375 * we don't obliterate any newer information in the
2376 * vnode than have here.
2378 * Right now, be pretty conservative: if there's a
2379 * callback or a pending call, skip it.
2381 if ((scp->cbServerp == NULL || (scp->flags & CM_SCACHEFLAG_EACCESS))
2383 (CM_SCACHEFLAG_FETCHING
2384 | CM_SCACHEFLAG_STORING
2385 | CM_SCACHEFLAG_SIZESTORING))) {
2386 cm_EndCallbackGrantingCall(scp, &cbReq,
2388 CM_CALLBACK_MAINTAINCOUNT);
2389 cm_MergeStatus(dscp, scp, &bb.stats[j], &volSync, userp, 0);
2391 lock_ReleaseMutex(&scp->mx);
2392 cm_ReleaseSCache(scp);
2393 } /* all files in the response */
2394 /* now tell it to drop the count,
2395 * after doing the vnode processing above */
2396 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2398 filex += filesThisCall;
2399 } /* while there are still more files to process */
2400 lock_ObtainMutex(&dscp->mx);
2402 /* If we did the InlineBulk RPC pull out the return code and log it */
2404 if ((&bb.stats[0])->errorCode) {
2405 osi_Log1(afsd_logp, "cm_TryBulkStat bulk stat error: %d",
2406 (&bb.stats[0])->errorCode);
2410 osi_Log0(afsd_logp, "END cm_TryBulkStat");
2414 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
2418 /* initialize store back mask as inexpensive local variable */
2420 memset(statusp, 0, sizeof(AFSStoreStatus));
2422 /* copy out queued info from scache first, if scp passed in */
2424 if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
2425 statusp->ClientModTime = scp->clientModTime;
2426 mask |= AFS_SETMODTIME;
2427 scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
2432 /* now add in our locally generated request */
2433 if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
2434 statusp->ClientModTime = attrp->clientModTime;
2435 mask |= AFS_SETMODTIME;
2437 if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
2438 statusp->UnixModeBits = attrp->unixModeBits;
2439 mask |= AFS_SETMODE;
2441 if (attrp->mask & CM_ATTRMASK_OWNER) {
2442 statusp->Owner = attrp->owner;
2443 mask |= AFS_SETOWNER;
2445 if (attrp->mask & CM_ATTRMASK_GROUP) {
2446 statusp->Group = attrp->group;
2447 mask |= AFS_SETGROUP;
2450 statusp->Mask = mask;
2453 /* set the file size, and make sure that all relevant buffers have been
2454 * truncated. Ensure that any partially truncated buffers have been zeroed
2455 * to the end of the buffer.
2457 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
2463 /* start by locking out buffer creation */
2464 lock_ObtainWrite(&scp->bufCreateLock);
2466 /* verify that this is a file, not a dir or a symlink */
2467 lock_ObtainMutex(&scp->mx);
2468 code = cm_SyncOp(scp, NULL, userp, reqp, 0,
2469 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2472 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2474 if (scp->fileType != CM_SCACHETYPE_FILE) {
2475 code = CM_ERROR_ISDIR;
2480 if (LargeIntegerLessThan(*sizep, scp->length))
2485 lock_ReleaseMutex(&scp->mx);
2487 /* can't hold scp->mx lock here, since we may wait for a storeback to
2488 * finish if the buffer package is cleaning a buffer by storing it to
2492 buf_Truncate(scp, userp, reqp, sizep);
2494 /* now ensure that file length is short enough, and update truncPos */
2495 lock_ObtainMutex(&scp->mx);
2497 /* make sure we have a callback (so we have the right value for the
2498 * length), and wait for it to be safe to do a truncate.
2500 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
2501 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2502 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2504 /* If we only have 'i' bits, then we should still be able to set
2505 the size of a file we created. */
2506 if (code == CM_ERROR_NOACCESS && scp->creator == userp) {
2507 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
2508 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2509 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2515 if (LargeIntegerLessThan(*sizep, scp->length)) {
2516 /* a real truncation. If truncPos is not set yet, or is bigger
2517 * than where we're truncating the file, set truncPos to this
2522 if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
2523 || LargeIntegerLessThan(*sizep, scp->length)) {
2525 scp->truncPos = *sizep;
2526 scp->mask |= CM_SCACHEMASK_TRUNCPOS;
2528 /* in either case, the new file size has been changed */
2529 scp->length = *sizep;
2530 scp->mask |= CM_SCACHEMASK_LENGTH;
2532 else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
2533 /* really extending the file */
2534 scp->length = *sizep;
2535 scp->mask |= CM_SCACHEMASK_LENGTH;
2538 /* done successfully */
2541 cm_SyncOpDone(scp, NULL,
2542 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2543 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2546 lock_ReleaseMutex(&scp->mx);
2547 lock_ReleaseWrite(&scp->bufCreateLock);
2552 /* set the file size or other attributes (but not both at once) */
2553 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
2557 AFSFetchStatus afsOutStatus;
2561 AFSStoreStatus afsInStatus;
2562 struct rx_connection * callp;
2564 /* handle file length setting */
2565 if (attrp->mask & CM_ATTRMASK_LENGTH)
2566 return cm_SetLength(scp, &attrp->length, userp, reqp);
2568 lock_ObtainMutex(&scp->mx);
2569 /* otherwise, we have to make an RPC to get the status */
2570 code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
2572 lock_ReleaseMutex(&scp->mx);
2576 /* make the attr structure */
2577 cm_StatusFromAttr(&afsInStatus, scp, attrp);
2579 tfid.Volume = scp->fid.volume;
2580 tfid.Vnode = scp->fid.vnode;
2581 tfid.Unique = scp->fid.unique;
2582 lock_ReleaseMutex(&scp->mx);
2584 /* now make the RPC */
2585 osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%p", scp);
2587 code = cm_ConnFromFID(&scp->fid, userp, reqp, &connp);
2591 callp = cm_GetRxConn(connp);
2592 code = RXAFS_StoreStatus(callp, &tfid,
2593 &afsInStatus, &afsOutStatus, &volSync);
2594 rx_PutConnection(callp);
2596 } while (cm_Analyze(connp, userp, reqp,
2597 &scp->fid, &volSync, NULL, NULL, code));
2598 code = cm_MapRPCError(code, reqp);
2601 osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
2603 osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
2605 lock_ObtainMutex(&scp->mx);
2606 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
2608 cm_MergeStatus(NULL, scp, &afsOutStatus, &volSync, userp,
2609 CM_MERGEFLAG_FORCE|CM_MERGEFLAG_STOREDATA);
2611 /* if we're changing the mode bits, discard the ACL cache,
2612 * since we changed the mode bits.
2614 if (afsInStatus.Mask & AFS_SETMODE)
2615 cm_FreeAllACLEnts(scp);
2616 lock_ReleaseMutex(&scp->mx);
2620 long cm_Create(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2621 cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
2626 cm_callbackRequest_t cbReq;
2629 cm_scache_t *scp = NULL;
2631 AFSStoreStatus inStatus;
2632 AFSFetchStatus updatedDirStatus;
2633 AFSFetchStatus newFileStatus;
2634 AFSCallBack newFileCallback;
2636 struct rx_connection * callp;
2639 /* can't create names with @sys in them; must expand it manually first.
2640 * return "invalid request" if they try.
2642 if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2643 return CM_ERROR_ATSYS;
2646 /* before starting the RPC, mark that we're changing the file data, so
2647 * that someone who does a chmod will know to wait until our call
2650 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2651 lock_ObtainMutex(&dscp->mx);
2652 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2653 lock_ReleaseMutex(&dscp->mx);
2655 cm_StartCallbackGrantingCall(NULL, &cbReq);
2657 cm_EndDirOp(&dirop);
2664 cm_StatusFromAttr(&inStatus, NULL, attrp);
2666 /* try the RPC now */
2667 osi_Log1(afsd_logp, "CALL CreateFile scp 0x%p", dscp);
2669 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2673 dirAFSFid.Volume = dscp->fid.volume;
2674 dirAFSFid.Vnode = dscp->fid.vnode;
2675 dirAFSFid.Unique = dscp->fid.unique;
2677 callp = cm_GetRxConn(connp);
2678 code = RXAFS_CreateFile(connp->callp, &dirAFSFid, namep,
2679 &inStatus, &newAFSFid, &newFileStatus,
2680 &updatedDirStatus, &newFileCallback,
2682 rx_PutConnection(callp);
2684 } while (cm_Analyze(connp, userp, reqp,
2685 &dscp->fid, &volSync, NULL, &cbReq, code));
2686 code = cm_MapRPCError(code, reqp);
2689 osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
2691 osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
2694 lock_ObtainWrite(&dirop.scp->dirlock);
2695 dirop.lockType = CM_DIRLOCK_WRITE;
2697 lock_ObtainMutex(&dscp->mx);
2698 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2700 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, 0);
2702 lock_ReleaseMutex(&dscp->mx);
2704 /* now try to create the file's entry, too, but be careful to
2705 * make sure that we don't merge in old info. Since we weren't locking
2706 * out any requests during the file's creation, we may have pretty old
2710 newFid.cell = dscp->fid.cell;
2711 newFid.volume = dscp->fid.volume;
2712 newFid.vnode = newAFSFid.Vnode;
2713 newFid.unique = newAFSFid.Unique;
2714 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2716 lock_ObtainMutex(&scp->mx);
2717 scp->creator = userp; /* remember who created it */
2718 if (!cm_HaveCallback(scp)) {
2719 cm_MergeStatus(dscp, scp, &newFileStatus, &volSync,
2721 cm_EndCallbackGrantingCall(scp, &cbReq,
2722 &newFileCallback, 0);
2725 lock_ReleaseMutex(&scp->mx);
2730 /* make sure we end things properly */
2732 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2734 if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
2735 cm_DirCreateEntry(&dirop, namep, &newFid);
2737 cm_BPlusDirCreateEntry(&dirop, namep, &newFid);
2740 cm_EndDirOp(&dirop);
2745 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
2749 lock_ObtainWrite(&scp->bufCreateLock);
2750 code = buf_CleanVnode(scp, userp, reqp);
2751 lock_ReleaseWrite(&scp->bufCreateLock);
2753 lock_ObtainMutex(&scp->mx);
2755 if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
2756 | CM_SCACHEMASK_CLIENTMODTIME
2757 | CM_SCACHEMASK_LENGTH))
2758 code = cm_StoreMini(scp, userp, reqp);
2760 if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) {
2761 code = (scp->flags & CM_SCACHEFLAG_OVERQUOTA) ? CM_ERROR_QUOTA : CM_ERROR_SPACE;
2762 scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE);
2765 lock_ReleaseMutex(&scp->mx);
2770 long cm_MakeDir(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2771 cm_user_t *userp, cm_req_t *reqp)
2776 cm_callbackRequest_t cbReq;
2779 cm_scache_t *scp = NULL;
2781 AFSStoreStatus inStatus;
2782 AFSFetchStatus updatedDirStatus;
2783 AFSFetchStatus newDirStatus;
2784 AFSCallBack newDirCallback;
2786 struct rx_connection * callp;
2789 /* can't create names with @sys in them; must expand it manually first.
2790 * return "invalid request" if they try.
2792 if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2793 return CM_ERROR_ATSYS;
2796 /* before starting the RPC, mark that we're changing the directory
2797 * data, so that someone who does a chmod on the dir will wait until
2798 * our call completes.
2800 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2801 lock_ObtainMutex(&dscp->mx);
2802 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2803 lock_ReleaseMutex(&dscp->mx);
2805 cm_StartCallbackGrantingCall(NULL, &cbReq);
2807 cm_EndDirOp(&dirop);
2814 cm_StatusFromAttr(&inStatus, NULL, attrp);
2816 /* try the RPC now */
2817 osi_Log1(afsd_logp, "CALL MakeDir scp 0x%p", dscp);
2819 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2823 dirAFSFid.Volume = dscp->fid.volume;
2824 dirAFSFid.Vnode = dscp->fid.vnode;
2825 dirAFSFid.Unique = dscp->fid.unique;
2827 callp = cm_GetRxConn(connp);
2828 code = RXAFS_MakeDir(connp->callp, &dirAFSFid, namep,
2829 &inStatus, &newAFSFid, &newDirStatus,
2830 &updatedDirStatus, &newDirCallback,
2832 rx_PutConnection(callp);
2834 } while (cm_Analyze(connp, userp, reqp,
2835 &dscp->fid, &volSync, NULL, &cbReq, code));
2836 code = cm_MapRPCError(code, reqp);
2839 osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
2841 osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
2844 lock_ObtainWrite(&dirop.scp->dirlock);
2845 dirop.lockType = CM_DIRLOCK_WRITE;
2847 lock_ObtainMutex(&dscp->mx);
2848 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2850 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, 0);
2852 lock_ReleaseMutex(&dscp->mx);
2854 /* now try to create the new dir's entry, too, but be careful to
2855 * make sure that we don't merge in old info. Since we weren't locking
2856 * out any requests during the file's creation, we may have pretty old
2860 newFid.cell = dscp->fid.cell;
2861 newFid.volume = dscp->fid.volume;
2862 newFid.vnode = newAFSFid.Vnode;
2863 newFid.unique = newAFSFid.Unique;
2864 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2866 lock_ObtainMutex(&scp->mx);
2867 if (!cm_HaveCallback(scp)) {
2868 cm_MergeStatus(dscp, scp, &newDirStatus, &volSync,
2870 cm_EndCallbackGrantingCall(scp, &cbReq,
2871 &newDirCallback, 0);
2874 lock_ReleaseMutex(&scp->mx);
2875 cm_ReleaseSCache(scp);
2879 /* make sure we end things properly */
2881 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2883 if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
2884 cm_DirCreateEntry(&dirop, namep, &newFid);
2886 cm_BPlusDirCreateEntry(&dirop, namep, &newFid);
2889 cm_EndDirOp(&dirop);
2891 /* and return error code */
2895 long cm_Link(cm_scache_t *dscp, char *namep, cm_scache_t *sscp, long flags,
2896 cm_user_t *userp, cm_req_t *reqp)
2901 AFSFid existingAFSFid;
2902 AFSFetchStatus updatedDirStatus;
2903 AFSFetchStatus newLinkStatus;
2905 struct rx_connection * callp;
2908 if (dscp->fid.cell != sscp->fid.cell ||
2909 dscp->fid.volume != sscp->fid.volume) {
2910 return CM_ERROR_CROSSDEVLINK;
2913 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2914 lock_ObtainMutex(&dscp->mx);
2915 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2916 lock_ReleaseMutex(&dscp->mx);
2918 cm_EndDirOp(&dirop);
2923 /* try the RPC now */
2924 osi_Log1(afsd_logp, "CALL Link scp 0x%p", dscp);
2926 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2929 dirAFSFid.Volume = dscp->fid.volume;
2930 dirAFSFid.Vnode = dscp->fid.vnode;
2931 dirAFSFid.Unique = dscp->fid.unique;
2933 existingAFSFid.Volume = sscp->fid.volume;
2934 existingAFSFid.Vnode = sscp->fid.vnode;
2935 existingAFSFid.Unique = sscp->fid.unique;
2937 callp = cm_GetRxConn(connp);
2938 code = RXAFS_Link(callp, &dirAFSFid, namep, &existingAFSFid,
2939 &newLinkStatus, &updatedDirStatus, &volSync);
2940 rx_PutConnection(callp);
2941 osi_Log1(smb_logp," RXAFS_Link returns 0x%x", code);
2943 } while (cm_Analyze(connp, userp, reqp,
2944 &dscp->fid, &volSync, NULL, NULL, code));
2946 code = cm_MapRPCError(code, reqp);
2949 osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
2951 osi_Log0(afsd_logp, "CALL Link SUCCESS");
2954 lock_ObtainWrite(&dirop.scp->dirlock);
2955 dirop.lockType = CM_DIRLOCK_WRITE;
2957 lock_ObtainMutex(&dscp->mx);
2958 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2960 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, 0);
2962 lock_ReleaseMutex(&dscp->mx);
2965 if (cm_CheckDirOpForSingleChange(&dirop)) {
2966 cm_DirCreateEntry(&dirop, namep, &sscp->fid);
2968 cm_BPlusDirCreateEntry(&dirop, namep, &sscp->fid);
2972 cm_EndDirOp(&dirop);
2977 long cm_SymLink(cm_scache_t *dscp, char *namep, char *contentsp, long flags,
2978 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
2986 AFSStoreStatus inStatus;
2987 AFSFetchStatus updatedDirStatus;
2988 AFSFetchStatus newLinkStatus;
2990 struct rx_connection * callp;
2993 /* before starting the RPC, mark that we're changing the directory data,
2994 * so that someone who does a chmod on the dir will wait until our
2997 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2998 lock_ObtainMutex(&dscp->mx);
2999 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3000 lock_ReleaseMutex(&dscp->mx);
3002 cm_EndDirOp(&dirop);
3007 cm_StatusFromAttr(&inStatus, NULL, attrp);
3009 /* try the RPC now */
3010 osi_Log1(afsd_logp, "CALL Symlink scp 0x%p", dscp);
3012 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3016 dirAFSFid.Volume = dscp->fid.volume;
3017 dirAFSFid.Vnode = dscp->fid.vnode;
3018 dirAFSFid.Unique = dscp->fid.unique;
3020 callp = cm_GetRxConn(connp);
3021 code = RXAFS_Symlink(callp, &dirAFSFid, namep, contentsp,
3022 &inStatus, &newAFSFid, &newLinkStatus,
3023 &updatedDirStatus, &volSync);
3024 rx_PutConnection(callp);
3026 } while (cm_Analyze(connp, userp, reqp,
3027 &dscp->fid, &volSync, NULL, NULL, code));
3028 code = cm_MapRPCError(code, reqp);
3031 osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
3033 osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
3036 lock_ObtainWrite(&dirop.scp->dirlock);
3037 dirop.lockType = CM_DIRLOCK_WRITE;
3039 lock_ObtainMutex(&dscp->mx);
3040 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3042 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, 0);
3044 lock_ReleaseMutex(&dscp->mx);
3047 if (cm_CheckDirOpForSingleChange(&dirop)) {
3048 newFid.cell = dscp->fid.cell;
3049 newFid.volume = dscp->fid.volume;
3050 newFid.vnode = newAFSFid.Vnode;
3051 newFid.unique = newAFSFid.Unique;
3053 cm_DirCreateEntry(&dirop, namep, &newFid);
3055 cm_BPlusDirCreateEntry(&dirop, namep, &newFid);
3059 cm_EndDirOp(&dirop);
3061 /* now try to create the new dir's entry, too, but be careful to
3062 * make sure that we don't merge in old info. Since we weren't locking
3063 * out any requests during the file's creation, we may have pretty old
3067 newFid.cell = dscp->fid.cell;
3068 newFid.volume = dscp->fid.volume;
3069 newFid.vnode = newAFSFid.Vnode;
3070 newFid.unique = newAFSFid.Unique;
3071 code = cm_GetSCache(&newFid, &scp, userp, reqp);
3073 lock_ObtainMutex(&scp->mx);
3074 if (!cm_HaveCallback(scp)) {
3075 cm_MergeStatus(dscp, scp, &newLinkStatus, &volSync,
3078 lock_ReleaseMutex(&scp->mx);
3079 cm_ReleaseSCache(scp);
3083 /* and return error code */
3087 long cm_RemoveDir(cm_scache_t *dscp, char *namep, cm_user_t *userp,
3094 AFSFetchStatus updatedDirStatus;
3096 struct rx_connection * callp;
3099 /* before starting the RPC, mark that we're changing the directory data,
3100 * so that someone who does a chmod on the dir will wait until our
3103 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
3104 lock_ObtainMutex(&dscp->mx);
3105 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3106 lock_ReleaseMutex(&dscp->mx);
3108 cm_EndDirOp(&dirop);
3113 /* try the RPC now */
3114 osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%p", dscp);
3116 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3120 dirAFSFid.Volume = dscp->fid.volume;
3121 dirAFSFid.Vnode = dscp->fid.vnode;
3122 dirAFSFid.Unique = dscp->fid.unique;
3124 callp = cm_GetRxConn(connp);
3125 code = RXAFS_RemoveDir(callp, &dirAFSFid, namep,
3126 &updatedDirStatus, &volSync);
3127 rx_PutConnection(callp);
3129 } while (cm_Analyze(connp, userp, reqp,
3130 &dscp->fid, &volSync, NULL, NULL, code));
3131 code = cm_MapRPCErrorRmdir(code, reqp);
3134 osi_Log1(afsd_logp, "CALL RemoveDir FAILURE, code 0x%x", code);
3136 osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
3139 lock_ObtainWrite(&dirop.scp->dirlock);
3140 dirop.lockType = CM_DIRLOCK_WRITE;
3142 lock_ObtainMutex(&dscp->mx);
3143 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3145 cm_dnlcRemove(dscp, namep);
3146 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, 0);
3148 lock_ReleaseMutex(&dscp->mx);
3151 if (cm_CheckDirOpForSingleChange(&dirop)) {
3152 cm_DirDeleteEntry(&dirop, namep);
3154 cm_BPlusDirDeleteEntry(&dirop, namep);
3158 cm_EndDirOp(&dirop);
3160 /* and return error code */
3164 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
3166 /* grab mutex on contents */
3167 lock_ObtainMutex(&scp->mx);
3169 /* reset the prefetch info */
3170 scp->prefetch.base.LowPart = 0; /* base */
3171 scp->prefetch.base.HighPart = 0;
3172 scp->prefetch.end.LowPart = 0; /* and end */
3173 scp->prefetch.end.HighPart = 0;
3175 /* release mutex on contents */
3176 lock_ReleaseMutex(&scp->mx);
3182 long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
3183 char *newNamep, cm_user_t *userp, cm_req_t *reqp)
3187 AFSFid oldDirAFSFid;
3188 AFSFid newDirAFSFid;
3190 AFSFetchStatus updatedOldDirStatus;
3191 AFSFetchStatus updatedNewDirStatus;
3194 struct rx_connection * callp;
3195 cm_dirOp_t oldDirOp;
3198 cm_dirOp_t newDirOp;
3200 /* before starting the RPC, mark that we're changing the directory data,
3201 * so that someone who does a chmod on the dir will wait until our call
3202 * completes. We do this in vnode order so that we don't deadlock,
3203 * which makes the code a little verbose.
3205 if (oldDscp == newDscp) {
3206 /* check for identical names */
3207 if (strcmp(oldNamep, newNamep) == 0)
3208 return CM_ERROR_RENAME_IDENTICAL;
3211 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3212 lock_ObtainMutex(&oldDscp->mx);
3213 cm_dnlcRemove(oldDscp, oldNamep);
3214 cm_dnlcRemove(oldDscp, newNamep);
3215 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3216 CM_SCACHESYNC_STOREDATA);
3217 lock_ReleaseMutex(&oldDscp->mx);
3219 cm_EndDirOp(&oldDirOp);
3223 /* two distinct dir vnodes */
3225 if (oldDscp->fid.cell != newDscp->fid.cell ||
3226 oldDscp->fid.volume != newDscp->fid.volume)
3227 return CM_ERROR_CROSSDEVLINK;
3229 /* shouldn't happen that we have distinct vnodes for two
3230 * different files, but could due to deliberate attack, or
3231 * stale info. Avoid deadlocks and quit now.
3233 if (oldDscp->fid.vnode == newDscp->fid.vnode)
3234 return CM_ERROR_CROSSDEVLINK;
3236 if (oldDscp->fid.vnode < newDscp->fid.vnode) {
3237 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3238 lock_ObtainMutex(&oldDscp->mx);
3239 cm_dnlcRemove(oldDscp, oldNamep);
3240 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3241 CM_SCACHESYNC_STOREDATA);
3242 lock_ReleaseMutex(&oldDscp->mx);
3244 cm_EndDirOp(&oldDirOp);
3246 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE, &newDirOp);
3247 lock_ObtainMutex(&newDscp->mx);
3248 cm_dnlcRemove(newDscp, newNamep);
3249 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3250 CM_SCACHESYNC_STOREDATA);
3251 lock_ReleaseMutex(&newDscp->mx);
3253 cm_EndDirOp(&newDirOp);
3255 /* cleanup first one */
3256 lock_ObtainMutex(&oldDscp->mx);
3257 cm_SyncOpDone(oldDscp, NULL,
3258 CM_SCACHESYNC_STOREDATA);
3259 lock_ReleaseMutex(&oldDscp->mx);
3260 cm_EndDirOp(&oldDirOp);
3265 /* lock the new vnode entry first */
3266 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE, &newDirOp);
3267 lock_ObtainMutex(&newDscp->mx);
3268 cm_dnlcRemove(newDscp, newNamep);
3269 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3270 CM_SCACHESYNC_STOREDATA);
3271 lock_ReleaseMutex(&newDscp->mx);
3273 cm_EndDirOp(&newDirOp);
3275 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3276 lock_ObtainMutex(&oldDscp->mx);
3277 cm_dnlcRemove(oldDscp, oldNamep);
3278 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3279 CM_SCACHESYNC_STOREDATA);
3280 lock_ReleaseMutex(&oldDscp->mx);
3282 cm_EndDirOp(&oldDirOp);
3284 /* cleanup first one */
3285 lock_ObtainMutex(&newDscp->mx);
3286 cm_SyncOpDone(newDscp, NULL,
3287 CM_SCACHESYNC_STOREDATA);
3288 lock_ReleaseMutex(&newDscp->mx);
3289 cm_EndDirOp(&newDirOp);
3293 } /* two distinct vnodes */
3300 /* try the RPC now */
3301 osi_Log2(afsd_logp, "CALL Rename old scp 0x%p new scp 0x%p",
3304 code = cm_ConnFromFID(&oldDscp->fid, userp, reqp, &connp);
3308 oldDirAFSFid.Volume = oldDscp->fid.volume;
3309 oldDirAFSFid.Vnode = oldDscp->fid.vnode;
3310 oldDirAFSFid.Unique = oldDscp->fid.unique;
3311 newDirAFSFid.Volume = newDscp->fid.volume;
3312 newDirAFSFid.Vnode = newDscp->fid.vnode;
3313 newDirAFSFid.Unique = newDscp->fid.unique;
3315 callp = cm_GetRxConn(connp);
3316 code = RXAFS_Rename(callp, &oldDirAFSFid, oldNamep,
3317 &newDirAFSFid, newNamep,
3318 &updatedOldDirStatus, &updatedNewDirStatus,
3320 rx_PutConnection(callp);
3322 } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
3323 &volSync, NULL, NULL, code));
3324 code = cm_MapRPCError(code, reqp);
3327 osi_Log1(afsd_logp, "CALL Rename FAILURE, code 0x%x", code);
3329 osi_Log0(afsd_logp, "CALL Rename SUCCESS");
3331 /* update the individual stat cache entries for the directories */
3333 lock_ObtainWrite(&oldDirOp.scp->dirlock);
3334 oldDirOp.lockType = CM_DIRLOCK_WRITE;
3336 lock_ObtainMutex(&oldDscp->mx);
3337 cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
3340 cm_MergeStatus(NULL, oldDscp, &updatedOldDirStatus, &volSync,
3342 lock_ReleaseMutex(&oldDscp->mx);
3345 if (cm_CheckDirOpForSingleChange(&oldDirOp)) {
3348 diropCode = cm_BPlusDirLookup(&oldDirOp, oldNamep, &fileFid);
3349 if (diropCode == CM_ERROR_INEXACT_MATCH)
3351 else if (diropCode == EINVAL)
3353 diropCode = cm_DirLookup(&oldDirOp, oldNamep, &fileFid);
3355 if (diropCode == 0) {
3357 diropCode = cm_DirCreateEntry(&oldDirOp, newNamep, &fileFid);
3359 cm_BPlusDirCreateEntry(&oldDirOp, newNamep, &fileFid);
3363 if (diropCode == 0) {
3364 diropCode = cm_DirDeleteEntry(&oldDirOp, oldNamep);
3366 cm_BPlusDirDeleteEntry(&oldDirOp, oldNamep);
3372 cm_EndDirOp(&oldDirOp);
3374 /* and update it for the new one, too, if necessary */
3377 lock_ObtainWrite(&newDirOp.scp->dirlock);
3378 newDirOp.lockType = CM_DIRLOCK_WRITE;
3380 lock_ObtainMutex(&newDscp->mx);
3381 cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
3383 cm_MergeStatus(NULL, newDscp, &updatedNewDirStatus, &volSync,
3385 lock_ReleaseMutex(&newDscp->mx);
3388 /* we only make the local change if we successfully made
3389 the change in the old directory AND there was only one
3390 change in the new directory */
3391 if (diropCode == 0 && cm_CheckDirOpForSingleChange(&newDirOp)) {
3392 cm_DirCreateEntry(&newDirOp, newNamep, &fileFid);
3394 cm_BPlusDirCreateEntry(&newDirOp, newNamep, &fileFid);
3398 cm_EndDirOp(&newDirOp);
3401 /* and return error code */
3405 /* Byte range locks:
3407 The OpenAFS Windows client has to fake byte range locks given no
3408 server side support for such locks. This is implemented as keyed
3409 byte range locks on the cache manager.
3411 Keyed byte range locks:
3413 Each cm_scache_t structure keeps track of a list of keyed locks.
3414 The key for a lock identifies an owner of a set of locks (referred
3415 to as a client). Each key is represented by a value. The set of
3416 key values used within a specific cm_scache_t structure form a
3417 namespace that has a scope of just that cm_scache_t structure. The
3418 same key value can be used with another cm_scache_t structure and
3419 correspond to a completely different client. However it is
3420 advantageous for the SMB or IFS layer to make sure that there is a
3421 1-1 mapping between client and keys over all cm_scache_t objects.
3423 Assume a client C has key Key(C) (although, since the scope of the
3424 key is a cm_scache_t, the key can be Key(C,S), where S is the
3425 cm_scache_t. But assume a 1-1 relation between keys and clients).
3426 A byte range (O,+L) denotes byte addresses (O) through (O+L-1)
3427 inclusive (a.k.a. [O,O+L-1]). The function Key(x) is implemented
3428 through cm_generateKey() function for both SMB and IFS.
3430 The list of locks for a cm_scache_t object S is maintained in
3431 S->fileLocks. The cache manager will set a lock on the AFS file
3432 server in order to assert the locks in S->fileLocks. If only
3433 shared locks are in place for S, then the cache manager will obtain
3434 a LockRead lock, while if there are any exclusive locks, it will
3435 obtain a LockWrite lock. If the exclusive locks are all released
3436 while the shared locks remain, then the cache manager will
3437 downgrade the lock from LockWrite to LockRead. Similarly, if an
3438 exclusive lock is obtained when only shared locks exist, then the
3439 cache manager will try to upgrade the lock from LockRead to
3442 Each lock L owned by client C maintains a key L->key such that
3443 L->key == Key(C), the effective range defined by L->LOffset and
3444 L->LLength such that the range of bytes affected by the lock is
3445 (L->LOffset, +L->LLength), a type maintained in L->LockType which
3446 is either exclusive or shared.
3450 A lock exists iff it is in S->fileLocks for some cm_scache_t
3451 S. Existing locks are in one of the following states: ACTIVE,
3452 WAITLOCK, WAITUNLOCK, LOST, DELETED.
3454 The following sections describe each lock and the associated
3457 1. ACTIVE: A lock L is ACTIVE iff the cache manager has asserted
3458 the lock with the AFS file server. This type of lock can be
3459 exercised by a client to read or write to the locked region (as
3462 1.1 ACTIVE->LOST: When the AFS file server fails to extend a
3463 server lock that was required to assert the lock. Before
3464 marking the lock as lost, the cache manager checks if the file
3465 has changed on the server. If the file has not changed, then
3466 the cache manager will attempt to obtain a new server lock
3467 that is sufficient to assert the client side locks for the
3468 file. If any of these fail, the lock is marked as LOST.
3469 Otherwise, it is left as ACTIVE.
3471 1.2 ACTIVE->DELETED: Lock is released.
3473 2. WAITLOCK: A lock is in a WAITLOCK state if the cache manager
3474 grants the lock but the lock is yet to be asserted with the AFS
3475 file server. Once the file server grants the lock, the state
3476 will transition to an ACTIVE lock.
3478 2.1 WAITLOCK->ACTIVE: The server granted the lock.
3480 2.2 WAITLOCK->DELETED: Lock is abandoned, or timed out during
3483 2.3 WAITLOCK->LOST: One or more locks from this client were
3484 marked as LOST. No further locks will be granted to this
3485 client until all lost locks are removed.
3487 3. WAITUNLOCK: A lock is in a WAITUNLOCK state if the cache manager
3488 receives a request for a lock that conflicts with an existing
3489 ACTIVE or WAITLOCK lock. The lock will be placed in the queue
3490 and will be granted at such time the conflicting locks are
3491 removed, at which point the state will transition to either
3494 3.1 WAITUNLOCK->ACTIVE: The conflicting lock was removed. The
3495 current serverLock is sufficient to assert this lock, or a
3496 sufficient serverLock is obtained.
3498 3.2 WAITUNLOCK->WAITLOCK: The conflicting lock was removed,
3499 however the required serverLock is yet to be asserted with the
3502 3.3 WAITUNLOCK->DELETED: The lock is abandoned, timed out or
3505 3.5 WAITUNLOCK->LOST: One or more locks from this client were
3506 marked as LOST. No further locks will be granted to this
3507 client until all lost locks are removed.
3509 4. LOST: A lock L is LOST if the server lock that was required to
3510 assert the lock could not be obtained or if it could not be
3511 extended, or if other locks by the same client were LOST.
3512 Essentially, once a lock is LOST, the contract between the cache
3513 manager and that specific client is no longer valid.
3515 The cache manager rechecks the server lock once every minute and
3516 extends it as appropriate. If this is not done for 5 minutes,
3517 the AFS file server will release the lock (the 5 minute timeout
3518 is based on current file server code and is fairly arbitrary).
3519 Once released, the lock cannot be re-obtained without verifying
3520 that the contents of the file hasn't been modified since the
3521 time the lock was released. Re-obtaining the lock without
3522 verifying this may lead to data corruption. If the lock can not
3523 be obtained safely, then all active locks for the cm_scache_t
3526 4.1 LOST->DELETED: The lock is released.
3528 5. DELETED: The lock is no longer relevant. Eventually, it will
3529 get removed from the cm_scache_t. In the meantime, it will be
3530 treated as if it does not exist.
3532 5.1 DELETED->not exist: The lock is removed from the
3535 The following are classifications of locks based on their state.
3537 6* A lock L is ACCEPTED if it is ACTIVE or WAITLOCK. These locks
3538 have been accepted by the cache manager, but may or may not have
3539 been granted back to the client.
3541 7* A lock L is QUEUED if it is ACTIVE, WAITLOCK or WAITUNLOCK.
3543 8* A lock L is WAITING if it is WAITLOCK or WAITUNLOCK.
3547 A client C can READ range (Offset,+Length) of a file represented by
3548 cm_scache_t S iff (1):
3550 1. for all _a_ in (Offset,+Length), all of the following is true:
3552 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3553 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3556 1.2 For each LOST lock L in S->fileLocks such that _a_ in
3557 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3560 (When locks are lost on an cm_scache_t, all locks are lost. By
3561 4.2 (below), if there is an exclusive LOST lock, then there
3562 can't be any overlapping ACTIVE locks.)
3564 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
3566 2. for all _a_ in (Offset,+Length), one of the following is true:
3568 2.1 Byte _a_ of S is unowned (as specified in 1.1) AND there
3569 does not exist a LOST lock L such that _a_ in
3570 (L->LOffset,+L->LLength).
3572 2.2 Byte _a_ of S is owned by C under lock L (as specified in
3573 1.2) AND L->LockType is exclusive.
3575 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
3577 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
3580 3.1 If L->LockType is exclusive then there does NOT exist a
3581 ACCEPTED lock M in S->fileLocks such that _a_ in
3582 (M->LOffset,+M->LLength).
3584 (If we count all QUEUED locks then we hit cases such as
3585 cascading waiting locks where the locks later on in the queue
3586 can be granted without compromising file integrity. On the
3587 other hand if only ACCEPTED locks are considered, then locks
3588 that were received earlier may end up waiting for locks that
3589 were received later to be unlocked. The choice of ACCEPTED
3590 locks was made to mimic the Windows byte range lock
3593 3.2 If L->LockType is shared then for each ACCEPTED lock M in
3594 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
3595 M->LockType is shared.
3597 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
3599 4.1 M->key != Key(C)
3601 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
3602 and (M->LOffset,+M->LLength) do not intersect.
3604 (Note: If a client loses a lock, it loses all locks.
3605 Subsequently, it will not be allowed to obtain any more locks
3606 until all existing LOST locks that belong to the client are
3607 released. Once all locks are released by a single client,
3608 there exists no further contract between the client and AFS
3609 about the contents of the file, hence the client can then
3610 proceed to obtain new locks and establish a new contract.
3612 This doesn't quite work as you think it should, because most
3613 applications aren't built to deal with losing locks they
3614 thought they once had. For now, we don't have a good
3615 solution to lost locks.
3617 Also, for consistency reasons, we have to hold off on
3618 granting locks that overlap exclusive LOST locks.)
3620 A client C can only unlock locks L in S->fileLocks which have
3623 The representation and invariants are as follows:
3625 - Each cm_scache_t structure keeps:
3627 - A queue of byte-range locks (cm_scache_t::fileLocks) which
3628 are of type cm_file_lock_t.
3630 - A record of the highest server-side lock that has been
3631 obtained for this object (cm_scache_t::serverLock), which is
3632 one of (-1), LockRead, LockWrite.
3634 - A count of ACCEPTED exclusive and shared locks that are in the
3635 queue (cm_scache_t::sharedLocks and
3636 cm_scache_t::exclusiveLocks)
3638 - Each cm_file_lock_t structure keeps:
3640 - The type of lock (cm_file_lock_t::LockType)
3642 - The key associated with the lock (cm_file_lock_t::key)
3644 - The offset and length of the lock (cm_file_lock_t::LOffset
3645 and cm_file_lock_t::LLength)
3647 - The state of the lock.
3649 - Time of issuance or last successful extension
3651 Semantic invariants:
3653 I1. The number of ACCEPTED locks in S->fileLocks are
3654 (S->sharedLocks + S->exclusiveLocks)
3656 External invariants:
3658 I3. S->serverLock is the lock that we have asserted with the
3659 AFS file server for this cm_scache_t.
3661 I4. S->serverLock == LockRead iff there is at least one ACTIVE
3662 shared lock, but no ACTIVE exclusive locks.
3664 I5. S->serverLock == LockWrite iff there is at least one ACTIVE
3667 I6. If L is a LOST lock, then for each lock M in S->fileLocks,
3668 M->key == L->key IMPLIES M is LOST or DELETED.
3673 #define IS_LOCK_ACTIVE(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == 0)
3675 #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)
3677 #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)
3679 #define IS_LOCK_LOST(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_LOST)
3681 #define IS_LOCK_DELETED(lockp) (((lockp)->flags & CM_FILELOCK_FLAG_DELETED) == CM_FILELOCK_FLAG_DELETED)
3684 #define IS_LOCK_ACCEPTED(lockp) (IS_LOCK_ACTIVE(lockp) || IS_LOCK_WAITLOCK(lockp))
3687 #define IS_LOCK_CLIENTONLY(lockp) ((((lockp)->scp->flags & CM_SCACHEFLAG_RO) == CM_SCACHEFLAG_RO) || (((lockp)->flags & CM_FILELOCK_FLAG_CLIENTONLY) == CM_FILELOCK_FLAG_CLIENTONLY))
3690 #define INTERSECT_RANGE(r1,r2) (((r2).offset+(r2).length) > (r1).offset && ((r1).offset +(r1).length) > (r2).offset)
3693 #define CONTAINS_RANGE(r1,r2) (((r2).offset+(r2).length) <= ((r1).offset+(r1).length) && (r1).offset <= (r2).offset)
3695 #if defined(VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS) && !defined(LOCK_TESTING)
3696 #define SCP_SUPPORTS_BRLOCKS(scp) ((scp)->cbServerp && ((scp)->cbServerp->capabilities & VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS))
3698 #define SCP_SUPPORTS_BRLOCKS(scp) (1)
3701 #define SERVERLOCKS_ENABLED(scp) (!((scp)->flags & CM_SCACHEFLAG_RO) && cm_enableServerLocks && SCP_SUPPORTS_BRLOCKS(scp))
3703 #if defined(VICED_CAPABILITY_WRITELOCKACL)
3704 #define SCP_SUPPORTS_WRITELOCKACL(scp) ((scp)->cbServerp && ((scp->cbServerp->capabilities & VICED_CAPABILITY_WRITELOCKACL)))
3706 #define SCP_SUPPORTS_WRITELOCKACL(scp) (0)
3708 /* This should really be defined in any build that this code is being
3710 #error VICED_CAPABILITY_WRITELOCKACL not defined.
3713 static void cm_LockRangeSubtract(cm_range_t * pos, const cm_range_t * neg)
3715 afs_int64 int_begin;
3718 int_begin = MAX(pos->offset, neg->offset);
3719 int_end = MIN(pos->offset+pos->length, neg->offset+neg->length);
3721 if (int_begin < int_end) {
3722 if (int_begin == pos->offset) {
3723 pos->length = pos->offset + pos->length - int_end;
3724 pos->offset = int_end;
3725 } else if (int_end == pos->offset + pos->length) {
3726 pos->length = int_begin - pos->offset;
3729 /* We only subtract ranges if the resulting range is
3730 contiguous. If we try to support non-contigous ranges, we
3731 aren't actually improving performance. */
3735 /* Called with scp->mx held. Returns 0 if all is clear to read the
3736 specified range by the client identified by key.
3738 long cm_LockCheckRead(cm_scache_t *scp,
3739 LARGE_INTEGER LOffset,
3740 LARGE_INTEGER LLength,
3743 #ifndef ADVISORY_LOCKS
3745 cm_file_lock_t *fileLock;
3749 int substract_ranges = FALSE;
3751 range.offset = LOffset.QuadPart;
3752 range.length = LLength.QuadPart;
3756 1. for all _a_ in (Offset,+Length), all of the following is true:
3758 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3759 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3762 1.2 For each LOST lock L in S->fileLocks such that _a_ in
3763 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3768 lock_ObtainRead(&cm_scacheLock);
3770 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
3772 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
3774 if (INTERSECT_RANGE(range, fileLock->range)) {
3775 if (IS_LOCK_ACTIVE(fileLock)) {
3776 if (fileLock->key == key) {
3778 /* If there is an active lock for this client, it
3779 is safe to substract ranges.*/
3780 cm_LockRangeSubtract(&range, &fileLock->range);
3781 substract_ranges = TRUE;
3783 if (fileLock->lockType != LockRead) {
3784 code = CM_ERROR_LOCK_CONFLICT;
3788 /* even if the entire range is locked for reading,
3789 we still can't grant the lock at this point
3790 because the client may have lost locks. That
3791 is, unless we have already seen an active lock
3792 belonging to the client, in which case there
3793 can't be any lost locks for this client. */
3794 if (substract_ranges)
3795 cm_LockRangeSubtract(&range, &fileLock->range);
3797 } else if (IS_LOCK_LOST(fileLock) &&
3798 (fileLock->key == key || fileLock->lockType == LockWrite)) {
3799 code = CM_ERROR_BADFD;
3805 lock_ReleaseRead(&cm_scacheLock);
3807 osi_Log4(afsd_logp, "cm_LockCheckRead scp 0x%x offset %d length %d code 0x%x",
3808 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
3819 /* Called with scp->mx held. Returns 0 if all is clear to write the
3820 specified range by the client identified by key.
3822 long cm_LockCheckWrite(cm_scache_t *scp,
3823 LARGE_INTEGER LOffset,
3824 LARGE_INTEGER LLength,
3827 #ifndef ADVISORY_LOCKS
3829 cm_file_lock_t *fileLock;
3834 range.offset = LOffset.QuadPart;
3835 range.length = LLength.QuadPart;
3838 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
3840 2. for all _a_ in (Offset,+Length), one of the following is true:
3842 2.1 Byte _a_ of S is unowned AND there does not exist a LOST
3843 lock L such that _a_ in (L->LOffset,+L->LLength).
3845 2.2 Byte _a_ of S is owned by C under lock L AND L->LockType is
3849 lock_ObtainRead(&cm_scacheLock);
3851 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
3853 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
3855 if (INTERSECT_RANGE(range, fileLock->range)) {
3856 if (IS_LOCK_ACTIVE(fileLock)) {
3857 if (fileLock->key == key) {
3858 if (fileLock->lockType == LockWrite) {
3860 /* if there is an active lock for this client, it
3861 is safe to substract ranges */
3862 cm_LockRangeSubtract(&range, &fileLock->range);
3864 code = CM_ERROR_LOCK_CONFLICT;
3868 code = CM_ERROR_LOCK_CONFLICT;
3871 } else if (IS_LOCK_LOST(fileLock)) {
3872 code = CM_ERROR_BADFD;
3878 lock_ReleaseRead(&cm_scacheLock);
3880 osi_Log4(afsd_logp, "cm_LockCheckWrite scp 0x%x offset %d length %d code 0x%x",
3881 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
3893 static void cm_LockMarkSCacheLost(cm_scache_t * scp);
3895 /* Called with cm_scacheLock write locked */
3896 static cm_file_lock_t * cm_GetFileLock(void) {
3899 l = (cm_file_lock_t *) cm_freeFileLocks;
3901 osi_QRemove(&cm_freeFileLocks, &l->q);
3903 l = malloc(sizeof(cm_file_lock_t));
3907 memset(l, 0, sizeof(cm_file_lock_t));
3912 /* Called with cm_scacheLock write locked */
3913 static void cm_PutFileLock(cm_file_lock_t *l) {
3914 osi_QAdd(&cm_freeFileLocks, &l->q);
3917 /* called with scp->mx held. May release it during processing, but
3918 leaves it held on exit. */
3919 long cm_IntSetLock(cm_scache_t * scp, cm_user_t * userp, int lockType,
3925 struct rx_connection * callp;
3928 tfid.Volume = scp->fid.volume;
3929 tfid.Vnode = scp->fid.vnode;
3930 tfid.Unique = scp->fid.unique;
3933 osi_Log2(afsd_logp, "CALL SetLock scp 0x%p for lock %d", scp, lockType);
3935 lock_ReleaseMutex(&scp->mx);
3938 code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
3942 callp = cm_GetRxConn(connp);
3943 code = RXAFS_SetLock(callp, &tfid, lockType,
3945 rx_PutConnection(callp);
3947 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
3950 code = cm_MapRPCError(code, reqp);
3952 osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
3954 osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
3957 lock_ObtainMutex(&scp->mx);
3962 /* called with scp->mx held. Releases it during processing */
3963 long cm_IntReleaseLock(cm_scache_t * scp, cm_user_t * userp,
3969 struct rx_connection * callp;
3972 tfid.Volume = scp->fid.volume;
3973 tfid.Vnode = scp->fid.vnode;
3974 tfid.Unique = scp->fid.unique;
3977 lock_ReleaseMutex(&scp->mx);
3979 osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
3982 code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
3986 callp = cm_GetRxConn(connp);
3987 code = RXAFS_ReleaseLock(callp, &tfid, &volSync);
3988 rx_PutConnection(callp);
3990 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
3992 code = cm_MapRPCError(code, reqp);
3995 "CALL ReleaseLock FAILURE, code 0x%x", code);
3998 "CALL ReleaseLock SUCCESS");
4000 lock_ObtainMutex(&scp->mx);
4005 /* called with scp->mx held. May release it during processing, but
4006 will exit with lock held.
4010 - 0 if the user has permission to get the specified lock for the scp
4012 - CM_ERROR_NOACCESS if not
4014 Any other error from cm_SyncOp will be sent down untranslated.
4016 If CM_ERROR_NOACCESS is returned and lock_type is LockRead, then
4017 phas_insert (if non-NULL) will receive a boolean value indicating
4018 whether the user has INSERT permission or not.
4020 long cm_LockCheckPerms(cm_scache_t * scp,
4027 long code = 0, code2 = 0;
4029 /* lock permissions are slightly tricky because of the 'i' bit.
4030 If the user has PRSFS_LOCK, she can read-lock the file. If the
4031 user has PRSFS_WRITE, she can write-lock the file. However, if
4032 the user has PRSFS_INSERT, then she can write-lock new files,
4033 but not old ones. Since we don't have information about
4034 whether a file is new or not, we assume that if the user owns
4035 the scp, then she has the permissions that are granted by
4038 osi_Log3(afsd_logp, "cm_LockCheckPerms for scp[0x%p] type[%d] user[0x%p]",
4039 scp, lock_type, userp);
4041 if (lock_type == LockRead)
4042 rights |= PRSFS_LOCK;
4043 else if (lock_type == LockWrite)
4044 rights |= PRSFS_WRITE | PRSFS_LOCK;
4052 *phas_insert = FALSE;
4054 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
4055 CM_SCACHESYNC_GETSTATUS |
4056 CM_SCACHESYNC_NEEDCALLBACK);
4058 if (phas_insert && scp->creator == userp) {
4060 /* If this file was created by the user, then we check for
4061 PRSFS_INSERT. If the file server is recent enough, then
4062 this should be sufficient for her to get a write-lock (but
4063 not necessarily a read-lock). VICED_CAPABILITY_WRITELOCKACL
4064 indicates whether a file server supports getting write
4065 locks when the user only has PRSFS_INSERT.
4067 If the file was not created by the user we skip the check
4068 because the INSERT bit will not apply to this user even
4072 code2 = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
4073 CM_SCACHESYNC_GETSTATUS |
4074 CM_SCACHESYNC_NEEDCALLBACK);
4076 if (code2 == CM_ERROR_NOACCESS) {
4077 osi_Log0(afsd_logp, "cm_LockCheckPerms user has no INSERT bits");
4079 *phas_insert = TRUE;
4080 osi_Log0(afsd_logp, "cm_LockCheckPerms user has INSERT bits");
4084 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
4086 osi_Log1(afsd_logp, "cm_LockCheckPerms returning code %d", code);
4091 /* called with scp->mx held */
4092 long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
4093 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
4095 int allowWait, cm_user_t *userp, cm_req_t *reqp,
4096 cm_file_lock_t **lockpp)
4099 int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
4100 cm_file_lock_t *fileLock;
4103 int wait_unlock = FALSE;
4104 int force_client_lock = FALSE;
4106 osi_Log4(afsd_logp, "cm_Lock scp 0x%x type 0x%x offset %d length %d",
4107 scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
4108 osi_Log3(afsd_logp, "... allowWait %d key 0x%x:%x", allowWait,
4109 (unsigned long)(key >> 32), (unsigned long)(key & 0xffffffff));
4112 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
4114 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
4117 3.1 If L->LockType is exclusive then there does NOT exist a
4118 ACCEPTED lock M in S->fileLocks such that _a_ in
4119 (M->LOffset,+M->LLength).
4121 3.2 If L->LockType is shared then for each ACCEPTED lock M in
4122 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
4123 M->LockType is shared.
4125 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
4127 4.1 M->key != Key(C)
4129 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
4130 and (M->LOffset,+M->LLength) do not intersect.
4133 range.offset = LOffset.QuadPart;
4134 range.length = LLength.QuadPart;
4136 lock_ObtainRead(&cm_scacheLock);
4138 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4140 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4142 if (IS_LOCK_LOST(fileLock)) {
4143 if (fileLock->key == key) {
4144 code = CM_ERROR_BADFD;
4146 } else if (fileLock->lockType == LockWrite && INTERSECT_RANGE(range, fileLock->range)) {
4147 code = CM_ERROR_WOULDBLOCK;
4153 /* we don't need to check for deleted locks here since deleted
4154 locks are dequeued from scp->fileLocks */
4155 if (IS_LOCK_ACCEPTED(fileLock) &&
4156 INTERSECT_RANGE(range, fileLock->range)) {
4158 if ((sLockType & LOCKING_ANDX_SHARED_LOCK) == 0 ||
4159 fileLock->lockType != LockRead) {
4161 code = CM_ERROR_WOULDBLOCK;
4167 lock_ReleaseRead(&cm_scacheLock);
4169 if (code == 0 && SERVERLOCKS_ENABLED(scp)) {
4170 if (Which == scp->serverLock ||
4171 (Which == LockRead && scp->serverLock == LockWrite)) {
4175 /* we already have the lock we need */
4176 osi_Log3(afsd_logp, " we already have the correct lock. exclusives[%d], shared[%d], serverLock[%d]",
4177 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4179 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4181 /* special case: if we don't have permission to read-lock
4182 the file, then we force a clientside lock. This is to
4183 compensate for applications that obtain a read-lock for
4184 reading files off of directories that don't grant
4185 read-locks to the user. */
4186 if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4188 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4189 osi_Log0(afsd_logp, " User has no read-lock perms, but has INSERT perms.");
4192 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4193 force_client_lock = TRUE;
4197 } else if ((scp->exclusiveLocks > 0) ||
4198 (scp->sharedLocks > 0 && scp->serverLock != LockRead)) {
4201 /* We are already waiting for some other lock. We should
4202 wait for the daemon to catch up instead of generating a
4203 flood of SetLock calls. */
4204 osi_Log3(afsd_logp, " already waiting for other lock. exclusives[%d], shared[%d], serverLock[%d]",
4205 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4207 /* see if we have permission to create the lock in the
4209 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4211 code = CM_ERROR_WOULDBLOCK;
4212 else if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4214 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4216 " User has no read-lock perms, but has INSERT perms.");
4217 code = CM_ERROR_WOULDBLOCK;
4220 " User has no read-lock perms. Forcing client-side lock");
4221 force_client_lock = TRUE;
4225 /* leave any other codes as-is */
4229 int check_data_version = FALSE;
4232 /* first check if we have permission to elevate or obtain
4234 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4236 if (code == CM_ERROR_NOACCESS && Which == LockRead &&
4237 (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp))) {
4238 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4239 force_client_lock = TRUE;
4244 /* has_insert => (Which == LockRead, code == CM_ERROR_NOACCESS) */
4246 if (scp->serverLock == LockRead && Which == LockWrite) {
4248 /* We want to escalate the lock to a LockWrite.
4249 * Unfortunately that's not really possible without
4250 * letting go of the current lock. But for now we do
4254 " attempting to UPGRADE from LockRead to LockWrite.");
4256 " dataVersion on scp: %d", scp->dataVersion);
4258 /* we assume at this point (because scp->serverLock
4259 was valid) that we had a valid server lock. */
4260 scp->lockDataVersion = scp->dataVersion;
4261 check_data_version = TRUE;
4263 code = cm_IntReleaseLock(scp, userp, reqp);
4266 /* We couldn't release the lock */
4269 scp->serverLock = -1;
4273 /* We need to obtain a server lock of type Which in order
4274 * to assert this file lock */
4275 #ifndef AGGRESSIVE_LOCKS
4278 newLock = LockWrite;
4281 code = cm_IntSetLock(scp, userp, newLock, reqp);
4283 #ifdef AGGRESSIVE_LOCKS
4284 if ((code == CM_ERROR_WOULDBLOCK ||
4285 code == CM_ERROR_NOACCESS) && newLock != Which) {
4286 /* we wanted LockRead. We tried LockWrite. Now try
4291 osi_assert(newLock == LockRead);
4293 code = cm_IntSetLock(scp, userp, newLock, reqp);
4297 if (code == CM_ERROR_NOACCESS) {
4298 if (Which == LockRead) {
4299 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4301 /* We requested a read-lock, but we have permission to
4302 * get a write-lock. Try that */
4304 tcode = cm_LockCheckPerms(scp, LockWrite, userp, reqp, NULL);
4307 newLock = LockWrite;
4309 osi_Log0(afsd_logp, " User has 'i' perms and the request was for a LockRead. Trying to get a LockWrite instead");
4311 code = cm_IntSetLock(scp, userp, newLock, reqp);
4314 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4315 force_client_lock = TRUE;
4317 } else if (Which == LockWrite &&
4318 scp->creator == userp && !SCP_SUPPORTS_WRITELOCKACL(scp)) {
4321 /* Special case: if the lock request was for a
4322 * LockWrite and the user owns the file and we weren't
4323 * allowed to obtain the serverlock, we either lost a
4324 * race (the permissions changed from under us), or we
4325 * have 'i' bits, but we aren't allowed to lock the
4328 /* check if we lost a race... */
4329 tcode = cm_LockCheckPerms(scp, Which, userp, reqp, NULL);
4332 osi_Log0(afsd_logp, " User has 'i' perms but can't obtain write locks. Using client-side locks.");
4333 force_client_lock = TRUE;
4338 if (code == 0 && check_data_version &&
4339 scp->dataVersion != scp->lockDataVersion) {
4340 /* We lost a race. Although we successfully obtained
4341 * a lock, someone modified the file in between. The
4342 * locks have all been technically lost. */
4345 " Data version mismatch while upgrading lock.");
4347 " Data versions before=%d, after=%d",
4348 scp->lockDataVersion,
4351 " Releasing stale lock for scp 0x%x", scp);
4353 code = cm_IntReleaseLock(scp, userp, reqp);
4355 scp->serverLock = -1;
4357 code = CM_ERROR_INVAL;
4358 } else if (code == 0) {
4359 scp->serverLock = newLock;
4360 scp->lockDataVersion = scp->dataVersion;
4364 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4365 scp->serverLock == -1) {
4366 /* Oops. We lost the lock. */
4367 cm_LockMarkSCacheLost(scp);
4370 } else if (code == 0) { /* server locks not enabled */
4372 " Skipping server lock for scp");
4377 if (code != 0 && !force_client_lock) {
4378 /* Special case error translations
4380 Applications don't expect certain errors from a
4381 LockFile/UnlockFile call. We need to translate some error
4382 code to codes that apps expect and handle. */
4384 /* We shouldn't actually need to handle this case since we
4385 simulate locks for RO scps anyway. */
4386 if (code == CM_ERROR_READONLY) {
4387 osi_Log0(afsd_logp, " Reinterpreting CM_ERROR_READONLY as CM_ERROR_NOACCESS");
4388 code = CM_ERROR_NOACCESS;
4392 if (code == 0 || (code == CM_ERROR_WOULDBLOCK && allowWait) ||
4393 force_client_lock) {
4395 /* clear the error if we are forcing a client lock, so we
4396 don't get confused later. */
4397 if (force_client_lock && code != CM_ERROR_WOULDBLOCK)
4400 lock_ObtainWrite(&cm_scacheLock);
4401 fileLock = cm_GetFileLock();
4402 lock_ReleaseWrite(&cm_scacheLock);
4404 fileLock->fid = scp->fid;
4406 fileLock->key = key;
4407 fileLock->lockType = Which;
4409 fileLock->userp = userp;
4410 fileLock->range = range;
4411 fileLock->flags = (code == 0 ? 0 :
4413 CM_FILELOCK_FLAG_WAITUNLOCK :
4414 CM_FILELOCK_FLAG_WAITLOCK));
4416 if (force_client_lock || !SERVERLOCKS_ENABLED(scp))
4417 fileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
4419 fileLock->lastUpdate = (code == 0 && !force_client_lock) ? time(NULL) : 0;
4421 lock_ObtainWrite(&cm_scacheLock);
4422 osi_QAddT(&scp->fileLocksH, &scp->fileLocksT, &fileLock->fileq);
4423 cm_HoldSCacheNoLock(scp);
4424 fileLock->scp = scp;
4425 osi_QAdd(&cm_allFileLocks, &fileLock->q);
4426 lock_ReleaseWrite(&cm_scacheLock);
4432 if (IS_LOCK_CLIENTONLY(fileLock)) {
4434 } else if (IS_LOCK_ACCEPTED(fileLock)) {
4435 if (Which == LockRead)
4438 scp->exclusiveLocks++;
4442 "cm_Lock Lock added 0x%p flags 0x%x to scp [0x%p]",
4443 fileLock, fileLock->flags, scp);
4445 " exclusives[%d] shared[%d] client[%d] serverLock[%d]",
4446 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4447 (int)(signed char) scp->serverLock);
4450 "cm_Lock Rejecting lock (code = 0x%x)", code);
4456 static int cm_KeyEquals(cm_key_t k1, cm_key_t k2, int flags);
4458 /* Called with scp->mx held */
4459 long cm_UnlockByKey(cm_scache_t * scp,
4466 cm_file_lock_t *fileLock;
4467 osi_queue_t *q, *qn;
4470 osi_Log4(afsd_logp, "cm_UnlockByKey scp 0x%p key 0x%x:%x flags=0x%x",
4472 (unsigned long)(key >> 32),
4473 (unsigned long)(key & 0xffffffff),
4476 lock_ObtainWrite(&cm_scacheLock);
4478 for (q = scp->fileLocksH; q; q = qn) {
4481 fileLock = (cm_file_lock_t *)
4482 ((char *) q - offsetof(cm_file_lock_t, fileq));
4485 osi_Log4(afsd_logp, " Checking lock[0x%x] range[%d,+%d] type[%d]",
4487 (unsigned long) fileLock->range.offset,
4488 (unsigned long) fileLock->range.length,
4489 fileLock->lockType);
4490 osi_Log3(afsd_logp, " key[0x%x:%x] flags[0x%x]",
4491 (unsigned long)(fileLock->key >> 32),
4492 (unsigned long)(fileLock->key & 0xffffffff),
4495 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
4496 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
4497 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4499 fileLock->fid.volume,
4500 fileLock->fid.vnode,
4501 fileLock->fid.unique);
4502 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4503 fileLock->scp->fid.cell,
4504 fileLock->scp->fid.volume,
4505 fileLock->scp->fid.vnode,
4506 fileLock->scp->fid.unique);
4511 if (!IS_LOCK_DELETED(fileLock) &&
4512 cm_KeyEquals(fileLock->key, key, flags)) {
4513 osi_Log3(afsd_logp, "...Unlock range [%d,+%d] type %d",
4514 fileLock->range.offset,
4515 fileLock->range.length,
4516 fileLock->lockType);
4518 if (scp->fileLocksT == q)
4519 scp->fileLocksT = osi_QPrev(q);
4520 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
4522 if (IS_LOCK_CLIENTONLY(fileLock)) {
4524 } else if (IS_LOCK_ACCEPTED(fileLock)) {
4525 if (fileLock->lockType == LockRead)
4528 scp->exclusiveLocks--;
4531 fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
4533 cm_ReleaseUser(fileLock->userp);
4534 cm_ReleaseSCacheNoLock(scp);
4536 fileLock->userp = NULL;
4537 fileLock->scp = NULL;
4543 lock_ReleaseWrite(&cm_scacheLock);
4545 if (n_unlocks == 0) {
4546 osi_Log0(afsd_logp, "cm_UnlockByKey no locks found");
4547 osi_Log3(afsd_logp, " Leaving scp with exclusives[%d], shared[%d], serverLock[%d]",
4548 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4553 osi_Log1(afsd_logp, "cm_UnlockByKey done with %d locks", n_unlocks);
4555 osi_assertx(scp->sharedLocks >= 0, "scp->sharedLocks < 0");
4556 osi_assertx(scp->exclusiveLocks >= 0, "scp->exclusiveLocks < 0");
4557 osi_assertx(scp->clientLocks >= 0, "scp->clientLocks < 0");
4559 if (!SERVERLOCKS_ENABLED(scp)) {
4560 osi_Log0(afsd_logp, " Skipping server lock for scp");
4564 /* Ideally we would go through the rest of the locks to determine
4565 * if one or more locks that were formerly in WAITUNLOCK can now
4566 * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
4567 * scp->sharedLocks accordingly. However, the retrying of locks
4568 * in that manner is done cm_RetryLock() manually.
4571 if (scp->serverLock == LockWrite &&
4572 scp->exclusiveLocks == 0 &&
4573 scp->sharedLocks > 0) {
4575 /* The serverLock should be downgraded to LockRead */
4576 osi_Log0(afsd_logp, " DOWNGRADE lock from LockWrite to LockRead");
4578 /* since scp->serverLock looked sane, we are going to assume
4579 that we have a valid server lock. */
4580 scp->lockDataVersion = scp->dataVersion;
4581 osi_Log1(afsd_logp, " dataVersion on scp = %d", scp->dataVersion);
4583 code = cm_IntReleaseLock(scp, userp, reqp);
4586 /* so we couldn't release it. Just let the lock be for now */
4590 scp->serverLock = -1;
4593 code = cm_IntSetLock(scp, userp, LockRead, reqp);
4595 if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
4596 scp->serverLock = LockRead;
4597 } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
4598 /* We lost a race condition. Although we have a valid
4599 lock on the file, the data has changed and essentially
4600 we have lost the lock we had during the transition. */
4602 osi_Log0(afsd_logp, "Data version mismatch during lock downgrade");
4603 osi_Log2(afsd_logp, " Data versions before=%d, after=%d",
4604 scp->lockDataVersion,
4607 code = cm_IntReleaseLock(scp, userp, reqp);
4609 code = CM_ERROR_INVAL;
4610 scp->serverLock = -1;
4614 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4615 (scp->serverLock == -1)) {
4617 cm_LockMarkSCacheLost(scp);
4620 /* failure here has no bearing on the return value of
4624 } else if (scp->serverLock != (-1) &&
4625 scp->exclusiveLocks == 0 &&
4626 scp->sharedLocks == 0) {
4627 /* The serverLock should be released entirely */
4629 code = cm_IntReleaseLock(scp, userp, reqp);
4632 scp->serverLock = (-1);
4637 osi_Log1(afsd_logp, "cm_UnlockByKey code 0x%x", code);
4638 osi_Log4(afsd_logp, " Leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
4639 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4640 (int)(signed char) scp->serverLock);
4645 long cm_Unlock(cm_scache_t *scp,
4646 unsigned char sLockType,
4647 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
4653 int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
4654 cm_file_lock_t *fileLock;
4656 int release_userp = FALSE;
4658 osi_Log4(afsd_logp, "cm_Unlock scp 0x%p type 0x%x offset %d length %d",
4659 scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
4660 osi_Log2(afsd_logp, "... key 0x%x:%x",
4661 (unsigned long) (key >> 32), (unsigned long) (key & 0xffffffff));
4663 lock_ObtainRead(&cm_scacheLock);
4665 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4666 fileLock = (cm_file_lock_t *)
4667 ((char *) q - offsetof(cm_file_lock_t, fileq));
4670 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
4671 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
4672 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4674 fileLock->fid.volume,
4675 fileLock->fid.vnode,
4676 fileLock->fid.unique);
4677 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4678 fileLock->scp->fid.cell,
4679 fileLock->scp->fid.volume,
4680 fileLock->scp->fid.vnode,
4681 fileLock->scp->fid.unique);
4685 if (!IS_LOCK_DELETED(fileLock) &&
4686 fileLock->key == key &&
4687 fileLock->range.offset == LOffset.QuadPart &&
4688 fileLock->range.length == LLength.QuadPart) {
4694 osi_Log0(afsd_logp, "cm_Unlock lock not found; failure");
4696 lock_ReleaseRead(&cm_scacheLock);
4698 /* The lock didn't exist anyway. *shrug* */
4702 lock_ReleaseRead(&cm_scacheLock);
4704 /* discard lock record */
4705 lock_ObtainWrite(&cm_scacheLock);
4706 if (scp->fileLocksT == q)
4707 scp->fileLocksT = osi_QPrev(q);
4708 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
4711 * Don't delete it here; let the daemon delete it, to simplify
4712 * the daemon's traversal of the list.
4715 if (IS_LOCK_CLIENTONLY(fileLock)) {
4717 } else if (IS_LOCK_ACCEPTED(fileLock)) {
4718 if (fileLock->lockType == LockRead)
4721 scp->exclusiveLocks--;
4724 fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
4725 if (userp != NULL) {
4726 cm_ReleaseUser(fileLock->userp);
4728 userp = fileLock->userp;
4729 release_userp = TRUE;
4731 fileLock->userp = NULL;
4732 cm_ReleaseSCacheNoLock(scp);
4733 fileLock->scp = NULL;
4734 lock_ReleaseWrite(&cm_scacheLock);
4736 if (!SERVERLOCKS_ENABLED(scp)) {
4737 osi_Log0(afsd_logp, " Skipping server locks for scp");
4741 /* Ideally we would go through the rest of the locks to determine
4742 * if one or more locks that were formerly in WAITUNLOCK can now
4743 * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
4744 * scp->sharedLocks accordingly. However, the retrying of locks
4745 * in that manner is done cm_RetryLock() manually.
4748 if (scp->serverLock == LockWrite &&
4749 scp->exclusiveLocks == 0 &&
4750 scp->sharedLocks > 0) {
4752 /* The serverLock should be downgraded to LockRead */
4753 osi_Log0(afsd_logp, " DOWNGRADE lock from LockWrite to LockRead");
4755 /* Since we already had a lock, we assume that there is a
4756 valid server lock. */
4757 scp->lockDataVersion = scp->dataVersion;
4758 osi_Log1(afsd_logp, " dataVersion on scp is %d", scp->dataVersion);
4760 /* before we downgrade, make sure that we have enough
4761 permissions to get the read lock. */
4762 code = cm_LockCheckPerms(scp, LockRead, userp, reqp, NULL);
4765 osi_Log0(afsd_logp, " SKIPPING downgrade because user doesn't have perms to get downgraded lock");
4771 code = cm_IntReleaseLock(scp, userp, reqp);
4774 /* so we couldn't release it. Just let the lock be for now */
4778 scp->serverLock = -1;
4781 code = cm_IntSetLock(scp, userp, LockRead, reqp);
4783 if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
4784 scp->serverLock = LockRead;
4785 } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
4786 /* Lost a race. We obtained a new lock, but that is
4787 meaningless since someone modified the file
4791 "Data version mismatch while downgrading lock");
4793 " Data versions before=%d, after=%d",
4794 scp->lockDataVersion,
4797 code = cm_IntReleaseLock(scp, userp, reqp);
4799 scp->serverLock = -1;
4800 code = CM_ERROR_INVAL;
4804 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4805 (scp->serverLock == -1)) {
4807 cm_LockMarkSCacheLost(scp);
4810 /* failure here has no bearing on the return value of
4814 } else if (scp->serverLock != (-1) &&
4815 scp->exclusiveLocks == 0 &&
4816 scp->sharedLocks == 0) {
4817 /* The serverLock should be released entirely */
4819 code = cm_IntReleaseLock(scp, userp, reqp);
4822 scp->serverLock = (-1);
4827 cm_ReleaseUser(userp);
4831 osi_Log1(afsd_logp, "cm_Unlock code 0x%x", code);
4832 osi_Log4(afsd_logp, " leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
4833 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4834 (int)(signed char) scp->serverLock);
4839 /* called with scp->mx held */
4840 static void cm_LockMarkSCacheLost(cm_scache_t * scp)
4842 cm_file_lock_t *fileLock;
4845 osi_Log1(afsd_logp, "cm_LockMarkSCacheLost scp 0x%x", scp);
4848 /* With the current code, we can't lose a lock on a RO scp */
4849 osi_assert(!(scp->flags & CM_SCACHEFLAG_RO));
4852 /* cm_scacheLock needed because we are modifying fileLock->flags */
4853 lock_ObtainWrite(&cm_scacheLock);
4855 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4857 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4859 if (IS_LOCK_ACTIVE(fileLock) &&
4860 !IS_LOCK_CLIENTONLY(fileLock)) {
4861 if (fileLock->lockType == LockRead)
4864 scp->exclusiveLocks--;
4866 fileLock->flags |= CM_FILELOCK_FLAG_LOST;
4870 scp->serverLock = -1;
4871 scp->lockDataVersion = -1;
4872 lock_ReleaseWrite(&cm_scacheLock);
4875 /* Called with no relevant locks held */
4876 void cm_CheckLocks()
4878 osi_queue_t *q, *nq;
4879 cm_file_lock_t *fileLock;
4885 struct rx_connection * callp;
4890 lock_ObtainWrite(&cm_scacheLock);
4892 cm_lockRefreshCycle++;
4894 osi_Log1(afsd_logp, "cm_CheckLocks starting lock check cycle %d", cm_lockRefreshCycle);
4896 for (q = cm_allFileLocks; q; q = nq) {
4897 fileLock = (cm_file_lock_t *) q;
4901 if (IS_LOCK_DELETED(fileLock)) {
4903 osi_QRemove(&cm_allFileLocks, q);
4904 cm_PutFileLock(fileLock);
4906 } else if (IS_LOCK_ACTIVE(fileLock) && !IS_LOCK_CLIENTONLY(fileLock)) {
4908 /* Server locks must have been enabled for us to have
4909 received an active non-client-only lock. */
4910 osi_assert(cm_enableServerLocks);
4912 scp = fileLock->scp;
4913 osi_assert(scp != NULL);
4915 cm_HoldSCacheNoLock(scp);
4918 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
4919 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
4920 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4922 fileLock->fid.volume,
4923 fileLock->fid.vnode,
4924 fileLock->fid.unique);
4925 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4926 fileLock->scp->fid.cell,
4927 fileLock->scp->fid.volume,
4928 fileLock->scp->fid.vnode,
4929 fileLock->scp->fid.unique);
4933 /* Server locks are extended once per scp per refresh
4935 if (scp->lastRefreshCycle != cm_lockRefreshCycle) {
4937 int scp_done = FALSE;
4939 osi_Log1(afsd_logp, "cm_CheckLocks Updating scp 0x%x", scp);
4941 lock_ReleaseWrite(&cm_scacheLock);
4942 lock_ObtainMutex(&scp->mx);
4944 /* did the lock change while we weren't holding the lock? */
4945 if (!IS_LOCK_ACTIVE(fileLock))
4946 goto post_syncopdone;
4948 code = cm_SyncOp(scp, NULL, fileLock->userp, &req, 0,
4949 CM_SCACHESYNC_NEEDCALLBACK
4950 | CM_SCACHESYNC_GETSTATUS
4951 | CM_SCACHESYNC_LOCK);
4955 "cm_CheckLocks SyncOp failure code 0x%x", code);
4956 goto post_syncopdone;
4959 /* cm_SyncOp releases scp->mx during which the lock
4960 may get released. */
4961 if (!IS_LOCK_ACTIVE(fileLock))
4962 goto pre_syncopdone;
4964 if (scp->serverLock != -1) {
4968 tfid.Volume = scp->fid.volume;
4969 tfid.Vnode = scp->fid.vnode;
4970 tfid.Unique = scp->fid.unique;
4972 userp = fileLock->userp;
4974 osi_Log3(afsd_logp, "CALL ExtendLock lock 0x%p for scp=0x%p with lock %d",
4977 (int) scp->serverLock);
4979 lock_ReleaseMutex(&scp->mx);
4982 code = cm_ConnFromFID(&cfid, userp,
4987 callp = cm_GetRxConn(connp);
4988 code = RXAFS_ExtendLock(callp, &tfid,
4990 rx_PutConnection(callp);
4992 osi_Log1(afsd_logp, " ExtendLock returns %d", code);
4994 } while (cm_Analyze(connp, userp, &req,
4995 &cfid, &volSync, NULL, NULL,
4998 code = cm_MapRPCError(code, &req);
5000 lock_ObtainMutex(&scp->mx);
5003 osi_Log1(afsd_logp, "CALL ExtendLock FAILURE, code 0x%x", code);
5005 osi_Log0(afsd_logp, "CALL ExtendLock SUCCESS");
5006 scp->lockDataVersion = scp->dataVersion;
5009 if ((code == EINVAL || code == CM_ERROR_INVAL) &&
5010 scp->lockDataVersion == scp->dataVersion) {
5014 (scp->exclusiveLocks > 0) ? LockWrite: LockRead;
5016 /* we might still have a chance to obtain a
5019 code = cm_IntSetLock(scp, userp, lockType, &req);
5022 code = CM_ERROR_INVAL;
5023 } else if (scp->lockDataVersion != scp->dataVersion) {
5025 /* now check if we still have the file at
5026 the right data version. */
5028 "Data version mismatch on scp 0x%p",
5031 " Data versions: before=%d, after=%d",
5032 scp->lockDataVersion,
5035 code = cm_IntReleaseLock(scp, userp, &req);
5037 code = CM_ERROR_INVAL;
5041 if (code == EINVAL || code == CM_ERROR_INVAL) {
5042 cm_LockMarkSCacheLost(scp);
5046 /* interestingly, we have found an active lock
5047 belonging to an scache that has no
5049 cm_LockMarkSCacheLost(scp);
5056 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5059 lock_ReleaseMutex(&scp->mx);
5061 lock_ObtainWrite(&cm_scacheLock);
5064 fileLock->lastUpdate = time(NULL);
5068 scp->lastRefreshCycle = cm_lockRefreshCycle;
5071 /* we have already refreshed the locks on this scp */
5072 fileLock->lastUpdate = time(NULL);
5075 cm_ReleaseSCacheNoLock(scp);
5077 } else if (IS_LOCK_ACTIVE(fileLock) && IS_LOCK_CLIENTONLY(fileLock)) {
5078 /* TODO: Check callbacks */
5082 lock_ReleaseWrite(&cm_scacheLock);
5083 osi_Log1(afsd_logp, "cm_CheckLocks completes lock check cycle %d", cm_lockRefreshCycle);
5086 /* NOT called with scp->mx held. */
5087 long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
5090 cm_scache_t *scp = NULL;
5091 cm_file_lock_t *fileLock;
5095 int force_client_lock = FALSE;
5096 int has_insert = FALSE;
5097 int check_data_version = FALSE;
5101 if (client_is_dead) {
5102 code = CM_ERROR_TIMEDOUT;
5106 lock_ObtainRead(&cm_scacheLock);
5108 osi_Log2(afsd_logp, "cm_RetryLock checking lock %p (scp=%p)", oldFileLock, oldFileLock->scp);
5109 osi_Log4(afsd_logp, " offset(%x:%x) length(%x:%x)",
5110 (unsigned)(oldFileLock->range.offset >> 32),
5111 (unsigned)(oldFileLock->range.offset & 0xffffffff),
5112 (unsigned)(oldFileLock->range.length >> 32),
5113 (unsigned)(oldFileLock->range.length & 0xffffffff));
5114 osi_Log3(afsd_logp, " key(%x:%x) flags=%x",
5115 (unsigned)(oldFileLock->key >> 32),
5116 (unsigned)(oldFileLock->key & 0xffffffff),
5117 (unsigned)(oldFileLock->flags));
5119 /* if the lock has already been granted, then we have nothing to do */
5120 if (IS_LOCK_ACTIVE(oldFileLock)) {
5121 lock_ReleaseRead(&cm_scacheLock);
5122 osi_Log0(afsd_logp, "cm_RetryLock lock already granted");
5126 /* we can't do anything with lost or deleted locks at the moment. */
5127 if (IS_LOCK_LOST(oldFileLock) || IS_LOCK_DELETED(oldFileLock)) {
5128 code = CM_ERROR_BADFD;
5129 osi_Log0(afsd_logp, "cm_RetryLock lock is lost or deleted");
5130 lock_ReleaseRead(&cm_scacheLock);
5134 scp = oldFileLock->scp;
5136 osi_assert(scp != NULL);
5138 lock_ReleaseRead(&cm_scacheLock);
5139 lock_ObtainMutex(&scp->mx);
5141 code = cm_LockCheckPerms(scp, oldFileLock->lockType,
5145 if (code == CM_ERROR_NOACCESS && oldFileLock->lockType == LockRead) {
5146 if (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp)) {
5147 force_client_lock = TRUE;
5151 lock_ReleaseMutex(&scp->mx);
5155 lock_ObtainWrite(&cm_scacheLock);
5157 /* Check if we already have a sufficient server lock to allow this
5158 lock to go through. */
5159 if (IS_LOCK_WAITLOCK(oldFileLock) &&
5160 (!SERVERLOCKS_ENABLED(scp) ||
5161 scp->serverLock == oldFileLock->lockType ||
5162 scp->serverLock == LockWrite)) {
5164 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5166 if (SERVERLOCKS_ENABLED(scp)) {
5167 osi_Log1(afsd_logp, "cm_RetryLock Server lock (%d) is sufficient for lock. Granting",
5168 (int) scp->serverLock);
5170 osi_Log0(afsd_logp, "cm_RetryLock skipping server lock for scp");
5173 lock_ReleaseWrite(&cm_scacheLock);
5174 lock_ReleaseMutex(&scp->mx);
5179 if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5181 /* check if the conflicting locks have dissappeared already */
5182 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5184 fileLock = (cm_file_lock_t *)
5185 ((char *) q - offsetof(cm_file_lock_t, fileq));
5187 if (IS_LOCK_LOST(fileLock)) {
5188 if (fileLock->key == oldFileLock->key) {
5189 code = CM_ERROR_BADFD;
5190 oldFileLock->flags |= CM_FILELOCK_FLAG_LOST;
5191 osi_Log1(afsd_logp, " found lost lock %p for same key. Marking lock as lost",
5194 } else if (fileLock->lockType == LockWrite &&
5195 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5196 osi_Log1(afsd_logp, " found conflicting LOST lock %p", fileLock);
5197 code = CM_ERROR_WOULDBLOCK;
5202 if (IS_LOCK_ACCEPTED(fileLock) &&
5203 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5205 if (oldFileLock->lockType != LockRead ||
5206 fileLock->lockType != LockRead) {
5208 osi_Log1(afsd_logp, " found conflicting lock %p", fileLock);
5209 code = CM_ERROR_WOULDBLOCK;
5217 lock_ReleaseWrite(&cm_scacheLock);
5218 lock_ReleaseMutex(&scp->mx);
5223 /* when we get here, the lock is either a WAITUNLOCK or WAITLOCK.
5224 If it is WAITUNLOCK, then we didn't find any conflicting lock
5225 but we haven't verfied whether the serverLock is sufficient to
5226 assert it. If it is WAITLOCK, then the serverLock is
5227 insufficient to assert it. Eitherway, we are ready to accept
5228 the lock as either ACTIVE or WAITLOCK depending on the
5231 /* First, promote the WAITUNLOCK to a WAITLOCK */
5232 if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5233 if (oldFileLock->lockType == LockRead)
5236 scp->exclusiveLocks++;
5238 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITUNLOCK;
5239 oldFileLock->flags |= CM_FILELOCK_FLAG_WAITLOCK;
5242 osi_assert(IS_LOCK_WAITLOCK(oldFileLock));
5244 if (force_client_lock ||
5245 !SERVERLOCKS_ENABLED(scp) ||
5246 scp->serverLock == oldFileLock->lockType ||
5247 (oldFileLock->lockType == LockRead &&
5248 scp->serverLock == LockWrite)) {
5250 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5252 if ((force_client_lock ||
5253 !SERVERLOCKS_ENABLED(scp)) &&
5254 !IS_LOCK_CLIENTONLY(oldFileLock)) {
5256 oldFileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
5258 if (oldFileLock->lockType == LockRead)
5261 scp->exclusiveLocks--;
5266 lock_ReleaseWrite(&cm_scacheLock);
5267 lock_ReleaseMutex(&scp->mx);
5274 code = cm_SyncOp(scp, NULL, oldFileLock->userp, &req, 0,
5275 CM_SCACHESYNC_NEEDCALLBACK
5276 | CM_SCACHESYNC_GETSTATUS
5277 | CM_SCACHESYNC_LOCK);
5279 osi_Log1(smb_logp, "cm_RetryLock SyncOp failure code 0x%x", code);
5280 lock_ReleaseWrite(&cm_scacheLock);
5281 goto post_syncopdone;
5284 if (!IS_LOCK_WAITLOCK(oldFileLock))
5285 goto pre_syncopdone;
5287 userp = oldFileLock->userp;
5289 #ifndef AGGRESSIVE_LOCKS
5290 newLock = oldFileLock->lockType;
5292 newLock = LockWrite;
5296 /* if has_insert is non-zero, then:
5297 - the lock a LockRead
5298 - we don't have permission to get a LockRead
5299 - we do have permission to get a LockWrite
5300 - the server supports VICED_CAPABILITY_WRITELOCKACL
5303 newLock = LockWrite;
5306 lock_ReleaseWrite(&cm_scacheLock);
5308 /* when we get here, either we have a read-lock and want a
5309 write-lock or we don't have any locks and we want some
5312 if (scp->serverLock == LockRead) {
5314 osi_assert(newLock == LockWrite);
5316 osi_Log0(afsd_logp, " Attempting to UPGRADE from LockRead to LockWrite");
5318 scp->lockDataVersion = scp->dataVersion;
5319 check_data_version = TRUE;
5321 code = cm_IntReleaseLock(scp, userp, &req);
5324 goto pre_syncopdone;
5326 scp->serverLock = -1;
5329 code = cm_IntSetLock(scp, userp, newLock, &req);
5332 if (scp->dataVersion != scp->lockDataVersion) {
5333 /* we lost a race. too bad */
5336 " Data version mismatch while upgrading lock.");
5338 " Data versions before=%d, after=%d",
5339 scp->lockDataVersion,
5342 " Releasing stale lock for scp 0x%x", scp);
5344 code = cm_IntReleaseLock(scp, userp, &req);
5346 scp->serverLock = -1;
5348 code = CM_ERROR_INVAL;
5350 cm_LockMarkSCacheLost(scp);
5352 scp->serverLock = newLock;
5357 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5363 if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
5364 lock_ObtainWrite(&cm_scacheLock);
5365 if (scp->fileLocksT == &oldFileLock->fileq)
5366 scp->fileLocksT = osi_QPrev(&oldFileLock->fileq);
5367 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, &oldFileLock->fileq);
5368 lock_ReleaseWrite(&cm_scacheLock);
5370 lock_ReleaseMutex(&scp->mx);
5373 lock_ObtainWrite(&cm_scacheLock);
5375 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5376 } else if (code != CM_ERROR_WOULDBLOCK) {
5377 oldFileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5378 cm_ReleaseUser(oldFileLock->userp);
5379 oldFileLock->userp = NULL;
5380 if (oldFileLock->scp) {
5381 cm_ReleaseSCacheNoLock(oldFileLock->scp);
5382 oldFileLock->scp = NULL;
5385 lock_ReleaseWrite(&cm_scacheLock);
5390 cm_key_t cm_GenerateKey(unsigned int session_id, unsigned long process_id, unsigned int file_id)
5393 osi_assert((process_id & 0xffffffff) == process_id);
5394 osi_assert((session_id & 0xffff) == session_id);
5395 osi_assert((file_id & 0xffff) == file_id);
5399 (((cm_key_t) (process_id & 0xffffffff)) << 32) |
5400 (((cm_key_t) (session_id & 0xffff)) << 16) |
5401 (((cm_key_t) (file_id & 0xffff)));
5404 static int cm_KeyEquals(cm_key_t k1, cm_key_t k2, int flags)
5406 if (flags & CM_UNLOCK_BY_FID) {
5407 return ((k1 & 0xffffffff) == (k2 & 0xffffffff));
5413 void cm_ReleaseAllLocks(void)
5419 cm_file_lock_t *fileLock;
5422 for (i = 0; i < cm_data.scacheHashTableSize; i++)
5424 for ( scp = cm_data.scacheHashTablep[i]; scp; scp = scp->nextp ) {
5425 while (scp->fileLocksH != NULL) {
5426 lock_ObtainMutex(&scp->mx);
5427 lock_ObtainWrite(&cm_scacheLock);
5428 if (!scp->fileLocksH) {
5429 lock_ReleaseWrite(&cm_scacheLock);
5430 lock_ReleaseMutex(&scp->mx);
5433 fileLock = (cm_file_lock_t *)((char *) scp->fileLocksH - offsetof(cm_file_lock_t, fileq));
5434 userp = fileLock->userp;
5436 key = fileLock->key;
5437 cm_HoldSCacheNoLock(scp);
5438 lock_ReleaseWrite(&cm_scacheLock);
5439 cm_UnlockByKey(scp, key, 0, userp, &req);
5440 cm_ReleaseSCache(scp);
5441 cm_ReleaseUser(userp);
5442 lock_ReleaseMutex(&scp->mx);