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>
30 extern void afsi_log(char *pattern, ...);
33 int cm_enableServerLocks = 1;
35 int cm_followBackupPath = 0;
38 * Case-folding array. This was constructed by inspecting of SMBtrace output.
39 * I do not know anything more about it.
41 unsigned char cm_foldUpper[256] = {
42 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
43 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
44 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
45 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
46 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
47 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
48 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
49 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
50 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
51 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
52 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
53 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
54 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
55 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
56 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
57 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
58 0x80, 0x9a, 0x90, 0x41, 0x8e, 0x41, 0x8f, 0x80,
59 0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x8e, 0x8f,
60 0x90, 0x92, 0x92, 0x4f, 0x99, 0x4f, 0x55, 0x55,
61 0x59, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
62 0x41, 0x49, 0x4f, 0x55, 0xa5, 0xa5, 0x56, 0xa7,
63 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
64 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
65 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
66 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
67 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
68 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
69 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
70 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
71 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
72 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
73 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
77 * Case-insensitive string comparison. We used to use stricmp, but it doesn't
78 * know about 8-bit characters (e.g. 129 is lowercase u-umlaut, 154 is
79 * upper-case u-umlaut).
81 int cm_stricmp(const char *str1, const char *str2)
93 c1 = (char) cm_foldUpper[(unsigned char)(*str1++)];
94 c2 = (char) cm_foldUpper[(unsigned char)(*str2++)];
104 /* return success if we can open this file in this mode */
105 long cm_CheckOpen(cm_scache_t *scp, int openMode, int trunc, cm_user_t *userp,
113 rights |= PRSFS_READ;
114 if (openMode == 1 || openMode == 2 || trunc)
115 rights |= PRSFS_WRITE;
117 lock_ObtainWrite(&scp->rw);
119 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
120 CM_SCACHESYNC_GETSTATUS
121 | CM_SCACHESYNC_NEEDCALLBACK
122 | CM_SCACHESYNC_LOCK);
125 ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
126 scp->fileType == CM_SCACHETYPE_FILE) {
129 unsigned int sLockType;
130 LARGE_INTEGER LOffset, LLength;
132 /* Check if there's some sort of lock on the file at the
135 key = cm_GenerateKey(CM_SESSION_CMINT,0,0);
137 if (rights & PRSFS_WRITE)
140 sLockType = LOCKING_ANDX_SHARED_LOCK;
142 LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
143 LOffset.LowPart = CM_FLSHARE_OFFSET_LOW;
144 LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
145 LLength.LowPart = CM_FLSHARE_LENGTH_LOW;
147 code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
150 cm_Unlock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp);
152 /* In this case, we allow the file open to go through even
153 though we can't enforce mandatory locking on the
155 if (code == CM_ERROR_NOACCESS &&
156 !(rights & PRSFS_WRITE))
160 case CM_ERROR_ALLOFFLINE:
161 case CM_ERROR_ALLDOWN:
162 case CM_ERROR_ALLBUSY:
163 case CM_ERROR_TIMEDOUT:
165 case CM_ERROR_WOULDBLOCK:
168 code = CM_ERROR_SHARING_VIOLATION;
173 } else if (code != 0) {
177 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
181 lock_ReleaseWrite(&scp->rw);
186 /* return success if we can open this file in this mode */
187 long cm_CheckNTOpen(cm_scache_t *scp, unsigned int desiredAccess,
188 unsigned int createDisp, cm_user_t *userp, cm_req_t *reqp,
189 cm_lock_data_t **ldpp)
194 osi_assertx(ldpp != NULL, "null cm_lock_data_t");
197 /* Always allow delete; the RPC will tell us if it's OK */
198 if (desiredAccess == DELETE)
203 if (desiredAccess & (AFS_ACCESS_READ|AFS_ACCESS_EXECUTE))
204 rights |= (scp->fileType == CM_SCACHETYPE_DIRECTORY ? PRSFS_LOOKUP : PRSFS_READ);
206 /* We used to require PRSFS_WRITE if createDisp was 4
207 (OPEN_ALWAYS) even if AFS_ACCESS_WRITE was not requested.
208 However, we don't need to do that since the existence of the
209 scp implies that we don't need to create it. */
210 if (desiredAccess & AFS_ACCESS_WRITE)
211 rights |= PRSFS_WRITE;
213 lock_ObtainWrite(&scp->rw);
215 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
216 CM_SCACHESYNC_GETSTATUS
217 | CM_SCACHESYNC_NEEDCALLBACK
218 | CM_SCACHESYNC_LOCK);
221 * If the open will fail because the volume is readonly, then we will
222 * return an access denied error instead. This is to help brain-dead
223 * apps run correctly on replicated volumes.
224 * See defect 10007 for more information.
226 if (code == CM_ERROR_READONLY)
227 code = CM_ERROR_NOACCESS;
230 ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
231 scp->fileType == CM_SCACHETYPE_FILE) {
233 unsigned int sLockType;
234 LARGE_INTEGER LOffset, LLength;
236 /* Check if there's some sort of lock on the file at the
239 key = cm_GenerateKey(CM_SESSION_CMINT,0,0);
240 if (rights & PRSFS_WRITE)
243 sLockType = LOCKING_ANDX_SHARED_LOCK;
245 /* single byte lock at offset 0x0100 0000 0000 0000 */
246 LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
247 LOffset.LowPart = CM_FLSHARE_OFFSET_LOW;
248 LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
249 LLength.LowPart = CM_FLSHARE_LENGTH_LOW;
251 code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
254 (*ldpp) = (cm_lock_data_t *)malloc(sizeof(cm_lock_data_t));
261 (*ldpp)->sLockType = sLockType;
262 (*ldpp)->LOffset.HighPart = LOffset.HighPart;
263 (*ldpp)->LOffset.LowPart = LOffset.LowPart;
264 (*ldpp)->LLength.HighPart = LLength.HighPart;
265 (*ldpp)->LLength.LowPart = LLength.LowPart;
267 /* In this case, we allow the file open to go through even
268 though we can't enforce mandatory locking on the
270 if (code == CM_ERROR_NOACCESS &&
271 !(rights & PRSFS_WRITE))
275 case CM_ERROR_ALLOFFLINE:
276 case CM_ERROR_ALLDOWN:
277 case CM_ERROR_ALLBUSY:
278 case CM_ERROR_TIMEDOUT:
280 case CM_ERROR_WOULDBLOCK:
283 code = CM_ERROR_SHARING_VIOLATION;
287 } else if (code != 0) {
292 lock_ReleaseWrite(&scp->rw);
294 osi_Log3(afsd_logp,"cm_CheckNTOpen scp 0x%p ldp 0x%p code 0x%x", scp, *ldpp, code);
298 extern long cm_CheckNTOpenDone(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp,
299 cm_lock_data_t ** ldpp)
301 osi_Log2(afsd_logp,"cm_CheckNTOpenDone scp 0x%p ldp 0x%p", scp, *ldpp);
302 lock_ObtainWrite(&scp->rw);
304 cm_Unlock(scp, (*ldpp)->sLockType, (*ldpp)->LOffset, (*ldpp)->LLength,
305 (*ldpp)->key, 0, userp, reqp);
309 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
310 lock_ReleaseWrite(&scp->rw);
314 * When CAP_NT_SMBS has been negotiated, deletion (of files or directories) is
315 * done in three steps:
316 * (1) open for deletion (NT_CREATE_AND_X)
317 * (2) set for deletion on close (NT_TRANSACTION2, SET_FILE_INFO)
319 * We must not do the RPC until step 3. But if we are going to return an error
320 * code (e.g. directory not empty), we must return it by step 2, otherwise most
321 * clients will not notice it. So we do a preliminary check. For deleting
322 * files, this is almost free, since we have already done the RPC to get the
323 * parent directory's status bits. But for deleting directories, we must do an
324 * additional RPC to get the directory's data to check if it is empty. Sigh.
326 long cm_CheckNTDelete(cm_scache_t *dscp, cm_scache_t *scp, cm_user_t *userp,
332 cm_dirEntry_t *dep = 0;
333 unsigned short *hashTable;
335 int BeyondPage = 0, HaveDot = 0, HaveDotDot = 0;
338 /* First check permissions */
339 lock_ObtainWrite(&scp->rw);
340 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_DELETE,
341 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
343 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
344 lock_ReleaseWrite(&scp->rw);
348 /* If deleting directory, must be empty */
350 if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
353 thyper.HighPart = 0; thyper.LowPart = 0;
354 code = buf_Get(scp, &thyper, reqp, &bufferp);
358 lock_ObtainMutex(&bufferp->mx);
359 lock_ObtainWrite(&scp->rw);
362 code = cm_SyncOp(scp, bufferp, userp, reqp, 0,
363 CM_SCACHESYNC_NEEDCALLBACK
365 | CM_SCACHESYNC_BUFLOCKED);
369 if (cm_HaveBuffer(scp, bufferp, 1))
372 /* otherwise, load the buffer and try again */
373 lock_ReleaseMutex(&bufferp->mx);
374 code = cm_GetBuffer(scp, bufferp, NULL, userp, reqp);
375 lock_ReleaseWrite(&scp->rw);
376 lock_ObtainMutex(&bufferp->mx);
377 lock_ObtainWrite(&scp->rw);
378 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
383 lock_ReleaseWrite(&scp->rw);
386 /* We try to determine emptiness without looking beyond the first page,
387 * and without assuming "." and ".." are present and are on the first
388 * page (though these assumptions might, after all, be reasonable).
390 hashTable = (unsigned short *)(bufferp->datap + (32 * 5));
391 for (i=0; i<128; i++) {
392 idx = ntohs(hashTable[i]);
398 dep = (cm_dirEntry_t *)(bufferp->datap + (32 * idx));
399 if (strcmp(dep->name, ".") == 0)
401 else if (strcmp(dep->name, "..") == 0)
404 code = CM_ERROR_NOTEMPTY;
407 idx = ntohs(dep->next);
410 if (BeyondPage && HaveDot && HaveDotDot)
411 code = CM_ERROR_NOTEMPTY;
415 lock_ReleaseMutex(&bufferp->mx);
416 buf_Release(bufferp);
418 lock_ReleaseWrite(&scp->rw);
423 * Iterate through all entries in a directory.
424 * When the function funcp is called, the buffer is locked but the
425 * directory vnode is not.
427 * If the retscp parameter is not NULL, the parmp must be a
428 * cm_lookupSearch_t object.
430 long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
431 osi_hyper_t *startOffsetp, cm_user_t *userp, cm_req_t *reqp,
432 cm_scache_t **retscp)
436 cm_dirEntry_t *dep = 0;
439 osi_hyper_t dirLength;
440 osi_hyper_t bufferOffset;
441 osi_hyper_t curOffset;
445 cm_pageHeader_t *pageHeaderp;
447 long nextEntryCookie;
448 int numDirChunks; /* # of 32 byte dir chunks in this entry */
450 /* get the directory size */
451 lock_ObtainWrite(&scp->rw);
452 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_LOOKUP,
453 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
454 lock_ReleaseWrite(&scp->rw);
458 if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
459 return CM_ERROR_NOTDIR;
461 if (retscp) /* if this is a lookup call */
463 cm_lookupSearch_t* sp = parmp;
466 #ifdef AFS_FREELANCE_CLIENT
467 /* Freelance entries never end up in the DNLC because they
468 * do not have an associated cm_server_t
470 !(cm_freelanceEnabled &&
471 sp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
472 sp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
473 #else /* !AFS_FREELANCE_CLIENT */
478 int casefold = sp->caseFold;
479 sp->caseFold = 0; /* we have a strong preference for exact matches */
480 if ( *retscp = cm_dnlcLookup(scp, sp)) /* dnlc hit */
482 sp->caseFold = casefold;
485 sp->caseFold = casefold;
489 * see if we can find it using the directory hash tables.
490 * we can only do exact matches, since the hash is case
493 if (funcp != (cm_DirFuncp_t)cm_BPlusDirFoo)
502 code = cm_BeginDirOp(scp, userp, reqp, CM_DIRLOCK_READ,
503 CM_DIROP_FLAG_NONE, &dirop);
507 code = cm_BPlusDirLookup(&dirop, sp->nsearchNamep, &sp->fid);
512 code = cm_DirLookup(&dirop, sp->searchNamep, &sp->fid);
520 sp->ExactFound = TRUE;
521 *retscp = NULL; /* force caller to call cm_GetSCache() */
526 if (sp->caseFold && code == CM_ERROR_INEXACT_MATCH) {
529 sp->ExactFound = FALSE;
530 *retscp = NULL; /* force caller to call cm_GetSCache() */
534 return CM_ERROR_BPLUS_NOMATCH;
541 * XXX We only get the length once. It might change when we drop the
544 dirLength = scp->length;
547 bufferOffset.LowPart = bufferOffset.HighPart = 0;
549 curOffset = *startOffsetp;
551 curOffset.HighPart = 0;
552 curOffset.LowPart = 0;
556 /* make sure that curOffset.LowPart doesn't point to the first
557 * 32 bytes in the 2nd through last dir page, and that it
558 * doesn't point at the first 13 32-byte chunks in the first
559 * dir page, since those are dir and page headers, and don't
560 * contain useful information.
562 temp = curOffset.LowPart & (2048-1);
563 if (curOffset.HighPart == 0 && curOffset.LowPart < 2048) {
564 /* we're in the first page */
565 if (temp < 13*32) temp = 13*32;
568 /* we're in a later dir page */
569 if (temp < 32) temp = 32;
572 /* make sure the low order 5 bits are zero */
575 /* now put temp bits back ito curOffset.LowPart */
576 curOffset.LowPart &= ~(2048-1);
577 curOffset.LowPart |= temp;
579 /* check if we've passed the dir's EOF */
580 if (LargeIntegerGreaterThanOrEqualTo(curOffset, dirLength))
583 /* see if we can use the bufferp we have now; compute in which
584 * page the current offset would be, and check whether that's
585 * the offset of the buffer we have. If not, get the buffer.
587 thyper.HighPart = curOffset.HighPart;
588 thyper.LowPart = curOffset.LowPart & ~(cm_data.buf_blockSize-1);
589 if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
592 lock_ReleaseMutex(&bufferp->mx);
593 buf_Release(bufferp);
597 code = buf_Get(scp, &thyper, reqp, &bufferp);
599 /* if buf_Get() fails we do not have a buffer object to lock */
604 lock_ObtainMutex(&bufferp->mx);
605 bufferOffset = thyper;
607 /* now get the data in the cache */
609 lock_ObtainWrite(&scp->rw);
610 code = cm_SyncOp(scp, bufferp, userp, reqp,
612 CM_SCACHESYNC_NEEDCALLBACK
614 | CM_SCACHESYNC_BUFLOCKED);
616 lock_ReleaseWrite(&scp->rw);
619 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
621 if (cm_HaveBuffer(scp, bufferp, 1)) {
622 lock_ReleaseWrite(&scp->rw);
626 /* otherwise, load the buffer and try again */
627 lock_ReleaseMutex(&bufferp->mx);
628 code = cm_GetBuffer(scp, bufferp, NULL, userp,
630 lock_ReleaseWrite(&scp->rw);
631 lock_ObtainMutex(&bufferp->mx);
636 lock_ReleaseMutex(&bufferp->mx);
637 buf_Release(bufferp);
641 } /* if (wrong buffer) ... */
643 /* now we have the buffer containing the entry we're interested
644 * in; copy it out if it represents a non-deleted entry.
646 entryInDir = curOffset.LowPart & (2048-1);
647 entryInBuffer = curOffset.LowPart & (cm_data.buf_blockSize - 1);
649 /* page header will help tell us which entries are free. Page
650 * header can change more often than once per buffer, since
651 * AFS 3 dir page size may be less than (but not more than) a
652 * buffer package buffer.
654 /* only look intra-buffer */
655 temp = curOffset.LowPart & (cm_data.buf_blockSize - 1);
656 temp &= ~(2048 - 1); /* turn off intra-page bits */
657 pageHeaderp = (cm_pageHeader_t *) (bufferp->datap + temp);
659 /* now determine which entry we're looking at in the page. If
660 * it is free (there's a free bitmap at the start of the dir),
661 * we should skip these 32 bytes.
663 slotInPage = (entryInDir & 0x7e0) >> 5;
664 if (!(pageHeaderp->freeBitmap[slotInPage>>3]
665 & (1 << (slotInPage & 0x7)))) {
666 /* this entry is free */
667 numDirChunks = 1; /* only skip this guy */
671 tp = bufferp->datap + entryInBuffer;
672 dep = (cm_dirEntry_t *) tp; /* now points to AFS3 dir entry */
674 /* while we're here, compute the next entry's location, too,
675 * since we'll need it when writing out the cookie into the
676 * dir listing stream.
678 numDirChunks = cm_NameEntries(dep->name, NULL);
680 /* compute the offset of the cookie representing the next entry */
681 nextEntryCookie = curOffset.LowPart
682 + (CM_DIR_CHUNKSIZE * numDirChunks);
684 if (dep->fid.vnode != 0) {
685 /* this is one of the entries to use: it is not deleted */
686 code = (*funcp)(scp, dep, parmp, &curOffset);
689 } /* if we're including this name */
692 /* and adjust curOffset to be where the new cookie is */
694 thyper.LowPart = CM_DIR_CHUNKSIZE * numDirChunks;
695 curOffset = LargeIntegerAdd(thyper, curOffset);
696 } /* while copying data for dir listing */
698 /* release the mutex */
700 lock_ReleaseMutex(&bufferp->mx);
701 buf_Release(bufferp);
706 int cm_NoneUpper(normchar_t *s)
710 if (c >= 'A' && c <= 'Z')
715 int cm_NoneLower(normchar_t *s)
719 if (c >= 'a' && c <= 'z')
724 long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
727 cm_lookupSearch_t *sp;
729 normchar_t matchName[MAX_PATH];
730 int looking_for_short_name = FALSE;
732 sp = (cm_lookupSearch_t *) rockp;
734 if (cm_FsStringToNormString(dep->name, -1, matchName, lengthof(matchName)) == 0) {
735 /* Can't normalize FS string. */
740 match = cm_NormStrCmpI(matchName, sp->nsearchNamep);
742 match = cm_NormStrCmp(matchName, sp->nsearchNamep);
746 && !cm_Is8Dot3(matchName)) {
748 cm_Gen8Dot3NameInt(dep->name, &dep->fid, matchName, NULL);
750 match = cm_NormStrCmpI(matchName, sp->nsearchNamep);
752 match = cm_NormStrCmp(matchName, sp->nsearchNamep);
753 looking_for_short_name = TRUE;
763 if (!sp->caseFold || looking_for_short_name) {
764 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
765 return CM_ERROR_STOPNOW;
769 * If we get here, we are doing a case-insensitive search, and we
770 * have found a match. Now we determine what kind of match it is:
771 * exact, lower-case, upper-case, or none of the above. This is done
772 * in order to choose among matches, if there are more than one.
775 /* Exact matches are the best. */
776 match = cm_NormStrCmp(matchName, sp->nsearchNamep);
779 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
780 return CM_ERROR_STOPNOW;
783 /* Lower-case matches are next. */
786 if (cm_NoneUpper(matchName)) {
791 /* Upper-case matches are next. */
794 if (cm_NoneLower(matchName)) {
799 /* General matches are last. */
805 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
809 /* read the contents of a mount point into the appropriate string.
810 * called with write locked scp, and returns with locked scp.
812 long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
815 cm_buf_t *bufp = NULL;
819 if (scp->mountPointStringp[0])
822 #ifdef AFS_FREELANCE_CLIENT
823 /* File servers do not have data for freelance entries */
824 if (cm_freelanceEnabled &&
825 scp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
826 scp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
828 code = cm_FreelanceFetchMountPointString(scp);
830 #endif /* AFS_FREELANCE_CLIENT */
832 /* otherwise, we have to read it in */
833 lock_ReleaseWrite(&scp->rw);
835 thyper.LowPart = thyper.HighPart = 0;
836 code = buf_Get(scp, &thyper, reqp, &bufp);
838 lock_ObtainWrite(&scp->rw);
843 code = cm_SyncOp(scp, bufp, userp, reqp, 0,
844 CM_SCACHESYNC_READ | CM_SCACHESYNC_NEEDCALLBACK);
848 cm_SyncOpDone(scp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
850 if (cm_HaveBuffer(scp, bufp, 0))
853 /* otherwise load buffer */
854 code = cm_GetBuffer(scp, bufp, NULL, userp, reqp);
858 /* locked, has callback, has valid data in buffer */
859 if ((tlen = scp->length.LowPart) > MOUNTPOINTLEN - 1)
860 return CM_ERROR_TOOBIG;
862 code = CM_ERROR_INVAL;
866 /* someone else did the work while we were out */
867 if (scp->mountPointStringp[0]) {
872 /* otherwise, copy out the link */
873 memcpy(scp->mountPointStringp, bufp->datap, tlen);
875 /* now make it null-terminated. Note that the original contents of a
876 * link that is a mount point is "#volname." where "." is there just to
877 * be turned into a null. That is, we can trash the last char of the
878 * link without damaging the vol name. This is a stupid convention,
879 * but that's the protocol.
881 scp->mountPointStringp[tlen-1] = 0;
892 /* called with a locked scp and chases the mount point, yielding outScpp.
893 * scp remains write locked, just for simplicity of describing the interface.
895 long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
896 cm_req_t *reqp, cm_scache_t **outScpp)
898 fschar_t *cellNamep = NULL;
899 fschar_t *volNamep = NULL;
903 cm_volume_t *volp = NULL;
912 if (scp->mountRootFid.cell != 0 && scp->mountRootGen >= cm_data.mountRootGen) {
913 tfid = scp->mountRootFid;
914 lock_ReleaseWrite(&scp->rw);
915 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
916 lock_ObtainWrite(&scp->rw);
920 /* parse the volume name */
921 mpNamep = scp->mountPointStringp;
923 return CM_ERROR_NOSUCHPATH;
924 mtType = *scp->mountPointStringp;
926 cp = cm_FsStrChr(mpNamep, _FS(':'));
928 /* cellular mount point */
929 cellNamep = (fschar_t *)malloc((cp - mpNamep) * sizeof(fschar_t));
930 cm_FsStrCpyN(cellNamep, cp - mpNamep, mpNamep + 1, cp - mpNamep - 1);
931 volNamep = cm_FsStrDup(cp+1);
933 /* now look up the cell */
934 lock_ReleaseWrite(&scp->rw);
935 cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE);
936 lock_ObtainWrite(&scp->rw);
939 volNamep = cm_FsStrDup(mpNamep + 1);
941 #ifdef AFS_FREELANCE_CLIENT
943 * Mount points in the Freelance cell should default
944 * to the workstation cell.
946 if (cm_freelanceEnabled &&
947 scp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
948 scp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
950 fschar_t rootCellName[256]="";
951 cm_GetRootCellName(rootCellName);
952 cellp = cm_GetCell(rootCellName, 0);
954 #endif /* AFS_FREELANCE_CLIENT */
955 cellp = cm_FindCellByID(scp->fid.cell, 0);
959 code = CM_ERROR_NOSUCHCELL;
963 vnLength = cm_FsStrLen(volNamep);
964 if (vnLength >= 8 && cm_FsStrCmp(volNamep + vnLength - 7, ".backup") == 0)
965 targetType = BACKVOL;
966 else if (vnLength >= 10
967 && cm_FsStrCmp(volNamep + vnLength - 9, ".readonly") == 0)
972 /* check for backups within backups */
973 if (targetType == BACKVOL
974 && (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
975 == CM_SCACHEFLAG_RO) {
976 code = CM_ERROR_NOSUCHVOLUME;
980 /* now we need to get the volume */
981 lock_ReleaseWrite(&scp->rw);
982 if (cm_VolNameIsID(volNamep)) {
983 code = cm_FindVolumeByID(cellp, atoi(volNamep), userp, reqp,
984 CM_GETVOL_FLAG_CREATE, &volp);
986 code = cm_FindVolumeByName(cellp, volNamep, userp, reqp,
987 CM_GETVOL_FLAG_CREATE, &volp);
989 lock_ObtainWrite(&scp->rw);
992 afs_uint32 cell, volume;
993 cm_vol_state_t *statep;
995 cell = cellp->cellID;
997 /* if the mt pt originates in a .backup volume (not a .readonly)
998 * and FollowBackupPath is active, and if there is a .backup
999 * volume for the target, then use the .backup of the target
1000 * instead of the read-write.
1002 if (cm_followBackupPath &&
1003 volp->vol[BACKVOL].ID != 0 &&
1004 (dscp->flags & (CM_SCACHEFLAG_RO|CM_SCACHEFLAG_PURERO)) == CM_SCACHEFLAG_RO &&
1005 (targetType == RWVOL || targetType == ROVOL && volp->vol[ROVOL].ID == 0)
1007 targetType = BACKVOL;
1009 /* if the mt pt is in a read-only volume (not just a
1010 * backup), and if there is a read-only volume for the
1011 * target, and if this is a targetType '#' mount point, use
1012 * the read-only, otherwise use the one specified.
1014 else if (mtType == '#' && targetType == RWVOL &&
1015 (scp->flags & CM_SCACHEFLAG_PURERO) &&
1016 volp->vol[ROVOL].ID != 0) {
1020 lock_ObtainWrite(&volp->rw);
1021 statep = cm_VolumeStateByType(volp, targetType);
1022 volume = statep->ID;
1023 statep->dotdotFid = dscp->fid;
1024 lock_ReleaseWrite(&volp->rw);
1026 /* the rest of the fid is a magic number */
1027 cm_SetFid(&scp->mountRootFid, cell, volume, 1, 1);
1028 scp->mountRootGen = cm_data.mountRootGen;
1030 tfid = scp->mountRootFid;
1031 lock_ReleaseWrite(&scp->rw);
1032 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
1033 lock_ObtainWrite(&scp->rw);
1046 long cm_LookupInternal(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_user_t *userp,
1047 cm_req_t *reqp, cm_scache_t **outScpp)
1050 int dnlcHit = 1; /* did we hit in the dnlc? yes, we did */
1051 cm_scache_t *tscp = NULL;
1052 cm_scache_t *mountedScp;
1053 cm_lookupSearch_t rock;
1055 normchar_t *nnamep = NULL;
1056 fschar_t *fnamep = NULL;
1061 memset(&rock, 0, sizeof(rock));
1063 if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
1064 && cm_ClientStrCmp(cnamep, _C("..")) == 0) {
1065 if (dscp->dotdotFid.volume == 0)
1066 return CM_ERROR_NOSUCHVOLUME;
1067 rock.fid = dscp->dotdotFid;
1069 } else if (cm_ClientStrCmp(cnamep, _C(".")) == 0) {
1070 rock.fid = dscp->fid;
1074 nnamep = cm_ClientStringToNormStringAlloc(cnamep, -1, NULL);
1076 code = CM_ERROR_NOSUCHFILE;
1079 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
1081 code = CM_ERROR_NOSUCHFILE;
1086 if (flags & CM_FLAG_NOMOUNTCHASE) {
1087 /* In this case, we should go and call cm_Dir* functions
1088 directly since the following cm_ApplyDir() function will
1096 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ,
1097 CM_DIROP_FLAG_NONE, &dirop);
1100 code = cm_BPlusDirLookup(&dirop, nnamep, &rock.fid);
1105 code = cm_DirLookup(&dirop, fnamep, &rock.fid);
1107 cm_EndDirOp(&dirop);
1117 if (code == CM_ERROR_INEXACT_MATCH && (flags & CM_FLAG_CASEFOLD)) {
1124 code = CM_ERROR_BPLUS_NOMATCH;
1130 rock.fid.cell = dscp->fid.cell;
1131 rock.fid.volume = dscp->fid.volume;
1132 rock.searchNamep = fnamep;
1133 rock.nsearchNamep = nnamep;
1134 rock.caseFold = (flags & CM_FLAG_CASEFOLD);
1135 rock.hasTilde = ((cm_ClientStrChr(cnamep, '~') != NULL) ? 1 : 0);
1137 /* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
1138 code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
1139 (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
1141 /* code == 0 means we fell off the end of the dir, while stopnow means
1142 * that we stopped early, probably because we found the entry we're
1143 * looking for. Any other non-zero code is an error.
1145 if (code && code != CM_ERROR_STOPNOW && code != CM_ERROR_BPLUS_NOMATCH) {
1146 /* if the cm_scache_t we are searching in is not a directory
1147 * we must return path not found because the error
1148 * is to describe the final component not an intermediary
1150 if (code == CM_ERROR_NOTDIR) {
1151 if (flags & CM_FLAG_CHECKPATH)
1152 code = CM_ERROR_NOSUCHPATH;
1154 code = CM_ERROR_NOSUCHFILE;
1160 getroot = (dscp==cm_data.rootSCachep) ;
1162 if (!cm_freelanceEnabled || !getroot) {
1163 if (flags & CM_FLAG_CHECKPATH)
1164 code = CM_ERROR_NOSUCHPATH;
1166 code = CM_ERROR_NOSUCHFILE;
1169 else if (!cm_ClientStrChr(cnamep, '#') &&
1170 !cm_ClientStrChr(cnamep, '%') &&
1171 cm_ClientStrCmpI(cnamep, _C("srvsvc")) &&
1172 cm_ClientStrCmpI(cnamep, _C("wkssvc")) &&
1173 cm_ClientStrCmpI(cnamep, _C("ipc$")))
1175 /* nonexistent dir on freelance root, so add it */
1176 fschar_t fullname[CELL_MAXNAMELEN + 1] = "."; /* +1 so that when we skip the . the size is still CELL_MAXNAMELEN */
1180 osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %S",
1181 osi_LogSaveClientString(afsd_logp,cnamep));
1184 * There is an ugly behavior where a share name "foo" will be searched
1185 * for as "fo". If the searched for name differs by an already existing
1186 * symlink or mount point in the Freelance directory, do not add the
1187 * new value automatically.
1191 fnlen = strlen(fnamep);
1192 if ( fnamep[fnlen-1] == '.') {
1193 fnamep[fnlen-1] = '\0';
1198 if (cnamep[0] == '.') {
1199 if (cm_GetCell_Gen(&fnamep[1], &fullname[1], CM_FLAG_CREATE)) {
1201 code = cm_FreelanceAddMount(fullname, &fullname[1], "root.cell.", 1, &rock.fid);
1202 if ( cm_FsStrCmpI(&fnamep[1], &fullname[1])) {
1204 * Do not permit symlinks that are one of:
1205 * . the cellname followed by a dot
1206 * . the cellname minus a single character
1207 * . a substring of the cellname that does not consist of full components
1209 if ( cm_strnicmp_utf8(&fnamep[1], fullname, (int)fnlen-1) == 0 &&
1210 (fnlen-1 == strlen(fullname)-1 || fullname[fnlen-1] != '.'))
1212 /* do not add; substitute fullname for the search */
1214 fnamep = malloc(strlen(fullname)+2);
1216 strncpy(&fnamep[1], fullname, strlen(fullname)+1);
1219 code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
1224 if (cm_GetCell_Gen(fnamep, fullname, CM_FLAG_CREATE)) {
1226 code = cm_FreelanceAddMount(fullname, fullname, "root.cell.", 0, &rock.fid);
1227 if ( cm_FsStrCmpI(fnamep, fullname)) {
1229 * Do not permit symlinks that are one of:
1230 * . the cellname followed by a dot
1231 * . the cellname minus a single character
1232 * . a substring of the cellname that does not consist of full components
1234 if ( cm_strnicmp_utf8(fnamep, fullname, (int)fnlen-1) == 0 &&
1235 (fnlen == strlen(fullname)-1 || fullname[fnlen] != '.'))
1237 /* do not add; substitute fullname for the search */
1239 fnamep = strdup(fullname);
1243 code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
1252 nnamep = cm_FsStringToNormStringAlloc(fnamep, -1, NULL);
1256 if (!found || code) { /* add mount point failed, so give up */
1257 if (flags & CM_FLAG_CHECKPATH)
1258 code = CM_ERROR_NOSUCHPATH;
1260 code = CM_ERROR_NOSUCHFILE;
1263 tscp = NULL; /* to force call of cm_GetSCache */
1265 if (flags & CM_FLAG_CHECKPATH)
1266 code = CM_ERROR_NOSUCHPATH;
1268 code = CM_ERROR_NOSUCHFILE;
1274 if ( !tscp ) /* we did not find it in the dnlc */
1277 code = cm_GetSCache(&rock.fid, &tscp, userp, reqp);
1281 /* tscp is now held */
1283 lock_ObtainWrite(&tscp->rw);
1284 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1285 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1287 lock_ReleaseWrite(&tscp->rw);
1288 cm_ReleaseSCache(tscp);
1291 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1292 /* tscp is now locked */
1294 if (!(flags & CM_FLAG_NOMOUNTCHASE)
1295 && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
1296 /* mount points are funny: they have a volume name to mount
1299 code = cm_ReadMountPoint(tscp, userp, reqp);
1301 code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
1303 lock_ReleaseWrite(&tscp->rw);
1304 cm_ReleaseSCache(tscp);
1311 lock_ReleaseWrite(&tscp->rw);
1314 /* copy back pointer */
1317 /* insert scache in dnlc */
1318 if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
1319 /* lock the directory entry to prevent racing callback revokes */
1320 lock_ObtainRead(&dscp->rw);
1321 if ( dscp->cbServerp != NULL && dscp->cbExpires > 0 ) {
1322 /* TODO: reuse nnamep from above */
1325 nnamep = cm_ClientStringToNormStringAlloc(cnamep, -1, NULL);
1327 cm_dnlcEnter(dscp, nnamep, tscp);
1329 lock_ReleaseRead(&dscp->rw);
1346 int cm_ExpandSysName(clientchar_t *inp, clientchar_t *outp, long outSizeCch, unsigned int index)
1351 tp = cm_ClientStrRChr(inp, '@');
1353 return 0; /* no @sys */
1355 if (cm_ClientStrCmp(tp, _C("@sys")) != 0)
1356 return 0; /* no @sys */
1358 /* caller just wants to know if this is a valid @sys type of name */
1362 if (index >= cm_sysNameCount)
1365 /* otherwise generate the properly expanded @sys name */
1366 prefixCount = (int)(tp - inp);
1368 cm_ClientStrCpyN(outp, outSizeCch, inp, prefixCount); /* copy out "a." from "a.@sys" */
1369 outp[prefixCount] = 0; /* null terminate the "a." */
1370 cm_ClientStrCat(outp, outSizeCch, cm_sysNameList[index]);/* append i386_nt40 */
1374 long cm_EvaluateVolumeReference(clientchar_t * namep, long flags, cm_user_t * userp,
1375 cm_req_t *reqp, cm_scache_t ** outScpp)
1377 afs_uint32 code = 0;
1378 fschar_t cellName[CELL_MAXNAMELEN];
1379 fschar_t volumeName[VL_MAXNAMELEN];
1383 fschar_t * fnamep = NULL;
1385 cm_cell_t * cellp = NULL;
1386 cm_volume_t * volp = NULL;
1390 int mountType = RWVOL;
1392 osi_Log1(afsd_logp, "cm_EvaluateVolumeReference for string [%S]",
1393 osi_LogSaveClientString(afsd_logp, namep));
1395 if (cm_ClientStrCmpNI(namep, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH) != 0) {
1396 goto _exit_invalid_path;
1399 /* namep is assumed to look like the following:
1401 @vol:<cellname>%<volume>\0
1403 @vol:<cellname>#<volume>\0
1407 fnamep = cm_ClientStringToFsStringAlloc(namep, -1, NULL);
1408 cp = fnamep + CM_PREFIX_VOL_CCH; /* cp points to cell name, hopefully */
1409 tp = cm_FsStrChr(cp, '%');
1411 tp = cm_FsStrChr(cp, '#');
1413 (len = tp - cp) == 0 ||
1414 len > CELL_MAXNAMELEN)
1415 goto _exit_invalid_path;
1416 cm_FsStrCpyN(cellName, lengthof(cellName), cp, len);
1421 cp = tp+1; /* cp now points to volume, supposedly */
1422 cm_FsStrCpy(volumeName, lengthof(volumeName), cp);
1424 /* OK, now we have the cell and the volume */
1425 osi_Log2(afsd_logp, " Found cell [%s] and volume [%s]",
1426 osi_LogSaveFsString(afsd_logp, cellName),
1427 osi_LogSaveFsString(afsd_logp, volumeName));
1429 cellp = cm_GetCell(cellName, CM_FLAG_CREATE);
1430 if (cellp == NULL) {
1431 goto _exit_invalid_path;
1434 len = cm_FsStrLen(volumeName);
1435 if (len >= 8 && cm_FsStrCmp(volumeName + len - 7, ".backup") == 0)
1437 else if (len >= 10 &&
1438 cm_FsStrCmp(volumeName + len - 9, ".readonly") == 0)
1443 if (cm_VolNameIsID(volumeName)) {
1444 code = cm_FindVolumeByID(cellp, atoi(volumeName), userp, reqp,
1445 CM_GETVOL_FLAG_CREATE, &volp);
1447 code = cm_FindVolumeByName(cellp, volumeName, userp, reqp,
1448 CM_GETVOL_FLAG_CREATE, &volp);
1454 if (volType == BACKVOL)
1455 volume = volp->vol[BACKVOL].ID;
1456 else if (volType == ROVOL ||
1457 (volType == RWVOL && mountType == ROVOL && volp->vol[ROVOL].ID != 0))
1458 volume = volp->vol[ROVOL].ID;
1460 volume = volp->vol[RWVOL].ID;
1462 cm_SetFid(&fid, cellp->cellID, volume, 1, 1);
1464 code = cm_GetSCache(&fid, outScpp, userp, reqp);
1477 if (flags & CM_FLAG_CHECKPATH)
1478 return CM_ERROR_NOSUCHPATH;
1480 return CM_ERROR_NOSUCHFILE;
1483 #ifdef DEBUG_REFCOUNT
1484 long cm_LookupDbg(cm_scache_t *dscp, clientchar_t *namep, long flags, cm_user_t *userp,
1485 cm_req_t *reqp, cm_scache_t **outScpp, char * file, long line)
1487 long cm_Lookup(cm_scache_t *dscp, clientchar_t *namep, long flags, cm_user_t *userp,
1488 cm_req_t *reqp, cm_scache_t **outScpp)
1492 clientchar_t tname[AFSPATHMAX];
1493 int sysNameIndex = 0;
1494 cm_scache_t *scp = NULL;
1496 #ifdef DEBUG_REFCOUNT
1497 afsi_log("%s:%d cm_Lookup dscp 0x%p ref %d", file, line, dscp, dscp->refCount, file, line);
1498 osi_Log2(afsd_logp, "cm_Lookup dscp 0x%p ref %d", dscp, dscp->refCount);
1501 if ( cm_ClientStrCmpI(namep,_C(SMB_IOCTL_FILENAME_NOSLASH)) == 0 ) {
1502 if (flags & CM_FLAG_CHECKPATH)
1503 return CM_ERROR_NOSUCHPATH;
1505 return CM_ERROR_NOSUCHFILE;
1508 if (dscp == cm_data.rootSCachep &&
1509 cm_ClientStrCmpNI(namep, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH) == 0) {
1510 return cm_EvaluateVolumeReference(namep, flags, userp, reqp, outScpp);
1513 if (cm_ExpandSysName(namep, NULL, 0, 0) > 0) {
1514 for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
1515 code = cm_ExpandSysName(namep, tname, lengthof(tname), sysNameIndex);
1517 code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
1518 #ifdef DEBUG_REFCOUNT
1519 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);
1520 osi_Log3(afsd_logp, "cm_LookupInternal (1) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1528 cm_ReleaseSCache(scp);
1532 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1533 #ifdef DEBUG_REFCOUNT
1534 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);
1535 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1542 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1543 #ifdef DEBUG_REFCOUNT
1544 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);
1545 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1551 /* None of the possible sysName expansions could be found */
1552 if (flags & CM_FLAG_CHECKPATH)
1553 return CM_ERROR_NOSUCHPATH;
1555 return CM_ERROR_NOSUCHFILE;
1558 /*! \brief Unlink a file name
1560 Encapsulates a call to RXAFS_RemoveFile().
1562 \param[in] dscp cm_scache_t pointing at the directory containing the
1563 name to be unlinked.
1565 \param[in] fnamep Original name to be unlinked. This is the
1566 name that will be passed into the RXAFS_RemoveFile() call.
1567 This parameter is optional. If not provided, the value will
1570 \param[in] came Client name to be unlinked. This name will be used
1571 to update the local directory caches.
1573 \param[in] userp cm_user_t for the request.
1575 \param[in] reqp Request tracker.
1578 long cm_Unlink(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t * cnamep,
1579 cm_user_t *userp, cm_req_t *reqp)
1585 AFSFetchStatus newDirStatus;
1587 struct rx_connection * rxconnp;
1589 cm_scache_t *scp = NULL;
1590 int free_fnamep = FALSE;
1592 memset(&volSync, 0, sizeof(volSync));
1594 if (fnamep == NULL) {
1597 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ,
1598 CM_DIROP_FLAG_NONE, &dirop);
1600 code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
1603 cm_EndDirOp(&dirop);
1610 #ifdef AFS_FREELANCE_CLIENT
1611 if (cm_freelanceEnabled && dscp == cm_data.rootSCachep) {
1612 /* deleting a mount point from the root dir. */
1613 code = cm_FreelanceRemoveMount(fnamep);
1618 code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
1620 /* make sure we don't screw up the dir status during the merge */
1621 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE,
1622 CM_DIROP_FLAG_NONE, &dirop);
1624 lock_ObtainWrite(&dscp->rw);
1625 sflags = CM_SCACHESYNC_STOREDATA;
1626 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1627 lock_ReleaseWrite(&dscp->rw);
1629 cm_EndDirOp(&dirop);
1634 afsFid.Volume = dscp->fid.volume;
1635 afsFid.Vnode = dscp->fid.vnode;
1636 afsFid.Unique = dscp->fid.unique;
1638 osi_Log1(afsd_logp, "CALL RemoveFile scp 0x%p", dscp);
1640 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
1644 rxconnp = cm_GetRxConn(connp);
1645 code = RXAFS_RemoveFile(rxconnp, &afsFid, fnamep,
1646 &newDirStatus, &volSync);
1647 rx_PutConnection(rxconnp);
1649 } while (cm_Analyze(connp, userp, reqp, &dscp->fid, &volSync, NULL, NULL, code));
1650 code = cm_MapRPCError(code, reqp);
1653 osi_Log1(afsd_logp, "CALL RemoveFile FAILURE, code 0x%x", code);
1655 osi_Log0(afsd_logp, "CALL RemoveFile SUCCESS");
1658 lock_ObtainWrite(&dirop.scp->dirlock);
1659 dirop.lockType = CM_DIRLOCK_WRITE;
1661 lock_ObtainWrite(&dscp->rw);
1662 cm_dnlcRemove(dscp, cnamep);
1663 cm_SyncOpDone(dscp, NULL, sflags);
1665 cm_MergeStatus(NULL, dscp, &newDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
1666 } else if (code == CM_ERROR_NOSUCHFILE) {
1667 /* windows would not have allowed the request to delete the file
1668 * if it did not believe the file existed. therefore, we must
1669 * have an inconsistent view of the world.
1671 dscp->cbServerp = NULL;
1673 lock_ReleaseWrite(&dscp->rw);
1675 if (code == 0 && cm_CheckDirOpForSingleChange(&dirop) && cnamep) {
1676 cm_DirDeleteEntry(&dirop, fnamep);
1678 cm_BPlusDirDeleteEntry(&dirop, cnamep);
1681 cm_EndDirOp(&dirop);
1684 cm_ReleaseSCache(scp);
1686 lock_ObtainWrite(&scp->rw);
1687 if (--scp->linkCount == 0)
1688 scp->flags |= CM_SCACHEFLAG_DELETED;
1689 cm_DiscardSCache(scp);
1690 lock_ReleaseWrite(&scp->rw);
1701 /* called with a write locked vnode, and fills in the link info.
1702 * returns this the vnode still write locked.
1704 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1711 lock_AssertWrite(&linkScp->rw);
1712 if (!linkScp->mountPointStringp[0]) {
1714 #ifdef AFS_FREELANCE_CLIENT
1715 /* File servers do not have data for freelance entries */
1716 if (cm_freelanceEnabled &&
1717 linkScp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
1718 linkScp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
1720 code = cm_FreelanceFetchMountPointString(linkScp);
1722 #endif /* AFS_FREELANCE_CLIENT */
1724 /* read the link data from the file server*/
1725 lock_ReleaseWrite(&linkScp->rw);
1726 thyper.LowPart = thyper.HighPart = 0;
1727 code = buf_Get(linkScp, &thyper, reqp, &bufp);
1728 lock_ObtainWrite(&linkScp->rw);
1732 code = cm_SyncOp(linkScp, bufp, userp, reqp, 0,
1733 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1738 cm_SyncOpDone(linkScp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1740 if (cm_HaveBuffer(linkScp, bufp, 0))
1743 code = cm_GetBuffer(linkScp, bufp, NULL, userp, reqp);
1748 } /* while loop to get the data */
1750 /* now if we still have no link read in,
1751 * copy the data from the buffer */
1752 if ((temp = linkScp->length.LowPart) >= MOUNTPOINTLEN) {
1754 return CM_ERROR_TOOBIG;
1757 /* otherwise, it fits; make sure it is still null (could have
1758 * lost race with someone else referencing this link above),
1759 * and if so, copy in the data.
1761 if (!linkScp->mountPointStringp[0]) {
1762 strncpy(linkScp->mountPointStringp, bufp->datap, temp);
1763 linkScp->mountPointStringp[temp] = 0; /* null terminate */
1768 if ( !strnicmp(linkScp->mountPointStringp, "msdfs:", strlen("msdfs:")) )
1769 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1771 } /* don't have sym link contents cached */
1776 /* called with a held vnode and a path suffix, with the held vnode being a
1777 * symbolic link. Our goal is to generate a new path to interpret, and return
1778 * this new path in newSpaceBufferp. If the new vnode is relative to a dir
1779 * other than the directory containing the symbolic link, then the new root is
1780 * returned in *newRootScpp, otherwise a null is returned there.
1782 long cm_AssembleLink(cm_scache_t *linkScp, fschar_t *pathSuffixp,
1783 cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1784 cm_user_t *userp, cm_req_t *reqp)
1791 *newRootScpp = NULL;
1792 *newSpaceBufferp = NULL;
1794 lock_ObtainWrite(&linkScp->rw);
1795 code = cm_HandleLink(linkScp, userp, reqp);
1799 /* if we may overflow the buffer, bail out; buffer is signficantly
1800 * bigger than max path length, so we don't really have to worry about
1801 * being a little conservative here.
1803 if (cm_FsStrLen(linkScp->mountPointStringp) + cm_FsStrLen(pathSuffixp) + 2
1804 >= CM_UTILS_SPACESIZE) {
1805 code = CM_ERROR_TOOBIG;
1809 tsp = cm_GetSpace();
1810 linkp = linkScp->mountPointStringp;
1811 if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1812 if (strlen(linkp) > cm_mountRootLen)
1813 StringCbCopyA((char *) tsp->data, sizeof(tsp->data), linkp+cm_mountRootLen+1);
1816 *newRootScpp = cm_RootSCachep(userp, reqp);
1817 cm_HoldSCache(*newRootScpp);
1818 } else if (linkp[0] == '\\' && linkp[1] == '\\') {
1819 if (!strnicmp(&linkp[2], cm_NetbiosName, (len = (long)strlen(cm_NetbiosName))))
1821 char * p = &linkp[len + 3];
1822 if (strnicmp(p, "all", 3) == 0)
1825 StringCbCopyA(tsp->data, sizeof(tsp->data), p);
1826 for (p = tsp->data; *p; p++) {
1830 *newRootScpp = cm_RootSCachep(userp, reqp);
1831 cm_HoldSCache(*newRootScpp);
1833 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1834 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1835 code = CM_ERROR_PATH_NOT_COVERED;
1837 } else if ( linkScp->fileType == CM_SCACHETYPE_DFSLINK ||
1838 !strnicmp(linkp, "msdfs:", (len = (long)strlen("msdfs:"))) ) {
1839 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1840 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1841 code = CM_ERROR_PATH_NOT_COVERED;
1842 } else if (*linkp == '\\' || *linkp == '/') {
1844 /* formerly, this was considered to be from the AFS root,
1845 * but this seems to create problems. instead, we will just
1846 * reject the link */
1847 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp+1);
1848 *newRootScpp = cm_RootSCachep(userp, reqp);
1849 cm_HoldSCache(*newRootScpp);
1851 /* we still copy the link data into the response so that
1852 * the user can see what the link points to
1854 linkScp->fileType = CM_SCACHETYPE_INVALID;
1855 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1856 code = CM_ERROR_NOSUCHPATH;
1859 /* a relative link */
1860 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1862 if (pathSuffixp[0] != 0) { /* if suffix string is non-null */
1863 StringCchCatA(tsp->data,lengthof(tsp->data), "\\");
1864 StringCchCatA(tsp->data,lengthof(tsp->data), pathSuffixp);
1868 clientchar_t * cpath = cm_FsStringToClientStringAlloc(tsp->data, -1, NULL);
1869 if (cpath != NULL) {
1870 cm_ClientStrCpy(tsp->wdata, lengthof(tsp->wdata), cpath);
1872 *newSpaceBufferp = tsp;
1874 code = CM_ERROR_NOSUCHPATH;
1881 if (code == CM_ERROR_PATH_NOT_COVERED && reqp->tidPathp && reqp->relPathp) {
1882 cm_VolStatus_Notify_DFS_Mapping(linkScp, reqp->tidPathp, reqp->relPathp);
1887 lock_ReleaseWrite(&linkScp->rw);
1890 #ifdef DEBUG_REFCOUNT
1891 long cm_NameIDbg(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
1892 cm_user_t *userp, clientchar_t *tidPathp, cm_req_t *reqp,
1893 cm_scache_t **outScpp,
1894 char * file, long line)
1896 long cm_NameI(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
1897 cm_user_t *userp, clientchar_t *tidPathp,
1898 cm_req_t *reqp, cm_scache_t **outScpp)
1902 clientchar_t *tp; /* ptr moving through input buffer */
1903 clientchar_t tc; /* temp char */
1904 int haveComponent; /* has new component started? */
1905 clientchar_t component[AFSPATHMAX]; /* this is the new component */
1906 clientchar_t *cp; /* component name being assembled */
1907 cm_scache_t *tscp; /* current location in the hierarchy */
1908 cm_scache_t *nscp; /* next dude down */
1909 cm_scache_t *dirScp; /* last dir we searched */
1910 cm_scache_t *linkScp; /* new root for the symlink we just
1912 cm_space_t *psp; /* space for current path, if we've hit
1914 cm_space_t *tempsp; /* temp vbl */
1915 clientchar_t *restp; /* rest of the pathname to interpret */
1916 int symlinkCount; /* count of # of symlinks traversed */
1917 int extraFlag; /* avoid chasing mt pts for dir cmd */
1918 int phase = 1; /* 1 = tidPathp, 2 = pathp */
1919 #define MAX_FID_COUNT 512
1920 cm_fid_t fids[MAX_FID_COUNT]; /* array of fids processed in this path walk */
1921 int fid_count = 0; /* number of fids processed in this path walk */
1926 #ifdef DEBUG_REFCOUNT
1927 afsi_log("%s:%d cm_NameI rootscp 0x%p ref %d", file, line, rootSCachep, rootSCachep->refCount);
1928 osi_Log4(afsd_logp,"cm_NameI rootscp 0x%p path %S tidpath %S flags 0x%x",
1929 rootSCachep, pathp ? pathp : L"<NULL>", tidPathp ? tidPathp : L"<NULL>",
1944 cm_HoldSCache(tscp);
1952 /* map Unix slashes into DOS ones so we can interpret Unix
1958 if (!haveComponent) {
1961 } else if (tc == 0) {
1975 /* we have a component here */
1976 if (tc == 0 || tc == '\\') {
1977 /* end of the component; we're at the last
1978 * component if tc == 0. However, if the last
1979 * is a symlink, we have more to do.
1981 *cp++ = 0; /* add null termination */
1983 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
1984 extraFlag = CM_FLAG_NOMOUNTCHASE;
1985 code = cm_Lookup(tscp, component,
1987 userp, reqp, &nscp);
1990 if (!cm_ClientStrCmp(component,_C("..")) ||
1991 !cm_ClientStrCmp(component,_C("."))) {
1993 * roll back the fid list until we find the
1994 * fid that matches where we are now. Its not
1995 * necessarily one or two fids because they
1996 * might have been symlinks or mount points or
1997 * both that were crossed.
1999 for ( i=fid_count-1; i>=0; i--) {
2000 if (!cm_FidCmp(&nscp->fid, &fids[i]))
2005 /* add the new fid to the list */
2006 if (fid_count == MAX_FID_COUNT) {
2007 code = CM_ERROR_TOO_MANY_SYMLINKS;
2008 cm_ReleaseSCache(nscp);
2012 fids[fid_count++] = nscp->fid;
2017 cm_ReleaseSCache(tscp);
2019 cm_ReleaseSCache(dirScp);
2022 if ((code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH) &&
2023 tscp->fileType == CM_SCACHETYPE_SYMLINK) {
2024 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_NOSUCHPATH");
2025 return CM_ERROR_NOSUCHPATH;
2027 osi_Log1(afsd_logp,"cm_NameI code 0x%x", code);
2032 haveComponent = 0; /* component done */
2034 cm_ReleaseSCache(dirScp);
2035 dirScp = tscp; /* for some symlinks */
2036 tscp = nscp; /* already held */
2038 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
2041 cm_ReleaseSCache(dirScp);
2047 /* now, if tscp is a symlink, we should follow it and
2048 * assemble the path again.
2050 lock_ObtainWrite(&tscp->rw);
2051 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
2052 CM_SCACHESYNC_GETSTATUS
2053 | CM_SCACHESYNC_NEEDCALLBACK);
2055 lock_ReleaseWrite(&tscp->rw);
2056 cm_ReleaseSCache(tscp);
2059 cm_ReleaseSCache(dirScp);
2064 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2066 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
2067 /* this is a symlink; assemble a new buffer */
2068 lock_ReleaseWrite(&tscp->rw);
2069 if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
2070 cm_ReleaseSCache(tscp);
2073 cm_ReleaseSCache(dirScp);
2078 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_TOO_MANY_SYMLINKS");
2079 return CM_ERROR_TOO_MANY_SYMLINKS;
2089 /* TODO: make this better */
2090 frestp = cm_ClientStringToFsStringAlloc(restp, -1, NULL);
2091 code = cm_AssembleLink(tscp, frestp, &linkScp, &tempsp, userp, reqp);
2095 if (code == 0 && linkScp != NULL) {
2096 if (linkScp == cm_data.rootSCachep) {
2100 for ( i=0; i<fid_count; i++) {
2101 if ( !cm_FidCmp(&linkScp->fid, &fids[i]) ) {
2102 code = CM_ERROR_TOO_MANY_SYMLINKS;
2103 cm_ReleaseSCache(linkScp);
2109 if (i == fid_count && fid_count < MAX_FID_COUNT) {
2110 fids[fid_count++] = linkScp->fid;
2115 /* something went wrong */
2116 cm_ReleaseSCache(tscp);
2119 cm_ReleaseSCache(dirScp);
2125 /* otherwise, tempsp has the new path,
2126 * and linkScp is the new root from
2127 * which to interpret that path.
2128 * Continue with the namei processing,
2129 * also doing the bookkeeping for the
2130 * space allocation and tracking the
2131 * vnode reference counts.
2137 cm_ReleaseSCache(tscp);
2142 * now, if linkScp is null, that's
2143 * AssembleLink's way of telling us that
2144 * the sym link is relative to the dir
2145 * containing the link. We have a ref
2146 * to it in dirScp, and we hold it now
2147 * and reuse it as the new spot in the
2155 /* not a symlink, we may be done */
2156 lock_ReleaseWrite(&tscp->rw);
2164 cm_ReleaseSCache(dirScp);
2172 cm_ReleaseSCache(dirScp);
2175 } /* end of a component */
2178 } /* we have a component */
2179 } /* big while loop over all components */
2183 cm_ReleaseSCache(dirScp);
2189 cm_ReleaseSCache(tscp);
2191 #ifdef DEBUG_REFCOUNT
2192 afsi_log("%s:%d cm_NameI code 0x%x outScpp 0x%p ref %d", file, line, code, *outScpp, (*outScpp) ? (*outScpp)->refCount : 0);
2194 osi_Log2(afsd_logp,"cm_NameI code 0x%x outScpp 0x%p", code, *outScpp);
2198 /* called with a dir, and a vnode within the dir that happens to be a symlink.
2199 * We chase the link, and return a held pointer to the target, if it exists,
2200 * in *outScpp. If we succeed, we return 0, otherwise we return an error code
2201 * and do not hold or return a target vnode.
2203 * This is very similar to calling cm_NameI with the last component of a name,
2204 * which happens to be a symlink, except that we've already passed by the name.
2206 * This function is typically called by the directory listing functions, which
2207 * encounter symlinks but need to return the proper file length so programs
2208 * like "more" work properly when they make use of the attributes retrieved from
2211 * The input vnode should not be locked when this function is called.
2213 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
2214 cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
2218 cm_scache_t *newRootScp;
2222 osi_Log1(afsd_logp, "Evaluating symlink scp 0x%p", linkScp);
2224 code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
2228 /* now, if newRootScp is NULL, we're really being told that the symlink
2229 * is relative to the current directory (dscp).
2231 if (newRootScp == NULL) {
2233 cm_HoldSCache(dscp);
2236 code = cm_NameI(newRootScp, spacep->wdata,
2237 CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
2238 userp, NULL, reqp, outScpp);
2240 if (code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH)
2241 code = CM_ERROR_NOSUCHPATH;
2243 /* this stuff is allocated no matter what happened on the namei call,
2245 cm_FreeSpace(spacep);
2246 cm_ReleaseSCache(newRootScp);
2248 if (linkScp == *outScpp) {
2249 cm_ReleaseSCache(*outScpp);
2251 code = CM_ERROR_NOSUCHPATH;
2257 /* for a given entry, make sure that it isn't in the stat cache, and then
2258 * add it to the list of file IDs to be obtained.
2260 * Don't bother adding it if we already have a vnode. Note that the dir
2261 * is locked, so we have to be careful checking the vnode we're thinking of
2262 * processing, to avoid deadlocks.
2264 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
2275 /* Don't overflow bsp. */
2276 if (bsp->counter >= CM_BULKMAX)
2277 return CM_ERROR_STOPNOW;
2279 thyper.LowPart = cm_data.buf_blockSize;
2280 thyper.HighPart = 0;
2281 thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
2283 /* thyper is now the first byte past the end of the record we're
2284 * interested in, and bsp->bufOffset is the first byte of the record
2285 * we're interested in.
2286 * Skip data in the others.
2289 if (LargeIntegerLessThan(*offp, bsp->bufOffset))
2291 if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
2292 return CM_ERROR_STOPNOW;
2293 if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
2296 cm_SetFid(&tfid, scp->fid.cell, scp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
2297 tscp = cm_FindSCache(&tfid);
2299 if (lock_TryWrite(&tscp->rw)) {
2300 /* we have an entry that we can look at */
2301 if (!(tscp->flags & CM_SCACHEFLAG_EACCESS) && cm_HaveCallback(tscp)) {
2302 /* we have a callback on it. Don't bother
2303 * fetching this stat entry, since we're happy
2304 * with the info we have.
2306 lock_ReleaseWrite(&tscp->rw);
2307 cm_ReleaseSCache(tscp);
2310 lock_ReleaseWrite(&tscp->rw);
2312 cm_ReleaseSCache(tscp);
2315 #ifdef AFS_FREELANCE_CLIENT
2316 // yj: if this is a mountpoint under root.afs then we don't want it
2317 // to be bulkstat-ed, instead, we call getSCache directly and under
2318 // getSCache, it is handled specially.
2319 if ( cm_freelanceEnabled &&
2320 tfid.cell==AFS_FAKE_ROOT_CELL_ID &&
2321 tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
2322 !(tfid.vnode==0x1 && tfid.unique==0x1) )
2324 osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
2325 return cm_GetSCache(&tfid, &tscp, NULL, NULL);
2327 #endif /* AFS_FREELANCE_CLIENT */
2330 bsp->fids[i].Volume = scp->fid.volume;
2331 bsp->fids[i].Vnode = tfid.vnode;
2332 bsp->fids[i].Unique = tfid.unique;
2337 cm_TryBulkStatRPC(cm_scache_t *dscp, cm_bulkStat_t *bbp, cm_user_t *userp, cm_req_t *reqp)
2340 AFSCBFids fidStruct;
2341 AFSBulkStats statStruct;
2343 AFSCBs callbackStruct;
2346 cm_callbackRequest_t cbReq;
2352 struct rx_connection * rxconnp;
2353 int inlinebulk; /* Did we use InlineBulkStatus RPC or not? */
2355 memset(&volSync, 0, sizeof(volSync));
2357 /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
2358 * make the calls to create the entries. Handle AFSCBMAX files at a
2361 for (filex = 0; filex < bbp->counter; filex += filesThisCall) {
2362 filesThisCall = bbp->counter - filex;
2363 if (filesThisCall > AFSCBMAX)
2364 filesThisCall = AFSCBMAX;
2366 fidStruct.AFSCBFids_len = filesThisCall;
2367 fidStruct.AFSCBFids_val = &bbp->fids[filex];
2368 statStruct.AFSBulkStats_len = filesThisCall;
2369 statStruct.AFSBulkStats_val = &bbp->stats[filex];
2370 callbackStruct.AFSCBs_len = filesThisCall;
2371 callbackStruct.AFSCBs_val = &bbp->callbacks[filex];
2372 cm_StartCallbackGrantingCall(NULL, &cbReq);
2373 osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
2376 * Whenever cm_Analyze is called for a RXAFS_ RPC there must
2377 * be a FID provided. However, the error code from RXAFS_BulkStatus
2378 * or RXAFS_InlinkBulkStatus does not apply to any FID. Therefore,
2379 * we generate an invalid FID to match with the RPC error.
2381 cm_SetFid(&tfid, dscp->fid.cell, dscp->fid.volume, 0, 0);
2386 code = cm_ConnFromFID(&tfid, userp, reqp, &connp);
2390 rxconnp = cm_GetRxConn(connp);
2391 if (!(connp->serverp->flags & CM_SERVERFLAG_NOINLINEBULK)) {
2392 code = RXAFS_InlineBulkStatus(rxconnp, &fidStruct,
2393 &statStruct, &callbackStruct, &volSync);
2394 if (code == RXGEN_OPCODE) {
2395 cm_SetServerNoInlineBulk(connp->serverp, 0);
2401 code = RXAFS_BulkStatus(rxconnp, &fidStruct,
2402 &statStruct, &callbackStruct, &volSync);
2404 rx_PutConnection(rxconnp);
2407 * If InlineBulk RPC was called and it succeeded,
2408 * then pull out the return code from the status info
2409 * and use it for cm_Analyze so that we can failover to other
2410 * .readonly volume instances. But only do it for errors that
2411 * are volume global.
2413 if (inlinebulk && code == 0 && (&bbp->stats[0])->errorCode) {
2414 osi_Log1(afsd_logp, "cm_TryBulkStat inline-bulk stat error: %d",
2415 (&bbp->stats[0])->errorCode);
2416 switch ((&bbp->stats[0])->errorCode) {
2425 code = (&bbp->stats[0])->errorCode;
2428 /* Rx and Rxkad errors are volume global */
2429 if ( (&bbp->stats[0])->errorCode >= -64 && (&bbp->stats[0])->errorCode < 0 ||
2430 (&bbp->stats[0])->errorCode >= ERROR_TABLE_BASE_RXK && (&bbp->stats[0])->errorCode < ERROR_TABLE_BASE_RXK + 256)
2431 code = (&bbp->stats[0])->errorCode;
2434 } while (cm_Analyze(connp, userp, reqp, &tfid, &volSync, NULL, &cbReq, code));
2435 code = cm_MapRPCError(code, reqp);
2438 * might as well quit on an error, since we're not going to do
2439 * much better on the next immediate call, either.
2442 osi_Log2(afsd_logp, "CALL %sBulkStatus FAILURE code 0x%x",
2443 inlinebulk ? "Inline" : "", code);
2444 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2449 * The bulk RPC has succeeded or at least not failed with a
2450 * volume global error result. For items that have inlineBulk
2451 * errors we must call cm_Analyze in order to perform required
2452 * logging of errors.
2454 * If the RPC was not inline bulk or the entry either has no error
2455 * the status must be merged.
2457 osi_Log1(afsd_logp, "CALL %sBulkStatus SUCCESS", inlinebulk ? "Inline" : "");
2459 for (i = 0; i<filesThisCall; i++) {
2461 cm_SetFid(&tfid, dscp->fid.cell, bbp->fids[j].Volume, bbp->fids[j].Vnode, bbp->fids[j].Unique);
2463 if (inlinebulk && (&bbp->stats[j])->errorCode) {
2464 cm_req_t treq = *reqp;
2465 cm_Analyze(NULL, userp, &treq, &tfid, &volSync, NULL, &cbReq, (&bbp->stats[j])->errorCode);
2467 code = cm_GetSCache(&tfid, &scp, userp, reqp);
2472 * otherwise, if this entry has no callback info,
2473 * merge in this. If there is existing callback info
2474 * we skip the merge because the existing data must be
2475 * current (we have a callback) and the response from
2476 * a non-inline bulk rpc might actually be wrong.
2478 * now, we have to be extra paranoid on merging in this
2479 * information, since we didn't use cm_SyncOp before
2480 * starting the fetch to make sure that no bad races
2481 * were occurring. Specifically, we need to make sure
2482 * we don't obliterate any newer information in the
2483 * vnode than have here.
2485 * Right now, be pretty conservative: if there's a
2486 * callback or a pending call, skip it.
2487 * However, if the prior attempt to obtain status
2488 * was refused access or the volume is .readonly,
2489 * take the data in any case since we have nothing
2490 * better for the in flight directory enumeration that
2491 * resulted in this function being called.
2493 lock_ObtainRead(&scp->rw);
2494 if ((scp->cbServerp == NULL &&
2495 !(scp->flags & (CM_SCACHEFLAG_FETCHING | CM_SCACHEFLAG_STORING | CM_SCACHEFLAG_SIZESTORING))) ||
2496 (scp->flags & CM_SCACHEFLAG_PURERO) ||
2497 (scp->flags & CM_SCACHEFLAG_EACCESS))
2499 lock_ConvertRToW(&scp->rw);
2500 cm_EndCallbackGrantingCall(scp, &cbReq,
2503 CM_CALLBACK_MAINTAINCOUNT);
2504 cm_MergeStatus(dscp, scp, &bbp->stats[j], &volSync, userp, reqp, 0);
2505 lock_ReleaseWrite(&scp->rw);
2507 lock_ReleaseRead(&scp->rw);
2509 cm_ReleaseSCache(scp);
2511 } /* all files in the response */
2512 /* now tell it to drop the count,
2513 * after doing the vnode processing above */
2514 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2515 } /* while there are still more files to process */
2520 /* called with a write locked scp and a pointer to a buffer. Make bulk stat
2521 * calls on all undeleted files in the page of the directory specified.
2524 cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
2530 osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%p", dscp);
2532 /* should be on a buffer boundary */
2533 osi_assertx((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0, "invalid offset");
2535 bbp = malloc(sizeof(cm_bulkStat_t));
2536 memset(bbp, 0, sizeof(cm_bulkStat_t));
2537 bbp->bufOffset = *offsetp;
2539 lock_ReleaseWrite(&dscp->rw);
2540 /* first, assemble the file IDs we need to stat */
2541 code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) bbp, offsetp, userp, reqp, NULL);
2543 /* if we failed, bail out early */
2544 if (code && code != CM_ERROR_STOPNOW) {
2546 lock_ObtainWrite(&dscp->rw);
2550 code = cm_TryBulkStatRPC(dscp, bbp, userp, reqp);
2551 osi_Log1(afsd_logp, "END cm_TryBulkStat code 0x%x", code);
2553 lock_ObtainWrite(&dscp->rw);
2558 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
2562 /* initialize store back mask as inexpensive local variable */
2564 memset(statusp, 0, sizeof(AFSStoreStatus));
2566 /* copy out queued info from scache first, if scp passed in */
2568 if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
2569 statusp->ClientModTime = scp->clientModTime;
2570 mask |= AFS_SETMODTIME;
2571 scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
2576 /* now add in our locally generated request */
2577 if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
2578 statusp->ClientModTime = attrp->clientModTime;
2579 mask |= AFS_SETMODTIME;
2581 if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
2582 statusp->UnixModeBits = attrp->unixModeBits;
2583 mask |= AFS_SETMODE;
2585 if (attrp->mask & CM_ATTRMASK_OWNER) {
2586 statusp->Owner = attrp->owner;
2587 mask |= AFS_SETOWNER;
2589 if (attrp->mask & CM_ATTRMASK_GROUP) {
2590 statusp->Group = attrp->group;
2591 mask |= AFS_SETGROUP;
2594 statusp->Mask = mask;
2597 /* set the file size, and make sure that all relevant buffers have been
2598 * truncated. Ensure that any partially truncated buffers have been zeroed
2599 * to the end of the buffer.
2601 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
2607 /* start by locking out buffer creation */
2608 lock_ObtainWrite(&scp->bufCreateLock);
2610 /* verify that this is a file, not a dir or a symlink */
2611 lock_ObtainWrite(&scp->rw);
2612 code = cm_SyncOp(scp, NULL, userp, reqp, 0,
2613 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2616 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2618 if (scp->fileType != CM_SCACHETYPE_FILE) {
2619 code = CM_ERROR_ISDIR;
2624 if (LargeIntegerLessThan(*sizep, scp->length))
2629 lock_ReleaseWrite(&scp->rw);
2631 /* can't hold scp->rw lock here, since we may wait for a storeback to
2632 * finish if the buffer package is cleaning a buffer by storing it to
2636 buf_Truncate(scp, userp, reqp, sizep);
2638 /* now ensure that file length is short enough, and update truncPos */
2639 lock_ObtainWrite(&scp->rw);
2641 /* make sure we have a callback (so we have the right value for the
2642 * length), and wait for it to be safe to do a truncate.
2644 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
2645 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2646 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2648 /* If we only have 'i' bits, then we should still be able to set
2649 the size of a file we created. */
2650 if (code == CM_ERROR_NOACCESS && scp->creator == userp) {
2651 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
2652 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2653 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2659 if (LargeIntegerLessThan(*sizep, scp->length)) {
2660 /* a real truncation. If truncPos is not set yet, or is bigger
2661 * than where we're truncating the file, set truncPos to this
2666 if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
2667 || LargeIntegerLessThan(*sizep, scp->length)) {
2669 scp->truncPos = *sizep;
2670 scp->mask |= CM_SCACHEMASK_TRUNCPOS;
2672 /* in either case, the new file size has been changed */
2673 scp->length = *sizep;
2674 scp->mask |= CM_SCACHEMASK_LENGTH;
2676 else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
2677 /* really extending the file */
2678 scp->length = *sizep;
2679 scp->mask |= CM_SCACHEMASK_LENGTH;
2682 /* done successfully */
2685 cm_SyncOpDone(scp, NULL,
2686 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2687 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2690 lock_ReleaseWrite(&scp->rw);
2691 lock_ReleaseWrite(&scp->bufCreateLock);
2696 /* set the file size or other attributes (but not both at once) */
2697 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
2701 AFSFetchStatus afsOutStatus;
2705 AFSStoreStatus afsInStatus;
2706 struct rx_connection * rxconnp;
2708 memset(&volSync, 0, sizeof(volSync));
2710 /* handle file length setting */
2711 if (attrp->mask & CM_ATTRMASK_LENGTH)
2712 return cm_SetLength(scp, &attrp->length, userp, reqp);
2714 lock_ObtainWrite(&scp->rw);
2715 /* otherwise, we have to make an RPC to get the status */
2716 code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
2718 lock_ReleaseWrite(&scp->rw);
2721 lock_ConvertWToR(&scp->rw);
2723 /* make the attr structure */
2724 cm_StatusFromAttr(&afsInStatus, scp, attrp);
2726 tfid.Volume = scp->fid.volume;
2727 tfid.Vnode = scp->fid.vnode;
2728 tfid.Unique = scp->fid.unique;
2729 lock_ReleaseRead(&scp->rw);
2731 /* now make the RPC */
2732 osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%p", scp);
2734 code = cm_ConnFromFID(&scp->fid, userp, reqp, &connp);
2738 rxconnp = cm_GetRxConn(connp);
2739 code = RXAFS_StoreStatus(rxconnp, &tfid,
2740 &afsInStatus, &afsOutStatus, &volSync);
2741 rx_PutConnection(rxconnp);
2743 } while (cm_Analyze(connp, userp, reqp,
2744 &scp->fid, &volSync, NULL, NULL, code));
2745 code = cm_MapRPCError(code, reqp);
2748 osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
2750 osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
2752 lock_ObtainWrite(&scp->rw);
2753 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
2755 cm_MergeStatus(NULL, scp, &afsOutStatus, &volSync, userp, reqp,
2756 CM_MERGEFLAG_FORCE|CM_MERGEFLAG_STOREDATA);
2758 /* if we're changing the mode bits, discard the ACL cache,
2759 * since we changed the mode bits.
2761 if (afsInStatus.Mask & AFS_SETMODE)
2762 cm_FreeAllACLEnts(scp);
2763 lock_ReleaseWrite(&scp->rw);
2767 long cm_Create(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
2768 cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
2773 cm_callbackRequest_t cbReq;
2776 cm_scache_t *scp = NULL;
2778 AFSStoreStatus inStatus;
2779 AFSFetchStatus updatedDirStatus;
2780 AFSFetchStatus newFileStatus;
2781 AFSCallBack newFileCallback;
2783 struct rx_connection * rxconnp;
2785 fschar_t * fnamep = NULL;
2787 memset(&volSync, 0, sizeof(volSync));
2789 /* can't create names with @sys in them; must expand it manually first.
2790 * return "invalid request" if they try.
2792 if (cm_ExpandSysName(cnamep, NULL, 0, 0)) {
2793 return CM_ERROR_ATSYS;
2796 #ifdef AFS_FREELANCE_CLIENT
2797 /* Freelance root volume does not hold files */
2798 if (cm_freelanceEnabled &&
2799 dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
2800 dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
2802 return CM_ERROR_NOACCESS;
2804 #endif /* AFS_FREELANCE_CLIENT */
2806 /* before starting the RPC, mark that we're changing the file data, so
2807 * that someone who does a chmod will know to wait until our call
2810 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
2812 lock_ObtainWrite(&dscp->rw);
2813 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2814 lock_ReleaseWrite(&dscp->rw);
2816 cm_StartCallbackGrantingCall(NULL, &cbReq);
2818 cm_EndDirOp(&dirop);
2825 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
2827 cm_StatusFromAttr(&inStatus, NULL, attrp);
2829 /* try the RPC now */
2830 osi_Log1(afsd_logp, "CALL CreateFile scp 0x%p", dscp);
2832 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2836 dirAFSFid.Volume = dscp->fid.volume;
2837 dirAFSFid.Vnode = dscp->fid.vnode;
2838 dirAFSFid.Unique = dscp->fid.unique;
2840 rxconnp = cm_GetRxConn(connp);
2841 code = RXAFS_CreateFile(connp->rxconnp, &dirAFSFid, fnamep,
2842 &inStatus, &newAFSFid, &newFileStatus,
2843 &updatedDirStatus, &newFileCallback,
2845 rx_PutConnection(rxconnp);
2847 } while (cm_Analyze(connp, userp, reqp,
2848 &dscp->fid, &volSync, NULL, &cbReq, code));
2849 code = cm_MapRPCError(code, reqp);
2852 osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
2854 osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
2857 lock_ObtainWrite(&dirop.scp->dirlock);
2858 dirop.lockType = CM_DIRLOCK_WRITE;
2860 lock_ObtainWrite(&dscp->rw);
2861 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2863 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
2865 lock_ReleaseWrite(&dscp->rw);
2867 /* now try to create the file's entry, too, but be careful to
2868 * make sure that we don't merge in old info. Since we weren't locking
2869 * out any requests during the file's creation, we may have pretty old
2873 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
2874 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2876 lock_ObtainWrite(&scp->rw);
2877 scp->creator = userp; /* remember who created it */
2878 if (!cm_HaveCallback(scp)) {
2879 cm_EndCallbackGrantingCall(scp, &cbReq,
2880 &newFileCallback, &volSync, 0);
2881 cm_MergeStatus(dscp, scp, &newFileStatus, &volSync,
2885 lock_ReleaseWrite(&scp->rw);
2889 /* make sure we end things properly */
2891 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2893 if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
2894 cm_DirCreateEntry(&dirop, fnamep, &newFid);
2896 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
2899 cm_EndDirOp(&dirop);
2908 cm_ReleaseSCache(scp);
2914 * locked if TRUE means write-locked
2915 * else the cm_scache_t rw must not be held
2917 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp, afs_uint32 locked)
2922 lock_ReleaseWrite(&scp->rw);
2923 code = buf_CleanVnode(scp, userp, reqp);
2925 lock_ObtainWrite(&scp->rw);
2927 if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
2928 | CM_SCACHEMASK_CLIENTMODTIME
2929 | CM_SCACHEMASK_LENGTH))
2930 code = cm_StoreMini(scp, userp, reqp);
2932 if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) {
2933 code = (scp->flags & CM_SCACHEFLAG_OVERQUOTA) ? CM_ERROR_QUOTA : CM_ERROR_SPACE;
2934 scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE);
2938 lock_ReleaseWrite(&scp->rw);
2939 } else if (locked) {
2940 lock_ObtainWrite(&scp->rw);
2945 long cm_MakeDir(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
2946 cm_user_t *userp, cm_req_t *reqp, cm_scache_t **scpp)
2951 cm_callbackRequest_t cbReq;
2954 cm_scache_t *scp = NULL;
2956 AFSStoreStatus inStatus;
2957 AFSFetchStatus updatedDirStatus;
2958 AFSFetchStatus newDirStatus;
2959 AFSCallBack newDirCallback;
2961 struct rx_connection * rxconnp;
2963 fschar_t * fnamep = NULL;
2965 memset(&volSync, 0, sizeof(volSync));
2967 /* can't create names with @sys in them; must expand it manually first.
2968 * return "invalid request" if they try.
2970 if (cm_ExpandSysName(cnamep, NULL, 0, 0)) {
2971 return CM_ERROR_ATSYS;
2974 #ifdef AFS_FREELANCE_CLIENT
2975 /* Freelance root volume does not hold subdirectories */
2976 if (cm_freelanceEnabled &&
2977 dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
2978 dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
2980 return CM_ERROR_NOACCESS;
2982 #endif /* AFS_FREELANCE_CLIENT */
2984 /* before starting the RPC, mark that we're changing the directory
2985 * data, so that someone who does a chmod on the dir will wait until
2986 * our call completes.
2988 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
2990 lock_ObtainWrite(&dscp->rw);
2991 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2992 lock_ReleaseWrite(&dscp->rw);
2994 cm_StartCallbackGrantingCall(NULL, &cbReq);
2996 cm_EndDirOp(&dirop);
3003 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3004 cm_StatusFromAttr(&inStatus, NULL, attrp);
3006 /* try the RPC now */
3007 osi_Log1(afsd_logp, "CALL MakeDir scp 0x%p", dscp);
3009 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3013 dirAFSFid.Volume = dscp->fid.volume;
3014 dirAFSFid.Vnode = dscp->fid.vnode;
3015 dirAFSFid.Unique = dscp->fid.unique;
3017 rxconnp = cm_GetRxConn(connp);
3018 code = RXAFS_MakeDir(connp->rxconnp, &dirAFSFid, fnamep,
3019 &inStatus, &newAFSFid, &newDirStatus,
3020 &updatedDirStatus, &newDirCallback,
3022 rx_PutConnection(rxconnp);
3024 } while (cm_Analyze(connp, userp, reqp,
3025 &dscp->fid, &volSync, NULL, &cbReq, code));
3026 code = cm_MapRPCError(code, reqp);
3029 osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
3031 osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
3034 lock_ObtainWrite(&dirop.scp->dirlock);
3035 dirop.lockType = CM_DIRLOCK_WRITE;
3037 lock_ObtainWrite(&dscp->rw);
3038 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3040 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3042 lock_ReleaseWrite(&dscp->rw);
3044 /* now try to create the new dir's entry, too, but be careful to
3045 * make sure that we don't merge in old info. Since we weren't locking
3046 * out any requests during the file's creation, we may have pretty old
3050 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3051 code = cm_GetSCache(&newFid, &scp, userp, reqp);
3053 lock_ObtainWrite(&scp->rw);
3054 if (!cm_HaveCallback(scp)) {
3055 cm_EndCallbackGrantingCall(scp, &cbReq,
3056 &newDirCallback, &volSync, 0);
3057 cm_MergeStatus(dscp, scp, &newDirStatus, &volSync,
3061 lock_ReleaseWrite(&scp->rw);
3065 /* make sure we end things properly */
3067 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
3069 if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
3070 cm_DirCreateEntry(&dirop, fnamep, &newFid);
3072 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
3075 cm_EndDirOp(&dirop);
3083 cm_ReleaseSCache(scp);
3086 /* and return error code */
3090 long cm_Link(cm_scache_t *dscp, clientchar_t *cnamep, cm_scache_t *sscp, long flags,
3091 cm_user_t *userp, cm_req_t *reqp)
3096 AFSFid existingAFSFid;
3097 AFSFetchStatus updatedDirStatus;
3098 AFSFetchStatus newLinkStatus;
3100 struct rx_connection * rxconnp;
3102 fschar_t * fnamep = NULL;
3104 memset(&volSync, 0, sizeof(volSync));
3106 if (dscp->fid.cell != sscp->fid.cell ||
3107 dscp->fid.volume != sscp->fid.volume) {
3108 return CM_ERROR_CROSSDEVLINK;
3111 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3113 lock_ObtainWrite(&dscp->rw);
3114 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3115 lock_ReleaseWrite(&dscp->rw);
3117 cm_EndDirOp(&dirop);
3122 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3124 /* try the RPC now */
3125 osi_Log1(afsd_logp, "CALL Link scp 0x%p", dscp);
3127 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3130 dirAFSFid.Volume = dscp->fid.volume;
3131 dirAFSFid.Vnode = dscp->fid.vnode;
3132 dirAFSFid.Unique = dscp->fid.unique;
3134 existingAFSFid.Volume = sscp->fid.volume;
3135 existingAFSFid.Vnode = sscp->fid.vnode;
3136 existingAFSFid.Unique = sscp->fid.unique;
3138 rxconnp = cm_GetRxConn(connp);
3139 code = RXAFS_Link(rxconnp, &dirAFSFid, fnamep, &existingAFSFid,
3140 &newLinkStatus, &updatedDirStatus, &volSync);
3141 rx_PutConnection(rxconnp);
3142 osi_Log1(afsd_logp," RXAFS_Link returns 0x%x", code);
3144 } while (cm_Analyze(connp, userp, reqp,
3145 &dscp->fid, &volSync, NULL, NULL, code));
3147 code = cm_MapRPCError(code, reqp);
3150 osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
3152 osi_Log0(afsd_logp, "CALL Link SUCCESS");
3155 lock_ObtainWrite(&dirop.scp->dirlock);
3156 dirop.lockType = CM_DIRLOCK_WRITE;
3158 lock_ObtainWrite(&dscp->rw);
3159 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3161 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3163 lock_ReleaseWrite(&dscp->rw);
3166 if (cm_CheckDirOpForSingleChange(&dirop)) {
3167 cm_DirCreateEntry(&dirop, fnamep, &sscp->fid);
3169 cm_BPlusDirCreateEntry(&dirop, cnamep, &sscp->fid);
3173 cm_EndDirOp(&dirop);
3175 /* Update the linked object status */
3177 lock_ObtainWrite(&sscp->rw);
3178 cm_MergeStatus(NULL, sscp, &newLinkStatus, &volSync, userp, reqp, 0);
3179 lock_ReleaseWrite(&sscp->rw);
3187 long cm_SymLink(cm_scache_t *dscp, clientchar_t *cnamep, fschar_t *contentsp, long flags,
3188 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
3196 AFSStoreStatus inStatus;
3197 AFSFetchStatus updatedDirStatus;
3198 AFSFetchStatus newLinkStatus;
3200 struct rx_connection * rxconnp;
3202 fschar_t *fnamep = NULL;
3204 memset(&volSync, 0, sizeof(volSync));
3206 /* before starting the RPC, mark that we're changing the directory data,
3207 * so that someone who does a chmod on the dir will wait until our
3210 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3212 lock_ObtainWrite(&dscp->rw);
3213 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3214 lock_ReleaseWrite(&dscp->rw);
3216 cm_EndDirOp(&dirop);
3221 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3223 cm_StatusFromAttr(&inStatus, NULL, attrp);
3225 /* try the RPC now */
3226 osi_Log1(afsd_logp, "CALL Symlink scp 0x%p", dscp);
3228 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3232 dirAFSFid.Volume = dscp->fid.volume;
3233 dirAFSFid.Vnode = dscp->fid.vnode;
3234 dirAFSFid.Unique = dscp->fid.unique;
3236 rxconnp = cm_GetRxConn(connp);
3237 code = RXAFS_Symlink(rxconnp, &dirAFSFid, fnamep, contentsp,
3238 &inStatus, &newAFSFid, &newLinkStatus,
3239 &updatedDirStatus, &volSync);
3240 rx_PutConnection(rxconnp);
3242 } while (cm_Analyze(connp, userp, reqp,
3243 &dscp->fid, &volSync, NULL, NULL, code));
3244 code = cm_MapRPCError(code, reqp);
3247 osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
3249 osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
3252 lock_ObtainWrite(&dirop.scp->dirlock);
3253 dirop.lockType = CM_DIRLOCK_WRITE;
3255 lock_ObtainWrite(&dscp->rw);
3256 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3258 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3260 lock_ReleaseWrite(&dscp->rw);
3263 if (cm_CheckDirOpForSingleChange(&dirop)) {
3264 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3266 cm_DirCreateEntry(&dirop, fnamep, &newFid);
3268 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
3272 cm_EndDirOp(&dirop);
3274 /* now try to create the new dir's entry, too, but be careful to
3275 * make sure that we don't merge in old info. Since we weren't locking
3276 * out any requests during the file's creation, we may have pretty old
3280 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3281 code = cm_GetSCache(&newFid, &scp, userp, reqp);
3283 lock_ObtainWrite(&scp->rw);
3284 if (!cm_HaveCallback(scp)) {
3285 cm_MergeStatus(dscp, scp, &newLinkStatus, &volSync,
3288 lock_ReleaseWrite(&scp->rw);
3289 cm_ReleaseSCache(scp);
3295 /* and return error code */
3299 /*! \brief Remove a directory
3301 Encapsulates a call to RXAFS_RemoveDir().
3303 \param[in] dscp cm_scache_t for the directory containing the
3304 directory to be removed.
3306 \param[in] fnamep This will be the original name of the directory
3307 as known to the file server. It will be passed in to RXAFS_RemoveDir().
3308 This parameter is optional. If it is not provided the value
3311 \param[in] cnamep Normalized name used to update the local
3314 \param[in] userp cm_user_t for the request.
3316 \param[in] reqp Request tracker.
3318 long cm_RemoveDir(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t *cnamep, cm_user_t *userp, cm_req_t *reqp)
3324 AFSFetchStatus updatedDirStatus;
3326 struct rx_connection * rxconnp;
3328 cm_scache_t *scp = NULL;
3329 int free_fnamep = FALSE;
3331 memset(&volSync, 0, sizeof(volSync));
3333 if (fnamep == NULL) {
3336 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ,
3337 CM_DIROP_FLAG_NONE, &dirop);
3339 code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
3342 cm_EndDirOp(&dirop);
3349 code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
3353 /* before starting the RPC, mark that we're changing the directory data,
3354 * so that someone who does a chmod on the dir will wait until our
3357 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3359 lock_ObtainWrite(&dscp->rw);
3360 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3361 lock_ReleaseWrite(&dscp->rw);
3363 cm_EndDirOp(&dirop);
3368 /* try the RPC now */
3369 osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%p", dscp);
3371 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3375 dirAFSFid.Volume = dscp->fid.volume;
3376 dirAFSFid.Vnode = dscp->fid.vnode;
3377 dirAFSFid.Unique = dscp->fid.unique;
3379 rxconnp = cm_GetRxConn(connp);
3380 code = RXAFS_RemoveDir(rxconnp, &dirAFSFid, fnamep,
3381 &updatedDirStatus, &volSync);
3382 rx_PutConnection(rxconnp);
3384 } while (cm_Analyze(connp, userp, reqp,
3385 &dscp->fid, &volSync, NULL, NULL, code));
3386 code = cm_MapRPCErrorRmdir(code, reqp);
3389 osi_Log1(afsd_logp, "CALL RemoveDir FAILURE, code 0x%x", code);
3391 osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
3394 lock_ObtainWrite(&dirop.scp->dirlock);
3395 dirop.lockType = CM_DIRLOCK_WRITE;
3397 lock_ObtainWrite(&dscp->rw);
3398 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3400 cm_dnlcRemove(dscp, cnamep);
3401 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3403 lock_ReleaseWrite(&dscp->rw);
3406 if (cm_CheckDirOpForSingleChange(&dirop) && cnamep != NULL) {
3407 cm_DirDeleteEntry(&dirop, fnamep);
3409 cm_BPlusDirDeleteEntry(&dirop, cnamep);
3413 cm_EndDirOp(&dirop);
3416 cm_ReleaseSCache(scp);
3418 lock_ObtainWrite(&scp->rw);
3419 scp->flags |= CM_SCACHEFLAG_DELETED;
3420 lock_ReleaseWrite(&scp->rw);
3428 /* and return error code */
3432 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
3434 /* grab mutex on contents */
3435 lock_ObtainWrite(&scp->rw);
3437 /* reset the prefetch info */
3438 scp->prefetch.base.LowPart = 0; /* base */
3439 scp->prefetch.base.HighPart = 0;
3440 scp->prefetch.end.LowPart = 0; /* and end */
3441 scp->prefetch.end.HighPart = 0;
3443 /* release mutex on contents */
3444 lock_ReleaseWrite(&scp->rw);
3450 /*! \brief Rename a file or directory
3452 Encapsulates a RXAFS_Rename() call.
3454 \param[in] oldDscp cm_scache_t for the directory containing the old
3457 \param[in] oldNamep The original old name known to the file server.
3458 This is the name that will be passed into the RXAFS_Rename().
3459 If it is not provided, it will be looked up.
3461 \param[in] normalizedOldNamep Normalized old name. This is used for
3462 updating local directory caches.
3464 \param[in] newDscp cm_scache_t for the directory containing the new
3467 \param[in] newNamep New name. Normalized.
3469 \param[in] userp cm_user_t for the request.
3471 \param[in,out] reqp Request tracker.
3474 long cm_Rename(cm_scache_t *oldDscp, fschar_t *oldNamep, clientchar_t *cOldNamep,
3475 cm_scache_t *newDscp, clientchar_t *cNewNamep, cm_user_t *userp,
3480 AFSFid oldDirAFSFid;
3481 AFSFid newDirAFSFid;
3483 AFSFetchStatus updatedOldDirStatus;
3484 AFSFetchStatus updatedNewDirStatus;
3487 struct rx_connection * rxconnp;
3488 cm_dirOp_t oldDirOp;
3491 cm_dirOp_t newDirOp;
3492 fschar_t * newNamep = NULL;
3493 int free_oldNamep = FALSE;
3494 cm_scache_t *oldScp = NULL, *newScp = NULL;
3496 memset(&volSync, 0, sizeof(volSync));
3498 if (cOldNamep == NULL || cNewNamep == NULL ||
3499 cm_ClientStrLen(cOldNamep) == 0 ||
3500 cm_ClientStrLen(cNewNamep) == 0)
3501 return CM_ERROR_INVAL;
3504 * Before we permit the operation, make sure that we do not already have
3505 * an object in the destination directory that has a case-insensitive match
3506 * for this name UNLESS the matching object is the object we are renaming.
3508 code = cm_Lookup(oldDscp, cOldNamep, 0, userp, reqp, &oldScp);
3510 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S old name lookup failed",
3511 oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3515 code = cm_Lookup(newDscp, cNewNamep, CM_FLAG_CASEFOLD, userp, reqp, &newScp);
3517 /* found a matching object with the new name */
3518 if (cm_FidCmp(&oldScp->fid, &newScp->fid)) {
3519 /* and they don't match so return an error */
3520 osi_Log2(afsd_logp, "cm_Rename newDscp 0x%p cNewName %S new name already exists",
3521 newDscp, osi_LogSaveStringW(afsd_logp, cNewNamep));
3522 code = CM_ERROR_EXISTS;
3524 cm_ReleaseSCache(newScp);
3526 } else if (code == CM_ERROR_AMBIGUOUS_FILENAME) {
3527 code = CM_ERROR_EXISTS;
3534 if (oldNamep == NULL) {
3537 code = cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_READ,
3538 CM_DIROP_FLAG_NONE, &oldDirOp);
3540 code = cm_BPlusDirLookupOriginalName(&oldDirOp, cOldNamep, &oldNamep);
3542 free_oldNamep = TRUE;
3543 cm_EndDirOp(&oldDirOp);
3547 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S Original Name lookup failed",
3548 oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3554 /* before starting the RPC, mark that we're changing the directory data,
3555 * so that someone who does a chmod on the dir will wait until our call
3556 * completes. We do this in vnode order so that we don't deadlock,
3557 * which makes the code a little verbose.
3559 if (oldDscp == newDscp) {
3560 /* check for identical names */
3561 if (cm_ClientStrCmp(cOldNamep, cNewNamep) == 0) {
3562 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_RENAME_IDENTICAL",
3564 code = CM_ERROR_RENAME_IDENTICAL;
3569 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE,
3570 CM_DIROP_FLAG_NONE, &oldDirOp);
3571 lock_ObtainWrite(&oldDscp->rw);
3572 cm_dnlcRemove(oldDscp, cOldNamep);
3573 cm_dnlcRemove(oldDscp, cNewNamep);
3574 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3575 CM_SCACHESYNC_STOREDATA);
3576 lock_ReleaseWrite(&oldDscp->rw);
3578 cm_EndDirOp(&oldDirOp);
3582 /* two distinct dir vnodes */
3584 if (oldDscp->fid.cell != newDscp->fid.cell ||
3585 oldDscp->fid.volume != newDscp->fid.volume) {
3586 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_CROSSDEVLINK",
3588 code = CM_ERROR_CROSSDEVLINK;
3592 /* shouldn't happen that we have distinct vnodes for two
3593 * different files, but could due to deliberate attack, or
3594 * stale info. Avoid deadlocks and quit now.
3596 if (oldDscp->fid.vnode == newDscp->fid.vnode) {
3597 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p vnode collision",
3599 code = CM_ERROR_CROSSDEVLINK;
3603 if (oldDscp->fid.vnode < newDscp->fid.vnode) {
3604 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE,
3605 CM_DIROP_FLAG_NONE, &oldDirOp);
3606 lock_ObtainWrite(&oldDscp->rw);
3607 cm_dnlcRemove(oldDscp, cOldNamep);
3608 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3609 CM_SCACHESYNC_STOREDATA);
3610 lock_ReleaseWrite(&oldDscp->rw);
3612 cm_EndDirOp(&oldDirOp);
3614 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE,
3615 CM_DIROP_FLAG_NONE, &newDirOp);
3616 lock_ObtainWrite(&newDscp->rw);
3617 cm_dnlcRemove(newDscp, cNewNamep);
3618 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3619 CM_SCACHESYNC_STOREDATA);
3620 lock_ReleaseWrite(&newDscp->rw);
3622 cm_EndDirOp(&newDirOp);
3624 /* cleanup first one */
3625 lock_ObtainWrite(&oldDscp->rw);
3626 cm_SyncOpDone(oldDscp, NULL,
3627 CM_SCACHESYNC_STOREDATA);
3628 lock_ReleaseWrite(&oldDscp->rw);
3629 cm_EndDirOp(&oldDirOp);
3634 /* lock the new vnode entry first */
3635 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE,
3636 CM_DIROP_FLAG_NONE, &newDirOp);
3637 lock_ObtainWrite(&newDscp->rw);
3638 cm_dnlcRemove(newDscp, cNewNamep);
3639 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3640 CM_SCACHESYNC_STOREDATA);
3641 lock_ReleaseWrite(&newDscp->rw);
3643 cm_EndDirOp(&newDirOp);
3645 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE,
3646 CM_DIROP_FLAG_NONE, &oldDirOp);
3647 lock_ObtainWrite(&oldDscp->rw);
3648 cm_dnlcRemove(oldDscp, cOldNamep);
3649 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3650 CM_SCACHESYNC_STOREDATA);
3651 lock_ReleaseWrite(&oldDscp->rw);
3653 cm_EndDirOp(&oldDirOp);
3655 /* cleanup first one */
3656 lock_ObtainWrite(&newDscp->rw);
3657 cm_SyncOpDone(newDscp, NULL,
3658 CM_SCACHESYNC_STOREDATA);
3659 lock_ReleaseWrite(&newDscp->rw);
3660 cm_EndDirOp(&newDirOp);
3664 } /* two distinct vnodes */
3671 newNamep = cm_ClientStringToFsStringAlloc(cNewNamep, -1, NULL);
3673 /* try the RPC now */
3674 osi_Log2(afsd_logp, "CALL Rename old scp 0x%p new scp 0x%p",
3677 code = cm_ConnFromFID(&oldDscp->fid, userp, reqp, &connp);
3681 oldDirAFSFid.Volume = oldDscp->fid.volume;
3682 oldDirAFSFid.Vnode = oldDscp->fid.vnode;
3683 oldDirAFSFid.Unique = oldDscp->fid.unique;
3684 newDirAFSFid.Volume = newDscp->fid.volume;
3685 newDirAFSFid.Vnode = newDscp->fid.vnode;
3686 newDirAFSFid.Unique = newDscp->fid.unique;
3688 rxconnp = cm_GetRxConn(connp);
3689 code = RXAFS_Rename(rxconnp, &oldDirAFSFid, oldNamep,
3690 &newDirAFSFid, newNamep,
3691 &updatedOldDirStatus, &updatedNewDirStatus,
3693 rx_PutConnection(rxconnp);
3695 } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
3696 &volSync, NULL, NULL, code));
3697 code = cm_MapRPCError(code, reqp);
3700 osi_Log1(afsd_logp, "CALL Rename FAILURE, code 0x%x", code);
3702 osi_Log0(afsd_logp, "CALL Rename SUCCESS");
3704 /* update the individual stat cache entries for the directories */
3706 lock_ObtainWrite(&oldDirOp.scp->dirlock);
3707 oldDirOp.lockType = CM_DIRLOCK_WRITE;
3709 lock_ObtainWrite(&oldDscp->rw);
3710 cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
3713 cm_MergeStatus(NULL, oldDscp, &updatedOldDirStatus, &volSync,
3714 userp, reqp, CM_MERGEFLAG_DIROP);
3715 lock_ReleaseWrite(&oldDscp->rw);
3717 if (code == 0 && cm_CheckDirOpForSingleChange(&oldDirOp)) {
3719 diropCode = cm_BPlusDirLookup(&oldDirOp, cOldNamep, &fileFid);
3720 if (diropCode == CM_ERROR_INEXACT_MATCH)
3722 else if (diropCode == EINVAL)
3724 diropCode = cm_DirLookup(&oldDirOp, oldNamep, &fileFid);
3726 if (diropCode == 0) {
3728 diropCode = cm_DirCreateEntry(&oldDirOp, newNamep, &fileFid);
3730 cm_BPlusDirCreateEntry(&oldDirOp, cNewNamep, &fileFid);
3734 if (diropCode == 0) {
3735 diropCode = cm_DirDeleteEntry(&oldDirOp, oldNamep);
3737 cm_BPlusDirDeleteEntry(&oldDirOp, cOldNamep);
3742 cm_EndDirOp(&oldDirOp);
3744 /* and update it for the new one, too, if necessary */
3747 lock_ObtainWrite(&newDirOp.scp->dirlock);
3748 newDirOp.lockType = CM_DIRLOCK_WRITE;
3750 lock_ObtainWrite(&newDscp->rw);
3751 cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
3753 cm_MergeStatus(NULL, newDscp, &updatedNewDirStatus, &volSync,
3754 userp, reqp, CM_MERGEFLAG_DIROP);
3755 lock_ReleaseWrite(&newDscp->rw);
3759 * The following optimization does not work.
3760 * When the file server processed a RXAFS_Rename() request the
3761 * FID of the object being moved between directories is not
3762 * preserved. The client does not know the new FID nor the
3763 * version number of the target. Not only can we not create
3764 * the directory entry in the new directory, but we can't
3765 * preserve the cached data for the file. It must be re-read
3766 * from the file server. - jaltman, 2009/02/20
3769 /* we only make the local change if we successfully made
3770 the change in the old directory AND there was only one
3771 change in the new directory */
3772 if (diropCode == 0 && cm_CheckDirOpForSingleChange(&newDirOp)) {
3773 cm_DirCreateEntry(&newDirOp, newNamep, &fileFid);
3775 cm_BPlusDirCreateEntry(&newDirOp, cNewNamep, &fileFid);
3780 cm_EndDirOp(&newDirOp);
3784 * After the rename the file server has invalidated the callbacks
3785 * on the file that was moved nor do we have a directory reference
3788 lock_ObtainWrite(&oldScp->rw);
3789 cm_DiscardSCache(oldScp);
3790 lock_ReleaseWrite(&oldScp->rw);
3794 cm_ReleaseSCache(oldScp);
3801 /* and return error code */
3805 /* Byte range locks:
3807 The OpenAFS Windows client has to fake byte range locks given no
3808 server side support for such locks. This is implemented as keyed
3809 byte range locks on the cache manager.
3811 Keyed byte range locks:
3813 Each cm_scache_t structure keeps track of a list of keyed locks.
3814 The key for a lock identifies an owner of a set of locks (referred
3815 to as a client). Each key is represented by a value. The set of
3816 key values used within a specific cm_scache_t structure form a
3817 namespace that has a scope of just that cm_scache_t structure. The
3818 same key value can be used with another cm_scache_t structure and
3819 correspond to a completely different client. However it is
3820 advantageous for the SMB or IFS layer to make sure that there is a
3821 1-1 mapping between client and keys over all cm_scache_t objects.
3823 Assume a client C has key Key(C) (although, since the scope of the
3824 key is a cm_scache_t, the key can be Key(C,S), where S is the
3825 cm_scache_t. But assume a 1-1 relation between keys and clients).
3826 A byte range (O,+L) denotes byte addresses (O) through (O+L-1)
3827 inclusive (a.k.a. [O,O+L-1]). The function Key(x) is implemented
3828 through cm_generateKey() function for both SMB and IFS.
3830 The list of locks for a cm_scache_t object S is maintained in
3831 S->fileLocks. The cache manager will set a lock on the AFS file
3832 server in order to assert the locks in S->fileLocks. If only
3833 shared locks are in place for S, then the cache manager will obtain
3834 a LockRead lock, while if there are any exclusive locks, it will
3835 obtain a LockWrite lock. If the exclusive locks are all released
3836 while the shared locks remain, then the cache manager will
3837 downgrade the lock from LockWrite to LockRead. Similarly, if an
3838 exclusive lock is obtained when only shared locks exist, then the
3839 cache manager will try to upgrade the lock from LockRead to
3842 Each lock L owned by client C maintains a key L->key such that
3843 L->key == Key(C), the effective range defined by L->LOffset and
3844 L->LLength such that the range of bytes affected by the lock is
3845 (L->LOffset, +L->LLength), a type maintained in L->LockType which
3846 is either exclusive or shared.
3850 A lock exists iff it is in S->fileLocks for some cm_scache_t
3851 S. Existing locks are in one of the following states: ACTIVE,
3852 WAITLOCK, WAITUNLOCK, LOST, DELETED.
3854 The following sections describe each lock and the associated
3857 1. ACTIVE: A lock L is ACTIVE iff the cache manager has asserted
3858 the lock with the AFS file server. This type of lock can be
3859 exercised by a client to read or write to the locked region (as
3862 1.1 ACTIVE->LOST: When the AFS file server fails to extend a
3863 server lock that was required to assert the lock. Before
3864 marking the lock as lost, the cache manager checks if the file
3865 has changed on the server. If the file has not changed, then
3866 the cache manager will attempt to obtain a new server lock
3867 that is sufficient to assert the client side locks for the
3868 file. If any of these fail, the lock is marked as LOST.
3869 Otherwise, it is left as ACTIVE.
3871 1.2 ACTIVE->DELETED: Lock is released.
3873 2. WAITLOCK: A lock is in a WAITLOCK state if the cache manager
3874 grants the lock but the lock is yet to be asserted with the AFS
3875 file server. Once the file server grants the lock, the state
3876 will transition to an ACTIVE lock.
3878 2.1 WAITLOCK->ACTIVE: The server granted the lock.
3880 2.2 WAITLOCK->DELETED: Lock is abandoned, or timed out during
3883 2.3 WAITLOCK->LOST: One or more locks from this client were
3884 marked as LOST. No further locks will be granted to this
3885 client until all lost locks are removed.
3887 3. WAITUNLOCK: A lock is in a WAITUNLOCK state if the cache manager
3888 receives a request for a lock that conflicts with an existing
3889 ACTIVE or WAITLOCK lock. The lock will be placed in the queue
3890 and will be granted at such time the conflicting locks are
3891 removed, at which point the state will transition to either
3894 3.1 WAITUNLOCK->ACTIVE: The conflicting lock was removed. The
3895 current serverLock is sufficient to assert this lock, or a
3896 sufficient serverLock is obtained.
3898 3.2 WAITUNLOCK->WAITLOCK: The conflicting lock was removed,
3899 however the required serverLock is yet to be asserted with the
3902 3.3 WAITUNLOCK->DELETED: The lock is abandoned, timed out or
3905 3.5 WAITUNLOCK->LOST: One or more locks from this client were
3906 marked as LOST. No further locks will be granted to this
3907 client until all lost locks are removed.
3909 4. LOST: A lock L is LOST if the server lock that was required to
3910 assert the lock could not be obtained or if it could not be
3911 extended, or if other locks by the same client were LOST.
3912 Essentially, once a lock is LOST, the contract between the cache
3913 manager and that specific client is no longer valid.
3915 The cache manager rechecks the server lock once every minute and
3916 extends it as appropriate. If this is not done for 5 minutes,
3917 the AFS file server will release the lock (the 5 minute timeout
3918 is based on current file server code and is fairly arbitrary).
3919 Once released, the lock cannot be re-obtained without verifying
3920 that the contents of the file hasn't been modified since the
3921 time the lock was released. Re-obtaining the lock without
3922 verifying this may lead to data corruption. If the lock can not
3923 be obtained safely, then all active locks for the cm_scache_t
3926 4.1 LOST->DELETED: The lock is released.
3928 5. DELETED: The lock is no longer relevant. Eventually, it will
3929 get removed from the cm_scache_t. In the meantime, it will be
3930 treated as if it does not exist.
3932 5.1 DELETED->not exist: The lock is removed from the
3935 The following are classifications of locks based on their state.
3937 6* A lock L is ACCEPTED if it is ACTIVE or WAITLOCK. These locks
3938 have been accepted by the cache manager, but may or may not have
3939 been granted back to the client.
3941 7* A lock L is QUEUED if it is ACTIVE, WAITLOCK or WAITUNLOCK.
3943 8* A lock L is WAITING if it is WAITLOCK or WAITUNLOCK.
3947 A client C can READ range (Offset,+Length) of a file represented by
3948 cm_scache_t S iff (1):
3950 1. for all _a_ in (Offset,+Length), all of the following is true:
3952 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3953 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3956 1.2 For each LOST lock L in S->fileLocks such that _a_ in
3957 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3960 (When locks are lost on an cm_scache_t, all locks are lost. By
3961 4.2 (below), if there is an exclusive LOST lock, then there
3962 can't be any overlapping ACTIVE locks.)
3964 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
3966 2. for all _a_ in (Offset,+Length), one of the following is true:
3968 2.1 Byte _a_ of S is unowned (as specified in 1.1) AND there
3969 does not exist a LOST lock L such that _a_ in
3970 (L->LOffset,+L->LLength).
3972 2.2 Byte _a_ of S is owned by C under lock L (as specified in
3973 1.2) AND L->LockType is exclusive.
3975 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
3977 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
3980 3.1 If L->LockType is exclusive then there does NOT exist a
3981 ACCEPTED lock M in S->fileLocks such that _a_ in
3982 (M->LOffset,+M->LLength).
3984 (If we count all QUEUED locks then we hit cases such as
3985 cascading waiting locks where the locks later on in the queue
3986 can be granted without compromising file integrity. On the
3987 other hand if only ACCEPTED locks are considered, then locks
3988 that were received earlier may end up waiting for locks that
3989 were received later to be unlocked. The choice of ACCEPTED
3990 locks was made to mimic the Windows byte range lock
3993 3.2 If L->LockType is shared then for each ACCEPTED lock M in
3994 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
3995 M->LockType is shared.
3997 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
3999 4.1 M->key != Key(C)
4001 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
4002 and (M->LOffset,+M->LLength) do not intersect.
4004 (Note: If a client loses a lock, it loses all locks.
4005 Subsequently, it will not be allowed to obtain any more locks
4006 until all existing LOST locks that belong to the client are
4007 released. Once all locks are released by a single client,
4008 there exists no further contract between the client and AFS
4009 about the contents of the file, hence the client can then
4010 proceed to obtain new locks and establish a new contract.
4012 This doesn't quite work as you think it should, because most
4013 applications aren't built to deal with losing locks they
4014 thought they once had. For now, we don't have a good
4015 solution to lost locks.
4017 Also, for consistency reasons, we have to hold off on
4018 granting locks that overlap exclusive LOST locks.)
4020 A client C can only unlock locks L in S->fileLocks which have
4023 The representation and invariants are as follows:
4025 - Each cm_scache_t structure keeps:
4027 - A queue of byte-range locks (cm_scache_t::fileLocks) which
4028 are of type cm_file_lock_t.
4030 - A record of the highest server-side lock that has been
4031 obtained for this object (cm_scache_t::serverLock), which is
4032 one of (-1), LockRead, LockWrite.
4034 - A count of ACCEPTED exclusive and shared locks that are in the
4035 queue (cm_scache_t::sharedLocks and
4036 cm_scache_t::exclusiveLocks)
4038 - Each cm_file_lock_t structure keeps:
4040 - The type of lock (cm_file_lock_t::LockType)
4042 - The key associated with the lock (cm_file_lock_t::key)
4044 - The offset and length of the lock (cm_file_lock_t::LOffset
4045 and cm_file_lock_t::LLength)
4047 - The state of the lock.
4049 - Time of issuance or last successful extension
4051 Semantic invariants:
4053 I1. The number of ACCEPTED locks in S->fileLocks are
4054 (S->sharedLocks + S->exclusiveLocks)
4056 External invariants:
4058 I3. S->serverLock is the lock that we have asserted with the
4059 AFS file server for this cm_scache_t.
4061 I4. S->serverLock == LockRead iff there is at least one ACTIVE
4062 shared lock, but no ACTIVE exclusive locks.
4064 I5. S->serverLock == LockWrite iff there is at least one ACTIVE
4067 I6. If L is a LOST lock, then for each lock M in S->fileLocks,
4068 M->key == L->key IMPLIES M is LOST or DELETED.
4073 #define IS_LOCK_ACTIVE(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == 0)
4075 #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)
4077 #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)
4079 #define IS_LOCK_LOST(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_LOST)
4081 #define IS_LOCK_DELETED(lockp) (((lockp)->flags & CM_FILELOCK_FLAG_DELETED) == CM_FILELOCK_FLAG_DELETED)
4084 #define IS_LOCK_ACCEPTED(lockp) (IS_LOCK_ACTIVE(lockp) || IS_LOCK_WAITLOCK(lockp))
4087 #define IS_LOCK_CLIENTONLY(lockp) ((((lockp)->scp->flags & CM_SCACHEFLAG_RO) == CM_SCACHEFLAG_RO) || (((lockp)->flags & CM_FILELOCK_FLAG_CLIENTONLY) == CM_FILELOCK_FLAG_CLIENTONLY))
4090 #define INTERSECT_RANGE(r1,r2) (((r2).offset+(r2).length) > (r1).offset && ((r1).offset +(r1).length) > (r2).offset)
4093 #define CONTAINS_RANGE(r1,r2) (((r2).offset+(r2).length) <= ((r1).offset+(r1).length) && (r1).offset <= (r2).offset)
4095 #if defined(VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS) && !defined(LOCK_TESTING)
4096 #define SCP_SUPPORTS_BRLOCKS(scp) ((scp)->cbServerp && ((scp)->cbServerp->capabilities & VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS))
4098 #define SCP_SUPPORTS_BRLOCKS(scp) (1)
4101 #define SERVERLOCKS_ENABLED(scp) (!((scp)->flags & CM_SCACHEFLAG_RO) && cm_enableServerLocks && SCP_SUPPORTS_BRLOCKS(scp))
4103 #if defined(VICED_CAPABILITY_WRITELOCKACL)
4104 #define SCP_SUPPORTS_WRITELOCKACL(scp) ((scp)->cbServerp && ((scp->cbServerp->capabilities & VICED_CAPABILITY_WRITELOCKACL)))
4106 #define SCP_SUPPORTS_WRITELOCKACL(scp) (0)
4108 /* This should really be defined in any build that this code is being
4110 #error VICED_CAPABILITY_WRITELOCKACL not defined.
4113 static void cm_LockRangeSubtract(cm_range_t * pos, const cm_range_t * neg)
4115 afs_int64 int_begin;
4118 int_begin = MAX(pos->offset, neg->offset);
4119 int_end = MIN(pos->offset+pos->length, neg->offset+neg->length);
4121 if (int_begin < int_end) {
4122 if (int_begin == pos->offset) {
4123 pos->length = pos->offset + pos->length - int_end;
4124 pos->offset = int_end;
4125 } else if (int_end == pos->offset + pos->length) {
4126 pos->length = int_begin - pos->offset;
4129 /* We only subtract ranges if the resulting range is
4130 contiguous. If we try to support non-contigous ranges, we
4131 aren't actually improving performance. */
4135 /* Called with scp->rw held. Returns 0 if all is clear to read the
4136 specified range by the client identified by key.
4138 long cm_LockCheckRead(cm_scache_t *scp,
4139 LARGE_INTEGER LOffset,
4140 LARGE_INTEGER LLength,
4143 #ifndef ADVISORY_LOCKS
4145 cm_file_lock_t *fileLock;
4149 int substract_ranges = FALSE;
4151 range.offset = LOffset.QuadPart;
4152 range.length = LLength.QuadPart;
4156 1. for all _a_ in (Offset,+Length), all of the following is true:
4158 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
4159 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
4162 1.2 For each LOST lock L in S->fileLocks such that _a_ in
4163 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
4168 lock_ObtainRead(&cm_scacheLock);
4170 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
4172 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4174 if (INTERSECT_RANGE(range, fileLock->range)) {
4175 if (IS_LOCK_ACTIVE(fileLock)) {
4176 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4178 /* If there is an active lock for this client, it
4179 is safe to substract ranges.*/
4180 cm_LockRangeSubtract(&range, &fileLock->range);
4181 substract_ranges = TRUE;
4183 if (fileLock->lockType != LockRead) {
4184 code = CM_ERROR_LOCK_CONFLICT;
4188 /* even if the entire range is locked for reading,
4189 we still can't grant the lock at this point
4190 because the client may have lost locks. That
4191 is, unless we have already seen an active lock
4192 belonging to the client, in which case there
4193 can't be any lost locks for this client. */
4194 if (substract_ranges)
4195 cm_LockRangeSubtract(&range, &fileLock->range);
4197 } else if (IS_LOCK_LOST(fileLock) &&
4198 (cm_KeyEquals(&fileLock->key, &key, 0) || fileLock->lockType == LockWrite)) {
4199 code = CM_ERROR_BADFD;
4205 lock_ReleaseRead(&cm_scacheLock);
4207 osi_Log4(afsd_logp, "cm_LockCheckRead scp 0x%x offset %d length %d code 0x%x",
4208 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
4219 /* Called with scp->rw held. Returns 0 if all is clear to write the
4220 specified range by the client identified by key.
4222 long cm_LockCheckWrite(cm_scache_t *scp,
4223 LARGE_INTEGER LOffset,
4224 LARGE_INTEGER LLength,
4227 #ifndef ADVISORY_LOCKS
4229 cm_file_lock_t *fileLock;
4234 range.offset = LOffset.QuadPart;
4235 range.length = LLength.QuadPart;
4238 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
4240 2. for all _a_ in (Offset,+Length), one of the following is true:
4242 2.1 Byte _a_ of S is unowned AND there does not exist a LOST
4243 lock L such that _a_ in (L->LOffset,+L->LLength).
4245 2.2 Byte _a_ of S is owned by C under lock L AND L->LockType is
4249 lock_ObtainRead(&cm_scacheLock);
4251 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
4253 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4255 if (INTERSECT_RANGE(range, fileLock->range)) {
4256 if (IS_LOCK_ACTIVE(fileLock)) {
4257 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4258 if (fileLock->lockType == LockWrite) {
4260 /* if there is an active lock for this client, it
4261 is safe to substract ranges */
4262 cm_LockRangeSubtract(&range, &fileLock->range);
4264 code = CM_ERROR_LOCK_CONFLICT;
4268 code = CM_ERROR_LOCK_CONFLICT;
4271 } else if (IS_LOCK_LOST(fileLock)) {
4272 code = CM_ERROR_BADFD;
4278 lock_ReleaseRead(&cm_scacheLock);
4280 osi_Log4(afsd_logp, "cm_LockCheckWrite scp 0x%x offset %d length %d code 0x%x",
4281 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
4292 /* Called with cm_scacheLock write locked */
4293 static cm_file_lock_t * cm_GetFileLock(void) {
4296 l = (cm_file_lock_t *) cm_freeFileLocks;
4298 osi_QRemove(&cm_freeFileLocks, &l->q);
4300 l = malloc(sizeof(cm_file_lock_t));
4301 osi_assertx(l, "null cm_file_lock_t");
4304 memset(l, 0, sizeof(cm_file_lock_t));
4309 /* Called with cm_scacheLock write locked */
4310 static void cm_PutFileLock(cm_file_lock_t *l) {
4311 osi_QAdd(&cm_freeFileLocks, &l->q);
4314 /* called with scp->rw held. May release it during processing, but
4315 leaves it held on exit. */
4316 long cm_IntSetLock(cm_scache_t * scp, cm_user_t * userp, int lockType,
4322 struct rx_connection * rxconnp;
4324 afs_uint32 reqflags = reqp->flags;
4326 memset(&volSync, 0, sizeof(volSync));
4328 tfid.Volume = scp->fid.volume;
4329 tfid.Vnode = scp->fid.vnode;
4330 tfid.Unique = scp->fid.unique;
4333 osi_Log2(afsd_logp, "CALL SetLock scp 0x%p for lock %d", scp, lockType);
4335 reqp->flags |= CM_REQ_NORETRY;
4336 lock_ReleaseWrite(&scp->rw);
4339 code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4343 rxconnp = cm_GetRxConn(connp);
4344 code = RXAFS_SetLock(rxconnp, &tfid, lockType,
4346 rx_PutConnection(rxconnp);
4348 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
4351 code = cm_MapRPCError(code, reqp);
4353 osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
4355 osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
4358 lock_ObtainWrite(&scp->rw);
4359 reqp->flags = reqflags;
4363 /* called with scp->rw held. Releases it during processing */
4364 long cm_IntReleaseLock(cm_scache_t * scp, cm_user_t * userp,
4370 struct rx_connection * rxconnp;
4373 memset(&volSync, 0, sizeof(volSync));
4375 tfid.Volume = scp->fid.volume;
4376 tfid.Vnode = scp->fid.vnode;
4377 tfid.Unique = scp->fid.unique;
4380 lock_ReleaseWrite(&scp->rw);
4382 osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
4385 code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4389 rxconnp = cm_GetRxConn(connp);
4390 code = RXAFS_ReleaseLock(rxconnp, &tfid, &volSync);
4391 rx_PutConnection(rxconnp);
4393 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
4395 code = cm_MapRPCError(code, reqp);
4398 "CALL ReleaseLock FAILURE, code 0x%x", code);
4401 "CALL ReleaseLock SUCCESS");
4403 lock_ObtainWrite(&scp->rw);
4408 /* called with scp->rw held. May release it during processing, but
4409 will exit with lock held.
4413 - 0 if the user has permission to get the specified lock for the scp
4415 - CM_ERROR_NOACCESS if not
4417 Any other error from cm_SyncOp will be sent down untranslated.
4419 If CM_ERROR_NOACCESS is returned and lock_type is LockRead, then
4420 phas_insert (if non-NULL) will receive a boolean value indicating
4421 whether the user has INSERT permission or not.
4423 long cm_LockCheckPerms(cm_scache_t * scp,
4430 long code = 0, code2 = 0;
4432 /* lock permissions are slightly tricky because of the 'i' bit.
4433 If the user has PRSFS_LOCK, she can read-lock the file. If the
4434 user has PRSFS_WRITE, she can write-lock the file. However, if
4435 the user has PRSFS_INSERT, then she can write-lock new files,
4436 but not old ones. Since we don't have information about
4437 whether a file is new or not, we assume that if the user owns
4438 the scp, then she has the permissions that are granted by
4441 osi_Log3(afsd_logp, "cm_LockCheckPerms for scp[0x%p] type[%d] user[0x%p]",
4442 scp, lock_type, userp);
4444 if (lock_type == LockRead)
4445 rights |= PRSFS_LOCK;
4446 else if (lock_type == LockWrite)
4447 rights |= PRSFS_WRITE | PRSFS_LOCK;
4450 osi_assertx(FALSE, "invalid lock type");
4455 *phas_insert = FALSE;
4457 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
4458 CM_SCACHESYNC_GETSTATUS |
4459 CM_SCACHESYNC_NEEDCALLBACK);
4461 if (phas_insert && scp->creator == userp) {
4463 /* If this file was created by the user, then we check for
4464 PRSFS_INSERT. If the file server is recent enough, then
4465 this should be sufficient for her to get a write-lock (but
4466 not necessarily a read-lock). VICED_CAPABILITY_WRITELOCKACL
4467 indicates whether a file server supports getting write
4468 locks when the user only has PRSFS_INSERT.
4470 If the file was not created by the user we skip the check
4471 because the INSERT bit will not apply to this user even
4475 code2 = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
4476 CM_SCACHESYNC_GETSTATUS |
4477 CM_SCACHESYNC_NEEDCALLBACK);
4479 if (code2 == CM_ERROR_NOACCESS) {
4480 osi_Log0(afsd_logp, "cm_LockCheckPerms user has no INSERT bits");
4482 *phas_insert = TRUE;
4483 osi_Log0(afsd_logp, "cm_LockCheckPerms user has INSERT bits");
4487 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
4489 osi_Log1(afsd_logp, "cm_LockCheckPerms returning code %d", code);
4494 /* called with scp->rw held */
4495 long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
4496 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
4498 int allowWait, cm_user_t *userp, cm_req_t *reqp,
4499 cm_file_lock_t **lockpp)
4502 int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
4503 cm_file_lock_t *fileLock;
4506 int wait_unlock = FALSE;
4507 int force_client_lock = FALSE;
4509 osi_Log4(afsd_logp, "cm_Lock scp 0x%x type 0x%x offset %d length %d",
4510 scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
4511 osi_Log4(afsd_logp, "... allowWait %d key <0x%x, 0x%x, 0x%x>", allowWait,
4512 key.process_id, key.session_id, key.file_id);
4515 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
4517 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
4520 3.1 If L->LockType is exclusive then there does NOT exist a
4521 ACCEPTED lock M in S->fileLocks such that _a_ in
4522 (M->LOffset,+M->LLength).
4524 3.2 If L->LockType is shared then for each ACCEPTED lock M in
4525 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
4526 M->LockType is shared.
4528 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
4530 4.1 M->key != Key(C)
4532 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
4533 and (M->LOffset,+M->LLength) do not intersect.
4536 range.offset = LOffset.QuadPart;
4537 range.length = LLength.QuadPart;
4539 lock_ObtainRead(&cm_scacheLock);
4541 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4543 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4545 if (IS_LOCK_LOST(fileLock)) {
4546 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4547 code = CM_ERROR_BADFD;
4549 } else if (fileLock->lockType == LockWrite && INTERSECT_RANGE(range, fileLock->range)) {
4550 code = CM_ERROR_WOULDBLOCK;
4556 /* we don't need to check for deleted locks here since deleted
4557 locks are dequeued from scp->fileLocks */
4558 if (IS_LOCK_ACCEPTED(fileLock) &&
4559 INTERSECT_RANGE(range, fileLock->range)) {
4561 if ((sLockType & LOCKING_ANDX_SHARED_LOCK) == 0 ||
4562 fileLock->lockType != LockRead) {
4564 code = CM_ERROR_WOULDBLOCK;
4570 lock_ReleaseRead(&cm_scacheLock);
4572 if (code == 0 && SERVERLOCKS_ENABLED(scp)) {
4573 if (Which == scp->serverLock ||
4574 (Which == LockRead && scp->serverLock == LockWrite)) {
4578 /* we already have the lock we need */
4579 osi_Log3(afsd_logp, " we already have the correct lock. exclusives[%d], shared[%d], serverLock[%d]",
4580 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4582 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4584 /* special case: if we don't have permission to read-lock
4585 the file, then we force a clientside lock. This is to
4586 compensate for applications that obtain a read-lock for
4587 reading files off of directories that don't grant
4588 read-locks to the user. */
4589 if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4591 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4592 osi_Log0(afsd_logp, " User has no read-lock perms, but has INSERT perms.");
4595 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4596 force_client_lock = TRUE;
4600 } else if ((scp->exclusiveLocks > 0) ||
4601 (scp->sharedLocks > 0 && scp->serverLock != LockRead)) {
4604 /* We are already waiting for some other lock. We should
4605 wait for the daemon to catch up instead of generating a
4606 flood of SetLock calls. */
4607 osi_Log3(afsd_logp, " already waiting for other lock. exclusives[%d], shared[%d], serverLock[%d]",
4608 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4610 /* see if we have permission to create the lock in the
4612 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4614 code = CM_ERROR_WOULDBLOCK;
4615 else if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4617 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4619 " User has no read-lock perms, but has INSERT perms.");
4620 code = CM_ERROR_WOULDBLOCK;
4623 " User has no read-lock perms. Forcing client-side lock");
4624 force_client_lock = TRUE;
4628 /* leave any other codes as-is */
4632 int check_data_version = FALSE;
4635 /* first check if we have permission to elevate or obtain
4637 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4639 if (code == CM_ERROR_NOACCESS && Which == LockRead &&
4640 (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp))) {
4641 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4642 force_client_lock = TRUE;
4647 /* has_insert => (Which == LockRead, code == CM_ERROR_NOACCESS) */
4649 if (scp->serverLock == LockRead && Which == LockWrite) {
4651 /* We want to escalate the lock to a LockWrite.
4652 * Unfortunately that's not really possible without
4653 * letting go of the current lock. But for now we do
4657 " attempting to UPGRADE from LockRead to LockWrite.");
4659 " dataVersion on scp: %I64d", scp->dataVersion);
4661 /* we assume at this point (because scp->serverLock
4662 was valid) that we had a valid server lock. */
4663 scp->lockDataVersion = scp->dataVersion;
4664 check_data_version = TRUE;
4666 code = cm_IntReleaseLock(scp, userp, reqp);
4669 /* We couldn't release the lock */
4672 scp->serverLock = -1;
4676 /* We need to obtain a server lock of type Which in order
4677 * to assert this file lock */
4678 #ifndef AGGRESSIVE_LOCKS
4681 newLock = LockWrite;
4684 code = cm_IntSetLock(scp, userp, newLock, reqp);
4686 #ifdef AGGRESSIVE_LOCKS
4687 if ((code == CM_ERROR_WOULDBLOCK ||
4688 code == CM_ERROR_NOACCESS) && newLock != Which) {
4689 /* we wanted LockRead. We tried LockWrite. Now try
4694 osi_assertx(newLock == LockRead, "lock type not read");
4696 code = cm_IntSetLock(scp, userp, newLock, reqp);
4700 if (code == CM_ERROR_NOACCESS) {
4701 if (Which == LockRead) {
4702 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4704 /* We requested a read-lock, but we have permission to
4705 * get a write-lock. Try that */
4707 tcode = cm_LockCheckPerms(scp, LockWrite, userp, reqp, NULL);
4710 newLock = LockWrite;
4712 osi_Log0(afsd_logp, " User has 'i' perms and the request was for a LockRead. Trying to get a LockWrite instead");
4714 code = cm_IntSetLock(scp, userp, newLock, reqp);
4717 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4718 force_client_lock = TRUE;
4720 } else if (Which == LockWrite &&
4721 scp->creator == userp && !SCP_SUPPORTS_WRITELOCKACL(scp)) {
4724 /* Special case: if the lock request was for a
4725 * LockWrite and the user owns the file and we weren't
4726 * allowed to obtain the serverlock, we either lost a
4727 * race (the permissions changed from under us), or we
4728 * have 'i' bits, but we aren't allowed to lock the
4731 /* check if we lost a race... */
4732 tcode = cm_LockCheckPerms(scp, Which, userp, reqp, NULL);
4735 osi_Log0(afsd_logp, " User has 'i' perms but can't obtain write locks. Using client-side locks.");
4736 force_client_lock = TRUE;
4741 if (code == 0 && check_data_version &&
4742 scp->dataVersion != scp->lockDataVersion) {
4743 /* We lost a race. Although we successfully obtained
4744 * a lock, someone modified the file in between. The
4745 * locks have all been technically lost. */
4748 " Data version mismatch while upgrading lock.");
4750 " Data versions before=%I64d, after=%I64d",
4751 scp->lockDataVersion,
4754 " Releasing stale lock for scp 0x%x", scp);
4756 code = cm_IntReleaseLock(scp, userp, reqp);
4758 scp->serverLock = -1;
4760 code = CM_ERROR_INVAL;
4761 } else if (code == 0) {
4762 scp->serverLock = newLock;
4763 scp->lockDataVersion = scp->dataVersion;
4767 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4768 scp->serverLock == -1) {
4769 /* Oops. We lost the lock. */
4770 cm_LockMarkSCacheLost(scp);
4773 } else if (code == 0) { /* server locks not enabled */
4775 " Skipping server lock for scp");
4780 if (code != 0 && !force_client_lock) {
4781 /* Special case error translations
4783 Applications don't expect certain errors from a
4784 LockFile/UnlockFile call. We need to translate some error
4785 code to codes that apps expect and handle. */
4787 /* We shouldn't actually need to handle this case since we
4788 simulate locks for RO scps anyway. */
4789 if (code == CM_ERROR_READONLY) {
4790 osi_Log0(afsd_logp, " Reinterpreting CM_ERROR_READONLY as CM_ERROR_NOACCESS");
4791 code = CM_ERROR_NOACCESS;
4795 if (code == 0 || (code == CM_ERROR_WOULDBLOCK && allowWait) ||
4796 force_client_lock) {
4798 /* clear the error if we are forcing a client lock, so we
4799 don't get confused later. */
4800 if (force_client_lock && code != CM_ERROR_WOULDBLOCK)
4803 lock_ObtainWrite(&cm_scacheLock);
4804 fileLock = cm_GetFileLock();
4805 lock_ReleaseWrite(&cm_scacheLock);
4807 fileLock->fid = scp->fid;
4809 fileLock->key = key;
4810 fileLock->lockType = Which;
4812 fileLock->userp = userp;
4813 fileLock->range = range;
4814 fileLock->flags = (code == 0 ? 0 :
4816 CM_FILELOCK_FLAG_WAITUNLOCK :
4817 CM_FILELOCK_FLAG_WAITLOCK));
4819 if (force_client_lock || !SERVERLOCKS_ENABLED(scp))
4820 fileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
4822 fileLock->lastUpdate = (code == 0 && !force_client_lock) ? time(NULL) : 0;
4824 lock_ObtainWrite(&cm_scacheLock);
4825 osi_QAddT(&scp->fileLocksH, &scp->fileLocksT, &fileLock->fileq);
4826 cm_HoldSCacheNoLock(scp);
4827 fileLock->scp = scp;
4828 osi_QAdd(&cm_allFileLocks, &fileLock->q);
4829 lock_ReleaseWrite(&cm_scacheLock);
4835 if (IS_LOCK_CLIENTONLY(fileLock)) {
4837 } else if (IS_LOCK_ACCEPTED(fileLock)) {
4838 if (Which == LockRead)
4841 scp->exclusiveLocks++;
4845 "cm_Lock Lock added 0x%p flags 0x%x to scp [0x%p]",
4846 fileLock, fileLock->flags, scp);
4848 " exclusives[%d] shared[%d] client[%d] serverLock[%d]",
4849 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4850 (int)(signed char) scp->serverLock);
4853 "cm_Lock Rejecting lock (code = 0x%x)", code);
4856 /* Convert from would block to lock not granted */
4857 if (code == CM_ERROR_WOULDBLOCK)
4858 code = CM_ERROR_LOCK_NOT_GRANTED;
4863 /* Called with scp->rw held */
4864 long cm_UnlockByKey(cm_scache_t * scp,
4871 cm_file_lock_t *fileLock;
4872 osi_queue_t *q, *qn;
4875 osi_Log4(afsd_logp, "cm_UnlockByKey scp 0x%p key <0x%x,0x%x,0x%x",
4876 scp, key.process_id, key.session_id, key.file_id);
4877 osi_Log1(afsd_logp, " flags=0x%x", flags);
4879 lock_ObtainWrite(&cm_scacheLock);
4881 for (q = scp->fileLocksH; q; q = qn) {
4884 fileLock = (cm_file_lock_t *)
4885 ((char *) q - offsetof(cm_file_lock_t, fileq));
4888 osi_Log4(afsd_logp, " Checking lock[0x%x] range[%d,+%d] type[%d]",
4890 (unsigned long) fileLock->range.offset,
4891 (unsigned long) fileLock->range.length,
4892 fileLock->lockType);
4893 osi_Log4(afsd_logp, " key<0x%x, 0x%x, 0x%x> flags[0x%x]",
4894 fileLock->key.process_id, fileLock->key.session_id, fileLock->key.file_id,
4897 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
4898 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
4899 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4901 fileLock->fid.volume,
4902 fileLock->fid.vnode,
4903 fileLock->fid.unique);
4904 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4905 fileLock->scp->fid.cell,
4906 fileLock->scp->fid.volume,
4907 fileLock->scp->fid.vnode,
4908 fileLock->scp->fid.unique);
4909 osi_assertx(FALSE, "invalid fid value");
4913 if (!IS_LOCK_DELETED(fileLock) &&
4914 cm_KeyEquals(&fileLock->key, &key, flags)) {
4915 osi_Log3(afsd_logp, "...Unlock range [%d,+%d] type %d",
4916 fileLock->range.offset,
4917 fileLock->range.length,
4918 fileLock->lockType);
4920 if (scp->fileLocksT == q)
4921 scp->fileLocksT = osi_QPrev(q);
4922 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
4924 if (IS_LOCK_CLIENTONLY(fileLock)) {
4926 } else if (IS_LOCK_ACCEPTED(fileLock)) {
4927 if (fileLock->lockType == LockRead)
4930 scp->exclusiveLocks--;
4933 fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
4935 cm_ReleaseUser(fileLock->userp);
4936 cm_ReleaseSCacheNoLock(scp);
4938 fileLock->userp = NULL;
4939 fileLock->scp = NULL;
4945 lock_ReleaseWrite(&cm_scacheLock);
4947 if (n_unlocks == 0) {
4948 osi_Log0(afsd_logp, "cm_UnlockByKey no locks found");
4949 osi_Log3(afsd_logp, " Leaving scp with exclusives[%d], shared[%d], serverLock[%d]",
4950 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4955 osi_Log1(afsd_logp, "cm_UnlockByKey done with %d locks", n_unlocks);
4957 osi_assertx(scp->sharedLocks >= 0, "scp->sharedLocks < 0");
4958 osi_assertx(scp->exclusiveLocks >= 0, "scp->exclusiveLocks < 0");
4959 osi_assertx(scp->clientLocks >= 0, "scp->clientLocks < 0");
4961 if (!SERVERLOCKS_ENABLED(scp)) {
4962 osi_Log0(afsd_logp, " Skipping server lock for scp");
4966 /* Ideally we would go through the rest of the locks to determine
4967 * if one or more locks that were formerly in WAITUNLOCK can now
4968 * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
4969 * scp->sharedLocks accordingly. However, the retrying of locks
4970 * in that manner is done cm_RetryLock() manually.
4973 if (scp->serverLock == LockWrite &&
4974 scp->exclusiveLocks == 0 &&
4975 scp->sharedLocks > 0) {
4976 /* The serverLock should be downgraded to LockRead */
4977 osi_Log0(afsd_logp, " DOWNGRADE lock from LockWrite to LockRead");
4979 /* Make sure there are no dirty buffers left. */
4980 code = cm_FSync(scp, userp, reqp, TRUE);
4982 /* since scp->serverLock looked sane, we are going to assume
4983 that we have a valid server lock. */
4984 scp->lockDataVersion = scp->dataVersion;
4985 osi_Log1(afsd_logp, " dataVersion on scp = %I64d", scp->dataVersion);
4987 code = cm_IntReleaseLock(scp, userp, reqp);
4990 /* so we couldn't release it. Just let the lock be for now */
4994 scp->serverLock = -1;
4997 code = cm_IntSetLock(scp, userp, LockRead, reqp);
4999 if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
5000 scp->serverLock = LockRead;
5001 } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
5002 /* We lost a race condition. Although we have a valid
5003 lock on the file, the data has changed and essentially
5004 we have lost the lock we had during the transition. */
5006 osi_Log0(afsd_logp, "Data version mismatch during lock downgrade");
5007 osi_Log2(afsd_logp, " Data versions before=%I64d, after=%I64d",
5008 scp->lockDataVersion,
5011 code = cm_IntReleaseLock(scp, userp, reqp);
5013 code = CM_ERROR_INVAL;
5014 scp->serverLock = -1;
5018 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
5019 (scp->serverLock == -1)) {
5021 cm_LockMarkSCacheLost(scp);
5024 /* failure here has no bearing on the return value of
5028 } else if (scp->serverLock != (-1) &&
5029 scp->exclusiveLocks == 0 &&
5030 scp->sharedLocks == 0) {
5031 /* The serverLock should be released entirely */
5033 if (scp->serverLock == LockWrite) {
5034 /* Make sure there are no dirty buffers left. */
5035 code = cm_FSync(scp, userp, reqp, TRUE);
5038 code = cm_IntReleaseLock(scp, userp, reqp);
5041 scp->serverLock = (-1);
5046 osi_Log1(afsd_logp, "cm_UnlockByKey code 0x%x", code);
5047 osi_Log4(afsd_logp, " Leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
5048 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
5049 (int)(signed char) scp->serverLock);
5054 long cm_Unlock(cm_scache_t *scp,
5055 unsigned char sLockType,
5056 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
5063 int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
5064 cm_file_lock_t *fileLock;
5066 int release_userp = FALSE;
5067 int exact_match = !(flags & CM_UNLOCK_FLAG_MATCH_RANGE);
5069 LARGE_INTEGER RangeEnd;
5071 osi_Log4(afsd_logp, "cm_Unlock scp 0x%p type 0x%x offset %d length %d",
5072 scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
5073 osi_Log4(afsd_logp, "... key <0x%x,0x%x,0x%x> flags 0x%x",
5074 key.process_id, key.session_id, key.file_id, flags);
5077 RangeEnd.QuadPart = LOffset.QuadPart + LLength.QuadPart;
5080 lock_ObtainRead(&cm_scacheLock);
5082 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5083 fileLock = (cm_file_lock_t *)
5084 ((char *) q - offsetof(cm_file_lock_t, fileq));
5087 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5088 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5089 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5091 fileLock->fid.volume,
5092 fileLock->fid.vnode,
5093 fileLock->fid.unique);
5094 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5095 fileLock->scp->fid.cell,
5096 fileLock->scp->fid.volume,
5097 fileLock->scp->fid.vnode,
5098 fileLock->scp->fid.unique);
5099 osi_assertx(FALSE, "invalid fid value");
5103 if (!IS_LOCK_DELETED(fileLock) &&
5104 cm_KeyEquals(&fileLock->key, &key, 0) &&
5105 fileLock->range.offset == LOffset.QuadPart &&
5106 fileLock->range.length == LLength.QuadPart) {
5112 if (!IS_LOCK_DELETED(fileLock) &&
5113 cm_KeyEquals(&fileLock->key, &key, 0) &&
5114 fileLock->range.offset >= LOffset.QuadPart &&
5115 fileLock->range.offset < RangeEnd.QuadPart &&
5116 (fileLock->range.offset + fileLock->range.length) <= RangeEnd.QuadPart) {
5124 lock_ReleaseRead(&cm_scacheLock);
5126 if (lock_found && !exact_match) {
5130 osi_Log0(afsd_logp, "cm_Unlock lock not found; failure");
5132 /* The lock didn't exist anyway. *shrug* */
5133 return CM_ERROR_RANGE_NOT_LOCKED;
5137 /* discard lock record */
5138 lock_ConvertRToW(&cm_scacheLock);
5139 if (scp->fileLocksT == q)
5140 scp->fileLocksT = osi_QPrev(q);
5141 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
5144 * Don't delete it here; let the daemon delete it, to simplify
5145 * the daemon's traversal of the list.
5148 if (IS_LOCK_CLIENTONLY(fileLock)) {
5150 } else if (IS_LOCK_ACCEPTED(fileLock)) {
5151 if (fileLock->lockType == LockRead)
5154 scp->exclusiveLocks--;
5157 fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5158 if (userp != NULL) {
5159 cm_ReleaseUser(fileLock->userp);
5161 userp = fileLock->userp;
5162 release_userp = TRUE;
5164 fileLock->userp = NULL;
5165 cm_ReleaseSCacheNoLock(scp);
5166 fileLock->scp = NULL;
5167 lock_ReleaseWrite(&cm_scacheLock);
5169 if (!SERVERLOCKS_ENABLED(scp)) {
5170 osi_Log0(afsd_logp, " Skipping server locks for scp");
5174 /* Ideally we would go through the rest of the locks to determine
5175 * if one or more locks that were formerly in WAITUNLOCK can now
5176 * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
5177 * scp->sharedLocks accordingly. However, the retrying of locks
5178 * in that manner is done cm_RetryLock() manually.
5181 if (scp->serverLock == LockWrite &&
5182 scp->exclusiveLocks == 0 &&
5183 scp->sharedLocks > 0) {
5185 /* The serverLock should be downgraded to LockRead */
5186 osi_Log0(afsd_logp, " DOWNGRADE lock from LockWrite to LockRead");
5188 /* Make sure there are no dirty buffers left. */
5189 code = cm_FSync(scp, userp, reqp, TRUE);
5191 /* Since we already had a lock, we assume that there is a
5192 valid server lock. */
5193 scp->lockDataVersion = scp->dataVersion;
5194 osi_Log1(afsd_logp, " dataVersion on scp is %I64d", scp->dataVersion);
5196 /* before we downgrade, make sure that we have enough
5197 permissions to get the read lock. */
5198 code = cm_LockCheckPerms(scp, LockRead, userp, reqp, NULL);
5201 osi_Log0(afsd_logp, " SKIPPING downgrade because user doesn't have perms to get downgraded lock");
5207 code = cm_IntReleaseLock(scp, userp, reqp);
5210 /* so we couldn't release it. Just let the lock be for now */
5214 scp->serverLock = -1;
5217 code = cm_IntSetLock(scp, userp, LockRead, reqp);
5219 if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
5220 scp->serverLock = LockRead;
5221 } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
5222 /* Lost a race. We obtained a new lock, but that is
5223 meaningless since someone modified the file
5227 "Data version mismatch while downgrading lock");
5229 " Data versions before=%I64d, after=%I64d",
5230 scp->lockDataVersion,
5233 code = cm_IntReleaseLock(scp, userp, reqp);
5235 scp->serverLock = -1;
5236 code = CM_ERROR_INVAL;
5240 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
5241 (scp->serverLock == -1)) {
5243 cm_LockMarkSCacheLost(scp);
5246 /* failure here has no bearing on the return value of
5250 } else if (scp->serverLock != (-1) &&
5251 scp->exclusiveLocks == 0 &&
5252 scp->sharedLocks == 0) {
5253 /* The serverLock should be released entirely */
5255 if (scp->serverLock == LockWrite) {
5256 /* Make sure there are no dirty buffers left. */
5257 code = cm_FSync(scp, userp, reqp, TRUE);
5260 code = cm_IntReleaseLock(scp, userp, reqp);
5263 scp->serverLock = (-1);
5267 if (release_userp) {
5268 cm_ReleaseUser(userp);
5269 release_userp = FALSE;
5273 goto try_again; /* might be more than one lock in the range */
5277 osi_Log1(afsd_logp, "cm_Unlock code 0x%x", code);
5278 osi_Log4(afsd_logp, " leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
5279 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
5280 (int)(signed char) scp->serverLock);
5285 /* called with scp->rw held */
5286 void cm_LockMarkSCacheLost(cm_scache_t * scp)
5288 cm_file_lock_t *fileLock;
5291 osi_Log1(afsd_logp, "cm_LockMarkSCacheLost scp 0x%x", scp);
5293 /* cm_scacheLock needed because we are modifying fileLock->flags */
5294 lock_ObtainWrite(&cm_scacheLock);
5296 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5298 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
5300 if (IS_LOCK_ACTIVE(fileLock) &&
5301 !IS_LOCK_CLIENTONLY(fileLock)) {
5302 if (fileLock->lockType == LockRead)
5305 scp->exclusiveLocks--;
5307 fileLock->flags |= CM_FILELOCK_FLAG_LOST;
5311 scp->serverLock = -1;
5312 scp->lockDataVersion = CM_SCACHE_VERSION_BAD;
5313 lock_ReleaseWrite(&cm_scacheLock);
5316 /* Called with no relevant locks held */
5317 void cm_CheckLocks()
5319 osi_queue_t *q, *nq;
5320 cm_file_lock_t *fileLock;
5326 struct rx_connection * rxconnp;
5329 memset(&volSync, 0, sizeof(volSync));
5333 lock_ObtainWrite(&cm_scacheLock);
5335 cm_lockRefreshCycle++;
5337 osi_Log1(afsd_logp, "cm_CheckLocks starting lock check cycle %d", cm_lockRefreshCycle);
5339 for (q = cm_allFileLocks; q; q = nq) {
5340 fileLock = (cm_file_lock_t *) q;
5344 if (IS_LOCK_DELETED(fileLock)) {
5346 osi_QRemove(&cm_allFileLocks, q);
5347 cm_PutFileLock(fileLock);
5349 } else if (IS_LOCK_ACTIVE(fileLock) && !IS_LOCK_CLIENTONLY(fileLock)) {
5351 /* Server locks must have been enabled for us to have
5352 received an active non-client-only lock. */
5353 osi_assertx(cm_enableServerLocks, "!cm_enableServerLocks");
5355 scp = fileLock->scp;
5356 osi_assertx(scp != NULL, "null cm_scache_t");
5358 cm_HoldSCacheNoLock(scp);
5361 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5362 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5363 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5365 fileLock->fid.volume,
5366 fileLock->fid.vnode,
5367 fileLock->fid.unique);
5368 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5369 fileLock->scp->fid.cell,
5370 fileLock->scp->fid.volume,
5371 fileLock->scp->fid.vnode,
5372 fileLock->scp->fid.unique);
5373 osi_assertx(FALSE, "invalid fid");
5376 /* Server locks are extended once per scp per refresh
5378 if (scp->lastRefreshCycle != cm_lockRefreshCycle) {
5380 int scp_done = FALSE;
5382 osi_Log1(afsd_logp, "cm_CheckLocks Updating scp 0x%x", scp);
5384 lock_ReleaseWrite(&cm_scacheLock);
5385 lock_ObtainWrite(&scp->rw);
5387 /* did the lock change while we weren't holding the lock? */
5388 if (!IS_LOCK_ACTIVE(fileLock))
5389 goto post_syncopdone;
5391 code = cm_SyncOp(scp, NULL, fileLock->userp, &req, 0,
5392 CM_SCACHESYNC_NEEDCALLBACK
5393 | CM_SCACHESYNC_GETSTATUS
5394 | CM_SCACHESYNC_LOCK);
5398 "cm_CheckLocks SyncOp failure code 0x%x", code);
5399 goto post_syncopdone;
5402 /* cm_SyncOp releases scp->rw during which the lock
5403 may get released. */
5404 if (!IS_LOCK_ACTIVE(fileLock))
5405 goto pre_syncopdone;
5407 if (scp->serverLock != -1 && !(scp->flags & CM_SCACHEFLAG_DELETED)) {
5411 tfid.Volume = scp->fid.volume;
5412 tfid.Vnode = scp->fid.vnode;
5413 tfid.Unique = scp->fid.unique;
5415 userp = fileLock->userp;
5417 osi_Log3(afsd_logp, "CALL ExtendLock lock 0x%p for scp=0x%p with lock %d",
5420 (int) scp->serverLock);
5422 lock_ReleaseWrite(&scp->rw);
5425 code = cm_ConnFromFID(&cfid, userp,
5430 rxconnp = cm_GetRxConn(connp);
5431 code = RXAFS_ExtendLock(rxconnp, &tfid,
5433 rx_PutConnection(rxconnp);
5435 osi_Log1(afsd_logp, " ExtendLock returns %d", code);
5437 } while (cm_Analyze(connp, userp, &req,
5438 &cfid, &volSync, NULL, NULL,
5441 code = cm_MapRPCError(code, &req);
5443 lock_ObtainWrite(&scp->rw);
5446 osi_Log1(afsd_logp, "CALL ExtendLock FAILURE, code 0x%x", code);
5448 osi_Log0(afsd_logp, "CALL ExtendLock SUCCESS");
5449 scp->lockDataVersion = scp->dataVersion;
5452 if ((code == EINVAL || code == CM_ERROR_INVAL) &&
5453 scp->lockDataVersion == scp->dataVersion) {
5457 (scp->exclusiveLocks > 0) ? LockWrite: LockRead;
5459 /* we might still have a chance to obtain a
5462 code = cm_IntSetLock(scp, userp, lockType, &req);
5465 code = CM_ERROR_INVAL;
5466 } else if (scp->lockDataVersion != scp->dataVersion) {
5468 /* now check if we still have the file at
5469 the right data version. */
5471 "Data version mismatch on scp 0x%p",
5474 " Data versions: before=%I64d, after=%I64d",
5475 scp->lockDataVersion,
5478 code = cm_IntReleaseLock(scp, userp, &req);
5480 code = CM_ERROR_INVAL;
5484 if (code == EINVAL || code == CM_ERROR_INVAL ||
5485 code == CM_ERROR_BADFD) {
5486 cm_LockMarkSCacheLost(scp);
5490 /* interestingly, we have found an active lock
5491 belonging to an scache that has no
5493 cm_LockMarkSCacheLost(scp);
5500 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5503 lock_ReleaseWrite(&scp->rw);
5505 lock_ObtainWrite(&cm_scacheLock);
5508 fileLock->lastUpdate = time(NULL);
5512 scp->lastRefreshCycle = cm_lockRefreshCycle;
5515 /* we have already refreshed the locks on this scp */
5516 fileLock->lastUpdate = time(NULL);
5519 cm_ReleaseSCacheNoLock(scp);
5521 } else if (IS_LOCK_ACTIVE(fileLock) && IS_LOCK_CLIENTONLY(fileLock)) {
5522 /* TODO: Check callbacks */
5526 lock_ReleaseWrite(&cm_scacheLock);
5527 osi_Log1(afsd_logp, "cm_CheckLocks completes lock check cycle %d", cm_lockRefreshCycle);
5530 /* NOT called with scp->rw held. */
5531 long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
5534 cm_scache_t *scp = NULL;
5535 cm_file_lock_t *fileLock;
5539 int force_client_lock = FALSE;
5540 int has_insert = FALSE;
5541 int check_data_version = FALSE;
5545 if (client_is_dead) {
5546 code = CM_ERROR_TIMEDOUT;
5550 lock_ObtainRead(&cm_scacheLock);
5552 osi_Log2(afsd_logp, "cm_RetryLock checking lock %p (scp=%p)", oldFileLock, oldFileLock->scp);
5553 osi_Log4(afsd_logp, " offset(%x:%x) length(%x:%x)",
5554 (unsigned)(oldFileLock->range.offset >> 32),
5555 (unsigned)(oldFileLock->range.offset & 0xffffffff),
5556 (unsigned)(oldFileLock->range.length >> 32),
5557 (unsigned)(oldFileLock->range.length & 0xffffffff));
5558 osi_Log4(afsd_logp, " key<0x%x,0x%x,0x%x> flags=%x",
5559 oldFileLock->key.process_id, oldFileLock->key.session_id, oldFileLock->key.file_id,
5560 (unsigned)(oldFileLock->flags));
5562 /* if the lock has already been granted, then we have nothing to do */
5563 if (IS_LOCK_ACTIVE(oldFileLock)) {
5564 lock_ReleaseRead(&cm_scacheLock);
5565 osi_Log0(afsd_logp, "cm_RetryLock lock already granted");
5569 /* we can't do anything with lost or deleted locks at the moment. */
5570 if (IS_LOCK_LOST(oldFileLock) || IS_LOCK_DELETED(oldFileLock)) {
5571 code = CM_ERROR_BADFD;
5572 osi_Log0(afsd_logp, "cm_RetryLock lock is lost or deleted");
5573 lock_ReleaseRead(&cm_scacheLock);
5577 scp = oldFileLock->scp;
5579 osi_assertx(scp != NULL, "null cm_scache_t");
5581 lock_ReleaseRead(&cm_scacheLock);
5582 lock_ObtainWrite(&scp->rw);
5584 code = cm_LockCheckPerms(scp, oldFileLock->lockType,
5588 if (code == CM_ERROR_NOACCESS && oldFileLock->lockType == LockRead) {
5589 if (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp)) {
5590 force_client_lock = TRUE;
5594 lock_ReleaseWrite(&scp->rw);
5598 lock_ObtainWrite(&cm_scacheLock);
5600 /* Check if we already have a sufficient server lock to allow this
5601 lock to go through. */
5602 if (IS_LOCK_WAITLOCK(oldFileLock) &&
5603 (!SERVERLOCKS_ENABLED(scp) ||
5604 scp->serverLock == oldFileLock->lockType ||
5605 scp->serverLock == LockWrite)) {
5607 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5609 if (SERVERLOCKS_ENABLED(scp)) {
5610 osi_Log1(afsd_logp, "cm_RetryLock Server lock (%d) is sufficient for lock. Granting",
5611 (int) scp->serverLock);
5613 osi_Log0(afsd_logp, "cm_RetryLock skipping server lock for scp");
5616 lock_ReleaseWrite(&cm_scacheLock);
5617 lock_ReleaseWrite(&scp->rw);
5622 if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5624 /* check if the conflicting locks have dissappeared already */
5625 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5627 fileLock = (cm_file_lock_t *)
5628 ((char *) q - offsetof(cm_file_lock_t, fileq));
5630 if (IS_LOCK_LOST(fileLock)) {
5631 if (cm_KeyEquals(&fileLock->key, &oldFileLock->key, 0)) {
5632 code = CM_ERROR_BADFD;
5633 oldFileLock->flags |= CM_FILELOCK_FLAG_LOST;
5634 osi_Log1(afsd_logp, " found lost lock %p for same key. Marking lock as lost",
5637 } else if (fileLock->lockType == LockWrite &&
5638 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5639 osi_Log1(afsd_logp, " found conflicting LOST lock %p", fileLock);
5640 code = CM_ERROR_WOULDBLOCK;
5645 if (IS_LOCK_ACCEPTED(fileLock) &&
5646 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5648 if (oldFileLock->lockType != LockRead ||
5649 fileLock->lockType != LockRead) {
5651 osi_Log1(afsd_logp, " found conflicting lock %p", fileLock);
5652 code = CM_ERROR_WOULDBLOCK;
5660 lock_ReleaseWrite(&cm_scacheLock);
5661 lock_ReleaseWrite(&scp->rw);
5666 /* when we get here, the lock is either a WAITUNLOCK or WAITLOCK.
5667 If it is WAITUNLOCK, then we didn't find any conflicting lock
5668 but we haven't verfied whether the serverLock is sufficient to
5669 assert it. If it is WAITLOCK, then the serverLock is
5670 insufficient to assert it. Eitherway, we are ready to accept
5671 the lock as either ACTIVE or WAITLOCK depending on the
5674 /* First, promote the WAITUNLOCK to a WAITLOCK */
5675 if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5676 if (oldFileLock->lockType == LockRead)
5679 scp->exclusiveLocks++;
5681 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITUNLOCK;
5682 oldFileLock->flags |= CM_FILELOCK_FLAG_WAITLOCK;
5685 osi_assertx(IS_LOCK_WAITLOCK(oldFileLock), "!IS_LOCK_WAITLOCK");
5687 if (force_client_lock ||
5688 !SERVERLOCKS_ENABLED(scp) ||
5689 scp->serverLock == oldFileLock->lockType ||
5690 (oldFileLock->lockType == LockRead &&
5691 scp->serverLock == LockWrite)) {
5693 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5695 if ((force_client_lock ||
5696 !SERVERLOCKS_ENABLED(scp)) &&
5697 !IS_LOCK_CLIENTONLY(oldFileLock)) {
5699 oldFileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
5701 if (oldFileLock->lockType == LockRead)
5704 scp->exclusiveLocks--;
5709 lock_ReleaseWrite(&cm_scacheLock);
5710 lock_ReleaseWrite(&scp->rw);
5717 code = cm_SyncOp(scp, NULL, oldFileLock->userp, &req, 0,
5718 CM_SCACHESYNC_NEEDCALLBACK
5719 | CM_SCACHESYNC_GETSTATUS
5720 | CM_SCACHESYNC_LOCK);
5722 osi_Log1(afsd_logp, "cm_RetryLock SyncOp failure code 0x%x", code);
5723 lock_ReleaseWrite(&cm_scacheLock);
5724 goto post_syncopdone;
5727 if (!IS_LOCK_WAITLOCK(oldFileLock))
5728 goto pre_syncopdone;
5730 userp = oldFileLock->userp;
5732 #ifndef AGGRESSIVE_LOCKS
5733 newLock = oldFileLock->lockType;
5735 newLock = LockWrite;
5739 /* if has_insert is non-zero, then:
5740 - the lock a LockRead
5741 - we don't have permission to get a LockRead
5742 - we do have permission to get a LockWrite
5743 - the server supports VICED_CAPABILITY_WRITELOCKACL
5746 newLock = LockWrite;
5749 lock_ReleaseWrite(&cm_scacheLock);
5751 /* when we get here, either we have a read-lock and want a
5752 write-lock or we don't have any locks and we want some
5755 if (scp->serverLock == LockRead) {
5757 osi_assertx(newLock == LockWrite, "!LockWrite");
5759 osi_Log0(afsd_logp, " Attempting to UPGRADE from LockRead to LockWrite");
5761 scp->lockDataVersion = scp->dataVersion;
5762 check_data_version = TRUE;
5764 code = cm_IntReleaseLock(scp, userp, &req);
5767 goto pre_syncopdone;
5769 scp->serverLock = -1;
5772 code = cm_IntSetLock(scp, userp, newLock, &req);
5775 if (scp->dataVersion != scp->lockDataVersion) {
5776 /* we lost a race. too bad */
5779 " Data version mismatch while upgrading lock.");
5781 " Data versions before=%I64d, after=%I64d",
5782 scp->lockDataVersion,
5785 " Releasing stale lock for scp 0x%x", scp);
5787 code = cm_IntReleaseLock(scp, userp, &req);
5789 scp->serverLock = -1;
5791 code = CM_ERROR_INVAL;
5793 cm_LockMarkSCacheLost(scp);
5795 scp->serverLock = newLock;
5800 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5806 if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
5807 lock_ObtainWrite(&cm_scacheLock);
5808 if (scp->fileLocksT == &oldFileLock->fileq)
5809 scp->fileLocksT = osi_QPrev(&oldFileLock->fileq);
5810 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, &oldFileLock->fileq);
5811 lock_ReleaseWrite(&cm_scacheLock);
5813 lock_ReleaseWrite(&scp->rw);
5816 lock_ObtainWrite(&cm_scacheLock);
5818 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5819 } else if (code != CM_ERROR_WOULDBLOCK) {
5820 oldFileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5821 cm_ReleaseUser(oldFileLock->userp);
5822 oldFileLock->userp = NULL;
5823 if (oldFileLock->scp) {
5824 cm_ReleaseSCacheNoLock(oldFileLock->scp);
5825 oldFileLock->scp = NULL;
5828 lock_ReleaseWrite(&cm_scacheLock);
5833 cm_key_t cm_GenerateKey(afs_uint16 session_id, afs_offs_t process_id, afs_uint16 file_id)
5837 key.process_id = process_id;
5838 key.session_id = session_id;
5839 key.file_id = file_id;
5844 int cm_KeyEquals(cm_key_t *k1, cm_key_t *k2, int flags)
5846 return (k1->session_id == k2->session_id) && (k1->file_id == k2->file_id) &&
5847 ((flags & CM_UNLOCK_BY_FID) || (k1->process_id == k2->process_id));
5850 void cm_ReleaseAllLocks(void)
5856 cm_file_lock_t *fileLock;
5859 for (i = 0; i < cm_data.scacheHashTableSize; i++)
5861 for ( scp = cm_data.scacheHashTablep[i]; scp; scp = scp->nextp ) {
5862 while (scp->fileLocksH != NULL) {
5863 lock_ObtainWrite(&scp->rw);
5864 lock_ObtainWrite(&cm_scacheLock);
5865 if (!scp->fileLocksH) {
5866 lock_ReleaseWrite(&cm_scacheLock);
5867 lock_ReleaseWrite(&scp->rw);
5870 fileLock = (cm_file_lock_t *)((char *) scp->fileLocksH - offsetof(cm_file_lock_t, fileq));
5871 userp = fileLock->userp;
5873 key = fileLock->key;
5874 cm_HoldSCacheNoLock(scp);
5875 lock_ReleaseWrite(&cm_scacheLock);
5876 cm_UnlockByKey(scp, key, 0, userp, &req);
5877 cm_ReleaseSCache(scp);
5878 cm_ReleaseUser(userp);
5879 lock_ReleaseWrite(&scp->rw);