2 * Copyright 2000, International Business Machines Corporation and others.
5 * This software has been released under the terms of the IBM Public
6 * License. For details, see the LICENSE file in the top-level source
7 * directory or online at http://www.openafs.org/dl/license10.html
10 #include <afs/param.h>
27 extern void afsi_log(char *pattern, ...);
30 int cm_enableServerLocks = 1;
32 int cm_followBackupPath = 0;
35 * Case-folding array. This was constructed by inspecting of SMBtrace output.
36 * I do not know anything more about it.
38 unsigned char cm_foldUpper[256] = {
39 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
40 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
41 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
42 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
43 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
44 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
45 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
46 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
47 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
48 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
49 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
50 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
51 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
52 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
53 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
54 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
55 0x80, 0x9a, 0x90, 0x41, 0x8e, 0x41, 0x8f, 0x80,
56 0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x8e, 0x8f,
57 0x90, 0x92, 0x92, 0x4f, 0x99, 0x4f, 0x55, 0x55,
58 0x59, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
59 0x41, 0x49, 0x4f, 0x55, 0xa5, 0xa5, 0x56, 0xa7,
60 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
61 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
62 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
63 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
64 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
65 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
66 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
67 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
68 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
69 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
70 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
74 * Case-insensitive string comparison. We used to use stricmp, but it doesn't
75 * know about 8-bit characters (e.g. 129 is lowercase u-umlaut, 154 is
76 * upper-case u-umlaut).
78 int cm_stricmp(const char *str1, const char *str2)
90 c1 = (char) cm_foldUpper[(unsigned char)(*str1++)];
91 c2 = (char) cm_foldUpper[(unsigned char)(*str2++)];
99 /* characters that are legal in an 8.3 name */
101 * We used to have 1's for all characters from 128 to 254. But
102 * the NT client behaves better if we create an 8.3 name for any
103 * name that has a character with the high bit on, and if we
104 * delete those characters from 8.3 names. In particular, see
105 * Sybase defect 10859.
107 char cm_LegalChars[256] = {
108 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
109 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
110 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0,
111 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
112 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
113 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1,
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, 1, 0, 1, 1, 1,
116 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
117 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
118 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
119 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
120 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
121 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
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
126 /* return true iff component is a valid 8.3 name */
127 int cm_Is8Dot3(char *namep)
134 * can't have a leading dot;
135 * special case for . and ..
137 if (namep[0] == '.') {
140 if (namep[1] == '.' && namep[2] == 0)
144 while (tc = *namep++) {
146 /* saw another dot */
147 if (sawDot) return 0; /* second dot */
152 if (cm_LegalChars[tc] == 0)
155 if (!sawDot && charCount > 8)
156 /* more than 8 chars in name */
158 if (sawDot && charCount > 3)
159 /* more than 3 chars in extension */
166 * Number unparsing map for generating 8.3 names;
167 * The version taken from DFS was on drugs.
168 * You can't include '&' and '@' in a file name.
170 char cm_8Dot3Mapping[42] =
171 {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
172 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K',
173 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U',
174 'V', 'W', 'X', 'Y', 'Z', '_', '-', '$', '#', '!', '+', '='
176 int cm_8Dot3MapSize = sizeof(cm_8Dot3Mapping);
178 void cm_Gen8Dot3NameInt(const char * longname, cm_dirFid_t * pfid,
179 char *shortName, char **shortNameEndp)
183 int vnode = ntohl(pfid->vnode);
185 int validExtension = 0;
189 /* Unparse the file's vnode number to get a "uniquifier" */
191 number[nsize] = cm_8Dot3Mapping[vnode % cm_8Dot3MapSize];
193 vnode /= cm_8Dot3MapSize;
197 * Look for valid extension. There has to be a dot, and
198 * at least one of the characters following has to be legal.
200 lastDot = strrchr(longname, '.');
202 temp = lastDot; temp++;
204 if (cm_LegalChars[tc])
210 /* Copy name characters */
211 for (i = 0, name = longname;
212 i < (7 - nsize) && name != lastDot; ) {
217 if (!cm_LegalChars[tc])
220 *shortName++ = toupper(tc);
226 /* Copy uniquifier characters */
227 memcpy(shortName, number, nsize);
230 if (validExtension) {
231 /* Copy extension characters */
232 *shortName++ = *lastDot++; /* copy dot */
233 for (i = 0, tc = *lastDot++;
236 if (cm_LegalChars[tc]) {
238 *shortName++ = toupper(tc);
247 *shortNameEndp = shortName;
250 /* return success if we can open this file in this mode */
251 long cm_CheckOpen(cm_scache_t *scp, int openMode, int trunc, cm_user_t *userp,
259 rights |= PRSFS_READ;
260 if (openMode == 1 || openMode == 2 || trunc)
261 rights |= PRSFS_WRITE;
263 lock_ObtainMutex(&scp->mx);
265 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
266 CM_SCACHESYNC_GETSTATUS
267 | CM_SCACHESYNC_NEEDCALLBACK
268 | CM_SCACHESYNC_LOCK);
271 ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
272 scp->fileType == CM_SCACHETYPE_FILE) {
275 unsigned int sLockType;
276 LARGE_INTEGER LOffset, LLength;
278 /* Check if there's some sort of lock on the file at the
281 key = cm_GenerateKey(CM_SESSION_CMINT,0,0);
283 if (rights & PRSFS_WRITE)
286 sLockType = LOCKING_ANDX_SHARED_LOCK;
288 LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
289 LOffset.LowPart = CM_FLSHARE_OFFSET_LOW;
290 LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
291 LLength.LowPart = CM_FLSHARE_LENGTH_LOW;
293 code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
296 cm_Unlock(scp, sLockType, LOffset, LLength, key, userp, reqp);
298 /* In this case, we allow the file open to go through even
299 though we can't enforce mandatory locking on the
301 if (code == CM_ERROR_NOACCESS &&
302 !(rights & PRSFS_WRITE))
306 case CM_ERROR_ALLOFFLINE:
307 case CM_ERROR_ALLDOWN:
308 case CM_ERROR_ALLBUSY:
309 case CM_ERROR_TIMEDOUT:
311 case CM_ERROR_WOULDBLOCK:
314 code = CM_ERROR_SHARING_VIOLATION;
319 } else if (code != 0) {
323 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
327 lock_ReleaseMutex(&scp->mx);
332 /* return success if we can open this file in this mode */
333 long cm_CheckNTOpen(cm_scache_t *scp, unsigned int desiredAccess,
334 unsigned int createDisp, cm_user_t *userp, cm_req_t *reqp,
335 cm_lock_data_t **ldpp)
340 osi_assertx(ldpp != NULL, "null cm_lock_data_t");
343 /* Always allow delete; the RPC will tell us if it's OK */
344 if (desiredAccess == DELETE)
349 if (desiredAccess & AFS_ACCESS_READ)
350 rights |= (scp->fileType == CM_SCACHETYPE_DIRECTORY ? PRSFS_LOOKUP : PRSFS_READ);
352 /* We used to require PRSFS_WRITE if createDisp was 4
353 (OPEN_ALWAYS) even if AFS_ACCESS_WRITE was not requested.
354 However, we don't need to do that since the existence of the
355 scp implies that we don't need to create it. */
356 if (desiredAccess & AFS_ACCESS_WRITE)
357 rights |= PRSFS_WRITE;
359 lock_ObtainMutex(&scp->mx);
361 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
362 CM_SCACHESYNC_GETSTATUS
363 | CM_SCACHESYNC_NEEDCALLBACK
364 | CM_SCACHESYNC_LOCK);
367 * If the open will fail because the volume is readonly, then we will
368 * return an access denied error instead. This is to help brain-dead
369 * apps run correctly on replicated volumes.
370 * See defect 10007 for more information.
372 if (code == CM_ERROR_READONLY)
373 code = CM_ERROR_NOACCESS;
376 ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
377 scp->fileType == CM_SCACHETYPE_FILE) {
379 unsigned int sLockType;
380 LARGE_INTEGER LOffset, LLength;
382 /* Check if there's some sort of lock on the file at the
385 key = cm_GenerateKey(CM_SESSION_CMINT,0,0);
386 if (rights & PRSFS_WRITE)
389 sLockType = LOCKING_ANDX_SHARED_LOCK;
391 /* single byte lock at offset 0x0100 0000 0000 0000 */
392 LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
393 LOffset.LowPart = CM_FLSHARE_OFFSET_LOW;
394 LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
395 LLength.LowPart = CM_FLSHARE_LENGTH_LOW;
397 code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
400 (*ldpp) = (cm_lock_data_t *)malloc(sizeof(cm_lock_data_t));
407 (*ldpp)->sLockType = sLockType;
408 (*ldpp)->LOffset.HighPart = LOffset.HighPart;
409 (*ldpp)->LOffset.LowPart = LOffset.LowPart;
410 (*ldpp)->LLength.HighPart = LLength.HighPart;
411 (*ldpp)->LLength.LowPart = LLength.LowPart;
413 /* In this case, we allow the file open to go through even
414 though we can't enforce mandatory locking on the
416 if (code == CM_ERROR_NOACCESS &&
417 !(rights & PRSFS_WRITE))
421 case CM_ERROR_ALLOFFLINE:
422 case CM_ERROR_ALLDOWN:
423 case CM_ERROR_ALLBUSY:
424 case CM_ERROR_TIMEDOUT:
426 case CM_ERROR_WOULDBLOCK:
429 code = CM_ERROR_SHARING_VIOLATION;
433 } else if (code != 0) {
437 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
440 lock_ReleaseMutex(&scp->mx);
445 extern long cm_CheckNTOpenDone(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp,
446 cm_lock_data_t ** ldpp)
449 lock_ObtainMutex(&scp->mx);
450 cm_Unlock(scp, (*ldpp)->sLockType, (*ldpp)->LOffset, (*ldpp)->LLength,
451 (*ldpp)->key, userp, reqp);
452 lock_ReleaseMutex(&scp->mx);
459 * When CAP_NT_SMBS has been negotiated, deletion (of files or directories) is
460 * done in three steps:
461 * (1) open for deletion (NT_CREATE_AND_X)
462 * (2) set for deletion on close (NT_TRANSACTION2, SET_FILE_INFO)
464 * We must not do the RPC until step 3. But if we are going to return an error
465 * code (e.g. directory not empty), we must return it by step 2, otherwise most
466 * clients will not notice it. So we do a preliminary check. For deleting
467 * files, this is almost free, since we have already done the RPC to get the
468 * parent directory's status bits. But for deleting directories, we must do an
469 * additional RPC to get the directory's data to check if it is empty. Sigh.
471 long cm_CheckNTDelete(cm_scache_t *dscp, cm_scache_t *scp, cm_user_t *userp,
478 unsigned short *hashTable;
480 int BeyondPage = 0, HaveDot = 0, HaveDotDot = 0;
482 /* First check permissions */
483 lock_ObtainMutex(&scp->mx);
484 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_DELETE,
485 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
486 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
487 lock_ReleaseMutex(&scp->mx);
491 /* If deleting directory, must be empty */
493 if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
496 thyper.HighPart = 0; thyper.LowPart = 0;
497 lock_ObtainRead(&scp->bufCreateLock);
498 code = buf_Get(scp, &thyper, &bufferp);
499 lock_ReleaseRead(&scp->bufCreateLock);
503 lock_ObtainMutex(&bufferp->mx);
504 lock_ObtainMutex(&scp->mx);
506 code = cm_SyncOp(scp, bufferp, userp, reqp, 0,
507 CM_SCACHESYNC_NEEDCALLBACK
509 | CM_SCACHESYNC_BUFLOCKED);
513 if (cm_HaveBuffer(scp, bufferp, 1))
516 /* otherwise, load the buffer and try again */
517 lock_ReleaseMutex(&bufferp->mx);
518 code = cm_GetBuffer(scp, bufferp, NULL, userp, reqp);
519 lock_ReleaseMutex(&scp->mx);
520 lock_ObtainMutex(&bufferp->mx);
521 lock_ObtainMutex(&scp->mx);
522 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
527 /* We try to determine emptiness without looking beyond the first page,
528 * and without assuming "." and ".." are present and are on the first
529 * page (though these assumptions might, after all, be reasonable).
531 hashTable = (unsigned short *)(bufferp->datap + (32 * 5));
532 for (i=0; i<128; i++) {
533 idx = ntohs(hashTable[i]);
539 dep = (cm_dirEntry_t *)(bufferp->datap + (32 * idx));
540 if (strcmp(dep->name, ".") == 0)
542 else if (strcmp(dep->name, "..") == 0)
545 code = CM_ERROR_NOTEMPTY;
548 idx = ntohs(dep->next);
551 if (BeyondPage && HaveDot && HaveDotDot)
552 code = CM_ERROR_NOTEMPTY;
556 lock_ReleaseMutex(&bufferp->mx);
557 buf_Release(bufferp);
558 lock_ReleaseMutex(&scp->mx);
563 * Iterate through all entries in a directory.
564 * When the function funcp is called, the buffer is locked but the
565 * directory vnode is not.
567 * If the retscp parameter is not NULL, the parmp must be a
568 * cm_lookupSearch_t object.
570 long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
571 osi_hyper_t *startOffsetp, cm_user_t *userp, cm_req_t *reqp,
572 cm_scache_t **retscp)
579 osi_hyper_t dirLength;
580 osi_hyper_t bufferOffset;
581 osi_hyper_t curOffset;
585 cm_pageHeader_t *pageHeaderp;
587 long nextEntryCookie;
588 int numDirChunks; /* # of 32 byte dir chunks in this entry */
590 /* get the directory size */
591 lock_ObtainMutex(&scp->mx);
592 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_LOOKUP,
593 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
594 lock_ReleaseMutex(&scp->mx);
598 if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
599 return CM_ERROR_NOTDIR;
601 if (retscp) /* if this is a lookup call */
603 cm_lookupSearch_t* sp = parmp;
606 #ifdef AFS_FREELANCE_CLIENT
607 /* Freelance entries never end up in the DNLC because they
608 * do not have an associated cm_server_t
610 !(cm_freelanceEnabled &&
611 sp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
612 sp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
613 #else /* !AFS_FREELANCE_CLIENT */
618 int casefold = sp->caseFold;
619 sp->caseFold = 0; /* we have a strong preference for exact matches */
620 if ( *retscp = cm_dnlcLookup(scp, sp)) /* dnlc hit */
622 sp->caseFold = casefold;
625 sp->caseFold = casefold;
627 /* see if we can find it using the directory hash tables.
628 we can only do exact matches, since the hash is case
638 code = cm_BeginDirOp(scp, userp, reqp, CM_DIRLOCK_READ, &dirop);
642 code = cm_BPlusDirLookup(&dirop, sp->searchNamep, &sp->fid);
647 code = cm_DirLookup(&dirop, sp->searchNamep, &sp->fid);
655 sp->ExactFound = TRUE;
656 *retscp = NULL; /* force caller to call cm_GetSCache() */
661 if (sp->caseFold && code == CM_ERROR_INEXACT_MATCH) {
664 sp->ExactFound = FALSE;
665 *retscp = NULL; /* force caller to call cm_GetSCache() */
669 return CM_ERROR_BPLUS_NOMATCH;
677 * XXX We only get the length once. It might change when we drop the
680 dirLength = scp->length;
683 bufferOffset.LowPart = bufferOffset.HighPart = 0;
685 curOffset = *startOffsetp;
687 curOffset.HighPart = 0;
688 curOffset.LowPart = 0;
692 /* make sure that curOffset.LowPart doesn't point to the first
693 * 32 bytes in the 2nd through last dir page, and that it
694 * doesn't point at the first 13 32-byte chunks in the first
695 * dir page, since those are dir and page headers, and don't
696 * contain useful information.
698 temp = curOffset.LowPart & (2048-1);
699 if (curOffset.HighPart == 0 && curOffset.LowPart < 2048) {
700 /* we're in the first page */
701 if (temp < 13*32) temp = 13*32;
704 /* we're in a later dir page */
705 if (temp < 32) temp = 32;
708 /* make sure the low order 5 bits are zero */
711 /* now put temp bits back ito curOffset.LowPart */
712 curOffset.LowPart &= ~(2048-1);
713 curOffset.LowPart |= temp;
715 /* check if we've passed the dir's EOF */
716 if (LargeIntegerGreaterThanOrEqualTo(curOffset, dirLength))
719 /* see if we can use the bufferp we have now; compute in which
720 * page the current offset would be, and check whether that's
721 * the offset of the buffer we have. If not, get the buffer.
723 thyper.HighPart = curOffset.HighPart;
724 thyper.LowPart = curOffset.LowPart & ~(cm_data.buf_blockSize-1);
725 if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
728 lock_ReleaseMutex(&bufferp->mx);
729 buf_Release(bufferp);
733 lock_ObtainRead(&scp->bufCreateLock);
734 code = buf_Get(scp, &thyper, &bufferp);
735 lock_ReleaseRead(&scp->bufCreateLock);
737 /* if buf_Get() fails we do not have a buffer object to lock */
743 /* for the IFS version, we bulkstat the dirents because this
744 routine is used in place of smb_ReceiveCoreSearchDir. our
745 other option is to modify smb_ReceiveCoreSearchDir itself,
746 but this seems to be the proper use for cm_ApplyDir. */
747 lock_ObtainMutex(&scp->mx);
748 if ((scp->flags & CM_SCACHEFLAG_BULKSTATTING) == 0
749 && (scp->bulkStatProgress.QuadPart <= thyper.QuadPart))
751 scp->flags |= CM_SCACHEFLAG_BULKSTATTING;
752 code = cm_TryBulkStat(scp, &thyper, userp, reqp);
753 scp->flags &= ~CM_SCACHEFLAG_BULKSTATTING;
754 scp->bulkStatProgress = thyper;
756 lock_ReleaseMutex(&scp->mx);
759 lock_ObtainMutex(&bufferp->mx);
760 bufferOffset = thyper;
762 /* now get the data in the cache */
764 lock_ObtainMutex(&scp->mx);
765 code = cm_SyncOp(scp, bufferp, userp, reqp,
767 CM_SCACHESYNC_NEEDCALLBACK
769 | CM_SCACHESYNC_BUFLOCKED);
771 lock_ReleaseMutex(&scp->mx);
774 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
776 if (cm_HaveBuffer(scp, bufferp, 1)) {
777 lock_ReleaseMutex(&scp->mx);
781 /* otherwise, load the buffer and try again */
782 lock_ReleaseMutex(&bufferp->mx);
783 code = cm_GetBuffer(scp, bufferp, NULL, userp,
785 lock_ReleaseMutex(&scp->mx);
786 lock_ObtainMutex(&bufferp->mx);
791 lock_ReleaseMutex(&bufferp->mx);
792 buf_Release(bufferp);
796 } /* if (wrong buffer) ... */
798 /* now we have the buffer containing the entry we're interested
799 * in; copy it out if it represents a non-deleted entry.
801 entryInDir = curOffset.LowPart & (2048-1);
802 entryInBuffer = curOffset.LowPart & (cm_data.buf_blockSize - 1);
804 /* page header will help tell us which entries are free. Page
805 * header can change more often than once per buffer, since
806 * AFS 3 dir page size may be less than (but not more than) a
807 * buffer package buffer.
809 /* only look intra-buffer */
810 temp = curOffset.LowPart & (cm_data.buf_blockSize - 1);
811 temp &= ~(2048 - 1); /* turn off intra-page bits */
812 pageHeaderp = (cm_pageHeader_t *) (bufferp->datap + temp);
814 /* now determine which entry we're looking at in the page. If
815 * it is free (there's a free bitmap at the start of the dir),
816 * we should skip these 32 bytes.
818 slotInPage = (entryInDir & 0x7e0) >> 5;
819 if (!(pageHeaderp->freeBitmap[slotInPage>>3]
820 & (1 << (slotInPage & 0x7)))) {
821 /* this entry is free */
822 numDirChunks = 1; /* only skip this guy */
826 tp = bufferp->datap + entryInBuffer;
827 dep = (cm_dirEntry_t *) tp; /* now points to AFS3 dir entry */
829 /* while we're here, compute the next entry's location, too,
830 * since we'll need it when writing out the cookie into the
831 * dir listing stream.
833 numDirChunks = cm_NameEntries(dep->name, NULL);
835 /* compute the offset of the cookie representing the next entry */
836 nextEntryCookie = curOffset.LowPart
837 + (CM_DIR_CHUNKSIZE * numDirChunks);
839 if (dep->fid.vnode != 0) {
840 /* this is one of the entries to use: it is not deleted */
841 code = (*funcp)(scp, dep, parmp, &curOffset);
844 } /* if we're including this name */
847 /* and adjust curOffset to be where the new cookie is */
849 thyper.LowPart = CM_DIR_CHUNKSIZE * numDirChunks;
850 curOffset = LargeIntegerAdd(thyper, curOffset);
851 } /* while copying data for dir listing */
853 /* release the mutex */
855 lock_ReleaseMutex(&bufferp->mx);
856 buf_Release(bufferp);
861 int cm_NoneUpper(char *s)
865 if (c >= 'A' && c <= 'Z')
870 int cm_NoneLower(char *s)
874 if (c >= 'a' && c <= 'z')
879 long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
882 cm_lookupSearch_t *sp;
887 sp = (cm_lookupSearch_t *) rockp;
889 matchName = dep->name;
891 match = cm_stricmp(matchName, sp->searchNamep);
893 match = strcmp(matchName, sp->searchNamep);
897 && !cm_Is8Dot3(dep->name)) {
898 matchName = shortName;
899 cm_Gen8Dot3Name(dep, shortName, NULL);
901 match = cm_stricmp(matchName, sp->searchNamep);
903 match = strcmp(matchName, sp->searchNamep);
913 if (!sp->caseFold || matchName == shortName) {
914 sp->fid.vnode = ntohl(dep->fid.vnode);
915 sp->fid.unique = ntohl(dep->fid.unique);
916 return CM_ERROR_STOPNOW;
920 * If we get here, we are doing a case-insensitive search, and we
921 * have found a match. Now we determine what kind of match it is:
922 * exact, lower-case, upper-case, or none of the above. This is done
923 * in order to choose among matches, if there are more than one.
926 /* Exact matches are the best. */
927 match = strcmp(matchName, sp->searchNamep);
930 sp->fid.vnode = ntohl(dep->fid.vnode);
931 sp->fid.unique = ntohl(dep->fid.unique);
932 return CM_ERROR_STOPNOW;
935 /* Lower-case matches are next. */
938 if (cm_NoneUpper(matchName)) {
943 /* Upper-case matches are next. */
946 if (cm_NoneLower(matchName)) {
951 /* General matches are last. */
957 sp->fid.vnode = ntohl(dep->fid.vnode);
958 sp->fid.unique = ntohl(dep->fid.unique);
962 /* read the contents of a mount point into the appropriate string.
963 * called with locked scp, and returns with locked scp.
965 long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
972 if (scp->mountPointStringp[0])
975 /* otherwise, we have to read it in */
976 lock_ReleaseMutex(&scp->mx);
978 lock_ObtainRead(&scp->bufCreateLock);
979 thyper.LowPart = thyper.HighPart = 0;
980 code = buf_Get(scp, &thyper, &bufp);
981 lock_ReleaseRead(&scp->bufCreateLock);
983 lock_ObtainMutex(&scp->mx);
988 code = cm_SyncOp(scp, bufp, userp, reqp, 0,
989 CM_SCACHESYNC_READ | CM_SCACHESYNC_NEEDCALLBACK);
993 cm_SyncOpDone(scp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
995 if (cm_HaveBuffer(scp, bufp, 0))
998 /* otherwise load buffer */
999 code = cm_GetBuffer(scp, bufp, NULL, userp, reqp);
1003 /* locked, has callback, has valid data in buffer */
1004 if ((tlen = scp->length.LowPart) > MOUNTPOINTLEN - 1)
1005 return CM_ERROR_TOOBIG;
1007 code = CM_ERROR_INVAL;
1011 /* someone else did the work while we were out */
1012 if (scp->mountPointStringp[0]) {
1017 /* otherwise, copy out the link */
1018 memcpy(scp->mountPointStringp, bufp->datap, tlen);
1020 /* now make it null-terminated. Note that the original contents of a
1021 * link that is a mount point is "#volname." where "." is there just to
1022 * be turned into a null. That is, we can trash the last char of the
1023 * link without damaging the vol name. This is a stupid convention,
1024 * but that's the protocol.
1026 scp->mountPointStringp[tlen-1] = 0;
1036 /* called with a locked scp and chases the mount point, yielding outScpp.
1037 * scp remains locked, just for simplicity of describing the interface.
1039 long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
1040 cm_req_t *reqp, cm_scache_t **outScpp)
1048 cm_volume_t *volp = NULL;
1055 if (scp->mountRootFid.cell != 0 && scp->mountRootGen >= cm_data.mountRootGen) {
1056 tfid = scp->mountRootFid;
1057 lock_ReleaseMutex(&scp->mx);
1058 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
1059 lock_ObtainMutex(&scp->mx);
1063 /* parse the volume name */
1064 mpNamep = scp->mountPointStringp;
1066 return CM_ERROR_NOSUCHPATH;
1067 tlen = (int)strlen(scp->mountPointStringp);
1068 mtType = *scp->mountPointStringp;
1069 cellNamep = malloc(tlen);
1070 volNamep = malloc(tlen);
1072 cp = strrchr(mpNamep, ':');
1074 /* cellular mount point */
1075 memset(cellNamep, 0, tlen);
1076 strncpy(cellNamep, mpNamep+1, cp - mpNamep - 1);
1077 strcpy(volNamep, cp+1);
1078 /* now look up the cell */
1079 lock_ReleaseMutex(&scp->mx);
1080 cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE);
1081 lock_ObtainMutex(&scp->mx);
1085 strcpy(volNamep, mpNamep+1);
1087 cellp = cm_FindCellByID(scp->fid.cell);
1091 code = CM_ERROR_NOSUCHCELL;
1095 vnLength = strlen(volNamep);
1096 if (vnLength >= 8 && strcmp(volNamep + vnLength - 7, ".backup") == 0)
1097 targetType = BACKVOL;
1098 else if (vnLength >= 10
1099 && strcmp(volNamep + vnLength - 9, ".readonly") == 0)
1104 /* check for backups within backups */
1105 if (targetType == BACKVOL
1106 && (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
1107 == CM_SCACHEFLAG_RO) {
1108 code = CM_ERROR_NOSUCHVOLUME;
1112 /* now we need to get the volume */
1113 lock_ReleaseMutex(&scp->mx);
1114 if (cm_VolNameIsID(volNamep)) {
1115 code = cm_GetVolumeByID(cellp, atoi(volNamep), userp, reqp,
1116 CM_GETVOL_FLAG_CREATE, &volp);
1118 code = cm_GetVolumeByName(cellp, volNamep, userp, reqp,
1119 CM_GETVOL_FLAG_CREATE, &volp);
1121 lock_ObtainMutex(&scp->mx);
1124 /* save the parent of the volume root for this is the
1125 * place where the volume is mounted and we must remember
1126 * this in the volume structure rather than just in the
1127 * scache entry lest the scache entry gets recycled
1130 lock_ObtainMutex(&volp->mx);
1131 volp->dotdotFid = dscp->fid;
1132 lock_ReleaseMutex(&volp->mx);
1134 scp->mountRootFid.cell = cellp->cellID;
1136 /* if the mt pt originates in a .backup volume (not a .readonly)
1137 * and FollowBackupPath is active, and if there is a .backup
1138 * volume for the target, then use the .backup of the target
1139 * instead of the read-write.
1141 if (cm_followBackupPath && targetType == RWVOL &&
1142 (scp->flags & CM_SCACHEFLAG_RO|CM_SCACHEFLAG_PURERO) == CM_SCACHEFLAG_RO &&
1144 targetType = BACKVOL;
1146 /* if the mt pt is in a read-only volume (not just a
1147 * backup), and if there is a read-only volume for the
1148 * target, and if this is a targetType '#' mount point, use
1149 * the read-only, otherwise use the one specified.
1151 else if (mtType == '#' && targetType == RWVOL &&
1152 (scp->flags & CM_SCACHEFLAG_PURERO) &&
1156 if (targetType == ROVOL)
1157 scp->mountRootFid.volume = volp->ro.ID;
1158 else if (targetType == BACKVOL)
1159 scp->mountRootFid.volume = volp->bk.ID;
1161 scp->mountRootFid.volume = volp->rw.ID;
1163 /* the rest of the fid is a magic number */
1164 scp->mountRootFid.vnode = 1;
1165 scp->mountRootFid.unique = 1;
1166 scp->mountRootGen = cm_data.mountRootGen;
1168 tfid = scp->mountRootFid;
1169 lock_ReleaseMutex(&scp->mx);
1170 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
1171 lock_ObtainMutex(&scp->mx);
1182 long cm_LookupInternal(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
1183 cm_req_t *reqp, cm_scache_t **outpScpp)
1186 int dnlcHit = 1; /* did we hit in the dnlc? yes, we did */
1187 cm_scache_t *tscp = NULL;
1188 cm_scache_t *mountedScp;
1189 cm_lookupSearch_t rock;
1192 memset(&rock, 0, sizeof(rock));
1194 if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
1195 && strcmp(namep, "..") == 0) {
1196 if (dscp->dotdotFid.volume == 0)
1197 return CM_ERROR_NOSUCHVOLUME;
1198 rock.fid = dscp->dotdotFid;
1200 } else if (strcmp(namep, ".") == 0) {
1201 rock.fid = dscp->fid;
1205 if (flags & CM_FLAG_NOMOUNTCHASE) {
1206 /* In this case, we should go and call cm_Dir* functions
1207 directly since the following cm_ApplyDir() function will
1215 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ, &dirop);
1218 code = cm_BPlusDirLookup(&dirop, namep, &rock.fid);
1223 code = cm_DirLookup(&dirop, namep, &rock.fid);
1225 cm_EndDirOp(&dirop);
1235 if (code == CM_ERROR_INEXACT_MATCH && (flags & CM_FLAG_CASEFOLD)) {
1242 return CM_ERROR_BPLUS_NOMATCH;
1247 rock.fid.cell = dscp->fid.cell;
1248 rock.fid.volume = dscp->fid.volume;
1249 rock.searchNamep = namep;
1250 rock.caseFold = (flags & CM_FLAG_CASEFOLD);
1251 rock.hasTilde = ((strchr(namep, '~') != NULL) ? 1 : 0);
1253 /* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
1254 code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
1255 (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
1257 /* code == 0 means we fell off the end of the dir, while stopnow means
1258 * that we stopped early, probably because we found the entry we're
1259 * looking for. Any other non-zero code is an error.
1261 if (code && code != CM_ERROR_STOPNOW) {
1262 /* if the cm_scache_t we are searching in is not a directory
1263 * we must return path not found because the error
1264 * is to describe the final component not an intermediary
1266 if (code == CM_ERROR_NOTDIR) {
1267 if (flags & CM_FLAG_CHECKPATH)
1268 return CM_ERROR_NOSUCHPATH;
1270 return CM_ERROR_NOSUCHFILE;
1275 getroot = (dscp==cm_data.rootSCachep) ;
1277 if (!cm_freelanceEnabled || !getroot) {
1278 if (flags & CM_FLAG_CHECKPATH)
1279 return CM_ERROR_NOSUCHPATH;
1281 return CM_ERROR_NOSUCHFILE;
1283 else { /* nonexistent dir on freelance root, so add it */
1284 char fullname[200] = ".";
1287 osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %s",
1288 osi_LogSaveString(afsd_logp,namep));
1289 if (namep[0] == '.') {
1290 if (cm_GetCell_Gen(&namep[1], &fullname[1], CM_FLAG_CREATE)) {
1292 if ( stricmp(&namep[1], &fullname[1]) )
1293 code = cm_FreelanceAddSymlink(namep, fullname, &rock.fid);
1295 code = cm_FreelanceAddMount(namep, &fullname[1], "root.cell.", 1, &rock.fid);
1298 if (cm_GetCell_Gen(namep, fullname, CM_FLAG_CREATE)) {
1300 if ( stricmp(namep, fullname) )
1301 code = cm_FreelanceAddSymlink(namep, fullname, &rock.fid);
1303 code = cm_FreelanceAddMount(namep, fullname, "root.cell.", 0, &rock.fid);
1306 if (!found || code < 0) { /* add mount point failed, so give up */
1307 if (flags & CM_FLAG_CHECKPATH)
1308 return CM_ERROR_NOSUCHPATH;
1310 return CM_ERROR_NOSUCHFILE;
1312 tscp = NULL; /* to force call of cm_GetSCache */
1317 if ( !tscp ) /* we did not find it in the dnlc */
1320 code = cm_GetSCache(&rock.fid, &tscp, userp, reqp);
1324 /* tscp is now held */
1326 lock_ObtainMutex(&tscp->mx);
1327 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1328 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1330 lock_ReleaseMutex(&tscp->mx);
1331 cm_ReleaseSCache(tscp);
1334 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1335 /* tscp is now locked */
1337 if (!(flags & CM_FLAG_NOMOUNTCHASE)
1338 && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
1339 /* mount points are funny: they have a volume name to mount
1342 code = cm_ReadMountPoint(tscp, userp, reqp);
1344 code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
1346 lock_ReleaseMutex(&tscp->mx);
1347 cm_ReleaseSCache(tscp);
1354 lock_ReleaseMutex(&tscp->mx);
1357 /* copy back pointer */
1360 /* insert scache in dnlc */
1361 if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
1362 /* lock the directory entry to prevent racing callback revokes */
1363 lock_ObtainMutex(&dscp->mx);
1364 if ( dscp->cbServerp != NULL && dscp->cbExpires > 0 )
1365 cm_dnlcEnter(dscp, namep, tscp);
1366 lock_ReleaseMutex(&dscp->mx);
1373 int cm_ExpandSysName(char *inp, char *outp, long outSize, unsigned int index)
1378 tp = strrchr(inp, '@');
1380 return 0; /* no @sys */
1382 if (strcmp(tp, "@sys") != 0)
1383 return 0; /* no @sys */
1385 /* caller just wants to know if this is a valid @sys type of name */
1389 if (index >= MAXNUMSYSNAMES)
1392 /* otherwise generate the properly expanded @sys name */
1393 prefixCount = (int)(tp - inp);
1395 strncpy(outp, inp, prefixCount); /* copy out "a." from "a.@sys" */
1396 outp[prefixCount] = 0; /* null terminate the "a." */
1397 strcat(outp, cm_sysNameList[index]);/* append i386_nt40 */
1401 long cm_EvaluateVolumeReference(char * namep, long flags, cm_user_t * userp,
1402 cm_req_t *reqp, cm_scache_t ** outpScpp)
1405 char cellName[CELL_MAXNAMELEN];
1406 char volumeName[VL_MAXNAMELEN];
1411 cm_cell_t * cellp = NULL;
1412 cm_volume_t * volp = NULL;
1415 int mountType = RWVOL;
1417 osi_Log1(afsd_logp, "cm_EvaluateVolumeReference for string [%s]",
1418 osi_LogSaveString(afsd_logp, namep));
1420 if (strnicmp(namep, CM_PREFIX_VOL, CM_PREFIX_VOL_CCH) != 0) {
1421 goto _exit_invalid_path;
1424 /* namep is assumed to look like the following:
1426 @vol:<cellname>%<volume>\0
1428 @vol:<cellname>#<volume>\0
1432 cp = namep + CM_PREFIX_VOL_CCH; /* cp points to cell name, hopefully */
1433 tp = strchr(cp, '%');
1435 tp = strchr(cp, '#');
1437 (len = tp - cp) == 0 ||
1438 len > CELL_MAXNAMELEN)
1439 goto _exit_invalid_path;
1440 strncpy(cellName, cp, len);
1441 cellName[len] = '\0';
1446 cp = tp+1; /* cp now points to volume, supposedly */
1447 strncpy(volumeName, cp, VL_MAXNAMELEN-1);
1448 volumeName[VL_MAXNAMELEN - 1] = 0;
1450 /* OK, now we have the cell and the volume */
1451 osi_Log2(afsd_logp, " Found cell [%s] and volume [%s]",
1452 osi_LogSaveString(afsd_logp, cellName),
1453 osi_LogSaveString(afsd_logp, volumeName));
1455 cellp = cm_GetCell(cellName, CM_FLAG_CREATE);
1456 if (cellp == NULL) {
1457 goto _exit_invalid_path;
1460 len = strlen(volumeName);
1461 if (len >= 8 && strcmp(volumeName + len - 7, ".backup") == 0)
1463 else if (len >= 10 &&
1464 strcmp(volumeName + len - 9, ".readonly") == 0)
1469 if (cm_VolNameIsID(volumeName)) {
1470 code = cm_GetVolumeByID(cellp, atoi(volumeName), userp, reqp,
1471 CM_GETVOL_FLAG_CREATE, &volp);
1473 code = cm_GetVolumeByName(cellp, volumeName, userp, reqp,
1474 CM_GETVOL_FLAG_CREATE, &volp);
1480 fid.cell = cellp->cellID;
1482 if (volType == BACKVOL)
1483 fid.volume = volp->bk.ID;
1484 else if (volType == ROVOL ||
1485 (volType == RWVOL && mountType == ROVOL && volp->ro.ID != 0))
1486 fid.volume = volp->ro.ID;
1488 fid.volume = volp->rw.ID;
1493 code = cm_GetSCache(&fid, outpScpp, userp, reqp);
1503 if (flags & CM_FLAG_CHECKPATH)
1504 return CM_ERROR_NOSUCHPATH;
1506 return CM_ERROR_NOSUCHFILE;
1509 #ifdef DEBUG_REFCOUNT
1510 long cm_LookupDbg(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
1511 cm_req_t *reqp, cm_scache_t **outpScpp, char * file, long line)
1513 long cm_Lookup(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
1514 cm_req_t *reqp, cm_scache_t **outpScpp)
1518 char tname[AFSPATHMAX];
1519 int sysNameIndex = 0;
1520 cm_scache_t *scp = NULL;
1522 #ifdef DEBUG_REFCOUNT
1523 afsi_log("%s:%d cm_Lookup dscp 0x%p ref %d", file, line, dscp, dscp->refCount, file, line);
1524 osi_Log2(afsd_logp, "cm_Lookup dscp 0x%p ref %d", dscp, dscp->refCount);
1527 if ( stricmp(namep,SMB_IOCTL_FILENAME_NOSLASH) == 0 ) {
1528 if (flags & CM_FLAG_CHECKPATH)
1529 return CM_ERROR_NOSUCHPATH;
1531 return CM_ERROR_NOSUCHFILE;
1534 if (dscp == cm_data.rootSCachep &&
1535 strnicmp(namep, CM_PREFIX_VOL, CM_PREFIX_VOL_CCH) == 0) {
1536 return cm_EvaluateVolumeReference(namep, flags, userp, reqp, outpScpp);
1539 if (cm_ExpandSysName(namep, NULL, 0, 0) > 0) {
1540 for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
1541 code = cm_ExpandSysName(namep, tname, sizeof(tname), sysNameIndex);
1543 code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
1544 #ifdef DEBUG_REFCOUNT
1545 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);
1546 osi_Log3(afsd_logp, "cm_LookupInternal (1) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1554 cm_ReleaseSCache(scp);
1558 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1559 #ifdef DEBUG_REFCOUNT
1560 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);
1561 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1568 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1569 #ifdef DEBUG_REFCOUNT
1570 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);
1571 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1577 /* None of the possible sysName expansions could be found */
1578 if (flags & CM_FLAG_CHECKPATH)
1579 return CM_ERROR_NOSUCHPATH;
1581 return CM_ERROR_NOSUCHFILE;
1584 long cm_Unlink(cm_scache_t *dscp, char *namep, cm_user_t *userp, cm_req_t *reqp)
1590 AFSFetchStatus newDirStatus;
1592 struct rx_connection * callp;
1595 #ifdef AFS_FREELANCE_CLIENT
1596 if (cm_freelanceEnabled && dscp == cm_data.rootSCachep) {
1597 /* deleting a mount point from the root dir. */
1598 code = cm_FreelanceRemoveMount(namep);
1603 /* make sure we don't screw up the dir status during the merge */
1604 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
1606 lock_ObtainMutex(&dscp->mx);
1607 sflags = CM_SCACHESYNC_STOREDATA;
1608 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1609 lock_ReleaseMutex(&dscp->mx);
1611 cm_EndDirOp(&dirop);
1616 afsFid.Volume = dscp->fid.volume;
1617 afsFid.Vnode = dscp->fid.vnode;
1618 afsFid.Unique = dscp->fid.unique;
1620 osi_Log1(afsd_logp, "CALL RemoveFile scp 0x%p", dscp);
1622 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
1626 callp = cm_GetRxConn(connp);
1627 code = RXAFS_RemoveFile(callp, &afsFid, namep,
1628 &newDirStatus, &volSync);
1629 rx_PutConnection(callp);
1631 } while (cm_Analyze(connp, userp, reqp, &dscp->fid, &volSync, NULL, NULL, code));
1632 code = cm_MapRPCError(code, reqp);
1635 osi_Log1(afsd_logp, "CALL RemoveFile FAILURE, code 0x%x", code);
1637 osi_Log0(afsd_logp, "CALL RemoveFile SUCCESS");
1640 lock_ObtainWrite(&dirop.scp->dirlock);
1641 dirop.lockType = CM_DIRLOCK_WRITE;
1643 lock_ObtainMutex(&dscp->mx);
1644 cm_dnlcRemove(dscp, namep);
1645 cm_SyncOpDone(dscp, NULL, sflags);
1647 cm_MergeStatus(NULL, dscp, &newDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
1648 } else if (code == CM_ERROR_NOSUCHFILE) {
1649 /* windows would not have allowed the request to delete the file
1650 * if it did not believe the file existed. therefore, we must
1651 * have an inconsistent view of the world.
1653 dscp->cbServerp = NULL;
1655 lock_ReleaseMutex(&dscp->mx);
1657 if (code == 0 && cm_CheckDirOpForSingleChange(&dirop)) {
1658 cm_DirDeleteEntry(&dirop, namep);
1660 cm_BPlusDirDeleteEntry(&dirop, namep);
1663 cm_EndDirOp(&dirop);
1668 /* called with a locked vnode, and fills in the link info.
1669 * returns this the vnode still locked.
1671 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1678 lock_AssertMutex(&linkScp->mx);
1679 if (!linkScp->mountPointStringp[0]) {
1680 /* read the link data */
1681 lock_ReleaseMutex(&linkScp->mx);
1682 thyper.LowPart = thyper.HighPart = 0;
1683 code = buf_Get(linkScp, &thyper, &bufp);
1684 lock_ObtainMutex(&linkScp->mx);
1688 code = cm_SyncOp(linkScp, bufp, userp, reqp, 0,
1689 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1694 cm_SyncOpDone(linkScp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1696 if (cm_HaveBuffer(linkScp, bufp, 0))
1699 code = cm_GetBuffer(linkScp, bufp, NULL, userp, reqp);
1704 } /* while loop to get the data */
1706 /* now if we still have no link read in,
1707 * copy the data from the buffer */
1708 if ((temp = linkScp->length.LowPart) >= MOUNTPOINTLEN) {
1710 return CM_ERROR_TOOBIG;
1713 /* otherwise, it fits; make sure it is still null (could have
1714 * lost race with someone else referencing this link above),
1715 * and if so, copy in the data.
1717 if (!linkScp->mountPointStringp[0]) {
1718 strncpy(linkScp->mountPointStringp, bufp->datap, temp);
1719 linkScp->mountPointStringp[temp] = 0; /* null terminate */
1722 } /* don't have sym link contents cached */
1727 /* called with a held vnode and a path suffix, with the held vnode being a
1728 * symbolic link. Our goal is to generate a new path to interpret, and return
1729 * this new path in newSpaceBufferp. If the new vnode is relative to a dir
1730 * other than the directory containing the symbolic link, then the new root is
1731 * returned in *newRootScpp, otherwise a null is returned there.
1733 long cm_AssembleLink(cm_scache_t *linkScp, char *pathSuffixp,
1734 cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1735 cm_user_t *userp, cm_req_t *reqp)
1742 lock_ObtainMutex(&linkScp->mx);
1743 code = cm_HandleLink(linkScp, userp, reqp);
1747 /* if we may overflow the buffer, bail out; buffer is signficantly
1748 * bigger than max path length, so we don't really have to worry about
1749 * being a little conservative here.
1751 if (strlen(linkScp->mountPointStringp) + strlen(pathSuffixp) + 2
1752 >= CM_UTILS_SPACESIZE)
1753 return CM_ERROR_TOOBIG;
1755 tsp = cm_GetSpace();
1756 linkp = linkScp->mountPointStringp;
1757 if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1758 if (strlen(linkp) > cm_mountRootLen)
1759 strcpy(tsp->data, linkp+cm_mountRootLen+1);
1762 *newRootScpp = cm_data.rootSCachep;
1763 cm_HoldSCache(cm_data.rootSCachep);
1764 } else if (linkp[0] == '\\' && linkp[1] == '\\') {
1765 if (!strnicmp(&linkp[2], cm_NetbiosName, (len = (long)strlen(cm_NetbiosName))))
1767 char * p = &linkp[len + 3];
1768 if (strnicmp(p, "all", 3) == 0)
1771 strcpy(tsp->data, p);
1772 for (p = tsp->data; *p; p++) {
1776 *newRootScpp = cm_data.rootSCachep;
1777 cm_HoldSCache(cm_data.rootSCachep);
1779 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1780 strcpy(tsp->data, linkp);
1781 *newRootScpp = NULL;
1782 code = CM_ERROR_PATH_NOT_COVERED;
1784 } else if ( !strnicmp(linkp, "msdfs:", (len = (long)strlen("msdfs:"))) ) {
1785 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1786 strcpy(tsp->data, linkp);
1787 *newRootScpp = NULL;
1788 code = CM_ERROR_PATH_NOT_COVERED;
1789 } else if (*linkp == '\\' || *linkp == '/') {
1791 /* formerly, this was considered to be from the AFS root,
1792 * but this seems to create problems. instead, we will just
1793 * reject the link */
1794 strcpy(tsp->data, linkp+1);
1795 *newRootScpp = cm_data.rootSCachep;
1796 cm_HoldSCache(cm_data.rootSCachep);
1798 /* we still copy the link data into the response so that
1799 * the user can see what the link points to
1801 linkScp->fileType = CM_SCACHETYPE_INVALID;
1802 strcpy(tsp->data, linkp);
1803 *newRootScpp = NULL;
1804 code = CM_ERROR_NOSUCHPATH;
1807 /* a relative link */
1808 strcpy(tsp->data, linkp);
1809 *newRootScpp = NULL;
1811 if (pathSuffixp[0] != 0) { /* if suffix string is non-null */
1812 strcat(tsp->data, "\\");
1813 strcat(tsp->data, pathSuffixp);
1815 *newSpaceBufferp = tsp;
1818 lock_ReleaseMutex(&linkScp->mx);
1821 #ifdef DEBUG_REFCOUNT
1822 long cm_NameIDbg(cm_scache_t *rootSCachep, char *pathp, long flags,
1823 cm_user_t *userp, char *tidPathp, cm_req_t *reqp, cm_scache_t **outScpp,
1824 char * file, long line)
1826 long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
1827 cm_user_t *userp, char *tidPathp, cm_req_t *reqp, cm_scache_t **outScpp)
1831 char *tp; /* ptr moving through input buffer */
1832 char tc; /* temp char */
1833 int haveComponent; /* has new component started? */
1834 char component[AFSPATHMAX]; /* this is the new component */
1835 char *cp; /* component name being assembled */
1836 cm_scache_t *tscp; /* current location in the hierarchy */
1837 cm_scache_t *nscp; /* next dude down */
1838 cm_scache_t *dirScp; /* last dir we searched */
1839 cm_scache_t *linkScp; /* new root for the symlink we just
1841 cm_space_t *psp; /* space for current path, if we've hit
1843 cm_space_t *tempsp; /* temp vbl */
1844 char *restp; /* rest of the pathname to interpret */
1845 int symlinkCount; /* count of # of symlinks traversed */
1846 int extraFlag; /* avoid chasing mt pts for dir cmd */
1847 int phase = 1; /* 1 = tidPathp, 2 = pathp */
1848 #define MAX_FID_COUNT 512
1849 cm_fid_t fids[MAX_FID_COUNT]; /* array of fids processed in this path walk */
1850 int fid_count = 0; /* number of fids processed in this path walk */
1853 #ifdef DEBUG_REFCOUNT
1854 afsi_log("%s:%d cm_NameI rootscp 0x%p ref %d", file, line, rootSCachep, rootSCachep->refCount);
1855 osi_Log4(afsd_logp,"cm_NameI rootscp 0x%p path %s tidpath %s flags 0x%x",
1856 rootSCachep, pathp ? pathp : "<NULL>", tidPathp ? tidPathp : "<NULL>",
1871 cm_HoldSCache(tscp);
1879 /* map Unix slashes into DOS ones so we can interpret Unix
1885 if (!haveComponent) {
1888 } else if (tc == 0) {
1902 /* we have a component here */
1903 if (tc == 0 || tc == '\\') {
1904 /* end of the component; we're at the last
1905 * component if tc == 0. However, if the last
1906 * is a symlink, we have more to do.
1908 *cp++ = 0; /* add null termination */
1910 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
1911 extraFlag = CM_FLAG_NOMOUNTCHASE;
1912 code = cm_Lookup(tscp, component,
1914 userp, reqp, &nscp);
1917 if (!strcmp(component,"..") || !strcmp(component,".")) {
1919 * roll back the fid list until we find the fid
1920 * that matches where we are now. Its not necessarily
1921 * one or two fids because they might have been
1922 * symlinks or mount points or both that were crossed.
1924 for ( i=fid_count-1; i>=0; i--) {
1925 if (!cm_FidCmp(&nscp->fid, &fids[i]))
1929 /* add the new fid to the list */
1930 for ( i=0; i<fid_count; i++) {
1931 if ( !cm_FidCmp(&nscp->fid, &fids[i]) ) {
1932 code = CM_ERROR_TOO_MANY_SYMLINKS;
1933 cm_ReleaseSCache(nscp);
1938 if (i == fid_count && fid_count < MAX_FID_COUNT) {
1939 fids[fid_count++] = nscp->fid;
1945 cm_ReleaseSCache(tscp);
1947 cm_ReleaseSCache(dirScp);
1950 if ((code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH) &&
1951 tscp->fileType == CM_SCACHETYPE_SYMLINK)
1953 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_NOSUCHPATH");
1954 return CM_ERROR_NOSUCHPATH;
1956 osi_Log1(afsd_logp,"cm_NameI code 0x%x", code);
1961 haveComponent = 0; /* component done */
1963 cm_ReleaseSCache(dirScp);
1964 dirScp = tscp; /* for some symlinks */
1965 tscp = nscp; /* already held */
1967 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
1970 cm_ReleaseSCache(dirScp);
1976 /* now, if tscp is a symlink, we should follow
1977 * it and assemble the path again.
1979 lock_ObtainMutex(&tscp->mx);
1980 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1981 CM_SCACHESYNC_GETSTATUS
1982 | CM_SCACHESYNC_NEEDCALLBACK);
1984 lock_ReleaseMutex(&tscp->mx);
1985 cm_ReleaseSCache(tscp);
1988 cm_ReleaseSCache(dirScp);
1993 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1995 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
1996 /* this is a symlink; assemble a new buffer */
1997 lock_ReleaseMutex(&tscp->mx);
1998 if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
1999 cm_ReleaseSCache(tscp);
2002 cm_ReleaseSCache(dirScp);
2007 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_TOO_MANY_SYMLINKS");
2008 return CM_ERROR_TOO_MANY_SYMLINKS;
2014 code = cm_AssembleLink(tscp, restp, &linkScp, &tempsp, userp, reqp);
2016 if (code == 0 && linkScp != NULL) {
2017 if (linkScp == cm_data.rootSCachep)
2020 for ( i=0; i<fid_count; i++) {
2021 if ( !cm_FidCmp(&linkScp->fid, &fids[i]) ) {
2022 code = CM_ERROR_TOO_MANY_SYMLINKS;
2023 cm_ReleaseSCache(linkScp);
2029 if (i == fid_count && fid_count < MAX_FID_COUNT) {
2030 fids[fid_count++] = linkScp->fid;
2035 /* something went wrong */
2036 cm_ReleaseSCache(tscp);
2039 cm_ReleaseSCache(dirScp);
2045 /* otherwise, tempsp has the new path,
2046 * and linkScp is the new root from
2047 * which to interpret that path.
2048 * Continue with the namei processing,
2049 * also doing the bookkeeping for the
2050 * space allocation and tracking the
2051 * vnode reference counts.
2057 cm_ReleaseSCache(tscp);
2062 * now, if linkScp is null, that's
2063 * AssembleLink's way of telling us that
2064 * the sym link is relative to the dir
2065 * containing the link. We have a ref
2066 * to it in dirScp, and we hold it now
2067 * and reuse it as the new spot in the
2075 /* not a symlink, we may be done */
2076 lock_ReleaseMutex(&tscp->mx);
2084 cm_ReleaseSCache(dirScp);
2092 cm_ReleaseSCache(dirScp);
2095 } /* end of a component */
2098 } /* we have a component */
2099 } /* big while loop over all components */
2103 cm_ReleaseSCache(dirScp);
2109 cm_ReleaseSCache(tscp);
2111 #ifdef DEBUG_REFCOUNT
2112 afsi_log("%s:%d cm_NameI code 0x%x outScpp 0x%p ref %d", file, line, code, *outScpp, (*outScpp)->refCount);
2114 osi_Log2(afsd_logp,"cm_NameI code 0x%x outScpp 0x%p", code, *outScpp);
2118 /* called with a dir, and a vnode within the dir that happens to be a symlink.
2119 * We chase the link, and return a held pointer to the target, if it exists,
2120 * in *outScpp. If we succeed, we return 0, otherwise we return an error code
2121 * and do not hold or return a target vnode.
2123 * This is very similar to calling cm_NameI with the last component of a name,
2124 * which happens to be a symlink, except that we've already passed by the name.
2126 * This function is typically called by the directory listing functions, which
2127 * encounter symlinks but need to return the proper file length so programs
2128 * like "more" work properly when they make use of the attributes retrieved from
2131 * The input vnode should not be locked when this function is called.
2133 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
2134 cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
2138 cm_scache_t *newRootScp;
2140 osi_Log1(afsd_logp, "Evaluating symlink scp 0x%p", linkScp);
2142 code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
2146 /* now, if newRootScp is NULL, we're really being told that the symlink
2147 * is relative to the current directory (dscp).
2149 if (newRootScp == NULL) {
2151 cm_HoldSCache(dscp);
2154 code = cm_NameI(newRootScp, spacep->data,
2155 CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
2156 userp, NULL, reqp, outScpp);
2158 if (code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH)
2159 code = CM_ERROR_NOSUCHPATH;
2161 /* this stuff is allocated no matter what happened on the namei call,
2163 cm_FreeSpace(spacep);
2164 cm_ReleaseSCache(newRootScp);
2166 if (linkScp == *outScpp) {
2167 cm_ReleaseSCache(*outScpp);
2169 code = CM_ERROR_NOSUCHPATH;
2175 /* make this big enough so that one buffer of dir pages won't overflow. We'll
2176 * check anyway, but we want to minimize the chance that we have to leave stuff
2179 #define CM_BULKMAX (3 * AFSCBMAX)
2181 /* rock for bulk stat calls */
2182 typedef struct cm_bulkStat {
2183 osi_hyper_t bufOffset; /* only do it for things in this buffer page */
2185 /* info for the actual call */
2186 int counter; /* next free slot */
2187 AFSFid fids[CM_BULKMAX];
2188 AFSFetchStatus stats[CM_BULKMAX];
2189 AFSCallBack callbacks[CM_BULKMAX];
2192 /* for a given entry, make sure that it isn't in the stat cache, and then
2193 * add it to the list of file IDs to be obtained.
2195 * Don't bother adding it if we already have a vnode. Note that the dir
2196 * is locked, so we have to be careful checking the vnode we're thinking of
2197 * processing, to avoid deadlocks.
2199 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
2210 /* Don't overflow bsp. */
2211 if (bsp->counter >= CM_BULKMAX)
2212 return CM_ERROR_STOPNOW;
2214 thyper.LowPart = cm_data.buf_blockSize;
2215 thyper.HighPart = 0;
2216 thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
2218 /* thyper is now the first byte past the end of the record we're
2219 * interested in, and bsp->bufOffset is the first byte of the record
2220 * we're interested in.
2221 * Skip data in the others.
2224 if (LargeIntegerLessThan(*offp, bsp->bufOffset))
2226 if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
2227 return CM_ERROR_STOPNOW;
2228 if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
2231 tfid.cell = scp->fid.cell;
2232 tfid.volume = scp->fid.volume;
2233 tfid.vnode = ntohl(dep->fid.vnode);
2234 tfid.unique = ntohl(dep->fid.unique);
2235 tscp = cm_FindSCache(&tfid);
2237 if (lock_TryMutex(&tscp->mx)) {
2238 /* we have an entry that we can look at */
2239 if (!(tscp->flags & CM_SCACHEFLAG_EACCESS) && cm_HaveCallback(tscp)) {
2240 /* we have a callback on it. Don't bother
2241 * fetching this stat entry, since we're happy
2242 * with the info we have.
2244 lock_ReleaseMutex(&tscp->mx);
2245 cm_ReleaseSCache(tscp);
2248 lock_ReleaseMutex(&tscp->mx);
2250 cm_ReleaseSCache(tscp);
2253 #ifdef AFS_FREELANCE_CLIENT
2254 // yj: if this is a mountpoint under root.afs then we don't want it
2255 // to be bulkstat-ed, instead, we call getSCache directly and under
2256 // getSCache, it is handled specially.
2257 if ( cm_freelanceEnabled &&
2258 tfid.cell==AFS_FAKE_ROOT_CELL_ID &&
2259 tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
2260 !(tfid.vnode==0x1 && tfid.unique==0x1) )
2262 osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
2263 return cm_GetSCache(&tfid, &tscp, NULL, NULL);
2265 #endif /* AFS_FREELANCE_CLIENT */
2268 bsp->fids[i].Volume = scp->fid.volume;
2269 bsp->fids[i].Vnode = tfid.vnode;
2270 bsp->fids[i].Unique = tfid.unique;
2274 /* called with a locked scp and a pointer to a buffer. Make bulk stat
2275 * calls on all undeleted files in the page of the directory specified.
2278 cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
2282 cm_bulkStat_t bb; /* this is *BIG*, probably 16K or so;
2283 * watch for stack problems */
2284 AFSCBFids fidStruct;
2285 AFSBulkStats statStruct;
2287 AFSCBs callbackStruct;
2290 cm_callbackRequest_t cbReq;
2296 struct rx_connection * callp;
2297 int inlinebulk = 0; /* Did we use InlineBulkStatus RPC or not? */
2299 osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%p", dscp);
2301 /* should be on a buffer boundary */
2302 osi_assertx((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0, "invalid offset");
2304 memset(&bb, 0, sizeof(bb));
2305 bb.bufOffset = *offsetp;
2307 lock_ReleaseMutex(&dscp->mx);
2308 /* first, assemble the file IDs we need to stat */
2309 code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) &bb, offsetp, userp, reqp, NULL);
2311 /* if we failed, bail out early */
2312 if (code && code != CM_ERROR_STOPNOW) {
2313 lock_ObtainMutex(&dscp->mx);
2317 /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
2318 * make the calls to create the entries. Handle AFSCBMAX files at a
2322 while (filex < bb.counter) {
2323 filesThisCall = bb.counter - filex;
2324 if (filesThisCall > AFSCBMAX)
2325 filesThisCall = AFSCBMAX;
2327 fidStruct.AFSCBFids_len = filesThisCall;
2328 fidStruct.AFSCBFids_val = &bb.fids[filex];
2329 statStruct.AFSBulkStats_len = filesThisCall;
2330 statStruct.AFSBulkStats_val = &bb.stats[filex];
2331 callbackStruct.AFSCBs_len = filesThisCall;
2332 callbackStruct.AFSCBs_val = &bb.callbacks[filex];
2333 cm_StartCallbackGrantingCall(NULL, &cbReq);
2334 osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
2336 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2340 callp = cm_GetRxConn(connp);
2341 if (!(connp->serverp->flags & CM_SERVERFLAG_NOINLINEBULK)) {
2342 code = RXAFS_InlineBulkStatus(callp, &fidStruct,
2343 &statStruct, &callbackStruct, &volSync);
2344 if (code == RXGEN_OPCODE) {
2345 cm_SetServerNoInlineBulk(connp->serverp, 0);
2351 code = RXAFS_BulkStatus(callp, &fidStruct,
2352 &statStruct, &callbackStruct, &volSync);
2354 rx_PutConnection(callp);
2356 } while (cm_Analyze(connp, userp, reqp, &dscp->fid,
2357 &volSync, NULL, &cbReq, code));
2358 code = cm_MapRPCError(code, reqp);
2360 osi_Log2(afsd_logp, "CALL %sBulkStatus FAILURE code 0x%x",
2361 inlinebulk ? "Inline" : "", code);
2363 osi_Log1(afsd_logp, "CALL %sBulkStatus SUCCESS", inlinebulk ? "Inline" : "");
2365 /* may as well quit on an error, since we're not going to do
2366 * much better on the next immediate call, either.
2369 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2373 /* otherwise, we should do the merges */
2374 for (i = 0; i<filesThisCall; i++) {
2376 tfid.cell = dscp->fid.cell;
2377 tfid.volume = bb.fids[j].Volume;
2378 tfid.vnode = bb.fids[j].Vnode;
2379 tfid.unique = bb.fids[j].Unique;
2380 code = cm_GetSCache(&tfid, &scp, userp, reqp);
2384 /* otherwise, if this entry has no callback info,
2387 lock_ObtainMutex(&scp->mx);
2388 /* now, we have to be extra paranoid on merging in this
2389 * information, since we didn't use cm_SyncOp before
2390 * starting the fetch to make sure that no bad races
2391 * were occurring. Specifically, we need to make sure
2392 * we don't obliterate any newer information in the
2393 * vnode than have here.
2395 * Right now, be pretty conservative: if there's a
2396 * callback or a pending call, skip it.
2398 if ((scp->cbServerp == NULL || (scp->flags & CM_SCACHEFLAG_EACCESS))
2400 (CM_SCACHEFLAG_FETCHING
2401 | CM_SCACHEFLAG_STORING
2402 | CM_SCACHEFLAG_SIZESTORING))) {
2403 cm_EndCallbackGrantingCall(scp, &cbReq,
2405 CM_CALLBACK_MAINTAINCOUNT);
2406 cm_MergeStatus(dscp, scp, &bb.stats[j], &volSync, userp, 0);
2408 lock_ReleaseMutex(&scp->mx);
2409 cm_ReleaseSCache(scp);
2410 } /* all files in the response */
2411 /* now tell it to drop the count,
2412 * after doing the vnode processing above */
2413 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2415 filex += filesThisCall;
2416 } /* while there are still more files to process */
2417 lock_ObtainMutex(&dscp->mx);
2419 /* If we did the InlineBulk RPC pull out the return code and log it */
2421 if ((&bb.stats[0])->errorCode) {
2422 osi_Log1(afsd_logp, "cm_TryBulkStat bulk stat error: %d",
2423 (&bb.stats[0])->errorCode);
2427 osi_Log0(afsd_logp, "END cm_TryBulkStat");
2431 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
2435 /* initialize store back mask as inexpensive local variable */
2437 memset(statusp, 0, sizeof(AFSStoreStatus));
2439 /* copy out queued info from scache first, if scp passed in */
2441 if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
2442 statusp->ClientModTime = scp->clientModTime;
2443 mask |= AFS_SETMODTIME;
2444 scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
2449 /* now add in our locally generated request */
2450 if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
2451 statusp->ClientModTime = attrp->clientModTime;
2452 mask |= AFS_SETMODTIME;
2454 if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
2455 statusp->UnixModeBits = attrp->unixModeBits;
2456 mask |= AFS_SETMODE;
2458 if (attrp->mask & CM_ATTRMASK_OWNER) {
2459 statusp->Owner = attrp->owner;
2460 mask |= AFS_SETOWNER;
2462 if (attrp->mask & CM_ATTRMASK_GROUP) {
2463 statusp->Group = attrp->group;
2464 mask |= AFS_SETGROUP;
2467 statusp->Mask = mask;
2470 /* set the file size, and make sure that all relevant buffers have been
2471 * truncated. Ensure that any partially truncated buffers have been zeroed
2472 * to the end of the buffer.
2474 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
2480 /* start by locking out buffer creation */
2481 lock_ObtainWrite(&scp->bufCreateLock);
2483 /* verify that this is a file, not a dir or a symlink */
2484 lock_ObtainMutex(&scp->mx);
2485 code = cm_SyncOp(scp, NULL, userp, reqp, 0,
2486 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2489 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2491 if (scp->fileType != CM_SCACHETYPE_FILE) {
2492 code = CM_ERROR_ISDIR;
2497 if (LargeIntegerLessThan(*sizep, scp->length))
2502 lock_ReleaseMutex(&scp->mx);
2504 /* can't hold scp->mx lock here, since we may wait for a storeback to
2505 * finish if the buffer package is cleaning a buffer by storing it to
2509 buf_Truncate(scp, userp, reqp, sizep);
2511 /* now ensure that file length is short enough, and update truncPos */
2512 lock_ObtainMutex(&scp->mx);
2514 /* make sure we have a callback (so we have the right value for the
2515 * length), and wait for it to be safe to do a truncate.
2517 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
2518 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2519 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2521 /* If we only have 'i' bits, then we should still be able to set
2522 the size of a file we created. */
2523 if (code == CM_ERROR_NOACCESS && scp->creator == userp) {
2524 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
2525 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2526 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2532 if (LargeIntegerLessThan(*sizep, scp->length)) {
2533 /* a real truncation. If truncPos is not set yet, or is bigger
2534 * than where we're truncating the file, set truncPos to this
2539 if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
2540 || LargeIntegerLessThan(*sizep, scp->length)) {
2542 scp->truncPos = *sizep;
2543 scp->mask |= CM_SCACHEMASK_TRUNCPOS;
2545 /* in either case, the new file size has been changed */
2546 scp->length = *sizep;
2547 scp->mask |= CM_SCACHEMASK_LENGTH;
2549 else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
2550 /* really extending the file */
2551 scp->length = *sizep;
2552 scp->mask |= CM_SCACHEMASK_LENGTH;
2555 /* done successfully */
2558 cm_SyncOpDone(scp, NULL,
2559 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2560 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2563 lock_ReleaseMutex(&scp->mx);
2564 lock_ReleaseWrite(&scp->bufCreateLock);
2569 /* set the file size or other attributes (but not both at once) */
2570 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
2574 AFSFetchStatus afsOutStatus;
2578 AFSStoreStatus afsInStatus;
2579 struct rx_connection * callp;
2581 /* handle file length setting */
2582 if (attrp->mask & CM_ATTRMASK_LENGTH)
2583 return cm_SetLength(scp, &attrp->length, userp, reqp);
2585 lock_ObtainMutex(&scp->mx);
2586 /* otherwise, we have to make an RPC to get the status */
2587 code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
2589 lock_ReleaseMutex(&scp->mx);
2593 /* make the attr structure */
2594 cm_StatusFromAttr(&afsInStatus, scp, attrp);
2596 tfid.Volume = scp->fid.volume;
2597 tfid.Vnode = scp->fid.vnode;
2598 tfid.Unique = scp->fid.unique;
2599 lock_ReleaseMutex(&scp->mx);
2601 /* now make the RPC */
2602 osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%p", scp);
2604 code = cm_ConnFromFID(&scp->fid, userp, reqp, &connp);
2608 callp = cm_GetRxConn(connp);
2609 code = RXAFS_StoreStatus(callp, &tfid,
2610 &afsInStatus, &afsOutStatus, &volSync);
2611 rx_PutConnection(callp);
2613 } while (cm_Analyze(connp, userp, reqp,
2614 &scp->fid, &volSync, NULL, NULL, code));
2615 code = cm_MapRPCError(code, reqp);
2618 osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
2620 osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
2622 lock_ObtainMutex(&scp->mx);
2623 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
2625 cm_MergeStatus(NULL, scp, &afsOutStatus, &volSync, userp,
2626 CM_MERGEFLAG_FORCE|CM_MERGEFLAG_STOREDATA);
2628 /* if we're changing the mode bits, discard the ACL cache,
2629 * since we changed the mode bits.
2631 if (afsInStatus.Mask & AFS_SETMODE)
2632 cm_FreeAllACLEnts(scp);
2633 lock_ReleaseMutex(&scp->mx);
2637 long cm_Create(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2638 cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
2643 cm_callbackRequest_t cbReq;
2646 cm_scache_t *scp = NULL;
2648 AFSStoreStatus inStatus;
2649 AFSFetchStatus updatedDirStatus;
2650 AFSFetchStatus newFileStatus;
2651 AFSCallBack newFileCallback;
2653 struct rx_connection * callp;
2656 /* can't create names with @sys in them; must expand it manually first.
2657 * return "invalid request" if they try.
2659 if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2660 return CM_ERROR_ATSYS;
2663 /* before starting the RPC, mark that we're changing the file data, so
2664 * that someone who does a chmod will know to wait until our call
2667 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2668 lock_ObtainMutex(&dscp->mx);
2669 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2670 lock_ReleaseMutex(&dscp->mx);
2672 cm_StartCallbackGrantingCall(NULL, &cbReq);
2674 cm_EndDirOp(&dirop);
2681 cm_StatusFromAttr(&inStatus, NULL, attrp);
2683 /* try the RPC now */
2684 osi_Log1(afsd_logp, "CALL CreateFile scp 0x%p", dscp);
2686 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2690 dirAFSFid.Volume = dscp->fid.volume;
2691 dirAFSFid.Vnode = dscp->fid.vnode;
2692 dirAFSFid.Unique = dscp->fid.unique;
2694 callp = cm_GetRxConn(connp);
2695 code = RXAFS_CreateFile(connp->callp, &dirAFSFid, namep,
2696 &inStatus, &newAFSFid, &newFileStatus,
2697 &updatedDirStatus, &newFileCallback,
2699 rx_PutConnection(callp);
2701 } while (cm_Analyze(connp, userp, reqp,
2702 &dscp->fid, &volSync, NULL, &cbReq, code));
2703 code = cm_MapRPCError(code, reqp);
2706 osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
2708 osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
2711 lock_ObtainWrite(&dirop.scp->dirlock);
2712 dirop.lockType = CM_DIRLOCK_WRITE;
2714 lock_ObtainMutex(&dscp->mx);
2715 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2717 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
2719 lock_ReleaseMutex(&dscp->mx);
2721 /* now try to create the file's entry, too, but be careful to
2722 * make sure that we don't merge in old info. Since we weren't locking
2723 * out any requests during the file's creation, we may have pretty old
2727 newFid.cell = dscp->fid.cell;
2728 newFid.volume = dscp->fid.volume;
2729 newFid.vnode = newAFSFid.Vnode;
2730 newFid.unique = newAFSFid.Unique;
2731 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2733 lock_ObtainMutex(&scp->mx);
2734 scp->creator = userp; /* remember who created it */
2735 if (!cm_HaveCallback(scp)) {
2736 cm_MergeStatus(dscp, scp, &newFileStatus, &volSync,
2738 cm_EndCallbackGrantingCall(scp, &cbReq,
2739 &newFileCallback, 0);
2742 lock_ReleaseMutex(&scp->mx);
2747 /* make sure we end things properly */
2749 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2751 if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
2752 cm_DirCreateEntry(&dirop, namep, &newFid);
2754 cm_BPlusDirCreateEntry(&dirop, namep, &newFid);
2757 cm_EndDirOp(&dirop);
2762 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
2766 lock_ObtainWrite(&scp->bufCreateLock);
2767 code = buf_CleanVnode(scp, userp, reqp);
2768 lock_ReleaseWrite(&scp->bufCreateLock);
2770 lock_ObtainMutex(&scp->mx);
2772 if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
2773 | CM_SCACHEMASK_CLIENTMODTIME
2774 | CM_SCACHEMASK_LENGTH))
2775 code = cm_StoreMini(scp, userp, reqp);
2777 if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) {
2778 code = (scp->flags & CM_SCACHEFLAG_OVERQUOTA) ? CM_ERROR_QUOTA : CM_ERROR_SPACE;
2779 scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE);
2782 lock_ReleaseMutex(&scp->mx);
2787 long cm_MakeDir(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2788 cm_user_t *userp, cm_req_t *reqp)
2793 cm_callbackRequest_t cbReq;
2796 cm_scache_t *scp = NULL;
2798 AFSStoreStatus inStatus;
2799 AFSFetchStatus updatedDirStatus;
2800 AFSFetchStatus newDirStatus;
2801 AFSCallBack newDirCallback;
2803 struct rx_connection * callp;
2806 /* can't create names with @sys in them; must expand it manually first.
2807 * return "invalid request" if they try.
2809 if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2810 return CM_ERROR_ATSYS;
2813 /* before starting the RPC, mark that we're changing the directory
2814 * data, so that someone who does a chmod on the dir will wait until
2815 * our call completes.
2817 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2818 lock_ObtainMutex(&dscp->mx);
2819 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2820 lock_ReleaseMutex(&dscp->mx);
2822 cm_StartCallbackGrantingCall(NULL, &cbReq);
2824 cm_EndDirOp(&dirop);
2831 cm_StatusFromAttr(&inStatus, NULL, attrp);
2833 /* try the RPC now */
2834 osi_Log1(afsd_logp, "CALL MakeDir scp 0x%p", dscp);
2836 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2840 dirAFSFid.Volume = dscp->fid.volume;
2841 dirAFSFid.Vnode = dscp->fid.vnode;
2842 dirAFSFid.Unique = dscp->fid.unique;
2844 callp = cm_GetRxConn(connp);
2845 code = RXAFS_MakeDir(connp->callp, &dirAFSFid, namep,
2846 &inStatus, &newAFSFid, &newDirStatus,
2847 &updatedDirStatus, &newDirCallback,
2849 rx_PutConnection(callp);
2851 } while (cm_Analyze(connp, userp, reqp,
2852 &dscp->fid, &volSync, NULL, &cbReq, code));
2853 code = cm_MapRPCError(code, reqp);
2856 osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
2858 osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
2861 lock_ObtainWrite(&dirop.scp->dirlock);
2862 dirop.lockType = CM_DIRLOCK_WRITE;
2864 lock_ObtainMutex(&dscp->mx);
2865 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2867 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
2869 lock_ReleaseMutex(&dscp->mx);
2871 /* now try to create the new dir's entry, too, but be careful to
2872 * make sure that we don't merge in old info. Since we weren't locking
2873 * out any requests during the file's creation, we may have pretty old
2877 newFid.cell = dscp->fid.cell;
2878 newFid.volume = dscp->fid.volume;
2879 newFid.vnode = newAFSFid.Vnode;
2880 newFid.unique = newAFSFid.Unique;
2881 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2883 lock_ObtainMutex(&scp->mx);
2884 if (!cm_HaveCallback(scp)) {
2885 cm_MergeStatus(dscp, scp, &newDirStatus, &volSync,
2887 cm_EndCallbackGrantingCall(scp, &cbReq,
2888 &newDirCallback, 0);
2891 lock_ReleaseMutex(&scp->mx);
2892 cm_ReleaseSCache(scp);
2896 /* make sure we end things properly */
2898 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2900 if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
2901 cm_DirCreateEntry(&dirop, namep, &newFid);
2903 cm_BPlusDirCreateEntry(&dirop, namep, &newFid);
2906 cm_EndDirOp(&dirop);
2908 /* and return error code */
2912 long cm_Link(cm_scache_t *dscp, char *namep, cm_scache_t *sscp, long flags,
2913 cm_user_t *userp, cm_req_t *reqp)
2918 AFSFid existingAFSFid;
2919 AFSFetchStatus updatedDirStatus;
2920 AFSFetchStatus newLinkStatus;
2922 struct rx_connection * callp;
2925 if (dscp->fid.cell != sscp->fid.cell ||
2926 dscp->fid.volume != sscp->fid.volume) {
2927 return CM_ERROR_CROSSDEVLINK;
2930 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2931 lock_ObtainMutex(&dscp->mx);
2932 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2933 lock_ReleaseMutex(&dscp->mx);
2935 cm_EndDirOp(&dirop);
2940 /* try the RPC now */
2941 osi_Log1(afsd_logp, "CALL Link scp 0x%p", dscp);
2943 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2946 dirAFSFid.Volume = dscp->fid.volume;
2947 dirAFSFid.Vnode = dscp->fid.vnode;
2948 dirAFSFid.Unique = dscp->fid.unique;
2950 existingAFSFid.Volume = sscp->fid.volume;
2951 existingAFSFid.Vnode = sscp->fid.vnode;
2952 existingAFSFid.Unique = sscp->fid.unique;
2954 callp = cm_GetRxConn(connp);
2955 code = RXAFS_Link(callp, &dirAFSFid, namep, &existingAFSFid,
2956 &newLinkStatus, &updatedDirStatus, &volSync);
2957 rx_PutConnection(callp);
2958 osi_Log1(smb_logp," RXAFS_Link returns 0x%x", code);
2960 } while (cm_Analyze(connp, userp, reqp,
2961 &dscp->fid, &volSync, NULL, NULL, code));
2963 code = cm_MapRPCError(code, reqp);
2966 osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
2968 osi_Log0(afsd_logp, "CALL Link SUCCESS");
2971 lock_ObtainWrite(&dirop.scp->dirlock);
2972 dirop.lockType = CM_DIRLOCK_WRITE;
2974 lock_ObtainMutex(&dscp->mx);
2975 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2977 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
2979 lock_ReleaseMutex(&dscp->mx);
2982 if (cm_CheckDirOpForSingleChange(&dirop)) {
2983 cm_DirCreateEntry(&dirop, namep, &sscp->fid);
2985 cm_BPlusDirCreateEntry(&dirop, namep, &sscp->fid);
2989 cm_EndDirOp(&dirop);
2994 long cm_SymLink(cm_scache_t *dscp, char *namep, char *contentsp, long flags,
2995 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
3003 AFSStoreStatus inStatus;
3004 AFSFetchStatus updatedDirStatus;
3005 AFSFetchStatus newLinkStatus;
3007 struct rx_connection * callp;
3010 /* before starting the RPC, mark that we're changing the directory data,
3011 * so that someone who does a chmod on the dir will wait until our
3014 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
3015 lock_ObtainMutex(&dscp->mx);
3016 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3017 lock_ReleaseMutex(&dscp->mx);
3019 cm_EndDirOp(&dirop);
3024 cm_StatusFromAttr(&inStatus, NULL, attrp);
3026 /* try the RPC now */
3027 osi_Log1(afsd_logp, "CALL Symlink scp 0x%p", dscp);
3029 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3033 dirAFSFid.Volume = dscp->fid.volume;
3034 dirAFSFid.Vnode = dscp->fid.vnode;
3035 dirAFSFid.Unique = dscp->fid.unique;
3037 callp = cm_GetRxConn(connp);
3038 code = RXAFS_Symlink(callp, &dirAFSFid, namep, contentsp,
3039 &inStatus, &newAFSFid, &newLinkStatus,
3040 &updatedDirStatus, &volSync);
3041 rx_PutConnection(callp);
3043 } while (cm_Analyze(connp, userp, reqp,
3044 &dscp->fid, &volSync, NULL, NULL, code));
3045 code = cm_MapRPCError(code, reqp);
3048 osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
3050 osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
3053 lock_ObtainWrite(&dirop.scp->dirlock);
3054 dirop.lockType = CM_DIRLOCK_WRITE;
3056 lock_ObtainMutex(&dscp->mx);
3057 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3059 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
3061 lock_ReleaseMutex(&dscp->mx);
3064 if (cm_CheckDirOpForSingleChange(&dirop)) {
3065 newFid.cell = dscp->fid.cell;
3066 newFid.volume = dscp->fid.volume;
3067 newFid.vnode = newAFSFid.Vnode;
3068 newFid.unique = newAFSFid.Unique;
3070 cm_DirCreateEntry(&dirop, namep, &newFid);
3072 cm_BPlusDirCreateEntry(&dirop, namep, &newFid);
3076 cm_EndDirOp(&dirop);
3078 /* now try to create the new dir's entry, too, but be careful to
3079 * make sure that we don't merge in old info. Since we weren't locking
3080 * out any requests during the file's creation, we may have pretty old
3084 newFid.cell = dscp->fid.cell;
3085 newFid.volume = dscp->fid.volume;
3086 newFid.vnode = newAFSFid.Vnode;
3087 newFid.unique = newAFSFid.Unique;
3088 code = cm_GetSCache(&newFid, &scp, userp, reqp);
3090 lock_ObtainMutex(&scp->mx);
3091 if (!cm_HaveCallback(scp)) {
3092 cm_MergeStatus(dscp, scp, &newLinkStatus, &volSync,
3095 lock_ReleaseMutex(&scp->mx);
3096 cm_ReleaseSCache(scp);
3100 /* and return error code */
3104 long cm_RemoveDir(cm_scache_t *dscp, char *namep, cm_user_t *userp,
3111 AFSFetchStatus updatedDirStatus;
3113 struct rx_connection * callp;
3116 /* before starting the RPC, mark that we're changing the directory data,
3117 * so that someone who does a chmod on the dir will wait until our
3120 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
3121 lock_ObtainMutex(&dscp->mx);
3122 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3123 lock_ReleaseMutex(&dscp->mx);
3125 cm_EndDirOp(&dirop);
3130 /* try the RPC now */
3131 osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%p", dscp);
3133 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3137 dirAFSFid.Volume = dscp->fid.volume;
3138 dirAFSFid.Vnode = dscp->fid.vnode;
3139 dirAFSFid.Unique = dscp->fid.unique;
3141 callp = cm_GetRxConn(connp);
3142 code = RXAFS_RemoveDir(callp, &dirAFSFid, namep,
3143 &updatedDirStatus, &volSync);
3144 rx_PutConnection(callp);
3146 } while (cm_Analyze(connp, userp, reqp,
3147 &dscp->fid, &volSync, NULL, NULL, code));
3148 code = cm_MapRPCErrorRmdir(code, reqp);
3151 osi_Log1(afsd_logp, "CALL RemoveDir FAILURE, code 0x%x", code);
3153 osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
3156 lock_ObtainWrite(&dirop.scp->dirlock);
3157 dirop.lockType = CM_DIRLOCK_WRITE;
3159 lock_ObtainMutex(&dscp->mx);
3160 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3162 cm_dnlcRemove(dscp, namep);
3163 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
3165 lock_ReleaseMutex(&dscp->mx);
3168 if (cm_CheckDirOpForSingleChange(&dirop)) {
3169 cm_DirDeleteEntry(&dirop, namep);
3171 cm_BPlusDirDeleteEntry(&dirop, namep);
3175 cm_EndDirOp(&dirop);
3177 /* and return error code */
3181 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
3183 /* grab mutex on contents */
3184 lock_ObtainMutex(&scp->mx);
3186 /* reset the prefetch info */
3187 scp->prefetch.base.LowPart = 0; /* base */
3188 scp->prefetch.base.HighPart = 0;
3189 scp->prefetch.end.LowPart = 0; /* and end */
3190 scp->prefetch.end.HighPart = 0;
3192 /* release mutex on contents */
3193 lock_ReleaseMutex(&scp->mx);
3199 long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
3200 char *newNamep, cm_user_t *userp, cm_req_t *reqp)
3204 AFSFid oldDirAFSFid;
3205 AFSFid newDirAFSFid;
3207 AFSFetchStatus updatedOldDirStatus;
3208 AFSFetchStatus updatedNewDirStatus;
3211 struct rx_connection * callp;
3212 cm_dirOp_t oldDirOp;
3215 cm_dirOp_t newDirOp;
3217 /* before starting the RPC, mark that we're changing the directory data,
3218 * so that someone who does a chmod on the dir will wait until our call
3219 * completes. We do this in vnode order so that we don't deadlock,
3220 * which makes the code a little verbose.
3222 if (oldDscp == newDscp) {
3223 /* check for identical names */
3224 if (strcmp(oldNamep, newNamep) == 0)
3225 return CM_ERROR_RENAME_IDENTICAL;
3228 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3229 lock_ObtainMutex(&oldDscp->mx);
3230 cm_dnlcRemove(oldDscp, oldNamep);
3231 cm_dnlcRemove(oldDscp, newNamep);
3232 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3233 CM_SCACHESYNC_STOREDATA);
3234 lock_ReleaseMutex(&oldDscp->mx);
3236 cm_EndDirOp(&oldDirOp);
3240 /* two distinct dir vnodes */
3242 if (oldDscp->fid.cell != newDscp->fid.cell ||
3243 oldDscp->fid.volume != newDscp->fid.volume)
3244 return CM_ERROR_CROSSDEVLINK;
3246 /* shouldn't happen that we have distinct vnodes for two
3247 * different files, but could due to deliberate attack, or
3248 * stale info. Avoid deadlocks and quit now.
3250 if (oldDscp->fid.vnode == newDscp->fid.vnode)
3251 return CM_ERROR_CROSSDEVLINK;
3253 if (oldDscp->fid.vnode < newDscp->fid.vnode) {
3254 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3255 lock_ObtainMutex(&oldDscp->mx);
3256 cm_dnlcRemove(oldDscp, oldNamep);
3257 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3258 CM_SCACHESYNC_STOREDATA);
3259 lock_ReleaseMutex(&oldDscp->mx);
3261 cm_EndDirOp(&oldDirOp);
3263 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE, &newDirOp);
3264 lock_ObtainMutex(&newDscp->mx);
3265 cm_dnlcRemove(newDscp, newNamep);
3266 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3267 CM_SCACHESYNC_STOREDATA);
3268 lock_ReleaseMutex(&newDscp->mx);
3270 cm_EndDirOp(&newDirOp);
3272 /* cleanup first one */
3273 lock_ObtainMutex(&oldDscp->mx);
3274 cm_SyncOpDone(oldDscp, NULL,
3275 CM_SCACHESYNC_STOREDATA);
3276 lock_ReleaseMutex(&oldDscp->mx);
3277 cm_EndDirOp(&oldDirOp);
3282 /* lock the new vnode entry first */
3283 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE, &newDirOp);
3284 lock_ObtainMutex(&newDscp->mx);
3285 cm_dnlcRemove(newDscp, newNamep);
3286 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3287 CM_SCACHESYNC_STOREDATA);
3288 lock_ReleaseMutex(&newDscp->mx);
3290 cm_EndDirOp(&newDirOp);
3292 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3293 lock_ObtainMutex(&oldDscp->mx);
3294 cm_dnlcRemove(oldDscp, oldNamep);
3295 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3296 CM_SCACHESYNC_STOREDATA);
3297 lock_ReleaseMutex(&oldDscp->mx);
3299 cm_EndDirOp(&oldDirOp);
3301 /* cleanup first one */
3302 lock_ObtainMutex(&newDscp->mx);
3303 cm_SyncOpDone(newDscp, NULL,
3304 CM_SCACHESYNC_STOREDATA);
3305 lock_ReleaseMutex(&newDscp->mx);
3306 cm_EndDirOp(&newDirOp);
3310 } /* two distinct vnodes */
3317 /* try the RPC now */
3318 osi_Log2(afsd_logp, "CALL Rename old scp 0x%p new scp 0x%p",
3321 code = cm_ConnFromFID(&oldDscp->fid, userp, reqp, &connp);
3325 oldDirAFSFid.Volume = oldDscp->fid.volume;
3326 oldDirAFSFid.Vnode = oldDscp->fid.vnode;
3327 oldDirAFSFid.Unique = oldDscp->fid.unique;
3328 newDirAFSFid.Volume = newDscp->fid.volume;
3329 newDirAFSFid.Vnode = newDscp->fid.vnode;
3330 newDirAFSFid.Unique = newDscp->fid.unique;
3332 callp = cm_GetRxConn(connp);
3333 code = RXAFS_Rename(callp, &oldDirAFSFid, oldNamep,
3334 &newDirAFSFid, newNamep,
3335 &updatedOldDirStatus, &updatedNewDirStatus,
3337 rx_PutConnection(callp);
3339 } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
3340 &volSync, NULL, NULL, code));
3341 code = cm_MapRPCError(code, reqp);
3344 osi_Log1(afsd_logp, "CALL Rename FAILURE, code 0x%x", code);
3346 osi_Log0(afsd_logp, "CALL Rename SUCCESS");
3348 /* update the individual stat cache entries for the directories */
3350 lock_ObtainWrite(&oldDirOp.scp->dirlock);
3351 oldDirOp.lockType = CM_DIRLOCK_WRITE;
3353 lock_ObtainMutex(&oldDscp->mx);
3354 cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
3357 cm_MergeStatus(NULL, oldDscp, &updatedOldDirStatus, &volSync,
3358 userp, CM_MERGEFLAG_DIROP);
3359 lock_ReleaseMutex(&oldDscp->mx);
3362 if (cm_CheckDirOpForSingleChange(&oldDirOp)) {
3365 diropCode = cm_BPlusDirLookup(&oldDirOp, oldNamep, &fileFid);
3366 if (diropCode == CM_ERROR_INEXACT_MATCH)
3368 else if (diropCode == EINVAL)
3370 diropCode = cm_DirLookup(&oldDirOp, oldNamep, &fileFid);
3372 if (diropCode == 0) {
3374 diropCode = cm_DirCreateEntry(&oldDirOp, newNamep, &fileFid);
3376 cm_BPlusDirCreateEntry(&oldDirOp, newNamep, &fileFid);
3380 if (diropCode == 0) {
3381 diropCode = cm_DirDeleteEntry(&oldDirOp, oldNamep);
3383 cm_BPlusDirDeleteEntry(&oldDirOp, oldNamep);
3389 cm_EndDirOp(&oldDirOp);
3391 /* and update it for the new one, too, if necessary */
3394 lock_ObtainWrite(&newDirOp.scp->dirlock);
3395 newDirOp.lockType = CM_DIRLOCK_WRITE;
3397 lock_ObtainMutex(&newDscp->mx);
3398 cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
3400 cm_MergeStatus(NULL, newDscp, &updatedNewDirStatus, &volSync,
3401 userp, CM_MERGEFLAG_DIROP);
3402 lock_ReleaseMutex(&newDscp->mx);
3405 /* we only make the local change if we successfully made
3406 the change in the old directory AND there was only one
3407 change in the new directory */
3408 if (diropCode == 0 && cm_CheckDirOpForSingleChange(&newDirOp)) {
3409 cm_DirCreateEntry(&newDirOp, newNamep, &fileFid);
3411 cm_BPlusDirCreateEntry(&newDirOp, newNamep, &fileFid);
3415 cm_EndDirOp(&newDirOp);
3418 /* and return error code */
3422 /* Byte range locks:
3424 The OpenAFS Windows client has to fake byte range locks given no
3425 server side support for such locks. This is implemented as keyed
3426 byte range locks on the cache manager.
3428 Keyed byte range locks:
3430 Each cm_scache_t structure keeps track of a list of keyed locks.
3431 The key for a lock identifies an owner of a set of locks (referred
3432 to as a client). Each key is represented by a value. The set of
3433 key values used within a specific cm_scache_t structure form a
3434 namespace that has a scope of just that cm_scache_t structure. The
3435 same key value can be used with another cm_scache_t structure and
3436 correspond to a completely different client. However it is
3437 advantageous for the SMB or IFS layer to make sure that there is a
3438 1-1 mapping between client and keys over all cm_scache_t objects.
3440 Assume a client C has key Key(C) (although, since the scope of the
3441 key is a cm_scache_t, the key can be Key(C,S), where S is the
3442 cm_scache_t. But assume a 1-1 relation between keys and clients).
3443 A byte range (O,+L) denotes byte addresses (O) through (O+L-1)
3444 inclusive (a.k.a. [O,O+L-1]). The function Key(x) is implemented
3445 through cm_generateKey() function for both SMB and IFS.
3447 The list of locks for a cm_scache_t object S is maintained in
3448 S->fileLocks. The cache manager will set a lock on the AFS file
3449 server in order to assert the locks in S->fileLocks. If only
3450 shared locks are in place for S, then the cache manager will obtain
3451 a LockRead lock, while if there are any exclusive locks, it will
3452 obtain a LockWrite lock. If the exclusive locks are all released
3453 while the shared locks remain, then the cache manager will
3454 downgrade the lock from LockWrite to LockRead. Similarly, if an
3455 exclusive lock is obtained when only shared locks exist, then the
3456 cache manager will try to upgrade the lock from LockRead to
3459 Each lock L owned by client C maintains a key L->key such that
3460 L->key == Key(C), the effective range defined by L->LOffset and
3461 L->LLength such that the range of bytes affected by the lock is
3462 (L->LOffset, +L->LLength), a type maintained in L->LockType which
3463 is either exclusive or shared.
3467 A lock exists iff it is in S->fileLocks for some cm_scache_t
3468 S. Existing locks are in one of the following states: ACTIVE,
3469 WAITLOCK, WAITUNLOCK, LOST, DELETED.
3471 The following sections describe each lock and the associated
3474 1. ACTIVE: A lock L is ACTIVE iff the cache manager has asserted
3475 the lock with the AFS file server. This type of lock can be
3476 exercised by a client to read or write to the locked region (as
3479 1.1 ACTIVE->LOST: When the AFS file server fails to extend a
3480 server lock that was required to assert the lock. Before
3481 marking the lock as lost, the cache manager checks if the file
3482 has changed on the server. If the file has not changed, then
3483 the cache manager will attempt to obtain a new server lock
3484 that is sufficient to assert the client side locks for the
3485 file. If any of these fail, the lock is marked as LOST.
3486 Otherwise, it is left as ACTIVE.
3488 1.2 ACTIVE->DELETED: Lock is released.
3490 2. WAITLOCK: A lock is in a WAITLOCK state if the cache manager
3491 grants the lock but the lock is yet to be asserted with the AFS
3492 file server. Once the file server grants the lock, the state
3493 will transition to an ACTIVE lock.
3495 2.1 WAITLOCK->ACTIVE: The server granted the lock.
3497 2.2 WAITLOCK->DELETED: Lock is abandoned, or timed out during
3500 2.3 WAITLOCK->LOST: One or more locks from this client were
3501 marked as LOST. No further locks will be granted to this
3502 client until all lost locks are removed.
3504 3. WAITUNLOCK: A lock is in a WAITUNLOCK state if the cache manager
3505 receives a request for a lock that conflicts with an existing
3506 ACTIVE or WAITLOCK lock. The lock will be placed in the queue
3507 and will be granted at such time the conflicting locks are
3508 removed, at which point the state will transition to either
3511 3.1 WAITUNLOCK->ACTIVE: The conflicting lock was removed. The
3512 current serverLock is sufficient to assert this lock, or a
3513 sufficient serverLock is obtained.
3515 3.2 WAITUNLOCK->WAITLOCK: The conflicting lock was removed,
3516 however the required serverLock is yet to be asserted with the
3519 3.3 WAITUNLOCK->DELETED: The lock is abandoned, timed out or
3522 3.5 WAITUNLOCK->LOST: One or more locks from this client were
3523 marked as LOST. No further locks will be granted to this
3524 client until all lost locks are removed.
3526 4. LOST: A lock L is LOST if the server lock that was required to
3527 assert the lock could not be obtained or if it could not be
3528 extended, or if other locks by the same client were LOST.
3529 Essentially, once a lock is LOST, the contract between the cache
3530 manager and that specific client is no longer valid.
3532 The cache manager rechecks the server lock once every minute and
3533 extends it as appropriate. If this is not done for 5 minutes,
3534 the AFS file server will release the lock (the 5 minute timeout
3535 is based on current file server code and is fairly arbitrary).
3536 Once released, the lock cannot be re-obtained without verifying
3537 that the contents of the file hasn't been modified since the
3538 time the lock was released. Re-obtaining the lock without
3539 verifying this may lead to data corruption. If the lock can not
3540 be obtained safely, then all active locks for the cm_scache_t
3543 4.1 LOST->DELETED: The lock is released.
3545 5. DELETED: The lock is no longer relevant. Eventually, it will
3546 get removed from the cm_scache_t. In the meantime, it will be
3547 treated as if it does not exist.
3549 5.1 DELETED->not exist: The lock is removed from the
3552 The following are classifications of locks based on their state.
3554 6* A lock L is ACCEPTED if it is ACTIVE or WAITLOCK. These locks
3555 have been accepted by the cache manager, but may or may not have
3556 been granted back to the client.
3558 7* A lock L is QUEUED if it is ACTIVE, WAITLOCK or WAITUNLOCK.
3560 8* A lock L is WAITING if it is WAITLOCK or WAITUNLOCK.
3564 A client C can READ range (Offset,+Length) of a file represented by
3565 cm_scache_t S iff (1):
3567 1. for all _a_ in (Offset,+Length), all of the following is true:
3569 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3570 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3573 1.2 For each LOST lock L in S->fileLocks such that _a_ in
3574 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3577 (When locks are lost on an cm_scache_t, all locks are lost. By
3578 4.2 (below), if there is an exclusive LOST lock, then there
3579 can't be any overlapping ACTIVE locks.)
3581 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
3583 2. for all _a_ in (Offset,+Length), one of the following is true:
3585 2.1 Byte _a_ of S is unowned (as specified in 1.1) AND there
3586 does not exist a LOST lock L such that _a_ in
3587 (L->LOffset,+L->LLength).
3589 2.2 Byte _a_ of S is owned by C under lock L (as specified in
3590 1.2) AND L->LockType is exclusive.
3592 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
3594 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
3597 3.1 If L->LockType is exclusive then there does NOT exist a
3598 ACCEPTED lock M in S->fileLocks such that _a_ in
3599 (M->LOffset,+M->LLength).
3601 (If we count all QUEUED locks then we hit cases such as
3602 cascading waiting locks where the locks later on in the queue
3603 can be granted without compromising file integrity. On the
3604 other hand if only ACCEPTED locks are considered, then locks
3605 that were received earlier may end up waiting for locks that
3606 were received later to be unlocked. The choice of ACCEPTED
3607 locks was made to mimic the Windows byte range lock
3610 3.2 If L->LockType is shared then for each ACCEPTED lock M in
3611 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
3612 M->LockType is shared.
3614 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
3616 4.1 M->key != Key(C)
3618 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
3619 and (M->LOffset,+M->LLength) do not intersect.
3621 (Note: If a client loses a lock, it loses all locks.
3622 Subsequently, it will not be allowed to obtain any more locks
3623 until all existing LOST locks that belong to the client are
3624 released. Once all locks are released by a single client,
3625 there exists no further contract between the client and AFS
3626 about the contents of the file, hence the client can then
3627 proceed to obtain new locks and establish a new contract.
3629 This doesn't quite work as you think it should, because most
3630 applications aren't built to deal with losing locks they
3631 thought they once had. For now, we don't have a good
3632 solution to lost locks.
3634 Also, for consistency reasons, we have to hold off on
3635 granting locks that overlap exclusive LOST locks.)
3637 A client C can only unlock locks L in S->fileLocks which have
3640 The representation and invariants are as follows:
3642 - Each cm_scache_t structure keeps:
3644 - A queue of byte-range locks (cm_scache_t::fileLocks) which
3645 are of type cm_file_lock_t.
3647 - A record of the highest server-side lock that has been
3648 obtained for this object (cm_scache_t::serverLock), which is
3649 one of (-1), LockRead, LockWrite.
3651 - A count of ACCEPTED exclusive and shared locks that are in the
3652 queue (cm_scache_t::sharedLocks and
3653 cm_scache_t::exclusiveLocks)
3655 - Each cm_file_lock_t structure keeps:
3657 - The type of lock (cm_file_lock_t::LockType)
3659 - The key associated with the lock (cm_file_lock_t::key)
3661 - The offset and length of the lock (cm_file_lock_t::LOffset
3662 and cm_file_lock_t::LLength)
3664 - The state of the lock.
3666 - Time of issuance or last successful extension
3668 Semantic invariants:
3670 I1. The number of ACCEPTED locks in S->fileLocks are
3671 (S->sharedLocks + S->exclusiveLocks)
3673 External invariants:
3675 I3. S->serverLock is the lock that we have asserted with the
3676 AFS file server for this cm_scache_t.
3678 I4. S->serverLock == LockRead iff there is at least one ACTIVE
3679 shared lock, but no ACTIVE exclusive locks.
3681 I5. S->serverLock == LockWrite iff there is at least one ACTIVE
3684 I6. If L is a LOST lock, then for each lock M in S->fileLocks,
3685 M->key == L->key IMPLIES M is LOST or DELETED.
3690 #define IS_LOCK_ACTIVE(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == 0)
3692 #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)
3694 #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)
3696 #define IS_LOCK_LOST(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_LOST)
3698 #define IS_LOCK_DELETED(lockp) (((lockp)->flags & CM_FILELOCK_FLAG_DELETED) == CM_FILELOCK_FLAG_DELETED)
3701 #define IS_LOCK_ACCEPTED(lockp) (IS_LOCK_ACTIVE(lockp) || IS_LOCK_WAITLOCK(lockp))
3704 #define IS_LOCK_CLIENTONLY(lockp) ((((lockp)->scp->flags & CM_SCACHEFLAG_RO) == CM_SCACHEFLAG_RO) || (((lockp)->flags & CM_FILELOCK_FLAG_CLIENTONLY) == CM_FILELOCK_FLAG_CLIENTONLY))
3707 #define INTERSECT_RANGE(r1,r2) (((r2).offset+(r2).length) > (r1).offset && ((r1).offset +(r1).length) > (r2).offset)
3710 #define CONTAINS_RANGE(r1,r2) (((r2).offset+(r2).length) <= ((r1).offset+(r1).length) && (r1).offset <= (r2).offset)
3712 #if defined(VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS) && !defined(LOCK_TESTING)
3713 #define SCP_SUPPORTS_BRLOCKS(scp) ((scp)->cbServerp && ((scp)->cbServerp->capabilities & VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS))
3715 #define SCP_SUPPORTS_BRLOCKS(scp) (1)
3718 #define SERVERLOCKS_ENABLED(scp) (!((scp)->flags & CM_SCACHEFLAG_RO) && cm_enableServerLocks && SCP_SUPPORTS_BRLOCKS(scp))
3720 #if defined(VICED_CAPABILITY_WRITELOCKACL)
3721 #define SCP_SUPPORTS_WRITELOCKACL(scp) ((scp)->cbServerp && ((scp->cbServerp->capabilities & VICED_CAPABILITY_WRITELOCKACL)))
3723 #define SCP_SUPPORTS_WRITELOCKACL(scp) (0)
3725 /* This should really be defined in any build that this code is being
3727 #error VICED_CAPABILITY_WRITELOCKACL not defined.
3730 static void cm_LockRangeSubtract(cm_range_t * pos, const cm_range_t * neg)
3732 afs_int64 int_begin;
3735 int_begin = MAX(pos->offset, neg->offset);
3736 int_end = MIN(pos->offset+pos->length, neg->offset+neg->length);
3738 if (int_begin < int_end) {
3739 if (int_begin == pos->offset) {
3740 pos->length = pos->offset + pos->length - int_end;
3741 pos->offset = int_end;
3742 } else if (int_end == pos->offset + pos->length) {
3743 pos->length = int_begin - pos->offset;
3746 /* We only subtract ranges if the resulting range is
3747 contiguous. If we try to support non-contigous ranges, we
3748 aren't actually improving performance. */
3752 /* Called with scp->mx held. Returns 0 if all is clear to read the
3753 specified range by the client identified by key.
3755 long cm_LockCheckRead(cm_scache_t *scp,
3756 LARGE_INTEGER LOffset,
3757 LARGE_INTEGER LLength,
3760 #ifndef ADVISORY_LOCKS
3762 cm_file_lock_t *fileLock;
3766 int substract_ranges = FALSE;
3768 range.offset = LOffset.QuadPart;
3769 range.length = LLength.QuadPart;
3773 1. for all _a_ in (Offset,+Length), all of the following is true:
3775 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3776 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3779 1.2 For each LOST lock L in S->fileLocks such that _a_ in
3780 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3785 lock_ObtainRead(&cm_scacheLock);
3787 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
3789 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
3791 if (INTERSECT_RANGE(range, fileLock->range)) {
3792 if (IS_LOCK_ACTIVE(fileLock)) {
3793 if (fileLock->key == key) {
3795 /* If there is an active lock for this client, it
3796 is safe to substract ranges.*/
3797 cm_LockRangeSubtract(&range, &fileLock->range);
3798 substract_ranges = TRUE;
3800 if (fileLock->lockType != LockRead) {
3801 code = CM_ERROR_LOCK_CONFLICT;
3805 /* even if the entire range is locked for reading,
3806 we still can't grant the lock at this point
3807 because the client may have lost locks. That
3808 is, unless we have already seen an active lock
3809 belonging to the client, in which case there
3810 can't be any lost locks for this client. */
3811 if (substract_ranges)
3812 cm_LockRangeSubtract(&range, &fileLock->range);
3814 } else if (IS_LOCK_LOST(fileLock) &&
3815 (fileLock->key == key || fileLock->lockType == LockWrite)) {
3816 code = CM_ERROR_BADFD;
3822 lock_ReleaseRead(&cm_scacheLock);
3824 osi_Log4(afsd_logp, "cm_LockCheckRead scp 0x%x offset %d length %d code 0x%x",
3825 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
3836 /* Called with scp->mx held. Returns 0 if all is clear to write the
3837 specified range by the client identified by key.
3839 long cm_LockCheckWrite(cm_scache_t *scp,
3840 LARGE_INTEGER LOffset,
3841 LARGE_INTEGER LLength,
3844 #ifndef ADVISORY_LOCKS
3846 cm_file_lock_t *fileLock;
3851 range.offset = LOffset.QuadPart;
3852 range.length = LLength.QuadPart;
3855 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
3857 2. for all _a_ in (Offset,+Length), one of the following is true:
3859 2.1 Byte _a_ of S is unowned AND there does not exist a LOST
3860 lock L such that _a_ in (L->LOffset,+L->LLength).
3862 2.2 Byte _a_ of S is owned by C under lock L AND L->LockType is
3866 lock_ObtainRead(&cm_scacheLock);
3868 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
3870 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
3872 if (INTERSECT_RANGE(range, fileLock->range)) {
3873 if (IS_LOCK_ACTIVE(fileLock)) {
3874 if (fileLock->key == key) {
3875 if (fileLock->lockType == LockWrite) {
3877 /* if there is an active lock for this client, it
3878 is safe to substract ranges */
3879 cm_LockRangeSubtract(&range, &fileLock->range);
3881 code = CM_ERROR_LOCK_CONFLICT;
3885 code = CM_ERROR_LOCK_CONFLICT;
3888 } else if (IS_LOCK_LOST(fileLock)) {
3889 code = CM_ERROR_BADFD;
3895 lock_ReleaseRead(&cm_scacheLock);
3897 osi_Log4(afsd_logp, "cm_LockCheckWrite scp 0x%x offset %d length %d code 0x%x",
3898 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
3910 static void cm_LockMarkSCacheLost(cm_scache_t * scp);
3912 /* Called with cm_scacheLock write locked */
3913 static cm_file_lock_t * cm_GetFileLock(void) {
3916 l = (cm_file_lock_t *) cm_freeFileLocks;
3918 osi_QRemove(&cm_freeFileLocks, &l->q);
3920 l = malloc(sizeof(cm_file_lock_t));
3921 osi_assertx(l, "null cm_file_lock_t");
3924 memset(l, 0, sizeof(cm_file_lock_t));
3929 /* Called with cm_scacheLock write locked */
3930 static void cm_PutFileLock(cm_file_lock_t *l) {
3931 osi_QAdd(&cm_freeFileLocks, &l->q);
3934 /* called with scp->mx held. May release it during processing, but
3935 leaves it held on exit. */
3936 long cm_IntSetLock(cm_scache_t * scp, cm_user_t * userp, int lockType,
3942 struct rx_connection * callp;
3945 tfid.Volume = scp->fid.volume;
3946 tfid.Vnode = scp->fid.vnode;
3947 tfid.Unique = scp->fid.unique;
3950 osi_Log2(afsd_logp, "CALL SetLock scp 0x%p for lock %d", scp, lockType);
3952 lock_ReleaseMutex(&scp->mx);
3955 code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
3959 callp = cm_GetRxConn(connp);
3960 code = RXAFS_SetLock(callp, &tfid, lockType,
3962 rx_PutConnection(callp);
3964 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
3967 code = cm_MapRPCError(code, reqp);
3969 osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
3971 osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
3974 lock_ObtainMutex(&scp->mx);
3979 /* called with scp->mx held. Releases it during processing */
3980 long cm_IntReleaseLock(cm_scache_t * scp, cm_user_t * userp,
3986 struct rx_connection * callp;
3989 tfid.Volume = scp->fid.volume;
3990 tfid.Vnode = scp->fid.vnode;
3991 tfid.Unique = scp->fid.unique;
3994 lock_ReleaseMutex(&scp->mx);
3996 osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
3999 code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4003 callp = cm_GetRxConn(connp);
4004 code = RXAFS_ReleaseLock(callp, &tfid, &volSync);
4005 rx_PutConnection(callp);
4007 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
4009 code = cm_MapRPCError(code, reqp);
4012 "CALL ReleaseLock FAILURE, code 0x%x", code);
4015 "CALL ReleaseLock SUCCESS");
4017 lock_ObtainMutex(&scp->mx);
4022 /* called with scp->mx held. May release it during processing, but
4023 will exit with lock held.
4027 - 0 if the user has permission to get the specified lock for the scp
4029 - CM_ERROR_NOACCESS if not
4031 Any other error from cm_SyncOp will be sent down untranslated.
4033 If CM_ERROR_NOACCESS is returned and lock_type is LockRead, then
4034 phas_insert (if non-NULL) will receive a boolean value indicating
4035 whether the user has INSERT permission or not.
4037 long cm_LockCheckPerms(cm_scache_t * scp,
4044 long code = 0, code2 = 0;
4046 /* lock permissions are slightly tricky because of the 'i' bit.
4047 If the user has PRSFS_LOCK, she can read-lock the file. If the
4048 user has PRSFS_WRITE, she can write-lock the file. However, if
4049 the user has PRSFS_INSERT, then she can write-lock new files,
4050 but not old ones. Since we don't have information about
4051 whether a file is new or not, we assume that if the user owns
4052 the scp, then she has the permissions that are granted by
4055 osi_Log3(afsd_logp, "cm_LockCheckPerms for scp[0x%p] type[%d] user[0x%p]",
4056 scp, lock_type, userp);
4058 if (lock_type == LockRead)
4059 rights |= PRSFS_LOCK;
4060 else if (lock_type == LockWrite)
4061 rights |= PRSFS_WRITE | PRSFS_LOCK;
4064 osi_assertx(FALSE, "invalid lock type");
4069 *phas_insert = FALSE;
4071 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
4072 CM_SCACHESYNC_GETSTATUS |
4073 CM_SCACHESYNC_NEEDCALLBACK);
4075 if (phas_insert && scp->creator == userp) {
4077 /* If this file was created by the user, then we check for
4078 PRSFS_INSERT. If the file server is recent enough, then
4079 this should be sufficient for her to get a write-lock (but
4080 not necessarily a read-lock). VICED_CAPABILITY_WRITELOCKACL
4081 indicates whether a file server supports getting write
4082 locks when the user only has PRSFS_INSERT.
4084 If the file was not created by the user we skip the check
4085 because the INSERT bit will not apply to this user even
4089 code2 = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
4090 CM_SCACHESYNC_GETSTATUS |
4091 CM_SCACHESYNC_NEEDCALLBACK);
4093 if (code2 == CM_ERROR_NOACCESS) {
4094 osi_Log0(afsd_logp, "cm_LockCheckPerms user has no INSERT bits");
4096 *phas_insert = TRUE;
4097 osi_Log0(afsd_logp, "cm_LockCheckPerms user has INSERT bits");
4101 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
4103 osi_Log1(afsd_logp, "cm_LockCheckPerms returning code %d", code);
4108 /* called with scp->mx held */
4109 long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
4110 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
4112 int allowWait, cm_user_t *userp, cm_req_t *reqp,
4113 cm_file_lock_t **lockpp)
4116 int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
4117 cm_file_lock_t *fileLock;
4120 int wait_unlock = FALSE;
4121 int force_client_lock = FALSE;
4123 osi_Log4(afsd_logp, "cm_Lock scp 0x%x type 0x%x offset %d length %d",
4124 scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
4125 osi_Log3(afsd_logp, "... allowWait %d key 0x%x:%x", allowWait,
4126 (unsigned long)(key >> 32), (unsigned long)(key & 0xffffffff));
4129 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
4131 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
4134 3.1 If L->LockType is exclusive then there does NOT exist a
4135 ACCEPTED lock M in S->fileLocks such that _a_ in
4136 (M->LOffset,+M->LLength).
4138 3.2 If L->LockType is shared then for each ACCEPTED lock M in
4139 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
4140 M->LockType is shared.
4142 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
4144 4.1 M->key != Key(C)
4146 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
4147 and (M->LOffset,+M->LLength) do not intersect.
4150 range.offset = LOffset.QuadPart;
4151 range.length = LLength.QuadPart;
4153 lock_ObtainRead(&cm_scacheLock);
4155 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4157 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4159 if (IS_LOCK_LOST(fileLock)) {
4160 if (fileLock->key == key) {
4161 code = CM_ERROR_BADFD;
4163 } else if (fileLock->lockType == LockWrite && INTERSECT_RANGE(range, fileLock->range)) {
4164 code = CM_ERROR_WOULDBLOCK;
4170 /* we don't need to check for deleted locks here since deleted
4171 locks are dequeued from scp->fileLocks */
4172 if (IS_LOCK_ACCEPTED(fileLock) &&
4173 INTERSECT_RANGE(range, fileLock->range)) {
4175 if ((sLockType & LOCKING_ANDX_SHARED_LOCK) == 0 ||
4176 fileLock->lockType != LockRead) {
4178 code = CM_ERROR_WOULDBLOCK;
4184 lock_ReleaseRead(&cm_scacheLock);
4186 if (code == 0 && SERVERLOCKS_ENABLED(scp)) {
4187 if (Which == scp->serverLock ||
4188 (Which == LockRead && scp->serverLock == LockWrite)) {
4192 /* we already have the lock we need */
4193 osi_Log3(afsd_logp, " we already have the correct lock. exclusives[%d], shared[%d], serverLock[%d]",
4194 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4196 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4198 /* special case: if we don't have permission to read-lock
4199 the file, then we force a clientside lock. This is to
4200 compensate for applications that obtain a read-lock for
4201 reading files off of directories that don't grant
4202 read-locks to the user. */
4203 if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4205 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4206 osi_Log0(afsd_logp, " User has no read-lock perms, but has INSERT perms.");
4209 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4210 force_client_lock = TRUE;
4214 } else if ((scp->exclusiveLocks > 0) ||
4215 (scp->sharedLocks > 0 && scp->serverLock != LockRead)) {
4218 /* We are already waiting for some other lock. We should
4219 wait for the daemon to catch up instead of generating a
4220 flood of SetLock calls. */
4221 osi_Log3(afsd_logp, " already waiting for other lock. exclusives[%d], shared[%d], serverLock[%d]",
4222 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4224 /* see if we have permission to create the lock in the
4226 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4228 code = CM_ERROR_WOULDBLOCK;
4229 else if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4231 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4233 " User has no read-lock perms, but has INSERT perms.");
4234 code = CM_ERROR_WOULDBLOCK;
4237 " User has no read-lock perms. Forcing client-side lock");
4238 force_client_lock = TRUE;
4242 /* leave any other codes as-is */
4246 int check_data_version = FALSE;
4249 /* first check if we have permission to elevate or obtain
4251 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4253 if (code == CM_ERROR_NOACCESS && Which == LockRead &&
4254 (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp))) {
4255 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4256 force_client_lock = TRUE;
4261 /* has_insert => (Which == LockRead, code == CM_ERROR_NOACCESS) */
4263 if (scp->serverLock == LockRead && Which == LockWrite) {
4265 /* We want to escalate the lock to a LockWrite.
4266 * Unfortunately that's not really possible without
4267 * letting go of the current lock. But for now we do
4271 " attempting to UPGRADE from LockRead to LockWrite.");
4273 " dataVersion on scp: %I64d", scp->dataVersion);
4275 /* we assume at this point (because scp->serverLock
4276 was valid) that we had a valid server lock. */
4277 scp->lockDataVersion = scp->dataVersion;
4278 check_data_version = TRUE;
4280 code = cm_IntReleaseLock(scp, userp, reqp);
4283 /* We couldn't release the lock */
4286 scp->serverLock = -1;
4290 /* We need to obtain a server lock of type Which in order
4291 * to assert this file lock */
4292 #ifndef AGGRESSIVE_LOCKS
4295 newLock = LockWrite;
4298 code = cm_IntSetLock(scp, userp, newLock, reqp);
4300 #ifdef AGGRESSIVE_LOCKS
4301 if ((code == CM_ERROR_WOULDBLOCK ||
4302 code == CM_ERROR_NOACCESS) && newLock != Which) {
4303 /* we wanted LockRead. We tried LockWrite. Now try
4308 osi_assertx(newLock == LockRead, "lock type not read");
4310 code = cm_IntSetLock(scp, userp, newLock, reqp);
4314 if (code == CM_ERROR_NOACCESS) {
4315 if (Which == LockRead) {
4316 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4318 /* We requested a read-lock, but we have permission to
4319 * get a write-lock. Try that */
4321 tcode = cm_LockCheckPerms(scp, LockWrite, userp, reqp, NULL);
4324 newLock = LockWrite;
4326 osi_Log0(afsd_logp, " User has 'i' perms and the request was for a LockRead. Trying to get a LockWrite instead");
4328 code = cm_IntSetLock(scp, userp, newLock, reqp);
4331 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4332 force_client_lock = TRUE;
4334 } else if (Which == LockWrite &&
4335 scp->creator == userp && !SCP_SUPPORTS_WRITELOCKACL(scp)) {
4338 /* Special case: if the lock request was for a
4339 * LockWrite and the user owns the file and we weren't
4340 * allowed to obtain the serverlock, we either lost a
4341 * race (the permissions changed from under us), or we
4342 * have 'i' bits, but we aren't allowed to lock the
4345 /* check if we lost a race... */
4346 tcode = cm_LockCheckPerms(scp, Which, userp, reqp, NULL);
4349 osi_Log0(afsd_logp, " User has 'i' perms but can't obtain write locks. Using client-side locks.");
4350 force_client_lock = TRUE;
4355 if (code == 0 && check_data_version &&
4356 scp->dataVersion != scp->lockDataVersion) {
4357 /* We lost a race. Although we successfully obtained
4358 * a lock, someone modified the file in between. The
4359 * locks have all been technically lost. */
4362 " Data version mismatch while upgrading lock.");
4364 " Data versions before=%I64d, after=%I64d",
4365 scp->lockDataVersion,
4368 " Releasing stale lock for scp 0x%x", scp);
4370 code = cm_IntReleaseLock(scp, userp, reqp);
4372 scp->serverLock = -1;
4374 code = CM_ERROR_INVAL;
4375 } else if (code == 0) {
4376 scp->serverLock = newLock;
4377 scp->lockDataVersion = scp->dataVersion;
4381 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4382 scp->serverLock == -1) {
4383 /* Oops. We lost the lock. */
4384 cm_LockMarkSCacheLost(scp);
4387 } else if (code == 0) { /* server locks not enabled */
4389 " Skipping server lock for scp");
4394 if (code != 0 && !force_client_lock) {
4395 /* Special case error translations
4397 Applications don't expect certain errors from a
4398 LockFile/UnlockFile call. We need to translate some error
4399 code to codes that apps expect and handle. */
4401 /* We shouldn't actually need to handle this case since we
4402 simulate locks for RO scps anyway. */
4403 if (code == CM_ERROR_READONLY) {
4404 osi_Log0(afsd_logp, " Reinterpreting CM_ERROR_READONLY as CM_ERROR_NOACCESS");
4405 code = CM_ERROR_NOACCESS;
4409 if (code == 0 || (code == CM_ERROR_WOULDBLOCK && allowWait) ||
4410 force_client_lock) {
4412 /* clear the error if we are forcing a client lock, so we
4413 don't get confused later. */
4414 if (force_client_lock && code != CM_ERROR_WOULDBLOCK)
4417 lock_ObtainWrite(&cm_scacheLock);
4418 fileLock = cm_GetFileLock();
4419 lock_ReleaseWrite(&cm_scacheLock);
4421 fileLock->fid = scp->fid;
4423 fileLock->key = key;
4424 fileLock->lockType = Which;
4426 fileLock->userp = userp;
4427 fileLock->range = range;
4428 fileLock->flags = (code == 0 ? 0 :
4430 CM_FILELOCK_FLAG_WAITUNLOCK :
4431 CM_FILELOCK_FLAG_WAITLOCK));
4433 if (force_client_lock || !SERVERLOCKS_ENABLED(scp))
4434 fileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
4436 fileLock->lastUpdate = (code == 0 && !force_client_lock) ? time(NULL) : 0;
4438 lock_ObtainWrite(&cm_scacheLock);
4439 osi_QAddT(&scp->fileLocksH, &scp->fileLocksT, &fileLock->fileq);
4440 cm_HoldSCacheNoLock(scp);
4441 fileLock->scp = scp;
4442 osi_QAdd(&cm_allFileLocks, &fileLock->q);
4443 lock_ReleaseWrite(&cm_scacheLock);
4449 if (IS_LOCK_CLIENTONLY(fileLock)) {
4451 } else if (IS_LOCK_ACCEPTED(fileLock)) {
4452 if (Which == LockRead)
4455 scp->exclusiveLocks++;
4459 "cm_Lock Lock added 0x%p flags 0x%x to scp [0x%p]",
4460 fileLock, fileLock->flags, scp);
4462 " exclusives[%d] shared[%d] client[%d] serverLock[%d]",
4463 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4464 (int)(signed char) scp->serverLock);
4467 "cm_Lock Rejecting lock (code = 0x%x)", code);
4473 static int cm_KeyEquals(cm_key_t k1, cm_key_t k2, int flags);
4475 /* Called with scp->mx held */
4476 long cm_UnlockByKey(cm_scache_t * scp,
4483 cm_file_lock_t *fileLock;
4484 osi_queue_t *q, *qn;
4487 osi_Log4(afsd_logp, "cm_UnlockByKey scp 0x%p key 0x%x:%x flags=0x%x",
4489 (unsigned long)(key >> 32),
4490 (unsigned long)(key & 0xffffffff),
4493 lock_ObtainWrite(&cm_scacheLock);
4495 for (q = scp->fileLocksH; q; q = qn) {
4498 fileLock = (cm_file_lock_t *)
4499 ((char *) q - offsetof(cm_file_lock_t, fileq));
4502 osi_Log4(afsd_logp, " Checking lock[0x%x] range[%d,+%d] type[%d]",
4504 (unsigned long) fileLock->range.offset,
4505 (unsigned long) fileLock->range.length,
4506 fileLock->lockType);
4507 osi_Log3(afsd_logp, " key[0x%x:%x] flags[0x%x]",
4508 (unsigned long)(fileLock->key >> 32),
4509 (unsigned long)(fileLock->key & 0xffffffff),
4512 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
4513 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
4514 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4516 fileLock->fid.volume,
4517 fileLock->fid.vnode,
4518 fileLock->fid.unique);
4519 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4520 fileLock->scp->fid.cell,
4521 fileLock->scp->fid.volume,
4522 fileLock->scp->fid.vnode,
4523 fileLock->scp->fid.unique);
4524 osi_assertx(FALSE, "invalid fid value");
4528 if (!IS_LOCK_DELETED(fileLock) &&
4529 cm_KeyEquals(fileLock->key, key, flags)) {
4530 osi_Log3(afsd_logp, "...Unlock range [%d,+%d] type %d",
4531 fileLock->range.offset,
4532 fileLock->range.length,
4533 fileLock->lockType);
4535 if (scp->fileLocksT == q)
4536 scp->fileLocksT = osi_QPrev(q);
4537 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
4539 if (IS_LOCK_CLIENTONLY(fileLock)) {
4541 } else if (IS_LOCK_ACCEPTED(fileLock)) {
4542 if (fileLock->lockType == LockRead)
4545 scp->exclusiveLocks--;
4548 fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
4550 cm_ReleaseUser(fileLock->userp);
4551 cm_ReleaseSCacheNoLock(scp);
4553 fileLock->userp = NULL;
4554 fileLock->scp = NULL;
4560 lock_ReleaseWrite(&cm_scacheLock);
4562 if (n_unlocks == 0) {
4563 osi_Log0(afsd_logp, "cm_UnlockByKey no locks found");
4564 osi_Log3(afsd_logp, " Leaving scp with exclusives[%d], shared[%d], serverLock[%d]",
4565 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4570 osi_Log1(afsd_logp, "cm_UnlockByKey done with %d locks", n_unlocks);
4572 osi_assertx(scp->sharedLocks >= 0, "scp->sharedLocks < 0");
4573 osi_assertx(scp->exclusiveLocks >= 0, "scp->exclusiveLocks < 0");
4574 osi_assertx(scp->clientLocks >= 0, "scp->clientLocks < 0");
4576 if (!SERVERLOCKS_ENABLED(scp)) {
4577 osi_Log0(afsd_logp, " Skipping server lock for scp");
4581 /* Ideally we would go through the rest of the locks to determine
4582 * if one or more locks that were formerly in WAITUNLOCK can now
4583 * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
4584 * scp->sharedLocks accordingly. However, the retrying of locks
4585 * in that manner is done cm_RetryLock() manually.
4588 if (scp->serverLock == LockWrite &&
4589 scp->exclusiveLocks == 0 &&
4590 scp->sharedLocks > 0) {
4592 /* The serverLock should be downgraded to LockRead */
4593 osi_Log0(afsd_logp, " DOWNGRADE lock from LockWrite to LockRead");
4595 /* since scp->serverLock looked sane, we are going to assume
4596 that we have a valid server lock. */
4597 scp->lockDataVersion = scp->dataVersion;
4598 osi_Log1(afsd_logp, " dataVersion on scp = %I64d", scp->dataVersion);
4600 code = cm_IntReleaseLock(scp, userp, reqp);
4603 /* so we couldn't release it. Just let the lock be for now */
4607 scp->serverLock = -1;
4610 code = cm_IntSetLock(scp, userp, LockRead, reqp);
4612 if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
4613 scp->serverLock = LockRead;
4614 } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
4615 /* We lost a race condition. Although we have a valid
4616 lock on the file, the data has changed and essentially
4617 we have lost the lock we had during the transition. */
4619 osi_Log0(afsd_logp, "Data version mismatch during lock downgrade");
4620 osi_Log2(afsd_logp, " Data versions before=%I64d, after=%I64d",
4621 scp->lockDataVersion,
4624 code = cm_IntReleaseLock(scp, userp, reqp);
4626 code = CM_ERROR_INVAL;
4627 scp->serverLock = -1;
4631 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4632 (scp->serverLock == -1)) {
4634 cm_LockMarkSCacheLost(scp);
4637 /* failure here has no bearing on the return value of
4641 } else if (scp->serverLock != (-1) &&
4642 scp->exclusiveLocks == 0 &&
4643 scp->sharedLocks == 0) {
4644 /* The serverLock should be released entirely */
4646 code = cm_IntReleaseLock(scp, userp, reqp);
4649 scp->serverLock = (-1);
4654 osi_Log1(afsd_logp, "cm_UnlockByKey code 0x%x", code);
4655 osi_Log4(afsd_logp, " Leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
4656 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4657 (int)(signed char) scp->serverLock);
4662 long cm_Unlock(cm_scache_t *scp,
4663 unsigned char sLockType,
4664 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
4670 int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
4671 cm_file_lock_t *fileLock;
4673 int release_userp = FALSE;
4675 osi_Log4(afsd_logp, "cm_Unlock scp 0x%p type 0x%x offset %d length %d",
4676 scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
4677 osi_Log2(afsd_logp, "... key 0x%x:%x",
4678 (unsigned long) (key >> 32), (unsigned long) (key & 0xffffffff));
4680 lock_ObtainRead(&cm_scacheLock);
4682 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4683 fileLock = (cm_file_lock_t *)
4684 ((char *) q - offsetof(cm_file_lock_t, fileq));
4687 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
4688 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
4689 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4691 fileLock->fid.volume,
4692 fileLock->fid.vnode,
4693 fileLock->fid.unique);
4694 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4695 fileLock->scp->fid.cell,
4696 fileLock->scp->fid.volume,
4697 fileLock->scp->fid.vnode,
4698 fileLock->scp->fid.unique);
4699 osi_assertx(FALSE, "invalid fid value");
4702 if (!IS_LOCK_DELETED(fileLock) &&
4703 fileLock->key == key &&
4704 fileLock->range.offset == LOffset.QuadPart &&
4705 fileLock->range.length == LLength.QuadPart) {
4711 osi_Log0(afsd_logp, "cm_Unlock lock not found; failure");
4713 lock_ReleaseRead(&cm_scacheLock);
4715 /* The lock didn't exist anyway. *shrug* */
4719 lock_ReleaseRead(&cm_scacheLock);
4721 /* discard lock record */
4722 lock_ObtainWrite(&cm_scacheLock);
4723 if (scp->fileLocksT == q)
4724 scp->fileLocksT = osi_QPrev(q);
4725 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
4728 * Don't delete it here; let the daemon delete it, to simplify
4729 * the daemon's traversal of the list.
4732 if (IS_LOCK_CLIENTONLY(fileLock)) {
4734 } else if (IS_LOCK_ACCEPTED(fileLock)) {
4735 if (fileLock->lockType == LockRead)
4738 scp->exclusiveLocks--;
4741 fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
4742 if (userp != NULL) {
4743 cm_ReleaseUser(fileLock->userp);
4745 userp = fileLock->userp;
4746 release_userp = TRUE;
4748 fileLock->userp = NULL;
4749 cm_ReleaseSCacheNoLock(scp);
4750 fileLock->scp = NULL;
4751 lock_ReleaseWrite(&cm_scacheLock);
4753 if (!SERVERLOCKS_ENABLED(scp)) {
4754 osi_Log0(afsd_logp, " Skipping server locks for scp");
4758 /* Ideally we would go through the rest of the locks to determine
4759 * if one or more locks that were formerly in WAITUNLOCK can now
4760 * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
4761 * scp->sharedLocks accordingly. However, the retrying of locks
4762 * in that manner is done cm_RetryLock() manually.
4765 if (scp->serverLock == LockWrite &&
4766 scp->exclusiveLocks == 0 &&
4767 scp->sharedLocks > 0) {
4769 /* The serverLock should be downgraded to LockRead */
4770 osi_Log0(afsd_logp, " DOWNGRADE lock from LockWrite to LockRead");
4772 /* Since we already had a lock, we assume that there is a
4773 valid server lock. */
4774 scp->lockDataVersion = scp->dataVersion;
4775 osi_Log1(afsd_logp, " dataVersion on scp is %I64d", scp->dataVersion);
4777 /* before we downgrade, make sure that we have enough
4778 permissions to get the read lock. */
4779 code = cm_LockCheckPerms(scp, LockRead, userp, reqp, NULL);
4782 osi_Log0(afsd_logp, " SKIPPING downgrade because user doesn't have perms to get downgraded lock");
4788 code = cm_IntReleaseLock(scp, userp, reqp);
4791 /* so we couldn't release it. Just let the lock be for now */
4795 scp->serverLock = -1;
4798 code = cm_IntSetLock(scp, userp, LockRead, reqp);
4800 if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
4801 scp->serverLock = LockRead;
4802 } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
4803 /* Lost a race. We obtained a new lock, but that is
4804 meaningless since someone modified the file
4808 "Data version mismatch while downgrading lock");
4810 " Data versions before=%I64d, after=%I64d",
4811 scp->lockDataVersion,
4814 code = cm_IntReleaseLock(scp, userp, reqp);
4816 scp->serverLock = -1;
4817 code = CM_ERROR_INVAL;
4821 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4822 (scp->serverLock == -1)) {
4824 cm_LockMarkSCacheLost(scp);
4827 /* failure here has no bearing on the return value of
4831 } else if (scp->serverLock != (-1) &&
4832 scp->exclusiveLocks == 0 &&
4833 scp->sharedLocks == 0) {
4834 /* The serverLock should be released entirely */
4836 code = cm_IntReleaseLock(scp, userp, reqp);
4839 scp->serverLock = (-1);
4844 cm_ReleaseUser(userp);
4848 osi_Log1(afsd_logp, "cm_Unlock code 0x%x", code);
4849 osi_Log4(afsd_logp, " leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
4850 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4851 (int)(signed char) scp->serverLock);
4856 /* called with scp->mx held */
4857 static void cm_LockMarkSCacheLost(cm_scache_t * scp)
4859 cm_file_lock_t *fileLock;
4862 osi_Log1(afsd_logp, "cm_LockMarkSCacheLost scp 0x%x", scp);
4865 /* With the current code, we can't lose a lock on a RO scp */
4866 osi_assertx(!(scp->flags & CM_SCACHEFLAG_RO), "CM_SCACHEFLAG_RO unexpected");
4869 /* cm_scacheLock needed because we are modifying fileLock->flags */
4870 lock_ObtainWrite(&cm_scacheLock);
4872 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4874 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4876 if (IS_LOCK_ACTIVE(fileLock) &&
4877 !IS_LOCK_CLIENTONLY(fileLock)) {
4878 if (fileLock->lockType == LockRead)
4881 scp->exclusiveLocks--;
4883 fileLock->flags |= CM_FILELOCK_FLAG_LOST;
4887 scp->serverLock = -1;
4888 scp->lockDataVersion = -1;
4889 lock_ReleaseWrite(&cm_scacheLock);
4892 /* Called with no relevant locks held */
4893 void cm_CheckLocks()
4895 osi_queue_t *q, *nq;
4896 cm_file_lock_t *fileLock;
4902 struct rx_connection * callp;
4907 lock_ObtainWrite(&cm_scacheLock);
4909 cm_lockRefreshCycle++;
4911 osi_Log1(afsd_logp, "cm_CheckLocks starting lock check cycle %d", cm_lockRefreshCycle);
4913 for (q = cm_allFileLocks; q; q = nq) {
4914 fileLock = (cm_file_lock_t *) q;
4918 if (IS_LOCK_DELETED(fileLock)) {
4920 osi_QRemove(&cm_allFileLocks, q);
4921 cm_PutFileLock(fileLock);
4923 } else if (IS_LOCK_ACTIVE(fileLock) && !IS_LOCK_CLIENTONLY(fileLock)) {
4925 /* Server locks must have been enabled for us to have
4926 received an active non-client-only lock. */
4927 osi_assertx(cm_enableServerLocks, "!cm_enableServerLocks");
4929 scp = fileLock->scp;
4930 osi_assertx(scp != NULL, "null cm_scache_t");
4932 cm_HoldSCacheNoLock(scp);
4935 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
4936 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
4937 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4939 fileLock->fid.volume,
4940 fileLock->fid.vnode,
4941 fileLock->fid.unique);
4942 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4943 fileLock->scp->fid.cell,
4944 fileLock->scp->fid.volume,
4945 fileLock->scp->fid.vnode,
4946 fileLock->scp->fid.unique);
4947 osi_assertx(FALSE, "invalid fid");
4950 /* Server locks are extended once per scp per refresh
4952 if (scp->lastRefreshCycle != cm_lockRefreshCycle) {
4954 int scp_done = FALSE;
4956 osi_Log1(afsd_logp, "cm_CheckLocks Updating scp 0x%x", scp);
4958 lock_ReleaseWrite(&cm_scacheLock);
4959 lock_ObtainMutex(&scp->mx);
4961 /* did the lock change while we weren't holding the lock? */
4962 if (!IS_LOCK_ACTIVE(fileLock))
4963 goto post_syncopdone;
4965 code = cm_SyncOp(scp, NULL, fileLock->userp, &req, 0,
4966 CM_SCACHESYNC_NEEDCALLBACK
4967 | CM_SCACHESYNC_GETSTATUS
4968 | CM_SCACHESYNC_LOCK);
4972 "cm_CheckLocks SyncOp failure code 0x%x", code);
4973 goto post_syncopdone;
4976 /* cm_SyncOp releases scp->mx during which the lock
4977 may get released. */
4978 if (!IS_LOCK_ACTIVE(fileLock))
4979 goto pre_syncopdone;
4981 if (scp->serverLock != -1) {
4985 tfid.Volume = scp->fid.volume;
4986 tfid.Vnode = scp->fid.vnode;
4987 tfid.Unique = scp->fid.unique;
4989 userp = fileLock->userp;
4991 osi_Log3(afsd_logp, "CALL ExtendLock lock 0x%p for scp=0x%p with lock %d",
4994 (int) scp->serverLock);
4996 lock_ReleaseMutex(&scp->mx);
4999 code = cm_ConnFromFID(&cfid, userp,
5004 callp = cm_GetRxConn(connp);
5005 code = RXAFS_ExtendLock(callp, &tfid,
5007 rx_PutConnection(callp);
5009 osi_Log1(afsd_logp, " ExtendLock returns %d", code);
5011 } while (cm_Analyze(connp, userp, &req,
5012 &cfid, &volSync, NULL, NULL,
5015 code = cm_MapRPCError(code, &req);
5017 lock_ObtainMutex(&scp->mx);
5020 osi_Log1(afsd_logp, "CALL ExtendLock FAILURE, code 0x%x", code);
5022 osi_Log0(afsd_logp, "CALL ExtendLock SUCCESS");
5023 scp->lockDataVersion = scp->dataVersion;
5026 if ((code == EINVAL || code == CM_ERROR_INVAL) &&
5027 scp->lockDataVersion == scp->dataVersion) {
5031 (scp->exclusiveLocks > 0) ? LockWrite: LockRead;
5033 /* we might still have a chance to obtain a
5036 code = cm_IntSetLock(scp, userp, lockType, &req);
5039 code = CM_ERROR_INVAL;
5040 } else if (scp->lockDataVersion != scp->dataVersion) {
5042 /* now check if we still have the file at
5043 the right data version. */
5045 "Data version mismatch on scp 0x%p",
5048 " Data versions: before=%I64d, after=%I64d",
5049 scp->lockDataVersion,
5052 code = cm_IntReleaseLock(scp, userp, &req);
5054 code = CM_ERROR_INVAL;
5058 if (code == EINVAL || code == CM_ERROR_INVAL) {
5059 cm_LockMarkSCacheLost(scp);
5063 /* interestingly, we have found an active lock
5064 belonging to an scache that has no
5066 cm_LockMarkSCacheLost(scp);
5073 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5076 lock_ReleaseMutex(&scp->mx);
5078 lock_ObtainWrite(&cm_scacheLock);
5081 fileLock->lastUpdate = time(NULL);
5085 scp->lastRefreshCycle = cm_lockRefreshCycle;
5088 /* we have already refreshed the locks on this scp */
5089 fileLock->lastUpdate = time(NULL);
5092 cm_ReleaseSCacheNoLock(scp);
5094 } else if (IS_LOCK_ACTIVE(fileLock) && IS_LOCK_CLIENTONLY(fileLock)) {
5095 /* TODO: Check callbacks */
5099 lock_ReleaseWrite(&cm_scacheLock);
5100 osi_Log1(afsd_logp, "cm_CheckLocks completes lock check cycle %d", cm_lockRefreshCycle);
5103 /* NOT called with scp->mx held. */
5104 long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
5107 cm_scache_t *scp = NULL;
5108 cm_file_lock_t *fileLock;
5112 int force_client_lock = FALSE;
5113 int has_insert = FALSE;
5114 int check_data_version = FALSE;
5118 if (client_is_dead) {
5119 code = CM_ERROR_TIMEDOUT;
5123 lock_ObtainRead(&cm_scacheLock);
5125 osi_Log2(afsd_logp, "cm_RetryLock checking lock %p (scp=%p)", oldFileLock, oldFileLock->scp);
5126 osi_Log4(afsd_logp, " offset(%x:%x) length(%x:%x)",
5127 (unsigned)(oldFileLock->range.offset >> 32),
5128 (unsigned)(oldFileLock->range.offset & 0xffffffff),
5129 (unsigned)(oldFileLock->range.length >> 32),
5130 (unsigned)(oldFileLock->range.length & 0xffffffff));
5131 osi_Log3(afsd_logp, " key(%x:%x) flags=%x",
5132 (unsigned)(oldFileLock->key >> 32),
5133 (unsigned)(oldFileLock->key & 0xffffffff),
5134 (unsigned)(oldFileLock->flags));
5136 /* if the lock has already been granted, then we have nothing to do */
5137 if (IS_LOCK_ACTIVE(oldFileLock)) {
5138 lock_ReleaseRead(&cm_scacheLock);
5139 osi_Log0(afsd_logp, "cm_RetryLock lock already granted");
5143 /* we can't do anything with lost or deleted locks at the moment. */
5144 if (IS_LOCK_LOST(oldFileLock) || IS_LOCK_DELETED(oldFileLock)) {
5145 code = CM_ERROR_BADFD;
5146 osi_Log0(afsd_logp, "cm_RetryLock lock is lost or deleted");
5147 lock_ReleaseRead(&cm_scacheLock);
5151 scp = oldFileLock->scp;
5153 osi_assertx(scp != NULL, "null cm_scache_t");
5155 lock_ReleaseRead(&cm_scacheLock);
5156 lock_ObtainMutex(&scp->mx);
5158 code = cm_LockCheckPerms(scp, oldFileLock->lockType,
5162 if (code == CM_ERROR_NOACCESS && oldFileLock->lockType == LockRead) {
5163 if (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp)) {
5164 force_client_lock = TRUE;
5168 lock_ReleaseMutex(&scp->mx);
5172 lock_ObtainWrite(&cm_scacheLock);
5174 /* Check if we already have a sufficient server lock to allow this
5175 lock to go through. */
5176 if (IS_LOCK_WAITLOCK(oldFileLock) &&
5177 (!SERVERLOCKS_ENABLED(scp) ||
5178 scp->serverLock == oldFileLock->lockType ||
5179 scp->serverLock == LockWrite)) {
5181 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5183 if (SERVERLOCKS_ENABLED(scp)) {
5184 osi_Log1(afsd_logp, "cm_RetryLock Server lock (%d) is sufficient for lock. Granting",
5185 (int) scp->serverLock);
5187 osi_Log0(afsd_logp, "cm_RetryLock skipping server lock for scp");
5190 lock_ReleaseWrite(&cm_scacheLock);
5191 lock_ReleaseMutex(&scp->mx);
5196 if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5198 /* check if the conflicting locks have dissappeared already */
5199 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5201 fileLock = (cm_file_lock_t *)
5202 ((char *) q - offsetof(cm_file_lock_t, fileq));
5204 if (IS_LOCK_LOST(fileLock)) {
5205 if (fileLock->key == oldFileLock->key) {
5206 code = CM_ERROR_BADFD;
5207 oldFileLock->flags |= CM_FILELOCK_FLAG_LOST;
5208 osi_Log1(afsd_logp, " found lost lock %p for same key. Marking lock as lost",
5211 } else if (fileLock->lockType == LockWrite &&
5212 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5213 osi_Log1(afsd_logp, " found conflicting LOST lock %p", fileLock);
5214 code = CM_ERROR_WOULDBLOCK;
5219 if (IS_LOCK_ACCEPTED(fileLock) &&
5220 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5222 if (oldFileLock->lockType != LockRead ||
5223 fileLock->lockType != LockRead) {
5225 osi_Log1(afsd_logp, " found conflicting lock %p", fileLock);
5226 code = CM_ERROR_WOULDBLOCK;
5234 lock_ReleaseWrite(&cm_scacheLock);
5235 lock_ReleaseMutex(&scp->mx);
5240 /* when we get here, the lock is either a WAITUNLOCK or WAITLOCK.
5241 If it is WAITUNLOCK, then we didn't find any conflicting lock
5242 but we haven't verfied whether the serverLock is sufficient to
5243 assert it. If it is WAITLOCK, then the serverLock is
5244 insufficient to assert it. Eitherway, we are ready to accept
5245 the lock as either ACTIVE or WAITLOCK depending on the
5248 /* First, promote the WAITUNLOCK to a WAITLOCK */
5249 if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5250 if (oldFileLock->lockType == LockRead)
5253 scp->exclusiveLocks++;
5255 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITUNLOCK;
5256 oldFileLock->flags |= CM_FILELOCK_FLAG_WAITLOCK;
5259 osi_assertx(IS_LOCK_WAITLOCK(oldFileLock), "!IS_LOCK_WAITLOCK");
5261 if (force_client_lock ||
5262 !SERVERLOCKS_ENABLED(scp) ||
5263 scp->serverLock == oldFileLock->lockType ||
5264 (oldFileLock->lockType == LockRead &&
5265 scp->serverLock == LockWrite)) {
5267 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5269 if ((force_client_lock ||
5270 !SERVERLOCKS_ENABLED(scp)) &&
5271 !IS_LOCK_CLIENTONLY(oldFileLock)) {
5273 oldFileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
5275 if (oldFileLock->lockType == LockRead)
5278 scp->exclusiveLocks--;
5283 lock_ReleaseWrite(&cm_scacheLock);
5284 lock_ReleaseMutex(&scp->mx);
5291 code = cm_SyncOp(scp, NULL, oldFileLock->userp, &req, 0,
5292 CM_SCACHESYNC_NEEDCALLBACK
5293 | CM_SCACHESYNC_GETSTATUS
5294 | CM_SCACHESYNC_LOCK);
5296 osi_Log1(smb_logp, "cm_RetryLock SyncOp failure code 0x%x", code);
5297 lock_ReleaseWrite(&cm_scacheLock);
5298 goto post_syncopdone;
5301 if (!IS_LOCK_WAITLOCK(oldFileLock))
5302 goto pre_syncopdone;
5304 userp = oldFileLock->userp;
5306 #ifndef AGGRESSIVE_LOCKS
5307 newLock = oldFileLock->lockType;
5309 newLock = LockWrite;
5313 /* if has_insert is non-zero, then:
5314 - the lock a LockRead
5315 - we don't have permission to get a LockRead
5316 - we do have permission to get a LockWrite
5317 - the server supports VICED_CAPABILITY_WRITELOCKACL
5320 newLock = LockWrite;
5323 lock_ReleaseWrite(&cm_scacheLock);
5325 /* when we get here, either we have a read-lock and want a
5326 write-lock or we don't have any locks and we want some
5329 if (scp->serverLock == LockRead) {
5331 osi_assertx(newLock == LockWrite, "!LockWrite");
5333 osi_Log0(afsd_logp, " Attempting to UPGRADE from LockRead to LockWrite");
5335 scp->lockDataVersion = scp->dataVersion;
5336 check_data_version = TRUE;
5338 code = cm_IntReleaseLock(scp, userp, &req);
5341 goto pre_syncopdone;
5343 scp->serverLock = -1;
5346 code = cm_IntSetLock(scp, userp, newLock, &req);
5349 if (scp->dataVersion != scp->lockDataVersion) {
5350 /* we lost a race. too bad */
5353 " Data version mismatch while upgrading lock.");
5355 " Data versions before=%I64d, after=%I64d",
5356 scp->lockDataVersion,
5359 " Releasing stale lock for scp 0x%x", scp);
5361 code = cm_IntReleaseLock(scp, userp, &req);
5363 scp->serverLock = -1;
5365 code = CM_ERROR_INVAL;
5367 cm_LockMarkSCacheLost(scp);
5369 scp->serverLock = newLock;
5374 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5380 if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
5381 lock_ObtainWrite(&cm_scacheLock);
5382 if (scp->fileLocksT == &oldFileLock->fileq)
5383 scp->fileLocksT = osi_QPrev(&oldFileLock->fileq);
5384 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, &oldFileLock->fileq);
5385 lock_ReleaseWrite(&cm_scacheLock);
5387 lock_ReleaseMutex(&scp->mx);
5390 lock_ObtainWrite(&cm_scacheLock);
5392 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5393 } else if (code != CM_ERROR_WOULDBLOCK) {
5394 oldFileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5395 cm_ReleaseUser(oldFileLock->userp);
5396 oldFileLock->userp = NULL;
5397 if (oldFileLock->scp) {
5398 cm_ReleaseSCacheNoLock(oldFileLock->scp);
5399 oldFileLock->scp = NULL;
5402 lock_ReleaseWrite(&cm_scacheLock);
5407 cm_key_t cm_GenerateKey(unsigned int session_id, unsigned long process_id, unsigned int file_id)
5410 osi_assertx((process_id & 0xffffffff) == process_id, "unexpected process_id");
5411 osi_assertx((session_id & 0xffff) == session_id, "unexpected session_id");
5412 osi_assertx((file_id & 0xffff) == file_id, "unexpected file_id");
5416 (((cm_key_t) (process_id & 0xffffffff)) << 32) |
5417 (((cm_key_t) (session_id & 0xffff)) << 16) |
5418 (((cm_key_t) (file_id & 0xffff)));
5421 static int cm_KeyEquals(cm_key_t k1, cm_key_t k2, int flags)
5423 if (flags & CM_UNLOCK_BY_FID) {
5424 return ((k1 & 0xffffffff) == (k2 & 0xffffffff));
5430 void cm_ReleaseAllLocks(void)
5436 cm_file_lock_t *fileLock;
5439 for (i = 0; i < cm_data.scacheHashTableSize; i++)
5441 for ( scp = cm_data.scacheHashTablep[i]; scp; scp = scp->nextp ) {
5442 while (scp->fileLocksH != NULL) {
5443 lock_ObtainMutex(&scp->mx);
5444 lock_ObtainWrite(&cm_scacheLock);
5445 if (!scp->fileLocksH) {
5446 lock_ReleaseWrite(&cm_scacheLock);
5447 lock_ReleaseMutex(&scp->mx);
5450 fileLock = (cm_file_lock_t *)((char *) scp->fileLocksH - offsetof(cm_file_lock_t, fileq));
5451 userp = fileLock->userp;
5453 key = fileLock->key;
5454 cm_HoldSCacheNoLock(scp);
5455 lock_ReleaseWrite(&cm_scacheLock);
5456 cm_UnlockByKey(scp, key, 0, userp, &req);
5457 cm_ReleaseSCache(scp);
5458 cm_ReleaseUser(userp);
5459 lock_ReleaseMutex(&scp->mx);