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_assertx(ldpp != NULL, "null cm_lock_data_t");
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 lock_ReleaseMutex(&scp->mx);
1078 cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE);
1079 lock_ObtainMutex(&scp->mx);
1083 strcpy(volNamep, mpNamep+1);
1085 cellp = cm_FindCellByID(scp->fid.cell);
1089 code = CM_ERROR_NOSUCHCELL;
1093 vnLength = strlen(volNamep);
1094 if (vnLength >= 8 && strcmp(volNamep + vnLength - 7, ".backup") == 0)
1096 else if (vnLength >= 10
1097 && strcmp(volNamep + vnLength - 9, ".readonly") == 0)
1102 /* check for backups within backups */
1104 && (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
1105 == CM_SCACHEFLAG_RO) {
1106 code = CM_ERROR_NOSUCHVOLUME;
1110 /* now we need to get the volume */
1111 lock_ReleaseMutex(&scp->mx);
1112 if (cm_VolNameIsID(volNamep)) {
1113 code = cm_GetVolumeByID(cellp, atoi(volNamep), userp, reqp,
1114 CM_GETVOL_FLAG_CREATE, &volp);
1116 code = cm_GetVolumeByName(cellp, volNamep, userp, reqp,
1117 CM_GETVOL_FLAG_CREATE, &volp);
1119 lock_ObtainMutex(&scp->mx);
1122 /* save the parent of the volume root for this is the
1123 * place where the volume is mounted and we must remember
1124 * this in the volume structure rather than just in the
1125 * scache entry lest the scache entry gets recycled
1128 lock_ObtainMutex(&volp->mx);
1129 volp->dotdotFid = dscp->fid;
1130 lock_ReleaseMutex(&volp->mx);
1132 scp->mountRootFid.cell = cellp->cellID;
1133 /* if the mt pt is in a read-only volume (not just a
1134 * backup), and if there is a read-only volume for the
1135 * target, and if this is a type '#' mount point, use
1136 * the read-only, otherwise use the one specified.
1138 if (mtType == '#' && (scp->flags & CM_SCACHEFLAG_PURERO)
1139 && volp->ro.ID != 0 && type == RWVOL)
1142 scp->mountRootFid.volume = volp->ro.ID;
1143 else if (type == BACKVOL)
1144 scp->mountRootFid.volume = volp->bk.ID;
1146 scp->mountRootFid.volume = volp->rw.ID;
1148 /* the rest of the fid is a magic number */
1149 scp->mountRootFid.vnode = 1;
1150 scp->mountRootFid.unique = 1;
1151 scp->mountRootGen = cm_data.mountRootGen;
1153 tfid = scp->mountRootFid;
1154 lock_ReleaseMutex(&scp->mx);
1155 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
1156 lock_ObtainMutex(&scp->mx);
1167 long cm_LookupInternal(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
1168 cm_req_t *reqp, cm_scache_t **outpScpp)
1171 int dnlcHit = 1; /* did we hit in the dnlc? yes, we did */
1172 cm_scache_t *tscp = NULL;
1173 cm_scache_t *mountedScp;
1174 cm_lookupSearch_t rock;
1177 memset(&rock, 0, sizeof(rock));
1179 if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
1180 && strcmp(namep, "..") == 0) {
1181 if (dscp->dotdotFid.volume == 0)
1182 return CM_ERROR_NOSUCHVOLUME;
1183 rock.fid = dscp->dotdotFid;
1185 } else if (strcmp(namep, ".") == 0) {
1186 rock.fid = dscp->fid;
1190 if (flags & CM_FLAG_NOMOUNTCHASE) {
1191 /* In this case, we should go and call cm_Dir* functions
1192 directly since the following cm_ApplyDir() function will
1200 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ, &dirop);
1203 code = cm_BPlusDirLookup(&dirop, namep, &rock.fid);
1208 code = cm_DirLookup(&dirop, namep, &rock.fid);
1210 cm_EndDirOp(&dirop);
1220 if (code == CM_ERROR_INEXACT_MATCH && (flags & CM_FLAG_CASEFOLD)) {
1227 return CM_ERROR_BPLUS_NOMATCH;
1232 rock.fid.cell = dscp->fid.cell;
1233 rock.fid.volume = dscp->fid.volume;
1234 rock.searchNamep = namep;
1235 rock.caseFold = (flags & CM_FLAG_CASEFOLD);
1236 rock.hasTilde = ((strchr(namep, '~') != NULL) ? 1 : 0);
1238 /* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
1239 code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
1240 (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
1242 /* code == 0 means we fell off the end of the dir, while stopnow means
1243 * that we stopped early, probably because we found the entry we're
1244 * looking for. Any other non-zero code is an error.
1246 if (code && code != CM_ERROR_STOPNOW) {
1247 /* if the cm_scache_t we are searching in is not a directory
1248 * we must return path not found because the error
1249 * is to describe the final component not an intermediary
1251 if (code == CM_ERROR_NOTDIR) {
1252 if (flags & CM_FLAG_CHECKPATH)
1253 return CM_ERROR_NOSUCHPATH;
1255 return CM_ERROR_NOSUCHFILE;
1260 getroot = (dscp==cm_data.rootSCachep) ;
1262 if (!cm_freelanceEnabled || !getroot) {
1263 if (flags & CM_FLAG_CHECKPATH)
1264 return CM_ERROR_NOSUCHPATH;
1266 return CM_ERROR_NOSUCHFILE;
1268 else { /* nonexistent dir on freelance root, so add it */
1269 char fullname[200] = ".";
1272 osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %s",
1273 osi_LogSaveString(afsd_logp,namep));
1274 if (namep[0] == '.') {
1275 if (cm_GetCell_Gen(&namep[1], &fullname[1], CM_FLAG_CREATE)) {
1277 if ( stricmp(&namep[1], &fullname[1]) )
1278 code = cm_FreelanceAddSymlink(namep, fullname, &rock.fid);
1280 code = cm_FreelanceAddMount(namep, &fullname[1], "root.cell.", 1, &rock.fid);
1283 if (cm_GetCell_Gen(namep, fullname, CM_FLAG_CREATE)) {
1285 if ( stricmp(namep, fullname) )
1286 code = cm_FreelanceAddSymlink(namep, fullname, &rock.fid);
1288 code = cm_FreelanceAddMount(namep, fullname, "root.cell.", 0, &rock.fid);
1291 if (!found || code < 0) { /* add mount point failed, so give up */
1292 if (flags & CM_FLAG_CHECKPATH)
1293 return CM_ERROR_NOSUCHPATH;
1295 return CM_ERROR_NOSUCHFILE;
1297 tscp = NULL; /* to force call of cm_GetSCache */
1302 if ( !tscp ) /* we did not find it in the dnlc */
1305 code = cm_GetSCache(&rock.fid, &tscp, userp, reqp);
1309 /* tscp is now held */
1311 lock_ObtainMutex(&tscp->mx);
1312 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1313 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1315 lock_ReleaseMutex(&tscp->mx);
1316 cm_ReleaseSCache(tscp);
1319 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1320 /* tscp is now locked */
1322 if (!(flags & CM_FLAG_NOMOUNTCHASE)
1323 && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
1324 /* mount points are funny: they have a volume name to mount
1327 code = cm_ReadMountPoint(tscp, userp, reqp);
1329 code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
1331 lock_ReleaseMutex(&tscp->mx);
1332 cm_ReleaseSCache(tscp);
1339 lock_ReleaseMutex(&tscp->mx);
1342 /* copy back pointer */
1345 /* insert scache in dnlc */
1346 if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
1347 /* lock the directory entry to prevent racing callback revokes */
1348 lock_ObtainMutex(&dscp->mx);
1349 if ( dscp->cbServerp != NULL && dscp->cbExpires > 0 )
1350 cm_dnlcEnter(dscp, namep, tscp);
1351 lock_ReleaseMutex(&dscp->mx);
1358 int cm_ExpandSysName(char *inp, char *outp, long outSize, unsigned int index)
1363 tp = strrchr(inp, '@');
1365 return 0; /* no @sys */
1367 if (strcmp(tp, "@sys") != 0)
1368 return 0; /* no @sys */
1370 /* caller just wants to know if this is a valid @sys type of name */
1374 if (index >= MAXNUMSYSNAMES)
1377 /* otherwise generate the properly expanded @sys name */
1378 prefixCount = (int)(tp - inp);
1380 strncpy(outp, inp, prefixCount); /* copy out "a." from "a.@sys" */
1381 outp[prefixCount] = 0; /* null terminate the "a." */
1382 strcat(outp, cm_sysNameList[index]);/* append i386_nt40 */
1386 long cm_EvaluateVolumeReference(char * namep, long flags, cm_user_t * userp,
1387 cm_req_t *reqp, cm_scache_t ** outpScpp)
1390 char cellName[CELL_MAXNAMELEN];
1391 char volumeName[VL_MAXNAMELEN];
1396 cm_cell_t * cellp = NULL;
1397 cm_volume_t * volp = NULL;
1400 int mountType = RWVOL;
1402 osi_Log1(afsd_logp, "cm_EvaluateVolumeReference for string [%s]",
1403 osi_LogSaveString(afsd_logp, namep));
1405 if (strnicmp(namep, CM_PREFIX_VOL, CM_PREFIX_VOL_CCH) != 0) {
1406 goto _exit_invalid_path;
1409 /* namep is assumed to look like the following:
1411 @vol:<cellname>%<volume>\0
1413 @vol:<cellname>#<volume>\0
1417 cp = namep + CM_PREFIX_VOL_CCH; /* cp points to cell name, hopefully */
1418 tp = strchr(cp, '%');
1420 tp = strchr(cp, '#');
1422 (len = tp - cp) == 0 ||
1423 len > CELL_MAXNAMELEN)
1424 goto _exit_invalid_path;
1425 strncpy(cellName, cp, len);
1426 cellName[len] = '\0';
1431 cp = tp+1; /* cp now points to volume, supposedly */
1432 strncpy(volumeName, cp, VL_MAXNAMELEN-1);
1433 volumeName[VL_MAXNAMELEN - 1] = 0;
1435 /* OK, now we have the cell and the volume */
1436 osi_Log2(afsd_logp, " Found cell [%s] and volume [%s]",
1437 osi_LogSaveString(afsd_logp, cellName),
1438 osi_LogSaveString(afsd_logp, volumeName));
1440 cellp = cm_GetCell(cellName, CM_FLAG_CREATE);
1441 if (cellp == NULL) {
1442 goto _exit_invalid_path;
1445 len = strlen(volumeName);
1446 if (len >= 8 && strcmp(volumeName + len - 7, ".backup") == 0)
1448 else if (len >= 10 &&
1449 strcmp(volumeName + len - 9, ".readonly") == 0)
1454 if (cm_VolNameIsID(volumeName)) {
1455 code = cm_GetVolumeByID(cellp, atoi(volumeName), userp, reqp,
1456 CM_GETVOL_FLAG_CREATE, &volp);
1458 code = cm_GetVolumeByName(cellp, volumeName, userp, reqp,
1459 CM_GETVOL_FLAG_CREATE, &volp);
1465 fid.cell = cellp->cellID;
1467 if (volType == BACKVOL)
1468 fid.volume = volp->bk.ID;
1469 else if (volType == ROVOL ||
1470 (volType == RWVOL && mountType == ROVOL && volp->ro.ID != 0))
1471 fid.volume = volp->ro.ID;
1473 fid.volume = volp->rw.ID;
1478 code = cm_GetSCache(&fid, outpScpp, userp, reqp);
1488 if (flags & CM_FLAG_CHECKPATH)
1489 return CM_ERROR_NOSUCHPATH;
1491 return CM_ERROR_NOSUCHFILE;
1494 #ifdef DEBUG_REFCOUNT
1495 long cm_LookupDbg(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
1496 cm_req_t *reqp, cm_scache_t **outpScpp, char * file, long line)
1498 long cm_Lookup(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
1499 cm_req_t *reqp, cm_scache_t **outpScpp)
1503 char tname[AFSPATHMAX];
1504 int sysNameIndex = 0;
1505 cm_scache_t *scp = NULL;
1507 #ifdef DEBUG_REFCOUNT
1508 afsi_log("%s:%d cm_Lookup dscp 0x%p ref %d", file, line, dscp, dscp->refCount, file, line);
1509 osi_Log2(afsd_logp, "cm_Lookup dscp 0x%p ref %d", dscp, dscp->refCount);
1512 if ( stricmp(namep,SMB_IOCTL_FILENAME_NOSLASH) == 0 ) {
1513 if (flags & CM_FLAG_CHECKPATH)
1514 return CM_ERROR_NOSUCHPATH;
1516 return CM_ERROR_NOSUCHFILE;
1519 if (dscp == cm_data.rootSCachep &&
1520 strnicmp(namep, CM_PREFIX_VOL, CM_PREFIX_VOL_CCH) == 0) {
1521 return cm_EvaluateVolumeReference(namep, flags, userp, reqp, outpScpp);
1524 if (cm_ExpandSysName(namep, NULL, 0, 0) > 0) {
1525 for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
1526 code = cm_ExpandSysName(namep, tname, sizeof(tname), sysNameIndex);
1528 code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
1529 #ifdef DEBUG_REFCOUNT
1530 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);
1531 osi_Log3(afsd_logp, "cm_LookupInternal (1) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1539 cm_ReleaseSCache(scp);
1543 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1544 #ifdef DEBUG_REFCOUNT
1545 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);
1546 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1553 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1554 #ifdef DEBUG_REFCOUNT
1555 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);
1556 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1562 /* None of the possible sysName expansions could be found */
1563 if (flags & CM_FLAG_CHECKPATH)
1564 return CM_ERROR_NOSUCHPATH;
1566 return CM_ERROR_NOSUCHFILE;
1569 long cm_Unlink(cm_scache_t *dscp, char *namep, cm_user_t *userp, cm_req_t *reqp)
1575 AFSFetchStatus newDirStatus;
1577 struct rx_connection * callp;
1580 #ifdef AFS_FREELANCE_CLIENT
1581 if (cm_freelanceEnabled && dscp == cm_data.rootSCachep) {
1582 /* deleting a mount point from the root dir. */
1583 code = cm_FreelanceRemoveMount(namep);
1588 /* make sure we don't screw up the dir status during the merge */
1589 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
1591 lock_ObtainMutex(&dscp->mx);
1592 sflags = CM_SCACHESYNC_STOREDATA;
1593 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1594 lock_ReleaseMutex(&dscp->mx);
1596 cm_EndDirOp(&dirop);
1601 afsFid.Volume = dscp->fid.volume;
1602 afsFid.Vnode = dscp->fid.vnode;
1603 afsFid.Unique = dscp->fid.unique;
1605 osi_Log1(afsd_logp, "CALL RemoveFile scp 0x%p", dscp);
1607 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
1611 callp = cm_GetRxConn(connp);
1612 code = RXAFS_RemoveFile(callp, &afsFid, namep,
1613 &newDirStatus, &volSync);
1614 rx_PutConnection(callp);
1616 } while (cm_Analyze(connp, userp, reqp, &dscp->fid, &volSync, NULL, NULL, code));
1617 code = cm_MapRPCError(code, reqp);
1620 osi_Log1(afsd_logp, "CALL RemoveFile FAILURE, code 0x%x", code);
1622 osi_Log0(afsd_logp, "CALL RemoveFile SUCCESS");
1625 lock_ObtainWrite(&dirop.scp->dirlock);
1626 dirop.lockType = CM_DIRLOCK_WRITE;
1628 lock_ObtainMutex(&dscp->mx);
1629 cm_dnlcRemove(dscp, namep);
1630 cm_SyncOpDone(dscp, NULL, sflags);
1632 cm_MergeStatus(NULL, dscp, &newDirStatus, &volSync, userp, 0);
1633 } else if (code == CM_ERROR_NOSUCHFILE) {
1634 /* windows would not have allowed the request to delete the file
1635 * if it did not believe the file existed. therefore, we must
1636 * have an inconsistent view of the world.
1638 dscp->cbServerp = NULL;
1640 lock_ReleaseMutex(&dscp->mx);
1642 if (code == 0 && cm_CheckDirOpForSingleChange(&dirop)) {
1643 cm_DirDeleteEntry(&dirop, namep);
1645 cm_BPlusDirDeleteEntry(&dirop, namep);
1648 cm_EndDirOp(&dirop);
1653 /* called with a locked vnode, and fills in the link info.
1654 * returns this the vnode still locked.
1656 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1663 lock_AssertMutex(&linkScp->mx);
1664 if (!linkScp->mountPointStringp[0]) {
1665 /* read the link data */
1666 lock_ReleaseMutex(&linkScp->mx);
1667 thyper.LowPart = thyper.HighPart = 0;
1668 code = buf_Get(linkScp, &thyper, &bufp);
1669 lock_ObtainMutex(&linkScp->mx);
1673 code = cm_SyncOp(linkScp, bufp, userp, reqp, 0,
1674 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1679 cm_SyncOpDone(linkScp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1681 if (cm_HaveBuffer(linkScp, bufp, 0))
1684 code = cm_GetBuffer(linkScp, bufp, NULL, userp, reqp);
1689 } /* while loop to get the data */
1691 /* now if we still have no link read in,
1692 * copy the data from the buffer */
1693 if ((temp = linkScp->length.LowPart) >= MOUNTPOINTLEN) {
1695 return CM_ERROR_TOOBIG;
1698 /* otherwise, it fits; make sure it is still null (could have
1699 * lost race with someone else referencing this link above),
1700 * and if so, copy in the data.
1702 if (!linkScp->mountPointStringp[0]) {
1703 strncpy(linkScp->mountPointStringp, bufp->datap, temp);
1704 linkScp->mountPointStringp[temp] = 0; /* null terminate */
1707 } /* don't have sym link contents cached */
1712 /* called with a held vnode and a path suffix, with the held vnode being a
1713 * symbolic link. Our goal is to generate a new path to interpret, and return
1714 * this new path in newSpaceBufferp. If the new vnode is relative to a dir
1715 * other than the directory containing the symbolic link, then the new root is
1716 * returned in *newRootScpp, otherwise a null is returned there.
1718 long cm_AssembleLink(cm_scache_t *linkScp, char *pathSuffixp,
1719 cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1720 cm_user_t *userp, cm_req_t *reqp)
1727 lock_ObtainMutex(&linkScp->mx);
1728 code = cm_HandleLink(linkScp, userp, reqp);
1732 /* if we may overflow the buffer, bail out; buffer is signficantly
1733 * bigger than max path length, so we don't really have to worry about
1734 * being a little conservative here.
1736 if (strlen(linkScp->mountPointStringp) + strlen(pathSuffixp) + 2
1737 >= CM_UTILS_SPACESIZE)
1738 return CM_ERROR_TOOBIG;
1740 tsp = cm_GetSpace();
1741 linkp = linkScp->mountPointStringp;
1742 if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1743 if (strlen(linkp) > cm_mountRootLen)
1744 strcpy(tsp->data, linkp+cm_mountRootLen+1);
1747 *newRootScpp = cm_data.rootSCachep;
1748 cm_HoldSCache(cm_data.rootSCachep);
1749 } else if (linkp[0] == '\\' && linkp[1] == '\\') {
1750 if (!strnicmp(&linkp[2], cm_NetbiosName, (len = (long)strlen(cm_NetbiosName))))
1752 char * p = &linkp[len + 3];
1753 if (strnicmp(p, "all", 3) == 0)
1756 strcpy(tsp->data, p);
1757 for (p = tsp->data; *p; p++) {
1761 *newRootScpp = cm_data.rootSCachep;
1762 cm_HoldSCache(cm_data.rootSCachep);
1764 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1765 strcpy(tsp->data, linkp);
1766 *newRootScpp = NULL;
1767 code = CM_ERROR_PATH_NOT_COVERED;
1769 } else if ( !strnicmp(linkp, "msdfs:", (len = (long)strlen("msdfs:"))) ) {
1770 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1771 strcpy(tsp->data, linkp);
1772 *newRootScpp = NULL;
1773 code = CM_ERROR_PATH_NOT_COVERED;
1774 } else if (*linkp == '\\' || *linkp == '/') {
1776 /* formerly, this was considered to be from the AFS root,
1777 * but this seems to create problems. instead, we will just
1778 * reject the link */
1779 strcpy(tsp->data, linkp+1);
1780 *newRootScpp = cm_data.rootSCachep;
1781 cm_HoldSCache(cm_data.rootSCachep);
1783 /* we still copy the link data into the response so that
1784 * the user can see what the link points to
1786 linkScp->fileType = CM_SCACHETYPE_INVALID;
1787 strcpy(tsp->data, linkp);
1788 *newRootScpp = NULL;
1789 code = CM_ERROR_NOSUCHPATH;
1792 /* a relative link */
1793 strcpy(tsp->data, linkp);
1794 *newRootScpp = NULL;
1796 if (pathSuffixp[0] != 0) { /* if suffix string is non-null */
1797 strcat(tsp->data, "\\");
1798 strcat(tsp->data, pathSuffixp);
1800 *newSpaceBufferp = tsp;
1803 lock_ReleaseMutex(&linkScp->mx);
1806 #ifdef DEBUG_REFCOUNT
1807 long cm_NameIDbg(cm_scache_t *rootSCachep, char *pathp, long flags,
1808 cm_user_t *userp, char *tidPathp, cm_req_t *reqp, cm_scache_t **outScpp,
1809 char * file, long line)
1811 long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
1812 cm_user_t *userp, char *tidPathp, cm_req_t *reqp, cm_scache_t **outScpp)
1816 char *tp; /* ptr moving through input buffer */
1817 char tc; /* temp char */
1818 int haveComponent; /* has new component started? */
1819 char component[AFSPATHMAX]; /* this is the new component */
1820 char *cp; /* component name being assembled */
1821 cm_scache_t *tscp; /* current location in the hierarchy */
1822 cm_scache_t *nscp; /* next dude down */
1823 cm_scache_t *dirScp; /* last dir we searched */
1824 cm_scache_t *linkScp; /* new root for the symlink we just
1826 cm_space_t *psp; /* space for current path, if we've hit
1828 cm_space_t *tempsp; /* temp vbl */
1829 char *restp; /* rest of the pathname to interpret */
1830 int symlinkCount; /* count of # of symlinks traversed */
1831 int extraFlag; /* avoid chasing mt pts for dir cmd */
1832 int phase = 1; /* 1 = tidPathp, 2 = pathp */
1833 #define MAX_FID_COUNT 512
1834 cm_fid_t fids[MAX_FID_COUNT]; /* array of fids processed in this path walk */
1835 int fid_count = 0; /* number of fids processed in this path walk */
1838 #ifdef DEBUG_REFCOUNT
1839 afsi_log("%s:%d cm_NameI rootscp 0x%p ref %d", file, line, rootSCachep, rootSCachep->refCount);
1840 osi_Log4(afsd_logp,"cm_NameI rootscp 0x%p path %s tidpath %s flags 0x%x",
1841 rootSCachep, pathp ? pathp : "<NULL>", tidPathp ? tidPathp : "<NULL>",
1856 cm_HoldSCache(tscp);
1864 /* map Unix slashes into DOS ones so we can interpret Unix
1870 if (!haveComponent) {
1873 } else if (tc == 0) {
1887 /* we have a component here */
1888 if (tc == 0 || tc == '\\') {
1889 /* end of the component; we're at the last
1890 * component if tc == 0. However, if the last
1891 * is a symlink, we have more to do.
1893 *cp++ = 0; /* add null termination */
1895 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
1896 extraFlag = CM_FLAG_NOMOUNTCHASE;
1897 code = cm_Lookup(tscp, component,
1899 userp, reqp, &nscp);
1902 if (!strcmp(component,"..") || !strcmp(component,".")) {
1904 * roll back the fid list until we find the fid
1905 * that matches where we are now. Its not necessarily
1906 * one or two fids because they might have been
1907 * symlinks or mount points or both that were crossed.
1909 for ( i=fid_count-1; i>=0; i--) {
1910 if (!cm_FidCmp(&nscp->fid, &fids[i]))
1914 /* add the new fid to the list */
1915 for ( i=0; i<fid_count; i++) {
1916 if ( !cm_FidCmp(&nscp->fid, &fids[i]) ) {
1917 code = CM_ERROR_TOO_MANY_SYMLINKS;
1918 cm_ReleaseSCache(nscp);
1923 if (i == fid_count && fid_count < MAX_FID_COUNT) {
1924 fids[fid_count++] = nscp->fid;
1930 cm_ReleaseSCache(tscp);
1932 cm_ReleaseSCache(dirScp);
1935 if ((code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH) &&
1936 tscp->fileType == CM_SCACHETYPE_SYMLINK)
1938 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_NOSUCHPATH");
1939 return CM_ERROR_NOSUCHPATH;
1941 osi_Log1(afsd_logp,"cm_NameI code 0x%x", code);
1946 haveComponent = 0; /* component done */
1948 cm_ReleaseSCache(dirScp);
1949 dirScp = tscp; /* for some symlinks */
1950 tscp = nscp; /* already held */
1952 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
1955 cm_ReleaseSCache(dirScp);
1961 /* now, if tscp is a symlink, we should follow
1962 * it and assemble the path again.
1964 lock_ObtainMutex(&tscp->mx);
1965 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1966 CM_SCACHESYNC_GETSTATUS
1967 | CM_SCACHESYNC_NEEDCALLBACK);
1969 lock_ReleaseMutex(&tscp->mx);
1970 cm_ReleaseSCache(tscp);
1973 cm_ReleaseSCache(dirScp);
1978 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1980 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
1981 /* this is a symlink; assemble a new buffer */
1982 lock_ReleaseMutex(&tscp->mx);
1983 if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
1984 cm_ReleaseSCache(tscp);
1987 cm_ReleaseSCache(dirScp);
1992 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_TOO_MANY_SYMLINKS");
1993 return CM_ERROR_TOO_MANY_SYMLINKS;
1999 code = cm_AssembleLink(tscp, restp, &linkScp, &tempsp, userp, reqp);
2001 if (code == 0 && linkScp != NULL) {
2002 if (linkScp == cm_data.rootSCachep)
2005 for ( i=0; i<fid_count; i++) {
2006 if ( !cm_FidCmp(&linkScp->fid, &fids[i]) ) {
2007 code = CM_ERROR_TOO_MANY_SYMLINKS;
2008 cm_ReleaseSCache(linkScp);
2014 if (i == fid_count && fid_count < MAX_FID_COUNT) {
2015 fids[fid_count++] = linkScp->fid;
2020 /* something went wrong */
2021 cm_ReleaseSCache(tscp);
2024 cm_ReleaseSCache(dirScp);
2030 /* otherwise, tempsp has the new path,
2031 * and linkScp is the new root from
2032 * which to interpret that path.
2033 * Continue with the namei processing,
2034 * also doing the bookkeeping for the
2035 * space allocation and tracking the
2036 * vnode reference counts.
2042 cm_ReleaseSCache(tscp);
2047 * now, if linkScp is null, that's
2048 * AssembleLink's way of telling us that
2049 * the sym link is relative to the dir
2050 * containing the link. We have a ref
2051 * to it in dirScp, and we hold it now
2052 * and reuse it as the new spot in the
2060 /* not a symlink, we may be done */
2061 lock_ReleaseMutex(&tscp->mx);
2069 cm_ReleaseSCache(dirScp);
2077 cm_ReleaseSCache(dirScp);
2080 } /* end of a component */
2083 } /* we have a component */
2084 } /* big while loop over all components */
2088 cm_ReleaseSCache(dirScp);
2094 cm_ReleaseSCache(tscp);
2096 #ifdef DEBUG_REFCOUNT
2097 afsi_log("%s:%d cm_NameI code 0x%x outScpp 0x%p ref %d", file, line, code, *outScpp, (*outScpp)->refCount);
2099 osi_Log2(afsd_logp,"cm_NameI code 0x%x outScpp 0x%p", code, *outScpp);
2103 /* called with a dir, and a vnode within the dir that happens to be a symlink.
2104 * We chase the link, and return a held pointer to the target, if it exists,
2105 * in *outScpp. If we succeed, we return 0, otherwise we return an error code
2106 * and do not hold or return a target vnode.
2108 * This is very similar to calling cm_NameI with the last component of a name,
2109 * which happens to be a symlink, except that we've already passed by the name.
2111 * This function is typically called by the directory listing functions, which
2112 * encounter symlinks but need to return the proper file length so programs
2113 * like "more" work properly when they make use of the attributes retrieved from
2116 * The input vnode should not be locked when this function is called.
2118 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
2119 cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
2123 cm_scache_t *newRootScp;
2125 osi_Log1(afsd_logp, "Evaluating symlink scp 0x%p", linkScp);
2127 code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
2131 /* now, if newRootScp is NULL, we're really being told that the symlink
2132 * is relative to the current directory (dscp).
2134 if (newRootScp == NULL) {
2136 cm_HoldSCache(dscp);
2139 code = cm_NameI(newRootScp, spacep->data,
2140 CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
2141 userp, NULL, reqp, outScpp);
2143 if (code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH)
2144 code = CM_ERROR_NOSUCHPATH;
2146 /* this stuff is allocated no matter what happened on the namei call,
2148 cm_FreeSpace(spacep);
2149 cm_ReleaseSCache(newRootScp);
2151 if (linkScp == *outScpp) {
2152 cm_ReleaseSCache(*outScpp);
2154 code = CM_ERROR_NOSUCHPATH;
2160 /* make this big enough so that one buffer of dir pages won't overflow. We'll
2161 * check anyway, but we want to minimize the chance that we have to leave stuff
2164 #define CM_BULKMAX (3 * AFSCBMAX)
2166 /* rock for bulk stat calls */
2167 typedef struct cm_bulkStat {
2168 osi_hyper_t bufOffset; /* only do it for things in this buffer page */
2170 /* info for the actual call */
2171 int counter; /* next free slot */
2172 AFSFid fids[CM_BULKMAX];
2173 AFSFetchStatus stats[CM_BULKMAX];
2174 AFSCallBack callbacks[CM_BULKMAX];
2177 /* for a given entry, make sure that it isn't in the stat cache, and then
2178 * add it to the list of file IDs to be obtained.
2180 * Don't bother adding it if we already have a vnode. Note that the dir
2181 * is locked, so we have to be careful checking the vnode we're thinking of
2182 * processing, to avoid deadlocks.
2184 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
2195 /* Don't overflow bsp. */
2196 if (bsp->counter >= CM_BULKMAX)
2197 return CM_ERROR_STOPNOW;
2199 thyper.LowPart = cm_data.buf_blockSize;
2200 thyper.HighPart = 0;
2201 thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
2203 /* thyper is now the first byte past the end of the record we're
2204 * interested in, and bsp->bufOffset is the first byte of the record
2205 * we're interested in.
2206 * Skip data in the others.
2209 if (LargeIntegerLessThan(*offp, bsp->bufOffset))
2211 if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
2212 return CM_ERROR_STOPNOW;
2213 if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
2216 tfid.cell = scp->fid.cell;
2217 tfid.volume = scp->fid.volume;
2218 tfid.vnode = ntohl(dep->fid.vnode);
2219 tfid.unique = ntohl(dep->fid.unique);
2220 tscp = cm_FindSCache(&tfid);
2222 if (lock_TryMutex(&tscp->mx)) {
2223 /* we have an entry that we can look at */
2224 if (!(tscp->flags & CM_SCACHEFLAG_EACCESS) && cm_HaveCallback(tscp)) {
2225 /* we have a callback on it. Don't bother
2226 * fetching this stat entry, since we're happy
2227 * with the info we have.
2229 lock_ReleaseMutex(&tscp->mx);
2230 cm_ReleaseSCache(tscp);
2233 lock_ReleaseMutex(&tscp->mx);
2235 cm_ReleaseSCache(tscp);
2238 #ifdef AFS_FREELANCE_CLIENT
2239 // yj: if this is a mountpoint under root.afs then we don't want it
2240 // to be bulkstat-ed, instead, we call getSCache directly and under
2241 // getSCache, it is handled specially.
2242 if ( cm_freelanceEnabled &&
2243 tfid.cell==AFS_FAKE_ROOT_CELL_ID &&
2244 tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
2245 !(tfid.vnode==0x1 && tfid.unique==0x1) )
2247 osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
2248 return cm_GetSCache(&tfid, &tscp, NULL, NULL);
2250 #endif /* AFS_FREELANCE_CLIENT */
2253 bsp->fids[i].Volume = scp->fid.volume;
2254 bsp->fids[i].Vnode = tfid.vnode;
2255 bsp->fids[i].Unique = tfid.unique;
2259 /* called with a locked scp and a pointer to a buffer. Make bulk stat
2260 * calls on all undeleted files in the page of the directory specified.
2263 cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
2267 cm_bulkStat_t bb; /* this is *BIG*, probably 16K or so;
2268 * watch for stack problems */
2269 AFSCBFids fidStruct;
2270 AFSBulkStats statStruct;
2272 AFSCBs callbackStruct;
2275 cm_callbackRequest_t cbReq;
2281 struct rx_connection * callp;
2282 int inlinebulk = 0; /* Did we use InlineBulkStatus RPC or not? */
2284 osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%p", dscp);
2286 /* should be on a buffer boundary */
2287 osi_assertx((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0, "invalid offset");
2289 memset(&bb, 0, sizeof(bb));
2290 bb.bufOffset = *offsetp;
2292 lock_ReleaseMutex(&dscp->mx);
2293 /* first, assemble the file IDs we need to stat */
2294 code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) &bb, offsetp, userp, reqp, NULL);
2296 /* if we failed, bail out early */
2297 if (code && code != CM_ERROR_STOPNOW) {
2298 lock_ObtainMutex(&dscp->mx);
2302 /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
2303 * make the calls to create the entries. Handle AFSCBMAX files at a
2307 while (filex < bb.counter) {
2308 filesThisCall = bb.counter - filex;
2309 if (filesThisCall > AFSCBMAX)
2310 filesThisCall = AFSCBMAX;
2312 fidStruct.AFSCBFids_len = filesThisCall;
2313 fidStruct.AFSCBFids_val = &bb.fids[filex];
2314 statStruct.AFSBulkStats_len = filesThisCall;
2315 statStruct.AFSBulkStats_val = &bb.stats[filex];
2316 callbackStruct.AFSCBs_len = filesThisCall;
2317 callbackStruct.AFSCBs_val = &bb.callbacks[filex];
2318 cm_StartCallbackGrantingCall(NULL, &cbReq);
2319 osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
2321 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2325 callp = cm_GetRxConn(connp);
2326 if (!(connp->serverp->flags & CM_SERVERFLAG_NOINLINEBULK)) {
2327 code = RXAFS_InlineBulkStatus(callp, &fidStruct,
2328 &statStruct, &callbackStruct, &volSync);
2329 if (code == RXGEN_OPCODE) {
2330 cm_SetServerNoInlineBulk(connp->serverp, 0);
2336 code = RXAFS_BulkStatus(callp, &fidStruct,
2337 &statStruct, &callbackStruct, &volSync);
2339 rx_PutConnection(callp);
2341 } while (cm_Analyze(connp, userp, reqp, &dscp->fid,
2342 &volSync, NULL, &cbReq, code));
2343 code = cm_MapRPCError(code, reqp);
2345 osi_Log2(afsd_logp, "CALL %sBulkStatus FAILURE code 0x%x",
2346 inlinebulk ? "Inline" : "", code);
2348 osi_Log1(afsd_logp, "CALL %sBulkStatus SUCCESS", inlinebulk ? "Inline" : "");
2350 /* may as well quit on an error, since we're not going to do
2351 * much better on the next immediate call, either.
2354 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2358 /* otherwise, we should do the merges */
2359 for (i = 0; i<filesThisCall; i++) {
2361 tfid.cell = dscp->fid.cell;
2362 tfid.volume = bb.fids[j].Volume;
2363 tfid.vnode = bb.fids[j].Vnode;
2364 tfid.unique = bb.fids[j].Unique;
2365 code = cm_GetSCache(&tfid, &scp, userp, reqp);
2369 /* otherwise, if this entry has no callback info,
2372 lock_ObtainMutex(&scp->mx);
2373 /* now, we have to be extra paranoid on merging in this
2374 * information, since we didn't use cm_SyncOp before
2375 * starting the fetch to make sure that no bad races
2376 * were occurring. Specifically, we need to make sure
2377 * we don't obliterate any newer information in the
2378 * vnode than have here.
2380 * Right now, be pretty conservative: if there's a
2381 * callback or a pending call, skip it.
2383 if ((scp->cbServerp == NULL || (scp->flags & CM_SCACHEFLAG_EACCESS))
2385 (CM_SCACHEFLAG_FETCHING
2386 | CM_SCACHEFLAG_STORING
2387 | CM_SCACHEFLAG_SIZESTORING))) {
2388 cm_EndCallbackGrantingCall(scp, &cbReq,
2390 CM_CALLBACK_MAINTAINCOUNT);
2391 cm_MergeStatus(dscp, scp, &bb.stats[j], &volSync, userp, 0);
2393 lock_ReleaseMutex(&scp->mx);
2394 cm_ReleaseSCache(scp);
2395 } /* all files in the response */
2396 /* now tell it to drop the count,
2397 * after doing the vnode processing above */
2398 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2400 filex += filesThisCall;
2401 } /* while there are still more files to process */
2402 lock_ObtainMutex(&dscp->mx);
2404 /* If we did the InlineBulk RPC pull out the return code and log it */
2406 if ((&bb.stats[0])->errorCode) {
2407 osi_Log1(afsd_logp, "cm_TryBulkStat bulk stat error: %d",
2408 (&bb.stats[0])->errorCode);
2412 osi_Log0(afsd_logp, "END cm_TryBulkStat");
2416 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
2420 /* initialize store back mask as inexpensive local variable */
2422 memset(statusp, 0, sizeof(AFSStoreStatus));
2424 /* copy out queued info from scache first, if scp passed in */
2426 if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
2427 statusp->ClientModTime = scp->clientModTime;
2428 mask |= AFS_SETMODTIME;
2429 scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
2434 /* now add in our locally generated request */
2435 if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
2436 statusp->ClientModTime = attrp->clientModTime;
2437 mask |= AFS_SETMODTIME;
2439 if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
2440 statusp->UnixModeBits = attrp->unixModeBits;
2441 mask |= AFS_SETMODE;
2443 if (attrp->mask & CM_ATTRMASK_OWNER) {
2444 statusp->Owner = attrp->owner;
2445 mask |= AFS_SETOWNER;
2447 if (attrp->mask & CM_ATTRMASK_GROUP) {
2448 statusp->Group = attrp->group;
2449 mask |= AFS_SETGROUP;
2452 statusp->Mask = mask;
2455 /* set the file size, and make sure that all relevant buffers have been
2456 * truncated. Ensure that any partially truncated buffers have been zeroed
2457 * to the end of the buffer.
2459 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
2465 /* start by locking out buffer creation */
2466 lock_ObtainWrite(&scp->bufCreateLock);
2468 /* verify that this is a file, not a dir or a symlink */
2469 lock_ObtainMutex(&scp->mx);
2470 code = cm_SyncOp(scp, NULL, userp, reqp, 0,
2471 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2474 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2476 if (scp->fileType != CM_SCACHETYPE_FILE) {
2477 code = CM_ERROR_ISDIR;
2482 if (LargeIntegerLessThan(*sizep, scp->length))
2487 lock_ReleaseMutex(&scp->mx);
2489 /* can't hold scp->mx lock here, since we may wait for a storeback to
2490 * finish if the buffer package is cleaning a buffer by storing it to
2494 buf_Truncate(scp, userp, reqp, sizep);
2496 /* now ensure that file length is short enough, and update truncPos */
2497 lock_ObtainMutex(&scp->mx);
2499 /* make sure we have a callback (so we have the right value for the
2500 * length), and wait for it to be safe to do a truncate.
2502 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
2503 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2504 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2506 /* If we only have 'i' bits, then we should still be able to set
2507 the size of a file we created. */
2508 if (code == CM_ERROR_NOACCESS && scp->creator == userp) {
2509 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
2510 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2511 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2517 if (LargeIntegerLessThan(*sizep, scp->length)) {
2518 /* a real truncation. If truncPos is not set yet, or is bigger
2519 * than where we're truncating the file, set truncPos to this
2524 if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
2525 || LargeIntegerLessThan(*sizep, scp->length)) {
2527 scp->truncPos = *sizep;
2528 scp->mask |= CM_SCACHEMASK_TRUNCPOS;
2530 /* in either case, the new file size has been changed */
2531 scp->length = *sizep;
2532 scp->mask |= CM_SCACHEMASK_LENGTH;
2534 else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
2535 /* really extending the file */
2536 scp->length = *sizep;
2537 scp->mask |= CM_SCACHEMASK_LENGTH;
2540 /* done successfully */
2543 cm_SyncOpDone(scp, NULL,
2544 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2545 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2548 lock_ReleaseMutex(&scp->mx);
2549 lock_ReleaseWrite(&scp->bufCreateLock);
2554 /* set the file size or other attributes (but not both at once) */
2555 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
2559 AFSFetchStatus afsOutStatus;
2563 AFSStoreStatus afsInStatus;
2564 struct rx_connection * callp;
2566 /* handle file length setting */
2567 if (attrp->mask & CM_ATTRMASK_LENGTH)
2568 return cm_SetLength(scp, &attrp->length, userp, reqp);
2570 lock_ObtainMutex(&scp->mx);
2571 /* otherwise, we have to make an RPC to get the status */
2572 code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
2574 lock_ReleaseMutex(&scp->mx);
2578 /* make the attr structure */
2579 cm_StatusFromAttr(&afsInStatus, scp, attrp);
2581 tfid.Volume = scp->fid.volume;
2582 tfid.Vnode = scp->fid.vnode;
2583 tfid.Unique = scp->fid.unique;
2584 lock_ReleaseMutex(&scp->mx);
2586 /* now make the RPC */
2587 osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%p", scp);
2589 code = cm_ConnFromFID(&scp->fid, userp, reqp, &connp);
2593 callp = cm_GetRxConn(connp);
2594 code = RXAFS_StoreStatus(callp, &tfid,
2595 &afsInStatus, &afsOutStatus, &volSync);
2596 rx_PutConnection(callp);
2598 } while (cm_Analyze(connp, userp, reqp,
2599 &scp->fid, &volSync, NULL, NULL, code));
2600 code = cm_MapRPCError(code, reqp);
2603 osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
2605 osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
2607 lock_ObtainMutex(&scp->mx);
2608 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
2610 cm_MergeStatus(NULL, scp, &afsOutStatus, &volSync, userp,
2611 CM_MERGEFLAG_FORCE|CM_MERGEFLAG_STOREDATA);
2613 /* if we're changing the mode bits, discard the ACL cache,
2614 * since we changed the mode bits.
2616 if (afsInStatus.Mask & AFS_SETMODE)
2617 cm_FreeAllACLEnts(scp);
2618 lock_ReleaseMutex(&scp->mx);
2622 long cm_Create(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2623 cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
2628 cm_callbackRequest_t cbReq;
2631 cm_scache_t *scp = NULL;
2633 AFSStoreStatus inStatus;
2634 AFSFetchStatus updatedDirStatus;
2635 AFSFetchStatus newFileStatus;
2636 AFSCallBack newFileCallback;
2638 struct rx_connection * callp;
2641 /* can't create names with @sys in them; must expand it manually first.
2642 * return "invalid request" if they try.
2644 if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2645 return CM_ERROR_ATSYS;
2648 /* before starting the RPC, mark that we're changing the file data, so
2649 * that someone who does a chmod will know to wait until our call
2652 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2653 lock_ObtainMutex(&dscp->mx);
2654 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2655 lock_ReleaseMutex(&dscp->mx);
2657 cm_StartCallbackGrantingCall(NULL, &cbReq);
2659 cm_EndDirOp(&dirop);
2666 cm_StatusFromAttr(&inStatus, NULL, attrp);
2668 /* try the RPC now */
2669 osi_Log1(afsd_logp, "CALL CreateFile scp 0x%p", dscp);
2671 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2675 dirAFSFid.Volume = dscp->fid.volume;
2676 dirAFSFid.Vnode = dscp->fid.vnode;
2677 dirAFSFid.Unique = dscp->fid.unique;
2679 callp = cm_GetRxConn(connp);
2680 code = RXAFS_CreateFile(connp->callp, &dirAFSFid, namep,
2681 &inStatus, &newAFSFid, &newFileStatus,
2682 &updatedDirStatus, &newFileCallback,
2684 rx_PutConnection(callp);
2686 } while (cm_Analyze(connp, userp, reqp,
2687 &dscp->fid, &volSync, NULL, &cbReq, code));
2688 code = cm_MapRPCError(code, reqp);
2691 osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
2693 osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
2696 lock_ObtainWrite(&dirop.scp->dirlock);
2697 dirop.lockType = CM_DIRLOCK_WRITE;
2699 lock_ObtainMutex(&dscp->mx);
2700 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2702 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, 0);
2704 lock_ReleaseMutex(&dscp->mx);
2706 /* now try to create the file's entry, too, but be careful to
2707 * make sure that we don't merge in old info. Since we weren't locking
2708 * out any requests during the file's creation, we may have pretty old
2712 newFid.cell = dscp->fid.cell;
2713 newFid.volume = dscp->fid.volume;
2714 newFid.vnode = newAFSFid.Vnode;
2715 newFid.unique = newAFSFid.Unique;
2716 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2718 lock_ObtainMutex(&scp->mx);
2719 scp->creator = userp; /* remember who created it */
2720 if (!cm_HaveCallback(scp)) {
2721 cm_MergeStatus(dscp, scp, &newFileStatus, &volSync,
2723 cm_EndCallbackGrantingCall(scp, &cbReq,
2724 &newFileCallback, 0);
2727 lock_ReleaseMutex(&scp->mx);
2732 /* make sure we end things properly */
2734 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2736 if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
2737 cm_DirCreateEntry(&dirop, namep, &newFid);
2739 cm_BPlusDirCreateEntry(&dirop, namep, &newFid);
2742 cm_EndDirOp(&dirop);
2747 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
2751 lock_ObtainWrite(&scp->bufCreateLock);
2752 code = buf_CleanVnode(scp, userp, reqp);
2753 lock_ReleaseWrite(&scp->bufCreateLock);
2755 lock_ObtainMutex(&scp->mx);
2757 if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
2758 | CM_SCACHEMASK_CLIENTMODTIME
2759 | CM_SCACHEMASK_LENGTH))
2760 code = cm_StoreMini(scp, userp, reqp);
2762 if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) {
2763 code = (scp->flags & CM_SCACHEFLAG_OVERQUOTA) ? CM_ERROR_QUOTA : CM_ERROR_SPACE;
2764 scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE);
2767 lock_ReleaseMutex(&scp->mx);
2772 long cm_MakeDir(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2773 cm_user_t *userp, cm_req_t *reqp)
2778 cm_callbackRequest_t cbReq;
2781 cm_scache_t *scp = NULL;
2783 AFSStoreStatus inStatus;
2784 AFSFetchStatus updatedDirStatus;
2785 AFSFetchStatus newDirStatus;
2786 AFSCallBack newDirCallback;
2788 struct rx_connection * callp;
2791 /* can't create names with @sys in them; must expand it manually first.
2792 * return "invalid request" if they try.
2794 if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2795 return CM_ERROR_ATSYS;
2798 /* before starting the RPC, mark that we're changing the directory
2799 * data, so that someone who does a chmod on the dir will wait until
2800 * our call completes.
2802 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2803 lock_ObtainMutex(&dscp->mx);
2804 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2805 lock_ReleaseMutex(&dscp->mx);
2807 cm_StartCallbackGrantingCall(NULL, &cbReq);
2809 cm_EndDirOp(&dirop);
2816 cm_StatusFromAttr(&inStatus, NULL, attrp);
2818 /* try the RPC now */
2819 osi_Log1(afsd_logp, "CALL MakeDir scp 0x%p", dscp);
2821 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2825 dirAFSFid.Volume = dscp->fid.volume;
2826 dirAFSFid.Vnode = dscp->fid.vnode;
2827 dirAFSFid.Unique = dscp->fid.unique;
2829 callp = cm_GetRxConn(connp);
2830 code = RXAFS_MakeDir(connp->callp, &dirAFSFid, namep,
2831 &inStatus, &newAFSFid, &newDirStatus,
2832 &updatedDirStatus, &newDirCallback,
2834 rx_PutConnection(callp);
2836 } while (cm_Analyze(connp, userp, reqp,
2837 &dscp->fid, &volSync, NULL, &cbReq, code));
2838 code = cm_MapRPCError(code, reqp);
2841 osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
2843 osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
2846 lock_ObtainWrite(&dirop.scp->dirlock);
2847 dirop.lockType = CM_DIRLOCK_WRITE;
2849 lock_ObtainMutex(&dscp->mx);
2850 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2852 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, 0);
2854 lock_ReleaseMutex(&dscp->mx);
2856 /* now try to create the new dir's entry, too, but be careful to
2857 * make sure that we don't merge in old info. Since we weren't locking
2858 * out any requests during the file's creation, we may have pretty old
2862 newFid.cell = dscp->fid.cell;
2863 newFid.volume = dscp->fid.volume;
2864 newFid.vnode = newAFSFid.Vnode;
2865 newFid.unique = newAFSFid.Unique;
2866 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2868 lock_ObtainMutex(&scp->mx);
2869 if (!cm_HaveCallback(scp)) {
2870 cm_MergeStatus(dscp, scp, &newDirStatus, &volSync,
2872 cm_EndCallbackGrantingCall(scp, &cbReq,
2873 &newDirCallback, 0);
2876 lock_ReleaseMutex(&scp->mx);
2877 cm_ReleaseSCache(scp);
2881 /* make sure we end things properly */
2883 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2885 if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
2886 cm_DirCreateEntry(&dirop, namep, &newFid);
2888 cm_BPlusDirCreateEntry(&dirop, namep, &newFid);
2891 cm_EndDirOp(&dirop);
2893 /* and return error code */
2897 long cm_Link(cm_scache_t *dscp, char *namep, cm_scache_t *sscp, long flags,
2898 cm_user_t *userp, cm_req_t *reqp)
2903 AFSFid existingAFSFid;
2904 AFSFetchStatus updatedDirStatus;
2905 AFSFetchStatus newLinkStatus;
2907 struct rx_connection * callp;
2910 if (dscp->fid.cell != sscp->fid.cell ||
2911 dscp->fid.volume != sscp->fid.volume) {
2912 return CM_ERROR_CROSSDEVLINK;
2915 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2916 lock_ObtainMutex(&dscp->mx);
2917 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2918 lock_ReleaseMutex(&dscp->mx);
2920 cm_EndDirOp(&dirop);
2925 /* try the RPC now */
2926 osi_Log1(afsd_logp, "CALL Link scp 0x%p", dscp);
2928 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2931 dirAFSFid.Volume = dscp->fid.volume;
2932 dirAFSFid.Vnode = dscp->fid.vnode;
2933 dirAFSFid.Unique = dscp->fid.unique;
2935 existingAFSFid.Volume = sscp->fid.volume;
2936 existingAFSFid.Vnode = sscp->fid.vnode;
2937 existingAFSFid.Unique = sscp->fid.unique;
2939 callp = cm_GetRxConn(connp);
2940 code = RXAFS_Link(callp, &dirAFSFid, namep, &existingAFSFid,
2941 &newLinkStatus, &updatedDirStatus, &volSync);
2942 rx_PutConnection(callp);
2943 osi_Log1(smb_logp," RXAFS_Link returns 0x%x", code);
2945 } while (cm_Analyze(connp, userp, reqp,
2946 &dscp->fid, &volSync, NULL, NULL, code));
2948 code = cm_MapRPCError(code, reqp);
2951 osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
2953 osi_Log0(afsd_logp, "CALL Link SUCCESS");
2956 lock_ObtainWrite(&dirop.scp->dirlock);
2957 dirop.lockType = CM_DIRLOCK_WRITE;
2959 lock_ObtainMutex(&dscp->mx);
2960 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2962 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, 0);
2964 lock_ReleaseMutex(&dscp->mx);
2967 if (cm_CheckDirOpForSingleChange(&dirop)) {
2968 cm_DirCreateEntry(&dirop, namep, &sscp->fid);
2970 cm_BPlusDirCreateEntry(&dirop, namep, &sscp->fid);
2974 cm_EndDirOp(&dirop);
2979 long cm_SymLink(cm_scache_t *dscp, char *namep, char *contentsp, long flags,
2980 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
2988 AFSStoreStatus inStatus;
2989 AFSFetchStatus updatedDirStatus;
2990 AFSFetchStatus newLinkStatus;
2992 struct rx_connection * callp;
2995 /* before starting the RPC, mark that we're changing the directory data,
2996 * so that someone who does a chmod on the dir will wait until our
2999 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
3000 lock_ObtainMutex(&dscp->mx);
3001 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3002 lock_ReleaseMutex(&dscp->mx);
3004 cm_EndDirOp(&dirop);
3009 cm_StatusFromAttr(&inStatus, NULL, attrp);
3011 /* try the RPC now */
3012 osi_Log1(afsd_logp, "CALL Symlink scp 0x%p", dscp);
3014 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3018 dirAFSFid.Volume = dscp->fid.volume;
3019 dirAFSFid.Vnode = dscp->fid.vnode;
3020 dirAFSFid.Unique = dscp->fid.unique;
3022 callp = cm_GetRxConn(connp);
3023 code = RXAFS_Symlink(callp, &dirAFSFid, namep, contentsp,
3024 &inStatus, &newAFSFid, &newLinkStatus,
3025 &updatedDirStatus, &volSync);
3026 rx_PutConnection(callp);
3028 } while (cm_Analyze(connp, userp, reqp,
3029 &dscp->fid, &volSync, NULL, NULL, code));
3030 code = cm_MapRPCError(code, reqp);
3033 osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
3035 osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
3038 lock_ObtainWrite(&dirop.scp->dirlock);
3039 dirop.lockType = CM_DIRLOCK_WRITE;
3041 lock_ObtainMutex(&dscp->mx);
3042 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3044 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, 0);
3046 lock_ReleaseMutex(&dscp->mx);
3049 if (cm_CheckDirOpForSingleChange(&dirop)) {
3050 newFid.cell = dscp->fid.cell;
3051 newFid.volume = dscp->fid.volume;
3052 newFid.vnode = newAFSFid.Vnode;
3053 newFid.unique = newAFSFid.Unique;
3055 cm_DirCreateEntry(&dirop, namep, &newFid);
3057 cm_BPlusDirCreateEntry(&dirop, namep, &newFid);
3061 cm_EndDirOp(&dirop);
3063 /* now try to create the new dir's entry, too, but be careful to
3064 * make sure that we don't merge in old info. Since we weren't locking
3065 * out any requests during the file's creation, we may have pretty old
3069 newFid.cell = dscp->fid.cell;
3070 newFid.volume = dscp->fid.volume;
3071 newFid.vnode = newAFSFid.Vnode;
3072 newFid.unique = newAFSFid.Unique;
3073 code = cm_GetSCache(&newFid, &scp, userp, reqp);
3075 lock_ObtainMutex(&scp->mx);
3076 if (!cm_HaveCallback(scp)) {
3077 cm_MergeStatus(dscp, scp, &newLinkStatus, &volSync,
3080 lock_ReleaseMutex(&scp->mx);
3081 cm_ReleaseSCache(scp);
3085 /* and return error code */
3089 long cm_RemoveDir(cm_scache_t *dscp, char *namep, cm_user_t *userp,
3096 AFSFetchStatus updatedDirStatus;
3098 struct rx_connection * callp;
3101 /* before starting the RPC, mark that we're changing the directory data,
3102 * so that someone who does a chmod on the dir will wait until our
3105 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
3106 lock_ObtainMutex(&dscp->mx);
3107 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3108 lock_ReleaseMutex(&dscp->mx);
3110 cm_EndDirOp(&dirop);
3115 /* try the RPC now */
3116 osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%p", dscp);
3118 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3122 dirAFSFid.Volume = dscp->fid.volume;
3123 dirAFSFid.Vnode = dscp->fid.vnode;
3124 dirAFSFid.Unique = dscp->fid.unique;
3126 callp = cm_GetRxConn(connp);
3127 code = RXAFS_RemoveDir(callp, &dirAFSFid, namep,
3128 &updatedDirStatus, &volSync);
3129 rx_PutConnection(callp);
3131 } while (cm_Analyze(connp, userp, reqp,
3132 &dscp->fid, &volSync, NULL, NULL, code));
3133 code = cm_MapRPCErrorRmdir(code, reqp);
3136 osi_Log1(afsd_logp, "CALL RemoveDir FAILURE, code 0x%x", code);
3138 osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
3141 lock_ObtainWrite(&dirop.scp->dirlock);
3142 dirop.lockType = CM_DIRLOCK_WRITE;
3144 lock_ObtainMutex(&dscp->mx);
3145 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3147 cm_dnlcRemove(dscp, namep);
3148 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, 0);
3150 lock_ReleaseMutex(&dscp->mx);
3153 if (cm_CheckDirOpForSingleChange(&dirop)) {
3154 cm_DirDeleteEntry(&dirop, namep);
3156 cm_BPlusDirDeleteEntry(&dirop, namep);
3160 cm_EndDirOp(&dirop);
3162 /* and return error code */
3166 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
3168 /* grab mutex on contents */
3169 lock_ObtainMutex(&scp->mx);
3171 /* reset the prefetch info */
3172 scp->prefetch.base.LowPart = 0; /* base */
3173 scp->prefetch.base.HighPart = 0;
3174 scp->prefetch.end.LowPart = 0; /* and end */
3175 scp->prefetch.end.HighPart = 0;
3177 /* release mutex on contents */
3178 lock_ReleaseMutex(&scp->mx);
3184 long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
3185 char *newNamep, cm_user_t *userp, cm_req_t *reqp)
3189 AFSFid oldDirAFSFid;
3190 AFSFid newDirAFSFid;
3192 AFSFetchStatus updatedOldDirStatus;
3193 AFSFetchStatus updatedNewDirStatus;
3196 struct rx_connection * callp;
3197 cm_dirOp_t oldDirOp;
3200 cm_dirOp_t newDirOp;
3202 /* before starting the RPC, mark that we're changing the directory data,
3203 * so that someone who does a chmod on the dir will wait until our call
3204 * completes. We do this in vnode order so that we don't deadlock,
3205 * which makes the code a little verbose.
3207 if (oldDscp == newDscp) {
3208 /* check for identical names */
3209 if (strcmp(oldNamep, newNamep) == 0)
3210 return CM_ERROR_RENAME_IDENTICAL;
3213 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3214 lock_ObtainMutex(&oldDscp->mx);
3215 cm_dnlcRemove(oldDscp, oldNamep);
3216 cm_dnlcRemove(oldDscp, newNamep);
3217 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3218 CM_SCACHESYNC_STOREDATA);
3219 lock_ReleaseMutex(&oldDscp->mx);
3221 cm_EndDirOp(&oldDirOp);
3225 /* two distinct dir vnodes */
3227 if (oldDscp->fid.cell != newDscp->fid.cell ||
3228 oldDscp->fid.volume != newDscp->fid.volume)
3229 return CM_ERROR_CROSSDEVLINK;
3231 /* shouldn't happen that we have distinct vnodes for two
3232 * different files, but could due to deliberate attack, or
3233 * stale info. Avoid deadlocks and quit now.
3235 if (oldDscp->fid.vnode == newDscp->fid.vnode)
3236 return CM_ERROR_CROSSDEVLINK;
3238 if (oldDscp->fid.vnode < newDscp->fid.vnode) {
3239 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3240 lock_ObtainMutex(&oldDscp->mx);
3241 cm_dnlcRemove(oldDscp, oldNamep);
3242 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3243 CM_SCACHESYNC_STOREDATA);
3244 lock_ReleaseMutex(&oldDscp->mx);
3246 cm_EndDirOp(&oldDirOp);
3248 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE, &newDirOp);
3249 lock_ObtainMutex(&newDscp->mx);
3250 cm_dnlcRemove(newDscp, newNamep);
3251 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3252 CM_SCACHESYNC_STOREDATA);
3253 lock_ReleaseMutex(&newDscp->mx);
3255 cm_EndDirOp(&newDirOp);
3257 /* cleanup first one */
3258 lock_ObtainMutex(&oldDscp->mx);
3259 cm_SyncOpDone(oldDscp, NULL,
3260 CM_SCACHESYNC_STOREDATA);
3261 lock_ReleaseMutex(&oldDscp->mx);
3262 cm_EndDirOp(&oldDirOp);
3267 /* lock the new vnode entry first */
3268 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE, &newDirOp);
3269 lock_ObtainMutex(&newDscp->mx);
3270 cm_dnlcRemove(newDscp, newNamep);
3271 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3272 CM_SCACHESYNC_STOREDATA);
3273 lock_ReleaseMutex(&newDscp->mx);
3275 cm_EndDirOp(&newDirOp);
3277 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3278 lock_ObtainMutex(&oldDscp->mx);
3279 cm_dnlcRemove(oldDscp, oldNamep);
3280 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3281 CM_SCACHESYNC_STOREDATA);
3282 lock_ReleaseMutex(&oldDscp->mx);
3284 cm_EndDirOp(&oldDirOp);
3286 /* cleanup first one */
3287 lock_ObtainMutex(&newDscp->mx);
3288 cm_SyncOpDone(newDscp, NULL,
3289 CM_SCACHESYNC_STOREDATA);
3290 lock_ReleaseMutex(&newDscp->mx);
3291 cm_EndDirOp(&newDirOp);
3295 } /* two distinct vnodes */
3302 /* try the RPC now */
3303 osi_Log2(afsd_logp, "CALL Rename old scp 0x%p new scp 0x%p",
3306 code = cm_ConnFromFID(&oldDscp->fid, userp, reqp, &connp);
3310 oldDirAFSFid.Volume = oldDscp->fid.volume;
3311 oldDirAFSFid.Vnode = oldDscp->fid.vnode;
3312 oldDirAFSFid.Unique = oldDscp->fid.unique;
3313 newDirAFSFid.Volume = newDscp->fid.volume;
3314 newDirAFSFid.Vnode = newDscp->fid.vnode;
3315 newDirAFSFid.Unique = newDscp->fid.unique;
3317 callp = cm_GetRxConn(connp);
3318 code = RXAFS_Rename(callp, &oldDirAFSFid, oldNamep,
3319 &newDirAFSFid, newNamep,
3320 &updatedOldDirStatus, &updatedNewDirStatus,
3322 rx_PutConnection(callp);
3324 } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
3325 &volSync, NULL, NULL, code));
3326 code = cm_MapRPCError(code, reqp);
3329 osi_Log1(afsd_logp, "CALL Rename FAILURE, code 0x%x", code);
3331 osi_Log0(afsd_logp, "CALL Rename SUCCESS");
3333 /* update the individual stat cache entries for the directories */
3335 lock_ObtainWrite(&oldDirOp.scp->dirlock);
3336 oldDirOp.lockType = CM_DIRLOCK_WRITE;
3338 lock_ObtainMutex(&oldDscp->mx);
3339 cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
3342 cm_MergeStatus(NULL, oldDscp, &updatedOldDirStatus, &volSync,
3344 lock_ReleaseMutex(&oldDscp->mx);
3347 if (cm_CheckDirOpForSingleChange(&oldDirOp)) {
3350 diropCode = cm_BPlusDirLookup(&oldDirOp, oldNamep, &fileFid);
3351 if (diropCode == CM_ERROR_INEXACT_MATCH)
3353 else if (diropCode == EINVAL)
3355 diropCode = cm_DirLookup(&oldDirOp, oldNamep, &fileFid);
3357 if (diropCode == 0) {
3359 diropCode = cm_DirCreateEntry(&oldDirOp, newNamep, &fileFid);
3361 cm_BPlusDirCreateEntry(&oldDirOp, newNamep, &fileFid);
3365 if (diropCode == 0) {
3366 diropCode = cm_DirDeleteEntry(&oldDirOp, oldNamep);
3368 cm_BPlusDirDeleteEntry(&oldDirOp, oldNamep);
3374 cm_EndDirOp(&oldDirOp);
3376 /* and update it for the new one, too, if necessary */
3379 lock_ObtainWrite(&newDirOp.scp->dirlock);
3380 newDirOp.lockType = CM_DIRLOCK_WRITE;
3382 lock_ObtainMutex(&newDscp->mx);
3383 cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
3385 cm_MergeStatus(NULL, newDscp, &updatedNewDirStatus, &volSync,
3387 lock_ReleaseMutex(&newDscp->mx);
3390 /* we only make the local change if we successfully made
3391 the change in the old directory AND there was only one
3392 change in the new directory */
3393 if (diropCode == 0 && cm_CheckDirOpForSingleChange(&newDirOp)) {
3394 cm_DirCreateEntry(&newDirOp, newNamep, &fileFid);
3396 cm_BPlusDirCreateEntry(&newDirOp, newNamep, &fileFid);
3400 cm_EndDirOp(&newDirOp);
3403 /* and return error code */
3407 /* Byte range locks:
3409 The OpenAFS Windows client has to fake byte range locks given no
3410 server side support for such locks. This is implemented as keyed
3411 byte range locks on the cache manager.
3413 Keyed byte range locks:
3415 Each cm_scache_t structure keeps track of a list of keyed locks.
3416 The key for a lock identifies an owner of a set of locks (referred
3417 to as a client). Each key is represented by a value. The set of
3418 key values used within a specific cm_scache_t structure form a
3419 namespace that has a scope of just that cm_scache_t structure. The
3420 same key value can be used with another cm_scache_t structure and
3421 correspond to a completely different client. However it is
3422 advantageous for the SMB or IFS layer to make sure that there is a
3423 1-1 mapping between client and keys over all cm_scache_t objects.
3425 Assume a client C has key Key(C) (although, since the scope of the
3426 key is a cm_scache_t, the key can be Key(C,S), where S is the
3427 cm_scache_t. But assume a 1-1 relation between keys and clients).
3428 A byte range (O,+L) denotes byte addresses (O) through (O+L-1)
3429 inclusive (a.k.a. [O,O+L-1]). The function Key(x) is implemented
3430 through cm_generateKey() function for both SMB and IFS.
3432 The list of locks for a cm_scache_t object S is maintained in
3433 S->fileLocks. The cache manager will set a lock on the AFS file
3434 server in order to assert the locks in S->fileLocks. If only
3435 shared locks are in place for S, then the cache manager will obtain
3436 a LockRead lock, while if there are any exclusive locks, it will
3437 obtain a LockWrite lock. If the exclusive locks are all released
3438 while the shared locks remain, then the cache manager will
3439 downgrade the lock from LockWrite to LockRead. Similarly, if an
3440 exclusive lock is obtained when only shared locks exist, then the
3441 cache manager will try to upgrade the lock from LockRead to
3444 Each lock L owned by client C maintains a key L->key such that
3445 L->key == Key(C), the effective range defined by L->LOffset and
3446 L->LLength such that the range of bytes affected by the lock is
3447 (L->LOffset, +L->LLength), a type maintained in L->LockType which
3448 is either exclusive or shared.
3452 A lock exists iff it is in S->fileLocks for some cm_scache_t
3453 S. Existing locks are in one of the following states: ACTIVE,
3454 WAITLOCK, WAITUNLOCK, LOST, DELETED.
3456 The following sections describe each lock and the associated
3459 1. ACTIVE: A lock L is ACTIVE iff the cache manager has asserted
3460 the lock with the AFS file server. This type of lock can be
3461 exercised by a client to read or write to the locked region (as
3464 1.1 ACTIVE->LOST: When the AFS file server fails to extend a
3465 server lock that was required to assert the lock. Before
3466 marking the lock as lost, the cache manager checks if the file
3467 has changed on the server. If the file has not changed, then
3468 the cache manager will attempt to obtain a new server lock
3469 that is sufficient to assert the client side locks for the
3470 file. If any of these fail, the lock is marked as LOST.
3471 Otherwise, it is left as ACTIVE.
3473 1.2 ACTIVE->DELETED: Lock is released.
3475 2. WAITLOCK: A lock is in a WAITLOCK state if the cache manager
3476 grants the lock but the lock is yet to be asserted with the AFS
3477 file server. Once the file server grants the lock, the state
3478 will transition to an ACTIVE lock.
3480 2.1 WAITLOCK->ACTIVE: The server granted the lock.
3482 2.2 WAITLOCK->DELETED: Lock is abandoned, or timed out during
3485 2.3 WAITLOCK->LOST: One or more locks from this client were
3486 marked as LOST. No further locks will be granted to this
3487 client until all lost locks are removed.
3489 3. WAITUNLOCK: A lock is in a WAITUNLOCK state if the cache manager
3490 receives a request for a lock that conflicts with an existing
3491 ACTIVE or WAITLOCK lock. The lock will be placed in the queue
3492 and will be granted at such time the conflicting locks are
3493 removed, at which point the state will transition to either
3496 3.1 WAITUNLOCK->ACTIVE: The conflicting lock was removed. The
3497 current serverLock is sufficient to assert this lock, or a
3498 sufficient serverLock is obtained.
3500 3.2 WAITUNLOCK->WAITLOCK: The conflicting lock was removed,
3501 however the required serverLock is yet to be asserted with the
3504 3.3 WAITUNLOCK->DELETED: The lock is abandoned, timed out or
3507 3.5 WAITUNLOCK->LOST: One or more locks from this client were
3508 marked as LOST. No further locks will be granted to this
3509 client until all lost locks are removed.
3511 4. LOST: A lock L is LOST if the server lock that was required to
3512 assert the lock could not be obtained or if it could not be
3513 extended, or if other locks by the same client were LOST.
3514 Essentially, once a lock is LOST, the contract between the cache
3515 manager and that specific client is no longer valid.
3517 The cache manager rechecks the server lock once every minute and
3518 extends it as appropriate. If this is not done for 5 minutes,
3519 the AFS file server will release the lock (the 5 minute timeout
3520 is based on current file server code and is fairly arbitrary).
3521 Once released, the lock cannot be re-obtained without verifying
3522 that the contents of the file hasn't been modified since the
3523 time the lock was released. Re-obtaining the lock without
3524 verifying this may lead to data corruption. If the lock can not
3525 be obtained safely, then all active locks for the cm_scache_t
3528 4.1 LOST->DELETED: The lock is released.
3530 5. DELETED: The lock is no longer relevant. Eventually, it will
3531 get removed from the cm_scache_t. In the meantime, it will be
3532 treated as if it does not exist.
3534 5.1 DELETED->not exist: The lock is removed from the
3537 The following are classifications of locks based on their state.
3539 6* A lock L is ACCEPTED if it is ACTIVE or WAITLOCK. These locks
3540 have been accepted by the cache manager, but may or may not have
3541 been granted back to the client.
3543 7* A lock L is QUEUED if it is ACTIVE, WAITLOCK or WAITUNLOCK.
3545 8* A lock L is WAITING if it is WAITLOCK or WAITUNLOCK.
3549 A client C can READ range (Offset,+Length) of a file represented by
3550 cm_scache_t S iff (1):
3552 1. for all _a_ in (Offset,+Length), all of the following is true:
3554 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3555 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3558 1.2 For each LOST lock L in S->fileLocks such that _a_ in
3559 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3562 (When locks are lost on an cm_scache_t, all locks are lost. By
3563 4.2 (below), if there is an exclusive LOST lock, then there
3564 can't be any overlapping ACTIVE locks.)
3566 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
3568 2. for all _a_ in (Offset,+Length), one of the following is true:
3570 2.1 Byte _a_ of S is unowned (as specified in 1.1) AND there
3571 does not exist a LOST lock L such that _a_ in
3572 (L->LOffset,+L->LLength).
3574 2.2 Byte _a_ of S is owned by C under lock L (as specified in
3575 1.2) AND L->LockType is exclusive.
3577 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
3579 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
3582 3.1 If L->LockType is exclusive then there does NOT exist a
3583 ACCEPTED lock M in S->fileLocks such that _a_ in
3584 (M->LOffset,+M->LLength).
3586 (If we count all QUEUED locks then we hit cases such as
3587 cascading waiting locks where the locks later on in the queue
3588 can be granted without compromising file integrity. On the
3589 other hand if only ACCEPTED locks are considered, then locks
3590 that were received earlier may end up waiting for locks that
3591 were received later to be unlocked. The choice of ACCEPTED
3592 locks was made to mimic the Windows byte range lock
3595 3.2 If L->LockType is shared then for each ACCEPTED lock M in
3596 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
3597 M->LockType is shared.
3599 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
3601 4.1 M->key != Key(C)
3603 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
3604 and (M->LOffset,+M->LLength) do not intersect.
3606 (Note: If a client loses a lock, it loses all locks.
3607 Subsequently, it will not be allowed to obtain any more locks
3608 until all existing LOST locks that belong to the client are
3609 released. Once all locks are released by a single client,
3610 there exists no further contract between the client and AFS
3611 about the contents of the file, hence the client can then
3612 proceed to obtain new locks and establish a new contract.
3614 This doesn't quite work as you think it should, because most
3615 applications aren't built to deal with losing locks they
3616 thought they once had. For now, we don't have a good
3617 solution to lost locks.
3619 Also, for consistency reasons, we have to hold off on
3620 granting locks that overlap exclusive LOST locks.)
3622 A client C can only unlock locks L in S->fileLocks which have
3625 The representation and invariants are as follows:
3627 - Each cm_scache_t structure keeps:
3629 - A queue of byte-range locks (cm_scache_t::fileLocks) which
3630 are of type cm_file_lock_t.
3632 - A record of the highest server-side lock that has been
3633 obtained for this object (cm_scache_t::serverLock), which is
3634 one of (-1), LockRead, LockWrite.
3636 - A count of ACCEPTED exclusive and shared locks that are in the
3637 queue (cm_scache_t::sharedLocks and
3638 cm_scache_t::exclusiveLocks)
3640 - Each cm_file_lock_t structure keeps:
3642 - The type of lock (cm_file_lock_t::LockType)
3644 - The key associated with the lock (cm_file_lock_t::key)
3646 - The offset and length of the lock (cm_file_lock_t::LOffset
3647 and cm_file_lock_t::LLength)
3649 - The state of the lock.
3651 - Time of issuance or last successful extension
3653 Semantic invariants:
3655 I1. The number of ACCEPTED locks in S->fileLocks are
3656 (S->sharedLocks + S->exclusiveLocks)
3658 External invariants:
3660 I3. S->serverLock is the lock that we have asserted with the
3661 AFS file server for this cm_scache_t.
3663 I4. S->serverLock == LockRead iff there is at least one ACTIVE
3664 shared lock, but no ACTIVE exclusive locks.
3666 I5. S->serverLock == LockWrite iff there is at least one ACTIVE
3669 I6. If L is a LOST lock, then for each lock M in S->fileLocks,
3670 M->key == L->key IMPLIES M is LOST or DELETED.
3675 #define IS_LOCK_ACTIVE(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == 0)
3677 #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)
3679 #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)
3681 #define IS_LOCK_LOST(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_LOST)
3683 #define IS_LOCK_DELETED(lockp) (((lockp)->flags & CM_FILELOCK_FLAG_DELETED) == CM_FILELOCK_FLAG_DELETED)
3686 #define IS_LOCK_ACCEPTED(lockp) (IS_LOCK_ACTIVE(lockp) || IS_LOCK_WAITLOCK(lockp))
3689 #define IS_LOCK_CLIENTONLY(lockp) ((((lockp)->scp->flags & CM_SCACHEFLAG_RO) == CM_SCACHEFLAG_RO) || (((lockp)->flags & CM_FILELOCK_FLAG_CLIENTONLY) == CM_FILELOCK_FLAG_CLIENTONLY))
3692 #define INTERSECT_RANGE(r1,r2) (((r2).offset+(r2).length) > (r1).offset && ((r1).offset +(r1).length) > (r2).offset)
3695 #define CONTAINS_RANGE(r1,r2) (((r2).offset+(r2).length) <= ((r1).offset+(r1).length) && (r1).offset <= (r2).offset)
3697 #if defined(VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS) && !defined(LOCK_TESTING)
3698 #define SCP_SUPPORTS_BRLOCKS(scp) ((scp)->cbServerp && ((scp)->cbServerp->capabilities & VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS))
3700 #define SCP_SUPPORTS_BRLOCKS(scp) (1)
3703 #define SERVERLOCKS_ENABLED(scp) (!((scp)->flags & CM_SCACHEFLAG_RO) && cm_enableServerLocks && SCP_SUPPORTS_BRLOCKS(scp))
3705 #if defined(VICED_CAPABILITY_WRITELOCKACL)
3706 #define SCP_SUPPORTS_WRITELOCKACL(scp) ((scp)->cbServerp && ((scp->cbServerp->capabilities & VICED_CAPABILITY_WRITELOCKACL)))
3708 #define SCP_SUPPORTS_WRITELOCKACL(scp) (0)
3710 /* This should really be defined in any build that this code is being
3712 #error VICED_CAPABILITY_WRITELOCKACL not defined.
3715 static void cm_LockRangeSubtract(cm_range_t * pos, const cm_range_t * neg)
3717 afs_int64 int_begin;
3720 int_begin = MAX(pos->offset, neg->offset);
3721 int_end = MIN(pos->offset+pos->length, neg->offset+neg->length);
3723 if (int_begin < int_end) {
3724 if (int_begin == pos->offset) {
3725 pos->length = pos->offset + pos->length - int_end;
3726 pos->offset = int_end;
3727 } else if (int_end == pos->offset + pos->length) {
3728 pos->length = int_begin - pos->offset;
3731 /* We only subtract ranges if the resulting range is
3732 contiguous. If we try to support non-contigous ranges, we
3733 aren't actually improving performance. */
3737 /* Called with scp->mx held. Returns 0 if all is clear to read the
3738 specified range by the client identified by key.
3740 long cm_LockCheckRead(cm_scache_t *scp,
3741 LARGE_INTEGER LOffset,
3742 LARGE_INTEGER LLength,
3745 #ifndef ADVISORY_LOCKS
3747 cm_file_lock_t *fileLock;
3751 int substract_ranges = FALSE;
3753 range.offset = LOffset.QuadPart;
3754 range.length = LLength.QuadPart;
3758 1. for all _a_ in (Offset,+Length), all of the following is true:
3760 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3761 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3764 1.2 For each LOST lock L in S->fileLocks such that _a_ in
3765 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3770 lock_ObtainRead(&cm_scacheLock);
3772 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
3774 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
3776 if (INTERSECT_RANGE(range, fileLock->range)) {
3777 if (IS_LOCK_ACTIVE(fileLock)) {
3778 if (fileLock->key == key) {
3780 /* If there is an active lock for this client, it
3781 is safe to substract ranges.*/
3782 cm_LockRangeSubtract(&range, &fileLock->range);
3783 substract_ranges = TRUE;
3785 if (fileLock->lockType != LockRead) {
3786 code = CM_ERROR_LOCK_CONFLICT;
3790 /* even if the entire range is locked for reading,
3791 we still can't grant the lock at this point
3792 because the client may have lost locks. That
3793 is, unless we have already seen an active lock
3794 belonging to the client, in which case there
3795 can't be any lost locks for this client. */
3796 if (substract_ranges)
3797 cm_LockRangeSubtract(&range, &fileLock->range);
3799 } else if (IS_LOCK_LOST(fileLock) &&
3800 (fileLock->key == key || fileLock->lockType == LockWrite)) {
3801 code = CM_ERROR_BADFD;
3807 lock_ReleaseRead(&cm_scacheLock);
3809 osi_Log4(afsd_logp, "cm_LockCheckRead scp 0x%x offset %d length %d code 0x%x",
3810 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
3821 /* Called with scp->mx held. Returns 0 if all is clear to write the
3822 specified range by the client identified by key.
3824 long cm_LockCheckWrite(cm_scache_t *scp,
3825 LARGE_INTEGER LOffset,
3826 LARGE_INTEGER LLength,
3829 #ifndef ADVISORY_LOCKS
3831 cm_file_lock_t *fileLock;
3836 range.offset = LOffset.QuadPart;
3837 range.length = LLength.QuadPart;
3840 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
3842 2. for all _a_ in (Offset,+Length), one of the following is true:
3844 2.1 Byte _a_ of S is unowned AND there does not exist a LOST
3845 lock L such that _a_ in (L->LOffset,+L->LLength).
3847 2.2 Byte _a_ of S is owned by C under lock L AND L->LockType is
3851 lock_ObtainRead(&cm_scacheLock);
3853 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
3855 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
3857 if (INTERSECT_RANGE(range, fileLock->range)) {
3858 if (IS_LOCK_ACTIVE(fileLock)) {
3859 if (fileLock->key == key) {
3860 if (fileLock->lockType == LockWrite) {
3862 /* if there is an active lock for this client, it
3863 is safe to substract ranges */
3864 cm_LockRangeSubtract(&range, &fileLock->range);
3866 code = CM_ERROR_LOCK_CONFLICT;
3870 code = CM_ERROR_LOCK_CONFLICT;
3873 } else if (IS_LOCK_LOST(fileLock)) {
3874 code = CM_ERROR_BADFD;
3880 lock_ReleaseRead(&cm_scacheLock);
3882 osi_Log4(afsd_logp, "cm_LockCheckWrite scp 0x%x offset %d length %d code 0x%x",
3883 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
3895 static void cm_LockMarkSCacheLost(cm_scache_t * scp);
3897 /* Called with cm_scacheLock write locked */
3898 static cm_file_lock_t * cm_GetFileLock(void) {
3901 l = (cm_file_lock_t *) cm_freeFileLocks;
3903 osi_QRemove(&cm_freeFileLocks, &l->q);
3905 l = malloc(sizeof(cm_file_lock_t));
3906 osi_assertx(l, "null cm_file_lock_t");
3909 memset(l, 0, sizeof(cm_file_lock_t));
3914 /* Called with cm_scacheLock write locked */
3915 static void cm_PutFileLock(cm_file_lock_t *l) {
3916 osi_QAdd(&cm_freeFileLocks, &l->q);
3919 /* called with scp->mx held. May release it during processing, but
3920 leaves it held on exit. */
3921 long cm_IntSetLock(cm_scache_t * scp, cm_user_t * userp, int lockType,
3927 struct rx_connection * callp;
3930 tfid.Volume = scp->fid.volume;
3931 tfid.Vnode = scp->fid.vnode;
3932 tfid.Unique = scp->fid.unique;
3935 osi_Log2(afsd_logp, "CALL SetLock scp 0x%p for lock %d", scp, lockType);
3937 lock_ReleaseMutex(&scp->mx);
3940 code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
3944 callp = cm_GetRxConn(connp);
3945 code = RXAFS_SetLock(callp, &tfid, lockType,
3947 rx_PutConnection(callp);
3949 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
3952 code = cm_MapRPCError(code, reqp);
3954 osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
3956 osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
3959 lock_ObtainMutex(&scp->mx);
3964 /* called with scp->mx held. Releases it during processing */
3965 long cm_IntReleaseLock(cm_scache_t * scp, cm_user_t * userp,
3971 struct rx_connection * callp;
3974 tfid.Volume = scp->fid.volume;
3975 tfid.Vnode = scp->fid.vnode;
3976 tfid.Unique = scp->fid.unique;
3979 lock_ReleaseMutex(&scp->mx);
3981 osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
3984 code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
3988 callp = cm_GetRxConn(connp);
3989 code = RXAFS_ReleaseLock(callp, &tfid, &volSync);
3990 rx_PutConnection(callp);
3992 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
3994 code = cm_MapRPCError(code, reqp);
3997 "CALL ReleaseLock FAILURE, code 0x%x", code);
4000 "CALL ReleaseLock SUCCESS");
4002 lock_ObtainMutex(&scp->mx);
4007 /* called with scp->mx held. May release it during processing, but
4008 will exit with lock held.
4012 - 0 if the user has permission to get the specified lock for the scp
4014 - CM_ERROR_NOACCESS if not
4016 Any other error from cm_SyncOp will be sent down untranslated.
4018 If CM_ERROR_NOACCESS is returned and lock_type is LockRead, then
4019 phas_insert (if non-NULL) will receive a boolean value indicating
4020 whether the user has INSERT permission or not.
4022 long cm_LockCheckPerms(cm_scache_t * scp,
4029 long code = 0, code2 = 0;
4031 /* lock permissions are slightly tricky because of the 'i' bit.
4032 If the user has PRSFS_LOCK, she can read-lock the file. If the
4033 user has PRSFS_WRITE, she can write-lock the file. However, if
4034 the user has PRSFS_INSERT, then she can write-lock new files,
4035 but not old ones. Since we don't have information about
4036 whether a file is new or not, we assume that if the user owns
4037 the scp, then she has the permissions that are granted by
4040 osi_Log3(afsd_logp, "cm_LockCheckPerms for scp[0x%p] type[%d] user[0x%p]",
4041 scp, lock_type, userp);
4043 if (lock_type == LockRead)
4044 rights |= PRSFS_LOCK;
4045 else if (lock_type == LockWrite)
4046 rights |= PRSFS_WRITE | PRSFS_LOCK;
4049 osi_assertx(FALSE, "invalid lock type");
4054 *phas_insert = FALSE;
4056 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
4057 CM_SCACHESYNC_GETSTATUS |
4058 CM_SCACHESYNC_NEEDCALLBACK);
4060 if (phas_insert && scp->creator == userp) {
4062 /* If this file was created by the user, then we check for
4063 PRSFS_INSERT. If the file server is recent enough, then
4064 this should be sufficient for her to get a write-lock (but
4065 not necessarily a read-lock). VICED_CAPABILITY_WRITELOCKACL
4066 indicates whether a file server supports getting write
4067 locks when the user only has PRSFS_INSERT.
4069 If the file was not created by the user we skip the check
4070 because the INSERT bit will not apply to this user even
4074 code2 = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
4075 CM_SCACHESYNC_GETSTATUS |
4076 CM_SCACHESYNC_NEEDCALLBACK);
4078 if (code2 == CM_ERROR_NOACCESS) {
4079 osi_Log0(afsd_logp, "cm_LockCheckPerms user has no INSERT bits");
4081 *phas_insert = TRUE;
4082 osi_Log0(afsd_logp, "cm_LockCheckPerms user has INSERT bits");
4086 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
4088 osi_Log1(afsd_logp, "cm_LockCheckPerms returning code %d", code);
4093 /* called with scp->mx held */
4094 long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
4095 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
4097 int allowWait, cm_user_t *userp, cm_req_t *reqp,
4098 cm_file_lock_t **lockpp)
4101 int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
4102 cm_file_lock_t *fileLock;
4105 int wait_unlock = FALSE;
4106 int force_client_lock = FALSE;
4108 osi_Log4(afsd_logp, "cm_Lock scp 0x%x type 0x%x offset %d length %d",
4109 scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
4110 osi_Log3(afsd_logp, "... allowWait %d key 0x%x:%x", allowWait,
4111 (unsigned long)(key >> 32), (unsigned long)(key & 0xffffffff));
4114 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
4116 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
4119 3.1 If L->LockType is exclusive then there does NOT exist a
4120 ACCEPTED lock M in S->fileLocks such that _a_ in
4121 (M->LOffset,+M->LLength).
4123 3.2 If L->LockType is shared then for each ACCEPTED lock M in
4124 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
4125 M->LockType is shared.
4127 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
4129 4.1 M->key != Key(C)
4131 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
4132 and (M->LOffset,+M->LLength) do not intersect.
4135 range.offset = LOffset.QuadPart;
4136 range.length = LLength.QuadPart;
4138 lock_ObtainRead(&cm_scacheLock);
4140 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4142 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4144 if (IS_LOCK_LOST(fileLock)) {
4145 if (fileLock->key == key) {
4146 code = CM_ERROR_BADFD;
4148 } else if (fileLock->lockType == LockWrite && INTERSECT_RANGE(range, fileLock->range)) {
4149 code = CM_ERROR_WOULDBLOCK;
4155 /* we don't need to check for deleted locks here since deleted
4156 locks are dequeued from scp->fileLocks */
4157 if (IS_LOCK_ACCEPTED(fileLock) &&
4158 INTERSECT_RANGE(range, fileLock->range)) {
4160 if ((sLockType & LOCKING_ANDX_SHARED_LOCK) == 0 ||
4161 fileLock->lockType != LockRead) {
4163 code = CM_ERROR_WOULDBLOCK;
4169 lock_ReleaseRead(&cm_scacheLock);
4171 if (code == 0 && SERVERLOCKS_ENABLED(scp)) {
4172 if (Which == scp->serverLock ||
4173 (Which == LockRead && scp->serverLock == LockWrite)) {
4177 /* we already have the lock we need */
4178 osi_Log3(afsd_logp, " we already have the correct lock. exclusives[%d], shared[%d], serverLock[%d]",
4179 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4181 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4183 /* special case: if we don't have permission to read-lock
4184 the file, then we force a clientside lock. This is to
4185 compensate for applications that obtain a read-lock for
4186 reading files off of directories that don't grant
4187 read-locks to the user. */
4188 if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4190 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4191 osi_Log0(afsd_logp, " User has no read-lock perms, but has INSERT perms.");
4194 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4195 force_client_lock = TRUE;
4199 } else if ((scp->exclusiveLocks > 0) ||
4200 (scp->sharedLocks > 0 && scp->serverLock != LockRead)) {
4203 /* We are already waiting for some other lock. We should
4204 wait for the daemon to catch up instead of generating a
4205 flood of SetLock calls. */
4206 osi_Log3(afsd_logp, " already waiting for other lock. exclusives[%d], shared[%d], serverLock[%d]",
4207 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4209 /* see if we have permission to create the lock in the
4211 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4213 code = CM_ERROR_WOULDBLOCK;
4214 else if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4216 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4218 " User has no read-lock perms, but has INSERT perms.");
4219 code = CM_ERROR_WOULDBLOCK;
4222 " User has no read-lock perms. Forcing client-side lock");
4223 force_client_lock = TRUE;
4227 /* leave any other codes as-is */
4231 int check_data_version = FALSE;
4234 /* first check if we have permission to elevate or obtain
4236 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4238 if (code == CM_ERROR_NOACCESS && Which == LockRead &&
4239 (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp))) {
4240 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4241 force_client_lock = TRUE;
4246 /* has_insert => (Which == LockRead, code == CM_ERROR_NOACCESS) */
4248 if (scp->serverLock == LockRead && Which == LockWrite) {
4250 /* We want to escalate the lock to a LockWrite.
4251 * Unfortunately that's not really possible without
4252 * letting go of the current lock. But for now we do
4256 " attempting to UPGRADE from LockRead to LockWrite.");
4258 " dataVersion on scp: %I64d", scp->dataVersion);
4260 /* we assume at this point (because scp->serverLock
4261 was valid) that we had a valid server lock. */
4262 scp->lockDataVersion = scp->dataVersion;
4263 check_data_version = TRUE;
4265 code = cm_IntReleaseLock(scp, userp, reqp);
4268 /* We couldn't release the lock */
4271 scp->serverLock = -1;
4275 /* We need to obtain a server lock of type Which in order
4276 * to assert this file lock */
4277 #ifndef AGGRESSIVE_LOCKS
4280 newLock = LockWrite;
4283 code = cm_IntSetLock(scp, userp, newLock, reqp);
4285 #ifdef AGGRESSIVE_LOCKS
4286 if ((code == CM_ERROR_WOULDBLOCK ||
4287 code == CM_ERROR_NOACCESS) && newLock != Which) {
4288 /* we wanted LockRead. We tried LockWrite. Now try
4293 osi_assertx(newLock == LockRead, "lock type not read");
4295 code = cm_IntSetLock(scp, userp, newLock, reqp);
4299 if (code == CM_ERROR_NOACCESS) {
4300 if (Which == LockRead) {
4301 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4303 /* We requested a read-lock, but we have permission to
4304 * get a write-lock. Try that */
4306 tcode = cm_LockCheckPerms(scp, LockWrite, userp, reqp, NULL);
4309 newLock = LockWrite;
4311 osi_Log0(afsd_logp, " User has 'i' perms and the request was for a LockRead. Trying to get a LockWrite instead");
4313 code = cm_IntSetLock(scp, userp, newLock, reqp);
4316 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4317 force_client_lock = TRUE;
4319 } else if (Which == LockWrite &&
4320 scp->creator == userp && !SCP_SUPPORTS_WRITELOCKACL(scp)) {
4323 /* Special case: if the lock request was for a
4324 * LockWrite and the user owns the file and we weren't
4325 * allowed to obtain the serverlock, we either lost a
4326 * race (the permissions changed from under us), or we
4327 * have 'i' bits, but we aren't allowed to lock the
4330 /* check if we lost a race... */
4331 tcode = cm_LockCheckPerms(scp, Which, userp, reqp, NULL);
4334 osi_Log0(afsd_logp, " User has 'i' perms but can't obtain write locks. Using client-side locks.");
4335 force_client_lock = TRUE;
4340 if (code == 0 && check_data_version &&
4341 scp->dataVersion != scp->lockDataVersion) {
4342 /* We lost a race. Although we successfully obtained
4343 * a lock, someone modified the file in between. The
4344 * locks have all been technically lost. */
4347 " Data version mismatch while upgrading lock.");
4349 " Data versions before=%I64d, after=%I64d",
4350 scp->lockDataVersion,
4353 " Releasing stale lock for scp 0x%x", scp);
4355 code = cm_IntReleaseLock(scp, userp, reqp);
4357 scp->serverLock = -1;
4359 code = CM_ERROR_INVAL;
4360 } else if (code == 0) {
4361 scp->serverLock = newLock;
4362 scp->lockDataVersion = scp->dataVersion;
4366 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4367 scp->serverLock == -1) {
4368 /* Oops. We lost the lock. */
4369 cm_LockMarkSCacheLost(scp);
4372 } else if (code == 0) { /* server locks not enabled */
4374 " Skipping server lock for scp");
4379 if (code != 0 && !force_client_lock) {
4380 /* Special case error translations
4382 Applications don't expect certain errors from a
4383 LockFile/UnlockFile call. We need to translate some error
4384 code to codes that apps expect and handle. */
4386 /* We shouldn't actually need to handle this case since we
4387 simulate locks for RO scps anyway. */
4388 if (code == CM_ERROR_READONLY) {
4389 osi_Log0(afsd_logp, " Reinterpreting CM_ERROR_READONLY as CM_ERROR_NOACCESS");
4390 code = CM_ERROR_NOACCESS;
4394 if (code == 0 || (code == CM_ERROR_WOULDBLOCK && allowWait) ||
4395 force_client_lock) {
4397 /* clear the error if we are forcing a client lock, so we
4398 don't get confused later. */
4399 if (force_client_lock && code != CM_ERROR_WOULDBLOCK)
4402 lock_ObtainWrite(&cm_scacheLock);
4403 fileLock = cm_GetFileLock();
4404 lock_ReleaseWrite(&cm_scacheLock);
4406 fileLock->fid = scp->fid;
4408 fileLock->key = key;
4409 fileLock->lockType = Which;
4411 fileLock->userp = userp;
4412 fileLock->range = range;
4413 fileLock->flags = (code == 0 ? 0 :
4415 CM_FILELOCK_FLAG_WAITUNLOCK :
4416 CM_FILELOCK_FLAG_WAITLOCK));
4418 if (force_client_lock || !SERVERLOCKS_ENABLED(scp))
4419 fileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
4421 fileLock->lastUpdate = (code == 0 && !force_client_lock) ? time(NULL) : 0;
4423 lock_ObtainWrite(&cm_scacheLock);
4424 osi_QAddT(&scp->fileLocksH, &scp->fileLocksT, &fileLock->fileq);
4425 cm_HoldSCacheNoLock(scp);
4426 fileLock->scp = scp;
4427 osi_QAdd(&cm_allFileLocks, &fileLock->q);
4428 lock_ReleaseWrite(&cm_scacheLock);
4434 if (IS_LOCK_CLIENTONLY(fileLock)) {
4436 } else if (IS_LOCK_ACCEPTED(fileLock)) {
4437 if (Which == LockRead)
4440 scp->exclusiveLocks++;
4444 "cm_Lock Lock added 0x%p flags 0x%x to scp [0x%p]",
4445 fileLock, fileLock->flags, scp);
4447 " exclusives[%d] shared[%d] client[%d] serverLock[%d]",
4448 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4449 (int)(signed char) scp->serverLock);
4452 "cm_Lock Rejecting lock (code = 0x%x)", code);
4458 static int cm_KeyEquals(cm_key_t k1, cm_key_t k2, int flags);
4460 /* Called with scp->mx held */
4461 long cm_UnlockByKey(cm_scache_t * scp,
4468 cm_file_lock_t *fileLock;
4469 osi_queue_t *q, *qn;
4472 osi_Log4(afsd_logp, "cm_UnlockByKey scp 0x%p key 0x%x:%x flags=0x%x",
4474 (unsigned long)(key >> 32),
4475 (unsigned long)(key & 0xffffffff),
4478 lock_ObtainWrite(&cm_scacheLock);
4480 for (q = scp->fileLocksH; q; q = qn) {
4483 fileLock = (cm_file_lock_t *)
4484 ((char *) q - offsetof(cm_file_lock_t, fileq));
4487 osi_Log4(afsd_logp, " Checking lock[0x%x] range[%d,+%d] type[%d]",
4489 (unsigned long) fileLock->range.offset,
4490 (unsigned long) fileLock->range.length,
4491 fileLock->lockType);
4492 osi_Log3(afsd_logp, " key[0x%x:%x] flags[0x%x]",
4493 (unsigned long)(fileLock->key >> 32),
4494 (unsigned long)(fileLock->key & 0xffffffff),
4497 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
4498 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
4499 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4501 fileLock->fid.volume,
4502 fileLock->fid.vnode,
4503 fileLock->fid.unique);
4504 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4505 fileLock->scp->fid.cell,
4506 fileLock->scp->fid.volume,
4507 fileLock->scp->fid.vnode,
4508 fileLock->scp->fid.unique);
4509 osi_assertx(FALSE, "invalid fid value");
4513 if (!IS_LOCK_DELETED(fileLock) &&
4514 cm_KeyEquals(fileLock->key, key, flags)) {
4515 osi_Log3(afsd_logp, "...Unlock range [%d,+%d] type %d",
4516 fileLock->range.offset,
4517 fileLock->range.length,
4518 fileLock->lockType);
4520 if (scp->fileLocksT == q)
4521 scp->fileLocksT = osi_QPrev(q);
4522 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
4524 if (IS_LOCK_CLIENTONLY(fileLock)) {
4526 } else if (IS_LOCK_ACCEPTED(fileLock)) {
4527 if (fileLock->lockType == LockRead)
4530 scp->exclusiveLocks--;
4533 fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
4535 cm_ReleaseUser(fileLock->userp);
4536 cm_ReleaseSCacheNoLock(scp);
4538 fileLock->userp = NULL;
4539 fileLock->scp = NULL;
4545 lock_ReleaseWrite(&cm_scacheLock);
4547 if (n_unlocks == 0) {
4548 osi_Log0(afsd_logp, "cm_UnlockByKey no locks found");
4549 osi_Log3(afsd_logp, " Leaving scp with exclusives[%d], shared[%d], serverLock[%d]",
4550 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4555 osi_Log1(afsd_logp, "cm_UnlockByKey done with %d locks", n_unlocks);
4557 osi_assertx(scp->sharedLocks >= 0, "scp->sharedLocks < 0");
4558 osi_assertx(scp->exclusiveLocks >= 0, "scp->exclusiveLocks < 0");
4559 osi_assertx(scp->clientLocks >= 0, "scp->clientLocks < 0");
4561 if (!SERVERLOCKS_ENABLED(scp)) {
4562 osi_Log0(afsd_logp, " Skipping server lock for scp");
4566 /* Ideally we would go through the rest of the locks to determine
4567 * if one or more locks that were formerly in WAITUNLOCK can now
4568 * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
4569 * scp->sharedLocks accordingly. However, the retrying of locks
4570 * in that manner is done cm_RetryLock() manually.
4573 if (scp->serverLock == LockWrite &&
4574 scp->exclusiveLocks == 0 &&
4575 scp->sharedLocks > 0) {
4577 /* The serverLock should be downgraded to LockRead */
4578 osi_Log0(afsd_logp, " DOWNGRADE lock from LockWrite to LockRead");
4580 /* since scp->serverLock looked sane, we are going to assume
4581 that we have a valid server lock. */
4582 scp->lockDataVersion = scp->dataVersion;
4583 osi_Log1(afsd_logp, " dataVersion on scp = %I64d", scp->dataVersion);
4585 code = cm_IntReleaseLock(scp, userp, reqp);
4588 /* so we couldn't release it. Just let the lock be for now */
4592 scp->serverLock = -1;
4595 code = cm_IntSetLock(scp, userp, LockRead, reqp);
4597 if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
4598 scp->serverLock = LockRead;
4599 } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
4600 /* We lost a race condition. Although we have a valid
4601 lock on the file, the data has changed and essentially
4602 we have lost the lock we had during the transition. */
4604 osi_Log0(afsd_logp, "Data version mismatch during lock downgrade");
4605 osi_Log2(afsd_logp, " Data versions before=%I64d, after=%I64d",
4606 scp->lockDataVersion,
4609 code = cm_IntReleaseLock(scp, userp, reqp);
4611 code = CM_ERROR_INVAL;
4612 scp->serverLock = -1;
4616 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4617 (scp->serverLock == -1)) {
4619 cm_LockMarkSCacheLost(scp);
4622 /* failure here has no bearing on the return value of
4626 } else if (scp->serverLock != (-1) &&
4627 scp->exclusiveLocks == 0 &&
4628 scp->sharedLocks == 0) {
4629 /* The serverLock should be released entirely */
4631 code = cm_IntReleaseLock(scp, userp, reqp);
4634 scp->serverLock = (-1);
4639 osi_Log1(afsd_logp, "cm_UnlockByKey code 0x%x", code);
4640 osi_Log4(afsd_logp, " Leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
4641 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4642 (int)(signed char) scp->serverLock);
4647 long cm_Unlock(cm_scache_t *scp,
4648 unsigned char sLockType,
4649 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
4655 int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
4656 cm_file_lock_t *fileLock;
4658 int release_userp = FALSE;
4660 osi_Log4(afsd_logp, "cm_Unlock scp 0x%p type 0x%x offset %d length %d",
4661 scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
4662 osi_Log2(afsd_logp, "... key 0x%x:%x",
4663 (unsigned long) (key >> 32), (unsigned long) (key & 0xffffffff));
4665 lock_ObtainRead(&cm_scacheLock);
4667 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4668 fileLock = (cm_file_lock_t *)
4669 ((char *) q - offsetof(cm_file_lock_t, fileq));
4672 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
4673 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
4674 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4676 fileLock->fid.volume,
4677 fileLock->fid.vnode,
4678 fileLock->fid.unique);
4679 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4680 fileLock->scp->fid.cell,
4681 fileLock->scp->fid.volume,
4682 fileLock->scp->fid.vnode,
4683 fileLock->scp->fid.unique);
4684 osi_assertx(FALSE, "invalid fid value");
4687 if (!IS_LOCK_DELETED(fileLock) &&
4688 fileLock->key == key &&
4689 fileLock->range.offset == LOffset.QuadPart &&
4690 fileLock->range.length == LLength.QuadPart) {
4696 osi_Log0(afsd_logp, "cm_Unlock lock not found; failure");
4698 lock_ReleaseRead(&cm_scacheLock);
4700 /* The lock didn't exist anyway. *shrug* */
4704 lock_ReleaseRead(&cm_scacheLock);
4706 /* discard lock record */
4707 lock_ObtainWrite(&cm_scacheLock);
4708 if (scp->fileLocksT == q)
4709 scp->fileLocksT = osi_QPrev(q);
4710 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
4713 * Don't delete it here; let the daemon delete it, to simplify
4714 * the daemon's traversal of the list.
4717 if (IS_LOCK_CLIENTONLY(fileLock)) {
4719 } else if (IS_LOCK_ACCEPTED(fileLock)) {
4720 if (fileLock->lockType == LockRead)
4723 scp->exclusiveLocks--;
4726 fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
4727 if (userp != NULL) {
4728 cm_ReleaseUser(fileLock->userp);
4730 userp = fileLock->userp;
4731 release_userp = TRUE;
4733 fileLock->userp = NULL;
4734 cm_ReleaseSCacheNoLock(scp);
4735 fileLock->scp = NULL;
4736 lock_ReleaseWrite(&cm_scacheLock);
4738 if (!SERVERLOCKS_ENABLED(scp)) {
4739 osi_Log0(afsd_logp, " Skipping server locks for scp");
4743 /* Ideally we would go through the rest of the locks to determine
4744 * if one or more locks that were formerly in WAITUNLOCK can now
4745 * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
4746 * scp->sharedLocks accordingly. However, the retrying of locks
4747 * in that manner is done cm_RetryLock() manually.
4750 if (scp->serverLock == LockWrite &&
4751 scp->exclusiveLocks == 0 &&
4752 scp->sharedLocks > 0) {
4754 /* The serverLock should be downgraded to LockRead */
4755 osi_Log0(afsd_logp, " DOWNGRADE lock from LockWrite to LockRead");
4757 /* Since we already had a lock, we assume that there is a
4758 valid server lock. */
4759 scp->lockDataVersion = scp->dataVersion;
4760 osi_Log1(afsd_logp, " dataVersion on scp is %I64d", scp->dataVersion);
4762 /* before we downgrade, make sure that we have enough
4763 permissions to get the read lock. */
4764 code = cm_LockCheckPerms(scp, LockRead, userp, reqp, NULL);
4767 osi_Log0(afsd_logp, " SKIPPING downgrade because user doesn't have perms to get downgraded lock");
4773 code = cm_IntReleaseLock(scp, userp, reqp);
4776 /* so we couldn't release it. Just let the lock be for now */
4780 scp->serverLock = -1;
4783 code = cm_IntSetLock(scp, userp, LockRead, reqp);
4785 if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
4786 scp->serverLock = LockRead;
4787 } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
4788 /* Lost a race. We obtained a new lock, but that is
4789 meaningless since someone modified the file
4793 "Data version mismatch while downgrading lock");
4795 " Data versions before=%I64d, after=%I64d",
4796 scp->lockDataVersion,
4799 code = cm_IntReleaseLock(scp, userp, reqp);
4801 scp->serverLock = -1;
4802 code = CM_ERROR_INVAL;
4806 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4807 (scp->serverLock == -1)) {
4809 cm_LockMarkSCacheLost(scp);
4812 /* failure here has no bearing on the return value of
4816 } else if (scp->serverLock != (-1) &&
4817 scp->exclusiveLocks == 0 &&
4818 scp->sharedLocks == 0) {
4819 /* The serverLock should be released entirely */
4821 code = cm_IntReleaseLock(scp, userp, reqp);
4824 scp->serverLock = (-1);
4829 cm_ReleaseUser(userp);
4833 osi_Log1(afsd_logp, "cm_Unlock code 0x%x", code);
4834 osi_Log4(afsd_logp, " leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
4835 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4836 (int)(signed char) scp->serverLock);
4841 /* called with scp->mx held */
4842 static void cm_LockMarkSCacheLost(cm_scache_t * scp)
4844 cm_file_lock_t *fileLock;
4847 osi_Log1(afsd_logp, "cm_LockMarkSCacheLost scp 0x%x", scp);
4850 /* With the current code, we can't lose a lock on a RO scp */
4851 osi_assertx(!(scp->flags & CM_SCACHEFLAG_RO), "CM_SCACHEFLAG_RO unexpected");
4854 /* cm_scacheLock needed because we are modifying fileLock->flags */
4855 lock_ObtainWrite(&cm_scacheLock);
4857 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4859 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4861 if (IS_LOCK_ACTIVE(fileLock) &&
4862 !IS_LOCK_CLIENTONLY(fileLock)) {
4863 if (fileLock->lockType == LockRead)
4866 scp->exclusiveLocks--;
4868 fileLock->flags |= CM_FILELOCK_FLAG_LOST;
4872 scp->serverLock = -1;
4873 scp->lockDataVersion = -1;
4874 lock_ReleaseWrite(&cm_scacheLock);
4877 /* Called with no relevant locks held */
4878 void cm_CheckLocks()
4880 osi_queue_t *q, *nq;
4881 cm_file_lock_t *fileLock;
4887 struct rx_connection * callp;
4892 lock_ObtainWrite(&cm_scacheLock);
4894 cm_lockRefreshCycle++;
4896 osi_Log1(afsd_logp, "cm_CheckLocks starting lock check cycle %d", cm_lockRefreshCycle);
4898 for (q = cm_allFileLocks; q; q = nq) {
4899 fileLock = (cm_file_lock_t *) q;
4903 if (IS_LOCK_DELETED(fileLock)) {
4905 osi_QRemove(&cm_allFileLocks, q);
4906 cm_PutFileLock(fileLock);
4908 } else if (IS_LOCK_ACTIVE(fileLock) && !IS_LOCK_CLIENTONLY(fileLock)) {
4910 /* Server locks must have been enabled for us to have
4911 received an active non-client-only lock. */
4912 osi_assertx(cm_enableServerLocks, "!cm_enableServerLocks");
4914 scp = fileLock->scp;
4915 osi_assertx(scp != NULL, "null cm_scache_t");
4917 cm_HoldSCacheNoLock(scp);
4920 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
4921 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
4922 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4924 fileLock->fid.volume,
4925 fileLock->fid.vnode,
4926 fileLock->fid.unique);
4927 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4928 fileLock->scp->fid.cell,
4929 fileLock->scp->fid.volume,
4930 fileLock->scp->fid.vnode,
4931 fileLock->scp->fid.unique);
4932 osi_assertx(FALSE, "invalid fid");
4935 /* Server locks are extended once per scp per refresh
4937 if (scp->lastRefreshCycle != cm_lockRefreshCycle) {
4939 int scp_done = FALSE;
4941 osi_Log1(afsd_logp, "cm_CheckLocks Updating scp 0x%x", scp);
4943 lock_ReleaseWrite(&cm_scacheLock);
4944 lock_ObtainMutex(&scp->mx);
4946 /* did the lock change while we weren't holding the lock? */
4947 if (!IS_LOCK_ACTIVE(fileLock))
4948 goto post_syncopdone;
4950 code = cm_SyncOp(scp, NULL, fileLock->userp, &req, 0,
4951 CM_SCACHESYNC_NEEDCALLBACK
4952 | CM_SCACHESYNC_GETSTATUS
4953 | CM_SCACHESYNC_LOCK);
4957 "cm_CheckLocks SyncOp failure code 0x%x", code);
4958 goto post_syncopdone;
4961 /* cm_SyncOp releases scp->mx during which the lock
4962 may get released. */
4963 if (!IS_LOCK_ACTIVE(fileLock))
4964 goto pre_syncopdone;
4966 if (scp->serverLock != -1) {
4970 tfid.Volume = scp->fid.volume;
4971 tfid.Vnode = scp->fid.vnode;
4972 tfid.Unique = scp->fid.unique;
4974 userp = fileLock->userp;
4976 osi_Log3(afsd_logp, "CALL ExtendLock lock 0x%p for scp=0x%p with lock %d",
4979 (int) scp->serverLock);
4981 lock_ReleaseMutex(&scp->mx);
4984 code = cm_ConnFromFID(&cfid, userp,
4989 callp = cm_GetRxConn(connp);
4990 code = RXAFS_ExtendLock(callp, &tfid,
4992 rx_PutConnection(callp);
4994 osi_Log1(afsd_logp, " ExtendLock returns %d", code);
4996 } while (cm_Analyze(connp, userp, &req,
4997 &cfid, &volSync, NULL, NULL,
5000 code = cm_MapRPCError(code, &req);
5002 lock_ObtainMutex(&scp->mx);
5005 osi_Log1(afsd_logp, "CALL ExtendLock FAILURE, code 0x%x", code);
5007 osi_Log0(afsd_logp, "CALL ExtendLock SUCCESS");
5008 scp->lockDataVersion = scp->dataVersion;
5011 if ((code == EINVAL || code == CM_ERROR_INVAL) &&
5012 scp->lockDataVersion == scp->dataVersion) {
5016 (scp->exclusiveLocks > 0) ? LockWrite: LockRead;
5018 /* we might still have a chance to obtain a
5021 code = cm_IntSetLock(scp, userp, lockType, &req);
5024 code = CM_ERROR_INVAL;
5025 } else if (scp->lockDataVersion != scp->dataVersion) {
5027 /* now check if we still have the file at
5028 the right data version. */
5030 "Data version mismatch on scp 0x%p",
5033 " Data versions: before=%I64d, after=%I64d",
5034 scp->lockDataVersion,
5037 code = cm_IntReleaseLock(scp, userp, &req);
5039 code = CM_ERROR_INVAL;
5043 if (code == EINVAL || code == CM_ERROR_INVAL) {
5044 cm_LockMarkSCacheLost(scp);
5048 /* interestingly, we have found an active lock
5049 belonging to an scache that has no
5051 cm_LockMarkSCacheLost(scp);
5058 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5061 lock_ReleaseMutex(&scp->mx);
5063 lock_ObtainWrite(&cm_scacheLock);
5066 fileLock->lastUpdate = time(NULL);
5070 scp->lastRefreshCycle = cm_lockRefreshCycle;
5073 /* we have already refreshed the locks on this scp */
5074 fileLock->lastUpdate = time(NULL);
5077 cm_ReleaseSCacheNoLock(scp);
5079 } else if (IS_LOCK_ACTIVE(fileLock) && IS_LOCK_CLIENTONLY(fileLock)) {
5080 /* TODO: Check callbacks */
5084 lock_ReleaseWrite(&cm_scacheLock);
5085 osi_Log1(afsd_logp, "cm_CheckLocks completes lock check cycle %d", cm_lockRefreshCycle);
5088 /* NOT called with scp->mx held. */
5089 long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
5092 cm_scache_t *scp = NULL;
5093 cm_file_lock_t *fileLock;
5097 int force_client_lock = FALSE;
5098 int has_insert = FALSE;
5099 int check_data_version = FALSE;
5103 if (client_is_dead) {
5104 code = CM_ERROR_TIMEDOUT;
5108 lock_ObtainRead(&cm_scacheLock);
5110 osi_Log2(afsd_logp, "cm_RetryLock checking lock %p (scp=%p)", oldFileLock, oldFileLock->scp);
5111 osi_Log4(afsd_logp, " offset(%x:%x) length(%x:%x)",
5112 (unsigned)(oldFileLock->range.offset >> 32),
5113 (unsigned)(oldFileLock->range.offset & 0xffffffff),
5114 (unsigned)(oldFileLock->range.length >> 32),
5115 (unsigned)(oldFileLock->range.length & 0xffffffff));
5116 osi_Log3(afsd_logp, " key(%x:%x) flags=%x",
5117 (unsigned)(oldFileLock->key >> 32),
5118 (unsigned)(oldFileLock->key & 0xffffffff),
5119 (unsigned)(oldFileLock->flags));
5121 /* if the lock has already been granted, then we have nothing to do */
5122 if (IS_LOCK_ACTIVE(oldFileLock)) {
5123 lock_ReleaseRead(&cm_scacheLock);
5124 osi_Log0(afsd_logp, "cm_RetryLock lock already granted");
5128 /* we can't do anything with lost or deleted locks at the moment. */
5129 if (IS_LOCK_LOST(oldFileLock) || IS_LOCK_DELETED(oldFileLock)) {
5130 code = CM_ERROR_BADFD;
5131 osi_Log0(afsd_logp, "cm_RetryLock lock is lost or deleted");
5132 lock_ReleaseRead(&cm_scacheLock);
5136 scp = oldFileLock->scp;
5138 osi_assertx(scp != NULL, "null cm_scache_t");
5140 lock_ReleaseRead(&cm_scacheLock);
5141 lock_ObtainMutex(&scp->mx);
5143 code = cm_LockCheckPerms(scp, oldFileLock->lockType,
5147 if (code == CM_ERROR_NOACCESS && oldFileLock->lockType == LockRead) {
5148 if (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp)) {
5149 force_client_lock = TRUE;
5153 lock_ReleaseMutex(&scp->mx);
5157 lock_ObtainWrite(&cm_scacheLock);
5159 /* Check if we already have a sufficient server lock to allow this
5160 lock to go through. */
5161 if (IS_LOCK_WAITLOCK(oldFileLock) &&
5162 (!SERVERLOCKS_ENABLED(scp) ||
5163 scp->serverLock == oldFileLock->lockType ||
5164 scp->serverLock == LockWrite)) {
5166 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5168 if (SERVERLOCKS_ENABLED(scp)) {
5169 osi_Log1(afsd_logp, "cm_RetryLock Server lock (%d) is sufficient for lock. Granting",
5170 (int) scp->serverLock);
5172 osi_Log0(afsd_logp, "cm_RetryLock skipping server lock for scp");
5175 lock_ReleaseWrite(&cm_scacheLock);
5176 lock_ReleaseMutex(&scp->mx);
5181 if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5183 /* check if the conflicting locks have dissappeared already */
5184 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5186 fileLock = (cm_file_lock_t *)
5187 ((char *) q - offsetof(cm_file_lock_t, fileq));
5189 if (IS_LOCK_LOST(fileLock)) {
5190 if (fileLock->key == oldFileLock->key) {
5191 code = CM_ERROR_BADFD;
5192 oldFileLock->flags |= CM_FILELOCK_FLAG_LOST;
5193 osi_Log1(afsd_logp, " found lost lock %p for same key. Marking lock as lost",
5196 } else if (fileLock->lockType == LockWrite &&
5197 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5198 osi_Log1(afsd_logp, " found conflicting LOST lock %p", fileLock);
5199 code = CM_ERROR_WOULDBLOCK;
5204 if (IS_LOCK_ACCEPTED(fileLock) &&
5205 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5207 if (oldFileLock->lockType != LockRead ||
5208 fileLock->lockType != LockRead) {
5210 osi_Log1(afsd_logp, " found conflicting lock %p", fileLock);
5211 code = CM_ERROR_WOULDBLOCK;
5219 lock_ReleaseWrite(&cm_scacheLock);
5220 lock_ReleaseMutex(&scp->mx);
5225 /* when we get here, the lock is either a WAITUNLOCK or WAITLOCK.
5226 If it is WAITUNLOCK, then we didn't find any conflicting lock
5227 but we haven't verfied whether the serverLock is sufficient to
5228 assert it. If it is WAITLOCK, then the serverLock is
5229 insufficient to assert it. Eitherway, we are ready to accept
5230 the lock as either ACTIVE or WAITLOCK depending on the
5233 /* First, promote the WAITUNLOCK to a WAITLOCK */
5234 if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5235 if (oldFileLock->lockType == LockRead)
5238 scp->exclusiveLocks++;
5240 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITUNLOCK;
5241 oldFileLock->flags |= CM_FILELOCK_FLAG_WAITLOCK;
5244 osi_assertx(IS_LOCK_WAITLOCK(oldFileLock), "!IS_LOCK_WAITLOCK");
5246 if (force_client_lock ||
5247 !SERVERLOCKS_ENABLED(scp) ||
5248 scp->serverLock == oldFileLock->lockType ||
5249 (oldFileLock->lockType == LockRead &&
5250 scp->serverLock == LockWrite)) {
5252 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5254 if ((force_client_lock ||
5255 !SERVERLOCKS_ENABLED(scp)) &&
5256 !IS_LOCK_CLIENTONLY(oldFileLock)) {
5258 oldFileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
5260 if (oldFileLock->lockType == LockRead)
5263 scp->exclusiveLocks--;
5268 lock_ReleaseWrite(&cm_scacheLock);
5269 lock_ReleaseMutex(&scp->mx);
5276 code = cm_SyncOp(scp, NULL, oldFileLock->userp, &req, 0,
5277 CM_SCACHESYNC_NEEDCALLBACK
5278 | CM_SCACHESYNC_GETSTATUS
5279 | CM_SCACHESYNC_LOCK);
5281 osi_Log1(smb_logp, "cm_RetryLock SyncOp failure code 0x%x", code);
5282 lock_ReleaseWrite(&cm_scacheLock);
5283 goto post_syncopdone;
5286 if (!IS_LOCK_WAITLOCK(oldFileLock))
5287 goto pre_syncopdone;
5289 userp = oldFileLock->userp;
5291 #ifndef AGGRESSIVE_LOCKS
5292 newLock = oldFileLock->lockType;
5294 newLock = LockWrite;
5298 /* if has_insert is non-zero, then:
5299 - the lock a LockRead
5300 - we don't have permission to get a LockRead
5301 - we do have permission to get a LockWrite
5302 - the server supports VICED_CAPABILITY_WRITELOCKACL
5305 newLock = LockWrite;
5308 lock_ReleaseWrite(&cm_scacheLock);
5310 /* when we get here, either we have a read-lock and want a
5311 write-lock or we don't have any locks and we want some
5314 if (scp->serverLock == LockRead) {
5316 osi_assertx(newLock == LockWrite, "!LockWrite");
5318 osi_Log0(afsd_logp, " Attempting to UPGRADE from LockRead to LockWrite");
5320 scp->lockDataVersion = scp->dataVersion;
5321 check_data_version = TRUE;
5323 code = cm_IntReleaseLock(scp, userp, &req);
5326 goto pre_syncopdone;
5328 scp->serverLock = -1;
5331 code = cm_IntSetLock(scp, userp, newLock, &req);
5334 if (scp->dataVersion != scp->lockDataVersion) {
5335 /* we lost a race. too bad */
5338 " Data version mismatch while upgrading lock.");
5340 " Data versions before=%I64d, after=%I64d",
5341 scp->lockDataVersion,
5344 " Releasing stale lock for scp 0x%x", scp);
5346 code = cm_IntReleaseLock(scp, userp, &req);
5348 scp->serverLock = -1;
5350 code = CM_ERROR_INVAL;
5352 cm_LockMarkSCacheLost(scp);
5354 scp->serverLock = newLock;
5359 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5365 if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
5366 lock_ObtainWrite(&cm_scacheLock);
5367 if (scp->fileLocksT == &oldFileLock->fileq)
5368 scp->fileLocksT = osi_QPrev(&oldFileLock->fileq);
5369 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, &oldFileLock->fileq);
5370 lock_ReleaseWrite(&cm_scacheLock);
5372 lock_ReleaseMutex(&scp->mx);
5375 lock_ObtainWrite(&cm_scacheLock);
5377 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5378 } else if (code != CM_ERROR_WOULDBLOCK) {
5379 oldFileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5380 cm_ReleaseUser(oldFileLock->userp);
5381 oldFileLock->userp = NULL;
5382 if (oldFileLock->scp) {
5383 cm_ReleaseSCacheNoLock(oldFileLock->scp);
5384 oldFileLock->scp = NULL;
5387 lock_ReleaseWrite(&cm_scacheLock);
5392 cm_key_t cm_GenerateKey(unsigned int session_id, unsigned long process_id, unsigned int file_id)
5395 osi_assertx((process_id & 0xffffffff) == process_id, "unexpected process_id");
5396 osi_assertx((session_id & 0xffff) == session_id, "unexpected session_id");
5397 osi_assertx((file_id & 0xffff) == file_id, "unexpected file_id");
5401 (((cm_key_t) (process_id & 0xffffffff)) << 32) |
5402 (((cm_key_t) (session_id & 0xffff)) << 16) |
5403 (((cm_key_t) (file_id & 0xffff)));
5406 static int cm_KeyEquals(cm_key_t k1, cm_key_t k2, int flags)
5408 if (flags & CM_UNLOCK_BY_FID) {
5409 return ((k1 & 0xffffffff) == (k2 & 0xffffffff));
5415 void cm_ReleaseAllLocks(void)
5421 cm_file_lock_t *fileLock;
5424 for (i = 0; i < cm_data.scacheHashTableSize; i++)
5426 for ( scp = cm_data.scacheHashTablep[i]; scp; scp = scp->nextp ) {
5427 while (scp->fileLocksH != NULL) {
5428 lock_ObtainMutex(&scp->mx);
5429 lock_ObtainWrite(&cm_scacheLock);
5430 if (!scp->fileLocksH) {
5431 lock_ReleaseWrite(&cm_scacheLock);
5432 lock_ReleaseMutex(&scp->mx);
5435 fileLock = (cm_file_lock_t *)((char *) scp->fileLocksH - offsetof(cm_file_lock_t, fileq));
5436 userp = fileLock->userp;
5438 key = fileLock->key;
5439 cm_HoldSCacheNoLock(scp);
5440 lock_ReleaseWrite(&cm_scacheLock);
5441 cm_UnlockByKey(scp, key, 0, userp, &req);
5442 cm_ReleaseSCache(scp);
5443 cm_ReleaseUser(userp);
5444 lock_ReleaseMutex(&scp->mx);