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,
479 cm_dirEntry_t *dep = 0;
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)
578 cm_dirEntry_t *dep = 0;
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, 0);
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 &&
1145 (dscp->flags & (CM_SCACHEFLAG_RO|CM_SCACHEFLAG_PURERO)) == CM_SCACHEFLAG_RO &&
1146 (targetType == RWVOL || targetType == ROVOL && volp->ro.ID == 0)
1148 targetType = BACKVOL;
1150 /* if the mt pt is in a read-only volume (not just a
1151 * backup), and if there is a read-only volume for the
1152 * target, and if this is a targetType '#' mount point, use
1153 * the read-only, otherwise use the one specified.
1155 else if (mtType == '#' && targetType == RWVOL &&
1156 (scp->flags & CM_SCACHEFLAG_PURERO) &&
1160 if (targetType == ROVOL)
1161 scp->mountRootFid.volume = volp->ro.ID;
1162 else if (targetType == BACKVOL)
1163 scp->mountRootFid.volume = volp->bk.ID;
1165 scp->mountRootFid.volume = volp->rw.ID;
1167 /* the rest of the fid is a magic number */
1168 scp->mountRootFid.vnode = 1;
1169 scp->mountRootFid.unique = 1;
1170 scp->mountRootGen = cm_data.mountRootGen;
1172 tfid = scp->mountRootFid;
1173 lock_ReleaseMutex(&scp->mx);
1174 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
1175 lock_ObtainMutex(&scp->mx);
1186 long cm_LookupInternal(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
1187 cm_req_t *reqp, cm_scache_t **outpScpp)
1190 int dnlcHit = 1; /* did we hit in the dnlc? yes, we did */
1191 cm_scache_t *tscp = NULL;
1192 cm_scache_t *mountedScp;
1193 cm_lookupSearch_t rock;
1196 memset(&rock, 0, sizeof(rock));
1198 if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
1199 && strcmp(namep, "..") == 0) {
1200 if (dscp->dotdotFid.volume == 0)
1201 return CM_ERROR_NOSUCHVOLUME;
1202 rock.fid = dscp->dotdotFid;
1204 } else if (strcmp(namep, ".") == 0) {
1205 rock.fid = dscp->fid;
1209 if (flags & CM_FLAG_NOMOUNTCHASE) {
1210 /* In this case, we should go and call cm_Dir* functions
1211 directly since the following cm_ApplyDir() function will
1219 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ, &dirop);
1222 code = cm_BPlusDirLookup(&dirop, namep, &rock.fid);
1227 code = cm_DirLookup(&dirop, namep, &rock.fid);
1229 cm_EndDirOp(&dirop);
1239 if (code == CM_ERROR_INEXACT_MATCH && (flags & CM_FLAG_CASEFOLD)) {
1246 return CM_ERROR_BPLUS_NOMATCH;
1251 rock.fid.cell = dscp->fid.cell;
1252 rock.fid.volume = dscp->fid.volume;
1253 rock.searchNamep = namep;
1254 rock.caseFold = (flags & CM_FLAG_CASEFOLD);
1255 rock.hasTilde = ((strchr(namep, '~') != NULL) ? 1 : 0);
1257 /* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
1258 code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
1259 (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
1261 /* code == 0 means we fell off the end of the dir, while stopnow means
1262 * that we stopped early, probably because we found the entry we're
1263 * looking for. Any other non-zero code is an error.
1265 if (code && code != CM_ERROR_STOPNOW) {
1266 /* if the cm_scache_t we are searching in is not a directory
1267 * we must return path not found because the error
1268 * is to describe the final component not an intermediary
1270 if (code == CM_ERROR_NOTDIR) {
1271 if (flags & CM_FLAG_CHECKPATH)
1272 return CM_ERROR_NOSUCHPATH;
1274 return CM_ERROR_NOSUCHFILE;
1279 getroot = (dscp==cm_data.rootSCachep) ;
1281 if (!cm_freelanceEnabled || !getroot) {
1282 if (flags & CM_FLAG_CHECKPATH)
1283 return CM_ERROR_NOSUCHPATH;
1285 return CM_ERROR_NOSUCHFILE;
1287 else { /* nonexistent dir on freelance root, so add it */
1288 char fullname[200] = ".";
1291 osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %s",
1292 osi_LogSaveString(afsd_logp,namep));
1293 if (namep[0] == '.') {
1294 if (cm_GetCell_Gen(&namep[1], &fullname[1], CM_FLAG_CREATE)) {
1296 if ( stricmp(&namep[1], &fullname[1]) )
1297 code = cm_FreelanceAddSymlink(namep, fullname, &rock.fid);
1299 code = cm_FreelanceAddMount(namep, &fullname[1], "root.cell.", 1, &rock.fid);
1302 if (cm_GetCell_Gen(namep, fullname, CM_FLAG_CREATE)) {
1304 if ( stricmp(namep, fullname) )
1305 code = cm_FreelanceAddSymlink(namep, fullname, &rock.fid);
1307 code = cm_FreelanceAddMount(namep, fullname, "root.cell.", 0, &rock.fid);
1310 if (!found || code < 0) { /* add mount point failed, so give up */
1311 if (flags & CM_FLAG_CHECKPATH)
1312 return CM_ERROR_NOSUCHPATH;
1314 return CM_ERROR_NOSUCHFILE;
1316 tscp = NULL; /* to force call of cm_GetSCache */
1321 if ( !tscp ) /* we did not find it in the dnlc */
1324 code = cm_GetSCache(&rock.fid, &tscp, userp, reqp);
1328 /* tscp is now held */
1330 lock_ObtainMutex(&tscp->mx);
1331 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1332 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1334 lock_ReleaseMutex(&tscp->mx);
1335 cm_ReleaseSCache(tscp);
1338 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1339 /* tscp is now locked */
1341 if (!(flags & CM_FLAG_NOMOUNTCHASE)
1342 && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
1343 /* mount points are funny: they have a volume name to mount
1346 code = cm_ReadMountPoint(tscp, userp, reqp);
1348 code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
1350 lock_ReleaseMutex(&tscp->mx);
1351 cm_ReleaseSCache(tscp);
1358 lock_ReleaseMutex(&tscp->mx);
1361 /* copy back pointer */
1364 /* insert scache in dnlc */
1365 if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
1366 /* lock the directory entry to prevent racing callback revokes */
1367 lock_ObtainMutex(&dscp->mx);
1368 if ( dscp->cbServerp != NULL && dscp->cbExpires > 0 )
1369 cm_dnlcEnter(dscp, namep, tscp);
1370 lock_ReleaseMutex(&dscp->mx);
1377 int cm_ExpandSysName(char *inp, char *outp, long outSize, unsigned int index)
1382 tp = strrchr(inp, '@');
1384 return 0; /* no @sys */
1386 if (strcmp(tp, "@sys") != 0)
1387 return 0; /* no @sys */
1389 /* caller just wants to know if this is a valid @sys type of name */
1393 if (index >= MAXNUMSYSNAMES)
1396 /* otherwise generate the properly expanded @sys name */
1397 prefixCount = (int)(tp - inp);
1399 strncpy(outp, inp, prefixCount); /* copy out "a." from "a.@sys" */
1400 outp[prefixCount] = 0; /* null terminate the "a." */
1401 strcat(outp, cm_sysNameList[index]);/* append i386_nt40 */
1405 long cm_EvaluateVolumeReference(char * namep, long flags, cm_user_t * userp,
1406 cm_req_t *reqp, cm_scache_t ** outpScpp)
1409 char cellName[CELL_MAXNAMELEN];
1410 char volumeName[VL_MAXNAMELEN];
1415 cm_cell_t * cellp = NULL;
1416 cm_volume_t * volp = NULL;
1419 int mountType = RWVOL;
1421 osi_Log1(afsd_logp, "cm_EvaluateVolumeReference for string [%s]",
1422 osi_LogSaveString(afsd_logp, namep));
1424 if (strnicmp(namep, CM_PREFIX_VOL, CM_PREFIX_VOL_CCH) != 0) {
1425 goto _exit_invalid_path;
1428 /* namep is assumed to look like the following:
1430 @vol:<cellname>%<volume>\0
1432 @vol:<cellname>#<volume>\0
1436 cp = namep + CM_PREFIX_VOL_CCH; /* cp points to cell name, hopefully */
1437 tp = strchr(cp, '%');
1439 tp = strchr(cp, '#');
1441 (len = tp - cp) == 0 ||
1442 len > CELL_MAXNAMELEN)
1443 goto _exit_invalid_path;
1444 strncpy(cellName, cp, len);
1445 cellName[len] = '\0';
1450 cp = tp+1; /* cp now points to volume, supposedly */
1451 strncpy(volumeName, cp, VL_MAXNAMELEN-1);
1452 volumeName[VL_MAXNAMELEN - 1] = 0;
1454 /* OK, now we have the cell and the volume */
1455 osi_Log2(afsd_logp, " Found cell [%s] and volume [%s]",
1456 osi_LogSaveString(afsd_logp, cellName),
1457 osi_LogSaveString(afsd_logp, volumeName));
1459 cellp = cm_GetCell(cellName, CM_FLAG_CREATE);
1460 if (cellp == NULL) {
1461 goto _exit_invalid_path;
1464 len = strlen(volumeName);
1465 if (len >= 8 && strcmp(volumeName + len - 7, ".backup") == 0)
1467 else if (len >= 10 &&
1468 strcmp(volumeName + len - 9, ".readonly") == 0)
1473 if (cm_VolNameIsID(volumeName)) {
1474 code = cm_GetVolumeByID(cellp, atoi(volumeName), userp, reqp,
1475 CM_GETVOL_FLAG_CREATE, &volp);
1477 code = cm_GetVolumeByName(cellp, volumeName, userp, reqp,
1478 CM_GETVOL_FLAG_CREATE, &volp);
1484 fid.cell = cellp->cellID;
1486 if (volType == BACKVOL)
1487 fid.volume = volp->bk.ID;
1488 else if (volType == ROVOL ||
1489 (volType == RWVOL && mountType == ROVOL && volp->ro.ID != 0))
1490 fid.volume = volp->ro.ID;
1492 fid.volume = volp->rw.ID;
1497 code = cm_GetSCache(&fid, outpScpp, userp, reqp);
1507 if (flags & CM_FLAG_CHECKPATH)
1508 return CM_ERROR_NOSUCHPATH;
1510 return CM_ERROR_NOSUCHFILE;
1513 #ifdef DEBUG_REFCOUNT
1514 long cm_LookupDbg(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
1515 cm_req_t *reqp, cm_scache_t **outpScpp, char * file, long line)
1517 long cm_Lookup(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
1518 cm_req_t *reqp, cm_scache_t **outpScpp)
1522 char tname[AFSPATHMAX];
1523 int sysNameIndex = 0;
1524 cm_scache_t *scp = NULL;
1526 #ifdef DEBUG_REFCOUNT
1527 afsi_log("%s:%d cm_Lookup dscp 0x%p ref %d", file, line, dscp, dscp->refCount, file, line);
1528 osi_Log2(afsd_logp, "cm_Lookup dscp 0x%p ref %d", dscp, dscp->refCount);
1531 if ( stricmp(namep,SMB_IOCTL_FILENAME_NOSLASH) == 0 ) {
1532 if (flags & CM_FLAG_CHECKPATH)
1533 return CM_ERROR_NOSUCHPATH;
1535 return CM_ERROR_NOSUCHFILE;
1538 if (dscp == cm_data.rootSCachep &&
1539 strnicmp(namep, CM_PREFIX_VOL, CM_PREFIX_VOL_CCH) == 0) {
1540 return cm_EvaluateVolumeReference(namep, flags, userp, reqp, outpScpp);
1543 if (cm_ExpandSysName(namep, NULL, 0, 0) > 0) {
1544 for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
1545 code = cm_ExpandSysName(namep, tname, sizeof(tname), sysNameIndex);
1547 code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
1548 #ifdef DEBUG_REFCOUNT
1549 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);
1550 osi_Log3(afsd_logp, "cm_LookupInternal (1) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1558 cm_ReleaseSCache(scp);
1562 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1563 #ifdef DEBUG_REFCOUNT
1564 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);
1565 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1572 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1573 #ifdef DEBUG_REFCOUNT
1574 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);
1575 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1581 /* None of the possible sysName expansions could be found */
1582 if (flags & CM_FLAG_CHECKPATH)
1583 return CM_ERROR_NOSUCHPATH;
1585 return CM_ERROR_NOSUCHFILE;
1588 long cm_Unlink(cm_scache_t *dscp, char *namep, cm_user_t *userp, cm_req_t *reqp)
1594 AFSFetchStatus newDirStatus;
1596 struct rx_connection * callp;
1599 #ifdef AFS_FREELANCE_CLIENT
1600 if (cm_freelanceEnabled && dscp == cm_data.rootSCachep) {
1601 /* deleting a mount point from the root dir. */
1602 code = cm_FreelanceRemoveMount(namep);
1607 /* make sure we don't screw up the dir status during the merge */
1608 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
1610 lock_ObtainMutex(&dscp->mx);
1611 sflags = CM_SCACHESYNC_STOREDATA;
1612 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1613 lock_ReleaseMutex(&dscp->mx);
1615 cm_EndDirOp(&dirop);
1620 afsFid.Volume = dscp->fid.volume;
1621 afsFid.Vnode = dscp->fid.vnode;
1622 afsFid.Unique = dscp->fid.unique;
1624 osi_Log1(afsd_logp, "CALL RemoveFile scp 0x%p", dscp);
1626 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
1630 callp = cm_GetRxConn(connp);
1631 code = RXAFS_RemoveFile(callp, &afsFid, namep,
1632 &newDirStatus, &volSync);
1633 rx_PutConnection(callp);
1635 } while (cm_Analyze(connp, userp, reqp, &dscp->fid, &volSync, NULL, NULL, code));
1636 code = cm_MapRPCError(code, reqp);
1639 osi_Log1(afsd_logp, "CALL RemoveFile FAILURE, code 0x%x", code);
1641 osi_Log0(afsd_logp, "CALL RemoveFile SUCCESS");
1644 lock_ObtainWrite(&dirop.scp->dirlock);
1645 dirop.lockType = CM_DIRLOCK_WRITE;
1647 lock_ObtainMutex(&dscp->mx);
1648 cm_dnlcRemove(dscp, namep);
1649 cm_SyncOpDone(dscp, NULL, sflags);
1651 cm_MergeStatus(NULL, dscp, &newDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
1652 } else if (code == CM_ERROR_NOSUCHFILE) {
1653 /* windows would not have allowed the request to delete the file
1654 * if it did not believe the file existed. therefore, we must
1655 * have an inconsistent view of the world.
1657 dscp->cbServerp = NULL;
1659 lock_ReleaseMutex(&dscp->mx);
1661 if (code == 0 && cm_CheckDirOpForSingleChange(&dirop)) {
1662 cm_DirDeleteEntry(&dirop, namep);
1664 cm_BPlusDirDeleteEntry(&dirop, namep);
1667 cm_EndDirOp(&dirop);
1672 /* called with a locked vnode, and fills in the link info.
1673 * returns this the vnode still locked.
1675 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1682 lock_AssertMutex(&linkScp->mx);
1683 if (!linkScp->mountPointStringp[0]) {
1684 /* read the link data */
1685 lock_ReleaseMutex(&linkScp->mx);
1686 thyper.LowPart = thyper.HighPart = 0;
1687 code = buf_Get(linkScp, &thyper, &bufp);
1688 lock_ObtainMutex(&linkScp->mx);
1692 code = cm_SyncOp(linkScp, bufp, userp, reqp, 0,
1693 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1698 cm_SyncOpDone(linkScp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1700 if (cm_HaveBuffer(linkScp, bufp, 0))
1703 code = cm_GetBuffer(linkScp, bufp, NULL, userp, reqp);
1708 } /* while loop to get the data */
1710 /* now if we still have no link read in,
1711 * copy the data from the buffer */
1712 if ((temp = linkScp->length.LowPart) >= MOUNTPOINTLEN) {
1714 return CM_ERROR_TOOBIG;
1717 /* otherwise, it fits; make sure it is still null (could have
1718 * lost race with someone else referencing this link above),
1719 * and if so, copy in the data.
1721 if (!linkScp->mountPointStringp[0]) {
1722 strncpy(linkScp->mountPointStringp, bufp->datap, temp);
1723 linkScp->mountPointStringp[temp] = 0; /* null terminate */
1725 if ( !strnicmp(linkScp->mountPointStringp, "msdfs:", strlen("msdfs:")) )
1726 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1729 } /* don't have sym link contents cached */
1734 /* called with a held vnode and a path suffix, with the held vnode being a
1735 * symbolic link. Our goal is to generate a new path to interpret, and return
1736 * this new path in newSpaceBufferp. If the new vnode is relative to a dir
1737 * other than the directory containing the symbolic link, then the new root is
1738 * returned in *newRootScpp, otherwise a null is returned there.
1740 long cm_AssembleLink(cm_scache_t *linkScp, char *pathSuffixp,
1741 cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1742 cm_user_t *userp, cm_req_t *reqp)
1749 *newRootScpp = NULL;
1750 *newSpaceBufferp = NULL;
1752 lock_ObtainMutex(&linkScp->mx);
1753 code = cm_HandleLink(linkScp, userp, reqp);
1757 /* if we may overflow the buffer, bail out; buffer is signficantly
1758 * bigger than max path length, so we don't really have to worry about
1759 * being a little conservative here.
1761 if (strlen(linkScp->mountPointStringp) + strlen(pathSuffixp) + 2
1762 >= CM_UTILS_SPACESIZE)
1763 return CM_ERROR_TOOBIG;
1765 tsp = cm_GetSpace();
1766 linkp = linkScp->mountPointStringp;
1767 if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1768 if (strlen(linkp) > cm_mountRootLen)
1769 strcpy(tsp->data, linkp+cm_mountRootLen+1);
1772 *newRootScpp = cm_data.rootSCachep;
1773 cm_HoldSCache(cm_data.rootSCachep);
1774 } else if (linkp[0] == '\\' && linkp[1] == '\\') {
1775 if (!strnicmp(&linkp[2], cm_NetbiosName, (len = (long)strlen(cm_NetbiosName))))
1777 char * p = &linkp[len + 3];
1778 if (strnicmp(p, "all", 3) == 0)
1781 strcpy(tsp->data, p);
1782 for (p = tsp->data; *p; p++) {
1786 *newRootScpp = cm_data.rootSCachep;
1787 cm_HoldSCache(cm_data.rootSCachep);
1789 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1790 strcpy(tsp->data, linkp);
1791 code = CM_ERROR_PATH_NOT_COVERED;
1793 } else if ( linkScp->fileType == CM_SCACHETYPE_DFSLINK ||
1794 !strnicmp(linkp, "msdfs:", (len = (long)strlen("msdfs:"))) ) {
1795 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1796 strcpy(tsp->data, linkp);
1797 code = CM_ERROR_PATH_NOT_COVERED;
1798 } else if (*linkp == '\\' || *linkp == '/') {
1800 /* formerly, this was considered to be from the AFS root,
1801 * but this seems to create problems. instead, we will just
1802 * reject the link */
1803 strcpy(tsp->data, linkp+1);
1804 *newRootScpp = cm_data.rootSCachep;
1805 cm_HoldSCache(cm_data.rootSCachep);
1807 /* we still copy the link data into the response so that
1808 * the user can see what the link points to
1810 linkScp->fileType = CM_SCACHETYPE_INVALID;
1811 strcpy(tsp->data, linkp);
1812 code = CM_ERROR_NOSUCHPATH;
1815 /* a relative link */
1816 strcpy(tsp->data, linkp);
1818 if (pathSuffixp[0] != 0) { /* if suffix string is non-null */
1819 strcat(tsp->data, "\\");
1820 strcat(tsp->data, pathSuffixp);
1823 *newSpaceBufferp = tsp;
1827 if (code == CM_ERROR_PATH_NOT_COVERED && reqp->tidPathp && reqp->relPathp)
1828 cm_VolStatus_Notify_DFS_Mapping(linkScp, reqp->tidPathp, reqp->relPathp);
1832 lock_ReleaseMutex(&linkScp->mx);
1835 #ifdef DEBUG_REFCOUNT
1836 long cm_NameIDbg(cm_scache_t *rootSCachep, char *pathp, long flags,
1837 cm_user_t *userp, char *tidPathp, cm_req_t *reqp, cm_scache_t **outScpp,
1838 char * file, long line)
1840 long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
1841 cm_user_t *userp, char *tidPathp, cm_req_t *reqp, cm_scache_t **outScpp)
1845 char *tp; /* ptr moving through input buffer */
1846 char tc; /* temp char */
1847 int haveComponent; /* has new component started? */
1848 char component[AFSPATHMAX]; /* this is the new component */
1849 char *cp; /* component name being assembled */
1850 cm_scache_t *tscp; /* current location in the hierarchy */
1851 cm_scache_t *nscp; /* next dude down */
1852 cm_scache_t *dirScp; /* last dir we searched */
1853 cm_scache_t *linkScp; /* new root for the symlink we just
1855 cm_space_t *psp; /* space for current path, if we've hit
1857 cm_space_t *tempsp; /* temp vbl */
1858 char *restp; /* rest of the pathname to interpret */
1859 int symlinkCount; /* count of # of symlinks traversed */
1860 int extraFlag; /* avoid chasing mt pts for dir cmd */
1861 int phase = 1; /* 1 = tidPathp, 2 = pathp */
1862 #define MAX_FID_COUNT 512
1863 cm_fid_t fids[MAX_FID_COUNT]; /* array of fids processed in this path walk */
1864 int fid_count = 0; /* number of fids processed in this path walk */
1867 #ifdef DEBUG_REFCOUNT
1868 afsi_log("%s:%d cm_NameI rootscp 0x%p ref %d", file, line, rootSCachep, rootSCachep->refCount);
1869 osi_Log4(afsd_logp,"cm_NameI rootscp 0x%p path %s tidpath %s flags 0x%x",
1870 rootSCachep, pathp ? pathp : "<NULL>", tidPathp ? tidPathp : "<NULL>",
1885 cm_HoldSCache(tscp);
1893 /* map Unix slashes into DOS ones so we can interpret Unix
1899 if (!haveComponent) {
1902 } else if (tc == 0) {
1916 /* we have a component here */
1917 if (tc == 0 || tc == '\\') {
1918 /* end of the component; we're at the last
1919 * component if tc == 0. However, if the last
1920 * is a symlink, we have more to do.
1922 *cp++ = 0; /* add null termination */
1924 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
1925 extraFlag = CM_FLAG_NOMOUNTCHASE;
1926 code = cm_Lookup(tscp, component,
1928 userp, reqp, &nscp);
1931 if (!strcmp(component,"..") || !strcmp(component,".")) {
1933 * roll back the fid list until we find the fid
1934 * that matches where we are now. Its not necessarily
1935 * one or two fids because they might have been
1936 * symlinks or mount points or both that were crossed.
1938 for ( i=fid_count-1; i>=0; i--) {
1939 if (!cm_FidCmp(&nscp->fid, &fids[i]))
1944 /* add the new fid to the list */
1945 for ( i=0; i<fid_count; i++) {
1946 if ( !cm_FidCmp(&nscp->fid, &fids[i]) ) {
1947 code = CM_ERROR_TOO_MANY_SYMLINKS;
1948 cm_ReleaseSCache(nscp);
1953 if (i == fid_count && fid_count < MAX_FID_COUNT) {
1954 fids[fid_count++] = nscp->fid;
1960 cm_ReleaseSCache(tscp);
1962 cm_ReleaseSCache(dirScp);
1965 if ((code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH) &&
1966 tscp->fileType == CM_SCACHETYPE_SYMLINK)
1968 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_NOSUCHPATH");
1969 return CM_ERROR_NOSUCHPATH;
1971 osi_Log1(afsd_logp,"cm_NameI code 0x%x", code);
1976 haveComponent = 0; /* component done */
1978 cm_ReleaseSCache(dirScp);
1979 dirScp = tscp; /* for some symlinks */
1980 tscp = nscp; /* already held */
1982 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
1985 cm_ReleaseSCache(dirScp);
1991 /* now, if tscp is a symlink, we should follow
1992 * it and assemble the path again.
1994 lock_ObtainMutex(&tscp->mx);
1995 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1996 CM_SCACHESYNC_GETSTATUS
1997 | CM_SCACHESYNC_NEEDCALLBACK);
1999 lock_ReleaseMutex(&tscp->mx);
2000 cm_ReleaseSCache(tscp);
2003 cm_ReleaseSCache(dirScp);
2008 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2010 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
2011 /* this is a symlink; assemble a new buffer */
2012 lock_ReleaseMutex(&tscp->mx);
2013 if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
2014 cm_ReleaseSCache(tscp);
2017 cm_ReleaseSCache(dirScp);
2022 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_TOO_MANY_SYMLINKS");
2023 return CM_ERROR_TOO_MANY_SYMLINKS;
2029 code = cm_AssembleLink(tscp, restp, &linkScp, &tempsp, userp, reqp);
2031 if (code == 0 && linkScp != NULL) {
2032 if (linkScp == cm_data.rootSCachep)
2035 for ( i=0; i<fid_count; i++) {
2036 if ( !cm_FidCmp(&linkScp->fid, &fids[i]) ) {
2037 code = CM_ERROR_TOO_MANY_SYMLINKS;
2038 cm_ReleaseSCache(linkScp);
2044 if (i == fid_count && fid_count < MAX_FID_COUNT) {
2045 fids[fid_count++] = linkScp->fid;
2050 /* something went wrong */
2051 cm_ReleaseSCache(tscp);
2054 cm_ReleaseSCache(dirScp);
2060 /* otherwise, tempsp has the new path,
2061 * and linkScp is the new root from
2062 * which to interpret that path.
2063 * Continue with the namei processing,
2064 * also doing the bookkeeping for the
2065 * space allocation and tracking the
2066 * vnode reference counts.
2072 cm_ReleaseSCache(tscp);
2077 * now, if linkScp is null, that's
2078 * AssembleLink's way of telling us that
2079 * the sym link is relative to the dir
2080 * containing the link. We have a ref
2081 * to it in dirScp, and we hold it now
2082 * and reuse it as the new spot in the
2090 /* not a symlink, we may be done */
2091 lock_ReleaseMutex(&tscp->mx);
2099 cm_ReleaseSCache(dirScp);
2107 cm_ReleaseSCache(dirScp);
2110 } /* end of a component */
2113 } /* we have a component */
2114 } /* big while loop over all components */
2118 cm_ReleaseSCache(dirScp);
2124 cm_ReleaseSCache(tscp);
2126 #ifdef DEBUG_REFCOUNT
2127 afsi_log("%s:%d cm_NameI code 0x%x outScpp 0x%p ref %d", file, line, code, *outScpp, (*outScpp)->refCount);
2129 osi_Log2(afsd_logp,"cm_NameI code 0x%x outScpp 0x%p", code, *outScpp);
2133 /* called with a dir, and a vnode within the dir that happens to be a symlink.
2134 * We chase the link, and return a held pointer to the target, if it exists,
2135 * in *outScpp. If we succeed, we return 0, otherwise we return an error code
2136 * and do not hold or return a target vnode.
2138 * This is very similar to calling cm_NameI with the last component of a name,
2139 * which happens to be a symlink, except that we've already passed by the name.
2141 * This function is typically called by the directory listing functions, which
2142 * encounter symlinks but need to return the proper file length so programs
2143 * like "more" work properly when they make use of the attributes retrieved from
2146 * The input vnode should not be locked when this function is called.
2148 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
2149 cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
2153 cm_scache_t *newRootScp;
2155 osi_Log1(afsd_logp, "Evaluating symlink scp 0x%p", linkScp);
2157 code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
2161 /* now, if newRootScp is NULL, we're really being told that the symlink
2162 * is relative to the current directory (dscp).
2164 if (newRootScp == NULL) {
2166 cm_HoldSCache(dscp);
2169 code = cm_NameI(newRootScp, spacep->data,
2170 CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
2171 userp, NULL, reqp, outScpp);
2173 if (code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH)
2174 code = CM_ERROR_NOSUCHPATH;
2176 /* this stuff is allocated no matter what happened on the namei call,
2178 cm_FreeSpace(spacep);
2179 cm_ReleaseSCache(newRootScp);
2181 if (linkScp == *outScpp) {
2182 cm_ReleaseSCache(*outScpp);
2184 code = CM_ERROR_NOSUCHPATH;
2190 /* make this big enough so that one buffer of dir pages won't overflow. We'll
2191 * check anyway, but we want to minimize the chance that we have to leave stuff
2194 #define CM_BULKMAX (3 * AFSCBMAX)
2196 /* rock for bulk stat calls */
2197 typedef struct cm_bulkStat {
2198 osi_hyper_t bufOffset; /* only do it for things in this buffer page */
2200 /* info for the actual call */
2201 int counter; /* next free slot */
2202 AFSFid fids[CM_BULKMAX];
2203 AFSFetchStatus stats[CM_BULKMAX];
2204 AFSCallBack callbacks[CM_BULKMAX];
2207 /* for a given entry, make sure that it isn't in the stat cache, and then
2208 * add it to the list of file IDs to be obtained.
2210 * Don't bother adding it if we already have a vnode. Note that the dir
2211 * is locked, so we have to be careful checking the vnode we're thinking of
2212 * processing, to avoid deadlocks.
2214 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
2225 /* Don't overflow bsp. */
2226 if (bsp->counter >= CM_BULKMAX)
2227 return CM_ERROR_STOPNOW;
2229 thyper.LowPart = cm_data.buf_blockSize;
2230 thyper.HighPart = 0;
2231 thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
2233 /* thyper is now the first byte past the end of the record we're
2234 * interested in, and bsp->bufOffset is the first byte of the record
2235 * we're interested in.
2236 * Skip data in the others.
2239 if (LargeIntegerLessThan(*offp, bsp->bufOffset))
2241 if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
2242 return CM_ERROR_STOPNOW;
2243 if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
2246 tfid.cell = scp->fid.cell;
2247 tfid.volume = scp->fid.volume;
2248 tfid.vnode = ntohl(dep->fid.vnode);
2249 tfid.unique = ntohl(dep->fid.unique);
2250 tscp = cm_FindSCache(&tfid);
2252 if (lock_TryMutex(&tscp->mx)) {
2253 /* we have an entry that we can look at */
2254 if (!(tscp->flags & CM_SCACHEFLAG_EACCESS) && cm_HaveCallback(tscp)) {
2255 /* we have a callback on it. Don't bother
2256 * fetching this stat entry, since we're happy
2257 * with the info we have.
2259 lock_ReleaseMutex(&tscp->mx);
2260 cm_ReleaseSCache(tscp);
2263 lock_ReleaseMutex(&tscp->mx);
2265 cm_ReleaseSCache(tscp);
2268 #ifdef AFS_FREELANCE_CLIENT
2269 // yj: if this is a mountpoint under root.afs then we don't want it
2270 // to be bulkstat-ed, instead, we call getSCache directly and under
2271 // getSCache, it is handled specially.
2272 if ( cm_freelanceEnabled &&
2273 tfid.cell==AFS_FAKE_ROOT_CELL_ID &&
2274 tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
2275 !(tfid.vnode==0x1 && tfid.unique==0x1) )
2277 osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
2278 return cm_GetSCache(&tfid, &tscp, NULL, NULL);
2280 #endif /* AFS_FREELANCE_CLIENT */
2283 bsp->fids[i].Volume = scp->fid.volume;
2284 bsp->fids[i].Vnode = tfid.vnode;
2285 bsp->fids[i].Unique = tfid.unique;
2289 /* called with a locked scp and a pointer to a buffer. Make bulk stat
2290 * calls on all undeleted files in the page of the directory specified.
2293 cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
2297 cm_bulkStat_t bb; /* this is *BIG*, probably 16K or so;
2298 * watch for stack problems */
2299 AFSCBFids fidStruct;
2300 AFSBulkStats statStruct;
2302 AFSCBs callbackStruct;
2305 cm_callbackRequest_t cbReq;
2311 struct rx_connection * callp;
2312 int inlinebulk = 0; /* Did we use InlineBulkStatus RPC or not? */
2314 osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%p", dscp);
2316 /* should be on a buffer boundary */
2317 osi_assertx((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0, "invalid offset");
2319 memset(&bb, 0, sizeof(bb));
2320 bb.bufOffset = *offsetp;
2322 lock_ReleaseMutex(&dscp->mx);
2323 /* first, assemble the file IDs we need to stat */
2324 code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) &bb, offsetp, userp, reqp, NULL);
2326 /* if we failed, bail out early */
2327 if (code && code != CM_ERROR_STOPNOW) {
2328 lock_ObtainMutex(&dscp->mx);
2332 /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
2333 * make the calls to create the entries. Handle AFSCBMAX files at a
2337 while (filex < bb.counter) {
2338 filesThisCall = bb.counter - filex;
2339 if (filesThisCall > AFSCBMAX)
2340 filesThisCall = AFSCBMAX;
2342 fidStruct.AFSCBFids_len = filesThisCall;
2343 fidStruct.AFSCBFids_val = &bb.fids[filex];
2344 statStruct.AFSBulkStats_len = filesThisCall;
2345 statStruct.AFSBulkStats_val = &bb.stats[filex];
2346 callbackStruct.AFSCBs_len = filesThisCall;
2347 callbackStruct.AFSCBs_val = &bb.callbacks[filex];
2348 cm_StartCallbackGrantingCall(NULL, &cbReq);
2349 osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
2351 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2355 callp = cm_GetRxConn(connp);
2356 if (!(connp->serverp->flags & CM_SERVERFLAG_NOINLINEBULK)) {
2357 code = RXAFS_InlineBulkStatus(callp, &fidStruct,
2358 &statStruct, &callbackStruct, &volSync);
2359 if (code == RXGEN_OPCODE) {
2360 cm_SetServerNoInlineBulk(connp->serverp, 0);
2366 code = RXAFS_BulkStatus(callp, &fidStruct,
2367 &statStruct, &callbackStruct, &volSync);
2369 rx_PutConnection(callp);
2371 } while (cm_Analyze(connp, userp, reqp, &dscp->fid,
2372 &volSync, NULL, &cbReq, code));
2373 code = cm_MapRPCError(code, reqp);
2375 osi_Log2(afsd_logp, "CALL %sBulkStatus FAILURE code 0x%x",
2376 inlinebulk ? "Inline" : "", code);
2378 osi_Log1(afsd_logp, "CALL %sBulkStatus SUCCESS", inlinebulk ? "Inline" : "");
2380 /* may as well quit on an error, since we're not going to do
2381 * much better on the next immediate call, either.
2384 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2388 /* otherwise, we should do the merges */
2389 for (i = 0; i<filesThisCall; i++) {
2391 tfid.cell = dscp->fid.cell;
2392 tfid.volume = bb.fids[j].Volume;
2393 tfid.vnode = bb.fids[j].Vnode;
2394 tfid.unique = bb.fids[j].Unique;
2395 code = cm_GetSCache(&tfid, &scp, userp, reqp);
2399 /* otherwise, if this entry has no callback info,
2402 lock_ObtainMutex(&scp->mx);
2403 /* now, we have to be extra paranoid on merging in this
2404 * information, since we didn't use cm_SyncOp before
2405 * starting the fetch to make sure that no bad races
2406 * were occurring. Specifically, we need to make sure
2407 * we don't obliterate any newer information in the
2408 * vnode than have here.
2410 * Right now, be pretty conservative: if there's a
2411 * callback or a pending call, skip it.
2413 if ((scp->cbServerp == NULL || (scp->flags & CM_SCACHEFLAG_EACCESS))
2415 (CM_SCACHEFLAG_FETCHING
2416 | CM_SCACHEFLAG_STORING
2417 | CM_SCACHEFLAG_SIZESTORING))) {
2418 cm_EndCallbackGrantingCall(scp, &cbReq,
2420 CM_CALLBACK_MAINTAINCOUNT);
2421 cm_MergeStatus(dscp, scp, &bb.stats[j], &volSync, userp, 0);
2423 lock_ReleaseMutex(&scp->mx);
2424 cm_ReleaseSCache(scp);
2425 } /* all files in the response */
2426 /* now tell it to drop the count,
2427 * after doing the vnode processing above */
2428 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2430 filex += filesThisCall;
2431 } /* while there are still more files to process */
2432 lock_ObtainMutex(&dscp->mx);
2434 /* If we did the InlineBulk RPC pull out the return code and log it */
2436 if ((&bb.stats[0])->errorCode) {
2437 osi_Log1(afsd_logp, "cm_TryBulkStat bulk stat error: %d",
2438 (&bb.stats[0])->errorCode);
2442 osi_Log0(afsd_logp, "END cm_TryBulkStat");
2446 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
2450 /* initialize store back mask as inexpensive local variable */
2452 memset(statusp, 0, sizeof(AFSStoreStatus));
2454 /* copy out queued info from scache first, if scp passed in */
2456 if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
2457 statusp->ClientModTime = scp->clientModTime;
2458 mask |= AFS_SETMODTIME;
2459 scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
2464 /* now add in our locally generated request */
2465 if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
2466 statusp->ClientModTime = attrp->clientModTime;
2467 mask |= AFS_SETMODTIME;
2469 if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
2470 statusp->UnixModeBits = attrp->unixModeBits;
2471 mask |= AFS_SETMODE;
2473 if (attrp->mask & CM_ATTRMASK_OWNER) {
2474 statusp->Owner = attrp->owner;
2475 mask |= AFS_SETOWNER;
2477 if (attrp->mask & CM_ATTRMASK_GROUP) {
2478 statusp->Group = attrp->group;
2479 mask |= AFS_SETGROUP;
2482 statusp->Mask = mask;
2485 /* set the file size, and make sure that all relevant buffers have been
2486 * truncated. Ensure that any partially truncated buffers have been zeroed
2487 * to the end of the buffer.
2489 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
2495 /* start by locking out buffer creation */
2496 lock_ObtainWrite(&scp->bufCreateLock);
2498 /* verify that this is a file, not a dir or a symlink */
2499 lock_ObtainMutex(&scp->mx);
2500 code = cm_SyncOp(scp, NULL, userp, reqp, 0,
2501 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2504 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2506 if (scp->fileType != CM_SCACHETYPE_FILE) {
2507 code = CM_ERROR_ISDIR;
2512 if (LargeIntegerLessThan(*sizep, scp->length))
2517 lock_ReleaseMutex(&scp->mx);
2519 /* can't hold scp->mx lock here, since we may wait for a storeback to
2520 * finish if the buffer package is cleaning a buffer by storing it to
2524 buf_Truncate(scp, userp, reqp, sizep);
2526 /* now ensure that file length is short enough, and update truncPos */
2527 lock_ObtainMutex(&scp->mx);
2529 /* make sure we have a callback (so we have the right value for the
2530 * length), and wait for it to be safe to do a truncate.
2532 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
2533 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2534 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2536 /* If we only have 'i' bits, then we should still be able to set
2537 the size of a file we created. */
2538 if (code == CM_ERROR_NOACCESS && scp->creator == userp) {
2539 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
2540 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2541 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2547 if (LargeIntegerLessThan(*sizep, scp->length)) {
2548 /* a real truncation. If truncPos is not set yet, or is bigger
2549 * than where we're truncating the file, set truncPos to this
2554 if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
2555 || LargeIntegerLessThan(*sizep, scp->length)) {
2557 scp->truncPos = *sizep;
2558 scp->mask |= CM_SCACHEMASK_TRUNCPOS;
2560 /* in either case, the new file size has been changed */
2561 scp->length = *sizep;
2562 scp->mask |= CM_SCACHEMASK_LENGTH;
2564 else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
2565 /* really extending the file */
2566 scp->length = *sizep;
2567 scp->mask |= CM_SCACHEMASK_LENGTH;
2570 /* done successfully */
2573 cm_SyncOpDone(scp, NULL,
2574 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2575 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2578 lock_ReleaseMutex(&scp->mx);
2579 lock_ReleaseWrite(&scp->bufCreateLock);
2584 /* set the file size or other attributes (but not both at once) */
2585 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
2589 AFSFetchStatus afsOutStatus;
2593 AFSStoreStatus afsInStatus;
2594 struct rx_connection * callp;
2596 /* handle file length setting */
2597 if (attrp->mask & CM_ATTRMASK_LENGTH)
2598 return cm_SetLength(scp, &attrp->length, userp, reqp);
2600 lock_ObtainMutex(&scp->mx);
2601 /* otherwise, we have to make an RPC to get the status */
2602 code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
2604 lock_ReleaseMutex(&scp->mx);
2608 /* make the attr structure */
2609 cm_StatusFromAttr(&afsInStatus, scp, attrp);
2611 tfid.Volume = scp->fid.volume;
2612 tfid.Vnode = scp->fid.vnode;
2613 tfid.Unique = scp->fid.unique;
2614 lock_ReleaseMutex(&scp->mx);
2616 /* now make the RPC */
2617 osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%p", scp);
2619 code = cm_ConnFromFID(&scp->fid, userp, reqp, &connp);
2623 callp = cm_GetRxConn(connp);
2624 code = RXAFS_StoreStatus(callp, &tfid,
2625 &afsInStatus, &afsOutStatus, &volSync);
2626 rx_PutConnection(callp);
2628 } while (cm_Analyze(connp, userp, reqp,
2629 &scp->fid, &volSync, NULL, NULL, code));
2630 code = cm_MapRPCError(code, reqp);
2633 osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
2635 osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
2637 lock_ObtainMutex(&scp->mx);
2638 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
2640 cm_MergeStatus(NULL, scp, &afsOutStatus, &volSync, userp,
2641 CM_MERGEFLAG_FORCE|CM_MERGEFLAG_STOREDATA);
2643 /* if we're changing the mode bits, discard the ACL cache,
2644 * since we changed the mode bits.
2646 if (afsInStatus.Mask & AFS_SETMODE)
2647 cm_FreeAllACLEnts(scp);
2648 lock_ReleaseMutex(&scp->mx);
2652 long cm_Create(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2653 cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
2658 cm_callbackRequest_t cbReq;
2661 cm_scache_t *scp = NULL;
2663 AFSStoreStatus inStatus;
2664 AFSFetchStatus updatedDirStatus;
2665 AFSFetchStatus newFileStatus;
2666 AFSCallBack newFileCallback;
2668 struct rx_connection * callp;
2671 /* can't create names with @sys in them; must expand it manually first.
2672 * return "invalid request" if they try.
2674 if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2675 return CM_ERROR_ATSYS;
2678 /* before starting the RPC, mark that we're changing the file data, so
2679 * that someone who does a chmod will know to wait until our call
2682 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2683 lock_ObtainMutex(&dscp->mx);
2684 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2685 lock_ReleaseMutex(&dscp->mx);
2687 cm_StartCallbackGrantingCall(NULL, &cbReq);
2689 cm_EndDirOp(&dirop);
2696 cm_StatusFromAttr(&inStatus, NULL, attrp);
2698 /* try the RPC now */
2699 osi_Log1(afsd_logp, "CALL CreateFile scp 0x%p", dscp);
2701 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2705 dirAFSFid.Volume = dscp->fid.volume;
2706 dirAFSFid.Vnode = dscp->fid.vnode;
2707 dirAFSFid.Unique = dscp->fid.unique;
2709 callp = cm_GetRxConn(connp);
2710 code = RXAFS_CreateFile(connp->callp, &dirAFSFid, namep,
2711 &inStatus, &newAFSFid, &newFileStatus,
2712 &updatedDirStatus, &newFileCallback,
2714 rx_PutConnection(callp);
2716 } while (cm_Analyze(connp, userp, reqp,
2717 &dscp->fid, &volSync, NULL, &cbReq, code));
2718 code = cm_MapRPCError(code, reqp);
2721 osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
2723 osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
2726 lock_ObtainWrite(&dirop.scp->dirlock);
2727 dirop.lockType = CM_DIRLOCK_WRITE;
2729 lock_ObtainMutex(&dscp->mx);
2730 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2732 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
2734 lock_ReleaseMutex(&dscp->mx);
2736 /* now try to create the file's entry, too, but be careful to
2737 * make sure that we don't merge in old info. Since we weren't locking
2738 * out any requests during the file's creation, we may have pretty old
2742 newFid.cell = dscp->fid.cell;
2743 newFid.volume = dscp->fid.volume;
2744 newFid.vnode = newAFSFid.Vnode;
2745 newFid.unique = newAFSFid.Unique;
2746 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2748 lock_ObtainMutex(&scp->mx);
2749 scp->creator = userp; /* remember who created it */
2750 if (!cm_HaveCallback(scp)) {
2751 cm_MergeStatus(dscp, scp, &newFileStatus, &volSync,
2753 cm_EndCallbackGrantingCall(scp, &cbReq,
2754 &newFileCallback, 0);
2757 lock_ReleaseMutex(&scp->mx);
2762 /* make sure we end things properly */
2764 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2766 if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
2767 cm_DirCreateEntry(&dirop, namep, &newFid);
2769 cm_BPlusDirCreateEntry(&dirop, namep, &newFid);
2772 cm_EndDirOp(&dirop);
2777 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
2781 lock_ObtainWrite(&scp->bufCreateLock);
2782 code = buf_CleanVnode(scp, userp, reqp);
2783 lock_ReleaseWrite(&scp->bufCreateLock);
2785 lock_ObtainMutex(&scp->mx);
2787 if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
2788 | CM_SCACHEMASK_CLIENTMODTIME
2789 | CM_SCACHEMASK_LENGTH))
2790 code = cm_StoreMini(scp, userp, reqp);
2792 if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) {
2793 code = (scp->flags & CM_SCACHEFLAG_OVERQUOTA) ? CM_ERROR_QUOTA : CM_ERROR_SPACE;
2794 scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE);
2797 lock_ReleaseMutex(&scp->mx);
2802 long cm_MakeDir(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2803 cm_user_t *userp, cm_req_t *reqp)
2808 cm_callbackRequest_t cbReq;
2811 cm_scache_t *scp = NULL;
2813 AFSStoreStatus inStatus;
2814 AFSFetchStatus updatedDirStatus;
2815 AFSFetchStatus newDirStatus;
2816 AFSCallBack newDirCallback;
2818 struct rx_connection * callp;
2821 /* can't create names with @sys in them; must expand it manually first.
2822 * return "invalid request" if they try.
2824 if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2825 return CM_ERROR_ATSYS;
2828 /* before starting the RPC, mark that we're changing the directory
2829 * data, so that someone who does a chmod on the dir will wait until
2830 * our call completes.
2832 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2833 lock_ObtainMutex(&dscp->mx);
2834 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2835 lock_ReleaseMutex(&dscp->mx);
2837 cm_StartCallbackGrantingCall(NULL, &cbReq);
2839 cm_EndDirOp(&dirop);
2846 cm_StatusFromAttr(&inStatus, NULL, attrp);
2848 /* try the RPC now */
2849 osi_Log1(afsd_logp, "CALL MakeDir scp 0x%p", dscp);
2851 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2855 dirAFSFid.Volume = dscp->fid.volume;
2856 dirAFSFid.Vnode = dscp->fid.vnode;
2857 dirAFSFid.Unique = dscp->fid.unique;
2859 callp = cm_GetRxConn(connp);
2860 code = RXAFS_MakeDir(connp->callp, &dirAFSFid, namep,
2861 &inStatus, &newAFSFid, &newDirStatus,
2862 &updatedDirStatus, &newDirCallback,
2864 rx_PutConnection(callp);
2866 } while (cm_Analyze(connp, userp, reqp,
2867 &dscp->fid, &volSync, NULL, &cbReq, code));
2868 code = cm_MapRPCError(code, reqp);
2871 osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
2873 osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
2876 lock_ObtainWrite(&dirop.scp->dirlock);
2877 dirop.lockType = CM_DIRLOCK_WRITE;
2879 lock_ObtainMutex(&dscp->mx);
2880 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2882 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
2884 lock_ReleaseMutex(&dscp->mx);
2886 /* now try to create the new dir's entry, too, but be careful to
2887 * make sure that we don't merge in old info. Since we weren't locking
2888 * out any requests during the file's creation, we may have pretty old
2892 newFid.cell = dscp->fid.cell;
2893 newFid.volume = dscp->fid.volume;
2894 newFid.vnode = newAFSFid.Vnode;
2895 newFid.unique = newAFSFid.Unique;
2896 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2898 lock_ObtainMutex(&scp->mx);
2899 if (!cm_HaveCallback(scp)) {
2900 cm_MergeStatus(dscp, scp, &newDirStatus, &volSync,
2902 cm_EndCallbackGrantingCall(scp, &cbReq,
2903 &newDirCallback, 0);
2906 lock_ReleaseMutex(&scp->mx);
2907 cm_ReleaseSCache(scp);
2911 /* make sure we end things properly */
2913 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2915 if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
2916 cm_DirCreateEntry(&dirop, namep, &newFid);
2918 cm_BPlusDirCreateEntry(&dirop, namep, &newFid);
2921 cm_EndDirOp(&dirop);
2923 /* and return error code */
2927 long cm_Link(cm_scache_t *dscp, char *namep, cm_scache_t *sscp, long flags,
2928 cm_user_t *userp, cm_req_t *reqp)
2933 AFSFid existingAFSFid;
2934 AFSFetchStatus updatedDirStatus;
2935 AFSFetchStatus newLinkStatus;
2937 struct rx_connection * callp;
2940 if (dscp->fid.cell != sscp->fid.cell ||
2941 dscp->fid.volume != sscp->fid.volume) {
2942 return CM_ERROR_CROSSDEVLINK;
2945 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2946 lock_ObtainMutex(&dscp->mx);
2947 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2948 lock_ReleaseMutex(&dscp->mx);
2950 cm_EndDirOp(&dirop);
2955 /* try the RPC now */
2956 osi_Log1(afsd_logp, "CALL Link scp 0x%p", dscp);
2958 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2961 dirAFSFid.Volume = dscp->fid.volume;
2962 dirAFSFid.Vnode = dscp->fid.vnode;
2963 dirAFSFid.Unique = dscp->fid.unique;
2965 existingAFSFid.Volume = sscp->fid.volume;
2966 existingAFSFid.Vnode = sscp->fid.vnode;
2967 existingAFSFid.Unique = sscp->fid.unique;
2969 callp = cm_GetRxConn(connp);
2970 code = RXAFS_Link(callp, &dirAFSFid, namep, &existingAFSFid,
2971 &newLinkStatus, &updatedDirStatus, &volSync);
2972 rx_PutConnection(callp);
2973 osi_Log1(smb_logp," RXAFS_Link returns 0x%x", code);
2975 } while (cm_Analyze(connp, userp, reqp,
2976 &dscp->fid, &volSync, NULL, NULL, code));
2978 code = cm_MapRPCError(code, reqp);
2981 osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
2983 osi_Log0(afsd_logp, "CALL Link SUCCESS");
2986 lock_ObtainWrite(&dirop.scp->dirlock);
2987 dirop.lockType = CM_DIRLOCK_WRITE;
2989 lock_ObtainMutex(&dscp->mx);
2990 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2992 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
2994 lock_ReleaseMutex(&dscp->mx);
2997 if (cm_CheckDirOpForSingleChange(&dirop)) {
2998 cm_DirCreateEntry(&dirop, namep, &sscp->fid);
3000 cm_BPlusDirCreateEntry(&dirop, namep, &sscp->fid);
3004 cm_EndDirOp(&dirop);
3009 long cm_SymLink(cm_scache_t *dscp, char *namep, char *contentsp, long flags,
3010 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
3018 AFSStoreStatus inStatus;
3019 AFSFetchStatus updatedDirStatus;
3020 AFSFetchStatus newLinkStatus;
3022 struct rx_connection * callp;
3025 /* before starting the RPC, mark that we're changing the directory data,
3026 * so that someone who does a chmod on the dir will wait until our
3029 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
3030 lock_ObtainMutex(&dscp->mx);
3031 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3032 lock_ReleaseMutex(&dscp->mx);
3034 cm_EndDirOp(&dirop);
3039 cm_StatusFromAttr(&inStatus, NULL, attrp);
3041 /* try the RPC now */
3042 osi_Log1(afsd_logp, "CALL Symlink scp 0x%p", dscp);
3044 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3048 dirAFSFid.Volume = dscp->fid.volume;
3049 dirAFSFid.Vnode = dscp->fid.vnode;
3050 dirAFSFid.Unique = dscp->fid.unique;
3052 callp = cm_GetRxConn(connp);
3053 code = RXAFS_Symlink(callp, &dirAFSFid, namep, contentsp,
3054 &inStatus, &newAFSFid, &newLinkStatus,
3055 &updatedDirStatus, &volSync);
3056 rx_PutConnection(callp);
3058 } while (cm_Analyze(connp, userp, reqp,
3059 &dscp->fid, &volSync, NULL, NULL, code));
3060 code = cm_MapRPCError(code, reqp);
3063 osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
3065 osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
3068 lock_ObtainWrite(&dirop.scp->dirlock);
3069 dirop.lockType = CM_DIRLOCK_WRITE;
3071 lock_ObtainMutex(&dscp->mx);
3072 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3074 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
3076 lock_ReleaseMutex(&dscp->mx);
3079 if (cm_CheckDirOpForSingleChange(&dirop)) {
3080 newFid.cell = dscp->fid.cell;
3081 newFid.volume = dscp->fid.volume;
3082 newFid.vnode = newAFSFid.Vnode;
3083 newFid.unique = newAFSFid.Unique;
3085 cm_DirCreateEntry(&dirop, namep, &newFid);
3087 cm_BPlusDirCreateEntry(&dirop, namep, &newFid);
3091 cm_EndDirOp(&dirop);
3093 /* now try to create the new dir's entry, too, but be careful to
3094 * make sure that we don't merge in old info. Since we weren't locking
3095 * out any requests during the file's creation, we may have pretty old
3099 newFid.cell = dscp->fid.cell;
3100 newFid.volume = dscp->fid.volume;
3101 newFid.vnode = newAFSFid.Vnode;
3102 newFid.unique = newAFSFid.Unique;
3103 code = cm_GetSCache(&newFid, &scp, userp, reqp);
3105 lock_ObtainMutex(&scp->mx);
3106 if (!cm_HaveCallback(scp)) {
3107 cm_MergeStatus(dscp, scp, &newLinkStatus, &volSync,
3110 lock_ReleaseMutex(&scp->mx);
3111 cm_ReleaseSCache(scp);
3115 /* and return error code */
3119 long cm_RemoveDir(cm_scache_t *dscp, char *namep, cm_user_t *userp,
3126 AFSFetchStatus updatedDirStatus;
3128 struct rx_connection * callp;
3131 /* before starting the RPC, mark that we're changing the directory data,
3132 * so that someone who does a chmod on the dir will wait until our
3135 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
3136 lock_ObtainMutex(&dscp->mx);
3137 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3138 lock_ReleaseMutex(&dscp->mx);
3140 cm_EndDirOp(&dirop);
3145 /* try the RPC now */
3146 osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%p", dscp);
3148 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3152 dirAFSFid.Volume = dscp->fid.volume;
3153 dirAFSFid.Vnode = dscp->fid.vnode;
3154 dirAFSFid.Unique = dscp->fid.unique;
3156 callp = cm_GetRxConn(connp);
3157 code = RXAFS_RemoveDir(callp, &dirAFSFid, namep,
3158 &updatedDirStatus, &volSync);
3159 rx_PutConnection(callp);
3161 } while (cm_Analyze(connp, userp, reqp,
3162 &dscp->fid, &volSync, NULL, NULL, code));
3163 code = cm_MapRPCErrorRmdir(code, reqp);
3166 osi_Log1(afsd_logp, "CALL RemoveDir FAILURE, code 0x%x", code);
3168 osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
3171 lock_ObtainWrite(&dirop.scp->dirlock);
3172 dirop.lockType = CM_DIRLOCK_WRITE;
3174 lock_ObtainMutex(&dscp->mx);
3175 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3177 cm_dnlcRemove(dscp, namep);
3178 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
3180 lock_ReleaseMutex(&dscp->mx);
3183 if (cm_CheckDirOpForSingleChange(&dirop)) {
3184 cm_DirDeleteEntry(&dirop, namep);
3186 cm_BPlusDirDeleteEntry(&dirop, namep);
3190 cm_EndDirOp(&dirop);
3192 /* and return error code */
3196 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
3198 /* grab mutex on contents */
3199 lock_ObtainMutex(&scp->mx);
3201 /* reset the prefetch info */
3202 scp->prefetch.base.LowPart = 0; /* base */
3203 scp->prefetch.base.HighPart = 0;
3204 scp->prefetch.end.LowPart = 0; /* and end */
3205 scp->prefetch.end.HighPart = 0;
3207 /* release mutex on contents */
3208 lock_ReleaseMutex(&scp->mx);
3214 long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
3215 char *newNamep, cm_user_t *userp, cm_req_t *reqp)
3219 AFSFid oldDirAFSFid;
3220 AFSFid newDirAFSFid;
3222 AFSFetchStatus updatedOldDirStatus;
3223 AFSFetchStatus updatedNewDirStatus;
3226 struct rx_connection * callp;
3227 cm_dirOp_t oldDirOp;
3230 cm_dirOp_t newDirOp;
3232 /* before starting the RPC, mark that we're changing the directory data,
3233 * so that someone who does a chmod on the dir will wait until our call
3234 * completes. We do this in vnode order so that we don't deadlock,
3235 * which makes the code a little verbose.
3237 if (oldDscp == newDscp) {
3238 /* check for identical names */
3239 if (strcmp(oldNamep, newNamep) == 0)
3240 return CM_ERROR_RENAME_IDENTICAL;
3243 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3244 lock_ObtainMutex(&oldDscp->mx);
3245 cm_dnlcRemove(oldDscp, oldNamep);
3246 cm_dnlcRemove(oldDscp, newNamep);
3247 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3248 CM_SCACHESYNC_STOREDATA);
3249 lock_ReleaseMutex(&oldDscp->mx);
3251 cm_EndDirOp(&oldDirOp);
3255 /* two distinct dir vnodes */
3257 if (oldDscp->fid.cell != newDscp->fid.cell ||
3258 oldDscp->fid.volume != newDscp->fid.volume)
3259 return CM_ERROR_CROSSDEVLINK;
3261 /* shouldn't happen that we have distinct vnodes for two
3262 * different files, but could due to deliberate attack, or
3263 * stale info. Avoid deadlocks and quit now.
3265 if (oldDscp->fid.vnode == newDscp->fid.vnode)
3266 return CM_ERROR_CROSSDEVLINK;
3268 if (oldDscp->fid.vnode < newDscp->fid.vnode) {
3269 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3270 lock_ObtainMutex(&oldDscp->mx);
3271 cm_dnlcRemove(oldDscp, oldNamep);
3272 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3273 CM_SCACHESYNC_STOREDATA);
3274 lock_ReleaseMutex(&oldDscp->mx);
3276 cm_EndDirOp(&oldDirOp);
3278 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE, &newDirOp);
3279 lock_ObtainMutex(&newDscp->mx);
3280 cm_dnlcRemove(newDscp, newNamep);
3281 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3282 CM_SCACHESYNC_STOREDATA);
3283 lock_ReleaseMutex(&newDscp->mx);
3285 cm_EndDirOp(&newDirOp);
3287 /* cleanup first one */
3288 lock_ObtainMutex(&oldDscp->mx);
3289 cm_SyncOpDone(oldDscp, NULL,
3290 CM_SCACHESYNC_STOREDATA);
3291 lock_ReleaseMutex(&oldDscp->mx);
3292 cm_EndDirOp(&oldDirOp);
3297 /* lock the new vnode entry first */
3298 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE, &newDirOp);
3299 lock_ObtainMutex(&newDscp->mx);
3300 cm_dnlcRemove(newDscp, newNamep);
3301 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3302 CM_SCACHESYNC_STOREDATA);
3303 lock_ReleaseMutex(&newDscp->mx);
3305 cm_EndDirOp(&newDirOp);
3307 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3308 lock_ObtainMutex(&oldDscp->mx);
3309 cm_dnlcRemove(oldDscp, oldNamep);
3310 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3311 CM_SCACHESYNC_STOREDATA);
3312 lock_ReleaseMutex(&oldDscp->mx);
3314 cm_EndDirOp(&oldDirOp);
3316 /* cleanup first one */
3317 lock_ObtainMutex(&newDscp->mx);
3318 cm_SyncOpDone(newDscp, NULL,
3319 CM_SCACHESYNC_STOREDATA);
3320 lock_ReleaseMutex(&newDscp->mx);
3321 cm_EndDirOp(&newDirOp);
3325 } /* two distinct vnodes */
3332 /* try the RPC now */
3333 osi_Log2(afsd_logp, "CALL Rename old scp 0x%p new scp 0x%p",
3336 code = cm_ConnFromFID(&oldDscp->fid, userp, reqp, &connp);
3340 oldDirAFSFid.Volume = oldDscp->fid.volume;
3341 oldDirAFSFid.Vnode = oldDscp->fid.vnode;
3342 oldDirAFSFid.Unique = oldDscp->fid.unique;
3343 newDirAFSFid.Volume = newDscp->fid.volume;
3344 newDirAFSFid.Vnode = newDscp->fid.vnode;
3345 newDirAFSFid.Unique = newDscp->fid.unique;
3347 callp = cm_GetRxConn(connp);
3348 code = RXAFS_Rename(callp, &oldDirAFSFid, oldNamep,
3349 &newDirAFSFid, newNamep,
3350 &updatedOldDirStatus, &updatedNewDirStatus,
3352 rx_PutConnection(callp);
3354 } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
3355 &volSync, NULL, NULL, code));
3356 code = cm_MapRPCError(code, reqp);
3359 osi_Log1(afsd_logp, "CALL Rename FAILURE, code 0x%x", code);
3361 osi_Log0(afsd_logp, "CALL Rename SUCCESS");
3363 /* update the individual stat cache entries for the directories */
3365 lock_ObtainWrite(&oldDirOp.scp->dirlock);
3366 oldDirOp.lockType = CM_DIRLOCK_WRITE;
3368 lock_ObtainMutex(&oldDscp->mx);
3369 cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
3372 cm_MergeStatus(NULL, oldDscp, &updatedOldDirStatus, &volSync,
3373 userp, CM_MERGEFLAG_DIROP);
3374 lock_ReleaseMutex(&oldDscp->mx);
3377 if (cm_CheckDirOpForSingleChange(&oldDirOp)) {
3380 diropCode = cm_BPlusDirLookup(&oldDirOp, oldNamep, &fileFid);
3381 if (diropCode == CM_ERROR_INEXACT_MATCH)
3383 else if (diropCode == EINVAL)
3385 diropCode = cm_DirLookup(&oldDirOp, oldNamep, &fileFid);
3387 if (diropCode == 0) {
3389 diropCode = cm_DirCreateEntry(&oldDirOp, newNamep, &fileFid);
3391 cm_BPlusDirCreateEntry(&oldDirOp, newNamep, &fileFid);
3395 if (diropCode == 0) {
3396 diropCode = cm_DirDeleteEntry(&oldDirOp, oldNamep);
3398 cm_BPlusDirDeleteEntry(&oldDirOp, oldNamep);
3404 cm_EndDirOp(&oldDirOp);
3406 /* and update it for the new one, too, if necessary */
3409 lock_ObtainWrite(&newDirOp.scp->dirlock);
3410 newDirOp.lockType = CM_DIRLOCK_WRITE;
3412 lock_ObtainMutex(&newDscp->mx);
3413 cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
3415 cm_MergeStatus(NULL, newDscp, &updatedNewDirStatus, &volSync,
3416 userp, CM_MERGEFLAG_DIROP);
3417 lock_ReleaseMutex(&newDscp->mx);
3420 /* we only make the local change if we successfully made
3421 the change in the old directory AND there was only one
3422 change in the new directory */
3423 if (diropCode == 0 && cm_CheckDirOpForSingleChange(&newDirOp)) {
3424 cm_DirCreateEntry(&newDirOp, newNamep, &fileFid);
3426 cm_BPlusDirCreateEntry(&newDirOp, newNamep, &fileFid);
3430 cm_EndDirOp(&newDirOp);
3433 /* and return error code */
3437 /* Byte range locks:
3439 The OpenAFS Windows client has to fake byte range locks given no
3440 server side support for such locks. This is implemented as keyed
3441 byte range locks on the cache manager.
3443 Keyed byte range locks:
3445 Each cm_scache_t structure keeps track of a list of keyed locks.
3446 The key for a lock identifies an owner of a set of locks (referred
3447 to as a client). Each key is represented by a value. The set of
3448 key values used within a specific cm_scache_t structure form a
3449 namespace that has a scope of just that cm_scache_t structure. The
3450 same key value can be used with another cm_scache_t structure and
3451 correspond to a completely different client. However it is
3452 advantageous for the SMB or IFS layer to make sure that there is a
3453 1-1 mapping between client and keys over all cm_scache_t objects.
3455 Assume a client C has key Key(C) (although, since the scope of the
3456 key is a cm_scache_t, the key can be Key(C,S), where S is the
3457 cm_scache_t. But assume a 1-1 relation between keys and clients).
3458 A byte range (O,+L) denotes byte addresses (O) through (O+L-1)
3459 inclusive (a.k.a. [O,O+L-1]). The function Key(x) is implemented
3460 through cm_generateKey() function for both SMB and IFS.
3462 The list of locks for a cm_scache_t object S is maintained in
3463 S->fileLocks. The cache manager will set a lock on the AFS file
3464 server in order to assert the locks in S->fileLocks. If only
3465 shared locks are in place for S, then the cache manager will obtain
3466 a LockRead lock, while if there are any exclusive locks, it will
3467 obtain a LockWrite lock. If the exclusive locks are all released
3468 while the shared locks remain, then the cache manager will
3469 downgrade the lock from LockWrite to LockRead. Similarly, if an
3470 exclusive lock is obtained when only shared locks exist, then the
3471 cache manager will try to upgrade the lock from LockRead to
3474 Each lock L owned by client C maintains a key L->key such that
3475 L->key == Key(C), the effective range defined by L->LOffset and
3476 L->LLength such that the range of bytes affected by the lock is
3477 (L->LOffset, +L->LLength), a type maintained in L->LockType which
3478 is either exclusive or shared.
3482 A lock exists iff it is in S->fileLocks for some cm_scache_t
3483 S. Existing locks are in one of the following states: ACTIVE,
3484 WAITLOCK, WAITUNLOCK, LOST, DELETED.
3486 The following sections describe each lock and the associated
3489 1. ACTIVE: A lock L is ACTIVE iff the cache manager has asserted
3490 the lock with the AFS file server. This type of lock can be
3491 exercised by a client to read or write to the locked region (as
3494 1.1 ACTIVE->LOST: When the AFS file server fails to extend a
3495 server lock that was required to assert the lock. Before
3496 marking the lock as lost, the cache manager checks if the file
3497 has changed on the server. If the file has not changed, then
3498 the cache manager will attempt to obtain a new server lock
3499 that is sufficient to assert the client side locks for the
3500 file. If any of these fail, the lock is marked as LOST.
3501 Otherwise, it is left as ACTIVE.
3503 1.2 ACTIVE->DELETED: Lock is released.
3505 2. WAITLOCK: A lock is in a WAITLOCK state if the cache manager
3506 grants the lock but the lock is yet to be asserted with the AFS
3507 file server. Once the file server grants the lock, the state
3508 will transition to an ACTIVE lock.
3510 2.1 WAITLOCK->ACTIVE: The server granted the lock.
3512 2.2 WAITLOCK->DELETED: Lock is abandoned, or timed out during
3515 2.3 WAITLOCK->LOST: One or more locks from this client were
3516 marked as LOST. No further locks will be granted to this
3517 client until all lost locks are removed.
3519 3. WAITUNLOCK: A lock is in a WAITUNLOCK state if the cache manager
3520 receives a request for a lock that conflicts with an existing
3521 ACTIVE or WAITLOCK lock. The lock will be placed in the queue
3522 and will be granted at such time the conflicting locks are
3523 removed, at which point the state will transition to either
3526 3.1 WAITUNLOCK->ACTIVE: The conflicting lock was removed. The
3527 current serverLock is sufficient to assert this lock, or a
3528 sufficient serverLock is obtained.
3530 3.2 WAITUNLOCK->WAITLOCK: The conflicting lock was removed,
3531 however the required serverLock is yet to be asserted with the
3534 3.3 WAITUNLOCK->DELETED: The lock is abandoned, timed out or
3537 3.5 WAITUNLOCK->LOST: One or more locks from this client were
3538 marked as LOST. No further locks will be granted to this
3539 client until all lost locks are removed.
3541 4. LOST: A lock L is LOST if the server lock that was required to
3542 assert the lock could not be obtained or if it could not be
3543 extended, or if other locks by the same client were LOST.
3544 Essentially, once a lock is LOST, the contract between the cache
3545 manager and that specific client is no longer valid.
3547 The cache manager rechecks the server lock once every minute and
3548 extends it as appropriate. If this is not done for 5 minutes,
3549 the AFS file server will release the lock (the 5 minute timeout
3550 is based on current file server code and is fairly arbitrary).
3551 Once released, the lock cannot be re-obtained without verifying
3552 that the contents of the file hasn't been modified since the
3553 time the lock was released. Re-obtaining the lock without
3554 verifying this may lead to data corruption. If the lock can not
3555 be obtained safely, then all active locks for the cm_scache_t
3558 4.1 LOST->DELETED: The lock is released.
3560 5. DELETED: The lock is no longer relevant. Eventually, it will
3561 get removed from the cm_scache_t. In the meantime, it will be
3562 treated as if it does not exist.
3564 5.1 DELETED->not exist: The lock is removed from the
3567 The following are classifications of locks based on their state.
3569 6* A lock L is ACCEPTED if it is ACTIVE or WAITLOCK. These locks
3570 have been accepted by the cache manager, but may or may not have
3571 been granted back to the client.
3573 7* A lock L is QUEUED if it is ACTIVE, WAITLOCK or WAITUNLOCK.
3575 8* A lock L is WAITING if it is WAITLOCK or WAITUNLOCK.
3579 A client C can READ range (Offset,+Length) of a file represented by
3580 cm_scache_t S iff (1):
3582 1. for all _a_ in (Offset,+Length), all of the following is true:
3584 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3585 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3588 1.2 For each LOST lock L in S->fileLocks such that _a_ in
3589 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3592 (When locks are lost on an cm_scache_t, all locks are lost. By
3593 4.2 (below), if there is an exclusive LOST lock, then there
3594 can't be any overlapping ACTIVE locks.)
3596 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
3598 2. for all _a_ in (Offset,+Length), one of the following is true:
3600 2.1 Byte _a_ of S is unowned (as specified in 1.1) AND there
3601 does not exist a LOST lock L such that _a_ in
3602 (L->LOffset,+L->LLength).
3604 2.2 Byte _a_ of S is owned by C under lock L (as specified in
3605 1.2) AND L->LockType is exclusive.
3607 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
3609 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
3612 3.1 If L->LockType is exclusive then there does NOT exist a
3613 ACCEPTED lock M in S->fileLocks such that _a_ in
3614 (M->LOffset,+M->LLength).
3616 (If we count all QUEUED locks then we hit cases such as
3617 cascading waiting locks where the locks later on in the queue
3618 can be granted without compromising file integrity. On the
3619 other hand if only ACCEPTED locks are considered, then locks
3620 that were received earlier may end up waiting for locks that
3621 were received later to be unlocked. The choice of ACCEPTED
3622 locks was made to mimic the Windows byte range lock
3625 3.2 If L->LockType is shared then for each ACCEPTED lock M in
3626 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
3627 M->LockType is shared.
3629 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
3631 4.1 M->key != Key(C)
3633 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
3634 and (M->LOffset,+M->LLength) do not intersect.
3636 (Note: If a client loses a lock, it loses all locks.
3637 Subsequently, it will not be allowed to obtain any more locks
3638 until all existing LOST locks that belong to the client are
3639 released. Once all locks are released by a single client,
3640 there exists no further contract between the client and AFS
3641 about the contents of the file, hence the client can then
3642 proceed to obtain new locks and establish a new contract.
3644 This doesn't quite work as you think it should, because most
3645 applications aren't built to deal with losing locks they
3646 thought they once had. For now, we don't have a good
3647 solution to lost locks.
3649 Also, for consistency reasons, we have to hold off on
3650 granting locks that overlap exclusive LOST locks.)
3652 A client C can only unlock locks L in S->fileLocks which have
3655 The representation and invariants are as follows:
3657 - Each cm_scache_t structure keeps:
3659 - A queue of byte-range locks (cm_scache_t::fileLocks) which
3660 are of type cm_file_lock_t.
3662 - A record of the highest server-side lock that has been
3663 obtained for this object (cm_scache_t::serverLock), which is
3664 one of (-1), LockRead, LockWrite.
3666 - A count of ACCEPTED exclusive and shared locks that are in the
3667 queue (cm_scache_t::sharedLocks and
3668 cm_scache_t::exclusiveLocks)
3670 - Each cm_file_lock_t structure keeps:
3672 - The type of lock (cm_file_lock_t::LockType)
3674 - The key associated with the lock (cm_file_lock_t::key)
3676 - The offset and length of the lock (cm_file_lock_t::LOffset
3677 and cm_file_lock_t::LLength)
3679 - The state of the lock.
3681 - Time of issuance or last successful extension
3683 Semantic invariants:
3685 I1. The number of ACCEPTED locks in S->fileLocks are
3686 (S->sharedLocks + S->exclusiveLocks)
3688 External invariants:
3690 I3. S->serverLock is the lock that we have asserted with the
3691 AFS file server for this cm_scache_t.
3693 I4. S->serverLock == LockRead iff there is at least one ACTIVE
3694 shared lock, but no ACTIVE exclusive locks.
3696 I5. S->serverLock == LockWrite iff there is at least one ACTIVE
3699 I6. If L is a LOST lock, then for each lock M in S->fileLocks,
3700 M->key == L->key IMPLIES M is LOST or DELETED.
3705 #define IS_LOCK_ACTIVE(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == 0)
3707 #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)
3709 #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)
3711 #define IS_LOCK_LOST(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_LOST)
3713 #define IS_LOCK_DELETED(lockp) (((lockp)->flags & CM_FILELOCK_FLAG_DELETED) == CM_FILELOCK_FLAG_DELETED)
3716 #define IS_LOCK_ACCEPTED(lockp) (IS_LOCK_ACTIVE(lockp) || IS_LOCK_WAITLOCK(lockp))
3719 #define IS_LOCK_CLIENTONLY(lockp) ((((lockp)->scp->flags & CM_SCACHEFLAG_RO) == CM_SCACHEFLAG_RO) || (((lockp)->flags & CM_FILELOCK_FLAG_CLIENTONLY) == CM_FILELOCK_FLAG_CLIENTONLY))
3722 #define INTERSECT_RANGE(r1,r2) (((r2).offset+(r2).length) > (r1).offset && ((r1).offset +(r1).length) > (r2).offset)
3725 #define CONTAINS_RANGE(r1,r2) (((r2).offset+(r2).length) <= ((r1).offset+(r1).length) && (r1).offset <= (r2).offset)
3727 #if defined(VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS) && !defined(LOCK_TESTING)
3728 #define SCP_SUPPORTS_BRLOCKS(scp) ((scp)->cbServerp && ((scp)->cbServerp->capabilities & VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS))
3730 #define SCP_SUPPORTS_BRLOCKS(scp) (1)
3733 #define SERVERLOCKS_ENABLED(scp) (!((scp)->flags & CM_SCACHEFLAG_RO) && cm_enableServerLocks && SCP_SUPPORTS_BRLOCKS(scp))
3735 #if defined(VICED_CAPABILITY_WRITELOCKACL)
3736 #define SCP_SUPPORTS_WRITELOCKACL(scp) ((scp)->cbServerp && ((scp->cbServerp->capabilities & VICED_CAPABILITY_WRITELOCKACL)))
3738 #define SCP_SUPPORTS_WRITELOCKACL(scp) (0)
3740 /* This should really be defined in any build that this code is being
3742 #error VICED_CAPABILITY_WRITELOCKACL not defined.
3745 static void cm_LockRangeSubtract(cm_range_t * pos, const cm_range_t * neg)
3747 afs_int64 int_begin;
3750 int_begin = MAX(pos->offset, neg->offset);
3751 int_end = MIN(pos->offset+pos->length, neg->offset+neg->length);
3753 if (int_begin < int_end) {
3754 if (int_begin == pos->offset) {
3755 pos->length = pos->offset + pos->length - int_end;
3756 pos->offset = int_end;
3757 } else if (int_end == pos->offset + pos->length) {
3758 pos->length = int_begin - pos->offset;
3761 /* We only subtract ranges if the resulting range is
3762 contiguous. If we try to support non-contigous ranges, we
3763 aren't actually improving performance. */
3767 /* Called with scp->mx held. Returns 0 if all is clear to read the
3768 specified range by the client identified by key.
3770 long cm_LockCheckRead(cm_scache_t *scp,
3771 LARGE_INTEGER LOffset,
3772 LARGE_INTEGER LLength,
3775 #ifndef ADVISORY_LOCKS
3777 cm_file_lock_t *fileLock;
3781 int substract_ranges = FALSE;
3783 range.offset = LOffset.QuadPart;
3784 range.length = LLength.QuadPart;
3788 1. for all _a_ in (Offset,+Length), all of the following is true:
3790 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3791 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3794 1.2 For each LOST lock L in S->fileLocks such that _a_ in
3795 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3800 lock_ObtainRead(&cm_scacheLock);
3802 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
3804 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
3806 if (INTERSECT_RANGE(range, fileLock->range)) {
3807 if (IS_LOCK_ACTIVE(fileLock)) {
3808 if (fileLock->key == key) {
3810 /* If there is an active lock for this client, it
3811 is safe to substract ranges.*/
3812 cm_LockRangeSubtract(&range, &fileLock->range);
3813 substract_ranges = TRUE;
3815 if (fileLock->lockType != LockRead) {
3816 code = CM_ERROR_LOCK_CONFLICT;
3820 /* even if the entire range is locked for reading,
3821 we still can't grant the lock at this point
3822 because the client may have lost locks. That
3823 is, unless we have already seen an active lock
3824 belonging to the client, in which case there
3825 can't be any lost locks for this client. */
3826 if (substract_ranges)
3827 cm_LockRangeSubtract(&range, &fileLock->range);
3829 } else if (IS_LOCK_LOST(fileLock) &&
3830 (fileLock->key == key || fileLock->lockType == LockWrite)) {
3831 code = CM_ERROR_BADFD;
3837 lock_ReleaseRead(&cm_scacheLock);
3839 osi_Log4(afsd_logp, "cm_LockCheckRead scp 0x%x offset %d length %d code 0x%x",
3840 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
3851 /* Called with scp->mx held. Returns 0 if all is clear to write the
3852 specified range by the client identified by key.
3854 long cm_LockCheckWrite(cm_scache_t *scp,
3855 LARGE_INTEGER LOffset,
3856 LARGE_INTEGER LLength,
3859 #ifndef ADVISORY_LOCKS
3861 cm_file_lock_t *fileLock;
3866 range.offset = LOffset.QuadPart;
3867 range.length = LLength.QuadPart;
3870 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
3872 2. for all _a_ in (Offset,+Length), one of the following is true:
3874 2.1 Byte _a_ of S is unowned AND there does not exist a LOST
3875 lock L such that _a_ in (L->LOffset,+L->LLength).
3877 2.2 Byte _a_ of S is owned by C under lock L AND L->LockType is
3881 lock_ObtainRead(&cm_scacheLock);
3883 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
3885 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
3887 if (INTERSECT_RANGE(range, fileLock->range)) {
3888 if (IS_LOCK_ACTIVE(fileLock)) {
3889 if (fileLock->key == key) {
3890 if (fileLock->lockType == LockWrite) {
3892 /* if there is an active lock for this client, it
3893 is safe to substract ranges */
3894 cm_LockRangeSubtract(&range, &fileLock->range);
3896 code = CM_ERROR_LOCK_CONFLICT;
3900 code = CM_ERROR_LOCK_CONFLICT;
3903 } else if (IS_LOCK_LOST(fileLock)) {
3904 code = CM_ERROR_BADFD;
3910 lock_ReleaseRead(&cm_scacheLock);
3912 osi_Log4(afsd_logp, "cm_LockCheckWrite scp 0x%x offset %d length %d code 0x%x",
3913 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
3925 static void cm_LockMarkSCacheLost(cm_scache_t * scp);
3927 /* Called with cm_scacheLock write locked */
3928 static cm_file_lock_t * cm_GetFileLock(void) {
3931 l = (cm_file_lock_t *) cm_freeFileLocks;
3933 osi_QRemove(&cm_freeFileLocks, &l->q);
3935 l = malloc(sizeof(cm_file_lock_t));
3936 osi_assertx(l, "null cm_file_lock_t");
3939 memset(l, 0, sizeof(cm_file_lock_t));
3944 /* Called with cm_scacheLock write locked */
3945 static void cm_PutFileLock(cm_file_lock_t *l) {
3946 osi_QAdd(&cm_freeFileLocks, &l->q);
3949 /* called with scp->mx held. May release it during processing, but
3950 leaves it held on exit. */
3951 long cm_IntSetLock(cm_scache_t * scp, cm_user_t * userp, int lockType,
3957 struct rx_connection * callp;
3960 tfid.Volume = scp->fid.volume;
3961 tfid.Vnode = scp->fid.vnode;
3962 tfid.Unique = scp->fid.unique;
3965 osi_Log2(afsd_logp, "CALL SetLock scp 0x%p for lock %d", scp, lockType);
3967 lock_ReleaseMutex(&scp->mx);
3970 code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
3974 callp = cm_GetRxConn(connp);
3975 code = RXAFS_SetLock(callp, &tfid, lockType,
3977 rx_PutConnection(callp);
3979 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
3982 code = cm_MapRPCError(code, reqp);
3984 osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
3986 osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
3989 lock_ObtainMutex(&scp->mx);
3994 /* called with scp->mx held. Releases it during processing */
3995 long cm_IntReleaseLock(cm_scache_t * scp, cm_user_t * userp,
4001 struct rx_connection * callp;
4004 tfid.Volume = scp->fid.volume;
4005 tfid.Vnode = scp->fid.vnode;
4006 tfid.Unique = scp->fid.unique;
4009 lock_ReleaseMutex(&scp->mx);
4011 osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
4014 code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4018 callp = cm_GetRxConn(connp);
4019 code = RXAFS_ReleaseLock(callp, &tfid, &volSync);
4020 rx_PutConnection(callp);
4022 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
4024 code = cm_MapRPCError(code, reqp);
4027 "CALL ReleaseLock FAILURE, code 0x%x", code);
4030 "CALL ReleaseLock SUCCESS");
4032 lock_ObtainMutex(&scp->mx);
4037 /* called with scp->mx held. May release it during processing, but
4038 will exit with lock held.
4042 - 0 if the user has permission to get the specified lock for the scp
4044 - CM_ERROR_NOACCESS if not
4046 Any other error from cm_SyncOp will be sent down untranslated.
4048 If CM_ERROR_NOACCESS is returned and lock_type is LockRead, then
4049 phas_insert (if non-NULL) will receive a boolean value indicating
4050 whether the user has INSERT permission or not.
4052 long cm_LockCheckPerms(cm_scache_t * scp,
4059 long code = 0, code2 = 0;
4061 /* lock permissions are slightly tricky because of the 'i' bit.
4062 If the user has PRSFS_LOCK, she can read-lock the file. If the
4063 user has PRSFS_WRITE, she can write-lock the file. However, if
4064 the user has PRSFS_INSERT, then she can write-lock new files,
4065 but not old ones. Since we don't have information about
4066 whether a file is new or not, we assume that if the user owns
4067 the scp, then she has the permissions that are granted by
4070 osi_Log3(afsd_logp, "cm_LockCheckPerms for scp[0x%p] type[%d] user[0x%p]",
4071 scp, lock_type, userp);
4073 if (lock_type == LockRead)
4074 rights |= PRSFS_LOCK;
4075 else if (lock_type == LockWrite)
4076 rights |= PRSFS_WRITE | PRSFS_LOCK;
4079 osi_assertx(FALSE, "invalid lock type");
4084 *phas_insert = FALSE;
4086 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
4087 CM_SCACHESYNC_GETSTATUS |
4088 CM_SCACHESYNC_NEEDCALLBACK);
4090 if (phas_insert && scp->creator == userp) {
4092 /* If this file was created by the user, then we check for
4093 PRSFS_INSERT. If the file server is recent enough, then
4094 this should be sufficient for her to get a write-lock (but
4095 not necessarily a read-lock). VICED_CAPABILITY_WRITELOCKACL
4096 indicates whether a file server supports getting write
4097 locks when the user only has PRSFS_INSERT.
4099 If the file was not created by the user we skip the check
4100 because the INSERT bit will not apply to this user even
4104 code2 = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
4105 CM_SCACHESYNC_GETSTATUS |
4106 CM_SCACHESYNC_NEEDCALLBACK);
4108 if (code2 == CM_ERROR_NOACCESS) {
4109 osi_Log0(afsd_logp, "cm_LockCheckPerms user has no INSERT bits");
4111 *phas_insert = TRUE;
4112 osi_Log0(afsd_logp, "cm_LockCheckPerms user has INSERT bits");
4116 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
4118 osi_Log1(afsd_logp, "cm_LockCheckPerms returning code %d", code);
4123 /* called with scp->mx held */
4124 long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
4125 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
4127 int allowWait, cm_user_t *userp, cm_req_t *reqp,
4128 cm_file_lock_t **lockpp)
4131 int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
4132 cm_file_lock_t *fileLock;
4135 int wait_unlock = FALSE;
4136 int force_client_lock = FALSE;
4138 osi_Log4(afsd_logp, "cm_Lock scp 0x%x type 0x%x offset %d length %d",
4139 scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
4140 osi_Log3(afsd_logp, "... allowWait %d key 0x%x:%x", allowWait,
4141 (unsigned long)(key >> 32), (unsigned long)(key & 0xffffffff));
4144 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
4146 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
4149 3.1 If L->LockType is exclusive then there does NOT exist a
4150 ACCEPTED lock M in S->fileLocks such that _a_ in
4151 (M->LOffset,+M->LLength).
4153 3.2 If L->LockType is shared then for each ACCEPTED lock M in
4154 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
4155 M->LockType is shared.
4157 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
4159 4.1 M->key != Key(C)
4161 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
4162 and (M->LOffset,+M->LLength) do not intersect.
4165 range.offset = LOffset.QuadPart;
4166 range.length = LLength.QuadPart;
4168 lock_ObtainRead(&cm_scacheLock);
4170 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4172 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4174 if (IS_LOCK_LOST(fileLock)) {
4175 if (fileLock->key == key) {
4176 code = CM_ERROR_BADFD;
4178 } else if (fileLock->lockType == LockWrite && INTERSECT_RANGE(range, fileLock->range)) {
4179 code = CM_ERROR_WOULDBLOCK;
4185 /* we don't need to check for deleted locks here since deleted
4186 locks are dequeued from scp->fileLocks */
4187 if (IS_LOCK_ACCEPTED(fileLock) &&
4188 INTERSECT_RANGE(range, fileLock->range)) {
4190 if ((sLockType & LOCKING_ANDX_SHARED_LOCK) == 0 ||
4191 fileLock->lockType != LockRead) {
4193 code = CM_ERROR_WOULDBLOCK;
4199 lock_ReleaseRead(&cm_scacheLock);
4201 if (code == 0 && SERVERLOCKS_ENABLED(scp)) {
4202 if (Which == scp->serverLock ||
4203 (Which == LockRead && scp->serverLock == LockWrite)) {
4207 /* we already have the lock we need */
4208 osi_Log3(afsd_logp, " we already have the correct lock. exclusives[%d], shared[%d], serverLock[%d]",
4209 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4211 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4213 /* special case: if we don't have permission to read-lock
4214 the file, then we force a clientside lock. This is to
4215 compensate for applications that obtain a read-lock for
4216 reading files off of directories that don't grant
4217 read-locks to the user. */
4218 if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4220 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4221 osi_Log0(afsd_logp, " User has no read-lock perms, but has INSERT perms.");
4224 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4225 force_client_lock = TRUE;
4229 } else if ((scp->exclusiveLocks > 0) ||
4230 (scp->sharedLocks > 0 && scp->serverLock != LockRead)) {
4233 /* We are already waiting for some other lock. We should
4234 wait for the daemon to catch up instead of generating a
4235 flood of SetLock calls. */
4236 osi_Log3(afsd_logp, " already waiting for other lock. exclusives[%d], shared[%d], serverLock[%d]",
4237 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4239 /* see if we have permission to create the lock in the
4241 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4243 code = CM_ERROR_WOULDBLOCK;
4244 else if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4246 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4248 " User has no read-lock perms, but has INSERT perms.");
4249 code = CM_ERROR_WOULDBLOCK;
4252 " User has no read-lock perms. Forcing client-side lock");
4253 force_client_lock = TRUE;
4257 /* leave any other codes as-is */
4261 int check_data_version = FALSE;
4264 /* first check if we have permission to elevate or obtain
4266 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4268 if (code == CM_ERROR_NOACCESS && Which == LockRead &&
4269 (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp))) {
4270 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4271 force_client_lock = TRUE;
4276 /* has_insert => (Which == LockRead, code == CM_ERROR_NOACCESS) */
4278 if (scp->serverLock == LockRead && Which == LockWrite) {
4280 /* We want to escalate the lock to a LockWrite.
4281 * Unfortunately that's not really possible without
4282 * letting go of the current lock. But for now we do
4286 " attempting to UPGRADE from LockRead to LockWrite.");
4288 " dataVersion on scp: %I64d", scp->dataVersion);
4290 /* we assume at this point (because scp->serverLock
4291 was valid) that we had a valid server lock. */
4292 scp->lockDataVersion = scp->dataVersion;
4293 check_data_version = TRUE;
4295 code = cm_IntReleaseLock(scp, userp, reqp);
4298 /* We couldn't release the lock */
4301 scp->serverLock = -1;
4305 /* We need to obtain a server lock of type Which in order
4306 * to assert this file lock */
4307 #ifndef AGGRESSIVE_LOCKS
4310 newLock = LockWrite;
4313 code = cm_IntSetLock(scp, userp, newLock, reqp);
4315 #ifdef AGGRESSIVE_LOCKS
4316 if ((code == CM_ERROR_WOULDBLOCK ||
4317 code == CM_ERROR_NOACCESS) && newLock != Which) {
4318 /* we wanted LockRead. We tried LockWrite. Now try
4323 osi_assertx(newLock == LockRead, "lock type not read");
4325 code = cm_IntSetLock(scp, userp, newLock, reqp);
4329 if (code == CM_ERROR_NOACCESS) {
4330 if (Which == LockRead) {
4331 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4333 /* We requested a read-lock, but we have permission to
4334 * get a write-lock. Try that */
4336 tcode = cm_LockCheckPerms(scp, LockWrite, userp, reqp, NULL);
4339 newLock = LockWrite;
4341 osi_Log0(afsd_logp, " User has 'i' perms and the request was for a LockRead. Trying to get a LockWrite instead");
4343 code = cm_IntSetLock(scp, userp, newLock, reqp);
4346 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4347 force_client_lock = TRUE;
4349 } else if (Which == LockWrite &&
4350 scp->creator == userp && !SCP_SUPPORTS_WRITELOCKACL(scp)) {
4353 /* Special case: if the lock request was for a
4354 * LockWrite and the user owns the file and we weren't
4355 * allowed to obtain the serverlock, we either lost a
4356 * race (the permissions changed from under us), or we
4357 * have 'i' bits, but we aren't allowed to lock the
4360 /* check if we lost a race... */
4361 tcode = cm_LockCheckPerms(scp, Which, userp, reqp, NULL);
4364 osi_Log0(afsd_logp, " User has 'i' perms but can't obtain write locks. Using client-side locks.");
4365 force_client_lock = TRUE;
4370 if (code == 0 && check_data_version &&
4371 scp->dataVersion != scp->lockDataVersion) {
4372 /* We lost a race. Although we successfully obtained
4373 * a lock, someone modified the file in between. The
4374 * locks have all been technically lost. */
4377 " Data version mismatch while upgrading lock.");
4379 " Data versions before=%I64d, after=%I64d",
4380 scp->lockDataVersion,
4383 " Releasing stale lock for scp 0x%x", scp);
4385 code = cm_IntReleaseLock(scp, userp, reqp);
4387 scp->serverLock = -1;
4389 code = CM_ERROR_INVAL;
4390 } else if (code == 0) {
4391 scp->serverLock = newLock;
4392 scp->lockDataVersion = scp->dataVersion;
4396 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4397 scp->serverLock == -1) {
4398 /* Oops. We lost the lock. */
4399 cm_LockMarkSCacheLost(scp);
4402 } else if (code == 0) { /* server locks not enabled */
4404 " Skipping server lock for scp");
4409 if (code != 0 && !force_client_lock) {
4410 /* Special case error translations
4412 Applications don't expect certain errors from a
4413 LockFile/UnlockFile call. We need to translate some error
4414 code to codes that apps expect and handle. */
4416 /* We shouldn't actually need to handle this case since we
4417 simulate locks for RO scps anyway. */
4418 if (code == CM_ERROR_READONLY) {
4419 osi_Log0(afsd_logp, " Reinterpreting CM_ERROR_READONLY as CM_ERROR_NOACCESS");
4420 code = CM_ERROR_NOACCESS;
4424 if (code == 0 || (code == CM_ERROR_WOULDBLOCK && allowWait) ||
4425 force_client_lock) {
4427 /* clear the error if we are forcing a client lock, so we
4428 don't get confused later. */
4429 if (force_client_lock && code != CM_ERROR_WOULDBLOCK)
4432 lock_ObtainWrite(&cm_scacheLock);
4433 fileLock = cm_GetFileLock();
4434 lock_ReleaseWrite(&cm_scacheLock);
4436 fileLock->fid = scp->fid;
4438 fileLock->key = key;
4439 fileLock->lockType = Which;
4441 fileLock->userp = userp;
4442 fileLock->range = range;
4443 fileLock->flags = (code == 0 ? 0 :
4445 CM_FILELOCK_FLAG_WAITUNLOCK :
4446 CM_FILELOCK_FLAG_WAITLOCK));
4448 if (force_client_lock || !SERVERLOCKS_ENABLED(scp))
4449 fileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
4451 fileLock->lastUpdate = (code == 0 && !force_client_lock) ? time(NULL) : 0;
4453 lock_ObtainWrite(&cm_scacheLock);
4454 osi_QAddT(&scp->fileLocksH, &scp->fileLocksT, &fileLock->fileq);
4455 cm_HoldSCacheNoLock(scp);
4456 fileLock->scp = scp;
4457 osi_QAdd(&cm_allFileLocks, &fileLock->q);
4458 lock_ReleaseWrite(&cm_scacheLock);
4464 if (IS_LOCK_CLIENTONLY(fileLock)) {
4466 } else if (IS_LOCK_ACCEPTED(fileLock)) {
4467 if (Which == LockRead)
4470 scp->exclusiveLocks++;
4474 "cm_Lock Lock added 0x%p flags 0x%x to scp [0x%p]",
4475 fileLock, fileLock->flags, scp);
4477 " exclusives[%d] shared[%d] client[%d] serverLock[%d]",
4478 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4479 (int)(signed char) scp->serverLock);
4482 "cm_Lock Rejecting lock (code = 0x%x)", code);
4488 static int cm_KeyEquals(cm_key_t k1, cm_key_t k2, int flags);
4490 /* Called with scp->mx held */
4491 long cm_UnlockByKey(cm_scache_t * scp,
4498 cm_file_lock_t *fileLock;
4499 osi_queue_t *q, *qn;
4502 osi_Log4(afsd_logp, "cm_UnlockByKey scp 0x%p key 0x%x:%x flags=0x%x",
4504 (unsigned long)(key >> 32),
4505 (unsigned long)(key & 0xffffffff),
4508 lock_ObtainWrite(&cm_scacheLock);
4510 for (q = scp->fileLocksH; q; q = qn) {
4513 fileLock = (cm_file_lock_t *)
4514 ((char *) q - offsetof(cm_file_lock_t, fileq));
4517 osi_Log4(afsd_logp, " Checking lock[0x%x] range[%d,+%d] type[%d]",
4519 (unsigned long) fileLock->range.offset,
4520 (unsigned long) fileLock->range.length,
4521 fileLock->lockType);
4522 osi_Log3(afsd_logp, " key[0x%x:%x] flags[0x%x]",
4523 (unsigned long)(fileLock->key >> 32),
4524 (unsigned long)(fileLock->key & 0xffffffff),
4527 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
4528 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
4529 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4531 fileLock->fid.volume,
4532 fileLock->fid.vnode,
4533 fileLock->fid.unique);
4534 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4535 fileLock->scp->fid.cell,
4536 fileLock->scp->fid.volume,
4537 fileLock->scp->fid.vnode,
4538 fileLock->scp->fid.unique);
4539 osi_assertx(FALSE, "invalid fid value");
4543 if (!IS_LOCK_DELETED(fileLock) &&
4544 cm_KeyEquals(fileLock->key, key, flags)) {
4545 osi_Log3(afsd_logp, "...Unlock range [%d,+%d] type %d",
4546 fileLock->range.offset,
4547 fileLock->range.length,
4548 fileLock->lockType);
4550 if (scp->fileLocksT == q)
4551 scp->fileLocksT = osi_QPrev(q);
4552 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
4554 if (IS_LOCK_CLIENTONLY(fileLock)) {
4556 } else if (IS_LOCK_ACCEPTED(fileLock)) {
4557 if (fileLock->lockType == LockRead)
4560 scp->exclusiveLocks--;
4563 fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
4565 cm_ReleaseUser(fileLock->userp);
4566 cm_ReleaseSCacheNoLock(scp);
4568 fileLock->userp = NULL;
4569 fileLock->scp = NULL;
4575 lock_ReleaseWrite(&cm_scacheLock);
4577 if (n_unlocks == 0) {
4578 osi_Log0(afsd_logp, "cm_UnlockByKey no locks found");
4579 osi_Log3(afsd_logp, " Leaving scp with exclusives[%d], shared[%d], serverLock[%d]",
4580 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4585 osi_Log1(afsd_logp, "cm_UnlockByKey done with %d locks", n_unlocks);
4587 osi_assertx(scp->sharedLocks >= 0, "scp->sharedLocks < 0");
4588 osi_assertx(scp->exclusiveLocks >= 0, "scp->exclusiveLocks < 0");
4589 osi_assertx(scp->clientLocks >= 0, "scp->clientLocks < 0");
4591 if (!SERVERLOCKS_ENABLED(scp)) {
4592 osi_Log0(afsd_logp, " Skipping server lock for scp");
4596 /* Ideally we would go through the rest of the locks to determine
4597 * if one or more locks that were formerly in WAITUNLOCK can now
4598 * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
4599 * scp->sharedLocks accordingly. However, the retrying of locks
4600 * in that manner is done cm_RetryLock() manually.
4603 if (scp->serverLock == LockWrite &&
4604 scp->exclusiveLocks == 0 &&
4605 scp->sharedLocks > 0) {
4607 /* The serverLock should be downgraded to LockRead */
4608 osi_Log0(afsd_logp, " DOWNGRADE lock from LockWrite to LockRead");
4610 /* since scp->serverLock looked sane, we are going to assume
4611 that we have a valid server lock. */
4612 scp->lockDataVersion = scp->dataVersion;
4613 osi_Log1(afsd_logp, " dataVersion on scp = %I64d", scp->dataVersion);
4615 code = cm_IntReleaseLock(scp, userp, reqp);
4618 /* so we couldn't release it. Just let the lock be for now */
4622 scp->serverLock = -1;
4625 code = cm_IntSetLock(scp, userp, LockRead, reqp);
4627 if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
4628 scp->serverLock = LockRead;
4629 } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
4630 /* We lost a race condition. Although we have a valid
4631 lock on the file, the data has changed and essentially
4632 we have lost the lock we had during the transition. */
4634 osi_Log0(afsd_logp, "Data version mismatch during lock downgrade");
4635 osi_Log2(afsd_logp, " Data versions before=%I64d, after=%I64d",
4636 scp->lockDataVersion,
4639 code = cm_IntReleaseLock(scp, userp, reqp);
4641 code = CM_ERROR_INVAL;
4642 scp->serverLock = -1;
4646 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4647 (scp->serverLock == -1)) {
4649 cm_LockMarkSCacheLost(scp);
4652 /* failure here has no bearing on the return value of
4656 } else if (scp->serverLock != (-1) &&
4657 scp->exclusiveLocks == 0 &&
4658 scp->sharedLocks == 0) {
4659 /* The serverLock should be released entirely */
4661 code = cm_IntReleaseLock(scp, userp, reqp);
4664 scp->serverLock = (-1);
4669 osi_Log1(afsd_logp, "cm_UnlockByKey code 0x%x", code);
4670 osi_Log4(afsd_logp, " Leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
4671 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4672 (int)(signed char) scp->serverLock);
4677 long cm_Unlock(cm_scache_t *scp,
4678 unsigned char sLockType,
4679 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
4685 int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
4686 cm_file_lock_t *fileLock;
4688 int release_userp = FALSE;
4690 osi_Log4(afsd_logp, "cm_Unlock scp 0x%p type 0x%x offset %d length %d",
4691 scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
4692 osi_Log2(afsd_logp, "... key 0x%x:%x",
4693 (unsigned long) (key >> 32), (unsigned long) (key & 0xffffffff));
4695 lock_ObtainRead(&cm_scacheLock);
4697 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4698 fileLock = (cm_file_lock_t *)
4699 ((char *) q - offsetof(cm_file_lock_t, fileq));
4702 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
4703 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
4704 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4706 fileLock->fid.volume,
4707 fileLock->fid.vnode,
4708 fileLock->fid.unique);
4709 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4710 fileLock->scp->fid.cell,
4711 fileLock->scp->fid.volume,
4712 fileLock->scp->fid.vnode,
4713 fileLock->scp->fid.unique);
4714 osi_assertx(FALSE, "invalid fid value");
4717 if (!IS_LOCK_DELETED(fileLock) &&
4718 fileLock->key == key &&
4719 fileLock->range.offset == LOffset.QuadPart &&
4720 fileLock->range.length == LLength.QuadPart) {
4726 osi_Log0(afsd_logp, "cm_Unlock lock not found; failure");
4728 lock_ReleaseRead(&cm_scacheLock);
4730 /* The lock didn't exist anyway. *shrug* */
4734 lock_ReleaseRead(&cm_scacheLock);
4736 /* discard lock record */
4737 lock_ObtainWrite(&cm_scacheLock);
4738 if (scp->fileLocksT == q)
4739 scp->fileLocksT = osi_QPrev(q);
4740 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
4743 * Don't delete it here; let the daemon delete it, to simplify
4744 * the daemon's traversal of the list.
4747 if (IS_LOCK_CLIENTONLY(fileLock)) {
4749 } else if (IS_LOCK_ACCEPTED(fileLock)) {
4750 if (fileLock->lockType == LockRead)
4753 scp->exclusiveLocks--;
4756 fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
4757 if (userp != NULL) {
4758 cm_ReleaseUser(fileLock->userp);
4760 userp = fileLock->userp;
4761 release_userp = TRUE;
4763 fileLock->userp = NULL;
4764 cm_ReleaseSCacheNoLock(scp);
4765 fileLock->scp = NULL;
4766 lock_ReleaseWrite(&cm_scacheLock);
4768 if (!SERVERLOCKS_ENABLED(scp)) {
4769 osi_Log0(afsd_logp, " Skipping server locks for scp");
4773 /* Ideally we would go through the rest of the locks to determine
4774 * if one or more locks that were formerly in WAITUNLOCK can now
4775 * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
4776 * scp->sharedLocks accordingly. However, the retrying of locks
4777 * in that manner is done cm_RetryLock() manually.
4780 if (scp->serverLock == LockWrite &&
4781 scp->exclusiveLocks == 0 &&
4782 scp->sharedLocks > 0) {
4784 /* The serverLock should be downgraded to LockRead */
4785 osi_Log0(afsd_logp, " DOWNGRADE lock from LockWrite to LockRead");
4787 /* Since we already had a lock, we assume that there is a
4788 valid server lock. */
4789 scp->lockDataVersion = scp->dataVersion;
4790 osi_Log1(afsd_logp, " dataVersion on scp is %I64d", scp->dataVersion);
4792 /* before we downgrade, make sure that we have enough
4793 permissions to get the read lock. */
4794 code = cm_LockCheckPerms(scp, LockRead, userp, reqp, NULL);
4797 osi_Log0(afsd_logp, " SKIPPING downgrade because user doesn't have perms to get downgraded lock");
4803 code = cm_IntReleaseLock(scp, userp, reqp);
4806 /* so we couldn't release it. Just let the lock be for now */
4810 scp->serverLock = -1;
4813 code = cm_IntSetLock(scp, userp, LockRead, reqp);
4815 if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
4816 scp->serverLock = LockRead;
4817 } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
4818 /* Lost a race. We obtained a new lock, but that is
4819 meaningless since someone modified the file
4823 "Data version mismatch while downgrading lock");
4825 " Data versions before=%I64d, after=%I64d",
4826 scp->lockDataVersion,
4829 code = cm_IntReleaseLock(scp, userp, reqp);
4831 scp->serverLock = -1;
4832 code = CM_ERROR_INVAL;
4836 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4837 (scp->serverLock == -1)) {
4839 cm_LockMarkSCacheLost(scp);
4842 /* failure here has no bearing on the return value of
4846 } else if (scp->serverLock != (-1) &&
4847 scp->exclusiveLocks == 0 &&
4848 scp->sharedLocks == 0) {
4849 /* The serverLock should be released entirely */
4851 code = cm_IntReleaseLock(scp, userp, reqp);
4854 scp->serverLock = (-1);
4859 cm_ReleaseUser(userp);
4863 osi_Log1(afsd_logp, "cm_Unlock code 0x%x", code);
4864 osi_Log4(afsd_logp, " leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
4865 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4866 (int)(signed char) scp->serverLock);
4871 /* called with scp->mx held */
4872 static void cm_LockMarkSCacheLost(cm_scache_t * scp)
4874 cm_file_lock_t *fileLock;
4877 osi_Log1(afsd_logp, "cm_LockMarkSCacheLost scp 0x%x", scp);
4880 /* With the current code, we can't lose a lock on a RO scp */
4881 osi_assertx(!(scp->flags & CM_SCACHEFLAG_RO), "CM_SCACHEFLAG_RO unexpected");
4884 /* cm_scacheLock needed because we are modifying fileLock->flags */
4885 lock_ObtainWrite(&cm_scacheLock);
4887 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4889 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4891 if (IS_LOCK_ACTIVE(fileLock) &&
4892 !IS_LOCK_CLIENTONLY(fileLock)) {
4893 if (fileLock->lockType == LockRead)
4896 scp->exclusiveLocks--;
4898 fileLock->flags |= CM_FILELOCK_FLAG_LOST;
4902 scp->serverLock = -1;
4903 scp->lockDataVersion = -1;
4904 lock_ReleaseWrite(&cm_scacheLock);
4907 /* Called with no relevant locks held */
4908 void cm_CheckLocks()
4910 osi_queue_t *q, *nq;
4911 cm_file_lock_t *fileLock;
4917 struct rx_connection * callp;
4922 lock_ObtainWrite(&cm_scacheLock);
4924 cm_lockRefreshCycle++;
4926 osi_Log1(afsd_logp, "cm_CheckLocks starting lock check cycle %d", cm_lockRefreshCycle);
4928 for (q = cm_allFileLocks; q; q = nq) {
4929 fileLock = (cm_file_lock_t *) q;
4933 if (IS_LOCK_DELETED(fileLock)) {
4935 osi_QRemove(&cm_allFileLocks, q);
4936 cm_PutFileLock(fileLock);
4938 } else if (IS_LOCK_ACTIVE(fileLock) && !IS_LOCK_CLIENTONLY(fileLock)) {
4940 /* Server locks must have been enabled for us to have
4941 received an active non-client-only lock. */
4942 osi_assertx(cm_enableServerLocks, "!cm_enableServerLocks");
4944 scp = fileLock->scp;
4945 osi_assertx(scp != NULL, "null cm_scache_t");
4947 cm_HoldSCacheNoLock(scp);
4950 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
4951 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
4952 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4954 fileLock->fid.volume,
4955 fileLock->fid.vnode,
4956 fileLock->fid.unique);
4957 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4958 fileLock->scp->fid.cell,
4959 fileLock->scp->fid.volume,
4960 fileLock->scp->fid.vnode,
4961 fileLock->scp->fid.unique);
4962 osi_assertx(FALSE, "invalid fid");
4965 /* Server locks are extended once per scp per refresh
4967 if (scp->lastRefreshCycle != cm_lockRefreshCycle) {
4969 int scp_done = FALSE;
4971 osi_Log1(afsd_logp, "cm_CheckLocks Updating scp 0x%x", scp);
4973 lock_ReleaseWrite(&cm_scacheLock);
4974 lock_ObtainMutex(&scp->mx);
4976 /* did the lock change while we weren't holding the lock? */
4977 if (!IS_LOCK_ACTIVE(fileLock))
4978 goto post_syncopdone;
4980 code = cm_SyncOp(scp, NULL, fileLock->userp, &req, 0,
4981 CM_SCACHESYNC_NEEDCALLBACK
4982 | CM_SCACHESYNC_GETSTATUS
4983 | CM_SCACHESYNC_LOCK);
4987 "cm_CheckLocks SyncOp failure code 0x%x", code);
4988 goto post_syncopdone;
4991 /* cm_SyncOp releases scp->mx during which the lock
4992 may get released. */
4993 if (!IS_LOCK_ACTIVE(fileLock))
4994 goto pre_syncopdone;
4996 if (scp->serverLock != -1) {
5000 tfid.Volume = scp->fid.volume;
5001 tfid.Vnode = scp->fid.vnode;
5002 tfid.Unique = scp->fid.unique;
5004 userp = fileLock->userp;
5006 osi_Log3(afsd_logp, "CALL ExtendLock lock 0x%p for scp=0x%p with lock %d",
5009 (int) scp->serverLock);
5011 lock_ReleaseMutex(&scp->mx);
5014 code = cm_ConnFromFID(&cfid, userp,
5019 callp = cm_GetRxConn(connp);
5020 code = RXAFS_ExtendLock(callp, &tfid,
5022 rx_PutConnection(callp);
5024 osi_Log1(afsd_logp, " ExtendLock returns %d", code);
5026 } while (cm_Analyze(connp, userp, &req,
5027 &cfid, &volSync, NULL, NULL,
5030 code = cm_MapRPCError(code, &req);
5032 lock_ObtainMutex(&scp->mx);
5035 osi_Log1(afsd_logp, "CALL ExtendLock FAILURE, code 0x%x", code);
5037 osi_Log0(afsd_logp, "CALL ExtendLock SUCCESS");
5038 scp->lockDataVersion = scp->dataVersion;
5041 if ((code == EINVAL || code == CM_ERROR_INVAL) &&
5042 scp->lockDataVersion == scp->dataVersion) {
5046 (scp->exclusiveLocks > 0) ? LockWrite: LockRead;
5048 /* we might still have a chance to obtain a
5051 code = cm_IntSetLock(scp, userp, lockType, &req);
5054 code = CM_ERROR_INVAL;
5055 } else if (scp->lockDataVersion != scp->dataVersion) {
5057 /* now check if we still have the file at
5058 the right data version. */
5060 "Data version mismatch on scp 0x%p",
5063 " Data versions: before=%I64d, after=%I64d",
5064 scp->lockDataVersion,
5067 code = cm_IntReleaseLock(scp, userp, &req);
5069 code = CM_ERROR_INVAL;
5073 if (code == EINVAL || code == CM_ERROR_INVAL) {
5074 cm_LockMarkSCacheLost(scp);
5078 /* interestingly, we have found an active lock
5079 belonging to an scache that has no
5081 cm_LockMarkSCacheLost(scp);
5088 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5091 lock_ReleaseMutex(&scp->mx);
5093 lock_ObtainWrite(&cm_scacheLock);
5096 fileLock->lastUpdate = time(NULL);
5100 scp->lastRefreshCycle = cm_lockRefreshCycle;
5103 /* we have already refreshed the locks on this scp */
5104 fileLock->lastUpdate = time(NULL);
5107 cm_ReleaseSCacheNoLock(scp);
5109 } else if (IS_LOCK_ACTIVE(fileLock) && IS_LOCK_CLIENTONLY(fileLock)) {
5110 /* TODO: Check callbacks */
5114 lock_ReleaseWrite(&cm_scacheLock);
5115 osi_Log1(afsd_logp, "cm_CheckLocks completes lock check cycle %d", cm_lockRefreshCycle);
5118 /* NOT called with scp->mx held. */
5119 long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
5122 cm_scache_t *scp = NULL;
5123 cm_file_lock_t *fileLock;
5127 int force_client_lock = FALSE;
5128 int has_insert = FALSE;
5129 int check_data_version = FALSE;
5133 if (client_is_dead) {
5134 code = CM_ERROR_TIMEDOUT;
5138 lock_ObtainRead(&cm_scacheLock);
5140 osi_Log2(afsd_logp, "cm_RetryLock checking lock %p (scp=%p)", oldFileLock, oldFileLock->scp);
5141 osi_Log4(afsd_logp, " offset(%x:%x) length(%x:%x)",
5142 (unsigned)(oldFileLock->range.offset >> 32),
5143 (unsigned)(oldFileLock->range.offset & 0xffffffff),
5144 (unsigned)(oldFileLock->range.length >> 32),
5145 (unsigned)(oldFileLock->range.length & 0xffffffff));
5146 osi_Log3(afsd_logp, " key(%x:%x) flags=%x",
5147 (unsigned)(oldFileLock->key >> 32),
5148 (unsigned)(oldFileLock->key & 0xffffffff),
5149 (unsigned)(oldFileLock->flags));
5151 /* if the lock has already been granted, then we have nothing to do */
5152 if (IS_LOCK_ACTIVE(oldFileLock)) {
5153 lock_ReleaseRead(&cm_scacheLock);
5154 osi_Log0(afsd_logp, "cm_RetryLock lock already granted");
5158 /* we can't do anything with lost or deleted locks at the moment. */
5159 if (IS_LOCK_LOST(oldFileLock) || IS_LOCK_DELETED(oldFileLock)) {
5160 code = CM_ERROR_BADFD;
5161 osi_Log0(afsd_logp, "cm_RetryLock lock is lost or deleted");
5162 lock_ReleaseRead(&cm_scacheLock);
5166 scp = oldFileLock->scp;
5168 osi_assertx(scp != NULL, "null cm_scache_t");
5170 lock_ReleaseRead(&cm_scacheLock);
5171 lock_ObtainMutex(&scp->mx);
5173 code = cm_LockCheckPerms(scp, oldFileLock->lockType,
5177 if (code == CM_ERROR_NOACCESS && oldFileLock->lockType == LockRead) {
5178 if (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp)) {
5179 force_client_lock = TRUE;
5183 lock_ReleaseMutex(&scp->mx);
5187 lock_ObtainWrite(&cm_scacheLock);
5189 /* Check if we already have a sufficient server lock to allow this
5190 lock to go through. */
5191 if (IS_LOCK_WAITLOCK(oldFileLock) &&
5192 (!SERVERLOCKS_ENABLED(scp) ||
5193 scp->serverLock == oldFileLock->lockType ||
5194 scp->serverLock == LockWrite)) {
5196 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5198 if (SERVERLOCKS_ENABLED(scp)) {
5199 osi_Log1(afsd_logp, "cm_RetryLock Server lock (%d) is sufficient for lock. Granting",
5200 (int) scp->serverLock);
5202 osi_Log0(afsd_logp, "cm_RetryLock skipping server lock for scp");
5205 lock_ReleaseWrite(&cm_scacheLock);
5206 lock_ReleaseMutex(&scp->mx);
5211 if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5213 /* check if the conflicting locks have dissappeared already */
5214 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5216 fileLock = (cm_file_lock_t *)
5217 ((char *) q - offsetof(cm_file_lock_t, fileq));
5219 if (IS_LOCK_LOST(fileLock)) {
5220 if (fileLock->key == oldFileLock->key) {
5221 code = CM_ERROR_BADFD;
5222 oldFileLock->flags |= CM_FILELOCK_FLAG_LOST;
5223 osi_Log1(afsd_logp, " found lost lock %p for same key. Marking lock as lost",
5226 } else if (fileLock->lockType == LockWrite &&
5227 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5228 osi_Log1(afsd_logp, " found conflicting LOST lock %p", fileLock);
5229 code = CM_ERROR_WOULDBLOCK;
5234 if (IS_LOCK_ACCEPTED(fileLock) &&
5235 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5237 if (oldFileLock->lockType != LockRead ||
5238 fileLock->lockType != LockRead) {
5240 osi_Log1(afsd_logp, " found conflicting lock %p", fileLock);
5241 code = CM_ERROR_WOULDBLOCK;
5249 lock_ReleaseWrite(&cm_scacheLock);
5250 lock_ReleaseMutex(&scp->mx);
5255 /* when we get here, the lock is either a WAITUNLOCK or WAITLOCK.
5256 If it is WAITUNLOCK, then we didn't find any conflicting lock
5257 but we haven't verfied whether the serverLock is sufficient to
5258 assert it. If it is WAITLOCK, then the serverLock is
5259 insufficient to assert it. Eitherway, we are ready to accept
5260 the lock as either ACTIVE or WAITLOCK depending on the
5263 /* First, promote the WAITUNLOCK to a WAITLOCK */
5264 if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5265 if (oldFileLock->lockType == LockRead)
5268 scp->exclusiveLocks++;
5270 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITUNLOCK;
5271 oldFileLock->flags |= CM_FILELOCK_FLAG_WAITLOCK;
5274 osi_assertx(IS_LOCK_WAITLOCK(oldFileLock), "!IS_LOCK_WAITLOCK");
5276 if (force_client_lock ||
5277 !SERVERLOCKS_ENABLED(scp) ||
5278 scp->serverLock == oldFileLock->lockType ||
5279 (oldFileLock->lockType == LockRead &&
5280 scp->serverLock == LockWrite)) {
5282 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5284 if ((force_client_lock ||
5285 !SERVERLOCKS_ENABLED(scp)) &&
5286 !IS_LOCK_CLIENTONLY(oldFileLock)) {
5288 oldFileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
5290 if (oldFileLock->lockType == LockRead)
5293 scp->exclusiveLocks--;
5298 lock_ReleaseWrite(&cm_scacheLock);
5299 lock_ReleaseMutex(&scp->mx);
5306 code = cm_SyncOp(scp, NULL, oldFileLock->userp, &req, 0,
5307 CM_SCACHESYNC_NEEDCALLBACK
5308 | CM_SCACHESYNC_GETSTATUS
5309 | CM_SCACHESYNC_LOCK);
5311 osi_Log1(smb_logp, "cm_RetryLock SyncOp failure code 0x%x", code);
5312 lock_ReleaseWrite(&cm_scacheLock);
5313 goto post_syncopdone;
5316 if (!IS_LOCK_WAITLOCK(oldFileLock))
5317 goto pre_syncopdone;
5319 userp = oldFileLock->userp;
5321 #ifndef AGGRESSIVE_LOCKS
5322 newLock = oldFileLock->lockType;
5324 newLock = LockWrite;
5328 /* if has_insert is non-zero, then:
5329 - the lock a LockRead
5330 - we don't have permission to get a LockRead
5331 - we do have permission to get a LockWrite
5332 - the server supports VICED_CAPABILITY_WRITELOCKACL
5335 newLock = LockWrite;
5338 lock_ReleaseWrite(&cm_scacheLock);
5340 /* when we get here, either we have a read-lock and want a
5341 write-lock or we don't have any locks and we want some
5344 if (scp->serverLock == LockRead) {
5346 osi_assertx(newLock == LockWrite, "!LockWrite");
5348 osi_Log0(afsd_logp, " Attempting to UPGRADE from LockRead to LockWrite");
5350 scp->lockDataVersion = scp->dataVersion;
5351 check_data_version = TRUE;
5353 code = cm_IntReleaseLock(scp, userp, &req);
5356 goto pre_syncopdone;
5358 scp->serverLock = -1;
5361 code = cm_IntSetLock(scp, userp, newLock, &req);
5364 if (scp->dataVersion != scp->lockDataVersion) {
5365 /* we lost a race. too bad */
5368 " Data version mismatch while upgrading lock.");
5370 " Data versions before=%I64d, after=%I64d",
5371 scp->lockDataVersion,
5374 " Releasing stale lock for scp 0x%x", scp);
5376 code = cm_IntReleaseLock(scp, userp, &req);
5378 scp->serverLock = -1;
5380 code = CM_ERROR_INVAL;
5382 cm_LockMarkSCacheLost(scp);
5384 scp->serverLock = newLock;
5389 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5395 if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
5396 lock_ObtainWrite(&cm_scacheLock);
5397 if (scp->fileLocksT == &oldFileLock->fileq)
5398 scp->fileLocksT = osi_QPrev(&oldFileLock->fileq);
5399 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, &oldFileLock->fileq);
5400 lock_ReleaseWrite(&cm_scacheLock);
5402 lock_ReleaseMutex(&scp->mx);
5405 lock_ObtainWrite(&cm_scacheLock);
5407 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5408 } else if (code != CM_ERROR_WOULDBLOCK) {
5409 oldFileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5410 cm_ReleaseUser(oldFileLock->userp);
5411 oldFileLock->userp = NULL;
5412 if (oldFileLock->scp) {
5413 cm_ReleaseSCacheNoLock(oldFileLock->scp);
5414 oldFileLock->scp = NULL;
5417 lock_ReleaseWrite(&cm_scacheLock);
5422 cm_key_t cm_GenerateKey(unsigned int session_id, unsigned long process_id, unsigned int file_id)
5425 osi_assertx((process_id & 0xffffffff) == process_id, "unexpected process_id");
5426 osi_assertx((session_id & 0xffff) == session_id, "unexpected session_id");
5427 osi_assertx((file_id & 0xffff) == file_id, "unexpected file_id");
5431 (((cm_key_t) (process_id & 0xffffffff)) << 32) |
5432 (((cm_key_t) (session_id & 0xffff)) << 16) |
5433 (((cm_key_t) (file_id & 0xffff)));
5436 static int cm_KeyEquals(cm_key_t k1, cm_key_t k2, int flags)
5438 if (flags & CM_UNLOCK_BY_FID) {
5439 return ((k1 & 0xffffffff) == (k2 & 0xffffffff));
5445 void cm_ReleaseAllLocks(void)
5451 cm_file_lock_t *fileLock;
5454 for (i = 0; i < cm_data.scacheHashTableSize; i++)
5456 for ( scp = cm_data.scacheHashTablep[i]; scp; scp = scp->nextp ) {
5457 while (scp->fileLocksH != NULL) {
5458 lock_ObtainMutex(&scp->mx);
5459 lock_ObtainWrite(&cm_scacheLock);
5460 if (!scp->fileLocksH) {
5461 lock_ReleaseWrite(&cm_scacheLock);
5462 lock_ReleaseMutex(&scp->mx);
5465 fileLock = (cm_file_lock_t *)((char *) scp->fileLocksH - offsetof(cm_file_lock_t, fileq));
5466 userp = fileLock->userp;
5468 key = fileLock->key;
5469 cm_HoldSCacheNoLock(scp);
5470 lock_ReleaseWrite(&cm_scacheLock);
5471 cm_UnlockByKey(scp, key, 0, userp, &req);
5472 cm_ReleaseSCache(scp);
5473 cm_ReleaseUser(userp);
5474 lock_ReleaseMutex(&scp->mx);