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 #ifdef DEBUG_REFCOUNT
1260 long cm_LookupDbg(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
1261 cm_req_t *reqp, cm_scache_t **outpScpp, char * file, long line)
1263 long cm_Lookup(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
1264 cm_req_t *reqp, cm_scache_t **outpScpp)
1269 int sysNameIndex = 0;
1270 cm_scache_t *scp = NULL;
1272 #ifdef DEBUG_REFCOUNT
1273 afsi_log("%s:%d cm_Lookup dscp 0x%p ref %d", file, line, dscp, dscp->refCount, file, line);
1274 osi_Log2(afsd_logp, "cm_Lookup dscp 0x%p ref %d", dscp, dscp->refCount);
1277 if ( stricmp(namep,SMB_IOCTL_FILENAME_NOSLASH) == 0 ) {
1278 if (flags & CM_FLAG_CHECKPATH)
1279 return CM_ERROR_NOSUCHPATH;
1281 return CM_ERROR_NOSUCHFILE;
1284 for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
1285 code = cm_ExpandSysName(namep, tname, sizeof(tname), sysNameIndex);
1287 code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
1288 #ifdef DEBUG_REFCOUNT
1289 afsi_log("%s:%d cm_LookupInternal (1) code 0x%x dscp 0x%p ref %d scp 0x%p ref %d", file, line, code, dscp, dscp->refCount, scp, scp ? scp->refCount : 0);
1290 osi_Log3(afsd_logp, "cm_LookupInternal (1) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1298 cm_ReleaseSCache(scp);
1302 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1303 #ifdef DEBUG_REFCOUNT
1304 afsi_log("%s:%d cm_LookupInternal (2) code 0x%x dscp 0x%p ref %d scp 0x%p ref %d", file, line, code, dscp, dscp->refCount, scp, scp ? scp->refCount : 0);
1305 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1312 /* None of the possible sysName expansions could be found */
1313 if (flags & CM_FLAG_CHECKPATH)
1314 return CM_ERROR_NOSUCHPATH;
1316 return CM_ERROR_NOSUCHFILE;
1319 long cm_Unlink(cm_scache_t *dscp, char *namep, cm_user_t *userp, cm_req_t *reqp)
1325 AFSFetchStatus newDirStatus;
1327 struct rx_connection * callp;
1329 #ifdef AFS_FREELANCE_CLIENT
1330 if (cm_freelanceEnabled && dscp == cm_data.rootSCachep) {
1331 /* deleting a mount point from the root dir. */
1332 code = cm_FreelanceRemoveMount(namep);
1337 /* make sure we don't screw up the dir status during the merge */
1338 lock_ObtainMutex(&dscp->mx);
1339 sflags = CM_SCACHESYNC_STOREDATA;
1340 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1341 lock_ReleaseMutex(&dscp->mx);
1346 afsFid.Volume = dscp->fid.volume;
1347 afsFid.Vnode = dscp->fid.vnode;
1348 afsFid.Unique = dscp->fid.unique;
1350 osi_Log1(afsd_logp, "CALL RemoveFile scp 0x%p", dscp);
1352 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1356 callp = cm_GetRxConn(connp);
1357 code = RXAFS_RemoveFile(callp, &afsFid, namep,
1358 &newDirStatus, &volSync);
1359 rx_PutConnection(callp);
1361 } while (cm_Analyze(connp, userp, reqp, &dscp->fid, &volSync, NULL, NULL, code));
1362 code = cm_MapRPCError(code, reqp);
1365 osi_Log1(afsd_logp, "CALL RemoveFile FAILURE, code 0x%x", code);
1367 osi_Log0(afsd_logp, "CALL RemoveFile SUCCESS");
1369 lock_ObtainMutex(&dscp->mx);
1370 cm_dnlcRemove(dscp, namep);
1371 cm_SyncOpDone(dscp, NULL, sflags);
1373 cm_MergeStatus(dscp, &newDirStatus, &volSync, userp, 0);
1374 else if (code == CM_ERROR_NOSUCHFILE) {
1375 /* windows would not have allowed the request to delete the file
1376 * if it did not believe the file existed. therefore, we must
1377 * have an inconsistent view of the world.
1379 dscp->cbServerp = NULL;
1381 lock_ReleaseMutex(&dscp->mx);
1386 /* called with a locked vnode, and fills in the link info.
1387 * returns this the vnode still locked.
1389 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1396 lock_AssertMutex(&linkScp->mx);
1397 if (!linkScp->mountPointStringp[0]) {
1398 /* read the link data */
1399 lock_ReleaseMutex(&linkScp->mx);
1400 thyper.LowPart = thyper.HighPart = 0;
1401 code = buf_Get(linkScp, &thyper, &bufp);
1402 lock_ObtainMutex(&linkScp->mx);
1406 code = cm_SyncOp(linkScp, bufp, userp, reqp, 0,
1407 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1412 cm_SyncOpDone(linkScp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1414 if (cm_HaveBuffer(linkScp, bufp, 0))
1417 code = cm_GetBuffer(linkScp, bufp, NULL, userp, reqp);
1422 } /* while loop to get the data */
1424 /* now if we still have no link read in,
1425 * copy the data from the buffer */
1426 if ((temp = linkScp->length.LowPart) >= MOUNTPOINTLEN) {
1428 return CM_ERROR_TOOBIG;
1431 /* otherwise, it fits; make sure it is still null (could have
1432 * lost race with someone else referencing this link above),
1433 * and if so, copy in the data.
1435 if (!linkScp->mountPointStringp[0]) {
1436 strncpy(linkScp->mountPointStringp, bufp->datap, temp);
1437 linkScp->mountPointStringp[temp] = 0; /* null terminate */
1440 } /* don't have sym link contents cached */
1445 /* called with a held vnode and a path suffix, with the held vnode being a
1446 * symbolic link. Our goal is to generate a new path to interpret, and return
1447 * this new path in newSpaceBufferp. If the new vnode is relative to a dir
1448 * other than the directory containing the symbolic link, then the new root is
1449 * returned in *newRootScpp, otherwise a null is returned there.
1451 long cm_AssembleLink(cm_scache_t *linkScp, char *pathSuffixp,
1452 cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1453 cm_user_t *userp, cm_req_t *reqp)
1460 lock_ObtainMutex(&linkScp->mx);
1461 code = cm_HandleLink(linkScp, userp, reqp);
1465 /* if we may overflow the buffer, bail out; buffer is signficantly
1466 * bigger than max path length, so we don't really have to worry about
1467 * being a little conservative here.
1469 if (strlen(linkScp->mountPointStringp) + strlen(pathSuffixp) + 2
1470 >= CM_UTILS_SPACESIZE)
1471 return CM_ERROR_TOOBIG;
1473 tsp = cm_GetSpace();
1474 linkp = linkScp->mountPointStringp;
1475 if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1476 if (strlen(linkp) > cm_mountRootLen)
1477 strcpy(tsp->data, linkp+cm_mountRootLen+1);
1480 *newRootScpp = cm_data.rootSCachep;
1481 cm_HoldSCache(cm_data.rootSCachep);
1482 } else if (linkp[0] == '\\' && linkp[1] == '\\') {
1483 if (!strnicmp(&linkp[2], cm_NetbiosName, (len = (long)strlen(cm_NetbiosName))))
1485 char * p = &linkp[len + 3];
1486 if (strnicmp(p, "all", 3) == 0)
1489 strcpy(tsp->data, p);
1490 for (p = tsp->data; *p; p++) {
1494 *newRootScpp = cm_data.rootSCachep;
1495 cm_HoldSCache(cm_data.rootSCachep);
1497 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1498 strcpy(tsp->data, linkp);
1499 *newRootScpp = NULL;
1500 code = CM_ERROR_PATH_NOT_COVERED;
1502 } else if ( !strnicmp(linkp, "msdfs:", (len = (long)strlen("msdfs:"))) ) {
1503 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1504 strcpy(tsp->data, linkp);
1505 *newRootScpp = NULL;
1506 code = CM_ERROR_PATH_NOT_COVERED;
1507 } else if (*linkp == '\\' || *linkp == '/') {
1509 /* formerly, this was considered to be from the AFS root,
1510 * but this seems to create problems. instead, we will just
1511 * reject the link */
1512 strcpy(tsp->data, linkp+1);
1513 *newRootScpp = cm_data.rootSCachep;
1514 cm_HoldSCache(cm_data.rootSCachep);
1516 /* we still copy the link data into the response so that
1517 * the user can see what the link points to
1519 linkScp->fileType = CM_SCACHETYPE_INVALID;
1520 strcpy(tsp->data, linkp);
1521 *newRootScpp = NULL;
1522 code = CM_ERROR_NOSUCHPATH;
1525 /* a relative link */
1526 strcpy(tsp->data, linkp);
1527 *newRootScpp = NULL;
1529 if (pathSuffixp[0] != 0) { /* if suffix string is non-null */
1530 strcat(tsp->data, "\\");
1531 strcat(tsp->data, pathSuffixp);
1533 *newSpaceBufferp = tsp;
1536 lock_ReleaseMutex(&linkScp->mx);
1539 #ifdef DEBUG_REFCOUNT
1540 long cm_NameIDbg(cm_scache_t *rootSCachep, char *pathp, long flags,
1541 cm_user_t *userp, char *tidPathp, cm_req_t *reqp, cm_scache_t **outScpp,
1542 char * file, long line)
1544 long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
1545 cm_user_t *userp, char *tidPathp, cm_req_t *reqp, cm_scache_t **outScpp)
1549 char *tp; /* ptr moving through input buffer */
1550 char tc; /* temp char */
1551 int haveComponent; /* has new component started? */
1552 char component[256]; /* this is the new component */
1553 char *cp; /* component name being assembled */
1554 cm_scache_t *tscp; /* current location in the hierarchy */
1555 cm_scache_t *nscp; /* next dude down */
1556 cm_scache_t *dirScp; /* last dir we searched */
1557 cm_scache_t *linkScp; /* new root for the symlink we just
1559 cm_space_t *psp; /* space for current path, if we've hit
1561 cm_space_t *tempsp; /* temp vbl */
1562 char *restp; /* rest of the pathname to interpret */
1563 int symlinkCount; /* count of # of symlinks traversed */
1564 int extraFlag; /* avoid chasing mt pts for dir cmd */
1565 int phase = 1; /* 1 = tidPathp, 2 = pathp */
1567 #ifdef DEBUG_REFCOUNT
1568 afsi_log("%s:%d cm_NameI rootscp 0x%p ref %d", file, line, rootSCachep, rootSCachep->refCount);
1569 osi_Log4(afsd_logp,"cm_NameI rootscp 0x%p path %s tidpath %s flags 0x%x",
1570 rootSCachep, pathp ? pathp : "<NULL>", tidPathp ? tidPathp : "<NULL>",
1585 cm_HoldSCache(tscp);
1593 /* map Unix slashes into DOS ones so we can interpret Unix
1599 if (!haveComponent) {
1602 } else if (tc == 0) {
1616 /* we have a component here */
1617 if (tc == 0 || tc == '\\') {
1618 /* end of the component; we're at the last
1619 * component if tc == 0. However, if the last
1620 * is a symlink, we have more to do.
1622 *cp++ = 0; /* add null termination */
1623 if (!strcmp(".",component)) {
1626 cm_ReleaseSCache(dirScp);
1632 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
1633 extraFlag = CM_FLAG_NOMOUNTCHASE;
1634 code = cm_Lookup(tscp, component,
1636 userp, reqp, &nscp);
1638 cm_ReleaseSCache(tscp);
1640 cm_ReleaseSCache(dirScp);
1643 if (code == CM_ERROR_NOSUCHFILE && tscp->fileType == CM_SCACHETYPE_SYMLINK) {
1644 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_NOSUCHPATH");
1645 return CM_ERROR_NOSUCHPATH;
1647 osi_Log1(afsd_logp,"cm_NameI code 0x%x", code);
1651 haveComponent = 0; /* component done */
1653 cm_ReleaseSCache(dirScp);
1654 dirScp = tscp; /* for some symlinks */
1655 tscp = nscp; /* already held */
1657 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
1660 cm_ReleaseSCache(dirScp);
1666 /* now, if tscp is a symlink, we should follow
1667 * it and assemble the path again.
1669 lock_ObtainMutex(&tscp->mx);
1670 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1671 CM_SCACHESYNC_GETSTATUS
1672 | CM_SCACHESYNC_NEEDCALLBACK);
1674 lock_ReleaseMutex(&tscp->mx);
1675 cm_ReleaseSCache(tscp);
1678 cm_ReleaseSCache(dirScp);
1683 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1685 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
1686 /* this is a symlink; assemble a new buffer */
1687 lock_ReleaseMutex(&tscp->mx);
1688 if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
1689 cm_ReleaseSCache(tscp);
1692 cm_ReleaseSCache(dirScp);
1697 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_TOO_MANY_SYMLINKS");
1698 return CM_ERROR_TOO_MANY_SYMLINKS;
1704 code = cm_AssembleLink(tscp, restp, &linkScp, &tempsp, userp, reqp);
1706 /* something went wrong */
1707 cm_ReleaseSCache(tscp);
1710 cm_ReleaseSCache(dirScp);
1716 /* otherwise, tempsp has the new path,
1717 * and linkScp is the new root from
1718 * which to interpret that path.
1719 * Continue with the namei processing,
1720 * also doing the bookkeeping for the
1721 * space allocation and tracking the
1722 * vnode reference counts.
1728 cm_ReleaseSCache(tscp);
1733 * now, if linkScp is null, that's
1734 * AssembleLink's way of telling us that
1735 * the sym link is relative to the dir
1736 * containing the link. We have a ref
1737 * to it in dirScp, and we hold it now
1738 * and reuse it as the new spot in the
1746 /* not a symlink, we may be done */
1747 lock_ReleaseMutex(&tscp->mx);
1755 cm_ReleaseSCache(dirScp);
1763 cm_ReleaseSCache(dirScp);
1766 } /* end of a component */
1769 } /* we have a component */
1770 } /* big while loop over all components */
1774 cm_ReleaseSCache(dirScp);
1780 cm_ReleaseSCache(tscp);
1782 #ifdef DEBUG_REFCOUNT
1783 afsi_log("%s:%d cm_NameI code 0x%x outScpp 0x%p ref %d", file, line, code, *outScpp, (*outScpp)->refCount);
1785 osi_Log2(afsd_logp,"cm_NameI code 0x%x outScpp 0x%p", code, *outScpp);
1789 /* called with a dir, and a vnode within the dir that happens to be a symlink.
1790 * We chase the link, and return a held pointer to the target, if it exists,
1791 * in *outScpp. If we succeed, we return 0, otherwise we return an error code
1792 * and do not hold or return a target vnode.
1794 * This is very similar to calling cm_NameI with the last component of a name,
1795 * which happens to be a symlink, except that we've already passed by the name.
1797 * This function is typically called by the directory listing functions, which
1798 * encounter symlinks but need to return the proper file length so programs
1799 * like "more" work properly when they make use of the attributes retrieved from
1802 * The input vnode should not be locked when this function is called.
1804 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
1805 cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
1809 cm_scache_t *newRootScp;
1811 osi_Log1(afsd_logp, "Evaluating symlink scp 0x%p", linkScp);
1813 code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
1817 /* now, if newRootScp is NULL, we're really being told that the symlink
1818 * is relative to the current directory (dscp).
1820 if (newRootScp == NULL) {
1822 cm_HoldSCache(dscp);
1825 code = cm_NameI(newRootScp, spacep->data,
1826 CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
1827 userp, NULL, reqp, outScpp);
1829 if (code == CM_ERROR_NOSUCHFILE)
1830 code = CM_ERROR_NOSUCHPATH;
1832 /* this stuff is allocated no matter what happened on the namei call,
1834 cm_FreeSpace(spacep);
1835 cm_ReleaseSCache(newRootScp);
1840 /* make this big enough so that one buffer of dir pages won't overflow. We'll
1841 * check anyway, but we want to minimize the chance that we have to leave stuff
1844 #define CM_BULKMAX (3 * AFSCBMAX)
1846 /* rock for bulk stat calls */
1847 typedef struct cm_bulkStat {
1848 osi_hyper_t bufOffset; /* only do it for things in this buffer page */
1850 /* info for the actual call */
1851 int counter; /* next free slot */
1852 AFSFid fids[CM_BULKMAX];
1853 AFSFetchStatus stats[CM_BULKMAX];
1854 AFSCallBack callbacks[CM_BULKMAX];
1857 /* for a given entry, make sure that it isn't in the stat cache, and then
1858 * add it to the list of file IDs to be obtained.
1860 * Don't bother adding it if we already have a vnode. Note that the dir
1861 * is locked, so we have to be careful checking the vnode we're thinking of
1862 * processing, to avoid deadlocks.
1864 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
1875 /* Don't overflow bsp. */
1876 if (bsp->counter >= CM_BULKMAX)
1877 return CM_ERROR_STOPNOW;
1879 thyper.LowPart = cm_data.buf_blockSize;
1880 thyper.HighPart = 0;
1881 thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
1883 /* thyper is now the first byte past the end of the record we're
1884 * interested in, and bsp->bufOffset is the first byte of the record
1885 * we're interested in.
1886 * Skip data in the others.
1889 if (LargeIntegerLessThan(*offp, bsp->bufOffset))
1891 if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
1892 return CM_ERROR_STOPNOW;
1893 if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
1896 tfid.cell = scp->fid.cell;
1897 tfid.volume = scp->fid.volume;
1898 tfid.vnode = ntohl(dep->fid.vnode);
1899 tfid.unique = ntohl(dep->fid.unique);
1900 tscp = cm_FindSCache(&tfid);
1902 if (lock_TryMutex(&tscp->mx)) {
1903 /* we have an entry that we can look at */
1904 if (!(tscp->flags & CM_SCACHEFLAG_EACCESS) && cm_HaveCallback(tscp)) {
1905 /* we have a callback on it. Don't bother
1906 * fetching this stat entry, since we're happy
1907 * with the info we have.
1909 lock_ReleaseMutex(&tscp->mx);
1910 cm_ReleaseSCache(tscp);
1913 lock_ReleaseMutex(&tscp->mx);
1915 cm_ReleaseSCache(tscp);
1918 #ifdef AFS_FREELANCE_CLIENT
1919 // yj: if this is a mountpoint under root.afs then we don't want it
1920 // to be bulkstat-ed, instead, we call getSCache directly and under
1921 // getSCache, it is handled specially.
1922 if ( cm_freelanceEnabled &&
1923 tfid.cell==AFS_FAKE_ROOT_CELL_ID &&
1924 tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
1925 !(tfid.vnode==0x1 && tfid.unique==0x1) )
1927 osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
1928 return cm_GetSCache(&tfid, &tscp, NULL, NULL);
1930 #endif /* AFS_FREELANCE_CLIENT */
1933 bsp->fids[i].Volume = scp->fid.volume;
1934 bsp->fids[i].Vnode = tfid.vnode;
1935 bsp->fids[i].Unique = tfid.unique;
1939 /* called with a locked scp and a pointer to a buffer. Make bulk stat
1940 * calls on all undeleted files in the page of the directory specified.
1943 cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
1947 cm_bulkStat_t bb; /* this is *BIG*, probably 16K or so;
1948 * watch for stack problems */
1949 AFSCBFids fidStruct;
1950 AFSBulkStats statStruct;
1952 AFSCBs callbackStruct;
1955 cm_callbackRequest_t cbReq;
1961 struct rx_connection * callp;
1962 int inlinebulk = 0; /* Did we use InlineBulkStatus RPC or not? */
1964 osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%p", dscp);
1966 /* should be on a buffer boundary */
1967 osi_assert((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0);
1969 memset(&bb, 0, sizeof(bb));
1970 bb.bufOffset = *offsetp;
1972 lock_ReleaseMutex(&dscp->mx);
1973 /* first, assemble the file IDs we need to stat */
1974 code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) &bb, offsetp, userp, reqp, NULL);
1976 /* if we failed, bail out early */
1977 if (code && code != CM_ERROR_STOPNOW) {
1978 lock_ObtainMutex(&dscp->mx);
1982 /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
1983 * make the calls to create the entries. Handle AFSCBMAX files at a
1987 while (filex < bb.counter) {
1988 filesThisCall = bb.counter - filex;
1989 if (filesThisCall > AFSCBMAX)
1990 filesThisCall = AFSCBMAX;
1992 fidStruct.AFSCBFids_len = filesThisCall;
1993 fidStruct.AFSCBFids_val = &bb.fids[filex];
1994 statStruct.AFSBulkStats_len = filesThisCall;
1995 statStruct.AFSBulkStats_val = &bb.stats[filex];
1996 callbackStruct.AFSCBs_len = filesThisCall;
1997 callbackStruct.AFSCBs_val = &bb.callbacks[filex];
1998 cm_StartCallbackGrantingCall(NULL, &cbReq);
1999 osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
2001 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2005 callp = cm_GetRxConn(connp);
2006 if (!(connp->serverp->flags & CM_SERVERFLAG_NOINLINEBULK)) {
2007 code = RXAFS_InlineBulkStatus(callp, &fidStruct,
2008 &statStruct, &callbackStruct, &volSync);
2009 if (code == RXGEN_OPCODE) {
2010 cm_SetServerNoInlineBulk(connp->serverp, 0);
2016 code = RXAFS_BulkStatus(callp, &fidStruct,
2017 &statStruct, &callbackStruct, &volSync);
2019 rx_PutConnection(callp);
2021 } while (cm_Analyze(connp, userp, reqp, &dscp->fid,
2022 &volSync, NULL, &cbReq, code));
2023 code = cm_MapRPCError(code, reqp);
2025 osi_Log2(afsd_logp, "CALL %sBulkStatus FAILURE code 0x%x",
2026 inlinebulk ? "Inline" : "", code);
2028 osi_Log1(afsd_logp, "CALL %sBulkStatus SUCCESS", inlinebulk ? "Inline" : "");
2030 /* may as well quit on an error, since we're not going to do
2031 * much better on the next immediate call, either.
2034 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2038 /* otherwise, we should do the merges */
2039 for (i = 0; i<filesThisCall; i++) {
2041 tfid.cell = dscp->fid.cell;
2042 tfid.volume = bb.fids[j].Volume;
2043 tfid.vnode = bb.fids[j].Vnode;
2044 tfid.unique = bb.fids[j].Unique;
2045 code = cm_GetSCache(&tfid, &scp, userp, reqp);
2049 /* otherwise, if this entry has no callback info,
2052 lock_ObtainMutex(&scp->mx);
2053 /* now, we have to be extra paranoid on merging in this
2054 * information, since we didn't use cm_SyncOp before
2055 * starting the fetch to make sure that no bad races
2056 * were occurring. Specifically, we need to make sure
2057 * we don't obliterate any newer information in the
2058 * vnode than have here.
2060 * Right now, be pretty conservative: if there's a
2061 * callback or a pending call, skip it.
2063 if ((scp->cbServerp == NULL || (scp->flags & CM_SCACHEFLAG_EACCESS))
2065 (CM_SCACHEFLAG_FETCHING
2066 | CM_SCACHEFLAG_STORING
2067 | CM_SCACHEFLAG_SIZESTORING))) {
2068 cm_EndCallbackGrantingCall(scp, &cbReq,
2070 CM_CALLBACK_MAINTAINCOUNT);
2071 cm_MergeStatus(scp, &bb.stats[j], &volSync, userp, 0);
2073 lock_ReleaseMutex(&scp->mx);
2074 cm_ReleaseSCache(scp);
2075 } /* all files in the response */
2076 /* now tell it to drop the count,
2077 * after doing the vnode processing above */
2078 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2080 filex += filesThisCall;
2081 } /* while there are still more files to process */
2082 lock_ObtainMutex(&dscp->mx);
2085 /* If we did the InlineBulk RPC pull out the return code */
2087 if ((&bb.stats[0])->errorCode) {
2088 cm_Analyze(NULL /*connp was released by the previous cm_Analyze */,
2089 userp, reqp, &dscp->fid, &volSync, NULL, NULL, (&bb.stats[0])->errorCode);
2090 code = cm_MapRPCError((&bb.stats[0])->errorCode, reqp);
2098 osi_Log1(afsd_logp, "END cm_TryBulkStat code = 0x%x", code);
2102 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
2106 /* initialize store back mask as inexpensive local variable */
2108 memset(statusp, 0, sizeof(AFSStoreStatus));
2110 /* copy out queued info from scache first, if scp passed in */
2112 if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
2113 statusp->ClientModTime = scp->clientModTime;
2114 mask |= AFS_SETMODTIME;
2115 scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
2120 /* now add in our locally generated request */
2121 if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
2122 statusp->ClientModTime = attrp->clientModTime;
2123 mask |= AFS_SETMODTIME;
2125 if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
2126 statusp->UnixModeBits = attrp->unixModeBits;
2127 mask |= AFS_SETMODE;
2129 if (attrp->mask & CM_ATTRMASK_OWNER) {
2130 statusp->Owner = attrp->owner;
2131 mask |= AFS_SETOWNER;
2133 if (attrp->mask & CM_ATTRMASK_GROUP) {
2134 statusp->Group = attrp->group;
2135 mask |= AFS_SETGROUP;
2138 statusp->Mask = mask;
2141 /* set the file size, and make sure that all relevant buffers have been
2142 * truncated. Ensure that any partially truncated buffers have been zeroed
2143 * to the end of the buffer.
2145 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
2151 /* start by locking out buffer creation */
2152 lock_ObtainWrite(&scp->bufCreateLock);
2154 /* verify that this is a file, not a dir or a symlink */
2155 lock_ObtainMutex(&scp->mx);
2156 code = cm_SyncOp(scp, NULL, userp, reqp, 0,
2157 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2160 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2162 if (scp->fileType != CM_SCACHETYPE_FILE) {
2163 code = CM_ERROR_ISDIR;
2168 if (LargeIntegerLessThan(*sizep, scp->length))
2173 lock_ReleaseMutex(&scp->mx);
2175 /* can't hold scp->mx lock here, since we may wait for a storeback to
2176 * finish if the buffer package is cleaning a buffer by storing it to
2180 buf_Truncate(scp, userp, reqp, sizep);
2182 /* now ensure that file length is short enough, and update truncPos */
2183 lock_ObtainMutex(&scp->mx);
2185 /* make sure we have a callback (so we have the right value for the
2186 * length), and wait for it to be safe to do a truncate.
2188 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
2189 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2190 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2194 if (LargeIntegerLessThan(*sizep, scp->length)) {
2195 /* a real truncation. If truncPos is not set yet, or is bigger
2196 * than where we're truncating the file, set truncPos to this
2201 if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
2202 || LargeIntegerLessThan(*sizep, scp->length)) {
2204 scp->truncPos = *sizep;
2205 scp->mask |= CM_SCACHEMASK_TRUNCPOS;
2207 /* in either case, the new file size has been changed */
2208 scp->length = *sizep;
2209 scp->mask |= CM_SCACHEMASK_LENGTH;
2211 else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
2212 /* really extending the file */
2213 scp->length = *sizep;
2214 scp->mask |= CM_SCACHEMASK_LENGTH;
2217 /* done successfully */
2220 cm_SyncOpDone(scp, NULL,
2221 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2222 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2225 lock_ReleaseMutex(&scp->mx);
2226 lock_ReleaseWrite(&scp->bufCreateLock);
2231 /* set the file size or other attributes (but not both at once) */
2232 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
2236 AFSFetchStatus afsOutStatus;
2240 AFSStoreStatus afsInStatus;
2241 struct rx_connection * callp;
2243 /* handle file length setting */
2244 if (attrp->mask & CM_ATTRMASK_LENGTH)
2245 return cm_SetLength(scp, &attrp->length, userp, reqp);
2247 lock_ObtainMutex(&scp->mx);
2248 /* otherwise, we have to make an RPC to get the status */
2249 code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
2251 lock_ReleaseMutex(&scp->mx);
2255 /* make the attr structure */
2256 cm_StatusFromAttr(&afsInStatus, scp, attrp);
2258 tfid.Volume = scp->fid.volume;
2259 tfid.Vnode = scp->fid.vnode;
2260 tfid.Unique = scp->fid.unique;
2261 lock_ReleaseMutex(&scp->mx);
2263 /* now make the RPC */
2264 osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%p", scp);
2266 code = cm_Conn(&scp->fid, userp, reqp, &connp);
2270 callp = cm_GetRxConn(connp);
2271 code = RXAFS_StoreStatus(callp, &tfid,
2272 &afsInStatus, &afsOutStatus, &volSync);
2273 rx_PutConnection(callp);
2275 } while (cm_Analyze(connp, userp, reqp,
2276 &scp->fid, &volSync, NULL, NULL, code));
2277 code = cm_MapRPCError(code, reqp);
2280 osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
2282 osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
2284 lock_ObtainMutex(&scp->mx);
2285 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
2287 cm_MergeStatus(scp, &afsOutStatus, &volSync, userp,
2288 CM_MERGEFLAG_FORCE);
2290 /* if we're changing the mode bits, discard the ACL cache,
2291 * since we changed the mode bits.
2293 if (afsInStatus.Mask & AFS_SETMODE) cm_FreeAllACLEnts(scp);
2294 lock_ReleaseMutex(&scp->mx);
2298 long cm_Create(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2299 cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
2304 cm_callbackRequest_t cbReq;
2309 AFSStoreStatus inStatus;
2310 AFSFetchStatus updatedDirStatus;
2311 AFSFetchStatus newFileStatus;
2312 AFSCallBack newFileCallback;
2314 struct rx_connection * callp;
2316 /* can't create names with @sys in them; must expand it manually first.
2317 * return "invalid request" if they try.
2319 if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2320 return CM_ERROR_ATSYS;
2323 /* before starting the RPC, mark that we're changing the file data, so
2324 * that someone who does a chmod will know to wait until our call
2327 lock_ObtainMutex(&dscp->mx);
2328 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2330 cm_StartCallbackGrantingCall(NULL, &cbReq);
2332 lock_ReleaseMutex(&dscp->mx);
2338 cm_StatusFromAttr(&inStatus, NULL, attrp);
2340 /* try the RPC now */
2341 osi_Log1(afsd_logp, "CALL CreateFile scp 0x%p", dscp);
2343 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2347 dirAFSFid.Volume = dscp->fid.volume;
2348 dirAFSFid.Vnode = dscp->fid.vnode;
2349 dirAFSFid.Unique = dscp->fid.unique;
2351 callp = cm_GetRxConn(connp);
2352 code = RXAFS_CreateFile(connp->callp, &dirAFSFid, namep,
2353 &inStatus, &newAFSFid, &newFileStatus,
2354 &updatedDirStatus, &newFileCallback,
2356 rx_PutConnection(callp);
2358 } while (cm_Analyze(connp, userp, reqp,
2359 &dscp->fid, &volSync, NULL, &cbReq, code));
2360 code = cm_MapRPCError(code, reqp);
2363 osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
2365 osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
2367 lock_ObtainMutex(&dscp->mx);
2368 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2370 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2372 lock_ReleaseMutex(&dscp->mx);
2374 /* now try to create the file's entry, too, but be careful to
2375 * make sure that we don't merge in old info. Since we weren't locking
2376 * out any requests during the file's creation, we may have pretty old
2380 newFid.cell = dscp->fid.cell;
2381 newFid.volume = dscp->fid.volume;
2382 newFid.vnode = newAFSFid.Vnode;
2383 newFid.unique = newAFSFid.Unique;
2384 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2386 lock_ObtainMutex(&scp->mx);
2387 scp->creator = userp; /* remember who created it */
2388 if (!cm_HaveCallback(scp)) {
2389 cm_MergeStatus(scp, &newFileStatus, &volSync,
2391 cm_EndCallbackGrantingCall(scp, &cbReq,
2392 &newFileCallback, 0);
2395 lock_ReleaseMutex(&scp->mx);
2400 /* make sure we end things properly */
2402 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2407 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
2411 lock_ObtainWrite(&scp->bufCreateLock);
2412 code = buf_CleanVnode(scp, userp, reqp);
2413 lock_ReleaseWrite(&scp->bufCreateLock);
2415 lock_ObtainMutex(&scp->mx);
2417 if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
2418 | CM_SCACHEMASK_CLIENTMODTIME
2419 | CM_SCACHEMASK_LENGTH))
2420 code = cm_StoreMini(scp, userp, reqp);
2422 if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) {
2423 code = (scp->flags & CM_SCACHEFLAG_OVERQUOTA) ? CM_ERROR_QUOTA : CM_ERROR_SPACE;
2424 scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE);
2427 lock_ReleaseMutex(&scp->mx);
2432 long cm_MakeDir(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2433 cm_user_t *userp, cm_req_t *reqp)
2438 cm_callbackRequest_t cbReq;
2443 AFSStoreStatus inStatus;
2444 AFSFetchStatus updatedDirStatus;
2445 AFSFetchStatus newDirStatus;
2446 AFSCallBack newDirCallback;
2448 struct rx_connection * callp;
2450 /* can't create names with @sys in them; must expand it manually first.
2451 * return "invalid request" if they try.
2453 if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2454 return CM_ERROR_ATSYS;
2457 /* before starting the RPC, mark that we're changing the directory
2458 * data, so that someone who does a chmod on the dir will wait until
2459 * our call completes.
2461 lock_ObtainMutex(&dscp->mx);
2462 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2464 cm_StartCallbackGrantingCall(NULL, &cbReq);
2466 lock_ReleaseMutex(&dscp->mx);
2472 cm_StatusFromAttr(&inStatus, NULL, attrp);
2474 /* try the RPC now */
2475 osi_Log1(afsd_logp, "CALL MakeDir scp 0x%p", dscp);
2477 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2481 dirAFSFid.Volume = dscp->fid.volume;
2482 dirAFSFid.Vnode = dscp->fid.vnode;
2483 dirAFSFid.Unique = dscp->fid.unique;
2485 callp = cm_GetRxConn(connp);
2486 code = RXAFS_MakeDir(connp->callp, &dirAFSFid, namep,
2487 &inStatus, &newAFSFid, &newDirStatus,
2488 &updatedDirStatus, &newDirCallback,
2490 rx_PutConnection(callp);
2492 } while (cm_Analyze(connp, userp, reqp,
2493 &dscp->fid, &volSync, NULL, &cbReq, code));
2494 code = cm_MapRPCError(code, reqp);
2497 osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
2499 osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
2501 lock_ObtainMutex(&dscp->mx);
2502 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2504 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2506 lock_ReleaseMutex(&dscp->mx);
2508 /* now try to create the new dir's entry, too, but be careful to
2509 * make sure that we don't merge in old info. Since we weren't locking
2510 * out any requests during the file's creation, we may have pretty old
2514 newFid.cell = dscp->fid.cell;
2515 newFid.volume = dscp->fid.volume;
2516 newFid.vnode = newAFSFid.Vnode;
2517 newFid.unique = newAFSFid.Unique;
2518 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2520 lock_ObtainMutex(&scp->mx);
2521 if (!cm_HaveCallback(scp)) {
2522 cm_MergeStatus(scp, &newDirStatus, &volSync,
2524 cm_EndCallbackGrantingCall(scp, &cbReq,
2525 &newDirCallback, 0);
2528 lock_ReleaseMutex(&scp->mx);
2529 cm_ReleaseSCache(scp);
2533 /* make sure we end things properly */
2535 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2537 /* and return error code */
2541 long cm_Link(cm_scache_t *dscp, char *namep, cm_scache_t *sscp, long flags,
2542 cm_user_t *userp, cm_req_t *reqp)
2547 AFSFid existingAFSFid;
2548 AFSFetchStatus updatedDirStatus;
2549 AFSFetchStatus newLinkStatus;
2551 struct rx_connection * callp;
2553 if (dscp->fid.cell != sscp->fid.cell ||
2554 dscp->fid.volume != sscp->fid.volume) {
2555 return CM_ERROR_CROSSDEVLINK;
2558 lock_ObtainMutex(&dscp->mx);
2559 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2560 lock_ReleaseMutex(&dscp->mx);
2565 /* try the RPC now */
2566 osi_Log1(afsd_logp, "CALL Link scp 0x%p", dscp);
2568 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2571 dirAFSFid.Volume = dscp->fid.volume;
2572 dirAFSFid.Vnode = dscp->fid.vnode;
2573 dirAFSFid.Unique = dscp->fid.unique;
2575 existingAFSFid.Volume = sscp->fid.volume;
2576 existingAFSFid.Vnode = sscp->fid.vnode;
2577 existingAFSFid.Unique = sscp->fid.unique;
2579 callp = cm_GetRxConn(connp);
2580 code = RXAFS_Link(callp, &dirAFSFid, namep, &existingAFSFid,
2581 &newLinkStatus, &updatedDirStatus, &volSync);
2582 rx_PutConnection(callp);
2583 osi_Log1(smb_logp," RXAFS_Link returns 0x%x", code);
2585 } while (cm_Analyze(connp, userp, reqp,
2586 &dscp->fid, &volSync, NULL, NULL, code));
2588 code = cm_MapRPCError(code, reqp);
2591 osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
2593 osi_Log0(afsd_logp, "CALL Link SUCCESS");
2595 lock_ObtainMutex(&dscp->mx);
2596 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2598 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2600 lock_ReleaseMutex(&dscp->mx);
2605 long cm_SymLink(cm_scache_t *dscp, char *namep, char *contentsp, long flags,
2606 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
2614 AFSStoreStatus inStatus;
2615 AFSFetchStatus updatedDirStatus;
2616 AFSFetchStatus newLinkStatus;
2618 struct rx_connection * callp;
2620 /* before starting the RPC, mark that we're changing the directory data,
2621 * so that someone who does a chmod on the dir will wait until our
2624 lock_ObtainMutex(&dscp->mx);
2625 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2626 lock_ReleaseMutex(&dscp->mx);
2631 cm_StatusFromAttr(&inStatus, NULL, attrp);
2633 /* try the RPC now */
2634 osi_Log1(afsd_logp, "CALL Symlink scp 0x%p", dscp);
2636 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2640 dirAFSFid.Volume = dscp->fid.volume;
2641 dirAFSFid.Vnode = dscp->fid.vnode;
2642 dirAFSFid.Unique = dscp->fid.unique;
2644 callp = cm_GetRxConn(connp);
2645 code = RXAFS_Symlink(callp, &dirAFSFid, namep, contentsp,
2646 &inStatus, &newAFSFid, &newLinkStatus,
2647 &updatedDirStatus, &volSync);
2648 rx_PutConnection(callp);
2650 } while (cm_Analyze(connp, userp, reqp,
2651 &dscp->fid, &volSync, NULL, NULL, code));
2652 code = cm_MapRPCError(code, reqp);
2655 osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
2657 osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
2659 lock_ObtainMutex(&dscp->mx);
2660 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2662 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2664 lock_ReleaseMutex(&dscp->mx);
2666 /* now try to create the new dir's entry, too, but be careful to
2667 * make sure that we don't merge in old info. Since we weren't locking
2668 * out any requests during the file's creation, we may have pretty old
2672 newFid.cell = dscp->fid.cell;
2673 newFid.volume = dscp->fid.volume;
2674 newFid.vnode = newAFSFid.Vnode;
2675 newFid.unique = newAFSFid.Unique;
2676 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2678 lock_ObtainMutex(&scp->mx);
2679 if (!cm_HaveCallback(scp)) {
2680 cm_MergeStatus(scp, &newLinkStatus, &volSync,
2683 lock_ReleaseMutex(&scp->mx);
2684 cm_ReleaseSCache(scp);
2688 /* and return error code */
2692 long cm_RemoveDir(cm_scache_t *dscp, char *namep, cm_user_t *userp,
2699 AFSFetchStatus updatedDirStatus;
2701 struct rx_connection * callp;
2703 /* before starting the RPC, mark that we're changing the directory data,
2704 * so that someone who does a chmod on the dir will wait until our
2707 lock_ObtainMutex(&dscp->mx);
2708 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2709 lock_ReleaseMutex(&dscp->mx);
2715 /* try the RPC now */
2716 osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%p", dscp);
2718 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2722 dirAFSFid.Volume = dscp->fid.volume;
2723 dirAFSFid.Vnode = dscp->fid.vnode;
2724 dirAFSFid.Unique = dscp->fid.unique;
2726 callp = cm_GetRxConn(connp);
2727 code = RXAFS_RemoveDir(callp, &dirAFSFid, namep,
2728 &updatedDirStatus, &volSync);
2729 rx_PutConnection(callp);
2731 } while (cm_Analyze(connp, userp, reqp,
2732 &dscp->fid, &volSync, NULL, NULL, code));
2733 code = cm_MapRPCErrorRmdir(code, reqp);
2736 osi_Log1(afsd_logp, "CALL RemoveDir FAILURE, code 0x%x", code);
2738 osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
2740 lock_ObtainMutex(&dscp->mx);
2741 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2743 cm_dnlcRemove(dscp, namep);
2744 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2746 lock_ReleaseMutex(&dscp->mx);
2748 /* and return error code */
2752 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
2754 /* grab mutex on contents */
2755 lock_ObtainMutex(&scp->mx);
2757 /* reset the prefetch info */
2758 scp->prefetch.base.LowPart = 0; /* base */
2759 scp->prefetch.base.HighPart = 0;
2760 scp->prefetch.end.LowPart = 0; /* and end */
2761 scp->prefetch.end.HighPart = 0;
2763 /* release mutex on contents */
2764 lock_ReleaseMutex(&scp->mx);
2770 long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
2771 char *newNamep, cm_user_t *userp, cm_req_t *reqp)
2775 AFSFid oldDirAFSFid;
2776 AFSFid newDirAFSFid;
2778 AFSFetchStatus updatedOldDirStatus;
2779 AFSFetchStatus updatedNewDirStatus;
2782 struct rx_connection * callp;
2784 /* before starting the RPC, mark that we're changing the directory data,
2785 * so that someone who does a chmod on the dir will wait until our call
2786 * completes. We do this in vnode order so that we don't deadlock,
2787 * which makes the code a little verbose.
2789 if (oldDscp == newDscp) {
2790 /* check for identical names */
2791 if (strcmp(oldNamep, newNamep) == 0)
2792 return CM_ERROR_RENAME_IDENTICAL;
2795 lock_ObtainMutex(&oldDscp->mx);
2796 cm_dnlcRemove(oldDscp, oldNamep);
2797 cm_dnlcRemove(oldDscp, newNamep);
2798 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2799 CM_SCACHESYNC_STOREDATA);
2800 lock_ReleaseMutex(&oldDscp->mx);
2803 /* two distinct dir vnodes */
2805 if (oldDscp->fid.cell != newDscp->fid.cell ||
2806 oldDscp->fid.volume != newDscp->fid.volume)
2807 return CM_ERROR_CROSSDEVLINK;
2809 /* shouldn't happen that we have distinct vnodes for two
2810 * different files, but could due to deliberate attack, or
2811 * stale info. Avoid deadlocks and quit now.
2813 if (oldDscp->fid.vnode == newDscp->fid.vnode)
2814 return CM_ERROR_CROSSDEVLINK;
2816 if (oldDscp->fid.vnode < newDscp->fid.vnode) {
2817 lock_ObtainMutex(&oldDscp->mx);
2818 cm_dnlcRemove(oldDscp, oldNamep);
2819 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2820 CM_SCACHESYNC_STOREDATA);
2821 lock_ReleaseMutex(&oldDscp->mx);
2823 lock_ObtainMutex(&newDscp->mx);
2824 cm_dnlcRemove(newDscp, newNamep);
2825 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2826 CM_SCACHESYNC_STOREDATA);
2827 lock_ReleaseMutex(&newDscp->mx);
2829 /* cleanup first one */
2830 lock_ObtainMutex(&oldDscp->mx);
2831 cm_SyncOpDone(oldDscp, NULL,
2832 CM_SCACHESYNC_STOREDATA);
2833 lock_ReleaseMutex(&oldDscp->mx);
2838 /* lock the new vnode entry first */
2839 lock_ObtainMutex(&newDscp->mx);
2840 cm_dnlcRemove(newDscp, newNamep);
2841 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2842 CM_SCACHESYNC_STOREDATA);
2843 lock_ReleaseMutex(&newDscp->mx);
2845 lock_ObtainMutex(&oldDscp->mx);
2846 cm_dnlcRemove(oldDscp, oldNamep);
2847 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2848 CM_SCACHESYNC_STOREDATA);
2849 lock_ReleaseMutex(&oldDscp->mx);
2851 /* cleanup first one */
2852 lock_ObtainMutex(&newDscp->mx);
2853 cm_SyncOpDone(newDscp, NULL,
2854 CM_SCACHESYNC_STOREDATA);
2855 lock_ReleaseMutex(&newDscp->mx);
2859 } /* two distinct vnodes */
2866 /* try the RPC now */
2867 osi_Log2(afsd_logp, "CALL Rename old scp 0x%p new scp 0x%p",
2870 code = cm_Conn(&oldDscp->fid, userp, reqp, &connp);
2874 oldDirAFSFid.Volume = oldDscp->fid.volume;
2875 oldDirAFSFid.Vnode = oldDscp->fid.vnode;
2876 oldDirAFSFid.Unique = oldDscp->fid.unique;
2877 newDirAFSFid.Volume = newDscp->fid.volume;
2878 newDirAFSFid.Vnode = newDscp->fid.vnode;
2879 newDirAFSFid.Unique = newDscp->fid.unique;
2881 callp = cm_GetRxConn(connp);
2882 code = RXAFS_Rename(callp, &oldDirAFSFid, oldNamep,
2883 &newDirAFSFid, newNamep,
2884 &updatedOldDirStatus, &updatedNewDirStatus,
2886 rx_PutConnection(callp);
2888 } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
2889 &volSync, NULL, NULL, code));
2890 code = cm_MapRPCError(code, reqp);
2893 osi_Log1(afsd_logp, "CALL Rename FAILURE, code 0x%x", code);
2895 osi_Log0(afsd_logp, "CALL Rename SUCCESS");
2897 /* update the individual stat cache entries for the directories */
2898 lock_ObtainMutex(&oldDscp->mx);
2899 cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
2901 cm_MergeStatus(oldDscp, &updatedOldDirStatus, &volSync,
2904 lock_ReleaseMutex(&oldDscp->mx);
2906 /* and update it for the new one, too, if necessary */
2908 lock_ObtainMutex(&newDscp->mx);
2909 cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
2911 cm_MergeStatus(newDscp, &updatedNewDirStatus, &volSync,
2914 lock_ReleaseMutex(&newDscp->mx);
2917 /* and return error code */
2921 /* Byte range locks:
2923 The OpenAFS Windows client has to fake byte range locks given no
2924 server side support for such locks. This is implemented as keyed
2925 byte range locks on the cache manager.
2927 Keyed byte range locks:
2929 Each cm_scache_t structure keeps track of a list of keyed locks.
2930 The key for a lock identifies an owner of a set of locks (referred
2931 to as a client). Each key is represented by a value. The set of
2932 key values used within a specific cm_scache_t structure form a
2933 namespace that has a scope of just that cm_scache_t structure. The
2934 same key value can be used with another cm_scache_t structure and
2935 correspond to a completely different client. However it is
2936 advantageous for the SMB or IFS layer to make sure that there is a
2937 1-1 mapping between client and keys over all cm_scache_t objects.
2939 Assume a client C has key Key(C) (although, since the scope of the
2940 key is a cm_scache_t, the key can be Key(C,S), where S is the
2941 cm_scache_t. But assume a 1-1 relation between keys and clients).
2942 A byte range (O,+L) denotes byte addresses (O) through (O+L-1)
2943 inclusive (a.k.a. [O,O+L-1]). The function Key(x) is implemented
2944 through cm_generateKey() function for both SMB and IFS.
2946 The list of locks for a cm_scache_t object S is maintained in
2947 S->fileLocks. The cache manager will set a lock on the AFS file
2948 server in order to assert the locks in S->fileLocks. If only
2949 shared locks are in place for S, then the cache manager will obtain
2950 a LockRead lock, while if there are any exclusive locks, it will
2951 obtain a LockWrite lock. If the exclusive locks are all released
2952 while the shared locks remain, then the cache manager will
2953 downgrade the lock from LockWrite to LockRead. Similarly, if an
2954 exclusive lock is obtained when only shared locks exist, then the
2955 cache manager will try to upgrade the lock from LockRead to
2958 Each lock L owned by client C maintains a key L->key such that
2959 L->key == Key(C), the effective range defined by L->LOffset and
2960 L->LLength such that the range of bytes affected by the lock is
2961 (L->LOffset, +L->LLength), a type maintained in L->LockType which
2962 is either exclusive or shared.
2966 A lock exists iff it is in S->fileLocks for some cm_scache_t
2967 S. Existing locks are in one of the following states: ACTIVE,
2968 WAITLOCK, WAITUNLOCK, LOST, DELETED.
2970 The following sections describe each lock and the associated
2973 1. ACTIVE: A lock L is ACTIVE iff the cache manager has asserted
2974 the lock with the AFS file server. This type of lock can be
2975 exercised by a client to read or write to the locked region (as
2978 1.1 ACTIVE->LOST: When the AFS file server fails to extend a
2979 server lock that was required to assert the lock. Before
2980 marking the lock as lost, the cache manager checks if the file
2981 has changed on the server. If the file has not changed, then
2982 the cache manager will attempt to obtain a new server lock
2983 that is sufficient to assert the client side locks for the
2984 file. If any of these fail, the lock is marked as LOST.
2985 Otherwise, it is left as ACTIVE.
2987 1.2 ACTIVE->DELETED: Lock is released.
2989 2. WAITLOCK: A lock is in a WAITLOCK state if the cache manager
2990 grants the lock but the lock is yet to be asserted with the AFS
2991 file server. Once the file server grants the lock, the state
2992 will transition to an ACTIVE lock.
2994 2.1 WAITLOCK->ACTIVE: The server granted the lock.
2996 2.2 WAITLOCK->DELETED: Lock is abandoned, or timed out during
2999 2.3 WAITLOCK->LOST: One or more locks from this client were
3000 marked as LOST. No further locks will be granted to this
3001 client until all lost locks are removed.
3003 3. WAITUNLOCK: A lock is in a WAITUNLOCK state if the cache manager
3004 receives a request for a lock that conflicts with an existing
3005 ACTIVE or WAITLOCK lock. The lock will be placed in the queue
3006 and will be granted at such time the conflicting locks are
3007 removed, at which point the state will transition to either
3010 3.1 WAITUNLOCK->ACTIVE: The conflicting lock was removed. The
3011 current serverLock is sufficient to assert this lock, or a
3012 sufficient serverLock is obtained.
3014 3.2 WAITUNLOCK->WAITLOCK: The conflicting lock was removed,
3015 however the required serverLock is yet to be asserted with the
3018 3.3 WAITUNLOCK->DELETED: The lock is abandoned, timed out or
3021 3.5 WAITUNLOCK->LOST: One or more locks from this client were
3022 marked as LOST. No further locks will be granted to this
3023 client until all lost locks are removed.
3025 4. LOST: A lock L is LOST if the server lock that was required to
3026 assert the lock could not be obtained or if it could not be
3027 extended, or if other locks by the same client were LOST.
3028 Essentially, once a lock is LOST, the contract between the cache
3029 manager and that specific client is no longer valid.
3031 The cache manager rechecks the server lock once every minute and
3032 extends it as appropriate. If this is not done for 5 minutes,
3033 the AFS file server will release the lock (the 5 minute timeout
3034 is based on current file server code and is fairly arbitrary).
3035 Once released, the lock cannot be re-obtained without verifying
3036 that the contents of the file hasn't been modified since the
3037 time the lock was released. Re-obtaining the lock without
3038 verifying this may lead to data corruption. If the lock can not
3039 be obtained safely, then all active locks for the cm_scache_t
3042 4.1 LOST->DELETED: The lock is released.
3044 5. DELETED: The lock is no longer relevant. Eventually, it will
3045 get removed from the cm_scache_t. In the meantime, it will be
3046 treated as if it does not exist.
3048 5.1 DELETED->not exist: The lock is removed from the
3051 The following are classifications of locks based on their state.
3053 6* A lock L is ACCEPTED if it is ACTIVE or WAITLOCK. These locks
3054 have been accepted by the cache manager, but may or may not have
3055 been granted back to the client.
3057 7* A lock L is QUEUED if it is ACTIVE, WAITLOCK or WAITUNLOCK.
3059 8* A lock L is WAITING if it is WAITLOCK or WAITUNLOCK.
3063 A client C can READ range (Offset,+Length) of a file represented by
3064 cm_scache_t S iff (1):
3066 1. for all _a_ in (Offset,+Length), all of the following is true:
3068 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3069 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3072 1.2 For each LOST lock L in S->fileLocks such that _a_ in
3073 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3076 (When locks are lost on an cm_scache_t, all locks are lost. By
3077 4.2 (below), if there is an exclusive LOST lock, then there
3078 can't be any overlapping ACTIVE locks.)
3080 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
3082 2. for all _a_ in (Offset,+Length), one of the following is true:
3084 2.1 Byte _a_ of S is unowned (as specified in 1.1) AND there
3085 does not exist a LOST lock L such that _a_ in
3086 (L->LOffset,+L->LLength).
3088 2.2 Byte _a_ of S is owned by C under lock L (as specified in
3089 1.2) AND L->LockType is exclusive.
3091 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
3093 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
3096 3.1 If L->LockType is exclusive then there does NOT exist a
3097 ACCEPTED lock M in S->fileLocks such that _a_ in
3098 (M->LOffset,+M->LLength).
3100 (If we count all QUEUED locks then we hit cases such as
3101 cascading waiting locks where the locks later on in the queue
3102 can be granted without compromising file integrity. On the
3103 other hand if only ACCEPTED locks are considered, then locks
3104 that were received earlier may end up waiting for locks that
3105 were received later to be unlocked. The choice of ACCEPTED
3106 locks was made to mimic the Windows byte range lock
3109 3.2 If L->LockType is shared then for each ACCEPTED lock M in
3110 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
3111 M->LockType is shared.
3113 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
3115 4.1 M->key != Key(C)
3117 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
3118 and (M->LOffset,+M->LLength) do not intersect.
3120 (Note: If a client loses a lock, it loses all locks.
3121 Subsequently, it will not be allowed to obtain any more locks
3122 until all existing LOST locks that belong to the client are
3123 released. Once all locks are released by a single client,
3124 there exists no further contract between the client and AFS
3125 about the contents of the file, hence the client can then
3126 proceed to obtain new locks and establish a new contract.
3128 This doesn't quite work as you think it should, because most
3129 applications aren't built to deal with losing locks they
3130 thought they once had. For now, we don't have a good
3131 solution to lost locks.
3133 Also, for consistency reasons, we have to hold off on
3134 granting locks that overlap exclusive LOST locks.)
3136 A client C can only unlock locks L in S->fileLocks which have
3139 The representation and invariants are as follows:
3141 - Each cm_scache_t structure keeps:
3143 - A queue of byte-range locks (cm_scache_t::fileLocks) which
3144 are of type cm_file_lock_t.
3146 - A record of the highest server-side lock that has been
3147 obtained for this object (cm_scache_t::serverLock), which is
3148 one of (-1), LockRead, LockWrite.
3150 - A count of ACCEPTED exclusive and shared locks that are in the
3151 queue (cm_scache_t::sharedLocks and
3152 cm_scache_t::exclusiveLocks)
3154 - Each cm_file_lock_t structure keeps:
3156 - The type of lock (cm_file_lock_t::LockType)
3158 - The key associated with the lock (cm_file_lock_t::key)
3160 - The offset and length of the lock (cm_file_lock_t::LOffset
3161 and cm_file_lock_t::LLength)
3163 - The state of the lock.
3165 - Time of issuance or last successful extension
3167 Semantic invariants:
3169 I1. The number of ACCEPTED locks in S->fileLocks are
3170 (S->sharedLocks + S->exclusiveLocks)
3172 External invariants:
3174 I3. S->serverLock is the lock that we have asserted with the
3175 AFS file server for this cm_scache_t.
3177 I4. S->serverLock == LockRead iff there is at least one ACTIVE
3178 shared lock, but no ACTIVE exclusive locks.
3180 I5. S->serverLock == LockWrite iff there is at least one ACTIVE
3183 I6. If L is a LOST lock, then for each lock M in S->fileLocks,
3184 M->key == L->key IMPLIES M is LOST or DELETED.
3189 #define IS_LOCK_ACTIVE(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == 0)
3191 #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)
3193 #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)
3195 #define IS_LOCK_LOST(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_LOST)
3197 #define IS_LOCK_DELETED(lockp) (((lockp)->flags & CM_FILELOCK_FLAG_DELETED) == CM_FILELOCK_FLAG_DELETED)
3200 #define IS_LOCK_ACCEPTED(lockp) (IS_LOCK_ACTIVE(lockp) || IS_LOCK_WAITLOCK(lockp))
3203 #define IS_LOCK_CLIENTONLY(lockp) ((((lockp)->scp->flags & CM_SCACHEFLAG_RO) == CM_SCACHEFLAG_RO) || (((lockp)->flags & CM_FILELOCK_FLAG_CLIENTONLY) == CM_FILELOCK_FLAG_CLIENTONLY))
3206 #define INTERSECT_RANGE(r1,r2) (((r2).offset+(r2).length) > (r1).offset && ((r1).offset +(r1).length) > (r2).offset)
3209 #define CONTAINS_RANGE(r1,r2) (((r2).offset+(r2).length) <= ((r1).offset+(r1).length) && (r1).offset <= (r2).offset)
3211 #if defined(VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS) && !defined(LOCK_TESTING)
3212 #define SCP_SUPPORTS_BRLOCKS(scp) ((scp)->cbServerp && ((scp)->cbServerp->capabilities & VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS))
3214 #define SCP_SUPPORTS_BRLOCKS(scp) (1)
3217 #define SERVERLOCKS_ENABLED(scp) (!((scp)->flags & CM_SCACHEFLAG_RO) && cm_enableServerLocks && SCP_SUPPORTS_BRLOCKS(scp))
3219 static void cm_LockRangeSubtract(cm_range_t * pos, const cm_range_t * neg)
3221 afs_int64 int_begin;
3224 int_begin = MAX(pos->offset, neg->offset);
3225 int_end = MIN(pos->offset+pos->length, neg->offset+neg->length);
3227 if (int_begin < int_end) {
3228 if (int_begin == pos->offset) {
3229 pos->length = pos->offset + pos->length - int_end;
3230 pos->offset = int_end;
3231 } else if (int_end == pos->offset + pos->length) {
3232 pos->length = int_begin - pos->offset;
3235 /* We only subtract ranges if the resulting range is
3236 contiguous. If we try to support non-contigous ranges, we
3237 aren't actually improving performance. */
3241 /* Called with scp->mx held. Returns 0 if all is clear to read the
3242 specified range by the client identified by key.
3244 long cm_LockCheckRead(cm_scache_t *scp,
3245 LARGE_INTEGER LOffset,
3246 LARGE_INTEGER LLength,
3249 #ifndef ADVISORY_LOCKS
3251 cm_file_lock_t *fileLock;
3255 int substract_ranges = FALSE;
3257 range.offset = LOffset.QuadPart;
3258 range.length = LLength.QuadPart;
3262 1. for all _a_ in (Offset,+Length), all of the following is true:
3264 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3265 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3268 1.2 For each LOST lock L in S->fileLocks such that _a_ in
3269 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3274 lock_ObtainRead(&cm_scacheLock);
3276 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
3278 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
3280 if (INTERSECT_RANGE(range, fileLock->range)) {
3281 if (IS_LOCK_ACTIVE(fileLock)) {
3282 if (fileLock->key == key) {
3284 /* If there is an active lock for this client, it
3285 is safe to substract ranges.*/
3286 cm_LockRangeSubtract(&range, &fileLock->range);
3287 substract_ranges = TRUE;
3289 if (fileLock->lockType != LockRead) {
3290 code = CM_ERROR_LOCK_CONFLICT;
3294 /* even if the entire range is locked for reading,
3295 we still can't grant the lock at this point
3296 because the client may have lost locks. That
3297 is, unless we have already seen an active lock
3298 belonging to the client, in which case there
3299 can't be any lost locks for this client. */
3300 if (substract_ranges)
3301 cm_LockRangeSubtract(&range, &fileLock->range);
3303 } else if (IS_LOCK_LOST(fileLock) &&
3304 (fileLock->key == key || fileLock->lockType == LockWrite)) {
3305 code = CM_ERROR_BADFD;
3311 lock_ReleaseRead(&cm_scacheLock);
3313 osi_Log4(afsd_logp, "cm_LockCheckRead scp 0x%x offset %d length %d code 0x%x",
3314 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
3325 /* Called with scp->mx held. Returns 0 if all is clear to write the
3326 specified range by the client identified by key.
3328 long cm_LockCheckWrite(cm_scache_t *scp,
3329 LARGE_INTEGER LOffset,
3330 LARGE_INTEGER LLength,
3333 #ifndef ADVISORY_LOCKS
3335 cm_file_lock_t *fileLock;
3340 range.offset = LOffset.QuadPart;
3341 range.length = LLength.QuadPart;
3344 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
3346 2. for all _a_ in (Offset,+Length), one of the following is true:
3348 2.1 Byte _a_ of S is unowned AND there does not exist a LOST
3349 lock L such that _a_ in (L->LOffset,+L->LLength).
3351 2.2 Byte _a_ of S is owned by C under lock L AND L->LockType is
3355 lock_ObtainRead(&cm_scacheLock);
3357 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
3359 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
3361 if (INTERSECT_RANGE(range, fileLock->range)) {
3362 if (IS_LOCK_ACTIVE(fileLock)) {
3363 if (fileLock->key == key) {
3364 if (fileLock->lockType == LockWrite) {
3366 /* if there is an active lock for this client, it
3367 is safe to substract ranges */
3368 cm_LockRangeSubtract(&range, &fileLock->range);
3370 code = CM_ERROR_LOCK_CONFLICT;
3374 code = CM_ERROR_LOCK_CONFLICT;
3377 } else if (IS_LOCK_LOST(fileLock)) {
3378 code = CM_ERROR_BADFD;
3384 lock_ReleaseRead(&cm_scacheLock);
3386 osi_Log4(afsd_logp, "cm_LockCheckWrite scp 0x%x offset %d length %d code 0x%x",
3387 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
3399 static void cm_LockMarkSCacheLost(cm_scache_t * scp);
3401 /* Called with cm_scacheLock write locked */
3402 static cm_file_lock_t * cm_GetFileLock(void) {
3405 l = (cm_file_lock_t *) cm_freeFileLocks;
3407 osi_QRemove(&cm_freeFileLocks, &l->q);
3409 l = malloc(sizeof(cm_file_lock_t));
3413 memset(l, 0, sizeof(cm_file_lock_t));
3418 /* Called with cm_scacheLock write locked */
3419 static void cm_PutFileLock(cm_file_lock_t *l) {
3420 osi_QAdd(&cm_freeFileLocks, &l->q);
3423 /* called with scp->mx held. May release it during processing, but
3424 leaves it held on exit. */
3425 long cm_IntSetLock(cm_scache_t * scp, cm_user_t * userp, int lockType,
3431 struct rx_connection * callp;
3434 tfid.Volume = scp->fid.volume;
3435 tfid.Vnode = scp->fid.vnode;
3436 tfid.Unique = scp->fid.unique;
3439 osi_Log2(afsd_logp, "CALL SetLock scp 0x%p for lock %d", scp, lockType);
3441 lock_ReleaseMutex(&scp->mx);
3444 code = cm_Conn(&cfid, userp, reqp, &connp);
3448 callp = cm_GetRxConn(connp);
3449 code = RXAFS_SetLock(callp, &tfid, lockType,
3451 rx_PutConnection(callp);
3453 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
3456 code = cm_MapRPCError(code, reqp);
3458 osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
3460 osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
3463 lock_ObtainMutex(&scp->mx);
3468 /* called with scp->mx held. Releases it during processing */
3469 long cm_IntReleaseLock(cm_scache_t * scp, cm_user_t * userp,