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 /* Used by cm_FollowMountPoint */
33 extern void afsi_log(char *pattern, ...);
36 int cm_enableServerLocks = 1;
39 * Case-folding array. This was constructed by inspecting of SMBtrace output.
40 * I do not know anything more about it.
42 unsigned char cm_foldUpper[256] = {
43 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
44 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
45 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
46 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
47 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
48 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
49 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
50 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
51 0x40, 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, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
55 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
56 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
57 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
58 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
59 0x80, 0x9a, 0x90, 0x41, 0x8e, 0x41, 0x8f, 0x80,
60 0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x8e, 0x8f,
61 0x90, 0x92, 0x92, 0x4f, 0x99, 0x4f, 0x55, 0x55,
62 0x59, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
63 0x41, 0x49, 0x4f, 0x55, 0xa5, 0xa5, 0x56, 0xa7,
64 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
65 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
66 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
67 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
68 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
69 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
70 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
71 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
72 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
73 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
74 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
78 * Case-insensitive string comparison. We used to use stricmp, but it doesn't
79 * know about 8-bit characters (e.g. 129 is lowercase u-umlaut, 154 is
80 * upper-case u-umlaut).
82 int cm_stricmp(const char *str1, const char *str2)
94 c1 = (char) cm_foldUpper[(unsigned char)(*str1++)];
95 c2 = (char) cm_foldUpper[(unsigned char)(*str2++)];
103 /* characters that are legal in an 8.3 name */
105 * We used to have 1's for all characters from 128 to 254. But
106 * the NT client behaves better if we create an 8.3 name for any
107 * name that has a character with the high bit on, and if we
108 * delete those characters from 8.3 names. In particular, see
109 * Sybase defect 10859.
111 char cm_LegalChars[256] = {
112 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
113 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
114 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0,
115 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
116 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
117 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1,
118 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
119 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1,
120 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
121 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
122 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
123 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
124 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
125 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
126 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
127 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
130 /* return true iff component is a valid 8.3 name */
131 int cm_Is8Dot3(char *namep)
138 * can't have a leading dot;
139 * special case for . and ..
141 if (namep[0] == '.') {
144 if (namep[1] == '.' && namep[2] == 0)
148 while (tc = *namep++) {
150 /* saw another dot */
151 if (sawDot) return 0; /* second dot */
156 if (cm_LegalChars[tc] == 0)
159 if (!sawDot && charCount > 8)
160 /* more than 8 chars in name */
162 if (sawDot && charCount > 3)
163 /* more than 3 chars in extension */
170 * Number unparsing map for generating 8.3 names;
171 * The version taken from DFS was on drugs.
172 * You can't include '&' and '@' in a file name.
174 char cm_8Dot3Mapping[42] =
175 {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
176 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K',
177 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U',
178 'V', 'W', 'X', 'Y', 'Z', '_', '-', '$', '#', '!', '+', '='
180 int cm_8Dot3MapSize = sizeof(cm_8Dot3Mapping);
182 void cm_Gen8Dot3Name(cm_dirEntry_t *dep, char *shortName, char **shortNameEndp)
186 int vnode = ntohl(dep->fid.vnode);
188 int validExtension = 0;
189 char tc, *temp, *name;
191 /* Unparse the file's vnode number to get a "uniquifier" */
193 number[nsize] = cm_8Dot3Mapping[vnode % cm_8Dot3MapSize];
195 vnode /= cm_8Dot3MapSize;
199 * Look for valid extension. There has to be a dot, and
200 * at least one of the characters following has to be legal.
202 lastDot = strrchr(dep->name, '.');
204 temp = lastDot; temp++;
206 if (cm_LegalChars[tc])
212 /* Copy name characters */
214 for (i = 0, name = dep->name;
215 i < (7 - nsize) && name != lastDot; ) {
220 if (!cm_LegalChars[tc])
223 *shortName++ = toupper(tc);
229 /* Copy uniquifier characters */
230 memcpy(shortName, number, nsize);
233 if (validExtension) {
234 /* Copy extension characters */
235 *shortName++ = *lastDot++; /* copy dot */
236 for (i = 0, tc = *lastDot++;
239 if (cm_LegalChars[tc]) {
241 *shortName++ = toupper(tc);
250 *shortNameEndp = shortName;
253 /* return success if we can open this file in this mode */
254 long cm_CheckOpen(cm_scache_t *scp, int openMode, int trunc, cm_user_t *userp,
261 if (openMode != 1) rights |= PRSFS_READ;
262 if (openMode == 1 || openMode == 2 || trunc) rights |= PRSFS_WRITE;
264 lock_ObtainMutex(&scp->mx);
266 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
267 CM_SCACHESYNC_GETSTATUS
268 | CM_SCACHESYNC_NEEDCALLBACK
269 | CM_SCACHESYNC_LOCK);
272 ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
273 scp->fileType == CM_SCACHETYPE_FILE) {
276 unsigned int sLockType;
277 LARGE_INTEGER LOffset, LLength;
279 /* Check if there's some sort of lock on the file at the
282 key = cm_GenerateKey(CM_SESSION_CMINT,0,0);
284 if (rights & PRSFS_WRITE)
287 sLockType = LOCKING_ANDX_SHARED_LOCK;
289 LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
290 LOffset.LowPart = CM_FLSHARE_OFFSET_LOW;
291 LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
292 LLength.LowPart = CM_FLSHARE_LENGTH_LOW;
294 code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
297 cm_Unlock(scp, sLockType, LOffset, LLength, key, userp, reqp);
299 /* In this case, we allow the file open to go through even
300 though we can't enforce mandatory locking on the
302 if (code == CM_ERROR_NOACCESS &&
303 !(rights & PRSFS_WRITE))
307 case CM_ERROR_ALLOFFLINE:
308 case CM_ERROR_ALLDOWN:
309 case CM_ERROR_ALLBUSY:
310 case CM_ERROR_TIMEDOUT:
312 case CM_ERROR_WOULDBLOCK:
315 code = CM_ERROR_SHARING_VIOLATION;
320 } else if (code != 0) {
324 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
328 lock_ReleaseMutex(&scp->mx);
333 /* return success if we can open this file in this mode */
334 long cm_CheckNTOpen(cm_scache_t *scp, unsigned int desiredAccess,
335 unsigned int createDisp, cm_user_t *userp, cm_req_t *reqp)
340 /* Always allow delete; the RPC will tell us if it's OK */
341 if (desiredAccess == DELETE)
346 if (desiredAccess & AFS_ACCESS_READ)
347 rights |= PRSFS_READ;
349 if ((desiredAccess & AFS_ACCESS_WRITE)
351 rights |= PRSFS_WRITE;
353 lock_ObtainMutex(&scp->mx);
355 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
356 CM_SCACHESYNC_GETSTATUS
357 | CM_SCACHESYNC_NEEDCALLBACK
358 | CM_SCACHESYNC_LOCK);
361 * If the open will fail because the volume is readonly, then we will
362 * return an access denied error instead. This is to help brain-dead
363 * apps run correctly on replicated volumes.
364 * See defect 10007 for more information.
366 if (code == CM_ERROR_READONLY)
367 code = CM_ERROR_NOACCESS;
370 ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
371 scp->fileType == CM_SCACHETYPE_FILE) {
373 unsigned int sLockType;
374 LARGE_INTEGER LOffset, LLength;
376 /* Check if there's some sort of lock on the file at the
379 key = cm_GenerateKey(CM_SESSION_CMINT,0,0);
380 if (rights & PRSFS_WRITE)
383 sLockType = LOCKING_ANDX_SHARED_LOCK;
385 /* single byte lock at offset 0x0100 0000 0000 0000 */
386 LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
387 LOffset.LowPart = CM_FLSHARE_OFFSET_LOW;
388 LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
389 LLength.LowPart = CM_FLSHARE_LENGTH_LOW;
391 code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
394 cm_Unlock(scp, sLockType, LOffset, LLength, key, userp, reqp);
396 /* In this case, we allow the file open to go through even
397 though we can't enforce mandatory locking on the
399 if (code == CM_ERROR_NOACCESS &&
400 !(rights & PRSFS_WRITE))
404 case CM_ERROR_ALLOFFLINE:
405 case CM_ERROR_ALLDOWN:
406 case CM_ERROR_ALLBUSY:
407 case CM_ERROR_TIMEDOUT:
409 case CM_ERROR_WOULDBLOCK:
412 code = CM_ERROR_SHARING_VIOLATION;
416 } else if (code != 0) {
420 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
423 lock_ReleaseMutex(&scp->mx);
429 * When CAP_NT_SMBS has been negotiated, deletion (of files or directories) is
430 * done in three steps:
431 * (1) open for deletion (NT_CREATE_AND_X)
432 * (2) set for deletion on close (NT_TRANSACTION2, SET_FILE_INFO)
434 * We must not do the RPC until step 3. But if we are going to return an error
435 * code (e.g. directory not empty), we must return it by step 2, otherwise most
436 * clients will not notice it. So we do a preliminary check. For deleting
437 * files, this is almost free, since we have already done the RPC to get the
438 * parent directory's status bits. But for deleting directories, we must do an
439 * additional RPC to get the directory's data to check if it is empty. Sigh.
441 long cm_CheckNTDelete(cm_scache_t *dscp, cm_scache_t *scp, cm_user_t *userp,
448 unsigned short *hashTable;
450 int BeyondPage = 0, HaveDot = 0, HaveDotDot = 0;
452 /* First check permissions */
453 lock_ObtainMutex(&dscp->mx);
454 code = cm_SyncOp(dscp, NULL, userp, reqp, PRSFS_DELETE,
455 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
456 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
457 lock_ReleaseMutex(&dscp->mx);
461 /* If deleting directory, must be empty */
463 if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
466 thyper.HighPart = 0; thyper.LowPart = 0;
467 lock_ObtainRead(&scp->bufCreateLock);
468 code = buf_Get(scp, &thyper, &bufferp);
469 lock_ReleaseRead(&scp->bufCreateLock);
473 lock_ObtainMutex(&bufferp->mx);
474 lock_ObtainMutex(&scp->mx);
476 code = cm_SyncOp(scp, bufferp, userp, reqp, 0,
477 CM_SCACHESYNC_NEEDCALLBACK
479 | CM_SCACHESYNC_BUFLOCKED);
483 if (cm_HaveBuffer(scp, bufferp, 1))
486 /* otherwise, load the buffer and try again */
487 lock_ReleaseMutex(&bufferp->mx);
488 code = cm_GetBuffer(scp, bufferp, NULL, userp, reqp);
489 lock_ReleaseMutex(&scp->mx);
490 lock_ObtainMutex(&bufferp->mx);
491 lock_ObtainMutex(&scp->mx);
492 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
497 /* We try to determine emptiness without looking beyond the first page,
498 * and without assuming "." and ".." are present and are on the first
499 * page (though these assumptions might, after all, be reasonable).
501 hashTable = (unsigned short *)(bufferp->datap + (32 * 5));
502 for (i=0; i<128; i++) {
503 idx = ntohs(hashTable[i]);
509 dep = (cm_dirEntry_t *)(bufferp->datap + (32 * idx));
510 if (strcmp(dep->name, ".") == 0)
512 else if (strcmp(dep->name, "..") == 0)
515 code = CM_ERROR_NOTEMPTY;
518 idx = ntohs(dep->next);
521 if (BeyondPage && HaveDot && HaveDotDot)
522 code = CM_ERROR_NOTEMPTY;
526 lock_ReleaseMutex(&bufferp->mx);
527 buf_Release(bufferp);
528 lock_ReleaseMutex(&scp->mx);
533 * Iterate through all entries in a directory.
534 * When the function funcp is called, the buffer is locked but the
535 * directory vnode is not.
537 * If the retscp parameter is not NULL, the parmp must be a
538 * cm_lookupSearch_t object.
540 long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
541 osi_hyper_t *startOffsetp, cm_user_t *userp, cm_req_t *reqp,
542 cm_scache_t **retscp)
549 osi_hyper_t dirLength;
550 osi_hyper_t bufferOffset;
551 osi_hyper_t curOffset;
555 cm_pageHeader_t *pageHeaderp;
557 long nextEntryCookie;
558 int numDirChunks; /* # of 32 byte dir chunks in this entry */
560 /* get the directory size */
561 lock_ObtainMutex(&scp->mx);
562 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_LOOKUP,
563 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
565 lock_ReleaseMutex(&scp->mx);
569 if (scp->fileType != CM_SCACHETYPE_DIRECTORY) {
570 lock_ReleaseMutex(&scp->mx);
571 return CM_ERROR_NOTDIR;
574 if (retscp) /* if this is a lookup call */
576 cm_lookupSearch_t* sp = parmp;
578 #ifdef AFS_FREELANCE_CLIENT
579 /* Freelance entries never end up in the DNLC because they
580 * do not have an associated cm_server_t
582 if ( !(cm_freelanceEnabled &&
583 sp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
584 sp->fid.volume==AFS_FAKE_ROOT_VOL_ID ) )
585 #endif /* AFS_FREELANCE_CLIENT */
587 int casefold = sp->caseFold;
588 sp->caseFold = 0; /* we have a strong preference for exact matches */
589 if ( *retscp = cm_dnlcLookup(scp, sp)) /* dnlc hit */
591 sp->caseFold = casefold;
592 lock_ReleaseMutex(&scp->mx);
595 sp->caseFold = casefold;
600 * XXX We only get the length once. It might change when we drop the
603 dirLength = scp->length;
605 lock_ReleaseMutex(&scp->mx);
608 bufferOffset.LowPart = bufferOffset.HighPart = 0;
610 curOffset = *startOffsetp;
612 curOffset.HighPart = 0;
613 curOffset.LowPart = 0;
617 /* make sure that curOffset.LowPart doesn't point to the first
618 * 32 bytes in the 2nd through last dir page, and that it
619 * doesn't point at the first 13 32-byte chunks in the first
620 * dir page, since those are dir and page headers, and don't
621 * contain useful information.
623 temp = curOffset.LowPart & (2048-1);
624 if (curOffset.HighPart == 0 && curOffset.LowPart < 2048) {
625 /* we're in the first page */
626 if (temp < 13*32) temp = 13*32;
629 /* we're in a later dir page */
630 if (temp < 32) temp = 32;
633 /* make sure the low order 5 bits are zero */
636 /* now put temp bits back ito curOffset.LowPart */
637 curOffset.LowPart &= ~(2048-1);
638 curOffset.LowPart |= temp;
640 /* check if we've passed the dir's EOF */
641 if (LargeIntegerGreaterThanOrEqualTo(curOffset, dirLength))
644 /* see if we can use the bufferp we have now; compute in which
645 * page the current offset would be, and check whether that's
646 * the offset of the buffer we have. If not, get the buffer.
648 thyper.HighPart = curOffset.HighPart;
649 thyper.LowPart = curOffset.LowPart & ~(cm_data.buf_blockSize-1);
650 if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
653 lock_ReleaseMutex(&bufferp->mx);
654 buf_Release(bufferp);
658 lock_ObtainRead(&scp->bufCreateLock);
659 code = buf_Get(scp, &thyper, &bufferp);
660 lock_ReleaseRead(&scp->bufCreateLock);
662 /* if buf_Get() fails we do not have a buffer object to lock */
668 /* for the IFS version, we bulkstat the dirents because this
669 routine is used in place of smb_ReceiveCoreSearchDir. our
670 other option is to modify smb_ReceiveCoreSearchDir itself,
671 but this seems to be the proper use for cm_ApplyDir. */
672 lock_ObtainMutex(&scp->mx);
673 if ((scp->flags & CM_SCACHEFLAG_BULKSTATTING) == 0
674 && (scp->bulkStatProgress.QuadPart <= thyper.QuadPart))
676 scp->flags |= CM_SCACHEFLAG_BULKSTATTING;
677 code = cm_TryBulkStat(scp, &thyper, userp, reqp);
678 scp->flags &= ~CM_SCACHEFLAG_BULKSTATTING;
679 scp->bulkStatProgress = thyper;
681 lock_ReleaseMutex(&scp->mx);
684 lock_ObtainMutex(&bufferp->mx);
685 bufferOffset = thyper;
687 /* now get the data in the cache */
689 lock_ObtainMutex(&scp->mx);
690 code = cm_SyncOp(scp, bufferp, userp, reqp,
692 CM_SCACHESYNC_NEEDCALLBACK
694 | CM_SCACHESYNC_BUFLOCKED);
696 lock_ReleaseMutex(&scp->mx);
699 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
701 if (cm_HaveBuffer(scp, bufferp, 1)) {
702 lock_ReleaseMutex(&scp->mx);
706 /* otherwise, load the buffer and try again */
707 lock_ReleaseMutex(&bufferp->mx);
708 code = cm_GetBuffer(scp, bufferp, NULL, userp,
710 lock_ReleaseMutex(&scp->mx);
711 lock_ObtainMutex(&bufferp->mx);
716 lock_ReleaseMutex(&bufferp->mx);
717 buf_Release(bufferp);
721 } /* if (wrong buffer) ... */
723 /* now we have the buffer containing the entry we're interested
724 * in; copy it out if it represents a non-deleted entry.
726 entryInDir = curOffset.LowPart & (2048-1);
727 entryInBuffer = curOffset.LowPart & (cm_data.buf_blockSize - 1);
729 /* page header will help tell us which entries are free. Page
730 * header can change more often than once per buffer, since
731 * AFS 3 dir page size may be less than (but not more than) a
732 * buffer package buffer.
734 /* only look intra-buffer */
735 temp = curOffset.LowPart & (cm_data.buf_blockSize - 1);
736 temp &= ~(2048 - 1); /* turn off intra-page bits */
737 pageHeaderp = (cm_pageHeader_t *) (bufferp->datap + temp);
739 /* now determine which entry we're looking at in the page. If
740 * it is free (there's a free bitmap at the start of the dir),
741 * we should skip these 32 bytes.
743 slotInPage = (entryInDir & 0x7e0) >> 5;
744 if (!(pageHeaderp->freeBitmap[slotInPage>>3]
745 & (1 << (slotInPage & 0x7)))) {
746 /* this entry is free */
747 numDirChunks = 1; /* only skip this guy */
751 tp = bufferp->datap + entryInBuffer;
752 dep = (cm_dirEntry_t *) tp; /* now points to AFS3 dir entry */
754 /* while we're here, compute the next entry's location, too,
755 * since we'll need it when writing out the cookie into the
756 * dir listing stream.
758 numDirChunks = cm_NameEntries(dep->name, NULL);
760 /* compute the offset of the cookie representing the next entry */
761 nextEntryCookie = curOffset.LowPart
762 + (CM_DIR_CHUNKSIZE * numDirChunks);
764 if (dep->fid.vnode != 0) {
765 /* this is one of the entries to use: it is not deleted */
766 code = (*funcp)(scp, dep, parmp, &curOffset);
769 } /* if we're including this name */
772 /* and adjust curOffset to be where the new cookie is */
774 thyper.LowPart = CM_DIR_CHUNKSIZE * numDirChunks;
775 curOffset = LargeIntegerAdd(thyper, curOffset);
776 } /* while copying data for dir listing */
778 /* release the mutex */
780 lock_ReleaseMutex(&bufferp->mx);
781 buf_Release(bufferp);
786 int cm_NoneUpper(char *s)
790 if (c >= 'A' && c <= 'Z')
795 int cm_NoneLower(char *s)
799 if (c >= 'a' && c <= 'z')
804 long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
807 cm_lookupSearch_t *sp;
812 sp = (cm_lookupSearch_t *) rockp;
814 matchName = dep->name;
816 match = cm_stricmp(matchName, sp->searchNamep);
818 match = strcmp(matchName, sp->searchNamep);
822 && !cm_Is8Dot3(dep->name)) {
823 matchName = shortName;
824 cm_Gen8Dot3Name(dep, shortName, NULL);
826 match = cm_stricmp(matchName, sp->searchNamep);
828 match = strcmp(matchName, sp->searchNamep);
838 if (!sp->caseFold || matchName == shortName) {
839 sp->fid.vnode = ntohl(dep->fid.vnode);
840 sp->fid.unique = ntohl(dep->fid.unique);
841 return CM_ERROR_STOPNOW;
845 * If we get here, we are doing a case-insensitive search, and we
846 * have found a match. Now we determine what kind of match it is:
847 * exact, lower-case, upper-case, or none of the above. This is done
848 * in order to choose among matches, if there are more than one.
851 /* Exact matches are the best. */
852 match = strcmp(matchName, sp->searchNamep);
855 sp->fid.vnode = ntohl(dep->fid.vnode);
856 sp->fid.unique = ntohl(dep->fid.unique);
857 return CM_ERROR_STOPNOW;
860 /* Lower-case matches are next. */
863 if (cm_NoneUpper(matchName)) {
868 /* Upper-case matches are next. */
871 if (cm_NoneLower(matchName)) {
876 /* General matches are last. */
882 sp->fid.vnode = ntohl(dep->fid.vnode);
883 sp->fid.unique = ntohl(dep->fid.unique);
887 /* read the contents of a mount point into the appropriate string.
888 * called with locked scp, and returns with locked scp.
890 long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
897 if (scp->mountPointStringp[0])
900 /* otherwise, we have to read it in */
901 lock_ReleaseMutex(&scp->mx);
903 lock_ObtainRead(&scp->bufCreateLock);
904 thyper.LowPart = thyper.HighPart = 0;
905 code = buf_Get(scp, &thyper, &bufp);
906 lock_ReleaseRead(&scp->bufCreateLock);
908 lock_ObtainMutex(&scp->mx);
913 code = cm_SyncOp(scp, bufp, userp, reqp, 0,
914 CM_SCACHESYNC_READ | CM_SCACHESYNC_NEEDCALLBACK);
918 cm_SyncOpDone(scp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
921 if (cm_HaveBuffer(scp, bufp, 0))
924 /* otherwise load buffer */
925 code = cm_GetBuffer(scp, bufp, NULL, userp, reqp);
930 /* locked, has callback, has valid data in buffer */
931 if ((tlen = scp->length.LowPart) > 1000)
932 return CM_ERROR_TOOBIG;
934 code = CM_ERROR_INVAL;
938 /* someone else did the work while we were out */
939 if (scp->mountPointStringp[0]) {
944 /* otherwise, copy out the link */
945 memcpy(scp->mountPointStringp, bufp->datap, tlen);
947 /* now make it null-terminated. Note that the original contents of a
948 * link that is a mount point is "#volname." where "." is there just to
949 * be turned into a null. That is, we can trash the last char of the
950 * link without damaging the vol name. This is a stupid convention,
951 * but that's the protocol.
953 scp->mountPointStringp[tlen-1] = 0;
962 /* called with a locked scp and chases the mount point, yielding outScpp.
963 * scp remains locked, just for simplicity of describing the interface.
965 long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
966 cm_req_t *reqp, cm_scache_t **outScpp)
981 if (scp->mountRootFid.cell != 0 && scp->mountRootGen >= cm_data.mountRootGen) {
982 tfid = scp->mountRootFid;
983 lock_ReleaseMutex(&scp->mx);
984 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
985 lock_ObtainMutex(&scp->mx);
989 /* parse the volume name */
990 mpNamep = scp->mountPointStringp;
992 return CM_ERROR_NOSUCHPATH;
993 tlen = (int)strlen(scp->mountPointStringp);
994 mtType = *scp->mountPointStringp;
995 cellNamep = malloc(tlen);
996 volNamep = malloc(tlen);
998 cp = strrchr(mpNamep, ':');
1000 /* cellular mount point */
1001 memset(cellNamep, 0, tlen);
1002 strncpy(cellNamep, mpNamep+1, cp - mpNamep - 1);
1003 strcpy(volNamep, cp+1);
1004 /* now look up the cell */
1005 cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE);
1009 strcpy(volNamep, mpNamep+1);
1011 cellp = cm_FindCellByID(scp->fid.cell);
1015 code = CM_ERROR_NOSUCHCELL;
1019 vnLength = strlen(volNamep);
1020 if (vnLength >= 8 && strcmp(volNamep + vnLength - 7, ".backup") == 0)
1022 else if (vnLength >= 10
1023 && strcmp(volNamep + vnLength - 9, ".readonly") == 0)
1028 /* check for backups within backups */
1030 && (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
1031 == CM_SCACHEFLAG_RO) {
1032 code = CM_ERROR_NOSUCHVOLUME;
1036 /* now we need to get the volume */
1037 lock_ReleaseMutex(&scp->mx);
1038 code = cm_GetVolumeByName(cellp, volNamep, userp, reqp, 0, &volp);
1039 lock_ObtainMutex(&scp->mx);
1042 /* save the parent of the volume root for this is the
1043 * place where the volume is mounted and we must remember
1044 * this in the volume structure rather than just in the
1045 * scache entry lest the scache entry gets recycled
1048 lock_ObtainMutex(&volp->mx);
1049 volp->dotdotFid = dscp->fid;
1050 lock_ReleaseMutex(&volp->mx);
1052 scp->mountRootFid.cell = cellp->cellID;
1053 /* if the mt pt is in a read-only volume (not just a
1054 * backup), and if there is a read-only volume for the
1055 * target, and if this is a type '#' mount point, use
1056 * the read-only, otherwise use the one specified.
1058 if (mtType == '#' && (scp->flags & CM_SCACHEFLAG_PURERO)
1059 && volp->roID != 0 && type == RWVOL)
1062 scp->mountRootFid.volume = volp->roID;
1063 else if (type == BACKVOL)
1064 scp->mountRootFid.volume = volp->bkID;
1066 scp->mountRootFid.volume = volp->rwID;
1068 /* the rest of the fid is a magic number */
1069 scp->mountRootFid.vnode = 1;
1070 scp->mountRootFid.unique = 1;
1071 scp->mountRootGen = cm_data.mountRootGen;
1073 tfid = scp->mountRootFid;
1074 lock_ReleaseMutex(&scp->mx);
1075 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
1076 lock_ObtainMutex(&scp->mx);
1085 long cm_LookupInternal(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
1086 cm_req_t *reqp, cm_scache_t **outpScpp)
1089 int dnlcHit = 1; /* did we hit in the dnlc? yes, we did */
1090 cm_scache_t *tscp = NULL;
1091 cm_scache_t *mountedScp;
1092 cm_lookupSearch_t rock;
1095 if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
1096 && strcmp(namep, "..") == 0) {
1097 if (dscp->dotdotFid.volume == 0)
1098 return CM_ERROR_NOSUCHVOLUME;
1099 rock.fid = dscp->dotdotFid;
1101 } else if (strcmp(namep, ".") == 0) {
1102 rock.fid = dscp->fid;
1106 memset(&rock, 0, sizeof(rock));
1107 rock.fid.cell = dscp->fid.cell;
1108 rock.fid.volume = dscp->fid.volume;
1109 rock.searchNamep = namep;
1110 rock.caseFold = (flags & CM_FLAG_CASEFOLD);
1111 rock.hasTilde = ((strchr(namep, '~') != NULL) ? 1 : 0);
1113 /* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
1114 code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
1115 (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
1117 /* code == 0 means we fell off the end of the dir, while stopnow means
1118 * that we stopped early, probably because we found the entry we're
1119 * looking for. Any other non-zero code is an error.
1121 if (code && code != CM_ERROR_STOPNOW) {
1122 /* if the cm_scache_t we are searching in is not a directory
1123 * we must return path not found because the error
1124 * is to describe the final component not an intermediary
1126 if (code == CM_ERROR_NOTDIR) {
1127 if (flags & CM_FLAG_CHECKPATH)
1128 return CM_ERROR_NOSUCHPATH;
1130 return CM_ERROR_NOSUCHFILE;
1135 getroot = (dscp==cm_data.rootSCachep) ;
1137 if (!cm_freelanceEnabled || !getroot) {
1138 if (flags & CM_FLAG_CHECKPATH)
1139 return CM_ERROR_NOSUCHPATH;
1141 return CM_ERROR_NOSUCHFILE;
1143 else { /* nonexistent dir on freelance root, so add it */
1144 char fullname[200] = ".";
1147 osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %s",
1148 osi_LogSaveString(afsd_logp,namep));
1149 if (namep[0] == '.') {
1150 if (cm_GetCell_Gen(&namep[1], &fullname[1], CM_FLAG_CREATE)) {
1152 if ( stricmp(&namep[1], &fullname[1]) )
1153 code = cm_FreelanceAddSymlink(namep, fullname, &rock.fid);
1155 code = cm_FreelanceAddMount(namep, &fullname[1], "root.cell.", 1, &rock.fid);
1158 if (cm_GetCell_Gen(namep, fullname, CM_FLAG_CREATE)) {
1160 if ( stricmp(namep, fullname) )
1161 code = cm_FreelanceAddSymlink(namep, fullname, &rock.fid);
1163 code = cm_FreelanceAddMount(namep, fullname, "root.cell.", 0, &rock.fid);
1166 if (!found || code < 0) { /* add mount point failed, so give up */
1167 if (flags & CM_FLAG_CHECKPATH)
1168 return CM_ERROR_NOSUCHPATH;
1170 return CM_ERROR_NOSUCHFILE;
1172 tscp = NULL; /* to force call of cm_GetSCache */
1177 if ( !tscp ) /* we did not find it in the dnlc */
1180 code = cm_GetSCache(&rock.fid, &tscp, userp, reqp);
1184 /* tscp is now held */
1186 lock_ObtainMutex(&tscp->mx);
1187 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1188 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1190 lock_ReleaseMutex(&tscp->mx);
1191 cm_ReleaseSCache(tscp);
1194 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1195 /* tscp is now locked */
1197 if (!(flags & CM_FLAG_NOMOUNTCHASE)
1198 && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
1199 /* mount points are funny: they have a volume name to mount
1202 code = cm_ReadMountPoint(tscp, userp, reqp);
1204 code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
1206 lock_ReleaseMutex(&tscp->mx);
1207 cm_ReleaseSCache(tscp);
1214 lock_ReleaseMutex(&tscp->mx);
1217 /* copy back pointer */
1220 /* insert scache in dnlc */
1221 if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
1222 /* lock the directory entry to prevent racing callback revokes */
1223 lock_ObtainMutex(&dscp->mx);
1224 if ( dscp->cbServerp != NULL && dscp->cbExpires > 0 )
1225 cm_dnlcEnter(dscp, namep, tscp);
1226 lock_ReleaseMutex(&dscp->mx);
1233 int cm_ExpandSysName(char *inp, char *outp, long outSize, unsigned int index)
1238 tp = strrchr(inp, '@');
1240 return 0; /* no @sys */
1242 if (strcmp(tp, "@sys") != 0)
1243 return 0; /* no @sys */
1245 /* caller just wants to know if this is a valid @sys type of name */
1249 if (index >= MAXNUMSYSNAMES)
1252 /* otherwise generate the properly expanded @sys name */
1253 prefixCount = (int)(tp - inp);
1255 strncpy(outp, inp, prefixCount); /* copy out "a." from "a.@sys" */
1256 outp[prefixCount] = 0; /* null terminate the "a." */
1257 strcat(outp, cm_sysNameList[index]);/* append i386_nt40 */
1261 long cm_Lookup(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
1262 cm_req_t *reqp, cm_scache_t **outpScpp)
1266 int sysNameIndex = 0;
1267 cm_scache_t *scp = NULL;
1269 if ( stricmp(namep,SMB_IOCTL_FILENAME_NOSLASH) == 0 ) {
1270 if (flags & CM_FLAG_CHECKPATH)
1271 return CM_ERROR_NOSUCHPATH;
1273 return CM_ERROR_NOSUCHFILE;
1276 for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
1277 code = cm_ExpandSysName(namep, tname, sizeof(tname), sysNameIndex);
1279 code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
1285 cm_ReleaseSCache(scp);
1289 return cm_LookupInternal(dscp, namep, flags, userp, reqp, outpScpp);
1293 /* None of the possible sysName expansions could be found */
1294 if (flags & CM_FLAG_CHECKPATH)
1295 return CM_ERROR_NOSUCHPATH;
1297 return CM_ERROR_NOSUCHFILE;
1300 long cm_Unlink(cm_scache_t *dscp, char *namep, cm_user_t *userp, cm_req_t *reqp)
1306 AFSFetchStatus newDirStatus;
1308 struct rx_connection * callp;
1310 #ifdef AFS_FREELANCE_CLIENT
1311 if (cm_freelanceEnabled && dscp == cm_data.rootSCachep) {
1312 /* deleting a mount point from the root dir. */
1313 code = cm_FreelanceRemoveMount(namep);
1318 /* make sure we don't screw up the dir status during the merge */
1319 lock_ObtainMutex(&dscp->mx);
1320 sflags = CM_SCACHESYNC_STOREDATA;
1321 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1322 lock_ReleaseMutex(&dscp->mx);
1327 afsFid.Volume = dscp->fid.volume;
1328 afsFid.Vnode = dscp->fid.vnode;
1329 afsFid.Unique = dscp->fid.unique;
1331 osi_Log1(afsd_logp, "CALL RemoveFile scp 0x%p", dscp);
1333 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1337 callp = cm_GetRxConn(connp);
1338 code = RXAFS_RemoveFile(callp, &afsFid, namep,
1339 &newDirStatus, &volSync);
1340 rx_PutConnection(callp);
1342 } while (cm_Analyze(connp, userp, reqp, &dscp->fid, &volSync, NULL, NULL, code));
1343 code = cm_MapRPCError(code, reqp);
1346 osi_Log1(afsd_logp, "CALL RemoveFile FAILURE, code 0x%x", code);
1348 osi_Log0(afsd_logp, "CALL RemoveFile SUCCESS");
1350 lock_ObtainMutex(&dscp->mx);
1351 cm_dnlcRemove(dscp, namep);
1352 cm_SyncOpDone(dscp, NULL, sflags);
1354 cm_MergeStatus(dscp, &newDirStatus, &volSync, userp, 0);
1355 else if (code == CM_ERROR_NOSUCHFILE) {
1356 /* windows would not have allowed the request to delete the file
1357 * if it did not believe the file existed. therefore, we must
1358 * have an inconsistent view of the world.
1360 dscp->cbServerp = NULL;
1362 lock_ReleaseMutex(&dscp->mx);
1367 /* called with a locked vnode, and fills in the link info.
1368 * returns this the vnode still locked.
1370 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1377 lock_AssertMutex(&linkScp->mx);
1378 if (!linkScp->mountPointStringp[0]) {
1379 /* read the link data */
1380 lock_ReleaseMutex(&linkScp->mx);
1381 thyper.LowPart = thyper.HighPart = 0;
1382 code = buf_Get(linkScp, &thyper, &bufp);
1383 lock_ObtainMutex(&linkScp->mx);
1387 code = cm_SyncOp(linkScp, bufp, userp, reqp, 0,
1388 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1393 cm_SyncOpDone(linkScp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1395 if (cm_HaveBuffer(linkScp, bufp, 0))
1398 code = cm_GetBuffer(linkScp, bufp, NULL, userp, reqp);
1403 } /* while loop to get the data */
1405 /* now if we still have no link read in,
1406 * copy the data from the buffer */
1407 if ((temp = linkScp->length.LowPart) >= MOUNTPOINTLEN) {
1409 return CM_ERROR_TOOBIG;
1412 /* otherwise, it fits; make sure it is still null (could have
1413 * lost race with someone else referencing this link above),
1414 * and if so, copy in the data.
1416 if (!linkScp->mountPointStringp[0]) {
1417 strncpy(linkScp->mountPointStringp, bufp->datap, temp);
1418 linkScp->mountPointStringp[temp] = 0; /* null terminate */
1421 } /* don't have sym link contents cached */
1426 /* called with a held vnode and a path suffix, with the held vnode being a
1427 * symbolic link. Our goal is to generate a new path to interpret, and return
1428 * this new path in newSpaceBufferp. If the new vnode is relative to a dir
1429 * other than the directory containing the symbolic link, then the new root is
1430 * returned in *newRootScpp, otherwise a null is returned there.
1432 long cm_AssembleLink(cm_scache_t *linkScp, char *pathSuffixp,
1433 cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1434 cm_user_t *userp, cm_req_t *reqp)
1441 lock_ObtainMutex(&linkScp->mx);
1442 code = cm_HandleLink(linkScp, userp, reqp);
1446 /* if we may overflow the buffer, bail out; buffer is signficantly
1447 * bigger than max path length, so we don't really have to worry about
1448 * being a little conservative here.
1450 if (strlen(linkScp->mountPointStringp) + strlen(pathSuffixp) + 2
1451 >= CM_UTILS_SPACESIZE)
1452 return CM_ERROR_TOOBIG;
1454 tsp = cm_GetSpace();
1455 linkp = linkScp->mountPointStringp;
1456 if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1457 if (strlen(linkp) > cm_mountRootLen)
1458 strcpy(tsp->data, linkp+cm_mountRootLen+1);
1461 *newRootScpp = cm_data.rootSCachep;
1462 cm_HoldSCache(cm_data.rootSCachep);
1463 } else if (linkp[0] == '\\' && linkp[1] == '\\') {
1464 if (!strnicmp(&linkp[2], cm_NetbiosName, (len = (long)strlen(cm_NetbiosName))))
1466 char * p = &linkp[len + 3];
1467 if (strnicmp(p, "all", 3) == 0)
1470 strcpy(tsp->data, p);
1471 for (p = tsp->data; *p; p++) {
1475 *newRootScpp = cm_data.rootSCachep;
1476 cm_HoldSCache(cm_data.rootSCachep);
1478 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1479 strcpy(tsp->data, linkp);
1480 *newRootScpp = NULL;
1481 code = CM_ERROR_PATH_NOT_COVERED;
1483 } else if ( !strnicmp(linkp, "msdfs:", (len = (long)strlen("msdfs:"))) ) {
1484 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1485 strcpy(tsp->data, linkp);
1486 *newRootScpp = NULL;
1487 code = CM_ERROR_PATH_NOT_COVERED;
1488 } else if (*linkp == '\\' || *linkp == '/') {
1490 /* formerly, this was considered to be from the AFS root,
1491 * but this seems to create problems. instead, we will just
1492 * reject the link */
1493 strcpy(tsp->data, linkp+1);
1494 *newRootScpp = cm_data.rootSCachep;
1495 cm_HoldSCache(cm_data.rootSCachep);
1497 /* we still copy the link data into the response so that
1498 * the user can see what the link points to
1500 linkScp->fileType = CM_SCACHETYPE_INVALID;
1501 strcpy(tsp->data, linkp);
1502 *newRootScpp = NULL;
1503 code = CM_ERROR_NOSUCHPATH;
1506 /* a relative link */
1507 strcpy(tsp->data, linkp);
1508 *newRootScpp = NULL;
1510 if (pathSuffixp[0] != 0) { /* if suffix string is non-null */
1511 strcat(tsp->data, "\\");
1512 strcat(tsp->data, pathSuffixp);
1514 *newSpaceBufferp = tsp;
1517 lock_ReleaseMutex(&linkScp->mx);
1521 long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
1522 cm_user_t *userp, char *tidPathp, cm_req_t *reqp, cm_scache_t **outScpp)
1525 char *tp; /* ptr moving through input buffer */
1526 char tc; /* temp char */
1527 int haveComponent; /* has new component started? */
1528 char component[256]; /* this is the new component */
1529 char *cp; /* component name being assembled */
1530 cm_scache_t *tscp; /* current location in the hierarchy */
1531 cm_scache_t *nscp; /* next dude down */
1532 cm_scache_t *dirScp; /* last dir we searched */
1533 cm_scache_t *linkScp; /* new root for the symlink we just
1535 cm_space_t *psp; /* space for current path, if we've hit
1537 cm_space_t *tempsp; /* temp vbl */
1538 char *restp; /* rest of the pathname to interpret */
1539 int symlinkCount; /* count of # of symlinks traversed */
1540 int extraFlag; /* avoid chasing mt pts for dir cmd */
1541 int phase = 1; /* 1 = tidPathp, 2 = pathp */
1554 cm_HoldSCache(tscp);
1561 /* map Unix slashes into DOS ones so we can interpret Unix
1567 if (!haveComponent) {
1570 } else if (tc == 0) {
1584 /* we have a component here */
1585 if (tc == 0 || tc == '\\') {
1586 /* end of the component; we're at the last
1587 * component if tc == 0. However, if the last
1588 * is a symlink, we have more to do.
1590 *cp++ = 0; /* add null termination */
1591 if (!strcmp(".",component)) {
1594 cm_ReleaseSCache(dirScp);
1600 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
1601 extraFlag = CM_FLAG_NOMOUNTCHASE;
1602 code = cm_Lookup(tscp, component,
1604 userp, reqp, &nscp);
1606 cm_ReleaseSCache(tscp);
1608 cm_ReleaseSCache(dirScp);
1611 if (code == CM_ERROR_NOSUCHFILE && tscp->fileType == CM_SCACHETYPE_SYMLINK)
1612 return CM_ERROR_NOSUCHPATH;
1616 haveComponent = 0; /* component done */
1618 cm_ReleaseSCache(dirScp);
1619 dirScp = tscp; /* for some symlinks */
1620 tscp = nscp; /* already held */
1622 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
1625 cm_ReleaseSCache(dirScp);
1631 /* now, if tscp is a symlink, we should follow
1632 * it and assemble the path again.
1634 lock_ObtainMutex(&tscp->mx);
1635 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1636 CM_SCACHESYNC_GETSTATUS
1637 | CM_SCACHESYNC_NEEDCALLBACK);
1639 lock_ReleaseMutex(&tscp->mx);
1640 cm_ReleaseSCache(tscp);
1643 cm_ReleaseSCache(dirScp);
1648 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1650 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
1651 /* this is a symlink; assemble a new buffer */
1652 lock_ReleaseMutex(&tscp->mx);
1653 if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
1654 cm_ReleaseSCache(tscp);
1657 cm_ReleaseSCache(dirScp);
1662 return CM_ERROR_TOO_MANY_SYMLINKS;
1668 code = cm_AssembleLink(tscp, restp, &linkScp, &tempsp, userp, reqp);
1670 /* something went wrong */
1671 cm_ReleaseSCache(tscp);
1674 cm_ReleaseSCache(dirScp);
1680 /* otherwise, tempsp has the new path,
1681 * and linkScp is the new root from
1682 * which to interpret that path.
1683 * Continue with the namei processing,
1684 * also doing the bookkeeping for the
1685 * space allocation and tracking the
1686 * vnode reference counts.
1692 cm_ReleaseSCache(tscp);
1697 * now, if linkScp is null, that's
1698 * AssembleLink's way of telling us that
1699 * the sym link is relative to the dir
1700 * containing the link. We have a ref
1701 * to it in dirScp, and we hold it now
1702 * and reuse it as the new spot in the
1710 /* not a symlink, we may be done */
1711 lock_ReleaseMutex(&tscp->mx);
1719 cm_ReleaseSCache(dirScp);
1727 cm_ReleaseSCache(dirScp);
1730 } /* end of a component */
1733 } /* we have a component */
1734 } /* big while loop over all components */
1738 cm_ReleaseSCache(dirScp);
1744 cm_ReleaseSCache(tscp);
1748 /* called with a dir, and a vnode within the dir that happens to be a symlink.
1749 * We chase the link, and return a held pointer to the target, if it exists,
1750 * in *outScpp. If we succeed, we return 0, otherwise we return an error code
1751 * and do not hold or return a target vnode.
1753 * This is very similar to calling cm_NameI with the last component of a name,
1754 * which happens to be a symlink, except that we've already passed by the name.
1756 * This function is typically called by the directory listing functions, which
1757 * encounter symlinks but need to return the proper file length so programs
1758 * like "more" work properly when they make use of the attributes retrieved from
1761 * The input vnode should not be locked when this function is called.
1763 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
1764 cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
1768 cm_scache_t *newRootScp;
1770 osi_Log1(afsd_logp, "Evaluating symlink scp 0x%p", linkScp);
1772 code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
1776 /* now, if newRootScp is NULL, we're really being told that the symlink
1777 * is relative to the current directory (dscp).
1779 if (newRootScp == NULL) {
1781 cm_HoldSCache(dscp);
1784 code = cm_NameI(newRootScp, spacep->data,
1785 CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
1786 userp, NULL, reqp, outScpp);
1788 if (code == CM_ERROR_NOSUCHFILE)
1789 code = CM_ERROR_NOSUCHPATH;
1791 /* this stuff is allocated no matter what happened on the namei call,
1793 cm_FreeSpace(spacep);
1794 cm_ReleaseSCache(newRootScp);
1799 /* make this big enough so that one buffer of dir pages won't overflow. We'll
1800 * check anyway, but we want to minimize the chance that we have to leave stuff
1803 #define CM_BULKMAX (3 * AFSCBMAX)
1805 /* rock for bulk stat calls */
1806 typedef struct cm_bulkStat {
1807 osi_hyper_t bufOffset; /* only do it for things in this buffer page */
1809 /* info for the actual call */
1810 int counter; /* next free slot */
1811 AFSFid fids[CM_BULKMAX];
1812 AFSFetchStatus stats[CM_BULKMAX];
1813 AFSCallBack callbacks[CM_BULKMAX];
1816 /* for a given entry, make sure that it isn't in the stat cache, and then
1817 * add it to the list of file IDs to be obtained.
1819 * Don't bother adding it if we already have a vnode. Note that the dir
1820 * is locked, so we have to be careful checking the vnode we're thinking of
1821 * processing, to avoid deadlocks.
1823 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
1834 /* Don't overflow bsp. */
1835 if (bsp->counter >= CM_BULKMAX)
1836 return CM_ERROR_STOPNOW;
1838 thyper.LowPart = cm_data.buf_blockSize;
1839 thyper.HighPart = 0;
1840 thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
1842 /* thyper is now the first byte past the end of the record we're
1843 * interested in, and bsp->bufOffset is the first byte of the record
1844 * we're interested in.
1845 * Skip data in the others.
1848 if (LargeIntegerLessThan(*offp, bsp->bufOffset))
1850 if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
1851 return CM_ERROR_STOPNOW;
1852 if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
1855 tfid.cell = scp->fid.cell;
1856 tfid.volume = scp->fid.volume;
1857 tfid.vnode = ntohl(dep->fid.vnode);
1858 tfid.unique = ntohl(dep->fid.unique);
1859 tscp = cm_FindSCache(&tfid);
1861 if (lock_TryMutex(&tscp->mx)) {
1862 /* we have an entry that we can look at */
1863 if (!(tscp->flags & CM_SCACHEFLAG_EACCESS) && cm_HaveCallback(tscp)) {
1864 /* we have a callback on it. Don't bother
1865 * fetching this stat entry, since we're happy
1866 * with the info we have.
1868 lock_ReleaseMutex(&tscp->mx);
1869 cm_ReleaseSCache(tscp);
1872 lock_ReleaseMutex(&tscp->mx);
1874 cm_ReleaseSCache(tscp);
1877 #ifdef AFS_FREELANCE_CLIENT
1878 // yj: if this is a mountpoint under root.afs then we don't want it
1879 // to be bulkstat-ed, instead, we call getSCache directly and under
1880 // getSCache, it is handled specially.
1881 if ( cm_freelanceEnabled &&
1882 tfid.cell==AFS_FAKE_ROOT_CELL_ID &&
1883 tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
1884 !(tfid.vnode==0x1 && tfid.unique==0x1) )
1886 osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
1887 return cm_GetSCache(&tfid, &tscp, NULL, NULL);
1889 #endif /* AFS_FREELANCE_CLIENT */
1892 bsp->fids[i].Volume = scp->fid.volume;
1893 bsp->fids[i].Vnode = tfid.vnode;
1894 bsp->fids[i].Unique = tfid.unique;
1898 /* called with a locked scp and a pointer to a buffer. Make bulk stat
1899 * calls on all undeleted files in the page of the directory specified.
1902 cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
1906 cm_bulkStat_t bb; /* this is *BIG*, probably 16K or so;
1907 * watch for stack problems */
1908 AFSCBFids fidStruct;
1909 AFSBulkStats statStruct;
1911 AFSCBs callbackStruct;
1914 cm_callbackRequest_t cbReq;
1920 struct rx_connection * callp;
1921 int inlinebulk = 0; /* Did we use InlineBulkStatus RPC or not? */
1923 osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%p", dscp);
1925 /* should be on a buffer boundary */
1926 osi_assert((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0);
1928 memset(&bb, 0, sizeof(bb));
1929 bb.bufOffset = *offsetp;
1931 lock_ReleaseMutex(&dscp->mx);
1932 /* first, assemble the file IDs we need to stat */
1933 code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) &bb, offsetp, userp, reqp, NULL);
1935 /* if we failed, bail out early */
1936 if (code && code != CM_ERROR_STOPNOW) {
1937 lock_ObtainMutex(&dscp->mx);
1941 /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
1942 * make the calls to create the entries. Handle AFSCBMAX files at a
1946 while (filex < bb.counter) {
1947 filesThisCall = bb.counter - filex;
1948 if (filesThisCall > AFSCBMAX)
1949 filesThisCall = AFSCBMAX;
1951 fidStruct.AFSCBFids_len = filesThisCall;
1952 fidStruct.AFSCBFids_val = &bb.fids[filex];
1953 statStruct.AFSBulkStats_len = filesThisCall;
1954 statStruct.AFSBulkStats_val = &bb.stats[filex];
1955 callbackStruct.AFSCBs_len = filesThisCall;
1956 callbackStruct.AFSCBs_val = &bb.callbacks[filex];
1957 cm_StartCallbackGrantingCall(NULL, &cbReq);
1958 osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
1960 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1964 callp = cm_GetRxConn(connp);
1965 if (!(connp->serverp->flags & CM_SERVERFLAG_NOINLINEBULK)) {
1966 code = RXAFS_InlineBulkStatus(callp, &fidStruct,
1967 &statStruct, &callbackStruct, &volSync);
1968 if (code == RXGEN_OPCODE) {
1969 cm_SetServerNoInlineBulk(connp->serverp, 0);
1975 code = RXAFS_BulkStatus(callp, &fidStruct,
1976 &statStruct, &callbackStruct, &volSync);
1978 rx_PutConnection(callp);
1980 } while (cm_Analyze(connp, userp, reqp, &dscp->fid,
1981 &volSync, NULL, &cbReq, code));
1982 code = cm_MapRPCError(code, reqp);
1984 osi_Log2(afsd_logp, "CALL %sBulkStatus FAILURE code 0x%x",
1985 inlinebulk ? "Inline" : "", code);
1987 osi_Log1(afsd_logp, "CALL %sBulkStatus SUCCESS", inlinebulk ? "Inline" : "");
1989 /* may as well quit on an error, since we're not going to do
1990 * much better on the next immediate call, either.
1993 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
1997 /* otherwise, we should do the merges */
1998 for (i = 0; i<filesThisCall; i++) {
2000 tfid.cell = dscp->fid.cell;
2001 tfid.volume = bb.fids[j].Volume;
2002 tfid.vnode = bb.fids[j].Vnode;
2003 tfid.unique = bb.fids[j].Unique;
2004 code = cm_GetSCache(&tfid, &scp, userp, reqp);
2008 /* otherwise, if this entry has no callback info,
2011 lock_ObtainMutex(&scp->mx);
2012 /* now, we have to be extra paranoid on merging in this
2013 * information, since we didn't use cm_SyncOp before
2014 * starting the fetch to make sure that no bad races
2015 * were occurring. Specifically, we need to make sure
2016 * we don't obliterate any newer information in the
2017 * vnode than have here.
2019 * Right now, be pretty conservative: if there's a
2020 * callback or a pending call, skip it.
2022 if ((scp->cbServerp == NULL || (scp->flags & CM_SCACHEFLAG_EACCESS))
2024 (CM_SCACHEFLAG_FETCHING
2025 | CM_SCACHEFLAG_STORING
2026 | CM_SCACHEFLAG_SIZESTORING))) {
2027 cm_EndCallbackGrantingCall(scp, &cbReq,
2029 CM_CALLBACK_MAINTAINCOUNT);
2030 cm_MergeStatus(scp, &bb.stats[j], &volSync, userp, 0);
2032 lock_ReleaseMutex(&scp->mx);
2033 cm_ReleaseSCache(scp);
2034 } /* all files in the response */
2035 /* now tell it to drop the count,
2036 * after doing the vnode processing above */
2037 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2039 filex += filesThisCall;
2040 } /* while there are still more files to process */
2041 lock_ObtainMutex(&dscp->mx);
2044 /* If we did the InlineBulk RPC pull out the return code */
2046 if ((&bb.stats[0])->errorCode) {
2047 cm_Analyze(NULL /*connp was released by the previous cm_Analyze */,
2048 userp, reqp, &dscp->fid, &volSync, NULL, NULL, (&bb.stats[0])->errorCode);
2049 code = cm_MapRPCError((&bb.stats[0])->errorCode, reqp);
2057 osi_Log1(afsd_logp, "END cm_TryBulkStat code = 0x%x", code);
2061 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
2065 /* initialize store back mask as inexpensive local variable */
2067 memset(statusp, 0, sizeof(AFSStoreStatus));
2069 /* copy out queued info from scache first, if scp passed in */
2071 if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
2072 statusp->ClientModTime = scp->clientModTime;
2073 mask |= AFS_SETMODTIME;
2074 scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
2079 /* now add in our locally generated request */
2080 if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
2081 statusp->ClientModTime = attrp->clientModTime;
2082 mask |= AFS_SETMODTIME;
2084 if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
2085 statusp->UnixModeBits = attrp->unixModeBits;
2086 mask |= AFS_SETMODE;
2088 if (attrp->mask & CM_ATTRMASK_OWNER) {
2089 statusp->Owner = attrp->owner;
2090 mask |= AFS_SETOWNER;
2092 if (attrp->mask & CM_ATTRMASK_GROUP) {
2093 statusp->Group = attrp->group;
2094 mask |= AFS_SETGROUP;
2097 statusp->Mask = mask;
2100 /* set the file size, and make sure that all relevant buffers have been
2101 * truncated. Ensure that any partially truncated buffers have been zeroed
2102 * to the end of the buffer.
2104 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
2110 /* start by locking out buffer creation */
2111 lock_ObtainWrite(&scp->bufCreateLock);
2113 /* verify that this is a file, not a dir or a symlink */
2114 lock_ObtainMutex(&scp->mx);
2115 code = cm_SyncOp(scp, NULL, userp, reqp, 0,
2116 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2119 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2121 if (scp->fileType != CM_SCACHETYPE_FILE) {
2122 code = CM_ERROR_ISDIR;
2127 if (LargeIntegerLessThan(*sizep, scp->length))
2132 lock_ReleaseMutex(&scp->mx);
2134 /* can't hold scp->mx lock here, since we may wait for a storeback to
2135 * finish if the buffer package is cleaning a buffer by storing it to
2139 buf_Truncate(scp, userp, reqp, sizep);
2141 /* now ensure that file length is short enough, and update truncPos */
2142 lock_ObtainMutex(&scp->mx);
2144 /* make sure we have a callback (so we have the right value for the
2145 * length), and wait for it to be safe to do a truncate.
2147 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
2148 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2149 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2153 if (LargeIntegerLessThan(*sizep, scp->length)) {
2154 /* a real truncation. If truncPos is not set yet, or is bigger
2155 * than where we're truncating the file, set truncPos to this
2160 if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
2161 || LargeIntegerLessThan(*sizep, scp->length)) {
2163 scp->truncPos = *sizep;
2164 scp->mask |= CM_SCACHEMASK_TRUNCPOS;
2166 /* in either case, the new file size has been changed */
2167 scp->length = *sizep;
2168 scp->mask |= CM_SCACHEMASK_LENGTH;
2170 else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
2171 /* really extending the file */
2172 scp->length = *sizep;
2173 scp->mask |= CM_SCACHEMASK_LENGTH;
2176 /* done successfully */
2179 cm_SyncOpDone(scp, NULL,
2180 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2181 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2184 lock_ReleaseMutex(&scp->mx);
2185 lock_ReleaseWrite(&scp->bufCreateLock);
2190 /* set the file size or other attributes (but not both at once) */
2191 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
2195 AFSFetchStatus afsOutStatus;
2199 AFSStoreStatus afsInStatus;
2200 struct rx_connection * callp;
2202 /* handle file length setting */
2203 if (attrp->mask & CM_ATTRMASK_LENGTH)
2204 return cm_SetLength(scp, &attrp->length, userp, reqp);
2206 lock_ObtainMutex(&scp->mx);
2207 /* otherwise, we have to make an RPC to get the status */
2208 code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
2210 /* make the attr structure */
2211 cm_StatusFromAttr(&afsInStatus, scp, attrp);
2213 tfid.Volume = scp->fid.volume;
2214 tfid.Vnode = scp->fid.vnode;
2215 tfid.Unique = scp->fid.unique;
2217 lock_ReleaseMutex(&scp->mx);
2221 /* now make the RPC */
2222 osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%p", scp);
2224 code = cm_Conn(&scp->fid, userp, reqp, &connp);
2228 callp = cm_GetRxConn(connp);
2229 code = RXAFS_StoreStatus(callp, &tfid,
2230 &afsInStatus, &afsOutStatus, &volSync);
2231 rx_PutConnection(callp);
2233 } while (cm_Analyze(connp, userp, reqp,
2234 &scp->fid, &volSync, NULL, NULL, code));
2235 code = cm_MapRPCError(code, reqp);
2238 osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
2240 osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
2242 lock_ObtainMutex(&scp->mx);
2243 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
2245 cm_MergeStatus(scp, &afsOutStatus, &volSync, userp,
2246 CM_MERGEFLAG_FORCE);
2248 /* if we're changing the mode bits, discard the ACL cache,
2249 * since we changed the mode bits.
2251 if (afsInStatus.Mask & AFS_SETMODE) cm_FreeAllACLEnts(scp);
2252 lock_ReleaseMutex(&scp->mx);
2256 long cm_Create(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2257 cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
2262 cm_callbackRequest_t cbReq;
2267 AFSStoreStatus inStatus;
2268 AFSFetchStatus updatedDirStatus;
2269 AFSFetchStatus newFileStatus;
2270 AFSCallBack newFileCallback;
2272 struct rx_connection * callp;
2274 /* can't create names with @sys in them; must expand it manually first.
2275 * return "invalid request" if they try.
2277 if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2278 return CM_ERROR_ATSYS;
2281 /* before starting the RPC, mark that we're changing the file data, so
2282 * that someone who does a chmod will know to wait until our call
2285 lock_ObtainMutex(&dscp->mx);
2286 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2288 cm_StartCallbackGrantingCall(NULL, &cbReq);
2290 lock_ReleaseMutex(&dscp->mx);
2296 cm_StatusFromAttr(&inStatus, NULL, attrp);
2298 /* try the RPC now */
2299 osi_Log1(afsd_logp, "CALL CreateFile scp 0x%p", dscp);
2301 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2305 dirAFSFid.Volume = dscp->fid.volume;
2306 dirAFSFid.Vnode = dscp->fid.vnode;
2307 dirAFSFid.Unique = dscp->fid.unique;
2309 callp = cm_GetRxConn(connp);
2310 code = RXAFS_CreateFile(connp->callp, &dirAFSFid, namep,
2311 &inStatus, &newAFSFid, &newFileStatus,
2312 &updatedDirStatus, &newFileCallback,
2314 rx_PutConnection(callp);
2316 } while (cm_Analyze(connp, userp, reqp,
2317 &dscp->fid, &volSync, NULL, &cbReq, code));
2318 code = cm_MapRPCError(code, reqp);
2321 osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
2323 osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
2325 lock_ObtainMutex(&dscp->mx);
2326 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2328 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2330 lock_ReleaseMutex(&dscp->mx);
2332 /* now try to create the file's entry, too, but be careful to
2333 * make sure that we don't merge in old info. Since we weren't locking
2334 * out any requests during the file's creation, we may have pretty old
2338 newFid.cell = dscp->fid.cell;
2339 newFid.volume = dscp->fid.volume;
2340 newFid.vnode = newAFSFid.Vnode;
2341 newFid.unique = newAFSFid.Unique;
2342 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2344 lock_ObtainMutex(&scp->mx);
2345 scp->creator = userp; /* remember who created it */
2346 if (!cm_HaveCallback(scp)) {
2347 cm_MergeStatus(scp, &newFileStatus, &volSync,
2349 cm_EndCallbackGrantingCall(scp, &cbReq,
2350 &newFileCallback, 0);
2353 lock_ReleaseMutex(&scp->mx);
2358 /* make sure we end things properly */
2360 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2365 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
2369 lock_ObtainWrite(&scp->bufCreateLock);
2370 code = buf_CleanVnode(scp, userp, reqp);
2371 lock_ReleaseWrite(&scp->bufCreateLock);
2373 lock_ObtainMutex(&scp->mx);
2375 if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
2376 | CM_SCACHEMASK_CLIENTMODTIME
2377 | CM_SCACHEMASK_LENGTH))
2378 code = cm_StoreMini(scp, userp, reqp);
2380 if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) {
2381 code = (scp->flags & CM_SCACHEFLAG_OVERQUOTA) ? CM_ERROR_QUOTA : CM_ERROR_SPACE;
2382 scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE);
2385 lock_ReleaseMutex(&scp->mx);
2390 long cm_MakeDir(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2391 cm_user_t *userp, cm_req_t *reqp)
2396 cm_callbackRequest_t cbReq;
2401 AFSStoreStatus inStatus;
2402 AFSFetchStatus updatedDirStatus;
2403 AFSFetchStatus newDirStatus;
2404 AFSCallBack newDirCallback;
2406 struct rx_connection * callp;
2408 /* can't create names with @sys in them; must expand it manually first.
2409 * return "invalid request" if they try.
2411 if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2412 return CM_ERROR_ATSYS;
2415 /* before starting the RPC, mark that we're changing the directory
2416 * data, so that someone who does a chmod on the dir will wait until
2417 * our call completes.
2419 lock_ObtainMutex(&dscp->mx);
2420 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2422 cm_StartCallbackGrantingCall(NULL, &cbReq);
2424 lock_ReleaseMutex(&dscp->mx);
2430 cm_StatusFromAttr(&inStatus, NULL, attrp);
2432 /* try the RPC now */
2433 osi_Log1(afsd_logp, "CALL MakeDir scp 0x%p", dscp);
2435 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2439 dirAFSFid.Volume = dscp->fid.volume;
2440 dirAFSFid.Vnode = dscp->fid.vnode;
2441 dirAFSFid.Unique = dscp->fid.unique;
2443 callp = cm_GetRxConn(connp);
2444 code = RXAFS_MakeDir(connp->callp, &dirAFSFid, namep,
2445 &inStatus, &newAFSFid, &newDirStatus,
2446 &updatedDirStatus, &newDirCallback,
2448 rx_PutConnection(callp);
2450 } while (cm_Analyze(connp, userp, reqp,
2451 &dscp->fid, &volSync, NULL, &cbReq, code));
2452 code = cm_MapRPCError(code, reqp);
2455 osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
2457 osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
2459 lock_ObtainMutex(&dscp->mx);
2460 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2462 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2464 lock_ReleaseMutex(&dscp->mx);
2466 /* now try to create the new dir's entry, too, but be careful to
2467 * make sure that we don't merge in old info. Since we weren't locking
2468 * out any requests during the file's creation, we may have pretty old
2472 newFid.cell = dscp->fid.cell;
2473 newFid.volume = dscp->fid.volume;
2474 newFid.vnode = newAFSFid.Vnode;
2475 newFid.unique = newAFSFid.Unique;
2476 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2478 lock_ObtainMutex(&scp->mx);
2479 if (!cm_HaveCallback(scp)) {
2480 cm_MergeStatus(scp, &newDirStatus, &volSync,
2482 cm_EndCallbackGrantingCall(scp, &cbReq,
2483 &newDirCallback, 0);
2486 lock_ReleaseMutex(&scp->mx);
2487 cm_ReleaseSCache(scp);
2491 /* make sure we end things properly */
2493 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2495 /* and return error code */
2499 long cm_Link(cm_scache_t *dscp, char *namep, cm_scache_t *sscp, long flags,
2500 cm_user_t *userp, cm_req_t *reqp)
2505 AFSFid existingAFSFid;
2506 AFSFetchStatus updatedDirStatus;
2507 AFSFetchStatus newLinkStatus;
2509 struct rx_connection * callp;
2511 if (dscp->fid.cell != sscp->fid.cell ||
2512 dscp->fid.volume != sscp->fid.volume) {
2513 return CM_ERROR_CROSSDEVLINK;
2516 lock_ObtainMutex(&dscp->mx);
2517 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2518 lock_ReleaseMutex(&dscp->mx);
2523 /* try the RPC now */
2524 osi_Log1(afsd_logp, "CALL Link scp 0x%p", dscp);
2526 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2529 dirAFSFid.Volume = dscp->fid.volume;
2530 dirAFSFid.Vnode = dscp->fid.vnode;
2531 dirAFSFid.Unique = dscp->fid.unique;
2533 existingAFSFid.Volume = sscp->fid.volume;
2534 existingAFSFid.Vnode = sscp->fid.vnode;
2535 existingAFSFid.Unique = sscp->fid.unique;
2537 callp = cm_GetRxConn(connp);
2538 code = RXAFS_Link(callp, &dirAFSFid, namep, &existingAFSFid,
2539 &newLinkStatus, &updatedDirStatus, &volSync);
2540 rx_PutConnection(callp);
2541 osi_Log1(smb_logp," RXAFS_Link returns 0x%x", code);
2543 } while (cm_Analyze(connp, userp, reqp,
2544 &dscp->fid, &volSync, NULL, NULL, code));
2546 code = cm_MapRPCError(code, reqp);
2549 osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
2551 osi_Log0(afsd_logp, "CALL Link SUCCESS");
2553 lock_ObtainMutex(&dscp->mx);
2554 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2556 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2558 lock_ReleaseMutex(&dscp->mx);
2563 long cm_SymLink(cm_scache_t *dscp, char *namep, char *contentsp, long flags,
2564 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
2572 AFSStoreStatus inStatus;
2573 AFSFetchStatus updatedDirStatus;
2574 AFSFetchStatus newLinkStatus;
2576 struct rx_connection * callp;
2578 /* before starting the RPC, mark that we're changing the directory data,
2579 * so that someone who does a chmod on the dir will wait until our
2582 lock_ObtainMutex(&dscp->mx);
2583 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2584 lock_ReleaseMutex(&dscp->mx);
2589 cm_StatusFromAttr(&inStatus, NULL, attrp);
2591 /* try the RPC now */
2592 osi_Log1(afsd_logp, "CALL Symlink scp 0x%p", dscp);
2594 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2598 dirAFSFid.Volume = dscp->fid.volume;
2599 dirAFSFid.Vnode = dscp->fid.vnode;
2600 dirAFSFid.Unique = dscp->fid.unique;
2602 callp = cm_GetRxConn(connp);
2603 code = RXAFS_Symlink(callp, &dirAFSFid, namep, contentsp,
2604 &inStatus, &newAFSFid, &newLinkStatus,
2605 &updatedDirStatus, &volSync);
2606 rx_PutConnection(callp);
2608 } while (cm_Analyze(connp, userp, reqp,
2609 &dscp->fid, &volSync, NULL, NULL, code));
2610 code = cm_MapRPCError(code, reqp);
2613 osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
2615 osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
2617 lock_ObtainMutex(&dscp->mx);
2618 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2620 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2622 lock_ReleaseMutex(&dscp->mx);
2624 /* now try to create the new dir's entry, too, but be careful to
2625 * make sure that we don't merge in old info. Since we weren't locking
2626 * out any requests during the file's creation, we may have pretty old
2630 newFid.cell = dscp->fid.cell;
2631 newFid.volume = dscp->fid.volume;
2632 newFid.vnode = newAFSFid.Vnode;
2633 newFid.unique = newAFSFid.Unique;
2634 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2636 lock_ObtainMutex(&scp->mx);
2637 if (!cm_HaveCallback(scp)) {
2638 cm_MergeStatus(scp, &newLinkStatus, &volSync,
2641 lock_ReleaseMutex(&scp->mx);
2642 cm_ReleaseSCache(scp);
2646 /* and return error code */
2650 long cm_RemoveDir(cm_scache_t *dscp, char *namep, cm_user_t *userp,
2657 AFSFetchStatus updatedDirStatus;
2659 struct rx_connection * callp;
2661 /* before starting the RPC, mark that we're changing the directory data,
2662 * so that someone who does a chmod on the dir will wait until our
2665 lock_ObtainMutex(&dscp->mx);
2666 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2667 lock_ReleaseMutex(&dscp->mx);
2673 /* try the RPC now */
2674 osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%p", dscp);
2676 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2680 dirAFSFid.Volume = dscp->fid.volume;
2681 dirAFSFid.Vnode = dscp->fid.vnode;
2682 dirAFSFid.Unique = dscp->fid.unique;
2684 callp = cm_GetRxConn(connp);
2685 code = RXAFS_RemoveDir(callp, &dirAFSFid, namep,
2686 &updatedDirStatus, &volSync);
2687 rx_PutConnection(callp);
2689 } while (cm_Analyze(connp, userp, reqp,
2690 &dscp->fid, &volSync, NULL, NULL, code));
2691 code = cm_MapRPCErrorRmdir(code, reqp);
2694 osi_Log1(afsd_logp, "CALL RemoveDir FAILURE, code 0x%x", code);
2696 osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
2698 lock_ObtainMutex(&dscp->mx);
2699 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2701 cm_dnlcRemove(dscp, namep);
2702 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2704 lock_ReleaseMutex(&dscp->mx);
2706 /* and return error code */
2710 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
2712 /* grab mutex on contents */
2713 lock_ObtainMutex(&scp->mx);
2715 /* reset the prefetch info */
2716 scp->prefetch.base.LowPart = 0; /* base */
2717 scp->prefetch.base.HighPart = 0;
2718 scp->prefetch.end.LowPart = 0; /* and end */
2719 scp->prefetch.end.HighPart = 0;
2721 /* release mutex on contents */
2722 lock_ReleaseMutex(&scp->mx);
2728 long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
2729 char *newNamep, cm_user_t *userp, cm_req_t *reqp)
2733 AFSFid oldDirAFSFid;
2734 AFSFid newDirAFSFid;
2736 AFSFetchStatus updatedOldDirStatus;
2737 AFSFetchStatus updatedNewDirStatus;
2740 struct rx_connection * callp;
2742 /* before starting the RPC, mark that we're changing the directory data,
2743 * so that someone who does a chmod on the dir will wait until our call
2744 * completes. We do this in vnode order so that we don't deadlock,
2745 * which makes the code a little verbose.
2747 if (oldDscp == newDscp) {
2748 /* check for identical names */
2749 if (strcmp(oldNamep, newNamep) == 0)
2750 return CM_ERROR_RENAME_IDENTICAL;
2753 lock_ObtainMutex(&oldDscp->mx);
2754 cm_dnlcRemove(oldDscp, oldNamep);
2755 cm_dnlcRemove(oldDscp, newNamep);
2756 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2757 CM_SCACHESYNC_STOREDATA);
2758 lock_ReleaseMutex(&oldDscp->mx);
2761 /* two distinct dir vnodes */
2763 if (oldDscp->fid.cell != newDscp->fid.cell ||
2764 oldDscp->fid.volume != newDscp->fid.volume)
2765 return CM_ERROR_CROSSDEVLINK;
2767 /* shouldn't happen that we have distinct vnodes for two
2768 * different files, but could due to deliberate attack, or
2769 * stale info. Avoid deadlocks and quit now.
2771 if (oldDscp->fid.vnode == newDscp->fid.vnode)
2772 return CM_ERROR_CROSSDEVLINK;
2774 if (oldDscp->fid.vnode < newDscp->fid.vnode) {
2775 lock_ObtainMutex(&oldDscp->mx);
2776 cm_dnlcRemove(oldDscp, oldNamep);
2777 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2778 CM_SCACHESYNC_STOREDATA);
2779 lock_ReleaseMutex(&oldDscp->mx);
2781 lock_ObtainMutex(&newDscp->mx);
2782 cm_dnlcRemove(newDscp, newNamep);
2783 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2784 CM_SCACHESYNC_STOREDATA);
2785 lock_ReleaseMutex(&newDscp->mx);
2787 /* cleanup first one */
2788 lock_ObtainMutex(&newDscp->mx);
2789 cm_SyncOpDone(oldDscp, NULL,
2790 CM_SCACHESYNC_STOREDATA);
2791 lock_ReleaseMutex(&oldDscp->mx);
2796 /* lock the new vnode entry first */
2797 lock_ObtainMutex(&newDscp->mx);
2798 cm_dnlcRemove(newDscp, newNamep);
2799 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2800 CM_SCACHESYNC_STOREDATA);
2801 lock_ReleaseMutex(&newDscp->mx);
2803 lock_ObtainMutex(&oldDscp->mx);
2804 cm_dnlcRemove(oldDscp, oldNamep);
2805 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2806 CM_SCACHESYNC_STOREDATA);
2807 lock_ReleaseMutex(&oldDscp->mx);
2809 /* cleanup first one */
2810 lock_ObtainMutex(&newDscp->mx);
2811 cm_SyncOpDone(newDscp, NULL,
2812 CM_SCACHESYNC_STOREDATA);
2813 lock_ReleaseMutex(&newDscp->mx);
2817 } /* two distinct vnodes */
2824 /* try the RPC now */
2825 osi_Log2(afsd_logp, "CALL Rename old scp 0x%p new scp 0x%p",
2828 code = cm_Conn(&oldDscp->fid, userp, reqp, &connp);
2832 oldDirAFSFid.Volume = oldDscp->fid.volume;
2833 oldDirAFSFid.Vnode = oldDscp->fid.vnode;
2834 oldDirAFSFid.Unique = oldDscp->fid.unique;
2835 newDirAFSFid.Volume = newDscp->fid.volume;
2836 newDirAFSFid.Vnode = newDscp->fid.vnode;
2837 newDirAFSFid.Unique = newDscp->fid.unique;
2839 callp = cm_GetRxConn(connp);
2840 code = RXAFS_Rename(callp, &oldDirAFSFid, oldNamep,
2841 &newDirAFSFid, newNamep,
2842 &updatedOldDirStatus, &updatedNewDirStatus,
2844 rx_PutConnection(callp);
2846 } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
2847 &volSync, NULL, NULL, code));
2848 code = cm_MapRPCError(code, reqp);
2851 osi_Log1(afsd_logp, "CALL Rename FAILURE, code 0x%x", code);
2853 osi_Log0(afsd_logp, "CALL Rename SUCCESS");
2855 /* update the individual stat cache entries for the directories */
2856 lock_ObtainMutex(&oldDscp->mx);
2857 cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
2859 cm_MergeStatus(oldDscp, &updatedOldDirStatus, &volSync,
2862 lock_ReleaseMutex(&oldDscp->mx);
2864 /* and update it for the new one, too, if necessary */
2866 lock_ObtainMutex(&newDscp->mx);
2867 cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
2869 cm_MergeStatus(newDscp, &updatedNewDirStatus, &volSync,
2872 lock_ReleaseMutex(&newDscp->mx);
2875 /* and return error code */
2879 /* Byte range locks:
2881 The OpenAFS Windows client has to fake byte range locks given no
2882 server side support for such locks. This is implemented as keyed
2883 byte range locks on the cache manager.
2885 Keyed byte range locks:
2887 Each cm_scache_t structure keeps track of a list of keyed locks.
2888 The key for a lock identifies an owner of a set of locks (referred
2889 to as a client). Each key is represented by a value. The set of
2890 key values used within a specific cm_scache_t structure form a
2891 namespace that has a scope of just that cm_scache_t structure. The
2892 same key value can be used with another cm_scache_t structure and
2893 correspond to a completely different client. However it is
2894 advantageous for the SMB or IFS layer to make sure that there is a
2895 1-1 mapping between client and keys over all cm_scache_t objects.
2897 Assume a client C has key Key(C) (although, since the scope of the
2898 key is a cm_scache_t, the key can be Key(C,S), where S is the
2899 cm_scache_t. But assume a 1-1 relation between keys and clients).
2900 A byte range (O,+L) denotes byte addresses (O) through (O+L-1)
2901 inclusive (a.k.a. [O,O+L-1]). The function Key(x) is implemented
2902 through cm_generateKey() function for both SMB and IFS.
2904 The list of locks for a cm_scache_t object S is maintained in
2905 S->fileLocks. The cache manager will set a lock on the AFS file
2906 server in order to assert the locks in S->fileLocks. If only
2907 shared locks are in place for S, then the cache manager will obtain
2908 a LockRead lock, while if there are any exclusive locks, it will
2909 obtain a LockWrite lock. If the exclusive locks are all released
2910 while the shared locks remain, then the cache manager will
2911 downgrade the lock from LockWrite to LockRead. Similarly, if an
2912 exclusive lock is obtained when only shared locks exist, then the
2913 cache manager will try to upgrade the lock from LockRead to
2916 Each lock L owned by client C maintains a key L->key such that
2917 L->key == Key(C), the effective range defined by L->LOffset and
2918 L->LLength such that the range of bytes affected by the lock is
2919 (L->LOffset, +L->LLength), a type maintained in L->LockType which
2920 is either exclusive or shared.
2924 A lock exists iff it is in S->fileLocks for some cm_scache_t
2925 S. Existing locks are in one of the following states: ACTIVE,
2926 WAITLOCK, WAITUNLOCK, LOST, DELETED.
2928 The following sections describe each lock and the associated
2931 1. ACTIVE: A lock L is ACTIVE iff the cache manager has asserted
2932 the lock with the AFS file server. This type of lock can be
2933 exercised by a client to read or write to the locked region (as
2936 1.1 ACTIVE->LOST: When the AFS file server fails to extend a
2937 server lock that was required to assert the lock. Before
2938 marking the lock as lost, the cache manager checks if the file
2939 has changed on the server. If the file has not changed, then
2940 the cache manager will attempt to obtain a new server lock
2941 that is sufficient to assert the client side locks for the
2942 file. If any of these fail, the lock is marked as LOST.
2943 Otherwise, it is left as ACTIVE.
2945 1.2 ACTIVE->DELETED: Lock is released.
2947 2. WAITLOCK: A lock is in a WAITLOCK state if the cache manager
2948 grants the lock but the lock is yet to be asserted with the AFS
2949 file server. Once the file server grants the lock, the state
2950 will transition to an ACTIVE lock.
2952 2.1 WAITLOCK->ACTIVE: The server granted the lock.
2954 2.2 WAITLOCK->DELETED: Lock is abandoned, or timed out during
2957 2.3 WAITLOCK->LOST: One or more locks from this client were
2958 marked as LOST. No further locks will be granted to this
2959 client until all lost locks are removed.
2961 3. WAITUNLOCK: A lock is in a WAITUNLOCK state if the cache manager
2962 receives a request for a lock that conflicts with an existing
2963 ACTIVE or WAITLOCK lock. The lock will be placed in the queue
2964 and will be granted at such time the conflicting locks are
2965 removed, at which point the state will transition to either
2968 3.1 WAITUNLOCK->ACTIVE: The conflicting lock was removed. The
2969 current serverLock is sufficient to assert this lock, or a
2970 sufficient serverLock is obtained.
2972 3.2 WAITUNLOCK->WAITLOCK: The conflicting lock was removed,
2973 however the required serverLock is yet to be asserted with the
2976 3.3 WAITUNLOCK->DELETED: The lock is abandoned, timed out or
2979 3.5 WAITUNLOCK->LOST: One or more locks from this client were
2980 marked as LOST. No further locks will be granted to this
2981 client until all lost locks are removed.
2983 4. LOST: A lock L is LOST if the server lock that was required to
2984 assert the lock could not be obtained or if it could not be
2985 extended, or if other locks by the same client were LOST.
2986 Essentially, once a lock is LOST, the contract between the cache
2987 manager and that specific client is no longer valid.
2989 The cache manager rechecks the server lock once every minute and
2990 extends it as appropriate. If this is not done for 5 minutes,
2991 the AFS file server will release the lock (the 5 minute timeout
2992 is based on current file server code and is fairly arbitrary).
2993 Once released, the lock cannot be re-obtained without verifying
2994 that the contents of the file hasn't been modified since the
2995 time the lock was released. Re-obtaining the lock without
2996 verifying this may lead to data corruption. If the lock can not
2997 be obtained safely, then all active locks for the cm_scache_t
3000 4.1 LOST->DELETED: The lock is released.
3002 5. DELETED: The lock is no longer relevant. Eventually, it will
3003 get removed from the cm_scache_t. In the meantime, it will be
3004 treated as if it does not exist.
3006 5.1 DELETED->not exist: The lock is removed from the
3009 The following are classifications of locks based on their state.
3011 6* A lock L is ACCEPTED if it is ACTIVE or WAITLOCK. These locks
3012 have been accepted by the cache manager, but may or may not have
3013 been granted back to the client.
3015 7* A lock L is QUEUED if it is ACTIVE, WAITLOCK or WAITUNLOCK.
3017 8* A lock L is WAITING if it is WAITLOCK or WAITUNLOCK.
3021 A client C can READ range (Offset,+Length) of a file represented by
3022 cm_scache_t S iff (1):
3024 1. for all _a_ in (Offset,+Length), all of the following is true:
3026 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3027 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3030 1.2 For each LOST lock L in S->fileLocks such that _a_ in
3031 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3034 (When locks are lost on an cm_scache_t, all locks are lost. By
3035 4.2 (below), if there is an exclusive LOST lock, then there
3036 can't be any overlapping ACTIVE locks.)
3038 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
3040 2. for all _a_ in (Offset,+Length), one of the following is true:
3042 2.1 Byte _a_ of S is unowned (as specified in 1.1) AND there
3043 does not exist a LOST lock L such that _a_ in
3044 (L->LOffset,+L->LLength).
3046 2.2 Byte _a_ of S is owned by C under lock L (as specified in
3047 1.2) AND L->LockType is exclusive.
3049 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
3051 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
3054 3.1 If L->LockType is exclusive then there does NOT exist a
3055 ACCEPTED lock M in S->fileLocks such that _a_ in
3056 (M->LOffset,+M->LLength).
3058 (If we count all QUEUED locks then we hit cases such as
3059 cascading waiting locks where the locks later on in the queue
3060 can be granted without compromising file integrity. On the
3061 other hand if only ACCEPTED locks are considered, then locks
3062 that were received earlier may end up waiting for locks that
3063 were received later to be unlocked. The choice of ACCEPTED
3064 locks was made to mimic the Windows byte range lock
3067 3.2 If L->LockType is shared then for each ACCEPTED lock M in
3068 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
3069 M->LockType is shared.
3071 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
3073 4.1 M->key != Key(C)
3075 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
3076 and (M->LOffset,+M->LLength) do not intersect.
3078 (Note: If a client loses a lock, it loses all locks.
3079 Subsequently, it will not be allowed to obtain any more locks
3080 until all existing LOST locks that belong to the client are
3081 released. Once all locks are released by a single client,
3082 there exists no further contract between the client and AFS
3083 about the contents of the file, hence the client can then
3084 proceed to obtain new locks and establish a new contract.
3086 This doesn't quite work as you think it should, because most
3087 applications aren't built to deal with losing locks they
3088 thought they once had. For now, we don't have a good
3089 solution to lost locks.
3091 Also, for consistency reasons, we have to hold off on
3092 granting locks that overlap exclusive LOST locks.)
3094 A client C can only unlock locks L in S->fileLocks which have
3097 The representation and invariants are as follows:
3099 - Each cm_scache_t structure keeps:
3101 - A queue of byte-range locks (cm_scache_t::fileLocks) which
3102 are of type cm_file_lock_t.
3104 - A record of the highest server-side lock that has been
3105 obtained for this object (cm_scache_t::serverLock), which is
3106 one of (-1), LockRead, LockWrite.
3108 - A count of ACCEPTED exclusive and shared locks that are in the
3109 queue (cm_scache_t::sharedLocks and
3110 cm_scache_t::exclusiveLocks)
3112 - Each cm_file_lock_t structure keeps:
3114 - The type of lock (cm_file_lock_t::LockType)
3116 - The key associated with the lock (cm_file_lock_t::key)
3118 - The offset and length of the lock (cm_file_lock_t::LOffset
3119 and cm_file_lock_t::LLength)
3121 - The state of the lock.
3123 - Time of issuance or last successful extension
3125 Semantic invariants:
3127 I1. The number of ACCEPTED locks in S->fileLocks are
3128 (S->sharedLocks + S->exclusiveLocks)
3130 External invariants:
3132 I3. S->serverLock is the lock that we have asserted with the
3133 AFS file server for this cm_scache_t.
3135 I4. S->serverLock == LockRead iff there is at least one ACTIVE
3136 shared lock, but no ACTIVE exclusive locks.
3138 I5. S->serverLock == LockWrite iff there is at least one ACTIVE
3141 I6. If L is a LOST lock, then for each lock M in S->fileLocks,
3142 M->key == L->key IMPLIES M is LOST or DELETED.
3147 #define IS_LOCK_ACTIVE(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == 0)
3149 #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)
3151 #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)
3153 #define IS_LOCK_LOST(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_LOST)
3155 #define IS_LOCK_DELETED(lockp) (((lockp)->flags & CM_FILELOCK_FLAG_DELETED) == CM_FILELOCK_FLAG_DELETED)
3158 #define IS_LOCK_ACCEPTED(lockp) (IS_LOCK_ACTIVE(lockp) || IS_LOCK_WAITLOCK(lockp))
3161 #define IS_LOCK_CLIENTONLY(lockp) ((((lockp)->scp->flags & CM_SCACHEFLAG_RO) == CM_SCACHEFLAG_RO) || (((lockp)->flags & CM_FILELOCK_FLAG_CLIENTONLY) == CM_FILELOCK_FLAG_CLIENTONLY))
3164 #define INTERSECT_RANGE(r1,r2) (((r2).offset+(r2).length) > (r1).offset && ((r1).offset +(r1).length) > (r2).offset)
3167 #define CONTAINS_RANGE(r1,r2) (((r2).offset+(r2).length) <= ((r1).offset+(r1).length) && (r1).offset <= (r2).offset)
3169 #if defined(VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS) && !defined(LOCK_TESTING)
3170 #define SCP_SUPPORTS_BRLOCKS(scp) ((scp)->cbServerp && ((scp)->cbServerp->capabilities & VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS))
3172 #define SCP_SUPPORTS_BRLOCKS(scp) (1)
3175 #define SERVERLOCKS_ENABLED(scp) (!((scp)->flags & CM_SCACHEFLAG_RO) && cm_enableServerLocks && SCP_SUPPORTS_BRLOCKS(scp))
3177 static void cm_LockRangeSubtract(cm_range_t * pos, const cm_range_t * neg)
3179 afs_int64 int_begin;
3182 int_begin = MAX(pos->offset, neg->offset);
3183 int_end = MIN(pos->offset+pos->length, neg->offset+neg->length);
3185 if (int_begin < int_end) {
3186 if (int_begin == pos->offset) {
3187 pos->length = pos->offset + pos->length - int_end;
3188 pos->offset = int_end;
3189 } else if (int_end == pos->offset + pos->length) {
3190 pos->length = int_begin - pos->offset;
3193 /* We only subtract ranges if the resulting range is
3194 contiguous. If we try to support non-contigous ranges, we
3195 aren't actually improving performance. */
3199 /* Called with scp->mx held. Returns 0 if all is clear to read the
3200 specified range by the client identified by key.
3202 long cm_LockCheckRead(cm_scache_t *scp,
3203 LARGE_INTEGER LOffset,
3204 LARGE_INTEGER LLength,
3207 #ifndef ADVISORY_LOCKS
3209 cm_file_lock_t *fileLock;
3213 int substract_ranges = FALSE;
3215 range.offset = LOffset.QuadPart;
3216 range.length = LLength.QuadPart;
3220 1. for all _a_ in (Offset,+Length), all of the following is true:
3222 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3223 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3226 1.2 For each LOST lock L in S->fileLocks such that _a_ in
3227 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3232 lock_ObtainRead(&cm_scacheLock);
3234 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
3236 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
3238 if (INTERSECT_RANGE(range, fileLock->range)) {
3239 if (IS_LOCK_ACTIVE(fileLock)) {
3240 if (fileLock->key == key) {
3242 /* If there is an active lock for this client, it
3243 is safe to substract ranges.*/
3244 cm_LockRangeSubtract(&range, &fileLock->range);
3245 substract_ranges = TRUE;
3247 if (fileLock->lockType != LockRead) {
3248 code = CM_ERROR_LOCK_CONFLICT;
3252 /* even if the entire range is locked for reading,
3253 we still can't grant the lock at this point
3254 because the client may have lost locks. That
3255 is, unless we have already seen an active lock
3256 belonging to the client, in which case there
3257 can't be any lost locks for this client. */
3258 if (substract_ranges)
3259 cm_LockRangeSubtract(&range, &fileLock->range);
3261 } else if (IS_LOCK_LOST(fileLock) &&
3262 (fileLock->key == key || fileLock->lockType == LockWrite)) {
3263 code = CM_ERROR_BADFD;
3269 lock_ReleaseRead(&cm_scacheLock);
3271 osi_Log4(afsd_logp, "cm_LockCheckRead scp 0x%x offset %d length %d code 0x%x",
3272 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
3283 /* Called with scp->mx held. Returns 0 if all is clear to write the
3284 specified range by the client identified by key.
3286 long cm_LockCheckWrite(cm_scache_t *scp,
3287 LARGE_INTEGER LOffset,
3288 LARGE_INTEGER LLength,
3291 #ifndef ADVISORY_LOCKS
3293 cm_file_lock_t *fileLock;
3298 range.offset = LOffset.QuadPart;
3299 range.length = LLength.QuadPart;
3302 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
3304 2. for all _a_ in (Offset,+Length), one of the following is true:
3306 2.1 Byte _a_ of S is unowned AND there does not exist a LOST
3307 lock L such that _a_ in (L->LOffset,+L->LLength).
3309 2.2 Byte _a_ of S is owned by C under lock L AND L->LockType is
3313 lock_ObtainRead(&cm_scacheLock);
3315 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
3317 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
3319 if (INTERSECT_RANGE(range, fileLock->range)) {
3320 if (IS_LOCK_ACTIVE(fileLock)) {
3321 if (fileLock->key == key) {
3322 if (fileLock->lockType == LockWrite) {
3324 /* if there is an active lock for this client, it
3325 is safe to substract ranges */
3326 cm_LockRangeSubtract(&range, &fileLock->range);
3328 code = CM_ERROR_LOCK_CONFLICT;
3332 code = CM_ERROR_LOCK_CONFLICT;
3335 } else if (IS_LOCK_LOST(fileLock)) {
3336 code = CM_ERROR_BADFD;
3342 lock_ReleaseRead(&cm_scacheLock);
3344 osi_Log4(afsd_logp, "cm_LockCheckWrite scp 0x%x offset %d length %d code 0x%x",
3345 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
3357 static void cm_LockMarkSCacheLost(cm_scache_t * scp);
3359 /* Called with cm_scacheLock write locked */
3360 static cm_file_lock_t * cm_GetFileLock(void) {
3363 l = (cm_file_lock_t *) cm_freeFileLocks;
3365 osi_QRemove(&cm_freeFileLocks, &l->q);
3367 l = malloc(sizeof(cm_file_lock_t));
3371 memset(l, 0, sizeof(cm_file_lock_t));
3376 /* Called with cm_scacheLock write locked */
3377 static void cm_PutFileLock(cm_file_lock_t *l) {
3378 osi_QAdd(&cm_freeFileLocks, &l->q);
3381 /* called with scp->mx held. May release it during processing, but
3382 leaves it held on exit. */
3383 long cm_IntSetLock(cm_scache_t * scp, cm_user_t * userp, int lockType,
3389 struct rx_connection * callp;
3392 tfid.Volume = scp->fid.volume;
3393 tfid.Vnode = scp->fid.vnode;
3394 tfid.Unique = scp->fid.unique;
3397 osi_Log2(afsd_logp, "CALL SetLock scp 0x%p for lock %d", scp, lockType);
3399 lock_ReleaseMutex(&scp->mx);
3402 code = cm_Conn(&cfid, userp, reqp, &connp);
3406 callp = cm_GetRxConn(connp);
3407 code = RXAFS_SetLock(callp, &tfid, lockType,
3409 rx_PutConnection(callp);
3411 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
3414 code = cm_MapRPCError(code, reqp);
3416 osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
3418 osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
3421 lock_ObtainMutex(&scp->mx);
3426 /* called with scp->mx held. Releases it during processing */
3427 long cm_IntReleaseLock(cm_scache_t * scp, cm_user_t * userp,
3433 struct rx_connection * callp;
3436 tfid.Volume = scp->fid.volume;
3437 tfid.Vnode = scp->fid.vnode;
3438 tfid.Unique = scp->fid.unique;
3441 lock_ReleaseMutex(&scp->mx);
3443 osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
3446 code = cm_Conn(&cfid, userp, reqp, &connp);
3450 callp = cm_GetRxConn(connp);
3451 code = RXAFS_ReleaseLock(callp, &tfid, &volSync);
3452 rx_PutConnection(callp);
3454 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
3456 code = cm_MapRPCError(code, reqp);
3459 "CALL ReleaseLock FAILURE, code 0x%x", code);
3462 "CALL ReleaseLock SUCCESS");
3464 lock_ObtainMutex(&scp->mx);
3469 /* called with scp->mx held. May release it during processing, but
3470 will exit with lock held.
3474 - 0 if the user has permission to get the specified lock for the scp
3476 - CM_ERROR_NOACCESS if not
3478 Any other error from cm_SyncOp will be sent down untranslated.
3480 long cm_LockCheckPerms(cm_scache_t * scp,
3488 /* lock permissions are slightly tricky because of the 'i' bit.
3489 If the user has PRSFS_LOCK, she can read-lock the file. If the
3490 user has PRSFS_WRITE, she can write-lock the file. However, if
3491 the user has PRSFS_INSERT, then she can write-lock new files,
3492 but not old ones. Since we don't have information about
3493 whether a file is new or not, we assume that if the user owns
3494 the scp, then she has the permissions that are granted by
3497 osi_Log3(afsd_logp, "cm_LockCheckPerms for scp[0x%p] type[%d] user[0x%p]",
3498 scp, lock_type, userp);
3500 if (lock_type == LockRead)
3501 rights |= PRSFS_LOCK;
3502 else if (lock_type == LockWrite)
3503 rights |= PRSFS_WRITE;
3510 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
3511 CM_SCACHESYNC_GETSTATUS |
3512 CM_SCACHESYNC_NEEDCALLBACK);
3514 if (code == CM_ERROR_NOACCESS &&
3515 lock_type == LockWrite &&
3516 scp->creator == userp) {
3517 /* check for PRSFS_INSERT. */
3519 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
3520 CM_SCACHESYNC_GETSTATUS |
3521 CM_SCACHESYNC_NEEDCALLBACK);
3523 if (code == CM_ERROR_NOACCESS)
3524 osi_Log0(afsd_logp, "cm_LockCheckPerms user is creator but has no INSERT bits for scp");
3527 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
3529 osi_Log1(afsd_logp, "cm_LockCheckPerms returning code %d", code);
3534 /* called with scp->mx held */
3535 long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
3536 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
3538 int allowWait, cm_user_t *userp, cm_req_t *reqp,
3539 cm_file_lock_t **lockpp)
3542 int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
3543 cm_file_lock_t *fileLock;
3546 int wait_unlock = FALSE;
3547 int force_client_lock = FALSE;
3549 osi_Log4(afsd_logp, "cm_Lock scp 0x%x type 0x%x offset %d length %d",
3550 scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
3551 osi_Log3(afsd_logp, "... allowWait %d key 0x%x:%x", allowWait,
3552 (unsigned long)(key >> 32), (unsigned long)(key & 0xffffffff));
3555 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
3557 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
3560 3.1 If L->LockType is exclusive then there does NOT exist a
3561 ACCEPTED lock M in S->fileLocks such that _a_ in
3562 (M->LOffset,+M->LLength).
3564 3.2 If L->LockType is shared then for each ACCEPTED lock M in
3565 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
3566 M->LockType is shared.
3568 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
3570 4.1 M->key != Key(C)
3572 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
3573 and (M->LOffset,+M->LLength) do not intersect.
3576 range.offset = LOffset.QuadPart;
3577 range.length = LLength.QuadPart;
3579 lock_ObtainRead(&cm_scacheLock);
3581 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
3583 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
3585 if (IS_LOCK_LOST(fileLock)) {
3586 if (fileLock->key == key) {
3587 code = CM_ERROR_BADFD;
3589 } else if (fileLock->lockType == LockWrite && INTERSECT_RANGE(range, fileLock->range)) {
3590 code = CM_ERROR_WOULDBLOCK;
3596 /* we don't need to check for deleted locks here since deleted
3597 locks are dequeued from scp->fileLocks */
3598 if (IS_LOCK_ACCEPTED(fileLock) &&
3599 INTERSECT_RANGE(range, fileLock->range)) {
3601 if ((sLockType & LOCKING_ANDX_SHARED_LOCK) == 0 ||
3602 fileLock->lockType != LockRead) {
3604 code = CM_ERROR_WOULDBLOCK;
3610 lock_ReleaseRead(&cm_scacheLock);
3612 if (code == 0 && SERVERLOCKS_ENABLED(scp)) {
3613 if (Which == scp->serverLock ||
3614 (Which == LockRead && scp->serverLock == LockWrite)) {
3616 /* we already have the lock we need */
3617 osi_Log3(afsd_logp, " we already have the correct lock. exclusives[%d], shared[%d], serverLock[%d]",
3618 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
3620 code = cm_LockCheckPerms(scp, Which, userp, reqp);
3622 /* special case: if we don't have permission to read-lock
3623 the file, then we force a clientside lock. This is to
3624 compensate for applications that obtain a read-lock for
3625 reading files off of directories that don't grant
3626 read-locks to the user. */
3627 if (code == CM_ERROR_NOACCESS && Which == LockRead) {
3628 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
3629 force_client_lock = TRUE;
3632 } else if ((scp->exclusiveLocks > 0) ||
3633 (scp->sharedLocks > 0 && scp->serverLock != LockRead)) {
3635 /* We are already waiting for some other lock. We should
3636 wait for the daemon to catch up instead of generating a
3637 flood of SetLock calls. */
3638 osi_Log3(afsd_logp, " already waiting for other lock. exclusives[%d], shared[%d], serverLock[%d]",
3639 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
3641 /* see if we have permission to create the lock in the
3643 code = cm_LockCheckPerms(scp, Which, userp, reqp);
3645 code = CM_ERROR_WOULDBLOCK;
3646 else if (code == CM_ERROR_NOACCESS && Which == LockRead) {
3647 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
3648 force_client_lock = TRUE;
3651 /* leave any other codes as-is */
3655 int check_data_version = FALSE;
3657 /* first check if we have permission to elevate or obtain
3659 code = cm_LockCheckPerms(scp, Which, userp, reqp);
3661 if (code == CM_ERROR_NOACCESS && Which == LockRead) {
3662 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
3663 force_client_lock = TRUE;
3668 if (scp->serverLock == LockRead && Which == LockWrite) {
3670 /* We want to escalate the lock to a LockWrite.
3671 Unfortunately that's not really possible without
3672 letting go of the current lock. But for now we do
3676 " attempting to UPGRADE from LockRead to LockWrite.");
3678 " dataVersion on scp: %d", scp->dataVersion);
3680 /* we assume at this point (because scp->serverLock
3681 was valid) that we had a valid server lock. */
3682 scp->lockDataVersion = scp->dataVersion;
3683 check_data_version = TRUE;
3685 code = cm_IntReleaseLock(scp, userp, reqp);
3688 /* We couldn't release the lock */
3691 scp->serverLock = -1;
3695 /* We need to obtain a server lock of type Which in order
3696 to assert this file lock */
3697 #ifndef AGGRESSIVE_LOCKS
3700 newLock = LockWrite;
3702 code = cm_IntSetLock(scp, userp, newLock, reqp);
3704 if (code == CM_ERROR_WOULDBLOCK && newLock != Which) {
3705 /* we wanted LockRead. We tried LockWrite. Now try
3710 osi_assert(newLock == LockRead);
3712 code = cm_IntSetLock(scp, userp, newLock, reqp);
3715 if (code == 0 && check_data_version &&
3716 scp->dataVersion != scp->lockDataVersion) {
3717 /* We lost a race. Although we successfully obtained
3718 a lock, someone modified the file in between. The
3719 locks have all been technically lost. */
3722 " Data version mismatch while upgrading lock.");
3724 " Data versions before=%d, after=%d",
3725 scp->lockDataVersion,
3728 " Releasing stale lock for scp 0x%x", scp);
3730 code = cm_IntReleaseLock(scp, userp, reqp);
3732 scp->serverLock = -1;
3734 code = CM_ERROR_INVAL;
3735 } else if (code == 0) {
3736 scp->serverLock = newLock;
3737 scp->lockDataVersion = scp->dataVersion;
3741 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
3742 scp->serverLock == -1) {
3743 /* Oops. We lost the lock. */
3744 cm_LockMarkSCacheLost(scp);
3747 } else if (code == 0) { /* server locks not enabled */
3749 " Skipping server lock for scp");
3754 if (code != 0 && !force_client_lock) {
3755 /* Special case error translations
3757 Applications don't expect certain errors from a
3758 LockFile/UnlockFile call. We need to translate some error
3759 code to codes that apps expect and handle. */
3761 /* We shouldn't actually need to handle this case since we
3762 simulate locks for RO scps anyway. */
3763 if (code == CM_ERROR_READONLY) {
3764 osi_Log0(afsd_logp, " Reinterpreting CM_ERROR_READONLY as CM_ERROR_NOACCESS");
3765 code = CM_ERROR_NOACCESS;
3769 if (code == 0 || (code == CM_ERROR_WOULDBLOCK && allowWait) ||
3770 force_client_lock) {
3772 /* clear the error if we are forcing a client lock, so we
3773 don't get confused later. */
3774 if (force_client_lock && code != CM_ERROR_WOULDBLOCK)
3777 lock_ObtainWrite(&cm_scacheLock);
3778 fileLock = cm_GetFileLock();
3779 lock_ReleaseWrite(&cm_scacheLock);
3781 fileLock->fid = scp->fid;
3783 fileLock->key = key;
3784 fileLock->lockType = Which;
3786 fileLock->userp = userp;
3787 fileLock->range = range;
3788 fileLock->flags = (code == 0 ? 0 :
3790 CM_FILELOCK_FLAG_WAITUNLOCK :
3791 CM_FILELOCK_FLAG_WAITLOCK));
3793 if (force_client_lock || !SERVERLOCKS_ENABLED(scp))
3794 fileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
3796 fileLock->lastUpdate = (code == 0 && !force_client_lock) ? time(NULL) : 0;
3798 lock_ObtainWrite(&cm_scacheLock);
3799 osi_QAddT(&scp->fileLocksH, &scp->fileLocksT, &fileLock->fileq);
3800 cm_HoldSCacheNoLock(scp);
3801 fileLock->scp = scp;
3802 osi_QAdd(&cm_allFileLocks, &fileLock->q);
3803 lock_ReleaseWrite(&cm_scacheLock);
3809 if (IS_LOCK_CLIENTONLY(fileLock)) {
3811 } else if (IS_LOCK_ACCEPTED(fileLock)) {
3812 if (Which == LockRead)
3815 scp->exclusiveLocks++;
3819 "cm_Lock Lock added 0x%p flags 0x%x to scp [0x%p]",
3820 fileLock, fileLock->flags, scp);
3822 " exclusives[%d] shared[%d] client[%d] serverLock[%d]",
3823 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
3824 (int)(signed char) scp->serverLock);
3827 "cm_Lock Rejecting lock (code = 0x%x)", code);
3833 static int cm_KeyEquals(cm_key_t k1, cm_key_t k2, int flags);
3835 /* Called with scp->mx held */
3836 long cm_UnlockByKey(cm_scache_t * scp,
3843 cm_file_lock_t *fileLock;
3844 osi_queue_t *q, *qn;
3847 osi_Log4(afsd_logp, "cm_UnlockByKey scp 0x%p key 0x%x:%x flags=0x%x",
3849 (unsigned long)(key >> 32),
3850 (unsigned long)(key & 0xffffffff),
3853 lock_ObtainWrite(&cm_scacheLock);
3855 for (q = scp->fileLocksH; q; q = qn) {
3858 fileLock = (cm_file_lock_t *)
3859 ((char *) q - offsetof(cm_file_lock_t, fileq));
3862 osi_Log4(afsd_logp, " Checking lock[0x%x] range[%d,+%d] type[%d]",
3864 (unsigned long) fileLock->range.offset,
3865 (unsigned long) fileLock->range.length,
3866 fileLock->lockType);
3867 osi_Log3(afsd_logp, " key[0x%x:%x] flags[0x%x]",
3868 (unsigned long)(fileLock->key >> 32),
3869 (unsigned long)(fileLock->key & 0xffffffff),
3872 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
3873 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
3874 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
3876 fileLock->fid.volume,
3877 fileLock->fid.vnode,
3878 fileLock->fid.unique);
3879 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
3880 fileLock->scp->fid.cell,
3881 fileLock->scp->fid.volume,
3882 fileLock->scp->fid.vnode,
3883 fileLock->scp->fid.unique);
3888 if (!IS_LOCK_DELETED(fileLock) &&
3889 cm_KeyEquals(fileLock->key, key, flags)) {
3890 osi_Log3(afsd_logp, "...Unlock range [%d,+%d] type %d",
3891 fileLock->range.offset,
3892 fileLock->range.length,
3893 fileLock->lockType);
3895 if (scp->fileLocksT == q)
3896 scp->fileLocksT = osi_QPrev(q);
3897 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
3899 if (IS_LOCK_CLIENTONLY(fileLock)) {
3901 } else if (IS_LOCK_ACCEPTED(fileLock)) {
3902 if (fileLock->lockType == LockRead)
3905 scp->exclusiveLocks--;
3908 fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
3910 cm_ReleaseUser(fileLock->userp);
3911 cm_ReleaseSCacheNoLock(scp);
3913 fileLock->userp = NULL;
3914 fileLock->scp = NULL;
3920 lock_ReleaseWrite(&cm_scacheLock);
3922 if (n_unlocks == 0) {
3923 osi_Log0(afsd_logp, "cm_UnlockByKey no locks found");
3924 osi_Log3(afsd_logp, " Leaving scp with exclusives[%d], shared[%d], serverLock[%d]",
3925 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
3930 osi_Log1(afsd_logp, "cm_UnlockByKey done with %d locks", n_unlocks);
3932 osi_assertx(scp->sharedLocks >= 0, "scp->sharedLocks < 0");
3933 osi_assertx(scp->exclusiveLocks >= 0, "scp->exclusiveLocks < 0");
3934 osi_assertx(scp->clientLocks >= 0, "scp->clientLocks < 0");
3936 if (!SERVERLOCKS_ENABLED(scp)) {
3937 osi_Log0(afsd_logp, " Skipping server lock for scp");
3941 /* Ideally we would go through the rest of the locks to determine
3942 * if one or more locks that were formerly in WAITUNLOCK can now
3943 * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
3944 * scp->sharedLocks accordingly. However, the retrying of locks
3945 * in that manner is done cm_RetryLock() manually.
3948 if (scp->serverLock == LockWrite &&
3949 scp->exclusiveLocks == 0 &&
3950 scp->sharedLocks > 0) {
3952 /* The serverLock should be downgraded to LockRead */
3953 osi_Log0(afsd_logp, " DOWNGRADE lock from LockWrite to LockRead");
3955 /* since scp->serverLock looked sane, we are going to assume
3956 that we have a valid server lock. */
3957 scp->lockDataVersion = scp->dataVersion;
3958 osi_Log1(afsd_logp, " dataVersion on scp = %d", scp->dataVersion);
3960 code = cm_IntReleaseLock(scp, userp, reqp);
3963 /* so we couldn't release it. Just let the lock be for now */
3967 scp->serverLock = -1;
3970 code = cm_IntSetLock(scp, userp, LockRead, reqp);
3972 if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
3973 scp->serverLock = LockRead;
3974 } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
3975 /* We lost a race condition. Although we have a valid
3976 lock on the file, the data has changed and essentially
3977 we have lost the lock we had during the transition. */
3979 osi_Log0(afsd_logp, "Data version mismatch during lock downgrade");
3980 osi_Log2(afsd_logp, " Data versions before=%d, after=%d",
3981 scp->lockDataVersion,
3984 code = cm_IntReleaseLock(scp, userp, reqp);
3986 code = CM_ERROR_INVAL;
3987 scp->serverLock = -1;
3991 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
3992 (scp->serverLock == -1)) {
3994 cm_LockMarkSCacheLost(scp);
3997 /* failure here has no bearing on the return value of
4001 } else if (scp->serverLock != (-1) &&
4002 scp->exclusiveLocks == 0 &&
4003 scp->sharedLocks == 0) {
4004 /* The serverLock should be released entirely */
4006 code = cm_IntReleaseLock(scp, userp, reqp);
4009 scp->serverLock = (-1);
4014 osi_Log1(afsd_logp, "cm_UnlockByKey code 0x%x", code);
4015 osi_Log4(afsd_logp, " Leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
4016 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4017 (int)(signed char) scp->serverLock);
4022 long cm_Unlock(cm_scache_t *scp,
4023 unsigned char sLockType,
4024 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
4030 int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
4031 cm_file_lock_t *fileLock;
4033 int release_userp = FALSE;
4035 osi_Log4(afsd_logp, "cm_Unlock scp 0x%p type 0x%x offset %d length %d",
4036 scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
4037 osi_Log2(afsd_logp, "... key 0x%x:%x",
4038 (unsigned long) (key >> 32), (unsigned long) (key & 0xffffffff));
4040 lock_ObtainRead(&cm_scacheLock);
4042 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4043 fileLock = (cm_file_lock_t *)
4044 ((char *) q - offsetof(cm_file_lock_t, fileq));
4047 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
4048 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
4049 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4051 fileLock->fid.volume,
4052 fileLock->fid.vnode,
4053 fileLock->fid.unique);
4054 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4055 fileLock->scp->fid.cell,
4056 fileLock->scp->fid.volume,
4057 fileLock->scp->fid.vnode,
4058 fileLock->scp->fid.unique);
4062 if (!IS_LOCK_DELETED(fileLock) &&
4063 fileLock->key == key &&
4064 fileLock->range.offset == LOffset.QuadPart &&
4065 fileLock->range.length == LLength.QuadPart) {
4071 osi_Log0(afsd_logp, "cm_Unlock lock not found; failure");
4073 lock_ReleaseRead(&cm_scacheLock);
4075 /* The lock didn't exist anyway. *shrug* */
4079 lock_ReleaseRead(&cm_scacheLock);
4081 /* discard lock record */
4082 lock_ObtainWrite(&cm_scacheLock);
4083 if (scp->fileLocksT == q)
4084 scp->fileLocksT = osi_QPrev(q);
4085 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
4088 * Don't delete it here; let the daemon delete it, to simplify
4089 * the daemon's traversal of the list.
4092 if (IS_LOCK_CLIENTONLY(fileLock)) {
4094 } else if (IS_LOCK_ACCEPTED(fileLock)) {
4095 if (fileLock->lockType == LockRead)
4098 scp->exclusiveLocks--;
4101 fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
4102 if (userp != NULL) {
4103 cm_ReleaseUser(fileLock->userp);
4105 userp = fileLock->userp;
4106 release_userp = TRUE;
4108 fileLock->userp = NULL;
4109 cm_ReleaseSCacheNoLock(scp);
4110 fileLock->scp = NULL;
4111 lock_ReleaseWrite(&cm_scacheLock);
4113 if (!SERVERLOCKS_ENABLED(scp)) {
4114 osi_Log0(afsd_logp, " Skipping server locks for scp");
4118 /* Ideally we would go through the rest of the locks to determine
4119 * if one or more locks that were formerly in WAITUNLOCK can now
4120 * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
4121 * scp->sharedLocks accordingly. However, the retrying of locks
4122 * in that manner is done cm_RetryLock() manually.
4125 if (scp->serverLock == LockWrite &&
4126 scp->exclusiveLocks == 0 &&
4127 scp->sharedLocks > 0) {
4129 /* The serverLock should be downgraded to LockRead */
4130 osi_Log0(afsd_logp, " DOWNGRADE lock from LockWrite to LockRead");
4132 /* Since we already had a lock, we assume that there is a
4133 valid server lock. */
4134 scp->lockDataVersion = scp->dataVersion;
4135 osi_Log1(afsd_logp, " dataVersion on scp is %d", scp->dataVersion);
4137 code = cm_IntReleaseLock(scp, userp, reqp);
4140 /* so we couldn't release it. Just let the lock be for now */
4144 scp->serverLock = -1;
4147 code = cm_IntSetLock(scp, userp, LockRead, reqp);
4149 if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
4150 scp->serverLock = LockRead;
4151 } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
4152 /* Lost a race. We obtained a new lock, but that is
4153 meaningless since someone modified the file
4157 "Data version mismatch while downgrading lock");
4159 " Data versions before=%d, after=%d",
4160 scp->lockDataVersion,
4163 code = cm_IntReleaseLock(scp, userp, reqp);
4165 scp->serverLock = -1;
4166 code = CM_ERROR_INVAL;
4170 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4171 (scp->serverLock == -1)) {
4173 cm_LockMarkSCacheLost(scp);
4176 /* failure here has no bearing on the return value of
4180 } else if (scp->serverLock != (-1) &&
4181 scp->exclusiveLocks == 0 &&
4182 scp->sharedLocks == 0) {
4183 /* The serverLock should be released entirely */
4185 code = cm_IntReleaseLock(scp, userp, reqp);
4188 scp->serverLock = (-1);
4193 cm_ReleaseUser(userp);
4197 osi_Log1(afsd_logp, "cm_Unlock code 0x%x", code);
4198 osi_Log4(afsd_logp, " leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
4199 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4200 (int)(signed char) scp->serverLock);
4205 /* called with scp->mx held */
4206 static void cm_LockMarkSCacheLost(cm_scache_t * scp)
4208 cm_file_lock_t *fileLock;
4211 osi_Log1(afsd_logp, "cm_LockMarkSCacheLost scp 0x%x", scp);
4214 /* With the current code, we can't lose a lock on a RO scp */
4215 osi_assert(!(scp->flags & CM_SCACHEFLAG_RO));
4218 /* cm_scacheLock needed because we are modifying fileLock->flags */
4219 lock_ObtainWrite(&cm_scacheLock);
4221 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4223 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4225 if (IS_LOCK_ACTIVE(fileLock) &&
4226 !IS_LOCK_CLIENTONLY(fileLock)) {
4227 if (fileLock->lockType == LockRead)
4230 scp->exclusiveLocks--;
4232 fileLock->flags |= CM_FILELOCK_FLAG_LOST;
4236 scp->serverLock = -1;
4237 scp->lockDataVersion = -1;
4238 lock_ReleaseWrite(&cm_scacheLock);
4241 /* Called with no relevant locks held */
4242 void cm_CheckLocks()
4244 osi_queue_t *q, *nq;
4245 cm_file_lock_t *fileLock;
4251 struct rx_connection * callp;
4256 lock_ObtainWrite(&cm_scacheLock);
4258 cm_lockRefreshCycle++;
4260 osi_Log1(afsd_logp, "cm_CheckLocks starting lock check cycle %d", cm_lockRefreshCycle);
4262 for (q = cm_allFileLocks; q; q = nq) {
4263 fileLock = (cm_file_lock_t *) q;
4267 if (IS_LOCK_DELETED(fileLock)) {
4269 osi_QRemove(&cm_allFileLocks, q);
4270 cm_PutFileLock(fileLock);
4272 } else if (IS_LOCK_ACTIVE(fileLock) && !IS_LOCK_CLIENTONLY(fileLock)) {
4274 /* Server locks must have been enabled for us to have
4275 received an active non-client-only lock. */
4276 osi_assert(cm_enableServerLocks);
4278 scp = fileLock->scp;
4279 osi_assert(scp != NULL);
4281 cm_HoldSCacheNoLock(scp);
4284 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
4285 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
4286 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4288 fileLock->fid.volume,
4289 fileLock->fid.vnode,
4290 fileLock->fid.unique);
4291 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4292 fileLock->scp->fid.cell,
4293 fileLock->scp->fid.volume,
4294 fileLock->scp->fid.vnode,
4295 fileLock->scp->fid.unique);
4299 /* Server locks are extended once per scp per refresh
4301 if (scp->lastRefreshCycle != cm_lockRefreshCycle) {
4303 int scp_done = FALSE;
4305 osi_Log1(afsd_logp, "cm_CheckLocks Updating scp 0x%x", scp);
4307 lock_ReleaseWrite(&cm_scacheLock);
4308 lock_ObtainMutex(&scp->mx);
4310 /* did the lock change while we weren't holding the lock? */
4311 if (!IS_LOCK_ACTIVE(fileLock))
4312 goto post_syncopdone;
4314 code = cm_SyncOp(scp, NULL, fileLock->userp, &req, 0,
4315 CM_SCACHESYNC_NEEDCALLBACK
4316 | CM_SCACHESYNC_GETSTATUS
4317 | CM_SCACHESYNC_LOCK);
4321 "cm_CheckLocks SyncOp failure code 0x%x", code);
4322 goto post_syncopdone;
4325 /* cm_SyncOp releases scp->mx during which the lock
4326 may get released. */
4327 if (!IS_LOCK_ACTIVE(fileLock))
4328 goto pre_syncopdone;
4330 if (scp->serverLock != -1) {
4334 tfid.Volume = scp->fid.volume;
4335 tfid.Vnode = scp->fid.vnode;
4336 tfid.Unique = scp->fid.unique;
4338 userp = fileLock->userp;
4340 osi_Log3(afsd_logp, "CALL ExtendLock lock 0x%p for scp=0x%p with lock %d",
4343 (int) scp->serverLock);
4345 lock_ReleaseMutex(&scp->mx);
4348 code = cm_Conn(&cfid, userp,
4353 callp = cm_GetRxConn(connp);
4354 code = RXAFS_ExtendLock(callp, &tfid,
4356 rx_PutConnection(callp);
4358 osi_Log1(afsd_logp, " ExtendLock returns %d", code);
4360 } while (cm_Analyze(connp, userp, &req,
4361 &cfid, &volSync, NULL, NULL,
4364 code = cm_MapRPCError(code, &req);
4366 lock_ObtainMutex(&scp->mx);
4369 osi_Log1(afsd_logp, "CALL ExtendLock FAILURE, code 0x%x", code);
4371 osi_Log0(afsd_logp, "CALL ExtendLock SUCCESS");
4372 scp->lockDataVersion = scp->dataVersion;
4375 if ((code == EINVAL || code == CM_ERROR_INVAL) &&
4376 scp->lockDataVersion == scp->dataVersion) {
4380 (scp->exclusiveLocks > 0) ? LockWrite: LockRead;
4382 /* we might still have a chance to obtain a
4385 code = cm_IntSetLock(scp, userp, lockType, &req);
4388 code = CM_ERROR_INVAL;
4389 } else if (scp->lockDataVersion != scp->dataVersion) {
4391 /* now check if we still have the file at
4392 the right data version. */
4394 "Data version mismatch on scp 0x%p",
4397 " Data versions: before=%d, after=%d",
4398 scp->lockDataVersion,
4401 code = cm_IntReleaseLock(scp, userp, &req);
4403 code = CM_ERROR_INVAL;
4407 if (code == EINVAL || code == CM_ERROR_INVAL) {
4408 cm_LockMarkSCacheLost(scp);
4412 /* interestingly, we have found an active lock
4413 belonging to an scache that has no
4415 cm_LockMarkSCacheLost(scp);
4422 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
4425 lock_ReleaseMutex(&scp->mx);
4427 lock_ObtainWrite(&cm_scacheLock);
4430 fileLock->lastUpdate = time(NULL);
4434 scp->lastRefreshCycle = cm_lockRefreshCycle;
4437 /* we have already refreshed the locks on this scp */
4438 fileLock->lastUpdate = time(NULL);
4441 cm_ReleaseSCacheNoLock(scp);
4443 } else if (IS_LOCK_ACTIVE(fileLock) && IS_LOCK_CLIENTONLY(fileLock)) {
4444 /* TODO: Check callbacks */
4448 lock_ReleaseWrite(&cm_scacheLock);
4449 osi_Log1(afsd_logp, "cm_CheckLocks completes lock check cycle %d", cm_lockRefreshCycle);
4452 /* NOT called with scp->mx held. */
4453 long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
4456 cm_scache_t *scp = NULL;
4457 cm_file_lock_t *fileLock;
4461 int force_client_lock = FALSE;
4465 if (client_is_dead) {
4466 code = CM_ERROR_TIMEDOUT;
4470 lock_ObtainRead(&cm_scacheLock);
4472 osi_Log2(afsd_logp, "cm_RetryLock checking lock %p (scp=%p)", oldFileLock, oldFileLock->scp);
4473 osi_Log4(afsd_logp, " offset(%x:%x) length(%x:%x)",
4474 (unsigned)(oldFileLock->range.offset >> 32),
4475 (unsigned)(oldFileLock->range.offset & 0xffffffff),
4476 (unsigned)(oldFileLock->range.length >> 32),
4477 (unsigned)(oldFileLock->range.length & 0xffffffff));
4478 osi_Log3(afsd_logp, " key(%x:%x) flags=%x",
4479 (unsigned)(oldFileLock->key >> 32),
4480 (unsigned)(oldFileLock->key & 0xffffffff),
4481 (unsigned)(oldFileLock->flags));
4483 /* if the lock has already been granted, then we have nothing to do */
4484 if (IS_LOCK_ACTIVE(oldFileLock)) {
4485 lock_ReleaseRead(&cm_scacheLock);
4486 osi_Log0(afsd_logp, "cm_RetryLock lock already granted");
4490 /* we can't do anything with lost or deleted locks at the moment. */
4491 if (IS_LOCK_LOST(oldFileLock) || IS_LOCK_DELETED(oldFileLock)) {
4492 code = CM_ERROR_BADFD;
4493 osi_Log0(afsd_logp, "cm_RetryLock lock is lost or deleted");
4494 lock_ReleaseRead(&cm_scacheLock);
4498 scp = oldFileLock->scp;
4500 osi_assert(scp != NULL);
4502 lock_ReleaseRead(&cm_scacheLock);
4503 lock_ObtainMutex(&scp->mx);
4505 code = cm_LockCheckPerms(scp, oldFileLock->lockType,
4509 if (code == CM_ERROR_NOACCESS && oldFileLock->lockType == LockRead) {
4510 force_client_lock = TRUE;
4513 lock_ReleaseMutex(&scp->mx);
4517 lock_ObtainWrite(&cm_scacheLock);
4519 /* Check if we already have a sufficient server lock to allow this
4520 lock to go through. */
4521 if (IS_LOCK_WAITLOCK(oldFileLock) &&
4522 (!SERVERLOCKS_ENABLED(scp) ||
4523 scp->serverLock == oldFileLock->lockType ||
4524 scp->serverLock == LockWrite)) {
4526 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
4528 if (SERVERLOCKS_ENABLED(scp)) {
4529 osi_Log1(afsd_logp, "cm_RetryLock Server lock (%d) is sufficient for lock. Granting",
4530 (int) scp->serverLock);
4532 osi_Log0(afsd_logp, "cm_RetryLock skipping server lock for scp");
4535 lock_ReleaseWrite(&cm_scacheLock);
4536 lock_ReleaseMutex(&scp->mx);
4541 if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
4543 /* check if the conflicting locks have dissappeared already */
4544 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4546 fileLock = (cm_file_lock_t *)
4547 ((char *) q - offsetof(cm_file_lock_t, fileq));
4549 if (IS_LOCK_LOST(fileLock)) {
4550 if (fileLock->key == oldFileLock->key) {
4551 code = CM_ERROR_BADFD;
4552 oldFileLock->flags |= CM_FILELOCK_FLAG_LOST;
4553 osi_Log1(afsd_logp, " found lost lock %p for same key. Marking lock as lost",
4556 } else if (fileLock->lockType == LockWrite &&
4557 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
4558 osi_Log1(afsd_logp, " found conflicting LOST lock %p", fileLock);
4559 code = CM_ERROR_WOULDBLOCK;
4564 if (IS_LOCK_ACCEPTED(fileLock) &&
4565 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
4567 if (oldFileLock->lockType != LockRead ||
4568 fileLock->lockType != LockRead) {
4570 osi_Log1(afsd_logp, " found conflicting lock %p", fileLock);
4571 code = CM_ERROR_WOULDBLOCK;
4579 lock_ReleaseWrite(&cm_scacheLock);
4580 lock_ReleaseMutex(&scp->mx);
4585 /* when we get here, the lock is either a WAITUNLOCK or WAITLOCK.
4586 If it is WAITUNLOCK, then we didn't find any conflicting lock
4587 but we haven't verfied whether the serverLock is sufficient to
4588 assert it. If it is WAITLOCK, then the serverLock is
4589 insufficient to assert it. Eitherway, we are ready to accept
4590 the lock as either ACTIVE or WAITLOCK depending on the
4593 /* First, promote the WAITUNLOCK to a WAITLOCK */
4594 if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
4595 if (oldFileLock->lockType == LockRead)
4598 scp->exclusiveLocks++;
4600 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITUNLOCK;
4601 oldFileLock->flags |= CM_FILELOCK_FLAG_WAITLOCK;
4604 if (force_client_lock ||
4605 !SERVERLOCKS_ENABLED(scp) ||
4606 scp->serverLock == oldFileLock->lockType ||
4607 (oldFileLock->lockType == LockRead &&
4608 scp->serverLock == LockWrite)) {
4610 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
4612 if ((force_client_lock ||
4613 !SERVERLOCKS_ENABLED(scp)) &&
4614 !IS_LOCK_CLIENTONLY(oldFileLock)) {
4616 oldFileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
4618 if (oldFileLock->lockType == LockRead)
4621 scp->exclusiveLocks--;
4626 lock_ReleaseWrite(&cm_scacheLock);
4627 lock_ReleaseMutex(&scp->mx);
4634 code = cm_SyncOp(scp, NULL, oldFileLock->userp, &req, 0,
4635 CM_SCACHESYNC_NEEDCALLBACK
4636 | CM_SCACHESYNC_GETSTATUS
4637 | CM_SCACHESYNC_LOCK);
4639 osi_Log1(smb_logp, "cm_RetryLock SyncOp failure code 0x%x", code);
4640 lock_ReleaseWrite(&cm_scacheLock);
4641 goto post_syncopdone;
4644 if (!IS_LOCK_WAITLOCK(oldFileLock))
4645 goto pre_syncopdone;
4647 userp = oldFileLock->userp;
4649 #ifndef AGGRESSIVE_LOCKS
4650 newLock = oldFileLock->lockType;
4652 newLock = LockWrite;
4655 lock_ReleaseWrite(&cm_scacheLock);
4657 code = cm_IntSetLock(scp, userp, newLock, &req);
4660 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
4666 if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
4667 lock_ObtainWrite(&cm_scacheLock);
4668 if (scp->fileLocksT == &oldFileLock->fileq)
4669 scp->fileLocksT = osi_QPrev(&oldFileLock->fileq);
4670 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, &oldFileLock->fileq);
4671 lock_ReleaseWrite(&cm_scacheLock);
4672 } else if (code == 0 && IS_LOCK_WAITLOCK(oldFileLock)) {
4673 scp->serverLock = newLock;
4675 lock_ReleaseMutex(&scp->mx);
4678 lock_ObtainWrite(&cm_scacheLock);
4680 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
4681 } else if (code != CM_ERROR_WOULDBLOCK) {
4682 oldFileLock->flags |= CM_FILELOCK_FLAG_DELETED;
4683 cm_ReleaseUser(oldFileLock->userp);
4684 oldFileLock->userp = NULL;
4685 if (oldFileLock->scp) {
4686 cm_ReleaseSCacheNoLock(oldFileLock->scp);
4687 oldFileLock->scp = NULL;
4690 lock_ReleaseWrite(&cm_scacheLock);
4695 cm_key_t cm_GenerateKey(unsigned int session_id, unsigned long process_id, unsigned int file_id)
4698 osi_assert((process_id & 0xffffffff) == process_id);
4699 osi_assert((session_id & 0xffff) == session_id);
4700 osi_assert((file_id & 0xffff) == file_id);
4704 (((cm_key_t) (process_id & 0xffffffff)) << 32) |
4705 (((cm_key_t) (session_id & 0xffff)) << 16) |
4706 (((cm_key_t) (file_id & 0xffff)));
4709 static int cm_KeyEquals(cm_key_t k1, cm_key_t k2, int flags)
4711 if (flags & CM_UNLOCK_BY_FID) {
4712 return ((k1 & 0xffffffff) == (k2 & 0xffffffff));
4718 void cm_ReleaseAllLocks(void)
4724 cm_file_lock_t *fileLock;
4727 for (i = 0; i < cm_data.hashTableSize; i++)
4729 for ( scp = cm_data.hashTablep[i]; scp; scp = scp->nextp ) {
4730 while (scp->fileLocksH != NULL) {
4731 lock_ObtainMutex(&scp->mx);
4732 lock_ObtainWrite(&cm_scacheLock);
4733 if (!scp->fileLocksH) {
4734 lock_ReleaseWrite(&cm_scacheLock);
4735 lock_ReleaseMutex(&scp->mx);
4738 fileLock = (cm_file_lock_t *)((char *) scp->fileLocksH - offsetof(cm_file_lock_t, fileq));
4739 userp = fileLock->userp;
4741 key = fileLock->key;
4742 cm_HoldSCacheNoLock(scp);
4743 lock_ReleaseWrite(&cm_scacheLock);
4744 cm_UnlockByKey(scp, key, 0, userp, &req);
4745 cm_ReleaseSCache(scp);
4746 cm_ReleaseUser(userp);
4747 lock_ReleaseMutex(&scp->mx);