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;
900 cm_volume_t *volp = NULL;
909 if (scp->mountRootFid.cell != 0 && scp->mountRootGen >= cm_data.mountRootGen) {
910 tfid = scp->mountRootFid;
911 lock_ReleaseWrite(&scp->rw);
912 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
913 lock_ObtainWrite(&scp->rw);
917 /* parse the volume name */
918 mpNamep = scp->mountPointStringp;
920 return CM_ERROR_NOSUCHPATH;
921 tlen = cm_FsStrLen(scp->mountPointStringp);
922 mtType = *scp->mountPointStringp;
924 cp = cm_FsStrChr(mpNamep, _FS(':'));
926 /* cellular mount point */
927 cellNamep = (fschar_t *)malloc((cp - mpNamep) * sizeof(fschar_t));
928 cm_FsStrCpyN(cellNamep, cp - mpNamep, mpNamep + 1, cp - mpNamep - 1);
929 volNamep = cm_FsStrDup(cp+1);
931 /* now look up the cell */
932 lock_ReleaseWrite(&scp->rw);
933 cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE);
934 lock_ObtainWrite(&scp->rw);
937 volNamep = cm_FsStrDup(mpNamep + 1);
939 #ifdef AFS_FREELANCE_CLIENT
941 * Mount points in the Freelance cell should default
942 * to the workstation cell.
944 if (cm_freelanceEnabled &&
945 scp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
946 scp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
948 fschar_t rootCellName[256]="";
949 cm_GetRootCellName(rootCellName);
950 cellp = cm_GetCell(rootCellName, 0);
952 #endif /* AFS_FREELANCE_CLIENT */
953 cellp = cm_FindCellByID(scp->fid.cell, 0);
957 code = CM_ERROR_NOSUCHCELL;
961 vnLength = cm_FsStrLen(volNamep);
962 if (vnLength >= 8 && cm_FsStrCmp(volNamep + vnLength - 7, ".backup") == 0)
963 targetType = BACKVOL;
964 else if (vnLength >= 10
965 && cm_FsStrCmp(volNamep + vnLength - 9, ".readonly") == 0)
970 /* check for backups within backups */
971 if (targetType == BACKVOL
972 && (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
973 == CM_SCACHEFLAG_RO) {
974 code = CM_ERROR_NOSUCHVOLUME;
978 /* now we need to get the volume */
979 lock_ReleaseWrite(&scp->rw);
980 if (cm_VolNameIsID(volNamep)) {
981 code = cm_FindVolumeByID(cellp, atoi(volNamep), userp, reqp,
982 CM_GETVOL_FLAG_CREATE, &volp);
984 code = cm_FindVolumeByName(cellp, volNamep, userp, reqp,
985 CM_GETVOL_FLAG_CREATE, &volp);
987 lock_ObtainWrite(&scp->rw);
990 afs_uint32 cell, volume;
991 cm_vol_state_t *statep;
993 cell = cellp->cellID;
995 /* if the mt pt originates in a .backup volume (not a .readonly)
996 * and FollowBackupPath is active, and if there is a .backup
997 * volume for the target, then use the .backup of the target
998 * instead of the read-write.
1000 if (cm_followBackupPath &&
1001 volp->vol[BACKVOL].ID != 0 &&
1002 (dscp->flags & (CM_SCACHEFLAG_RO|CM_SCACHEFLAG_PURERO)) == CM_SCACHEFLAG_RO &&
1003 (targetType == RWVOL || targetType == ROVOL && volp->vol[ROVOL].ID == 0)
1005 targetType = BACKVOL;
1007 /* if the mt pt is in a read-only volume (not just a
1008 * backup), and if there is a read-only volume for the
1009 * target, and if this is a targetType '#' mount point, use
1010 * the read-only, otherwise use the one specified.
1012 else if (mtType == '#' && targetType == RWVOL &&
1013 (scp->flags & CM_SCACHEFLAG_PURERO) &&
1014 volp->vol[ROVOL].ID != 0) {
1018 lock_ObtainWrite(&volp->rw);
1019 statep = cm_VolumeStateByType(volp, targetType);
1020 volume = statep->ID;
1021 statep->dotdotFid = dscp->fid;
1022 lock_ReleaseWrite(&volp->rw);
1024 /* the rest of the fid is a magic number */
1025 cm_SetFid(&scp->mountRootFid, cell, volume, 1, 1);
1026 scp->mountRootGen = cm_data.mountRootGen;
1028 tfid = scp->mountRootFid;
1029 lock_ReleaseWrite(&scp->rw);
1030 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
1031 lock_ObtainWrite(&scp->rw);
1044 long cm_LookupInternal(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_user_t *userp,
1045 cm_req_t *reqp, cm_scache_t **outScpp)
1048 int dnlcHit = 1; /* did we hit in the dnlc? yes, we did */
1049 cm_scache_t *tscp = NULL;
1050 cm_scache_t *mountedScp;
1051 cm_lookupSearch_t rock;
1053 normchar_t *nnamep = NULL;
1054 fschar_t *fnamep = NULL;
1058 memset(&rock, 0, sizeof(rock));
1060 if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
1061 && cm_ClientStrCmp(cnamep, _C("..")) == 0) {
1062 if (dscp->dotdotFid.volume == 0)
1063 return CM_ERROR_NOSUCHVOLUME;
1064 rock.fid = dscp->dotdotFid;
1066 } else if (cm_ClientStrCmp(cnamep, _C(".")) == 0) {
1067 rock.fid = dscp->fid;
1071 nnamep = cm_ClientStringToNormStringAlloc(cnamep, -1, NULL);
1073 code = CM_ERROR_NOSUCHFILE;
1076 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
1078 code = CM_ERROR_NOSUCHFILE;
1082 if (flags & CM_FLAG_NOMOUNTCHASE) {
1083 /* In this case, we should go and call cm_Dir* functions
1084 directly since the following cm_ApplyDir() function will
1092 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ, &dirop);
1095 code = cm_BPlusDirLookup(&dirop, nnamep, &rock.fid);
1100 code = cm_DirLookup(&dirop, fnamep, &rock.fid);
1102 cm_EndDirOp(&dirop);
1112 if (code == CM_ERROR_INEXACT_MATCH && (flags & CM_FLAG_CASEFOLD)) {
1119 return CM_ERROR_BPLUS_NOMATCH;
1124 rock.fid.cell = dscp->fid.cell;
1125 rock.fid.volume = dscp->fid.volume;
1126 rock.searchNamep = fnamep;
1127 rock.nsearchNamep = nnamep;
1128 rock.caseFold = (flags & CM_FLAG_CASEFOLD);
1129 rock.hasTilde = ((cm_ClientStrChr(cnamep, '~') != NULL) ? 1 : 0);
1131 /* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
1132 code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
1133 (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
1135 /* code == 0 means we fell off the end of the dir, while stopnow means
1136 * that we stopped early, probably because we found the entry we're
1137 * looking for. Any other non-zero code is an error.
1139 if (code && code != CM_ERROR_STOPNOW) {
1140 /* if the cm_scache_t we are searching in is not a directory
1141 * we must return path not found because the error
1142 * is to describe the final component not an intermediary
1144 if (code == CM_ERROR_NOTDIR) {
1145 if (flags & CM_FLAG_CHECKPATH)
1146 code = CM_ERROR_NOSUCHPATH;
1148 code = CM_ERROR_NOSUCHFILE;
1153 getroot = (dscp==cm_data.rootSCachep) ;
1155 if (!cm_freelanceEnabled || !getroot) {
1156 if (flags & CM_FLAG_CHECKPATH)
1157 code = CM_ERROR_NOSUCHPATH;
1159 code = CM_ERROR_NOSUCHFILE;
1162 else if (!cm_ClientStrChr(cnamep, '#') &&
1163 !cm_ClientStrChr(cnamep, '%') &&
1164 cm_ClientStrCmpI(cnamep, _C("srvsvc")) &&
1165 cm_ClientStrCmpI(cnamep, _C("wkssvc")) &&
1166 cm_ClientStrCmpI(cnamep, _C("ipc$")))
1168 /* nonexistent dir on freelance root, so add it */
1169 fschar_t fullname[CELL_MAXNAMELEN + 1] = "."; /* +1 so that when we skip the . the size is still CELL_MAXNAMELEN */
1172 osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %S",
1173 osi_LogSaveClientString(afsd_logp,cnamep));
1176 * There is an ugly behavior where a share name "foo" will be searched
1177 * for as "fo". If the searched for name differs by an already existing
1178 * symlink or mount point in the Freelance directory, do not add the
1179 * new value automatically.
1183 if (cnamep[0] == '.') {
1184 if (cm_GetCell_Gen(&fnamep[1], &fullname[1], CM_FLAG_CREATE)) {
1186 if (!cm_FreelanceMountPointExists(fullname, 0))
1187 code = cm_FreelanceAddMount(fullname, &fullname[1], "root.cell.",
1189 if ( cm_FsStrCmpI(&fnamep[1], &fullname[1]) &&
1190 !cm_FreelanceMountPointExists(fnamep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0) &&
1191 !cm_FreelanceSymlinkExists(fnamep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0))
1192 code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
1195 if (cm_GetCell_Gen(fnamep, fullname, CM_FLAG_CREATE)) {
1197 if (!cm_FreelanceMountPointExists(fullname, 0))
1198 code = cm_FreelanceAddMount(fullname, fullname, "root.cell.", 0, &rock.fid);
1199 if ( cm_FsStrCmpI(fnamep, fullname) &&
1200 !cm_FreelanceMountPointExists(fnamep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0) &&
1201 !cm_FreelanceSymlinkExists(fnamep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0))
1202 code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
1205 if (!found || code) { /* add mount point failed, so give up */
1206 if (flags & CM_FLAG_CHECKPATH)
1207 code = CM_ERROR_NOSUCHPATH;
1209 code = CM_ERROR_NOSUCHFILE;
1212 tscp = NULL; /* to force call of cm_GetSCache */
1214 if (flags & CM_FLAG_CHECKPATH)
1215 code = CM_ERROR_NOSUCHPATH;
1217 code = CM_ERROR_NOSUCHFILE;
1223 if ( !tscp ) /* we did not find it in the dnlc */
1226 code = cm_GetSCache(&rock.fid, &tscp, userp, reqp);
1230 /* tscp is now held */
1232 lock_ObtainWrite(&tscp->rw);
1233 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1234 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1236 lock_ReleaseWrite(&tscp->rw);
1237 cm_ReleaseSCache(tscp);
1240 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1241 /* tscp is now locked */
1243 if (!(flags & CM_FLAG_NOMOUNTCHASE)
1244 && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
1245 /* mount points are funny: they have a volume name to mount
1248 code = cm_ReadMountPoint(tscp, userp, reqp);
1250 code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
1252 lock_ReleaseWrite(&tscp->rw);
1253 cm_ReleaseSCache(tscp);
1260 lock_ReleaseWrite(&tscp->rw);
1263 /* copy back pointer */
1266 /* insert scache in dnlc */
1267 if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
1268 /* lock the directory entry to prevent racing callback revokes */
1269 lock_ObtainRead(&dscp->rw);
1270 if ( dscp->cbServerp != NULL && dscp->cbExpires > 0 ) {
1271 /* TODO: reuse nnamep from above */
1274 nnamep = cm_ClientStringToNormStringAlloc(cnamep, -1, NULL);
1276 cm_dnlcEnter(dscp, nnamep, tscp);
1278 lock_ReleaseRead(&dscp->rw);
1295 int cm_ExpandSysName(clientchar_t *inp, clientchar_t *outp, long outSizeCch, unsigned int index)
1300 tp = cm_ClientStrRChr(inp, '@');
1302 return 0; /* no @sys */
1304 if (cm_ClientStrCmp(tp, _C("@sys")) != 0)
1305 return 0; /* no @sys */
1307 /* caller just wants to know if this is a valid @sys type of name */
1311 if (index >= cm_sysNameCount)
1314 /* otherwise generate the properly expanded @sys name */
1315 prefixCount = (int)(tp - inp);
1317 cm_ClientStrCpyN(outp, outSizeCch, inp, prefixCount); /* copy out "a." from "a.@sys" */
1318 outp[prefixCount] = 0; /* null terminate the "a." */
1319 cm_ClientStrCat(outp, outSizeCch, cm_sysNameList[index]);/* append i386_nt40 */
1323 long cm_EvaluateVolumeReference(clientchar_t * namep, long flags, cm_user_t * userp,
1324 cm_req_t *reqp, cm_scache_t ** outScpp)
1326 afs_uint32 code = 0;
1327 fschar_t cellName[CELL_MAXNAMELEN];
1328 fschar_t volumeName[VL_MAXNAMELEN];
1332 fschar_t * fnamep = NULL;
1334 cm_cell_t * cellp = NULL;
1335 cm_volume_t * volp = NULL;
1339 int mountType = RWVOL;
1341 osi_Log1(afsd_logp, "cm_EvaluateVolumeReference for string [%S]",
1342 osi_LogSaveClientString(afsd_logp, namep));
1344 if (cm_ClientStrCmpNI(namep, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH) != 0) {
1345 goto _exit_invalid_path;
1348 /* namep is assumed to look like the following:
1350 @vol:<cellname>%<volume>\0
1352 @vol:<cellname>#<volume>\0
1356 fnamep = cm_ClientStringToFsStringAlloc(namep, -1, NULL);
1357 cp = fnamep + CM_PREFIX_VOL_CCH; /* cp points to cell name, hopefully */
1358 tp = cm_FsStrChr(cp, '%');
1360 tp = cm_FsStrChr(cp, '#');
1362 (len = tp - cp) == 0 ||
1363 len > CELL_MAXNAMELEN)
1364 goto _exit_invalid_path;
1365 cm_FsStrCpyN(cellName, lengthof(cellName), cp, len);
1370 cp = tp+1; /* cp now points to volume, supposedly */
1371 cm_FsStrCpy(volumeName, lengthof(volumeName), cp);
1373 /* OK, now we have the cell and the volume */
1374 osi_Log2(afsd_logp, " Found cell [%s] and volume [%s]",
1375 osi_LogSaveFsString(afsd_logp, cellName),
1376 osi_LogSaveFsString(afsd_logp, volumeName));
1378 cellp = cm_GetCell(cellName, CM_FLAG_CREATE);
1379 if (cellp == NULL) {
1380 goto _exit_invalid_path;
1383 len = cm_FsStrLen(volumeName);
1384 if (len >= 8 && cm_FsStrCmp(volumeName + len - 7, ".backup") == 0)
1386 else if (len >= 10 &&
1387 cm_FsStrCmp(volumeName + len - 9, ".readonly") == 0)
1392 if (cm_VolNameIsID(volumeName)) {
1393 code = cm_FindVolumeByID(cellp, atoi(volumeName), userp, reqp,
1394 CM_GETVOL_FLAG_CREATE, &volp);
1396 code = cm_FindVolumeByName(cellp, volumeName, userp, reqp,
1397 CM_GETVOL_FLAG_CREATE, &volp);
1403 if (volType == BACKVOL)
1404 volume = volp->vol[BACKVOL].ID;
1405 else if (volType == ROVOL ||
1406 (volType == RWVOL && mountType == ROVOL && volp->vol[ROVOL].ID != 0))
1407 volume = volp->vol[ROVOL].ID;
1409 volume = volp->vol[RWVOL].ID;
1411 cm_SetFid(&fid, cellp->cellID, volume, 1, 1);
1413 code = cm_GetSCache(&fid, outScpp, userp, reqp);
1426 if (flags & CM_FLAG_CHECKPATH)
1427 return CM_ERROR_NOSUCHPATH;
1429 return CM_ERROR_NOSUCHFILE;
1432 #ifdef DEBUG_REFCOUNT
1433 long cm_LookupDbg(cm_scache_t *dscp, clientchar_t *namep, long flags, cm_user_t *userp,
1434 cm_req_t *reqp, cm_scache_t **outScpp, char * file, long line)
1436 long cm_Lookup(cm_scache_t *dscp, clientchar_t *namep, long flags, cm_user_t *userp,
1437 cm_req_t *reqp, cm_scache_t **outScpp)
1441 clientchar_t tname[AFSPATHMAX];
1442 int sysNameIndex = 0;
1443 cm_scache_t *scp = NULL;
1445 #ifdef DEBUG_REFCOUNT
1446 afsi_log("%s:%d cm_Lookup dscp 0x%p ref %d", file, line, dscp, dscp->refCount, file, line);
1447 osi_Log2(afsd_logp, "cm_Lookup dscp 0x%p ref %d", dscp, dscp->refCount);
1450 if ( cm_ClientStrCmpI(namep,_C(SMB_IOCTL_FILENAME_NOSLASH)) == 0 ) {
1451 if (flags & CM_FLAG_CHECKPATH)
1452 return CM_ERROR_NOSUCHPATH;
1454 return CM_ERROR_NOSUCHFILE;
1457 if (dscp == cm_data.rootSCachep &&
1458 cm_ClientStrCmpNI(namep, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH) == 0) {
1459 return cm_EvaluateVolumeReference(namep, flags, userp, reqp, outScpp);
1462 if (cm_ExpandSysName(namep, NULL, 0, 0) > 0) {
1463 for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
1464 code = cm_ExpandSysName(namep, tname, lengthof(tname), sysNameIndex);
1466 code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
1467 #ifdef DEBUG_REFCOUNT
1468 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);
1469 osi_Log3(afsd_logp, "cm_LookupInternal (1) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1477 cm_ReleaseSCache(scp);
1481 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1482 #ifdef DEBUG_REFCOUNT
1483 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);
1484 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1491 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1492 #ifdef DEBUG_REFCOUNT
1493 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);
1494 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1500 /* None of the possible sysName expansions could be found */
1501 if (flags & CM_FLAG_CHECKPATH)
1502 return CM_ERROR_NOSUCHPATH;
1504 return CM_ERROR_NOSUCHFILE;
1507 /*! \brief Unlink a file name
1509 Encapsulates a call to RXAFS_RemoveFile().
1511 \param[in] dscp cm_scache_t pointing at the directory containing the
1512 name to be unlinked.
1514 \param[in] fnamep Original name to be unlinked. This is the
1515 name that will be passed into the RXAFS_RemoveFile() call.
1516 This parameter is optional. If not provided, the value will
1519 \param[in] came Client name to be unlinked. This name will be used
1520 to update the local directory caches.
1522 \param[in] userp cm_user_t for the request.
1524 \param[in] reqp Request tracker.
1527 long cm_Unlink(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t * cnamep,
1528 cm_user_t *userp, cm_req_t *reqp)
1534 AFSFetchStatus newDirStatus;
1536 struct rx_connection * rxconnp;
1538 cm_scache_t *scp = NULL;
1539 int free_fnamep = FALSE;
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 /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
2301 * make the calls to create the entries. Handle AFSCBMAX files at a
2304 for (filex = 0; filex < bbp->counter; filex += filesThisCall) {
2305 filesThisCall = bbp->counter - filex;
2306 if (filesThisCall > AFSCBMAX)
2307 filesThisCall = AFSCBMAX;
2309 fidStruct.AFSCBFids_len = filesThisCall;
2310 fidStruct.AFSCBFids_val = &bbp->fids[filex];
2311 statStruct.AFSBulkStats_len = filesThisCall;
2312 statStruct.AFSBulkStats_val = &bbp->stats[filex];
2313 callbackStruct.AFSCBs_len = filesThisCall;
2314 callbackStruct.AFSCBs_val = &bbp->callbacks[filex];
2315 cm_StartCallbackGrantingCall(NULL, &cbReq);
2316 osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
2318 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2322 rxconnp = cm_GetRxConn(connp);
2323 if (!(connp->serverp->flags & CM_SERVERFLAG_NOINLINEBULK)) {
2324 code = RXAFS_InlineBulkStatus(rxconnp, &fidStruct,
2325 &statStruct, &callbackStruct, &volSync);
2326 if (code == RXGEN_OPCODE) {
2327 cm_SetServerNoInlineBulk(connp->serverp, 0);
2333 code = RXAFS_BulkStatus(rxconnp, &fidStruct,
2334 &statStruct, &callbackStruct, &volSync);
2336 rx_PutConnection(rxconnp);
2338 } while (cm_Analyze(connp, userp, reqp, &dscp->fid,
2339 &volSync, NULL, &cbReq, code));
2340 code = cm_MapRPCError(code, reqp);
2342 osi_Log2(afsd_logp, "CALL %sBulkStatus FAILURE code 0x%x",
2343 inlinebulk ? "Inline" : "", code);
2345 osi_Log1(afsd_logp, "CALL %sBulkStatus SUCCESS", inlinebulk ? "Inline" : "");
2347 /* may as well quit on an error, since we're not going to do
2348 * much better on the next immediate call, either.
2351 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2355 /* otherwise, we should do the merges */
2356 for (i = 0; i<filesThisCall; i++) {
2358 cm_SetFid(&tfid, dscp->fid.cell, bbp->fids[j].Volume, bbp->fids[j].Vnode, bbp->fids[j].Unique);
2359 code = cm_GetSCache(&tfid, &scp, userp, reqp);
2363 /* otherwise, if this entry has no callback info,
2366 lock_ObtainWrite(&scp->rw);
2367 /* now, we have to be extra paranoid on merging in this
2368 * information, since we didn't use cm_SyncOp before
2369 * starting the fetch to make sure that no bad races
2370 * were occurring. Specifically, we need to make sure
2371 * we don't obliterate any newer information in the
2372 * vnode than have here.
2374 * Right now, be pretty conservative: if there's a
2375 * callback or a pending call, skip it.
2377 if ((scp->cbServerp == NULL || (scp->flags & CM_SCACHEFLAG_EACCESS))
2379 (CM_SCACHEFLAG_FETCHING
2380 | CM_SCACHEFLAG_STORING
2381 | CM_SCACHEFLAG_SIZESTORING))) {
2382 cm_EndCallbackGrantingCall(scp, &cbReq,
2384 CM_CALLBACK_MAINTAINCOUNT);
2385 cm_MergeStatus(dscp, scp, &bbp->stats[j], &volSync, userp, reqp, 0);
2387 lock_ReleaseWrite(&scp->rw);
2388 cm_ReleaseSCache(scp);
2389 } /* all files in the response */
2390 /* now tell it to drop the count,
2391 * after doing the vnode processing above */
2392 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2393 } /* while there are still more files to process */
2395 /* If we did the InlineBulk RPC pull out the return code and log it */
2397 if ((&bbp->stats[0])->errorCode) {
2398 osi_Log1(afsd_logp, "cm_TryBulkStat bulk stat error: %d",
2399 (&bbp->stats[0])->errorCode);
2400 code = (&bbp->stats[0])->errorCode;
2407 /* called with a write locked scp and a pointer to a buffer. Make bulk stat
2408 * calls on all undeleted files in the page of the directory specified.
2411 cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
2417 osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%p", dscp);
2419 /* should be on a buffer boundary */
2420 osi_assertx((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0, "invalid offset");
2422 bbp = malloc(sizeof(cm_bulkStat_t));
2423 memset(bbp, 0, sizeof(cm_bulkStat_t));
2424 bbp->bufOffset = *offsetp;
2426 lock_ReleaseWrite(&dscp->rw);
2427 /* first, assemble the file IDs we need to stat */
2428 code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) bbp, offsetp, userp, reqp, NULL);
2430 /* if we failed, bail out early */
2431 if (code && code != CM_ERROR_STOPNOW) {
2433 lock_ObtainWrite(&dscp->rw);
2437 code = cm_TryBulkStatRPC(dscp, bbp, userp, reqp);
2438 osi_Log1(afsd_logp, "END cm_TryBulkStat code 0x%x", code);
2440 lock_ObtainWrite(&dscp->rw);
2445 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
2449 /* initialize store back mask as inexpensive local variable */
2451 memset(statusp, 0, sizeof(AFSStoreStatus));
2453 /* copy out queued info from scache first, if scp passed in */
2455 if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
2456 statusp->ClientModTime = scp->clientModTime;
2457 mask |= AFS_SETMODTIME;
2458 scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
2463 /* now add in our locally generated request */
2464 if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
2465 statusp->ClientModTime = attrp->clientModTime;
2466 mask |= AFS_SETMODTIME;
2468 if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
2469 statusp->UnixModeBits = attrp->unixModeBits;
2470 mask |= AFS_SETMODE;
2472 if (attrp->mask & CM_ATTRMASK_OWNER) {
2473 statusp->Owner = attrp->owner;
2474 mask |= AFS_SETOWNER;
2476 if (attrp->mask & CM_ATTRMASK_GROUP) {
2477 statusp->Group = attrp->group;
2478 mask |= AFS_SETGROUP;
2481 statusp->Mask = mask;
2484 /* set the file size, and make sure that all relevant buffers have been
2485 * truncated. Ensure that any partially truncated buffers have been zeroed
2486 * to the end of the buffer.
2488 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
2494 /* start by locking out buffer creation */
2495 lock_ObtainWrite(&scp->bufCreateLock);
2497 /* verify that this is a file, not a dir or a symlink */
2498 lock_ObtainWrite(&scp->rw);
2499 code = cm_SyncOp(scp, NULL, userp, reqp, 0,
2500 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2503 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2505 if (scp->fileType != CM_SCACHETYPE_FILE) {
2506 code = CM_ERROR_ISDIR;
2511 if (LargeIntegerLessThan(*sizep, scp->length))
2516 lock_ReleaseWrite(&scp->rw);
2518 /* can't hold scp->rw lock here, since we may wait for a storeback to
2519 * finish if the buffer package is cleaning a buffer by storing it to
2523 buf_Truncate(scp, userp, reqp, sizep);
2525 /* now ensure that file length is short enough, and update truncPos */
2526 lock_ObtainWrite(&scp->rw);
2528 /* make sure we have a callback (so we have the right value for the
2529 * length), and wait for it to be safe to do a truncate.
2531 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
2532 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2533 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2535 /* If we only have 'i' bits, then we should still be able to set
2536 the size of a file we created. */
2537 if (code == CM_ERROR_NOACCESS && scp->creator == userp) {
2538 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
2539 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2540 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2546 if (LargeIntegerLessThan(*sizep, scp->length)) {
2547 /* a real truncation. If truncPos is not set yet, or is bigger
2548 * than where we're truncating the file, set truncPos to this
2553 if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
2554 || LargeIntegerLessThan(*sizep, scp->length)) {
2556 scp->truncPos = *sizep;
2557 scp->mask |= CM_SCACHEMASK_TRUNCPOS;
2559 /* in either case, the new file size has been changed */
2560 scp->length = *sizep;
2561 scp->mask |= CM_SCACHEMASK_LENGTH;
2563 else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
2564 /* really extending the file */
2565 scp->length = *sizep;
2566 scp->mask |= CM_SCACHEMASK_LENGTH;
2569 /* done successfully */
2572 cm_SyncOpDone(scp, NULL,
2573 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2574 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2577 lock_ReleaseWrite(&scp->rw);
2578 lock_ReleaseWrite(&scp->bufCreateLock);
2583 /* set the file size or other attributes (but not both at once) */
2584 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
2588 AFSFetchStatus afsOutStatus;
2592 AFSStoreStatus afsInStatus;
2593 struct rx_connection * rxconnp;
2595 /* handle file length setting */
2596 if (attrp->mask & CM_ATTRMASK_LENGTH)
2597 return cm_SetLength(scp, &attrp->length, userp, reqp);
2599 lock_ObtainWrite(&scp->rw);
2600 /* otherwise, we have to make an RPC to get the status */
2601 code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
2603 lock_ReleaseWrite(&scp->rw);
2606 lock_ConvertWToR(&scp->rw);
2608 /* make the attr structure */
2609 cm_StatusFromAttr(&afsInStatus, scp, attrp);
2611 tfid.Volume = scp->fid.volume;
2612 tfid.Vnode = scp->fid.vnode;
2613 tfid.Unique = scp->fid.unique;
2614 lock_ReleaseRead(&scp->rw);
2616 /* now make the RPC */
2617 osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%p", scp);
2619 code = cm_ConnFromFID(&scp->fid, userp, reqp, &connp);
2623 rxconnp = cm_GetRxConn(connp);
2624 code = RXAFS_StoreStatus(rxconnp, &tfid,
2625 &afsInStatus, &afsOutStatus, &volSync);
2626 rx_PutConnection(rxconnp);
2628 } while (cm_Analyze(connp, userp, reqp,
2629 &scp->fid, &volSync, NULL, NULL, code));
2630 code = cm_MapRPCError(code, reqp);
2633 osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
2635 osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
2637 lock_ObtainWrite(&scp->rw);
2638 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
2640 cm_MergeStatus(NULL, scp, &afsOutStatus, &volSync, userp, reqp,
2641 CM_MERGEFLAG_FORCE|CM_MERGEFLAG_STOREDATA);
2643 /* if we're changing the mode bits, discard the ACL cache,
2644 * since we changed the mode bits.
2646 if (afsInStatus.Mask & AFS_SETMODE)
2647 cm_FreeAllACLEnts(scp);
2648 lock_ReleaseWrite(&scp->rw);
2652 long cm_Create(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
2653 cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
2658 cm_callbackRequest_t cbReq;
2661 cm_scache_t *scp = NULL;
2663 AFSStoreStatus inStatus;
2664 AFSFetchStatus updatedDirStatus;
2665 AFSFetchStatus newFileStatus;
2666 AFSCallBack newFileCallback;
2668 struct rx_connection * rxconnp;
2670 fschar_t * fnamep = NULL;
2672 /* can't create names with @sys in them; must expand it manually first.
2673 * return "invalid request" if they try.
2675 if (cm_ExpandSysName(cnamep, NULL, 0, 0)) {
2676 return CM_ERROR_ATSYS;
2679 #ifdef AFS_FREELANCE_CLIENT
2680 /* Freelance root volume does not hold files */
2681 if (cm_freelanceEnabled &&
2682 dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
2683 dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
2685 return CM_ERROR_NOACCESS;
2687 #endif /* AFS_FREELANCE_CLIENT */
2689 /* before starting the RPC, mark that we're changing the file data, so
2690 * that someone who does a chmod will know to wait until our call
2693 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2694 lock_ObtainWrite(&dscp->rw);
2695 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2696 lock_ReleaseWrite(&dscp->rw);
2698 cm_StartCallbackGrantingCall(NULL, &cbReq);
2700 cm_EndDirOp(&dirop);
2707 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
2709 cm_StatusFromAttr(&inStatus, NULL, attrp);
2711 /* try the RPC now */
2712 osi_Log1(afsd_logp, "CALL CreateFile scp 0x%p", dscp);
2714 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2718 dirAFSFid.Volume = dscp->fid.volume;
2719 dirAFSFid.Vnode = dscp->fid.vnode;
2720 dirAFSFid.Unique = dscp->fid.unique;
2722 rxconnp = cm_GetRxConn(connp);
2723 code = RXAFS_CreateFile(connp->rxconnp, &dirAFSFid, fnamep,
2724 &inStatus, &newAFSFid, &newFileStatus,
2725 &updatedDirStatus, &newFileCallback,
2727 rx_PutConnection(rxconnp);
2729 } while (cm_Analyze(connp, userp, reqp,
2730 &dscp->fid, &volSync, NULL, &cbReq, code));
2731 code = cm_MapRPCError(code, reqp);
2734 osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
2736 osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
2739 lock_ObtainWrite(&dirop.scp->dirlock);
2740 dirop.lockType = CM_DIRLOCK_WRITE;
2742 lock_ObtainWrite(&dscp->rw);
2743 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2745 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
2747 lock_ReleaseWrite(&dscp->rw);
2749 /* now try to create the file's entry, too, but be careful to
2750 * make sure that we don't merge in old info. Since we weren't locking
2751 * out any requests during the file's creation, we may have pretty old
2755 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
2756 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2758 lock_ObtainWrite(&scp->rw);
2759 scp->creator = userp; /* remember who created it */
2760 if (!cm_HaveCallback(scp)) {
2761 cm_MergeStatus(dscp, scp, &newFileStatus, &volSync,
2763 cm_EndCallbackGrantingCall(scp, &cbReq,
2764 &newFileCallback, 0);
2767 lock_ReleaseWrite(&scp->rw);
2771 /* make sure we end things properly */
2773 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2775 if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
2776 cm_DirCreateEntry(&dirop, fnamep, &newFid);
2778 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
2781 cm_EndDirOp(&dirop);
2790 cm_ReleaseSCache(scp);
2795 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
2799 code = buf_CleanVnode(scp, userp, reqp);
2801 lock_ObtainWrite(&scp->rw);
2803 if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
2804 | CM_SCACHEMASK_CLIENTMODTIME
2805 | CM_SCACHEMASK_LENGTH))
2806 code = cm_StoreMini(scp, userp, reqp);
2808 if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) {
2809 code = (scp->flags & CM_SCACHEFLAG_OVERQUOTA) ? CM_ERROR_QUOTA : CM_ERROR_SPACE;
2810 scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE);
2813 lock_ReleaseWrite(&scp->rw);
2818 long cm_MakeDir(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
2819 cm_user_t *userp, cm_req_t *reqp, cm_scache_t **scpp)
2824 cm_callbackRequest_t cbReq;
2827 cm_scache_t *scp = NULL;
2829 AFSStoreStatus inStatus;
2830 AFSFetchStatus updatedDirStatus;
2831 AFSFetchStatus newDirStatus;
2832 AFSCallBack newDirCallback;
2834 struct rx_connection * rxconnp;
2836 fschar_t * fnamep = NULL;
2838 /* can't create names with @sys in them; must expand it manually first.
2839 * return "invalid request" if they try.
2841 if (cm_ExpandSysName(cnamep, NULL, 0, 0)) {
2842 return CM_ERROR_ATSYS;
2845 #ifdef AFS_FREELANCE_CLIENT
2846 /* Freelance root volume does not hold subdirectories */
2847 if (cm_freelanceEnabled &&
2848 dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
2849 dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
2851 return CM_ERROR_NOACCESS;
2853 #endif /* AFS_FREELANCE_CLIENT */
2855 /* before starting the RPC, mark that we're changing the directory
2856 * data, so that someone who does a chmod on the dir will wait until
2857 * our call completes.
2859 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2860 lock_ObtainWrite(&dscp->rw);
2861 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2862 lock_ReleaseWrite(&dscp->rw);
2864 cm_StartCallbackGrantingCall(NULL, &cbReq);
2866 cm_EndDirOp(&dirop);
2873 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
2874 cm_StatusFromAttr(&inStatus, NULL, attrp);
2876 /* try the RPC now */
2877 osi_Log1(afsd_logp, "CALL MakeDir scp 0x%p", dscp);
2879 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2883 dirAFSFid.Volume = dscp->fid.volume;
2884 dirAFSFid.Vnode = dscp->fid.vnode;
2885 dirAFSFid.Unique = dscp->fid.unique;
2887 rxconnp = cm_GetRxConn(connp);
2888 code = RXAFS_MakeDir(connp->rxconnp, &dirAFSFid, fnamep,
2889 &inStatus, &newAFSFid, &newDirStatus,
2890 &updatedDirStatus, &newDirCallback,
2892 rx_PutConnection(rxconnp);
2894 } while (cm_Analyze(connp, userp, reqp,
2895 &dscp->fid, &volSync, NULL, &cbReq, code));
2896 code = cm_MapRPCError(code, reqp);
2899 osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
2901 osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
2904 lock_ObtainWrite(&dirop.scp->dirlock);
2905 dirop.lockType = CM_DIRLOCK_WRITE;
2907 lock_ObtainWrite(&dscp->rw);
2908 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2910 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
2912 lock_ReleaseWrite(&dscp->rw);
2914 /* now try to create the new dir's entry, too, but be careful to
2915 * make sure that we don't merge in old info. Since we weren't locking
2916 * out any requests during the file's creation, we may have pretty old
2920 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
2921 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2923 lock_ObtainWrite(&scp->rw);
2924 if (!cm_HaveCallback(scp)) {
2925 cm_MergeStatus(dscp, scp, &newDirStatus, &volSync,
2927 cm_EndCallbackGrantingCall(scp, &cbReq,
2928 &newDirCallback, 0);
2931 lock_ReleaseWrite(&scp->rw);
2935 /* make sure we end things properly */
2937 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2939 if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
2940 cm_DirCreateEntry(&dirop, fnamep, &newFid);
2942 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
2945 cm_EndDirOp(&dirop);
2953 cm_ReleaseSCache(scp);
2956 /* and return error code */
2960 long cm_Link(cm_scache_t *dscp, clientchar_t *cnamep, cm_scache_t *sscp, long flags,
2961 cm_user_t *userp, cm_req_t *reqp)
2966 AFSFid existingAFSFid;
2967 AFSFetchStatus updatedDirStatus;
2968 AFSFetchStatus newLinkStatus;
2970 struct rx_connection * rxconnp;
2972 fschar_t * fnamep = NULL;
2974 if (dscp->fid.cell != sscp->fid.cell ||
2975 dscp->fid.volume != sscp->fid.volume) {
2976 return CM_ERROR_CROSSDEVLINK;
2979 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2980 lock_ObtainWrite(&dscp->rw);
2981 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2982 lock_ReleaseWrite(&dscp->rw);
2984 cm_EndDirOp(&dirop);
2989 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
2991 /* try the RPC now */
2992 osi_Log1(afsd_logp, "CALL Link scp 0x%p", dscp);
2994 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2997 dirAFSFid.Volume = dscp->fid.volume;
2998 dirAFSFid.Vnode = dscp->fid.vnode;
2999 dirAFSFid.Unique = dscp->fid.unique;
3001 existingAFSFid.Volume = sscp->fid.volume;
3002 existingAFSFid.Vnode = sscp->fid.vnode;
3003 existingAFSFid.Unique = sscp->fid.unique;
3005 rxconnp = cm_GetRxConn(connp);
3006 code = RXAFS_Link(rxconnp, &dirAFSFid, fnamep, &existingAFSFid,
3007 &newLinkStatus, &updatedDirStatus, &volSync);
3008 rx_PutConnection(rxconnp);
3009 osi_Log1(afsd_logp," RXAFS_Link returns 0x%x", code);
3011 } while (cm_Analyze(connp, userp, reqp,
3012 &dscp->fid, &volSync, NULL, NULL, code));
3014 code = cm_MapRPCError(code, reqp);
3017 osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
3019 osi_Log0(afsd_logp, "CALL Link SUCCESS");
3022 lock_ObtainWrite(&dirop.scp->dirlock);
3023 dirop.lockType = CM_DIRLOCK_WRITE;
3025 lock_ObtainWrite(&dscp->rw);
3026 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3028 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3030 lock_ReleaseWrite(&dscp->rw);
3033 if (cm_CheckDirOpForSingleChange(&dirop)) {
3034 cm_DirCreateEntry(&dirop, fnamep, &sscp->fid);
3036 cm_BPlusDirCreateEntry(&dirop, cnamep, &sscp->fid);
3040 cm_EndDirOp(&dirop);
3042 /* Update the linked object status */
3044 lock_ObtainWrite(&sscp->rw);
3045 cm_MergeStatus(NULL, sscp, &newLinkStatus, &volSync, userp, reqp, 0);
3046 lock_ReleaseWrite(&sscp->rw);
3054 long cm_SymLink(cm_scache_t *dscp, clientchar_t *cnamep, fschar_t *contentsp, long flags,
3055 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
3063 AFSStoreStatus inStatus;
3064 AFSFetchStatus updatedDirStatus;
3065 AFSFetchStatus newLinkStatus;
3067 struct rx_connection * rxconnp;
3069 fschar_t *fnamep = NULL;
3071 /* before starting the RPC, mark that we're changing the directory data,
3072 * so that someone who does a chmod on the dir will wait until our
3075 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
3076 lock_ObtainWrite(&dscp->rw);
3077 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3078 lock_ReleaseWrite(&dscp->rw);
3080 cm_EndDirOp(&dirop);
3085 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3087 cm_StatusFromAttr(&inStatus, NULL, attrp);
3089 /* try the RPC now */
3090 osi_Log1(afsd_logp, "CALL Symlink scp 0x%p", dscp);
3092 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3096 dirAFSFid.Volume = dscp->fid.volume;
3097 dirAFSFid.Vnode = dscp->fid.vnode;
3098 dirAFSFid.Unique = dscp->fid.unique;
3100 rxconnp = cm_GetRxConn(connp);
3101 code = RXAFS_Symlink(rxconnp, &dirAFSFid, fnamep, contentsp,
3102 &inStatus, &newAFSFid, &newLinkStatus,
3103 &updatedDirStatus, &volSync);
3104 rx_PutConnection(rxconnp);
3106 } while (cm_Analyze(connp, userp, reqp,
3107 &dscp->fid, &volSync, NULL, NULL, code));
3108 code = cm_MapRPCError(code, reqp);
3111 osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
3113 osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
3116 lock_ObtainWrite(&dirop.scp->dirlock);
3117 dirop.lockType = CM_DIRLOCK_WRITE;
3119 lock_ObtainWrite(&dscp->rw);
3120 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3122 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3124 lock_ReleaseWrite(&dscp->rw);
3127 if (cm_CheckDirOpForSingleChange(&dirop)) {
3128 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3130 cm_DirCreateEntry(&dirop, fnamep, &newFid);
3132 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
3136 cm_EndDirOp(&dirop);
3138 /* now try to create the new dir's entry, too, but be careful to
3139 * make sure that we don't merge in old info. Since we weren't locking
3140 * out any requests during the file's creation, we may have pretty old
3144 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3145 code = cm_GetSCache(&newFid, &scp, userp, reqp);
3147 lock_ObtainWrite(&scp->rw);
3148 if (!cm_HaveCallback(scp)) {
3149 cm_MergeStatus(dscp, scp, &newLinkStatus, &volSync,
3152 lock_ReleaseWrite(&scp->rw);
3153 cm_ReleaseSCache(scp);
3159 /* and return error code */
3163 /*! \brief Remove a directory
3165 Encapsulates a call to RXAFS_RemoveDir().
3167 \param[in] dscp cm_scache_t for the directory containing the
3168 directory to be removed.
3170 \param[in] fnamep This will be the original name of the directory
3171 as known to the file server. It will be passed in to RXAFS_RemoveDir().
3172 This parameter is optional. If it is not provided the value
3175 \param[in] cnamep Normalized name used to update the local
3178 \param[in] userp cm_user_t for the request.
3180 \param[in] reqp Request tracker.
3182 long cm_RemoveDir(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t *cnamep, cm_user_t *userp, cm_req_t *reqp)
3188 AFSFetchStatus updatedDirStatus;
3190 struct rx_connection * rxconnp;
3192 cm_scache_t *scp = NULL;
3193 int free_fnamep = FALSE;
3195 if (fnamep == NULL) {
3198 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ, &dirop);
3200 code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
3203 cm_EndDirOp(&dirop);
3210 code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
3214 /* before starting the RPC, mark that we're changing the directory data,
3215 * so that someone who does a chmod on the dir will wait until our
3218 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
3219 lock_ObtainWrite(&dscp->rw);
3220 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3221 lock_ReleaseWrite(&dscp->rw);
3223 cm_EndDirOp(&dirop);
3228 /* try the RPC now */
3229 osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%p", dscp);
3231 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3235 dirAFSFid.Volume = dscp->fid.volume;
3236 dirAFSFid.Vnode = dscp->fid.vnode;
3237 dirAFSFid.Unique = dscp->fid.unique;
3239 rxconnp = cm_GetRxConn(connp);
3240 code = RXAFS_RemoveDir(rxconnp, &dirAFSFid, fnamep,
3241 &updatedDirStatus, &volSync);
3242 rx_PutConnection(rxconnp);
3244 } while (cm_Analyze(connp, userp, reqp,
3245 &dscp->fid, &volSync, NULL, NULL, code));
3246 code = cm_MapRPCErrorRmdir(code, reqp);
3249 osi_Log1(afsd_logp, "CALL RemoveDir FAILURE, code 0x%x", code);
3251 osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
3254 lock_ObtainWrite(&dirop.scp->dirlock);
3255 dirop.lockType = CM_DIRLOCK_WRITE;
3257 lock_ObtainWrite(&dscp->rw);
3258 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3260 cm_dnlcRemove(dscp, cnamep);
3261 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3263 lock_ReleaseWrite(&dscp->rw);
3266 if (cm_CheckDirOpForSingleChange(&dirop) && cnamep != NULL) {
3267 cm_DirDeleteEntry(&dirop, fnamep);
3269 cm_BPlusDirDeleteEntry(&dirop, cnamep);
3273 cm_EndDirOp(&dirop);
3276 cm_ReleaseSCache(scp);
3278 lock_ObtainWrite(&scp->rw);
3279 scp->flags |= CM_SCACHEFLAG_DELETED;
3280 lock_ReleaseWrite(&scp->rw);
3288 /* and return error code */
3292 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
3294 /* grab mutex on contents */
3295 lock_ObtainWrite(&scp->rw);
3297 /* reset the prefetch info */
3298 scp->prefetch.base.LowPart = 0; /* base */
3299 scp->prefetch.base.HighPart = 0;
3300 scp->prefetch.end.LowPart = 0; /* and end */
3301 scp->prefetch.end.HighPart = 0;
3303 /* release mutex on contents */
3304 lock_ReleaseWrite(&scp->rw);
3310 /*! \brief Rename a file or directory
3312 Encapsulates a RXAFS_Rename() call.
3314 \param[in] oldDscp cm_scache_t for the directory containing the old
3317 \param[in] oldNamep The original old name known to the file server.
3318 This is the name that will be passed into the RXAFS_Rename().
3319 If it is not provided, it will be looked up.
3321 \param[in] normalizedOldNamep Normalized old name. This is used for
3322 updating local directory caches.
3324 \param[in] newDscp cm_scache_t for the directory containing the new
3327 \param[in] newNamep New name. Normalized.
3329 \param[in] userp cm_user_t for the request.
3331 \param[in,out] reqp Request tracker.
3334 long cm_Rename(cm_scache_t *oldDscp, fschar_t *oldNamep, clientchar_t *cOldNamep,
3335 cm_scache_t *newDscp, clientchar_t *cNewNamep, cm_user_t *userp,
3340 AFSFid oldDirAFSFid;
3341 AFSFid newDirAFSFid;
3343 AFSFetchStatus updatedOldDirStatus;
3344 AFSFetchStatus updatedNewDirStatus;
3347 struct rx_connection * rxconnp;
3348 cm_dirOp_t oldDirOp;
3351 cm_dirOp_t newDirOp;
3352 fschar_t * newNamep = NULL;
3353 int free_oldNamep = FALSE;
3354 cm_scache_t *oldScp = NULL, *newScp = NULL;
3356 if (cOldNamep == NULL || cNewNamep == NULL ||
3357 cm_ClientStrLen(cOldNamep) == 0 ||
3358 cm_ClientStrLen(cNewNamep) == 0)
3359 return CM_ERROR_INVAL;
3362 * Before we permit the operation, make sure that we do not already have
3363 * an object in the destination directory that has a case-insensitive match
3364 * for this name UNLESS the matching object is the object we are renaming.
3366 code = cm_Lookup(oldDscp, cOldNamep, 0, userp, reqp, &oldScp);
3368 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S old name lookup failed",
3369 oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3373 code = cm_Lookup(newDscp, cNewNamep, CM_FLAG_CASEFOLD, userp, reqp, &newScp);
3375 /* found a matching object with the new name */
3376 if (cm_FidCmp(&oldScp->fid, &newScp->fid)) {
3377 /* and they don't match so return an error */
3378 osi_Log2(afsd_logp, "cm_Rename newDscp 0x%p cNewName %S new name already exists",
3379 newDscp, osi_LogSaveStringW(afsd_logp, cNewNamep));
3380 code = CM_ERROR_EXISTS;
3382 cm_ReleaseSCache(newScp);
3384 } else if (code == CM_ERROR_AMBIGUOUS_FILENAME) {
3385 code = CM_ERROR_EXISTS;
3392 if (oldNamep == NULL) {
3395 code = cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_READ, &oldDirOp);
3397 code = cm_BPlusDirLookupOriginalName(&oldDirOp, cOldNamep, &oldNamep);
3399 free_oldNamep = TRUE;
3400 cm_EndDirOp(&oldDirOp);
3404 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S Original Name lookup failed",
3405 oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3411 /* before starting the RPC, mark that we're changing the directory data,
3412 * so that someone who does a chmod on the dir will wait until our call
3413 * completes. We do this in vnode order so that we don't deadlock,
3414 * which makes the code a little verbose.
3416 if (oldDscp == newDscp) {
3417 /* check for identical names */
3418 if (cm_ClientStrCmp(cOldNamep, cNewNamep) == 0) {
3419 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_RENAME_IDENTICAL",
3421 code = CM_ERROR_RENAME_IDENTICAL;
3426 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3427 lock_ObtainWrite(&oldDscp->rw);
3428 cm_dnlcRemove(oldDscp, cOldNamep);
3429 cm_dnlcRemove(oldDscp, cNewNamep);
3430 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3431 CM_SCACHESYNC_STOREDATA);
3432 lock_ReleaseWrite(&oldDscp->rw);
3434 cm_EndDirOp(&oldDirOp);
3438 /* two distinct dir vnodes */
3440 if (oldDscp->fid.cell != newDscp->fid.cell ||
3441 oldDscp->fid.volume != newDscp->fid.volume) {
3442 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_CROSSDEVLINK",
3444 code = CM_ERROR_CROSSDEVLINK;
3448 /* shouldn't happen that we have distinct vnodes for two
3449 * different files, but could due to deliberate attack, or
3450 * stale info. Avoid deadlocks and quit now.
3452 if (oldDscp->fid.vnode == newDscp->fid.vnode) {
3453 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p vnode collision",
3455 code = CM_ERROR_CROSSDEVLINK;
3459 if (oldDscp->fid.vnode < newDscp->fid.vnode) {
3460 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3461 lock_ObtainWrite(&oldDscp->rw);
3462 cm_dnlcRemove(oldDscp, cOldNamep);
3463 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3464 CM_SCACHESYNC_STOREDATA);
3465 lock_ReleaseWrite(&oldDscp->rw);
3467 cm_EndDirOp(&oldDirOp);
3469 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE, &newDirOp);
3470 lock_ObtainWrite(&newDscp->rw);
3471 cm_dnlcRemove(newDscp, cNewNamep);
3472 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3473 CM_SCACHESYNC_STOREDATA);
3474 lock_ReleaseWrite(&newDscp->rw);
3476 cm_EndDirOp(&newDirOp);
3478 /* cleanup first one */
3479 lock_ObtainWrite(&oldDscp->rw);
3480 cm_SyncOpDone(oldDscp, NULL,
3481 CM_SCACHESYNC_STOREDATA);
3482 lock_ReleaseWrite(&oldDscp->rw);
3483 cm_EndDirOp(&oldDirOp);
3488 /* lock the new vnode entry first */
3489 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE, &newDirOp);
3490 lock_ObtainWrite(&newDscp->rw);
3491 cm_dnlcRemove(newDscp, cNewNamep);
3492 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3493 CM_SCACHESYNC_STOREDATA);
3494 lock_ReleaseWrite(&newDscp->rw);
3496 cm_EndDirOp(&newDirOp);
3498 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3499 lock_ObtainWrite(&oldDscp->rw);
3500 cm_dnlcRemove(oldDscp, cOldNamep);
3501 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3502 CM_SCACHESYNC_STOREDATA);
3503 lock_ReleaseWrite(&oldDscp->rw);
3505 cm_EndDirOp(&oldDirOp);
3507 /* cleanup first one */
3508 lock_ObtainWrite(&newDscp->rw);
3509 cm_SyncOpDone(newDscp, NULL,
3510 CM_SCACHESYNC_STOREDATA);
3511 lock_ReleaseWrite(&newDscp->rw);
3512 cm_EndDirOp(&newDirOp);
3516 } /* two distinct vnodes */
3523 newNamep = cm_ClientStringToFsStringAlloc(cNewNamep, -1, NULL);
3525 /* try the RPC now */
3526 osi_Log2(afsd_logp, "CALL Rename old scp 0x%p new scp 0x%p",
3529 code = cm_ConnFromFID(&oldDscp->fid, userp, reqp, &connp);
3533 oldDirAFSFid.Volume = oldDscp->fid.volume;
3534 oldDirAFSFid.Vnode = oldDscp->fid.vnode;
3535 oldDirAFSFid.Unique = oldDscp->fid.unique;
3536 newDirAFSFid.Volume = newDscp->fid.volume;
3537 newDirAFSFid.Vnode = newDscp->fid.vnode;
3538 newDirAFSFid.Unique = newDscp->fid.unique;
3540 rxconnp = cm_GetRxConn(connp);
3541 code = RXAFS_Rename(rxconnp, &oldDirAFSFid, oldNamep,
3542 &newDirAFSFid, newNamep,
3543 &updatedOldDirStatus, &updatedNewDirStatus,
3545 rx_PutConnection(rxconnp);
3547 } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
3548 &volSync, NULL, NULL, code));
3549 code = cm_MapRPCError(code, reqp);
3552 osi_Log1(afsd_logp, "CALL Rename FAILURE, code 0x%x", code);
3554 osi_Log0(afsd_logp, "CALL Rename SUCCESS");
3556 /* update the individual stat cache entries for the directories */
3558 lock_ObtainWrite(&oldDirOp.scp->dirlock);
3559 oldDirOp.lockType = CM_DIRLOCK_WRITE;
3561 lock_ObtainWrite(&oldDscp->rw);
3562 cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
3565 cm_MergeStatus(NULL, oldDscp, &updatedOldDirStatus, &volSync,
3566 userp, reqp, CM_MERGEFLAG_DIROP);
3567 lock_ReleaseWrite(&oldDscp->rw);
3569 if (code == 0 && cm_CheckDirOpForSingleChange(&oldDirOp)) {
3571 diropCode = cm_BPlusDirLookup(&oldDirOp, cOldNamep, &fileFid);
3572 if (diropCode == CM_ERROR_INEXACT_MATCH)
3574 else if (diropCode == EINVAL)
3576 diropCode = cm_DirLookup(&oldDirOp, oldNamep, &fileFid);
3578 if (diropCode == 0) {
3580 diropCode = cm_DirCreateEntry(&oldDirOp, newNamep, &fileFid);
3582 cm_BPlusDirCreateEntry(&oldDirOp, cNewNamep, &fileFid);
3586 if (diropCode == 0) {
3587 diropCode = cm_DirDeleteEntry(&oldDirOp, oldNamep);
3589 cm_BPlusDirDeleteEntry(&oldDirOp, cOldNamep);
3594 cm_EndDirOp(&oldDirOp);
3596 /* and update it for the new one, too, if necessary */
3599 lock_ObtainWrite(&newDirOp.scp->dirlock);
3600 newDirOp.lockType = CM_DIRLOCK_WRITE;
3602 lock_ObtainWrite(&newDscp->rw);
3603 cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
3605 cm_MergeStatus(NULL, newDscp, &updatedNewDirStatus, &volSync,
3606 userp, reqp, CM_MERGEFLAG_DIROP);
3607 lock_ReleaseWrite(&newDscp->rw);
3611 * The following optimization does not work.
3612 * When the file server processed a RXAFS_Rename() request the
3613 * FID of the object being moved between directories is not
3614 * preserved. The client does not know the new FID nor the
3615 * version number of the target. Not only can we not create
3616 * the directory entry in the new directory, but we can't
3617 * preserve the cached data for the file. It must be re-read
3618 * from the file server. - jaltman, 2009/02/20
3621 /* we only make the local change if we successfully made
3622 the change in the old directory AND there was only one
3623 change in the new directory */
3624 if (diropCode == 0 && cm_CheckDirOpForSingleChange(&newDirOp)) {
3625 cm_DirCreateEntry(&newDirOp, newNamep, &fileFid);
3627 cm_BPlusDirCreateEntry(&newDirOp, cNewNamep, &fileFid);
3632 cm_EndDirOp(&newDirOp);
3636 * After the rename the file server has invalidated the callbacks
3637 * on the file that was moved nor do we have a directory reference
3640 lock_ObtainWrite(&oldScp->rw);
3641 cm_DiscardSCache(oldScp);
3642 lock_ReleaseWrite(&oldScp->rw);
3646 cm_ReleaseSCache(oldScp);
3653 /* and return error code */
3657 /* Byte range locks:
3659 The OpenAFS Windows client has to fake byte range locks given no
3660 server side support for such locks. This is implemented as keyed
3661 byte range locks on the cache manager.
3663 Keyed byte range locks:
3665 Each cm_scache_t structure keeps track of a list of keyed locks.
3666 The key for a lock identifies an owner of a set of locks (referred
3667 to as a client). Each key is represented by a value. The set of
3668 key values used within a specific cm_scache_t structure form a
3669 namespace that has a scope of just that cm_scache_t structure. The
3670 same key value can be used with another cm_scache_t structure and
3671 correspond to a completely different client. However it is
3672 advantageous for the SMB or IFS layer to make sure that there is a
3673 1-1 mapping between client and keys over all cm_scache_t objects.
3675 Assume a client C has key Key(C) (although, since the scope of the
3676 key is a cm_scache_t, the key can be Key(C,S), where S is the
3677 cm_scache_t. But assume a 1-1 relation between keys and clients).
3678 A byte range (O,+L) denotes byte addresses (O) through (O+L-1)
3679 inclusive (a.k.a. [O,O+L-1]). The function Key(x) is implemented
3680 through cm_generateKey() function for both SMB and IFS.
3682 The list of locks for a cm_scache_t object S is maintained in
3683 S->fileLocks. The cache manager will set a lock on the AFS file
3684 server in order to assert the locks in S->fileLocks. If only
3685 shared locks are in place for S, then the cache manager will obtain
3686 a LockRead lock, while if there are any exclusive locks, it will
3687 obtain a LockWrite lock. If the exclusive locks are all released
3688 while the shared locks remain, then the cache manager will
3689 downgrade the lock from LockWrite to LockRead. Similarly, if an
3690 exclusive lock is obtained when only shared locks exist, then the
3691 cache manager will try to upgrade the lock from LockRead to
3694 Each lock L owned by client C maintains a key L->key such that
3695 L->key == Key(C), the effective range defined by L->LOffset and
3696 L->LLength such that the range of bytes affected by the lock is
3697 (L->LOffset, +L->LLength), a type maintained in L->LockType which
3698 is either exclusive or shared.
3702 A lock exists iff it is in S->fileLocks for some cm_scache_t
3703 S. Existing locks are in one of the following states: ACTIVE,
3704 WAITLOCK, WAITUNLOCK, LOST, DELETED.
3706 The following sections describe each lock and the associated
3709 1. ACTIVE: A lock L is ACTIVE iff the cache manager has asserted
3710 the lock with the AFS file server. This type of lock can be
3711 exercised by a client to read or write to the locked region (as
3714 1.1 ACTIVE->LOST: When the AFS file server fails to extend a
3715 server lock that was required to assert the lock. Before
3716 marking the lock as lost, the cache manager checks if the file
3717 has changed on the server. If the file has not changed, then
3718 the cache manager will attempt to obtain a new server lock
3719 that is sufficient to assert the client side locks for the
3720 file. If any of these fail, the lock is marked as LOST.
3721 Otherwise, it is left as ACTIVE.
3723 1.2 ACTIVE->DELETED: Lock is released.
3725 2. WAITLOCK: A lock is in a WAITLOCK state if the cache manager
3726 grants the lock but the lock is yet to be asserted with the AFS
3727 file server. Once the file server grants the lock, the state
3728 will transition to an ACTIVE lock.
3730 2.1 WAITLOCK->ACTIVE: The server granted the lock.
3732 2.2 WAITLOCK->DELETED: Lock is abandoned, or timed out during
3735 2.3 WAITLOCK->LOST: One or more locks from this client were
3736 marked as LOST. No further locks will be granted to this
3737 client until all lost locks are removed.
3739 3. WAITUNLOCK: A lock is in a WAITUNLOCK state if the cache manager
3740 receives a request for a lock that conflicts with an existing
3741 ACTIVE or WAITLOCK lock. The lock will be placed in the queue
3742 and will be granted at such time the conflicting locks are
3743 removed, at which point the state will transition to either
3746 3.1 WAITUNLOCK->ACTIVE: The conflicting lock was removed. The
3747 current serverLock is sufficient to assert this lock, or a
3748 sufficient serverLock is obtained.
3750 3.2 WAITUNLOCK->WAITLOCK: The conflicting lock was removed,
3751 however the required serverLock is yet to be asserted with the
3754 3.3 WAITUNLOCK->DELETED: The lock is abandoned, timed out or
3757 3.5 WAITUNLOCK->LOST: One or more locks from this client were
3758 marked as LOST. No further locks will be granted to this
3759 client until all lost locks are removed.
3761 4. LOST: A lock L is LOST if the server lock that was required to
3762 assert the lock could not be obtained or if it could not be
3763 extended, or if other locks by the same client were LOST.
3764 Essentially, once a lock is LOST, the contract between the cache
3765 manager and that specific client is no longer valid.
3767 The cache manager rechecks the server lock once every minute and
3768 extends it as appropriate. If this is not done for 5 minutes,
3769 the AFS file server will release the lock (the 5 minute timeout
3770 is based on current file server code and is fairly arbitrary).
3771 Once released, the lock cannot be re-obtained without verifying
3772 that the contents of the file hasn't been modified since the
3773 time the lock was released. Re-obtaining the lock without
3774 verifying this may lead to data corruption. If the lock can not
3775 be obtained safely, then all active locks for the cm_scache_t
3778 4.1 LOST->DELETED: The lock is released.
3780 5. DELETED: The lock is no longer relevant. Eventually, it will
3781 get removed from the cm_scache_t. In the meantime, it will be
3782 treated as if it does not exist.
3784 5.1 DELETED->not exist: The lock is removed from the
3787 The following are classifications of locks based on their state.
3789 6* A lock L is ACCEPTED if it is ACTIVE or WAITLOCK. These locks
3790 have been accepted by the cache manager, but may or may not have
3791 been granted back to the client.
3793 7* A lock L is QUEUED if it is ACTIVE, WAITLOCK or WAITUNLOCK.
3795 8* A lock L is WAITING if it is WAITLOCK or WAITUNLOCK.
3799 A client C can READ range (Offset,+Length) of a file represented by
3800 cm_scache_t S iff (1):
3802 1. for all _a_ in (Offset,+Length), all of the following is true:
3804 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3805 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3808 1.2 For each LOST lock L in S->fileLocks such that _a_ in
3809 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3812 (When locks are lost on an cm_scache_t, all locks are lost. By
3813 4.2 (below), if there is an exclusive LOST lock, then there
3814 can't be any overlapping ACTIVE locks.)
3816 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
3818 2. for all _a_ in (Offset,+Length), one of the following is true:
3820 2.1 Byte _a_ of S is unowned (as specified in 1.1) AND there
3821 does not exist a LOST lock L such that _a_ in
3822 (L->LOffset,+L->LLength).
3824 2.2 Byte _a_ of S is owned by C under lock L (as specified in
3825 1.2) AND L->LockType is exclusive.
3827 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
3829 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
3832 3.1 If L->LockType is exclusive then there does NOT exist a
3833 ACCEPTED lock M in S->fileLocks such that _a_ in
3834 (M->LOffset,+M->LLength).
3836 (If we count all QUEUED locks then we hit cases such as
3837 cascading waiting locks where the locks later on in the queue
3838 can be granted without compromising file integrity. On the
3839 other hand if only ACCEPTED locks are considered, then locks
3840 that were received earlier may end up waiting for locks that
3841 were received later to be unlocked. The choice of ACCEPTED
3842 locks was made to mimic the Windows byte range lock
3845 3.2 If L->LockType is shared then for each ACCEPTED lock M in
3846 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
3847 M->LockType is shared.
3849 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
3851 4.1 M->key != Key(C)
3853 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
3854 and (M->LOffset,+M->LLength) do not intersect.
3856 (Note: If a client loses a lock, it loses all locks.
3857 Subsequently, it will not be allowed to obtain any more locks
3858 until all existing LOST locks that belong to the client are
3859 released. Once all locks are released by a single client,
3860 there exists no further contract between the client and AFS
3861 about the contents of the file, hence the client can then
3862 proceed to obtain new locks and establish a new contract.
3864 This doesn't quite work as you think it should, because most
3865 applications aren't built to deal with losing locks they
3866 thought they once had. For now, we don't have a good
3867 solution to lost locks.
3869 Also, for consistency reasons, we have to hold off on
3870 granting locks that overlap exclusive LOST locks.)
3872 A client C can only unlock locks L in S->fileLocks which have
3875 The representation and invariants are as follows:
3877 - Each cm_scache_t structure keeps:
3879 - A queue of byte-range locks (cm_scache_t::fileLocks) which
3880 are of type cm_file_lock_t.
3882 - A record of the highest server-side lock that has been
3883 obtained for this object (cm_scache_t::serverLock), which is
3884 one of (-1), LockRead, LockWrite.
3886 - A count of ACCEPTED exclusive and shared locks that are in the
3887 queue (cm_scache_t::sharedLocks and
3888 cm_scache_t::exclusiveLocks)
3890 - Each cm_file_lock_t structure keeps:
3892 - The type of lock (cm_file_lock_t::LockType)
3894 - The key associated with the lock (cm_file_lock_t::key)
3896 - The offset and length of the lock (cm_file_lock_t::LOffset
3897 and cm_file_lock_t::LLength)
3899 - The state of the lock.
3901 - Time of issuance or last successful extension
3903 Semantic invariants:
3905 I1. The number of ACCEPTED locks in S->fileLocks are
3906 (S->sharedLocks + S->exclusiveLocks)
3908 External invariants:
3910 I3. S->serverLock is the lock that we have asserted with the
3911 AFS file server for this cm_scache_t.
3913 I4. S->serverLock == LockRead iff there is at least one ACTIVE
3914 shared lock, but no ACTIVE exclusive locks.
3916 I5. S->serverLock == LockWrite iff there is at least one ACTIVE
3919 I6. If L is a LOST lock, then for each lock M in S->fileLocks,
3920 M->key == L->key IMPLIES M is LOST or DELETED.
3925 #define IS_LOCK_ACTIVE(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == 0)
3927 #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)
3929 #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)
3931 #define IS_LOCK_LOST(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_LOST)
3933 #define IS_LOCK_DELETED(lockp) (((lockp)->flags & CM_FILELOCK_FLAG_DELETED) == CM_FILELOCK_FLAG_DELETED)
3936 #define IS_LOCK_ACCEPTED(lockp) (IS_LOCK_ACTIVE(lockp) || IS_LOCK_WAITLOCK(lockp))
3939 #define IS_LOCK_CLIENTONLY(lockp) ((((lockp)->scp->flags & CM_SCACHEFLAG_RO) == CM_SCACHEFLAG_RO) || (((lockp)->flags & CM_FILELOCK_FLAG_CLIENTONLY) == CM_FILELOCK_FLAG_CLIENTONLY))
3942 #define INTERSECT_RANGE(r1,r2) (((r2).offset+(r2).length) > (r1).offset && ((r1).offset +(r1).length) > (r2).offset)
3945 #define CONTAINS_RANGE(r1,r2) (((r2).offset+(r2).length) <= ((r1).offset+(r1).length) && (r1).offset <= (r2).offset)
3947 #if defined(VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS) && !defined(LOCK_TESTING)
3948 #define SCP_SUPPORTS_BRLOCKS(scp) ((scp)->cbServerp && ((scp)->cbServerp->capabilities & VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS))
3950 #define SCP_SUPPORTS_BRLOCKS(scp) (1)
3953 #define SERVERLOCKS_ENABLED(scp) (!((scp)->flags & CM_SCACHEFLAG_RO) && cm_enableServerLocks && SCP_SUPPORTS_BRLOCKS(scp))
3955 #if defined(VICED_CAPABILITY_WRITELOCKACL)
3956 #define SCP_SUPPORTS_WRITELOCKACL(scp) ((scp)->cbServerp && ((scp->cbServerp->capabilities & VICED_CAPABILITY_WRITELOCKACL)))
3958 #define SCP_SUPPORTS_WRITELOCKACL(scp) (0)
3960 /* This should really be defined in any build that this code is being
3962 #error VICED_CAPABILITY_WRITELOCKACL not defined.
3965 static void cm_LockRangeSubtract(cm_range_t * pos, const cm_range_t * neg)
3967 afs_int64 int_begin;
3970 int_begin = MAX(pos->offset, neg->offset);
3971 int_end = MIN(pos->offset+pos->length, neg->offset+neg->length);
3973 if (int_begin < int_end) {
3974 if (int_begin == pos->offset) {
3975 pos->length = pos->offset + pos->length - int_end;
3976 pos->offset = int_end;
3977 } else if (int_end == pos->offset + pos->length) {
3978 pos->length = int_begin - pos->offset;
3981 /* We only subtract ranges if the resulting range is
3982 contiguous. If we try to support non-contigous ranges, we
3983 aren't actually improving performance. */
3987 /* Called with scp->rw held. Returns 0 if all is clear to read the
3988 specified range by the client identified by key.
3990 long cm_LockCheckRead(cm_scache_t *scp,
3991 LARGE_INTEGER LOffset,
3992 LARGE_INTEGER LLength,
3995 #ifndef ADVISORY_LOCKS
3997 cm_file_lock_t *fileLock;
4001 int substract_ranges = FALSE;
4003 range.offset = LOffset.QuadPart;
4004 range.length = LLength.QuadPart;
4008 1. for all _a_ in (Offset,+Length), all of the following is true:
4010 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
4011 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
4014 1.2 For each LOST lock L in S->fileLocks such that _a_ in
4015 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
4020 lock_ObtainRead(&cm_scacheLock);
4022 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
4024 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4026 if (INTERSECT_RANGE(range, fileLock->range)) {
4027 if (IS_LOCK_ACTIVE(fileLock)) {
4028 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4030 /* If there is an active lock for this client, it
4031 is safe to substract ranges.*/
4032 cm_LockRangeSubtract(&range, &fileLock->range);
4033 substract_ranges = TRUE;
4035 if (fileLock->lockType != LockRead) {
4036 code = CM_ERROR_LOCK_CONFLICT;
4040 /* even if the entire range is locked for reading,
4041 we still can't grant the lock at this point
4042 because the client may have lost locks. That
4043 is, unless we have already seen an active lock
4044 belonging to the client, in which case there
4045 can't be any lost locks for this client. */
4046 if (substract_ranges)
4047 cm_LockRangeSubtract(&range, &fileLock->range);
4049 } else if (IS_LOCK_LOST(fileLock) &&
4050 (cm_KeyEquals(&fileLock->key, &key, 0) || fileLock->lockType == LockWrite)) {
4051 code = CM_ERROR_BADFD;
4057 lock_ReleaseRead(&cm_scacheLock);
4059 osi_Log4(afsd_logp, "cm_LockCheckRead scp 0x%x offset %d length %d code 0x%x",
4060 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
4071 /* Called with scp->rw held. Returns 0 if all is clear to write the
4072 specified range by the client identified by key.
4074 long cm_LockCheckWrite(cm_scache_t *scp,
4075 LARGE_INTEGER LOffset,
4076 LARGE_INTEGER LLength,
4079 #ifndef ADVISORY_LOCKS
4081 cm_file_lock_t *fileLock;
4086 range.offset = LOffset.QuadPart;
4087 range.length = LLength.QuadPart;
4090 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
4092 2. for all _a_ in (Offset,+Length), one of the following is true:
4094 2.1 Byte _a_ of S is unowned AND there does not exist a LOST
4095 lock L such that _a_ in (L->LOffset,+L->LLength).
4097 2.2 Byte _a_ of S is owned by C under lock L AND L->LockType is
4101 lock_ObtainRead(&cm_scacheLock);
4103 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
4105 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4107 if (INTERSECT_RANGE(range, fileLock->range)) {
4108 if (IS_LOCK_ACTIVE(fileLock)) {
4109 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4110 if (fileLock->lockType == LockWrite) {
4112 /* if there is an active lock for this client, it
4113 is safe to substract ranges */
4114 cm_LockRangeSubtract(&range, &fileLock->range);
4116 code = CM_ERROR_LOCK_CONFLICT;
4120 code = CM_ERROR_LOCK_CONFLICT;
4123 } else if (IS_LOCK_LOST(fileLock)) {
4124 code = CM_ERROR_BADFD;
4130 lock_ReleaseRead(&cm_scacheLock);
4132 osi_Log4(afsd_logp, "cm_LockCheckWrite scp 0x%x offset %d length %d code 0x%x",
4133 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
4144 /* Called with cm_scacheLock write locked */
4145 static cm_file_lock_t * cm_GetFileLock(void) {
4148 l = (cm_file_lock_t *) cm_freeFileLocks;
4150 osi_QRemove(&cm_freeFileLocks, &l->q);
4152 l = malloc(sizeof(cm_file_lock_t));
4153 osi_assertx(l, "null cm_file_lock_t");
4156 memset(l, 0, sizeof(cm_file_lock_t));
4161 /* Called with cm_scacheLock write locked */
4162 static void cm_PutFileLock(cm_file_lock_t *l) {
4163 osi_QAdd(&cm_freeFileLocks, &l->q);
4166 /* called with scp->rw held. May release it during processing, but
4167 leaves it held on exit. */
4168 long cm_IntSetLock(cm_scache_t * scp, cm_user_t * userp, int lockType,
4174 struct rx_connection * rxconnp;
4176 afs_uint32 reqflags = reqp->flags;
4178 tfid.Volume = scp->fid.volume;
4179 tfid.Vnode = scp->fid.vnode;
4180 tfid.Unique = scp->fid.unique;
4183 osi_Log2(afsd_logp, "CALL SetLock scp 0x%p for lock %d", scp, lockType);
4185 reqp->flags |= CM_REQ_NORETRY;
4186 lock_ReleaseWrite(&scp->rw);
4189 code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4193 rxconnp = cm_GetRxConn(connp);
4194 code = RXAFS_SetLock(rxconnp, &tfid, lockType,
4196 rx_PutConnection(rxconnp);
4198 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
4201 code = cm_MapRPCError(code, reqp);
4203 osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
4205 osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
4208 lock_ObtainWrite(&scp->rw);
4209 reqp->flags = reqflags;
4213 /* called with scp->rw held. Releases it during processing */
4214 long cm_IntReleaseLock(cm_scache_t * scp, cm_user_t * userp,
4220 struct rx_connection * rxconnp;
4223 tfid.Volume = scp->fid.volume;
4224 tfid.Vnode = scp->fid.vnode;
4225 tfid.Unique = scp->fid.unique;
4228 lock_ReleaseWrite(&scp->rw);
4230 osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
4233 code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4237 rxconnp = cm_GetRxConn(connp);
4238 code = RXAFS_ReleaseLock(rxconnp, &tfid, &volSync);
4239 rx_PutConnection(rxconnp);
4241 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
4243 code = cm_MapRPCError(code, reqp);
4246 "CALL ReleaseLock FAILURE, code 0x%x", code);
4249 "CALL ReleaseLock SUCCESS");
4251 lock_ObtainWrite(&scp->rw);
4256 /* called with scp->rw held. May release it during processing, but
4257 will exit with lock held.
4261 - 0 if the user has permission to get the specified lock for the scp
4263 - CM_ERROR_NOACCESS if not
4265 Any other error from cm_SyncOp will be sent down untranslated.
4267 If CM_ERROR_NOACCESS is returned and lock_type is LockRead, then
4268 phas_insert (if non-NULL) will receive a boolean value indicating
4269 whether the user has INSERT permission or not.
4271 long cm_LockCheckPerms(cm_scache_t * scp,
4278 long code = 0, code2 = 0;
4280 /* lock permissions are slightly tricky because of the 'i' bit.
4281 If the user has PRSFS_LOCK, she can read-lock the file. If the
4282 user has PRSFS_WRITE, she can write-lock the file. However, if
4283 the user has PRSFS_INSERT, then she can write-lock new files,
4284 but not old ones. Since we don't have information about
4285 whether a file is new or not, we assume that if the user owns
4286 the scp, then she has the permissions that are granted by
4289 osi_Log3(afsd_logp, "cm_LockCheckPerms for scp[0x%p] type[%d] user[0x%p]",
4290 scp, lock_type, userp);
4292 if (lock_type == LockRead)
4293 rights |= PRSFS_LOCK;
4294 else if (lock_type == LockWrite)
4295 rights |= PRSFS_WRITE | PRSFS_LOCK;
4298 osi_assertx(FALSE, "invalid lock type");
4303 *phas_insert = FALSE;
4305 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
4306 CM_SCACHESYNC_GETSTATUS |
4307 CM_SCACHESYNC_NEEDCALLBACK);
4309 if (phas_insert && scp->creator == userp) {
4311 /* If this file was created by the user, then we check for
4312 PRSFS_INSERT. If the file server is recent enough, then
4313 this should be sufficient for her to get a write-lock (but
4314 not necessarily a read-lock). VICED_CAPABILITY_WRITELOCKACL
4315 indicates whether a file server supports getting write
4316 locks when the user only has PRSFS_INSERT.
4318 If the file was not created by the user we skip the check
4319 because the INSERT bit will not apply to this user even
4323 code2 = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
4324 CM_SCACHESYNC_GETSTATUS |
4325 CM_SCACHESYNC_NEEDCALLBACK);
4327 if (code2 == CM_ERROR_NOACCESS) {
4328 osi_Log0(afsd_logp, "cm_LockCheckPerms user has no INSERT bits");
4330 *phas_insert = TRUE;
4331 osi_Log0(afsd_logp, "cm_LockCheckPerms user has INSERT bits");
4335 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
4337 osi_Log1(afsd_logp, "cm_LockCheckPerms returning code %d", code);
4342 /* called with scp->rw held */
4343 long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
4344 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
4346 int allowWait, cm_user_t *userp, cm_req_t *reqp,
4347 cm_file_lock_t **lockpp)
4350 int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
4351 cm_file_lock_t *fileLock;
4354 int wait_unlock = FALSE;
4355 int force_client_lock = FALSE;
4357 osi_Log4(afsd_logp, "cm_Lock scp 0x%x type 0x%x offset %d length %d",
4358 scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
4359 osi_Log4(afsd_logp, "... allowWait %d key <0x%x, 0x%x, 0x%x>", allowWait,
4360 key.process_id, key.session_id, key.file_id);
4363 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
4365 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
4368 3.1 If L->LockType is exclusive then there does NOT exist a
4369 ACCEPTED lock M in S->fileLocks such that _a_ in
4370 (M->LOffset,+M->LLength).
4372 3.2 If L->LockType is shared then for each ACCEPTED lock M in
4373 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
4374 M->LockType is shared.
4376 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
4378 4.1 M->key != Key(C)
4380 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
4381 and (M->LOffset,+M->LLength) do not intersect.
4384 range.offset = LOffset.QuadPart;
4385 range.length = LLength.QuadPart;
4387 lock_ObtainRead(&cm_scacheLock);
4389 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4391 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4393 if (IS_LOCK_LOST(fileLock)) {
4394 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4395 code = CM_ERROR_BADFD;
4397 } else if (fileLock->lockType == LockWrite && INTERSECT_RANGE(range, fileLock->range)) {
4398 code = CM_ERROR_WOULDBLOCK;
4404 /* we don't need to check for deleted locks here since deleted
4405 locks are dequeued from scp->fileLocks */
4406 if (IS_LOCK_ACCEPTED(fileLock) &&
4407 INTERSECT_RANGE(range, fileLock->range)) {
4409 if ((sLockType & LOCKING_ANDX_SHARED_LOCK) == 0 ||
4410 fileLock->lockType != LockRead) {
4412 code = CM_ERROR_WOULDBLOCK;
4418 lock_ReleaseRead(&cm_scacheLock);
4420 if (code == 0 && SERVERLOCKS_ENABLED(scp)) {
4421 if (Which == scp->serverLock ||
4422 (Which == LockRead && scp->serverLock == LockWrite)) {
4426 /* we already have the lock we need */
4427 osi_Log3(afsd_logp, " we already have the correct lock. exclusives[%d], shared[%d], serverLock[%d]",
4428 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4430 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4432 /* special case: if we don't have permission to read-lock
4433 the file, then we force a clientside lock. This is to
4434 compensate for applications that obtain a read-lock for
4435 reading files off of directories that don't grant
4436 read-locks to the user. */
4437 if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4439 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4440 osi_Log0(afsd_logp, " User has no read-lock perms, but has INSERT perms.");
4443 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4444 force_client_lock = TRUE;
4448 } else if ((scp->exclusiveLocks > 0) ||
4449 (scp->sharedLocks > 0 && scp->serverLock != LockRead)) {
4452 /* We are already waiting for some other lock. We should
4453 wait for the daemon to catch up instead of generating a
4454 flood of SetLock calls. */
4455 osi_Log3(afsd_logp, " already waiting for other lock. exclusives[%d], shared[%d], serverLock[%d]",
4456 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4458 /* see if we have permission to create the lock in the
4460 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4462 code = CM_ERROR_WOULDBLOCK;
4463 else if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4465 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4467 " User has no read-lock perms, but has INSERT perms.");
4468 code = CM_ERROR_WOULDBLOCK;
4471 " User has no read-lock perms. Forcing client-side lock");
4472 force_client_lock = TRUE;
4476 /* leave any other codes as-is */
4480 int check_data_version = FALSE;
4483 /* first check if we have permission to elevate or obtain
4485 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4487 if (code == CM_ERROR_NOACCESS && Which == LockRead &&
4488 (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp))) {
4489 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4490 force_client_lock = TRUE;
4495 /* has_insert => (Which == LockRead, code == CM_ERROR_NOACCESS) */
4497 if (scp->serverLock == LockRead && Which == LockWrite) {
4499 /* We want to escalate the lock to a LockWrite.
4500 * Unfortunately that's not really possible without
4501 * letting go of the current lock. But for now we do
4505 " attempting to UPGRADE from LockRead to LockWrite.");
4507 " dataVersion on scp: %I64d", scp->dataVersion);
4509 /* we assume at this point (because scp->serverLock
4510 was valid) that we had a valid server lock. */
4511 scp->lockDataVersion = scp->dataVersion;
4512 check_data_version = TRUE;
4514 code = cm_IntReleaseLock(scp, userp, reqp);
4517 /* We couldn't release the lock */
4520 scp->serverLock = -1;
4524 /* We need to obtain a server lock of type Which in order
4525 * to assert this file lock */
4526 #ifndef AGGRESSIVE_LOCKS
4529 newLock = LockWrite;
4532 code = cm_IntSetLock(scp, userp, newLock, reqp);
4534 #ifdef AGGRESSIVE_LOCKS
4535 if ((code == CM_ERROR_WOULDBLOCK ||
4536 code == CM_ERROR_NOACCESS) && newLock != Which) {
4537 /* we wanted LockRead. We tried LockWrite. Now try
4542 osi_assertx(newLock == LockRead, "lock type not read");
4544 code = cm_IntSetLock(scp, userp, newLock, reqp);
4548 if (code == CM_ERROR_NOACCESS) {
4549 if (Which == LockRead) {
4550 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4552 /* We requested a read-lock, but we have permission to
4553 * get a write-lock. Try that */
4555 tcode = cm_LockCheckPerms(scp, LockWrite, userp, reqp, NULL);
4558 newLock = LockWrite;
4560 osi_Log0(afsd_logp, " User has 'i' perms and the request was for a LockRead. Trying to get a LockWrite instead");
4562 code = cm_IntSetLock(scp, userp, newLock, reqp);
4565 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4566 force_client_lock = TRUE;
4568 } else if (Which == LockWrite &&
4569 scp->creator == userp && !SCP_SUPPORTS_WRITELOCKACL(scp)) {
4572 /* Special case: if the lock request was for a
4573 * LockWrite and the user owns the file and we weren't
4574 * allowed to obtain the serverlock, we either lost a
4575 * race (the permissions changed from under us), or we
4576 * have 'i' bits, but we aren't allowed to lock the
4579 /* check if we lost a race... */
4580 tcode = cm_LockCheckPerms(scp, Which, userp, reqp, NULL);
4583 osi_Log0(afsd_logp, " User has 'i' perms but can't obtain write locks. Using client-side locks.");
4584 force_client_lock = TRUE;
4589 if (code == 0 && check_data_version &&
4590 scp->dataVersion != scp->lockDataVersion) {
4591 /* We lost a race. Although we successfully obtained
4592 * a lock, someone modified the file in between. The
4593 * locks have all been technically lost. */
4596 " Data version mismatch while upgrading lock.");
4598 " Data versions before=%I64d, after=%I64d",
4599 scp->lockDataVersion,
4602 " Releasing stale lock for scp 0x%x", scp);
4604 code = cm_IntReleaseLock(scp, userp, reqp);
4606 scp->serverLock = -1;
4608 code = CM_ERROR_INVAL;
4609 } else if (code == 0) {
4610 scp->serverLock = newLock;
4611 scp->lockDataVersion = scp->dataVersion;
4615 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4616 scp->serverLock == -1) {
4617 /* Oops. We lost the lock. */
4618 cm_LockMarkSCacheLost(scp);
4621 } else if (code == 0) { /* server locks not enabled */
4623 " Skipping server lock for scp");
4628 if (code != 0 && !force_client_lock) {
4629 /* Special case error translations
4631 Applications don't expect certain errors from a
4632 LockFile/UnlockFile call. We need to translate some error
4633 code to codes that apps expect and handle. */
4635 /* We shouldn't actually need to handle this case since we
4636 simulate locks for RO scps anyway. */
4637 if (code == CM_ERROR_READONLY) {
4638 osi_Log0(afsd_logp, " Reinterpreting CM_ERROR_READONLY as CM_ERROR_NOACCESS");
4639 code = CM_ERROR_NOACCESS;
4643 if (code == 0 || (code == CM_ERROR_WOULDBLOCK && allowWait) ||
4644 force_client_lock) {
4646 /* clear the error if we are forcing a client lock, so we
4647 don't get confused later. */
4648 if (force_client_lock && code != CM_ERROR_WOULDBLOCK)
4651 lock_ObtainWrite(&cm_scacheLock);
4652 fileLock = cm_GetFileLock();
4653 lock_ReleaseWrite(&cm_scacheLock);
4655 fileLock->fid = scp->fid;
4657 fileLock->key = key;
4658 fileLock->lockType = Which;
4660 fileLock->userp = userp;
4661 fileLock->range = range;
4662 fileLock->flags = (code == 0 ? 0 :
4664 CM_FILELOCK_FLAG_WAITUNLOCK :
4665 CM_FILELOCK_FLAG_WAITLOCK));
4667 if (force_client_lock || !SERVERLOCKS_ENABLED(scp))
4668 fileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
4670 fileLock->lastUpdate = (code == 0 && !force_client_lock) ? time(NULL) : 0;
4672 lock_ObtainWrite(&cm_scacheLock);
4673 osi_QAddT(&scp->fileLocksH, &scp->fileLocksT, &fileLock->fileq);
4674 cm_HoldSCacheNoLock(scp);
4675 fileLock->scp = scp;
4676 osi_QAdd(&cm_allFileLocks, &fileLock->q);
4677 lock_ReleaseWrite(&cm_scacheLock);
4683 if (IS_LOCK_CLIENTONLY(fileLock)) {
4685 } else if (IS_LOCK_ACCEPTED(fileLock)) {
4686 if (Which == LockRead)
4689 scp->exclusiveLocks++;
4693 "cm_Lock Lock added 0x%p flags 0x%x to scp [0x%p]",
4694 fileLock, fileLock->flags, scp);
4696 " exclusives[%d] shared[%d] client[%d] serverLock[%d]",
4697 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4698 (int)(signed char) scp->serverLock);
4701 "cm_Lock Rejecting lock (code = 0x%x)", code);
4704 /* Convert from would block to lock not granted */
4705 if (code == CM_ERROR_WOULDBLOCK)
4706 code = CM_ERROR_LOCK_NOT_GRANTED;
4711 /* Called with scp->rw held */
4712 long cm_UnlockByKey(cm_scache_t * scp,
4719 cm_file_lock_t *fileLock;
4720 osi_queue_t *q, *qn;
4723 osi_Log4(afsd_logp, "cm_UnlockByKey scp 0x%p key <0x%x,0x%x,0x%x",
4724 scp, key.process_id, key.session_id, key.file_id);
4725 osi_Log1(afsd_logp, " flags=0x%x", flags);
4727 lock_ObtainWrite(&cm_scacheLock);
4729 for (q = scp->fileLocksH; q; q = qn) {
4732 fileLock = (cm_file_lock_t *)
4733 ((char *) q - offsetof(cm_file_lock_t, fileq));
4736 osi_Log4(afsd_logp, " Checking lock[0x%x] range[%d,+%d] type[%d]",
4738 (unsigned long) fileLock->range.offset,
4739 (unsigned long) fileLock->range.length,
4740 fileLock->lockType);
4741 osi_Log4(afsd_logp, " key<0x%x, 0x%x, 0x%x> flags[0x%x]",
4742 fileLock->key.process_id, fileLock->key.session_id, fileLock->key.file_id,
4745 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
4746 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
4747 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4749 fileLock->fid.volume,
4750 fileLock->fid.vnode,
4751 fileLock->fid.unique);
4752 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4753 fileLock->scp->fid.cell,
4754 fileLock->scp->fid.volume,
4755 fileLock->scp->fid.vnode,
4756 fileLock->scp->fid.unique);
4757 osi_assertx(FALSE, "invalid fid value");
4761 if (!IS_LOCK_DELETED(fileLock) &&
4762 cm_KeyEquals(&fileLock->key, &key, flags)) {
4763 osi_Log3(afsd_logp, "...Unlock range [%d,+%d] type %d",
4764 fileLock->range.offset,
4765 fileLock->range.length,
4766 fileLock->lockType);
4768 if (scp->fileLocksT == q)
4769 scp->fileLocksT = osi_QPrev(q);
4770 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
4772 if (IS_LOCK_CLIENTONLY(fileLock)) {
4774 } else if (IS_LOCK_ACCEPTED(fileLock)) {
4775 if (fileLock->lockType == LockRead)
4778 scp->exclusiveLocks--;
4781 fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
4783 cm_ReleaseUser(fileLock->userp);
4784 cm_ReleaseSCacheNoLock(scp);
4786 fileLock->userp = NULL;
4787 fileLock->scp = NULL;
4793 lock_ReleaseWrite(&cm_scacheLock);
4795 if (n_unlocks == 0) {
4796 osi_Log0(afsd_logp, "cm_UnlockByKey no locks found");
4797 osi_Log3(afsd_logp, " Leaving scp with exclusives[%d], shared[%d], serverLock[%d]",
4798 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4803 osi_Log1(afsd_logp, "cm_UnlockByKey done with %d locks", n_unlocks);
4805 osi_assertx(scp->sharedLocks >= 0, "scp->sharedLocks < 0");
4806 osi_assertx(scp->exclusiveLocks >= 0, "scp->exclusiveLocks < 0");
4807 osi_assertx(scp->clientLocks >= 0, "scp->clientLocks < 0");
4809 if (!SERVERLOCKS_ENABLED(scp)) {
4810 osi_Log0(afsd_logp, " Skipping server lock for scp");
4814 /* Ideally we would go through the rest of the locks to determine
4815 * if one or more locks that were formerly in WAITUNLOCK can now
4816 * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
4817 * scp->sharedLocks accordingly. However, the retrying of locks
4818 * in that manner is done cm_RetryLock() manually.
4821 if (scp->serverLock == LockWrite &&
4822 scp->exclusiveLocks == 0 &&
4823 scp->sharedLocks > 0) {
4825 /* The serverLock should be downgraded to LockRead */
4826 osi_Log0(afsd_logp, " DOWNGRADE lock from LockWrite to LockRead");
4828 /* since scp->serverLock looked sane, we are going to assume
4829 that we have a valid server lock. */
4830 scp->lockDataVersion = scp->dataVersion;
4831 osi_Log1(afsd_logp, " dataVersion on scp = %I64d", scp->dataVersion);
4833 code = cm_IntReleaseLock(scp, userp, reqp);
4836 /* so we couldn't release it. Just let the lock be for now */
4840 scp->serverLock = -1;
4843 code = cm_IntSetLock(scp, userp, LockRead, reqp);
4845 if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
4846 scp->serverLock = LockRead;
4847 } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
4848 /* We lost a race condition. Although we have a valid
4849 lock on the file, the data has changed and essentially
4850 we have lost the lock we had during the transition. */
4852 osi_Log0(afsd_logp, "Data version mismatch during lock downgrade");
4853 osi_Log2(afsd_logp, " Data versions before=%I64d, after=%I64d",
4854 scp->lockDataVersion,
4857 code = cm_IntReleaseLock(scp, userp, reqp);
4859 code = CM_ERROR_INVAL;
4860 scp->serverLock = -1;
4864 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4865 (scp->serverLock == -1)) {
4867 cm_LockMarkSCacheLost(scp);
4870 /* failure here has no bearing on the return value of
4874 } else if (scp->serverLock != (-1) &&
4875 scp->exclusiveLocks == 0 &&
4876 scp->sharedLocks == 0) {
4877 /* The serverLock should be released entirely */
4879 code = cm_IntReleaseLock(scp, userp, reqp);
4882 scp->serverLock = (-1);
4887 osi_Log1(afsd_logp, "cm_UnlockByKey code 0x%x", code);
4888 osi_Log4(afsd_logp, " Leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
4889 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4890 (int)(signed char) scp->serverLock);
4895 long cm_Unlock(cm_scache_t *scp,
4896 unsigned char sLockType,
4897 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
4904 int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
4905 cm_file_lock_t *fileLock;
4907 int release_userp = FALSE;
4908 int exact_match = !(flags & CM_UNLOCK_FLAG_MATCH_RANGE);
4910 LARGE_INTEGER RangeEnd;
4912 osi_Log4(afsd_logp, "cm_Unlock scp 0x%p type 0x%x offset %d length %d",
4913 scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
4914 osi_Log4(afsd_logp, "... key <0x%x,0x%x,0x%x> flags 0x%x",
4915 key.process_id, key.session_id, key.file_id, flags);
4918 RangeEnd.QuadPart = LOffset.QuadPart + LLength.QuadPart;
4921 lock_ObtainRead(&cm_scacheLock);
4923 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4924 fileLock = (cm_file_lock_t *)
4925 ((char *) q - offsetof(cm_file_lock_t, fileq));
4928 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
4929 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
4930 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4932 fileLock->fid.volume,
4933 fileLock->fid.vnode,
4934 fileLock->fid.unique);
4935 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4936 fileLock->scp->fid.cell,
4937 fileLock->scp->fid.volume,
4938 fileLock->scp->fid.vnode,
4939 fileLock->scp->fid.unique);
4940 osi_assertx(FALSE, "invalid fid value");
4944 if (!IS_LOCK_DELETED(fileLock) &&
4945 cm_KeyEquals(&fileLock->key, &key, 0) &&
4946 fileLock->range.offset == LOffset.QuadPart &&
4947 fileLock->range.length == LLength.QuadPart) {
4953 if (!IS_LOCK_DELETED(fileLock) &&
4954 cm_KeyEquals(&fileLock->key, &key, 0) &&
4955 fileLock->range.offset >= LOffset.QuadPart &&
4956 fileLock->range.offset < RangeEnd.QuadPart &&
4957 (fileLock->range.offset + fileLock->range.length) <= RangeEnd.QuadPart) {
4965 lock_ReleaseRead(&cm_scacheLock);
4967 if (lock_found && !exact_match) {
4971 osi_Log0(afsd_logp, "cm_Unlock lock not found; failure");
4973 /* The lock didn't exist anyway. *shrug* */
4974 return CM_ERROR_RANGE_NOT_LOCKED;
4978 /* discard lock record */
4979 lock_ConvertRToW(&cm_scacheLock);
4980 if (scp->fileLocksT == q)
4981 scp->fileLocksT = osi_QPrev(q);
4982 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
4985 * Don't delete it here; let the daemon delete it, to simplify
4986 * the daemon's traversal of the list.
4989 if (IS_LOCK_CLIENTONLY(fileLock)) {
4991 } else if (IS_LOCK_ACCEPTED(fileLock)) {
4992 if (fileLock->lockType == LockRead)
4995 scp->exclusiveLocks--;
4998 fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
4999 if (userp != NULL) {
5000 cm_ReleaseUser(fileLock->userp);
5002 userp = fileLock->userp;
5003 release_userp = TRUE;
5005 fileLock->userp = NULL;
5006 cm_ReleaseSCacheNoLock(scp);
5007 fileLock->scp = NULL;
5008 lock_ReleaseWrite(&cm_scacheLock);
5010 if (!SERVERLOCKS_ENABLED(scp)) {
5011 osi_Log0(afsd_logp, " Skipping server locks for scp");
5015 /* Ideally we would go through the rest of the locks to determine
5016 * if one or more locks that were formerly in WAITUNLOCK can now
5017 * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
5018 * scp->sharedLocks accordingly. However, the retrying of locks
5019 * in that manner is done cm_RetryLock() manually.
5022 if (scp->serverLock == LockWrite &&
5023 scp->exclusiveLocks == 0 &&
5024 scp->sharedLocks > 0) {
5026 /* The serverLock should be downgraded to LockRead */
5027 osi_Log0(afsd_logp, " DOWNGRADE lock from LockWrite to LockRead");
5029 /* Since we already had a lock, we assume that there is a
5030 valid server lock. */
5031 scp->lockDataVersion = scp->dataVersion;
5032 osi_Log1(afsd_logp, " dataVersion on scp is %I64d", scp->dataVersion);
5034 /* before we downgrade, make sure that we have enough
5035 permissions to get the read lock. */
5036 code = cm_LockCheckPerms(scp, LockRead, userp, reqp, NULL);
5039 osi_Log0(afsd_logp, " SKIPPING downgrade because user doesn't have perms to get downgraded lock");
5045 code = cm_IntReleaseLock(scp, userp, reqp);
5048 /* so we couldn't release it. Just let the lock be for now */
5052 scp->serverLock = -1;
5055 code = cm_IntSetLock(scp, userp, LockRead, reqp);
5057 if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
5058 scp->serverLock = LockRead;
5059 } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
5060 /* Lost a race. We obtained a new lock, but that is
5061 meaningless since someone modified the file
5065 "Data version mismatch while downgrading lock");
5067 " Data versions before=%I64d, after=%I64d",
5068 scp->lockDataVersion,
5071 code = cm_IntReleaseLock(scp, userp, reqp);
5073 scp->serverLock = -1;
5074 code = CM_ERROR_INVAL;
5078 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
5079 (scp->serverLock == -1)) {
5081 cm_LockMarkSCacheLost(scp);
5084 /* failure here has no bearing on the return value of
5088 } else if (scp->serverLock != (-1) &&
5089 scp->exclusiveLocks == 0 &&
5090 scp->sharedLocks == 0) {
5091 /* The serverLock should be released entirely */
5093 code = cm_IntReleaseLock(scp, userp, reqp);
5096 scp->serverLock = (-1);
5100 if (release_userp) {
5101 cm_ReleaseUser(userp);
5102 release_userp = FALSE;
5106 goto try_again; /* might be more than one lock in the range */
5110 osi_Log1(afsd_logp, "cm_Unlock code 0x%x", code);
5111 osi_Log4(afsd_logp, " leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
5112 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
5113 (int)(signed char) scp->serverLock);
5118 /* called with scp->rw held */
5119 void cm_LockMarkSCacheLost(cm_scache_t * scp)
5121 cm_file_lock_t *fileLock;
5124 osi_Log1(afsd_logp, "cm_LockMarkSCacheLost scp 0x%x", scp);
5126 /* cm_scacheLock needed because we are modifying fileLock->flags */
5127 lock_ObtainWrite(&cm_scacheLock);
5129 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5131 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
5133 if (IS_LOCK_ACTIVE(fileLock) &&
5134 !IS_LOCK_CLIENTONLY(fileLock)) {
5135 if (fileLock->lockType == LockRead)
5138 scp->exclusiveLocks--;
5140 fileLock->flags |= CM_FILELOCK_FLAG_LOST;
5144 scp->serverLock = -1;
5145 scp->lockDataVersion = CM_SCACHE_VERSION_BAD;
5146 lock_ReleaseWrite(&cm_scacheLock);
5149 /* Called with no relevant locks held */
5150 void cm_CheckLocks()
5152 osi_queue_t *q, *nq;
5153 cm_file_lock_t *fileLock;
5159 struct rx_connection * rxconnp;
5164 lock_ObtainWrite(&cm_scacheLock);
5166 cm_lockRefreshCycle++;
5168 osi_Log1(afsd_logp, "cm_CheckLocks starting lock check cycle %d", cm_lockRefreshCycle);
5170 for (q = cm_allFileLocks; q; q = nq) {
5171 fileLock = (cm_file_lock_t *) q;
5175 if (IS_LOCK_DELETED(fileLock)) {
5177 osi_QRemove(&cm_allFileLocks, q);
5178 cm_PutFileLock(fileLock);
5180 } else if (IS_LOCK_ACTIVE(fileLock) && !IS_LOCK_CLIENTONLY(fileLock)) {
5182 /* Server locks must have been enabled for us to have
5183 received an active non-client-only lock. */
5184 osi_assertx(cm_enableServerLocks, "!cm_enableServerLocks");
5186 scp = fileLock->scp;
5187 osi_assertx(scp != NULL, "null cm_scache_t");
5189 cm_HoldSCacheNoLock(scp);
5192 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5193 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5194 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5196 fileLock->fid.volume,
5197 fileLock->fid.vnode,
5198 fileLock->fid.unique);
5199 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5200 fileLock->scp->fid.cell,
5201 fileLock->scp->fid.volume,
5202 fileLock->scp->fid.vnode,
5203 fileLock->scp->fid.unique);
5204 osi_assertx(FALSE, "invalid fid");
5207 /* Server locks are extended once per scp per refresh
5209 if (scp->lastRefreshCycle != cm_lockRefreshCycle) {
5211 int scp_done = FALSE;
5213 osi_Log1(afsd_logp, "cm_CheckLocks Updating scp 0x%x", scp);
5215 lock_ReleaseWrite(&cm_scacheLock);
5216 lock_ObtainWrite(&scp->rw);
5218 /* did the lock change while we weren't holding the lock? */
5219 if (!IS_LOCK_ACTIVE(fileLock))
5220 goto post_syncopdone;
5222 code = cm_SyncOp(scp, NULL, fileLock->userp, &req, 0,
5223 CM_SCACHESYNC_NEEDCALLBACK
5224 | CM_SCACHESYNC_GETSTATUS
5225 | CM_SCACHESYNC_LOCK);
5229 "cm_CheckLocks SyncOp failure code 0x%x", code);
5230 goto post_syncopdone;
5233 /* cm_SyncOp releases scp->rw during which the lock
5234 may get released. */
5235 if (!IS_LOCK_ACTIVE(fileLock))
5236 goto pre_syncopdone;
5238 if (scp->serverLock != -1 && !(scp->flags & CM_SCACHEFLAG_DELETED)) {
5242 tfid.Volume = scp->fid.volume;
5243 tfid.Vnode = scp->fid.vnode;
5244 tfid.Unique = scp->fid.unique;
5246 userp = fileLock->userp;
5248 osi_Log3(afsd_logp, "CALL ExtendLock lock 0x%p for scp=0x%p with lock %d",
5251 (int) scp->serverLock);
5253 lock_ReleaseWrite(&scp->rw);
5256 code = cm_ConnFromFID(&cfid, userp,
5261 rxconnp = cm_GetRxConn(connp);
5262 code = RXAFS_ExtendLock(rxconnp, &tfid,
5264 rx_PutConnection(rxconnp);
5266 osi_Log1(afsd_logp, " ExtendLock returns %d", code);
5268 } while (cm_Analyze(connp, userp, &req,
5269 &cfid, &volSync, NULL, NULL,
5272 code = cm_MapRPCError(code, &req);
5274 lock_ObtainWrite(&scp->rw);
5277 osi_Log1(afsd_logp, "CALL ExtendLock FAILURE, code 0x%x", code);
5279 osi_Log0(afsd_logp, "CALL ExtendLock SUCCESS");
5280 scp->lockDataVersion = scp->dataVersion;
5283 if ((code == EINVAL || code == CM_ERROR_INVAL) &&
5284 scp->lockDataVersion == scp->dataVersion) {
5288 (scp->exclusiveLocks > 0) ? LockWrite: LockRead;
5290 /* we might still have a chance to obtain a
5293 code = cm_IntSetLock(scp, userp, lockType, &req);
5296 code = CM_ERROR_INVAL;
5297 } else if (scp->lockDataVersion != scp->dataVersion) {
5299 /* now check if we still have the file at
5300 the right data version. */
5302 "Data version mismatch on scp 0x%p",
5305 " Data versions: before=%I64d, after=%I64d",
5306 scp->lockDataVersion,
5309 code = cm_IntReleaseLock(scp, userp, &req);
5311 code = CM_ERROR_INVAL;
5315 if (code == EINVAL || code == CM_ERROR_INVAL ||
5316 code == CM_ERROR_BADFD) {
5317 cm_LockMarkSCacheLost(scp);
5321 /* interestingly, we have found an active lock
5322 belonging to an scache that has no
5324 cm_LockMarkSCacheLost(scp);
5331 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5334 lock_ReleaseWrite(&scp->rw);
5336 lock_ObtainWrite(&cm_scacheLock);
5339 fileLock->lastUpdate = time(NULL);
5343 scp->lastRefreshCycle = cm_lockRefreshCycle;
5346 /* we have already refreshed the locks on this scp */
5347 fileLock->lastUpdate = time(NULL);
5350 cm_ReleaseSCacheNoLock(scp);
5352 } else if (IS_LOCK_ACTIVE(fileLock) && IS_LOCK_CLIENTONLY(fileLock)) {
5353 /* TODO: Check callbacks */
5357 lock_ReleaseWrite(&cm_scacheLock);
5358 osi_Log1(afsd_logp, "cm_CheckLocks completes lock check cycle %d", cm_lockRefreshCycle);
5361 /* NOT called with scp->rw held. */
5362 long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
5365 cm_scache_t *scp = NULL;
5366 cm_file_lock_t *fileLock;
5370 int force_client_lock = FALSE;
5371 int has_insert = FALSE;
5372 int check_data_version = FALSE;
5376 if (client_is_dead) {
5377 code = CM_ERROR_TIMEDOUT;
5381 lock_ObtainRead(&cm_scacheLock);
5383 osi_Log2(afsd_logp, "cm_RetryLock checking lock %p (scp=%p)", oldFileLock, oldFileLock->scp);
5384 osi_Log4(afsd_logp, " offset(%x:%x) length(%x:%x)",
5385 (unsigned)(oldFileLock->range.offset >> 32),
5386 (unsigned)(oldFileLock->range.offset & 0xffffffff),
5387 (unsigned)(oldFileLock->range.length >> 32),
5388 (unsigned)(oldFileLock->range.length & 0xffffffff));
5389 osi_Log4(afsd_logp, " key<0x%x,0x%x,0x%x> flags=%x",
5390 oldFileLock->key.process_id, oldFileLock->key.session_id, oldFileLock->key.file_id,
5391 (unsigned)(oldFileLock->flags));
5393 /* if the lock has already been granted, then we have nothing to do */
5394 if (IS_LOCK_ACTIVE(oldFileLock)) {
5395 lock_ReleaseRead(&cm_scacheLock);
5396 osi_Log0(afsd_logp, "cm_RetryLock lock already granted");
5400 /* we can't do anything with lost or deleted locks at the moment. */
5401 if (IS_LOCK_LOST(oldFileLock) || IS_LOCK_DELETED(oldFileLock)) {
5402 code = CM_ERROR_BADFD;
5403 osi_Log0(afsd_logp, "cm_RetryLock lock is lost or deleted");
5404 lock_ReleaseRead(&cm_scacheLock);
5408 scp = oldFileLock->scp;
5410 osi_assertx(scp != NULL, "null cm_scache_t");
5412 lock_ReleaseRead(&cm_scacheLock);
5413 lock_ObtainWrite(&scp->rw);
5415 code = cm_LockCheckPerms(scp, oldFileLock->lockType,
5419 if (code == CM_ERROR_NOACCESS && oldFileLock->lockType == LockRead) {
5420 if (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp)) {
5421 force_client_lock = TRUE;
5425 lock_ReleaseWrite(&scp->rw);
5429 lock_ObtainWrite(&cm_scacheLock);
5431 /* Check if we already have a sufficient server lock to allow this
5432 lock to go through. */
5433 if (IS_LOCK_WAITLOCK(oldFileLock) &&
5434 (!SERVERLOCKS_ENABLED(scp) ||
5435 scp->serverLock == oldFileLock->lockType ||
5436 scp->serverLock == LockWrite)) {
5438 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5440 if (SERVERLOCKS_ENABLED(scp)) {
5441 osi_Log1(afsd_logp, "cm_RetryLock Server lock (%d) is sufficient for lock. Granting",
5442 (int) scp->serverLock);
5444 osi_Log0(afsd_logp, "cm_RetryLock skipping server lock for scp");
5447 lock_ReleaseWrite(&cm_scacheLock);
5448 lock_ReleaseWrite(&scp->rw);
5453 if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5455 /* check if the conflicting locks have dissappeared already */
5456 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5458 fileLock = (cm_file_lock_t *)
5459 ((char *) q - offsetof(cm_file_lock_t, fileq));
5461 if (IS_LOCK_LOST(fileLock)) {
5462 if (cm_KeyEquals(&fileLock->key, &oldFileLock->key, 0)) {
5463 code = CM_ERROR_BADFD;
5464 oldFileLock->flags |= CM_FILELOCK_FLAG_LOST;
5465 osi_Log1(afsd_logp, " found lost lock %p for same key. Marking lock as lost",
5468 } else if (fileLock->lockType == LockWrite &&
5469 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5470 osi_Log1(afsd_logp, " found conflicting LOST lock %p", fileLock);
5471 code = CM_ERROR_WOULDBLOCK;
5476 if (IS_LOCK_ACCEPTED(fileLock) &&
5477 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5479 if (oldFileLock->lockType != LockRead ||
5480 fileLock->lockType != LockRead) {
5482 osi_Log1(afsd_logp, " found conflicting lock %p", fileLock);
5483 code = CM_ERROR_WOULDBLOCK;
5491 lock_ReleaseWrite(&cm_scacheLock);
5492 lock_ReleaseWrite(&scp->rw);
5497 /* when we get here, the lock is either a WAITUNLOCK or WAITLOCK.
5498 If it is WAITUNLOCK, then we didn't find any conflicting lock
5499 but we haven't verfied whether the serverLock is sufficient to
5500 assert it. If it is WAITLOCK, then the serverLock is
5501 insufficient to assert it. Eitherway, we are ready to accept
5502 the lock as either ACTIVE or WAITLOCK depending on the
5505 /* First, promote the WAITUNLOCK to a WAITLOCK */
5506 if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5507 if (oldFileLock->lockType == LockRead)
5510 scp->exclusiveLocks++;
5512 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITUNLOCK;
5513 oldFileLock->flags |= CM_FILELOCK_FLAG_WAITLOCK;
5516 osi_assertx(IS_LOCK_WAITLOCK(oldFileLock), "!IS_LOCK_WAITLOCK");
5518 if (force_client_lock ||
5519 !SERVERLOCKS_ENABLED(scp) ||
5520 scp->serverLock == oldFileLock->lockType ||
5521 (oldFileLock->lockType == LockRead &&
5522 scp->serverLock == LockWrite)) {
5524 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5526 if ((force_client_lock ||
5527 !SERVERLOCKS_ENABLED(scp)) &&
5528 !IS_LOCK_CLIENTONLY(oldFileLock)) {
5530 oldFileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
5532 if (oldFileLock->lockType == LockRead)
5535 scp->exclusiveLocks--;
5540 lock_ReleaseWrite(&cm_scacheLock);
5541 lock_ReleaseWrite(&scp->rw);
5548 code = cm_SyncOp(scp, NULL, oldFileLock->userp, &req, 0,
5549 CM_SCACHESYNC_NEEDCALLBACK
5550 | CM_SCACHESYNC_GETSTATUS
5551 | CM_SCACHESYNC_LOCK);
5553 osi_Log1(afsd_logp, "cm_RetryLock SyncOp failure code 0x%x", code);
5554 lock_ReleaseWrite(&cm_scacheLock);
5555 goto post_syncopdone;
5558 if (!IS_LOCK_WAITLOCK(oldFileLock))
5559 goto pre_syncopdone;
5561 userp = oldFileLock->userp;
5563 #ifndef AGGRESSIVE_LOCKS
5564 newLock = oldFileLock->lockType;
5566 newLock = LockWrite;
5570 /* if has_insert is non-zero, then:
5571 - the lock a LockRead
5572 - we don't have permission to get a LockRead
5573 - we do have permission to get a LockWrite
5574 - the server supports VICED_CAPABILITY_WRITELOCKACL
5577 newLock = LockWrite;
5580 lock_ReleaseWrite(&cm_scacheLock);
5582 /* when we get here, either we have a read-lock and want a
5583 write-lock or we don't have any locks and we want some
5586 if (scp->serverLock == LockRead) {
5588 osi_assertx(newLock == LockWrite, "!LockWrite");
5590 osi_Log0(afsd_logp, " Attempting to UPGRADE from LockRead to LockWrite");
5592 scp->lockDataVersion = scp->dataVersion;
5593 check_data_version = TRUE;
5595 code = cm_IntReleaseLock(scp, userp, &req);
5598 goto pre_syncopdone;
5600 scp->serverLock = -1;
5603 code = cm_IntSetLock(scp, userp, newLock, &req);
5606 if (scp->dataVersion != scp->lockDataVersion) {
5607 /* we lost a race. too bad */
5610 " Data version mismatch while upgrading lock.");
5612 " Data versions before=%I64d, after=%I64d",
5613 scp->lockDataVersion,
5616 " Releasing stale lock for scp 0x%x", scp);
5618 code = cm_IntReleaseLock(scp, userp, &req);
5620 scp->serverLock = -1;
5622 code = CM_ERROR_INVAL;
5624 cm_LockMarkSCacheLost(scp);
5626 scp->serverLock = newLock;
5631 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5637 if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
5638 lock_ObtainWrite(&cm_scacheLock);
5639 if (scp->fileLocksT == &oldFileLock->fileq)
5640 scp->fileLocksT = osi_QPrev(&oldFileLock->fileq);
5641 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, &oldFileLock->fileq);
5642 lock_ReleaseWrite(&cm_scacheLock);
5644 lock_ReleaseWrite(&scp->rw);
5647 lock_ObtainWrite(&cm_scacheLock);
5649 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5650 } else if (code != CM_ERROR_WOULDBLOCK) {
5651 oldFileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5652 cm_ReleaseUser(oldFileLock->userp);
5653 oldFileLock->userp = NULL;
5654 if (oldFileLock->scp) {
5655 cm_ReleaseSCacheNoLock(oldFileLock->scp);
5656 oldFileLock->scp = NULL;
5659 lock_ReleaseWrite(&cm_scacheLock);
5664 cm_key_t cm_GenerateKey(afs_uint16 session_id, afs_offs_t process_id, afs_uint16 file_id)
5668 key.process_id = process_id;
5669 key.session_id = session_id;
5670 key.file_id = file_id;
5675 int cm_KeyEquals(cm_key_t *k1, cm_key_t *k2, int flags)
5677 return (k1->session_id == k2->session_id) && (k1->file_id == k2->file_id) &&
5678 ((flags & CM_UNLOCK_BY_FID) || (k1->process_id == k2->process_id));
5681 void cm_ReleaseAllLocks(void)
5687 cm_file_lock_t *fileLock;
5690 for (i = 0; i < cm_data.scacheHashTableSize; i++)
5692 for ( scp = cm_data.scacheHashTablep[i]; scp; scp = scp->nextp ) {
5693 while (scp->fileLocksH != NULL) {
5694 lock_ObtainWrite(&scp->rw);
5695 lock_ObtainWrite(&cm_scacheLock);
5696 if (!scp->fileLocksH) {
5697 lock_ReleaseWrite(&cm_scacheLock);
5698 lock_ReleaseWrite(&scp->rw);
5701 fileLock = (cm_file_lock_t *)((char *) scp->fileLocksH - offsetof(cm_file_lock_t, fileq));
5702 userp = fileLock->userp;
5704 key = fileLock->key;
5705 cm_HoldSCacheNoLock(scp);
5706 lock_ReleaseWrite(&cm_scacheLock);
5707 cm_UnlockByKey(scp, key, 0, userp, &req);
5708 cm_ReleaseSCache(scp);
5709 cm_ReleaseUser(userp);
5710 lock_ReleaseWrite(&scp->rw);