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>
29 extern void afsi_log(char *pattern, ...);
32 int cm_enableServerLocks = 1;
34 int cm_followBackupPath = 0;
37 * Case-folding array. This was constructed by inspecting of SMBtrace output.
38 * I do not know anything more about it.
40 unsigned char cm_foldUpper[256] = {
41 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
42 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
43 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
44 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
45 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
46 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
47 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
48 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
49 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
50 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
51 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
52 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
53 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
54 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
55 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
56 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
57 0x80, 0x9a, 0x90, 0x41, 0x8e, 0x41, 0x8f, 0x80,
58 0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x8e, 0x8f,
59 0x90, 0x92, 0x92, 0x4f, 0x99, 0x4f, 0x55, 0x55,
60 0x59, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
61 0x41, 0x49, 0x4f, 0x55, 0xa5, 0xa5, 0x56, 0xa7,
62 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
63 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
64 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
65 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
66 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
67 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
68 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
69 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
70 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
71 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
72 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
76 * Case-insensitive string comparison. We used to use stricmp, but it doesn't
77 * know about 8-bit characters (e.g. 129 is lowercase u-umlaut, 154 is
78 * upper-case u-umlaut).
80 int cm_stricmp(const char *str1, const char *str2)
92 c1 = (char) cm_foldUpper[(unsigned char)(*str1++)];
93 c2 = (char) cm_foldUpper[(unsigned char)(*str2++)];
101 /* characters that are legal in an 8.3 name */
103 * We used to have 1's for all characters from 128 to 254. But
104 * the NT client behaves better if we create an 8.3 name for any
105 * name that has a character with the high bit on, and if we
106 * delete those characters from 8.3 names. In particular, see
107 * Sybase defect 10859.
109 char cm_LegalChars[256] = {
110 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
111 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
112 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0,
113 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
114 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
115 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1,
116 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
117 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1,
118 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
119 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
120 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
121 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
122 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
123 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
124 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
125 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
128 /* return true iff component is a valid 8.3 name */
129 int cm_Is8Dot3(char *namep)
136 * can't have a leading dot;
137 * special case for . and ..
139 if (namep[0] == '.') {
142 if (namep[1] == '.' && namep[2] == 0)
146 while (tc = *namep++) {
148 /* saw another dot */
149 if (sawDot) return 0; /* second dot */
154 if (cm_LegalChars[tc] == 0)
157 if (!sawDot && charCount > 8)
158 /* more than 8 chars in name */
160 if (sawDot && charCount > 3)
161 /* more than 3 chars in extension */
168 * Number unparsing map for generating 8.3 names;
169 * The version taken from DFS was on drugs.
170 * You can't include '&' and '@' in a file name.
172 char cm_8Dot3Mapping[42] =
173 {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
174 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K',
175 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U',
176 'V', 'W', 'X', 'Y', 'Z', '_', '-', '$', '#', '!', '+', '='
178 int cm_8Dot3MapSize = sizeof(cm_8Dot3Mapping);
180 void cm_Gen8Dot3NameInt(const char * longname, cm_dirFid_t * pfid,
181 char *shortName, char **shortNameEndp)
185 int vnode = ntohl(pfid->vnode);
187 int validExtension = 0;
191 /* Unparse the file's vnode number to get a "uniquifier" */
193 number[nsize] = cm_8Dot3Mapping[vnode % cm_8Dot3MapSize];
195 vnode /= cm_8Dot3MapSize;
199 * Look for valid extension. There has to be a dot, and
200 * at least one of the characters following has to be legal.
202 lastDot = strrchr(longname, '.');
204 temp = lastDot; temp++;
206 if (cm_LegalChars[tc])
212 /* Copy name characters */
213 for (i = 0, name = longname;
214 i < (7 - nsize) && name != lastDot; ) {
219 if (!cm_LegalChars[tc])
222 *shortName++ = toupper(tc);
228 /* Copy uniquifier characters */
229 memcpy(shortName, number, nsize);
232 if (validExtension) {
233 /* Copy extension characters */
234 *shortName++ = *lastDot++; /* copy dot */
235 for (i = 0, tc = *lastDot++;
238 if (cm_LegalChars[tc]) {
240 *shortName++ = toupper(tc);
249 *shortNameEndp = shortName;
252 /* return success if we can open this file in this mode */
253 long cm_CheckOpen(cm_scache_t *scp, int openMode, int trunc, cm_user_t *userp,
261 rights |= PRSFS_READ;
262 if (openMode == 1 || openMode == 2 || trunc)
263 rights |= PRSFS_WRITE;
265 lock_ObtainMutex(&scp->mx);
267 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
268 CM_SCACHESYNC_GETSTATUS
269 | CM_SCACHESYNC_NEEDCALLBACK
270 | CM_SCACHESYNC_LOCK);
273 ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
274 scp->fileType == CM_SCACHETYPE_FILE) {
277 unsigned int sLockType;
278 LARGE_INTEGER LOffset, LLength;
280 /* Check if there's some sort of lock on the file at the
283 key = cm_GenerateKey(CM_SESSION_CMINT,0,0);
285 if (rights & PRSFS_WRITE)
288 sLockType = LOCKING_ANDX_SHARED_LOCK;
290 LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
291 LOffset.LowPart = CM_FLSHARE_OFFSET_LOW;
292 LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
293 LLength.LowPart = CM_FLSHARE_LENGTH_LOW;
295 code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
298 cm_Unlock(scp, sLockType, LOffset, LLength, key, userp, reqp);
300 /* In this case, we allow the file open to go through even
301 though we can't enforce mandatory locking on the
303 if (code == CM_ERROR_NOACCESS &&
304 !(rights & PRSFS_WRITE))
308 case CM_ERROR_ALLOFFLINE:
309 case CM_ERROR_ALLDOWN:
310 case CM_ERROR_ALLBUSY:
311 case CM_ERROR_TIMEDOUT:
313 case CM_ERROR_WOULDBLOCK:
316 code = CM_ERROR_SHARING_VIOLATION;
321 } else if (code != 0) {
325 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
329 lock_ReleaseMutex(&scp->mx);
334 /* return success if we can open this file in this mode */
335 long cm_CheckNTOpen(cm_scache_t *scp, unsigned int desiredAccess,
336 unsigned int createDisp, cm_user_t *userp, cm_req_t *reqp,
337 cm_lock_data_t **ldpp)
342 osi_assertx(ldpp != NULL, "null cm_lock_data_t");
345 /* Always allow delete; the RPC will tell us if it's OK */
346 if (desiredAccess == DELETE)
351 if (desiredAccess & AFS_ACCESS_READ)
352 rights |= (scp->fileType == CM_SCACHETYPE_DIRECTORY ? PRSFS_LOOKUP : PRSFS_READ);
354 /* We used to require PRSFS_WRITE if createDisp was 4
355 (OPEN_ALWAYS) even if AFS_ACCESS_WRITE was not requested.
356 However, we don't need to do that since the existence of the
357 scp implies that we don't need to create it. */
358 if (desiredAccess & AFS_ACCESS_WRITE)
359 rights |= PRSFS_WRITE;
361 lock_ObtainMutex(&scp->mx);
363 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
364 CM_SCACHESYNC_GETSTATUS
365 | CM_SCACHESYNC_NEEDCALLBACK
366 | CM_SCACHESYNC_LOCK);
369 * If the open will fail because the volume is readonly, then we will
370 * return an access denied error instead. This is to help brain-dead
371 * apps run correctly on replicated volumes.
372 * See defect 10007 for more information.
374 if (code == CM_ERROR_READONLY)
375 code = CM_ERROR_NOACCESS;
378 ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
379 scp->fileType == CM_SCACHETYPE_FILE) {
381 unsigned int sLockType;
382 LARGE_INTEGER LOffset, LLength;
384 /* Check if there's some sort of lock on the file at the
387 key = cm_GenerateKey(CM_SESSION_CMINT,0,0);
388 if (rights & PRSFS_WRITE)
391 sLockType = LOCKING_ANDX_SHARED_LOCK;
393 /* single byte lock at offset 0x0100 0000 0000 0000 */
394 LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
395 LOffset.LowPart = CM_FLSHARE_OFFSET_LOW;
396 LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
397 LLength.LowPart = CM_FLSHARE_LENGTH_LOW;
399 code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
402 (*ldpp) = (cm_lock_data_t *)malloc(sizeof(cm_lock_data_t));
409 (*ldpp)->sLockType = sLockType;
410 (*ldpp)->LOffset.HighPart = LOffset.HighPart;
411 (*ldpp)->LOffset.LowPart = LOffset.LowPart;
412 (*ldpp)->LLength.HighPart = LLength.HighPart;
413 (*ldpp)->LLength.LowPart = LLength.LowPart;
415 /* In this case, we allow the file open to go through even
416 though we can't enforce mandatory locking on the
418 if (code == CM_ERROR_NOACCESS &&
419 !(rights & PRSFS_WRITE))
423 case CM_ERROR_ALLOFFLINE:
424 case CM_ERROR_ALLDOWN:
425 case CM_ERROR_ALLBUSY:
426 case CM_ERROR_TIMEDOUT:
428 case CM_ERROR_WOULDBLOCK:
431 code = CM_ERROR_SHARING_VIOLATION;
435 } else if (code != 0) {
439 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
442 lock_ReleaseMutex(&scp->mx);
447 extern long cm_CheckNTOpenDone(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp,
448 cm_lock_data_t ** ldpp)
451 lock_ObtainMutex(&scp->mx);
452 cm_Unlock(scp, (*ldpp)->sLockType, (*ldpp)->LOffset, (*ldpp)->LLength,
453 (*ldpp)->key, userp, reqp);
454 lock_ReleaseMutex(&scp->mx);
461 * When CAP_NT_SMBS has been negotiated, deletion (of files or directories) is
462 * done in three steps:
463 * (1) open for deletion (NT_CREATE_AND_X)
464 * (2) set for deletion on close (NT_TRANSACTION2, SET_FILE_INFO)
466 * We must not do the RPC until step 3. But if we are going to return an error
467 * code (e.g. directory not empty), we must return it by step 2, otherwise most
468 * clients will not notice it. So we do a preliminary check. For deleting
469 * files, this is almost free, since we have already done the RPC to get the
470 * parent directory's status bits. But for deleting directories, we must do an
471 * additional RPC to get the directory's data to check if it is empty. Sigh.
473 long cm_CheckNTDelete(cm_scache_t *dscp, cm_scache_t *scp, cm_user_t *userp,
480 unsigned short *hashTable;
482 int BeyondPage = 0, HaveDot = 0, HaveDotDot = 0;
484 /* First check permissions */
485 lock_ObtainMutex(&scp->mx);
486 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_DELETE,
487 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
488 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
489 lock_ReleaseMutex(&scp->mx);
493 /* If deleting directory, must be empty */
495 if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
498 thyper.HighPart = 0; thyper.LowPart = 0;
499 lock_ObtainRead(&scp->bufCreateLock);
500 code = buf_Get(scp, &thyper, &bufferp);
501 lock_ReleaseRead(&scp->bufCreateLock);
505 lock_ObtainMutex(&bufferp->mx);
506 lock_ObtainMutex(&scp->mx);
508 code = cm_SyncOp(scp, bufferp, userp, reqp, 0,
509 CM_SCACHESYNC_NEEDCALLBACK
511 | CM_SCACHESYNC_BUFLOCKED);
515 if (cm_HaveBuffer(scp, bufferp, 1))
518 /* otherwise, load the buffer and try again */
519 lock_ReleaseMutex(&bufferp->mx);
520 code = cm_GetBuffer(scp, bufferp, NULL, userp, reqp);
521 lock_ReleaseMutex(&scp->mx);
522 lock_ObtainMutex(&bufferp->mx);
523 lock_ObtainMutex(&scp->mx);
524 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
529 /* We try to determine emptiness without looking beyond the first page,
530 * and without assuming "." and ".." are present and are on the first
531 * page (though these assumptions might, after all, be reasonable).
533 hashTable = (unsigned short *)(bufferp->datap + (32 * 5));
534 for (i=0; i<128; i++) {
535 idx = ntohs(hashTable[i]);
541 dep = (cm_dirEntry_t *)(bufferp->datap + (32 * idx));
542 if (strcmp(dep->name, ".") == 0)
544 else if (strcmp(dep->name, "..") == 0)
547 code = CM_ERROR_NOTEMPTY;
550 idx = ntohs(dep->next);
553 if (BeyondPage && HaveDot && HaveDotDot)
554 code = CM_ERROR_NOTEMPTY;
558 lock_ReleaseMutex(&bufferp->mx);
559 buf_Release(bufferp);
560 lock_ReleaseMutex(&scp->mx);
565 * Iterate through all entries in a directory.
566 * When the function funcp is called, the buffer is locked but the
567 * directory vnode is not.
569 * If the retscp parameter is not NULL, the parmp must be a
570 * cm_lookupSearch_t object.
572 long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
573 osi_hyper_t *startOffsetp, cm_user_t *userp, cm_req_t *reqp,
574 cm_scache_t **retscp)
581 osi_hyper_t dirLength;
582 osi_hyper_t bufferOffset;
583 osi_hyper_t curOffset;
587 cm_pageHeader_t *pageHeaderp;
589 long nextEntryCookie;
590 int numDirChunks; /* # of 32 byte dir chunks in this entry */
592 /* get the directory size */
593 lock_ObtainMutex(&scp->mx);
594 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_LOOKUP,
595 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
596 lock_ReleaseMutex(&scp->mx);
600 if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
601 return CM_ERROR_NOTDIR;
603 if (retscp) /* if this is a lookup call */
605 cm_lookupSearch_t* sp = parmp;
608 #ifdef AFS_FREELANCE_CLIENT
609 /* Freelance entries never end up in the DNLC because they
610 * do not have an associated cm_server_t
612 !(cm_freelanceEnabled &&
613 sp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
614 sp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
615 #else /* !AFS_FREELANCE_CLIENT */
620 int casefold = sp->caseFold;
621 sp->caseFold = 0; /* we have a strong preference for exact matches */
622 if ( *retscp = cm_dnlcLookup(scp, sp)) /* dnlc hit */
624 sp->caseFold = casefold;
627 sp->caseFold = casefold;
629 /* see if we can find it using the directory hash tables.
630 we can only do exact matches, since the hash is case
640 code = cm_BeginDirOp(scp, userp, reqp, CM_DIRLOCK_READ, &dirop);
644 code = cm_BPlusDirLookup(&dirop, sp->searchNamep, &sp->fid);
649 code = cm_DirLookup(&dirop, sp->searchNamep, &sp->fid);
657 sp->ExactFound = TRUE;
658 *retscp = NULL; /* force caller to call cm_GetSCache() */
663 if (sp->caseFold && code == CM_ERROR_INEXACT_MATCH) {
666 sp->ExactFound = FALSE;
667 *retscp = NULL; /* force caller to call cm_GetSCache() */
671 return CM_ERROR_BPLUS_NOMATCH;
679 * XXX We only get the length once. It might change when we drop the
682 dirLength = scp->length;
685 bufferOffset.LowPart = bufferOffset.HighPart = 0;
687 curOffset = *startOffsetp;
689 curOffset.HighPart = 0;
690 curOffset.LowPart = 0;
694 /* make sure that curOffset.LowPart doesn't point to the first
695 * 32 bytes in the 2nd through last dir page, and that it
696 * doesn't point at the first 13 32-byte chunks in the first
697 * dir page, since those are dir and page headers, and don't
698 * contain useful information.
700 temp = curOffset.LowPart & (2048-1);
701 if (curOffset.HighPart == 0 && curOffset.LowPart < 2048) {
702 /* we're in the first page */
703 if (temp < 13*32) temp = 13*32;
706 /* we're in a later dir page */
707 if (temp < 32) temp = 32;
710 /* make sure the low order 5 bits are zero */
713 /* now put temp bits back ito curOffset.LowPart */
714 curOffset.LowPart &= ~(2048-1);
715 curOffset.LowPart |= temp;
717 /* check if we've passed the dir's EOF */
718 if (LargeIntegerGreaterThanOrEqualTo(curOffset, dirLength))
721 /* see if we can use the bufferp we have now; compute in which
722 * page the current offset would be, and check whether that's
723 * the offset of the buffer we have. If not, get the buffer.
725 thyper.HighPart = curOffset.HighPart;
726 thyper.LowPart = curOffset.LowPart & ~(cm_data.buf_blockSize-1);
727 if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
730 lock_ReleaseMutex(&bufferp->mx);
731 buf_Release(bufferp);
735 lock_ObtainRead(&scp->bufCreateLock);
736 code = buf_Get(scp, &thyper, &bufferp);
737 lock_ReleaseRead(&scp->bufCreateLock);
739 /* if buf_Get() fails we do not have a buffer object to lock */
745 /* for the IFS version, we bulkstat the dirents because this
746 routine is used in place of smb_ReceiveCoreSearchDir. our
747 other option is to modify smb_ReceiveCoreSearchDir itself,
748 but this seems to be the proper use for cm_ApplyDir. */
749 lock_ObtainMutex(&scp->mx);
750 if ((scp->flags & CM_SCACHEFLAG_BULKSTATTING) == 0
751 && (scp->bulkStatProgress.QuadPart <= thyper.QuadPart))
753 scp->flags |= CM_SCACHEFLAG_BULKSTATTING;
754 code = cm_TryBulkStat(scp, &thyper, userp, reqp);
755 scp->flags &= ~CM_SCACHEFLAG_BULKSTATTING;
756 scp->bulkStatProgress = thyper;
758 lock_ReleaseMutex(&scp->mx);
761 lock_ObtainMutex(&bufferp->mx);
762 bufferOffset = thyper;
764 /* now get the data in the cache */
766 lock_ObtainMutex(&scp->mx);
767 code = cm_SyncOp(scp, bufferp, userp, reqp,
769 CM_SCACHESYNC_NEEDCALLBACK
771 | CM_SCACHESYNC_BUFLOCKED);
773 lock_ReleaseMutex(&scp->mx);
776 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
778 if (cm_HaveBuffer(scp, bufferp, 1)) {
779 lock_ReleaseMutex(&scp->mx);
783 /* otherwise, load the buffer and try again */
784 lock_ReleaseMutex(&bufferp->mx);
785 code = cm_GetBuffer(scp, bufferp, NULL, userp,
787 lock_ReleaseMutex(&scp->mx);
788 lock_ObtainMutex(&bufferp->mx);
793 lock_ReleaseMutex(&bufferp->mx);
794 buf_Release(bufferp);
798 } /* if (wrong buffer) ... */
800 /* now we have the buffer containing the entry we're interested
801 * in; copy it out if it represents a non-deleted entry.
803 entryInDir = curOffset.LowPart & (2048-1);
804 entryInBuffer = curOffset.LowPart & (cm_data.buf_blockSize - 1);
806 /* page header will help tell us which entries are free. Page
807 * header can change more often than once per buffer, since
808 * AFS 3 dir page size may be less than (but not more than) a
809 * buffer package buffer.
811 /* only look intra-buffer */
812 temp = curOffset.LowPart & (cm_data.buf_blockSize - 1);
813 temp &= ~(2048 - 1); /* turn off intra-page bits */
814 pageHeaderp = (cm_pageHeader_t *) (bufferp->datap + temp);
816 /* now determine which entry we're looking at in the page. If
817 * it is free (there's a free bitmap at the start of the dir),
818 * we should skip these 32 bytes.
820 slotInPage = (entryInDir & 0x7e0) >> 5;
821 if (!(pageHeaderp->freeBitmap[slotInPage>>3]
822 & (1 << (slotInPage & 0x7)))) {
823 /* this entry is free */
824 numDirChunks = 1; /* only skip this guy */
828 tp = bufferp->datap + entryInBuffer;
829 dep = (cm_dirEntry_t *) tp; /* now points to AFS3 dir entry */
831 /* while we're here, compute the next entry's location, too,
832 * since we'll need it when writing out the cookie into the
833 * dir listing stream.
835 numDirChunks = cm_NameEntries(dep->name, NULL);
837 /* compute the offset of the cookie representing the next entry */
838 nextEntryCookie = curOffset.LowPart
839 + (CM_DIR_CHUNKSIZE * numDirChunks);
841 if (dep->fid.vnode != 0) {
842 /* this is one of the entries to use: it is not deleted */
843 code = (*funcp)(scp, dep, parmp, &curOffset);
846 } /* if we're including this name */
849 /* and adjust curOffset to be where the new cookie is */
851 thyper.LowPart = CM_DIR_CHUNKSIZE * numDirChunks;
852 curOffset = LargeIntegerAdd(thyper, curOffset);
853 } /* while copying data for dir listing */
855 /* release the mutex */
857 lock_ReleaseMutex(&bufferp->mx);
858 buf_Release(bufferp);
863 int cm_NoneUpper(char *s)
867 if (c >= 'A' && c <= 'Z')
872 int cm_NoneLower(char *s)
876 if (c >= 'a' && c <= 'z')
881 long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
884 cm_lookupSearch_t *sp;
889 sp = (cm_lookupSearch_t *) rockp;
891 matchName = dep->name;
893 match = cm_stricmp(matchName, sp->searchNamep);
895 match = strcmp(matchName, sp->searchNamep);
899 && !cm_Is8Dot3(dep->name)) {
900 matchName = shortName;
901 cm_Gen8Dot3Name(dep, shortName, NULL);
903 match = cm_stricmp(matchName, sp->searchNamep);
905 match = strcmp(matchName, sp->searchNamep);
915 if (!sp->caseFold || matchName == shortName) {
916 sp->fid.vnode = ntohl(dep->fid.vnode);
917 sp->fid.unique = ntohl(dep->fid.unique);
918 return CM_ERROR_STOPNOW;
922 * If we get here, we are doing a case-insensitive search, and we
923 * have found a match. Now we determine what kind of match it is:
924 * exact, lower-case, upper-case, or none of the above. This is done
925 * in order to choose among matches, if there are more than one.
928 /* Exact matches are the best. */
929 match = strcmp(matchName, sp->searchNamep);
932 sp->fid.vnode = ntohl(dep->fid.vnode);
933 sp->fid.unique = ntohl(dep->fid.unique);
934 return CM_ERROR_STOPNOW;
937 /* Lower-case matches are next. */
940 if (cm_NoneUpper(matchName)) {
945 /* Upper-case matches are next. */
948 if (cm_NoneLower(matchName)) {
953 /* General matches are last. */
959 sp->fid.vnode = ntohl(dep->fid.vnode);
960 sp->fid.unique = ntohl(dep->fid.unique);
964 /* read the contents of a mount point into the appropriate string.
965 * called with locked scp, and returns with locked scp.
967 long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
974 if (scp->mountPointStringp[0])
977 /* otherwise, we have to read it in */
978 lock_ReleaseMutex(&scp->mx);
980 lock_ObtainRead(&scp->bufCreateLock);
981 thyper.LowPart = thyper.HighPart = 0;
982 code = buf_Get(scp, &thyper, &bufp);
983 lock_ReleaseRead(&scp->bufCreateLock);
985 lock_ObtainMutex(&scp->mx);
990 code = cm_SyncOp(scp, bufp, userp, reqp, 0,
991 CM_SCACHESYNC_READ | CM_SCACHESYNC_NEEDCALLBACK);
995 cm_SyncOpDone(scp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
997 if (cm_HaveBuffer(scp, bufp, 0))
1000 /* otherwise load buffer */
1001 code = cm_GetBuffer(scp, bufp, NULL, userp, reqp);
1005 /* locked, has callback, has valid data in buffer */
1006 if ((tlen = scp->length.LowPart) > MOUNTPOINTLEN - 1)
1007 return CM_ERROR_TOOBIG;
1009 code = CM_ERROR_INVAL;
1013 /* someone else did the work while we were out */
1014 if (scp->mountPointStringp[0]) {
1019 /* otherwise, copy out the link */
1020 memcpy(scp->mountPointStringp, bufp->datap, tlen);
1022 /* now make it null-terminated. Note that the original contents of a
1023 * link that is a mount point is "#volname." where "." is there just to
1024 * be turned into a null. That is, we can trash the last char of the
1025 * link without damaging the vol name. This is a stupid convention,
1026 * but that's the protocol.
1028 scp->mountPointStringp[tlen-1] = 0;
1038 /* called with a locked scp and chases the mount point, yielding outScpp.
1039 * scp remains locked, just for simplicity of describing the interface.
1041 long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
1042 cm_req_t *reqp, cm_scache_t **outScpp)
1050 cm_volume_t *volp = NULL;
1057 if (scp->mountRootFid.cell != 0 && scp->mountRootGen >= cm_data.mountRootGen) {
1058 tfid = scp->mountRootFid;
1059 lock_ReleaseMutex(&scp->mx);
1060 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
1061 lock_ObtainMutex(&scp->mx);
1065 /* parse the volume name */
1066 mpNamep = scp->mountPointStringp;
1068 return CM_ERROR_NOSUCHPATH;
1069 tlen = (int)strlen(scp->mountPointStringp);
1070 mtType = *scp->mountPointStringp;
1071 cellNamep = malloc(tlen);
1072 volNamep = malloc(tlen);
1074 cp = strrchr(mpNamep, ':');
1076 /* cellular mount point */
1077 memset(cellNamep, 0, tlen);
1078 strncpy(cellNamep, mpNamep+1, cp - mpNamep - 1);
1079 strcpy(volNamep, cp+1);
1080 /* now look up the cell */
1081 lock_ReleaseMutex(&scp->mx);
1082 cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE);
1083 lock_ObtainMutex(&scp->mx);
1087 strcpy(volNamep, mpNamep+1);
1089 cellp = cm_FindCellByID(scp->fid.cell);
1093 code = CM_ERROR_NOSUCHCELL;
1097 vnLength = strlen(volNamep);
1098 if (vnLength >= 8 && strcmp(volNamep + vnLength - 7, ".backup") == 0)
1099 targetType = BACKVOL;
1100 else if (vnLength >= 10
1101 && strcmp(volNamep + vnLength - 9, ".readonly") == 0)
1106 /* check for backups within backups */
1107 if (targetType == BACKVOL
1108 && (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
1109 == CM_SCACHEFLAG_RO) {
1110 code = CM_ERROR_NOSUCHVOLUME;
1114 /* now we need to get the volume */
1115 lock_ReleaseMutex(&scp->mx);
1116 if (cm_VolNameIsID(volNamep)) {
1117 code = cm_GetVolumeByID(cellp, atoi(volNamep), userp, reqp,
1118 CM_GETVOL_FLAG_CREATE, &volp);
1120 code = cm_GetVolumeByName(cellp, volNamep, userp, reqp,
1121 CM_GETVOL_FLAG_CREATE, &volp);
1123 lock_ObtainMutex(&scp->mx);
1126 /* save the parent of the volume root for this is the
1127 * place where the volume is mounted and we must remember
1128 * this in the volume structure rather than just in the
1129 * scache entry lest the scache entry gets recycled
1132 lock_ObtainMutex(&volp->mx);
1133 volp->dotdotFid = dscp->fid;
1134 lock_ReleaseMutex(&volp->mx);
1136 scp->mountRootFid.cell = cellp->cellID;
1138 /* if the mt pt originates in a .backup volume (not a .readonly)
1139 * and FollowBackupPath is active, and if there is a .backup
1140 * volume for the target, then use the .backup of the target
1141 * instead of the read-write.
1143 if (cm_followBackupPath && targetType == RWVOL &&
1144 (scp->flags & CM_SCACHEFLAG_RO|CM_SCACHEFLAG_PURERO) == CM_SCACHEFLAG_RO &&
1146 targetType = BACKVOL;
1148 /* if the mt pt is in a read-only volume (not just a
1149 * backup), and if there is a read-only volume for the
1150 * target, and if this is a targetType '#' mount point, use
1151 * the read-only, otherwise use the one specified.
1153 else if (mtType == '#' && targetType == RWVOL &&
1154 (scp->flags & CM_SCACHEFLAG_PURERO) &&
1158 if (targetType == ROVOL)
1159 scp->mountRootFid.volume = volp->ro.ID;
1160 else if (targetType == BACKVOL)
1161 scp->mountRootFid.volume = volp->bk.ID;
1163 scp->mountRootFid.volume = volp->rw.ID;
1165 /* the rest of the fid is a magic number */
1166 scp->mountRootFid.vnode = 1;
1167 scp->mountRootFid.unique = 1;
1168 scp->mountRootGen = cm_data.mountRootGen;
1170 tfid = scp->mountRootFid;
1171 lock_ReleaseMutex(&scp->mx);
1172 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
1173 lock_ObtainMutex(&scp->mx);
1184 long cm_LookupInternal(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
1185 cm_req_t *reqp, cm_scache_t **outpScpp)
1188 int dnlcHit = 1; /* did we hit in the dnlc? yes, we did */
1189 cm_scache_t *tscp = NULL;
1190 cm_scache_t *mountedScp;
1191 cm_lookupSearch_t rock;
1194 memset(&rock, 0, sizeof(rock));
1196 if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
1197 && strcmp(namep, "..") == 0) {
1198 if (dscp->dotdotFid.volume == 0)
1199 return CM_ERROR_NOSUCHVOLUME;
1200 rock.fid = dscp->dotdotFid;
1202 } else if (strcmp(namep, ".") == 0) {
1203 rock.fid = dscp->fid;
1207 if (flags & CM_FLAG_NOMOUNTCHASE) {
1208 /* In this case, we should go and call cm_Dir* functions
1209 directly since the following cm_ApplyDir() function will
1217 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ, &dirop);
1220 code = cm_BPlusDirLookup(&dirop, namep, &rock.fid);
1225 code = cm_DirLookup(&dirop, namep, &rock.fid);
1227 cm_EndDirOp(&dirop);
1237 if (code == CM_ERROR_INEXACT_MATCH && (flags & CM_FLAG_CASEFOLD)) {
1244 return CM_ERROR_BPLUS_NOMATCH;
1249 rock.fid.cell = dscp->fid.cell;
1250 rock.fid.volume = dscp->fid.volume;
1251 rock.searchNamep = namep;
1252 rock.caseFold = (flags & CM_FLAG_CASEFOLD);
1253 rock.hasTilde = ((strchr(namep, '~') != NULL) ? 1 : 0);
1255 /* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
1256 code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
1257 (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
1259 /* code == 0 means we fell off the end of the dir, while stopnow means
1260 * that we stopped early, probably because we found the entry we're
1261 * looking for. Any other non-zero code is an error.
1263 if (code && code != CM_ERROR_STOPNOW) {
1264 /* if the cm_scache_t we are searching in is not a directory
1265 * we must return path not found because the error
1266 * is to describe the final component not an intermediary
1268 if (code == CM_ERROR_NOTDIR) {
1269 if (flags & CM_FLAG_CHECKPATH)
1270 return CM_ERROR_NOSUCHPATH;
1272 return CM_ERROR_NOSUCHFILE;
1277 getroot = (dscp==cm_data.rootSCachep) ;
1279 if (!cm_freelanceEnabled || !getroot) {
1280 if (flags & CM_FLAG_CHECKPATH)
1281 return CM_ERROR_NOSUCHPATH;
1283 return CM_ERROR_NOSUCHFILE;
1285 else { /* nonexistent dir on freelance root, so add it */
1286 char fullname[200] = ".";
1289 osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %s",
1290 osi_LogSaveString(afsd_logp,namep));
1291 if (namep[0] == '.') {
1292 if (cm_GetCell_Gen(&namep[1], &fullname[1], CM_FLAG_CREATE)) {
1294 if ( stricmp(&namep[1], &fullname[1]) )
1295 code = cm_FreelanceAddSymlink(namep, fullname, &rock.fid);
1297 code = cm_FreelanceAddMount(namep, &fullname[1], "root.cell.", 1, &rock.fid);
1300 if (cm_GetCell_Gen(namep, fullname, CM_FLAG_CREATE)) {
1302 if ( stricmp(namep, fullname) )
1303 code = cm_FreelanceAddSymlink(namep, fullname, &rock.fid);
1305 code = cm_FreelanceAddMount(namep, fullname, "root.cell.", 0, &rock.fid);
1308 if (!found || code < 0) { /* add mount point failed, so give up */
1309 if (flags & CM_FLAG_CHECKPATH)
1310 return CM_ERROR_NOSUCHPATH;
1312 return CM_ERROR_NOSUCHFILE;
1314 tscp = NULL; /* to force call of cm_GetSCache */
1319 if ( !tscp ) /* we did not find it in the dnlc */
1322 code = cm_GetSCache(&rock.fid, &tscp, userp, reqp);
1326 /* tscp is now held */
1328 lock_ObtainMutex(&tscp->mx);
1329 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1330 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1332 lock_ReleaseMutex(&tscp->mx);
1333 cm_ReleaseSCache(tscp);
1336 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1337 /* tscp is now locked */
1339 if (!(flags & CM_FLAG_NOMOUNTCHASE)
1340 && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
1341 /* mount points are funny: they have a volume name to mount
1344 code = cm_ReadMountPoint(tscp, userp, reqp);
1346 code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
1348 lock_ReleaseMutex(&tscp->mx);
1349 cm_ReleaseSCache(tscp);
1356 lock_ReleaseMutex(&tscp->mx);
1359 /* copy back pointer */
1362 /* insert scache in dnlc */
1363 if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
1364 /* lock the directory entry to prevent racing callback revokes */
1365 lock_ObtainMutex(&dscp->mx);
1366 if ( dscp->cbServerp != NULL && dscp->cbExpires > 0 )
1367 cm_dnlcEnter(dscp, namep, tscp);
1368 lock_ReleaseMutex(&dscp->mx);
1375 int cm_ExpandSysName(char *inp, char *outp, long outSize, unsigned int index)
1380 tp = strrchr(inp, '@');
1382 return 0; /* no @sys */
1384 if (strcmp(tp, "@sys") != 0)
1385 return 0; /* no @sys */
1387 /* caller just wants to know if this is a valid @sys type of name */
1391 if (index >= MAXNUMSYSNAMES)
1394 /* otherwise generate the properly expanded @sys name */
1395 prefixCount = (int)(tp - inp);
1397 strncpy(outp, inp, prefixCount); /* copy out "a." from "a.@sys" */
1398 outp[prefixCount] = 0; /* null terminate the "a." */
1399 strcat(outp, cm_sysNameList[index]);/* append i386_nt40 */
1403 long cm_EvaluateVolumeReference(char * namep, long flags, cm_user_t * userp,
1404 cm_req_t *reqp, cm_scache_t ** outpScpp)
1407 char cellName[CELL_MAXNAMELEN];
1408 char volumeName[VL_MAXNAMELEN];
1413 cm_cell_t * cellp = NULL;
1414 cm_volume_t * volp = NULL;
1417 int mountType = RWVOL;
1419 osi_Log1(afsd_logp, "cm_EvaluateVolumeReference for string [%s]",
1420 osi_LogSaveString(afsd_logp, namep));
1422 if (strnicmp(namep, CM_PREFIX_VOL, CM_PREFIX_VOL_CCH) != 0) {
1423 goto _exit_invalid_path;
1426 /* namep is assumed to look like the following:
1428 @vol:<cellname>%<volume>\0
1430 @vol:<cellname>#<volume>\0
1434 cp = namep + CM_PREFIX_VOL_CCH; /* cp points to cell name, hopefully */
1435 tp = strchr(cp, '%');
1437 tp = strchr(cp, '#');
1439 (len = tp - cp) == 0 ||
1440 len > CELL_MAXNAMELEN)
1441 goto _exit_invalid_path;
1442 strncpy(cellName, cp, len);
1443 cellName[len] = '\0';
1448 cp = tp+1; /* cp now points to volume, supposedly */
1449 strncpy(volumeName, cp, VL_MAXNAMELEN-1);
1450 volumeName[VL_MAXNAMELEN - 1] = 0;
1452 /* OK, now we have the cell and the volume */
1453 osi_Log2(afsd_logp, " Found cell [%s] and volume [%s]",
1454 osi_LogSaveString(afsd_logp, cellName),
1455 osi_LogSaveString(afsd_logp, volumeName));
1457 cellp = cm_GetCell(cellName, CM_FLAG_CREATE);
1458 if (cellp == NULL) {
1459 goto _exit_invalid_path;
1462 len = strlen(volumeName);
1463 if (len >= 8 && strcmp(volumeName + len - 7, ".backup") == 0)
1465 else if (len >= 10 &&
1466 strcmp(volumeName + len - 9, ".readonly") == 0)
1471 if (cm_VolNameIsID(volumeName)) {
1472 code = cm_GetVolumeByID(cellp, atoi(volumeName), userp, reqp,
1473 CM_GETVOL_FLAG_CREATE, &volp);
1475 code = cm_GetVolumeByName(cellp, volumeName, userp, reqp,
1476 CM_GETVOL_FLAG_CREATE, &volp);
1482 fid.cell = cellp->cellID;
1484 if (volType == BACKVOL)
1485 fid.volume = volp->bk.ID;
1486 else if (volType == ROVOL ||
1487 (volType == RWVOL && mountType == ROVOL && volp->ro.ID != 0))
1488 fid.volume = volp->ro.ID;
1490 fid.volume = volp->rw.ID;
1495 code = cm_GetSCache(&fid, outpScpp, userp, reqp);
1505 if (flags & CM_FLAG_CHECKPATH)
1506 return CM_ERROR_NOSUCHPATH;
1508 return CM_ERROR_NOSUCHFILE;
1511 #ifdef DEBUG_REFCOUNT
1512 long cm_LookupDbg(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
1513 cm_req_t *reqp, cm_scache_t **outpScpp, char * file, long line)
1515 long cm_Lookup(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
1516 cm_req_t *reqp, cm_scache_t **outpScpp)
1520 char tname[AFSPATHMAX];
1521 int sysNameIndex = 0;
1522 cm_scache_t *scp = NULL;
1524 #ifdef DEBUG_REFCOUNT
1525 afsi_log("%s:%d cm_Lookup dscp 0x%p ref %d", file, line, dscp, dscp->refCount, file, line);
1526 osi_Log2(afsd_logp, "cm_Lookup dscp 0x%p ref %d", dscp, dscp->refCount);
1529 if ( stricmp(namep,SMB_IOCTL_FILENAME_NOSLASH) == 0 ) {
1530 if (flags & CM_FLAG_CHECKPATH)
1531 return CM_ERROR_NOSUCHPATH;
1533 return CM_ERROR_NOSUCHFILE;
1536 if (dscp == cm_data.rootSCachep &&
1537 strnicmp(namep, CM_PREFIX_VOL, CM_PREFIX_VOL_CCH) == 0) {
1538 return cm_EvaluateVolumeReference(namep, flags, userp, reqp, outpScpp);
1541 if (cm_ExpandSysName(namep, NULL, 0, 0) > 0) {
1542 for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
1543 code = cm_ExpandSysName(namep, tname, sizeof(tname), sysNameIndex);
1545 code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
1546 #ifdef DEBUG_REFCOUNT
1547 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);
1548 osi_Log3(afsd_logp, "cm_LookupInternal (1) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1556 cm_ReleaseSCache(scp);
1560 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1561 #ifdef DEBUG_REFCOUNT
1562 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);
1563 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1570 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1571 #ifdef DEBUG_REFCOUNT
1572 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);
1573 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1579 /* None of the possible sysName expansions could be found */
1580 if (flags & CM_FLAG_CHECKPATH)
1581 return CM_ERROR_NOSUCHPATH;
1583 return CM_ERROR_NOSUCHFILE;
1586 long cm_Unlink(cm_scache_t *dscp, char *namep, cm_user_t *userp, cm_req_t *reqp)
1592 AFSFetchStatus newDirStatus;
1594 struct rx_connection * callp;
1597 #ifdef AFS_FREELANCE_CLIENT
1598 if (cm_freelanceEnabled && dscp == cm_data.rootSCachep) {
1599 /* deleting a mount point from the root dir. */
1600 code = cm_FreelanceRemoveMount(namep);
1605 /* make sure we don't screw up the dir status during the merge */
1606 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
1608 lock_ObtainMutex(&dscp->mx);
1609 sflags = CM_SCACHESYNC_STOREDATA;
1610 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1611 lock_ReleaseMutex(&dscp->mx);
1613 cm_EndDirOp(&dirop);
1618 afsFid.Volume = dscp->fid.volume;
1619 afsFid.Vnode = dscp->fid.vnode;
1620 afsFid.Unique = dscp->fid.unique;
1622 osi_Log1(afsd_logp, "CALL RemoveFile scp 0x%p", dscp);
1624 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
1628 callp = cm_GetRxConn(connp);
1629 code = RXAFS_RemoveFile(callp, &afsFid, namep,
1630 &newDirStatus, &volSync);
1631 rx_PutConnection(callp);
1633 } while (cm_Analyze(connp, userp, reqp, &dscp->fid, &volSync, NULL, NULL, code));
1634 code = cm_MapRPCError(code, reqp);
1637 osi_Log1(afsd_logp, "CALL RemoveFile FAILURE, code 0x%x", code);
1639 osi_Log0(afsd_logp, "CALL RemoveFile SUCCESS");
1642 lock_ObtainWrite(&dirop.scp->dirlock);
1643 dirop.lockType = CM_DIRLOCK_WRITE;
1645 lock_ObtainMutex(&dscp->mx);
1646 cm_dnlcRemove(dscp, namep);
1647 cm_SyncOpDone(dscp, NULL, sflags);
1649 cm_MergeStatus(NULL, dscp, &newDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
1650 } else if (code == CM_ERROR_NOSUCHFILE) {
1651 /* windows would not have allowed the request to delete the file
1652 * if it did not believe the file existed. therefore, we must
1653 * have an inconsistent view of the world.
1655 dscp->cbServerp = NULL;
1657 lock_ReleaseMutex(&dscp->mx);
1659 if (code == 0 && cm_CheckDirOpForSingleChange(&dirop)) {
1660 cm_DirDeleteEntry(&dirop, namep);
1662 cm_BPlusDirDeleteEntry(&dirop, namep);
1665 cm_EndDirOp(&dirop);
1670 /* called with a locked vnode, and fills in the link info.
1671 * returns this the vnode still locked.
1673 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1680 lock_AssertMutex(&linkScp->mx);
1681 if (!linkScp->mountPointStringp[0]) {
1682 /* read the link data */
1683 lock_ReleaseMutex(&linkScp->mx);
1684 thyper.LowPart = thyper.HighPart = 0;
1685 code = buf_Get(linkScp, &thyper, &bufp);
1686 lock_ObtainMutex(&linkScp->mx);
1690 code = cm_SyncOp(linkScp, bufp, userp, reqp, 0,
1691 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1696 cm_SyncOpDone(linkScp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1698 if (cm_HaveBuffer(linkScp, bufp, 0))
1701 code = cm_GetBuffer(linkScp, bufp, NULL, userp, reqp);
1706 } /* while loop to get the data */
1708 /* now if we still have no link read in,
1709 * copy the data from the buffer */
1710 if ((temp = linkScp->length.LowPart) >= MOUNTPOINTLEN) {
1712 return CM_ERROR_TOOBIG;
1715 /* otherwise, it fits; make sure it is still null (could have
1716 * lost race with someone else referencing this link above),
1717 * and if so, copy in the data.
1719 if (!linkScp->mountPointStringp[0]) {
1720 strncpy(linkScp->mountPointStringp, bufp->datap, temp);
1721 linkScp->mountPointStringp[temp] = 0; /* null terminate */
1724 } /* don't have sym link contents cached */
1729 /* called with a held vnode and a path suffix, with the held vnode being a
1730 * symbolic link. Our goal is to generate a new path to interpret, and return
1731 * this new path in newSpaceBufferp. If the new vnode is relative to a dir
1732 * other than the directory containing the symbolic link, then the new root is
1733 * returned in *newRootScpp, otherwise a null is returned there.
1735 long cm_AssembleLink(cm_scache_t *linkScp, char *pathSuffixp,
1736 cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1737 cm_user_t *userp, cm_req_t *reqp)
1744 lock_ObtainMutex(&linkScp->mx);
1745 code = cm_HandleLink(linkScp, userp, reqp);
1749 /* if we may overflow the buffer, bail out; buffer is signficantly
1750 * bigger than max path length, so we don't really have to worry about
1751 * being a little conservative here.
1753 if (strlen(linkScp->mountPointStringp) + strlen(pathSuffixp) + 2
1754 >= CM_UTILS_SPACESIZE)
1755 return CM_ERROR_TOOBIG;
1757 tsp = cm_GetSpace();
1758 linkp = linkScp->mountPointStringp;
1759 if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1760 if (strlen(linkp) > cm_mountRootLen)
1761 strcpy(tsp->data, linkp+cm_mountRootLen+1);
1764 *newRootScpp = cm_data.rootSCachep;
1765 cm_HoldSCache(cm_data.rootSCachep);
1766 } else if (linkp[0] == '\\' && linkp[1] == '\\') {
1767 if (!strnicmp(&linkp[2], cm_NetbiosName, (len = (long)strlen(cm_NetbiosName))))
1769 char * p = &linkp[len + 3];
1770 if (strnicmp(p, "all", 3) == 0)
1773 strcpy(tsp->data, p);
1774 for (p = tsp->data; *p; p++) {
1778 *newRootScpp = cm_data.rootSCachep;
1779 cm_HoldSCache(cm_data.rootSCachep);
1781 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1782 strcpy(tsp->data, linkp);
1783 *newRootScpp = NULL;
1784 code = CM_ERROR_PATH_NOT_COVERED;
1786 } else if ( !strnicmp(linkp, "msdfs:", (len = (long)strlen("msdfs:"))) ) {
1787 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1788 strcpy(tsp->data, linkp);
1789 *newRootScpp = NULL;
1790 code = CM_ERROR_PATH_NOT_COVERED;
1791 } else if (*linkp == '\\' || *linkp == '/') {
1793 /* formerly, this was considered to be from the AFS root,
1794 * but this seems to create problems. instead, we will just
1795 * reject the link */
1796 strcpy(tsp->data, linkp+1);
1797 *newRootScpp = cm_data.rootSCachep;
1798 cm_HoldSCache(cm_data.rootSCachep);
1800 /* we still copy the link data into the response so that
1801 * the user can see what the link points to
1803 linkScp->fileType = CM_SCACHETYPE_INVALID;
1804 strcpy(tsp->data, linkp);
1805 *newRootScpp = NULL;
1806 code = CM_ERROR_NOSUCHPATH;
1809 /* a relative link */
1810 strcpy(tsp->data, linkp);
1811 *newRootScpp = NULL;
1813 if (pathSuffixp[0] != 0) { /* if suffix string is non-null */
1814 strcat(tsp->data, "\\");
1815 strcat(tsp->data, pathSuffixp);
1817 *newSpaceBufferp = tsp;
1820 lock_ReleaseMutex(&linkScp->mx);
1823 #ifdef DEBUG_REFCOUNT
1824 long cm_NameIDbg(cm_scache_t *rootSCachep, char *pathp, long flags,
1825 cm_user_t *userp, char *tidPathp, cm_req_t *reqp, cm_scache_t **outScpp,
1826 char * file, long line)
1828 long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
1829 cm_user_t *userp, char *tidPathp, cm_req_t *reqp, cm_scache_t **outScpp)
1833 char *tp; /* ptr moving through input buffer */
1834 char tc; /* temp char */
1835 int haveComponent; /* has new component started? */
1836 char component[AFSPATHMAX]; /* this is the new component */
1837 char *cp; /* component name being assembled */
1838 cm_scache_t *tscp; /* current location in the hierarchy */
1839 cm_scache_t *nscp; /* next dude down */
1840 cm_scache_t *dirScp; /* last dir we searched */
1841 cm_scache_t *linkScp; /* new root for the symlink we just
1843 cm_space_t *psp; /* space for current path, if we've hit
1845 cm_space_t *tempsp; /* temp vbl */
1846 char *restp; /* rest of the pathname to interpret */
1847 int symlinkCount; /* count of # of symlinks traversed */
1848 int extraFlag; /* avoid chasing mt pts for dir cmd */
1849 int phase = 1; /* 1 = tidPathp, 2 = pathp */
1850 #define MAX_FID_COUNT 512
1851 cm_fid_t fids[MAX_FID_COUNT]; /* array of fids processed in this path walk */
1852 int fid_count = 0; /* number of fids processed in this path walk */
1855 #ifdef DEBUG_REFCOUNT
1856 afsi_log("%s:%d cm_NameI rootscp 0x%p ref %d", file, line, rootSCachep, rootSCachep->refCount);
1857 osi_Log4(afsd_logp,"cm_NameI rootscp 0x%p path %s tidpath %s flags 0x%x",
1858 rootSCachep, pathp ? pathp : "<NULL>", tidPathp ? tidPathp : "<NULL>",
1873 cm_HoldSCache(tscp);
1881 /* map Unix slashes into DOS ones so we can interpret Unix
1887 if (!haveComponent) {
1890 } else if (tc == 0) {
1904 /* we have a component here */
1905 if (tc == 0 || tc == '\\') {
1906 /* end of the component; we're at the last
1907 * component if tc == 0. However, if the last
1908 * is a symlink, we have more to do.
1910 *cp++ = 0; /* add null termination */
1912 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
1913 extraFlag = CM_FLAG_NOMOUNTCHASE;
1914 code = cm_Lookup(tscp, component,
1916 userp, reqp, &nscp);
1919 if (!strcmp(component,"..") || !strcmp(component,".")) {
1921 * roll back the fid list until we find the fid
1922 * that matches where we are now. Its not necessarily
1923 * one or two fids because they might have been
1924 * symlinks or mount points or both that were crossed.
1926 for ( i=fid_count-1; i>=0; i--) {
1927 if (!cm_FidCmp(&nscp->fid, &fids[i]))
1931 /* add the new fid to the list */
1932 for ( i=0; i<fid_count; i++) {
1933 if ( !cm_FidCmp(&nscp->fid, &fids[i]) ) {
1934 code = CM_ERROR_TOO_MANY_SYMLINKS;
1935 cm_ReleaseSCache(nscp);
1940 if (i == fid_count && fid_count < MAX_FID_COUNT) {
1941 fids[fid_count++] = nscp->fid;
1947 cm_ReleaseSCache(tscp);
1949 cm_ReleaseSCache(dirScp);
1952 if ((code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH) &&
1953 tscp->fileType == CM_SCACHETYPE_SYMLINK)
1955 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_NOSUCHPATH");
1956 return CM_ERROR_NOSUCHPATH;
1958 osi_Log1(afsd_logp,"cm_NameI code 0x%x", code);
1963 haveComponent = 0; /* component done */
1965 cm_ReleaseSCache(dirScp);
1966 dirScp = tscp; /* for some symlinks */
1967 tscp = nscp; /* already held */
1969 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
1972 cm_ReleaseSCache(dirScp);
1978 /* now, if tscp is a symlink, we should follow
1979 * it and assemble the path again.
1981 lock_ObtainMutex(&tscp->mx);
1982 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1983 CM_SCACHESYNC_GETSTATUS
1984 | CM_SCACHESYNC_NEEDCALLBACK);
1986 lock_ReleaseMutex(&tscp->mx);
1987 cm_ReleaseSCache(tscp);
1990 cm_ReleaseSCache(dirScp);
1995 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1997 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
1998 /* this is a symlink; assemble a new buffer */
1999 lock_ReleaseMutex(&tscp->mx);
2000 if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
2001 cm_ReleaseSCache(tscp);
2004 cm_ReleaseSCache(dirScp);
2009 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_TOO_MANY_SYMLINKS");
2010 return CM_ERROR_TOO_MANY_SYMLINKS;
2016 code = cm_AssembleLink(tscp, restp, &linkScp, &tempsp, userp, reqp);
2018 if (code == 0 && linkScp != NULL) {
2019 if (linkScp == cm_data.rootSCachep)
2022 for ( i=0; i<fid_count; i++) {
2023 if ( !cm_FidCmp(&linkScp->fid, &fids[i]) ) {
2024 code = CM_ERROR_TOO_MANY_SYMLINKS;
2025 cm_ReleaseSCache(linkScp);
2031 if (i == fid_count && fid_count < MAX_FID_COUNT) {
2032 fids[fid_count++] = linkScp->fid;
2037 /* something went wrong */
2038 cm_ReleaseSCache(tscp);
2041 cm_ReleaseSCache(dirScp);
2047 /* otherwise, tempsp has the new path,
2048 * and linkScp is the new root from
2049 * which to interpret that path.
2050 * Continue with the namei processing,
2051 * also doing the bookkeeping for the
2052 * space allocation and tracking the
2053 * vnode reference counts.
2059 cm_ReleaseSCache(tscp);
2064 * now, if linkScp is null, that's
2065 * AssembleLink's way of telling us that
2066 * the sym link is relative to the dir
2067 * containing the link. We have a ref
2068 * to it in dirScp, and we hold it now
2069 * and reuse it as the new spot in the
2077 /* not a symlink, we may be done */
2078 lock_ReleaseMutex(&tscp->mx);
2086 cm_ReleaseSCache(dirScp);
2094 cm_ReleaseSCache(dirScp);
2097 } /* end of a component */
2100 } /* we have a component */
2101 } /* big while loop over all components */
2105 cm_ReleaseSCache(dirScp);
2111 cm_ReleaseSCache(tscp);
2113 #ifdef DEBUG_REFCOUNT
2114 afsi_log("%s:%d cm_NameI code 0x%x outScpp 0x%p ref %d", file, line, code, *outScpp, (*outScpp)->refCount);
2116 osi_Log2(afsd_logp,"cm_NameI code 0x%x outScpp 0x%p", code, *outScpp);
2120 /* called with a dir, and a vnode within the dir that happens to be a symlink.
2121 * We chase the link, and return a held pointer to the target, if it exists,
2122 * in *outScpp. If we succeed, we return 0, otherwise we return an error code
2123 * and do not hold or return a target vnode.
2125 * This is very similar to calling cm_NameI with the last component of a name,
2126 * which happens to be a symlink, except that we've already passed by the name.
2128 * This function is typically called by the directory listing functions, which
2129 * encounter symlinks but need to return the proper file length so programs
2130 * like "more" work properly when they make use of the attributes retrieved from
2133 * The input vnode should not be locked when this function is called.
2135 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
2136 cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
2140 cm_scache_t *newRootScp;
2142 osi_Log1(afsd_logp, "Evaluating symlink scp 0x%p", linkScp);
2144 code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
2148 /* now, if newRootScp is NULL, we're really being told that the symlink
2149 * is relative to the current directory (dscp).
2151 if (newRootScp == NULL) {
2153 cm_HoldSCache(dscp);
2156 code = cm_NameI(newRootScp, spacep->data,
2157 CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
2158 userp, NULL, reqp, outScpp);
2160 if (code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH)
2161 code = CM_ERROR_NOSUCHPATH;
2163 /* this stuff is allocated no matter what happened on the namei call,
2165 cm_FreeSpace(spacep);
2166 cm_ReleaseSCache(newRootScp);
2168 if (linkScp == *outScpp) {
2169 cm_ReleaseSCache(*outScpp);
2171 code = CM_ERROR_NOSUCHPATH;
2177 /* make this big enough so that one buffer of dir pages won't overflow. We'll
2178 * check anyway, but we want to minimize the chance that we have to leave stuff
2181 #define CM_BULKMAX (3 * AFSCBMAX)
2183 /* rock for bulk stat calls */
2184 typedef struct cm_bulkStat {
2185 osi_hyper_t bufOffset; /* only do it for things in this buffer page */
2187 /* info for the actual call */
2188 int counter; /* next free slot */
2189 AFSFid fids[CM_BULKMAX];
2190 AFSFetchStatus stats[CM_BULKMAX];
2191 AFSCallBack callbacks[CM_BULKMAX];
2194 /* for a given entry, make sure that it isn't in the stat cache, and then
2195 * add it to the list of file IDs to be obtained.
2197 * Don't bother adding it if we already have a vnode. Note that the dir
2198 * is locked, so we have to be careful checking the vnode we're thinking of
2199 * processing, to avoid deadlocks.
2201 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
2212 /* Don't overflow bsp. */
2213 if (bsp->counter >= CM_BULKMAX)
2214 return CM_ERROR_STOPNOW;
2216 thyper.LowPart = cm_data.buf_blockSize;
2217 thyper.HighPart = 0;
2218 thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
2220 /* thyper is now the first byte past the end of the record we're
2221 * interested in, and bsp->bufOffset is the first byte of the record
2222 * we're interested in.
2223 * Skip data in the others.
2226 if (LargeIntegerLessThan(*offp, bsp->bufOffset))
2228 if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
2229 return CM_ERROR_STOPNOW;
2230 if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
2233 tfid.cell = scp->fid.cell;
2234 tfid.volume = scp->fid.volume;
2235 tfid.vnode = ntohl(dep->fid.vnode);
2236 tfid.unique = ntohl(dep->fid.unique);
2237 tscp = cm_FindSCache(&tfid);
2239 if (lock_TryMutex(&tscp->mx)) {
2240 /* we have an entry that we can look at */
2241 if (!(tscp->flags & CM_SCACHEFLAG_EACCESS) && cm_HaveCallback(tscp)) {
2242 /* we have a callback on it. Don't bother
2243 * fetching this stat entry, since we're happy
2244 * with the info we have.
2246 lock_ReleaseMutex(&tscp->mx);
2247 cm_ReleaseSCache(tscp);
2250 lock_ReleaseMutex(&tscp->mx);
2252 cm_ReleaseSCache(tscp);
2255 #ifdef AFS_FREELANCE_CLIENT
2256 // yj: if this is a mountpoint under root.afs then we don't want it
2257 // to be bulkstat-ed, instead, we call getSCache directly and under
2258 // getSCache, it is handled specially.
2259 if ( cm_freelanceEnabled &&
2260 tfid.cell==AFS_FAKE_ROOT_CELL_ID &&
2261 tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
2262 !(tfid.vnode==0x1 && tfid.unique==0x1) )
2264 osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
2265 return cm_GetSCache(&tfid, &tscp, NULL, NULL);
2267 #endif /* AFS_FREELANCE_CLIENT */
2270 bsp->fids[i].Volume = scp->fid.volume;
2271 bsp->fids[i].Vnode = tfid.vnode;
2272 bsp->fids[i].Unique = tfid.unique;
2276 /* called with a locked scp and a pointer to a buffer. Make bulk stat
2277 * calls on all undeleted files in the page of the directory specified.
2280 cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
2284 cm_bulkStat_t bb; /* this is *BIG*, probably 16K or so;
2285 * watch for stack problems */
2286 AFSCBFids fidStruct;
2287 AFSBulkStats statStruct;
2289 AFSCBs callbackStruct;
2292 cm_callbackRequest_t cbReq;
2298 struct rx_connection * callp;
2299 int inlinebulk = 0; /* Did we use InlineBulkStatus RPC or not? */
2301 osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%p", dscp);
2303 /* should be on a buffer boundary */
2304 osi_assertx((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0, "invalid offset");
2306 memset(&bb, 0, sizeof(bb));
2307 bb.bufOffset = *offsetp;
2309 lock_ReleaseMutex(&dscp->mx);
2310 /* first, assemble the file IDs we need to stat */
2311 code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) &bb, offsetp, userp, reqp, NULL);
2313 /* if we failed, bail out early */
2314 if (code && code != CM_ERROR_STOPNOW) {
2315 lock_ObtainMutex(&dscp->mx);
2319 /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
2320 * make the calls to create the entries. Handle AFSCBMAX files at a
2324 while (filex < bb.counter) {
2325 filesThisCall = bb.counter - filex;
2326 if (filesThisCall > AFSCBMAX)
2327 filesThisCall = AFSCBMAX;
2329 fidStruct.AFSCBFids_len = filesThisCall;
2330 fidStruct.AFSCBFids_val = &bb.fids[filex];
2331 statStruct.AFSBulkStats_len = filesThisCall;
2332 statStruct.AFSBulkStats_val = &bb.stats[filex];
2333 callbackStruct.AFSCBs_len = filesThisCall;
2334 callbackStruct.AFSCBs_val = &bb.callbacks[filex];
2335 cm_StartCallbackGrantingCall(NULL, &cbReq);
2336 osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
2338 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2342 callp = cm_GetRxConn(connp);
2343 if (!(connp->serverp->flags & CM_SERVERFLAG_NOINLINEBULK)) {
2344 code = RXAFS_InlineBulkStatus(callp, &fidStruct,
2345 &statStruct, &callbackStruct, &volSync);
2346 if (code == RXGEN_OPCODE) {
2347 cm_SetServerNoInlineBulk(connp->serverp, 0);
2353 code = RXAFS_BulkStatus(callp, &fidStruct,
2354 &statStruct, &callbackStruct, &volSync);
2356 rx_PutConnection(callp);
2358 } while (cm_Analyze(connp, userp, reqp, &dscp->fid,
2359 &volSync, NULL, &cbReq, code));
2360 code = cm_MapRPCError(code, reqp);
2362 osi_Log2(afsd_logp, "CALL %sBulkStatus FAILURE code 0x%x",
2363 inlinebulk ? "Inline" : "", code);
2365 osi_Log1(afsd_logp, "CALL %sBulkStatus SUCCESS", inlinebulk ? "Inline" : "");
2367 /* may as well quit on an error, since we're not going to do
2368 * much better on the next immediate call, either.
2371 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2375 /* otherwise, we should do the merges */
2376 for (i = 0; i<filesThisCall; i++) {
2378 tfid.cell = dscp->fid.cell;
2379 tfid.volume = bb.fids[j].Volume;
2380 tfid.vnode = bb.fids[j].Vnode;
2381 tfid.unique = bb.fids[j].Unique;
2382 code = cm_GetSCache(&tfid, &scp, userp, reqp);
2386 /* otherwise, if this entry has no callback info,
2389 lock_ObtainMutex(&scp->mx);
2390 /* now, we have to be extra paranoid on merging in this
2391 * information, since we didn't use cm_SyncOp before
2392 * starting the fetch to make sure that no bad races
2393 * were occurring. Specifically, we need to make sure
2394 * we don't obliterate any newer information in the
2395 * vnode than have here.
2397 * Right now, be pretty conservative: if there's a
2398 * callback or a pending call, skip it.
2400 if ((scp->cbServerp == NULL || (scp->flags & CM_SCACHEFLAG_EACCESS))
2402 (CM_SCACHEFLAG_FETCHING
2403 | CM_SCACHEFLAG_STORING
2404 | CM_SCACHEFLAG_SIZESTORING))) {
2405 cm_EndCallbackGrantingCall(scp, &cbReq,
2407 CM_CALLBACK_MAINTAINCOUNT);
2408 cm_MergeStatus(dscp, scp, &bb.stats[j], &volSync, userp, 0);
2410 lock_ReleaseMutex(&scp->mx);
2411 cm_ReleaseSCache(scp);
2412 } /* all files in the response */
2413 /* now tell it to drop the count,
2414 * after doing the vnode processing above */
2415 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2417 filex += filesThisCall;
2418 } /* while there are still more files to process */
2419 lock_ObtainMutex(&dscp->mx);
2421 /* If we did the InlineBulk RPC pull out the return code and log it */
2423 if ((&bb.stats[0])->errorCode) {
2424 osi_Log1(afsd_logp, "cm_TryBulkStat bulk stat error: %d",
2425 (&bb.stats[0])->errorCode);
2429 osi_Log0(afsd_logp, "END cm_TryBulkStat");
2433 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
2437 /* initialize store back mask as inexpensive local variable */
2439 memset(statusp, 0, sizeof(AFSStoreStatus));
2441 /* copy out queued info from scache first, if scp passed in */
2443 if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
2444 statusp->ClientModTime = scp->clientModTime;
2445 mask |= AFS_SETMODTIME;
2446 scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
2451 /* now add in our locally generated request */
2452 if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
2453 statusp->ClientModTime = attrp->clientModTime;
2454 mask |= AFS_SETMODTIME;
2456 if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
2457 statusp->UnixModeBits = attrp->unixModeBits;
2458 mask |= AFS_SETMODE;
2460 if (attrp->mask & CM_ATTRMASK_OWNER) {
2461 statusp->Owner = attrp->owner;
2462 mask |= AFS_SETOWNER;
2464 if (attrp->mask & CM_ATTRMASK_GROUP) {
2465 statusp->Group = attrp->group;
2466 mask |= AFS_SETGROUP;
2469 statusp->Mask = mask;
2472 /* set the file size, and make sure that all relevant buffers have been
2473 * truncated. Ensure that any partially truncated buffers have been zeroed
2474 * to the end of the buffer.
2476 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
2482 /* start by locking out buffer creation */
2483 lock_ObtainWrite(&scp->bufCreateLock);
2485 /* verify that this is a file, not a dir or a symlink */
2486 lock_ObtainMutex(&scp->mx);
2487 code = cm_SyncOp(scp, NULL, userp, reqp, 0,
2488 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2491 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2493 if (scp->fileType != CM_SCACHETYPE_FILE) {
2494 code = CM_ERROR_ISDIR;
2499 if (LargeIntegerLessThan(*sizep, scp->length))
2504 lock_ReleaseMutex(&scp->mx);
2506 /* can't hold scp->mx lock here, since we may wait for a storeback to
2507 * finish if the buffer package is cleaning a buffer by storing it to
2511 buf_Truncate(scp, userp, reqp, sizep);
2513 /* now ensure that file length is short enough, and update truncPos */
2514 lock_ObtainMutex(&scp->mx);
2516 /* make sure we have a callback (so we have the right value for the
2517 * length), and wait for it to be safe to do a truncate.
2519 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
2520 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2521 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2523 /* If we only have 'i' bits, then we should still be able to set
2524 the size of a file we created. */
2525 if (code == CM_ERROR_NOACCESS && scp->creator == userp) {
2526 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
2527 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2528 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2534 if (LargeIntegerLessThan(*sizep, scp->length)) {
2535 /* a real truncation. If truncPos is not set yet, or is bigger
2536 * than where we're truncating the file, set truncPos to this
2541 if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
2542 || LargeIntegerLessThan(*sizep, scp->length)) {
2544 scp->truncPos = *sizep;
2545 scp->mask |= CM_SCACHEMASK_TRUNCPOS;
2547 /* in either case, the new file size has been changed */
2548 scp->length = *sizep;
2549 scp->mask |= CM_SCACHEMASK_LENGTH;
2551 else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
2552 /* really extending the file */
2553 scp->length = *sizep;
2554 scp->mask |= CM_SCACHEMASK_LENGTH;
2557 /* done successfully */
2560 cm_SyncOpDone(scp, NULL,
2561 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2562 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2565 lock_ReleaseMutex(&scp->mx);
2566 lock_ReleaseWrite(&scp->bufCreateLock);
2571 /* set the file size or other attributes (but not both at once) */
2572 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
2576 AFSFetchStatus afsOutStatus;
2580 AFSStoreStatus afsInStatus;
2581 struct rx_connection * callp;
2583 /* handle file length setting */
2584 if (attrp->mask & CM_ATTRMASK_LENGTH)
2585 return cm_SetLength(scp, &attrp->length, userp, reqp);
2587 lock_ObtainMutex(&scp->mx);
2588 /* otherwise, we have to make an RPC to get the status */
2589 code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
2591 lock_ReleaseMutex(&scp->mx);
2595 /* make the attr structure */
2596 cm_StatusFromAttr(&afsInStatus, scp, attrp);
2598 tfid.Volume = scp->fid.volume;
2599 tfid.Vnode = scp->fid.vnode;
2600 tfid.Unique = scp->fid.unique;
2601 lock_ReleaseMutex(&scp->mx);
2603 /* now make the RPC */
2604 osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%p", scp);
2606 code = cm_ConnFromFID(&scp->fid, userp, reqp, &connp);
2610 callp = cm_GetRxConn(connp);
2611 code = RXAFS_StoreStatus(callp, &tfid,
2612 &afsInStatus, &afsOutStatus, &volSync);
2613 rx_PutConnection(callp);
2615 } while (cm_Analyze(connp, userp, reqp,
2616 &scp->fid, &volSync, NULL, NULL, code));
2617 code = cm_MapRPCError(code, reqp);
2620 osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
2622 osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
2624 lock_ObtainMutex(&scp->mx);
2625 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
2627 cm_MergeStatus(NULL, scp, &afsOutStatus, &volSync, userp,
2628 CM_MERGEFLAG_FORCE|CM_MERGEFLAG_STOREDATA);
2630 /* if we're changing the mode bits, discard the ACL cache,
2631 * since we changed the mode bits.
2633 if (afsInStatus.Mask & AFS_SETMODE)
2634 cm_FreeAllACLEnts(scp);
2635 lock_ReleaseMutex(&scp->mx);
2639 long cm_Create(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2640 cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
2645 cm_callbackRequest_t cbReq;
2648 cm_scache_t *scp = NULL;
2650 AFSStoreStatus inStatus;
2651 AFSFetchStatus updatedDirStatus;
2652 AFSFetchStatus newFileStatus;
2653 AFSCallBack newFileCallback;
2655 struct rx_connection * callp;
2658 /* can't create names with @sys in them; must expand it manually first.
2659 * return "invalid request" if they try.
2661 if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2662 return CM_ERROR_ATSYS;
2665 /* before starting the RPC, mark that we're changing the file data, so
2666 * that someone who does a chmod will know to wait until our call
2669 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2670 lock_ObtainMutex(&dscp->mx);
2671 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2672 lock_ReleaseMutex(&dscp->mx);
2674 cm_StartCallbackGrantingCall(NULL, &cbReq);
2676 cm_EndDirOp(&dirop);
2683 cm_StatusFromAttr(&inStatus, NULL, attrp);
2685 /* try the RPC now */
2686 osi_Log1(afsd_logp, "CALL CreateFile scp 0x%p", dscp);
2688 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2692 dirAFSFid.Volume = dscp->fid.volume;
2693 dirAFSFid.Vnode = dscp->fid.vnode;
2694 dirAFSFid.Unique = dscp->fid.unique;
2696 callp = cm_GetRxConn(connp);
2697 code = RXAFS_CreateFile(connp->callp, &dirAFSFid, namep,
2698 &inStatus, &newAFSFid, &newFileStatus,
2699 &updatedDirStatus, &newFileCallback,
2701 rx_PutConnection(callp);
2703 } while (cm_Analyze(connp, userp, reqp,
2704 &dscp->fid, &volSync, NULL, &cbReq, code));
2705 code = cm_MapRPCError(code, reqp);
2708 osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
2710 osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
2713 lock_ObtainWrite(&dirop.scp->dirlock);
2714 dirop.lockType = CM_DIRLOCK_WRITE;
2716 lock_ObtainMutex(&dscp->mx);
2717 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2719 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
2721 lock_ReleaseMutex(&dscp->mx);
2723 /* now try to create the file's entry, too, but be careful to
2724 * make sure that we don't merge in old info. Since we weren't locking
2725 * out any requests during the file's creation, we may have pretty old
2729 newFid.cell = dscp->fid.cell;
2730 newFid.volume = dscp->fid.volume;
2731 newFid.vnode = newAFSFid.Vnode;
2732 newFid.unique = newAFSFid.Unique;
2733 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2735 lock_ObtainMutex(&scp->mx);
2736 scp->creator = userp; /* remember who created it */
2737 if (!cm_HaveCallback(scp)) {
2738 cm_MergeStatus(dscp, scp, &newFileStatus, &volSync,
2740 cm_EndCallbackGrantingCall(scp, &cbReq,
2741 &newFileCallback, 0);
2744 lock_ReleaseMutex(&scp->mx);
2749 /* make sure we end things properly */
2751 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2753 if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
2754 cm_DirCreateEntry(&dirop, namep, &newFid);
2756 cm_BPlusDirCreateEntry(&dirop, namep, &newFid);
2759 cm_EndDirOp(&dirop);
2764 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
2768 lock_ObtainWrite(&scp->bufCreateLock);
2769 code = buf_CleanVnode(scp, userp, reqp);
2770 lock_ReleaseWrite(&scp->bufCreateLock);
2772 lock_ObtainMutex(&scp->mx);
2774 if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
2775 | CM_SCACHEMASK_CLIENTMODTIME
2776 | CM_SCACHEMASK_LENGTH))
2777 code = cm_StoreMini(scp, userp, reqp);
2779 if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) {
2780 code = (scp->flags & CM_SCACHEFLAG_OVERQUOTA) ? CM_ERROR_QUOTA : CM_ERROR_SPACE;
2781 scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE);
2784 lock_ReleaseMutex(&scp->mx);
2789 long cm_MakeDir(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2790 cm_user_t *userp, cm_req_t *reqp)
2795 cm_callbackRequest_t cbReq;
2798 cm_scache_t *scp = NULL;
2800 AFSStoreStatus inStatus;
2801 AFSFetchStatus updatedDirStatus;
2802 AFSFetchStatus newDirStatus;
2803 AFSCallBack newDirCallback;
2805 struct rx_connection * callp;
2808 /* can't create names with @sys in them; must expand it manually first.
2809 * return "invalid request" if they try.
2811 if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2812 return CM_ERROR_ATSYS;
2815 /* before starting the RPC, mark that we're changing the directory
2816 * data, so that someone who does a chmod on the dir will wait until
2817 * our call completes.
2819 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2820 lock_ObtainMutex(&dscp->mx);
2821 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2822 lock_ReleaseMutex(&dscp->mx);
2824 cm_StartCallbackGrantingCall(NULL, &cbReq);
2826 cm_EndDirOp(&dirop);
2833 cm_StatusFromAttr(&inStatus, NULL, attrp);
2835 /* try the RPC now */
2836 osi_Log1(afsd_logp, "CALL MakeDir scp 0x%p", dscp);
2838 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2842 dirAFSFid.Volume = dscp->fid.volume;
2843 dirAFSFid.Vnode = dscp->fid.vnode;
2844 dirAFSFid.Unique = dscp->fid.unique;
2846 callp = cm_GetRxConn(connp);
2847 code = RXAFS_MakeDir(connp->callp, &dirAFSFid, namep,
2848 &inStatus, &newAFSFid, &newDirStatus,
2849 &updatedDirStatus, &newDirCallback,
2851 rx_PutConnection(callp);
2853 } while (cm_Analyze(connp, userp, reqp,
2854 &dscp->fid, &volSync, NULL, &cbReq, code));
2855 code = cm_MapRPCError(code, reqp);
2858 osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
2860 osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
2863 lock_ObtainWrite(&dirop.scp->dirlock);
2864 dirop.lockType = CM_DIRLOCK_WRITE;
2866 lock_ObtainMutex(&dscp->mx);
2867 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2869 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
2871 lock_ReleaseMutex(&dscp->mx);
2873 /* now try to create the new dir's entry, too, but be careful to
2874 * make sure that we don't merge in old info. Since we weren't locking
2875 * out any requests during the file's creation, we may have pretty old
2879 newFid.cell = dscp->fid.cell;
2880 newFid.volume = dscp->fid.volume;
2881 newFid.vnode = newAFSFid.Vnode;
2882 newFid.unique = newAFSFid.Unique;
2883 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2885 lock_ObtainMutex(&scp->mx);
2886 if (!cm_HaveCallback(scp)) {
2887 cm_MergeStatus(dscp, scp, &newDirStatus, &volSync,
2889 cm_EndCallbackGrantingCall(scp, &cbReq,
2890 &newDirCallback, 0);
2893 lock_ReleaseMutex(&scp->mx);
2894 cm_ReleaseSCache(scp);
2898 /* make sure we end things properly */
2900 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2902 if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
2903 cm_DirCreateEntry(&dirop, namep, &newFid);
2905 cm_BPlusDirCreateEntry(&dirop, namep, &newFid);
2908 cm_EndDirOp(&dirop);
2910 /* and return error code */
2914 long cm_Link(cm_scache_t *dscp, char *namep, cm_scache_t *sscp, long flags,
2915 cm_user_t *userp, cm_req_t *reqp)
2920 AFSFid existingAFSFid;
2921 AFSFetchStatus updatedDirStatus;
2922 AFSFetchStatus newLinkStatus;
2924 struct rx_connection * callp;
2927 if (dscp->fid.cell != sscp->fid.cell ||
2928 dscp->fid.volume != sscp->fid.volume) {
2929 return CM_ERROR_CROSSDEVLINK;
2932 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2933 lock_ObtainMutex(&dscp->mx);
2934 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2935 lock_ReleaseMutex(&dscp->mx);
2937 cm_EndDirOp(&dirop);
2942 /* try the RPC now */
2943 osi_Log1(afsd_logp, "CALL Link scp 0x%p", dscp);
2945 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2948 dirAFSFid.Volume = dscp->fid.volume;
2949 dirAFSFid.Vnode = dscp->fid.vnode;
2950 dirAFSFid.Unique = dscp->fid.unique;
2952 existingAFSFid.Volume = sscp->fid.volume;
2953 existingAFSFid.Vnode = sscp->fid.vnode;
2954 existingAFSFid.Unique = sscp->fid.unique;
2956 callp = cm_GetRxConn(connp);
2957 code = RXAFS_Link(callp, &dirAFSFid, namep, &existingAFSFid,
2958 &newLinkStatus, &updatedDirStatus, &volSync);
2959 rx_PutConnection(callp);
2960 osi_Log1(smb_logp," RXAFS_Link returns 0x%x", code);
2962 } while (cm_Analyze(connp, userp, reqp,
2963 &dscp->fid, &volSync, NULL, NULL, code));
2965 code = cm_MapRPCError(code, reqp);
2968 osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
2970 osi_Log0(afsd_logp, "CALL Link SUCCESS");
2973 lock_ObtainWrite(&dirop.scp->dirlock);
2974 dirop.lockType = CM_DIRLOCK_WRITE;
2976 lock_ObtainMutex(&dscp->mx);
2977 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2979 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
2981 lock_ReleaseMutex(&dscp->mx);
2984 if (cm_CheckDirOpForSingleChange(&dirop)) {
2985 cm_DirCreateEntry(&dirop, namep, &sscp->fid);
2987 cm_BPlusDirCreateEntry(&dirop, namep, &sscp->fid);
2991 cm_EndDirOp(&dirop);
2996 long cm_SymLink(cm_scache_t *dscp, char *namep, char *contentsp, long flags,
2997 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
3005 AFSStoreStatus inStatus;
3006 AFSFetchStatus updatedDirStatus;
3007 AFSFetchStatus newLinkStatus;
3009 struct rx_connection * callp;
3012 /* before starting the RPC, mark that we're changing the directory data,
3013 * so that someone who does a chmod on the dir will wait until our
3016 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
3017 lock_ObtainMutex(&dscp->mx);
3018 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3019 lock_ReleaseMutex(&dscp->mx);
3021 cm_EndDirOp(&dirop);
3026 cm_StatusFromAttr(&inStatus, NULL, attrp);
3028 /* try the RPC now */
3029 osi_Log1(afsd_logp, "CALL Symlink scp 0x%p", dscp);
3031 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3035 dirAFSFid.Volume = dscp->fid.volume;
3036 dirAFSFid.Vnode = dscp->fid.vnode;
3037 dirAFSFid.Unique = dscp->fid.unique;
3039 callp = cm_GetRxConn(connp);
3040 code = RXAFS_Symlink(callp, &dirAFSFid, namep, contentsp,
3041 &inStatus, &newAFSFid, &newLinkStatus,
3042 &updatedDirStatus, &volSync);
3043 rx_PutConnection(callp);
3045 } while (cm_Analyze(connp, userp, reqp,
3046 &dscp->fid, &volSync, NULL, NULL, code));
3047 code = cm_MapRPCError(code, reqp);
3050 osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
3052 osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
3055 lock_ObtainWrite(&dirop.scp->dirlock);
3056 dirop.lockType = CM_DIRLOCK_WRITE;
3058 lock_ObtainMutex(&dscp->mx);
3059 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3061 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
3063 lock_ReleaseMutex(&dscp->mx);
3066 if (cm_CheckDirOpForSingleChange(&dirop)) {
3067 newFid.cell = dscp->fid.cell;
3068 newFid.volume = dscp->fid.volume;
3069 newFid.vnode = newAFSFid.Vnode;
3070 newFid.unique = newAFSFid.Unique;
3072 cm_DirCreateEntry(&dirop, namep, &newFid);
3074 cm_BPlusDirCreateEntry(&dirop, namep, &newFid);
3078 cm_EndDirOp(&dirop);
3080 /* now try to create the new dir's entry, too, but be careful to
3081 * make sure that we don't merge in old info. Since we weren't locking
3082 * out any requests during the file's creation, we may have pretty old
3086 newFid.cell = dscp->fid.cell;
3087 newFid.volume = dscp->fid.volume;
3088 newFid.vnode = newAFSFid.Vnode;
3089 newFid.unique = newAFSFid.Unique;
3090 code = cm_GetSCache(&newFid, &scp, userp, reqp);
3092 lock_ObtainMutex(&scp->mx);
3093 if (!cm_HaveCallback(scp)) {
3094 cm_MergeStatus(dscp, scp, &newLinkStatus, &volSync,
3097 lock_ReleaseMutex(&scp->mx);
3098 cm_ReleaseSCache(scp);
3102 /* and return error code */
3106 long cm_RemoveDir(cm_scache_t *dscp, char *namep, cm_user_t *userp,
3113 AFSFetchStatus updatedDirStatus;
3115 struct rx_connection * callp;
3118 /* before starting the RPC, mark that we're changing the directory data,
3119 * so that someone who does a chmod on the dir will wait until our
3122 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
3123 lock_ObtainMutex(&dscp->mx);
3124 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3125 lock_ReleaseMutex(&dscp->mx);
3127 cm_EndDirOp(&dirop);
3132 /* try the RPC now */
3133 osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%p", dscp);
3135 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3139 dirAFSFid.Volume = dscp->fid.volume;
3140 dirAFSFid.Vnode = dscp->fid.vnode;
3141 dirAFSFid.Unique = dscp->fid.unique;
3143 callp = cm_GetRxConn(connp);
3144 code = RXAFS_RemoveDir(callp, &dirAFSFid, namep,
3145 &updatedDirStatus, &volSync);
3146 rx_PutConnection(callp);
3148 } while (cm_Analyze(connp, userp, reqp,
3149 &dscp->fid, &volSync, NULL, NULL, code));
3150 code = cm_MapRPCErrorRmdir(code, reqp);
3153 osi_Log1(afsd_logp, "CALL RemoveDir FAILURE, code 0x%x", code);
3155 osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
3158 lock_ObtainWrite(&dirop.scp->dirlock);
3159 dirop.lockType = CM_DIRLOCK_WRITE;
3161 lock_ObtainMutex(&dscp->mx);
3162 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3164 cm_dnlcRemove(dscp, namep);
3165 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
3167 lock_ReleaseMutex(&dscp->mx);
3170 if (cm_CheckDirOpForSingleChange(&dirop)) {
3171 cm_DirDeleteEntry(&dirop, namep);
3173 cm_BPlusDirDeleteEntry(&dirop, namep);
3177 cm_EndDirOp(&dirop);
3179 /* and return error code */
3183 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
3185 /* grab mutex on contents */
3186 lock_ObtainMutex(&scp->mx);
3188 /* reset the prefetch info */
3189 scp->prefetch.base.LowPart = 0; /* base */
3190 scp->prefetch.base.HighPart = 0;
3191 scp->prefetch.end.LowPart = 0; /* and end */
3192 scp->prefetch.end.HighPart = 0;
3194 /* release mutex on contents */
3195 lock_ReleaseMutex(&scp->mx);
3201 long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
3202 char *newNamep, cm_user_t *userp, cm_req_t *reqp)
3206 AFSFid oldDirAFSFid;
3207 AFSFid newDirAFSFid;
3209 AFSFetchStatus updatedOldDirStatus;
3210 AFSFetchStatus updatedNewDirStatus;
3213 struct rx_connection * callp;
3214 cm_dirOp_t oldDirOp;
3217 cm_dirOp_t newDirOp;
3219 /* before starting the RPC, mark that we're changing the directory data,
3220 * so that someone who does a chmod on the dir will wait until our call
3221 * completes. We do this in vnode order so that we don't deadlock,
3222 * which makes the code a little verbose.
3224 if (oldDscp == newDscp) {
3225 /* check for identical names */
3226 if (strcmp(oldNamep, newNamep) == 0)
3227 return CM_ERROR_RENAME_IDENTICAL;
3230 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3231 lock_ObtainMutex(&oldDscp->mx);
3232 cm_dnlcRemove(oldDscp, oldNamep);
3233 cm_dnlcRemove(oldDscp, newNamep);
3234 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3235 CM_SCACHESYNC_STOREDATA);
3236 lock_ReleaseMutex(&oldDscp->mx);
3238 cm_EndDirOp(&oldDirOp);
3242 /* two distinct dir vnodes */
3244 if (oldDscp->fid.cell != newDscp->fid.cell ||
3245 oldDscp->fid.volume != newDscp->fid.volume)
3246 return CM_ERROR_CROSSDEVLINK;
3248 /* shouldn't happen that we have distinct vnodes for two
3249 * different files, but could due to deliberate attack, or
3250 * stale info. Avoid deadlocks and quit now.
3252 if (oldDscp->fid.vnode == newDscp->fid.vnode)
3253 return CM_ERROR_CROSSDEVLINK;
3255 if (oldDscp->fid.vnode < newDscp->fid.vnode) {
3256 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3257 lock_ObtainMutex(&oldDscp->mx);
3258 cm_dnlcRemove(oldDscp, oldNamep);
3259 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3260 CM_SCACHESYNC_STOREDATA);
3261 lock_ReleaseMutex(&oldDscp->mx);
3263 cm_EndDirOp(&oldDirOp);
3265 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE, &newDirOp);
3266 lock_ObtainMutex(&newDscp->mx);
3267 cm_dnlcRemove(newDscp, newNamep);
3268 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3269 CM_SCACHESYNC_STOREDATA);
3270 lock_ReleaseMutex(&newDscp->mx);
3272 cm_EndDirOp(&newDirOp);
3274 /* cleanup first one */
3275 lock_ObtainMutex(&oldDscp->mx);
3276 cm_SyncOpDone(oldDscp, NULL,
3277 CM_SCACHESYNC_STOREDATA);
3278 lock_ReleaseMutex(&oldDscp->mx);
3279 cm_EndDirOp(&oldDirOp);
3284 /* lock the new vnode entry first */
3285 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE, &newDirOp);
3286 lock_ObtainMutex(&newDscp->mx);
3287 cm_dnlcRemove(newDscp, newNamep);
3288 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3289 CM_SCACHESYNC_STOREDATA);
3290 lock_ReleaseMutex(&newDscp->mx);
3292 cm_EndDirOp(&newDirOp);
3294 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3295 lock_ObtainMutex(&oldDscp->mx);
3296 cm_dnlcRemove(oldDscp, oldNamep);
3297 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3298 CM_SCACHESYNC_STOREDATA);
3299 lock_ReleaseMutex(&oldDscp->mx);
3301 cm_EndDirOp(&oldDirOp);
3303 /* cleanup first one */
3304 lock_ObtainMutex(&newDscp->mx);
3305 cm_SyncOpDone(newDscp, NULL,
3306 CM_SCACHESYNC_STOREDATA);
3307 lock_ReleaseMutex(&newDscp->mx);
3308 cm_EndDirOp(&newDirOp);
3312 } /* two distinct vnodes */
3319 /* try the RPC now */
3320 osi_Log2(afsd_logp, "CALL Rename old scp 0x%p new scp 0x%p",
3323 code = cm_ConnFromFID(&oldDscp->fid, userp, reqp, &connp);
3327 oldDirAFSFid.Volume = oldDscp->fid.volume;
3328 oldDirAFSFid.Vnode = oldDscp->fid.vnode;
3329 oldDirAFSFid.Unique = oldDscp->fid.unique;
3330 newDirAFSFid.Volume = newDscp->fid.volume;
3331 newDirAFSFid.Vnode = newDscp->fid.vnode;
3332 newDirAFSFid.Unique = newDscp->fid.unique;
3334 callp = cm_GetRxConn(connp);
3335 code = RXAFS_Rename(callp, &oldDirAFSFid, oldNamep,
3336 &newDirAFSFid, newNamep,
3337 &updatedOldDirStatus, &updatedNewDirStatus,
3339 rx_PutConnection(callp);
3341 } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
3342 &volSync, NULL, NULL, code));
3343 code = cm_MapRPCError(code, reqp);
3346 osi_Log1(afsd_logp, "CALL Rename FAILURE, code 0x%x", code);
3348 osi_Log0(afsd_logp, "CALL Rename SUCCESS");
3350 /* update the individual stat cache entries for the directories */
3352 lock_ObtainWrite(&oldDirOp.scp->dirlock);
3353 oldDirOp.lockType = CM_DIRLOCK_WRITE;
3355 lock_ObtainMutex(&oldDscp->mx);
3356 cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
3359 cm_MergeStatus(NULL, oldDscp, &updatedOldDirStatus, &volSync,
3360 userp, CM_MERGEFLAG_DIROP);
3361 lock_ReleaseMutex(&oldDscp->mx);
3364 if (cm_CheckDirOpForSingleChange(&oldDirOp)) {
3367 diropCode = cm_BPlusDirLookup(&oldDirOp, oldNamep, &fileFid);
3368 if (diropCode == CM_ERROR_INEXACT_MATCH)
3370 else if (diropCode == EINVAL)
3372 diropCode = cm_DirLookup(&oldDirOp, oldNamep, &fileFid);
3374 if (diropCode == 0) {
3376 diropCode = cm_DirCreateEntry(&oldDirOp, newNamep, &fileFid);
3378 cm_BPlusDirCreateEntry(&oldDirOp, newNamep, &fileFid);
3382 if (diropCode == 0) {
3383 diropCode = cm_DirDeleteEntry(&oldDirOp, oldNamep);
3385 cm_BPlusDirDeleteEntry(&oldDirOp, oldNamep);
3391 cm_EndDirOp(&oldDirOp);
3393 /* and update it for the new one, too, if necessary */
3396 lock_ObtainWrite(&newDirOp.scp->dirlock);
3397 newDirOp.lockType = CM_DIRLOCK_WRITE;
3399 lock_ObtainMutex(&newDscp->mx);
3400 cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
3402 cm_MergeStatus(NULL, newDscp, &updatedNewDirStatus, &volSync,
3403 userp, CM_MERGEFLAG_DIROP);
3404 lock_ReleaseMutex(&newDscp->mx);
3407 /* we only make the local change if we successfully made
3408 the change in the old directory AND there was only one
3409 change in the new directory */
3410 if (diropCode == 0 && cm_CheckDirOpForSingleChange(&newDirOp)) {
3411 cm_DirCreateEntry(&newDirOp, newNamep, &fileFid);
3413 cm_BPlusDirCreateEntry(&newDirOp, newNamep, &fileFid);
3417 cm_EndDirOp(&newDirOp);
3420 /* and return error code */
3424 /* Byte range locks:
3426 The OpenAFS Windows client has to fake byte range locks given no
3427 server side support for such locks. This is implemented as keyed
3428 byte range locks on the cache manager.
3430 Keyed byte range locks:
3432 Each cm_scache_t structure keeps track of a list of keyed locks.
3433 The key for a lock identifies an owner of a set of locks (referred
3434 to as a client). Each key is represented by a value. The set of
3435 key values used within a specific cm_scache_t structure form a
3436 namespace that has a scope of just that cm_scache_t structure. The
3437 same key value can be used with another cm_scache_t structure and
3438 correspond to a completely different client. However it is
3439 advantageous for the SMB or IFS layer to make sure that there is a
3440 1-1 mapping between client and keys over all cm_scache_t objects.
3442 Assume a client C has key Key(C) (although, since the scope of the
3443 key is a cm_scache_t, the key can be Key(C,S), where S is the
3444 cm_scache_t. But assume a 1-1 relation between keys and clients).
3445 A byte range (O,+L) denotes byte addresses (O) through (O+L-1)
3446 inclusive (a.k.a. [O,O+L-1]). The function Key(x) is implemented
3447 through cm_generateKey() function for both SMB and IFS.
3449 The list of locks for a cm_scache_t object S is maintained in
3450 S->fileLocks. The cache manager will set a lock on the AFS file
3451 server in order to assert the locks in S->fileLocks. If only
3452 shared locks are in place for S, then the cache manager will obtain
3453 a LockRead lock, while if there are any exclusive locks, it will
3454 obtain a LockWrite lock. If the exclusive locks are all released
3455 while the shared locks remain, then the cache manager will
3456 downgrade the lock from LockWrite to LockRead. Similarly, if an
3457 exclusive lock is obtained when only shared locks exist, then the
3458 cache manager will try to upgrade the lock from LockRead to
3461 Each lock L owned by client C maintains a key L->key such that
3462 L->key == Key(C), the effective range defined by L->LOffset and
3463 L->LLength such that the range of bytes affected by the lock is
3464 (L->LOffset, +L->LLength), a type maintained in L->LockType which
3465 is either exclusive or shared.
3469 A lock exists iff it is in S->fileLocks for some cm_scache_t
3470 S. Existing locks are in one of the following states: ACTIVE,
3471 WAITLOCK, WAITUNLOCK, LOST, DELETED.
3473 The following sections describe each lock and the associated
3476 1. ACTIVE: A lock L is ACTIVE iff the cache manager has asserted
3477 the lock with the AFS file server. This type of lock can be
3478 exercised by a client to read or write to the locked region (as
3481 1.1 ACTIVE->LOST: When the AFS file server fails to extend a
3482 server lock that was required to assert the lock. Before
3483 marking the lock as lost, the cache manager checks if the file
3484 has changed on the server. If the file has not changed, then
3485 the cache manager will attempt to obtain a new server lock
3486 that is sufficient to assert the client side locks for the
3487 file. If any of these fail, the lock is marked as LOST.
3488 Otherwise, it is left as ACTIVE.
3490 1.2 ACTIVE->DELETED: Lock is released.
3492 2. WAITLOCK: A lock is in a WAITLOCK state if the cache manager
3493 grants the lock but the lock is yet to be asserted with the AFS
3494 file server. Once the file server grants the lock, the state
3495 will transition to an ACTIVE lock.
3497 2.1 WAITLOCK->ACTIVE: The server granted the lock.
3499 2.2 WAITLOCK->DELETED: Lock is abandoned, or timed out during
3502 2.3 WAITLOCK->LOST: One or more locks from this client were
3503 marked as LOST. No further locks will be granted to this
3504 client until all lost locks are removed.
3506 3. WAITUNLOCK: A lock is in a WAITUNLOCK state if the cache manager
3507 receives a request for a lock that conflicts with an existing
3508 ACTIVE or WAITLOCK lock. The lock will be placed in the queue
3509 and will be granted at such time the conflicting locks are
3510 removed, at which point the state will transition to either
3513 3.1 WAITUNLOCK->ACTIVE: The conflicting lock was removed. The
3514 current serverLock is sufficient to assert this lock, or a
3515 sufficient serverLock is obtained.
3517 3.2 WAITUNLOCK->WAITLOCK: The conflicting lock was removed,
3518 however the required serverLock is yet to be asserted with the
3521 3.3 WAITUNLOCK->DELETED: The lock is abandoned, timed out or
3524 3.5 WAITUNLOCK->LOST: One or more locks from this client were
3525 marked as LOST. No further locks will be granted to this
3526 client until all lost locks are removed.
3528 4. LOST: A lock L is LOST if the server lock that was required to
3529 assert the lock could not be obtained or if it could not be
3530 extended, or if other locks by the same client were LOST.
3531 Essentially, once a lock is LOST, the contract between the cache
3532 manager and that specific client is no longer valid.
3534 The cache manager rechecks the server lock once every minute and
3535 extends it as appropriate. If this is not done for 5 minutes,
3536 the AFS file server will release the lock (the 5 minute timeout
3537 is based on current file server code and is fairly arbitrary).
3538 Once released, the lock cannot be re-obtained without verifying
3539 that the contents of the file hasn't been modified since the
3540 time the lock was released. Re-obtaining the lock without
3541 verifying this may lead to data corruption. If the lock can not
3542 be obtained safely, then all active locks for the cm_scache_t
3545 4.1 LOST->DELETED: The lock is released.
3547 5. DELETED: The lock is no longer relevant. Eventually, it will
3548 get removed from the cm_scache_t. In the meantime, it will be
3549 treated as if it does not exist.
3551 5.1 DELETED->not exist: The lock is removed from the
3554 The following are classifications of locks based on their state.
3556 6* A lock L is ACCEPTED if it is ACTIVE or WAITLOCK. These locks
3557 have been accepted by the cache manager, but may or may not have
3558 been granted back to the client.
3560 7* A lock L is QUEUED if it is ACTIVE, WAITLOCK or WAITUNLOCK.
3562 8* A lock L is WAITING if it is WAITLOCK or WAITUNLOCK.
3566 A client C can READ range (Offset,+Length) of a file represented by
3567 cm_scache_t S iff (1):
3569 1. for all _a_ in (Offset,+Length), all of the following is true:
3571 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3572 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3575 1.2 For each LOST lock L in S->fileLocks such that _a_ in
3576 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3579 (When locks are lost on an cm_scache_t, all locks are lost. By
3580 4.2 (below), if there is an exclusive LOST lock, then there
3581 can't be any overlapping ACTIVE locks.)
3583 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
3585 2. for all _a_ in (Offset,+Length), one of the following is true:
3587 2.1 Byte _a_ of S is unowned (as specified in 1.1) AND there
3588 does not exist a LOST lock L such that _a_ in
3589 (L->LOffset,+L->LLength).
3591 2.2 Byte _a_ of S is owned by C under lock L (as specified in
3592 1.2) AND L->LockType is exclusive.
3594 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
3596 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
3599 3.1 If L->LockType is exclusive then there does NOT exist a
3600 ACCEPTED lock M in S->fileLocks such that _a_ in
3601 (M->LOffset,+M->LLength).
3603 (If we count all QUEUED locks then we hit cases such as
3604 cascading waiting locks where the locks later on in the queue
3605 can be granted without compromising file integrity. On the
3606 other hand if only ACCEPTED locks are considered, then locks
3607 that were received earlier may end up waiting for locks that
3608 were received later to be unlocked. The choice of ACCEPTED
3609 locks was made to mimic the Windows byte range lock
3612 3.2 If L->LockType is shared then for each ACCEPTED lock M in
3613 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
3614 M->LockType is shared.
3616 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
3618 4.1 M->key != Key(C)
3620 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
3621 and (M->LOffset,+M->LLength) do not intersect.
3623 (Note: If a client loses a lock, it loses all locks.
3624 Subsequently, it will not be allowed to obtain any more locks
3625 until all existing LOST locks that belong to the client are
3626 released. Once all locks are released by a single client,
3627 there exists no further contract between the client and AFS
3628 about the contents of the file, hence the client can then
3629 proceed to obtain new locks and establish a new contract.
3631 This doesn't quite work as you think it should, because most
3632 applications aren't built to deal with losing locks they
3633 thought they once had. For now, we don't have a good
3634 solution to lost locks.
3636 Also, for consistency reasons, we have to hold off on
3637 granting locks that overlap exclusive LOST locks.)
3639 A client C can only unlock locks L in S->fileLocks which have
3642 The representation and invariants are as follows:
3644 - Each cm_scache_t structure keeps:
3646 - A queue of byte-range locks (cm_scache_t::fileLocks) which
3647 are of type cm_file_lock_t.
3649 - A record of the highest server-side lock that has been
3650 obtained for this object (cm_scache_t::serverLock), which is
3651 one of (-1), LockRead, LockWrite.
3653 - A count of ACCEPTED exclusive and shared locks that are in the
3654 queue (cm_scache_t::sharedLocks and
3655 cm_scache_t::exclusiveLocks)
3657 - Each cm_file_lock_t structure keeps:
3659 - The type of lock (cm_file_lock_t::LockType)
3661 - The key associated with the lock (cm_file_lock_t::key)
3663 - The offset and length of the lock (cm_file_lock_t::LOffset
3664 and cm_file_lock_t::LLength)
3666 - The state of the lock.
3668 - Time of issuance or last successful extension
3670 Semantic invariants:
3672 I1. The number of ACCEPTED locks in S->fileLocks are
3673 (S->sharedLocks + S->exclusiveLocks)
3675 External invariants:
3677 I3. S->serverLock is the lock that we have asserted with the
3678 AFS file server for this cm_scache_t.
3680 I4. S->serverLock == LockRead iff there is at least one ACTIVE
3681 shared lock, but no ACTIVE exclusive locks.
3683 I5. S->serverLock == LockWrite iff there is at least one ACTIVE
3686 I6. If L is a LOST lock, then for each lock M in S->fileLocks,
3687 M->key == L->key IMPLIES M is LOST or DELETED.
3692 #define IS_LOCK_ACTIVE(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == 0)
3694 #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)
3696 #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)
3698 #define IS_LOCK_LOST(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_LOST)
3700 #define IS_LOCK_DELETED(lockp) (((lockp)->flags & CM_FILELOCK_FLAG_DELETED) == CM_FILELOCK_FLAG_DELETED)
3703 #define IS_LOCK_ACCEPTED(lockp) (IS_LOCK_ACTIVE(lockp) || IS_LOCK_WAITLOCK(lockp))
3706 #define IS_LOCK_CLIENTONLY(lockp) ((((lockp)->scp->flags & CM_SCACHEFLAG_RO) == CM_SCACHEFLAG_RO) || (((lockp)->flags & CM_FILELOCK_FLAG_CLIENTONLY) == CM_FILELOCK_FLAG_CLIENTONLY))
3709 #define INTERSECT_RANGE(r1,r2) (((r2).offset+(r2).length) > (r1).offset && ((r1).offset +(r1).length) > (r2).offset)
3712 #define CONTAINS_RANGE(r1,r2) (((r2).offset+(r2).length) <= ((r1).offset+(r1).length) && (r1).offset <= (r2).offset)
3714 #if defined(VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS) && !defined(LOCK_TESTING)
3715 #define SCP_SUPPORTS_BRLOCKS(scp) ((scp)->cbServerp && ((scp)->cbServerp->capabilities & VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS))
3717 #define SCP_SUPPORTS_BRLOCKS(scp) (1)
3720 #define SERVERLOCKS_ENABLED(scp) (!((scp)->flags & CM_SCACHEFLAG_RO) && cm_enableServerLocks && SCP_SUPPORTS_BRLOCKS(scp))
3722 #if defined(VICED_CAPABILITY_WRITELOCKACL)
3723 #define SCP_SUPPORTS_WRITELOCKACL(scp) ((scp)->cbServerp && ((scp->cbServerp->capabilities & VICED_CAPABILITY_WRITELOCKACL)))
3725 #define SCP_SUPPORTS_WRITELOCKACL(scp) (0)
3727 /* This should really be defined in any build that this code is being
3729 #error VICED_CAPABILITY_WRITELOCKACL not defined.
3732 static void cm_LockRangeSubtract(cm_range_t * pos, const cm_range_t * neg)
3734 afs_int64 int_begin;
3737 int_begin = MAX(pos->offset, neg->offset);
3738 int_end = MIN(pos->offset+pos->length, neg->offset+neg->length);
3740 if (int_begin < int_end) {
3741 if (int_begin == pos->offset) {
3742 pos->length = pos->offset + pos->length - int_end;
3743 pos->offset = int_end;
3744 } else if (int_end == pos->offset + pos->length) {
3745 pos->length = int_begin - pos->offset;
3748 /* We only subtract ranges if the resulting range is
3749 contiguous. If we try to support non-contigous ranges, we
3750 aren't actually improving performance. */
3754 /* Called with scp->mx held. Returns 0 if all is clear to read the
3755 specified range by the client identified by key.
3757 long cm_LockCheckRead(cm_scache_t *scp,
3758 LARGE_INTEGER LOffset,
3759 LARGE_INTEGER LLength,
3762 #ifndef ADVISORY_LOCKS
3764 cm_file_lock_t *fileLock;
3768 int substract_ranges = FALSE;
3770 range.offset = LOffset.QuadPart;
3771 range.length = LLength.QuadPart;
3775 1. for all _a_ in (Offset,+Length), all of the following is true:
3777 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3778 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3781 1.2 For each LOST lock L in S->fileLocks such that _a_ in
3782 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3787 lock_ObtainRead(&cm_scacheLock);
3789 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
3791 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
3793 if (INTERSECT_RANGE(range, fileLock->range)) {
3794 if (IS_LOCK_ACTIVE(fileLock)) {
3795 if (fileLock->key == key) {
3797 /* If there is an active lock for this client, it
3798 is safe to substract ranges.*/
3799 cm_LockRangeSubtract(&range, &fileLock->range);
3800 substract_ranges = TRUE;
3802 if (fileLock->lockType != LockRead) {
3803 code = CM_ERROR_LOCK_CONFLICT;
3807 /* even if the entire range is locked for reading,
3808 we still can't grant the lock at this point
3809 because the client may have lost locks. That
3810 is, unless we have already seen an active lock
3811 belonging to the client, in which case there
3812 can't be any lost locks for this client. */
3813 if (substract_ranges)
3814 cm_LockRangeSubtract(&range, &fileLock->range);
3816 } else if (IS_LOCK_LOST(fileLock) &&
3817 (fileLock->key == key || fileLock->lockType == LockWrite)) {
3818 code = CM_ERROR_BADFD;
3824 lock_ReleaseRead(&cm_scacheLock);
3826 osi_Log4(afsd_logp, "cm_LockCheckRead scp 0x%x offset %d length %d code 0x%x",
3827 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
3838 /* Called with scp->mx held. Returns 0 if all is clear to write the
3839 specified range by the client identified by key.
3841 long cm_LockCheckWrite(cm_scache_t *scp,
3842 LARGE_INTEGER LOffset,
3843 LARGE_INTEGER LLength,
3846 #ifndef ADVISORY_LOCKS
3848 cm_file_lock_t *fileLock;
3853 range.offset = LOffset.QuadPart;
3854 range.length = LLength.QuadPart;
3857 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
3859 2. for all _a_ in (Offset,+Length), one of the following is true:
3861 2.1 Byte _a_ of S is unowned AND there does not exist a LOST
3862 lock L such that _a_ in (L->LOffset,+L->LLength).
3864 2.2 Byte _a_ of S is owned by C under lock L AND L->LockType is
3868 lock_ObtainRead(&cm_scacheLock);
3870 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
3872 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
3874 if (INTERSECT_RANGE(range, fileLock->range)) {
3875 if (IS_LOCK_ACTIVE(fileLock)) {
3876 if (fileLock->key == key) {
3877 if (fileLock->lockType == LockWrite) {
3879 /* if there is an active lock for this client, it
3880 is safe to substract ranges */
3881 cm_LockRangeSubtract(&range, &fileLock->range);
3883 code = CM_ERROR_LOCK_CONFLICT;
3887 code = CM_ERROR_LOCK_CONFLICT;
3890 } else if (IS_LOCK_LOST(fileLock)) {
3891 code = CM_ERROR_BADFD;
3897 lock_ReleaseRead(&cm_scacheLock);
3899 osi_Log4(afsd_logp, "cm_LockCheckWrite scp 0x%x offset %d length %d code 0x%x",
3900 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
3912 static void cm_LockMarkSCacheLost(cm_scache_t * scp);
3914 /* Called with cm_scacheLock write locked */
3915 static cm_file_lock_t * cm_GetFileLock(void) {
3918 l = (cm_file_lock_t *) cm_freeFileLocks;
3920 osi_QRemove(&cm_freeFileLocks, &l->q);
3922 l = malloc(sizeof(cm_file_lock_t));
3923 osi_assertx(l, "null cm_file_lock_t");
3926 memset(l, 0, sizeof(cm_file_lock_t));
3931 /* Called with cm_scacheLock write locked */
3932 static void cm_PutFileLock(cm_file_lock_t *l) {
3933 osi_QAdd(&cm_freeFileLocks, &l->q);
3936 /* called with scp->mx held. May release it during processing, but
3937 leaves it held on exit. */
3938 long cm_IntSetLock(cm_scache_t * scp, cm_user_t * userp, int lockType,
3944 struct rx_connection * callp;
3947 tfid.Volume = scp->fid.volume;
3948 tfid.Vnode = scp->fid.vnode;
3949 tfid.Unique = scp->fid.unique;
3952 osi_Log2(afsd_logp, "CALL SetLock scp 0x%p for lock %d", scp, lockType);
3954 lock_ReleaseMutex(&scp->mx);
3957 code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
3961 callp = cm_GetRxConn(connp);
3962 code = RXAFS_SetLock(callp, &tfid, lockType,
3964 rx_PutConnection(callp);
3966 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
3969 code = cm_MapRPCError(code, reqp);
3971 osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
3973 osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
3976 lock_ObtainMutex(&scp->mx);
3981 /* called with scp->mx held. Releases it during processing */
3982 long cm_IntReleaseLock(cm_scache_t * scp, cm_user_t * userp,
3988 struct rx_connection * callp;
3991 tfid.Volume = scp->fid.volume;
3992 tfid.Vnode = scp->fid.vnode;
3993 tfid.Unique = scp->fid.unique;
3996 lock_ReleaseMutex(&scp->mx);
3998 osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
4001 code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4005 callp = cm_GetRxConn(connp);
4006 code = RXAFS_ReleaseLock(callp, &tfid, &volSync);
4007 rx_PutConnection(callp);
4009 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
4011 code = cm_MapRPCError(code, reqp);
4014 "CALL ReleaseLock FAILURE, code 0x%x", code);
4017 "CALL ReleaseLock SUCCESS");
4019 lock_ObtainMutex(&scp->mx);
4024 /* called with scp->mx held. May release it during processing, but
4025 will exit with lock held.
4029 - 0 if the user has permission to get the specified lock for the scp
4031 - CM_ERROR_NOACCESS if not
4033 Any other error from cm_SyncOp will be sent down untranslated.
4035 If CM_ERROR_NOACCESS is returned and lock_type is LockRead, then
4036 phas_insert (if non-NULL) will receive a boolean value indicating
4037 whether the user has INSERT permission or not.
4039 long cm_LockCheckPerms(cm_scache_t * scp,
4046 long code = 0, code2 = 0;
4048 /* lock permissions are slightly tricky because of the 'i' bit.
4049 If the user has PRSFS_LOCK, she can read-lock the file. If the
4050 user has PRSFS_WRITE, she can write-lock the file. However, if
4051 the user has PRSFS_INSERT, then she can write-lock new files,
4052 but not old ones. Since we don't have information about
4053 whether a file is new or not, we assume that if the user owns
4054 the scp, then she has the permissions that are granted by
4057 osi_Log3(afsd_logp, "cm_LockCheckPerms for scp[0x%p] type[%d] user[0x%p]",
4058 scp, lock_type, userp);
4060 if (lock_type == LockRead)
4061 rights |= PRSFS_LOCK;
4062 else if (lock_type == LockWrite)
4063 rights |= PRSFS_WRITE | PRSFS_LOCK;
4066 osi_assertx(FALSE, "invalid lock type");
4071 *phas_insert = FALSE;
4073 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
4074 CM_SCACHESYNC_GETSTATUS |
4075 CM_SCACHESYNC_NEEDCALLBACK);
4077 if (phas_insert && scp->creator == userp) {
4079 /* If this file was created by the user, then we check for
4080 PRSFS_INSERT. If the file server is recent enough, then
4081 this should be sufficient for her to get a write-lock (but
4082 not necessarily a read-lock). VICED_CAPABILITY_WRITELOCKACL
4083 indicates whether a file server supports getting write
4084 locks when the user only has PRSFS_INSERT.
4086 If the file was not created by the user we skip the check
4087 because the INSERT bit will not apply to this user even
4091 code2 = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
4092 CM_SCACHESYNC_GETSTATUS |
4093 CM_SCACHESYNC_NEEDCALLBACK);
4095 if (code2 == CM_ERROR_NOACCESS) {
4096 osi_Log0(afsd_logp, "cm_LockCheckPerms user has no INSERT bits");
4098 *phas_insert = TRUE;
4099 osi_Log0(afsd_logp, "cm_LockCheckPerms user has INSERT bits");
4103 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
4105 osi_Log1(afsd_logp, "cm_LockCheckPerms returning code %d", code);
4110 /* called with scp->mx held */
4111 long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
4112 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
4114 int allowWait, cm_user_t *userp, cm_req_t *reqp,
4115 cm_file_lock_t **lockpp)
4118 int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
4119 cm_file_lock_t *fileLock;
4122 int wait_unlock = FALSE;
4123 int force_client_lock = FALSE;
4125 osi_Log4(afsd_logp, "cm_Lock scp 0x%x type 0x%x offset %d length %d",
4126 scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
4127 osi_Log3(afsd_logp, "... allowWait %d key 0x%x:%x", allowWait,
4128 (unsigned long)(key >> 32), (unsigned long)(key & 0xffffffff));
4131 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
4133 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
4136 3.1 If L->LockType is exclusive then there does NOT exist a
4137 ACCEPTED lock M in S->fileLocks such that _a_ in
4138 (M->LOffset,+M->LLength).
4140 3.2 If L->LockType is shared then for each ACCEPTED lock M in
4141 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
4142 M->LockType is shared.
4144 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
4146 4.1 M->key != Key(C)
4148 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
4149 and (M->LOffset,+M->LLength) do not intersect.
4152 range.offset = LOffset.QuadPart;
4153 range.length = LLength.QuadPart;
4155 lock_ObtainRead(&cm_scacheLock);
4157 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4159 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4161 if (IS_LOCK_LOST(fileLock)) {
4162 if (fileLock->key == key) {
4163 code = CM_ERROR_BADFD;
4165 } else if (fileLock->lockType == LockWrite && INTERSECT_RANGE(range, fileLock->range)) {
4166 code = CM_ERROR_WOULDBLOCK;
4172 /* we don't need to check for deleted locks here since deleted
4173 locks are dequeued from scp->fileLocks */
4174 if (IS_LOCK_ACCEPTED(fileLock) &&
4175 INTERSECT_RANGE(range, fileLock->range)) {
4177 if ((sLockType & LOCKING_ANDX_SHARED_LOCK) == 0 ||
4178 fileLock->lockType != LockRead) {
4180 code = CM_ERROR_WOULDBLOCK;
4186 lock_ReleaseRead(&cm_scacheLock);
4188 if (code == 0 && SERVERLOCKS_ENABLED(scp)) {
4189 if (Which == scp->serverLock ||
4190 (Which == LockRead && scp->serverLock == LockWrite)) {
4194 /* we already have the lock we need */
4195 osi_Log3(afsd_logp, " we already have the correct lock. exclusives[%d], shared[%d], serverLock[%d]",
4196 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4198 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4200 /* special case: if we don't have permission to read-lock
4201 the file, then we force a clientside lock. This is to
4202 compensate for applications that obtain a read-lock for
4203 reading files off of directories that don't grant
4204 read-locks to the user. */
4205 if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4207 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4208 osi_Log0(afsd_logp, " User has no read-lock perms, but has INSERT perms.");
4211 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4212 force_client_lock = TRUE;
4216 } else if ((scp->exclusiveLocks > 0) ||
4217 (scp->sharedLocks > 0 && scp->serverLock != LockRead)) {
4220 /* We are already waiting for some other lock. We should
4221 wait for the daemon to catch up instead of generating a
4222 flood of SetLock calls. */
4223 osi_Log3(afsd_logp, " already waiting for other lock. exclusives[%d], shared[%d], serverLock[%d]",
4224 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4226 /* see if we have permission to create the lock in the
4228 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4230 code = CM_ERROR_WOULDBLOCK;
4231 else if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4233 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4235 " User has no read-lock perms, but has INSERT perms.");
4236 code = CM_ERROR_WOULDBLOCK;
4239 " User has no read-lock perms. Forcing client-side lock");
4240 force_client_lock = TRUE;
4244 /* leave any other codes as-is */
4248 int check_data_version = FALSE;
4251 /* first check if we have permission to elevate or obtain
4253 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4255 if (code == CM_ERROR_NOACCESS && Which == LockRead &&
4256 (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp))) {
4257 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4258 force_client_lock = TRUE;
4263 /* has_insert => (Which == LockRead, code == CM_ERROR_NOACCESS) */
4265 if (scp->serverLock == LockRead && Which == LockWrite) {
4267 /* We want to escalate the lock to a LockWrite.
4268 * Unfortunately that's not really possible without
4269 * letting go of the current lock. But for now we do
4273 " attempting to UPGRADE from LockRead to LockWrite.");
4275 " dataVersion on scp: %I64d", scp->dataVersion);
4277 /* we assume at this point (because scp->serverLock
4278 was valid) that we had a valid server lock. */
4279 scp->lockDataVersion = scp->dataVersion;
4280 check_data_version = TRUE;
4282 code = cm_IntReleaseLock(scp, userp, reqp);
4285 /* We couldn't release the lock */
4288 scp->serverLock = -1;
4292 /* We need to obtain a server lock of type Which in order
4293 * to assert this file lock */
4294 #ifndef AGGRESSIVE_LOCKS
4297 newLock = LockWrite;
4300 code = cm_IntSetLock(scp, userp, newLock, reqp);
4302 #ifdef AGGRESSIVE_LOCKS
4303 if ((code == CM_ERROR_WOULDBLOCK ||
4304 code == CM_ERROR_NOACCESS) && newLock != Which) {
4305 /* we wanted LockRead. We tried LockWrite. Now try
4310 osi_assertx(newLock == LockRead, "lock type not read");
4312 code = cm_IntSetLock(scp, userp, newLock, reqp);
4316 if (code == CM_ERROR_NOACCESS) {
4317 if (Which == LockRead) {
4318 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4320 /* We requested a read-lock, but we have permission to
4321 * get a write-lock. Try that */
4323 tcode = cm_LockCheckPerms(scp, LockWrite, userp, reqp, NULL);
4326 newLock = LockWrite;
4328 osi_Log0(afsd_logp, " User has 'i' perms and the request was for a LockRead. Trying to get a LockWrite instead");
4330 code = cm_IntSetLock(scp, userp, newLock, reqp);
4333 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4334 force_client_lock = TRUE;
4336 } else if (Which == LockWrite &&
4337 scp->creator == userp && !SCP_SUPPORTS_WRITELOCKACL(scp)) {
4340 /* Special case: if the lock request was for a
4341 * LockWrite and the user owns the file and we weren't
4342 * allowed to obtain the serverlock, we either lost a
4343 * race (the permissions changed from under us), or we
4344 * have 'i' bits, but we aren't allowed to lock the
4347 /* check if we lost a race... */
4348 tcode = cm_LockCheckPerms(scp, Which, userp, reqp, NULL);
4351 osi_Log0(afsd_logp, " User has 'i' perms but can't obtain write locks. Using client-side locks.");
4352 force_client_lock = TRUE;
4357 if (code == 0 && check_data_version &&
4358 scp->dataVersion != scp->lockDataVersion) {
4359 /* We lost a race. Although we successfully obtained
4360 * a lock, someone modified the file in between. The
4361 * locks have all been technically lost. */
4364 " Data version mismatch while upgrading lock.");
4366 " Data versions before=%I64d, after=%I64d",
4367 scp->lockDataVersion,
4370 " Releasing stale lock for scp 0x%x", scp);
4372 code = cm_IntReleaseLock(scp, userp, reqp);
4374 scp->serverLock = -1;
4376 code = CM_ERROR_INVAL;
4377 } else if (code == 0) {
4378 scp->serverLock = newLock;
4379 scp->lockDataVersion = scp->dataVersion;
4383 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4384 scp->serverLock == -1) {
4385 /* Oops. We lost the lock. */
4386 cm_LockMarkSCacheLost(scp);
4389 } else if (code == 0) { /* server locks not enabled */
4391 " Skipping server lock for scp");
4396 if (code != 0 && !force_client_lock) {
4397 /* Special case error translations
4399 Applications don't expect certain errors from a
4400 LockFile/UnlockFile call. We need to translate some error
4401 code to codes that apps expect and handle. */
4403 /* We shouldn't actually need to handle this case since we
4404 simulate locks for RO scps anyway. */
4405 if (code == CM_ERROR_READONLY) {
4406 osi_Log0(afsd_logp, " Reinterpreting CM_ERROR_READONLY as CM_ERROR_NOACCESS");
4407 code = CM_ERROR_NOACCESS;
4411 if (code == 0 || (code == CM_ERROR_WOULDBLOCK && allowWait) ||
4412 force_client_lock) {
4414 /* clear the error if we are forcing a client lock, so we
4415 don't get confused later. */
4416 if (force_client_lock && code != CM_ERROR_WOULDBLOCK)
4419 lock_ObtainWrite(&cm_scacheLock);
4420 fileLock = cm_GetFileLock();
4421 lock_ReleaseWrite(&cm_scacheLock);
4423 fileLock->fid = scp->fid;
4425 fileLock->key = key;
4426 fileLock->lockType = Which;
4428 fileLock->userp = userp;
4429 fileLock->range = range;
4430 fileLock->flags = (code == 0 ? 0 :
4432 CM_FILELOCK_FLAG_WAITUNLOCK :
4433 CM_FILELOCK_FLAG_WAITLOCK));
4435 if (force_client_lock || !SERVERLOCKS_ENABLED(scp))
4436 fileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
4438 fileLock->lastUpdate = (code == 0 && !force_client_lock) ? time(NULL) : 0;
4440 lock_ObtainWrite(&cm_scacheLock);
4441 osi_QAddT(&scp->fileLocksH, &scp->fileLocksT, &fileLock->fileq);
4442 cm_HoldSCacheNoLock(scp);
4443 fileLock->scp = scp;
4444 osi_QAdd(&cm_allFileLocks, &fileLock->q);
4445 lock_ReleaseWrite(&cm_scacheLock);
4451 if (IS_LOCK_CLIENTONLY(fileLock)) {
4453 } else if (IS_LOCK_ACCEPTED(fileLock)) {
4454 if (Which == LockRead)
4457 scp->exclusiveLocks++;
4461 "cm_Lock Lock added 0x%p flags 0x%x to scp [0x%p]",
4462 fileLock, fileLock->flags, scp);
4464 " exclusives[%d] shared[%d] client[%d] serverLock[%d]",
4465 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4466 (int)(signed char) scp->serverLock);
4469 "cm_Lock Rejecting lock (code = 0x%x)", code);
4475 static int cm_KeyEquals(cm_key_t k1, cm_key_t k2, int flags);
4477 /* Called with scp->mx held */
4478 long cm_UnlockByKey(cm_scache_t * scp,
4485 cm_file_lock_t *fileLock;
4486 osi_queue_t *q, *qn;
4489 osi_Log4(afsd_logp, "cm_UnlockByKey scp 0x%p key 0x%x:%x flags=0x%x",
4491 (unsigned long)(key >> 32),
4492 (unsigned long)(key & 0xffffffff),
4495 lock_ObtainWrite(&cm_scacheLock);
4497 for (q = scp->fileLocksH; q; q = qn) {
4500 fileLock = (cm_file_lock_t *)
4501 ((char *) q - offsetof(cm_file_lock_t, fileq));
4504 osi_Log4(afsd_logp, " Checking lock[0x%x] range[%d,+%d] type[%d]",
4506 (unsigned long) fileLock->range.offset,
4507 (unsigned long) fileLock->range.length,
4508 fileLock->lockType);
4509 osi_Log3(afsd_logp, " key[0x%x:%x] flags[0x%x]",
4510 (unsigned long)(fileLock->key >> 32),
4511 (unsigned long)(fileLock->key & 0xffffffff),
4514 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
4515 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
4516 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4518 fileLock->fid.volume,
4519 fileLock->fid.vnode,
4520 fileLock->fid.unique);
4521 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4522 fileLock->scp->fid.cell,
4523 fileLock->scp->fid.volume,
4524 fileLock->scp->fid.vnode,
4525 fileLock->scp->fid.unique);
4526 osi_assertx(FALSE, "invalid fid value");
4530 if (!IS_LOCK_DELETED(fileLock) &&
4531 cm_KeyEquals(fileLock->key, key, flags)) {
4532 osi_Log3(afsd_logp, "...Unlock range [%d,+%d] type %d",
4533 fileLock->range.offset,
4534 fileLock->range.length,
4535 fileLock->lockType);
4537 if (scp->fileLocksT == q)
4538 scp->fileLocksT = osi_QPrev(q);
4539 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
4541 if (IS_LOCK_CLIENTONLY(fileLock)) {
4543 } else if (IS_LOCK_ACCEPTED(fileLock)) {
4544 if (fileLock->lockType == LockRead)
4547 scp->exclusiveLocks--;
4550 fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
4552 cm_ReleaseUser(fileLock->userp);
4553 cm_ReleaseSCacheNoLock(scp);
4555 fileLock->userp = NULL;
4556 fileLock->scp = NULL;
4562 lock_ReleaseWrite(&cm_scacheLock);
4564 if (n_unlocks == 0) {
4565 osi_Log0(afsd_logp, "cm_UnlockByKey no locks found");
4566 osi_Log3(afsd_logp, " Leaving scp with exclusives[%d], shared[%d], serverLock[%d]",
4567 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4572 osi_Log1(afsd_logp, "cm_UnlockByKey done with %d locks", n_unlocks);
4574 osi_assertx(scp->sharedLocks >= 0, "scp->sharedLocks < 0");
4575 osi_assertx(scp->exclusiveLocks >= 0, "scp->exclusiveLocks < 0");
4576 osi_assertx(scp->clientLocks >= 0, "scp->clientLocks < 0");
4578 if (!SERVERLOCKS_ENABLED(scp)) {
4579 osi_Log0(afsd_logp, " Skipping server lock for scp");
4583 /* Ideally we would go through the rest of the locks to determine
4584 * if one or more locks that were formerly in WAITUNLOCK can now
4585 * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
4586 * scp->sharedLocks accordingly. However, the retrying of locks
4587 * in that manner is done cm_RetryLock() manually.
4590 if (scp->serverLock == LockWrite &&
4591 scp->exclusiveLocks == 0 &&
4592 scp->sharedLocks > 0) {
4594 /* The serverLock should be downgraded to LockRead */
4595 osi_Log0(afsd_logp, " DOWNGRADE lock from LockWrite to LockRead");
4597 /* since scp->serverLock looked sane, we are going to assume
4598 that we have a valid server lock. */
4599 scp->lockDataVersion = scp->dataVersion;
4600 osi_Log1(afsd_logp, " dataVersion on scp = %I64d", scp->dataVersion);
4602 code = cm_IntReleaseLock(scp, userp, reqp);
4605 /* so we couldn't release it. Just let the lock be for now */
4609 scp->serverLock = -1;
4612 code = cm_IntSetLock(scp, userp, LockRead, reqp);
4614 if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
4615 scp->serverLock = LockRead;
4616 } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
4617 /* We lost a race condition. Although we have a valid
4618 lock on the file, the data has changed and essentially
4619 we have lost the lock we had during the transition. */
4621 osi_Log0(afsd_logp, "Data version mismatch during lock downgrade");
4622 osi_Log2(afsd_logp, " Data versions before=%I64d, after=%I64d",
4623 scp->lockDataVersion,
4626 code = cm_IntReleaseLock(scp, userp, reqp);
4628 code = CM_ERROR_INVAL;
4629 scp->serverLock = -1;
4633 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4634 (scp->serverLock == -1)) {
4636 cm_LockMarkSCacheLost(scp);
4639 /* failure here has no bearing on the return value of
4643 } else if (scp->serverLock != (-1) &&
4644 scp->exclusiveLocks == 0 &&
4645 scp->sharedLocks == 0) {
4646 /* The serverLock should be released entirely */
4648 code = cm_IntReleaseLock(scp, userp, reqp);
4651 scp->serverLock = (-1);
4656 osi_Log1(afsd_logp, "cm_UnlockByKey code 0x%x", code);
4657 osi_Log4(afsd_logp, " Leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
4658 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4659 (int)(signed char) scp->serverLock);
4664 long cm_Unlock(cm_scache_t *scp,
4665 unsigned char sLockType,
4666 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
4672 int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
4673 cm_file_lock_t *fileLock;
4675 int release_userp = FALSE;
4677 osi_Log4(afsd_logp, "cm_Unlock scp 0x%p type 0x%x offset %d length %d",
4678 scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
4679 osi_Log2(afsd_logp, "... key 0x%x:%x",
4680 (unsigned long) (key >> 32), (unsigned long) (key & 0xffffffff));
4682 lock_ObtainRead(&cm_scacheLock);
4684 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4685 fileLock = (cm_file_lock_t *)
4686 ((char *) q - offsetof(cm_file_lock_t, fileq));
4689 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
4690 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
4691 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4693 fileLock->fid.volume,
4694 fileLock->fid.vnode,
4695 fileLock->fid.unique);
4696 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4697 fileLock->scp->fid.cell,
4698 fileLock->scp->fid.volume,
4699 fileLock->scp->fid.vnode,
4700 fileLock->scp->fid.unique);
4701 osi_assertx(FALSE, "invalid fid value");
4704 if (!IS_LOCK_DELETED(fileLock) &&
4705 fileLock->key == key &&
4706 fileLock->range.offset == LOffset.QuadPart &&
4707 fileLock->range.length == LLength.QuadPart) {
4713 osi_Log0(afsd_logp, "cm_Unlock lock not found; failure");
4715 lock_ReleaseRead(&cm_scacheLock);
4717 /* The lock didn't exist anyway. *shrug* */
4721 lock_ReleaseRead(&cm_scacheLock);
4723 /* discard lock record */
4724 lock_ObtainWrite(&cm_scacheLock);
4725 if (scp->fileLocksT == q)
4726 scp->fileLocksT = osi_QPrev(q);
4727 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
4730 * Don't delete it here; let the daemon delete it, to simplify
4731 * the daemon's traversal of the list.
4734 if (IS_LOCK_CLIENTONLY(fileLock)) {
4736 } else if (IS_LOCK_ACCEPTED(fileLock)) {
4737 if (fileLock->lockType == LockRead)
4740 scp->exclusiveLocks--;
4743 fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
4744 if (userp != NULL) {
4745 cm_ReleaseUser(fileLock->userp);
4747 userp = fileLock->userp;
4748 release_userp = TRUE;
4750 fileLock->userp = NULL;
4751 cm_ReleaseSCacheNoLock(scp);
4752 fileLock->scp = NULL;
4753 lock_ReleaseWrite(&cm_scacheLock);
4755 if (!SERVERLOCKS_ENABLED(scp)) {
4756 osi_Log0(afsd_logp, " Skipping server locks for scp");
4760 /* Ideally we would go through the rest of the locks to determine
4761 * if one or more locks that were formerly in WAITUNLOCK can now
4762 * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
4763 * scp->sharedLocks accordingly. However, the retrying of locks
4764 * in that manner is done cm_RetryLock() manually.
4767 if (scp->serverLock == LockWrite &&
4768 scp->exclusiveLocks == 0 &&
4769 scp->sharedLocks > 0) {
4771 /* The serverLock should be downgraded to LockRead */
4772 osi_Log0(afsd_logp, " DOWNGRADE lock from LockWrite to LockRead");
4774 /* Since we already had a lock, we assume that there is a
4775 valid server lock. */
4776 scp->lockDataVersion = scp->dataVersion;
4777 osi_Log1(afsd_logp, " dataVersion on scp is %I64d", scp->dataVersion);
4779 /* before we downgrade, make sure that we have enough
4780 permissions to get the read lock. */
4781 code = cm_LockCheckPerms(scp, LockRead, userp, reqp, NULL);
4784 osi_Log0(afsd_logp, " SKIPPING downgrade because user doesn't have perms to get downgraded lock");
4790 code = cm_IntReleaseLock(scp, userp, reqp);
4793 /* so we couldn't release it. Just let the lock be for now */
4797 scp->serverLock = -1;
4800 code = cm_IntSetLock(scp, userp, LockRead, reqp);
4802 if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
4803 scp->serverLock = LockRead;
4804 } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
4805 /* Lost a race. We obtained a new lock, but that is
4806 meaningless since someone modified the file
4810 "Data version mismatch while downgrading lock");
4812 " Data versions before=%I64d, after=%I64d",
4813 scp->lockDataVersion,
4816 code = cm_IntReleaseLock(scp, userp, reqp);
4818 scp->serverLock = -1;
4819 code = CM_ERROR_INVAL;
4823 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4824 (scp->serverLock == -1)) {
4826 cm_LockMarkSCacheLost(scp);
4829 /* failure here has no bearing on the return value of
4833 } else if (scp->serverLock != (-1) &&
4834 scp->exclusiveLocks == 0 &&
4835 scp->sharedLocks == 0) {
4836 /* The serverLock should be released entirely */
4838 code = cm_IntReleaseLock(scp, userp, reqp);
4841 scp->serverLock = (-1);
4846 cm_ReleaseUser(userp);
4850 osi_Log1(afsd_logp, "cm_Unlock code 0x%x", code);
4851 osi_Log4(afsd_logp, " leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
4852 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4853 (int)(signed char) scp->serverLock);
4858 /* called with scp->mx held */
4859 static void cm_LockMarkSCacheLost(cm_scache_t * scp)
4861 cm_file_lock_t *fileLock;
4864 osi_Log1(afsd_logp, "cm_LockMarkSCacheLost scp 0x%x", scp);
4867 /* With the current code, we can't lose a lock on a RO scp */
4868 osi_assertx(!(scp->flags & CM_SCACHEFLAG_RO), "CM_SCACHEFLAG_RO unexpected");
4871 /* cm_scacheLock needed because we are modifying fileLock->flags */
4872 lock_ObtainWrite(&cm_scacheLock);
4874 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4876 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4878 if (IS_LOCK_ACTIVE(fileLock) &&
4879 !IS_LOCK_CLIENTONLY(fileLock)) {
4880 if (fileLock->lockType == LockRead)
4883 scp->exclusiveLocks--;
4885 fileLock->flags |= CM_FILELOCK_FLAG_LOST;
4889 scp->serverLock = -1;
4890 scp->lockDataVersion = -1;
4891 lock_ReleaseWrite(&cm_scacheLock);
4894 /* Called with no relevant locks held */
4895 void cm_CheckLocks()
4897 osi_queue_t *q, *nq;
4898 cm_file_lock_t *fileLock;
4904 struct rx_connection * callp;
4909 lock_ObtainWrite(&cm_scacheLock);
4911 cm_lockRefreshCycle++;
4913 osi_Log1(afsd_logp, "cm_CheckLocks starting lock check cycle %d", cm_lockRefreshCycle);
4915 for (q = cm_allFileLocks; q; q = nq) {
4916 fileLock = (cm_file_lock_t *) q;
4920 if (IS_LOCK_DELETED(fileLock)) {
4922 osi_QRemove(&cm_allFileLocks, q);
4923 cm_PutFileLock(fileLock);
4925 } else if (IS_LOCK_ACTIVE(fileLock) && !IS_LOCK_CLIENTONLY(fileLock)) {
4927 /* Server locks must have been enabled for us to have
4928 received an active non-client-only lock. */
4929 osi_assertx(cm_enableServerLocks, "!cm_enableServerLocks");
4931 scp = fileLock->scp;
4932 osi_assertx(scp != NULL, "null cm_scache_t");
4934 cm_HoldSCacheNoLock(scp);
4937 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
4938 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
4939 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4941 fileLock->fid.volume,
4942 fileLock->fid.vnode,
4943 fileLock->fid.unique);
4944 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4945 fileLock->scp->fid.cell,
4946 fileLock->scp->fid.volume,
4947 fileLock->scp->fid.vnode,
4948 fileLock->scp->fid.unique);
4949 osi_assertx(FALSE, "invalid fid");
4952 /* Server locks are extended once per scp per refresh
4954 if (scp->lastRefreshCycle != cm_lockRefreshCycle) {
4956 int scp_done = FALSE;
4958 osi_Log1(afsd_logp, "cm_CheckLocks Updating scp 0x%x", scp);
4960 lock_ReleaseWrite(&cm_scacheLock);
4961 lock_ObtainMutex(&scp->mx);
4963 /* did the lock change while we weren't holding the lock? */
4964 if (!IS_LOCK_ACTIVE(fileLock))
4965 goto post_syncopdone;
4967 code = cm_SyncOp(scp, NULL, fileLock->userp, &req, 0,
4968 CM_SCACHESYNC_NEEDCALLBACK
4969 | CM_SCACHESYNC_GETSTATUS
4970 | CM_SCACHESYNC_LOCK);
4974 "cm_CheckLocks SyncOp failure code 0x%x", code);
4975 goto post_syncopdone;
4978 /* cm_SyncOp releases scp->mx during which the lock
4979 may get released. */
4980 if (!IS_LOCK_ACTIVE(fileLock))
4981 goto pre_syncopdone;
4983 if (scp->serverLock != -1) {
4987 tfid.Volume = scp->fid.volume;
4988 tfid.Vnode = scp->fid.vnode;
4989 tfid.Unique = scp->fid.unique;
4991 userp = fileLock->userp;
4993 osi_Log3(afsd_logp, "CALL ExtendLock lock 0x%p for scp=0x%p with lock %d",
4996 (int) scp->serverLock);
4998 lock_ReleaseMutex(&scp->mx);
5001 code = cm_ConnFromFID(&cfid, userp,
5006 callp = cm_GetRxConn(connp);
5007 code = RXAFS_ExtendLock(callp, &tfid,
5009 rx_PutConnection(callp);
5011 osi_Log1(afsd_logp, " ExtendLock returns %d", code);
5013 } while (cm_Analyze(connp, userp, &req,
5014 &cfid, &volSync, NULL, NULL,
5017 code = cm_MapRPCError(code, &req);
5019 lock_ObtainMutex(&scp->mx);
5022 osi_Log1(afsd_logp, "CALL ExtendLock FAILURE, code 0x%x", code);
5024 osi_Log0(afsd_logp, "CALL ExtendLock SUCCESS");
5025 scp->lockDataVersion = scp->dataVersion;
5028 if ((code == EINVAL || code == CM_ERROR_INVAL) &&
5029 scp->lockDataVersion == scp->dataVersion) {
5033 (scp->exclusiveLocks > 0) ? LockWrite: LockRead;
5035 /* we might still have a chance to obtain a
5038 code = cm_IntSetLock(scp, userp, lockType, &req);
5041 code = CM_ERROR_INVAL;
5042 } else if (scp->lockDataVersion != scp->dataVersion) {
5044 /* now check if we still have the file at
5045 the right data version. */
5047 "Data version mismatch on scp 0x%p",
5050 " Data versions: before=%I64d, after=%I64d",
5051 scp->lockDataVersion,
5054 code = cm_IntReleaseLock(scp, userp, &req);
5056 code = CM_ERROR_INVAL;
5060 if (code == EINVAL || code == CM_ERROR_INVAL) {
5061 cm_LockMarkSCacheLost(scp);
5065 /* interestingly, we have found an active lock
5066 belonging to an scache that has no
5068 cm_LockMarkSCacheLost(scp);
5075 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5078 lock_ReleaseMutex(&scp->mx);
5080 lock_ObtainWrite(&cm_scacheLock);
5083 fileLock->lastUpdate = time(NULL);
5087 scp->lastRefreshCycle = cm_lockRefreshCycle;
5090 /* we have already refreshed the locks on this scp */
5091 fileLock->lastUpdate = time(NULL);
5094 cm_ReleaseSCacheNoLock(scp);
5096 } else if (IS_LOCK_ACTIVE(fileLock) && IS_LOCK_CLIENTONLY(fileLock)) {
5097 /* TODO: Check callbacks */
5101 lock_ReleaseWrite(&cm_scacheLock);
5102 osi_Log1(afsd_logp, "cm_CheckLocks completes lock check cycle %d", cm_lockRefreshCycle);
5105 /* NOT called with scp->mx held. */
5106 long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
5109 cm_scache_t *scp = NULL;
5110 cm_file_lock_t *fileLock;
5114 int force_client_lock = FALSE;
5115 int has_insert = FALSE;
5116 int check_data_version = FALSE;
5120 if (client_is_dead) {
5121 code = CM_ERROR_TIMEDOUT;
5125 lock_ObtainRead(&cm_scacheLock);
5127 osi_Log2(afsd_logp, "cm_RetryLock checking lock %p (scp=%p)", oldFileLock, oldFileLock->scp);
5128 osi_Log4(afsd_logp, " offset(%x:%x) length(%x:%x)",
5129 (unsigned)(oldFileLock->range.offset >> 32),
5130 (unsigned)(oldFileLock->range.offset & 0xffffffff),
5131 (unsigned)(oldFileLock->range.length >> 32),
5132 (unsigned)(oldFileLock->range.length & 0xffffffff));
5133 osi_Log3(afsd_logp, " key(%x:%x) flags=%x",
5134 (unsigned)(oldFileLock->key >> 32),
5135 (unsigned)(oldFileLock->key & 0xffffffff),
5136 (unsigned)(oldFileLock->flags));
5138 /* if the lock has already been granted, then we have nothing to do */
5139 if (IS_LOCK_ACTIVE(oldFileLock)) {
5140 lock_ReleaseRead(&cm_scacheLock);
5141 osi_Log0(afsd_logp, "cm_RetryLock lock already granted");
5145 /* we can't do anything with lost or deleted locks at the moment. */
5146 if (IS_LOCK_LOST(oldFileLock) || IS_LOCK_DELETED(oldFileLock)) {
5147 code = CM_ERROR_BADFD;
5148 osi_Log0(afsd_logp, "cm_RetryLock lock is lost or deleted");
5149 lock_ReleaseRead(&cm_scacheLock);
5153 scp = oldFileLock->scp;
5155 osi_assertx(scp != NULL, "null cm_scache_t");
5157 lock_ReleaseRead(&cm_scacheLock);
5158 lock_ObtainMutex(&scp->mx);
5160 code = cm_LockCheckPerms(scp, oldFileLock->lockType,
5164 if (code == CM_ERROR_NOACCESS && oldFileLock->lockType == LockRead) {
5165 if (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp)) {
5166 force_client_lock = TRUE;
5170 lock_ReleaseMutex(&scp->mx);
5174 lock_ObtainWrite(&cm_scacheLock);
5176 /* Check if we already have a sufficient server lock to allow this
5177 lock to go through. */
5178 if (IS_LOCK_WAITLOCK(oldFileLock) &&
5179 (!SERVERLOCKS_ENABLED(scp) ||
5180 scp->serverLock == oldFileLock->lockType ||
5181 scp->serverLock == LockWrite)) {
5183 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5185 if (SERVERLOCKS_ENABLED(scp)) {
5186 osi_Log1(afsd_logp, "cm_RetryLock Server lock (%d) is sufficient for lock. Granting",
5187 (int) scp->serverLock);
5189 osi_Log0(afsd_logp, "cm_RetryLock skipping server lock for scp");
5192 lock_ReleaseWrite(&cm_scacheLock);
5193 lock_ReleaseMutex(&scp->mx);
5198 if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5200 /* check if the conflicting locks have dissappeared already */
5201 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5203 fileLock = (cm_file_lock_t *)
5204 ((char *) q - offsetof(cm_file_lock_t, fileq));
5206 if (IS_LOCK_LOST(fileLock)) {
5207 if (fileLock->key == oldFileLock->key) {
5208 code = CM_ERROR_BADFD;
5209 oldFileLock->flags |= CM_FILELOCK_FLAG_LOST;
5210 osi_Log1(afsd_logp, " found lost lock %p for same key. Marking lock as lost",
5213 } else if (fileLock->lockType == LockWrite &&
5214 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5215 osi_Log1(afsd_logp, " found conflicting LOST lock %p", fileLock);
5216 code = CM_ERROR_WOULDBLOCK;
5221 if (IS_LOCK_ACCEPTED(fileLock) &&
5222 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5224 if (oldFileLock->lockType != LockRead ||
5225 fileLock->lockType != LockRead) {
5227 osi_Log1(afsd_logp, " found conflicting lock %p", fileLock);
5228 code = CM_ERROR_WOULDBLOCK;
5236 lock_ReleaseWrite(&cm_scacheLock);
5237 lock_ReleaseMutex(&scp->mx);
5242 /* when we get here, the lock is either a WAITUNLOCK or WAITLOCK.
5243 If it is WAITUNLOCK, then we didn't find any conflicting lock
5244 but we haven't verfied whether the serverLock is sufficient to
5245 assert it. If it is WAITLOCK, then the serverLock is
5246 insufficient to assert it. Eitherway, we are ready to accept
5247 the lock as either ACTIVE or WAITLOCK depending on the
5250 /* First, promote the WAITUNLOCK to a WAITLOCK */
5251 if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5252 if (oldFileLock->lockType == LockRead)
5255 scp->exclusiveLocks++;
5257 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITUNLOCK;
5258 oldFileLock->flags |= CM_FILELOCK_FLAG_WAITLOCK;
5261 osi_assertx(IS_LOCK_WAITLOCK(oldFileLock), "!IS_LOCK_WAITLOCK");
5263 if (force_client_lock ||
5264 !SERVERLOCKS_ENABLED(scp) ||
5265 scp->serverLock == oldFileLock->lockType ||
5266 (oldFileLock->lockType == LockRead &&
5267 scp->serverLock == LockWrite)) {
5269 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5271 if ((force_client_lock ||
5272 !SERVERLOCKS_ENABLED(scp)) &&
5273 !IS_LOCK_CLIENTONLY(oldFileLock)) {
5275 oldFileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
5277 if (oldFileLock->lockType == LockRead)
5280 scp->exclusiveLocks--;
5285 lock_ReleaseWrite(&cm_scacheLock);
5286 lock_ReleaseMutex(&scp->mx);
5293 code = cm_SyncOp(scp, NULL, oldFileLock->userp, &req, 0,
5294 CM_SCACHESYNC_NEEDCALLBACK
5295 | CM_SCACHESYNC_GETSTATUS
5296 | CM_SCACHESYNC_LOCK);
5298 osi_Log1(smb_logp, "cm_RetryLock SyncOp failure code 0x%x", code);
5299 lock_ReleaseWrite(&cm_scacheLock);
5300 goto post_syncopdone;
5303 if (!IS_LOCK_WAITLOCK(oldFileLock))
5304 goto pre_syncopdone;
5306 userp = oldFileLock->userp;
5308 #ifndef AGGRESSIVE_LOCKS
5309 newLock = oldFileLock->lockType;
5311 newLock = LockWrite;
5315 /* if has_insert is non-zero, then:
5316 - the lock a LockRead
5317 - we don't have permission to get a LockRead
5318 - we do have permission to get a LockWrite
5319 - the server supports VICED_CAPABILITY_WRITELOCKACL
5322 newLock = LockWrite;
5325 lock_ReleaseWrite(&cm_scacheLock);
5327 /* when we get here, either we have a read-lock and want a
5328 write-lock or we don't have any locks and we want some
5331 if (scp->serverLock == LockRead) {
5333 osi_assertx(newLock == LockWrite, "!LockWrite");
5335 osi_Log0(afsd_logp, " Attempting to UPGRADE from LockRead to LockWrite");
5337 scp->lockDataVersion = scp->dataVersion;
5338 check_data_version = TRUE;
5340 code = cm_IntReleaseLock(scp, userp, &req);
5343 goto pre_syncopdone;
5345 scp->serverLock = -1;
5348 code = cm_IntSetLock(scp, userp, newLock, &req);
5351 if (scp->dataVersion != scp->lockDataVersion) {
5352 /* we lost a race. too bad */
5355 " Data version mismatch while upgrading lock.");
5357 " Data versions before=%I64d, after=%I64d",
5358 scp->lockDataVersion,
5361 " Releasing stale lock for scp 0x%x", scp);
5363 code = cm_IntReleaseLock(scp, userp, &req);
5365 scp->serverLock = -1;
5367 code = CM_ERROR_INVAL;
5369 cm_LockMarkSCacheLost(scp);
5371 scp->serverLock = newLock;
5376 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5382 if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
5383 lock_ObtainWrite(&cm_scacheLock);
5384 if (scp->fileLocksT == &oldFileLock->fileq)
5385 scp->fileLocksT = osi_QPrev(&oldFileLock->fileq);
5386 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, &oldFileLock->fileq);
5387 lock_ReleaseWrite(&cm_scacheLock);
5389 lock_ReleaseMutex(&scp->mx);
5392 lock_ObtainWrite(&cm_scacheLock);
5394 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5395 } else if (code != CM_ERROR_WOULDBLOCK) {
5396 oldFileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5397 cm_ReleaseUser(oldFileLock->userp);
5398 oldFileLock->userp = NULL;
5399 if (oldFileLock->scp) {
5400 cm_ReleaseSCacheNoLock(oldFileLock->scp);
5401 oldFileLock->scp = NULL;
5404 lock_ReleaseWrite(&cm_scacheLock);
5409 cm_key_t cm_GenerateKey(unsigned int session_id, unsigned long process_id, unsigned int file_id)
5412 osi_assertx((process_id & 0xffffffff) == process_id, "unexpected process_id");
5413 osi_assertx((session_id & 0xffff) == session_id, "unexpected session_id");
5414 osi_assertx((file_id & 0xffff) == file_id, "unexpected file_id");
5418 (((cm_key_t) (process_id & 0xffffffff)) << 32) |
5419 (((cm_key_t) (session_id & 0xffff)) << 16) |
5420 (((cm_key_t) (file_id & 0xffff)));
5423 static int cm_KeyEquals(cm_key_t k1, cm_key_t k2, int flags)
5425 if (flags & CM_UNLOCK_BY_FID) {
5426 return ((k1 & 0xffffffff) == (k2 & 0xffffffff));
5432 void cm_ReleaseAllLocks(void)
5438 cm_file_lock_t *fileLock;
5441 for (i = 0; i < cm_data.scacheHashTableSize; i++)
5443 for ( scp = cm_data.scacheHashTablep[i]; scp; scp = scp->nextp ) {
5444 while (scp->fileLocksH != NULL) {
5445 lock_ObtainMutex(&scp->mx);
5446 lock_ObtainWrite(&cm_scacheLock);
5447 if (!scp->fileLocksH) {
5448 lock_ReleaseWrite(&cm_scacheLock);
5449 lock_ReleaseMutex(&scp->mx);
5452 fileLock = (cm_file_lock_t *)((char *) scp->fileLocksH - offsetof(cm_file_lock_t, fileq));
5453 userp = fileLock->userp;
5455 key = fileLock->key;
5456 cm_HoldSCacheNoLock(scp);
5457 lock_ReleaseWrite(&cm_scacheLock);
5458 cm_UnlockByKey(scp, key, 0, userp, &req);
5459 cm_ReleaseSCache(scp);
5460 cm_ReleaseUser(userp);
5461 lock_ReleaseMutex(&scp->mx);