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>
25 /* Used by cm_FollowMountPoint */
31 extern void afsi_log(char *pattern, ...);
34 int cm_enableServerLocks = 1;
37 * Case-folding array. This was constructed by inspecting of SMBtrace output.
38 * I do not know anything more about it.
40 unsigned char cm_foldUpper[256] = {
41 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
42 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
43 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
44 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
45 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
46 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
47 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
48 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
49 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
50 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
51 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
52 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
53 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
54 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
55 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
56 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
57 0x80, 0x9a, 0x90, 0x41, 0x8e, 0x41, 0x8f, 0x80,
58 0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x8e, 0x8f,
59 0x90, 0x92, 0x92, 0x4f, 0x99, 0x4f, 0x55, 0x55,
60 0x59, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
61 0x41, 0x49, 0x4f, 0x55, 0xa5, 0xa5, 0x56, 0xa7,
62 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
63 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
64 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
65 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
66 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
67 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
68 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
69 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
70 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
71 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
72 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
76 * Case-insensitive string comparison. We used to use stricmp, but it doesn't
77 * know about 8-bit characters (e.g. 129 is lowercase u-umlaut, 154 is
78 * upper-case u-umlaut).
80 int cm_stricmp(const char *str1, const char *str2)
92 c1 = (char) cm_foldUpper[(unsigned char)(*str1++)];
93 c2 = (char) cm_foldUpper[(unsigned char)(*str2++)];
101 /* characters that are legal in an 8.3 name */
103 * We used to have 1's for all characters from 128 to 254. But
104 * the NT client behaves better if we create an 8.3 name for any
105 * name that has a character with the high bit on, and if we
106 * delete those characters from 8.3 names. In particular, see
107 * Sybase defect 10859.
109 char cm_LegalChars[256] = {
110 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
111 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
112 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0,
113 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
114 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
115 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1,
116 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
117 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1,
118 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
119 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
120 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
121 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
122 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
123 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
124 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
125 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
128 /* return true iff component is a valid 8.3 name */
129 int cm_Is8Dot3(char *namep)
136 * can't have a leading dot;
137 * special case for . and ..
139 if (namep[0] == '.') {
142 if (namep[1] == '.' && namep[2] == 0)
146 while (tc = *namep++) {
148 /* saw another dot */
149 if (sawDot) return 0; /* second dot */
154 if (cm_LegalChars[tc] == 0)
157 if (!sawDot && charCount > 8)
158 /* more than 8 chars in name */
160 if (sawDot && charCount > 3)
161 /* more than 3 chars in extension */
168 * Number unparsing map for generating 8.3 names;
169 * The version taken from DFS was on drugs.
170 * You can't include '&' and '@' in a file name.
172 char cm_8Dot3Mapping[42] =
173 {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
174 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K',
175 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U',
176 'V', 'W', 'X', 'Y', 'Z', '_', '-', '$', '#', '!', '+', '='
178 int cm_8Dot3MapSize = sizeof(cm_8Dot3Mapping);
180 void cm_Gen8Dot3Name(cm_dirEntry_t *dep, char *shortName, char **shortNameEndp)
184 int vnode = ntohl(dep->fid.vnode);
186 int validExtension = 0;
187 char tc, *temp, *name;
189 /* Unparse the file's vnode number to get a "uniquifier" */
191 number[nsize] = cm_8Dot3Mapping[vnode % cm_8Dot3MapSize];
193 vnode /= cm_8Dot3MapSize;
197 * Look for valid extension. There has to be a dot, and
198 * at least one of the characters following has to be legal.
200 lastDot = strrchr(dep->name, '.');
202 temp = lastDot; temp++;
204 if (cm_LegalChars[tc])
210 /* Copy name characters */
212 for (i = 0, name = dep->name;
213 i < (7 - nsize) && name != lastDot; ) {
218 if (!cm_LegalChars[tc])
221 *shortName++ = toupper(tc);
227 /* Copy uniquifier characters */
228 memcpy(shortName, number, nsize);
231 if (validExtension) {
232 /* Copy extension characters */
233 *shortName++ = *lastDot++; /* copy dot */
234 for (i = 0, tc = *lastDot++;
237 if (cm_LegalChars[tc]) {
239 *shortName++ = toupper(tc);
248 *shortNameEndp = shortName;
251 /* return success if we can open this file in this mode */
252 long cm_CheckOpen(cm_scache_t *scp, int openMode, int trunc, cm_user_t *userp,
259 if (openMode != 1) rights |= PRSFS_READ;
260 if (openMode == 1 || openMode == 2 || trunc) rights |= PRSFS_WRITE;
262 lock_ObtainMutex(&scp->mx);
264 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
265 CM_SCACHESYNC_GETSTATUS
266 | CM_SCACHESYNC_NEEDCALLBACK
267 | CM_SCACHESYNC_LOCK);
270 ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
271 scp->fileType == CM_SCACHETYPE_FILE) {
274 unsigned int sLockType;
275 LARGE_INTEGER LOffset, LLength;
277 /* Check if there's some sort of lock on the file at the
280 key = cm_GenerateKey(CM_SESSION_CMINT,0,0);
282 if (rights & PRSFS_WRITE)
285 sLockType = LOCKING_ANDX_SHARED_LOCK;
287 LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
288 LOffset.LowPart = CM_FLSHARE_OFFSET_LOW;
289 LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
290 LLength.LowPart = CM_FLSHARE_LENGTH_LOW;
292 code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
295 cm_Unlock(scp, sLockType, LOffset, LLength, key, userp, reqp);
297 /* In this case, we allow the file open to go through even
298 though we can't enforce mandatory locking on the
300 if (code == CM_ERROR_NOACCESS &&
301 !(rights & PRSFS_WRITE))
305 case CM_ERROR_ALLOFFLINE:
306 case CM_ERROR_ALLDOWN:
307 case CM_ERROR_ALLBUSY:
308 case CM_ERROR_TIMEDOUT:
310 case CM_ERROR_WOULDBLOCK:
313 code = CM_ERROR_SHARING_VIOLATION;
318 } else if (code != 0) {
322 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
326 lock_ReleaseMutex(&scp->mx);
331 /* return success if we can open this file in this mode */
332 long cm_CheckNTOpen(cm_scache_t *scp, unsigned int desiredAccess,
333 unsigned int createDisp, cm_user_t *userp, cm_req_t *reqp)
338 /* Always allow delete; the RPC will tell us if it's OK */
339 if (desiredAccess == DELETE)
344 if (desiredAccess & AFS_ACCESS_READ)
345 rights |= PRSFS_READ;
347 if ((desiredAccess & AFS_ACCESS_WRITE)
349 rights |= PRSFS_WRITE;
351 lock_ObtainMutex(&scp->mx);
353 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
354 CM_SCACHESYNC_GETSTATUS
355 | CM_SCACHESYNC_NEEDCALLBACK
356 | CM_SCACHESYNC_LOCK);
359 * If the open will fail because the volume is readonly, then we will
360 * return an access denied error instead. This is to help brain-dead
361 * apps run correctly on replicated volumes.
362 * See defect 10007 for more information.
364 if (code == CM_ERROR_READONLY)
365 code = CM_ERROR_NOACCESS;
368 ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
369 scp->fileType == CM_SCACHETYPE_FILE) {
371 unsigned int sLockType;
372 LARGE_INTEGER LOffset, LLength;
374 /* Check if there's some sort of lock on the file at the
377 key = cm_GenerateKey(CM_SESSION_CMINT,0,0);
378 if (rights & PRSFS_WRITE)
381 sLockType = LOCKING_ANDX_SHARED_LOCK;
383 /* single byte lock at offset 0x0100 0000 0000 0000 */
384 LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
385 LOffset.LowPart = CM_FLSHARE_OFFSET_LOW;
386 LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
387 LLength.LowPart = CM_FLSHARE_LENGTH_LOW;
389 code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
392 cm_Unlock(scp, sLockType, LOffset, LLength, key, userp, reqp);
394 /* In this case, we allow the file open to go through even
395 though we can't enforce mandatory locking on the
397 if (code == CM_ERROR_NOACCESS &&
398 !(rights & PRSFS_WRITE))
402 case CM_ERROR_ALLOFFLINE:
403 case CM_ERROR_ALLDOWN:
404 case CM_ERROR_ALLBUSY:
405 case CM_ERROR_TIMEDOUT:
407 case CM_ERROR_WOULDBLOCK:
410 code = CM_ERROR_SHARING_VIOLATION;
414 } else if (code != 0) {
418 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
421 lock_ReleaseMutex(&scp->mx);
427 * When CAP_NT_SMBS has been negotiated, deletion (of files or directories) is
428 * done in three steps:
429 * (1) open for deletion (NT_CREATE_AND_X)
430 * (2) set for deletion on close (NT_TRANSACTION2, SET_FILE_INFO)
432 * We must not do the RPC until step 3. But if we are going to return an error
433 * code (e.g. directory not empty), we must return it by step 2, otherwise most
434 * clients will not notice it. So we do a preliminary check. For deleting
435 * files, this is almost free, since we have already done the RPC to get the
436 * parent directory's status bits. But for deleting directories, we must do an
437 * additional RPC to get the directory's data to check if it is empty. Sigh.
439 long cm_CheckNTDelete(cm_scache_t *dscp, cm_scache_t *scp, cm_user_t *userp,
446 unsigned short *hashTable;
448 int BeyondPage = 0, HaveDot = 0, HaveDotDot = 0;
450 /* First check permissions */
451 lock_ObtainMutex(&dscp->mx);
452 code = cm_SyncOp(dscp, NULL, userp, reqp, PRSFS_DELETE,
453 CM_SCACHESYNC_GETSTATUS
454 | CM_SCACHESYNC_NEEDCALLBACK);
455 lock_ReleaseMutex(&dscp->mx);
459 /* If deleting directory, must be empty */
461 if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
464 thyper.HighPart = 0; thyper.LowPart = 0;
465 lock_ObtainRead(&scp->bufCreateLock);
466 code = buf_Get(scp, &thyper, &bufferp);
467 lock_ReleaseRead(&scp->bufCreateLock);
471 lock_ObtainMutex(&bufferp->mx);
472 lock_ObtainMutex(&scp->mx);
474 code = cm_SyncOp(scp, bufferp, userp, reqp, 0,
475 CM_SCACHESYNC_NEEDCALLBACK
477 | CM_SCACHESYNC_BUFLOCKED);
481 if (cm_HaveBuffer(scp, bufferp, 1))
484 /* otherwise, load the buffer and try again */
485 lock_ReleaseMutex(&bufferp->mx);
486 code = cm_GetBuffer(scp, bufferp, NULL, userp, reqp);
487 lock_ReleaseMutex(&scp->mx);
488 lock_ObtainMutex(&bufferp->mx);
489 lock_ObtainMutex(&scp->mx);
494 /* We try to determine emptiness without looking beyond the first page,
495 * and without assuming "." and ".." are present and are on the first
496 * page (though these assumptions might, after all, be reasonable).
498 hashTable = (unsigned short *)(bufferp->datap + (32 * 5));
499 for (i=0; i<128; i++) {
500 idx = ntohs(hashTable[i]);
506 dep = (cm_dirEntry_t *)(bufferp->datap + (32 * idx));
507 if (strcmp(dep->name, ".") == 0)
509 else if (strcmp(dep->name, "..") == 0)
512 code = CM_ERROR_NOTEMPTY;
515 idx = ntohs(dep->next);
518 if (BeyondPage && HaveDot && HaveDotDot)
519 code = CM_ERROR_NOTEMPTY;
523 lock_ReleaseMutex(&bufferp->mx);
524 buf_Release(bufferp);
525 lock_ReleaseMutex(&scp->mx);
530 * Iterate through all entries in a directory.
531 * When the function funcp is called, the buffer is locked but the
532 * directory vnode is not.
534 * If the retscp parameter is not NULL, the parmp must be a
535 * cm_lookupSearch_t object.
537 long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
538 osi_hyper_t *startOffsetp, cm_user_t *userp, cm_req_t *reqp,
539 cm_scache_t **retscp)
546 osi_hyper_t dirLength;
547 osi_hyper_t bufferOffset;
548 osi_hyper_t curOffset;
552 cm_pageHeader_t *pageHeaderp;
554 long nextEntryCookie;
555 int numDirChunks; /* # of 32 byte dir chunks in this entry */
557 /* get the directory size */
558 lock_ObtainMutex(&scp->mx);
559 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_LOOKUP,
560 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
562 lock_ReleaseMutex(&scp->mx);
566 if (scp->fileType != CM_SCACHETYPE_DIRECTORY) {
567 lock_ReleaseMutex(&scp->mx);
568 return CM_ERROR_NOTDIR;
571 if (retscp) /* if this is a lookup call */
573 cm_lookupSearch_t* sp = parmp;
575 #ifdef AFS_FREELANCE_CLIENT
576 /* Freelance entries never end up in the DNLC because they
577 * do not have an associated cm_server_t
579 if ( !(cm_freelanceEnabled &&
580 sp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
581 sp->fid.volume==AFS_FAKE_ROOT_VOL_ID ) )
582 #endif /* AFS_FREELANCE_CLIENT */
584 int casefold = sp->caseFold;
585 sp->caseFold = 0; /* we have a strong preference for exact matches */
586 if ( *retscp = cm_dnlcLookup(scp, sp)) /* dnlc hit */
588 sp->caseFold = casefold;
589 lock_ReleaseMutex(&scp->mx);
592 sp->caseFold = casefold;
597 * XXX We only get the length once. It might change when we drop the
600 dirLength = scp->length;
602 lock_ReleaseMutex(&scp->mx);
605 bufferOffset.LowPart = bufferOffset.HighPart = 0;
607 curOffset = *startOffsetp;
609 curOffset.HighPart = 0;
610 curOffset.LowPart = 0;
614 /* make sure that curOffset.LowPart doesn't point to the first
615 * 32 bytes in the 2nd through last dir page, and that it
616 * doesn't point at the first 13 32-byte chunks in the first
617 * dir page, since those are dir and page headers, and don't
618 * contain useful information.
620 temp = curOffset.LowPart & (2048-1);
621 if (curOffset.HighPart == 0 && curOffset.LowPart < 2048) {
622 /* we're in the first page */
623 if (temp < 13*32) temp = 13*32;
626 /* we're in a later dir page */
627 if (temp < 32) temp = 32;
630 /* make sure the low order 5 bits are zero */
633 /* now put temp bits back ito curOffset.LowPart */
634 curOffset.LowPart &= ~(2048-1);
635 curOffset.LowPart |= temp;
637 /* check if we've passed the dir's EOF */
638 if (LargeIntegerGreaterThanOrEqualTo(curOffset, dirLength))
641 /* see if we can use the bufferp we have now; compute in which
642 * page the current offset would be, and check whether that's
643 * the offset of the buffer we have. If not, get the buffer.
645 thyper.HighPart = curOffset.HighPart;
646 thyper.LowPart = curOffset.LowPart & ~(cm_data.buf_blockSize-1);
647 if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
650 lock_ReleaseMutex(&bufferp->mx);
651 buf_Release(bufferp);
655 lock_ObtainRead(&scp->bufCreateLock);
656 code = buf_Get(scp, &thyper, &bufferp);
657 lock_ReleaseRead(&scp->bufCreateLock);
659 /* if buf_Get() fails we do not have a buffer object to lock */
665 /* for the IFS version, we bulkstat the dirents because this
666 routine is used in place of smb_ReceiveCoreSearchDir. our
667 other option is to modify smb_ReceiveCoreSearchDir itself,
668 but this seems to be the proper use for cm_ApplyDir. */
669 lock_ObtainMutex(&scp->mx);
670 if ((scp->flags & CM_SCACHEFLAG_BULKSTATTING) == 0
671 && (scp->bulkStatProgress.QuadPart <= thyper.QuadPart))
673 scp->flags |= CM_SCACHEFLAG_BULKSTATTING;
674 code = cm_TryBulkStat(scp, &thyper, userp, reqp);
675 scp->flags &= ~CM_SCACHEFLAG_BULKSTATTING;
676 scp->bulkStatProgress = thyper;
678 lock_ReleaseMutex(&scp->mx);
681 lock_ObtainMutex(&bufferp->mx);
682 bufferOffset = thyper;
684 /* now get the data in the cache */
686 lock_ObtainMutex(&scp->mx);
687 code = cm_SyncOp(scp, bufferp, userp, reqp,
689 CM_SCACHESYNC_NEEDCALLBACK
691 | CM_SCACHESYNC_BUFLOCKED);
693 lock_ReleaseMutex(&scp->mx);
697 if (cm_HaveBuffer(scp, bufferp, 1)) {
698 lock_ReleaseMutex(&scp->mx);
702 /* otherwise, load the buffer and try again */
703 lock_ReleaseMutex(&bufferp->mx);
704 code = cm_GetBuffer(scp, bufferp, NULL, userp,
706 lock_ReleaseMutex(&scp->mx);
707 lock_ObtainMutex(&bufferp->mx);
712 lock_ReleaseMutex(&bufferp->mx);
713 buf_Release(bufferp);
717 } /* if (wrong buffer) ... */
719 /* now we have the buffer containing the entry we're interested
720 * in; copy it out if it represents a non-deleted entry.
722 entryInDir = curOffset.LowPart & (2048-1);
723 entryInBuffer = curOffset.LowPart & (cm_data.buf_blockSize - 1);
725 /* page header will help tell us which entries are free. Page
726 * header can change more often than once per buffer, since
727 * AFS 3 dir page size may be less than (but not more than) a
728 * buffer package buffer.
730 /* only look intra-buffer */
731 temp = curOffset.LowPart & (cm_data.buf_blockSize - 1);
732 temp &= ~(2048 - 1); /* turn off intra-page bits */
733 pageHeaderp = (cm_pageHeader_t *) (bufferp->datap + temp);
735 /* now determine which entry we're looking at in the page. If
736 * it is free (there's a free bitmap at the start of the dir),
737 * we should skip these 32 bytes.
739 slotInPage = (entryInDir & 0x7e0) >> 5;
740 if (!(pageHeaderp->freeBitmap[slotInPage>>3]
741 & (1 << (slotInPage & 0x7)))) {
742 /* this entry is free */
743 numDirChunks = 1; /* only skip this guy */
747 tp = bufferp->datap + entryInBuffer;
748 dep = (cm_dirEntry_t *) tp; /* now points to AFS3 dir entry */
750 /* while we're here, compute the next entry's location, too,
751 * since we'll need it when writing out the cookie into the
752 * dir listing stream.
754 numDirChunks = cm_NameEntries(dep->name, NULL);
756 /* compute the offset of the cookie representing the next entry */
757 nextEntryCookie = curOffset.LowPart
758 + (CM_DIR_CHUNKSIZE * numDirChunks);
760 if (dep->fid.vnode != 0) {
761 /* this is one of the entries to use: it is not deleted */
762 code = (*funcp)(scp, dep, parmp, &curOffset);
765 } /* if we're including this name */
768 /* and adjust curOffset to be where the new cookie is */
770 thyper.LowPart = CM_DIR_CHUNKSIZE * numDirChunks;
771 curOffset = LargeIntegerAdd(thyper, curOffset);
772 } /* while copying data for dir listing */
774 /* release the mutex */
776 lock_ReleaseMutex(&bufferp->mx);
777 buf_Release(bufferp);
782 int cm_NoneUpper(char *s)
786 if (c >= 'A' && c <= 'Z')
791 int cm_NoneLower(char *s)
795 if (c >= 'a' && c <= 'z')
800 long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
803 cm_lookupSearch_t *sp;
808 sp = (cm_lookupSearch_t *) rockp;
810 matchName = dep->name;
812 match = cm_stricmp(matchName, sp->searchNamep);
814 match = strcmp(matchName, sp->searchNamep);
818 && !cm_Is8Dot3(dep->name)) {
819 matchName = shortName;
820 cm_Gen8Dot3Name(dep, shortName, NULL);
822 match = cm_stricmp(matchName, sp->searchNamep);
824 match = strcmp(matchName, sp->searchNamep);
834 if (!sp->caseFold || matchName == shortName) {
835 sp->fid.vnode = ntohl(dep->fid.vnode);
836 sp->fid.unique = ntohl(dep->fid.unique);
837 return CM_ERROR_STOPNOW;
841 * If we get here, we are doing a case-insensitive search, and we
842 * have found a match. Now we determine what kind of match it is:
843 * exact, lower-case, upper-case, or none of the above. This is done
844 * in order to choose among matches, if there are more than one.
847 /* Exact matches are the best. */
848 match = strcmp(matchName, sp->searchNamep);
851 sp->fid.vnode = ntohl(dep->fid.vnode);
852 sp->fid.unique = ntohl(dep->fid.unique);
853 return CM_ERROR_STOPNOW;
856 /* Lower-case matches are next. */
859 if (cm_NoneUpper(matchName)) {
864 /* Upper-case matches are next. */
867 if (cm_NoneLower(matchName)) {
872 /* General matches are last. */
878 sp->fid.vnode = ntohl(dep->fid.vnode);
879 sp->fid.unique = ntohl(dep->fid.unique);
883 /* read the contents of a mount point into the appropriate string.
884 * called with locked scp, and returns with locked scp.
886 long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
893 if (scp->mountPointStringp[0])
896 /* otherwise, we have to read it in */
897 lock_ReleaseMutex(&scp->mx);
899 lock_ObtainRead(&scp->bufCreateLock);
900 thyper.LowPart = thyper.HighPart = 0;
901 code = buf_Get(scp, &thyper, &bufp);
902 lock_ReleaseRead(&scp->bufCreateLock);
904 lock_ObtainMutex(&scp->mx);
909 code = cm_SyncOp(scp, bufp, userp, reqp, 0,
910 CM_SCACHESYNC_READ | CM_SCACHESYNC_NEEDCALLBACK);
915 if (cm_HaveBuffer(scp, bufp, 0))
918 /* otherwise load buffer */
919 code = cm_GetBuffer(scp, bufp, NULL, userp, reqp);
924 /* locked, has callback, has valid data in buffer */
925 if ((tlen = scp->length.LowPart) > 1000)
926 return CM_ERROR_TOOBIG;
928 code = CM_ERROR_INVAL;
932 /* someone else did the work while we were out */
933 if (scp->mountPointStringp[0]) {
938 /* otherwise, copy out the link */
939 memcpy(scp->mountPointStringp, bufp->datap, tlen);
941 /* now make it null-terminated. Note that the original contents of a
942 * link that is a mount point is "#volname." where "." is there just to
943 * be turned into a null. That is, we can trash the last char of the
944 * link without damaging the vol name. This is a stupid convention,
945 * but that's the protocol.
947 scp->mountPointStringp[tlen-1] = 0;
956 /* called with a locked scp and chases the mount point, yielding outScpp.
957 * scp remains locked, just for simplicity of describing the interface.
959 long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
960 cm_req_t *reqp, cm_scache_t **outScpp)
975 if (scp->mountRootFid.cell != 0 && scp->mountRootGen >= cm_data.mountRootGen) {
976 tfid = scp->mountRootFid;
977 lock_ReleaseMutex(&scp->mx);
978 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
979 lock_ObtainMutex(&scp->mx);
983 /* parse the volume name */
984 mpNamep = scp->mountPointStringp;
986 return CM_ERROR_NOSUCHPATH;
987 tlen = (int)strlen(scp->mountPointStringp);
988 mtType = *scp->mountPointStringp;
989 cellNamep = malloc(tlen);
990 volNamep = malloc(tlen);
992 cp = strrchr(mpNamep, ':');
994 /* cellular mount point */
995 memset(cellNamep, 0, tlen);
996 strncpy(cellNamep, mpNamep+1, cp - mpNamep - 1);
997 strcpy(volNamep, cp+1);
998 /* now look up the cell */
999 cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE);
1003 strcpy(volNamep, mpNamep+1);
1005 cellp = cm_FindCellByID(scp->fid.cell);
1009 code = CM_ERROR_NOSUCHCELL;
1013 vnLength = strlen(volNamep);
1014 if (vnLength >= 8 && strcmp(volNamep + vnLength - 7, ".backup") == 0)
1016 else if (vnLength >= 10
1017 && strcmp(volNamep + vnLength - 9, ".readonly") == 0)
1022 /* check for backups within backups */
1024 && (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
1025 == CM_SCACHEFLAG_RO) {
1026 code = CM_ERROR_NOSUCHVOLUME;
1030 /* now we need to get the volume */
1031 lock_ReleaseMutex(&scp->mx);
1032 code = cm_GetVolumeByName(cellp, volNamep, userp, reqp, 0, &volp);
1033 lock_ObtainMutex(&scp->mx);
1036 /* save the parent of the volume root for this is the
1037 * place where the volume is mounted and we must remember
1038 * this in the volume structure rather than just in the
1039 * scache entry lest the scache entry gets recycled
1042 lock_ObtainMutex(&volp->mx);
1043 volp->dotdotFid = dscp->fid;
1044 lock_ReleaseMutex(&volp->mx);
1046 scp->mountRootFid.cell = cellp->cellID;
1047 /* if the mt pt is in a read-only volume (not just a
1048 * backup), and if there is a read-only volume for the
1049 * target, and if this is a type '#' mount point, use
1050 * the read-only, otherwise use the one specified.
1052 if (mtType == '#' && (scp->flags & CM_SCACHEFLAG_PURERO)
1053 && volp->roID != 0 && type == RWVOL)
1056 scp->mountRootFid.volume = volp->roID;
1057 else if (type == BACKVOL)
1058 scp->mountRootFid.volume = volp->bkID;
1060 scp->mountRootFid.volume = volp->rwID;
1062 /* the rest of the fid is a magic number */
1063 scp->mountRootFid.vnode = 1;
1064 scp->mountRootFid.unique = 1;
1065 scp->mountRootGen = cm_data.mountRootGen;
1067 tfid = scp->mountRootFid;
1068 lock_ReleaseMutex(&scp->mx);
1069 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
1070 lock_ObtainMutex(&scp->mx);
1079 long cm_LookupInternal(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
1080 cm_req_t *reqp, cm_scache_t **outpScpp)
1083 int dnlcHit = 1; /* did we hit in the dnlc? yes, we did */
1084 cm_scache_t *tscp = NULL;
1085 cm_scache_t *mountedScp;
1086 cm_lookupSearch_t rock;
1089 if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
1090 && strcmp(namep, "..") == 0) {
1091 if (dscp->dotdotFid.volume == 0)
1092 return CM_ERROR_NOSUCHVOLUME;
1093 rock.fid = dscp->dotdotFid;
1095 } else if (strcmp(namep, ".") == 0) {
1096 rock.fid = dscp->fid;
1100 memset(&rock, 0, sizeof(rock));
1101 rock.fid.cell = dscp->fid.cell;
1102 rock.fid.volume = dscp->fid.volume;
1103 rock.searchNamep = namep;
1104 rock.caseFold = (flags & CM_FLAG_CASEFOLD);
1105 rock.hasTilde = ((strchr(namep, '~') != NULL) ? 1 : 0);
1107 /* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
1108 code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
1109 (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
1111 /* code == 0 means we fell off the end of the dir, while stopnow means
1112 * that we stopped early, probably because we found the entry we're
1113 * looking for. Any other non-zero code is an error.
1115 if (code && code != CM_ERROR_STOPNOW) {
1116 /* if the cm_scache_t we are searching in is not a directory
1117 * we must return path not found because the error
1118 * is to describe the final component not an intermediary
1120 if (code == CM_ERROR_NOTDIR) {
1121 if (flags & CM_FLAG_CHECKPATH)
1122 return CM_ERROR_NOSUCHPATH;
1124 return CM_ERROR_NOSUCHFILE;
1129 getroot = (dscp==cm_data.rootSCachep) ;
1131 if (!cm_freelanceEnabled || !getroot) {
1132 if (flags & CM_FLAG_CHECKPATH)
1133 return CM_ERROR_NOSUCHPATH;
1135 return CM_ERROR_NOSUCHFILE;
1137 else { /* nonexistent dir on freelance root, so add it */
1138 char fullname[200] = ".";
1141 osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %s",
1142 osi_LogSaveString(afsd_logp,namep));
1143 if (namep[0] == '.') {
1144 if (cm_GetCell_Gen(&namep[1], &fullname[1], CM_FLAG_CREATE)) {
1146 if ( stricmp(&namep[1], &fullname[1]) )
1147 code = cm_FreelanceAddSymlink(namep, fullname, &rock.fid);
1149 code = cm_FreelanceAddMount(namep, &fullname[1], "root.cell.", 1, &rock.fid);
1152 if (cm_GetCell_Gen(namep, fullname, CM_FLAG_CREATE)) {
1154 if ( stricmp(namep, fullname) )
1155 code = cm_FreelanceAddSymlink(namep, fullname, &rock.fid);
1157 code = cm_FreelanceAddMount(namep, fullname, "root.cell.", 0, &rock.fid);
1160 if (!found || code < 0) { /* add mount point failed, so give up */
1161 if (flags & CM_FLAG_CHECKPATH)
1162 return CM_ERROR_NOSUCHPATH;
1164 return CM_ERROR_NOSUCHFILE;
1166 tscp = NULL; /* to force call of cm_GetSCache */
1171 if ( !tscp ) /* we did not find it in the dnlc */
1174 code = cm_GetSCache(&rock.fid, &tscp, userp, reqp);
1178 /* tscp is now held */
1180 lock_ObtainMutex(&tscp->mx);
1181 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1182 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1184 lock_ReleaseMutex(&tscp->mx);
1185 cm_ReleaseSCache(tscp);
1188 /* tscp is now locked */
1190 if (!(flags & CM_FLAG_NOMOUNTCHASE)
1191 && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
1192 /* mount points are funny: they have a volume name to mount
1195 code = cm_ReadMountPoint(tscp, userp, reqp);
1197 code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
1199 lock_ReleaseMutex(&tscp->mx);
1200 cm_ReleaseSCache(tscp);
1207 lock_ReleaseMutex(&tscp->mx);
1210 /* copy back pointer */
1213 /* insert scache in dnlc */
1214 if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
1215 /* lock the directory entry to prevent racing callback revokes */
1216 lock_ObtainMutex(&dscp->mx);
1217 if ( dscp->cbServerp != NULL && dscp->cbExpires > 0 )
1218 cm_dnlcEnter(dscp, namep, tscp);
1219 lock_ReleaseMutex(&dscp->mx);
1226 int cm_ExpandSysName(char *inp, char *outp, long outSize, unsigned int index)
1231 tp = strrchr(inp, '@');
1233 return 0; /* no @sys */
1235 if (strcmp(tp, "@sys") != 0)
1236 return 0; /* no @sys */
1238 /* caller just wants to know if this is a valid @sys type of name */
1242 if (index >= MAXNUMSYSNAMES)
1245 /* otherwise generate the properly expanded @sys name */
1246 prefixCount = (int)(tp - inp);
1248 strncpy(outp, inp, prefixCount); /* copy out "a." from "a.@sys" */
1249 outp[prefixCount] = 0; /* null terminate the "a." */
1250 strcat(outp, cm_sysNameList[index]);/* append i386_nt40 */
1254 long cm_Lookup(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
1255 cm_req_t *reqp, cm_scache_t **outpScpp)
1259 int sysNameIndex = 0;
1260 cm_scache_t *scp = NULL;
1262 if ( stricmp(namep,SMB_IOCTL_FILENAME_NOSLASH) == 0 ) {
1263 if (flags & CM_FLAG_CHECKPATH)
1264 return CM_ERROR_NOSUCHPATH;
1266 return CM_ERROR_NOSUCHFILE;
1269 for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
1270 code = cm_ExpandSysName(namep, tname, sizeof(tname), sysNameIndex);
1272 code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
1278 cm_ReleaseSCache(scp);
1282 return cm_LookupInternal(dscp, namep, flags, userp, reqp, outpScpp);
1286 /* None of the possible sysName expansions could be found */
1287 if (flags & CM_FLAG_CHECKPATH)
1288 return CM_ERROR_NOSUCHPATH;
1290 return CM_ERROR_NOSUCHFILE;
1293 long cm_Unlink(cm_scache_t *dscp, char *namep, cm_user_t *userp, cm_req_t *reqp)
1299 AFSFetchStatus newDirStatus;
1301 struct rx_connection * callp;
1303 #ifdef AFS_FREELANCE_CLIENT
1304 if (cm_freelanceEnabled && dscp == cm_data.rootSCachep) {
1305 /* deleting a mount point from the root dir. */
1306 code = cm_FreelanceRemoveMount(namep);
1311 /* make sure we don't screw up the dir status during the merge */
1312 lock_ObtainMutex(&dscp->mx);
1313 sflags = CM_SCACHESYNC_STOREDATA;
1314 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1315 lock_ReleaseMutex(&dscp->mx);
1320 afsFid.Volume = dscp->fid.volume;
1321 afsFid.Vnode = dscp->fid.vnode;
1322 afsFid.Unique = dscp->fid.unique;
1324 osi_Log1(afsd_logp, "CALL RemoveFile scp 0x%p", dscp);
1326 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1330 callp = cm_GetRxConn(connp);
1331 code = RXAFS_RemoveFile(callp, &afsFid, namep,
1332 &newDirStatus, &volSync);
1333 rx_PutConnection(callp);
1335 } while (cm_Analyze(connp, userp, reqp, &dscp->fid, &volSync, NULL, NULL, code));
1336 code = cm_MapRPCError(code, reqp);
1339 osi_Log1(afsd_logp, "CALL RemoveFile FAILURE, code 0x%x", code);
1341 osi_Log0(afsd_logp, "CALL RemoveFile SUCCESS");
1343 lock_ObtainMutex(&dscp->mx);
1344 cm_dnlcRemove(dscp, namep);
1345 cm_SyncOpDone(dscp, NULL, sflags);
1347 cm_MergeStatus(dscp, &newDirStatus, &volSync, userp, 0);
1348 else if (code == CM_ERROR_NOSUCHFILE) {
1349 /* windows would not have allowed the request to delete the file
1350 * if it did not believe the file existed. therefore, we must
1351 * have an inconsistent view of the world.
1353 dscp->cbServerp = NULL;
1355 lock_ReleaseMutex(&dscp->mx);
1360 /* called with a locked vnode, and fills in the link info.
1361 * returns this the vnode still locked.
1363 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1370 lock_AssertMutex(&linkScp->mx);
1371 if (!linkScp->mountPointStringp[0]) {
1372 /* read the link data */
1373 lock_ReleaseMutex(&linkScp->mx);
1374 thyper.LowPart = thyper.HighPart = 0;
1375 code = buf_Get(linkScp, &thyper, &bufp);
1376 lock_ObtainMutex(&linkScp->mx);
1380 code = cm_SyncOp(linkScp, bufp, userp, reqp, 0,
1381 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1386 if (cm_HaveBuffer(linkScp, bufp, 0))
1389 code = cm_GetBuffer(linkScp, bufp, NULL, userp, reqp);
1394 } /* while loop to get the data */
1396 /* now if we still have no link read in,
1397 * copy the data from the buffer */
1398 if ((temp = linkScp->length.LowPart) >= MOUNTPOINTLEN) {
1400 return CM_ERROR_TOOBIG;
1403 /* otherwise, it fits; make sure it is still null (could have
1404 * lost race with someone else referencing this link above),
1405 * and if so, copy in the data.
1407 if (!linkScp->mountPointStringp[0]) {
1408 strncpy(linkScp->mountPointStringp, bufp->datap, temp);
1409 linkScp->mountPointStringp[temp] = 0; /* null terminate */
1412 } /* don't have sym link contents cached */
1417 /* called with a held vnode and a path suffix, with the held vnode being a
1418 * symbolic link. Our goal is to generate a new path to interpret, and return
1419 * this new path in newSpaceBufferp. If the new vnode is relative to a dir
1420 * other than the directory containing the symbolic link, then the new root is
1421 * returned in *newRootScpp, otherwise a null is returned there.
1423 long cm_AssembleLink(cm_scache_t *linkScp, char *pathSuffixp,
1424 cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1425 cm_user_t *userp, cm_req_t *reqp)
1432 lock_ObtainMutex(&linkScp->mx);
1433 code = cm_HandleLink(linkScp, userp, reqp);
1437 /* if we may overflow the buffer, bail out; buffer is signficantly
1438 * bigger than max path length, so we don't really have to worry about
1439 * being a little conservative here.
1441 if (strlen(linkScp->mountPointStringp) + strlen(pathSuffixp) + 2
1442 >= CM_UTILS_SPACESIZE)
1443 return CM_ERROR_TOOBIG;
1445 tsp = cm_GetSpace();
1446 linkp = linkScp->mountPointStringp;
1447 if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1448 if (strlen(linkp) > cm_mountRootLen)
1449 strcpy(tsp->data, linkp+cm_mountRootLen+1);
1452 *newRootScpp = cm_data.rootSCachep;
1453 cm_HoldSCache(cm_data.rootSCachep);
1454 } else if (linkp[0] == '\\' && linkp[1] == '\\') {
1455 if (!strnicmp(&linkp[2], cm_NetbiosName, (len = (long)strlen(cm_NetbiosName))))
1457 char * p = &linkp[len + 3];
1458 if (strnicmp(p, "all", 3) == 0)
1461 strcpy(tsp->data, p);
1462 for (p = tsp->data; *p; p++) {
1466 *newRootScpp = cm_data.rootSCachep;
1467 cm_HoldSCache(cm_data.rootSCachep);
1469 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1470 strcpy(tsp->data, linkp);
1471 *newRootScpp = NULL;
1472 code = CM_ERROR_PATH_NOT_COVERED;
1474 } else if ( !strnicmp(linkp, "msdfs:", (len = (long)strlen("msdfs:"))) ) {
1475 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1476 strcpy(tsp->data, linkp);
1477 *newRootScpp = NULL;
1478 code = CM_ERROR_PATH_NOT_COVERED;
1479 } else if (*linkp == '\\' || *linkp == '/') {
1481 /* formerly, this was considered to be from the AFS root,
1482 * but this seems to create problems. instead, we will just
1483 * reject the link */
1484 strcpy(tsp->data, linkp+1);
1485 *newRootScpp = cm_data.rootSCachep;
1486 cm_HoldSCache(cm_data.rootSCachep);
1488 /* we still copy the link data into the response so that
1489 * the user can see what the link points to
1491 linkScp->fileType = CM_SCACHETYPE_INVALID;
1492 strcpy(tsp->data, linkp);
1493 *newRootScpp = NULL;
1494 code = CM_ERROR_NOSUCHPATH;
1497 /* a relative link */
1498 strcpy(tsp->data, linkp);
1499 *newRootScpp = NULL;
1501 if (pathSuffixp[0] != 0) { /* if suffix string is non-null */
1502 strcat(tsp->data, "\\");
1503 strcat(tsp->data, pathSuffixp);
1505 *newSpaceBufferp = tsp;
1508 lock_ReleaseMutex(&linkScp->mx);
1512 long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
1513 cm_user_t *userp, char *tidPathp, cm_req_t *reqp, cm_scache_t **outScpp)
1516 char *tp; /* ptr moving through input buffer */
1517 char tc; /* temp char */
1518 int haveComponent; /* has new component started? */
1519 char component[256]; /* this is the new component */
1520 char *cp; /* component name being assembled */
1521 cm_scache_t *tscp; /* current location in the hierarchy */
1522 cm_scache_t *nscp; /* next dude down */
1523 cm_scache_t *dirScp; /* last dir we searched */
1524 cm_scache_t *linkScp; /* new root for the symlink we just
1526 cm_space_t *psp; /* space for current path, if we've hit
1528 cm_space_t *tempsp; /* temp vbl */
1529 char *restp; /* rest of the pathname to interpret */
1530 int symlinkCount; /* count of # of symlinks traversed */
1531 int extraFlag; /* avoid chasing mt pts for dir cmd */
1532 int phase = 1; /* 1 = tidPathp, 2 = pathp */
1545 cm_HoldSCache(tscp);
1552 /* map Unix slashes into DOS ones so we can interpret Unix
1558 if (!haveComponent) {
1561 } else if (tc == 0) {
1575 /* we have a component here */
1576 if (tc == 0 || tc == '\\') {
1577 /* end of the component; we're at the last
1578 * component if tc == 0. However, if the last
1579 * is a symlink, we have more to do.
1581 *cp++ = 0; /* add null termination */
1582 if (!strcmp(".",component)) {
1585 cm_ReleaseSCache(dirScp);
1591 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
1592 extraFlag = CM_FLAG_NOMOUNTCHASE;
1593 code = cm_Lookup(tscp, component,
1595 userp, reqp, &nscp);
1597 cm_ReleaseSCache(tscp);
1599 cm_ReleaseSCache(dirScp);
1602 if (code == CM_ERROR_NOSUCHFILE && tscp->fileType == CM_SCACHETYPE_SYMLINK)
1603 return CM_ERROR_NOSUCHPATH;
1607 haveComponent = 0; /* component done */
1609 cm_ReleaseSCache(dirScp);
1610 dirScp = tscp; /* for some symlinks */
1611 tscp = nscp; /* already held */
1613 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
1616 cm_ReleaseSCache(dirScp);
1622 /* now, if tscp is a symlink, we should follow
1623 * it and assemble the path again.
1625 lock_ObtainMutex(&tscp->mx);
1626 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1627 CM_SCACHESYNC_GETSTATUS
1628 | CM_SCACHESYNC_NEEDCALLBACK);
1630 lock_ReleaseMutex(&tscp->mx);
1631 cm_ReleaseSCache(tscp);
1634 cm_ReleaseSCache(dirScp);
1639 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
1640 /* this is a symlink; assemble a new buffer */
1641 lock_ReleaseMutex(&tscp->mx);
1642 if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
1643 cm_ReleaseSCache(tscp);
1646 cm_ReleaseSCache(dirScp);
1651 return CM_ERROR_TOO_MANY_SYMLINKS;
1657 code = cm_AssembleLink(tscp, restp, &linkScp, &tempsp, userp, reqp);
1659 /* something went wrong */
1660 cm_ReleaseSCache(tscp);
1663 cm_ReleaseSCache(dirScp);
1669 /* otherwise, tempsp has the new path,
1670 * and linkScp is the new root from
1671 * which to interpret that path.
1672 * Continue with the namei processing,
1673 * also doing the bookkeeping for the
1674 * space allocation and tracking the
1675 * vnode reference counts.
1681 cm_ReleaseSCache(tscp);
1686 * now, if linkScp is null, that's
1687 * AssembleLink's way of telling us that
1688 * the sym link is relative to the dir
1689 * containing the link. We have a ref
1690 * to it in dirScp, and we hold it now
1691 * and reuse it as the new spot in the
1699 /* not a symlink, we may be done */
1700 lock_ReleaseMutex(&tscp->mx);
1708 cm_ReleaseSCache(dirScp);
1716 cm_ReleaseSCache(dirScp);
1719 } /* end of a component */
1722 } /* we have a component */
1723 } /* big while loop over all components */
1727 cm_ReleaseSCache(dirScp);
1733 cm_ReleaseSCache(tscp);
1737 /* called with a dir, and a vnode within the dir that happens to be a symlink.
1738 * We chase the link, and return a held pointer to the target, if it exists,
1739 * in *outScpp. If we succeed, we return 0, otherwise we return an error code
1740 * and do not hold or return a target vnode.
1742 * This is very similar to calling cm_NameI with the last component of a name,
1743 * which happens to be a symlink, except that we've already passed by the name.
1745 * This function is typically called by the directory listing functions, which
1746 * encounter symlinks but need to return the proper file length so programs
1747 * like "more" work properly when they make use of the attributes retrieved from
1750 * The input vnode should not be locked when this function is called.
1752 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
1753 cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
1757 cm_scache_t *newRootScp;
1759 osi_Log1(afsd_logp, "Evaluating symlink scp 0x%p", linkScp);
1761 code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
1765 /* now, if newRootScp is NULL, we're really being told that the symlink
1766 * is relative to the current directory (dscp).
1768 if (newRootScp == NULL) {
1770 cm_HoldSCache(dscp);
1773 code = cm_NameI(newRootScp, spacep->data,
1774 CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
1775 userp, NULL, reqp, outScpp);
1777 if (code == CM_ERROR_NOSUCHFILE)
1778 code = CM_ERROR_NOSUCHPATH;
1780 /* this stuff is allocated no matter what happened on the namei call,
1782 cm_FreeSpace(spacep);
1783 cm_ReleaseSCache(newRootScp);
1788 /* make this big enough so that one buffer of dir pages won't overflow. We'll
1789 * check anyway, but we want to minimize the chance that we have to leave stuff
1792 #define CM_BULKMAX (3 * AFSCBMAX)
1794 /* rock for bulk stat calls */
1795 typedef struct cm_bulkStat {
1796 osi_hyper_t bufOffset; /* only do it for things in this buffer page */
1798 /* info for the actual call */
1799 int counter; /* next free slot */
1800 AFSFid fids[CM_BULKMAX];
1801 AFSFetchStatus stats[CM_BULKMAX];
1802 AFSCallBack callbacks[CM_BULKMAX];
1805 /* for a given entry, make sure that it isn't in the stat cache, and then
1806 * add it to the list of file IDs to be obtained.
1808 * Don't bother adding it if we already have a vnode. Note that the dir
1809 * is locked, so we have to be careful checking the vnode we're thinking of
1810 * processing, to avoid deadlocks.
1812 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
1823 /* Don't overflow bsp. */
1824 if (bsp->counter >= CM_BULKMAX)
1825 return CM_ERROR_STOPNOW;
1827 thyper.LowPart = cm_data.buf_blockSize;
1828 thyper.HighPart = 0;
1829 thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
1831 /* thyper is now the first byte past the end of the record we're
1832 * interested in, and bsp->bufOffset is the first byte of the record
1833 * we're interested in.
1834 * Skip data in the others.
1837 if (LargeIntegerLessThan(*offp, bsp->bufOffset))
1839 if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
1840 return CM_ERROR_STOPNOW;
1841 if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
1844 tfid.cell = scp->fid.cell;
1845 tfid.volume = scp->fid.volume;
1846 tfid.vnode = ntohl(dep->fid.vnode);
1847 tfid.unique = ntohl(dep->fid.unique);
1848 tscp = cm_FindSCache(&tfid);
1850 if (lock_TryMutex(&tscp->mx)) {
1851 /* we have an entry that we can look at */
1852 if (!(tscp->flags & CM_SCACHEFLAG_EACCESS) && cm_HaveCallback(tscp)) {
1853 /* we have a callback on it. Don't bother
1854 * fetching this stat entry, since we're happy
1855 * with the info we have.
1857 lock_ReleaseMutex(&tscp->mx);
1858 cm_ReleaseSCache(tscp);
1861 lock_ReleaseMutex(&tscp->mx);
1863 cm_ReleaseSCache(tscp);
1866 #ifdef AFS_FREELANCE_CLIENT
1867 // yj: if this is a mountpoint under root.afs then we don't want it
1868 // to be bulkstat-ed, instead, we call getSCache directly and under
1869 // getSCache, it is handled specially.
1870 if ( cm_freelanceEnabled &&
1871 tfid.cell==AFS_FAKE_ROOT_CELL_ID &&
1872 tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
1873 !(tfid.vnode==0x1 && tfid.unique==0x1) )
1875 osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
1876 return cm_GetSCache(&tfid, &tscp, NULL, NULL);
1878 #endif /* AFS_FREELANCE_CLIENT */
1881 bsp->fids[i].Volume = scp->fid.volume;
1882 bsp->fids[i].Vnode = tfid.vnode;
1883 bsp->fids[i].Unique = tfid.unique;
1887 /* called with a locked scp and a pointer to a buffer. Make bulk stat
1888 * calls on all undeleted files in the page of the directory specified.
1891 cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
1895 cm_bulkStat_t bb; /* this is *BIG*, probably 16K or so;
1896 * watch for stack problems */
1897 AFSCBFids fidStruct;
1898 AFSBulkStats statStruct;
1900 AFSCBs callbackStruct;
1903 cm_callbackRequest_t cbReq;
1909 struct rx_connection * callp;
1910 int inlinebulk = 0; /* Did we use InlineBulkStatus RPC or not? */
1912 osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%p", dscp);
1914 /* should be on a buffer boundary */
1915 osi_assert((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0);
1917 memset(&bb, 0, sizeof(bb));
1918 bb.bufOffset = *offsetp;
1920 lock_ReleaseMutex(&dscp->mx);
1921 /* first, assemble the file IDs we need to stat */
1922 code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) &bb, offsetp, userp, reqp, NULL);
1924 /* if we failed, bail out early */
1925 if (code && code != CM_ERROR_STOPNOW) {
1926 lock_ObtainMutex(&dscp->mx);
1930 /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
1931 * make the calls to create the entries. Handle AFSCBMAX files at a
1935 while (filex < bb.counter) {
1936 filesThisCall = bb.counter - filex;
1937 if (filesThisCall > AFSCBMAX)
1938 filesThisCall = AFSCBMAX;
1940 fidStruct.AFSCBFids_len = filesThisCall;
1941 fidStruct.AFSCBFids_val = &bb.fids[filex];
1942 statStruct.AFSBulkStats_len = filesThisCall;
1943 statStruct.AFSBulkStats_val = &bb.stats[filex];
1944 callbackStruct.AFSCBs_len = filesThisCall;
1945 callbackStruct.AFSCBs_val = &bb.callbacks[filex];
1946 cm_StartCallbackGrantingCall(NULL, &cbReq);
1947 osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
1949 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1953 callp = cm_GetRxConn(connp);
1954 if (!(connp->serverp->flags & CM_SERVERFLAG_NOINLINEBULK)) {
1955 code = RXAFS_InlineBulkStatus(callp, &fidStruct,
1956 &statStruct, &callbackStruct, &volSync);
1957 if (code == RXGEN_OPCODE) {
1958 cm_SetServerNoInlineBulk(connp->serverp, 0);
1964 code = RXAFS_BulkStatus(callp, &fidStruct,
1965 &statStruct, &callbackStruct, &volSync);
1967 rx_PutConnection(callp);
1969 } while (cm_Analyze(connp, userp, reqp, &dscp->fid,
1970 &volSync, NULL, &cbReq, code));
1971 code = cm_MapRPCError(code, reqp);
1973 osi_Log2(afsd_logp, "CALL %sBulkStatus FAILURE code 0x%x",
1974 inlinebulk ? "Inline" : "", code);
1976 osi_Log1(afsd_logp, "CALL %sBulkStatus SUCCESS", inlinebulk ? "Inline" : "");
1978 /* may as well quit on an error, since we're not going to do
1979 * much better on the next immediate call, either.
1982 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
1986 /* otherwise, we should do the merges */
1987 for (i = 0; i<filesThisCall; i++) {
1989 tfid.cell = dscp->fid.cell;
1990 tfid.volume = bb.fids[j].Volume;
1991 tfid.vnode = bb.fids[j].Vnode;
1992 tfid.unique = bb.fids[j].Unique;
1993 code = cm_GetSCache(&tfid, &scp, userp, reqp);
1997 /* otherwise, if this entry has no callback info,
2000 lock_ObtainMutex(&scp->mx);
2001 /* now, we have to be extra paranoid on merging in this
2002 * information, since we didn't use cm_SyncOp before
2003 * starting the fetch to make sure that no bad races
2004 * were occurring. Specifically, we need to make sure
2005 * we don't obliterate any newer information in the
2006 * vnode than have here.
2008 * Right now, be pretty conservative: if there's a
2009 * callback or a pending call, skip it.
2011 if ((scp->cbServerp == NULL || (scp->flags & CM_SCACHEFLAG_EACCESS))
2013 (CM_SCACHEFLAG_FETCHING
2014 | CM_SCACHEFLAG_STORING
2015 | CM_SCACHEFLAG_SIZESTORING))) {
2016 cm_EndCallbackGrantingCall(scp, &cbReq,
2018 CM_CALLBACK_MAINTAINCOUNT);
2019 cm_MergeStatus(scp, &bb.stats[j], &volSync, userp, 0);
2021 lock_ReleaseMutex(&scp->mx);
2022 cm_ReleaseSCache(scp);
2023 } /* all files in the response */
2024 /* now tell it to drop the count,
2025 * after doing the vnode processing above */
2026 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2028 filex += filesThisCall;
2029 } /* while there are still more files to process */
2030 lock_ObtainMutex(&dscp->mx);
2033 /* If we did the InlineBulk RPC pull out the return code */
2035 if ((&bb.stats[0])->errorCode) {
2036 cm_Analyze(NULL /*connp was released by the previous cm_Analyze */,
2037 userp, reqp, &dscp->fid, &volSync, NULL, NULL, (&bb.stats[0])->errorCode);
2038 code = cm_MapRPCError((&bb.stats[0])->errorCode, reqp);
2046 osi_Log1(afsd_logp, "END cm_TryBulkStat code = 0x%x", code);
2050 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
2054 /* initialize store back mask as inexpensive local variable */
2056 memset(statusp, 0, sizeof(AFSStoreStatus));
2058 /* copy out queued info from scache first, if scp passed in */
2060 if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
2061 statusp->ClientModTime = scp->clientModTime;
2062 mask |= AFS_SETMODTIME;
2063 scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
2068 /* now add in our locally generated request */
2069 if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
2070 statusp->ClientModTime = attrp->clientModTime;
2071 mask |= AFS_SETMODTIME;
2073 if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
2074 statusp->UnixModeBits = attrp->unixModeBits;
2075 mask |= AFS_SETMODE;
2077 if (attrp->mask & CM_ATTRMASK_OWNER) {
2078 statusp->Owner = attrp->owner;
2079 mask |= AFS_SETOWNER;
2081 if (attrp->mask & CM_ATTRMASK_GROUP) {
2082 statusp->Group = attrp->group;
2083 mask |= AFS_SETGROUP;
2086 statusp->Mask = mask;
2089 /* set the file size, and make sure that all relevant buffers have been
2090 * truncated. Ensure that any partially truncated buffers have been zeroed
2091 * to the end of the buffer.
2093 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
2099 /* start by locking out buffer creation */
2100 lock_ObtainWrite(&scp->bufCreateLock);
2102 /* verify that this is a file, not a dir or a symlink */
2103 lock_ObtainMutex(&scp->mx);
2104 code = cm_SyncOp(scp, NULL, userp, reqp, 0,
2105 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2109 if (scp->fileType != CM_SCACHETYPE_FILE) {
2110 code = CM_ERROR_ISDIR;
2115 if (LargeIntegerLessThan(*sizep, scp->length))
2120 lock_ReleaseMutex(&scp->mx);
2122 /* can't hold scp->mx lock here, since we may wait for a storeback to
2123 * finish if the buffer package is cleaning a buffer by storing it to
2127 buf_Truncate(scp, userp, reqp, sizep);
2129 /* now ensure that file length is short enough, and update truncPos */
2130 lock_ObtainMutex(&scp->mx);
2132 /* make sure we have a callback (so we have the right value for the
2133 * length), and wait for it to be safe to do a truncate.
2135 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
2136 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2137 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2141 if (LargeIntegerLessThan(*sizep, scp->length)) {
2142 /* a real truncation. If truncPos is not set yet, or is bigger
2143 * than where we're truncating the file, set truncPos to this
2148 if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
2149 || LargeIntegerLessThan(*sizep, scp->length)) {
2151 scp->truncPos = *sizep;
2152 scp->mask |= CM_SCACHEMASK_TRUNCPOS;
2154 /* in either case, the new file size has been changed */
2155 scp->length = *sizep;
2156 scp->mask |= CM_SCACHEMASK_LENGTH;
2158 else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
2159 /* really extending the file */
2160 scp->length = *sizep;
2161 scp->mask |= CM_SCACHEMASK_LENGTH;
2164 /* done successfully */
2168 lock_ReleaseMutex(&scp->mx);
2169 lock_ReleaseWrite(&scp->bufCreateLock);
2174 /* set the file size or other attributes (but not both at once) */
2175 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
2180 AFSFetchStatus afsOutStatus;
2184 AFSStoreStatus afsInStatus;
2185 struct rx_connection * callp;
2187 /* handle file length setting */
2188 if (attrp->mask & CM_ATTRMASK_LENGTH)
2189 return cm_SetLength(scp, &attrp->length, userp, reqp);
2191 flags = CM_SCACHESYNC_STORESTATUS;
2193 lock_ObtainMutex(&scp->mx);
2194 /* otherwise, we have to make an RPC to get the status */
2195 code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
2197 /* make the attr structure */
2198 cm_StatusFromAttr(&afsInStatus, scp, attrp);
2200 tfid.Volume = scp->fid.volume;
2201 tfid.Vnode = scp->fid.vnode;
2202 tfid.Unique = scp->fid.unique;
2204 lock_ReleaseMutex(&scp->mx);
2208 /* now make the RPC */
2209 osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%p", scp);
2211 code = cm_Conn(&scp->fid, userp, reqp, &connp);
2215 callp = cm_GetRxConn(connp);
2216 code = RXAFS_StoreStatus(callp, &tfid,
2217 &afsInStatus, &afsOutStatus, &volSync);
2218 rx_PutConnection(callp);
2220 } while (cm_Analyze(connp, userp, reqp,
2221 &scp->fid, &volSync, NULL, NULL, code));
2222 code = cm_MapRPCError(code, reqp);
2225 osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
2227 osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
2229 lock_ObtainMutex(&scp->mx);
2230 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
2232 cm_MergeStatus(scp, &afsOutStatus, &volSync, userp,
2233 CM_MERGEFLAG_FORCE);
2235 /* if we're changing the mode bits, discard the ACL cache,
2236 * since we changed the mode bits.
2238 if (afsInStatus.Mask & AFS_SETMODE) cm_FreeAllACLEnts(scp);
2239 lock_ReleaseMutex(&scp->mx);
2243 long cm_Create(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2244 cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
2249 cm_callbackRequest_t cbReq;
2254 AFSStoreStatus inStatus;
2255 AFSFetchStatus updatedDirStatus;
2256 AFSFetchStatus newFileStatus;
2257 AFSCallBack newFileCallback;
2259 struct rx_connection * callp;
2261 /* can't create names with @sys in them; must expand it manually first.
2262 * return "invalid request" if they try.
2264 if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2265 return CM_ERROR_ATSYS;
2268 /* before starting the RPC, mark that we're changing the file data, so
2269 * that someone who does a chmod will know to wait until our call
2272 lock_ObtainMutex(&dscp->mx);
2273 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2275 cm_StartCallbackGrantingCall(NULL, &cbReq);
2277 lock_ReleaseMutex(&dscp->mx);
2283 cm_StatusFromAttr(&inStatus, NULL, attrp);
2285 /* try the RPC now */
2286 osi_Log1(afsd_logp, "CALL CreateFile scp 0x%p", dscp);
2288 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2292 dirAFSFid.Volume = dscp->fid.volume;
2293 dirAFSFid.Vnode = dscp->fid.vnode;
2294 dirAFSFid.Unique = dscp->fid.unique;
2296 callp = cm_GetRxConn(connp);
2297 code = RXAFS_CreateFile(connp->callp, &dirAFSFid, namep,
2298 &inStatus, &newAFSFid, &newFileStatus,
2299 &updatedDirStatus, &newFileCallback,
2301 rx_PutConnection(callp);
2303 } while (cm_Analyze(connp, userp, reqp,
2304 &dscp->fid, &volSync, NULL, &cbReq, code));
2305 code = cm_MapRPCError(code, reqp);
2308 osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
2310 osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
2312 lock_ObtainMutex(&dscp->mx);
2313 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2315 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2317 lock_ReleaseMutex(&dscp->mx);
2319 /* now try to create the file's entry, too, but be careful to
2320 * make sure that we don't merge in old info. Since we weren't locking
2321 * out any requests during the file's creation, we may have pretty old
2325 newFid.cell = dscp->fid.cell;
2326 newFid.volume = dscp->fid.volume;
2327 newFid.vnode = newAFSFid.Vnode;
2328 newFid.unique = newAFSFid.Unique;
2329 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2331 lock_ObtainMutex(&scp->mx);
2332 scp->creator = userp; /* remember who created it */
2333 if (!cm_HaveCallback(scp)) {
2334 cm_MergeStatus(scp, &newFileStatus, &volSync,
2336 cm_EndCallbackGrantingCall(scp, &cbReq,
2337 &newFileCallback, 0);
2340 lock_ReleaseMutex(&scp->mx);
2345 /* make sure we end things properly */
2347 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2352 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
2356 lock_ObtainWrite(&scp->bufCreateLock);
2357 code = buf_CleanVnode(scp, userp, reqp);
2358 lock_ReleaseWrite(&scp->bufCreateLock);
2360 lock_ObtainMutex(&scp->mx);
2362 if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
2363 | CM_SCACHEMASK_CLIENTMODTIME
2364 | CM_SCACHEMASK_LENGTH))
2365 code = cm_StoreMini(scp, userp, reqp);
2367 if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) {
2368 code = (scp->flags & CM_SCACHEFLAG_OVERQUOTA) ? CM_ERROR_QUOTA : CM_ERROR_SPACE;
2369 scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE);
2372 lock_ReleaseMutex(&scp->mx);
2377 long cm_MakeDir(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2378 cm_user_t *userp, cm_req_t *reqp)
2383 cm_callbackRequest_t cbReq;
2388 AFSStoreStatus inStatus;
2389 AFSFetchStatus updatedDirStatus;
2390 AFSFetchStatus newDirStatus;
2391 AFSCallBack newDirCallback;
2393 struct rx_connection * callp;
2395 /* can't create names with @sys in them; must expand it manually first.
2396 * return "invalid request" if they try.
2398 if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2399 return CM_ERROR_ATSYS;
2402 /* before starting the RPC, mark that we're changing the directory
2403 * data, so that someone who does a chmod on the dir will wait until
2404 * our call completes.
2406 lock_ObtainMutex(&dscp->mx);
2407 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2409 cm_StartCallbackGrantingCall(NULL, &cbReq);
2411 lock_ReleaseMutex(&dscp->mx);
2417 cm_StatusFromAttr(&inStatus, NULL, attrp);
2419 /* try the RPC now */
2420 osi_Log1(afsd_logp, "CALL MakeDir scp 0x%p", dscp);
2422 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2426 dirAFSFid.Volume = dscp->fid.volume;
2427 dirAFSFid.Vnode = dscp->fid.vnode;
2428 dirAFSFid.Unique = dscp->fid.unique;
2430 callp = cm_GetRxConn(connp);
2431 code = RXAFS_MakeDir(connp->callp, &dirAFSFid, namep,
2432 &inStatus, &newAFSFid, &newDirStatus,
2433 &updatedDirStatus, &newDirCallback,
2435 rx_PutConnection(callp);
2437 } while (cm_Analyze(connp, userp, reqp,
2438 &dscp->fid, &volSync, NULL, &cbReq, code));
2439 code = cm_MapRPCError(code, reqp);
2442 osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
2444 osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
2446 lock_ObtainMutex(&dscp->mx);
2447 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2449 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2451 lock_ReleaseMutex(&dscp->mx);
2453 /* now try to create the new dir's entry, too, but be careful to
2454 * make sure that we don't merge in old info. Since we weren't locking
2455 * out any requests during the file's creation, we may have pretty old
2459 newFid.cell = dscp->fid.cell;
2460 newFid.volume = dscp->fid.volume;
2461 newFid.vnode = newAFSFid.Vnode;
2462 newFid.unique = newAFSFid.Unique;
2463 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2465 lock_ObtainMutex(&scp->mx);
2466 if (!cm_HaveCallback(scp)) {
2467 cm_MergeStatus(scp, &newDirStatus, &volSync,
2469 cm_EndCallbackGrantingCall(scp, &cbReq,
2470 &newDirCallback, 0);
2473 lock_ReleaseMutex(&scp->mx);
2474 cm_ReleaseSCache(scp);
2478 /* make sure we end things properly */
2480 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2482 /* and return error code */
2486 long cm_Link(cm_scache_t *dscp, char *namep, cm_scache_t *sscp, long flags,
2487 cm_user_t *userp, cm_req_t *reqp)
2492 AFSFid existingAFSFid;
2493 AFSFetchStatus updatedDirStatus;
2494 AFSFetchStatus newLinkStatus;
2496 struct rx_connection * callp;
2498 if (dscp->fid.cell != sscp->fid.cell ||
2499 dscp->fid.volume != sscp->fid.volume) {
2500 return CM_ERROR_CROSSDEVLINK;
2503 lock_ObtainMutex(&dscp->mx);
2504 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2505 lock_ReleaseMutex(&dscp->mx);
2510 /* try the RPC now */
2511 osi_Log1(afsd_logp, "CALL Link scp 0x%p", dscp);
2513 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2516 dirAFSFid.Volume = dscp->fid.volume;
2517 dirAFSFid.Vnode = dscp->fid.vnode;
2518 dirAFSFid.Unique = dscp->fid.unique;
2520 existingAFSFid.Volume = sscp->fid.volume;
2521 existingAFSFid.Vnode = sscp->fid.vnode;
2522 existingAFSFid.Unique = sscp->fid.unique;
2524 callp = cm_GetRxConn(connp);
2525 code = RXAFS_Link(callp, &dirAFSFid, namep, &existingAFSFid,
2526 &newLinkStatus, &updatedDirStatus, &volSync);
2527 rx_PutConnection(callp);
2528 osi_Log1(smb_logp," RXAFS_Link returns 0x%x", code);
2530 } while (cm_Analyze(connp, userp, reqp,
2531 &dscp->fid, &volSync, NULL, NULL, code));
2533 code = cm_MapRPCError(code, reqp);
2536 osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
2538 osi_Log0(afsd_logp, "CALL Link SUCCESS");
2540 lock_ObtainMutex(&dscp->mx);
2541 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2543 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2545 lock_ReleaseMutex(&dscp->mx);
2550 long cm_SymLink(cm_scache_t *dscp, char *namep, char *contentsp, long flags,
2551 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
2559 AFSStoreStatus inStatus;
2560 AFSFetchStatus updatedDirStatus;
2561 AFSFetchStatus newLinkStatus;
2563 struct rx_connection * callp;
2565 /* before starting the RPC, mark that we're changing the directory data,
2566 * so that someone who does a chmod on the dir will wait until our
2569 lock_ObtainMutex(&dscp->mx);
2570 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2571 lock_ReleaseMutex(&dscp->mx);
2576 cm_StatusFromAttr(&inStatus, NULL, attrp);
2578 /* try the RPC now */
2579 osi_Log1(afsd_logp, "CALL Symlink scp 0x%p", dscp);
2581 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2585 dirAFSFid.Volume = dscp->fid.volume;
2586 dirAFSFid.Vnode = dscp->fid.vnode;
2587 dirAFSFid.Unique = dscp->fid.unique;
2589 callp = cm_GetRxConn(connp);
2590 code = RXAFS_Symlink(callp, &dirAFSFid, namep, contentsp,
2591 &inStatus, &newAFSFid, &newLinkStatus,
2592 &updatedDirStatus, &volSync);
2593 rx_PutConnection(callp);
2595 } while (cm_Analyze(connp, userp, reqp,
2596 &dscp->fid, &volSync, NULL, NULL, code));
2597 code = cm_MapRPCError(code, reqp);
2600 osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
2602 osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
2604 lock_ObtainMutex(&dscp->mx);
2605 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2607 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2609 lock_ReleaseMutex(&dscp->mx);
2611 /* now try to create the new dir's entry, too, but be careful to
2612 * make sure that we don't merge in old info. Since we weren't locking
2613 * out any requests during the file's creation, we may have pretty old
2617 newFid.cell = dscp->fid.cell;
2618 newFid.volume = dscp->fid.volume;
2619 newFid.vnode = newAFSFid.Vnode;
2620 newFid.unique = newAFSFid.Unique;
2621 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2623 lock_ObtainMutex(&scp->mx);
2624 if (!cm_HaveCallback(scp)) {
2625 cm_MergeStatus(scp, &newLinkStatus, &volSync,
2628 lock_ReleaseMutex(&scp->mx);
2629 cm_ReleaseSCache(scp);
2633 /* and return error code */
2637 long cm_RemoveDir(cm_scache_t *dscp, char *namep, cm_user_t *userp,
2644 AFSFetchStatus updatedDirStatus;
2646 struct rx_connection * callp;
2648 /* before starting the RPC, mark that we're changing the directory data,
2649 * so that someone who does a chmod on the dir will wait until our
2652 lock_ObtainMutex(&dscp->mx);
2653 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2654 lock_ReleaseMutex(&dscp->mx);
2660 /* try the RPC now */
2661 osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%p", dscp);
2663 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2667 dirAFSFid.Volume = dscp->fid.volume;
2668 dirAFSFid.Vnode = dscp->fid.vnode;
2669 dirAFSFid.Unique = dscp->fid.unique;
2671 callp = cm_GetRxConn(connp);
2672 code = RXAFS_RemoveDir(callp, &dirAFSFid, namep,
2673 &updatedDirStatus, &volSync);
2674 rx_PutConnection(callp);
2676 } while (cm_Analyze(connp, userp, reqp,
2677 &dscp->fid, &volSync, NULL, NULL, code));
2678 code = cm_MapRPCErrorRmdir(code, reqp);
2681 osi_Log1(afsd_logp, "CALL RemoveDir FAILURE, code 0x%x", code);
2683 osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
2685 lock_ObtainMutex(&dscp->mx);
2686 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2688 cm_dnlcRemove(dscp, namep);
2689 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2691 lock_ReleaseMutex(&dscp->mx);
2693 /* and return error code */
2697 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
2699 /* grab mutex on contents */
2700 lock_ObtainMutex(&scp->mx);
2702 /* reset the prefetch info */
2703 scp->prefetch.base.LowPart = 0; /* base */
2704 scp->prefetch.base.HighPart = 0;
2705 scp->prefetch.end.LowPart = 0; /* and end */
2706 scp->prefetch.end.HighPart = 0;
2708 /* release mutex on contents */
2709 lock_ReleaseMutex(&scp->mx);
2715 long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
2716 char *newNamep, cm_user_t *userp, cm_req_t *reqp)
2720 AFSFid oldDirAFSFid;
2721 AFSFid newDirAFSFid;
2723 AFSFetchStatus updatedOldDirStatus;
2724 AFSFetchStatus updatedNewDirStatus;
2727 struct rx_connection * callp;
2729 /* before starting the RPC, mark that we're changing the directory data,
2730 * so that someone who does a chmod on the dir will wait until our call
2731 * completes. We do this in vnode order so that we don't deadlock,
2732 * which makes the code a little verbose.
2734 if (oldDscp == newDscp) {
2735 /* check for identical names */
2736 if (strcmp(oldNamep, newNamep) == 0)
2737 return CM_ERROR_RENAME_IDENTICAL;
2740 lock_ObtainMutex(&oldDscp->mx);
2741 cm_dnlcRemove(oldDscp, oldNamep);
2742 cm_dnlcRemove(oldDscp, newNamep);
2743 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2744 CM_SCACHESYNC_STOREDATA);
2745 lock_ReleaseMutex(&oldDscp->mx);
2748 /* two distinct dir vnodes */
2750 if (oldDscp->fid.cell != newDscp->fid.cell ||
2751 oldDscp->fid.volume != newDscp->fid.volume)
2752 return CM_ERROR_CROSSDEVLINK;
2754 /* shouldn't happen that we have distinct vnodes for two
2755 * different files, but could due to deliberate attack, or
2756 * stale info. Avoid deadlocks and quit now.
2758 if (oldDscp->fid.vnode == newDscp->fid.vnode)
2759 return CM_ERROR_CROSSDEVLINK;
2761 if (oldDscp->fid.vnode < newDscp->fid.vnode) {
2762 lock_ObtainMutex(&oldDscp->mx);
2763 cm_dnlcRemove(oldDscp, oldNamep);
2764 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2765 CM_SCACHESYNC_STOREDATA);
2766 lock_ReleaseMutex(&oldDscp->mx);
2768 lock_ObtainMutex(&newDscp->mx);
2769 cm_dnlcRemove(newDscp, newNamep);
2770 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2771 CM_SCACHESYNC_STOREDATA);
2772 lock_ReleaseMutex(&newDscp->mx);
2774 /* cleanup first one */
2775 lock_ObtainMutex(&newDscp->mx);
2776 cm_SyncOpDone(oldDscp, NULL,
2777 CM_SCACHESYNC_STOREDATA);
2778 lock_ReleaseMutex(&oldDscp->mx);
2783 /* lock the new vnode entry first */
2784 lock_ObtainMutex(&newDscp->mx);
2785 cm_dnlcRemove(newDscp, newNamep);
2786 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2787 CM_SCACHESYNC_STOREDATA);
2788 lock_ReleaseMutex(&newDscp->mx);
2790 lock_ObtainMutex(&oldDscp->mx);
2791 cm_dnlcRemove(oldDscp, oldNamep);
2792 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2793 CM_SCACHESYNC_STOREDATA);
2794 lock_ReleaseMutex(&oldDscp->mx);
2796 /* cleanup first one */
2797 lock_ObtainMutex(&newDscp->mx);
2798 cm_SyncOpDone(newDscp, NULL,
2799 CM_SCACHESYNC_STOREDATA);
2800 lock_ReleaseMutex(&newDscp->mx);
2804 } /* two distinct vnodes */
2811 /* try the RPC now */
2812 osi_Log2(afsd_logp, "CALL Rename old scp 0x%p new scp 0x%p",
2815 code = cm_Conn(&oldDscp->fid, userp, reqp, &connp);
2819 oldDirAFSFid.Volume = oldDscp->fid.volume;
2820 oldDirAFSFid.Vnode = oldDscp->fid.vnode;
2821 oldDirAFSFid.Unique = oldDscp->fid.unique;
2822 newDirAFSFid.Volume = newDscp->fid.volume;
2823 newDirAFSFid.Vnode = newDscp->fid.vnode;
2824 newDirAFSFid.Unique = newDscp->fid.unique;
2826 callp = cm_GetRxConn(connp);
2827 code = RXAFS_Rename(callp, &oldDirAFSFid, oldNamep,
2828 &newDirAFSFid, newNamep,
2829 &updatedOldDirStatus, &updatedNewDirStatus,
2831 rx_PutConnection(callp);
2833 } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
2834 &volSync, NULL, NULL, code));
2835 code = cm_MapRPCError(code, reqp);
2838 osi_Log1(afsd_logp, "CALL Rename FAILURE, code 0x%x", code);
2840 osi_Log0(afsd_logp, "CALL Rename SUCCESS");
2842 /* update the individual stat cache entries for the directories */
2843 lock_ObtainMutex(&oldDscp->mx);
2844 cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
2846 cm_MergeStatus(oldDscp, &updatedOldDirStatus, &volSync,
2849 lock_ReleaseMutex(&oldDscp->mx);
2851 /* and update it for the new one, too, if necessary */
2853 lock_ObtainMutex(&newDscp->mx);
2854 cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
2856 cm_MergeStatus(newDscp, &updatedNewDirStatus, &volSync,
2859 lock_ReleaseMutex(&newDscp->mx);
2862 /* and return error code */
2866 /* Byte range locks:
2868 The OpenAFS Windows client has to fake byte range locks given no
2869 server side support for such locks. This is implemented as keyed
2870 byte range locks on the cache manager.
2872 Keyed byte range locks:
2874 Each cm_scache_t structure keeps track of a list of keyed locks.
2875 The key for a lock identifies an owner of a set of locks (referred
2876 to as a client). Each key is represented by a value. The set of
2877 key values used within a specific cm_scache_t structure form a
2878 namespace that has a scope of just that cm_scache_t structure. The
2879 same key value can be used with another cm_scache_t structure and
2880 correspond to a completely different client. However it is
2881 advantageous for the SMB or IFS layer to make sure that there is a
2882 1-1 mapping between client and keys over all cm_scache_t objects.
2884 Assume a client C has key Key(C) (although, since the scope of the
2885 key is a cm_scache_t, the key can be Key(C,S), where S is the
2886 cm_scache_t. But assume a 1-1 relation between keys and clients).
2887 A byte range (O,+L) denotes byte addresses (O) through (O+L-1)
2888 inclusive (a.k.a. [O,O+L-1]). The function Key(x) is implemented
2889 through cm_generateKey() function for both SMB and IFS.
2891 The list of locks for a cm_scache_t object S is maintained in
2892 S->fileLocks. The cache manager will set a lock on the AFS file
2893 server in order to assert the locks in S->fileLocks. If only
2894 shared locks are in place for S, then the cache manager will obtain
2895 a LockRead lock, while if there are any exclusive locks, it will
2896 obtain a LockWrite lock. If the exclusive locks are all released
2897 while the shared locks remain, then the cache manager will
2898 downgrade the lock from LockWrite to LockRead. Similarly, if an
2899 exclusive lock is obtained when only shared locks exist, then the
2900 cache manager will try to upgrade the lock from LockRead to
2903 Each lock L owned by client C maintains a key L->key such that
2904 L->key == Key(C), the effective range defined by L->LOffset and
2905 L->LLength such that the range of bytes affected by the lock is
2906 (L->LOffset, +L->LLength), a type maintained in L->LockType which
2907 is either exclusive or shared.
2911 A lock exists iff it is in S->fileLocks for some cm_scache_t
2912 S. Existing locks are in one of the following states: ACTIVE,
2913 WAITLOCK, WAITUNLOCK, LOST, DELETED.
2915 The following sections describe each lock and the associated
2918 1. ACTIVE: A lock L is ACTIVE iff the cache manager has asserted
2919 the lock with the AFS file server. This type of lock can be
2920 exercised by a client to read or write to the locked region (as
2923 1.1 ACTIVE->LOST: When the AFS file server fails to extend a
2924 server lock that was required to assert the lock. Before
2925 marking the lock as lost, the cache manager checks if the file
2926 has changed on the server. If the file has not changed, then
2927 the cache manager will attempt to obtain a new server lock
2928 that is sufficient to assert the client side locks for the
2929 file. If any of these fail, the lock is marked as LOST.
2930 Otherwise, it is left as ACTIVE.
2932 1.2 ACTIVE->DELETED: Lock is released.
2934 2. WAITLOCK: A lock is in a WAITLOCK state if the cache manager
2935 grants the lock but the lock is yet to be asserted with the AFS
2936 file server. Once the file server grants the lock, the state
2937 will transition to an ACTIVE lock.
2939 2.1 WAITLOCK->ACTIVE: The server granted the lock.
2941 2.2 WAITLOCK->DELETED: Lock is abandoned, or timed out during
2944 2.3 WAITLOCK->LOST: One or more locks from this client were
2945 marked as LOST. No further locks will be granted to this
2946 client until all lost locks are removed.
2948 3. WAITUNLOCK: A lock is in a WAITUNLOCK state if the cache manager
2949 receives a request for a lock that conflicts with an existing
2950 ACTIVE or WAITLOCK lock. The lock will be placed in the queue
2951 and will be granted at such time the conflicting locks are
2952 removed, at which point the state will transition to either
2955 3.1 WAITUNLOCK->ACTIVE: The conflicting lock was removed. The
2956 current serverLock is sufficient to assert this lock, or a
2957 sufficient serverLock is obtained.
2959 3.2 WAITUNLOCK->WAITLOCK: The conflicting lock was removed,
2960 however the required serverLock is yet to be asserted with the
2963 3.3 WAITUNLOCK->DELETED: The lock is abandoned, timed out or
2966 3.5 WAITUNLOCK->LOST: One or more locks from this client were
2967 marked as LOST. No further locks will be granted to this
2968 client until all lost locks are removed.
2970 4. LOST: A lock L is LOST if the server lock that was required to
2971 assert the lock could not be obtained or if it could not be
2972 extended, or if other locks by the same client were LOST.
2973 Essentially, once a lock is LOST, the contract between the cache
2974 manager and that specific client is no longer valid.
2976 The cache manager rechecks the server lock once every minute and
2977 extends it as appropriate. If this is not done for 5 minutes,
2978 the AFS file server will release the lock (the 5 minute timeout
2979 is based on current file server code and is fairly arbitrary).
2980 Once released, the lock cannot be re-obtained without verifying
2981 that the contents of the file hasn't been modified since the
2982 time the lock was released. Re-obtaining the lock without
2983 verifying this may lead to data corruption. If the lock can not
2984 be obtained safely, then all active locks for the cm_scache_t
2987 4.1 LOST->DELETED: The lock is released.
2989 5. DELETED: The lock is no longer relevant. Eventually, it will
2990 get removed from the cm_scache_t. In the meantime, it will be
2991 treated as if it does not exist.
2993 5.1 DELETED->not exist: The lock is removed from the
2996 The following are classifications of locks based on their state.
2998 6* A lock L is ACCEPTED if it is ACTIVE or WAITLOCK. These locks
2999 have been accepted by the cache manager, but may or may not have
3000 been granted back to the client.
3002 7* A lock L is QUEUED if it is ACTIVE, WAITLOCK or WAITUNLOCK.
3004 8* A lock L is WAITING if it is WAITLOCK or WAITUNLOCK.
3008 A client C can READ range (Offset,+Length) of a file represented by
3009 cm_scache_t S iff (1):
3011 1. for all _a_ in (Offset,+Length), all of the following is true:
3013 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3014 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3017 1.2 For each LOST lock L in S->fileLocks such that _a_ in
3018 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3021 (When locks are lost on an cm_scache_t, all locks are lost. By
3022 4.2 (below), if there is an exclusive LOST lock, then there
3023 can't be any overlapping ACTIVE locks.)
3025 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
3027 2. for all _a_ in (Offset,+Length), one of the following is true:
3029 2.1 Byte _a_ of S is unowned (as specified in 1.1) AND there
3030 does not exist a LOST lock L such that _a_ in
3031 (L->LOffset,+L->LLength).
3033 2.2 Byte _a_ of S is owned by C under lock L (as specified in
3034 1.2) AND L->LockType is exclusive.
3036 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
3038 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
3041 3.1 If L->LockType is exclusive then there does NOT exist a
3042 ACCEPTED lock M in S->fileLocks such that _a_ in
3043 (M->LOffset,+M->LLength).
3045 (If we count all QUEUED locks then we hit cases such as
3046 cascading waiting locks where the locks later on in the queue
3047 can be granted without compromising file integrity. On the
3048 other hand if only ACCEPTED locks are considered, then locks
3049 that were received earlier may end up waiting for locks that
3050 were received later to be unlocked. The choice of ACCEPTED
3051 locks was made to mimic the Windows byte range lock
3054 3.2 If L->LockType is shared then for each ACCEPTED lock M in
3055 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
3056 M->LockType is shared.
3058 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
3060 4.1 M->key != Key(C)
3062 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
3063 and (M->LOffset,+M->LLength) do not intersect.
3065 (Note: If a client loses a lock, it loses all locks.
3066 Subsequently, it will not be allowed to obtain any more locks
3067 until all existing LOST locks that belong to the client are
3068 released. Once all locks are released by a single client,
3069 there exists no further contract between the client and AFS
3070 about the contents of the file, hence the client can then
3071 proceed to obtain new locks and establish a new contract.
3073 This doesn't quite work as you think it should, because most
3074 applications aren't built to deal with losing locks they
3075 thought they once had. For now, we don't have a good
3076 solution to lost locks.
3078 Also, for consistency reasons, we have to hold off on
3079 granting locks that overlap exclusive LOST locks.)
3081 A client C can only unlock locks L in S->fileLocks which have
3084 The representation and invariants are as follows:
3086 - Each cm_scache_t structure keeps:
3088 - A queue of byte-range locks (cm_scache_t::fileLocks) which
3089 are of type cm_file_lock_t.
3091 - A record of the highest server-side lock that has been
3092 obtained for this object (cm_scache_t::serverLock), which is
3093 one of (-1), LockRead, LockWrite.
3095 - A count of ACCEPTED exclusive and shared locks that are in the
3096 queue (cm_scache_t::sharedLocks and
3097 cm_scache_t::exclusiveLocks)
3099 - Each cm_file_lock_t structure keeps:
3101 - The type of lock (cm_file_lock_t::LockType)
3103 - The key associated with the lock (cm_file_lock_t::key)
3105 - The offset and length of the lock (cm_file_lock_t::LOffset
3106 and cm_file_lock_t::LLength)
3108 - The state of the lock.
3110 - Time of issuance or last successful extension
3112 Semantic invariants:
3114 I1. The number of ACCEPTED locks in S->fileLocks are
3115 (S->sharedLocks + S->exclusiveLocks)
3117 External invariants:
3119 I3. S->serverLock is the lock that we have asserted with the
3120 AFS file server for this cm_scache_t.
3122 I4. S->serverLock == LockRead iff there is at least one ACTIVE
3123 shared lock, but no ACTIVE exclusive locks.
3125 I5. S->serverLock == LockWrite iff there is at least one ACTIVE
3128 I6. If L is a LOST lock, then for each lock M in S->fileLocks,
3129 M->key == L->key IMPLIES M is LOST or DELETED.
3134 #define IS_LOCK_ACTIVE(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == 0)
3136 #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)
3138 #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)
3140 #define IS_LOCK_LOST(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_LOST)
3142 #define IS_LOCK_DELETED(lockp) (((lockp)->flags & CM_FILELOCK_FLAG_DELETED) == CM_FILELOCK_FLAG_DELETED)
3145 #define IS_LOCK_ACCEPTED(lockp) (IS_LOCK_ACTIVE(lockp) || IS_LOCK_WAITLOCK(lockp))
3148 #define IS_LOCK_CLIENTONLY(lockp) ((((lockp)->scp->flags & CM_SCACHEFLAG_RO) == CM_SCACHEFLAG_RO) || (((lockp)->flags & CM_FILELOCK_FLAG_CLIENTONLY) == CM_FILELOCK_FLAG_CLIENTONLY))
3151 #define INTERSECT_RANGE(r1,r2) (((r2).offset+(r2).length) > (r1).offset && ((r1).offset +(r1).length) > (r2).offset)
3154 #define CONTAINS_RANGE(r1,r2) (((r2).offset+(r2).length) <= ((r1).offset+(r1).length) && (r1).offset <= (r2).offset)
3156 #if defined(VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS) && !defined(LOCK_TESTING)
3157 #define SCP_SUPPORTS_BRLOCKS(scp) ((scp)->cbServerp && ((scp)->cbServerp->capabilities & VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS))
3159 #define SCP_SUPPORTS_BRLOCKS(scp) (1)
3162 #define SERVERLOCKS_ENABLED(scp) (!((scp)->flags & CM_SCACHEFLAG_RO) && cm_enableServerLocks && SCP_SUPPORTS_BRLOCKS(scp))
3164 static void cm_LockRangeSubtract(cm_range_t * pos, const cm_range_t * neg)
3166 afs_int64 int_begin;
3169 int_begin = MAX(pos->offset, neg->offset);
3170 int_end = MIN(pos->offset+pos->length, neg->offset+neg->length);
3172 if (int_begin < int_end) {
3173 if (int_begin == pos->offset) {
3174 pos->length = pos->offset + pos->length - int_end;
3175 pos->offset = int_end;
3176 } else if (int_end == pos->offset + pos->length) {
3177 pos->length = int_begin - pos->offset;
3180 /* We only subtract ranges if the resulting range is
3181 contiguous. If we try to support non-contigous ranges, we
3182 aren't actually improving performance. */
3186 /* Called with scp->mx held. Returns 0 if all is clear to read the
3187 specified range by the client identified by key.
3189 long cm_LockCheckRead(cm_scache_t *scp,
3190 LARGE_INTEGER LOffset,
3191 LARGE_INTEGER LLength,
3194 #ifndef ADVISORY_LOCKS
3196 cm_file_lock_t *fileLock;
3200 int substract_ranges = FALSE;
3202 range.offset = LOffset.QuadPart;
3203 range.length = LLength.QuadPart;
3207 1. for all _a_ in (Offset,+Length), all of the following is true:
3209 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3210 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3213 1.2 For each LOST lock L in S->fileLocks such that _a_ in
3214 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3219 lock_ObtainRead(&cm_scacheLock);
3221 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
3223 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
3225 if (INTERSECT_RANGE(range, fileLock->range)) {
3226 if (IS_LOCK_ACTIVE(fileLock)) {
3227 if (fileLock->key == key) {
3229 /* If there is an active lock for this client, it
3230 is safe to substract ranges.*/
3231 cm_LockRangeSubtract(&range, &fileLock->range);
3232 substract_ranges = TRUE;
3234 if (fileLock->lockType != LockRead) {
3235 code = CM_ERROR_LOCK_CONFLICT;
3239 /* even if the entire range is locked for reading,
3240 we still can't grant the lock at this point
3241 because the client may have lost locks. That
3242 is, unless we have already seen an active lock
3243 belonging to the client, in which case there
3244 can't be any lost locks for this client. */
3245 if (substract_ranges)
3246 cm_LockRangeSubtract(&range, &fileLock->range);
3248 } else if (IS_LOCK_LOST(fileLock) &&
3249 (fileLock->key == key || fileLock->lockType == LockWrite)) {
3250 code = CM_ERROR_BADFD;
3256 lock_ReleaseRead(&cm_scacheLock);
3258 osi_Log4(afsd_logp, "cm_LockCheckRead scp 0x%x offset %d length %d code 0x%x",
3259 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
3270 /* Called with scp->mx held. Returns 0 if all is clear to write the
3271 specified range by the client identified by key.
3273 long cm_LockCheckWrite(cm_scache_t *scp,
3274 LARGE_INTEGER LOffset,
3275 LARGE_INTEGER LLength,
3278 #ifndef ADVISORY_LOCKS
3280 cm_file_lock_t *fileLock;
3285 range.offset = LOffset.QuadPart;
3286 range.length = LLength.QuadPart;
3289 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
3291 2. for all _a_ in (Offset,+Length), one of the following is true:
3293 2.1 Byte _a_ of S is unowned AND there does not exist a LOST
3294 lock L such that _a_ in (L->LOffset,+L->LLength).
3296 2.2 Byte _a_ of S is owned by C under lock L AND L->LockType is
3300 lock_ObtainRead(&cm_scacheLock);
3302 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
3304 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
3306 if (INTERSECT_RANGE(range, fileLock->range)) {
3307 if (IS_LOCK_ACTIVE(fileLock)) {
3308 if (fileLock->key == key) {
3309 if (fileLock->lockType == LockWrite) {
3311 /* if there is an active lock for this client, it
3312 is safe to substract ranges */
3313 cm_LockRangeSubtract(&range, &fileLock->range);
3315 code = CM_ERROR_LOCK_CONFLICT;
3319 code = CM_ERROR_LOCK_CONFLICT;
3322 } else if (IS_LOCK_LOST(fileLock)) {
3323 code = CM_ERROR_BADFD;
3329 lock_ReleaseRead(&cm_scacheLock);
3331 osi_Log4(afsd_logp, "cm_LockCheckWrite scp 0x%x offset %d length %d code 0x%x",
3332 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
3344 static void cm_LockMarkSCacheLost(cm_scache_t * scp);
3346 /* Called with cm_scacheLock write locked */
3347 static cm_file_lock_t * cm_GetFileLock(void) {
3350 l = (cm_file_lock_t *) cm_freeFileLocks;
3352 osi_QRemove(&cm_freeFileLocks, &l->q);
3354 l = malloc(sizeof(cm_file_lock_t));
3358 memset(l, 0, sizeof(cm_file_lock_t));
3363 /* Called with cm_scacheLock write locked */
3364 static void cm_PutFileLock(cm_file_lock_t *l) {
3365 osi_QAdd(&cm_freeFileLocks, &l->q);
3368 /* called with scp->mx held. May release it during processing, but
3369 leaves it held on exit. */
3370 long cm_IntSetLock(cm_scache_t * scp, cm_user_t * userp, int lockType,
3376 struct rx_connection * callp;
3379 tfid.Volume = scp->fid.volume;
3380 tfid.Vnode = scp->fid.vnode;
3381 tfid.Unique = scp->fid.unique;
3384 osi_Log2(afsd_logp, "CALL SetLock scp 0x%p for lock %d", scp, lockType);
3386 lock_ReleaseMutex(&scp->mx);
3389 code = cm_Conn(&cfid, userp, reqp, &connp);
3393 callp = cm_GetRxConn(connp);
3394 code = RXAFS_SetLock(callp, &tfid, lockType,
3396 rx_PutConnection(callp);
3398 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
3401 code = cm_MapRPCError(code, reqp);
3403 osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
3405 osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
3408 lock_ObtainMutex(&scp->mx);
3413 /* called with scp->mx held. Releases it during processing */
3414 long cm_IntReleaseLock(cm_scache_t * scp, cm_user_t * userp,
3420 struct rx_connection * callp;
3423 tfid.Volume = scp->fid.volume;
3424 tfid.Vnode = scp->fid.vnode;
3425 tfid.Unique = scp->fid.unique;
3428 lock_ReleaseMutex(&scp->mx);
3430 osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
3433 code = cm_Conn(&cfid, userp, reqp, &connp);
3437 callp = cm_GetRxConn(connp);
3438 code = RXAFS_ReleaseLock(callp, &tfid, &volSync);
3439 rx_PutConnection(callp);
3441 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
3443 code = cm_MapRPCError(code, reqp);
3446 "CALL ReleaseLock FAILURE, code 0x%x", code);
3449 "CALL ReleaseLock SUCCESS");
3451 lock_ObtainMutex(&scp->mx);
3456 /* called with scp->mx held. May release it during processing, but
3457 will exit with lock held.
3461 - 0 if the user has permission to get the specified lock for the scp
3463 - CM_ERROR_NOACCESS if not
3465 Any other error from cm_SyncOp will be sent down untranslated.
3467 long cm_LockCheckPerms(cm_scache_t * scp,
3475 /* lock permissions are slightly tricky because of the 'i' bit.
3476 If the user has PRSFS_LOCK, she can read-lock the file. If the
3477 user has PRSFS_WRITE, she can write-lock the file. However, if
3478 the user has PRSFS_INSERT, then she can write-lock new files,
3479 but not old ones. Since we don't have information about
3480 whether a file is new o