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;
1097 memset(&rock, 0, sizeof(rock));
1098 rock.fid.cell = dscp->fid.cell;
1099 rock.fid.volume = dscp->fid.volume;
1100 rock.searchNamep = namep;
1101 rock.caseFold = (flags & CM_FLAG_CASEFOLD);
1102 rock.hasTilde = ((strchr(namep, '~') != NULL) ? 1 : 0);
1104 /* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
1105 code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
1106 (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
1108 /* code == 0 means we fell off the end of the dir, while stopnow means
1109 * that we stopped early, probably because we found the entry we're
1110 * looking for. Any other non-zero code is an error.
1112 if (code && code != CM_ERROR_STOPNOW) {
1113 /* if the cm_scache_t we are searching in is not a directory
1114 * we must return path not found because the error
1115 * is to describe the final component not an intermediary
1117 if (code == CM_ERROR_NOTDIR) {
1118 if (flags & CM_FLAG_CHECKPATH)
1119 return CM_ERROR_NOSUCHPATH;
1121 return CM_ERROR_NOSUCHFILE;
1126 getroot = (dscp==cm_data.rootSCachep) ;
1128 if (!cm_freelanceEnabled || !getroot) {
1129 if (flags & CM_FLAG_CHECKPATH)
1130 return CM_ERROR_NOSUCHPATH;
1132 return CM_ERROR_NOSUCHFILE;
1134 else { /* nonexistent dir on freelance root, so add it */
1135 char fullname[200] = ".";
1138 osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %s",
1139 osi_LogSaveString(afsd_logp,namep));
1140 if (namep[0] == '.') {
1141 if (cm_GetCell_Gen(&namep[1], &fullname[1], CM_FLAG_CREATE)) {
1143 if ( stricmp(&namep[1], &fullname[1]) )
1144 code = cm_FreelanceAddSymlink(namep, fullname, &rock.fid);
1146 code = cm_FreelanceAddMount(namep, &fullname[1], "root.cell.", 1, &rock.fid);
1149 if (cm_GetCell_Gen(namep, fullname, CM_FLAG_CREATE)) {
1151 if ( stricmp(namep, fullname) )
1152 code = cm_FreelanceAddSymlink(namep, fullname, &rock.fid);
1154 code = cm_FreelanceAddMount(namep, fullname, "root.cell.", 0, &rock.fid);
1157 if (!found || code < 0) { /* add mount point failed, so give up */
1158 if (flags & CM_FLAG_CHECKPATH)
1159 return CM_ERROR_NOSUCHPATH;
1161 return CM_ERROR_NOSUCHFILE;
1163 tscp = NULL; /* to force call of cm_GetSCache */
1168 if ( !tscp ) /* we did not find it in the dnlc */
1171 code = cm_GetSCache(&rock.fid, &tscp, userp, reqp);
1175 /* tscp is now held */
1177 lock_ObtainMutex(&tscp->mx);
1178 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1179 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1181 lock_ReleaseMutex(&tscp->mx);
1182 cm_ReleaseSCache(tscp);
1185 /* tscp is now locked */
1187 if (!(flags & CM_FLAG_NOMOUNTCHASE)
1188 && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
1189 /* mount points are funny: they have a volume name to mount
1192 code = cm_ReadMountPoint(tscp, userp, reqp);
1194 code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
1196 lock_ReleaseMutex(&tscp->mx);
1197 cm_ReleaseSCache(tscp);
1204 lock_ReleaseMutex(&tscp->mx);
1207 /* copy back pointer */
1210 /* insert scache in dnlc */
1211 if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
1212 /* lock the directory entry to prevent racing callback revokes */
1213 lock_ObtainMutex(&dscp->mx);
1214 if ( dscp->cbServerp != NULL && dscp->cbExpires > 0 )
1215 cm_dnlcEnter(dscp, namep, tscp);
1216 lock_ReleaseMutex(&dscp->mx);
1223 int cm_ExpandSysName(char *inp, char *outp, long outSize, unsigned int index)
1228 tp = strrchr(inp, '@');
1230 return 0; /* no @sys */
1232 if (strcmp(tp, "@sys") != 0)
1233 return 0; /* no @sys */
1235 /* caller just wants to know if this is a valid @sys type of name */
1239 if (index >= MAXNUMSYSNAMES)
1242 /* otherwise generate the properly expanded @sys name */
1243 prefixCount = (int)(tp - inp);
1245 strncpy(outp, inp, prefixCount); /* copy out "a." from "a.@sys" */
1246 outp[prefixCount] = 0; /* null terminate the "a." */
1247 strcat(outp, cm_sysNameList[index]);/* append i386_nt40 */
1251 long cm_Lookup(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
1252 cm_req_t *reqp, cm_scache_t **outpScpp)
1256 int sysNameIndex = 0;
1257 cm_scache_t *scp = NULL;
1259 if ( stricmp(namep,SMB_IOCTL_FILENAME_NOSLASH) == 0 ) {
1260 if (flags & CM_FLAG_CHECKPATH)
1261 return CM_ERROR_NOSUCHPATH;
1263 return CM_ERROR_NOSUCHFILE;
1266 for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
1267 code = cm_ExpandSysName(namep, tname, sizeof(tname), sysNameIndex);
1269 code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
1275 cm_ReleaseSCache(scp);
1279 return cm_LookupInternal(dscp, namep, flags, userp, reqp, outpScpp);
1283 /* None of the possible sysName expansions could be found */
1284 if (flags & CM_FLAG_CHECKPATH)
1285 return CM_ERROR_NOSUCHPATH;
1287 return CM_ERROR_NOSUCHFILE;
1290 long cm_Unlink(cm_scache_t *dscp, char *namep, cm_user_t *userp, cm_req_t *reqp)
1296 AFSFetchStatus newDirStatus;
1298 struct rx_connection * callp;
1300 #ifdef AFS_FREELANCE_CLIENT
1301 if (cm_freelanceEnabled && dscp == cm_data.rootSCachep) {
1302 /* deleting a mount point from the root dir. */
1303 code = cm_FreelanceRemoveMount(namep);
1308 /* make sure we don't screw up the dir status during the merge */
1309 lock_ObtainMutex(&dscp->mx);
1310 sflags = CM_SCACHESYNC_STOREDATA;
1311 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1312 lock_ReleaseMutex(&dscp->mx);
1317 afsFid.Volume = dscp->fid.volume;
1318 afsFid.Vnode = dscp->fid.vnode;
1319 afsFid.Unique = dscp->fid.unique;
1321 osi_Log1(afsd_logp, "CALL RemoveFile scp 0x%p", dscp);
1323 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1327 callp = cm_GetRxConn(connp);
1328 code = RXAFS_RemoveFile(callp, &afsFid, namep,
1329 &newDirStatus, &volSync);
1330 rx_PutConnection(callp);
1332 } while (cm_Analyze(connp, userp, reqp, &dscp->fid, &volSync, NULL, NULL, code));
1333 code = cm_MapRPCError(code, reqp);
1336 osi_Log1(afsd_logp, "CALL RemoveFile FAILURE, code 0x%x", code);
1338 osi_Log0(afsd_logp, "CALL RemoveFile SUCCESS");
1340 lock_ObtainMutex(&dscp->mx);
1341 cm_dnlcRemove(dscp, namep);
1342 cm_SyncOpDone(dscp, NULL, sflags);
1344 cm_MergeStatus(dscp, &newDirStatus, &volSync, userp, 0);
1345 else if (code == CM_ERROR_NOSUCHFILE) {
1346 /* windows would not have allowed the request to delete the file
1347 * if it did not believe the file existed. therefore, we must
1348 * have an inconsistent view of the world.
1350 dscp->cbServerp = NULL;
1352 lock_ReleaseMutex(&dscp->mx);
1357 /* called with a locked vnode, and fills in the link info.
1358 * returns this the vnode still locked.
1360 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1367 lock_AssertMutex(&linkScp->mx);
1368 if (!linkScp->mountPointStringp[0]) {
1369 /* read the link data */
1370 lock_ReleaseMutex(&linkScp->mx);
1371 thyper.LowPart = thyper.HighPart = 0;
1372 code = buf_Get(linkScp, &thyper, &bufp);
1373 lock_ObtainMutex(&linkScp->mx);
1377 code = cm_SyncOp(linkScp, bufp, userp, reqp, 0,
1378 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1383 if (cm_HaveBuffer(linkScp, bufp, 0))
1386 code = cm_GetBuffer(linkScp, bufp, NULL, userp, reqp);
1391 } /* while loop to get the data */
1393 /* now if we still have no link read in,
1394 * copy the data from the buffer */
1395 if ((temp = linkScp->length.LowPart) >= MOUNTPOINTLEN) {
1397 return CM_ERROR_TOOBIG;
1400 /* otherwise, it fits; make sure it is still null (could have
1401 * lost race with someone else referencing this link above),
1402 * and if so, copy in the data.
1404 if (!linkScp->mountPointStringp[0]) {
1405 strncpy(linkScp->mountPointStringp, bufp->datap, temp);
1406 linkScp->mountPointStringp[temp] = 0; /* null terminate */
1409 } /* don't have sym link contents cached */
1414 /* called with a held vnode and a path suffix, with the held vnode being a
1415 * symbolic link. Our goal is to generate a new path to interpret, and return
1416 * this new path in newSpaceBufferp. If the new vnode is relative to a dir
1417 * other than the directory containing the symbolic link, then the new root is
1418 * returned in *newRootScpp, otherwise a null is returned there.
1420 long cm_AssembleLink(cm_scache_t *linkScp, char *pathSuffixp,
1421 cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1422 cm_user_t *userp, cm_req_t *reqp)
1429 lock_ObtainMutex(&linkScp->mx);
1430 code = cm_HandleLink(linkScp, userp, reqp);
1434 /* if we may overflow the buffer, bail out; buffer is signficantly
1435 * bigger than max path length, so we don't really have to worry about
1436 * being a little conservative here.
1438 if (strlen(linkScp->mountPointStringp) + strlen(pathSuffixp) + 2
1439 >= CM_UTILS_SPACESIZE)
1440 return CM_ERROR_TOOBIG;
1442 tsp = cm_GetSpace();
1443 linkp = linkScp->mountPointStringp;
1444 if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1445 if (strlen(linkp) > cm_mountRootLen)
1446 strcpy(tsp->data, linkp+cm_mountRootLen+1);
1449 *newRootScpp = cm_data.rootSCachep;
1450 cm_HoldSCache(cm_data.rootSCachep);
1451 } else if (linkp[0] == '\\' && linkp[1] == '\\') {
1452 if (!strnicmp(&linkp[2], cm_NetbiosName, (len = (long)strlen(cm_NetbiosName))))
1454 char * p = &linkp[len + 3];
1455 if (strnicmp(p, "all", 3) == 0)
1458 strcpy(tsp->data, p);
1459 for (p = tsp->data; *p; p++) {
1463 *newRootScpp = cm_data.rootSCachep;
1464 cm_HoldSCache(cm_data.rootSCachep);
1466 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1467 strcpy(tsp->data, linkp);
1468 *newRootScpp = NULL;
1469 code = CM_ERROR_PATH_NOT_COVERED;
1471 } else if ( !strnicmp(linkp, "msdfs:", (len = (long)strlen("msdfs:"))) ) {
1472 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1473 strcpy(tsp->data, linkp);
1474 *newRootScpp = NULL;
1475 code = CM_ERROR_PATH_NOT_COVERED;
1476 } else if (*linkp == '\\' || *linkp == '/') {
1478 /* formerly, this was considered to be from the AFS root,
1479 * but this seems to create problems. instead, we will just
1480 * reject the link */
1481 strcpy(tsp->data, linkp+1);
1482 *newRootScpp = cm_data.rootSCachep;
1483 cm_HoldSCache(cm_data.rootSCachep);
1485 /* we still copy the link data into the response so that
1486 * the user can see what the link points to
1488 linkScp->fileType = CM_SCACHETYPE_INVALID;
1489 strcpy(tsp->data, linkp);
1490 *newRootScpp = NULL;
1491 code = CM_ERROR_NOSUCHPATH;
1494 /* a relative link */
1495 strcpy(tsp->data, linkp);
1496 *newRootScpp = NULL;
1498 if (pathSuffixp[0] != 0) { /* if suffix string is non-null */
1499 strcat(tsp->data, "\\");
1500 strcat(tsp->data, pathSuffixp);
1502 *newSpaceBufferp = tsp;
1505 lock_ReleaseMutex(&linkScp->mx);
1509 long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
1510 cm_user_t *userp, char *tidPathp, cm_req_t *reqp, cm_scache_t **outScpp)
1513 char *tp; /* ptr moving through input buffer */
1514 char tc; /* temp char */
1515 int haveComponent; /* has new component started? */
1516 char component[256]; /* this is the new component */
1517 char *cp; /* component name being assembled */
1518 cm_scache_t *tscp; /* current location in the hierarchy */
1519 cm_scache_t *nscp; /* next dude down */
1520 cm_scache_t *dirScp; /* last dir we searched */
1521 cm_scache_t *linkScp; /* new root for the symlink we just
1523 cm_space_t *psp; /* space for current path, if we've hit
1525 cm_space_t *tempsp; /* temp vbl */
1526 char *restp; /* rest of the pathname to interpret */
1527 int symlinkCount; /* count of # of symlinks traversed */
1528 int extraFlag; /* avoid chasing mt pts for dir cmd */
1529 int phase = 1; /* 1 = tidPathp, 2 = pathp */
1542 cm_HoldSCache(tscp);
1549 /* map Unix slashes into DOS ones so we can interpret Unix
1555 if (!haveComponent) {
1558 } else if (tc == 0) {
1572 /* we have a component here */
1573 if (tc == 0 || tc == '\\') {
1574 /* end of the component; we're at the last
1575 * component if tc == 0. However, if the last
1576 * is a symlink, we have more to do.
1578 *cp++ = 0; /* add null termination */
1579 if (!strcmp(".",component)) {
1582 cm_ReleaseSCache(dirScp);
1588 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
1589 extraFlag = CM_FLAG_NOMOUNTCHASE;
1590 code = cm_Lookup(tscp, component,
1592 userp, reqp, &nscp);
1594 cm_ReleaseSCache(tscp);
1596 cm_ReleaseSCache(dirScp);
1599 if (code == CM_ERROR_NOSUCHFILE && tscp->fileType == CM_SCACHETYPE_SYMLINK)
1600 return CM_ERROR_NOSUCHPATH;
1604 haveComponent = 0; /* component done */
1606 cm_ReleaseSCache(dirScp);
1607 dirScp = tscp; /* for some symlinks */
1608 tscp = nscp; /* already held */
1610 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
1613 cm_ReleaseSCache(dirScp);
1619 /* now, if tscp is a symlink, we should follow
1620 * it and assemble the path again.
1622 lock_ObtainMutex(&tscp->mx);
1623 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1624 CM_SCACHESYNC_GETSTATUS
1625 | CM_SCACHESYNC_NEEDCALLBACK);
1627 lock_ReleaseMutex(&tscp->mx);
1628 cm_ReleaseSCache(tscp);
1631 cm_ReleaseSCache(dirScp);
1636 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
1637 /* this is a symlink; assemble a new buffer */
1638 lock_ReleaseMutex(&tscp->mx);
1639 if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
1640 cm_ReleaseSCache(tscp);
1643 cm_ReleaseSCache(dirScp);
1648 return CM_ERROR_TOO_MANY_SYMLINKS;
1654 code = cm_AssembleLink(tscp, restp, &linkScp, &tempsp, userp, reqp);
1656 /* something went wrong */
1657 cm_ReleaseSCache(tscp);
1660 cm_ReleaseSCache(dirScp);
1666 /* otherwise, tempsp has the new path,
1667 * and linkScp is the new root from
1668 * which to interpret that path.
1669 * Continue with the namei processing,
1670 * also doing the bookkeeping for the
1671 * space allocation and tracking the
1672 * vnode reference counts.
1678 cm_ReleaseSCache(tscp);
1683 * now, if linkScp is null, that's
1684 * AssembleLink's way of telling us that
1685 * the sym link is relative to the dir
1686 * containing the link. We have a ref
1687 * to it in dirScp, and we hold it now
1688 * and reuse it as the new spot in the
1696 /* not a symlink, we may be done */
1697 lock_ReleaseMutex(&tscp->mx);
1705 cm_ReleaseSCache(dirScp);
1713 cm_ReleaseSCache(dirScp);
1716 } /* end of a component */
1719 } /* we have a component */
1720 } /* big while loop over all components */
1724 cm_ReleaseSCache(dirScp);
1730 cm_ReleaseSCache(tscp);
1734 /* called with a dir, and a vnode within the dir that happens to be a symlink.
1735 * We chase the link, and return a held pointer to the target, if it exists,
1736 * in *outScpp. If we succeed, we return 0, otherwise we return an error code
1737 * and do not hold or return a target vnode.
1739 * This is very similar to calling cm_NameI with the last component of a name,
1740 * which happens to be a symlink, except that we've already passed by the name.
1742 * This function is typically called by the directory listing functions, which
1743 * encounter symlinks but need to return the proper file length so programs
1744 * like "more" work properly when they make use of the attributes retrieved from
1747 * The input vnode should not be locked when this function is called.
1749 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
1750 cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
1754 cm_scache_t *newRootScp;
1756 osi_Log1(afsd_logp, "Evaluating symlink scp 0x%p", linkScp);
1758 code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
1762 /* now, if newRootScp is NULL, we're really being told that the symlink
1763 * is relative to the current directory (dscp).
1765 if (newRootScp == NULL) {
1767 cm_HoldSCache(dscp);
1770 code = cm_NameI(newRootScp, spacep->data,
1771 CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
1772 userp, NULL, reqp, outScpp);
1774 if (code == CM_ERROR_NOSUCHFILE)
1775 code = CM_ERROR_NOSUCHPATH;
1777 /* this stuff is allocated no matter what happened on the namei call,
1779 cm_FreeSpace(spacep);
1780 cm_ReleaseSCache(newRootScp);
1785 /* make this big enough so that one buffer of dir pages won't overflow. We'll
1786 * check anyway, but we want to minimize the chance that we have to leave stuff
1789 #define CM_BULKMAX (3 * AFSCBMAX)
1791 /* rock for bulk stat calls */
1792 typedef struct cm_bulkStat {
1793 osi_hyper_t bufOffset; /* only do it for things in this buffer page */
1795 /* info for the actual call */
1796 int counter; /* next free slot */
1797 AFSFid fids[CM_BULKMAX];
1798 AFSFetchStatus stats[CM_BULKMAX];
1799 AFSCallBack callbacks[CM_BULKMAX];
1802 /* for a given entry, make sure that it isn't in the stat cache, and then
1803 * add it to the list of file IDs to be obtained.
1805 * Don't bother adding it if we already have a vnode. Note that the dir
1806 * is locked, so we have to be careful checking the vnode we're thinking of
1807 * processing, to avoid deadlocks.
1809 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
1820 /* Don't overflow bsp. */
1821 if (bsp->counter >= CM_BULKMAX)
1822 return CM_ERROR_STOPNOW;
1824 thyper.LowPart = cm_data.buf_blockSize;
1825 thyper.HighPart = 0;
1826 thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
1828 /* thyper is now the first byte past the end of the record we're
1829 * interested in, and bsp->bufOffset is the first byte of the record
1830 * we're interested in.
1831 * Skip data in the others.
1834 if (LargeIntegerLessThan(*offp, bsp->bufOffset))
1836 if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
1837 return CM_ERROR_STOPNOW;
1838 if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
1841 tfid.cell = scp->fid.cell;
1842 tfid.volume = scp->fid.volume;
1843 tfid.vnode = ntohl(dep->fid.vnode);
1844 tfid.unique = ntohl(dep->fid.unique);
1845 tscp = cm_FindSCache(&tfid);
1847 if (lock_TryMutex(&tscp->mx)) {
1848 /* we have an entry that we can look at */
1849 if (!(tscp->flags & CM_SCACHEFLAG_EACCESS) && cm_HaveCallback(tscp)) {
1850 /* we have a callback on it. Don't bother
1851 * fetching this stat entry, since we're happy
1852 * with the info we have.
1854 lock_ReleaseMutex(&tscp->mx);
1855 cm_ReleaseSCache(tscp);
1858 lock_ReleaseMutex(&tscp->mx);
1860 cm_ReleaseSCache(tscp);
1863 #ifdef AFS_FREELANCE_CLIENT
1864 // yj: if this is a mountpoint under root.afs then we don't want it
1865 // to be bulkstat-ed, instead, we call getSCache directly and under
1866 // getSCache, it is handled specially.
1867 if ( cm_freelanceEnabled &&
1868 tfid.cell==AFS_FAKE_ROOT_CELL_ID &&
1869 tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
1870 !(tfid.vnode==0x1 && tfid.unique==0x1) )
1872 osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
1873 return cm_GetSCache(&tfid, &tscp, NULL, NULL);
1875 #endif /* AFS_FREELANCE_CLIENT */
1878 bsp->fids[i].Volume = scp->fid.volume;
1879 bsp->fids[i].Vnode = tfid.vnode;
1880 bsp->fids[i].Unique = tfid.unique;
1884 /* called with a locked scp and a pointer to a buffer. Make bulk stat
1885 * calls on all undeleted files in the page of the directory specified.
1888 cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
1892 cm_bulkStat_t bb; /* this is *BIG*, probably 16K or so;
1893 * watch for stack problems */
1894 AFSCBFids fidStruct;
1895 AFSBulkStats statStruct;
1897 AFSCBs callbackStruct;
1900 cm_callbackRequest_t cbReq;
1906 struct rx_connection * callp;
1907 int inlinebulk = 0; /* Did we use InlineBulkStatus RPC or not? */
1909 osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%p", dscp);
1911 /* should be on a buffer boundary */
1912 osi_assert((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0);
1914 memset(&bb, 0, sizeof(bb));
1915 bb.bufOffset = *offsetp;
1917 lock_ReleaseMutex(&dscp->mx);
1918 /* first, assemble the file IDs we need to stat */
1919 code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) &bb, offsetp, userp, reqp, NULL);
1921 /* if we failed, bail out early */
1922 if (code && code != CM_ERROR_STOPNOW) {
1923 lock_ObtainMutex(&dscp->mx);
1927 /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
1928 * make the calls to create the entries. Handle AFSCBMAX files at a
1932 while (filex < bb.counter) {
1933 filesThisCall = bb.counter - filex;
1934 if (filesThisCall > AFSCBMAX)
1935 filesThisCall = AFSCBMAX;
1937 fidStruct.AFSCBFids_len = filesThisCall;
1938 fidStruct.AFSCBFids_val = &bb.fids[filex];
1939 statStruct.AFSBulkStats_len = filesThisCall;
1940 statStruct.AFSBulkStats_val = &bb.stats[filex];
1941 callbackStruct.AFSCBs_len = filesThisCall;
1942 callbackStruct.AFSCBs_val = &bb.callbacks[filex];
1943 cm_StartCallbackGrantingCall(NULL, &cbReq);
1944 osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
1946 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1950 callp = cm_GetRxConn(connp);
1951 if (!(connp->serverp->flags & CM_SERVERFLAG_NOINLINEBULK)) {
1952 code = RXAFS_InlineBulkStatus(callp, &fidStruct,
1953 &statStruct, &callbackStruct, &volSync);
1954 if (code == RXGEN_OPCODE) {
1955 cm_SetServerNoInlineBulk(connp->serverp, 0);
1961 code = RXAFS_BulkStatus(callp, &fidStruct,
1962 &statStruct, &callbackStruct, &volSync);
1964 rx_PutConnection(callp);
1966 } while (cm_Analyze(connp, userp, reqp, &dscp->fid,
1967 &volSync, NULL, &cbReq, code));
1968 code = cm_MapRPCError(code, reqp);
1970 osi_Log2(afsd_logp, "CALL %sBulkStatus FAILURE code 0x%x",
1971 inlinebulk ? "Inline" : "", code);
1973 osi_Log1(afsd_logp, "CALL %sBulkStatus SUCCESS", inlinebulk ? "Inline" : "");
1975 /* may as well quit on an error, since we're not going to do
1976 * much better on the next immediate call, either.
1979 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
1983 /* otherwise, we should do the merges */
1984 for (i = 0; i<filesThisCall; i++) {
1986 tfid.cell = dscp->fid.cell;
1987 tfid.volume = bb.fids[j].Volume;
1988 tfid.vnode = bb.fids[j].Vnode;
1989 tfid.unique = bb.fids[j].Unique;
1990 code = cm_GetSCache(&tfid, &scp, userp, reqp);
1994 /* otherwise, if this entry has no callback info,
1997 lock_ObtainMutex(&scp->mx);
1998 /* now, we have to be extra paranoid on merging in this
1999 * information, since we didn't use cm_SyncOp before
2000 * starting the fetch to make sure that no bad races
2001 * were occurring. Specifically, we need to make sure
2002 * we don't obliterate any newer information in the
2003 * vnode than have here.
2005 * Right now, be pretty conservative: if there's a
2006 * callback or a pending call, skip it.
2008 if ((scp->cbServerp == NULL || (scp->flags & CM_SCACHEFLAG_EACCESS))
2010 (CM_SCACHEFLAG_FETCHING
2011 | CM_SCACHEFLAG_STORING
2012 | CM_SCACHEFLAG_SIZESTORING))) {
2013 cm_EndCallbackGrantingCall(scp, &cbReq,
2015 CM_CALLBACK_MAINTAINCOUNT);
2016 cm_MergeStatus(scp, &bb.stats[j], &volSync, userp, 0);
2018 lock_ReleaseMutex(&scp->mx);
2019 cm_ReleaseSCache(scp);
2020 } /* all files in the response */
2021 /* now tell it to drop the count,
2022 * after doing the vnode processing above */
2023 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2025 filex += filesThisCall;
2026 } /* while there are still more files to process */
2027 lock_ObtainMutex(&dscp->mx);
2030 /* If we did the InlineBulk RPC pull out the return code */
2032 if ((&bb.stats[0])->errorCode) {
2033 cm_Analyze(NULL /*connp was released by the previous cm_Analyze */,
2034 userp, reqp, &dscp->fid, &volSync, NULL, NULL, (&bb.stats[0])->errorCode);
2035 code = cm_MapRPCError((&bb.stats[0])->errorCode, reqp);
2043 osi_Log1(afsd_logp, "END cm_TryBulkStat code = 0x%x", code);
2047 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
2051 /* initialize store back mask as inexpensive local variable */
2053 memset(statusp, 0, sizeof(AFSStoreStatus));
2055 /* copy out queued info from scache first, if scp passed in */
2057 if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
2058 statusp->ClientModTime = scp->clientModTime;
2059 mask |= AFS_SETMODTIME;
2060 scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
2065 /* now add in our locally generated request */
2066 if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
2067 statusp->ClientModTime = attrp->clientModTime;
2068 mask |= AFS_SETMODTIME;
2070 if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
2071 statusp->UnixModeBits = attrp->unixModeBits;
2072 mask |= AFS_SETMODE;
2074 if (attrp->mask & CM_ATTRMASK_OWNER) {
2075 statusp->Owner = attrp->owner;
2076 mask |= AFS_SETOWNER;
2078 if (attrp->mask & CM_ATTRMASK_GROUP) {
2079 statusp->Group = attrp->group;
2080 mask |= AFS_SETGROUP;
2083 statusp->Mask = mask;
2086 /* set the file size, and make sure that all relevant buffers have been
2087 * truncated. Ensure that any partially truncated buffers have been zeroed
2088 * to the end of the buffer.
2090 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
2096 /* start by locking out buffer creation */
2097 lock_ObtainWrite(&scp->bufCreateLock);
2099 /* verify that this is a file, not a dir or a symlink */
2100 lock_ObtainMutex(&scp->mx);
2101 code = cm_SyncOp(scp, NULL, userp, reqp, 0,
2102 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2106 if (scp->fileType != CM_SCACHETYPE_FILE) {
2107 code = CM_ERROR_ISDIR;
2112 if (LargeIntegerLessThan(*sizep, scp->length))
2117 lock_ReleaseMutex(&scp->mx);
2119 /* can't hold scp->mx lock here, since we may wait for a storeback to
2120 * finish if the buffer package is cleaning a buffer by storing it to
2124 buf_Truncate(scp, userp, reqp, sizep);
2126 /* now ensure that file length is short enough, and update truncPos */
2127 lock_ObtainMutex(&scp->mx);
2129 /* make sure we have a callback (so we have the right value for the
2130 * length), and wait for it to be safe to do a truncate.
2132 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
2133 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2134 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2138 if (LargeIntegerLessThan(*sizep, scp->length)) {
2139 /* a real truncation. If truncPos is not set yet, or is bigger
2140 * than where we're truncating the file, set truncPos to this
2145 if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
2146 || LargeIntegerLessThan(*sizep, scp->length)) {
2148 scp->truncPos = *sizep;
2149 scp->mask |= CM_SCACHEMASK_TRUNCPOS;
2151 /* in either case, the new file size has been changed */
2152 scp->length = *sizep;
2153 scp->mask |= CM_SCACHEMASK_LENGTH;
2155 else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
2156 /* really extending the file */
2157 scp->length = *sizep;
2158 scp->mask |= CM_SCACHEMASK_LENGTH;
2161 /* done successfully */
2165 lock_ReleaseMutex(&scp->mx);
2166 lock_ReleaseWrite(&scp->bufCreateLock);
2171 /* set the file size or other attributes (but not both at once) */
2172 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
2177 AFSFetchStatus afsOutStatus;
2181 AFSStoreStatus afsInStatus;
2182 struct rx_connection * callp;
2184 /* handle file length setting */
2185 if (attrp->mask & CM_ATTRMASK_LENGTH)
2186 return cm_SetLength(scp, &attrp->length, userp, reqp);
2188 flags = CM_SCACHESYNC_STORESTATUS;
2190 lock_ObtainMutex(&scp->mx);
2191 /* otherwise, we have to make an RPC to get the status */
2192 code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
2194 /* make the attr structure */
2195 cm_StatusFromAttr(&afsInStatus, scp, attrp);
2197 tfid.Volume = scp->fid.volume;
2198 tfid.Vnode = scp->fid.vnode;
2199 tfid.Unique = scp->fid.unique;
2201 lock_ReleaseMutex(&scp->mx);
2205 /* now make the RPC */
2206 osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%p", scp);
2208 code = cm_Conn(&scp->fid, userp, reqp, &connp);
2212 callp = cm_GetRxConn(connp);
2213 code = RXAFS_StoreStatus(callp, &tfid,
2214 &afsInStatus, &afsOutStatus, &volSync);
2215 rx_PutConnection(callp);
2217 } while (cm_Analyze(connp, userp, reqp,
2218 &scp->fid, &volSync, NULL, NULL, code));
2219 code = cm_MapRPCError(code, reqp);
2222 osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
2224 osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
2226 lock_ObtainMutex(&scp->mx);
2227 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
2229 cm_MergeStatus(scp, &afsOutStatus, &volSync, userp,
2230 CM_MERGEFLAG_FORCE);
2232 /* if we're changing the mode bits, discard the ACL cache,
2233 * since we changed the mode bits.
2235 if (afsInStatus.Mask & AFS_SETMODE) cm_FreeAllACLEnts(scp);
2236 lock_ReleaseMutex(&scp->mx);
2240 long cm_Create(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2241 cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
2246 cm_callbackRequest_t cbReq;
2251 AFSStoreStatus inStatus;
2252 AFSFetchStatus updatedDirStatus;
2253 AFSFetchStatus newFileStatus;
2254 AFSCallBack newFileCallback;
2256 struct rx_connection * callp;
2258 /* can't create names with @sys in them; must expand it manually first.
2259 * return "invalid request" if they try.
2261 if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2262 return CM_ERROR_ATSYS;
2265 /* before starting the RPC, mark that we're changing the file data, so
2266 * that someone who does a chmod will know to wait until our call
2269 lock_ObtainMutex(&dscp->mx);
2270 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2272 cm_StartCallbackGrantingCall(NULL, &cbReq);
2274 lock_ReleaseMutex(&dscp->mx);
2280 cm_StatusFromAttr(&inStatus, NULL, attrp);
2282 /* try the RPC now */
2283 osi_Log1(afsd_logp, "CALL CreateFile scp 0x%p", dscp);
2285 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2289 dirAFSFid.Volume = dscp->fid.volume;
2290 dirAFSFid.Vnode = dscp->fid.vnode;
2291 dirAFSFid.Unique = dscp->fid.unique;
2293 callp = cm_GetRxConn(connp);
2294 code = RXAFS_CreateFile(connp->callp, &dirAFSFid, namep,
2295 &inStatus, &newAFSFid, &newFileStatus,
2296 &updatedDirStatus, &newFileCallback,
2298 rx_PutConnection(callp);
2300 } while (cm_Analyze(connp, userp, reqp,
2301 &dscp->fid, &volSync, NULL, &cbReq, code));
2302 code = cm_MapRPCError(code, reqp);
2305 osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
2307 osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
2309 lock_ObtainMutex(&dscp->mx);
2310 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2312 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2314 lock_ReleaseMutex(&dscp->mx);
2316 /* now try to create the file's entry, too, but be careful to
2317 * make sure that we don't merge in old info. Since we weren't locking
2318 * out any requests during the file's creation, we may have pretty old
2322 newFid.cell = dscp->fid.cell;
2323 newFid.volume = dscp->fid.volume;
2324 newFid.vnode = newAFSFid.Vnode;
2325 newFid.unique = newAFSFid.Unique;
2326 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2328 lock_ObtainMutex(&scp->mx);
2329 scp->creator = userp; /* remember who created it */
2330 if (!cm_HaveCallback(scp)) {
2331 cm_MergeStatus(scp, &newFileStatus, &volSync,
2333 cm_EndCallbackGrantingCall(scp, &cbReq,
2334 &newFileCallback, 0);
2337 lock_ReleaseMutex(&scp->mx);
2342 /* make sure we end things properly */
2344 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2349 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
2353 lock_ObtainWrite(&scp->bufCreateLock);
2354 code = buf_CleanVnode(scp, userp, reqp);
2355 lock_ReleaseWrite(&scp->bufCreateLock);
2357 lock_ObtainMutex(&scp->mx);
2359 if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
2360 | CM_SCACHEMASK_CLIENTMODTIME
2361 | CM_SCACHEMASK_LENGTH))
2362 code = cm_StoreMini(scp, userp, reqp);
2364 if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) {
2365 code = (scp->flags & CM_SCACHEFLAG_OVERQUOTA) ? CM_ERROR_QUOTA : CM_ERROR_SPACE;
2366 scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE);
2369 lock_ReleaseMutex(&scp->mx);
2374 long cm_MakeDir(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2375 cm_user_t *userp, cm_req_t *reqp)
2380 cm_callbackRequest_t cbReq;
2385 AFSStoreStatus inStatus;
2386 AFSFetchStatus updatedDirStatus;
2387 AFSFetchStatus newDirStatus;
2388 AFSCallBack newDirCallback;
2390 struct rx_connection * callp;
2392 /* can't create names with @sys in them; must expand it manually first.
2393 * return "invalid request" if they try.
2395 if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2396 return CM_ERROR_ATSYS;
2399 /* before starting the RPC, mark that we're changing the directory
2400 * data, so that someone who does a chmod on the dir will wait until
2401 * our call completes.
2403 lock_ObtainMutex(&dscp->mx);
2404 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2406 cm_StartCallbackGrantingCall(NULL, &cbReq);
2408 lock_ReleaseMutex(&dscp->mx);
2414 cm_StatusFromAttr(&inStatus, NULL, attrp);
2416 /* try the RPC now */
2417 osi_Log1(afsd_logp, "CALL MakeDir scp 0x%p", dscp);
2419 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2423 dirAFSFid.Volume = dscp->fid.volume;
2424 dirAFSFid.Vnode = dscp->fid.vnode;
2425 dirAFSFid.Unique = dscp->fid.unique;
2427 callp = cm_GetRxConn(connp);
2428 code = RXAFS_MakeDir(connp->callp, &dirAFSFid, namep,
2429 &inStatus, &newAFSFid, &newDirStatus,
2430 &updatedDirStatus, &newDirCallback,
2432 rx_PutConnection(callp);
2434 } while (cm_Analyze(connp, userp, reqp,
2435 &dscp->fid, &volSync, NULL, &cbReq, code));
2436 code = cm_MapRPCError(code, reqp);
2439 osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
2441 osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
2443 lock_ObtainMutex(&dscp->mx);
2444 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2446 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2448 lock_ReleaseMutex(&dscp->mx);
2450 /* now try to create the new dir's entry, too, but be careful to
2451 * make sure that we don't merge in old info. Since we weren't locking
2452 * out any requests during the file's creation, we may have pretty old
2456 newFid.cell = dscp->fid.cell;
2457 newFid.volume = dscp->fid.volume;
2458 newFid.vnode = newAFSFid.Vnode;
2459 newFid.unique = newAFSFid.Unique;
2460 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2462 lock_ObtainMutex(&scp->mx);
2463 if (!cm_HaveCallback(scp)) {
2464 cm_MergeStatus(scp, &newDirStatus, &volSync,
2466 cm_EndCallbackGrantingCall(scp, &cbReq,
2467 &newDirCallback, 0);
2470 lock_ReleaseMutex(&scp->mx);
2471 cm_ReleaseSCache(scp);
2475 /* make sure we end things properly */
2477 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2479 /* and return error code */
2483 long cm_Link(cm_scache_t *dscp, char *namep, cm_scache_t *sscp, long flags,
2484 cm_user_t *userp, cm_req_t *reqp)
2489 AFSFid existingAFSFid;
2490 AFSFetchStatus updatedDirStatus;
2491 AFSFetchStatus newLinkStatus;
2493 struct rx_connection * callp;
2495 if (dscp->fid.cell != sscp->fid.cell ||
2496 dscp->fid.volume != sscp->fid.volume) {
2497 return CM_ERROR_CROSSDEVLINK;
2500 lock_ObtainMutex(&dscp->mx);
2501 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2502 lock_ReleaseMutex(&dscp->mx);
2507 /* try the RPC now */
2508 osi_Log1(afsd_logp, "CALL Link scp 0x%p", dscp);
2510 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2513 dirAFSFid.Volume = dscp->fid.volume;
2514 dirAFSFid.Vnode = dscp->fid.vnode;
2515 dirAFSFid.Unique = dscp->fid.unique;
2517 existingAFSFid.Volume = sscp->fid.volume;
2518 existingAFSFid.Vnode = sscp->fid.vnode;
2519 existingAFSFid.Unique = sscp->fid.unique;
2521 callp = cm_GetRxConn(connp);
2522 code = RXAFS_Link(callp, &dirAFSFid, namep, &existingAFSFid,
2523 &newLinkStatus, &updatedDirStatus, &volSync);
2524 rx_PutConnection(callp);
2525 osi_Log1(smb_logp," RXAFS_Link returns 0x%x", code);
2527 } while (cm_Analyze(connp, userp, reqp,
2528 &dscp->fid, &volSync, NULL, NULL, code));
2530 code = cm_MapRPCError(code, reqp);
2533 osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
2535 osi_Log0(afsd_logp, "CALL Link SUCCESS");
2537 lock_ObtainMutex(&dscp->mx);
2538 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2540 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2542 lock_ReleaseMutex(&dscp->mx);
2547 long cm_SymLink(cm_scache_t *dscp, char *namep, char *contentsp, long flags,
2548 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
2556 AFSStoreStatus inStatus;
2557 AFSFetchStatus updatedDirStatus;
2558 AFSFetchStatus newLinkStatus;
2560 struct rx_connection * callp;
2562 /* before starting the RPC, mark that we're changing the directory data,
2563 * so that someone who does a chmod on the dir will wait until our
2566 lock_ObtainMutex(&dscp->mx);
2567 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2568 lock_ReleaseMutex(&dscp->mx);
2573 cm_StatusFromAttr(&inStatus, NULL, attrp);
2575 /* try the RPC now */
2576 osi_Log1(afsd_logp, "CALL Symlink scp 0x%p", dscp);
2578 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2582 dirAFSFid.Volume = dscp->fid.volume;
2583 dirAFSFid.Vnode = dscp->fid.vnode;
2584 dirAFSFid.Unique = dscp->fid.unique;
2586 callp = cm_GetRxConn(connp);
2587 code = RXAFS_Symlink(callp, &dirAFSFid, namep, contentsp,
2588 &inStatus, &newAFSFid, &newLinkStatus,
2589 &updatedDirStatus, &volSync);
2590 rx_PutConnection(callp);
2592 } while (cm_Analyze(connp, userp, reqp,
2593 &dscp->fid, &volSync, NULL, NULL, code));
2594 code = cm_MapRPCError(code, reqp);
2597 osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
2599 osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
2601 lock_ObtainMutex(&dscp->mx);
2602 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2604 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2606 lock_ReleaseMutex(&dscp->mx);
2608 /* now try to create the new dir's entry, too, but be careful to
2609 * make sure that we don't merge in old info. Since we weren't locking
2610 * out any requests during the file's creation, we may have pretty old
2614 newFid.cell = dscp->fid.cell;
2615 newFid.volume = dscp->fid.volume;
2616 newFid.vnode = newAFSFid.Vnode;
2617 newFid.unique = newAFSFid.Unique;
2618 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2620 lock_ObtainMutex(&scp->mx);
2621 if (!cm_HaveCallback(scp)) {
2622 cm_MergeStatus(scp, &newLinkStatus, &volSync,
2625 lock_ReleaseMutex(&scp->mx);
2626 cm_ReleaseSCache(scp);
2630 /* and return error code */
2634 long cm_RemoveDir(cm_scache_t *dscp, char *namep, cm_user_t *userp,
2641 AFSFetchStatus updatedDirStatus;
2643 struct rx_connection * callp;
2645 /* before starting the RPC, mark that we're changing the directory data,
2646 * so that someone who does a chmod on the dir will wait until our
2649 lock_ObtainMutex(&dscp->mx);
2650 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2651 lock_ReleaseMutex(&dscp->mx);
2657 /* try the RPC now */
2658 osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%p", dscp);
2660 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2664 dirAFSFid.Volume = dscp->fid.volume;
2665 dirAFSFid.Vnode = dscp->fid.vnode;
2666 dirAFSFid.Unique = dscp->fid.unique;
2668 callp = cm_GetRxConn(connp);
2669 code = RXAFS_RemoveDir(callp, &dirAFSFid, namep,
2670 &updatedDirStatus, &volSync);
2671 rx_PutConnection(callp);
2673 } while (cm_Analyze(connp, userp, reqp,
2674 &dscp->fid, &volSync, NULL, NULL, code));
2675 code = cm_MapRPCErrorRmdir(code, reqp);
2678 osi_Log1(afsd_logp, "CALL RemoveDir FAILURE, code 0x%x", code);
2680 osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
2682 lock_ObtainMutex(&dscp->mx);
2683 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2685 cm_dnlcRemove(dscp, namep);
2686 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2688 lock_ReleaseMutex(&dscp->mx);
2690 /* and return error code */
2694 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
2696 /* grab mutex on contents */
2697 lock_ObtainMutex(&scp->mx);
2699 /* reset the prefetch info */
2700 scp->prefetch.base.LowPart = 0; /* base */
2701 scp->prefetch.base.HighPart = 0;
2702 scp->prefetch.end.LowPart = 0; /* and end */
2703 scp->prefetch.end.HighPart = 0;
2705 /* release mutex on contents */
2706 lock_ReleaseMutex(&scp->mx);
2712 long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
2713 char *newNamep, cm_user_t *userp, cm_req_t *reqp)
2717 AFSFid oldDirAFSFid;
2718 AFSFid newDirAFSFid;
2720 AFSFetchStatus updatedOldDirStatus;
2721 AFSFetchStatus updatedNewDirStatus;
2724 struct rx_connection * callp;
2726 /* before starting the RPC, mark that we're changing the directory data,
2727 * so that someone who does a chmod on the dir will wait until our call
2728 * completes. We do this in vnode order so that we don't deadlock,
2729 * which makes the code a little verbose.
2731 if (oldDscp == newDscp) {
2732 /* check for identical names */
2733 if (strcmp(oldNamep, newNamep) == 0)
2734 return CM_ERROR_RENAME_IDENTICAL;
2737 lock_ObtainMutex(&oldDscp->mx);
2738 cm_dnlcRemove(oldDscp, oldNamep);
2739 cm_dnlcRemove(oldDscp, newNamep);
2740 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2741 CM_SCACHESYNC_STOREDATA);
2742 lock_ReleaseMutex(&oldDscp->mx);
2745 /* two distinct dir vnodes */
2747 if (oldDscp->fid.cell != newDscp->fid.cell ||
2748 oldDscp->fid.volume != newDscp->fid.volume)
2749 return CM_ERROR_CROSSDEVLINK;
2751 /* shouldn't happen that we have distinct vnodes for two
2752 * different files, but could due to deliberate attack, or
2753 * stale info. Avoid deadlocks and quit now.
2755 if (oldDscp->fid.vnode == newDscp->fid.vnode)
2756 return CM_ERROR_CROSSDEVLINK;
2758 if (oldDscp->fid.vnode < newDscp->fid.vnode) {
2759 lock_ObtainMutex(&oldDscp->mx);
2760 cm_dnlcRemove(oldDscp, oldNamep);
2761 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2762 CM_SCACHESYNC_STOREDATA);
2763 lock_ReleaseMutex(&oldDscp->mx);
2765 lock_ObtainMutex(&newDscp->mx);
2766 cm_dnlcRemove(newDscp, newNamep);
2767 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2768 CM_SCACHESYNC_STOREDATA);
2769 lock_ReleaseMutex(&newDscp->mx);
2771 /* cleanup first one */
2772 lock_ObtainMutex(&newDscp->mx);
2773 cm_SyncOpDone(oldDscp, NULL,
2774 CM_SCACHESYNC_STOREDATA);
2775 lock_ReleaseMutex(&oldDscp->mx);
2780 /* lock the new vnode entry first */
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 lock_ObtainMutex(&oldDscp->mx);
2788 cm_dnlcRemove(oldDscp, oldNamep);
2789 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2790 CM_SCACHESYNC_STOREDATA);
2791 lock_ReleaseMutex(&oldDscp->mx);
2793 /* cleanup first one */
2794 lock_ObtainMutex(&newDscp->mx);
2795 cm_SyncOpDone(newDscp, NULL,
2796 CM_SCACHESYNC_STOREDATA);
2797 lock_ReleaseMutex(&newDscp->mx);
2801 } /* two distinct vnodes */
2808 /* try the RPC now */
2809 osi_Log2(afsd_logp, "CALL Rename old scp 0x%p new scp 0x%p",
2812 code = cm_Conn(&oldDscp->fid, userp, reqp, &connp);
2816 oldDirAFSFid.Volume = oldDscp->fid.volume;
2817 oldDirAFSFid.Vnode = oldDscp->fid.vnode;
2818 oldDirAFSFid.Unique = oldDscp->fid.unique;
2819 newDirAFSFid.Volume = newDscp->fid.volume;
2820 newDirAFSFid.Vnode = newDscp->fid.vnode;
2821 newDirAFSFid.Unique = newDscp->fid.unique;
2823 callp = cm_GetRxConn(connp);
2824 code = RXAFS_Rename(callp, &oldDirAFSFid, oldNamep,
2825 &newDirAFSFid, newNamep,
2826 &updatedOldDirStatus, &updatedNewDirStatus,
2828 rx_PutConnection(callp);
2830 } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
2831 &volSync, NULL, NULL, code));
2832 code = cm_MapRPCError(code, reqp);
2835 osi_Log1(afsd_logp, "CALL Rename FAILURE, code 0x%x", code);
2837 osi_Log0(afsd_logp, "CALL Rename SUCCESS");
2839 /* update the individual stat cache entries for the directories */
2840 lock_ObtainMutex(&oldDscp->mx);
2841 cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
2843 cm_MergeStatus(oldDscp, &updatedOldDirStatus, &volSync,
2846 lock_ReleaseMutex(&oldDscp->mx);
2848 /* and update it for the new one, too, if necessary */
2850 lock_ObtainMutex(&newDscp->mx);
2851 cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
2853 cm_MergeStatus(newDscp, &updatedNewDirStatus, &volSync,
2856 lock_ReleaseMutex(&newDscp->mx);
2859 /* and return error code */
2863 /* Byte range locks:
2865 The OpenAFS Windows client has to fake byte range locks given no
2866 server side support for such locks. This is implemented as keyed
2867 byte range locks on the cache manager.
2869 Keyed byte range locks:
2871 Each cm_scache_t structure keeps track of a list of keyed locks.
2872 The key for a lock identifies an owner of a set of locks (referred
2873 to as a client). Each key is represented by a value. The set of
2874 key values used within a specific cm_scache_t structure form a
2875 namespace that has a scope of just that cm_scache_t structure. The
2876 same key value can be used with another cm_scache_t structure and
2877 correspond to a completely different client. However it is
2878 advantageous for the SMB or IFS layer to make sure that there is a
2879 1-1 mapping between client and keys over all cm_scache_t objects.
2881 Assume a client C has key Key(C) (although, since the scope of the
2882 key is a cm_scache_t, the key can be Key(C,S), where S is the
2883 cm_scache_t. But assume a 1-1 relation between keys and clients).
2884 A byte range (O,+L) denotes byte addresses (O) through (O+L-1)
2885 inclusive (a.k.a. [O,O+L-1]). The function Key(x) is implemented
2886 through cm_generateKey() function for both SMB and IFS.
2888 The list of locks for a cm_scache_t object S is maintained in
2889 S->fileLocks. The cache manager will set a lock on the AFS file
2890 server in order to assert the locks in S->fileLocks. If only
2891 shared locks are in place for S, then the cache manager will obtain
2892 a LockRead lock, while if there are any exclusive locks, it will
2893 obtain a LockWrite lock. If the exclusive locks are all released
2894 while the shared locks remain, then the cache manager will
2895 downgrade the lock from LockWrite to LockRead. Similarly, if an
2896 exclusive lock is obtained when only shared locks exist, then the
2897 cache manager will try to upgrade the lock from LockRead to
2900 Each lock L owned by client C maintains a key L->key such that
2901 L->key == Key(C), the effective range defined by L->LOffset and
2902 L->LLength such that the range of bytes affected by the lock is
2903 (L->LOffset, +L->LLength), a type maintained in L->LockType which
2904 is either exclusive or shared.
2908 A lock exists iff it is in S->fileLocks for some cm_scache_t
2909 S. Existing locks are in one of the following states: ACTIVE,
2910 WAITLOCK, WAITUNLOCK, LOST, DELETED.
2912 The following sections describe each lock and the associated
2915 1. ACTIVE: A lock L is ACTIVE iff the cache manager has asserted
2916 the lock with the AFS file server. This type of lock can be
2917 exercised by a client to read or write to the locked region (as
2920 1.1 ACTIVE->LOST: When the AFS file server fails to extend a
2921 server lock that was required to assert the lock. Before
2922 marking the lock as lost, the cache manager checks if the file
2923 has changed on the server. If the file has not changed, then
2924 the cache manager will attempt to obtain a new server lock
2925 that is sufficient to assert the client side locks for the
2926 file. If any of these fail, the lock is marked as LOST.
2927 Otherwise, it is left as ACTIVE.
2929 1.2 ACTIVE->DELETED: Lock is released.
2931 2. WAITLOCK: A lock is in a WAITLOCK state if the cache manager
2932 grants the lock but the lock is yet to be asserted with the AFS
2933 file server. Once the file server grants the lock, the state
2934 will transition to an ACTIVE lock.
2936 2.1 WAITLOCK->ACTIVE: The server granted the lock.
2938 2.2 WAITLOCK->DELETED: Lock is abandoned, or timed out during
2941 2.3 WAITLOCK->LOST: One or more locks from this client were
2942 marked as LOST. No further locks will be granted to this
2943 client until all lost locks are removed.
2945 3. WAITUNLOCK: A lock is in a WAITUNLOCK state if the cache manager
2946 receives a request for a lock that conflicts with an existing
2947 ACTIVE or WAITLOCK lock. The lock will be placed in the queue
2948 and will be granted at such time the conflicting locks are
2949 removed, at which point the state will transition to either
2952 3.1 WAITUNLOCK->ACTIVE: The conflicting lock was removed. The
2953 current serverLock is sufficient to assert this lock, or a
2954 sufficient serverLock is obtained.
2956 3.2 WAITUNLOCK->WAITLOCK: The conflicting lock was removed,
2957 however the required serverLock is yet to be asserted with the
2960 3.3 WAITUNLOCK->DELETED: The lock is abandoned, timed out or
2963 3.5 WAITUNLOCK->LOST: One or more locks from this client were
2964 marked as LOST. No further locks will be granted to this
2965 client until all lost locks are removed.
2967 4. LOST: A lock L is LOST if the server lock that was required to
2968 assert the lock could not be obtained or if it could not be
2969 extended, or if other locks by the same client were LOST.
2970 Essentially, once a lock is LOST, the contract between the cache
2971 manager and that specific client is no longer valid.
2973 The cache manager rechecks the server lock once every minute and
2974 extends it as appropriate. If this is not done for 5 minutes,
2975 the AFS file server will release the lock (the 5 minute timeout
2976 is based on current file server code and is fairly arbitrary).
2977 Once released, the lock cannot be re-obtained without verifying
2978 that the contents of the file hasn't been modified since the
2979 time the lock was released. Re-obtaining the lock without
2980 verifying this may lead to data corruption. If the lock can not
2981 be obtained safely, then all active locks for the cm_scache_t
2984 4.1 LOST->DELETED: The lock is released.
2986 5. DELETED: The lock is no longer relevant. Eventually, it will
2987 get removed from the cm_scache_t. In the meantime, it will be
2988 treated as if it does not exist.
2990 5.1 DELETED->not exist: The lock is removed from the
2993 The following are classifications of locks based on their state.
2995 6* A lock L is ACCEPTED if it is ACTIVE or WAITLOCK. These locks
2996 have been accepted by the cache manager, but may or may not have
2997 been granted back to the client.
2999 7* A lock L is QUEUED if it is ACTIVE, WAITLOCK or WAITUNLOCK.
3001 8* A lock L is WAITING if it is WAITLOCK or WAITUNLOCK.
3005 A client C can READ range (Offset,+Length) of a file represented by
3006 cm_scache_t S iff (1):
3008 1. for all _a_ in (Offset,+Length), all of the following is true:
3010 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3011 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3014 1.2 For each LOST lock L in S->fileLocks such that _a_ in
3015 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3018 (When locks are lost on an cm_scache_t, all locks are lost. By
3019 4.2 (below), if there is an exclusive LOST lock, then there
3020 can't be any overlapping ACTIVE locks.)
3022 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
3024 2. for all _a_ in (Offset,+Length), one of the following is true:
3026 2.1 Byte _a_ of S is unowned (as specified in 1.1) AND there
3027 does not exist a LOST lock L such that _a_ in
3028 (L->LOffset,+L->LLength).
3030 2.2 Byte _a_ of S is owned by C under lock L (as specified in
3031 1.2) AND L->LockType is exclusive.
3033 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
3035 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
3038 3.1 If L->LockType is exclusive then there does NOT exist a
3039 ACCEPTED lock M in S->fileLocks such that _a_ in
3040 (M->LOffset,+M->LLength).
3042 (If we count all QUEUED locks then we hit cases such as
3043 cascading waiting locks where the locks later on in the queue
3044 can be granted without compromising file integrity. On the
3045 other hand if only ACCEPTED locks are considered, then locks
3046 that were received earlier may end up waiting for locks that
3047 were received later to be unlocked. The choice of ACCEPTED
3048 locks was made to mimic the Windows byte range lock
3051 3.2 If L->LockType is shared then for each ACCEPTED lock M in
3052 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
3053 M->LockType is shared.
3055 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
3057 4.1 M->key != Key(C)
3059 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
3060 and (M->LOffset,+M->LLength) do not intersect.
3062 (Note: If a client loses a lock, it loses all locks.
3063 Subsequently, it will not be allowed to obtain any more locks
3064 until all existing LOST locks that belong to the client are
3065 released. Once all locks are released by a single client,
3066 there exists no further contract between the client and AFS
3067 about the contents of the file, hence the client can then
3068 proceed to obtain new locks and establish a new contract.
3070 This doesn't quite work as you think it should, because most
3071 applications aren't built to deal with losing locks they
3072 thought they once had. For now, we don't have a good
3073 solution to lost locks.
3075 Also, for consistency reasons, we have to hold off on
3076 granting locks that overlap exclusive LOST locks.)
3078 A client C can only unlock locks L in S->fileLocks which have
3081 The representation and invariants are as follows:
3083 - Each cm_scache_t structure keeps:
3085 - A queue of byte-range locks (cm_scache_t::fileLocks) which
3086 are of type cm_file_lock_t.
3088 - A record of the highest server-side lock that has been
3089 obtained for this object (cm_scache_t::serverLock), which is
3090 one of (-1), LockRead, LockWrite.
3092 - A count of ACCEPTED exclusive and shared locks that are in the
3093 queue (cm_scache_t::sharedLocks and
3094 cm_scache_t::exclusiveLocks)
3096 - Each cm_file_lock_t structure keeps:
3098 - The type of lock (cm_file_lock_t::LockType)
3100 - The key associated with the lock (cm_file_lock_t::key)
3102 - The offset and length of the lock (cm_file_lock_t::LOffset
3103 and cm_file_lock_t::LLength)
3105 - The state of the lock.
3107 - Time of issuance or last successful extension
3109 Semantic invariants:
3111 I1. The number of ACCEPTED locks in S->fileLocks are
3112 (S->sharedLocks + S->exclusiveLocks)
3114 External invariants:
3116 I3. S->serverLock is the lock that we have asserted with the
3117 AFS file server for this cm_scache_t.
3119 I4. S->serverLock == LockRead iff there is at least one ACTIVE
3120 shared lock, but no ACTIVE exclusive locks.
3122 I5. S->serverLock == LockWrite iff there is at least one ACTIVE
3125 I6. If L is a LOST lock, then for each lock M in S->fileLocks,
3126 M->key == L->key IMPLIES M is LOST or DELETED.
3131 #define IS_LOCK_ACTIVE(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == 0)
3133 #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)
3135 #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)
3137 #define IS_LOCK_LOST(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_LOST)
3139 #define IS_LOCK_DELETED(lockp) (((lockp)->flags & CM_FILELOCK_FLAG_DELETED) == CM_FILELOCK_FLAG_DELETED)
3142 #define IS_LOCK_ACCEPTED(lockp) (IS_LOCK_ACTIVE(lockp) || IS_LOCK_WAITLOCK(lockp))
3145 #define IS_LOCK_CLIENTONLY(lockp) ((((lockp)->scp->flags & CM_SCACHEFLAG_RO) == CM_SCACHEFLAG_RO) || (((lockp)->flags & CM_FILELOCK_FLAG_CLIENTONLY) == CM_FILELOCK_FLAG_CLIENTONLY))
3148 #define INTERSECT_RANGE(r1,r2) (((r2).offset+(r2).length) > (r1).offset && ((r1).offset +(r1).length) > (r2).offset)
3151 #define CONTAINS_RANGE(r1,r2) (((r2).offset+(r2).length) <= ((r1).offset+(r1).length) && (r1).offset <= (r2).offset)
3153 #if defined(VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS) && !defined(LOCK_TESTING)
3154 #define SCP_SUPPORTS_BRLOCKS(scp) ((scp)->cbServerp && ((scp)->cbServerp->capabilities & VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS))
3156 #define SCP_SUPPORTS_BRLOCKS(scp) (1)
3159 #define SERVERLOCKS_ENABLED(scp) (!((scp)->flags & CM_SCACHEFLAG_RO) && cm_enableServerLocks && SCP_SUPPORTS_BRLOCKS(scp))
3161 static void cm_LockRangeSubtract(cm_range_t * pos, const cm_range_t * neg)
3163 afs_int64 int_begin;
3166 int_begin = MAX(pos->offset, neg->offset);
3167 int_end = MIN(pos->offset+pos->length, neg->offset+neg->length);
3169 if (int_begin < int_end) {
3170 if (int_begin == pos->offset) {
3171 pos->length = pos->offset + pos->length - int_end;
3172 pos->offset = int_end;
3173 } else if (int_end == pos->offset + pos->length) {
3174 pos->length = int_begin - pos->offset;
3177 /* We only subtract ranges if the resulting range is
3178 contiguous. If we try to support non-contigous ranges, we
3179 aren't actually improving performance. */
3183 /* Called with scp->mx held. Returns 0 if all is clear to read the
3184 specified range by the client identified by key.
3186 long cm_LockCheckRead(cm_scache_t *scp,
3187 LARGE_INTEGER LOffset,
3188 LARGE_INTEGER LLength,
3191 #ifndef ADVISORY_LOCKS
3193 cm_file_lock_t *fileLock;
3197 int substract_ranges = FALSE;
3199 range.offset = LOffset.QuadPart;
3200 range.length = LLength.QuadPart;
3204 1. for all _a_ in (Offset,+Length), all of the following is true:
3206 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3207 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3210 1.2 For each LOST lock L in S->fileLocks such that _a_ in
3211 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3216 lock_ObtainRead(&cm_scacheLock);
3218 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
3220 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
3222 if (INTERSECT_RANGE(range, fileLock->range)) {
3223 if (IS_LOCK_ACTIVE(fileLock)) {
3224 if (fileLock->key == key) {
3226 /* If there is an active lock for this client, it
3227 is safe to substract ranges.*/
3228 cm_LockRangeSubtract(&range, &fileLock->range);
3229 substract_ranges = TRUE;
3231 if (fileLock->lockType != LockRead) {
3232 code = CM_ERROR_LOCK_CONFLICT;
3236 /* even if the entire range is locked for reading,
3237 we still can't grant the lock at this point
3238 because the client may have lost locks. That
3239 is, unless we have already seen an active lock
3240 belonging to the client, in which case there
3241 can't be any lost locks for this client. */
3242 if (substract_ranges)
3243 cm_LockRangeSubtract(&range, &fileLock->range);
3245 } else if (IS_LOCK_LOST(fileLock) &&
3246 (fileLock->key == key || fileLock->lockType == LockWrite)) {
3247 code = CM_ERROR_BADFD;
3253 lock_ReleaseRead(&cm_scacheLock);
3255 osi_Log4(afsd_logp, "cm_LockCheckRead scp 0x%x offset %d length %d code 0x%x",
3256 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
3267 /* Called with scp->mx held. Returns 0 if all is clear to write the
3268 specified range by the client identified by key.
3270 long cm_LockCheckWrite(cm_scache_t *scp,
3271 LARGE_INTEGER LOffset,
3272 LARGE_INTEGER LLength,
3275 #ifndef ADVISORY_LOCKS
3277 cm_file_lock_t *fileLock;
3282 range.offset = LOffset.QuadPart;
3283 range.length = LLength.QuadPart;
3286 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
3288 2. for all _a_ in (Offset,+Length), one of the following is true:
3290 2.1 Byte _a_ of S is unowned AND there does not exist a LOST
3291 lock L such that _a_ in (L->LOffset,+L->LLength).
3293 2.2 Byte _a_ of S is owned by C under lock L AND L->LockType is
3297 lock_ObtainRead(&cm_scacheLock);
3299 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
3301 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
3303 if (INTERSECT_RANGE(range, fileLock->range)) {
3304 if (IS_LOCK_ACTIVE(fileLock)) {
3305 if (fileLock->key == key) {
3306 if (fileLock->lockType == LockWrite) {
3308 /* if there is an active lock for this client, it
3309 is safe to substract ranges */
3310 cm_LockRangeSubtract(&range, &fileLock->range);
3312 code = CM_ERROR_LOCK_CONFLICT;
3316 code = CM_ERROR_LOCK_CONFLICT;
3319 } else if (IS_LOCK_LOST(fileLock)) {
3320 code = CM_ERROR_BADFD;
3326 lock_ReleaseRead(&cm_scacheLock);
3328 osi_Log4(afsd_logp, "cm_LockCheckWrite scp 0x%x offset %d length %d code 0x%x",
3329 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
3341 static void cm_LockMarkSCacheLost(cm_scache_t * scp);
3343 /* Called with cm_scacheLock write locked */
3344 static cm_file_lock_t * cm_GetFileLock(void) {
3347 l = (cm_file_lock_t *) cm_freeFileLocks;
3349 osi_QRemove(&cm_freeFileLocks, &l->q);
3351 l = malloc(sizeof(cm_file_lock_t));
3355 memset(l, 0, sizeof(cm_file_lock_t));
3360 /* Called with cm_scacheLock write locked */
3361 static void cm_PutFileLock(cm_file_lock_t *l) {
3362 osi_QAdd(&cm_freeFileLocks, &l->q);
3365 /* called with scp->mx held. May release it during processing, but
3366 leaves it held on exit. */
3367 long cm_IntSetLock(cm_scache_t * scp, cm_user_t * userp, int lockType,
3373 struct rx_connection * callp;
3376 tfid.Volume = scp->fid.volume;
3377 tfid.Vnode = scp->fid.vnode;
3378 tfid.Unique = scp->fid.unique;
3381 osi_Log2(afsd_logp, "CALL SetLock scp 0x%p for lock %d", scp, lockType);
3383 lock_ReleaseMutex(&scp->mx);
3386 code = cm_Conn(&cfid, userp, reqp, &connp);
3390 callp = cm_GetRxConn(connp);
3391 code = RXAFS_SetLock(callp, &tfid, lockType,
3393 rx_PutConnection(callp);
3395 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
3398 code = cm_MapRPCError(code, reqp);
3400 osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
3402 osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
3405 lock_ObtainMutex(&scp->mx);
3410 /* called with scp->mx held. Releases it during processing */
3411 long cm_IntReleaseLock(cm_scache_t * scp, cm_user_t * userp,
3417 struct rx_connection * callp;
3420 tfid.Volume = scp->fid.volume;
3421 tfid.Vnode = scp->fid.vnode;
3422 tfid.Unique = scp->fid.unique;
3425 lock_ReleaseMutex(&scp->mx);
3427 osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
3430 code = cm_Conn(&cfid, userp, reqp, &connp);
3434 callp = cm_GetRxConn(connp);
3435 code = RXAFS_ReleaseLock(callp, &tfid, &volSync);
3436 rx_PutConnection(callp);
3438 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
3440 code = cm_MapRPCError(code, reqp);
3443 "CALL ReleaseLock FAILURE, code 0x%x", code);
3446 "CALL ReleaseLock SUCCESS");
3448 lock_ObtainMutex(&scp->mx);
3453 /* called with scp->mx held. May release it during processing, but
3454 will exit with lock held.
3458 - 0 if the user has permission to get the specified lock for the scp
3460 - CM_ERROR_NOACCESS if not
3462 Any other error from cm_SyncOp will be sent down untranslated.
3464 long cm_LockCheckPerms(cm_scache_t * scp,
3472 /* lock permissions are slightly tricky because of the 'i' bit.
3473 If the user has PRSFS_LOCK, she can read-lock the file. If the
3474 user has PRSFS_WRITE, she can write-lock the file. However, if
3475 the user has PRSFS_INSERT, then she can write-lock new files,
3476 but not old ones. Since we don't have information about
3477 whether a file is new or not, we assume that if the user owns
3478 the scp, then she has the permissions that are granted by
3481 osi_Log3(afsd_logp, "cm_LockCheckPerms for scp[0x%p] type[%d] user[0x%p]",
3482 scp, lock_type, userp);