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 | CM_SCACHESYNC_NEEDCALLBACK);
454 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
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);
490 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
495 /* We try to determine emptiness without looking beyond the first page,
496 * and without assuming "." and ".." are present and are on the first
497 * page (though these assumptions might, after all, be reasonable).
499 hashTable = (unsigned short *)(bufferp->datap + (32 * 5));
500 for (i=0; i<128; i++) {
501 idx = ntohs(hashTable[i]);
507 dep = (cm_dirEntry_t *)(bufferp->datap + (32 * idx));
508 if (strcmp(dep->name, ".") == 0)
510 else if (strcmp(dep->name, "..") == 0)
513 code = CM_ERROR_NOTEMPTY;
516 idx = ntohs(dep->next);
519 if (BeyondPage && HaveDot && HaveDotDot)
520 code = CM_ERROR_NOTEMPTY;
524 lock_ReleaseMutex(&bufferp->mx);
525 buf_Release(bufferp);
526 lock_ReleaseMutex(&scp->mx);
531 * Iterate through all entries in a directory.
532 * When the function funcp is called, the buffer is locked but the
533 * directory vnode is not.
535 * If the retscp parameter is not NULL, the parmp must be a
536 * cm_lookupSearch_t object.
538 long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
539 osi_hyper_t *startOffsetp, cm_user_t *userp, cm_req_t *reqp,
540 cm_scache_t **retscp)
547 osi_hyper_t dirLength;
548 osi_hyper_t bufferOffset;
549 osi_hyper_t curOffset;
553 cm_pageHeader_t *pageHeaderp;
555 long nextEntryCookie;
556 int numDirChunks; /* # of 32 byte dir chunks in this entry */
558 /* get the directory size */
559 lock_ObtainMutex(&scp->mx);
560 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_LOOKUP,
561 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
563 lock_ReleaseMutex(&scp->mx);
567 if (scp->fileType != CM_SCACHETYPE_DIRECTORY) {
568 lock_ReleaseMutex(&scp->mx);
569 return CM_ERROR_NOTDIR;
572 if (retscp) /* if this is a lookup call */
574 cm_lookupSearch_t* sp = parmp;
576 #ifdef AFS_FREELANCE_CLIENT
577 /* Freelance entries never end up in the DNLC because they
578 * do not have an associated cm_server_t
580 if ( !(cm_freelanceEnabled &&
581 sp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
582 sp->fid.volume==AFS_FAKE_ROOT_VOL_ID ) )
583 #endif /* AFS_FREELANCE_CLIENT */
585 int casefold = sp->caseFold;
586 sp->caseFold = 0; /* we have a strong preference for exact matches */
587 if ( *retscp = cm_dnlcLookup(scp, sp)) /* dnlc hit */
589 sp->caseFold = casefold;
590 lock_ReleaseMutex(&scp->mx);
593 sp->caseFold = casefold;
598 * XXX We only get the length once. It might change when we drop the
601 dirLength = scp->length;
603 lock_ReleaseMutex(&scp->mx);
606 bufferOffset.LowPart = bufferOffset.HighPart = 0;
608 curOffset = *startOffsetp;
610 curOffset.HighPart = 0;
611 curOffset.LowPart = 0;
615 /* make sure that curOffset.LowPart doesn't point to the first
616 * 32 bytes in the 2nd through last dir page, and that it
617 * doesn't point at the first 13 32-byte chunks in the first
618 * dir page, since those are dir and page headers, and don't
619 * contain useful information.
621 temp = curOffset.LowPart & (2048-1);
622 if (curOffset.HighPart == 0 && curOffset.LowPart < 2048) {
623 /* we're in the first page */
624 if (temp < 13*32) temp = 13*32;
627 /* we're in a later dir page */
628 if (temp < 32) temp = 32;
631 /* make sure the low order 5 bits are zero */
634 /* now put temp bits back ito curOffset.LowPart */
635 curOffset.LowPart &= ~(2048-1);
636 curOffset.LowPart |= temp;
638 /* check if we've passed the dir's EOF */
639 if (LargeIntegerGreaterThanOrEqualTo(curOffset, dirLength))
642 /* see if we can use the bufferp we have now; compute in which
643 * page the current offset would be, and check whether that's
644 * the offset of the buffer we have. If not, get the buffer.
646 thyper.HighPart = curOffset.HighPart;
647 thyper.LowPart = curOffset.LowPart & ~(cm_data.buf_blockSize-1);
648 if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
651 lock_ReleaseMutex(&bufferp->mx);
652 buf_Release(bufferp);
656 lock_ObtainRead(&scp->bufCreateLock);
657 code = buf_Get(scp, &thyper, &bufferp);
658 lock_ReleaseRead(&scp->bufCreateLock);
660 /* if buf_Get() fails we do not have a buffer object to lock */
666 /* for the IFS version, we bulkstat the dirents because this
667 routine is used in place of smb_ReceiveCoreSearchDir. our
668 other option is to modify smb_ReceiveCoreSearchDir itself,
669 but this seems to be the proper use for cm_ApplyDir. */
670 lock_ObtainMutex(&scp->mx);
671 if ((scp->flags & CM_SCACHEFLAG_BULKSTATTING) == 0
672 && (scp->bulkStatProgress.QuadPart <= thyper.QuadPart))
674 scp->flags |= CM_SCACHEFLAG_BULKSTATTING;
675 code = cm_TryBulkStat(scp, &thyper, userp, reqp);
676 scp->flags &= ~CM_SCACHEFLAG_BULKSTATTING;
677 scp->bulkStatProgress = thyper;
679 lock_ReleaseMutex(&scp->mx);
682 lock_ObtainMutex(&bufferp->mx);
683 bufferOffset = thyper;
685 /* now get the data in the cache */
687 lock_ObtainMutex(&scp->mx);
688 code = cm_SyncOp(scp, bufferp, userp, reqp,
690 CM_SCACHESYNC_NEEDCALLBACK
692 | CM_SCACHESYNC_BUFLOCKED);
694 lock_ReleaseMutex(&scp->mx);
697 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
699 if (cm_HaveBuffer(scp, bufferp, 1)) {
700 lock_ReleaseMutex(&scp->mx);
704 /* otherwise, load the buffer and try again */
705 lock_ReleaseMutex(&bufferp->mx);
706 code = cm_GetBuffer(scp, bufferp, NULL, userp,
708 lock_ReleaseMutex(&scp->mx);
709 lock_ObtainMutex(&bufferp->mx);
714 lock_ReleaseMutex(&bufferp->mx);
715 buf_Release(bufferp);
719 } /* if (wrong buffer) ... */
721 /* now we have the buffer containing the entry we're interested
722 * in; copy it out if it represents a non-deleted entry.
724 entryInDir = curOffset.LowPart & (2048-1);
725 entryInBuffer = curOffset.LowPart & (cm_data.buf_blockSize - 1);
727 /* page header will help tell us which entries are free. Page
728 * header can change more often than once per buffer, since
729 * AFS 3 dir page size may be less than (but not more than) a
730 * buffer package buffer.
732 /* only look intra-buffer */
733 temp = curOffset.LowPart & (cm_data.buf_blockSize - 1);
734 temp &= ~(2048 - 1); /* turn off intra-page bits */
735 pageHeaderp = (cm_pageHeader_t *) (bufferp->datap + temp);
737 /* now determine which entry we're looking at in the page. If
738 * it is free (there's a free bitmap at the start of the dir),
739 * we should skip these 32 bytes.
741 slotInPage = (entryInDir & 0x7e0) >> 5;
742 if (!(pageHeaderp->freeBitmap[slotInPage>>3]
743 & (1 << (slotInPage & 0x7)))) {
744 /* this entry is free */
745 numDirChunks = 1; /* only skip this guy */
749 tp = bufferp->datap + entryInBuffer;
750 dep = (cm_dirEntry_t *) tp; /* now points to AFS3 dir entry */
752 /* while we're here, compute the next entry's location, too,
753 * since we'll need it when writing out the cookie into the
754 * dir listing stream.
756 numDirChunks = cm_NameEntries(dep->name, NULL);
758 /* compute the offset of the cookie representing the next entry */
759 nextEntryCookie = curOffset.LowPart
760 + (CM_DIR_CHUNKSIZE * numDirChunks);
762 if (dep->fid.vnode != 0) {
763 /* this is one of the entries to use: it is not deleted */
764 code = (*funcp)(scp, dep, parmp, &curOffset);
767 } /* if we're including this name */
770 /* and adjust curOffset to be where the new cookie is */
772 thyper.LowPart = CM_DIR_CHUNKSIZE * numDirChunks;
773 curOffset = LargeIntegerAdd(thyper, curOffset);
774 } /* while copying data for dir listing */
776 /* release the mutex */
778 lock_ReleaseMutex(&bufferp->mx);
779 buf_Release(bufferp);
784 int cm_NoneUpper(char *s)
788 if (c >= 'A' && c <= 'Z')
793 int cm_NoneLower(char *s)
797 if (c >= 'a' && c <= 'z')
802 long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
805 cm_lookupSearch_t *sp;
810 sp = (cm_lookupSearch_t *) rockp;
812 matchName = dep->name;
814 match = cm_stricmp(matchName, sp->searchNamep);
816 match = strcmp(matchName, sp->searchNamep);
820 && !cm_Is8Dot3(dep->name)) {
821 matchName = shortName;
822 cm_Gen8Dot3Name(dep, shortName, NULL);
824 match = cm_stricmp(matchName, sp->searchNamep);
826 match = strcmp(matchName, sp->searchNamep);
836 if (!sp->caseFold || matchName == shortName) {
837 sp->fid.vnode = ntohl(dep->fid.vnode);
838 sp->fid.unique = ntohl(dep->fid.unique);
839 return CM_ERROR_STOPNOW;
843 * If we get here, we are doing a case-insensitive search, and we
844 * have found a match. Now we determine what kind of match it is:
845 * exact, lower-case, upper-case, or none of the above. This is done
846 * in order to choose among matches, if there are more than one.
849 /* Exact matches are the best. */
850 match = strcmp(matchName, sp->searchNamep);
853 sp->fid.vnode = ntohl(dep->fid.vnode);
854 sp->fid.unique = ntohl(dep->fid.unique);
855 return CM_ERROR_STOPNOW;
858 /* Lower-case matches are next. */
861 if (cm_NoneUpper(matchName)) {
866 /* Upper-case matches are next. */
869 if (cm_NoneLower(matchName)) {
874 /* General matches are last. */
880 sp->fid.vnode = ntohl(dep->fid.vnode);
881 sp->fid.unique = ntohl(dep->fid.unique);
885 /* read the contents of a mount point into the appropriate string.
886 * called with locked scp, and returns with locked scp.
888 long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
895 if (scp->mountPointStringp[0])
898 /* otherwise, we have to read it in */
899 lock_ReleaseMutex(&scp->mx);
901 lock_ObtainRead(&scp->bufCreateLock);
902 thyper.LowPart = thyper.HighPart = 0;
903 code = buf_Get(scp, &thyper, &bufp);
904 lock_ReleaseRead(&scp->bufCreateLock);
906 lock_ObtainMutex(&scp->mx);
911 code = cm_SyncOp(scp, bufp, userp, reqp, 0,
912 CM_SCACHESYNC_READ | CM_SCACHESYNC_NEEDCALLBACK);
916 cm_SyncOpDone(scp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
919 if (cm_HaveBuffer(scp, bufp, 0))
922 /* otherwise load buffer */
923 code = cm_GetBuffer(scp, bufp, NULL, userp, reqp);
928 /* locked, has callback, has valid data in buffer */
929 if ((tlen = scp->length.LowPart) > 1000)
930 return CM_ERROR_TOOBIG;
932 code = CM_ERROR_INVAL;
936 /* someone else did the work while we were out */
937 if (scp->mountPointStringp[0]) {
942 /* otherwise, copy out the link */
943 memcpy(scp->mountPointStringp, bufp->datap, tlen);
945 /* now make it null-terminated. Note that the original contents of a
946 * link that is a mount point is "#volname." where "." is there just to
947 * be turned into a null. That is, we can trash the last char of the
948 * link without damaging the vol name. This is a stupid convention,
949 * but that's the protocol.
951 scp->mountPointStringp[tlen-1] = 0;
960 /* called with a locked scp and chases the mount point, yielding outScpp.
961 * scp remains locked, just for simplicity of describing the interface.
963 long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
964 cm_req_t *reqp, cm_scache_t **outScpp)
979 if (scp->mountRootFid.cell != 0 && scp->mountRootGen >= cm_data.mountRootGen) {
980 tfid = scp->mountRootFid;
981 lock_ReleaseMutex(&scp->mx);
982 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
983 lock_ObtainMutex(&scp->mx);
987 /* parse the volume name */
988 mpNamep = scp->mountPointStringp;
990 return CM_ERROR_NOSUCHPATH;
991 tlen = (int)strlen(scp->mountPointStringp);
992 mtType = *scp->mountPointStringp;
993 cellNamep = malloc(tlen);
994 volNamep = malloc(tlen);
996 cp = strrchr(mpNamep, ':');
998 /* cellular mount point */
999 memset(cellNamep, 0, tlen);
1000 strncpy(cellNamep, mpNamep+1, cp - mpNamep - 1);
1001 strcpy(volNamep, cp+1);
1002 /* now look up the cell */
1003 cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE);
1007 strcpy(volNamep, mpNamep+1);
1009 cellp = cm_FindCellByID(scp->fid.cell);
1013 code = CM_ERROR_NOSUCHCELL;
1017 vnLength = strlen(volNamep);
1018 if (vnLength >= 8 && strcmp(volNamep + vnLength - 7, ".backup") == 0)
1020 else if (vnLength >= 10
1021 && strcmp(volNamep + vnLength - 9, ".readonly") == 0)
1026 /* check for backups within backups */
1028 && (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
1029 == CM_SCACHEFLAG_RO) {
1030 code = CM_ERROR_NOSUCHVOLUME;
1034 /* now we need to get the volume */
1035 lock_ReleaseMutex(&scp->mx);
1036 code = cm_GetVolumeByName(cellp, volNamep, userp, reqp, 0, &volp);
1037 lock_ObtainMutex(&scp->mx);
1040 /* save the parent of the volume root for this is the
1041 * place where the volume is mounted and we must remember
1042 * this in the volume structure rather than just in the
1043 * scache entry lest the scache entry gets recycled
1046 lock_ObtainMutex(&volp->mx);
1047 volp->dotdotFid = dscp->fid;
1048 lock_ReleaseMutex(&volp->mx);
1050 scp->mountRootFid.cell = cellp->cellID;
1051 /* if the mt pt is in a read-only volume (not just a
1052 * backup), and if there is a read-only volume for the
1053 * target, and if this is a type '#' mount point, use
1054 * the read-only, otherwise use the one specified.
1056 if (mtType == '#' && (scp->flags & CM_SCACHEFLAG_PURERO)
1057 && volp->roID != 0 && type == RWVOL)
1060 scp->mountRootFid.volume = volp->roID;
1061 else if (type == BACKVOL)
1062 scp->mountRootFid.volume = volp->bkID;
1064 scp->mountRootFid.volume = volp->rwID;
1066 /* the rest of the fid is a magic number */
1067 scp->mountRootFid.vnode = 1;
1068 scp->mountRootFid.unique = 1;
1069 scp->mountRootGen = cm_data.mountRootGen;
1071 tfid = scp->mountRootFid;
1072 lock_ReleaseMutex(&scp->mx);
1073 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
1074 lock_ObtainMutex(&scp->mx);
1083 long cm_LookupInternal(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
1084 cm_req_t *reqp, cm_scache_t **outpScpp)
1087 int dnlcHit = 1; /* did we hit in the dnlc? yes, we did */
1088 cm_scache_t *tscp = NULL;
1089 cm_scache_t *mountedScp;
1090 cm_lookupSearch_t rock;
1093 if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
1094 && strcmp(namep, "..") == 0) {
1095 if (dscp->dotdotFid.volume == 0)
1096 return CM_ERROR_NOSUCHVOLUME;
1097 rock.fid = dscp->dotdotFid;
1099 } else if (strcmp(namep, ".") == 0) {
1100 rock.fid = dscp->fid;
1104 memset(&rock, 0, sizeof(rock));
1105 rock.fid.cell = dscp->fid.cell;
1106 rock.fid.volume = dscp->fid.volume;
1107 rock.searchNamep = namep;
1108 rock.caseFold = (flags & CM_FLAG_CASEFOLD);
1109 rock.hasTilde = ((strchr(namep, '~') != NULL) ? 1 : 0);
1111 /* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
1112 code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
1113 (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
1115 /* code == 0 means we fell off the end of the dir, while stopnow means
1116 * that we stopped early, probably because we found the entry we're
1117 * looking for. Any other non-zero code is an error.
1119 if (code && code != CM_ERROR_STOPNOW) {
1120 /* if the cm_scache_t we are searching in is not a directory
1121 * we must return path not found because the error
1122 * is to describe the final component not an intermediary
1124 if (code == CM_ERROR_NOTDIR) {
1125 if (flags & CM_FLAG_CHECKPATH)
1126 return CM_ERROR_NOSUCHPATH;
1128 return CM_ERROR_NOSUCHFILE;
1133 getroot = (dscp==cm_data.rootSCachep) ;
1135 if (!cm_freelanceEnabled || !getroot) {
1136 if (flags & CM_FLAG_CHECKPATH)
1137 return CM_ERROR_NOSUCHPATH;
1139 return CM_ERROR_NOSUCHFILE;
1141 else { /* nonexistent dir on freelance root, so add it */
1142 char fullname[200] = ".";
1145 osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %s",
1146 osi_LogSaveString(afsd_logp,namep));
1147 if (namep[0] == '.') {
1148 if (cm_GetCell_Gen(&namep[1], &fullname[1], CM_FLAG_CREATE)) {
1150 if ( stricmp(&namep[1], &fullname[1]) )
1151 code = cm_FreelanceAddSymlink(namep, fullname, &rock.fid);
1153 code = cm_FreelanceAddMount(namep, &fullname[1], "root.cell.", 1, &rock.fid);
1156 if (cm_GetCell_Gen(namep, fullname, CM_FLAG_CREATE)) {
1158 if ( stricmp(namep, fullname) )
1159 code = cm_FreelanceAddSymlink(namep, fullname, &rock.fid);
1161 code = cm_FreelanceAddMount(namep, fullname, "root.cell.", 0, &rock.fid);
1164 if (!found || code < 0) { /* add mount point failed, so give up */
1165 if (flags & CM_FLAG_CHECKPATH)
1166 return CM_ERROR_NOSUCHPATH;
1168 return CM_ERROR_NOSUCHFILE;
1170 tscp = NULL; /* to force call of cm_GetSCache */
1175 if ( !tscp ) /* we did not find it in the dnlc */
1178 code = cm_GetSCache(&rock.fid, &tscp, userp, reqp);
1182 /* tscp is now held */
1184 lock_ObtainMutex(&tscp->mx);
1185 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1186 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1188 lock_ReleaseMutex(&tscp->mx);
1189 cm_ReleaseSCache(tscp);
1192 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1193 /* tscp is now locked */
1195 if (!(flags & CM_FLAG_NOMOUNTCHASE)
1196 && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
1197 /* mount points are funny: they have a volume name to mount
1200 code = cm_ReadMountPoint(tscp, userp, reqp);
1202 code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
1204 lock_ReleaseMutex(&tscp->mx);
1205 cm_ReleaseSCache(tscp);
1212 lock_ReleaseMutex(&tscp->mx);
1215 /* copy back pointer */
1218 /* insert scache in dnlc */
1219 if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
1220 /* lock the directory entry to prevent racing callback revokes */
1221 lock_ObtainMutex(&dscp->mx);
1222 if ( dscp->cbServerp != NULL && dscp->cbExpires > 0 )
1223 cm_dnlcEnter(dscp, namep, tscp);
1224 lock_ReleaseMutex(&dscp->mx);
1231 int cm_ExpandSysName(char *inp, char *outp, long outSize, unsigned int index)
1236 tp = strrchr(inp, '@');
1238 return 0; /* no @sys */
1240 if (strcmp(tp, "@sys") != 0)
1241 return 0; /* no @sys */
1243 /* caller just wants to know if this is a valid @sys type of name */
1247 if (index >= MAXNUMSYSNAMES)
1250 /* otherwise generate the properly expanded @sys name */
1251 prefixCount = (int)(tp - inp);
1253 strncpy(outp, inp, prefixCount); /* copy out "a." from "a.@sys" */
1254 outp[prefixCount] = 0; /* null terminate the "a." */
1255 strcat(outp, cm_sysNameList[index]);/* append i386_nt40 */
1259 long cm_Lookup(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
1260 cm_req_t *reqp, cm_scache_t **outpScpp)
1264 int sysNameIndex = 0;
1265 cm_scache_t *scp = NULL;
1267 if ( stricmp(namep,SMB_IOCTL_FILENAME_NOSLASH) == 0 ) {
1268 if (flags & CM_FLAG_CHECKPATH)
1269 return CM_ERROR_NOSUCHPATH;
1271 return CM_ERROR_NOSUCHFILE;
1274 for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
1275 code = cm_ExpandSysName(namep, tname, sizeof(tname), sysNameIndex);
1277 code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
1283 cm_ReleaseSCache(scp);
1287 return cm_LookupInternal(dscp, namep, flags, userp, reqp, outpScpp);
1291 /* None of the possible sysName expansions could be found */
1292 if (flags & CM_FLAG_CHECKPATH)
1293 return CM_ERROR_NOSUCHPATH;
1295 return CM_ERROR_NOSUCHFILE;
1298 long cm_Unlink(cm_scache_t *dscp, char *namep, cm_user_t *userp, cm_req_t *reqp)
1304 AFSFetchStatus newDirStatus;
1306 struct rx_connection * callp;
1308 #ifdef AFS_FREELANCE_CLIENT
1309 if (cm_freelanceEnabled && dscp == cm_data.rootSCachep) {
1310 /* deleting a mount point from the root dir. */
1311 code = cm_FreelanceRemoveMount(namep);
1316 /* make sure we don't screw up the dir status during the merge */
1317 lock_ObtainMutex(&dscp->mx);
1318 sflags = CM_SCACHESYNC_STOREDATA;
1319 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1320 lock_ReleaseMutex(&dscp->mx);
1325 afsFid.Volume = dscp->fid.volume;
1326 afsFid.Vnode = dscp->fid.vnode;
1327 afsFid.Unique = dscp->fid.unique;
1329 osi_Log1(afsd_logp, "CALL RemoveFile scp 0x%p", dscp);
1331 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1335 callp = cm_GetRxConn(connp);
1336 code = RXAFS_RemoveFile(callp, &afsFid, namep,
1337 &newDirStatus, &volSync);
1338 rx_PutConnection(callp);
1340 } while (cm_Analyze(connp, userp, reqp, &dscp->fid, &volSync, NULL, NULL, code));
1341 code = cm_MapRPCError(code, reqp);
1344 osi_Log1(afsd_logp, "CALL RemoveFile FAILURE, code 0x%x", code);
1346 osi_Log0(afsd_logp, "CALL RemoveFile SUCCESS");
1348 lock_ObtainMutex(&dscp->mx);
1349 cm_dnlcRemove(dscp, namep);
1350 cm_SyncOpDone(dscp, NULL, sflags);
1352 cm_MergeStatus(dscp, &newDirStatus, &volSync, userp, 0);
1353 else if (code == CM_ERROR_NOSUCHFILE) {
1354 /* windows would not have allowed the request to delete the file
1355 * if it did not believe the file existed. therefore, we must
1356 * have an inconsistent view of the world.
1358 dscp->cbServerp = NULL;
1360 lock_ReleaseMutex(&dscp->mx);
1365 /* called with a locked vnode, and fills in the link info.
1366 * returns this the vnode still locked.
1368 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1375 lock_AssertMutex(&linkScp->mx);
1376 if (!linkScp->mountPointStringp[0]) {
1377 /* read the link data */
1378 lock_ReleaseMutex(&linkScp->mx);
1379 thyper.LowPart = thyper.HighPart = 0;
1380 code = buf_Get(linkScp, &thyper, &bufp);
1381 lock_ObtainMutex(&linkScp->mx);
1385 code = cm_SyncOp(linkScp, bufp, userp, reqp, 0,
1386 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1391 cm_SyncOpDone(linkScp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1393 if (cm_HaveBuffer(linkScp, bufp, 0))
1396 code = cm_GetBuffer(linkScp, bufp, NULL, userp, reqp);
1401 } /* while loop to get the data */
1403 /* now if we still have no link read in,
1404 * copy the data from the buffer */
1405 if ((temp = linkScp->length.LowPart) >= MOUNTPOINTLEN) {
1407 return CM_ERROR_TOOBIG;
1410 /* otherwise, it fits; make sure it is still null (could have
1411 * lost race with someone else referencing this link above),
1412 * and if so, copy in the data.
1414 if (!linkScp->mountPointStringp[0]) {
1415 strncpy(linkScp->mountPointStringp, bufp->datap, temp);
1416 linkScp->mountPointStringp[temp] = 0; /* null terminate */
1419 } /* don't have sym link contents cached */
1424 /* called with a held vnode and a path suffix, with the held vnode being a
1425 * symbolic link. Our goal is to generate a new path to interpret, and return
1426 * this new path in newSpaceBufferp. If the new vnode is relative to a dir
1427 * other than the directory containing the symbolic link, then the new root is
1428 * returned in *newRootScpp, otherwise a null is returned there.
1430 long cm_AssembleLink(cm_scache_t *linkScp, char *pathSuffixp,
1431 cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1432 cm_user_t *userp, cm_req_t *reqp)
1439 lock_ObtainMutex(&linkScp->mx);
1440 code = cm_HandleLink(linkScp, userp, reqp);
1444 /* if we may overflow the buffer, bail out; buffer is signficantly
1445 * bigger than max path length, so we don't really have to worry about
1446 * being a little conservative here.
1448 if (strlen(linkScp->mountPointStringp) + strlen(pathSuffixp) + 2
1449 >= CM_UTILS_SPACESIZE)
1450 return CM_ERROR_TOOBIG;
1452 tsp = cm_GetSpace();
1453 linkp = linkScp->mountPointStringp;
1454 if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1455 if (strlen(linkp) > cm_mountRootLen)
1456 strcpy(tsp->data, linkp+cm_mountRootLen+1);
1459 *newRootScpp = cm_data.rootSCachep;
1460 cm_HoldSCache(cm_data.rootSCachep);
1461 } else if (linkp[0] == '\\' && linkp[1] == '\\') {
1462 if (!strnicmp(&linkp[2], cm_NetbiosName, (len = (long)strlen(cm_NetbiosName))))
1464 char * p = &linkp[len + 3];
1465 if (strnicmp(p, "all", 3) == 0)
1468 strcpy(tsp->data, p);
1469 for (p = tsp->data; *p; p++) {
1473 *newRootScpp = cm_data.rootSCachep;
1474 cm_HoldSCache(cm_data.rootSCachep);
1476 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1477 strcpy(tsp->data, linkp);
1478 *newRootScpp = NULL;
1479 code = CM_ERROR_PATH_NOT_COVERED;
1481 } else if ( !strnicmp(linkp, "msdfs:", (len = (long)strlen("msdfs:"))) ) {
1482 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1483 strcpy(tsp->data, linkp);
1484 *newRootScpp = NULL;
1485 code = CM_ERROR_PATH_NOT_COVERED;
1486 } else if (*linkp == '\\' || *linkp == '/') {
1488 /* formerly, this was considered to be from the AFS root,
1489 * but this seems to create problems. instead, we will just
1490 * reject the link */
1491 strcpy(tsp->data, linkp+1);
1492 *newRootScpp = cm_data.rootSCachep;
1493 cm_HoldSCache(cm_data.rootSCachep);
1495 /* we still copy the link data into the response so that
1496 * the user can see what the link points to
1498 linkScp->fileType = CM_SCACHETYPE_INVALID;
1499 strcpy(tsp->data, linkp);
1500 *newRootScpp = NULL;
1501 code = CM_ERROR_NOSUCHPATH;
1504 /* a relative link */
1505 strcpy(tsp->data, linkp);
1506 *newRootScpp = NULL;
1508 if (pathSuffixp[0] != 0) { /* if suffix string is non-null */
1509 strcat(tsp->data, "\\");
1510 strcat(tsp->data, pathSuffixp);
1512 *newSpaceBufferp = tsp;
1515 lock_ReleaseMutex(&linkScp->mx);
1519 long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
1520 cm_user_t *userp, char *tidPathp, cm_req_t *reqp, cm_scache_t **outScpp)
1523 char *tp; /* ptr moving through input buffer */
1524 char tc; /* temp char */
1525 int haveComponent; /* has new component started? */
1526 char component[256]; /* this is the new component */
1527 char *cp; /* component name being assembled */
1528 cm_scache_t *tscp; /* current location in the hierarchy */
1529 cm_scache_t *nscp; /* next dude down */
1530 cm_scache_t *dirScp; /* last dir we searched */
1531 cm_scache_t *linkScp; /* new root for the symlink we just
1533 cm_space_t *psp; /* space for current path, if we've hit
1535 cm_space_t *tempsp; /* temp vbl */
1536 char *restp; /* rest of the pathname to interpret */
1537 int symlinkCount; /* count of # of symlinks traversed */
1538 int extraFlag; /* avoid chasing mt pts for dir cmd */
1539 int phase = 1; /* 1 = tidPathp, 2 = pathp */
1552 cm_HoldSCache(tscp);
1559 /* map Unix slashes into DOS ones so we can interpret Unix
1565 if (!haveComponent) {
1568 } else if (tc == 0) {
1582 /* we have a component here */
1583 if (tc == 0 || tc == '\\') {
1584 /* end of the component; we're at the last
1585 * component if tc == 0. However, if the last
1586 * is a symlink, we have more to do.
1588 *cp++ = 0; /* add null termination */
1589 if (!strcmp(".",component)) {
1592 cm_ReleaseSCache(dirScp);
1598 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
1599 extraFlag = CM_FLAG_NOMOUNTCHASE;
1600 code = cm_Lookup(tscp, component,
1602 userp, reqp, &nscp);
1604 cm_ReleaseSCache(tscp);
1606 cm_ReleaseSCache(dirScp);
1609 if (code == CM_ERROR_NOSUCHFILE && tscp->fileType == CM_SCACHETYPE_SYMLINK)
1610 return CM_ERROR_NOSUCHPATH;
1614 haveComponent = 0; /* component done */
1616 cm_ReleaseSCache(dirScp);
1617 dirScp = tscp; /* for some symlinks */
1618 tscp = nscp; /* already held */
1620 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
1623 cm_ReleaseSCache(dirScp);
1629 /* now, if tscp is a symlink, we should follow
1630 * it and assemble the path again.
1632 lock_ObtainMutex(&tscp->mx);
1633 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1634 CM_SCACHESYNC_GETSTATUS
1635 | CM_SCACHESYNC_NEEDCALLBACK);
1637 lock_ReleaseMutex(&tscp->mx);
1638 cm_ReleaseSCache(tscp);
1641 cm_ReleaseSCache(dirScp);
1646 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1648 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
1649 /* this is a symlink; assemble a new buffer */
1650 lock_ReleaseMutex(&tscp->mx);
1651 if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
1652 cm_ReleaseSCache(tscp);
1655 cm_ReleaseSCache(dirScp);
1660 return CM_ERROR_TOO_MANY_SYMLINKS;
1666 code = cm_AssembleLink(tscp, restp, &linkScp, &tempsp, userp, reqp);
1668 /* something went wrong */
1669 cm_ReleaseSCache(tscp);
1672 cm_ReleaseSCache(dirScp);
1678 /* otherwise, tempsp has the new path,
1679 * and linkScp is the new root from
1680 * which to interpret that path.
1681 * Continue with the namei processing,
1682 * also doing the bookkeeping for the
1683 * space allocation and tracking the
1684 * vnode reference counts.
1690 cm_ReleaseSCache(tscp);
1695 * now, if linkScp is null, that's
1696 * AssembleLink's way of telling us that
1697 * the sym link is relative to the dir
1698 * containing the link. We have a ref
1699 * to it in dirScp, and we hold it now
1700 * and reuse it as the new spot in the
1708 /* not a symlink, we may be done */
1709 lock_ReleaseMutex(&tscp->mx);
1717 cm_ReleaseSCache(dirScp);
1725 cm_ReleaseSCache(dirScp);
1728 } /* end of a component */
1731 } /* we have a component */
1732 } /* big while loop over all components */
1736 cm_ReleaseSCache(dirScp);
1742 cm_ReleaseSCache(tscp);
1746 /* called with a dir, and a vnode within the dir that happens to be a symlink.
1747 * We chase the link, and return a held pointer to the target, if it exists,
1748 * in *outScpp. If we succeed, we return 0, otherwise we return an error code
1749 * and do not hold or return a target vnode.
1751 * This is very similar to calling cm_NameI with the last component of a name,
1752 * which happens to be a symlink, except that we've already passed by the name.
1754 * This function is typically called by the directory listing functions, which
1755 * encounter symlinks but need to return the proper file length so programs
1756 * like "more" work properly when they make use of the attributes retrieved from
1759 * The input vnode should not be locked when this function is called.
1761 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
1762 cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
1766 cm_scache_t *newRootScp;
1768 osi_Log1(afsd_logp, "Evaluating symlink scp 0x%p", linkScp);
1770 code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
1774 /* now, if newRootScp is NULL, we're really being told that the symlink
1775 * is relative to the current directory (dscp).
1777 if (newRootScp == NULL) {
1779 cm_HoldSCache(dscp);
1782 code = cm_NameI(newRootScp, spacep->data,
1783 CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
1784 userp, NULL, reqp, outScpp);
1786 if (code == CM_ERROR_NOSUCHFILE)
1787 code = CM_ERROR_NOSUCHPATH;
1789 /* this stuff is allocated no matter what happened on the namei call,
1791 cm_FreeSpace(spacep);
1792 cm_ReleaseSCache(newRootScp);
1797 /* make this big enough so that one buffer of dir pages won't overflow. We'll
1798 * check anyway, but we want to minimize the chance that we have to leave stuff
1801 #define CM_BULKMAX (3 * AFSCBMAX)
1803 /* rock for bulk stat calls */
1804 typedef struct cm_bulkStat {
1805 osi_hyper_t bufOffset; /* only do it for things in this buffer page */
1807 /* info for the actual call */
1808 int counter; /* next free slot */
1809 AFSFid fids[CM_BULKMAX];
1810 AFSFetchStatus stats[CM_BULKMAX];
1811 AFSCallBack callbacks[CM_BULKMAX];
1814 /* for a given entry, make sure that it isn't in the stat cache, and then
1815 * add it to the list of file IDs to be obtained.
1817 * Don't bother adding it if we already have a vnode. Note that the dir
1818 * is locked, so we have to be careful checking the vnode we're thinking of
1819 * processing, to avoid deadlocks.
1821 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
1832 /* Don't overflow bsp. */
1833 if (bsp->counter >= CM_BULKMAX)
1834 return CM_ERROR_STOPNOW;
1836 thyper.LowPart = cm_data.buf_blockSize;
1837 thyper.HighPart = 0;
1838 thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
1840 /* thyper is now the first byte past the end of the record we're
1841 * interested in, and bsp->bufOffset is the first byte of the record
1842 * we're interested in.
1843 * Skip data in the others.
1846 if (LargeIntegerLessThan(*offp, bsp->bufOffset))
1848 if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
1849 return CM_ERROR_STOPNOW;
1850 if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
1853 tfid.cell = scp->fid.cell;
1854 tfid.volume = scp->fid.volume;
1855 tfid.vnode = ntohl(dep->fid.vnode);
1856 tfid.unique = ntohl(dep->fid.unique);
1857 tscp = cm_FindSCache(&tfid);
1859 if (lock_TryMutex(&tscp->mx)) {
1860 /* we have an entry that we can look at */
1861 if (!(tscp->flags & CM_SCACHEFLAG_EACCESS) && cm_HaveCallback(tscp)) {
1862 /* we have a callback on it. Don't bother
1863 * fetching this stat entry, since we're happy
1864 * with the info we have.
1866 lock_ReleaseMutex(&tscp->mx);
1867 cm_ReleaseSCache(tscp);
1870 lock_ReleaseMutex(&tscp->mx);
1872 cm_ReleaseSCache(tscp);
1875 #ifdef AFS_FREELANCE_CLIENT
1876 // yj: if this is a mountpoint under root.afs then we don't want it
1877 // to be bulkstat-ed, instead, we call getSCache directly and under
1878 // getSCache, it is handled specially.
1879 if ( cm_freelanceEnabled &&
1880 tfid.cell==AFS_FAKE_ROOT_CELL_ID &&
1881 tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
1882 !(tfid.vnode==0x1 && tfid.unique==0x1) )
1884 osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
1885 return cm_GetSCache(&tfid, &tscp, NULL, NULL);
1887 #endif /* AFS_FREELANCE_CLIENT */
1890 bsp->fids[i].Volume = scp->fid.volume;
1891 bsp->fids[i].Vnode = tfid.vnode;
1892 bsp->fids[i].Unique = tfid.unique;
1896 /* called with a locked scp and a pointer to a buffer. Make bulk stat
1897 * calls on all undeleted files in the page of the directory specified.
1900 cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
1904 cm_bulkStat_t bb; /* this is *BIG*, probably 16K or so;
1905 * watch for stack problems */
1906 AFSCBFids fidStruct;
1907 AFSBulkStats statStruct;
1909 AFSCBs callbackStruct;
1912 cm_callbackRequest_t cbReq;
1918 struct rx_connection * callp;
1919 int inlinebulk = 0; /* Did we use InlineBulkStatus RPC or not? */
1921 osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%p", dscp);
1923 /* should be on a buffer boundary */
1924 osi_assert((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0);
1926 memset(&bb, 0, sizeof(bb));
1927 bb.bufOffset = *offsetp;
1929 lock_ReleaseMutex(&dscp->mx);
1930 /* first, assemble the file IDs we need to stat */
1931 code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) &bb, offsetp, userp, reqp, NULL);
1933 /* if we failed, bail out early */
1934 if (code && code != CM_ERROR_STOPNOW) {
1935 lock_ObtainMutex(&dscp->mx);
1939 /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
1940 * make the calls to create the entries. Handle AFSCBMAX files at a
1944 while (filex < bb.counter) {
1945 filesThisCall = bb.counter - filex;
1946 if (filesThisCall > AFSCBMAX)
1947 filesThisCall = AFSCBMAX;
1949 fidStruct.AFSCBFids_len = filesThisCall;
1950 fidStruct.AFSCBFids_val = &bb.fids[filex];
1951 statStruct.AFSBulkStats_len = filesThisCall;
1952 statStruct.AFSBulkStats_val = &bb.stats[filex];
1953 callbackStruct.AFSCBs_len = filesThisCall;
1954 callbackStruct.AFSCBs_val = &bb.callbacks[filex];
1955 cm_StartCallbackGrantingCall(NULL, &cbReq);
1956 osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
1958 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1962 callp = cm_GetRxConn(connp);
1963 if (!(connp->serverp->flags & CM_SERVERFLAG_NOINLINEBULK)) {
1964 code = RXAFS_InlineBulkStatus(callp, &fidStruct,
1965 &statStruct, &callbackStruct, &volSync);
1966 if (code == RXGEN_OPCODE) {
1967 cm_SetServerNoInlineBulk(connp->serverp, 0);
1973 code = RXAFS_BulkStatus(callp, &fidStruct,
1974 &statStruct, &callbackStruct, &volSync);
1976 rx_PutConnection(callp);
1978 } while (cm_Analyze(connp, userp, reqp, &dscp->fid,
1979 &volSync, NULL, &cbReq, code));
1980 code = cm_MapRPCError(code, reqp);
1982 osi_Log2(afsd_logp, "CALL %sBulkStatus FAILURE code 0x%x",
1983 inlinebulk ? "Inline" : "", code);
1985 osi_Log1(afsd_logp, "CALL %sBulkStatus SUCCESS", inlinebulk ? "Inline" : "");
1987 /* may as well quit on an error, since we're not going to do
1988 * much better on the next immediate call, either.
1991 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
1995 /* otherwise, we should do the merges */
1996 for (i = 0; i<filesThisCall; i++) {
1998 tfid.cell = dscp->fid.cell;
1999 tfid.volume = bb.fids[j].Volume;
2000 tfid.vnode = bb.fids[j].Vnode;
2001 tfid.unique = bb.fids[j].Unique;
2002 code = cm_GetSCache(&tfid, &scp, userp, reqp);
2006 /* otherwise, if this entry has no callback info,
2009 lock_ObtainMutex(&scp->mx);
2010 /* now, we have to be extra paranoid on merging in this
2011 * information, since we didn't use cm_SyncOp before
2012 * starting the fetch to make sure that no bad races
2013 * were occurring. Specifically, we need to make sure
2014 * we don't obliterate any newer information in the
2015 * vnode than have here.
2017 * Right now, be pretty conservative: if there's a
2018 * callback or a pending call, skip it.
2020 if ((scp->cbServerp == NULL || (scp->flags & CM_SCACHEFLAG_EACCESS))
2022 (CM_SCACHEFLAG_FETCHING
2023 | CM_SCACHEFLAG_STORING
2024 | CM_SCACHEFLAG_SIZESTORING))) {
2025 cm_EndCallbackGrantingCall(scp, &cbReq,
2027 CM_CALLBACK_MAINTAINCOUNT);
2028 cm_MergeStatus(scp, &bb.stats[j], &volSync, userp, 0);
2030 lock_ReleaseMutex(&scp->mx);
2031 cm_ReleaseSCache(scp);
2032 } /* all files in the response */
2033 /* now tell it to drop the count,
2034 * after doing the vnode processing above */
2035 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2037 filex += filesThisCall;
2038 } /* while there are still more files to process */
2039 lock_ObtainMutex(&dscp->mx);
2042 /* If we did the InlineBulk RPC pull out the return code */
2044 if ((&bb.stats[0])->errorCode) {
2045 cm_Analyze(NULL /*connp was released by the previous cm_Analyze */,
2046 userp, reqp, &dscp->fid, &volSync, NULL, NULL, (&bb.stats[0])->errorCode);
2047 code = cm_MapRPCError((&bb.stats[0])->errorCode, reqp);
2055 osi_Log1(afsd_logp, "END cm_TryBulkStat code = 0x%x", code);
2059 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
2063 /* initialize store back mask as inexpensive local variable */
2065 memset(statusp, 0, sizeof(AFSStoreStatus));
2067 /* copy out queued info from scache first, if scp passed in */
2069 if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
2070 statusp->ClientModTime = scp->clientModTime;
2071 mask |= AFS_SETMODTIME;
2072 scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
2077 /* now add in our locally generated request */
2078 if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
2079 statusp->ClientModTime = attrp->clientModTime;
2080 mask |= AFS_SETMODTIME;
2082 if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
2083 statusp->UnixModeBits = attrp->unixModeBits;
2084 mask |= AFS_SETMODE;
2086 if (attrp->mask & CM_ATTRMASK_OWNER) {
2087 statusp->Owner = attrp->owner;
2088 mask |= AFS_SETOWNER;
2090 if (attrp->mask & CM_ATTRMASK_GROUP) {
2091 statusp->Group = attrp->group;
2092 mask |= AFS_SETGROUP;
2095 statusp->Mask = mask;
2098 /* set the file size, and make sure that all relevant buffers have been
2099 * truncated. Ensure that any partially truncated buffers have been zeroed
2100 * to the end of the buffer.
2102 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
2108 /* start by locking out buffer creation */
2109 lock_ObtainWrite(&scp->bufCreateLock);
2111 /* verify that this is a file, not a dir or a symlink */
2112 lock_ObtainMutex(&scp->mx);
2113 code = cm_SyncOp(scp, NULL, userp, reqp, 0,
2114 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2117 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2119 if (scp->fileType != CM_SCACHETYPE_FILE) {
2120 code = CM_ERROR_ISDIR;
2125 if (LargeIntegerLessThan(*sizep, scp->length))
2130 lock_ReleaseMutex(&scp->mx);
2132 /* can't hold scp->mx lock here, since we may wait for a storeback to
2133 * finish if the buffer package is cleaning a buffer by storing it to
2137 buf_Truncate(scp, userp, reqp, sizep);
2139 /* now ensure that file length is short enough, and update truncPos */
2140 lock_ObtainMutex(&scp->mx);
2142 /* make sure we have a callback (so we have the right value for the
2143 * length), and wait for it to be safe to do a truncate.
2145 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
2146 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2147 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2151 if (LargeIntegerLessThan(*sizep, scp->length)) {
2152 /* a real truncation. If truncPos is not set yet, or is bigger
2153 * than where we're truncating the file, set truncPos to this
2158 if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
2159 || LargeIntegerLessThan(*sizep, scp->length)) {
2161 scp->truncPos = *sizep;
2162 scp->mask |= CM_SCACHEMASK_TRUNCPOS;
2164 /* in either case, the new file size has been changed */
2165 scp->length = *sizep;
2166 scp->mask |= CM_SCACHEMASK_LENGTH;
2168 else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
2169 /* really extending the file */
2170 scp->length = *sizep;
2171 scp->mask |= CM_SCACHEMASK_LENGTH;
2174 /* done successfully */
2177 cm_SyncOpDone(scp, NULL,
2178 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2179 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2182 lock_ReleaseMutex(&scp->mx);
2183 lock_ReleaseWrite(&scp->bufCreateLock);
2188 /* set the file size or other attributes (but not both at once) */
2189 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
2193 AFSFetchStatus afsOutStatus;
2197 AFSStoreStatus afsInStatus;
2198 struct rx_connection * callp;
2200 /* handle file length setting */
2201 if (attrp->mask & CM_ATTRMASK_LENGTH)
2202 return cm_SetLength(scp, &attrp->length, userp, reqp);
2204 lock_ObtainMutex(&scp->mx);
2205 /* otherwise, we have to make an RPC to get the status */
2206 code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
2208 lock_ReleaseMutex(&scp->mx);
2212 /* make the attr structure */
2213 cm_StatusFromAttr(&afsInStatus, scp, attrp);
2215 tfid.Volume = scp->fid.volume;
2216 tfid.Vnode = scp->fid.vnode;
2217 tfid.Unique = scp->fid.unique;
2220 /* now make the RPC */
2221 osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%p", scp);
2223 code = cm_Conn(&scp->fid, userp, reqp, &connp);
2227 callp = cm_GetRxConn(connp);
2228 code = RXAFS_StoreStatus(callp, &tfid,
2229 &afsInStatus, &afsOutStatus, &volSync);
2230 rx_PutConnection(callp);
2232 } while (cm_Analyze(connp, userp, reqp,
2233 &scp->fid, &volSync, NULL, NULL, code));
2234 code = cm_MapRPCError(code, reqp);
2237 osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
2239 osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
2241 lock_ObtainMutex(&scp->mx);
2242 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
2244 cm_MergeStatus(scp, &afsOutStatus, &volSync, userp,
2245 CM_MERGEFLAG_FORCE);
2247 /* if we're changing the mode bits, discard the ACL cache,
2248 * since we changed the mode bits.
2250 if (afsInStatus.Mask & AFS_SETMODE) cm_FreeAllACLEnts(scp);
2251 lock_ReleaseMutex(&scp->mx);
2255 long cm_Create(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2256 cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
2261 cm_callbackRequest_t cbReq;
2266 AFSStoreStatus inStatus;
2267 AFSFetchStatus updatedDirStatus;
2268 AFSFetchStatus newFileStatus;
2269 AFSCallBack newFileCallback;
2271 struct rx_connection * callp;
2273 /* can't create names with @sys in them; must expand it manually first.
2274 * return "invalid request" if they try.
2276 if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2277 return CM_ERROR_ATSYS;
2280 /* before starting the RPC, mark that we're changing the file data, so
2281 * that someone who does a chmod will know to wait until our call
2284 lock_ObtainMutex(&dscp->mx);
2285 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2287 cm_StartCallbackGrantingCall(NULL, &cbReq);
2289 lock_ReleaseMutex(&dscp->mx);
2295 cm_StatusFromAttr(&inStatus, NULL, attrp);
2297 /* try the RPC now */
2298 osi_Log1(afsd_logp, "CALL CreateFile scp 0x%p", dscp);
2300 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2304 dirAFSFid.Volume = dscp->fid.volume;
2305 dirAFSFid.Vnode = dscp->fid.vnode;
2306 dirAFSFid.Unique = dscp->fid.unique;
2308 callp = cm_GetRxConn(connp);
2309 code = RXAFS_CreateFile(connp->callp, &dirAFSFid, namep,
2310 &inStatus, &newAFSFid, &newFileStatus,
2311 &updatedDirStatus, &newFileCallback,
2313 rx_PutConnection(callp);
2315 } while (cm_Analyze(connp, userp, reqp,
2316 &dscp->fid, &volSync, NULL, &cbReq, code));
2317 code = cm_MapRPCError(code, reqp);
2320 osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
2322 osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
2324 lock_ObtainMutex(&dscp->mx);
2325 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2327 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2329 lock_ReleaseMutex(&dscp->mx);
2331 /* now try to create the file's entry, too, but be careful to
2332 * make sure that we don't merge in old info. Since we weren't locking
2333 * out any requests during the file's creation, we may have pretty old
2337 newFid.cell = dscp->fid.cell;
2338 newFid.volume = dscp->fid.volume;
2339 newFid.vnode = newAFSFid.Vnode;
2340 newFid.unique = newAFSFid.Unique;
2341 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2343 lock_ObtainMutex(&scp->mx);
2344 scp->creator = userp; /* remember who created it */
2345 if (!cm_HaveCallback(scp)) {
2346 cm_MergeStatus(scp, &newFileStatus, &volSync,
2348 cm_EndCallbackGrantingCall(scp, &cbReq,
2349 &newFileCallback, 0);
2352 lock_ReleaseMutex(&scp->mx);
2357 /* make sure we end things properly */
2359 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2364 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
2368 lock_ObtainWrite(&scp->bufCreateLock);
2369 code = buf_CleanVnode(scp, userp, reqp);
2370 lock_ReleaseWrite(&scp->bufCreateLock);
2372 lock_ObtainMutex(&scp->mx);
2374 if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
2375 | CM_SCACHEMASK_CLIENTMODTIME
2376 | CM_SCACHEMASK_LENGTH))
2377 code = cm_StoreMini(scp, userp, reqp);
2379 if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) {
2380 code = (scp->flags & CM_SCACHEFLAG_OVERQUOTA) ? CM_ERROR_QUOTA : CM_ERROR_SPACE;
2381 scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE);
2384 lock_ReleaseMutex(&scp->mx);
2389 long cm_MakeDir(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2390 cm_user_t *userp, cm_req_t *reqp)
2395 cm_callbackRequest_t cbReq;
2400 AFSStoreStatus inStatus;
2401 AFSFetchStatus updatedDirStatus;
2402 AFSFetchStatus newDirStatus;
2403 AFSCallBack newDirCallback;
2405 struct rx_connection * callp;
2407 /* can't create names with @sys in them; must expand it manually first.
2408 * return "invalid request" if they try.
2410 if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2411 return CM_ERROR_ATSYS;
2414 /* before starting the RPC, mark that we're changing the directory
2415 * data, so that someone who does a chmod on the dir will wait until
2416 * our call completes.
2418 lock_ObtainMutex(&dscp->mx);
2419 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2421 cm_StartCallbackGrantingCall(NULL, &cbReq);
2423 lock_ReleaseMutex(&dscp->mx);
2429 cm_StatusFromAttr(&inStatus, NULL, attrp);
2431 /* try the RPC now */
2432 osi_Log1(afsd_logp, "CALL MakeDir scp 0x%p", dscp);
2434 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2438 dirAFSFid.Volume = dscp->fid.volume;
2439 dirAFSFid.Vnode = dscp->fid.vnode;
2440 dirAFSFid.Unique = dscp->fid.unique;
2442 callp = cm_GetRxConn(connp);
2443 code = RXAFS_MakeDir(connp->callp, &dirAFSFid, namep,
2444 &inStatus, &newAFSFid, &newDirStatus,
2445 &updatedDirStatus, &newDirCallback,
2447 rx_PutConnection(callp);
2449 } while (cm_Analyze(connp, userp, reqp,
2450 &dscp->fid, &volSync, NULL, &cbReq, code));
2451 code = cm_MapRPCError(code, reqp);
2454 osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
2456 osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
2458 lock_ObtainMutex(&dscp->mx);
2459 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2461 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2463 lock_ReleaseMutex(&dscp->mx);
2465 /* now try to create the new dir's entry, too, but be careful to
2466 * make sure that we don't merge in old info. Since we weren't locking
2467 * out any requests during the file's creation, we may have pretty old
2471 newFid.cell = dscp->fid.cell;
2472 newFid.volume = dscp->fid.volume;
2473 newFid.vnode = newAFSFid.Vnode;
2474 newFid.unique = newAFSFid.Unique;
2475 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2477 lock_ObtainMutex(&scp->mx);
2478 if (!cm_HaveCallback(scp)) {
2479 cm_MergeStatus(scp, &newDirStatus, &volSync,
2481 cm_EndCallbackGrantingCall(scp, &cbReq,
2482 &newDirCallback, 0);
2485 lock_ReleaseMutex(&scp->mx);
2486 cm_ReleaseSCache(scp);
2490 /* make sure we end things properly */
2492 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2494 /* and return error code */
2498 long cm_Link(cm_scache_t *dscp, char *namep, cm_scache_t *sscp, long flags,
2499 cm_user_t *userp, cm_req_t *reqp)
2504 AFSFid existingAFSFid;
2505 AFSFetchStatus updatedDirStatus;
2506 AFSFetchStatus newLinkStatus;
2508 struct rx_connection * callp;
2510 if (dscp->fid.cell != sscp->fid.cell ||
2511 dscp->fid.volume != sscp->fid.volume) {
2512 return CM_ERROR_CROSSDEVLINK;
2515 lock_ObtainMutex(&dscp->mx);
2516 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2517 lock_ReleaseMutex(&dscp->mx);
2522 /* try the RPC now */
2523 osi_Log1(afsd_logp, "CALL Link scp 0x%p", dscp);
2525 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2528 dirAFSFid.Volume = dscp->fid.volume;
2529 dirAFSFid.Vnode = dscp->fid.vnode;
2530 dirAFSFid.Unique = dscp->fid.unique;
2532 existingAFSFid.Volume = sscp->fid.volume;
2533 existingAFSFid.Vnode = sscp->fid.vnode;
2534 existingAFSFid.Unique = sscp->fid.unique;
2536 callp = cm_GetRxConn(connp);
2537 code = RXAFS_Link(callp, &dirAFSFid, namep, &existingAFSFid,
2538 &newLinkStatus, &updatedDirStatus, &volSync);
2539 rx_PutConnection(callp);
2540 osi_Log1(smb_logp," RXAFS_Link returns 0x%x", code);
2542 } while (cm_Analyze(connp, userp, reqp,
2543 &dscp->fid, &volSync, NULL, NULL, code));
2545 code = cm_MapRPCError(code, reqp);
2548 osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
2550 osi_Log0(afsd_logp, "CALL Link SUCCESS");
2552 lock_ObtainMutex(&dscp->mx);
2553 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2555 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2557 lock_ReleaseMutex(&dscp->mx);
2562 long cm_SymLink(cm_scache_t *dscp, char *namep, char *contentsp, long flags,
2563 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
2571 AFSStoreStatus inStatus;
2572 AFSFetchStatus updatedDirStatus;
2573 AFSFetchStatus newLinkStatus;
2575 struct rx_connection * callp;
2577 /* before starting the RPC, mark that we're changing the directory data,
2578 * so that someone who does a chmod on the dir will wait until our
2581 lock_ObtainMutex(&dscp->mx);
2582 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2583 lock_ReleaseMutex(&dscp->mx);
2588 cm_StatusFromAttr(&inStatus, NULL, attrp);
2590 /* try the RPC now */
2591 osi_Log1(afsd_logp, "CALL Symlink scp 0x%p", dscp);
2593 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2597 dirAFSFid.Volume = dscp->fid.volume;
2598 dirAFSFid.Vnode = dscp->fid.vnode;
2599 dirAFSFid.Unique = dscp->fid.unique;
2601 callp = cm_GetRxConn(connp);
2602 code = RXAFS_Symlink(callp, &dirAFSFid, namep, contentsp,
2603 &inStatus, &newAFSFid, &newLinkStatus,
2604 &updatedDirStatus, &volSync);
2605 rx_PutConnection(callp);
2607 } while (cm_Analyze(connp, userp, reqp,
2608 &dscp->fid, &volSync, NULL, NULL, code));
2609 code = cm_MapRPCError(code, reqp);
2612 osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
2614 osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
2616 lock_ObtainMutex(&dscp->mx);
2617 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2619 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2621 lock_ReleaseMutex(&dscp->mx);
2623 /* now try to create the new dir's entry, too, but be careful to
2624 * make sure that we don't merge in old info. Since we weren't locking
2625 * out any requests during the file's creation, we may have pretty old
2629 newFid.cell = dscp->fid.cell;
2630 newFid.volume = dscp->fid.volume;
2631 newFid.vnode = newAFSFid.Vnode;
2632 newFid.unique = newAFSFid.Unique;
2633 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2635 lock_ObtainMutex(&scp->mx);
2636 if (!cm_HaveCallback(scp)) {
2637 cm_MergeStatus(scp, &newLinkStatus, &volSync,
2640 lock_ReleaseMutex(&scp->mx);
2641 cm_ReleaseSCache(scp);
2645 /* and return error code */
2649 long cm_RemoveDir(cm_scache_t *dscp, char *namep, cm_user_t *userp,
2656 AFSFetchStatus updatedDirStatus;
2658 struct rx_connection * callp;
2660 /* before starting the RPC, mark that we're changing the directory data,
2661 * so that someone who does a chmod on the dir will wait until our
2664 lock_ObtainMutex(&dscp->mx);
2665 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2666 lock_ReleaseMutex(&dscp->mx);
2672 /* try the RPC now */
2673 osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%p", dscp);
2675 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2679 dirAFSFid.Volume = dscp->fid.volume;
2680 dirAFSFid.Vnode = dscp->fid.vnode;
2681 dirAFSFid.Unique = dscp->fid.unique;
2683 callp = cm_GetRxConn(connp);
2684 code = RXAFS_RemoveDir(callp, &dirAFSFid, namep,
2685 &updatedDirStatus, &volSync);
2686 rx_PutConnection(callp);
2688 } while (cm_Analyze(connp, userp, reqp,
2689 &dscp->fid, &volSync, NULL, NULL, code));
2690 code = cm_MapRPCErrorRmdir(code, reqp);
2693 osi_Log1(afsd_logp, "CALL RemoveDir FAILURE, code 0x%x", code);
2695 osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
2697 lock_ObtainMutex(&dscp->mx);
2698 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2700 cm_dnlcRemove(dscp, namep);
2701 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2703 lock_ReleaseMutex(&dscp->mx);
2705 /* and return error code */
2709 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
2711 /* grab mutex on contents */
2712 lock_ObtainMutex(&scp->mx);
2714 /* reset the prefetch info */
2715 scp->prefetch.base.LowPart = 0; /* base */
2716 scp->prefetch.base.HighPart = 0;
2717 scp->prefetch.end.LowPart = 0; /* and end */
2718 scp->prefetch.end.HighPart = 0;
2720 /* release mutex on contents */
2721 lock_ReleaseMutex(&scp->mx);
2727 long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
2728 char *newNamep, cm_user_t *userp, cm_req_t *reqp)
2732 AFSFid oldDirAFSFid;
2733 AFSFid newDirAFSFid;
2735 AFSFetchStatus updatedOldDirStatus;
2736 AFSFetchStatus updatedNewDirStatus;
2739 struct rx_connection * callp;
2741 /* before starting the RPC, mark that we're changing the directory data,
2742 * so that someone who does a chmod on the dir will wait until our call
2743 * completes. We do this in vnode order so that we don't deadlock,
2744 * which makes the code a little verbose.
2746 if (oldDscp == newDscp) {
2747 /* check for identical names */
2748 if (strcmp(oldNamep, newNamep) == 0)
2749 return CM_ERROR_RENAME_IDENTICAL;
2752 lock_ObtainMutex(&oldDscp->mx);
2753 cm_dnlcRemove(oldDscp, oldNamep);
2754 cm_dnlcRemove(oldDscp, newNamep);
2755 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2756 CM_SCACHESYNC_STOREDATA);
2757 lock_ReleaseMutex(&oldDscp->mx);
2760 /* two distinct dir vnodes */
2762 if (oldDscp->fid.cell != newDscp->fid.cell ||
2763 oldDscp->fid.volume != newDscp->fid.volume)
2764 return CM_ERROR_CROSSDEVLINK;
2766 /* shouldn't happen that we have distinct vnodes for two
2767 * different files, but could due to deliberate attack, or
2768 * stale info. Avoid deadlocks and quit now.
2770 if (oldDscp->fid.vnode == newDscp->fid.vnode)
2771 return CM_ERROR_CROSSDEVLINK;
2773 if (oldDscp->fid.vnode < newDscp->fid.vnode) {
2774 lock_ObtainMutex(&oldDscp->mx);
2775 cm_dnlcRemove(oldDscp, oldNamep);
2776 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2777 CM_SCACHESYNC_STOREDATA);
2778 lock_ReleaseMutex(&oldDscp->mx);
2780 lock_ObtainMutex(&newDscp->mx);
2781 cm_dnlcRemove(newDscp, newNamep);
2782 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2783 CM_SCACHESYNC_STOREDATA);
2784 lock_ReleaseMutex(&newDscp->mx);
2786 /* cleanup first one */
2787 lock_ObtainMutex(&newDscp->mx);
2788 cm_SyncOpDone(oldDscp, NULL,
2789 CM_SCACHESYNC_STOREDATA);
2790 lock_ReleaseMutex(&oldDscp->mx);
2795 /* lock the new vnode entry first */
2796 lock_ObtainMutex(&newDscp->mx);
2797 cm_dnlcRemove(newDscp, newNamep);
2798 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2799 CM_SCACHESYNC_STOREDATA);
2800 lock_ReleaseMutex(&newDscp->mx);
2802 lock_ObtainMutex(&oldDscp->mx);
2803 cm_dnlcRemove(oldDscp, oldNamep);
2804 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2805 CM_SCACHESYNC_STOREDATA);
2806 lock_ReleaseMutex(&oldDscp->mx);
2808 /* cleanup first one */
2809 lock_ObtainMutex(&newDscp->mx);
2810 cm_SyncOpDone(newDscp, NULL,
2811 CM_SCACHESYNC_STOREDATA);
2812 lock_ReleaseMutex(&newDscp->mx);
2816 } /* two distinct vnodes */
2823 /* try the RPC now */
2824 osi_Log2(afsd_logp, "CALL Rename old scp 0x%p new scp 0x%p",
2827 code = cm_Conn(&oldDscp->fid, userp, reqp, &connp);
2831 oldDirAFSFid.Volume = oldDscp->fid.volume;
2832 oldDirAFSFid.Vnode = oldDscp->fid.vnode;
2833 oldDirAFSFid.Unique = oldDscp->fid.unique;
2834 newDirAFSFid.Volume = newDscp->fid.volume;
2835 newDirAFSFid.Vnode = newDscp->fid.vnode;
2836 newDirAFSFid.Unique = newDscp->fid.unique;
2838 callp = cm_GetRxConn(connp);
2839 code = RXAFS_Rename(callp, &oldDirAFSFid, oldNamep,
2840 &newDirAFSFid, newNamep,
2841 &updatedOldDirStatus, &updatedNewDirStatus,
2843 rx_PutConnection(callp);
2845 } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
2846 &volSync, NULL, NULL, code));
2847 code = cm_MapRPCError(code, reqp);
2850 osi_Log1(afsd_logp, "CALL Rename FAILURE, code 0x%x", code);
2852 osi_Log0(afsd_logp, "CALL Rename SUCCESS");
2854 /* update the individual stat cache entries for the directories */
2855 lock_ObtainMutex(&oldDscp->mx);
2856 cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
2858 cm_MergeStatus(oldDscp, &updatedOldDirStatus, &volSync,
2861 lock_ReleaseMutex(&oldDscp->mx);
2863 /* and update it for the new one, too, if necessary */
2865 lock_ObtainMutex(&newDscp->mx);
2866 cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
2868 cm_MergeStatus(newDscp, &updatedNewDirStatus, &volSync,
2871 lock_ReleaseMutex(&newDscp->mx);
2874 /* and return error code */
2878 /* Byte range locks:
2880 The OpenAFS Windows client has to fake byte range locks given no
2881 server side support for such locks. This is implemented as keyed
2882 byte range locks on the cache manager.
2884 Keyed byte range locks:
2886 Each cm_scache_t structure keeps track of a list of keyed locks.
2887 The key for a lock identifies an owner of a set of locks (referred
2888 to as a client). Each key is represented by a value. The set of
2889 key values used within a specific cm_scache_t structure form a
2890 namespace that has a scope of just that cm_scache_t structure. The
2891 same key value can be used with another cm_scache_t structure and
2892 correspond to a completely different client. However it is
2893 advantageous for the SMB or IFS layer to make sure that there is a
2894 1-1 mapping between client and keys over all cm_scache_t objects.
2896 Assume a client C has key Key(C) (although, since the scope of the
2897 key is a cm_scache_t, the key can be Key(C,S), where S is the
2898 cm_scache_t. But assume a 1-1 relation between keys and clients).
2899 A byte range (O,+L) denotes byte addresses (O) through (O+L-1)
2900 inclusive (a.k.a. [O,O+L-1]). The function Key(x) is implemented
2901 through cm_generateKey() function for both SMB and IFS.
2903 The list of locks for a cm_scache_t object S is maintained in
2904 S->fileLocks. The cache manager will set a lock on the AFS file
2905 server in order to assert the locks in S->fileLocks. If only
2906 shared locks are in place for S, then the cache manager will obtain
2907 a LockRead lock, while if there are any exclusive locks, it will
2908 obtain a LockWrite lock. If the exclusive locks are all released
2909 while the shared locks remain, then the cache manager will
2910 downgrade the lock from LockWrite to LockRead. Similarly, if an
2911 exclusive lock is obtained when only shared locks exist, then the
2912 cache manager will try to upgrade the lock from LockRead to
2915 Each lock L owned by client C maintains a key L->key such that
2916 L->key == Key(C), the effective range defined by L->LOffset and
2917 L->LLength such that the range of bytes affected by the lock is
2918 (L->LOffset, +L->LLength), a type maintained in L->LockType which
2919 is either exclusive or shared.
2923 A lock exists iff it is in S->fileLocks for some cm_scache_t
2924 S. Existing locks are in one of the following states: ACTIVE,
2925 WAITLOCK, WAITUNLOCK, LOST, DELETED.
2927 The following sections describe each lock and the associated
2930 1. ACTIVE: A lock L is ACTIVE iff the cache manager has asserted
2931 the lock with the AFS file server. This type of lock can be
2932 exercised by a client to read or write to the locked region (as
2935 1.1 ACTIVE->LOST: When the AFS file server fails to extend a
2936 server lock that was required to assert the lock. Before
2937 marking the lock as lost, the cache manager checks if the file
2938 has changed on the server. If the file has not changed, then
2939 the cache manager will attempt to obtain a new server lock
2940 that is sufficient to assert the client side locks for the
2941 file. If any of these fail, the lock is marked as LOST.
2942 Otherwise, it is left as ACTIVE.
2944 1.2 ACTIVE->DELETED: Lock is released.
2946 2. WAITLOCK: A lock is in a WAITLOCK state if the cache manager
2947 grants the lock but the lock is yet to be asserted with the AFS
2948 file server. Once the file server grants the lock, the state
2949 will transition to an ACTIVE lock.
2951 2.1 WAITLOCK->ACTIVE: The server granted the lock.
2953 2.2 WAITLOCK->DELETED: Lock is abandoned, or timed out during
2956 2.3 WAITLOCK->LOST: One or more locks from this client were
2957 marked as LOST. No further locks will be granted to this
2958 client until all lost locks are removed.
2960 3. WAITUNLOCK: A lock is in a WAITUNLOCK state if the cache manager
2961 receives a request for a lock that conflicts with an existing
2962 ACTIVE or WAITLOCK lock. The lock will be placed in the queue
2963 and will be granted at such time the conflicting locks are
2964 removed, at which point the state will transition to either
2967 3.1 WAITUNLOCK->ACTIVE: The conflicting lock was removed. The
2968 current serverLock is sufficient to assert this lock, or a
2969 sufficient serverLock is obtained.
2971 3.2 WAITUNLOCK->WAITLOCK: The conflicting lock was removed,
2972 however the required serverLock is yet to be asserted with the
2975 3.3 WAITUNLOCK->DELETED: The lock is abandoned, timed out or
2978 3.5 WAITUNLOCK->LOST: One or more locks from this client were
2979 marked as LOST. No further locks will be granted to this
2980 client until all lost locks are removed.
2982 4. LOST: A lock L is LOST if the server lock that was required to
2983 assert the lock could not be obtained or if it could not be
2984 extended, or if other locks by the same client were LOST.
2985 Essentially, once a lock is LOST, the contract between the cache
2986 manager and that specific client is no longer valid.
2988 The cache manager rechecks the server lock once every minute and
2989 extends it as appropriate. If this is not done for 5 minutes,
2990 the AFS file server will release the lock (the 5 minute timeout
2991 is based on current file server code and is fairly arbitrary).
2992 Once released, the lock cannot be re-obtained without verifying
2993 that the contents of the file hasn't been modified since the
2994 time the lock was released. Re-obtaining the lock without
2995 verifying this may lead to data corruption. If the lock can not
2996 be obtained safely, then all active locks for the cm_scache_t
2999 4.1 LOST->DELETED: The lock is released.
3001 5. DELETED: The lock is no longer relevant. Eventually, it will
3002 get removed from the cm_scache_t. In the meantime, it will be
3003 treated as if it does not exist.
3005 5.1 DELETED->not exist: The lock is removed from the
3008 The following are classifications of locks based on their state.
3010 6* A lock L is ACCEPTED if it is ACTIVE or WAITLOCK. These locks
3011 have been accepted by the cache manager, but may or may not have
3012 been granted back to the client.
3014 7* A lock L is QUEUED if it is ACTIVE, WAITLOCK or WAITUNLOCK.
3016 8* A lock L is WAITING if it is WAITLOCK or WAITUNLOCK.
3020 A client C can READ range (Offset,+Length) of a file represented by
3021 cm_scache_t S iff (1):
3023 1. for all _a_ in (Offset,+Length), all of the following is true:
3025 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3026 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3029 1.2 For each LOST lock L in S->fileLocks such that _a_ in
3030 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3033 (When locks are lost on an cm_scache_t, all locks are lost. By
3034 4.2 (below), if there is an exclusive LOST lock, then there
3035 can't be any overlapping ACTIVE locks.)
3037 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
3039 2. for all _a_ in (Offset,+Length), one of the following is true:
3041 2.1 Byte _a_ of S is unowned (as specified in 1.1) AND there
3042 does not exist a LOST lock L such that _a_ in
3043 (L->LOffset,+L->LLength).
3045 2.2 Byte _a_ of S is owned by C under lock L (as specified in
3046 1.2) AND L->LockType is exclusive.
3048 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
3050 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
3053 3.1 If L->LockType is exclusive then there does NOT exist a
3054 ACCEPTED lock M in S->fileLocks such that _a_ in
3055 (M->LOffset,+M->LLength).
3057 (If we count all QUEUED locks then we hit cases such as
3058 cascading waiting locks where the locks later on in the queue
3059 can be granted without compromising file integrity. On the
3060 other hand if only ACCEPTED locks are considered, then locks
3061 that were received earlier may end up waiting for locks that
3062 were received later to be unlocked. The choice of ACCEPTED
3063 locks was made to mimic the Windows byte range lock
3066 3.2 If L->LockType is shared then for each ACCEPTED lock M in
3067 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
3068 M->LockType is shared.
3070 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
3072 4.1 M->key != Key(C)
3074 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
3075 and (M->LOffset,+M->LLength) do not intersect.
3077 (Note: If a client loses a lock, it loses all locks.
3078 Subsequently, it will not be allowed to obtain any more locks
3079 until all existing LOST locks that belong to the client are
3080 released. Once all locks are released by a single client,
3081 there exists no further contract between the client and AFS
3082 about the contents of the file, hence the client can then
3083 proceed to obtain new locks and establish a new contract.
3085 This doesn't quite work as you think it should, because most
3086 applications aren't built to deal with losing locks they
3087 thought they once had. For now, we don't have a good
3088 solution to lost locks.
3090 Also, for consistency reasons, we have to hold off on
3091 granting locks that overlap exclusive LOST locks.)
3093 A client C can only unlock locks L in S->fileLocks which have
3096 The representation and invariants are as follows:
3098 - Each cm_scache_t structure keeps:
3100 - A queue of byte-range locks (cm_scache_t::fileLocks) which
3101 are of type cm_file_lock_t.
3103 - A record of the highest server-side lock that has been
3104 obtained for this object (cm_scache_t::serverLock), which is
3105 one of (-1), LockRead, LockWrite.
3107 - A count of ACCEPTED exclusive and shared locks that are in the
3108 queue (cm_scache_t::sharedLocks and
3109 cm_scache_t::exclusiveLocks)
3111 - Each cm_file_lock_t structure keeps:
3113 - The type of lock (cm_file_lock_t::LockType)
3115 - The key associated with the lock (cm_file_lock_t::key)
3117 - The offset and length of the lock (cm_file_lock_t::LOffset
3118 and cm_file_lock_t::LLength)
3120 - The state of the lock.
3122 - Time of issuance or last successful extension
3124 Semantic invariants:
3126 I1. The number of ACCEPTED locks in S->fileLocks are
3127 (S->sharedLocks + S->exclusiveLocks)
3129 External invariants:
3131 I3. S->serverLock is the lock that we have asserted with the
3132 AFS file server for this cm_scache_t.
3134 I4. S->serverLock == LockRead iff there is at least one ACTIVE
3135 shared lock, but no ACTIVE exclusive locks.
3137 I5. S->serverLock == LockWrite iff there is at least one ACTIVE
3140 I6. If L is a LOST lock, then for each lock M in S->fileLocks,
3141 M->key == L->key IMPLIES M is LOST or DELETED.
3146 #define IS_LOCK_ACTIVE(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == 0)
3148 #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)
3150 #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)
3152 #define IS_LOCK_LOST(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_LOST)
3154 #define IS_LOCK_DELETED(lockp) (((lockp)->flags & CM_FILELOCK_FLAG_DELETED) == CM_FILELOCK_FLAG_DELETED)
3157 #define IS_LOCK_ACCEPTED(lockp) (IS_LOCK_ACTIVE(lockp) || IS_LOCK_WAITLOCK(lockp))
3160 #define IS_LOCK_CLIENTONLY(lockp) ((((lockp)->scp->flags & CM_SCACHEFLAG_RO) == CM_SCACHEFLAG_RO) || (((lockp)->flags & CM_FILELOCK_FLAG_CLIENTONLY) == CM_FILELOCK_FLAG_CLIENTONLY))
3163 #define INTERSECT_RANGE(r1,r2) (((r2).offset+(r2).length) > (r1).offset && ((r1).offset +(r1).length) > (r2).offset)
3166 #define CONTAINS_RANGE(r1,r2) (((r2).offset+(r2).length) <= ((r1).offset+(r1).length) && (r1).offset <= (r2).offset)
3168 #if defined(VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS) && !defined(LOCK_TESTING)
3169 #define SCP_SUPPORTS_BRLOCKS(scp) ((scp)->cbServerp && ((scp)->cbServerp->capabilities & VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS))
3171 #define SCP_SUPPORTS_BRLOCKS(scp) (1)
3174 #define SERVERLOCKS_ENABLED(scp) (!((scp)->flags & CM_SCACHEFLAG_RO) && cm_enableServerLocks && SCP_SUPPORTS_BRLOCKS(scp))
3176 static void cm_LockRangeSubtract(cm_range_t * pos, const cm_range_t * neg)
3178 afs_int64 int_begin;
3181 int_begin = MAX(pos->offset, neg->offset);
3182 int_end = MIN(pos->offset+pos->length, neg->offset+neg->length);
3184 if (int_begin < int_end) {
3185 if (int_begin == pos->offset) {
3186 pos->length = pos->offset + pos->length - int_end;
3187 pos->offset = int_end;
3188 } else if (int_end == pos->offset + pos->length) {
3189 pos->length = int_begin - pos->offset;
3192 /* We only subtract ranges if the resulting range is
3193 contiguous. If we try to support non-contigous ranges, we
3194 aren't actually improving performance. */
3198 /* Called with scp->mx held. Returns 0 if all is clear to read the
3199 specified range by the client identified by key.
3201 long cm_LockCheckRead(cm_scache_t *scp,
3202 LARGE_INTEGER LOffset,
3203 LARGE_INTEGER LLength,
3206 #ifndef ADVISORY_LOCKS
3208 cm_file_lock_t *fileLock;
3212 int substract_ranges = FALSE;
3214 range.offset = LOffset.QuadPart;
3215 range.length = LLength.QuadPart;
3219 1. for all _a_ in (Offset,+Length), all of the following is true:
3221 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3222 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3225 1.2 For each LOST lock L in S->fileLocks such that _a_ in
3226 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3231 lock_ObtainRead(&cm_scacheLock);
3233 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
3235 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
3237 if (INTERSECT_RANGE(range, fileLock->range)) {
3238 if (IS_LOCK_ACTIVE(fileLock)) {
3239 if (fileLock->key == key) {
3241 /* If there is an active lock for this client, it
3242 is safe to substract ranges.*/
3243 cm_LockRangeSubtract(&range, &fileLock->range);
3244 substract_ranges = TRUE;
3246 if (fileLock->lockType != LockRead) {
3247 code = CM_ERROR_LOCK_CONFLICT;
3251 /* even if the entire range is locked for reading,
3252 we still can't grant the lock at this point
3253 because the client may have lost locks. That
3254 is, unless we have already seen an active lock
3255 belonging to the client, in which case there
3256 can't be any lost locks for this client. */
3257 if (substract_ranges)
3258 cm_LockRangeSubtract(&range, &fileLock->range);
3260 } else if (IS_LOCK_LOST(fileLock) &&
3261 (fileLock->key == key || fileLock->lockType == LockWrite)) {
3262 code = CM_ERROR_BADFD;
3268 lock_ReleaseRead(&cm_scacheLock);
3270 osi_Log4(afsd_logp, "cm_LockCheckRead scp 0x%x offset %d length %d code 0x%x",
3271 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
3282 /* Called with scp->mx held. Returns 0 if all is clear to write the
3283 specified range by the client identified by key.
3285 long cm_LockCheckWrite(cm_scache_t *scp,
3286 LARGE_INTEGER LOffset,
3287 LARGE_INTEGER LLength,
3290 #ifndef ADVISORY_LOCKS
3292 cm_file_lock_t *fileLock;
3297 range.offset = LOffset.QuadPart;
3298 range.length = LLength.QuadPart;
3301 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
3303 2. for all _a_ in (Offset,+Length), one of the following is true:
3305 2.1 Byte _a_ of S is unowned AND there does not exist a LOST
3306 lock L such that _a_ in (L->LOffset,+L->LLength).
3308 2.2 Byte _a_ of S is owned by C under lock L AND L->LockType is
3312 lock_ObtainRead(&cm_scacheLock);
3314 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
3316 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
3318 if (INTERSECT_RANGE(range, fileLock->range)) {
3319 if (IS_LOCK_ACTIVE(fileLock)) {
3320 if (fileLock->key == key) {
3321 if (fileLock->lockType == LockWrite) {
3323 /* if there is an active lock for this client, it
3324 is safe to substract ranges */
3325 cm_LockRangeSubtract(&range, &fileLock->range);
3327 code = CM_ERROR_LOCK_CONFLICT;
3331 code = CM_ERROR_LOCK_CONFLICT;
3334 } else if (IS_LOCK_LOST(fileLock)) {
3335 code = CM_ERROR_BADFD;
3341 lock_ReleaseRead(&cm_scacheLock);
3343 osi_Log4(afsd_logp, "cm_LockCheckWrite scp 0x%x offset %d length %d code 0x%x",
3344 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
3356 static void cm_LockMarkSCacheLost(cm_scache_t * scp);
3358 /* Called with cm_scacheLock write locked */
3359 static cm_file_lock_t * cm_GetFileLock(void) {
3362 l = (cm_file_lock_t *) cm_freeFileLocks;
3364 osi_QRemove(&cm_freeFileLocks, &l->q);
3366 l = malloc(sizeof(cm_file_lock_t));
3370 memset(l, 0, sizeof(cm_file_lock_t));
3375 /* Called with cm_scacheLock write locked */
3376 static void cm_PutFileLock(cm_file_lock_t *l) {
3377 osi_QAdd(&cm_freeFileLocks, &l->q);
3380 /* called with scp->mx held. May release it during processing, but
3381 leaves it held on exit. */
3382 long cm_IntSetLock(cm_scache_t * scp, cm_user_t * userp, int lockType,
3388 struct rx_connection * callp;
3391 tfid.Volume = scp->fid.volume;
3392 tfid.Vnode = scp->fid.vnode;
3393 tfid.Unique = scp->fid.unique;
3396 osi_Log2(afsd_logp, "CALL SetLock scp 0x%p for lock %d", scp, lockType);
3398 lock_ReleaseMutex(&scp->mx);
3401 code = cm_Conn(&cfid, userp, reqp, &connp);
3405 callp = cm_GetRxConn(connp);
3406 code = RXAFS_SetLock(callp, &tfid, lockType,
3408 rx_PutConnection(callp);
3410 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
3413 code = cm_MapRPCError(code, reqp);
3415 osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
3417 osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
3420 lock_ObtainMutex(&scp->mx);
3425 /* called with scp->mx held. Releases it during processing */
3426 long cm_IntReleaseLock(cm_scache_t * scp, cm_user_t * userp,
3432 struct rx_connection * callp;
3435 tfid.Volume = scp->fid.volume;
3436 tfid.Vnode = scp->fid.vnode;
3437 tfid.Unique = scp->fid.unique;
3440 lock_ReleaseMutex(&scp->mx);
3442 osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
3445 code = cm_Conn(&cfid, userp, reqp, &connp);
3449 callp = cm_GetRxConn(connp);
3450 code = RXAFS_ReleaseLock(callp, &tfid, &volSync);
3451 rx_PutConnection(callp);
3453 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
3455 code = cm_MapRPCError(code, reqp);
3458 "CALL ReleaseLock FAILURE, code 0x%x", code);
3461 "CALL ReleaseLock SUCCESS");
3463 lock_ObtainMutex(&scp->mx);
3468 /* called with scp->mx held. May release it during processing, but
3469 will exit with lock held.
3473 - 0 if the user has permission to get the specified lock for the scp
3475 - CM_ERROR_NOACCESS if not
3477 Any other error from cm_SyncOp will be sent down untranslated.
3479 long cm_LockCheckPerms(cm_scache_t * scp,