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_ObtainWrite(&scp->rw);
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_ReleaseWrite(&scp->rw);
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|AFS_ACCESS_EXECUTE))
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_ObtainWrite(&scp->rw);
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) {
438 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
441 lock_ReleaseWrite(&scp->rw);
443 osi_Log3(afsd_logp,"cm_CheckNTOpen scp 0x%p ldp 0x%p code 0x%x", scp, *ldpp, code);
447 extern long cm_CheckNTOpenDone(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp,
448 cm_lock_data_t ** ldpp)
450 osi_Log2(afsd_logp,"cm_CheckNTOpenDone scp 0x%p ldp 0x%p", scp, *ldpp);
452 lock_ObtainWrite(&scp->rw);
453 cm_Unlock(scp, (*ldpp)->sLockType, (*ldpp)->LOffset, (*ldpp)->LLength,
454 (*ldpp)->key, userp, reqp);
455 lock_ReleaseWrite(&scp->rw);
462 * When CAP_NT_SMBS has been negotiated, deletion (of files or directories) is
463 * done in three steps:
464 * (1) open for deletion (NT_CREATE_AND_X)
465 * (2) set for deletion on close (NT_TRANSACTION2, SET_FILE_INFO)
467 * We must not do the RPC until step 3. But if we are going to return an error
468 * code (e.g. directory not empty), we must return it by step 2, otherwise most
469 * clients will not notice it. So we do a preliminary check. For deleting
470 * files, this is almost free, since we have already done the RPC to get the
471 * parent directory's status bits. But for deleting directories, we must do an
472 * additional RPC to get the directory's data to check if it is empty. Sigh.
474 long cm_CheckNTDelete(cm_scache_t *dscp, cm_scache_t *scp, cm_user_t *userp,
480 cm_dirEntry_t *dep = 0;
481 unsigned short *hashTable;
483 int BeyondPage = 0, HaveDot = 0, HaveDotDot = 0;
486 /* First check permissions */
487 lock_ObtainWrite(&scp->rw);
488 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_DELETE,
489 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
490 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
491 lock_ReleaseWrite(&scp->rw);
495 /* If deleting directory, must be empty */
497 if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
500 thyper.HighPart = 0; thyper.LowPart = 0;
501 code = buf_Get(scp, &thyper, &bufferp);
505 lock_ObtainMutex(&bufferp->mx);
506 lock_ObtainWrite(&scp->rw);
509 code = cm_SyncOp(scp, bufferp, userp, reqp, 0,
510 CM_SCACHESYNC_NEEDCALLBACK
512 | CM_SCACHESYNC_BUFLOCKED);
516 if (cm_HaveBuffer(scp, bufferp, 1))
519 /* otherwise, load the buffer and try again */
520 lock_ReleaseMutex(&bufferp->mx);
521 code = cm_GetBuffer(scp, bufferp, NULL, userp, reqp);
522 lock_ReleaseWrite(&scp->rw);
523 lock_ObtainMutex(&bufferp->mx);
524 lock_ObtainWrite(&scp->rw);
525 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
530 lock_ReleaseWrite(&scp->rw);
533 /* We try to determine emptiness without looking beyond the first page,
534 * and without assuming "." and ".." are present and are on the first
535 * page (though these assumptions might, after all, be reasonable).
537 hashTable = (unsigned short *)(bufferp->datap + (32 * 5));
538 for (i=0; i<128; i++) {
539 idx = ntohs(hashTable[i]);
545 dep = (cm_dirEntry_t *)(bufferp->datap + (32 * idx));
546 if (strcmp(dep->name, ".") == 0)
548 else if (strcmp(dep->name, "..") == 0)
551 code = CM_ERROR_NOTEMPTY;
554 idx = ntohs(dep->next);
557 if (BeyondPage && HaveDot && HaveDotDot)
558 code = CM_ERROR_NOTEMPTY;
562 lock_ReleaseMutex(&bufferp->mx);
563 buf_Release(bufferp);
565 lock_ReleaseWrite(&scp->rw);
570 * Iterate through all entries in a directory.
571 * When the function funcp is called, the buffer is locked but the
572 * directory vnode is not.
574 * If the retscp parameter is not NULL, the parmp must be a
575 * cm_lookupSearch_t object.
577 long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
578 osi_hyper_t *startOffsetp, cm_user_t *userp, cm_req_t *reqp,
579 cm_scache_t **retscp)
583 cm_dirEntry_t *dep = 0;
586 osi_hyper_t dirLength;
587 osi_hyper_t bufferOffset;
588 osi_hyper_t curOffset;
592 cm_pageHeader_t *pageHeaderp;
594 long nextEntryCookie;
595 int numDirChunks; /* # of 32 byte dir chunks in this entry */
597 /* get the directory size */
598 lock_ObtainWrite(&scp->rw);
599 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_LOOKUP,
600 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
601 lock_ReleaseWrite(&scp->rw);
605 if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
606 return CM_ERROR_NOTDIR;
608 if (retscp) /* if this is a lookup call */
610 cm_lookupSearch_t* sp = parmp;
613 #ifdef AFS_FREELANCE_CLIENT
614 /* Freelance entries never end up in the DNLC because they
615 * do not have an associated cm_server_t
617 !(cm_freelanceEnabled &&
618 sp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
619 sp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
620 #else /* !AFS_FREELANCE_CLIENT */
625 int casefold = sp->caseFold;
626 sp->caseFold = 0; /* we have a strong preference for exact matches */
627 if ( *retscp = cm_dnlcLookup(scp, sp)) /* dnlc hit */
629 sp->caseFold = casefold;
632 sp->caseFold = casefold;
634 /* see if we can find it using the directory hash tables.
635 we can only do exact matches, since the hash is case
645 code = cm_BeginDirOp(scp, userp, reqp, CM_DIRLOCK_READ, &dirop);
649 code = cm_BPlusDirLookup(&dirop, sp->searchNamep, &sp->fid);
654 code = cm_DirLookup(&dirop, sp->searchNamep, &sp->fid);
662 sp->ExactFound = TRUE;
663 *retscp = NULL; /* force caller to call cm_GetSCache() */
668 if (sp->caseFold && code == CM_ERROR_INEXACT_MATCH) {
671 sp->ExactFound = FALSE;
672 *retscp = NULL; /* force caller to call cm_GetSCache() */
676 return CM_ERROR_BPLUS_NOMATCH;
684 * XXX We only get the length once. It might change when we drop the
687 dirLength = scp->length;
690 bufferOffset.LowPart = bufferOffset.HighPart = 0;
692 curOffset = *startOffsetp;
694 curOffset.HighPart = 0;
695 curOffset.LowPart = 0;
699 /* make sure that curOffset.LowPart doesn't point to the first
700 * 32 bytes in the 2nd through last dir page, and that it
701 * doesn't point at the first 13 32-byte chunks in the first
702 * dir page, since those are dir and page headers, and don't
703 * contain useful information.
705 temp = curOffset.LowPart & (2048-1);
706 if (curOffset.HighPart == 0 && curOffset.LowPart < 2048) {
707 /* we're in the first page */
708 if (temp < 13*32) temp = 13*32;
711 /* we're in a later dir page */
712 if (temp < 32) temp = 32;
715 /* make sure the low order 5 bits are zero */
718 /* now put temp bits back ito curOffset.LowPart */
719 curOffset.LowPart &= ~(2048-1);
720 curOffset.LowPart |= temp;
722 /* check if we've passed the dir's EOF */
723 if (LargeIntegerGreaterThanOrEqualTo(curOffset, dirLength))
726 /* see if we can use the bufferp we have now; compute in which
727 * page the current offset would be, and check whether that's
728 * the offset of the buffer we have. If not, get the buffer.
730 thyper.HighPart = curOffset.HighPart;
731 thyper.LowPart = curOffset.LowPart & ~(cm_data.buf_blockSize-1);
732 if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
735 lock_ReleaseMutex(&bufferp->mx);
736 buf_Release(bufferp);
740 code = buf_Get(scp, &thyper, &bufferp);
742 /* if buf_Get() fails we do not have a buffer object to lock */
747 lock_ObtainMutex(&bufferp->mx);
748 bufferOffset = thyper;
750 /* now get the data in the cache */
752 lock_ObtainWrite(&scp->rw);
753 code = cm_SyncOp(scp, bufferp, userp, reqp,
755 CM_SCACHESYNC_NEEDCALLBACK
757 | CM_SCACHESYNC_BUFLOCKED);
759 lock_ReleaseWrite(&scp->rw);
762 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
764 if (cm_HaveBuffer(scp, bufferp, 1)) {
765 lock_ReleaseWrite(&scp->rw);
769 /* otherwise, load the buffer and try again */
770 lock_ReleaseMutex(&bufferp->mx);
771 code = cm_GetBuffer(scp, bufferp, NULL, userp,
773 lock_ReleaseWrite(&scp->rw);
774 lock_ObtainMutex(&bufferp->mx);
779 lock_ReleaseMutex(&bufferp->mx);
780 buf_Release(bufferp);
784 } /* if (wrong buffer) ... */
786 /* now we have the buffer containing the entry we're interested
787 * in; copy it out if it represents a non-deleted entry.
789 entryInDir = curOffset.LowPart & (2048-1);
790 entryInBuffer = curOffset.LowPart & (cm_data.buf_blockSize - 1);
792 /* page header will help tell us which entries are free. Page
793 * header can change more often than once per buffer, since
794 * AFS 3 dir page size may be less than (but not more than) a
795 * buffer package buffer.
797 /* only look intra-buffer */
798 temp = curOffset.LowPart & (cm_data.buf_blockSize - 1);
799 temp &= ~(2048 - 1); /* turn off intra-page bits */
800 pageHeaderp = (cm_pageHeader_t *) (bufferp->datap + temp);
802 /* now determine which entry we're looking at in the page. If
803 * it is free (there's a free bitmap at the start of the dir),
804 * we should skip these 32 bytes.
806 slotInPage = (entryInDir & 0x7e0) >> 5;
807 if (!(pageHeaderp->freeBitmap[slotInPage>>3]
808 & (1 << (slotInPage & 0x7)))) {
809 /* this entry is free */
810 numDirChunks = 1; /* only skip this guy */
814 tp = bufferp->datap + entryInBuffer;
815 dep = (cm_dirEntry_t *) tp; /* now points to AFS3 dir entry */
817 /* while we're here, compute the next entry's location, too,
818 * since we'll need it when writing out the cookie into the
819 * dir listing stream.
821 numDirChunks = cm_NameEntries(dep->name, NULL);
823 /* compute the offset of the cookie representing the next entry */
824 nextEntryCookie = curOffset.LowPart
825 + (CM_DIR_CHUNKSIZE * numDirChunks);
827 if (dep->fid.vnode != 0) {
828 /* this is one of the entries to use: it is not deleted */
829 code = (*funcp)(scp, dep, parmp, &curOffset);
832 } /* if we're including this name */
835 /* and adjust curOffset to be where the new cookie is */
837 thyper.LowPart = CM_DIR_CHUNKSIZE * numDirChunks;
838 curOffset = LargeIntegerAdd(thyper, curOffset);
839 } /* while copying data for dir listing */
841 /* release the mutex */
843 lock_ReleaseMutex(&bufferp->mx);
844 buf_Release(bufferp);
849 int cm_NoneUpper(char *s)
853 if (c >= 'A' && c <= 'Z')
858 int cm_NoneLower(char *s)
862 if (c >= 'a' && c <= 'z')
867 long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
870 cm_lookupSearch_t *sp;
875 sp = (cm_lookupSearch_t *) rockp;
877 matchName = dep->name;
879 match = cm_stricmp(matchName, sp->searchNamep);
881 match = strcmp(matchName, sp->searchNamep);
885 && !cm_Is8Dot3(dep->name)) {
886 matchName = shortName;
887 cm_Gen8Dot3Name(dep, shortName, NULL);
889 match = cm_stricmp(matchName, sp->searchNamep);
891 match = strcmp(matchName, sp->searchNamep);
901 if (!sp->caseFold || matchName == shortName) {
902 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
903 return CM_ERROR_STOPNOW;
907 * If we get here, we are doing a case-insensitive search, and we
908 * have found a match. Now we determine what kind of match it is:
909 * exact, lower-case, upper-case, or none of the above. This is done
910 * in order to choose among matches, if there are more than one.
913 /* Exact matches are the best. */
914 match = strcmp(matchName, sp->searchNamep);
917 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
918 return CM_ERROR_STOPNOW;
921 /* Lower-case matches are next. */
924 if (cm_NoneUpper(matchName)) {
929 /* Upper-case matches are next. */
932 if (cm_NoneLower(matchName)) {
937 /* General matches are last. */
943 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
947 /* read the contents of a mount point into the appropriate string.
948 * called with write locked scp, and returns with locked scp.
950 long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
957 if (scp->mountPointStringp[0])
960 /* otherwise, we have to read it in */
961 lock_ReleaseWrite(&scp->rw);
963 thyper.LowPart = thyper.HighPart = 0;
964 code = buf_Get(scp, &thyper, &bufp);
966 lock_ObtainWrite(&scp->rw);
971 code = cm_SyncOp(scp, bufp, userp, reqp, 0,
972 CM_SCACHESYNC_READ | CM_SCACHESYNC_NEEDCALLBACK);
976 cm_SyncOpDone(scp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
978 if (cm_HaveBuffer(scp, bufp, 0))
981 /* otherwise load buffer */
982 code = cm_GetBuffer(scp, bufp, NULL, userp, reqp);
986 /* locked, has callback, has valid data in buffer */
987 if ((tlen = scp->length.LowPart) > MOUNTPOINTLEN - 1)
988 return CM_ERROR_TOOBIG;
990 code = CM_ERROR_INVAL;
994 /* someone else did the work while we were out */
995 if (scp->mountPointStringp[0]) {
1000 /* otherwise, copy out the link */
1001 memcpy(scp->mountPointStringp, bufp->datap, tlen);
1003 /* now make it null-terminated. Note that the original contents of a
1004 * link that is a mount point is "#volname." where "." is there just to
1005 * be turned into a null. That is, we can trash the last char of the
1006 * link without damaging the vol name. This is a stupid convention,
1007 * but that's the protocol.
1009 scp->mountPointStringp[tlen-1] = 0;
1019 /* called with a locked scp and chases the mount point, yielding outScpp.
1020 * scp remains write locked, just for simplicity of describing the interface.
1022 long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
1023 cm_req_t *reqp, cm_scache_t **outScpp)
1031 cm_volume_t *volp = NULL;
1038 if (scp->mountRootFid.cell != 0 && scp->mountRootGen >= cm_data.mountRootGen) {
1039 tfid = scp->mountRootFid;
1040 lock_ReleaseWrite(&scp->rw);
1041 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
1042 lock_ObtainWrite(&scp->rw);
1046 /* parse the volume name */
1047 mpNamep = scp->mountPointStringp;
1049 return CM_ERROR_NOSUCHPATH;
1050 tlen = (int)strlen(scp->mountPointStringp);
1051 mtType = *scp->mountPointStringp;
1052 cellNamep = malloc(tlen);
1053 volNamep = malloc(tlen);
1055 cp = strrchr(mpNamep, ':');
1057 /* cellular mount point */
1058 memset(cellNamep, 0, tlen);
1059 strncpy(cellNamep, mpNamep+1, cp - mpNamep - 1);
1060 strcpy(volNamep, cp+1);
1061 /* now look up the cell */
1062 lock_ReleaseWrite(&scp->rw);
1063 cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE);
1064 lock_ObtainWrite(&scp->rw);
1068 strcpy(volNamep, mpNamep+1);
1070 cellp = cm_FindCellByID(scp->fid.cell, 0);
1074 code = CM_ERROR_NOSUCHCELL;
1078 vnLength = strlen(volNamep);
1079 if (vnLength >= 8 && strcmp(volNamep + vnLength - 7, ".backup") == 0)
1080 targetType = BACKVOL;
1081 else if (vnLength >= 10
1082 && strcmp(volNamep + vnLength - 9, ".readonly") == 0)
1087 /* check for backups within backups */
1088 if (targetType == BACKVOL
1089 && (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
1090 == CM_SCACHEFLAG_RO) {
1091 code = CM_ERROR_NOSUCHVOLUME;
1095 /* now we need to get the volume */
1096 lock_ReleaseWrite(&scp->rw);
1097 if (cm_VolNameIsID(volNamep)) {
1098 code = cm_FindVolumeByID(cellp, atoi(volNamep), userp, reqp,
1099 CM_GETVOL_FLAG_CREATE, &volp);
1101 code = cm_FindVolumeByName(cellp, volNamep, userp, reqp,
1102 CM_GETVOL_FLAG_CREATE, &volp);
1104 lock_ObtainWrite(&scp->rw);
1107 afs_uint32 cell, volume;
1108 cm_vol_state_t *statep;
1110 cell = cellp->cellID;
1112 /* if the mt pt originates in a .backup volume (not a .readonly)
1113 * and FollowBackupPath is active, and if there is a .backup
1114 * volume for the target, then use the .backup of the target
1115 * instead of the read-write.
1117 if (cm_followBackupPath &&
1118 volp->vol[BACKVOL].ID != 0 &&
1119 (dscp->flags & (CM_SCACHEFLAG_RO|CM_SCACHEFLAG_PURERO)) == CM_SCACHEFLAG_RO &&
1120 (targetType == RWVOL || targetType == ROVOL && volp->vol[ROVOL].ID == 0)
1122 targetType = BACKVOL;
1124 /* if the mt pt is in a read-only volume (not just a
1125 * backup), and if there is a read-only volume for the
1126 * target, and if this is a targetType '#' mount point, use
1127 * the read-only, otherwise use the one specified.
1129 else if (mtType == '#' && targetType == RWVOL &&
1130 (scp->flags & CM_SCACHEFLAG_PURERO) &&
1131 volp->vol[ROVOL].ID != 0) {
1135 lock_ObtainWrite(&volp->rw);
1136 statep = cm_VolumeStateByType(volp, targetType);
1137 volume = statep->ID;
1138 statep->dotdotFid = dscp->fid;
1139 lock_ReleaseWrite(&volp->rw);
1141 /* the rest of the fid is a magic number */
1142 cm_SetFid(&scp->mountRootFid, cell, volume, 1, 1);
1143 scp->mountRootGen = cm_data.mountRootGen;
1145 tfid = scp->mountRootFid;
1146 lock_ReleaseWrite(&scp->rw);
1147 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
1148 lock_ObtainWrite(&scp->rw);
1159 long cm_LookupInternal(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
1160 cm_req_t *reqp, cm_scache_t **outpScpp)
1163 int dnlcHit = 1; /* did we hit in the dnlc? yes, we did */
1164 cm_scache_t *tscp = NULL;
1165 cm_scache_t *mountedScp;
1166 cm_lookupSearch_t rock;
1169 memset(&rock, 0, sizeof(rock));
1171 if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
1172 && strcmp(namep, "..") == 0) {
1173 if (dscp->dotdotFid.volume == 0)
1174 return CM_ERROR_NOSUCHVOLUME;
1175 rock.fid = dscp->dotdotFid;
1177 } else if (strcmp(namep, ".") == 0) {
1178 rock.fid = dscp->fid;
1182 if (flags & CM_FLAG_NOMOUNTCHASE) {
1183 /* In this case, we should go and call cm_Dir* functions
1184 directly since the following cm_ApplyDir() function will
1192 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ, &dirop);
1195 code = cm_BPlusDirLookup(&dirop, namep, &rock.fid);
1200 code = cm_DirLookup(&dirop, namep, &rock.fid);
1202 cm_EndDirOp(&dirop);
1212 if (code == CM_ERROR_INEXACT_MATCH && (flags & CM_FLAG_CASEFOLD)) {
1219 return CM_ERROR_BPLUS_NOMATCH;
1224 rock.fid.cell = dscp->fid.cell;
1225 rock.fid.volume = dscp->fid.volume;
1226 rock.searchNamep = namep;
1227 rock.caseFold = (flags & CM_FLAG_CASEFOLD);
1228 rock.hasTilde = ((strchr(namep, '~') != NULL) ? 1 : 0);
1230 /* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
1231 code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
1232 (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
1234 /* code == 0 means we fell off the end of the dir, while stopnow means
1235 * that we stopped early, probably because we found the entry we're
1236 * looking for. Any other non-zero code is an error.
1238 if (code && code != CM_ERROR_STOPNOW) {
1239 /* if the cm_scache_t we are searching in is not a directory
1240 * we must return path not found because the error
1241 * is to describe the final component not an intermediary
1243 if (code == CM_ERROR_NOTDIR) {
1244 if (flags & CM_FLAG_CHECKPATH)
1245 return CM_ERROR_NOSUCHPATH;
1247 return CM_ERROR_NOSUCHFILE;
1252 getroot = (dscp==cm_data.rootSCachep) ;
1254 if (!cm_freelanceEnabled || !getroot) {
1255 if (flags & CM_FLAG_CHECKPATH)
1256 return CM_ERROR_NOSUCHPATH;
1258 return CM_ERROR_NOSUCHFILE;
1260 else if (!strchr(namep, '#') && !strchr(namep, '%') &&
1261 strcmp(namep, "srvsvc") && strcmp(namep, "wkssvc") &&
1262 strcmp(namep, "ipc$"))
1264 /* nonexistent dir on freelance root, so add it */
1265 char fullname[200] = ".";
1268 osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %s",
1269 osi_LogSaveString(afsd_logp,namep));
1272 * There is an ugly behavior where a share name "foo" will be searched
1273 * for as "fo". If the searched for name differs by an already existing
1274 * symlink or mount point in the Freelance directory, do not add the
1275 * new value automatically.
1279 if (namep[0] == '.') {
1280 if (cm_GetCell_Gen(&namep[1], &fullname[1], CM_FLAG_CREATE)) {
1282 if (!cm_FreelanceMountPointExists(fullname, 0))
1283 code = cm_FreelanceAddMount(fullname, &fullname[1], "root.cell.", 1, &rock.fid);
1284 if ( stricmp(&namep[1], &fullname[1]) &&
1285 !cm_FreelanceMountPointExists(namep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0) &&
1286 !cm_FreelanceSymlinkExists(namep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0))
1287 code = cm_FreelanceAddSymlink(namep, fullname, &rock.fid);
1290 if (cm_GetCell_Gen(namep, fullname, CM_FLAG_CREATE)) {
1292 if (!cm_FreelanceMountPointExists(fullname, 0))
1293 code = cm_FreelanceAddMount(fullname, fullname, "root.cell.", 0, &rock.fid);
1294 if ( stricmp(namep, fullname) &&
1295 !cm_FreelanceMountPointExists(namep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0) &&
1296 !cm_FreelanceSymlinkExists(namep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0))
1297 code = cm_FreelanceAddSymlink(namep, fullname, &rock.fid);
1300 if (!found || code < 0) { /* add mount point failed, so give up */
1301 if (flags & CM_FLAG_CHECKPATH)
1302 return CM_ERROR_NOSUCHPATH;
1304 return CM_ERROR_NOSUCHFILE;
1306 tscp = NULL; /* to force call of cm_GetSCache */
1308 if (flags & CM_FLAG_CHECKPATH)
1309 return CM_ERROR_NOSUCHPATH;
1311 return CM_ERROR_NOSUCHFILE;
1316 if ( !tscp ) /* we did not find it in the dnlc */
1319 code = cm_GetSCache(&rock.fid, &tscp, userp, reqp);
1323 /* tscp is now held */
1325 lock_ObtainWrite(&tscp->rw);
1326 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1327 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1329 lock_ReleaseWrite(&tscp->rw);
1330 cm_ReleaseSCache(tscp);
1333 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1334 /* tscp is now locked */
1336 if (!(flags & CM_FLAG_NOMOUNTCHASE)
1337 && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
1338 /* mount points are funny: they have a volume name to mount
1341 code = cm_ReadMountPoint(tscp, userp, reqp);
1343 code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
1345 lock_ReleaseWrite(&tscp->rw);
1346 cm_ReleaseSCache(tscp);
1353 lock_ReleaseWrite(&tscp->rw);
1356 /* copy back pointer */
1359 /* insert scache in dnlc */
1360 if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
1361 /* lock the directory entry to prevent racing callback revokes */
1362 lock_ObtainRead(&dscp->rw);
1363 if ( dscp->cbServerp != NULL && dscp->cbExpires > 0 )
1364 cm_dnlcEnter(dscp, namep, tscp);
1365 lock_ReleaseRead(&dscp->rw);
1372 int cm_ExpandSysName(char *inp, char *outp, long outSize, unsigned int index)
1377 tp = strrchr(inp, '@');
1379 return 0; /* no @sys */
1381 if (strcmp(tp, "@sys") != 0)
1382 return 0; /* no @sys */
1384 /* caller just wants to know if this is a valid @sys type of name */
1388 if (index >= cm_sysNameCount)
1391 /* otherwise generate the properly expanded @sys name */
1392 prefixCount = (int)(tp - inp);
1394 strncpy(outp, inp, prefixCount); /* copy out "a." from "a.@sys" */
1395 outp[prefixCount] = 0; /* null terminate the "a." */
1396 strcat(outp, cm_sysNameList[index]);/* append i386_nt40 */
1400 long cm_EvaluateVolumeReference(char * namep, long flags, cm_user_t * userp,
1401 cm_req_t *reqp, cm_scache_t ** outpScpp)
1404 char cellName[CELL_MAXNAMELEN];
1405 char volumeName[VL_MAXNAMELEN];
1410 cm_cell_t * cellp = NULL;
1411 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_FindVolumeByID(cellp, atoi(volumeName), userp, reqp,
1471 CM_GETVOL_FLAG_CREATE, &volp);
1473 code = cm_FindVolumeByName(cellp, volumeName, userp, reqp,
1474 CM_GETVOL_FLAG_CREATE, &volp);
1480 if (volType == BACKVOL)
1481 volume = volp->vol[BACKVOL].ID;
1482 else if (volType == ROVOL ||
1483 (volType == RWVOL && mountType == ROVOL && volp->vol[ROVOL].ID != 0))
1484 volume = volp->vol[ROVOL].ID;
1486 volume = volp->vol[RWVOL].ID;
1488 cm_SetFid(&fid, cellp->cellID, volume, 1, 1);
1490 code = cm_GetSCache(&fid, outpScpp, userp, reqp);
1500 if (flags & CM_FLAG_CHECKPATH)
1501 return CM_ERROR_NOSUCHPATH;
1503 return CM_ERROR_NOSUCHFILE;
1506 #ifdef DEBUG_REFCOUNT
1507 long cm_LookupDbg(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
1508 cm_req_t *reqp, cm_scache_t **outpScpp, char * file, long line)
1510 long cm_Lookup(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
1511 cm_req_t *reqp, cm_scache_t **outpScpp)
1515 char tname[AFSPATHMAX];
1516 int sysNameIndex = 0;
1517 cm_scache_t *scp = NULL;
1519 #ifdef DEBUG_REFCOUNT
1520 afsi_log("%s:%d cm_Lookup dscp 0x%p ref %d", file, line, dscp, dscp->refCount, file, line);
1521 osi_Log2(afsd_logp, "cm_Lookup dscp 0x%p ref %d", dscp, dscp->refCount);
1524 if ( stricmp(namep,SMB_IOCTL_FILENAME_NOSLASH) == 0 ) {
1525 if (flags & CM_FLAG_CHECKPATH)
1526 return CM_ERROR_NOSUCHPATH;
1528 return CM_ERROR_NOSUCHFILE;
1531 if (dscp == cm_data.rootSCachep &&
1532 strnicmp(namep, CM_PREFIX_VOL, CM_PREFIX_VOL_CCH) == 0) {
1533 return cm_EvaluateVolumeReference(namep, flags, userp, reqp, outpScpp);
1536 if (cm_ExpandSysName(namep, NULL, 0, 0) > 0) {
1537 for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
1538 code = cm_ExpandSysName(namep, tname, sizeof(tname), sysNameIndex);
1540 code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
1541 #ifdef DEBUG_REFCOUNT
1542 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);
1543 osi_Log3(afsd_logp, "cm_LookupInternal (1) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1551 cm_ReleaseSCache(scp);
1555 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1556 #ifdef DEBUG_REFCOUNT
1557 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);
1558 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1565 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1566 #ifdef DEBUG_REFCOUNT
1567 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);
1568 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1574 /* None of the possible sysName expansions could be found */
1575 if (flags & CM_FLAG_CHECKPATH)
1576 return CM_ERROR_NOSUCHPATH;
1578 return CM_ERROR_NOSUCHFILE;
1581 long cm_Unlink(cm_scache_t *dscp, char *namep, cm_user_t *userp, cm_req_t *reqp)
1587 AFSFetchStatus newDirStatus;
1589 struct rx_connection * callp;
1592 #ifdef AFS_FREELANCE_CLIENT
1593 if (cm_freelanceEnabled && dscp == cm_data.rootSCachep) {
1594 /* deleting a mount point from the root dir. */
1595 code = cm_FreelanceRemoveMount(namep);
1600 /* make sure we don't screw up the dir status during the merge */
1601 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
1603 lock_ObtainWrite(&dscp->rw);
1604 sflags = CM_SCACHESYNC_STOREDATA;
1605 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1606 lock_ReleaseWrite(&dscp->rw);
1608 cm_EndDirOp(&dirop);
1613 afsFid.Volume = dscp->fid.volume;
1614 afsFid.Vnode = dscp->fid.vnode;
1615 afsFid.Unique = dscp->fid.unique;
1617 osi_Log1(afsd_logp, "CALL RemoveFile scp 0x%p", dscp);
1619 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
1623 callp = cm_GetRxConn(connp);
1624 code = RXAFS_RemoveFile(callp, &afsFid, namep,
1625 &newDirStatus, &volSync);
1626 rx_PutConnection(callp);
1628 } while (cm_Analyze(connp, userp, reqp, &dscp->fid, &volSync, NULL, NULL, code));
1629 code = cm_MapRPCError(code, reqp);
1632 osi_Log1(afsd_logp, "CALL RemoveFile FAILURE, code 0x%x", code);
1634 osi_Log0(afsd_logp, "CALL RemoveFile SUCCESS");
1637 lock_ObtainWrite(&dirop.scp->dirlock);
1638 dirop.lockType = CM_DIRLOCK_WRITE;
1640 lock_ObtainWrite(&dscp->rw);
1641 cm_dnlcRemove(dscp, namep);
1642 cm_SyncOpDone(dscp, NULL, sflags);
1644 cm_MergeStatus(NULL, dscp, &newDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
1645 } else if (code == CM_ERROR_NOSUCHFILE) {
1646 /* windows would not have allowed the request to delete the file
1647 * if it did not believe the file existed. therefore, we must
1648 * have an inconsistent view of the world.
1650 dscp->cbServerp = NULL;
1652 lock_ReleaseWrite(&dscp->rw);
1654 if (code == 0 && cm_CheckDirOpForSingleChange(&dirop)) {
1655 cm_DirDeleteEntry(&dirop, namep);
1657 cm_BPlusDirDeleteEntry(&dirop, namep);
1660 cm_EndDirOp(&dirop);
1665 /* called with a write locked vnode, and fills in the link info.
1666 * returns this the vnode still write locked.
1668 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1675 lock_AssertWrite(&linkScp->rw);
1676 if (!linkScp->mountPointStringp[0]) {
1677 /* read the link data */
1678 lock_ReleaseWrite(&linkScp->rw);
1679 thyper.LowPart = thyper.HighPart = 0;
1680 code = buf_Get(linkScp, &thyper, &bufp);
1681 lock_ObtainWrite(&linkScp->rw);
1685 code = cm_SyncOp(linkScp, bufp, userp, reqp, 0,
1686 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1691 cm_SyncOpDone(linkScp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1693 if (cm_HaveBuffer(linkScp, bufp, 0))
1696 code = cm_GetBuffer(linkScp, bufp, NULL, userp, reqp);
1701 } /* while loop to get the data */
1703 /* now if we still have no link read in,
1704 * copy the data from the buffer */
1705 if ((temp = linkScp->length.LowPart) >= MOUNTPOINTLEN) {
1707 return CM_ERROR_TOOBIG;
1710 /* otherwise, it fits; make sure it is still null (could have
1711 * lost race with someone else referencing this link above),
1712 * and if so, copy in the data.
1714 if (!linkScp->mountPointStringp[0]) {
1715 strncpy(linkScp->mountPointStringp, bufp->datap, temp);
1716 linkScp->mountPointStringp[temp] = 0; /* null terminate */
1718 if ( !strnicmp(linkScp->mountPointStringp, "msdfs:", strlen("msdfs:")) )
1719 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
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 *newRootScpp = NULL;
1743 *newSpaceBufferp = NULL;
1745 lock_ObtainWrite(&linkScp->rw);
1746 code = cm_HandleLink(linkScp, userp, reqp);
1750 /* if we may overflow the buffer, bail out; buffer is signficantly
1751 * bigger than max path length, so we don't really have to worry about
1752 * being a little conservative here.
1754 if (strlen(linkScp->mountPointStringp) + strlen(pathSuffixp) + 2
1755 >= CM_UTILS_SPACESIZE) {
1756 code = CM_ERROR_TOOBIG;
1760 tsp = cm_GetSpace();
1761 linkp = linkScp->mountPointStringp;
1762 if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1763 if (strlen(linkp) > cm_mountRootLen)
1764 strcpy(tsp->data, linkp+cm_mountRootLen+1);
1767 *newRootScpp = cm_data.rootSCachep;
1768 cm_HoldSCache(cm_data.rootSCachep);
1769 } else if (linkp[0] == '\\' && linkp[1] == '\\') {
1770 if (!strnicmp(&linkp[2], cm_NetbiosName, (len = (long)strlen(cm_NetbiosName))))
1772 char * p = &linkp[len + 3];
1773 if (strnicmp(p, "all", 3) == 0)
1776 strcpy(tsp->data, p);
1777 for (p = tsp->data; *p; p++) {
1781 *newRootScpp = cm_data.rootSCachep;
1782 cm_HoldSCache(cm_data.rootSCachep);
1784 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1785 strcpy(tsp->data, linkp);
1786 code = CM_ERROR_PATH_NOT_COVERED;
1788 } else if ( linkScp->fileType == CM_SCACHETYPE_DFSLINK ||
1789 !strnicmp(linkp, "msdfs:", (len = (long)strlen("msdfs:"))) ) {
1790 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1791 strcpy(tsp->data, linkp);
1792 code = CM_ERROR_PATH_NOT_COVERED;
1793 } else if (*linkp == '\\' || *linkp == '/') {
1795 /* formerly, this was considered to be from the AFS root,
1796 * but this seems to create problems. instead, we will just
1797 * reject the link */
1798 strcpy(tsp->data, linkp+1);
1799 *newRootScpp = cm_data.rootSCachep;
1800 cm_HoldSCache(cm_data.rootSCachep);
1802 /* we still copy the link data into the response so that
1803 * the user can see what the link points to
1805 linkScp->fileType = CM_SCACHETYPE_INVALID;
1806 strcpy(tsp->data, linkp);
1807 code = CM_ERROR_NOSUCHPATH;
1810 /* a relative link */
1811 strcpy(tsp->data, linkp);
1813 if (pathSuffixp[0] != 0) { /* if suffix string is non-null */
1814 strcat(tsp->data, "\\");
1815 strcat(tsp->data, pathSuffixp);
1818 *newSpaceBufferp = tsp;
1822 if (code == CM_ERROR_PATH_NOT_COVERED && reqp->tidPathp && reqp->relPathp)
1823 cm_VolStatus_Notify_DFS_Mapping(linkScp, reqp->tidPathp, reqp->relPathp);
1827 lock_ReleaseWrite(&linkScp->rw);
1830 #ifdef DEBUG_REFCOUNT
1831 long cm_NameIDbg(cm_scache_t *rootSCachep, char *pathp, long flags,
1832 cm_user_t *userp, char *tidPathp, cm_req_t *reqp, cm_scache_t **outScpp,
1833 char * file, long line)
1835 long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
1836 cm_user_t *userp, char *tidPathp, cm_req_t *reqp, cm_scache_t **outScpp)
1840 char *tp; /* ptr moving through input buffer */
1841 char tc; /* temp char */
1842 int haveComponent; /* has new component started? */
1843 char component[AFSPATHMAX]; /* this is the new component */
1844 char *cp; /* component name being assembled */
1845 cm_scache_t *tscp; /* current location in the hierarchy */
1846 cm_scache_t *nscp; /* next dude down */
1847 cm_scache_t *dirScp; /* last dir we searched */
1848 cm_scache_t *linkScp; /* new root for the symlink we just
1850 cm_space_t *psp; /* space for current path, if we've hit
1852 cm_space_t *tempsp; /* temp vbl */
1853 char *restp; /* rest of the pathname to interpret */
1854 int symlinkCount; /* count of # of symlinks traversed */
1855 int extraFlag; /* avoid chasing mt pts for dir cmd */
1856 int phase = 1; /* 1 = tidPathp, 2 = pathp */
1857 #define MAX_FID_COUNT 512
1858 cm_fid_t fids[MAX_FID_COUNT]; /* array of fids processed in this path walk */
1859 int fid_count = 0; /* number of fids processed in this path walk */
1862 #ifdef DEBUG_REFCOUNT
1863 afsi_log("%s:%d cm_NameI rootscp 0x%p ref %d", file, line, rootSCachep, rootSCachep->refCount);
1864 osi_Log4(afsd_logp,"cm_NameI rootscp 0x%p path %s tidpath %s flags 0x%x",
1865 rootSCachep, pathp ? pathp : "<NULL>", tidPathp ? tidPathp : "<NULL>",
1880 cm_HoldSCache(tscp);
1888 /* map Unix slashes into DOS ones so we can interpret Unix
1894 if (!haveComponent) {
1897 } else if (tc == 0) {
1911 /* we have a component here */
1912 if (tc == 0 || tc == '\\') {
1913 /* end of the component; we're at the last
1914 * component if tc == 0. However, if the last
1915 * is a symlink, we have more to do.
1917 *cp++ = 0; /* add null termination */
1919 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
1920 extraFlag = CM_FLAG_NOMOUNTCHASE;
1921 code = cm_Lookup(tscp, component,
1923 userp, reqp, &nscp);
1926 if (!strcmp(component,"..") || !strcmp(component,".")) {
1928 * roll back the fid list until we find the fid
1929 * that matches where we are now. Its not necessarily
1930 * one or two fids because they might have been
1931 * symlinks or mount points or both that were crossed.
1933 for ( i=fid_count-1; i>=0; i--) {
1934 if (!cm_FidCmp(&nscp->fid, &fids[i]))
1939 /* add the new fid to the list */
1940 for ( i=0; i<fid_count; i++) {
1941 if ( !cm_FidCmp(&nscp->fid, &fids[i]) ) {
1942 code = CM_ERROR_TOO_MANY_SYMLINKS;
1943 cm_ReleaseSCache(nscp);
1948 if (i == fid_count && fid_count < MAX_FID_COUNT) {
1949 fids[fid_count++] = nscp->fid;
1955 cm_ReleaseSCache(tscp);
1957 cm_ReleaseSCache(dirScp);
1960 if ((code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH) &&
1961 tscp->fileType == CM_SCACHETYPE_SYMLINK)
1963 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_NOSUCHPATH");
1964 return CM_ERROR_NOSUCHPATH;
1966 osi_Log1(afsd_logp,"cm_NameI code 0x%x", code);
1971 haveComponent = 0; /* component done */
1973 cm_ReleaseSCache(dirScp);
1974 dirScp = tscp; /* for some symlinks */
1975 tscp = nscp; /* already held */
1977 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
1980 cm_ReleaseSCache(dirScp);
1986 /* now, if tscp is a symlink, we should follow
1987 * it and assemble the path again.
1989 lock_ObtainWrite(&tscp->rw);
1990 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1991 CM_SCACHESYNC_GETSTATUS
1992 | CM_SCACHESYNC_NEEDCALLBACK);
1994 lock_ReleaseWrite(&tscp->rw);
1995 cm_ReleaseSCache(tscp);
1998 cm_ReleaseSCache(dirScp);
2003 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2005 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
2006 /* this is a symlink; assemble a new buffer */
2007 lock_ReleaseWrite(&tscp->rw);
2008 if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
2009 cm_ReleaseSCache(tscp);
2012 cm_ReleaseSCache(dirScp);
2017 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_TOO_MANY_SYMLINKS");
2018 return CM_ERROR_TOO_MANY_SYMLINKS;
2024 code = cm_AssembleLink(tscp, restp, &linkScp, &tempsp, userp, reqp);
2026 if (code == 0 && linkScp != NULL) {
2027 if (linkScp == cm_data.rootSCachep)
2030 for ( i=0; i<fid_count; i++) {
2031 if ( !cm_FidCmp(&linkScp->fid, &fids[i]) ) {
2032 code = CM_ERROR_TOO_MANY_SYMLINKS;
2033 cm_ReleaseSCache(linkScp);
2039 if (i == fid_count && fid_count < MAX_FID_COUNT) {
2040 fids[fid_count++] = linkScp->fid;
2045 /* something went wrong */
2046 cm_ReleaseSCache(tscp);
2049 cm_ReleaseSCache(dirScp);
2055 /* otherwise, tempsp has the new path,
2056 * and linkScp is the new root from
2057 * which to interpret that path.
2058 * Continue with the namei processing,
2059 * also doing the bookkeeping for the
2060 * space allocation and tracking the
2061 * vnode reference counts.
2067 cm_ReleaseSCache(tscp);
2072 * now, if linkScp is null, that's
2073 * AssembleLink's way of telling us that
2074 * the sym link is relative to the dir
2075 * containing the link. We have a ref
2076 * to it in dirScp, and we hold it now
2077 * and reuse it as the new spot in the
2085 /* not a symlink, we may be done */
2086 lock_ReleaseWrite(&tscp->rw);
2094 cm_ReleaseSCache(dirScp);
2102 cm_ReleaseSCache(dirScp);
2105 } /* end of a component */
2108 } /* we have a component */
2109 } /* big while loop over all components */
2113 cm_ReleaseSCache(dirScp);
2119 cm_ReleaseSCache(tscp);
2121 #ifdef DEBUG_REFCOUNT
2122 afsi_log("%s:%d cm_NameI code 0x%x outScpp 0x%p ref %d", file, line, code, *outScpp, (*outScpp)->refCount);
2124 osi_Log2(afsd_logp,"cm_NameI code 0x%x outScpp 0x%p", code, *outScpp);
2128 /* called with a dir, and a vnode within the dir that happens to be a symlink.
2129 * We chase the link, and return a held pointer to the target, if it exists,
2130 * in *outScpp. If we succeed, we return 0, otherwise we return an error code
2131 * and do not hold or return a target vnode.
2133 * This is very similar to calling cm_NameI with the last component of a name,
2134 * which happens to be a symlink, except that we've already passed by the name.
2136 * This function is typically called by the directory listing functions, which
2137 * encounter symlinks but need to return the proper file length so programs
2138 * like "more" work properly when they make use of the attributes retrieved from
2141 * The input vnode should not be locked when this function is called.
2143 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
2144 cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
2148 cm_scache_t *newRootScp;
2150 osi_Log1(afsd_logp, "Evaluating symlink scp 0x%p", linkScp);
2152 code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
2156 /* now, if newRootScp is NULL, we're really being told that the symlink
2157 * is relative to the current directory (dscp).
2159 if (newRootScp == NULL) {
2161 cm_HoldSCache(dscp);
2164 code = cm_NameI(newRootScp, spacep->data,
2165 CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
2166 userp, NULL, reqp, outScpp);
2168 if (code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH)
2169 code = CM_ERROR_NOSUCHPATH;
2171 /* this stuff is allocated no matter what happened on the namei call,
2173 cm_FreeSpace(spacep);
2174 cm_ReleaseSCache(newRootScp);
2176 if (linkScp == *outScpp) {
2177 cm_ReleaseSCache(*outScpp);
2179 code = CM_ERROR_NOSUCHPATH;
2185 /* make this big enough so that one buffer of dir pages won't overflow. We'll
2186 * check anyway, but we want to minimize the chance that we have to leave stuff
2189 #define CM_BULKMAX (3 * AFSCBMAX)
2191 /* rock for bulk stat calls */
2192 typedef struct cm_bulkStat {
2193 osi_hyper_t bufOffset; /* only do it for things in this buffer page */
2195 /* info for the actual call */
2196 int counter; /* next free slot */
2197 AFSFid fids[CM_BULKMAX];
2198 AFSFetchStatus stats[CM_BULKMAX];
2199 AFSCallBack callbacks[CM_BULKMAX];
2202 /* for a given entry, make sure that it isn't in the stat cache, and then
2203 * add it to the list of file IDs to be obtained.
2205 * Don't bother adding it if we already have a vnode. Note that the dir
2206 * is locked, so we have to be careful checking the vnode we're thinking of
2207 * processing, to avoid deadlocks.
2209 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
2220 /* Don't overflow bsp. */
2221 if (bsp->counter >= CM_BULKMAX)
2222 return CM_ERROR_STOPNOW;
2224 thyper.LowPart = cm_data.buf_blockSize;
2225 thyper.HighPart = 0;
2226 thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
2228 /* thyper is now the first byte past the end of the record we're
2229 * interested in, and bsp->bufOffset is the first byte of the record
2230 * we're interested in.
2231 * Skip data in the others.
2234 if (LargeIntegerLessThan(*offp, bsp->bufOffset))
2236 if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
2237 return CM_ERROR_STOPNOW;
2238 if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
2241 cm_SetFid(&tfid, scp->fid.cell, scp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
2242 tscp = cm_FindSCache(&tfid);
2244 if (lock_TryWrite(&tscp->rw)) {
2245 /* we have an entry that we can look at */
2246 if (!(tscp->flags & CM_SCACHEFLAG_EACCESS) && cm_HaveCallback(tscp)) {
2247 /* we have a callback on it. Don't bother
2248 * fetching this stat entry, since we're happy
2249 * with the info we have.
2251 lock_ReleaseWrite(&tscp->rw);
2252 cm_ReleaseSCache(tscp);
2255 lock_ReleaseWrite(&tscp->rw);
2257 cm_ReleaseSCache(tscp);
2260 #ifdef AFS_FREELANCE_CLIENT
2261 // yj: if this is a mountpoint under root.afs then we don't want it
2262 // to be bulkstat-ed, instead, we call getSCache directly and under
2263 // getSCache, it is handled specially.
2264 if ( cm_freelanceEnabled &&
2265 tfid.cell==AFS_FAKE_ROOT_CELL_ID &&
2266 tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
2267 !(tfid.vnode==0x1 && tfid.unique==0x1) )
2269 osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
2270 return cm_GetSCache(&tfid, &tscp, NULL, NULL);
2272 #endif /* AFS_FREELANCE_CLIENT */
2275 bsp->fids[i].Volume = scp->fid.volume;
2276 bsp->fids[i].Vnode = tfid.vnode;
2277 bsp->fids[i].Unique = tfid.unique;
2281 /* called with a write locked scp and a pointer to a buffer. Make bulk stat
2282 * calls on all undeleted files in the page of the directory specified.
2285 cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
2289 cm_bulkStat_t bb; /* this is *BIG*, probably 16K or so;
2290 * watch for stack problems */
2291 AFSCBFids fidStruct;
2292 AFSBulkStats statStruct;
2294 AFSCBs callbackStruct;
2297 cm_callbackRequest_t cbReq;
2303 struct rx_connection * callp;
2304 int inlinebulk = 0; /* Did we use InlineBulkStatus RPC or not? */
2306 osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%p", dscp);
2308 /* should be on a buffer boundary */
2309 osi_assertx((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0, "invalid offset");
2311 memset(&bb, 0, sizeof(bb));
2312 bb.bufOffset = *offsetp;
2314 lock_ReleaseWrite(&dscp->rw);
2315 /* first, assemble the file IDs we need to stat */
2316 code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) &bb, offsetp, userp, reqp, NULL);
2318 /* if we failed, bail out early */
2319 if (code && code != CM_ERROR_STOPNOW) {
2320 lock_ObtainWrite(&dscp->rw);
2324 /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
2325 * make the calls to create the entries. Handle AFSCBMAX files at a
2329 while (filex < bb.counter) {
2330 filesThisCall = bb.counter - filex;
2331 if (filesThisCall > AFSCBMAX)
2332 filesThisCall = AFSCBMAX;
2334 fidStruct.AFSCBFids_len = filesThisCall;
2335 fidStruct.AFSCBFids_val = &bb.fids[filex];
2336 statStruct.AFSBulkStats_len = filesThisCall;
2337 statStruct.AFSBulkStats_val = &bb.stats[filex];
2338 callbackStruct.AFSCBs_len = filesThisCall;
2339 callbackStruct.AFSCBs_val = &bb.callbacks[filex];
2340 cm_StartCallbackGrantingCall(NULL, &cbReq);
2341 osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
2343 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2347 callp = cm_GetRxConn(connp);
2348 if (!(connp->serverp->flags & CM_SERVERFLAG_NOINLINEBULK)) {
2349 code = RXAFS_InlineBulkStatus(callp, &fidStruct,
2350 &statStruct, &callbackStruct, &volSync);
2351 if (code == RXGEN_OPCODE) {
2352 cm_SetServerNoInlineBulk(connp->serverp, 0);
2358 code = RXAFS_BulkStatus(callp, &fidStruct,
2359 &statStruct, &callbackStruct, &volSync);
2361 rx_PutConnection(callp);
2363 } while (cm_Analyze(connp, userp, reqp, &dscp->fid,
2364 &volSync, NULL, &cbReq, code));
2365 code = cm_MapRPCError(code, reqp);
2367 osi_Log2(afsd_logp, "CALL %sBulkStatus FAILURE code 0x%x",
2368 inlinebulk ? "Inline" : "", code);
2370 osi_Log1(afsd_logp, "CALL %sBulkStatus SUCCESS", inlinebulk ? "Inline" : "");
2372 /* may as well quit on an error, since we're not going to do
2373 * much better on the next immediate call, either.
2376 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2380 /* otherwise, we should do the merges */
2381 for (i = 0; i<filesThisCall; i++) {
2383 cm_SetFid(&tfid, dscp->fid.cell, bb.fids[j].Volume, bb.fids[j].Vnode, bb.fids[j].Unique);
2384 code = cm_GetSCache(&tfid, &scp, userp, reqp);
2388 /* otherwise, if this entry has no callback info,
2391 lock_ObtainWrite(&scp->rw);
2392 /* now, we have to be extra paranoid on merging in this
2393 * information, since we didn't use cm_SyncOp before
2394 * starting the fetch to make sure that no bad races
2395 * were occurring. Specifically, we need to make sure
2396 * we don't obliterate any newer information in the
2397 * vnode than have here.
2399 * Right now, be pretty conservative: if there's a
2400 * callback or a pending call, skip it.
2402 if ((scp->cbServerp == NULL || (scp->flags & CM_SCACHEFLAG_EACCESS))
2404 (CM_SCACHEFLAG_FETCHING
2405 | CM_SCACHEFLAG_STORING
2406 | CM_SCACHEFLAG_SIZESTORING))) {
2407 cm_EndCallbackGrantingCall(scp, &cbReq,
2409 CM_CALLBACK_MAINTAINCOUNT);
2410 cm_MergeStatus(dscp, scp, &bb.stats[j], &volSync, userp, 0);
2412 lock_ReleaseWrite(&scp->rw);
2413 cm_ReleaseSCache(scp);
2414 } /* all files in the response */
2415 /* now tell it to drop the count,
2416 * after doing the vnode processing above */
2417 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2419 filex += filesThisCall;
2420 } /* while there are still more files to process */
2421 lock_ObtainWrite(&dscp->rw);
2423 /* If we did the InlineBulk RPC pull out the return code and log it */
2425 if ((&bb.stats[0])->errorCode) {
2426 osi_Log1(afsd_logp, "cm_TryBulkStat bulk stat error: %d",
2427 (&bb.stats[0])->errorCode);
2431 osi_Log0(afsd_logp, "END cm_TryBulkStat");
2435 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
2439 /* initialize store back mask as inexpensive local variable */
2441 memset(statusp, 0, sizeof(AFSStoreStatus));
2443 /* copy out queued info from scache first, if scp passed in */
2445 if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
2446 statusp->ClientModTime = scp->clientModTime;
2447 mask |= AFS_SETMODTIME;
2448 scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
2453 /* now add in our locally generated request */
2454 if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
2455 statusp->ClientModTime = attrp->clientModTime;
2456 mask |= AFS_SETMODTIME;
2458 if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
2459 statusp->UnixModeBits = attrp->unixModeBits;
2460 mask |= AFS_SETMODE;
2462 if (attrp->mask & CM_ATTRMASK_OWNER) {
2463 statusp->Owner = attrp->owner;
2464 mask |= AFS_SETOWNER;
2466 if (attrp->mask & CM_ATTRMASK_GROUP) {
2467 statusp->Group = attrp->group;
2468 mask |= AFS_SETGROUP;
2471 statusp->Mask = mask;
2474 /* set the file size, and make sure that all relevant buffers have been
2475 * truncated. Ensure that any partially truncated buffers have been zeroed
2476 * to the end of the buffer.
2478 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
2484 /* start by locking out buffer creation */
2485 lock_ObtainWrite(&scp->bufCreateLock);
2487 /* verify that this is a file, not a dir or a symlink */
2488 lock_ObtainWrite(&scp->rw);
2489 code = cm_SyncOp(scp, NULL, userp, reqp, 0,
2490 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2493 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2495 if (scp->fileType != CM_SCACHETYPE_FILE) {
2496 code = CM_ERROR_ISDIR;
2501 if (LargeIntegerLessThan(*sizep, scp->length))
2506 lock_ReleaseWrite(&scp->rw);
2508 /* can't hold scp->rw lock here, since we may wait for a storeback to
2509 * finish if the buffer package is cleaning a buffer by storing it to
2513 buf_Truncate(scp, userp, reqp, sizep);
2515 /* now ensure that file length is short enough, and update truncPos */
2516 lock_ObtainWrite(&scp->rw);
2518 /* make sure we have a callback (so we have the right value for the
2519 * length), and wait for it to be safe to do a truncate.
2521 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
2522 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2523 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2525 /* If we only have 'i' bits, then we should still be able to set
2526 the size of a file we created. */
2527 if (code == CM_ERROR_NOACCESS && scp->creator == userp) {
2528 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
2529 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2530 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2536 if (LargeIntegerLessThan(*sizep, scp->length)) {
2537 /* a real truncation. If truncPos is not set yet, or is bigger
2538 * than where we're truncating the file, set truncPos to this
2543 if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
2544 || LargeIntegerLessThan(*sizep, scp->length)) {
2546 scp->truncPos = *sizep;
2547 scp->mask |= CM_SCACHEMASK_TRUNCPOS;
2549 /* in either case, the new file size has been changed */
2550 scp->length = *sizep;
2551 scp->mask |= CM_SCACHEMASK_LENGTH;
2553 else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
2554 /* really extending the file */
2555 scp->length = *sizep;
2556 scp->mask |= CM_SCACHEMASK_LENGTH;
2559 /* done successfully */
2562 cm_SyncOpDone(scp, NULL,
2563 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2564 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2567 lock_ReleaseWrite(&scp->rw);
2568 lock_ReleaseWrite(&scp->bufCreateLock);
2573 /* set the file size or other attributes (but not both at once) */
2574 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
2578 AFSFetchStatus afsOutStatus;
2582 AFSStoreStatus afsInStatus;
2583 struct rx_connection * callp;
2585 /* handle file length setting */
2586 if (attrp->mask & CM_ATTRMASK_LENGTH)
2587 return cm_SetLength(scp, &attrp->length, userp, reqp);
2589 lock_ObtainWrite(&scp->rw);
2590 /* otherwise, we have to make an RPC to get the status */
2591 code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
2593 lock_ReleaseWrite(&scp->rw);
2596 lock_ConvertWToR(&scp->rw);
2598 /* make the attr structure */
2599 cm_StatusFromAttr(&afsInStatus, scp, attrp);
2601 tfid.Volume = scp->fid.volume;
2602 tfid.Vnode = scp->fid.vnode;
2603 tfid.Unique = scp->fid.unique;
2604 lock_ReleaseRead(&scp->rw);
2606 /* now make the RPC */
2607 osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%p", scp);
2609 code = cm_ConnFromFID(&scp->fid, userp, reqp, &connp);
2613 callp = cm_GetRxConn(connp);
2614 code = RXAFS_StoreStatus(callp, &tfid,
2615 &afsInStatus, &afsOutStatus, &volSync);
2616 rx_PutConnection(callp);
2618 } while (cm_Analyze(connp, userp, reqp,
2619 &scp->fid, &volSync, NULL, NULL, code));
2620 code = cm_MapRPCError(code, reqp);
2623 osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
2625 osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
2627 lock_ObtainWrite(&scp->rw);
2628 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
2630 cm_MergeStatus(NULL, scp, &afsOutStatus, &volSync, userp,
2631 CM_MERGEFLAG_FORCE|CM_MERGEFLAG_STOREDATA);
2633 /* if we're changing the mode bits, discard the ACL cache,
2634 * since we changed the mode bits.
2636 if (afsInStatus.Mask & AFS_SETMODE)
2637 cm_FreeAllACLEnts(scp);
2638 lock_ReleaseWrite(&scp->rw);
2642 long cm_Create(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2643 cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
2648 cm_callbackRequest_t cbReq;
2651 cm_scache_t *scp = NULL;
2653 AFSStoreStatus inStatus;
2654 AFSFetchStatus updatedDirStatus;
2655 AFSFetchStatus newFileStatus;
2656 AFSCallBack newFileCallback;
2658 struct rx_connection * callp;
2661 /* can't create names with @sys in them; must expand it manually first.
2662 * return "invalid request" if they try.
2664 if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2665 return CM_ERROR_ATSYS;
2668 #ifdef AFS_FREELANCE_CLIENT
2669 /* Freelance root volume does not hold files */
2670 if (cm_freelanceEnabled &&
2671 dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
2672 dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
2674 return CM_ERROR_NOACCESS;
2676 #endif /* AFS_FREELANCE_CLIENT */
2678 /* before starting the RPC, mark that we're changing the file data, so
2679 * that someone who does a chmod will know to wait until our call
2682 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2683 lock_ObtainWrite(&dscp->rw);
2684 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2685 lock_ReleaseWrite(&dscp->rw);
2687 cm_StartCallbackGrantingCall(NULL, &cbReq);
2689 cm_EndDirOp(&dirop);
2696 cm_StatusFromAttr(&inStatus, NULL, attrp);
2698 /* try the RPC now */
2699 osi_Log1(afsd_logp, "CALL CreateFile scp 0x%p", dscp);
2701 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2705 dirAFSFid.Volume = dscp->fid.volume;
2706 dirAFSFid.Vnode = dscp->fid.vnode;
2707 dirAFSFid.Unique = dscp->fid.unique;
2709 callp = cm_GetRxConn(connp);
2710 code = RXAFS_CreateFile(connp->callp, &dirAFSFid, namep,
2711 &inStatus, &newAFSFid, &newFileStatus,
2712 &updatedDirStatus, &newFileCallback,
2714 rx_PutConnection(callp);
2716 } while (cm_Analyze(connp, userp, reqp,
2717 &dscp->fid, &volSync, NULL, &cbReq, code));
2718 code = cm_MapRPCError(code, reqp);
2721 osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
2723 osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
2726 lock_ObtainWrite(&dirop.scp->dirlock);
2727 dirop.lockType = CM_DIRLOCK_WRITE;
2729 lock_ObtainWrite(&dscp->rw);
2730 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2732 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
2734 lock_ReleaseWrite(&dscp->rw);
2736 /* now try to create the file's entry, too, but be careful to
2737 * make sure that we don't merge in old info. Since we weren't locking
2738 * out any requests during the file's creation, we may have pretty old
2742 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
2743 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2745 lock_ObtainWrite(&scp->rw);
2746 scp->creator = userp; /* remember who created it */
2747 if (!cm_HaveCallback(scp)) {
2748 cm_MergeStatus(dscp, scp, &newFileStatus, &volSync,
2750 cm_EndCallbackGrantingCall(scp, &cbReq,
2751 &newFileCallback, 0);
2754 lock_ReleaseWrite(&scp->rw);
2759 /* make sure we end things properly */
2761 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2763 if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
2764 cm_DirCreateEntry(&dirop, namep, &newFid);
2766 cm_BPlusDirCreateEntry(&dirop, namep, &newFid);
2769 cm_EndDirOp(&dirop);
2774 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
2778 code = buf_CleanVnode(scp, userp, reqp);
2780 lock_ObtainWrite(&scp->rw);
2782 if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
2783 | CM_SCACHEMASK_CLIENTMODTIME
2784 | CM_SCACHEMASK_LENGTH))
2785 code = cm_StoreMini(scp, userp, reqp);
2787 if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) {
2788 code = (scp->flags & CM_SCACHEFLAG_OVERQUOTA) ? CM_ERROR_QUOTA : CM_ERROR_SPACE;
2789 scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE);
2792 lock_ReleaseWrite(&scp->rw);
2797 long cm_MakeDir(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2798 cm_user_t *userp, cm_req_t *reqp)
2803 cm_callbackRequest_t cbReq;
2806 cm_scache_t *scp = NULL;
2808 AFSStoreStatus inStatus;
2809 AFSFetchStatus updatedDirStatus;
2810 AFSFetchStatus newDirStatus;
2811 AFSCallBack newDirCallback;
2813 struct rx_connection * callp;
2816 /* can't create names with @sys in them; must expand it manually first.
2817 * return "invalid request" if they try.
2819 if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2820 return CM_ERROR_ATSYS;
2823 #ifdef AFS_FREELANCE_CLIENT
2824 /* Freelance root volume does not hold subdirectories */
2825 if (cm_freelanceEnabled &&
2826 dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
2827 dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
2829 return CM_ERROR_NOACCESS;
2831 #endif /* AFS_FREELANCE_CLIENT */
2833 /* before starting the RPC, mark that we're changing the directory
2834 * data, so that someone who does a chmod on the dir will wait until
2835 * our call completes.
2837 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2838 lock_ObtainWrite(&dscp->rw);
2839 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2840 lock_ReleaseWrite(&dscp->rw);
2842 cm_StartCallbackGrantingCall(NULL, &cbReq);
2844 cm_EndDirOp(&dirop);
2851 cm_StatusFromAttr(&inStatus, NULL, attrp);
2853 /* try the RPC now */
2854 osi_Log1(afsd_logp, "CALL MakeDir scp 0x%p", dscp);
2856 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2860 dirAFSFid.Volume = dscp->fid.volume;
2861 dirAFSFid.Vnode = dscp->fid.vnode;
2862 dirAFSFid.Unique = dscp->fid.unique;
2864 callp = cm_GetRxConn(connp);
2865 code = RXAFS_MakeDir(connp->callp, &dirAFSFid, namep,
2866 &inStatus, &newAFSFid, &newDirStatus,
2867 &updatedDirStatus, &newDirCallback,
2869 rx_PutConnection(callp);
2871 } while (cm_Analyze(connp, userp, reqp,
2872 &dscp->fid, &volSync, NULL, &cbReq, code));
2873 code = cm_MapRPCError(code, reqp);
2876 osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
2878 osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
2881 lock_ObtainWrite(&dirop.scp->dirlock);
2882 dirop.lockType = CM_DIRLOCK_WRITE;
2884 lock_ObtainWrite(&dscp->rw);
2885 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2887 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
2889 lock_ReleaseWrite(&dscp->rw);
2891 /* now try to create the new dir's entry, too, but be careful to
2892 * make sure that we don't merge in old info. Since we weren't locking
2893 * out any requests during the file's creation, we may have pretty old
2897 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
2898 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2900 lock_ObtainWrite(&scp->rw);
2901 if (!cm_HaveCallback(scp)) {
2902 cm_MergeStatus(dscp, scp, &newDirStatus, &volSync,
2904 cm_EndCallbackGrantingCall(scp, &cbReq,
2905 &newDirCallback, 0);
2908 lock_ReleaseWrite(&scp->rw);
2909 cm_ReleaseSCache(scp);
2913 /* make sure we end things properly */
2915 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2917 if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
2918 cm_DirCreateEntry(&dirop, namep, &newFid);
2920 cm_BPlusDirCreateEntry(&dirop, namep, &newFid);
2923 cm_EndDirOp(&dirop);
2925 /* and return error code */
2929 long cm_Link(cm_scache_t *dscp, char *namep, cm_scache_t *sscp, long flags,
2930 cm_user_t *userp, cm_req_t *reqp)
2935 AFSFid existingAFSFid;
2936 AFSFetchStatus updatedDirStatus;
2937 AFSFetchStatus newLinkStatus;
2939 struct rx_connection * callp;
2942 if (dscp->fid.cell != sscp->fid.cell ||
2943 dscp->fid.volume != sscp->fid.volume) {
2944 return CM_ERROR_CROSSDEVLINK;
2947 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2948 lock_ObtainWrite(&dscp->rw);
2949 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2950 lock_ReleaseWrite(&dscp->rw);
2952 cm_EndDirOp(&dirop);
2957 /* try the RPC now */
2958 osi_Log1(afsd_logp, "CALL Link scp 0x%p", dscp);
2960 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2963 dirAFSFid.Volume = dscp->fid.volume;
2964 dirAFSFid.Vnode = dscp->fid.vnode;
2965 dirAFSFid.Unique = dscp->fid.unique;
2967 existingAFSFid.Volume = sscp->fid.volume;
2968 existingAFSFid.Vnode = sscp->fid.vnode;
2969 existingAFSFid.Unique = sscp->fid.unique;
2971 callp = cm_GetRxConn(connp);
2972 code = RXAFS_Link(callp, &dirAFSFid, namep, &existingAFSFid,
2973 &newLinkStatus, &updatedDirStatus, &volSync);
2974 rx_PutConnection(callp);
2975 osi_Log1(smb_logp," RXAFS_Link returns 0x%x", code);
2977 } while (cm_Analyze(connp, userp, reqp,
2978 &dscp->fid, &volSync, NULL, NULL, code));
2980 code = cm_MapRPCError(code, reqp);
2983 osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
2985 osi_Log0(afsd_logp, "CALL Link SUCCESS");
2988 lock_ObtainWrite(&dirop.scp->dirlock);
2989 dirop.lockType = CM_DIRLOCK_WRITE;
2991 lock_ObtainWrite(&dscp->rw);
2992 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2994 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
2996 lock_ReleaseWrite(&dscp->rw);
2999 if (cm_CheckDirOpForSingleChange(&dirop)) {
3000 cm_DirCreateEntry(&dirop, namep, &sscp->fid);
3002 cm_BPlusDirCreateEntry(&dirop, namep, &sscp->fid);
3006 cm_EndDirOp(&dirop);
3011 long cm_SymLink(cm_scache_t *dscp, char *namep, char *contentsp, long flags,
3012 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
3020 AFSStoreStatus inStatus;
3021 AFSFetchStatus updatedDirStatus;
3022 AFSFetchStatus newLinkStatus;
3024 struct rx_connection * callp;
3027 /* before starting the RPC, mark that we're changing the directory data,
3028 * so that someone who does a chmod on the dir will wait until our
3031 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
3032 lock_ObtainWrite(&dscp->rw);
3033 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3034 lock_ReleaseWrite(&dscp->rw);
3036 cm_EndDirOp(&dirop);
3041 cm_StatusFromAttr(&inStatus, NULL, attrp);
3043 /* try the RPC now */
3044 osi_Log1(afsd_logp, "CALL Symlink scp 0x%p", dscp);
3046 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3050 dirAFSFid.Volume = dscp->fid.volume;
3051 dirAFSFid.Vnode = dscp->fid.vnode;
3052 dirAFSFid.Unique = dscp->fid.unique;
3054 callp = cm_GetRxConn(connp);
3055 code = RXAFS_Symlink(callp, &dirAFSFid, namep, contentsp,
3056 &inStatus, &newAFSFid, &newLinkStatus,
3057 &updatedDirStatus, &volSync);
3058 rx_PutConnection(callp);
3060 } while (cm_Analyze(connp, userp, reqp,
3061 &dscp->fid, &volSync, NULL, NULL, code));
3062 code = cm_MapRPCError(code, reqp);
3065 osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
3067 osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
3070 lock_ObtainWrite(&dirop.scp->dirlock);
3071 dirop.lockType = CM_DIRLOCK_WRITE;
3073 lock_ObtainWrite(&dscp->rw);
3074 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3076 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
3078 lock_ReleaseWrite(&dscp->rw);
3081 if (cm_CheckDirOpForSingleChange(&dirop)) {
3082 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3084 cm_DirCreateEntry(&dirop, namep, &newFid);
3086 cm_BPlusDirCreateEntry(&dirop, namep, &newFid);
3090 cm_EndDirOp(&dirop);
3092 /* now try to create the new dir's entry, too, but be careful to
3093 * make sure that we don't merge in old info. Since we weren't locking
3094 * out any requests during the file's creation, we may have pretty old
3098 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3099 code = cm_GetSCache(&newFid, &scp, userp, reqp);
3101 lock_ObtainWrite(&scp->rw);
3102 if (!cm_HaveCallback(scp)) {
3103 cm_MergeStatus(dscp, scp, &newLinkStatus, &volSync,
3106 lock_ReleaseWrite(&scp->rw);
3107 cm_ReleaseSCache(scp);
3111 /* and return error code */
3115 long cm_RemoveDir(cm_scache_t *dscp, char *namep, cm_user_t *userp,
3122 AFSFetchStatus updatedDirStatus;
3124 struct rx_connection * callp;
3127 /* before starting the RPC, mark that we're changing the directory data,
3128 * so that someone who does a chmod on the dir will wait until our
3131 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
3132 lock_ObtainWrite(&dscp->rw);
3133 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3134 lock_ReleaseWrite(&dscp->rw);
3136 cm_EndDirOp(&dirop);
3141 /* try the RPC now */
3142 osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%p", dscp);
3144 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3148 dirAFSFid.Volume = dscp->fid.volume;
3149 dirAFSFid.Vnode = dscp->fid.vnode;
3150 dirAFSFid.Unique = dscp->fid.unique;
3152 callp = cm_GetRxConn(connp);
3153 code = RXAFS_RemoveDir(callp, &dirAFSFid, namep,
3154 &updatedDirStatus, &volSync);
3155 rx_PutConnection(callp);
3157 } while (cm_Analyze(connp, userp, reqp,
3158 &dscp->fid, &volSync, NULL, NULL, code));
3159 code = cm_MapRPCErrorRmdir(code, reqp);
3162 osi_Log1(afsd_logp, "CALL RemoveDir FAILURE, code 0x%x", code);
3164 osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
3167 lock_ObtainWrite(&dirop.scp->dirlock);
3168 dirop.lockType = CM_DIRLOCK_WRITE;
3170 lock_ObtainWrite(&dscp->rw);
3171 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3173 cm_dnlcRemove(dscp, namep);
3174 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
3176 lock_ReleaseWrite(&dscp->rw);
3179 if (cm_CheckDirOpForSingleChange(&dirop)) {
3180 cm_DirDeleteEntry(&dirop, namep);
3182 cm_BPlusDirDeleteEntry(&dirop, namep);
3186 cm_EndDirOp(&dirop);
3188 /* and return error code */
3192 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
3194 /* grab mutex on contents */
3195 lock_ObtainWrite(&scp->rw);
3197 /* reset the prefetch info */
3198 scp->prefetch.base.LowPart = 0; /* base */
3199 scp->prefetch.base.HighPart = 0;
3200 scp->prefetch.end.LowPart = 0; /* and end */
3201 scp->prefetch.end.HighPart = 0;
3203 /* release mutex on contents */
3204 lock_ReleaseWrite(&scp->rw);
3210 long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
3211 char *newNamep, cm_user_t *userp, cm_req_t *reqp)
3215 AFSFid oldDirAFSFid;
3216 AFSFid newDirAFSFid;
3218 AFSFetchStatus updatedOldDirStatus;
3219 AFSFetchStatus updatedNewDirStatus;
3222 struct rx_connection * callp;
3223 cm_dirOp_t oldDirOp;
3226 cm_dirOp_t newDirOp;
3228 /* before starting the RPC, mark that we're changing the directory data,
3229 * so that someone who does a chmod on the dir will wait until our call
3230 * completes. We do this in vnode order so that we don't deadlock,
3231 * which makes the code a little verbose.
3233 if (oldDscp == newDscp) {
3234 /* check for identical names */
3235 if (strcmp(oldNamep, newNamep) == 0)
3236 return CM_ERROR_RENAME_IDENTICAL;
3239 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3240 lock_ObtainWrite(&oldDscp->rw);
3241 cm_dnlcRemove(oldDscp, oldNamep);
3242 cm_dnlcRemove(oldDscp, newNamep);
3243 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3244 CM_SCACHESYNC_STOREDATA);
3245 lock_ReleaseWrite(&oldDscp->rw);
3247 cm_EndDirOp(&oldDirOp);
3251 /* two distinct dir vnodes */
3253 if (oldDscp->fid.cell != newDscp->fid.cell ||
3254 oldDscp->fid.volume != newDscp->fid.volume)
3255 return CM_ERROR_CROSSDEVLINK;
3257 /* shouldn't happen that we have distinct vnodes for two
3258 * different files, but could due to deliberate attack, or
3259 * stale info. Avoid deadlocks and quit now.
3261 if (oldDscp->fid.vnode == newDscp->fid.vnode)
3262 return CM_ERROR_CROSSDEVLINK;
3264 if (oldDscp->fid.vnode < newDscp->fid.vnode) {
3265 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3266 lock_ObtainWrite(&oldDscp->rw);
3267 cm_dnlcRemove(oldDscp, oldNamep);
3268 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3269 CM_SCACHESYNC_STOREDATA);
3270 lock_ReleaseWrite(&oldDscp->rw);
3272 cm_EndDirOp(&oldDirOp);
3274 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE, &newDirOp);
3275 lock_ObtainWrite(&newDscp->rw);
3276 cm_dnlcRemove(newDscp, newNamep);
3277 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3278 CM_SCACHESYNC_STOREDATA);
3279 lock_ReleaseWrite(&newDscp->rw);
3281 cm_EndDirOp(&newDirOp);
3283 /* cleanup first one */
3284 lock_ObtainWrite(&oldDscp->rw);
3285 cm_SyncOpDone(oldDscp, NULL,
3286 CM_SCACHESYNC_STOREDATA);
3287 lock_ReleaseWrite(&oldDscp->rw);
3288 cm_EndDirOp(&oldDirOp);
3293 /* lock the new vnode entry first */
3294 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE, &newDirOp);
3295 lock_ObtainWrite(&newDscp->rw);
3296 cm_dnlcRemove(newDscp, newNamep);
3297 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3298 CM_SCACHESYNC_STOREDATA);
3299 lock_ReleaseWrite(&newDscp->rw);
3301 cm_EndDirOp(&newDirOp);
3303 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3304 lock_ObtainWrite(&oldDscp->rw);
3305 cm_dnlcRemove(oldDscp, oldNamep);
3306 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3307 CM_SCACHESYNC_STOREDATA);
3308 lock_ReleaseWrite(&oldDscp->rw);
3310 cm_EndDirOp(&oldDirOp);
3312 /* cleanup first one */
3313 lock_ObtainWrite(&newDscp->rw);
3314 cm_SyncOpDone(newDscp, NULL,
3315 CM_SCACHESYNC_STOREDATA);
3316 lock_ReleaseWrite(&newDscp->rw);
3317 cm_EndDirOp(&newDirOp);
3321 } /* two distinct vnodes */
3328 /* try the RPC now */
3329 osi_Log2(afsd_logp, "CALL Rename old scp 0x%p new scp 0x%p",
3332 code = cm_ConnFromFID(&oldDscp->fid, userp, reqp, &connp);
3336 oldDirAFSFid.Volume = oldDscp->fid.volume;
3337 oldDirAFSFid.Vnode = oldDscp->fid.vnode;
3338 oldDirAFSFid.Unique = oldDscp->fid.unique;
3339 newDirAFSFid.Volume = newDscp->fid.volume;
3340 newDirAFSFid.Vnode = newDscp->fid.vnode;
3341 newDirAFSFid.Unique = newDscp->fid.unique;
3343 callp = cm_GetRxConn(connp);
3344 code = RXAFS_Rename(callp, &oldDirAFSFid, oldNamep,
3345 &newDirAFSFid, newNamep,
3346 &updatedOldDirStatus, &updatedNewDirStatus,
3348 rx_PutConnection(callp);
3350 } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
3351 &volSync, NULL, NULL, code));
3352 code = cm_MapRPCError(code, reqp);
3355 osi_Log1(afsd_logp, "CALL Rename FAILURE, code 0x%x", code);
3357 osi_Log0(afsd_logp, "CALL Rename SUCCESS");
3359 /* update the individual stat cache entries for the directories */
3361 lock_ObtainWrite(&oldDirOp.scp->dirlock);
3362 oldDirOp.lockType = CM_DIRLOCK_WRITE;
3364 lock_ObtainWrite(&oldDscp->rw);
3365 cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
3368 cm_MergeStatus(NULL, oldDscp, &updatedOldDirStatus, &volSync,
3369 userp, CM_MERGEFLAG_DIROP);
3370 lock_ReleaseWrite(&oldDscp->rw);
3373 if (cm_CheckDirOpForSingleChange(&oldDirOp)) {
3376 diropCode = cm_BPlusDirLookup(&oldDirOp, oldNamep, &fileFid);
3377 if (diropCode == CM_ERROR_INEXACT_MATCH)
3379 else if (diropCode == EINVAL)
3381 diropCode = cm_DirLookup(&oldDirOp, oldNamep, &fileFid);
3383 if (diropCode == 0) {
3385 diropCode = cm_DirCreateEntry(&oldDirOp, newNamep, &fileFid);
3387 cm_BPlusDirCreateEntry(&oldDirOp, newNamep, &fileFid);
3391 if (diropCode == 0) {
3392 diropCode = cm_DirDeleteEntry(&oldDirOp, oldNamep);
3394 cm_BPlusDirDeleteEntry(&oldDirOp, oldNamep);
3400 cm_EndDirOp(&oldDirOp);
3402 /* and update it for the new one, too, if necessary */
3405 lock_ObtainWrite(&newDirOp.scp->dirlock);
3406 newDirOp.lockType = CM_DIRLOCK_WRITE;
3408 lock_ObtainWrite(&newDscp->rw);
3409 cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
3411 cm_MergeStatus(NULL, newDscp, &updatedNewDirStatus, &volSync,
3412 userp, CM_MERGEFLAG_DIROP);
3413 lock_ReleaseWrite(&newDscp->rw);
3416 /* we only make the local change if we successfully made
3417 the change in the old directory AND there was only one
3418 change in the new directory */
3419 if (diropCode == 0 && cm_CheckDirOpForSingleChange(&newDirOp)) {
3420 cm_DirCreateEntry(&newDirOp, newNamep, &fileFid);
3422 cm_BPlusDirCreateEntry(&newDirOp, newNamep, &fileFid);
3426 cm_EndDirOp(&newDirOp);
3429 /* and return error code */
3433 /* Byte range locks:
3435 The OpenAFS Windows client has to fake byte range locks given no
3436 server side support for such locks. This is implemented as keyed
3437 byte range locks on the cache manager.
3439 Keyed byte range locks:
3441 Each cm_scache_t structure keeps track of a list of keyed locks.
3442 The key for a lock identifies an owner of a set of locks (referred
3443 to as a client). Each key is represented by a value. The set of
3444 key values used within a specific cm_scache_t structure form a
3445 namespace that has a scope of just that cm_scache_t structure. The
3446 same key value can be used with another cm_scache_t structure and
3447 correspond to a completely different client. However it is
3448 advantageous for the SMB or IFS layer to make sure that there is a
3449 1-1 mapping between client and keys over all cm_scache_t objects.
3451 Assume a client C has key Key(C) (although, since the scope of the
3452 key is a cm_scache_t, the key can be Key(C,S), where S is the
3453 cm_scache_t. But assume a 1-1 relation between keys and clients).
3454 A byte range (O,+L) denotes byte addresses (O) through (O+L-1)
3455 inclusive (a.k.a. [O,O+L-1]). The function Key(x) is implemented
3456 through cm_generateKey() function for both SMB and IFS.
3458 The list of locks for a cm_scache_t object S is maintained in
3459 S->fileLocks. The cache manager will set a lock on the AFS file
3460 server in order to assert the locks in S->fileLocks. If only
3461 shared locks are in place for S, then the cache manager will obtain
3462 a LockRead lock, while if there are any exclusive locks, it will
3463 obtain a LockWrite lock. If the exclusive locks are all released
3464 while the shared locks remain, then the cache manager will
3465 downgrade the lock from LockWrite to LockRead. Similarly, if an
3466 exclusive lock is obtained when only shared locks exist, then the
3467 cache manager will try to upgrade the lock from LockRead to
3470 Each lock L owned by client C maintains a key L->key such that
3471 L->key == Key(C), the effective range defined by L->LOffset and
3472 L->LLength such that the range of bytes affected by the lock is
3473 (L->LOffset, +L->LLength), a type maintained in L->LockType which
3474 is either exclusive or shared.
3478 A lock exists iff it is in S->fileLocks for some cm_scache_t
3479 S. Existing locks are in one of the following states: ACTIVE,
3480 WAITLOCK, WAITUNLOCK, LOST, DELETED.
3482 The following sections describe each lock and the associated
3485 1. ACTIVE: A lock L is ACTIVE iff the cache manager has asserted
3486 the lock with the AFS file server. This type of lock can be
3487 exercised by a client to read or write to the locked region (as
3490 1.1 ACTIVE->LOST: When the AFS file server fails to extend a
3491 server lock that was required to assert the lock. Before
3492 marking the lock as lost, the cache manager checks if the file
3493 has changed on the server. If the file has not changed, then
3494 the cache manager will attempt to obtain a new server lock
3495 that is sufficient to assert the client side locks for the
3496 file. If any of these fail, the lock is marked as LOST.
3497 Otherwise, it is left as ACTIVE.
3499 1.2 ACTIVE->DELETED: Lock is released.
3501 2. WAITLOCK: A lock is in a WAITLOCK state if the cache manager
3502 grants the lock but the lock is yet to be asserted with the AFS
3503 file server. Once the file server grants the lock, the state
3504 will transition to an ACTIVE lock.
3506 2.1 WAITLOCK->ACTIVE: The server granted the lock.
3508 2.2 WAITLOCK->DELETED: Lock is abandoned, or timed out during
3511 2.3 WAITLOCK->LOST: One or more locks from this client were
3512 marked as LOST. No further locks will be granted to this
3513 client until all lost locks are removed.
3515 3. WAITUNLOCK: A lock is in a WAITUNLOCK state if the cache manager
3516 receives a request for a lock that conflicts with an existing
3517 ACTIVE or WAITLOCK lock. The lock will be placed in the queue
3518 and will be granted at such time the conflicting locks are
3519 removed, at which point the state will transition to either
3522 3.1 WAITUNLOCK->ACTIVE: The conflicting lock was removed. The
3523 current serverLock is sufficient to assert this lock, or a
3524 sufficient serverLock is obtained.
3526 3.2 WAITUNLOCK->WAITLOCK: The conflicting lock was removed,
3527 however the required serverLock is yet to be asserted with the
3530 3.3 WAITUNLOCK->DELETED: The lock is abandoned, timed out or
3533 3.5 WAITUNLOCK->LOST: One or more locks from this client were
3534 marked as LOST. No further locks will be granted to this
3535 client until all lost locks are removed.
3537 4. LOST: A lock L is LOST if the server lock that was required to
3538 assert the lock could not be obtained or if it could not be
3539 extended, or if other locks by the same client were LOST.
3540 Essentially, once a lock is LOST, the contract between the cache
3541 manager and that specific client is no longer valid.
3543 The cache manager rechecks the server lock once every minute and
3544 extends it as appropriate. If this is not done for 5 minutes,
3545 the AFS file server will release the lock (the 5 minute timeout
3546 is based on current file server code and is fairly arbitrary).
3547 Once released, the lock cannot be re-obtained without verifying
3548 that the contents of the file hasn't been modified since the
3549 time the lock was released. Re-obtaining the lock without
3550 verifying this may lead to data corruption. If the lock can not
3551 be obtained safely, then all active locks for the cm_scache_t
3554 4.1 LOST->DELETED: The lock is released.
3556 5. DELETED: The lock is no longer relevant. Eventually, it will
3557 get removed from the cm_scache_t. In the meantime, it will be
3558 treated as if it does not exist.
3560 5.1 DELETED->not exist: The lock is removed from the
3563 The following are classifications of locks based on their state.
3565 6* A lock L is ACCEPTED if it is ACTIVE or WAITLOCK. These locks
3566 have been accepted by the cache manager, but may or may not have
3567 been granted back to the client.
3569 7* A lock L is QUEUED if it is ACTIVE, WAITLOCK or WAITUNLOCK.
3571 8* A lock L is WAITING if it is WAITLOCK or WAITUNLOCK.
3575 A client C can READ range (Offset,+Length) of a file represented by
3576 cm_scache_t S iff (1):
3578 1. for all _a_ in (Offset,+Length), all of the following is true:
3580 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3581 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3584 1.2 For each LOST lock L in S->fileLocks such that _a_ in
3585 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3588 (When locks are lost on an cm_scache_t, all locks are lost. By
3589 4.2 (below), if there is an exclusive LOST lock, then there
3590 can't be any overlapping ACTIVE locks.)
3592 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
3594 2. for all _a_ in (Offset,+Length), one of the following is true:
3596 2.1 Byte _a_ of S is unowned (as specified in 1.1) AND there
3597 does not exist a LOST lock L such that _a_ in
3598 (L->LOffset,+L->LLength).
3600 2.2 Byte _a_ of S is owned by C under lock L (as specified in
3601 1.2) AND L->LockType is exclusive.
3603 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
3605 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
3608 3.1 If L->LockType is exclusive then there does NOT exist a
3609 ACCEPTED lock M in S->fileLocks such that _a_ in
3610 (M->LOffset,+M->LLength).
3612 (If we count all QUEUED locks then we hit cases such as
3613 cascading waiting locks where the locks later on in the queue
3614 can be granted without compromising file integrity. On the
3615 other hand if only ACCEPTED locks are considered, then locks
3616 that were received earlier may end up waiting for locks that
3617 were received later to be unlocked. The choice of ACCEPTED
3618 locks was made to mimic the Windows byte range lock
3621 3.2 If L->LockType is shared then for each ACCEPTED lock M in
3622 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
3623 M->LockType is shared.
3625 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
3627 4.1 M->key != Key(C)
3629 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
3630 and (M->LOffset,+M->LLength) do not intersect.
3632 (Note: If a client loses a lock, it loses all locks.
3633 Subsequently, it will not be allowed to obtain any more locks
3634 until all existing LOST locks that belong to the client are
3635 released. Once all locks are released by a single client,
3636 there exists no further contract between the client and AFS
3637 about the contents of the file, hence the client can then
3638 proceed to obtain new locks and establish a new contract.
3640 This doesn't quite work as you think it should, because most
3641 applications aren't built to deal with losing locks they
3642 thought they once had. For now, we don't have a good
3643 solution to lost locks.
3645 Also, for consistency reasons, we have to hold off on
3646 granting locks that overlap exclusive LOST locks.)
3648 A client C can only unlock locks L in S->fileLocks which have
3651 The representation and invariants are as follows:
3653 - Each cm_scache_t structure keeps:
3655 - A queue of byte-range locks (cm_scache_t::fileLocks) which
3656 are of type cm_file_lock_t.
3658 - A record of the highest server-side lock that has been
3659 obtained for this object (cm_scache_t::serverLock), which is
3660 one of (-1), LockRead, LockWrite.
3662 - A count of ACCEPTED exclusive and shared locks that are in the
3663 queue (cm_scache_t::sharedLocks and
3664 cm_scache_t::exclusiveLocks)
3666 - Each cm_file_lock_t structure keeps:
3668 - The type of lock (cm_file_lock_t::LockType)
3670 - The key associated with the lock (cm_file_lock_t::key)
3672 - The offset and length of the lock (cm_file_lock_t::LOffset
3673 and cm_file_lock_t::LLength)
3675 - The state of the lock.
3677 - Time of issuance or last successful extension
3679 Semantic invariants:
3681 I1. The number of ACCEPTED locks in S->fileLocks are
3682 (S->sharedLocks + S->exclusiveLocks)
3684 External invariants:
3686 I3. S->serverLock is the lock that we have asserted with the
3687 AFS file server for this cm_scache_t.
3689 I4. S->serverLock == LockRead iff there is at least one ACTIVE
3690 shared lock, but no ACTIVE exclusive locks.
3692 I5. S->serverLock == LockWrite iff there is at least one ACTIVE
3695 I6. If L is a LOST lock, then for each lock M in S->fileLocks,
3696 M->key == L->key IMPLIES M is LOST or DELETED.
3701 #define IS_LOCK_ACTIVE(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == 0)
3703 #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)
3705 #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)
3707 #define IS_LOCK_LOST(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_LOST)
3709 #define IS_LOCK_DELETED(lockp) (((lockp)->flags & CM_FILELOCK_FLAG_DELETED) == CM_FILELOCK_FLAG_DELETED)
3712 #define IS_LOCK_ACCEPTED(lockp) (IS_LOCK_ACTIVE(lockp) || IS_LOCK_WAITLOCK(lockp))
3715 #define IS_LOCK_CLIENTONLY(lockp) ((((lockp)->scp->flags & CM_SCACHEFLAG_RO) == CM_SCACHEFLAG_RO) || (((lockp)->flags & CM_FILELOCK_FLAG_CLIENTONLY) == CM_FILELOCK_FLAG_CLIENTONLY))
3718 #define INTERSECT_RANGE(r1,r2) (((r2).offset+(r2).length) > (r1).offset && ((r1).offset +(r1).length) > (r2).offset)
3721 #define CONTAINS_RANGE(r1,r2) (((r2).offset+(r2).length) <= ((r1).offset+(r1).length) && (r1).offset <= (r2).offset)
3723 #if defined(VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS) && !defined(LOCK_TESTING)
3724 #define SCP_SUPPORTS_BRLOCKS(scp) ((scp)->cbServerp && ((scp)->cbServerp->capabilities & VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS))
3726 #define SCP_SUPPORTS_BRLOCKS(scp) (1)
3729 #define SERVERLOCKS_ENABLED(scp) (!((scp)->flags & CM_SCACHEFLAG_RO) && cm_enableServerLocks && SCP_SUPPORTS_BRLOCKS(scp))
3731 #if defined(VICED_CAPABILITY_WRITELOCKACL)
3732 #define SCP_SUPPORTS_WRITELOCKACL(scp) ((scp)->cbServerp && ((scp->cbServerp->capabilities & VICED_CAPABILITY_WRITELOCKACL)))
3734 #define SCP_SUPPORTS_WRITELOCKACL(scp) (0)
3736 /* This should really be defined in any build that this code is being
3738 #error VICED_CAPABILITY_WRITELOCKACL not defined.
3741 static void cm_LockRangeSubtract(cm_range_t * pos, const cm_range_t * neg)
3743 afs_int64 int_begin;
3746 int_begin = MAX(pos->offset, neg->offset);
3747 int_end = MIN(pos->offset+pos->length, neg->offset+neg->length);
3749 if (int_begin < int_end) {
3750 if (int_begin == pos->offset) {
3751 pos->length = pos->offset + pos->length - int_end;
3752 pos->offset = int_end;
3753 } else if (int_end == pos->offset + pos->length) {
3754 pos->length = int_begin - pos->offset;
3757 /* We only subtract ranges if the resulting range is
3758 contiguous. If we try to support non-contigous ranges, we
3759 aren't actually improving performance. */
3763 /* Called with scp->rw held. Returns 0 if all is clear to read the
3764 specified range by the client identified by key.
3766 long cm_LockCheckRead(cm_scache_t *scp,
3767 LARGE_INTEGER LOffset,
3768 LARGE_INTEGER LLength,
3771 #ifndef ADVISORY_LOCKS
3773 cm_file_lock_t *fileLock;
3777 int substract_ranges = FALSE;
3779 range.offset = LOffset.QuadPart;
3780 range.length = LLength.QuadPart;
3784 1. for all _a_ in (Offset,+Length), all of the following is true:
3786 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3787 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3790 1.2 For each LOST lock L in S->fileLocks such that _a_ in
3791 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3796 lock_ObtainRead(&cm_scacheLock);
3798 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
3800 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
3802 if (INTERSECT_RANGE(range, fileLock->range)) {
3803 if (IS_LOCK_ACTIVE(fileLock)) {
3804 if (fileLock->key == key) {
3806 /* If there is an active lock for this client, it
3807 is safe to substract ranges.*/
3808 cm_LockRangeSubtract(&range, &fileLock->range);
3809 substract_ranges = TRUE;
3811 if (fileLock->lockType != LockRead) {
3812 code = CM_ERROR_LOCK_CONFLICT;
3816 /* even if the entire range is locked for reading,
3817 we still can't grant the lock at this point
3818 because the client may have lost locks. That
3819 is, unless we have already seen an active lock
3820 belonging to the client, in which case there
3821 can't be any lost locks for this client. */
3822 if (substract_ranges)
3823 cm_LockRangeSubtract(&range, &fileLock->range);
3825 } else if (IS_LOCK_LOST(fileLock) &&
3826 (fileLock->key == key || fileLock->lockType == LockWrite)) {
3827 code = CM_ERROR_BADFD;
3833 lock_ReleaseRead(&cm_scacheLock);
3835 osi_Log4(afsd_logp, "cm_LockCheckRead scp 0x%x offset %d length %d code 0x%x",
3836 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
3847 /* Called with scp->rw held. Returns 0 if all is clear to write the
3848 specified range by the client identified by key.
3850 long cm_LockCheckWrite(cm_scache_t *scp,
3851 LARGE_INTEGER LOffset,
3852 LARGE_INTEGER LLength,
3855 #ifndef ADVISORY_LOCKS
3857 cm_file_lock_t *fileLock;
3862 range.offset = LOffset.QuadPart;
3863 range.length = LLength.QuadPart;
3866 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
3868 2. for all _a_ in (Offset,+Length), one of the following is true:
3870 2.1 Byte _a_ of S is unowned AND there does not exist a LOST
3871 lock L such that _a_ in (L->LOffset,+L->LLength).
3873 2.2 Byte _a_ of S is owned by C under lock L AND L->LockType is
3877 lock_ObtainRead(&cm_scacheLock);
3879 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
3881 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
3883 if (INTERSECT_RANGE(range, fileLock->range)) {
3884 if (IS_LOCK_ACTIVE(fileLock)) {
3885 if (fileLock->key == key) {
3886 if (fileLock->lockType == LockWrite) {
3888 /* if there is an active lock for this client, it
3889 is safe to substract ranges */
3890 cm_LockRangeSubtract(&range, &fileLock->range);
3892 code = CM_ERROR_LOCK_CONFLICT;
3896 code = CM_ERROR_LOCK_CONFLICT;
3899 } else if (IS_LOCK_LOST(fileLock)) {
3900 code = CM_ERROR_BADFD;
3906 lock_ReleaseRead(&cm_scacheLock);
3908 osi_Log4(afsd_logp, "cm_LockCheckWrite scp 0x%x offset %d length %d code 0x%x",
3909 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
3920 /* Called with cm_scacheLock write locked */
3921 static cm_file_lock_t * cm_GetFileLock(void) {
3924 l = (cm_file_lock_t *) cm_freeFileLocks;
3926 osi_QRemove(&cm_freeFileLocks, &l->q);
3928 l = malloc(sizeof(cm_file_lock_t));
3929 osi_assertx(l, "null cm_file_lock_t");
3932 memset(l, 0, sizeof(cm_file_lock_t));
3937 /* Called with cm_scacheLock write locked */
3938 static void cm_PutFileLock(cm_file_lock_t *l) {
3939 osi_QAdd(&cm_freeFileLocks, &l->q);
3942 /* called with scp->rw held. May release it during processing, but
3943 leaves it held on exit. */
3944 long cm_IntSetLock(cm_scache_t * scp, cm_user_t * userp, int lockType,
3950 struct rx_connection * callp;
3952 afs_uint32 reqflags = reqp->flags;
3954 tfid.Volume = scp->fid.volume;
3955 tfid.Vnode = scp->fid.vnode;
3956 tfid.Unique = scp->fid.unique;
3959 osi_Log2(afsd_logp, "CALL SetLock scp 0x%p for lock %d", scp, lockType);
3961 reqp->flags |= CM_REQ_NORETRY;
3962 lock_ReleaseWrite(&scp->rw);
3965 code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
3969 callp = cm_GetRxConn(connp);
3970 code = RXAFS_SetLock(callp, &tfid, lockType,
3972 rx_PutConnection(callp);
3974 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
3977 code = cm_MapRPCError(code, reqp);
3979 osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
3981 osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
3984 lock_ObtainWrite(&scp->rw);
3985 reqp->flags = reqflags;
3989 /* called with scp->rw held. Releases it during processing */
3990 long cm_IntReleaseLock(cm_scache_t * scp, cm_user_t * userp,
3996 struct rx_connection * callp;
3999 tfid.Volume = scp->fid.volume;
4000 tfid.Vnode = scp->fid.vnode;
4001 tfid.Unique = scp->fid.unique;
4004 lock_ReleaseWrite(&scp->rw);
4006 osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
4009 code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4013 callp = cm_GetRxConn(connp);
4014 code = RXAFS_ReleaseLock(callp, &tfid, &volSync);
4015 rx_PutConnection(callp);
4017 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
4019 code = cm_MapRPCError(code, reqp);
4022 "CALL ReleaseLock FAILURE, code 0x%x", code);
4025 "CALL ReleaseLock SUCCESS");
4027 lock_ObtainWrite(&scp->rw);
4032 /* called with scp->rw held. May release it during processing, but
4033 will exit with lock held.
4037 - 0 if the user has permission to get the specified lock for the scp
4039 - CM_ERROR_NOACCESS if not
4041 Any other error from cm_SyncOp will be sent down untranslated.
4043 If CM_ERROR_NOACCESS is returned and lock_type is LockRead, then
4044 phas_insert (if non-NULL) will receive a boolean value indicating
4045 whether the user has INSERT permission or not.
4047 long cm_LockCheckPerms(cm_scache_t * scp,
4054 long code = 0, code2 = 0;
4056 /* lock permissions are slightly tricky because of the 'i' bit.
4057 If the user has PRSFS_LOCK, she can read-lock the file. If the
4058 user has PRSFS_WRITE, she can write-lock the file. However, if
4059 the user has PRSFS_INSERT, then she can write-lock new files,
4060 but not old ones. Since we don't have information about
4061 whether a file is new or not, we assume that if the user owns
4062 the scp, then she has the permissions that are granted by
4065 osi_Log3(afsd_logp, "cm_LockCheckPerms for scp[0x%p] type[%d] user[0x%p]",
4066 scp, lock_type, userp);
4068 if (lock_type == LockRead)
4069 rights |= PRSFS_LOCK;
4070 else if (lock_type == LockWrite)
4071 rights |= PRSFS_WRITE | PRSFS_LOCK;
4074 osi_assertx(FALSE, "invalid lock type");
4079 *phas_insert = FALSE;
4081 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
4082 CM_SCACHESYNC_GETSTATUS |
4083 CM_SCACHESYNC_NEEDCALLBACK);
4085 if (phas_insert && scp->creator == userp) {
4087 /* If this file was created by the user, then we check for
4088 PRSFS_INSERT. If the file server is recent enough, then
4089 this should be sufficient for her to get a write-lock (but
4090 not necessarily a read-lock). VICED_CAPABILITY_WRITELOCKACL
4091 indicates whether a file server supports getting write
4092 locks when the user only has PRSFS_INSERT.
4094 If the file was not created by the user we skip the check
4095 because the INSERT bit will not apply to this user even
4099 code2 = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
4100 CM_SCACHESYNC_GETSTATUS |
4101 CM_SCACHESYNC_NEEDCALLBACK);
4103 if (code2 == CM_ERROR_NOACCESS) {
4104 osi_Log0(afsd_logp, "cm_LockCheckPerms user has no INSERT bits");
4106 *phas_insert = TRUE;
4107 osi_Log0(afsd_logp, "cm_LockCheckPerms user has INSERT bits");
4111 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
4113 osi_Log1(afsd_logp, "cm_LockCheckPerms returning code %d", code);
4118 /* called with scp->rw held */
4119 long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
4120 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
4122 int allowWait, cm_user_t *userp, cm_req_t *reqp,
4123 cm_file_lock_t **lockpp)
4126 int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
4127 cm_file_lock_t *fileLock;
4130 int wait_unlock = FALSE;
4131 int force_client_lock = FALSE;
4133 osi_Log4(afsd_logp, "cm_Lock scp 0x%x type 0x%x offset %d length %d",
4134 scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
4135 osi_Log3(afsd_logp, "... allowWait %d key 0x%x:%x", allowWait,
4136 (unsigned long)(key >> 32), (unsigned long)(key & 0xffffffff));
4139 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
4141 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
4144 3.1 If L->LockType is exclusive then there does NOT exist a
4145 ACCEPTED lock M in S->fileLocks such that _a_ in
4146 (M->LOffset,+M->LLength).
4148 3.2 If L->LockType is shared then for each ACCEPTED lock M in
4149 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
4150 M->LockType is shared.
4152 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
4154 4.1 M->key != Key(C)
4156 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
4157 and (M->LOffset,+M->LLength) do not intersect.
4160 range.offset = LOffset.QuadPart;
4161 range.length = LLength.QuadPart;
4163 lock_ObtainRead(&cm_scacheLock);
4165 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4167 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4169 if (IS_LOCK_LOST(fileLock)) {
4170 if (fileLock->key == key) {
4171 code = CM_ERROR_BADFD;
4173 } else if (fileLock->lockType == LockWrite && INTERSECT_RANGE(range, fileLock->range)) {
4174 code = CM_ERROR_WOULDBLOCK;
4180 /* we don't need to check for deleted locks here since deleted
4181 locks are dequeued from scp->fileLocks */
4182 if (IS_LOCK_ACCEPTED(fileLock) &&
4183 INTERSECT_RANGE(range, fileLock->range)) {
4185 if ((sLockType & LOCKING_ANDX_SHARED_LOCK) == 0 ||
4186 fileLock->lockType != LockRead) {
4188 code = CM_ERROR_WOULDBLOCK;
4194 lock_ReleaseRead(&cm_scacheLock);
4196 if (code == 0 && SERVERLOCKS_ENABLED(scp)) {
4197 if (Which == scp->serverLock ||
4198 (Which == LockRead && scp->serverLock == LockWrite)) {
4202 /* we already have the lock we need */
4203 osi_Log3(afsd_logp, " we already have the correct lock. exclusives[%d], shared[%d], serverLock[%d]",
4204 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4206 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4208 /* special case: if we don't have permission to read-lock
4209 the file, then we force a clientside lock. This is to
4210 compensate for applications that obtain a read-lock for
4211 reading files off of directories that don't grant
4212 read-locks to the user. */
4213 if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4215 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4216 osi_Log0(afsd_logp, " User has no read-lock perms, but has INSERT perms.");
4219 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4220 force_client_lock = TRUE;
4224 } else if ((scp->exclusiveLocks > 0) ||
4225 (scp->sharedLocks > 0 && scp->serverLock != LockRead)) {
4228 /* We are already waiting for some other lock. We should
4229 wait for the daemon to catch up instead of generating a
4230 flood of SetLock calls. */
4231 osi_Log3(afsd_logp, " already waiting for other lock. exclusives[%d], shared[%d], serverLock[%d]",
4232 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4234 /* see if we have permission to create the lock in the
4236 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4238 code = CM_ERROR_WOULDBLOCK;
4239 else if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4241 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4243 " User has no read-lock perms, but has INSERT perms.");
4244 code = CM_ERROR_WOULDBLOCK;
4247 " User has no read-lock perms. Forcing client-side lock");
4248 force_client_lock = TRUE;
4252 /* leave any other codes as-is */
4256 int check_data_version = FALSE;
4259 /* first check if we have permission to elevate or obtain
4261 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4263 if (code == CM_ERROR_NOACCESS && Which == LockRead &&
4264 (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp))) {
4265 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4266 force_client_lock = TRUE;
4271 /* has_insert => (Which == LockRead, code == CM_ERROR_NOACCESS) */
4273 if (scp->serverLock == LockRead && Which == LockWrite) {
4275 /* We want to escalate the lock to a LockWrite.
4276 * Unfortunately that's not really possible without
4277 * letting go of the current lock. But for now we do
4281 " attempting to UPGRADE from LockRead to LockWrite.");
4283 " dataVersion on scp: %I64d", scp->dataVersion);
4285 /* we assume at this point (because scp->serverLock
4286 was valid) that we had a valid server lock. */
4287 scp->lockDataVersion = scp->dataVersion;
4288 check_data_version = TRUE;
4290 code = cm_IntReleaseLock(scp, userp, reqp);
4293 /* We couldn't release the lock */
4296 scp->serverLock = -1;
4300 /* We need to obtain a server lock of type Which in order
4301 * to assert this file lock */
4302 #ifndef AGGRESSIVE_LOCKS
4305 newLock = LockWrite;
4308 code = cm_IntSetLock(scp, userp, newLock, reqp);
4310 #ifdef AGGRESSIVE_LOCKS
4311 if ((code == CM_ERROR_WOULDBLOCK ||
4312 code == CM_ERROR_NOACCESS) && newLock != Which) {
4313 /* we wanted LockRead. We tried LockWrite. Now try
4318 osi_assertx(newLock == LockRead, "lock type not read");
4320 code = cm_IntSetLock(scp, userp, newLock, reqp);
4324 if (code == CM_ERROR_NOACCESS) {
4325 if (Which == LockRead) {
4326 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4328 /* We requested a read-lock, but we have permission to
4329 * get a write-lock. Try that */
4331 tcode = cm_LockCheckPerms(scp, LockWrite, userp, reqp, NULL);
4334 newLock = LockWrite;
4336 osi_Log0(afsd_logp, " User has 'i' perms and the request was for a LockRead. Trying to get a LockWrite instead");
4338 code = cm_IntSetLock(scp, userp, newLock, reqp);
4341 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4342 force_client_lock = TRUE;
4344 } else if (Which == LockWrite &&
4345 scp->creator == userp && !SCP_SUPPORTS_WRITELOCKACL(scp)) {
4348 /* Special case: if the lock request was for a
4349 * LockWrite and the user owns the file and we weren't
4350 * allowed to obtain the serverlock, we either lost a
4351 * race (the permissions changed from under us), or we
4352 * have 'i' bits, but we aren't allowed to lock the
4355 /* check if we lost a race... */
4356 tcode = cm_LockCheckPerms(scp, Which, userp, reqp, NULL);
4359 osi_Log0(afsd_logp, " User has 'i' perms but can't obtain write locks. Using client-side locks.");
4360 force_client_lock = TRUE;
4365 if (code == 0 && check_data_version &&
4366 scp->dataVersion != scp->lockDataVersion) {
4367 /* We lost a race. Although we successfully obtained
4368 * a lock, someone modified the file in between. The
4369 * locks have all been technically lost. */
4372 " Data version mismatch while upgrading lock.");
4374 " Data versions before=%I64d, after=%I64d",
4375 scp->lockDataVersion,
4378 " Releasing stale lock for scp 0x%x", scp);
4380 code = cm_IntReleaseLock(scp, userp, reqp);
4382 scp->serverLock = -1;
4384 code = CM_ERROR_INVAL;
4385 } else if (code == 0) {
4386 scp->serverLock = newLock;
4387 scp->lockDataVersion = scp->dataVersion;
4391 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4392 scp->serverLock == -1) {
4393 /* Oops. We lost the lock. */
4394 cm_LockMarkSCacheLost(scp);
4397 } else if (code == 0) { /* server locks not enabled */
4399 " Skipping server lock for scp");
4404 if (code != 0 && !force_client_lock) {
4405 /* Special case error translations
4407 Applications don't expect certain errors from a
4408 LockFile/UnlockFile call. We need to translate some error
4409 code to codes that apps expect and handle. */
4411 /* We shouldn't actually need to handle this case since we
4412 simulate locks for RO scps anyway. */
4413 if (code == CM_ERROR_READONLY) {
4414 osi_Log0(afsd_logp, " Reinterpreting CM_ERROR_READONLY as CM_ERROR_NOACCESS");
4415 code = CM_ERROR_NOACCESS;
4419 if (code == 0 || (code == CM_ERROR_WOULDBLOCK && allowWait) ||
4420 force_client_lock) {
4422 /* clear the error if we are forcing a client lock, so we
4423 don't get confused later. */
4424 if (force_client_lock && code != CM_ERROR_WOULDBLOCK)
4427 lock_ObtainWrite(&cm_scacheLock);
4428 fileLock = cm_GetFileLock();
4429 lock_ReleaseWrite(&cm_scacheLock);
4431 fileLock->fid = scp->fid;
4433 fileLock->key = key;
4434 fileLock->lockType = Which;
4436 fileLock->userp = userp;
4437 fileLock->range = range;
4438 fileLock->flags = (code == 0 ? 0 :
4440 CM_FILELOCK_FLAG_WAITUNLOCK :
4441 CM_FILELOCK_FLAG_WAITLOCK));
4443 if (force_client_lock || !SERVERLOCKS_ENABLED(scp))
4444 fileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
4446 fileLock->lastUpdate = (code == 0 && !force_client_lock) ? time(NULL) : 0;
4448 lock_ObtainWrite(&cm_scacheLock);
4449 osi_QAddT(&scp->fileLocksH, &scp->fileLocksT, &fileLock->fileq);
4450 cm_HoldSCacheNoLock(scp);
4451 fileLock->scp = scp;
4452 osi_QAdd(&cm_allFileLocks, &fileLock->q);
4453 lock_ReleaseWrite(&cm_scacheLock);
4459 if (IS_LOCK_CLIENTONLY(fileLock)) {
4461 } else if (IS_LOCK_ACCEPTED(fileLock)) {
4462 if (Which == LockRead)
4465 scp->exclusiveLocks++;
4469 "cm_Lock Lock added 0x%p flags 0x%x to scp [0x%p]",
4470 fileLock, fileLock->flags, scp);
4472 " exclusives[%d] shared[%d] client[%d] serverLock[%d]",
4473 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4474 (int)(signed char) scp->serverLock);
4477 "cm_Lock Rejecting lock (code = 0x%x)", code);
4483 static int cm_KeyEquals(cm_key_t k1, cm_key_t k2, int flags);
4485 /* Called with scp->rw held */
4486 long cm_UnlockByKey(cm_scache_t * scp,
4493 cm_file_lock_t *fileLock;
4494 osi_queue_t *q, *qn;
4497 osi_Log4(afsd_logp, "cm_UnlockByKey scp 0x%p key 0x%x:%x flags=0x%x",
4499 (unsigned long)(key >> 32),
4500 (unsigned long)(key & 0xffffffff),
4503 lock_ObtainWrite(&cm_scacheLock);
4505 for (q = scp->fileLocksH; q; q = qn) {
4508 fileLock = (cm_file_lock_t *)
4509 ((char *) q - offsetof(cm_file_lock_t, fileq));
4512 osi_Log4(afsd_logp, " Checking lock[0x%x] range[%d,+%d] type[%d]",
4514 (unsigned long) fileLock->range.offset,
4515 (unsigned long) fileLock->range.length,
4516 fileLock->lockType);
4517 osi_Log3(afsd_logp, " key[0x%x:%x] flags[0x%x]",
4518 (unsigned long)(fileLock->key >> 32),
4519 (unsigned long)(fileLock->key & 0xffffffff),
4522 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
4523 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
4524 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4526 fileLock->fid.volume,
4527 fileLock->fid.vnode,
4528 fileLock->fid.unique);
4529 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4530 fileLock->scp->fid.cell,
4531 fileLock->scp->fid.volume,
4532 fileLock->scp->fid.vnode,
4533 fileLock->scp->fid.unique);
4534 osi_assertx(FALSE, "invalid fid value");
4538 if (!IS_LOCK_DELETED(fileLock) &&
4539 cm_KeyEquals(fileLock->key, key, flags)) {
4540 osi_Log3(afsd_logp, "...Unlock range [%d,+%d] type %d",
4541 fileLock->range.offset,
4542 fileLock->range.length,
4543 fileLock->lockType);
4545 if (scp->fileLocksT == q)
4546 scp->fileLocksT = osi_QPrev(q);
4547 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
4549 if (IS_LOCK_CLIENTONLY(fileLock)) {
4551 } else if (IS_LOCK_ACCEPTED(fileLock)) {
4552 if (fileLock->lockType == LockRead)
4555 scp->exclusiveLocks--;
4558 fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
4560 cm_ReleaseUser(fileLock->userp);
4561 cm_ReleaseSCacheNoLock(scp);
4563 fileLock->userp = NULL;
4564 fileLock->scp = NULL;
4570 lock_ReleaseWrite(&cm_scacheLock);
4572 if (n_unlocks == 0) {
4573 osi_Log0(afsd_logp, "cm_UnlockByKey no locks found");
4574 osi_Log3(afsd_logp, " Leaving scp with exclusives[%d], shared[%d], serverLock[%d]",
4575 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4580 osi_Log1(afsd_logp, "cm_UnlockByKey done with %d locks", n_unlocks);
4582 osi_assertx(scp->sharedLocks >= 0, "scp->sharedLocks < 0");
4583 osi_assertx(scp->exclusiveLocks >= 0, "scp->exclusiveLocks < 0");
4584 osi_assertx(scp->clientLocks >= 0, "scp->clientLocks < 0");
4586 if (!SERVERLOCKS_ENABLED(scp)) {
4587 osi_Log0(afsd_logp, " Skipping server lock for scp");
4591 /* Ideally we would go through the rest of the locks to determine
4592 * if one or more locks that were formerly in WAITUNLOCK can now
4593 * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
4594 * scp->sharedLocks accordingly. However, the retrying of locks
4595 * in that manner is done cm_RetryLock() manually.
4598 if (scp->serverLock == LockWrite &&
4599 scp->exclusiveLocks == 0 &&
4600 scp->sharedLocks > 0) {
4602 /* The serverLock should be downgraded to LockRead */
4603 osi_Log0(afsd_logp, " DOWNGRADE lock from LockWrite to LockRead");
4605 /* since scp->serverLock looked sane, we are going to assume
4606 that we have a valid server lock. */
4607 scp->lockDataVersion = scp->dataVersion;
4608 osi_Log1(afsd_logp, " dataVersion on scp = %I64d", scp->dataVersion);
4610 code = cm_IntReleaseLock(scp, userp, reqp);
4613 /* so we couldn't release it. Just let the lock be for now */
4617 scp->serverLock = -1;
4620 code = cm_IntSetLock(scp, userp, LockRead, reqp);
4622 if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
4623 scp->serverLock = LockRead;
4624 } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
4625 /* We lost a race condition. Although we have a valid
4626 lock on the file, the data has changed and essentially
4627 we have lost the lock we had during the transition. */
4629 osi_Log0(afsd_logp, "Data version mismatch during lock downgrade");
4630 osi_Log2(afsd_logp, " Data versions before=%I64d, after=%I64d",
4631 scp->lockDataVersion,
4634 code = cm_IntReleaseLock(scp, userp, reqp);
4636 code = CM_ERROR_INVAL;
4637 scp->serverLock = -1;
4641 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4642 (scp->serverLock == -1)) {
4644 cm_LockMarkSCacheLost(scp);
4647 /* failure here has no bearing on the return value of
4651 } else if (scp->serverLock != (-1) &&
4652 scp->exclusiveLocks == 0 &&
4653 scp->sharedLocks == 0) {
4654 /* The serverLock should be released entirely */
4656 code = cm_IntReleaseLock(scp, userp, reqp);
4659 scp->serverLock = (-1);
4664 osi_Log1(afsd_logp, "cm_UnlockByKey code 0x%x", code);
4665 osi_Log4(afsd_logp, " Leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
4666 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4667 (int)(signed char) scp->serverLock);
4672 long cm_Unlock(cm_scache_t *scp,
4673 unsigned char sLockType,
4674 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
4680 int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
4681 cm_file_lock_t *fileLock;
4683 int release_userp = FALSE;
4685 osi_Log4(afsd_logp, "cm_Unlock scp 0x%p type 0x%x offset %d length %d",
4686 scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
4687 osi_Log2(afsd_logp, "... key 0x%x:%x",
4688 (unsigned long) (key >> 32), (unsigned long) (key & 0xffffffff));
4690 lock_ObtainRead(&cm_scacheLock);
4692 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4693 fileLock = (cm_file_lock_t *)
4694 ((char *) q - offsetof(cm_file_lock_t, fileq));
4697 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
4698 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
4699 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4701 fileLock->fid.volume,
4702 fileLock->fid.vnode,
4703 fileLock->fid.unique);
4704 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4705 fileLock->scp->fid.cell,
4706 fileLock->scp->fid.volume,
4707 fileLock->scp->fid.vnode,
4708 fileLock->scp->fid.unique);
4709 osi_assertx(FALSE, "invalid fid value");
4712 if (!IS_LOCK_DELETED(fileLock) &&
4713 fileLock->key == key &&
4714 fileLock->range.offset == LOffset.QuadPart &&
4715 fileLock->range.length == LLength.QuadPart) {
4721 osi_Log0(afsd_logp, "cm_Unlock lock not found; failure");
4723 lock_ReleaseRead(&cm_scacheLock);
4725 /* The lock didn't exist anyway. *shrug* */
4726 return CM_ERROR_RANGE_NOT_LOCKED;
4729 /* discard lock record */
4730 lock_ConvertRToW(&cm_scacheLock);
4731 if (scp->fileLocksT == q)
4732 scp->fileLocksT = osi_QPrev(q);
4733 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
4736 * Don't delete it here; let the daemon delete it, to simplify
4737 * the daemon's traversal of the list.
4740 if (IS_LOCK_CLIENTONLY(fileLock)) {
4742 } else if (IS_LOCK_ACCEPTED(fileLock)) {
4743 if (fileLock->lockType == LockRead)
4746 scp->exclusiveLocks--;
4749 fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
4750 if (userp != NULL) {
4751 cm_ReleaseUser(fileLock->userp);
4753 userp = fileLock->userp;
4754 release_userp = TRUE;
4756 fileLock->userp = NULL;
4757 cm_ReleaseSCacheNoLock(scp);
4758 fileLock->scp = NULL;
4759 lock_ReleaseWrite(&cm_scacheLock);
4761 if (!SERVERLOCKS_ENABLED(scp)) {
4762 osi_Log0(afsd_logp, " Skipping server locks for scp");
4766 /* Ideally we would go through the rest of the locks to determine
4767 * if one or more locks that were formerly in WAITUNLOCK can now
4768 * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
4769 * scp->sharedLocks accordingly. However, the retrying of locks
4770 * in that manner is done cm_RetryLock() manually.
4773 if (scp->serverLock == LockWrite &&
4774 scp->exclusiveLocks == 0 &&
4775 scp->sharedLocks > 0) {
4777 /* The serverLock should be downgraded to LockRead */
4778 osi_Log0(afsd_logp, " DOWNGRADE lock from LockWrite to LockRead");
4780 /* Since we already had a lock, we assume that there is a
4781 valid server lock. */
4782 scp->lockDataVersion = scp->dataVersion;
4783 osi_Log1(afsd_logp, " dataVersion on scp is %I64d", scp->dataVersion);
4785 /* before we downgrade, make sure that we have enough
4786 permissions to get the read lock. */
4787 code = cm_LockCheckPerms(scp, LockRead, userp, reqp, NULL);
4790 osi_Log0(afsd_logp, " SKIPPING downgrade because user doesn't have perms to get downgraded lock");
4796 code = cm_IntReleaseLock(scp, userp, reqp);
4799 /* so we couldn't release it. Just let the lock be for now */
4803 scp->serverLock = -1;
4806 code = cm_IntSetLock(scp, userp, LockRead, reqp);
4808 if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
4809 scp->serverLock = LockRead;
4810 } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
4811 /* Lost a race. We obtained a new lock, but that is
4812 meaningless since someone modified the file
4816 "Data version mismatch while downgrading lock");
4818 " Data versions before=%I64d, after=%I64d",
4819 scp->lockDataVersion,
4822 code = cm_IntReleaseLock(scp, userp, reqp);
4824 scp->serverLock = -1;
4825 code = CM_ERROR_INVAL;
4829 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4830 (scp->serverLock == -1)) {
4832 cm_LockMarkSCacheLost(scp);
4835 /* failure here has no bearing on the return value of
4839 } else if (scp->serverLock != (-1) &&
4840 scp->exclusiveLocks == 0 &&
4841 scp->sharedLocks == 0) {
4842 /* The serverLock should be released entirely */
4844 code = cm_IntReleaseLock(scp, userp, reqp);
4847 scp->serverLock = (-1);
4852 cm_ReleaseUser(userp);
4856 osi_Log1(afsd_logp, "cm_Unlock code 0x%x", code);
4857 osi_Log4(afsd_logp, " leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
4858 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4859 (int)(signed char) scp->serverLock);
4864 /* called with scp->rw held */
4865 void cm_LockMarkSCacheLost(cm_scache_t * scp)
4867 cm_file_lock_t *fileLock;
4870 osi_Log1(afsd_logp, "cm_LockMarkSCacheLost scp 0x%x", scp);
4872 /* cm_scacheLock needed because we are modifying fileLock->flags */
4873 lock_ObtainWrite(&cm_scacheLock);
4875 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4877 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4879 if (IS_LOCK_ACTIVE(fileLock) &&
4880 !IS_LOCK_CLIENTONLY(fileLock)) {
4881 if (fileLock->lockType == LockRead)
4884 scp->exclusiveLocks--;
4886 fileLock->flags |= CM_FILELOCK_FLAG_LOST;
4890 scp->serverLock = -1;
4891 scp->lockDataVersion = -1;
4892 lock_ReleaseWrite(&cm_scacheLock);
4895 /* Called with no relevant locks held */
4896 void cm_CheckLocks()
4898 osi_queue_t *q, *nq;
4899 cm_file_lock_t *fileLock;
4905 struct rx_connection * callp;
4910 lock_ObtainWrite(&cm_scacheLock);
4912 cm_lockRefreshCycle++;
4914 osi_Log1(afsd_logp, "cm_CheckLocks starting lock check cycle %d", cm_lockRefreshCycle);
4916 for (q = cm_allFileLocks; q; q = nq) {
4917 fileLock = (cm_file_lock_t *) q;
4921 if (IS_LOCK_DELETED(fileLock)) {
4923 osi_QRemove(&cm_allFileLocks, q);
4924 cm_PutFileLock(fileLock);
4926 } else if (IS_LOCK_ACTIVE(fileLock) && !IS_LOCK_CLIENTONLY(fileLock)) {
4928 /* Server locks must have been enabled for us to have
4929 received an active non-client-only lock. */
4930 osi_assertx(cm_enableServerLocks, "!cm_enableServerLocks");
4932 scp = fileLock->scp;
4933 osi_assertx(scp != NULL, "null cm_scache_t");
4935 cm_HoldSCacheNoLock(scp);
4938 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
4939 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
4940 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4942 fileLock->fid.volume,
4943 fileLock->fid.vnode,
4944 fileLock->fid.unique);
4945 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4946 fileLock->scp->fid.cell,
4947 fileLock->scp->fid.volume,
4948 fileLock->scp->fid.vnode,
4949 fileLock->scp->fid.unique);
4950 osi_assertx(FALSE, "invalid fid");
4953 /* Server locks are extended once per scp per refresh
4955 if (scp->lastRefreshCycle != cm_lockRefreshCycle) {
4957 int scp_done = FALSE;
4959 osi_Log1(afsd_logp, "cm_CheckLocks Updating scp 0x%x", scp);
4961 lock_ReleaseWrite(&cm_scacheLock);
4962 lock_ObtainWrite(&scp->rw);
4964 /* did the lock change while we weren't holding the lock? */
4965 if (!IS_LOCK_ACTIVE(fileLock))
4966 goto post_syncopdone;
4968 code = cm_SyncOp(scp, NULL, fileLock->userp, &req, 0,
4969 CM_SCACHESYNC_NEEDCALLBACK
4970 | CM_SCACHESYNC_GETSTATUS
4971 | CM_SCACHESYNC_LOCK);
4975 "cm_CheckLocks SyncOp failure code 0x%x", code);
4976 goto post_syncopdone;
4979 /* cm_SyncOp releases scp->rw during which the lock
4980 may get released. */
4981 if (!IS_LOCK_ACTIVE(fileLock))
4982 goto pre_syncopdone;
4984 if (scp->serverLock != -1 && !(scp->flags & CM_SCACHEFLAG_DELETED)) {
4988 tfid.Volume = scp->fid.volume;
4989 tfid.Vnode = scp->fid.vnode;
4990 tfid.Unique = scp->fid.unique;
4992 userp = fileLock->userp;
4994 osi_Log3(afsd_logp, "CALL ExtendLock lock 0x%p for scp=0x%p with lock %d",
4997 (int) scp->serverLock);
4999 lock_ReleaseWrite(&scp->rw);
5002 code = cm_ConnFromFID(&cfid, userp,
5007 callp = cm_GetRxConn(connp);
5008 code = RXAFS_ExtendLock(callp, &tfid,
5010 rx_PutConnection(callp);
5012 osi_Log1(afsd_logp, " ExtendLock returns %d", code);
5014 } while (cm_Analyze(connp, userp, &req,
5015 &cfid, &volSync, NULL, NULL,
5018 code = cm_MapRPCError(code, &req);
5020 lock_ObtainWrite(&scp->rw);
5023 osi_Log1(afsd_logp, "CALL ExtendLock FAILURE, code 0x%x", code);
5025 osi_Log0(afsd_logp, "CALL ExtendLock SUCCESS");
5026 scp->lockDataVersion = scp->dataVersion;
5029 if ((code == EINVAL || code == CM_ERROR_INVAL) &&
5030 scp->lockDataVersion == scp->dataVersion) {
5034 (scp->exclusiveLocks > 0) ? LockWrite: LockRead;
5036 /* we might still have a chance to obtain a
5039 code = cm_IntSetLock(scp, userp, lockType, &req);
5042 code = CM_ERROR_INVAL;
5043 } else if (scp->lockDataVersion != scp->dataVersion) {
5045 /* now check if we still have the file at
5046 the right data version. */
5048 "Data version mismatch on scp 0x%p",
5051 " Data versions: before=%I64d, after=%I64d",
5052 scp->lockDataVersion,
5055 code = cm_IntReleaseLock(scp, userp, &req);
5057 code = CM_ERROR_INVAL;
5061 if (code == EINVAL || code == CM_ERROR_INVAL ||
5062 code == CM_ERROR_BADFD) {
5063 cm_LockMarkSCacheLost(scp);
5067 /* interestingly, we have found an active lock
5068 belonging to an scache that has no
5070 cm_LockMarkSCacheLost(scp);
5077 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5080 lock_ReleaseWrite(&scp->rw);
5082 lock_ObtainWrite(&cm_scacheLock);
5085 fileLock->lastUpdate = time(NULL);
5089 scp->lastRefreshCycle = cm_lockRefreshCycle;
5092 /* we have already refreshed the locks on this scp */
5093 fileLock->lastUpdate = time(NULL);
5096 cm_ReleaseSCacheNoLock(scp);
5098 } else if (IS_LOCK_ACTIVE(fileLock) && IS_LOCK_CLIENTONLY(fileLock)) {
5099 /* TODO: Check callbacks */
5103 lock_ReleaseWrite(&cm_scacheLock);
5104 osi_Log1(afsd_logp, "cm_CheckLocks completes lock check cycle %d", cm_lockRefreshCycle);
5107 /* NOT called with scp->rw held. */
5108 long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
5111 cm_scache_t *scp = NULL;
5112 cm_file_lock_t *fileLock;
5116 int force_client_lock = FALSE;
5117 int has_insert = FALSE;
5118 int check_data_version = FALSE;
5122 if (client_is_dead) {
5123 code = CM_ERROR_TIMEDOUT;
5127 lock_ObtainRead(&cm_scacheLock);
5129 osi_Log2(afsd_logp, "cm_RetryLock checking lock %p (scp=%p)", oldFileLock, oldFileLock->scp);
5130 osi_Log4(afsd_logp, " offset(%x:%x) length(%x:%x)",
5131 (unsigned)(oldFileLock->range.offset >> 32),
5132 (unsigned)(oldFileLock->range.offset & 0xffffffff),
5133 (unsigned)(oldFileLock->range.length >> 32),
5134 (unsigned)(oldFileLock->range.length & 0xffffffff));
5135 osi_Log3(afsd_logp, " key(%x:%x) flags=%x",
5136 (unsigned)(oldFileLock->key >> 32),
5137 (unsigned)(oldFileLock->key & 0xffffffff),
5138 (unsigned)(oldFileLock->flags));
5140 /* if the lock has already been granted, then we have nothing to do */
5141 if (IS_LOCK_ACTIVE(oldFileLock)) {
5142 lock_ReleaseRead(&cm_scacheLock);
5143 osi_Log0(afsd_logp, "cm_RetryLock lock already granted");
5147 /* we can't do anything with lost or deleted locks at the moment. */
5148 if (IS_LOCK_LOST(oldFileLock) || IS_LOCK_DELETED(oldFileLock)) {
5149 code = CM_ERROR_BADFD;
5150 osi_Log0(afsd_logp, "cm_RetryLock lock is lost or deleted");
5151 lock_ReleaseRead(&cm_scacheLock);
5155 scp = oldFileLock->scp;
5157 osi_assertx(scp != NULL, "null cm_scache_t");
5159 lock_ReleaseRead(&cm_scacheLock);
5160 lock_ObtainWrite(&scp->rw);
5162 code = cm_LockCheckPerms(scp, oldFileLock->lockType,
5166 if (code == CM_ERROR_NOACCESS && oldFileLock->lockType == LockRead) {
5167 if (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp)) {
5168 force_client_lock = TRUE;
5172 lock_ReleaseWrite(&scp->rw);
5176 lock_ObtainWrite(&cm_scacheLock);
5178 /* Check if we already have a sufficient server lock to allow this
5179 lock to go through. */
5180 if (IS_LOCK_WAITLOCK(oldFileLock) &&
5181 (!SERVERLOCKS_ENABLED(scp) ||
5182 scp->serverLock == oldFileLock->lockType ||
5183 scp->serverLock == LockWrite)) {
5185 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5187 if (SERVERLOCKS_ENABLED(scp)) {
5188 osi_Log1(afsd_logp, "cm_RetryLock Server lock (%d) is sufficient for lock. Granting",
5189 (int) scp->serverLock);
5191 osi_Log0(afsd_logp, "cm_RetryLock skipping server lock for scp");
5194 lock_ReleaseWrite(&cm_scacheLock);
5195 lock_ReleaseWrite(&scp->rw);
5200 if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5202 /* check if the conflicting locks have dissappeared already */
5203 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5205 fileLock = (cm_file_lock_t *)
5206 ((char *) q - offsetof(cm_file_lock_t, fileq));
5208 if (IS_LOCK_LOST(fileLock)) {
5209 if (fileLock->key == oldFileLock->key) {
5210 code = CM_ERROR_BADFD;
5211 oldFileLock->flags |= CM_FILELOCK_FLAG_LOST;
5212 osi_Log1(afsd_logp, " found lost lock %p for same key. Marking lock as lost",
5215 } else if (fileLock->lockType == LockWrite &&
5216 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5217 osi_Log1(afsd_logp, " found conflicting LOST lock %p", fileLock);
5218 code = CM_ERROR_WOULDBLOCK;
5223 if (IS_LOCK_ACCEPTED(fileLock) &&
5224 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5226 if (oldFileLock->lockType != LockRead ||
5227 fileLock->lockType != LockRead) {
5229 osi_Log1(afsd_logp, " found conflicting lock %p", fileLock);
5230 code = CM_ERROR_WOULDBLOCK;
5238 lock_ReleaseWrite(&cm_scacheLock);
5239 lock_ReleaseWrite(&scp->rw);
5244 /* when we get here, the lock is either a WAITUNLOCK or WAITLOCK.
5245 If it is WAITUNLOCK, then we didn't find any conflicting lock
5246 but we haven't verfied whether the serverLock is sufficient to
5247 assert it. If it is WAITLOCK, then the serverLock is
5248 insufficient to assert it. Eitherway, we are ready to accept
5249 the lock as either ACTIVE or WAITLOCK depending on the
5252 /* First, promote the WAITUNLOCK to a WAITLOCK */
5253 if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5254 if (oldFileLock->lockType == LockRead)
5257 scp->exclusiveLocks++;
5259 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITUNLOCK;
5260 oldFileLock->flags |= CM_FILELOCK_FLAG_WAITLOCK;
5263 osi_assertx(IS_LOCK_WAITLOCK(oldFileLock), "!IS_LOCK_WAITLOCK");
5265 if (force_client_lock ||
5266 !SERVERLOCKS_ENABLED(scp) ||
5267 scp->serverLock == oldFileLock->lockType ||
5268 (oldFileLock->lockType == LockRead &&
5269 scp->serverLock == LockWrite)) {
5271 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5273 if ((force_client_lock ||
5274 !SERVERLOCKS_ENABLED(scp)) &&
5275 !IS_LOCK_CLIENTONLY(oldFileLock)) {
5277 oldFileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
5279 if (oldFileLock->lockType == LockRead)
5282 scp->exclusiveLocks--;
5287 lock_ReleaseWrite(&cm_scacheLock);
5288 lock_ReleaseWrite(&scp->rw);
5295 code = cm_SyncOp(scp, NULL, oldFileLock->userp, &req, 0,
5296 CM_SCACHESYNC_NEEDCALLBACK
5297 | CM_SCACHESYNC_GETSTATUS
5298 | CM_SCACHESYNC_LOCK);
5300 osi_Log1(smb_logp, "cm_RetryLock SyncOp failure code 0x%x", code);
5301 lock_ReleaseWrite(&cm_scacheLock);
5302 goto post_syncopdone;
5305 if (!IS_LOCK_WAITLOCK(oldFileLock))
5306 goto pre_syncopdone;
5308 userp = oldFileLock->userp;
5310 #ifndef AGGRESSIVE_LOCKS
5311 newLock = oldFileLock->lockType;
5313 newLock = LockWrite;
5317 /* if has_insert is non-zero, then:
5318 - the lock a LockRead
5319 - we don't have permission to get a LockRead
5320 - we do have permission to get a LockWrite
5321 - the server supports VICED_CAPABILITY_WRITELOCKACL
5324 newLock = LockWrite;
5327 lock_ReleaseWrite(&cm_scacheLock);
5329 /* when we get here, either we have a read-lock and want a
5330 write-lock or we don't have any locks and we want some
5333 if (scp->serverLock == LockRead) {
5335 osi_assertx(newLock == LockWrite, "!LockWrite");
5337 osi_Log0(afsd_logp, " Attempting to UPGRADE from LockRead to LockWrite");
5339 scp->lockDataVersion = scp->dataVersion;
5340 check_data_version = TRUE;
5342 code = cm_IntReleaseLock(scp, userp, &req);
5345 goto pre_syncopdone;
5347 scp->serverLock = -1;
5350 code = cm_IntSetLock(scp, userp, newLock, &req);
5353 if (scp->dataVersion != scp->lockDataVersion) {
5354 /* we lost a race. too bad */
5357 " Data version mismatch while upgrading lock.");
5359 " Data versions before=%I64d, after=%I64d",
5360 scp->lockDataVersion,
5363 " Releasing stale lock for scp 0x%x", scp);
5365 code = cm_IntReleaseLock(scp, userp, &req);
5367 scp->serverLock = -1;
5369 code = CM_ERROR_INVAL;
5371 cm_LockMarkSCacheLost(scp);
5373 scp->serverLock = newLock;
5378 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5384 if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
5385 lock_ObtainWrite(&cm_scacheLock);
5386 if (scp->fileLocksT == &oldFileLock->fileq)
5387 scp->fileLocksT = osi_QPrev(&oldFileLock->fileq);
5388 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, &oldFileLock->fileq);
5389 lock_ReleaseWrite(&cm_scacheLock);
5391 lock_ReleaseWrite(&scp->rw);
5394 lock_ObtainWrite(&cm_scacheLock);
5396 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5397 } else if (code != CM_ERROR_WOULDBLOCK) {
5398 oldFileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5399 cm_ReleaseUser(oldFileLock->userp);
5400 oldFileLock->userp = NULL;
5401 if (oldFileLock->scp) {
5402 cm_ReleaseSCacheNoLock(oldFileLock->scp);
5403 oldFileLock->scp = NULL;
5406 lock_ReleaseWrite(&cm_scacheLock);
5411 cm_key_t cm_GenerateKey(unsigned int session_id, unsigned long process_id, unsigned int file_id)
5414 osi_assertx((process_id & 0xffffffff) == process_id, "unexpected process_id");
5415 osi_assertx((session_id & 0xffff) == session_id, "unexpected session_id");
5416 osi_assertx((file_id & 0xffff) == file_id, "unexpected file_id");
5420 (((cm_key_t) (process_id & 0xffffffff)) << 32) |
5421 (((cm_key_t) (session_id & 0xffff)) << 16) |
5422 (((cm_key_t) (file_id & 0xffff)));
5425 static int cm_KeyEquals(cm_key_t k1, cm_key_t k2, int flags)
5427 if (flags & CM_UNLOCK_BY_FID) {
5428 return ((k1 & 0xffffffff) == (k2 & 0xffffffff));
5434 void cm_ReleaseAllLocks(void)
5440 cm_file_lock_t *fileLock;
5443 for (i = 0; i < cm_data.scacheHashTableSize; i++)
5445 for ( scp = cm_data.scacheHashTablep[i]; scp; scp = scp->nextp ) {
5446 while (scp->fileLocksH != NULL) {
5447 lock_ObtainWrite(&scp->rw);
5448 lock_ObtainWrite(&cm_scacheLock);
5449 if (!scp->fileLocksH) {
5450 lock_ReleaseWrite(&cm_scacheLock);
5451 lock_ReleaseWrite(&scp->rw);
5454 fileLock = (cm_file_lock_t *)((char *) scp->fileLocksH - offsetof(cm_file_lock_t, fileq));
5455 userp = fileLock->userp;
5457 key = fileLock->key;
5458 cm_HoldSCacheNoLock(scp);
5459 lock_ReleaseWrite(&cm_scacheLock);
5460 cm_UnlockByKey(scp, key, 0, userp, &req);
5461 cm_ReleaseSCache(scp);
5462 cm_ReleaseUser(userp);
5463 lock_ReleaseWrite(&scp->rw);