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 /* make the attr structure */
2209 cm_StatusFromAttr(&afsInStatus, scp, attrp);
2211 tfid.Volume = scp->fid.volume;
2212 tfid.Vnode = scp->fid.vnode;
2213 tfid.Unique = scp->fid.unique;
2215 lock_ReleaseMutex(&scp->mx);
2219 /* now make the RPC */
2220 osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%p", scp);
2222 code = cm_Conn(&scp->fid, userp, reqp, &connp);
2226 callp = cm_GetRxConn(connp);
2227 code = RXAFS_StoreStatus(callp, &tfid,
2228 &afsInStatus, &afsOutStatus, &volSync);
2229 rx_PutConnection(callp);
2231 } while (cm_Analyze(connp, userp, reqp,
2232 &scp->fid, &volSync, NULL, NULL, code));
2233 code = cm_MapRPCError(code, reqp);
2236 osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
2238 osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
2240 lock_ObtainMutex(&scp->mx);
2241 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
2243 cm_MergeStatus(scp, &afsOutStatus, &volSync, userp,
2244 CM_MERGEFLAG_FORCE);
2246 /* if we're changing the mode bits, discard the ACL cache,
2247 * since we changed the mode bits.
2249 if (afsInStatus.Mask & AFS_SETMODE) cm_FreeAllACLEnts(scp);
2250 lock_ReleaseMutex(&scp->mx);
2254 long cm_Create(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2255 cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
2260 cm_callbackRequest_t cbReq;
2265 AFSStoreStatus inStatus;
2266 AFSFetchStatus updatedDirStatus;
2267 AFSFetchStatus newFileStatus;
2268 AFSCallBack newFileCallback;
2270 struct rx_connection * callp;
2272 /* can't create names with @sys in them; must expand it manually first.
2273 * return "invalid request" if they try.
2275 if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2276 return CM_ERROR_ATSYS;
2279 /* before starting the RPC, mark that we're changing the file data, so
2280 * that someone who does a chmod will know to wait until our call
2283 lock_ObtainMutex(&dscp->mx);
2284 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2286 cm_StartCallbackGrantingCall(NULL, &cbReq);
2288 lock_ReleaseMutex(&dscp->mx);
2294 cm_StatusFromAttr(&inStatus, NULL, attrp);
2296 /* try the RPC now */
2297 osi_Log1(afsd_logp, "CALL CreateFile scp 0x%p", dscp);
2299 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2303 dirAFSFid.Volume = dscp->fid.volume;
2304 dirAFSFid.Vnode = dscp->fid.vnode;
2305 dirAFSFid.Unique = dscp->fid.unique;
2307 callp = cm_GetRxConn(connp);
2308 code = RXAFS_CreateFile(connp->callp, &dirAFSFid, namep,
2309 &inStatus, &newAFSFid, &newFileStatus,
2310 &updatedDirStatus, &newFileCallback,
2312 rx_PutConnection(callp);
2314 } while (cm_Analyze(connp, userp, reqp,
2315 &dscp->fid, &volSync, NULL, &cbReq, code));
2316 code = cm_MapRPCError(code, reqp);
2319 osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
2321 osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
2323 lock_ObtainMutex(&dscp->mx);
2324 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2326 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2328 lock_ReleaseMutex(&dscp->mx);
2330 /* now try to create the file's entry, too, but be careful to
2331 * make sure that we don't merge in old info. Since we weren't locking
2332 * out any requests during the file's creation, we may have pretty old
2336 newFid.cell = dscp->fid.cell;
2337 newFid.volume = dscp->fid.volume;
2338 newFid.vnode = newAFSFid.Vnode;
2339 newFid.unique = newAFSFid.Unique;
2340 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2342 lock_ObtainMutex(&scp->mx);
2343 scp->creator = userp; /* remember who created it */
2344 if (!cm_HaveCallback(scp)) {
2345 cm_MergeStatus(scp, &newFileStatus, &volSync,
2347 cm_EndCallbackGrantingCall(scp, &cbReq,
2348 &newFileCallback, 0);
2351 lock_ReleaseMutex(&scp->mx);
2356 /* make sure we end things properly */
2358 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2363 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
2367 lock_ObtainWrite(&scp->bufCreateLock);
2368 code = buf_CleanVnode(scp, userp, reqp);
2369 lock_ReleaseWrite(&scp->bufCreateLock);
2371 lock_ObtainMutex(&scp->mx);
2373 if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
2374 | CM_SCACHEMASK_CLIENTMODTIME
2375 | CM_SCACHEMASK_LENGTH))
2376 code = cm_StoreMini(scp, userp, reqp);
2378 if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) {
2379 code = (scp->flags & CM_SCACHEFLAG_OVERQUOTA) ? CM_ERROR_QUOTA : CM_ERROR_SPACE;
2380 scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE);
2383 lock_ReleaseMutex(&scp->mx);
2388 long cm_MakeDir(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2389 cm_user_t *userp, cm_req_t *reqp)
2394 cm_callbackRequest_t cbReq;
2399 AFSStoreStatus inStatus;
2400 AFSFetchStatus updatedDirStatus;
2401 AFSFetchStatus newDirStatus;
2402 AFSCallBack newDirCallback;
2404 struct rx_connection * callp;
2406 /* can't create names with @sys in them; must expand it manually first.
2407 * return "invalid request" if they try.
2409 if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2410 return CM_ERROR_ATSYS;
2413 /* before starting the RPC, mark that we're changing the directory
2414 * data, so that someone who does a chmod on the dir will wait until
2415 * our call completes.
2417 lock_ObtainMutex(&dscp->mx);
2418 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2420 cm_StartCallbackGrantingCall(NULL, &cbReq);
2422 lock_ReleaseMutex(&dscp->mx);
2428 cm_StatusFromAttr(&inStatus, NULL, attrp);
2430 /* try the RPC now */
2431 osi_Log1(afsd_logp, "CALL MakeDir scp 0x%p", dscp);
2433 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2437 dirAFSFid.Volume = dscp->fid.volume;
2438 dirAFSFid.Vnode = dscp->fid.vnode;
2439 dirAFSFid.Unique = dscp->fid.unique;
2441 callp = cm_GetRxConn(connp);
2442 code = RXAFS_MakeDir(connp->callp, &dirAFSFid, namep,
2443 &inStatus, &newAFSFid, &newDirStatus,
2444 &updatedDirStatus, &newDirCallback,
2446 rx_PutConnection(callp);
2448 } while (cm_Analyze(connp, userp, reqp,
2449 &dscp->fid, &volSync, NULL, &cbReq, code));
2450 code = cm_MapRPCError(code, reqp);
2453 osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
2455 osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
2457 lock_ObtainMutex(&dscp->mx);
2458 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2460 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2462 lock_ReleaseMutex(&dscp->mx);
2464 /* now try to create the new dir's entry, too, but be careful to
2465 * make sure that we don't merge in old info. Since we weren't locking
2466 * out any requests during the file's creation, we may have pretty old
2470 newFid.cell = dscp->fid.cell;
2471 newFid.volume = dscp->fid.volume;
2472 newFid.vnode = newAFSFid.Vnode;
2473 newFid.unique = newAFSFid.Unique;
2474 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2476 lock_ObtainMutex(&scp->mx);
2477 if (!cm_HaveCallback(scp)) {
2478 cm_MergeStatus(scp, &newDirStatus, &volSync,
2480 cm_EndCallbackGrantingCall(scp, &cbReq,
2481 &newDirCallback, 0);
2484 lock_ReleaseMutex(&scp->mx);
2485 cm_ReleaseSCache(scp);
2489 /* make sure we end things properly */
2491 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2493 /* and return error code */
2497 long cm_Link(cm_scache_t *dscp, char *namep, cm_scache_t *sscp, long flags,
2498 cm_user_t *userp, cm_req_t *reqp)
2503 AFSFid existingAFSFid;
2504 AFSFetchStatus updatedDirStatus;
2505 AFSFetchStatus newLinkStatus;
2507 struct rx_connection * callp;
2509 if (dscp->fid.cell != sscp->fid.cell ||
2510 dscp->fid.volume != sscp->fid.volume) {
2511 return CM_ERROR_CROSSDEVLINK;
2514 lock_ObtainMutex(&dscp->mx);
2515 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2516 lock_ReleaseMutex(&dscp->mx);
2521 /* try the RPC now */
2522 osi_Log1(afsd_logp, "CALL Link scp 0x%p", dscp);
2524 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2527 dirAFSFid.Volume = dscp->fid.volume;
2528 dirAFSFid.Vnode = dscp->fid.vnode;
2529 dirAFSFid.Unique = dscp->fid.unique;
2531 existingAFSFid.Volume = sscp->fid.volume;
2532 existingAFSFid.Vnode = sscp->fid.vnode;
2533 existingAFSFid.Unique = sscp->fid.unique;
2535 callp = cm_GetRxConn(connp);
2536 code = RXAFS_Link(callp, &dirAFSFid, namep, &existingAFSFid,
2537 &newLinkStatus, &updatedDirStatus, &volSync);
2538 rx_PutConnection(callp);
2539 osi_Log1(smb_logp," RXAFS_Link returns 0x%x", code);
2541 } while (cm_Analyze(connp, userp, reqp,
2542 &dscp->fid, &volSync, NULL, NULL, code));
2544 code = cm_MapRPCError(code, reqp);
2547 osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
2549 osi_Log0(afsd_logp, "CALL Link SUCCESS");
2551 lock_ObtainMutex(&dscp->mx);
2552 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2554 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2556 lock_ReleaseMutex(&dscp->mx);
2561 long cm_SymLink(cm_scache_t *dscp, char *namep, char *contentsp, long flags,
2562 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
2570 AFSStoreStatus inStatus;
2571 AFSFetchStatus updatedDirStatus;
2572 AFSFetchStatus newLinkStatus;
2574 struct rx_connection * callp;
2576 /* before starting the RPC, mark that we're changing the directory data,
2577 * so that someone who does a chmod on the dir will wait until our
2580 lock_ObtainMutex(&dscp->mx);
2581 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2582 lock_ReleaseMutex(&dscp->mx);
2587 cm_StatusFromAttr(&inStatus, NULL, attrp);
2589 /* try the RPC now */
2590 osi_Log1(afsd_logp, "CALL Symlink scp 0x%p", dscp);
2592 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2596 dirAFSFid.Volume = dscp->fid.volume;
2597 dirAFSFid.Vnode = dscp->fid.vnode;
2598 dirAFSFid.Unique = dscp->fid.unique;
2600 callp = cm_GetRxConn(connp);
2601 code = RXAFS_Symlink(callp, &dirAFSFid, namep, contentsp,
2602 &inStatus, &newAFSFid, &newLinkStatus,
2603 &updatedDirStatus, &volSync);
2604 rx_PutConnection(callp);
2606 } while (cm_Analyze(connp, userp, reqp,
2607 &dscp->fid, &volSync, NULL, NULL, code));
2608 code = cm_MapRPCError(code, reqp);
2611 osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
2613 osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
2615 lock_ObtainMutex(&dscp->mx);
2616 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2618 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2620 lock_ReleaseMutex(&dscp->mx);
2622 /* now try to create the new dir's entry, too, but be careful to
2623 * make sure that we don't merge in old info. Since we weren't locking
2624 * out any requests during the file's creation, we may have pretty old
2628 newFid.cell = dscp->fid.cell;
2629 newFid.volume = dscp->fid.volume;
2630 newFid.vnode = newAFSFid.Vnode;
2631 newFid.unique = newAFSFid.Unique;
2632 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2634 lock_ObtainMutex(&scp->mx);
2635 if (!cm_HaveCallback(scp)) {
2636 cm_MergeStatus(scp, &newLinkStatus, &volSync,
2639 lock_ReleaseMutex(&scp->mx);
2640 cm_ReleaseSCache(scp);
2644 /* and return error code */
2648 long cm_RemoveDir(cm_scache_t *dscp, char *namep, cm_user_t *userp,
2655 AFSFetchStatus updatedDirStatus;
2657 struct rx_connection * callp;
2659 /* before starting the RPC, mark that we're changing the directory data,
2660 * so that someone who does a chmod on the dir will wait until our
2663 lock_ObtainMutex(&dscp->mx);
2664 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2665 lock_ReleaseMutex(&dscp->mx);
2671 /* try the RPC now */
2672 osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%p", dscp);
2674 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2678 dirAFSFid.Volume = dscp->fid.volume;
2679 dirAFSFid.Vnode = dscp->fid.vnode;
2680 dirAFSFid.Unique = dscp->fid.unique;
2682 callp = cm_GetRxConn(connp);
2683 code = RXAFS_RemoveDir(callp, &dirAFSFid, namep,
2684 &updatedDirStatus, &volSync);
2685 rx_PutConnection(callp);
2687 } while (cm_Analyze(connp, userp, reqp,
2688 &dscp->fid, &volSync, NULL, NULL, code));
2689 code = cm_MapRPCErrorRmdir(code, reqp);
2692 osi_Log1(afsd_logp, "CALL RemoveDir FAILURE, code 0x%x", code);
2694 osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
2696 lock_ObtainMutex(&dscp->mx);
2697 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2699 cm_dnlcRemove(dscp, namep);
2700 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2702 lock_ReleaseMutex(&dscp->mx);
2704 /* and return error code */
2708 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
2710 /* grab mutex on contents */
2711 lock_ObtainMutex(&scp->mx);
2713 /* reset the prefetch info */
2714 scp->prefetch.base.LowPart = 0; /* base */
2715 scp->prefetch.base.HighPart = 0;
2716 scp->prefetch.end.LowPart = 0; /* and end */
2717 scp->prefetch.end.HighPart = 0;
2719 /* release mutex on contents */
2720 lock_ReleaseMutex(&scp->mx);
2726 long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
2727 char *newNamep, cm_user_t *userp, cm_req_t *reqp)
2731 AFSFid oldDirAFSFid;
2732 AFSFid newDirAFSFid;
2734 AFSFetchStatus updatedOldDirStatus;
2735 AFSFetchStatus updatedNewDirStatus;
2738 struct rx_connection * callp;
2740 /* before starting the RPC, mark that we're changing the directory data,
2741 * so that someone who does a chmod on the dir will wait until our call
2742 * completes. We do this in vnode order so that we don't deadlock,
2743 * which makes the code a little verbose.
2745 if (oldDscp == newDscp) {
2746 /* check for identical names */
2747 if (strcmp(oldNamep, newNamep) == 0)
2748 return CM_ERROR_RENAME_IDENTICAL;
2751 lock_ObtainMutex(&oldDscp->mx);
2752 cm_dnlcRemove(oldDscp, oldNamep);
2753 cm_dnlcRemove(oldDscp, newNamep);
2754 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2755 CM_SCACHESYNC_STOREDATA);
2756 lock_ReleaseMutex(&oldDscp->mx);
2759 /* two distinct dir vnodes */
2761 if (oldDscp->fid.cell != newDscp->fid.cell ||
2762 oldDscp->fid.volume != newDscp->fid.volume)
2763 return CM_ERROR_CROSSDEVLINK;
2765 /* shouldn't happen that we have distinct vnodes for two
2766 * different files, but could due to deliberate attack, or
2767 * stale info. Avoid deadlocks and quit now.
2769 if (oldDscp->fid.vnode == newDscp->fid.vnode)
2770 return CM_ERROR_CROSSDEVLINK;
2772 if (oldDscp->fid.vnode < newDscp->fid.vnode) {
2773 lock_ObtainMutex(&oldDscp->mx);
2774 cm_dnlcRemove(oldDscp, oldNamep);
2775 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2776 CM_SCACHESYNC_STOREDATA);
2777 lock_ReleaseMutex(&oldDscp->mx);
2779 lock_ObtainMutex(&newDscp->mx);
2780 cm_dnlcRemove(newDscp, newNamep);
2781 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2782 CM_SCACHESYNC_STOREDATA);
2783 lock_ReleaseMutex(&newDscp->mx);
2785 /* cleanup first one */
2786 lock_ObtainMutex(&newDscp->mx);
2787 cm_SyncOpDone(oldDscp, NULL,
2788 CM_SCACHESYNC_STOREDATA);
2789 lock_ReleaseMutex(&oldDscp->mx);
2794 /* lock the new vnode entry first */
2795 lock_ObtainMutex(&newDscp->mx);
2796 cm_dnlcRemove(newDscp, newNamep);
2797 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2798 CM_SCACHESYNC_STOREDATA);
2799 lock_ReleaseMutex(&newDscp->mx);
2801 lock_ObtainMutex(&oldDscp->mx);
2802 cm_dnlcRemove(oldDscp, oldNamep);
2803 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2804 CM_SCACHESYNC_STOREDATA);
2805 lock_ReleaseMutex(&oldDscp->mx);
2807 /* cleanup first one */
2808 lock_ObtainMutex(&newDscp->mx);
2809 cm_SyncOpDone(newDscp, NULL,
2810 CM_SCACHESYNC_STOREDATA);
2811 lock_ReleaseMutex(&newDscp->mx);
2815 } /* two distinct vnodes */
2822 /* try the RPC now */
2823 osi_Log2(afsd_logp, "CALL Rename old scp 0x%p new scp 0x%p",
2826 code = cm_Conn(&oldDscp->fid, userp, reqp, &connp);
2830 oldDirAFSFid.Volume = oldDscp->fid.volume;
2831 oldDirAFSFid.Vnode = oldDscp->fid.vnode;
2832 oldDirAFSFid.Unique = oldDscp->fid.unique;
2833 newDirAFSFid.Volume = newDscp->fid.volume;
2834 newDirAFSFid.Vnode = newDscp->fid.vnode;
2835 newDirAFSFid.Unique = newDscp->fid.unique;
2837 callp = cm_GetRxConn(connp);
2838 code = RXAFS_Rename(callp, &oldDirAFSFid, oldNamep,
2839 &newDirAFSFid, newNamep,
2840 &updatedOldDirStatus, &updatedNewDirStatus,
2842 rx_PutConnection(callp);
2844 } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
2845 &volSync, NULL, NULL, code));
2846 code = cm_MapRPCError(code, reqp);
2849 osi_Log1(afsd_logp, "CALL Rename FAILURE, code 0x%x", code);
2851 osi_Log0(afsd_logp, "CALL Rename SUCCESS");
2853 /* update the individual stat cache entries for the directories */
2854 lock_ObtainMutex(&oldDscp->mx);
2855 cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
2857 cm_MergeStatus(oldDscp, &updatedOldDirStatus, &volSync,
2860 lock_ReleaseMutex(&oldDscp->mx);
2862 /* and update it for the new one, too, if necessary */
2864 lock_ObtainMutex(&newDscp->mx);
2865 cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
2867 cm_MergeStatus(newDscp, &updatedNewDirStatus, &volSync,
2870 lock_ReleaseMutex(&newDscp->mx);
2873 /* and return error code */
2877 /* Byte range locks:
2879 The OpenAFS Windows client has to fake byte range locks given no
2880 server side support for such locks. This is implemented as keyed
2881 byte range locks on the cache manager.
2883 Keyed byte range locks:
2885 Each cm_scache_t structure keeps track of a list of keyed locks.
2886 The key for a lock identifies an owner of a set of locks (referred
2887 to as a client). Each key is represented by a value. The set of
2888 key values used within a specific cm_scache_t structure form a
2889 namespace that has a scope of just that cm_scache_t structure. The
2890 same key value can be used with another cm_scache_t structure and
2891 correspond to a completely different client. However it is
2892 advantageous for the SMB or IFS layer to make sure that there is a
2893 1-1 mapping between client and keys over all cm_scache_t objects.
2895 Assume a client C has key Key(C) (although, since the scope of the
2896 key is a cm_scache_t, the key can be Key(C,S), where S is the
2897 cm_scache_t. But assume a 1-1 relation between keys and clients).
2898 A byte range (O,+L) denotes byte addresses (O) through (O+L-1)
2899 inclusive (a.k.a. [O,O+L-1]). The function Key(x) is implemented
2900 through cm_generateKey() function for both SMB and IFS.
2902 The list of locks for a cm_scache_t object S is maintained in
2903 S->fileLocks. The cache manager will set a lock on the AFS file
2904 server in order to assert the locks in S->fileLocks. If only
2905 shared locks are in place for S, then the cache manager will obtain
2906 a LockRead lock, while if there are any exclusive locks, it will
2907 obtain a LockWrite lock. If the exclusive locks are all released
2908 while the shared locks remain, then the cache manager will
2909 downgrade the lock from LockWrite to LockRead. Similarly, if an
2910 exclusive lock is obtained when only shared locks exist, then the
2911 cache manager will try to upgrade the lock from LockRead to
2914 Each lock L owned by client C maintains a key L->key such that
2915 L->key == Key(C), the effective range defined by L->LOffset and
2916 L->LLength such that the range of bytes affected by the lock is
2917 (L->LOffset, +L->LLength), a type maintained in L->LockType which
2918 is either exclusive or shared.
2922 A lock exists iff it is in S->fileLocks for some cm_scache_t
2923 S. Existing locks are in one of the following states: ACTIVE,
2924 WAITLOCK, WAITUNLOCK, LOST, DELETED.
2926 The following sections describe each lock and the associated
2929 1. ACTIVE: A lock L is ACTIVE iff the cache manager has asserted
2930 the lock with the AFS file server. This type of lock can be
2931 exercised by a client to read or write to the locked region (as
2934 1.1 ACTIVE->LOST: When the AFS file server fails to extend a
2935 server lock that was required to assert the lock. Before
2936 marking the lock as lost, the cache manager checks if the file
2937 has changed on the server. If the file has not changed, then
2938 the cache manager will attempt to obtain a new server lock
2939 that is sufficient to assert the client side locks for the
2940 file. If any of these fail, the lock is marked as LOST.
2941 Otherwise, it is left as ACTIVE.
2943 1.2 ACTIVE->DELETED: Lock is released.
2945 2. WAITLOCK: A lock is in a WAITLOCK state if the cache manager
2946 grants the lock but the lock is yet to be asserted with the AFS
2947 file server. Once the file server grants the lock, the state
2948 will transition to an ACTIVE lock.
2950 2.1 WAITLOCK->ACTIVE: The server granted the lock.
2952 2.2 WAITLOCK->DELETED: Lock is abandoned, or timed out during
2955 2.3 WAITLOCK->LOST: One or more locks from this client were
2956 marked as LOST. No further locks will be granted to this
2957 client until all lost locks are removed.
2959 3. WAITUNLOCK: A lock is in a WAITUNLOCK state if the cache manager
2960 receives a request for a lock that conflicts with an existing
2961 ACTIVE or WAITLOCK lock. The lock will be placed in the queue
2962 and will be granted at such time the conflicting locks are
2963 removed, at which point the state will transition to either
2966 3.1 WAITUNLOCK->ACTIVE: The conflicting lock was removed. The
2967 current serverLock is sufficient to assert this lock, or a
2968 sufficient serverLock is obtained.
2970 3.2 WAITUNLOCK->WAITLOCK: The conflicting lock was removed,
2971 however the required serverLock is yet to be asserted with the
2974 3.3 WAITUNLOCK->DELETED: The lock is abandoned, timed out or
2977 3.5 WAITUNLOCK->LOST: One or more locks from this client were
2978 marked as LOST. No further locks will be granted to this
2979 client until all lost locks are removed.
2981 4. LOST: A lock L is LOST if the server lock that was required to
2982 assert the lock could not be obtained or if it could not be
2983 extended, or if other locks by the same client were LOST.
2984 Essentially, once a lock is LOST, the contract between the cache
2985 manager and that specific client is no longer valid.
2987 The cache manager rechecks the server lock once every minute and
2988 extends it as appropriate. If this is not done for 5 minutes,
2989 the AFS file server will release the lock (the 5 minute timeout
2990 is based on current file server code and is fairly arbitrary).
2991 Once released, the lock cannot be re-obtained without verifying
2992 that the contents of the file hasn't been modified since the
2993 time the lock was released. Re-obtaining the lock without
2994 verifying this may lead to data corruption. If the lock can not
2995 be obtained safely, then all active locks for the cm_scache_t
2998 4.1 LOST->DELETED: The lock is released.
3000 5. DELETED: The lock is no longer relevant. Eventually, it will
3001 get removed from the cm_scache_t. In the meantime, it will be
3002 treated as if it does not exist.
3004 5.1 DELETED->not exist: The lock is removed from the
3007 The following are classifications of locks based on their state.
3009 6* A lock L is ACCEPTED if it is ACTIVE or WAITLOCK. These locks
3010 have been accepted by the cache manager, but may or may not have
3011 been granted back to the client.
3013 7* A lock L is QUEUED if it is ACTIVE, WAITLOCK or WAITUNLOCK.
3015 8* A lock L is WAITING if it is WAITLOCK or WAITUNLOCK.
3019 A client C can READ range (Offset,+Length) of a file represented by
3020 cm_scache_t S iff (1):
3022 1. for all _a_ in (Offset,+Length), all of the following is true:
3024 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3025 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3028 1.2 For each LOST lock L in S->fileLocks such that _a_ in
3029 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3032 (When locks are lost on an cm_scache_t, all locks are lost. By
3033 4.2 (below), if there is an exclusive LOST lock, then there
3034 can't be any overlapping ACTIVE locks.)
3036 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
3038 2. for all _a_ in (Offset,+Length), one of the following is true:
3040 2.1 Byte _a_ of S is unowned (as specified in 1.1) AND there
3041 does not exist a LOST lock L such that _a_ in
3042 (L->LOffset,+L->LLength).
3044 2.2 Byte _a_ of S is owned by C under lock L (as specified in
3045 1.2) AND L->LockType is exclusive.
3047 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
3049 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
3052 3.1 If L->LockType is exclusive then there does NOT exist a
3053 ACCEPTED lock M in S->fileLocks such that _a_ in
3054 (M->LOffset,+M->LLength).
3056 (If we count all QUEUED locks then we hit cases such as
3057 cascading waiting locks where the locks later on in the queue
3058 can be granted without compromising file integrity. On the
3059 other hand if only ACCEPTED locks are considered, then locks
3060 that were received earlier may end up waiting for locks that
3061 were received later to be unlocked. The choice of ACCEPTED
3062 locks was made to mimic the Windows byte range lock
3065 3.2 If L->LockType is shared then for each ACCEPTED lock M in
3066 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
3067 M->LockType is shared.
3069 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
3071 4.1 M->key != Key(C)
3073 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
3074 and (M->LOffset,+M->LLength) do not intersect.
3076 (Note: If a client loses a lock, it loses all locks.
3077 Subsequently, it will not be allowed to obtain any more locks
3078 until all existing LOST locks that belong to the client are
3079 released. Once all locks are released by a single client,
3080 there exists no further contract between the client and AFS
3081 about the contents of the file, hence the client can then
3082 proceed to obtain new locks and establish a new contract.
3084 This doesn't quite work as you think it should, because most
3085 applications aren't built to deal with losing locks they
3086 thought they once had. For now, we don't have a good
3087 solution to lost locks.
3089 Also, for consistency reasons, we have to hold off on
3090 granting locks that overlap exclusive LOST locks.)
3092 A client C can only unlock locks L in S->fileLocks which have
3095 The representation and invariants are as follows:
3097 - Each cm_scache_t structure keeps:
3099 - A queue of byte-range locks (cm_scache_t::fileLocks) which
3100 are of type cm_file_lock_t.
3102 - A record of the highest server-side lock that has been
3103 obtained for this object (cm_scache_t::serverLock), which is
3104 one of (-1), LockRead, LockWrite.
3106 - A count of ACCEPTED exclusive and shared locks that are in the
3107 queue (cm_scache_t::sharedLocks and
3108 cm_scache_t::exclusiveLocks)
3110 - Each cm_file_lock_t structure keeps:
3112 - The type of lock (cm_file_lock_t::LockType)
3114 - The key associated with the lock (cm_file_lock_t::key)
3116 - The offset and length of the lock (cm_file_lock_t::LOffset
3117 and cm_file_lock_t::LLength)
3119 - The state of the lock.
3121 - Time of issuance or last successful extension
3123 Semantic invariants:
3125 I1. The number of ACCEPTED locks in S->fileLocks are
3126 (S->sharedLocks + S->exclusiveLocks)
3128 External invariants:
3130 I3. S->serverLock is the lock that we have asserted with the
3131 AFS file server for this cm_scache_t.
3133 I4. S->serverLock == LockRead iff there is at least one ACTIVE
3134 shared lock, but no ACTIVE exclusive locks.
3136 I5. S->serverLock == LockWrite iff there is at least one ACTIVE
3139 I6. If L is a LOST lock, then for each lock M in S->fileLocks,
3140 M->key == L->key IMPLIES M is LOST or DELETED.
3145 #define IS_LOCK_ACTIVE(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == 0)
3147 #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)
3149 #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)
3151 #define IS_LOCK_LOST(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_LOST)
3153 #define IS_LOCK_DELETED(lockp) (((lockp)->flags & CM_FILELOCK_FLAG_DELETED) == CM_FILELOCK_FLAG_DELETED)
3156 #define IS_LOCK_ACCEPTED(lockp) (IS_LOCK_ACTIVE(lockp) || IS_LOCK_WAITLOCK(lockp))
3159 #define IS_LOCK_CLIENTONLY(lockp) ((((lockp)->scp->flags & CM_SCACHEFLAG_RO) == CM_SCACHEFLAG_RO) || (((lockp)->flags & CM_FILELOCK_FLAG_CLIENTONLY) == CM_FILELOCK_FLAG_CLIENTONLY))
3162 #define INTERSECT_RANGE(r1,r2) (((r2).offset+(r2).length) > (r1).offset && ((r1).offset +(r1).length) > (r2).offset)
3165 #define CONTAINS_RANGE(r1,r2) (((r2).offset+(r2).length) <= ((r1).offset+(r1).length) && (r1).offset <= (r2).offset)
3167 #if defined(VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS) && !defined(LOCK_TESTING)
3168 #define SCP_SUPPORTS_BRLOCKS(scp) ((scp)->cbServerp && ((scp)->cbServerp->capabilities & VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS))
3170 #define SCP_SUPPORTS_BRLOCKS(scp) (1)
3173 #define SERVERLOCKS_ENABLED(scp) (!((scp)->flags & CM_SCACHEFLAG_RO) && cm_enableServerLocks && SCP_SUPPORTS_BRLOCKS(scp))
3175 static void cm_LockRangeSubtract(cm_range_t * pos, const cm_range_t * neg)
3177 afs_int64 int_begin;
3180 int_begin = MAX(pos->offset, neg->offset);
3181 int_end = MIN(pos->offset+pos->length, neg->offset+neg->length);
3183 if (int_begin < int_end) {
3184 if (int_begin == pos->offset) {
3185 pos->length = pos->offset + pos->length - int_end;
3186 pos->offset = int_end;
3187 } else if (int_end == pos->offset + pos->length) {
3188 pos->length = int_begin - pos->offset;
3191 /* We only subtract ranges if the resulting range is
3192 contiguous. If we try to support non-contigous ranges, we
3193 aren't actually improving performance. */
3197 /* Called with scp->mx held. Returns 0 if all is clear to read the
3198 specified range by the client identified by key.
3200 long cm_LockCheckRead(cm_scache_t *scp,
3201 LARGE_INTEGER LOffset,
3202 LARGE_INTEGER LLength,
3205 #ifndef ADVISORY_LOCKS
3207 cm_file_lock_t *fileLock;
3211 int substract_ranges = FALSE;
3213 range.offset = LOffset.QuadPart;
3214 range.length = LLength.QuadPart;
3218 1. for all _a_ in (Offset,+Length), all of the following is true:
3220 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3221 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3224 1.2 For each LOST lock L in S->fileLocks such that _a_ in
3225 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3230 lock_ObtainRead(&cm_scacheLock);
3232 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
3234 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
3236 if (INTERSECT_RANGE(range, fileLock->range)) {
3237 if (IS_LOCK_ACTIVE(fileLock)) {
3238 if (fileLock->key == key) {
3240 /* If there is an active lock for this client, it
3241 is safe to substract ranges.*/
3242 cm_LockRangeSubtract(&range, &fileLock->range);
3243 substract_ranges = TRUE;
3245 if (fileLock->lockType != LockRead) {
3246 code = CM_ERROR_LOCK_CONFLICT;
3250 /* even if the entire range is locked for reading,
3251 we still can't grant the lock at this point
3252 because the client may have lost locks. That
3253 is, unless we have already seen an active lock
3254 belonging to the client, in which case there
3255 can't be any lost locks for this client. */
3256 if (substract_ranges)
3257 cm_LockRangeSubtract(&range, &fileLock->range);
3259 } else if (IS_LOCK_LOST(fileLock) &&
3260 (fileLock->key == key || fileLock->lockType == LockWrite)) {
3261 code = CM_ERROR_BADFD;
3267 lock_ReleaseRead(&cm_scacheLock);
3269 osi_Log4(afsd_logp, "cm_LockCheckRead scp 0x%x offset %d length %d code 0x%x",
3270 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
3281 /* Called with scp->mx held. Returns 0 if all is clear to write the
3282 specified range by the client identified by key.
3284 long cm_LockCheckWrite(cm_scache_t *scp,
3285 LARGE_INTEGER LOffset,
3286 LARGE_INTEGER LLength,
3289 #ifndef ADVISORY_LOCKS
3291 cm_file_lock_t *fileLock;
3296 range.offset = LOffset.QuadPart;
3297 range.length = LLength.QuadPart;
3300 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
3302 2. for all _a_ in (Offset,+Length), one of the following is true:
3304 2.1 Byte _a_ of S is unowned AND there does not exist a LOST
3305 lock L such that _a_ in (L->LOffset,+L->LLength).
3307 2.2 Byte _a_ of S is owned by C under lock L AND L->LockType is
3311 lock_ObtainRead(&cm_scacheLock);
3313 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
3315 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
3317 if (INTERSECT_RANGE(range, fileLock->range)) {
3318 if (IS_LOCK_ACTIVE(fileLock)) {
3319 if (fileLock->key == key) {
3320 if (fileLock->lockType == LockWrite) {
3322 /* if there is an active lock for this client, it
3323 is safe to substract ranges */
3324 cm_LockRangeSubtract(&range, &fileLock->range);
3326 code = CM_ERROR_LOCK_CONFLICT;
3330 code = CM_ERROR_LOCK_CONFLICT;
3333 } else if (IS_LOCK_LOST(fileLock)) {
3334 code = CM_ERROR_BADFD;
3340 lock_ReleaseRead(&cm_scacheLock);
3342 osi_Log4(afsd_logp, "cm_LockCheckWrite scp 0x%x offset %d length %d code 0x%x",
3343 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
3355 static void cm_LockMarkSCacheLost(cm_scache_t * scp);
3357 /* Called with cm_scacheLock write locked */
3358 static cm_file_lock_t * cm_GetFileLock(void) {
3361 l = (cm_file_lock_t *) cm_freeFileLocks;
3363 osi_QRemove(&cm_freeFileLocks, &l->q);
3365 l = malloc(sizeof(cm_file_lock_t));
3369 memset(l, 0, sizeof(cm_file_lock_t));
3374 /* Called with cm_scacheLock write locked */
3375 static void cm_PutFileLock(cm_file_lock_t *l) {
3376 osi_QAdd(&cm_freeFileLocks, &l->q);
3379 /* called with scp->mx held. May release it during processing, but
3380 leaves it held on exit. */
3381 long cm_IntSetLock(cm_scache_t * scp, cm_user_t * userp, int lockType,
3387 struct rx_connection * callp;
3390 tfid.Volume = scp->fid.volume;
3391 tfid.Vnode = scp->fid.vnode;
3392 tfid.Unique = scp->fid.unique;
3395 osi_Log2(afsd_logp, "CALL SetLock scp 0x%p for lock %d", scp, lockType);
3397 lock_ReleaseMutex(&scp->mx);
3400 code = cm_Conn(&cfid, userp, reqp, &connp);
3404 callp = cm_GetRxConn(connp);
3405 code = RXAFS_SetLock(callp, &tfid, lockType,
3407 rx_PutConnection(callp);
3409 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
3412 code = cm_MapRPCError(code, reqp);
3414 osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
3416 osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
3419 lock_ObtainMutex(&scp->mx);
3424 /* called with scp->mx held. Releases it during processing */
3425 long cm_IntReleaseLock(cm_scache_t * scp, cm_user_t * userp,
3431 struct rx_connection * callp;
3434 tfid.Volume = scp->fid.volume;
3435 tfid.Vnode = scp->fid.vnode;
3436 tfid.Unique = scp->fid.unique;
3439 lock_ReleaseMutex(&scp->mx);
3441 osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
3444 code = cm_Conn(&cfid, userp, reqp, &connp);
3448 callp = cm_GetRxConn(connp);
3449 code = RXAFS_ReleaseLock(callp, &tfid, &volSync);
3450 rx_PutConnection(callp);
3452 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
3454 code = cm_MapRPCError(code, reqp);
3457 "CALL ReleaseLock FAILURE, code 0x%x", code);
3460 "CALL ReleaseLock SUCCESS");
3462 lock_ObtainMutex(&scp->mx);
3467 /* called with scp->mx held. May release it during processing, but
3468 will exit with lock held.
3472 - 0 if the user has permission to get the specified lock for the scp
3474 - CM_ERROR_NOACCESS if not
3476 Any other error from cm_SyncOp will be sent down untranslated.
3478 long cm_LockCheckPerms(cm_scache_t * scp,