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;
487 /* see if we can find it using the directory hash tables.
488 we can only do exact matches, since the hash is case
498 code = cm_BeginDirOp(scp, userp, reqp, CM_DIRLOCK_READ, &dirop);
502 code = cm_BPlusDirLookup(&dirop, sp->nsearchNamep, &sp->fid);
507 code = cm_DirLookup(&dirop, sp->searchNamep, &sp->fid);
515 sp->ExactFound = TRUE;
516 *retscp = NULL; /* force caller to call cm_GetSCache() */
521 if (sp->caseFold && code == CM_ERROR_INEXACT_MATCH) {
524 sp->ExactFound = FALSE;
525 *retscp = NULL; /* force caller to call cm_GetSCache() */
529 return CM_ERROR_BPLUS_NOMATCH;
537 * XXX We only get the length once. It might change when we drop the
540 dirLength = scp->length;
543 bufferOffset.LowPart = bufferOffset.HighPart = 0;
545 curOffset = *startOffsetp;
547 curOffset.HighPart = 0;
548 curOffset.LowPart = 0;
552 /* make sure that curOffset.LowPart doesn't point to the first
553 * 32 bytes in the 2nd through last dir page, and that it
554 * doesn't point at the first 13 32-byte chunks in the first
555 * dir page, since those are dir and page headers, and don't
556 * contain useful information.
558 temp = curOffset.LowPart & (2048-1);
559 if (curOffset.HighPart == 0 && curOffset.LowPart < 2048) {
560 /* we're in the first page */
561 if (temp < 13*32) temp = 13*32;
564 /* we're in a later dir page */
565 if (temp < 32) temp = 32;
568 /* make sure the low order 5 bits are zero */
571 /* now put temp bits back ito curOffset.LowPart */
572 curOffset.LowPart &= ~(2048-1);
573 curOffset.LowPart |= temp;
575 /* check if we've passed the dir's EOF */
576 if (LargeIntegerGreaterThanOrEqualTo(curOffset, dirLength))
579 /* see if we can use the bufferp we have now; compute in which
580 * page the current offset would be, and check whether that's
581 * the offset of the buffer we have. If not, get the buffer.
583 thyper.HighPart = curOffset.HighPart;
584 thyper.LowPart = curOffset.LowPart & ~(cm_data.buf_blockSize-1);
585 if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
588 lock_ReleaseMutex(&bufferp->mx);
589 buf_Release(bufferp);
593 code = buf_Get(scp, &thyper, reqp, &bufferp);
595 /* if buf_Get() fails we do not have a buffer object to lock */
600 lock_ObtainMutex(&bufferp->mx);
601 bufferOffset = thyper;
603 /* now get the data in the cache */
605 lock_ObtainWrite(&scp->rw);
606 code = cm_SyncOp(scp, bufferp, userp, reqp,
608 CM_SCACHESYNC_NEEDCALLBACK
610 | CM_SCACHESYNC_BUFLOCKED);
612 lock_ReleaseWrite(&scp->rw);
615 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
617 if (cm_HaveBuffer(scp, bufferp, 1)) {
618 lock_ReleaseWrite(&scp->rw);
622 /* otherwise, load the buffer and try again */
623 lock_ReleaseMutex(&bufferp->mx);
624 code = cm_GetBuffer(scp, bufferp, NULL, userp,
626 lock_ReleaseWrite(&scp->rw);
627 lock_ObtainMutex(&bufferp->mx);
632 lock_ReleaseMutex(&bufferp->mx);
633 buf_Release(bufferp);
637 } /* if (wrong buffer) ... */
639 /* now we have the buffer containing the entry we're interested
640 * in; copy it out if it represents a non-deleted entry.
642 entryInDir = curOffset.LowPart & (2048-1);
643 entryInBuffer = curOffset.LowPart & (cm_data.buf_blockSize - 1);
645 /* page header will help tell us which entries are free. Page
646 * header can change more often than once per buffer, since
647 * AFS 3 dir page size may be less than (but not more than) a
648 * buffer package buffer.
650 /* only look intra-buffer */
651 temp = curOffset.LowPart & (cm_data.buf_blockSize - 1);
652 temp &= ~(2048 - 1); /* turn off intra-page bits */
653 pageHeaderp = (cm_pageHeader_t *) (bufferp->datap + temp);
655 /* now determine which entry we're looking at in the page. If
656 * it is free (there's a free bitmap at the start of the dir),
657 * we should skip these 32 bytes.
659 slotInPage = (entryInDir & 0x7e0) >> 5;
660 if (!(pageHeaderp->freeBitmap[slotInPage>>3]
661 & (1 << (slotInPage & 0x7)))) {
662 /* this entry is free */
663 numDirChunks = 1; /* only skip this guy */
667 tp = bufferp->datap + entryInBuffer;
668 dep = (cm_dirEntry_t *) tp; /* now points to AFS3 dir entry */
670 /* while we're here, compute the next entry's location, too,
671 * since we'll need it when writing out the cookie into the
672 * dir listing stream.
674 numDirChunks = cm_NameEntries(dep->name, NULL);
676 /* compute the offset of the cookie representing the next entry */
677 nextEntryCookie = curOffset.LowPart
678 + (CM_DIR_CHUNKSIZE * numDirChunks);
680 if (dep->fid.vnode != 0) {
681 /* this is one of the entries to use: it is not deleted */
682 code = (*funcp)(scp, dep, parmp, &curOffset);
685 } /* if we're including this name */
688 /* and adjust curOffset to be where the new cookie is */
690 thyper.LowPart = CM_DIR_CHUNKSIZE * numDirChunks;
691 curOffset = LargeIntegerAdd(thyper, curOffset);
692 } /* while copying data for dir listing */
694 /* release the mutex */
696 lock_ReleaseMutex(&bufferp->mx);
697 buf_Release(bufferp);
702 int cm_NoneUpper(normchar_t *s)
706 if (c >= 'A' && c <= 'Z')
711 int cm_NoneLower(normchar_t *s)
715 if (c >= 'a' && c <= 'z')
720 long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
723 cm_lookupSearch_t *sp;
725 normchar_t matchName[MAX_PATH];
726 int looking_for_short_name = FALSE;
728 sp = (cm_lookupSearch_t *) rockp;
730 if (cm_FsStringToNormString(dep->name, -1, matchName, lengthof(matchName)) == 0) {
731 /* Can't normalize FS string. */
736 match = cm_NormStrCmpI(matchName, sp->nsearchNamep);
738 match = cm_NormStrCmp(matchName, sp->nsearchNamep);
742 && !cm_Is8Dot3(matchName)) {
744 cm_Gen8Dot3NameInt(dep->name, &dep->fid, matchName, NULL);
746 match = cm_NormStrCmpI(matchName, sp->nsearchNamep);
748 match = cm_NormStrCmp(matchName, sp->nsearchNamep);
749 looking_for_short_name = TRUE;
759 if (!sp->caseFold || looking_for_short_name) {
760 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
761 return CM_ERROR_STOPNOW;
765 * If we get here, we are doing a case-insensitive search, and we
766 * have found a match. Now we determine what kind of match it is:
767 * exact, lower-case, upper-case, or none of the above. This is done
768 * in order to choose among matches, if there are more than one.
771 /* Exact matches are the best. */
772 match = cm_NormStrCmp(matchName, sp->nsearchNamep);
775 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
776 return CM_ERROR_STOPNOW;
779 /* Lower-case matches are next. */
782 if (cm_NoneUpper(matchName)) {
787 /* Upper-case matches are next. */
790 if (cm_NoneLower(matchName)) {
795 /* General matches are last. */
801 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
805 /* read the contents of a mount point into the appropriate string.
806 * called with write locked scp, and returns with locked scp.
808 long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
811 cm_buf_t *bufp = NULL;
815 if (scp->mountPointStringp[0])
818 #ifdef AFS_FREELANCE_CLIENT
819 /* File servers do not have data for freelance entries */
820 if (cm_freelanceEnabled &&
821 scp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
822 scp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
824 code = cm_FreelanceFetchMountPointString(scp);
826 #endif /* AFS_FREELANCE_CLIENT */
828 /* otherwise, we have to read it in */
829 lock_ReleaseWrite(&scp->rw);
831 thyper.LowPart = thyper.HighPart = 0;
832 code = buf_Get(scp, &thyper, reqp, &bufp);
834 lock_ObtainWrite(&scp->rw);
839 code = cm_SyncOp(scp, bufp, userp, reqp, 0,
840 CM_SCACHESYNC_READ | CM_SCACHESYNC_NEEDCALLBACK);
844 cm_SyncOpDone(scp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
846 if (cm_HaveBuffer(scp, bufp, 0))
849 /* otherwise load buffer */
850 code = cm_GetBuffer(scp, bufp, NULL, userp, reqp);
854 /* locked, has callback, has valid data in buffer */
855 if ((tlen = scp->length.LowPart) > MOUNTPOINTLEN - 1)
856 return CM_ERROR_TOOBIG;
858 code = CM_ERROR_INVAL;
862 /* someone else did the work while we were out */
863 if (scp->mountPointStringp[0]) {
868 /* otherwise, copy out the link */
869 memcpy(scp->mountPointStringp, bufp->datap, tlen);
871 /* now make it null-terminated. Note that the original contents of a
872 * link that is a mount point is "#volname." where "." is there just to
873 * be turned into a null. That is, we can trash the last char of the
874 * link without damaging the vol name. This is a stupid convention,
875 * but that's the protocol.
877 scp->mountPointStringp[tlen-1] = 0;
888 /* called with a locked scp and chases the mount point, yielding outScpp.
889 * scp remains write locked, just for simplicity of describing the interface.
891 long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
892 cm_req_t *reqp, cm_scache_t **outScpp)
894 fschar_t *cellNamep = NULL;
895 fschar_t *volNamep = NULL;
899 cm_volume_t *volp = NULL;
908 if (scp->mountRootFid.cell != 0 && scp->mountRootGen >= cm_data.mountRootGen) {
909 tfid = scp->mountRootFid;
910 lock_ReleaseWrite(&scp->rw);
911 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
912 lock_ObtainWrite(&scp->rw);
916 /* parse the volume name */
917 mpNamep = scp->mountPointStringp;
919 return CM_ERROR_NOSUCHPATH;
920 mtType = *scp->mountPointStringp;
922 cp = cm_FsStrChr(mpNamep, _FS(':'));
924 /* cellular mount point */
925 cellNamep = (fschar_t *)malloc((cp - mpNamep) * sizeof(fschar_t));
926 cm_FsStrCpyN(cellNamep, cp - mpNamep, mpNamep + 1, cp - mpNamep - 1);
927 volNamep = cm_FsStrDup(cp+1);
929 /* now look up the cell */
930 lock_ReleaseWrite(&scp->rw);
931 cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE);
932 lock_ObtainWrite(&scp->rw);
935 volNamep = cm_FsStrDup(mpNamep + 1);
937 #ifdef AFS_FREELANCE_CLIENT
939 * Mount points in the Freelance cell should default
940 * to the workstation cell.
942 if (cm_freelanceEnabled &&
943 scp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
944 scp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
946 fschar_t rootCellName[256]="";
947 cm_GetRootCellName(rootCellName);
948 cellp = cm_GetCell(rootCellName, 0);
950 #endif /* AFS_FREELANCE_CLIENT */
951 cellp = cm_FindCellByID(scp->fid.cell, 0);
955 code = CM_ERROR_NOSUCHCELL;
959 vnLength = cm_FsStrLen(volNamep);
960 if (vnLength >= 8 && cm_FsStrCmp(volNamep + vnLength - 7, ".backup") == 0)
961 targetType = BACKVOL;
962 else if (vnLength >= 10
963 && cm_FsStrCmp(volNamep + vnLength - 9, ".readonly") == 0)
968 /* check for backups within backups */
969 if (targetType == BACKVOL
970 && (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
971 == CM_SCACHEFLAG_RO) {
972 code = CM_ERROR_NOSUCHVOLUME;
976 /* now we need to get the volume */
977 lock_ReleaseWrite(&scp->rw);
978 if (cm_VolNameIsID(volNamep)) {
979 code = cm_FindVolumeByID(cellp, atoi(volNamep), userp, reqp,
980 CM_GETVOL_FLAG_CREATE, &volp);
982 code = cm_FindVolumeByName(cellp, volNamep, userp, reqp,
983 CM_GETVOL_FLAG_CREATE, &volp);
985 lock_ObtainWrite(&scp->rw);
988 afs_uint32 cell, volume;
989 cm_vol_state_t *statep;
991 cell = cellp->cellID;
993 /* if the mt pt originates in a .backup volume (not a .readonly)
994 * and FollowBackupPath is active, and if there is a .backup
995 * volume for the target, then use the .backup of the target
996 * instead of the read-write.
998 if (cm_followBackupPath &&
999 volp->vol[BACKVOL].ID != 0 &&
1000 (dscp->flags & (CM_SCACHEFLAG_RO|CM_SCACHEFLAG_PURERO)) == CM_SCACHEFLAG_RO &&
1001 (targetType == RWVOL || targetType == ROVOL && volp->vol[ROVOL].ID == 0)
1003 targetType = BACKVOL;
1005 /* if the mt pt is in a read-only volume (not just a
1006 * backup), and if there is a read-only volume for the
1007 * target, and if this is a targetType '#' mount point, use
1008 * the read-only, otherwise use the one specified.
1010 else if (mtType == '#' && targetType == RWVOL &&
1011 (scp->flags & CM_SCACHEFLAG_PURERO) &&
1012 volp->vol[ROVOL].ID != 0) {
1016 lock_ObtainWrite(&volp->rw);
1017 statep = cm_VolumeStateByType(volp, targetType);
1018 volume = statep->ID;
1019 statep->dotdotFid = dscp->fid;
1020 lock_ReleaseWrite(&volp->rw);
1022 /* the rest of the fid is a magic number */
1023 cm_SetFid(&scp->mountRootFid, cell, volume, 1, 1);
1024 scp->mountRootGen = cm_data.mountRootGen;
1026 tfid = scp->mountRootFid;
1027 lock_ReleaseWrite(&scp->rw);
1028 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
1029 lock_ObtainWrite(&scp->rw);
1042 long cm_LookupInternal(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_user_t *userp,
1043 cm_req_t *reqp, cm_scache_t **outScpp)
1046 int dnlcHit = 1; /* did we hit in the dnlc? yes, we did */
1047 cm_scache_t *tscp = NULL;
1048 cm_scache_t *mountedScp;
1049 cm_lookupSearch_t rock;
1051 normchar_t *nnamep = NULL;
1052 fschar_t *fnamep = NULL;
1056 memset(&rock, 0, sizeof(rock));
1058 if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
1059 && cm_ClientStrCmp(cnamep, _C("..")) == 0) {
1060 if (dscp->dotdotFid.volume == 0)
1061 return CM_ERROR_NOSUCHVOLUME;
1062 rock.fid = dscp->dotdotFid;
1064 } else if (cm_ClientStrCmp(cnamep, _C(".")) == 0) {
1065 rock.fid = dscp->fid;
1069 nnamep = cm_ClientStringToNormStringAlloc(cnamep, -1, NULL);
1071 code = CM_ERROR_NOSUCHFILE;
1074 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
1076 code = CM_ERROR_NOSUCHFILE;
1080 if (flags & CM_FLAG_NOMOUNTCHASE) {
1081 /* In this case, we should go and call cm_Dir* functions
1082 directly since the following cm_ApplyDir() function will
1090 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ, &dirop);
1093 code = cm_BPlusDirLookup(&dirop, nnamep, &rock.fid);
1098 code = cm_DirLookup(&dirop, fnamep, &rock.fid);
1100 cm_EndDirOp(&dirop);
1110 if (code == CM_ERROR_INEXACT_MATCH && (flags & CM_FLAG_CASEFOLD)) {
1117 return CM_ERROR_BPLUS_NOMATCH;
1122 rock.fid.cell = dscp->fid.cell;
1123 rock.fid.volume = dscp->fid.volume;
1124 rock.searchNamep = fnamep;
1125 rock.nsearchNamep = nnamep;
1126 rock.caseFold = (flags & CM_FLAG_CASEFOLD);
1127 rock.hasTilde = ((cm_ClientStrChr(cnamep, '~') != NULL) ? 1 : 0);
1129 /* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
1130 code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
1131 (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
1133 /* code == 0 means we fell off the end of the dir, while stopnow means
1134 * that we stopped early, probably because we found the entry we're
1135 * looking for. Any other non-zero code is an error.
1137 if (code && code != CM_ERROR_STOPNOW) {
1138 /* if the cm_scache_t we are searching in is not a directory
1139 * we must return path not found because the error
1140 * is to describe the final component not an intermediary
1142 if (code == CM_ERROR_NOTDIR) {
1143 if (flags & CM_FLAG_CHECKPATH)
1144 code = CM_ERROR_NOSUCHPATH;
1146 code = CM_ERROR_NOSUCHFILE;
1151 getroot = (dscp==cm_data.rootSCachep) ;
1153 if (!cm_freelanceEnabled || !getroot) {
1154 if (flags & CM_FLAG_CHECKPATH)
1155 code = CM_ERROR_NOSUCHPATH;
1157 code = CM_ERROR_NOSUCHFILE;
1160 else if (!cm_ClientStrChr(cnamep, '#') &&
1161 !cm_ClientStrChr(cnamep, '%') &&
1162 cm_ClientStrCmpI(cnamep, _C("srvsvc")) &&
1163 cm_ClientStrCmpI(cnamep, _C("wkssvc")) &&
1164 cm_ClientStrCmpI(cnamep, _C("ipc$")))
1166 /* nonexistent dir on freelance root, so add it */
1167 fschar_t fullname[CELL_MAXNAMELEN + 1] = "."; /* +1 so that when we skip the . the size is still CELL_MAXNAMELEN */
1170 osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %S",
1171 osi_LogSaveClientString(afsd_logp,cnamep));
1174 * There is an ugly behavior where a share name "foo" will be searched
1175 * for as "fo". If the searched for name differs by an already existing
1176 * symlink or mount point in the Freelance directory, do not add the
1177 * new value automatically.
1181 if (cnamep[0] == '.') {
1182 if (cm_GetCell_Gen(&fnamep[1], &fullname[1], CM_FLAG_CREATE)) {
1184 if (!cm_FreelanceMountPointExists(fullname, 0))
1185 code = cm_FreelanceAddMount(fullname, &fullname[1], "root.cell.",
1187 if ( cm_FsStrCmpI(&fnamep[1], &fullname[1]) &&
1188 !cm_FreelanceMountPointExists(fnamep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0) &&
1189 !cm_FreelanceSymlinkExists(fnamep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0))
1190 code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
1193 if (cm_GetCell_Gen(fnamep, fullname, CM_FLAG_CREATE)) {
1195 if (!cm_FreelanceMountPointExists(fullname, 0))
1196 code = cm_FreelanceAddMount(fullname, fullname, "root.cell.", 0, &rock.fid);
1197 if ( cm_FsStrCmpI(fnamep, fullname) &&
1198 !cm_FreelanceMountPointExists(fnamep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0) &&
1199 !cm_FreelanceSymlinkExists(fnamep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0))
1200 code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
1203 if (!found || code) { /* add mount point failed, so give up */
1204 if (flags & CM_FLAG_CHECKPATH)
1205 code = CM_ERROR_NOSUCHPATH;
1207 code = CM_ERROR_NOSUCHFILE;
1210 tscp = NULL; /* to force call of cm_GetSCache */
1212 if (flags & CM_FLAG_CHECKPATH)
1213 code = CM_ERROR_NOSUCHPATH;
1215 code = CM_ERROR_NOSUCHFILE;
1221 if ( !tscp ) /* we did not find it in the dnlc */
1224 code = cm_GetSCache(&rock.fid, &tscp, userp, reqp);
1228 /* tscp is now held */
1230 lock_ObtainWrite(&tscp->rw);
1231 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1232 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1234 lock_ReleaseWrite(&tscp->rw);
1235 cm_ReleaseSCache(tscp);
1238 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1239 /* tscp is now locked */
1241 if (!(flags & CM_FLAG_NOMOUNTCHASE)
1242 && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
1243 /* mount points are funny: they have a volume name to mount
1246 code = cm_ReadMountPoint(tscp, userp, reqp);
1248 code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
1250 lock_ReleaseWrite(&tscp->rw);
1251 cm_ReleaseSCache(tscp);
1258 lock_ReleaseWrite(&tscp->rw);
1261 /* copy back pointer */
1264 /* insert scache in dnlc */
1265 if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
1266 /* lock the directory entry to prevent racing callback revokes */
1267 lock_ObtainRead(&dscp->rw);
1268 if ( dscp->cbServerp != NULL && dscp->cbExpires > 0 ) {
1269 /* TODO: reuse nnamep from above */
1272 nnamep = cm_ClientStringToNormStringAlloc(cnamep, -1, NULL);
1274 cm_dnlcEnter(dscp, nnamep, tscp);
1276 lock_ReleaseRead(&dscp->rw);
1293 int cm_ExpandSysName(clientchar_t *inp, clientchar_t *outp, long outSizeCch, unsigned int index)
1298 tp = cm_ClientStrRChr(inp, '@');
1300 return 0; /* no @sys */
1302 if (cm_ClientStrCmp(tp, _C("@sys")) != 0)
1303 return 0; /* no @sys */
1305 /* caller just wants to know if this is a valid @sys type of name */
1309 if (index >= cm_sysNameCount)
1312 /* otherwise generate the properly expanded @sys name */
1313 prefixCount = (int)(tp - inp);
1315 cm_ClientStrCpyN(outp, outSizeCch, inp, prefixCount); /* copy out "a." from "a.@sys" */
1316 outp[prefixCount] = 0; /* null terminate the "a." */
1317 cm_ClientStrCat(outp, outSizeCch, cm_sysNameList[index]);/* append i386_nt40 */
1321 long cm_EvaluateVolumeReference(clientchar_t * namep, long flags, cm_user_t * userp,
1322 cm_req_t *reqp, cm_scache_t ** outScpp)
1324 afs_uint32 code = 0;
1325 fschar_t cellName[CELL_MAXNAMELEN];
1326 fschar_t volumeName[VL_MAXNAMELEN];
1330 fschar_t * fnamep = NULL;
1332 cm_cell_t * cellp = NULL;
1333 cm_volume_t * volp = NULL;
1337 int mountType = RWVOL;
1339 osi_Log1(afsd_logp, "cm_EvaluateVolumeReference for string [%S]",
1340 osi_LogSaveClientString(afsd_logp, namep));
1342 if (cm_ClientStrCmpNI(namep, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH) != 0) {
1343 goto _exit_invalid_path;
1346 /* namep is assumed to look like the following:
1348 @vol:<cellname>%<volume>\0
1350 @vol:<cellname>#<volume>\0
1354 fnamep = cm_ClientStringToFsStringAlloc(namep, -1, NULL);
1355 cp = fnamep + CM_PREFIX_VOL_CCH; /* cp points to cell name, hopefully */
1356 tp = cm_FsStrChr(cp, '%');
1358 tp = cm_FsStrChr(cp, '#');
1360 (len = tp - cp) == 0 ||
1361 len > CELL_MAXNAMELEN)
1362 goto _exit_invalid_path;
1363 cm_FsStrCpyN(cellName, lengthof(cellName), cp, len);
1368 cp = tp+1; /* cp now points to volume, supposedly */
1369 cm_FsStrCpy(volumeName, lengthof(volumeName), cp);
1371 /* OK, now we have the cell and the volume */
1372 osi_Log2(afsd_logp, " Found cell [%s] and volume [%s]",
1373 osi_LogSaveFsString(afsd_logp, cellName),
1374 osi_LogSaveFsString(afsd_logp, volumeName));
1376 cellp = cm_GetCell(cellName, CM_FLAG_CREATE);
1377 if (cellp == NULL) {
1378 goto _exit_invalid_path;
1381 len = cm_FsStrLen(volumeName);
1382 if (len >= 8 && cm_FsStrCmp(volumeName + len - 7, ".backup") == 0)
1384 else if (len >= 10 &&
1385 cm_FsStrCmp(volumeName + len - 9, ".readonly") == 0)
1390 if (cm_VolNameIsID(volumeName)) {
1391 code = cm_FindVolumeByID(cellp, atoi(volumeName), userp, reqp,
1392 CM_GETVOL_FLAG_CREATE, &volp);
1394 code = cm_FindVolumeByName(cellp, volumeName, userp, reqp,
1395 CM_GETVOL_FLAG_CREATE, &volp);
1401 if (volType == BACKVOL)
1402 volume = volp->vol[BACKVOL].ID;
1403 else if (volType == ROVOL ||
1404 (volType == RWVOL && mountType == ROVOL && volp->vol[ROVOL].ID != 0))
1405 volume = volp->vol[ROVOL].ID;
1407 volume = volp->vol[RWVOL].ID;
1409 cm_SetFid(&fid, cellp->cellID, volume, 1, 1);
1411 code = cm_GetSCache(&fid, outScpp, userp, reqp);
1424 if (flags & CM_FLAG_CHECKPATH)
1425 return CM_ERROR_NOSUCHPATH;
1427 return CM_ERROR_NOSUCHFILE;
1430 #ifdef DEBUG_REFCOUNT
1431 long cm_LookupDbg(cm_scache_t *dscp, clientchar_t *namep, long flags, cm_user_t *userp,
1432 cm_req_t *reqp, cm_scache_t **outScpp, char * file, long line)
1434 long cm_Lookup(cm_scache_t *dscp, clientchar_t *namep, long flags, cm_user_t *userp,
1435 cm_req_t *reqp, cm_scache_t **outScpp)
1439 clientchar_t tname[AFSPATHMAX];
1440 int sysNameIndex = 0;
1441 cm_scache_t *scp = NULL;
1443 #ifdef DEBUG_REFCOUNT
1444 afsi_log("%s:%d cm_Lookup dscp 0x%p ref %d", file, line, dscp, dscp->refCount, file, line);
1445 osi_Log2(afsd_logp, "cm_Lookup dscp 0x%p ref %d", dscp, dscp->refCount);
1448 if ( cm_ClientStrCmpI(namep,_C(SMB_IOCTL_FILENAME_NOSLASH)) == 0 ) {
1449 if (flags & CM_FLAG_CHECKPATH)
1450 return CM_ERROR_NOSUCHPATH;
1452 return CM_ERROR_NOSUCHFILE;
1455 if (dscp == cm_data.rootSCachep &&
1456 cm_ClientStrCmpNI(namep, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH) == 0) {
1457 return cm_EvaluateVolumeReference(namep, flags, userp, reqp, outScpp);
1460 if (cm_ExpandSysName(namep, NULL, 0, 0) > 0) {
1461 for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
1462 code = cm_ExpandSysName(namep, tname, lengthof(tname), sysNameIndex);
1464 code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
1465 #ifdef DEBUG_REFCOUNT
1466 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);
1467 osi_Log3(afsd_logp, "cm_LookupInternal (1) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1475 cm_ReleaseSCache(scp);
1479 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1480 #ifdef DEBUG_REFCOUNT
1481 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);
1482 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1489 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1490 #ifdef DEBUG_REFCOUNT
1491 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);
1492 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1498 /* None of the possible sysName expansions could be found */
1499 if (flags & CM_FLAG_CHECKPATH)
1500 return CM_ERROR_NOSUCHPATH;
1502 return CM_ERROR_NOSUCHFILE;
1505 /*! \brief Unlink a file name
1507 Encapsulates a call to RXAFS_RemoveFile().
1509 \param[in] dscp cm_scache_t pointing at the directory containing the
1510 name to be unlinked.
1512 \param[in] fnamep Original name to be unlinked. This is the
1513 name that will be passed into the RXAFS_RemoveFile() call.
1514 This parameter is optional. If not provided, the value will
1517 \param[in] came Client name to be unlinked. This name will be used
1518 to update the local directory caches.
1520 \param[in] userp cm_user_t for the request.
1522 \param[in] reqp Request tracker.
1525 long cm_Unlink(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t * cnamep,
1526 cm_user_t *userp, cm_req_t *reqp)
1532 AFSFetchStatus newDirStatus;
1534 struct rx_connection * rxconnp;
1536 cm_scache_t *scp = NULL;
1537 int free_fnamep = FALSE;
1539 memset(&volSync, 0, sizeof(volSync));
1541 if (fnamep == NULL) {
1544 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ, &dirop);
1546 code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
1549 cm_EndDirOp(&dirop);
1556 #ifdef AFS_FREELANCE_CLIENT
1557 if (cm_freelanceEnabled && dscp == cm_data.rootSCachep) {
1558 /* deleting a mount point from the root dir. */
1559 code = cm_FreelanceRemoveMount(fnamep);
1564 code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
1566 /* make sure we don't screw up the dir status during the merge */
1567 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
1569 lock_ObtainWrite(&dscp->rw);
1570 sflags = CM_SCACHESYNC_STOREDATA;
1571 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1572 lock_ReleaseWrite(&dscp->rw);
1574 cm_EndDirOp(&dirop);
1579 afsFid.Volume = dscp->fid.volume;
1580 afsFid.Vnode = dscp->fid.vnode;
1581 afsFid.Unique = dscp->fid.unique;
1583 osi_Log1(afsd_logp, "CALL RemoveFile scp 0x%p", dscp);
1585 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
1589 rxconnp = cm_GetRxConn(connp);
1590 code = RXAFS_RemoveFile(rxconnp, &afsFid, fnamep,
1591 &newDirStatus, &volSync);
1592 rx_PutConnection(rxconnp);
1594 } while (cm_Analyze(connp, userp, reqp, &dscp->fid, &volSync, NULL, NULL, code));
1595 code = cm_MapRPCError(code, reqp);
1598 osi_Log1(afsd_logp, "CALL RemoveFile FAILURE, code 0x%x", code);
1600 osi_Log0(afsd_logp, "CALL RemoveFile SUCCESS");
1603 lock_ObtainWrite(&dirop.scp->dirlock);
1604 dirop.lockType = CM_DIRLOCK_WRITE;
1606 lock_ObtainWrite(&dscp->rw);
1607 cm_dnlcRemove(dscp, cnamep);
1608 cm_SyncOpDone(dscp, NULL, sflags);
1610 cm_MergeStatus(NULL, dscp, &newDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
1611 } else if (code == CM_ERROR_NOSUCHFILE) {
1612 /* windows would not have allowed the request to delete the file
1613 * if it did not believe the file existed. therefore, we must
1614 * have an inconsistent view of the world.
1616 dscp->cbServerp = NULL;
1618 lock_ReleaseWrite(&dscp->rw);
1620 if (code == 0 && cm_CheckDirOpForSingleChange(&dirop) && cnamep) {
1621 cm_DirDeleteEntry(&dirop, fnamep);
1623 cm_BPlusDirDeleteEntry(&dirop, cnamep);
1626 cm_EndDirOp(&dirop);
1629 cm_ReleaseSCache(scp);
1631 lock_ObtainWrite(&scp->rw);
1632 if (--scp->linkCount == 0)
1633 scp->flags |= CM_SCACHEFLAG_DELETED;
1634 cm_DiscardSCache(scp);
1635 lock_ReleaseWrite(&scp->rw);
1646 /* called with a write locked vnode, and fills in the link info.
1647 * returns this the vnode still write locked.
1649 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1656 lock_AssertWrite(&linkScp->rw);
1657 if (!linkScp->mountPointStringp[0]) {
1659 #ifdef AFS_FREELANCE_CLIENT
1660 /* File servers do not have data for freelance entries */
1661 if (cm_freelanceEnabled &&
1662 linkScp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
1663 linkScp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
1665 code = cm_FreelanceFetchMountPointString(linkScp);
1667 #endif /* AFS_FREELANCE_CLIENT */
1669 /* read the link data from the file server*/
1670 lock_ReleaseWrite(&linkScp->rw);
1671 thyper.LowPart = thyper.HighPart = 0;
1672 code = buf_Get(linkScp, &thyper, reqp, &bufp);
1673 lock_ObtainWrite(&linkScp->rw);
1677 code = cm_SyncOp(linkScp, bufp, userp, reqp, 0,
1678 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1683 cm_SyncOpDone(linkScp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1685 if (cm_HaveBuffer(linkScp, bufp, 0))
1688 code = cm_GetBuffer(linkScp, bufp, NULL, userp, reqp);
1693 } /* while loop to get the data */
1695 /* now if we still have no link read in,
1696 * copy the data from the buffer */
1697 if ((temp = linkScp->length.LowPart) >= MOUNTPOINTLEN) {
1699 return CM_ERROR_TOOBIG;
1702 /* otherwise, it fits; make sure it is still null (could have
1703 * lost race with someone else referencing this link above),
1704 * and if so, copy in the data.
1706 if (!linkScp->mountPointStringp[0]) {
1707 strncpy(linkScp->mountPointStringp, bufp->datap, temp);
1708 linkScp->mountPointStringp[temp] = 0; /* null terminate */
1713 if ( !strnicmp(linkScp->mountPointStringp, "msdfs:", strlen("msdfs:")) )
1714 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1716 } /* don't have sym link contents cached */
1721 /* called with a held vnode and a path suffix, with the held vnode being a
1722 * symbolic link. Our goal is to generate a new path to interpret, and return
1723 * this new path in newSpaceBufferp. If the new vnode is relative to a dir
1724 * other than the directory containing the symbolic link, then the new root is
1725 * returned in *newRootScpp, otherwise a null is returned there.
1727 long cm_AssembleLink(cm_scache_t *linkScp, fschar_t *pathSuffixp,
1728 cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1729 cm_user_t *userp, cm_req_t *reqp)
1736 *newRootScpp = NULL;
1737 *newSpaceBufferp = NULL;
1739 lock_ObtainWrite(&linkScp->rw);
1740 code = cm_HandleLink(linkScp, userp, reqp);
1744 /* if we may overflow the buffer, bail out; buffer is signficantly
1745 * bigger than max path length, so we don't really have to worry about
1746 * being a little conservative here.
1748 if (cm_FsStrLen(linkScp->mountPointStringp) + cm_FsStrLen(pathSuffixp) + 2
1749 >= CM_UTILS_SPACESIZE) {
1750 code = CM_ERROR_TOOBIG;
1754 tsp = cm_GetSpace();
1755 linkp = linkScp->mountPointStringp;
1756 if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1757 if (strlen(linkp) > cm_mountRootLen)
1758 StringCbCopyA((char *) tsp->data, sizeof(tsp->data), linkp+cm_mountRootLen+1);
1761 *newRootScpp = cm_data.rootSCachep;
1762 cm_HoldSCache(cm_data.rootSCachep);
1763 } else if (linkp[0] == '\\' && linkp[1] == '\\') {
1764 if (!strnicmp(&linkp[2], cm_NetbiosName, (len = (long)strlen(cm_NetbiosName))))
1766 char * p = &linkp[len + 3];
1767 if (strnicmp(p, "all", 3) == 0)
1770 StringCbCopyA(tsp->data, sizeof(tsp->data), p);
1771 for (p = tsp->data; *p; p++) {
1775 *newRootScpp = cm_data.rootSCachep;
1776 cm_HoldSCache(cm_data.rootSCachep);
1778 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1779 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1780 code = CM_ERROR_PATH_NOT_COVERED;
1782 } else if ( linkScp->fileType == CM_SCACHETYPE_DFSLINK ||
1783 !strnicmp(linkp, "msdfs:", (len = (long)strlen("msdfs:"))) ) {
1784 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1785 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1786 code = CM_ERROR_PATH_NOT_COVERED;
1787 } else if (*linkp == '\\' || *linkp == '/') {
1789 /* formerly, this was considered to be from the AFS root,
1790 * but this seems to create problems. instead, we will just
1791 * reject the link */
1792 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp+1);
1793 *newRootScpp = cm_data.rootSCachep;
1794 cm_HoldSCache(cm_data.rootSCachep);
1796 /* we still copy the link data into the response so that
1797 * the user can see what the link points to
1799 linkScp->fileType = CM_SCACHETYPE_INVALID;
1800 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1801 code = CM_ERROR_NOSUCHPATH;
1804 /* a relative link */
1805 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1807 if (pathSuffixp[0] != 0) { /* if suffix string is non-null */
1808 StringCchCatA(tsp->data,lengthof(tsp->data), "\\");
1809 StringCchCatA(tsp->data,lengthof(tsp->data), pathSuffixp);
1813 clientchar_t * cpath = cm_FsStringToClientStringAlloc(tsp->data, -1, NULL);
1814 if (cpath != NULL) {
1815 cm_ClientStrCpy(tsp->wdata, lengthof(tsp->wdata), cpath);
1817 *newSpaceBufferp = tsp;
1819 code = CM_ERROR_NOSUCHPATH;
1826 if (code == CM_ERROR_PATH_NOT_COVERED && reqp->tidPathp && reqp->relPathp) {
1827 cm_VolStatus_Notify_DFS_Mapping(linkScp, reqp->tidPathp, reqp->relPathp);
1832 lock_ReleaseWrite(&linkScp->rw);
1835 #ifdef DEBUG_REFCOUNT
1836 long cm_NameIDbg(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
1837 cm_user_t *userp, clientchar_t *tidPathp, cm_req_t *reqp,
1838 cm_scache_t **outScpp,
1839 char * file, long line)
1841 long cm_NameI(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
1842 cm_user_t *userp, clientchar_t *tidPathp,
1843 cm_req_t *reqp, cm_scache_t **outScpp)
1847 clientchar_t *tp; /* ptr moving through input buffer */
1848 clientchar_t tc; /* temp char */
1849 int haveComponent; /* has new component started? */
1850 clientchar_t component[AFSPATHMAX]; /* this is the new component */
1851 clientchar_t *cp; /* component name being assembled */
1852 cm_scache_t *tscp; /* current location in the hierarchy */
1853 cm_scache_t *nscp; /* next dude down */
1854 cm_scache_t *dirScp; /* last dir we searched */
1855 cm_scache_t *linkScp; /* new root for the symlink we just
1857 cm_space_t *psp; /* space for current path, if we've hit
1859 cm_space_t *tempsp; /* temp vbl */
1860 clientchar_t *restp; /* rest of the pathname to interpret */
1861 int symlinkCount; /* count of # of symlinks traversed */
1862 int extraFlag; /* avoid chasing mt pts for dir cmd */
1863 int phase = 1; /* 1 = tidPathp, 2 = pathp */
1864 #define MAX_FID_COUNT 512
1865 cm_fid_t fids[MAX_FID_COUNT]; /* array of fids processed in this path walk */
1866 int fid_count = 0; /* number of fids processed in this path walk */
1871 #ifdef DEBUG_REFCOUNT
1872 afsi_log("%s:%d cm_NameI rootscp 0x%p ref %d", file, line, rootSCachep, rootSCachep->refCount);
1873 osi_Log4(afsd_logp,"cm_NameI rootscp 0x%p path %S tidpath %S flags 0x%x",
1874 rootSCachep, pathp ? pathp : L"<NULL>", tidPathp ? tidPathp : L"<NULL>",
1889 cm_HoldSCache(tscp);
1897 /* map Unix slashes into DOS ones so we can interpret Unix
1903 if (!haveComponent) {
1906 } else if (tc == 0) {
1920 /* we have a component here */
1921 if (tc == 0 || tc == '\\') {
1922 /* end of the component; we're at the last
1923 * component if tc == 0. However, if the last
1924 * is a symlink, we have more to do.
1926 *cp++ = 0; /* add null termination */
1928 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
1929 extraFlag = CM_FLAG_NOMOUNTCHASE;
1930 code = cm_Lookup(tscp, component,
1932 userp, reqp, &nscp);
1935 if (!cm_ClientStrCmp(component,_C("..")) ||
1936 !cm_ClientStrCmp(component,_C("."))) {
1938 * roll back the fid list until we find the
1939 * fid that matches where we are now. Its not
1940 * necessarily one or two fids because they
1941 * might have been symlinks or mount points or
1942 * both that were crossed.
1944 for ( i=fid_count-1; i>=0; i--) {
1945 if (!cm_FidCmp(&nscp->fid, &fids[i]))
1950 /* add the new fid to the list */
1951 if (fid_count == MAX_FID_COUNT) {
1952 code = CM_ERROR_TOO_MANY_SYMLINKS;
1953 cm_ReleaseSCache(nscp);
1957 fids[fid_count++] = nscp->fid;
1962 cm_ReleaseSCache(tscp);
1964 cm_ReleaseSCache(dirScp);
1967 if ((code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH) &&
1968 tscp->fileType == CM_SCACHETYPE_SYMLINK) {
1969 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_NOSUCHPATH");
1970 return CM_ERROR_NOSUCHPATH;
1972 osi_Log1(afsd_logp,"cm_NameI code 0x%x", code);
1977 haveComponent = 0; /* component done */
1979 cm_ReleaseSCache(dirScp);
1980 dirScp = tscp; /* for some symlinks */
1981 tscp = nscp; /* already held */
1983 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
1986 cm_ReleaseSCache(dirScp);
1992 /* now, if tscp is a symlink, we should follow it and
1993 * assemble the path again.
1995 lock_ObtainWrite(&tscp->rw);
1996 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1997 CM_SCACHESYNC_GETSTATUS
1998 | CM_SCACHESYNC_NEEDCALLBACK);
2000 lock_ReleaseWrite(&tscp->rw);
2001 cm_ReleaseSCache(tscp);
2004 cm_ReleaseSCache(dirScp);
2009 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2011 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
2012 /* this is a symlink; assemble a new buffer */
2013 lock_ReleaseWrite(&tscp->rw);
2014 if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
2015 cm_ReleaseSCache(tscp);
2018 cm_ReleaseSCache(dirScp);
2023 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_TOO_MANY_SYMLINKS");
2024 return CM_ERROR_TOO_MANY_SYMLINKS;
2034 /* TODO: make this better */
2035 frestp = cm_ClientStringToFsStringAlloc(restp, -1, NULL);
2036 code = cm_AssembleLink(tscp, frestp, &linkScp, &tempsp, userp, reqp);
2040 if (code == 0 && linkScp != NULL) {
2041 if (linkScp == cm_data.rootSCachep) {
2045 for ( i=0; i<fid_count; i++) {
2046 if ( !cm_FidCmp(&linkScp->fid, &fids[i]) ) {
2047 code = CM_ERROR_TOO_MANY_SYMLINKS;
2048 cm_ReleaseSCache(linkScp);
2054 if (i == fid_count && fid_count < MAX_FID_COUNT) {
2055 fids[fid_count++] = linkScp->fid;
2060 /* something went wrong */
2061 cm_ReleaseSCache(tscp);
2064 cm_ReleaseSCache(dirScp);
2070 /* otherwise, tempsp has the new path,
2071 * and linkScp is the new root from
2072 * which to interpret that path.
2073 * Continue with the namei processing,
2074 * also doing the bookkeeping for the
2075 * space allocation and tracking the
2076 * vnode reference counts.
2082 cm_ReleaseSCache(tscp);
2087 * now, if linkScp is null, that's
2088 * AssembleLink's way of telling us that
2089 * the sym link is relative to the dir
2090 * containing the link. We have a ref
2091 * to it in dirScp, and we hold it now
2092 * and reuse it as the new spot in the
2100 /* not a symlink, we may be done */
2101 lock_ReleaseWrite(&tscp->rw);
2109 cm_ReleaseSCache(dirScp);
2117 cm_ReleaseSCache(dirScp);
2120 } /* end of a component */
2123 } /* we have a component */
2124 } /* big while loop over all components */
2128 cm_ReleaseSCache(dirScp);
2134 cm_ReleaseSCache(tscp);
2136 #ifdef DEBUG_REFCOUNT
2137 afsi_log("%s:%d cm_NameI code 0x%x outScpp 0x%p ref %d", file, line, code, *outScpp, (*outScpp) ? (*outScpp)->refCount : 0);
2139 osi_Log2(afsd_logp,"cm_NameI code 0x%x outScpp 0x%p", code, *outScpp);
2143 /* called with a dir, and a vnode within the dir that happens to be a symlink.
2144 * We chase the link, and return a held pointer to the target, if it exists,
2145 * in *outScpp. If we succeed, we return 0, otherwise we return an error code
2146 * and do not hold or return a target vnode.
2148 * This is very similar to calling cm_NameI with the last component of a name,
2149 * which happens to be a symlink, except that we've already passed by the name.
2151 * This function is typically called by the directory listing functions, which
2152 * encounter symlinks but need to return the proper file length so programs
2153 * like "more" work properly when they make use of the attributes retrieved from
2156 * The input vnode should not be locked when this function is called.
2158 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
2159 cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
2163 cm_scache_t *newRootScp;
2167 osi_Log1(afsd_logp, "Evaluating symlink scp 0x%p", linkScp);
2169 code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
2173 /* now, if newRootScp is NULL, we're really being told that the symlink
2174 * is relative to the current directory (dscp).
2176 if (newRootScp == NULL) {
2178 cm_HoldSCache(dscp);
2181 code = cm_NameI(newRootScp, spacep->wdata,
2182 CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
2183 userp, NULL, reqp, outScpp);
2185 if (code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH)
2186 code = CM_ERROR_NOSUCHPATH;
2188 /* this stuff is allocated no matter what happened on the namei call,
2190 cm_FreeSpace(spacep);
2191 cm_ReleaseSCache(newRootScp);
2193 if (linkScp == *outScpp) {
2194 cm_ReleaseSCache(*outScpp);
2196 code = CM_ERROR_NOSUCHPATH;
2202 /* for a given entry, make sure that it isn't in the stat cache, and then
2203 * add it to the list of file IDs to be obtained.
2205 * Don't bother adding it if we already have a vnode. Note that the dir
2206 * is locked, so we have to be careful checking the vnode we're thinking of
2207 * processing, to avoid deadlocks.
2209 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
2220 /* Don't overflow bsp. */
2221 if (bsp->counter >= CM_BULKMAX)
2222 return CM_ERROR_STOPNOW;
2224 thyper.LowPart = cm_data.buf_blockSize;
2225 thyper.HighPart = 0;
2226 thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
2228 /* thyper is now the first byte past the end of the record we're
2229 * interested in, and bsp->bufOffset is the first byte of the record
2230 * we're interested in.
2231 * Skip data in the others.
2234 if (LargeIntegerLessThan(*offp, bsp->bufOffset))
2236 if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
2237 return CM_ERROR_STOPNOW;
2238 if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
2241 cm_SetFid(&tfid, scp->fid.cell, scp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
2242 tscp = cm_FindSCache(&tfid);
2244 if (lock_TryWrite(&tscp->rw)) {
2245 /* we have an entry that we can look at */
2246 if (!(tscp->flags & CM_SCACHEFLAG_EACCESS) && cm_HaveCallback(tscp)) {
2247 /* we have a callback on it. Don't bother
2248 * fetching this stat entry, since we're happy
2249 * with the info we have.
2251 lock_ReleaseWrite(&tscp->rw);
2252 cm_ReleaseSCache(tscp);
2255 lock_ReleaseWrite(&tscp->rw);
2257 cm_ReleaseSCache(tscp);
2260 #ifdef AFS_FREELANCE_CLIENT
2261 // yj: if this is a mountpoint under root.afs then we don't want it
2262 // to be bulkstat-ed, instead, we call getSCache directly and under
2263 // getSCache, it is handled specially.
2264 if ( cm_freelanceEnabled &&
2265 tfid.cell==AFS_FAKE_ROOT_CELL_ID &&
2266 tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
2267 !(tfid.vnode==0x1 && tfid.unique==0x1) )
2269 osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
2270 return cm_GetSCache(&tfid, &tscp, NULL, NULL);
2272 #endif /* AFS_FREELANCE_CLIENT */
2275 bsp->fids[i].Volume = scp->fid.volume;
2276 bsp->fids[i].Vnode = tfid.vnode;
2277 bsp->fids[i].Unique = tfid.unique;
2282 cm_TryBulkStatRPC(cm_scache_t *dscp, cm_bulkStat_t *bbp, cm_user_t *userp, cm_req_t *reqp)
2285 AFSCBFids fidStruct;
2286 AFSBulkStats statStruct;
2288 AFSCBs callbackStruct;
2291 cm_callbackRequest_t cbReq;
2297 struct rx_connection * rxconnp;
2298 int inlinebulk = 0; /* Did we use InlineBulkStatus RPC or not? */
2300 memset(&volSync, 0, sizeof(volSync));
2302 /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
2303 * make the calls to create the entries. Handle AFSCBMAX files at a
2306 for (filex = 0; filex < bbp->counter; filex += filesThisCall) {
2307 filesThisCall = bbp->counter - filex;
2308 if (filesThisCall > AFSCBMAX)
2309 filesThisCall = AFSCBMAX;
2311 fidStruct.AFSCBFids_len = filesThisCall;
2312 fidStruct.AFSCBFids_val = &bbp->fids[filex];
2313 statStruct.AFSBulkStats_len = filesThisCall;
2314 statStruct.AFSBulkStats_val = &bbp->stats[filex];
2315 callbackStruct.AFSCBs_len = filesThisCall;
2316 callbackStruct.AFSCBs_val = &bbp->callbacks[filex];
2317 cm_StartCallbackGrantingCall(NULL, &cbReq);
2318 osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
2320 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2324 rxconnp = cm_GetRxConn(connp);
2325 if (!(connp->serverp->flags & CM_SERVERFLAG_NOINLINEBULK)) {
2326 code = RXAFS_InlineBulkStatus(rxconnp, &fidStruct,
2327 &statStruct, &callbackStruct, &volSync);
2328 if (code == RXGEN_OPCODE) {
2329 cm_SetServerNoInlineBulk(connp->serverp, 0);
2335 code = RXAFS_BulkStatus(rxconnp, &fidStruct,
2336 &statStruct, &callbackStruct, &volSync);
2338 rx_PutConnection(rxconnp);
2340 } while (cm_Analyze(connp, userp, reqp, &dscp->fid,
2341 &volSync, NULL, &cbReq, code));
2342 code = cm_MapRPCError(code, reqp);
2344 /* may as well quit on an error, since we're not going to do
2345 * much better on the next immediate call, either.
2348 osi_Log2(afsd_logp, "CALL %sBulkStatus FAILURE code 0x%x",
2349 inlinebulk ? "Inline" : "", code);
2350 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2353 osi_Log1(afsd_logp, "CALL %sBulkStatus SUCCESS", inlinebulk ? "Inline" : "");
2356 /* otherwise, we should do the merges */
2357 for (i = 0; i<filesThisCall; i++) {
2359 cm_SetFid(&tfid, dscp->fid.cell, bbp->fids[j].Volume, bbp->fids[j].Vnode, bbp->fids[j].Unique);
2360 code = cm_GetSCache(&tfid, &scp, userp, reqp);
2364 /* otherwise, if this entry has no callback info,
2367 lock_ObtainWrite(&scp->rw);
2368 /* now, we have to be extra paranoid on merging in this
2369 * information, since we didn't use cm_SyncOp before
2370 * starting the fetch to make sure that no bad races
2371 * were occurring. Specifically, we need to make sure
2372 * we don't obliterate any newer information in the
2373 * vnode than have here.
2375 * Right now, be pretty conservative: if there's a
2376 * callback or a pending call, skip it.
2377 * However, if the prior attempt to obtain status
2378 * was refused access or the volume is .readonly,
2379 * take the data in any case since we have nothing
2380 * better for the in flight directory enumeration that
2381 * resulted in this function being called.
2383 if ((scp->cbServerp == NULL &&
2384 !(scp->flags & (CM_SCACHEFLAG_FETCHING | CM_SCACHEFLAG_STORING | CM_SCACHEFLAG_SIZESTORING))) ||
2385 (scp->flags & CM_SCACHEFLAG_PURERO) ||
2386 (scp->flags & CM_SCACHEFLAG_EACCESS)) {
2387 cm_EndCallbackGrantingCall(scp, &cbReq,
2390 CM_CALLBACK_MAINTAINCOUNT);
2391 cm_MergeStatus(dscp, scp, &bbp->stats[j], &volSync, userp, reqp, 0);
2393 lock_ReleaseWrite(&scp->rw);
2394 cm_ReleaseSCache(scp);
2395 } /* all files in the response */
2396 /* now tell it to drop the count,
2397 * after doing the vnode processing above */
2398 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2399 } /* while there are still more files to process */
2401 /* If we did the InlineBulk RPC pull out the return code and log it */
2403 if ((&bbp->stats[0])->errorCode) {
2404 osi_Log1(afsd_logp, "cm_TryBulkStat bulk stat error: %d",
2405 (&bbp->stats[0])->errorCode);
2406 code = (&bbp->stats[0])->errorCode;
2413 /* called with a write locked scp and a pointer to a buffer. Make bulk stat
2414 * calls on all undeleted files in the page of the directory specified.
2417 cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
2423 osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%p", dscp);
2425 /* should be on a buffer boundary */
2426 osi_assertx((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0, "invalid offset");
2428 bbp = malloc(sizeof(cm_bulkStat_t));
2429 memset(bbp, 0, sizeof(cm_bulkStat_t));
2430 bbp->bufOffset = *offsetp;
2432 lock_ReleaseWrite(&dscp->rw);
2433 /* first, assemble the file IDs we need to stat */
2434 code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) bbp, offsetp, userp, reqp, NULL);
2436 /* if we failed, bail out early */
2437 if (code && code != CM_ERROR_STOPNOW) {
2439 lock_ObtainWrite(&dscp->rw);
2443 code = cm_TryBulkStatRPC(dscp, bbp, userp, reqp);
2444 osi_Log1(afsd_logp, "END cm_TryBulkStat code 0x%x", code);
2446 lock_ObtainWrite(&dscp->rw);
2451 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
2455 /* initialize store back mask as inexpensive local variable */
2457 memset(statusp, 0, sizeof(AFSStoreStatus));
2459 /* copy out queued info from scache first, if scp passed in */
2461 if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
2462 statusp->ClientModTime = scp->clientModTime;
2463 mask |= AFS_SETMODTIME;
2464 scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
2469 /* now add in our locally generated request */
2470 if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
2471 statusp->ClientModTime = attrp->clientModTime;
2472 mask |= AFS_SETMODTIME;
2474 if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
2475 statusp->UnixModeBits = attrp->unixModeBits;
2476 mask |= AFS_SETMODE;
2478 if (attrp->mask & CM_ATTRMASK_OWNER) {
2479 statusp->Owner = attrp->owner;
2480 mask |= AFS_SETOWNER;
2482 if (attrp->mask & CM_ATTRMASK_GROUP) {
2483 statusp->Group = attrp->group;
2484 mask |= AFS_SETGROUP;
2487 statusp->Mask = mask;
2490 /* set the file size, and make sure that all relevant buffers have been
2491 * truncated. Ensure that any partially truncated buffers have been zeroed
2492 * to the end of the buffer.
2494 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
2500 /* start by locking out buffer creation */
2501 lock_ObtainWrite(&scp->bufCreateLock);
2503 /* verify that this is a file, not a dir or a symlink */
2504 lock_ObtainWrite(&scp->rw);
2505 code = cm_SyncOp(scp, NULL, userp, reqp, 0,
2506 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2509 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2511 if (scp->fileType != CM_SCACHETYPE_FILE) {
2512 code = CM_ERROR_ISDIR;
2517 if (LargeIntegerLessThan(*sizep, scp->length))
2522 lock_ReleaseWrite(&scp->rw);
2524 /* can't hold scp->rw lock here, since we may wait for a storeback to
2525 * finish if the buffer package is cleaning a buffer by storing it to
2529 buf_Truncate(scp, userp, reqp, sizep);
2531 /* now ensure that file length is short enough, and update truncPos */
2532 lock_ObtainWrite(&scp->rw);
2534 /* make sure we have a callback (so we have the right value for the
2535 * length), and wait for it to be safe to do a truncate.
2537 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
2538 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2539 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2541 /* If we only have 'i' bits, then we should still be able to set
2542 the size of a file we created. */
2543 if (code == CM_ERROR_NOACCESS && scp->creator == userp) {
2544 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
2545 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2546 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2552 if (LargeIntegerLessThan(*sizep, scp->length)) {
2553 /* a real truncation. If truncPos is not set yet, or is bigger
2554 * than where we're truncating the file, set truncPos to this
2559 if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
2560 || LargeIntegerLessThan(*sizep, scp->length)) {
2562 scp->truncPos = *sizep;
2563 scp->mask |= CM_SCACHEMASK_TRUNCPOS;
2565 /* in either case, the new file size has been changed */
2566 scp->length = *sizep;
2567 scp->mask |= CM_SCACHEMASK_LENGTH;
2569 else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
2570 /* really extending the file */
2571 scp->length = *sizep;
2572 scp->mask |= CM_SCACHEMASK_LENGTH;
2575 /* done successfully */
2578 cm_SyncOpDone(scp, NULL,
2579 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2580 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2583 lock_ReleaseWrite(&scp->rw);
2584 lock_ReleaseWrite(&scp->bufCreateLock);
2589 /* set the file size or other attributes (but not both at once) */
2590 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
2594 AFSFetchStatus afsOutStatus;
2598 AFSStoreStatus afsInStatus;
2599 struct rx_connection * rxconnp;
2601 memset(&volSync, 0, sizeof(volSync));
2603 /* handle file length setting */
2604 if (attrp->mask & CM_ATTRMASK_LENGTH)
2605 return cm_SetLength(scp, &attrp->length, userp, reqp);
2607 lock_ObtainWrite(&scp->rw);
2608 /* otherwise, we have to make an RPC to get the status */
2609 code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
2611 lock_ReleaseWrite(&scp->rw);
2614 lock_ConvertWToR(&scp->rw);
2616 /* make the attr structure */
2617 cm_StatusFromAttr(&afsInStatus, scp, attrp);
2619 tfid.Volume = scp->fid.volume;
2620 tfid.Vnode = scp->fid.vnode;
2621 tfid.Unique = scp->fid.unique;
2622 lock_ReleaseRead(&scp->rw);
2624 /* now make the RPC */
2625 osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%p", scp);
2627 code = cm_ConnFromFID(&scp->fid, userp, reqp, &connp);
2631 rxconnp = cm_GetRxConn(connp);
2632 code = RXAFS_StoreStatus(rxconnp, &tfid,
2633 &afsInStatus, &afsOutStatus, &volSync);
2634 rx_PutConnection(rxconnp);
2636 } while (cm_Analyze(connp, userp, reqp,
2637 &scp->fid, &volSync, NULL, NULL, code));
2638 code = cm_MapRPCError(code, reqp);
2641 osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
2643 osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
2645 lock_ObtainWrite(&scp->rw);
2646 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
2648 cm_MergeStatus(NULL, scp, &afsOutStatus, &volSync, userp, reqp,
2649 CM_MERGEFLAG_FORCE|CM_MERGEFLAG_STOREDATA);
2651 /* if we're changing the mode bits, discard the ACL cache,
2652 * since we changed the mode bits.
2654 if (afsInStatus.Mask & AFS_SETMODE)
2655 cm_FreeAllACLEnts(scp);
2656 lock_ReleaseWrite(&scp->rw);
2660 long cm_Create(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
2661 cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
2666 cm_callbackRequest_t cbReq;
2669 cm_scache_t *scp = NULL;
2671 AFSStoreStatus inStatus;
2672 AFSFetchStatus updatedDirStatus;
2673 AFSFetchStatus newFileStatus;
2674 AFSCallBack newFileCallback;
2676 struct rx_connection * rxconnp;
2678 fschar_t * fnamep = NULL;
2680 memset(&volSync, 0, sizeof(volSync));
2682 /* can't create names with @sys in them; must expand it manually first.
2683 * return "invalid request" if they try.
2685 if (cm_ExpandSysName(cnamep, NULL, 0, 0)) {
2686 return CM_ERROR_ATSYS;
2689 #ifdef AFS_FREELANCE_CLIENT
2690 /* Freelance root volume does not hold files */
2691 if (cm_freelanceEnabled &&
2692 dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
2693 dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
2695 return CM_ERROR_NOACCESS;
2697 #endif /* AFS_FREELANCE_CLIENT */
2699 /* before starting the RPC, mark that we're changing the file data, so
2700 * that someone who does a chmod will know to wait until our call
2703 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2704 lock_ObtainWrite(&dscp->rw);
2705 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2706 lock_ReleaseWrite(&dscp->rw);
2708 cm_StartCallbackGrantingCall(NULL, &cbReq);
2710 cm_EndDirOp(&dirop);
2717 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
2719 cm_StatusFromAttr(&inStatus, NULL, attrp);
2721 /* try the RPC now */
2722 osi_Log1(afsd_logp, "CALL CreateFile scp 0x%p", dscp);
2724 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2728 dirAFSFid.Volume = dscp->fid.volume;
2729 dirAFSFid.Vnode = dscp->fid.vnode;
2730 dirAFSFid.Unique = dscp->fid.unique;
2732 rxconnp = cm_GetRxConn(connp);
2733 code = RXAFS_CreateFile(connp->rxconnp, &dirAFSFid, fnamep,
2734 &inStatus, &newAFSFid, &newFileStatus,
2735 &updatedDirStatus, &newFileCallback,
2737 rx_PutConnection(rxconnp);
2739 } while (cm_Analyze(connp, userp, reqp,
2740 &dscp->fid, &volSync, NULL, &cbReq, code));
2741 code = cm_MapRPCError(code, reqp);
2744 osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
2746 osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
2749 lock_ObtainWrite(&dirop.scp->dirlock);
2750 dirop.lockType = CM_DIRLOCK_WRITE;
2752 lock_ObtainWrite(&dscp->rw);
2753 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2755 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
2757 lock_ReleaseWrite(&dscp->rw);
2759 /* now try to create the file's entry, too, but be careful to
2760 * make sure that we don't merge in old info. Since we weren't locking
2761 * out any requests during the file's creation, we may have pretty old
2765 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
2766 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2768 lock_ObtainWrite(&scp->rw);
2769 scp->creator = userp; /* remember who created it */
2770 if (!cm_HaveCallback(scp)) {
2771 cm_EndCallbackGrantingCall(scp, &cbReq,
2772 &newFileCallback, &volSync, 0);
2773 cm_MergeStatus(dscp, scp, &newFileStatus, &volSync,
2777 lock_ReleaseWrite(&scp->rw);
2781 /* make sure we end things properly */
2783 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2785 if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
2786 cm_DirCreateEntry(&dirop, fnamep, &newFid);
2788 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
2791 cm_EndDirOp(&dirop);
2800 cm_ReleaseSCache(scp);
2806 * locked if TRUE means write-locked
2807 * else the cm_scache_t rw must not be held
2809 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp, afs_uint32 locked)
2814 lock_ReleaseWrite(&scp->rw);
2815 code = buf_CleanVnode(scp, userp, reqp);
2817 lock_ObtainWrite(&scp->rw);
2819 if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
2820 | CM_SCACHEMASK_CLIENTMODTIME
2821 | CM_SCACHEMASK_LENGTH))
2822 code = cm_StoreMini(scp, userp, reqp);
2824 if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) {
2825 code = (scp->flags & CM_SCACHEFLAG_OVERQUOTA) ? CM_ERROR_QUOTA : CM_ERROR_SPACE;
2826 scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE);
2830 lock_ReleaseWrite(&scp->rw);
2831 } else if (locked) {
2832 lock_ObtainWrite(&scp->rw);
2837 long cm_MakeDir(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
2838 cm_user_t *userp, cm_req_t *reqp, cm_scache_t **scpp)
2843 cm_callbackRequest_t cbReq;
2846 cm_scache_t *scp = NULL;
2848 AFSStoreStatus inStatus;
2849 AFSFetchStatus updatedDirStatus;
2850 AFSFetchStatus newDirStatus;
2851 AFSCallBack newDirCallback;
2853 struct rx_connection * rxconnp;
2855 fschar_t * fnamep = NULL;
2857 memset(&volSync, 0, sizeof(volSync));
2859 /* can't create names with @sys in them; must expand it manually first.
2860 * return "invalid request" if they try.
2862 if (cm_ExpandSysName(cnamep, NULL, 0, 0)) {
2863 return CM_ERROR_ATSYS;
2866 #ifdef AFS_FREELANCE_CLIENT
2867 /* Freelance root volume does not hold subdirectories */
2868 if (cm_freelanceEnabled &&
2869 dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
2870 dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
2872 return CM_ERROR_NOACCESS;
2874 #endif /* AFS_FREELANCE_CLIENT */
2876 /* before starting the RPC, mark that we're changing the directory
2877 * data, so that someone who does a chmod on the dir will wait until
2878 * our call completes.
2880 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2881 lock_ObtainWrite(&dscp->rw);
2882 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2883 lock_ReleaseWrite(&dscp->rw);
2885 cm_StartCallbackGrantingCall(NULL, &cbReq);
2887 cm_EndDirOp(&dirop);
2894 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
2895 cm_StatusFromAttr(&inStatus, NULL, attrp);
2897 /* try the RPC now */
2898 osi_Log1(afsd_logp, "CALL MakeDir scp 0x%p", dscp);
2900 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2904 dirAFSFid.Volume = dscp->fid.volume;
2905 dirAFSFid.Vnode = dscp->fid.vnode;
2906 dirAFSFid.Unique = dscp->fid.unique;
2908 rxconnp = cm_GetRxConn(connp);
2909 code = RXAFS_MakeDir(connp->rxconnp, &dirAFSFid, fnamep,
2910 &inStatus, &newAFSFid, &newDirStatus,
2911 &updatedDirStatus, &newDirCallback,
2913 rx_PutConnection(rxconnp);
2915 } while (cm_Analyze(connp, userp, reqp,
2916 &dscp->fid, &volSync, NULL, &cbReq, code));
2917 code = cm_MapRPCError(code, reqp);
2920 osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
2922 osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
2925 lock_ObtainWrite(&dirop.scp->dirlock);
2926 dirop.lockType = CM_DIRLOCK_WRITE;
2928 lock_ObtainWrite(&dscp->rw);
2929 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2931 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
2933 lock_ReleaseWrite(&dscp->rw);
2935 /* now try to create the new dir's entry, too, but be careful to
2936 * make sure that we don't merge in old info. Since we weren't locking
2937 * out any requests during the file's creation, we may have pretty old
2941 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
2942 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2944 lock_ObtainWrite(&scp->rw);
2945 if (!cm_HaveCallback(scp)) {
2946 cm_EndCallbackGrantingCall(scp, &cbReq,
2947 &newDirCallback, &volSync, 0);
2948 cm_MergeStatus(dscp, scp, &newDirStatus, &volSync,
2952 lock_ReleaseWrite(&scp->rw);
2956 /* make sure we end things properly */
2958 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2960 if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
2961 cm_DirCreateEntry(&dirop, fnamep, &newFid);
2963 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
2966 cm_EndDirOp(&dirop);
2974 cm_ReleaseSCache(scp);
2977 /* and return error code */
2981 long cm_Link(cm_scache_t *dscp, clientchar_t *cnamep, cm_scache_t *sscp, long flags,
2982 cm_user_t *userp, cm_req_t *reqp)
2987 AFSFid existingAFSFid;
2988 AFSFetchStatus updatedDirStatus;
2989 AFSFetchStatus newLinkStatus;
2991 struct rx_connection * rxconnp;
2993 fschar_t * fnamep = NULL;
2995 memset(&volSync, 0, sizeof(volSync));
2997 if (dscp->fid.cell != sscp->fid.cell ||
2998 dscp->fid.volume != sscp->fid.volume) {
2999 return CM_ERROR_CROSSDEVLINK;
3002 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
3003 lock_ObtainWrite(&dscp->rw);
3004 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3005 lock_ReleaseWrite(&dscp->rw);
3007 cm_EndDirOp(&dirop);
3012 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3014 /* try the RPC now */
3015 osi_Log1(afsd_logp, "CALL Link scp 0x%p", dscp);
3017 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3020 dirAFSFid.Volume = dscp->fid.volume;
3021 dirAFSFid.Vnode = dscp->fid.vnode;
3022 dirAFSFid.Unique = dscp->fid.unique;
3024 existingAFSFid.Volume = sscp->fid.volume;
3025 existingAFSFid.Vnode = sscp->fid.vnode;
3026 existingAFSFid.Unique = sscp->fid.unique;
3028 rxconnp = cm_GetRxConn(connp);
3029 code = RXAFS_Link(rxconnp, &dirAFSFid, fnamep, &existingAFSFid,
3030 &newLinkStatus, &updatedDirStatus, &volSync);
3031 rx_PutConnection(rxconnp);
3032 osi_Log1(afsd_logp," RXAFS_Link returns 0x%x", code);
3034 } while (cm_Analyze(connp, userp, reqp,
3035 &dscp->fid, &volSync, NULL, NULL, code));
3037 code = cm_MapRPCError(code, reqp);
3040 osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
3042 osi_Log0(afsd_logp, "CALL Link SUCCESS");
3045 lock_ObtainWrite(&dirop.scp->dirlock);
3046 dirop.lockType = CM_DIRLOCK_WRITE;
3048 lock_ObtainWrite(&dscp->rw);
3049 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3051 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3053 lock_ReleaseWrite(&dscp->rw);
3056 if (cm_CheckDirOpForSingleChange(&dirop)) {
3057 cm_DirCreateEntry(&dirop, fnamep, &sscp->fid);
3059 cm_BPlusDirCreateEntry(&dirop, cnamep, &sscp->fid);
3063 cm_EndDirOp(&dirop);
3065 /* Update the linked object status */
3067 lock_ObtainWrite(&sscp->rw);
3068 cm_MergeStatus(NULL, sscp, &newLinkStatus, &volSync, userp, reqp, 0);
3069 lock_ReleaseWrite(&sscp->rw);
3077 long cm_SymLink(cm_scache_t *dscp, clientchar_t *cnamep, fschar_t *contentsp, long flags,
3078 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
3086 AFSStoreStatus inStatus;
3087 AFSFetchStatus updatedDirStatus;
3088 AFSFetchStatus newLinkStatus;
3090 struct rx_connection * rxconnp;
3092 fschar_t *fnamep = NULL;
3094 memset(&volSync, 0, sizeof(volSync));
3096 /* before starting the RPC, mark that we're changing the directory data,
3097 * so that someone who does a chmod on the dir will wait until our
3100 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
3101 lock_ObtainWrite(&dscp->rw);
3102 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3103 lock_ReleaseWrite(&dscp->rw);
3105 cm_EndDirOp(&dirop);
3110 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3112 cm_StatusFromAttr(&inStatus, NULL, attrp);
3114 /* try the RPC now */
3115 osi_Log1(afsd_logp, "CALL Symlink scp 0x%p", dscp);
3117 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3121 dirAFSFid.Volume = dscp->fid.volume;
3122 dirAFSFid.Vnode = dscp->fid.vnode;
3123 dirAFSFid.Unique = dscp->fid.unique;
3125 rxconnp = cm_GetRxConn(connp);
3126 code = RXAFS_Symlink(rxconnp, &dirAFSFid, fnamep, contentsp,
3127 &inStatus, &newAFSFid, &newLinkStatus,
3128 &updatedDirStatus, &volSync);
3129 rx_PutConnection(rxconnp);
3131 } while (cm_Analyze(connp, userp, reqp,
3132 &dscp->fid, &volSync, NULL, NULL, code));
3133 code = cm_MapRPCError(code, reqp);
3136 osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
3138 osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
3141 lock_ObtainWrite(&dirop.scp->dirlock);
3142 dirop.lockType = CM_DIRLOCK_WRITE;
3144 lock_ObtainWrite(&dscp->rw);
3145 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3147 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3149 lock_ReleaseWrite(&dscp->rw);
3152 if (cm_CheckDirOpForSingleChange(&dirop)) {
3153 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3155 cm_DirCreateEntry(&dirop, fnamep, &newFid);
3157 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
3161 cm_EndDirOp(&dirop);
3163 /* now try to create the new dir's entry, too, but be careful to
3164 * make sure that we don't merge in old info. Since we weren't locking
3165 * out any requests during the file's creation, we may have pretty old
3169 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3170 code = cm_GetSCache(&newFid, &scp, userp, reqp);
3172 lock_ObtainWrite(&scp->rw);
3173 if (!cm_HaveCallback(scp)) {
3174 cm_MergeStatus(dscp, scp, &newLinkStatus, &volSync,
3177 lock_ReleaseWrite(&scp->rw);
3178 cm_ReleaseSCache(scp);
3184 /* and return error code */
3188 /*! \brief Remove a directory
3190 Encapsulates a call to RXAFS_RemoveDir().
3192 \param[in] dscp cm_scache_t for the directory containing the
3193 directory to be removed.
3195 \param[in] fnamep This will be the original name of the directory
3196 as known to the file server. It will be passed in to RXAFS_RemoveDir().
3197 This parameter is optional. If it is not provided the value
3200 \param[in] cnamep Normalized name used to update the local
3203 \param[in] userp cm_user_t for the request.
3205 \param[in] reqp Request tracker.
3207 long cm_RemoveDir(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t *cnamep, cm_user_t *userp, cm_req_t *reqp)
3213 AFSFetchStatus updatedDirStatus;
3215 struct rx_connection * rxconnp;
3217 cm_scache_t *scp = NULL;
3218 int free_fnamep = FALSE;
3220 memset(&volSync, 0, sizeof(volSync));
3222 if (fnamep == NULL) {
3225 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ, &dirop);
3227 code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
3230 cm_EndDirOp(&dirop);
3237 code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
3241 /* before starting the RPC, mark that we're changing the directory data,
3242 * so that someone who does a chmod on the dir will wait until our
3245 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
3246 lock_ObtainWrite(&dscp->rw);
3247 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3248 lock_ReleaseWrite(&dscp->rw);
3250 cm_EndDirOp(&dirop);
3255 /* try the RPC now */
3256 osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%p", dscp);
3258 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3262 dirAFSFid.Volume = dscp->fid.volume;
3263 dirAFSFid.Vnode = dscp->fid.vnode;
3264 dirAFSFid.Unique = dscp->fid.unique;
3266 rxconnp = cm_GetRxConn(connp);
3267 code = RXAFS_RemoveDir(rxconnp, &dirAFSFid, fnamep,
3268 &updatedDirStatus, &volSync);
3269 rx_PutConnection(rxconnp);
3271 } while (cm_Analyze(connp, userp, reqp,
3272 &dscp->fid, &volSync, NULL, NULL, code));
3273 code = cm_MapRPCErrorRmdir(code, reqp);
3276 osi_Log1(afsd_logp, "CALL RemoveDir FAILURE, code 0x%x", code);
3278 osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
3281 lock_ObtainWrite(&dirop.scp->dirlock);
3282 dirop.lockType = CM_DIRLOCK_WRITE;
3284 lock_ObtainWrite(&dscp->rw);
3285 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3287 cm_dnlcRemove(dscp, cnamep);
3288 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3290 lock_ReleaseWrite(&dscp->rw);
3293 if (cm_CheckDirOpForSingleChange(&dirop) && cnamep != NULL) {
3294 cm_DirDeleteEntry(&dirop, fnamep);
3296 cm_BPlusDirDeleteEntry(&dirop, cnamep);
3300 cm_EndDirOp(&dirop);
3303 cm_ReleaseSCache(scp);
3305 lock_ObtainWrite(&scp->rw);
3306 scp->flags |= CM_SCACHEFLAG_DELETED;
3307 lock_ReleaseWrite(&scp->rw);
3315 /* and return error code */
3319 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
3321 /* grab mutex on contents */
3322 lock_ObtainWrite(&scp->rw);
3324 /* reset the prefetch info */
3325 scp->prefetch.base.LowPart = 0; /* base */
3326 scp->prefetch.base.HighPart = 0;
3327 scp->prefetch.end.LowPart = 0; /* and end */
3328 scp->prefetch.end.HighPart = 0;
3330 /* release mutex on contents */
3331 lock_ReleaseWrite(&scp->rw);
3337 /*! \brief Rename a file or directory
3339 Encapsulates a RXAFS_Rename() call.
3341 \param[in] oldDscp cm_scache_t for the directory containing the old
3344 \param[in] oldNamep The original old name known to the file server.
3345 This is the name that will be passed into the RXAFS_Rename().
3346 If it is not provided, it will be looked up.
3348 \param[in] normalizedOldNamep Normalized old name. This is used for
3349 updating local directory caches.
3351 \param[in] newDscp cm_scache_t for the directory containing the new
3354 \param[in] newNamep New name. Normalized.
3356 \param[in] userp cm_user_t for the request.
3358 \param[in,out] reqp Request tracker.
3361 long cm_Rename(cm_scache_t *oldDscp, fschar_t *oldNamep, clientchar_t *cOldNamep,
3362 cm_scache_t *newDscp, clientchar_t *cNewNamep, cm_user_t *userp,
3367 AFSFid oldDirAFSFid;
3368 AFSFid newDirAFSFid;
3370 AFSFetchStatus updatedOldDirStatus;
3371 AFSFetchStatus updatedNewDirStatus;
3374 struct rx_connection * rxconnp;
3375 cm_dirOp_t oldDirOp;
3378 cm_dirOp_t newDirOp;
3379 fschar_t * newNamep = NULL;
3380 int free_oldNamep = FALSE;
3381 cm_scache_t *oldScp = NULL, *newScp = NULL;
3383 memset(&volSync, 0, sizeof(volSync));
3385 if (cOldNamep == NULL || cNewNamep == NULL ||
3386 cm_ClientStrLen(cOldNamep) == 0 ||
3387 cm_ClientStrLen(cNewNamep) == 0)
3388 return CM_ERROR_INVAL;
3391 * Before we permit the operation, make sure that we do not already have
3392 * an object in the destination directory that has a case-insensitive match
3393 * for this name UNLESS the matching object is the object we are renaming.
3395 code = cm_Lookup(oldDscp, cOldNamep, 0, userp, reqp, &oldScp);
3397 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S old name lookup failed",
3398 oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3402 code = cm_Lookup(newDscp, cNewNamep, CM_FLAG_CASEFOLD, userp, reqp, &newScp);
3404 /* found a matching object with the new name */
3405 if (cm_FidCmp(&oldScp->fid, &newScp->fid)) {
3406 /* and they don't match so return an error */
3407 osi_Log2(afsd_logp, "cm_Rename newDscp 0x%p cNewName %S new name already exists",
3408 newDscp, osi_LogSaveStringW(afsd_logp, cNewNamep));
3409 code = CM_ERROR_EXISTS;
3411 cm_ReleaseSCache(newScp);
3413 } else if (code == CM_ERROR_AMBIGUOUS_FILENAME) {
3414 code = CM_ERROR_EXISTS;
3421 if (oldNamep == NULL) {
3424 code = cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_READ, &oldDirOp);
3426 code = cm_BPlusDirLookupOriginalName(&oldDirOp, cOldNamep, &oldNamep);
3428 free_oldNamep = TRUE;
3429 cm_EndDirOp(&oldDirOp);
3433 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S Original Name lookup failed",
3434 oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3440 /* before starting the RPC, mark that we're changing the directory data,
3441 * so that someone who does a chmod on the dir will wait until our call
3442 * completes. We do this in vnode order so that we don't deadlock,
3443 * which makes the code a little verbose.
3445 if (oldDscp == newDscp) {
3446 /* check for identical names */
3447 if (cm_ClientStrCmp(cOldNamep, cNewNamep) == 0) {
3448 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_RENAME_IDENTICAL",
3450 code = CM_ERROR_RENAME_IDENTICAL;
3455 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3456 lock_ObtainWrite(&oldDscp->rw);
3457 cm_dnlcRemove(oldDscp, cOldNamep);
3458 cm_dnlcRemove(oldDscp, cNewNamep);
3459 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3460 CM_SCACHESYNC_STOREDATA);
3461 lock_ReleaseWrite(&oldDscp->rw);
3463 cm_EndDirOp(&oldDirOp);
3467 /* two distinct dir vnodes */
3469 if (oldDscp->fid.cell != newDscp->fid.cell ||
3470 oldDscp->fid.volume != newDscp->fid.volume) {
3471 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_CROSSDEVLINK",
3473 code = CM_ERROR_CROSSDEVLINK;
3477 /* shouldn't happen that we have distinct vnodes for two
3478 * different files, but could due to deliberate attack, or
3479 * stale info. Avoid deadlocks and quit now.
3481 if (oldDscp->fid.vnode == newDscp->fid.vnode) {
3482 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p vnode collision",
3484 code = CM_ERROR_CROSSDEVLINK;
3488 if (oldDscp->fid.vnode < newDscp->fid.vnode) {
3489 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3490 lock_ObtainWrite(&oldDscp->rw);
3491 cm_dnlcRemove(oldDscp, cOldNamep);
3492 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3493 CM_SCACHESYNC_STOREDATA);
3494 lock_ReleaseWrite(&oldDscp->rw);
3496 cm_EndDirOp(&oldDirOp);
3498 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE, &newDirOp);
3499 lock_ObtainWrite(&newDscp->rw);
3500 cm_dnlcRemove(newDscp, cNewNamep);
3501 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3502 CM_SCACHESYNC_STOREDATA);
3503 lock_ReleaseWrite(&newDscp->rw);
3505 cm_EndDirOp(&newDirOp);
3507 /* cleanup first one */
3508 lock_ObtainWrite(&oldDscp->rw);
3509 cm_SyncOpDone(oldDscp, NULL,
3510 CM_SCACHESYNC_STOREDATA);
3511 lock_ReleaseWrite(&oldDscp->rw);
3512 cm_EndDirOp(&oldDirOp);
3517 /* lock the new vnode entry first */
3518 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE, &newDirOp);
3519 lock_ObtainWrite(&newDscp->rw);
3520 cm_dnlcRemove(newDscp, cNewNamep);
3521 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3522 CM_SCACHESYNC_STOREDATA);
3523 lock_ReleaseWrite(&newDscp->rw);
3525 cm_EndDirOp(&newDirOp);
3527 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3528 lock_ObtainWrite(&oldDscp->rw);
3529 cm_dnlcRemove(oldDscp, cOldNamep);
3530 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3531 CM_SCACHESYNC_STOREDATA);
3532 lock_ReleaseWrite(&oldDscp->rw);
3534 cm_EndDirOp(&oldDirOp);
3536 /* cleanup first one */
3537 lock_ObtainWrite(&newDscp->rw);
3538 cm_SyncOpDone(newDscp, NULL,
3539 CM_SCACHESYNC_STOREDATA);
3540 lock_ReleaseWrite(&newDscp->rw);
3541 cm_EndDirOp(&newDirOp);
3545 } /* two distinct vnodes */
3552 newNamep = cm_ClientStringToFsStringAlloc(cNewNamep, -1, NULL);
3554 /* try the RPC now */
3555 osi_Log2(afsd_logp, "CALL Rename old scp 0x%p new scp 0x%p",
3558 code = cm_ConnFromFID(&oldDscp->fid, userp, reqp, &connp);
3562 oldDirAFSFid.Volume = oldDscp->fid.volume;
3563 oldDirAFSFid.Vnode = oldDscp->fid.vnode;
3564 oldDirAFSFid.Unique = oldDscp->fid.unique;
3565 newDirAFSFid.Volume = newDscp->fid.volume;
3566 newDirAFSFid.Vnode = newDscp->fid.vnode;
3567 newDirAFSFid.Unique = newDscp->fid.unique;
3569 rxconnp = cm_GetRxConn(connp);
3570 code = RXAFS_Rename(rxconnp, &oldDirAFSFid, oldNamep,
3571 &newDirAFSFid, newNamep,
3572 &updatedOldDirStatus, &updatedNewDirStatus,
3574 rx_PutConnection(rxconnp);
3576 } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
3577 &volSync, NULL, NULL, code));
3578 code = cm_MapRPCError(code, reqp);
3581 osi_Log1(afsd_logp, "CALL Rename FAILURE, code 0x%x", code);
3583 osi_Log0(afsd_logp, "CALL Rename SUCCESS");
3585 /* update the individual stat cache entries for the directories */
3587 lock_ObtainWrite(&oldDirOp.scp->dirlock);
3588 oldDirOp.lockType = CM_DIRLOCK_WRITE;
3590 lock_ObtainWrite(&oldDscp->rw);
3591 cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
3594 cm_MergeStatus(NULL, oldDscp, &updatedOldDirStatus, &volSync,
3595 userp, reqp, CM_MERGEFLAG_DIROP);
3596 lock_ReleaseWrite(&oldDscp->rw);
3598 if (code == 0 && cm_CheckDirOpForSingleChange(&oldDirOp)) {
3600 diropCode = cm_BPlusDirLookup(&oldDirOp, cOldNamep, &fileFid);
3601 if (diropCode == CM_ERROR_INEXACT_MATCH)
3603 else if (diropCode == EINVAL)
3605 diropCode = cm_DirLookup(&oldDirOp, oldNamep, &fileFid);
3607 if (diropCode == 0) {
3609 diropCode = cm_DirCreateEntry(&oldDirOp, newNamep, &fileFid);
3611 cm_BPlusDirCreateEntry(&oldDirOp, cNewNamep, &fileFid);
3615 if (diropCode == 0) {
3616 diropCode = cm_DirDeleteEntry(&oldDirOp, oldNamep);
3618 cm_BPlusDirDeleteEntry(&oldDirOp, cOldNamep);
3623 cm_EndDirOp(&oldDirOp);
3625 /* and update it for the new one, too, if necessary */
3628 lock_ObtainWrite(&newDirOp.scp->dirlock);
3629 newDirOp.lockType = CM_DIRLOCK_WRITE;
3631 lock_ObtainWrite(&newDscp->rw);
3632 cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
3634 cm_MergeStatus(NULL, newDscp, &updatedNewDirStatus, &volSync,
3635 userp, reqp, CM_MERGEFLAG_DIROP);
3636 lock_ReleaseWrite(&newDscp->rw);
3640 * The following optimization does not work.
3641 * When the file server processed a RXAFS_Rename() request the
3642 * FID of the object being moved between directories is not
3643 * preserved. The client does not know the new FID nor the
3644 * version number of the target. Not only can we not create
3645 * the directory entry in the new directory, but we can't
3646 * preserve the cached data for the file. It must be re-read
3647 * from the file server. - jaltman, 2009/02/20
3650 /* we only make the local change if we successfully made
3651 the change in the old directory AND there was only one
3652 change in the new directory */
3653 if (diropCode == 0 && cm_CheckDirOpForSingleChange(&newDirOp)) {
3654 cm_DirCreateEntry(&newDirOp, newNamep, &fileFid);
3656 cm_BPlusDirCreateEntry(&newDirOp, cNewNamep, &fileFid);
3661 cm_EndDirOp(&newDirOp);
3665 * After the rename the file server has invalidated the callbacks
3666 * on the file that was moved nor do we have a directory reference
3669 lock_ObtainWrite(&oldScp->rw);
3670 cm_DiscardSCache(oldScp);
3671 lock_ReleaseWrite(&oldScp->rw);
3675 cm_ReleaseSCache(oldScp);
3682 /* and return error code */
3686 /* Byte range locks:
3688 The OpenAFS Windows client has to fake byte range locks given no
3689 server side support for such locks. This is implemented as keyed
3690 byte range locks on the cache manager.
3692 Keyed byte range locks:
3694 Each cm_scache_t structure keeps track of a list of keyed locks.
3695 The key for a lock identifies an owner of a set of locks (referred
3696 to as a client). Each key is represented by a value. The set of
3697 key values used within a specific cm_scache_t structure form a
3698 namespace that has a scope of just that cm_scache_t structure. The
3699 same key value can be used with another cm_scache_t structure and
3700 correspond to a completely different client. However it is
3701 advantageous for the SMB or IFS layer to make sure that there is a
3702 1-1 mapping between client and keys over all cm_scache_t objects.
3704 Assume a client C has key Key(C) (although, since the scope of the
3705 key is a cm_scache_t, the key can be Key(C,S), where S is the
3706 cm_scache_t. But assume a 1-1 relation between keys and clients).
3707 A byte range (O,+L) denotes byte addresses (O) through (O+L-1)
3708 inclusive (a.k.a. [O,O+L-1]). The function Key(x) is implemented
3709 through cm_generateKey() function for both SMB and IFS.
3711 The list of locks for a cm_scache_t object S is maintained in
3712 S->fileLocks. The cache manager will set a lock on the AFS file
3713 server in order to assert the locks in S->fileLocks. If only
3714 shared locks are in place for S, then the cache manager will obtain
3715 a LockRead lock, while if there are any exclusive locks, it will
3716 obtain a LockWrite lock. If the exclusive locks are all released
3717 while the shared locks remain, then the cache manager will
3718 downgrade the lock from LockWrite to LockRead. Similarly, if an
3719 exclusive lock is obtained when only shared locks exist, then the
3720 cache manager will try to upgrade the lock from LockRead to
3723 Each lock L owned by client C maintains a key L->key such that
3724 L->key == Key(C), the effective range defined by L->LOffset and
3725 L->LLength such that the range of bytes affected by the lock is
3726 (L->LOffset, +L->LLength), a type maintained in L->LockType which
3727 is either exclusive or shared.
3731 A lock exists iff it is in S->fileLocks for some cm_scache_t
3732 S. Existing locks are in one of the following states: ACTIVE,
3733 WAITLOCK, WAITUNLOCK, LOST, DELETED.
3735 The following sections describe each lock and the associated
3738 1. ACTIVE: A lock L is ACTIVE iff the cache manager has asserted
3739 the lock with the AFS file server. This type of lock can be
3740 exercised by a client to read or write to the locked region (as
3743 1.1 ACTIVE->LOST: When the AFS file server fails to extend a
3744 server lock that was required to assert the lock. Before
3745 marking the lock as lost, the cache manager checks if the file
3746 has changed on the server. If the file has not changed, then
3747 the cache manager will attempt to obtain a new server lock
3748 that is sufficient to assert the client side locks for the
3749 file. If any of these fail, the lock is marked as LOST.
3750 Otherwise, it is left as ACTIVE.
3752 1.2 ACTIVE->DELETED: Lock is released.
3754 2. WAITLOCK: A lock is in a WAITLOCK state if the cache manager
3755 grants the lock but the lock is yet to be asserted with the AFS
3756 file server. Once the file server grants the lock, the state
3757 will transition to an ACTIVE lock.
3759 2.1 WAITLOCK->ACTIVE: The server granted the lock.
3761 2.2 WAITLOCK->DELETED: Lock is abandoned, or timed out during
3764 2.3 WAITLOCK->LOST: One or more locks from this client were
3765 marked as LOST. No further locks will be granted to this
3766 client until all lost locks are removed.
3768 3. WAITUNLOCK: A lock is in a WAITUNLOCK state if the cache manager
3769 receives a request for a lock that conflicts with an existing
3770 ACTIVE or WAITLOCK lock. The lock will be placed in the queue
3771 and will be granted at such time the conflicting locks are
3772 removed, at which point the state will transition to either
3775 3.1 WAITUNLOCK->ACTIVE: The conflicting lock was removed. The
3776 current serverLock is sufficient to assert this lock, or a
3777 sufficient serverLock is obtained.
3779 3.2 WAITUNLOCK->WAITLOCK: The conflicting lock was removed,
3780 however the required serverLock is yet to be asserted with the
3783 3.3 WAITUNLOCK->DELETED: The lock is abandoned, timed out or
3786 3.5 WAITUNLOCK->LOST: One or more locks from this client were
3787 marked as LOST. No further locks will be granted to this
3788 client until all lost locks are removed.
3790 4. LOST: A lock L is LOST if the server lock that was required to
3791 assert the lock could not be obtained or if it could not be
3792 extended, or if other locks by the same client were LOST.
3793 Essentially, once a lock is LOST, the contract between the cache
3794 manager and that specific client is no longer valid.
3796 The cache manager rechecks the server lock once every minute and
3797 extends it as appropriate. If this is not done for 5 minutes,
3798 the AFS file server will release the lock (the 5 minute timeout
3799 is based on current file server code and is fairly arbitrary).
3800 Once released, the lock cannot be re-obtained without verifying
3801 that the contents of the file hasn't been modified since the
3802 time the lock was released. Re-obtaining the lock without
3803 verifying this may lead to data corruption. If the lock can not
3804 be obtained safely, then all active locks for the cm_scache_t
3807 4.1 LOST->DELETED: The lock is released.
3809 5. DELETED: The lock is no longer relevant. Eventually, it will
3810 get removed from the cm_scache_t. In the meantime, it will be
3811 treated as if it does not exist.
3813 5.1 DELETED->not exist: The lock is removed from the
3816 The following are classifications of locks based on their state.
3818 6* A lock L is ACCEPTED if it is ACTIVE or WAITLOCK. These locks
3819 have been accepted by the cache manager, but may or may not have
3820 been granted back to the client.
3822 7* A lock L is QUEUED if it is ACTIVE, WAITLOCK or WAITUNLOCK.
3824 8* A lock L is WAITING if it is WAITLOCK or WAITUNLOCK.
3828 A client C can READ range (Offset,+Length) of a file represented by
3829 cm_scache_t S iff (1):
3831 1. for all _a_ in (Offset,+Length), all of the following is true:
3833 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3834 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3837 1.2 For each LOST lock L in S->fileLocks such that _a_ in
3838 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3841 (When locks are lost on an cm_scache_t, all locks are lost. By
3842 4.2 (below), if there is an exclusive LOST lock, then there
3843 can't be any overlapping ACTIVE locks.)
3845 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
3847 2. for all _a_ in (Offset,+Length), one of the following is true:
3849 2.1 Byte _a_ of S is unowned (as specified in 1.1) AND there
3850 does not exist a LOST lock L such that _a_ in
3851 (L->LOffset,+L->LLength).
3853 2.2 Byte _a_ of S is owned by C under lock L (as specified in
3854 1.2) AND L->LockType is exclusive.
3856 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
3858 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
3861 3.1 If L->LockType is exclusive then there does NOT exist a
3862 ACCEPTED lock M in S->fileLocks such that _a_ in
3863 (M->LOffset,+M->LLength).
3865 (If we count all QUEUED locks then we hit cases such as
3866 cascading waiting locks where the locks later on in the queue
3867 can be granted without compromising file integrity. On the
3868 other hand if only ACCEPTED locks are considered, then locks
3869 that were received earlier may end up waiting for locks that
3870 were received later to be unlocked. The choice of ACCEPTED
3871 locks was made to mimic the Windows byte range lock
3874 3.2 If L->LockType is shared then for each ACCEPTED lock M in
3875 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
3876 M->LockType is shared.
3878 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
3880 4.1 M->key != Key(C)
3882 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
3883 and (M->LOffset,+M->LLength) do not intersect.
3885 (Note: If a client loses a lock, it loses all locks.
3886 Subsequently, it will not be allowed to obtain any more locks
3887 until all existing LOST locks that belong to the client are
3888 released. Once all locks are released by a single client,
3889 there exists no further contract between the client and AFS
3890 about the contents of the file, hence the client can then
3891 proceed to obtain new locks and establish a new contract.
3893 This doesn't quite work as you think it should, because most
3894 applications aren't built to deal with losing locks they
3895 thought they once had. For now, we don't have a good
3896 solution to lost locks.
3898 Also, for consistency reasons, we have to hold off on
3899 granting locks that overlap exclusive LOST locks.)
3901 A client C can only unlock locks L in S->fileLocks which have
3904 The representation and invariants are as follows:
3906 - Each cm_scache_t structure keeps:
3908 - A queue of byte-range locks (cm_scache_t::fileLocks) which
3909 are of type cm_file_lock_t.
3911 - A record of the highest server-side lock that has been
3912 obtained for this object (cm_scache_t::serverLock), which is
3913 one of (-1), LockRead, LockWrite.
3915 - A count of ACCEPTED exclusive and shared locks that are in the
3916 queue (cm_scache_t::sharedLocks and
3917 cm_scache_t::exclusiveLocks)
3919 - Each cm_file_lock_t structure keeps:
3921 - The type of lock (cm_file_lock_t::LockType)
3923 - The key associated with the lock (cm_file_lock_t::key)
3925 - The offset and length of the lock (cm_file_lock_t::LOffset
3926 and cm_file_lock_t::LLength)
3928 - The state of the lock.
3930 - Time of issuance or last successful extension
3932 Semantic invariants:
3934 I1. The number of ACCEPTED locks in S->fileLocks are
3935 (S->sharedLocks + S->exclusiveLocks)
3937 External invariants:
3939 I3. S->serverLock is the lock that we have asserted with the
3940 AFS file server for this cm_scache_t.
3942 I4. S->serverLock == LockRead iff there is at least one ACTIVE
3943 shared lock, but no ACTIVE exclusive locks.
3945 I5. S->serverLock == LockWrite iff there is at least one ACTIVE
3948 I6. If L is a LOST lock, then for each lock M in S->fileLocks,
3949 M->key == L->key IMPLIES M is LOST or DELETED.
3954 #define IS_LOCK_ACTIVE(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == 0)
3956 #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)
3958 #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)
3960 #define IS_LOCK_LOST(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_LOST)
3962 #define IS_LOCK_DELETED(lockp) (((lockp)->flags & CM_FILELOCK_FLAG_DELETED) == CM_FILELOCK_FLAG_DELETED)
3965 #define IS_LOCK_ACCEPTED(lockp) (IS_LOCK_ACTIVE(lockp) || IS_LOCK_WAITLOCK(lockp))
3968 #define IS_LOCK_CLIENTONLY(lockp) ((((lockp)->scp->flags & CM_SCACHEFLAG_RO) == CM_SCACHEFLAG_RO) || (((lockp)->flags & CM_FILELOCK_FLAG_CLIENTONLY) == CM_FILELOCK_FLAG_CLIENTONLY))
3971 #define INTERSECT_RANGE(r1,r2) (((r2).offset+(r2).length) > (r1).offset && ((r1).offset +(r1).length) > (r2).offset)
3974 #define CONTAINS_RANGE(r1,r2) (((r2).offset+(r2).length) <= ((r1).offset+(r1).length) && (r1).offset <= (r2).offset)
3976 #if defined(VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS) && !defined(LOCK_TESTING)
3977 #define SCP_SUPPORTS_BRLOCKS(scp) ((scp)->cbServerp && ((scp)->cbServerp->capabilities & VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS))
3979 #define SCP_SUPPORTS_BRLOCKS(scp) (1)
3982 #define SERVERLOCKS_ENABLED(scp) (!((scp)->flags & CM_SCACHEFLAG_RO) && cm_enableServerLocks && SCP_SUPPORTS_BRLOCKS(scp))
3984 #if defined(VICED_CAPABILITY_WRITELOCKACL)
3985 #define SCP_SUPPORTS_WRITELOCKACL(scp) ((scp)->cbServerp && ((scp->cbServerp->capabilities & VICED_CAPABILITY_WRITELOCKACL)))
3987 #define SCP_SUPPORTS_WRITELOCKACL(scp) (0)
3989 /* This should really be defined in any build that this code is being
3991 #error VICED_CAPABILITY_WRITELOCKACL not defined.
3994 static void cm_LockRangeSubtract(cm_range_t * pos, const cm_range_t * neg)
3996 afs_int64 int_begin;
3999 int_begin = MAX(pos->offset, neg->offset);
4000 int_end = MIN(pos->offset+pos->length, neg->offset+neg->length);
4002 if (int_begin < int_end) {
4003 if (int_begin == pos->offset) {
4004 pos->length = pos->offset + pos->length - int_end;
4005 pos->offset = int_end;
4006 } else if (int_end == pos->offset + pos->length) {
4007 pos->length = int_begin - pos->offset;
4010 /* We only subtract ranges if the resulting range is
4011 contiguous. If we try to support non-contigous ranges, we
4012 aren't actually improving performance. */
4016 /* Called with scp->rw held. Returns 0 if all is clear to read the
4017 specified range by the client identified by key.
4019 long cm_LockCheckRead(cm_scache_t *scp,
4020 LARGE_INTEGER LOffset,
4021 LARGE_INTEGER LLength,
4024 #ifndef ADVISORY_LOCKS
4026 cm_file_lock_t *fileLock;
4030 int substract_ranges = FALSE;
4032 range.offset = LOffset.QuadPart;
4033 range.length = LLength.QuadPart;
4037 1. for all _a_ in (Offset,+Length), all of the following is true:
4039 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
4040 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
4043 1.2 For each LOST lock L in S->fileLocks such that _a_ in
4044 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
4049 lock_ObtainRead(&cm_scacheLock);
4051 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
4053 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4055 if (INTERSECT_RANGE(range, fileLock->range)) {
4056 if (IS_LOCK_ACTIVE(fileLock)) {
4057 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4059 /* If there is an active lock for this client, it
4060 is safe to substract ranges.*/
4061 cm_LockRangeSubtract(&range, &fileLock->range);
4062 substract_ranges = TRUE;
4064 if (fileLock->lockType != LockRead) {
4065 code = CM_ERROR_LOCK_CONFLICT;
4069 /* even if the entire range is locked for reading,
4070 we still can't grant the lock at this point
4071 because the client may have lost locks. That
4072 is, unless we have already seen an active lock
4073 belonging to the client, in which case there
4074 can't be any lost locks for this client. */
4075 if (substract_ranges)
4076 cm_LockRangeSubtract(&range, &fileLock->range);
4078 } else if (IS_LOCK_LOST(fileLock) &&
4079 (cm_KeyEquals(&fileLock->key, &key, 0) || fileLock->lockType == LockWrite)) {
4080 code = CM_ERROR_BADFD;
4086 lock_ReleaseRead(&cm_scacheLock);
4088 osi_Log4(afsd_logp, "cm_LockCheckRead scp 0x%x offset %d length %d code 0x%x",
4089 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
4100 /* Called with scp->rw held. Returns 0 if all is clear to write the
4101 specified range by the client identified by key.
4103 long cm_LockCheckWrite(cm_scache_t *scp,
4104 LARGE_INTEGER LOffset,
4105 LARGE_INTEGER LLength,
4108 #ifndef ADVISORY_LOCKS
4110 cm_file_lock_t *fileLock;
4115 range.offset = LOffset.QuadPart;
4116 range.length = LLength.QuadPart;
4119 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
4121 2. for all _a_ in (Offset,+Length), one of the following is true:
4123 2.1 Byte _a_ of S is unowned AND there does not exist a LOST
4124 lock L such that _a_ in (L->LOffset,+L->LLength).
4126 2.2 Byte _a_ of S is owned by C under lock L AND L->LockType is
4130 lock_ObtainRead(&cm_scacheLock);
4132 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
4134 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4136 if (INTERSECT_RANGE(range, fileLock->range)) {
4137 if (IS_LOCK_ACTIVE(fileLock)) {
4138 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4139 if (fileLock->lockType == LockWrite) {
4141 /* if there is an active lock for this client, it
4142 is safe to substract ranges */
4143 cm_LockRangeSubtract(&range, &fileLock->range);
4145 code = CM_ERROR_LOCK_CONFLICT;
4149 code = CM_ERROR_LOCK_CONFLICT;
4152 } else if (IS_LOCK_LOST(fileLock)) {
4153 code = CM_ERROR_BADFD;
4159 lock_ReleaseRead(&cm_scacheLock);
4161 osi_Log4(afsd_logp, "cm_LockCheckWrite scp 0x%x offset %d length %d code 0x%x",
4162 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
4173 /* Called with cm_scacheLock write locked */
4174 static cm_file_lock_t * cm_GetFileLock(void) {
4177 l = (cm_file_lock_t *) cm_freeFileLocks;
4179 osi_QRemove(&cm_freeFileLocks, &l->q);
4181 l = malloc(sizeof(cm_file_lock_t));
4182 osi_assertx(l, "null cm_file_lock_t");
4185 memset(l, 0, sizeof(cm_file_lock_t));
4190 /* Called with cm_scacheLock write locked */
4191 static void cm_PutFileLock(cm_file_lock_t *l) {
4192 osi_QAdd(&cm_freeFileLocks, &l->q);
4195 /* called with scp->rw held. May release it during processing, but
4196 leaves it held on exit. */
4197 long cm_IntSetLock(cm_scache_t * scp, cm_user_t * userp, int lockType,
4203 struct rx_connection * rxconnp;
4205 afs_uint32 reqflags = reqp->flags;
4207 memset(&volSync, 0, sizeof(volSync));
4209 tfid.Volume = scp->fid.volume;
4210 tfid.Vnode = scp->fid.vnode;
4211 tfid.Unique = scp->fid.unique;
4214 osi_Log2(afsd_logp, "CALL SetLock scp 0x%p for lock %d", scp, lockType);
4216 reqp->flags |= CM_REQ_NORETRY;
4217 lock_ReleaseWrite(&scp->rw);
4220 code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4224 rxconnp = cm_GetRxConn(connp);
4225 code = RXAFS_SetLock(rxconnp, &tfid, lockType,
4227 rx_PutConnection(rxconnp);
4229 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
4232 code = cm_MapRPCError(code, reqp);
4234 osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
4236 osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
4239 lock_ObtainWrite(&scp->rw);
4240 reqp->flags = reqflags;
4244 /* called with scp->rw held. Releases it during processing */
4245 long cm_IntReleaseLock(cm_scache_t * scp, cm_user_t * userp,
4251 struct rx_connection * rxconnp;
4254 memset(&volSync, 0, sizeof(volSync));
4256 tfid.Volume = scp->fid.volume;
4257 tfid.Vnode = scp->fid.vnode;
4258 tfid.Unique = scp->fid.unique;
4261 lock_ReleaseWrite(&scp->rw);
4263 osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
4266 code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4270 rxconnp = cm_GetRxConn(connp);
4271 code = RXAFS_ReleaseLock(rxconnp, &tfid, &volSync);
4272 rx_PutConnection(rxconnp);
4274 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
4276 code = cm_MapRPCError(code, reqp);
4279 "CALL ReleaseLock FAILURE, code 0x%x", code);
4282 "CALL ReleaseLock SUCCESS");
4284 lock_ObtainWrite(&scp->rw);
4289 /* called with scp->rw held. May release it during processing, but
4290 will exit with lock held.
4294 - 0 if the user has permission to get the specified lock for the scp
4296 - CM_ERROR_NOACCESS if not
4298 Any other error from cm_SyncOp will be sent down untranslated.
4300 If CM_ERROR_NOACCESS is returned and lock_type is LockRead, then
4301 phas_insert (if non-NULL) will receive a boolean value indicating
4302 whether the user has INSERT permission or not.
4304 long cm_LockCheckPerms(cm_scache_t * scp,
4311 long code = 0, code2 = 0;
4313 /* lock permissions are slightly tricky because of the 'i' bit.
4314 If the user has PRSFS_LOCK, she can read-lock the file. If the
4315 user has PRSFS_WRITE, she can write-lock the file. However, if
4316 the user has PRSFS_INSERT, then she can write-lock new files,
4317 but not old ones. Since we don't have information about
4318 whether a file is new or not, we assume that if the user owns
4319 the scp, then she has the permissions that are granted by
4322 osi_Log3(afsd_logp, "cm_LockCheckPerms for scp[0x%p] type[%d] user[0x%p]",
4323 scp, lock_type, userp);
4325 if (lock_type == LockRead)
4326 rights |= PRSFS_LOCK;
4327 else if (lock_type == LockWrite)
4328 rights |= PRSFS_WRITE | PRSFS_LOCK;
4331 osi_assertx(FALSE, "invalid lock type");
4336 *phas_insert = FALSE;
4338 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
4339 CM_SCACHESYNC_GETSTATUS |
4340 CM_SCACHESYNC_NEEDCALLBACK);
4342 if (phas_insert && scp->creator == userp) {
4344 /* If this file was created by the user, then we check for
4345 PRSFS_INSERT. If the file server is recent enough, then
4346 this should be sufficient for her to get a write-lock (but
4347 not necessarily a read-lock). VICED_CAPABILITY_WRITELOCKACL
4348 indicates whether a file server supports getting write
4349 locks when the user only has PRSFS_INSERT.
4351 If the file was not created by the user we skip the check
4352 because the INSERT bit will not apply to this user even
4356 code2 = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
4357 CM_SCACHESYNC_GETSTATUS |
4358 CM_SCACHESYNC_NEEDCALLBACK);
4360 if (code2 == CM_ERROR_NOACCESS) {
4361 osi_Log0(afsd_logp, "cm_LockCheckPerms user has no INSERT bits");
4363 *phas_insert = TRUE;
4364 osi_Log0(afsd_logp, "cm_LockCheckPerms user has INSERT bits");
4368 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
4370 osi_Log1(afsd_logp, "cm_LockCheckPerms returning code %d", code);
4375 /* called with scp->rw held */
4376 long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
4377 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
4379 int allowWait, cm_user_t *userp, cm_req_t *reqp,
4380 cm_file_lock_t **lockpp)
4383 int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
4384 cm_file_lock_t *fileLock;
4387 int wait_unlock = FALSE;
4388 int force_client_lock = FALSE;
4390 osi_Log4(afsd_logp, "cm_Lock scp 0x%x type 0x%x offset %d length %d",
4391 scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
4392 osi_Log4(afsd_logp, "... allowWait %d key <0x%x, 0x%x, 0x%x>", allowWait,
4393 key.process_id, key.session_id, key.file_id);
4396 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
4398 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
4401 3.1 If L->LockType is exclusive then there does NOT exist a
4402 ACCEPTED lock M in S->fileLocks such that _a_ in
4403 (M->LOffset,+M->LLength).
4405 3.2 If L->LockType is shared then for each ACCEPTED lock M in
4406 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
4407 M->LockType is shared.
4409 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
4411 4.1 M->key != Key(C)
4413 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
4414 and (M->LOffset,+M->LLength) do not intersect.
4417 range.offset = LOffset.QuadPart;
4418 range.length = LLength.QuadPart;
4420 lock_ObtainRead(&cm_scacheLock);
4422 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4424 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4426 if (IS_LOCK_LOST(fileLock)) {
4427 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4428 code = CM_ERROR_BADFD;
4430 } else if (fileLock->lockType == LockWrite && INTERSECT_RANGE(range, fileLock->range)) {
4431 code = CM_ERROR_WOULDBLOCK;
4437 /* we don't need to check for deleted locks here since deleted
4438 locks are dequeued from scp->fileLocks */
4439 if (IS_LOCK_ACCEPTED(fileLock) &&
4440 INTERSECT_RANGE(range, fileLock->range)) {
4442 if ((sLockType & LOCKING_ANDX_SHARED_LOCK) == 0 ||
4443 fileLock->lockType != LockRead) {
4445 code = CM_ERROR_WOULDBLOCK;
4451 lock_ReleaseRead(&cm_scacheLock);
4453 if (code == 0 && SERVERLOCKS_ENABLED(scp)) {
4454 if (Which == scp->serverLock ||
4455 (Which == LockRead && scp->serverLock == LockWrite)) {
4459 /* we already have the lock we need */
4460 osi_Log3(afsd_logp, " we already have the correct lock. exclusives[%d], shared[%d], serverLock[%d]",
4461 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4463 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4465 /* special case: if we don't have permission to read-lock
4466 the file, then we force a clientside lock. This is to
4467 compensate for applications that obtain a read-lock for
4468 reading files off of directories that don't grant
4469 read-locks to the user. */
4470 if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4472 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4473 osi_Log0(afsd_logp, " User has no read-lock perms, but has INSERT perms.");
4476 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4477 force_client_lock = TRUE;
4481 } else if ((scp->exclusiveLocks > 0) ||
4482 (scp->sharedLocks > 0 && scp->serverLock != LockRead)) {
4485 /* We are already waiting for some other lock. We should
4486 wait for the daemon to catch up instead of generating a
4487 flood of SetLock calls. */
4488 osi_Log3(afsd_logp, " already waiting for other lock. exclusives[%d], shared[%d], serverLock[%d]",
4489 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4491 /* see if we have permission to create the lock in the
4493 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4495 code = CM_ERROR_WOULDBLOCK;
4496 else if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4498 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4500 " User has no read-lock perms, but has INSERT perms.");
4501 code = CM_ERROR_WOULDBLOCK;
4504 " User has no read-lock perms. Forcing client-side lock");
4505 force_client_lock = TRUE;
4509 /* leave any other codes as-is */
4513 int check_data_version = FALSE;
4516 /* first check if we have permission to elevate or obtain
4518 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4520 if (code == CM_ERROR_NOACCESS && Which == LockRead &&
4521 (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp))) {
4522 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4523 force_client_lock = TRUE;
4528 /* has_insert => (Which == LockRead, code == CM_ERROR_NOACCESS) */
4530 if (scp->serverLock == LockRead && Which == LockWrite) {
4532 /* We want to escalate the lock to a LockWrite.
4533 * Unfortunately that's not really possible without
4534 * letting go of the current lock. But for now we do
4538 " attempting to UPGRADE from LockRead to LockWrite.");
4540 " dataVersion on scp: %I64d", scp->dataVersion);
4542 /* we assume at this point (because scp->serverLock
4543 was valid) that we had a valid server lock. */
4544 scp->lockDataVersion = scp->dataVersion;
4545 check_data_version = TRUE;
4547 code = cm_IntReleaseLock(scp, userp, reqp);
4550 /* We couldn't release the lock */
4553 scp->serverLock = -1;
4557 /* We need to obtain a server lock of type Which in order
4558 * to assert this file lock */
4559 #ifndef AGGRESSIVE_LOCKS
4562 newLock = LockWrite;
4565 code = cm_IntSetLock(scp, userp, newLock, reqp);
4567 #ifdef AGGRESSIVE_LOCKS
4568 if ((code == CM_ERROR_WOULDBLOCK ||
4569 code == CM_ERROR_NOACCESS) && newLock != Which) {
4570 /* we wanted LockRead. We tried LockWrite. Now try
4575 osi_assertx(newLock == LockRead, "lock type not read");
4577 code = cm_IntSetLock(scp, userp, newLock, reqp);
4581 if (code == CM_ERROR_NOACCESS) {
4582 if (Which == LockRead) {
4583 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4585 /* We requested a read-lock, but we have permission to
4586 * get a write-lock. Try that */
4588 tcode = cm_LockCheckPerms(scp, LockWrite, userp, reqp, NULL);
4591 newLock = LockWrite;
4593 osi_Log0(afsd_logp, " User has 'i' perms and the request was for a LockRead. Trying to get a LockWrite instead");
4595 code = cm_IntSetLock(scp, userp, newLock, reqp);
4598 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4599 force_client_lock = TRUE;
4601 } else if (Which == LockWrite &&
4602 scp->creator == userp && !SCP_SUPPORTS_WRITELOCKACL(scp)) {
4605 /* Special case: if the lock request was for a
4606 * LockWrite and the user owns the file and we weren't
4607 * allowed to obtain the serverlock, we either lost a
4608 * race (the permissions changed from under us), or we
4609 * have 'i' bits, but we aren't allowed to lock the
4612 /* check if we lost a race... */
4613 tcode = cm_LockCheckPerms(scp, Which, userp, reqp, NULL);
4616 osi_Log0(afsd_logp, " User has 'i' perms but can't obtain write locks. Using client-side locks.");
4617 force_client_lock = TRUE;
4622 if (code == 0 && check_data_version &&
4623 scp->dataVersion != scp->lockDataVersion) {
4624 /* We lost a race. Although we successfully obtained
4625 * a lock, someone modified the file in between. The
4626 * locks have all been technically lost. */
4629 " Data version mismatch while upgrading lock.");
4631 " Data versions before=%I64d, after=%I64d",
4632 scp->lockDataVersion,
4635 " Releasing stale lock for scp 0x%x", scp);
4637 code = cm_IntReleaseLock(scp, userp, reqp);
4639 scp->serverLock = -1;
4641 code = CM_ERROR_INVAL;
4642 } else if (code == 0) {
4643 scp->serverLock = newLock;
4644 scp->lockDataVersion = scp->dataVersion;
4648 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4649 scp->serverLock == -1) {
4650 /* Oops. We lost the lock. */
4651 cm_LockMarkSCacheLost(scp);
4654 } else if (code == 0) { /* server locks not enabled */
4656 " Skipping server lock for scp");
4661 if (code != 0 && !force_client_lock) {
4662 /* Special case error translations
4664 Applications don't expect certain errors from a
4665 LockFile/UnlockFile call. We need to translate some error
4666 code to codes that apps expect and handle. */
4668 /* We shouldn't actually need to handle this case since we
4669 simulate locks for RO scps anyway. */
4670 if (code == CM_ERROR_READONLY) {
4671 osi_Log0(afsd_logp, " Reinterpreting CM_ERROR_READONLY as CM_ERROR_NOACCESS");
4672 code = CM_ERROR_NOACCESS;
4676 if (code == 0 || (code == CM_ERROR_WOULDBLOCK && allowWait) ||
4677 force_client_lock) {
4679 /* clear the error if we are forcing a client lock, so we
4680 don't get confused later. */
4681 if (force_client_lock && code != CM_ERROR_WOULDBLOCK)
4684 lock_ObtainWrite(&cm_scacheLock);
4685 fileLock = cm_GetFileLock();
4686 lock_ReleaseWrite(&cm_scacheLock);
4688 fileLock->fid = scp->fid;
4690 fileLock->key = key;
4691 fileLock->lockType = Which;
4693 fileLock->userp = userp;
4694 fileLock->range = range;
4695 fileLock->flags = (code == 0 ? 0 :
4697 CM_FILELOCK_FLAG_WAITUNLOCK :
4698 CM_FILELOCK_FLAG_WAITLOCK));
4700 if (force_client_lock || !SERVERLOCKS_ENABLED(scp))
4701 fileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
4703 fileLock->lastUpdate = (code == 0 && !force_client_lock) ? time(NULL) : 0;
4705 lock_ObtainWrite(&cm_scacheLock);
4706 osi_QAddT(&scp->fileLocksH, &scp->fileLocksT, &fileLock->fileq);
4707 cm_HoldSCacheNoLock(scp);
4708 fileLock->scp = scp;
4709 osi_QAdd(&cm_allFileLocks, &fileLock->q);
4710 lock_ReleaseWrite(&cm_scacheLock);
4716 if (IS_LOCK_CLIENTONLY(fileLock)) {
4718 } else if (IS_LOCK_ACCEPTED(fileLock)) {
4719 if (Which == LockRead)
4722 scp->exclusiveLocks++;
4726 "cm_Lock Lock added 0x%p flags 0x%x to scp [0x%p]",
4727 fileLock, fileLock->flags, scp);
4729 " exclusives[%d] shared[%d] client[%d] serverLock[%d]",
4730 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4731 (int)(signed char) scp->serverLock);
4734 "cm_Lock Rejecting lock (code = 0x%x)", code);
4737 /* Convert from would block to lock not granted */
4738 if (code == CM_ERROR_WOULDBLOCK)
4739 code = CM_ERROR_LOCK_NOT_GRANTED;
4744 /* Called with scp->rw held */
4745 long cm_UnlockByKey(cm_scache_t * scp,
4752 cm_file_lock_t *fileLock;
4753 osi_queue_t *q, *qn;
4756 osi_Log4(afsd_logp, "cm_UnlockByKey scp 0x%p key <0x%x,0x%x,0x%x",
4757 scp, key.process_id, key.session_id, key.file_id);
4758 osi_Log1(afsd_logp, " flags=0x%x", flags);
4760 lock_ObtainWrite(&cm_scacheLock);
4762 for (q = scp->fileLocksH; q; q = qn) {
4765 fileLock = (cm_file_lock_t *)
4766 ((char *) q - offsetof(cm_file_lock_t, fileq));
4769 osi_Log4(afsd_logp, " Checking lock[0x%x] range[%d,+%d] type[%d]",
4771 (unsigned long) fileLock->range.offset,
4772 (unsigned long) fileLock->range.length,
4773 fileLock->lockType);
4774 osi_Log4(afsd_logp, " key<0x%x, 0x%x, 0x%x> flags[0x%x]",
4775 fileLock->key.process_id, fileLock->key.session_id, fileLock->key.file_id,
4778 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
4779 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
4780 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4782 fileLock->fid.volume,
4783 fileLock->fid.vnode,
4784 fileLock->fid.unique);
4785 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4786 fileLock->scp->fid.cell,
4787 fileLock->scp->fid.volume,
4788 fileLock->scp->fid.vnode,
4789 fileLock->scp->fid.unique);
4790 osi_assertx(FALSE, "invalid fid value");
4794 if (!IS_LOCK_DELETED(fileLock) &&
4795 cm_KeyEquals(&fileLock->key, &key, flags)) {
4796 osi_Log3(afsd_logp, "...Unlock range [%d,+%d] type %d",
4797 fileLock->range.offset,
4798 fileLock->range.length,
4799 fileLock->lockType);
4801 if (scp->fileLocksT == q)
4802 scp->fileLocksT = osi_QPrev(q);
4803 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
4805 if (IS_LOCK_CLIENTONLY(fileLock)) {
4807 } else if (IS_LOCK_ACCEPTED(fileLock)) {
4808 if (fileLock->lockType == LockRead)
4811 scp->exclusiveLocks--;
4814 fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
4816 cm_ReleaseUser(fileLock->userp);
4817 cm_ReleaseSCacheNoLock(scp);
4819 fileLock->userp = NULL;
4820 fileLock->scp = NULL;
4826 lock_ReleaseWrite(&cm_scacheLock);
4828 if (n_unlocks == 0) {
4829 osi_Log0(afsd_logp, "cm_UnlockByKey no locks found");
4830 osi_Log3(afsd_logp, " Leaving scp with exclusives[%d], shared[%d], serverLock[%d]",
4831 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4836 osi_Log1(afsd_logp, "cm_UnlockByKey done with %d locks", n_unlocks);
4838 osi_assertx(scp->sharedLocks >= 0, "scp->sharedLocks < 0");
4839 osi_assertx(scp->exclusiveLocks >= 0, "scp->exclusiveLocks < 0");
4840 osi_assertx(scp->clientLocks >= 0, "scp->clientLocks < 0");
4842 if (!SERVERLOCKS_ENABLED(scp)) {
4843 osi_Log0(afsd_logp, " Skipping server lock for scp");
4847 /* Ideally we would go through the rest of the locks to determine
4848 * if one or more locks that were formerly in WAITUNLOCK can now
4849 * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
4850 * scp->sharedLocks accordingly. However, the retrying of locks
4851 * in that manner is done cm_RetryLock() manually.
4854 if (scp->serverLock == LockWrite &&
4855 scp->exclusiveLocks == 0 &&
4856 scp->sharedLocks > 0) {
4857 /* The serverLock should be downgraded to LockRead */
4858 osi_Log0(afsd_logp, " DOWNGRADE lock from LockWrite to LockRead");
4860 /* Make sure there are no dirty buffers left. */
4861 code = cm_FSync(scp, userp, reqp, TRUE);
4863 /* since scp->serverLock looked sane, we are going to assume
4864 that we have a valid server lock. */
4865 scp->lockDataVersion = scp->dataVersion;
4866 osi_Log1(afsd_logp, " dataVersion on scp = %I64d", scp->dataVersion);
4868 code = cm_IntReleaseLock(scp, userp, reqp);
4871 /* so we couldn't release it. Just let the lock be for now */
4875 scp->serverLock = -1;
4878 code = cm_IntSetLock(scp, userp, LockRead, reqp);
4880 if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
4881 scp->serverLock = LockRead;
4882 } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
4883 /* We lost a race condition. Although we have a valid
4884 lock on the file, the data has changed and essentially
4885 we have lost the lock we had during the transition. */
4887 osi_Log0(afsd_logp, "Data version mismatch during lock downgrade");
4888 osi_Log2(afsd_logp, " Data versions before=%I64d, after=%I64d",
4889 scp->lockDataVersion,
4892 code = cm_IntReleaseLock(scp, userp, reqp);
4894 code = CM_ERROR_INVAL;
4895 scp->serverLock = -1;
4899 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4900 (scp->serverLock == -1)) {
4902 cm_LockMarkSCacheLost(scp);
4905 /* failure here has no bearing on the return value of
4909 } else if (scp->serverLock != (-1) &&
4910 scp->exclusiveLocks == 0 &&
4911 scp->sharedLocks == 0) {
4912 /* The serverLock should be released entirely */
4914 if (scp->serverLock == LockWrite) {
4915 /* Make sure there are no dirty buffers left. */
4916 code = cm_FSync(scp, userp, reqp, TRUE);
4919 code = cm_IntReleaseLock(scp, userp, reqp);
4922 scp->serverLock = (-1);
4927 osi_Log1(afsd_logp, "cm_UnlockByKey code 0x%x", code);
4928 osi_Log4(afsd_logp, " Leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
4929 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4930 (int)(signed char) scp->serverLock);
4935 long cm_Unlock(cm_scache_t *scp,
4936 unsigned char sLockType,
4937 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
4944 int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
4945 cm_file_lock_t *fileLock;
4947 int release_userp = FALSE;
4948 int exact_match = !(flags & CM_UNLOCK_FLAG_MATCH_RANGE);
4950 LARGE_INTEGER RangeEnd;
4952 osi_Log4(afsd_logp, "cm_Unlock scp 0x%p type 0x%x offset %d length %d",
4953 scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
4954 osi_Log4(afsd_logp, "... key <0x%x,0x%x,0x%x> flags 0x%x",
4955 key.process_id, key.session_id, key.file_id, flags);
4958 RangeEnd.QuadPart = LOffset.QuadPart + LLength.QuadPart;
4961 lock_ObtainRead(&cm_scacheLock);
4963 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4964 fileLock = (cm_file_lock_t *)
4965 ((char *) q - offsetof(cm_file_lock_t, fileq));
4968 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
4969 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
4970 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4972 fileLock->fid.volume,
4973 fileLock->fid.vnode,
4974 fileLock->fid.unique);
4975 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4976 fileLock->scp->fid.cell,
4977 fileLock->scp->fid.volume,
4978 fileLock->scp->fid.vnode,
4979 fileLock->scp->fid.unique);
4980 osi_assertx(FALSE, "invalid fid value");
4984 if (!IS_LOCK_DELETED(fileLock) &&
4985 cm_KeyEquals(&fileLock->key, &key, 0) &&
4986 fileLock->range.offset == LOffset.QuadPart &&
4987 fileLock->range.length == LLength.QuadPart) {
4993 if (!IS_LOCK_DELETED(fileLock) &&
4994 cm_KeyEquals(&fileLock->key, &key, 0) &&
4995 fileLock->range.offset >= LOffset.QuadPart &&
4996 fileLock->range.offset < RangeEnd.QuadPart &&
4997 (fileLock->range.offset + fileLock->range.length) <= RangeEnd.QuadPart) {
5005 lock_ReleaseRead(&cm_scacheLock);
5007 if (lock_found && !exact_match) {
5011 osi_Log0(afsd_logp, "cm_Unlock lock not found; failure");
5013 /* The lock didn't exist anyway. *shrug* */
5014 return CM_ERROR_RANGE_NOT_LOCKED;
5018 /* discard lock record */
5019 lock_ConvertRToW(&cm_scacheLock);
5020 if (scp->fileLocksT == q)
5021 scp->fileLocksT = osi_QPrev(q);
5022 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
5025 * Don't delete it here; let the daemon delete it, to simplify
5026 * the daemon's traversal of the list.
5029 if (IS_LOCK_CLIENTONLY(fileLock)) {
5031 } else if (IS_LOCK_ACCEPTED(fileLock)) {
5032 if (fileLock->lockType == LockRead)
5035 scp->exclusiveLocks--;
5038 fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5039 if (userp != NULL) {
5040 cm_ReleaseUser(fileLock->userp);
5042 userp = fileLock->userp;
5043 release_userp = TRUE;
5045 fileLock->userp = NULL;
5046 cm_ReleaseSCacheNoLock(scp);
5047 fileLock->scp = NULL;
5048 lock_ReleaseWrite(&cm_scacheLock);
5050 if (!SERVERLOCKS_ENABLED(scp)) {
5051 osi_Log0(afsd_logp, " Skipping server locks for scp");
5055 /* Ideally we would go through the rest of the locks to determine
5056 * if one or more locks that were formerly in WAITUNLOCK can now
5057 * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
5058 * scp->sharedLocks accordingly. However, the retrying of locks
5059 * in that manner is done cm_RetryLock() manually.
5062 if (scp->serverLock == LockWrite &&
5063 scp->exclusiveLocks == 0 &&
5064 scp->sharedLocks > 0) {
5066 /* The serverLock should be downgraded to LockRead */
5067 osi_Log0(afsd_logp, " DOWNGRADE lock from LockWrite to LockRead");
5069 /* Make sure there are no dirty buffers left. */
5070 code = cm_FSync(scp, userp, reqp, TRUE);
5072 /* Since we already had a lock, we assume that there is a
5073 valid server lock. */
5074 scp->lockDataVersion = scp->dataVersion;
5075 osi_Log1(afsd_logp, " dataVersion on scp is %I64d", scp->dataVersion);
5077 /* before we downgrade, make sure that we have enough
5078 permissions to get the read lock. */
5079 code = cm_LockCheckPerms(scp, LockRead, userp, reqp, NULL);
5082 osi_Log0(afsd_logp, " SKIPPING downgrade because user doesn't have perms to get downgraded lock");
5088 code = cm_IntReleaseLock(scp, userp, reqp);
5091 /* so we couldn't release it. Just let the lock be for now */
5095 scp->serverLock = -1;
5098 code = cm_IntSetLock(scp, userp, LockRead, reqp);
5100 if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
5101 scp->serverLock = LockRead;
5102 } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
5103 /* Lost a race. We obtained a new lock, but that is
5104 meaningless since someone modified the file
5108 "Data version mismatch while downgrading lock");
5110 " Data versions before=%I64d, after=%I64d",
5111 scp->lockDataVersion,
5114 code = cm_IntReleaseLock(scp, userp, reqp);
5116 scp->serverLock = -1;
5117 code = CM_ERROR_INVAL;
5121 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
5122 (scp->serverLock == -1)) {
5124 cm_LockMarkSCacheLost(scp);
5127 /* failure here has no bearing on the return value of
5131 } else if (scp->serverLock != (-1) &&
5132 scp->exclusiveLocks == 0 &&
5133 scp->sharedLocks == 0) {
5134 /* The serverLock should be released entirely */
5136 if (scp->serverLock == LockWrite) {
5137 /* Make sure there are no dirty buffers left. */
5138 code = cm_FSync(scp, userp, reqp, TRUE);
5141 code = cm_IntReleaseLock(scp, userp, reqp);
5144 scp->serverLock = (-1);
5148 if (release_userp) {
5149 cm_ReleaseUser(userp);
5150 release_userp = FALSE;
5154 goto try_again; /* might be more than one lock in the range */
5158 osi_Log1(afsd_logp, "cm_Unlock code 0x%x", code);
5159 osi_Log4(afsd_logp, " leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
5160 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
5161 (int)(signed char) scp->serverLock);
5166 /* called with scp->rw held */
5167 void cm_LockMarkSCacheLost(cm_scache_t * scp)
5169 cm_file_lock_t *fileLock;
5172 osi_Log1(afsd_logp, "cm_LockMarkSCacheLost scp 0x%x", scp);
5174 /* cm_scacheLock needed because we are modifying fileLock->flags */
5175 lock_ObtainWrite(&cm_scacheLock);
5177 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5179 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
5181 if (IS_LOCK_ACTIVE(fileLock) &&
5182 !IS_LOCK_CLIENTONLY(fileLock)) {
5183 if (fileLock->lockType == LockRead)
5186 scp->exclusiveLocks--;
5188 fileLock->flags |= CM_FILELOCK_FLAG_LOST;
5192 scp->serverLock = -1;
5193 scp->lockDataVersion = CM_SCACHE_VERSION_BAD;
5194 lock_ReleaseWrite(&cm_scacheLock);
5197 /* Called with no relevant locks held */
5198 void cm_CheckLocks()
5200 osi_queue_t *q, *nq;
5201 cm_file_lock_t *fileLock;
5207 struct rx_connection * rxconnp;
5210 memset(&volSync, 0, sizeof(volSync));
5214 lock_ObtainWrite(&cm_scacheLock);
5216 cm_lockRefreshCycle++;
5218 osi_Log1(afsd_logp, "cm_CheckLocks starting lock check cycle %d", cm_lockRefreshCycle);
5220 for (q = cm_allFileLocks; q; q = nq) {
5221 fileLock = (cm_file_lock_t *) q;
5225 if (IS_LOCK_DELETED(fileLock)) {
5227 osi_QRemove(&cm_allFileLocks, q);
5228 cm_PutFileLock(fileLock);
5230 } else if (IS_LOCK_ACTIVE(fileLock) && !IS_LOCK_CLIENTONLY(fileLock)) {
5232 /* Server locks must have been enabled for us to have
5233 received an active non-client-only lock. */
5234 osi_assertx(cm_enableServerLocks, "!cm_enableServerLocks");
5236 scp = fileLock->scp;
5237 osi_assertx(scp != NULL, "null cm_scache_t");
5239 cm_HoldSCacheNoLock(scp);
5242 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5243 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5244 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5246 fileLock->fid.volume,
5247 fileLock->fid.vnode,
5248 fileLock->fid.unique);
5249 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5250 fileLock->scp->fid.cell,
5251 fileLock->scp->fid.volume,
5252 fileLock->scp->fid.vnode,
5253 fileLock->scp->fid.unique);
5254 osi_assertx(FALSE, "invalid fid");
5257 /* Server locks are extended once per scp per refresh
5259 if (scp->lastRefreshCycle != cm_lockRefreshCycle) {
5261 int scp_done = FALSE;
5263 osi_Log1(afsd_logp, "cm_CheckLocks Updating scp 0x%x", scp);
5265 lock_ReleaseWrite(&cm_scacheLock);
5266 lock_ObtainWrite(&scp->rw);
5268 /* did the lock change while we weren't holding the lock? */
5269 if (!IS_LOCK_ACTIVE(fileLock))
5270 goto post_syncopdone;
5272 code = cm_SyncOp(scp, NULL, fileLock->userp, &req, 0,
5273 CM_SCACHESYNC_NEEDCALLBACK
5274 | CM_SCACHESYNC_GETSTATUS
5275 | CM_SCACHESYNC_LOCK);
5279 "cm_CheckLocks SyncOp failure code 0x%x", code);
5280 goto post_syncopdone;
5283 /* cm_SyncOp releases scp->rw during which the lock
5284 may get released. */
5285 if (!IS_LOCK_ACTIVE(fileLock))
5286 goto pre_syncopdone;
5288 if (scp->serverLock != -1 && !(scp->flags & CM_SCACHEFLAG_DELETED)) {
5292 tfid.Volume = scp->fid.volume;
5293 tfid.Vnode = scp->fid.vnode;
5294 tfid.Unique = scp->fid.unique;
5296 userp = fileLock->userp;
5298 osi_Log3(afsd_logp, "CALL ExtendLock lock 0x%p for scp=0x%p with lock %d",
5301 (int) scp->serverLock);
5303 lock_ReleaseWrite(&scp->rw);
5306 code = cm_ConnFromFID(&cfid, userp,
5311 rxconnp = cm_GetRxConn(connp);
5312 code = RXAFS_ExtendLock(rxconnp, &tfid,
5314 rx_PutConnection(rxconnp);
5316 osi_Log1(afsd_logp, " ExtendLock returns %d", code);
5318 } while (cm_Analyze(connp, userp, &req,
5319 &cfid, &volSync, NULL, NULL,
5322 code = cm_MapRPCError(code, &req);
5324 lock_ObtainWrite(&scp->rw);
5327 osi_Log1(afsd_logp, "CALL ExtendLock FAILURE, code 0x%x", code);
5329 osi_Log0(afsd_logp, "CALL ExtendLock SUCCESS");
5330 scp->lockDataVersion = scp->dataVersion;
5333 if ((code == EINVAL || code == CM_ERROR_INVAL) &&
5334 scp->lockDataVersion == scp->dataVersion) {
5338 (scp->exclusiveLocks > 0) ? LockWrite: LockRead;
5340 /* we might still have a chance to obtain a
5343 code = cm_IntSetLock(scp, userp, lockType, &req);
5346 code = CM_ERROR_INVAL;
5347 } else if (scp->lockDataVersion != scp->dataVersion) {
5349 /* now check if we still have the file at
5350 the right data version. */
5352 "Data version mismatch on scp 0x%p",
5355 " Data versions: before=%I64d, after=%I64d",
5356 scp->lockDataVersion,
5359 code = cm_IntReleaseLock(scp, userp, &req);
5361 code = CM_ERROR_INVAL;
5365 if (code == EINVAL || code == CM_ERROR_INVAL ||
5366 code == CM_ERROR_BADFD) {
5367 cm_LockMarkSCacheLost(scp);
5371 /* interestingly, we have found an active lock
5372 belonging to an scache that has no
5374 cm_LockMarkSCacheLost(scp);
5381 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5384 lock_ReleaseWrite(&scp->rw);
5386 lock_ObtainWrite(&cm_scacheLock);
5389 fileLock->lastUpdate = time(NULL);
5393 scp->lastRefreshCycle = cm_lockRefreshCycle;
5396 /* we have already refreshed the locks on this scp */
5397 fileLock->lastUpdate = time(NULL);
5400 cm_ReleaseSCacheNoLock(scp);
5402 } else if (IS_LOCK_ACTIVE(fileLock) && IS_LOCK_CLIENTONLY(fileLock)) {
5403 /* TODO: Check callbacks */
5407 lock_ReleaseWrite(&cm_scacheLock);
5408 osi_Log1(afsd_logp, "cm_CheckLocks completes lock check cycle %d", cm_lockRefreshCycle);
5411 /* NOT called with scp->rw held. */
5412 long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
5415 cm_scache_t *scp = NULL;
5416 cm_file_lock_t *fileLock;
5420 int force_client_lock = FALSE;
5421 int has_insert = FALSE;
5422 int check_data_version = FALSE;
5426 if (client_is_dead) {
5427 code = CM_ERROR_TIMEDOUT;
5431 lock_ObtainRead(&cm_scacheLock);
5433 osi_Log2(afsd_logp, "cm_RetryLock checking lock %p (scp=%p)", oldFileLock, oldFileLock->scp);
5434 osi_Log4(afsd_logp, " offset(%x:%x) length(%x:%x)",
5435 (unsigned)(oldFileLock->range.offset >> 32),
5436 (unsigned)(oldFileLock->range.offset & 0xffffffff),
5437 (unsigned)(oldFileLock->range.length >> 32),
5438 (unsigned)(oldFileLock->range.length & 0xffffffff));
5439 osi_Log4(afsd_logp, " key<0x%x,0x%x,0x%x> flags=%x",
5440 oldFileLock->key.process_id, oldFileLock->key.session_id, oldFileLock->key.file_id,
5441 (unsigned)(oldFileLock->flags));
5443 /* if the lock has already been granted, then we have nothing to do */
5444 if (IS_LOCK_ACTIVE(oldFileLock)) {
5445 lock_ReleaseRead(&cm_scacheLock);
5446 osi_Log0(afsd_logp, "cm_RetryLock lock already granted");
5450 /* we can't do anything with lost or deleted locks at the moment. */
5451 if (IS_LOCK_LOST(oldFileLock) || IS_LOCK_DELETED(oldFileLock)) {
5452 code = CM_ERROR_BADFD;
5453 osi_Log0(afsd_logp, "cm_RetryLock lock is lost or deleted");
5454 lock_ReleaseRead(&cm_scacheLock);
5458 scp = oldFileLock->scp;
5460 osi_assertx(scp != NULL, "null cm_scache_t");
5462 lock_ReleaseRead(&cm_scacheLock);
5463 lock_ObtainWrite(&scp->rw);
5465 code = cm_LockCheckPerms(scp, oldFileLock->lockType,
5469 if (code == CM_ERROR_NOACCESS && oldFileLock->lockType == LockRead) {
5470 if (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp)) {
5471 force_client_lock = TRUE;
5475 lock_ReleaseWrite(&scp->rw);
5479 lock_ObtainWrite(&cm_scacheLock);
5481 /* Check if we already have a sufficient server lock to allow this
5482 lock to go through. */
5483 if (IS_LOCK_WAITLOCK(oldFileLock) &&
5484 (!SERVERLOCKS_ENABLED(scp) ||
5485 scp->serverLock == oldFileLock->lockType ||
5486 scp->serverLock == LockWrite)) {
5488 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5490 if (SERVERLOCKS_ENABLED(scp)) {
5491 osi_Log1(afsd_logp, "cm_RetryLock Server lock (%d) is sufficient for lock. Granting",
5492 (int) scp->serverLock);
5494 osi_Log0(afsd_logp, "cm_RetryLock skipping server lock for scp");
5497 lock_ReleaseWrite(&cm_scacheLock);
5498 lock_ReleaseWrite(&scp->rw);
5503 if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5505 /* check if the conflicting locks have dissappeared already */
5506 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5508 fileLock = (cm_file_lock_t *)
5509 ((char *) q - offsetof(cm_file_lock_t, fileq));
5511 if (IS_LOCK_LOST(fileLock)) {
5512 if (cm_KeyEquals(&fileLock->key, &oldFileLock->key, 0)) {
5513 code = CM_ERROR_BADFD;
5514 oldFileLock->flags |= CM_FILELOCK_FLAG_LOST;
5515 osi_Log1(afsd_logp, " found lost lock %p for same key. Marking lock as lost",
5518 } else if (fileLock->lockType == LockWrite &&
5519 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5520 osi_Log1(afsd_logp, " found conflicting LOST lock %p", fileLock);
5521 code = CM_ERROR_WOULDBLOCK;
5526 if (IS_LOCK_ACCEPTED(fileLock) &&
5527 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5529 if (oldFileLock->lockType != LockRead ||
5530 fileLock->lockType != LockRead) {
5532 osi_Log1(afsd_logp, " found conflicting lock %p", fileLock);
5533 code = CM_ERROR_WOULDBLOCK;
5541 lock_ReleaseWrite(&cm_scacheLock);
5542 lock_ReleaseWrite(&scp->rw);
5547 /* when we get here, the lock is either a WAITUNLOCK or WAITLOCK.
5548 If it is WAITUNLOCK, then we didn't find any conflicting lock
5549 but we haven't verfied whether the serverLock is sufficient to
5550 assert it. If it is WAITLOCK, then the serverLock is
5551 insufficient to assert it. Eitherway, we are ready to accept
5552 the lock as either ACTIVE or WAITLOCK depending on the
5555 /* First, promote the WAITUNLOCK to a WAITLOCK */
5556 if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5557 if (oldFileLock->lockType == LockRead)
5560 scp->exclusiveLocks++;
5562 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITUNLOCK;
5563 oldFileLock->flags |= CM_FILELOCK_FLAG_WAITLOCK;
5566 osi_assertx(IS_LOCK_WAITLOCK(oldFileLock), "!IS_LOCK_WAITLOCK");
5568 if (force_client_lock ||
5569 !SERVERLOCKS_ENABLED(scp) ||
5570 scp->serverLock == oldFileLock->lockType ||
5571 (oldFileLock->lockType == LockRead &&
5572 scp->serverLock == LockWrite)) {
5574 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5576 if ((force_client_lock ||
5577 !SERVERLOCKS_ENABLED(scp)) &&
5578 !IS_LOCK_CLIENTONLY(oldFileLock)) {
5580 oldFileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
5582 if (oldFileLock->lockType == LockRead)
5585 scp->exclusiveLocks--;
5590 lock_ReleaseWrite(&cm_scacheLock);
5591 lock_ReleaseWrite(&scp->rw);
5598 code = cm_SyncOp(scp, NULL, oldFileLock->userp, &req, 0,
5599 CM_SCACHESYNC_NEEDCALLBACK
5600 | CM_SCACHESYNC_GETSTATUS
5601 | CM_SCACHESYNC_LOCK);
5603 osi_Log1(afsd_logp, "cm_RetryLock SyncOp failure code 0x%x", code);
5604 lock_ReleaseWrite(&cm_scacheLock);
5605 goto post_syncopdone;
5608 if (!IS_LOCK_WAITLOCK(oldFileLock))
5609 goto pre_syncopdone;
5611 userp = oldFileLock->userp;
5613 #ifndef AGGRESSIVE_LOCKS
5614 newLock = oldFileLock->lockType;
5616 newLock = LockWrite;
5620 /* if has_insert is non-zero, then:
5621 - the lock a LockRead
5622 - we don't have permission to get a LockRead
5623 - we do have permission to get a LockWrite
5624 - the server supports VICED_CAPABILITY_WRITELOCKACL
5627 newLock = LockWrite;
5630 lock_ReleaseWrite(&cm_scacheLock);
5632 /* when we get here, either we have a read-lock and want a
5633 write-lock or we don't have any locks and we want some
5636 if (scp->serverLock == LockRead) {
5638 osi_assertx(newLock == LockWrite, "!LockWrite");
5640 osi_Log0(afsd_logp, " Attempting to UPGRADE from LockRead to LockWrite");
5642 scp->lockDataVersion = scp->dataVersion;
5643 check_data_version = TRUE;
5645 code = cm_IntReleaseLock(scp, userp, &req);
5648 goto pre_syncopdone;
5650 scp->serverLock = -1;
5653 code = cm_IntSetLock(scp, userp, newLock, &req);
5656 if (scp->dataVersion != scp->lockDataVersion) {
5657 /* we lost a race. too bad */
5660 " Data version mismatch while upgrading lock.");
5662 " Data versions before=%I64d, after=%I64d",
5663 scp->lockDataVersion,
5666 " Releasing stale lock for scp 0x%x", scp);
5668 code = cm_IntReleaseLock(scp, userp, &req);
5670 scp->serverLock = -1;
5672 code = CM_ERROR_INVAL;
5674 cm_LockMarkSCacheLost(scp);
5676 scp->serverLock = newLock;
5681 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5687 if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
5688 lock_ObtainWrite(&cm_scacheLock);
5689 if (scp->fileLocksT == &oldFileLock->fileq)
5690 scp->fileLocksT = osi_QPrev(&oldFileLock->fileq);
5691 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, &oldFileLock->fileq);
5692 lock_ReleaseWrite(&cm_scacheLock);
5694 lock_ReleaseWrite(&scp->rw);
5697 lock_ObtainWrite(&cm_scacheLock);
5699 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5700 } else if (code != CM_ERROR_WOULDBLOCK) {
5701 oldFileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5702 cm_ReleaseUser(oldFileLock->userp);
5703 oldFileLock->userp = NULL;
5704 if (oldFileLock->scp) {
5705 cm_ReleaseSCacheNoLock(oldFileLock->scp);
5706 oldFileLock->scp = NULL;
5709 lock_ReleaseWrite(&cm_scacheLock);
5714 cm_key_t cm_GenerateKey(afs_uint16 session_id, afs_offs_t process_id, afs_uint16 file_id)
5718 key.process_id = process_id;
5719 key.session_id = session_id;
5720 key.file_id = file_id;
5725 int cm_KeyEquals(cm_key_t *k1, cm_key_t *k2, int flags)
5727 return (k1->session_id == k2->session_id) && (k1->file_id == k2->file_id) &&
5728 ((flags & CM_UNLOCK_BY_FID) || (k1->process_id == k2->process_id));
5731 void cm_ReleaseAllLocks(void)
5737 cm_file_lock_t *fileLock;
5740 for (i = 0; i < cm_data.scacheHashTableSize; i++)
5742 for ( scp = cm_data.scacheHashTablep[i]; scp; scp = scp->nextp ) {
5743 while (scp->fileLocksH != NULL) {
5744 lock_ObtainWrite(&scp->rw);
5745 lock_ObtainWrite(&cm_scacheLock);
5746 if (!scp->fileLocksH) {
5747 lock_ReleaseWrite(&cm_scacheLock);
5748 lock_ReleaseWrite(&scp->rw);
5751 fileLock = (cm_file_lock_t *)((char *) scp->fileLocksH - offsetof(cm_file_lock_t, fileq));
5752 userp = fileLock->userp;
5754 key = fileLock->key;
5755 cm_HoldSCacheNoLock(scp);
5756 lock_ReleaseWrite(&cm_scacheLock);
5757 cm_UnlockByKey(scp, key, 0, userp, &req);
5758 cm_ReleaseSCache(scp);
5759 cm_ReleaseUser(userp);
5760 lock_ReleaseWrite(&scp->rw);