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, &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, &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, &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, 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 scp->flags |= CM_SCACHEFLAG_DELETED;
1633 lock_ReleaseWrite(&scp->rw);
1644 /* called with a write locked vnode, and fills in the link info.
1645 * returns this the vnode still write locked.
1647 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1654 lock_AssertWrite(&linkScp->rw);
1655 if (!linkScp->mountPointStringp[0]) {
1657 #ifdef AFS_FREELANCE_CLIENT
1658 /* File servers do not have data for freelance entries */
1659 if (cm_freelanceEnabled &&
1660 linkScp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
1661 linkScp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
1663 code = cm_FreelanceFetchMountPointString(linkScp);
1665 #endif /* AFS_FREELANCE_CLIENT */
1667 /* read the link data from the file server*/
1668 lock_ReleaseWrite(&linkScp->rw);
1669 thyper.LowPart = thyper.HighPart = 0;
1670 code = buf_Get(linkScp, &thyper, &bufp);
1671 lock_ObtainWrite(&linkScp->rw);
1675 code = cm_SyncOp(linkScp, bufp, userp, reqp, 0,
1676 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1681 cm_SyncOpDone(linkScp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1683 if (cm_HaveBuffer(linkScp, bufp, 0))
1686 code = cm_GetBuffer(linkScp, bufp, NULL, userp, reqp);
1691 } /* while loop to get the data */
1693 /* now if we still have no link read in,
1694 * copy the data from the buffer */
1695 if ((temp = linkScp->length.LowPart) >= MOUNTPOINTLEN) {
1697 return CM_ERROR_TOOBIG;
1700 /* otherwise, it fits; make sure it is still null (could have
1701 * lost race with someone else referencing this link above),
1702 * and if so, copy in the data.
1704 if (!linkScp->mountPointStringp[0]) {
1705 strncpy(linkScp->mountPointStringp, bufp->datap, temp);
1706 linkScp->mountPointStringp[temp] = 0; /* null terminate */
1711 if ( !strnicmp(linkScp->mountPointStringp, "msdfs:", strlen("msdfs:")) )
1712 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1714 } /* don't have sym link contents cached */
1719 /* called with a held vnode and a path suffix, with the held vnode being a
1720 * symbolic link. Our goal is to generate a new path to interpret, and return
1721 * this new path in newSpaceBufferp. If the new vnode is relative to a dir
1722 * other than the directory containing the symbolic link, then the new root is
1723 * returned in *newRootScpp, otherwise a null is returned there.
1725 long cm_AssembleLink(cm_scache_t *linkScp, fschar_t *pathSuffixp,
1726 cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1727 cm_user_t *userp, cm_req_t *reqp)
1734 *newRootScpp = NULL;
1735 *newSpaceBufferp = NULL;
1737 lock_ObtainWrite(&linkScp->rw);
1738 code = cm_HandleLink(linkScp, userp, reqp);
1742 /* if we may overflow the buffer, bail out; buffer is signficantly
1743 * bigger than max path length, so we don't really have to worry about
1744 * being a little conservative here.
1746 if (cm_FsStrLen(linkScp->mountPointStringp) + cm_FsStrLen(pathSuffixp) + 2
1747 >= CM_UTILS_SPACESIZE) {
1748 code = CM_ERROR_TOOBIG;
1752 tsp = cm_GetSpace();
1753 linkp = linkScp->mountPointStringp;
1754 if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1755 if (strlen(linkp) > cm_mountRootLen)
1756 StringCbCopyA((char *) tsp->data, sizeof(tsp->data), linkp+cm_mountRootLen+1);
1759 *newRootScpp = cm_data.rootSCachep;
1760 cm_HoldSCache(cm_data.rootSCachep);
1761 } else if (linkp[0] == '\\' && linkp[1] == '\\') {
1762 if (!strnicmp(&linkp[2], cm_NetbiosName, (len = (long)strlen(cm_NetbiosName))))
1764 char * p = &linkp[len + 3];
1765 if (strnicmp(p, "all", 3) == 0)
1768 StringCbCopyA(tsp->data, sizeof(tsp->data), p);
1769 for (p = tsp->data; *p; p++) {
1773 *newRootScpp = cm_data.rootSCachep;
1774 cm_HoldSCache(cm_data.rootSCachep);
1776 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1777 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1778 code = CM_ERROR_PATH_NOT_COVERED;
1780 } else if ( linkScp->fileType == CM_SCACHETYPE_DFSLINK ||
1781 !strnicmp(linkp, "msdfs:", (len = (long)strlen("msdfs:"))) ) {
1782 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1783 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1784 code = CM_ERROR_PATH_NOT_COVERED;
1785 } else if (*linkp == '\\' || *linkp == '/') {
1787 /* formerly, this was considered to be from the AFS root,
1788 * but this seems to create problems. instead, we will just
1789 * reject the link */
1790 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp+1);
1791 *newRootScpp = cm_data.rootSCachep;
1792 cm_HoldSCache(cm_data.rootSCachep);
1794 /* we still copy the link data into the response so that
1795 * the user can see what the link points to
1797 linkScp->fileType = CM_SCACHETYPE_INVALID;
1798 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1799 code = CM_ERROR_NOSUCHPATH;
1802 /* a relative link */
1803 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1805 if (pathSuffixp[0] != 0) { /* if suffix string is non-null */
1806 StringCchCatA(tsp->data,lengthof(tsp->data), "\\");
1807 StringCchCatA(tsp->data,lengthof(tsp->data), pathSuffixp);
1811 clientchar_t * cpath = cm_FsStringToClientStringAlloc(tsp->data, -1, NULL);
1812 if (cpath != NULL) {
1813 cm_ClientStrCpy(tsp->wdata, lengthof(tsp->wdata), cpath);
1815 *newSpaceBufferp = tsp;
1817 code = CM_ERROR_NOSUCHPATH;
1824 if (code == CM_ERROR_PATH_NOT_COVERED && reqp->tidPathp && reqp->relPathp) {
1825 cm_VolStatus_Notify_DFS_Mapping(linkScp, reqp->tidPathp, reqp->relPathp);
1830 lock_ReleaseWrite(&linkScp->rw);
1833 #ifdef DEBUG_REFCOUNT
1834 long cm_NameIDbg(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
1835 cm_user_t *userp, clientchar_t *tidPathp, cm_req_t *reqp,
1836 cm_scache_t **outScpp,
1837 char * file, long line)
1839 long cm_NameI(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
1840 cm_user_t *userp, clientchar_t *tidPathp,
1841 cm_req_t *reqp, cm_scache_t **outScpp)
1845 clientchar_t *tp; /* ptr moving through input buffer */
1846 clientchar_t tc; /* temp char */
1847 int haveComponent; /* has new component started? */
1848 clientchar_t component[AFSPATHMAX]; /* this is the new component */
1849 clientchar_t *cp; /* component name being assembled */
1850 cm_scache_t *tscp; /* current location in the hierarchy */
1851 cm_scache_t *nscp; /* next dude down */
1852 cm_scache_t *dirScp; /* last dir we searched */
1853 cm_scache_t *linkScp; /* new root for the symlink we just
1855 cm_space_t *psp; /* space for current path, if we've hit
1857 cm_space_t *tempsp; /* temp vbl */
1858 clientchar_t *restp; /* rest of the pathname to interpret */
1859 int symlinkCount; /* count of # of symlinks traversed */
1860 int extraFlag; /* avoid chasing mt pts for dir cmd */
1861 int phase = 1; /* 1 = tidPathp, 2 = pathp */
1862 #define MAX_FID_COUNT 512
1863 cm_fid_t fids[MAX_FID_COUNT]; /* array of fids processed in this path walk */
1864 int fid_count = 0; /* number of fids processed in this path walk */
1869 #ifdef DEBUG_REFCOUNT
1870 afsi_log("%s:%d cm_NameI rootscp 0x%p ref %d", file, line, rootSCachep, rootSCachep->refCount);
1871 osi_Log4(afsd_logp,"cm_NameI rootscp 0x%p path %S tidpath %S flags 0x%x",
1872 rootSCachep, pathp ? pathp : L"<NULL>", tidPathp ? tidPathp : L"<NULL>",
1887 cm_HoldSCache(tscp);
1895 /* map Unix slashes into DOS ones so we can interpret Unix
1901 if (!haveComponent) {
1904 } else if (tc == 0) {
1918 /* we have a component here */
1919 if (tc == 0 || tc == '\\') {
1920 /* end of the component; we're at the last
1921 * component if tc == 0. However, if the last
1922 * is a symlink, we have more to do.
1924 *cp++ = 0; /* add null termination */
1926 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
1927 extraFlag = CM_FLAG_NOMOUNTCHASE;
1928 code = cm_Lookup(tscp, component,
1930 userp, reqp, &nscp);
1933 if (!cm_ClientStrCmp(component,_C("..")) ||
1934 !cm_ClientStrCmp(component,_C("."))) {
1936 * roll back the fid list until we find the
1937 * fid that matches where we are now. Its not
1938 * necessarily one or two fids because they
1939 * might have been symlinks or mount points or
1940 * both that were crossed.
1942 for ( i=fid_count-1; i>=0; i--) {
1943 if (!cm_FidCmp(&nscp->fid, &fids[i]))
1948 /* add the new fid to the list */
1949 if (fid_count == MAX_FID_COUNT) {
1950 code = CM_ERROR_TOO_MANY_SYMLINKS;
1951 cm_ReleaseSCache(nscp);
1955 fids[fid_count++] = nscp->fid;
1960 cm_ReleaseSCache(tscp);
1962 cm_ReleaseSCache(dirScp);
1965 if ((code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH) &&
1966 tscp->fileType == CM_SCACHETYPE_SYMLINK) {
1967 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_NOSUCHPATH");
1968 return CM_ERROR_NOSUCHPATH;
1970 osi_Log1(afsd_logp,"cm_NameI code 0x%x", code);
1975 haveComponent = 0; /* component done */
1977 cm_ReleaseSCache(dirScp);
1978 dirScp = tscp; /* for some symlinks */
1979 tscp = nscp; /* already held */
1981 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
1984 cm_ReleaseSCache(dirScp);
1990 /* now, if tscp is a symlink, we should follow it and
1991 * assemble the path again.
1993 lock_ObtainWrite(&tscp->rw);
1994 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1995 CM_SCACHESYNC_GETSTATUS
1996 | CM_SCACHESYNC_NEEDCALLBACK);
1998 lock_ReleaseWrite(&tscp->rw);
1999 cm_ReleaseSCache(tscp);
2002 cm_ReleaseSCache(dirScp);
2007 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2009 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
2010 /* this is a symlink; assemble a new buffer */
2011 lock_ReleaseWrite(&tscp->rw);
2012 if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
2013 cm_ReleaseSCache(tscp);
2016 cm_ReleaseSCache(dirScp);
2021 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_TOO_MANY_SYMLINKS");
2022 return CM_ERROR_TOO_MANY_SYMLINKS;
2032 /* TODO: make this better */
2033 frestp = cm_ClientStringToFsStringAlloc(restp, -1, NULL);
2034 code = cm_AssembleLink(tscp, frestp, &linkScp, &tempsp, userp, reqp);
2038 if (code == 0 && linkScp != NULL) {
2039 if (linkScp == cm_data.rootSCachep)
2042 for ( i=0; i<fid_count; i++) {
2043 if ( !cm_FidCmp(&linkScp->fid, &fids[i]) ) {
2044 code = CM_ERROR_TOO_MANY_SYMLINKS;
2045 cm_ReleaseSCache(linkScp);
2051 if (i == fid_count && fid_count < MAX_FID_COUNT) {
2052 fids[fid_count++] = linkScp->fid;
2057 /* something went wrong */
2058 cm_ReleaseSCache(tscp);
2061 cm_ReleaseSCache(dirScp);
2067 /* otherwise, tempsp has the new path,
2068 * and linkScp is the new root from
2069 * which to interpret that path.
2070 * Continue with the namei processing,
2071 * also doing the bookkeeping for the
2072 * space allocation and tracking the
2073 * vnode reference counts.
2079 cm_ReleaseSCache(tscp);
2084 * now, if linkScp is null, that's
2085 * AssembleLink's way of telling us that
2086 * the sym link is relative to the dir
2087 * containing the link. We have a ref
2088 * to it in dirScp, and we hold it now
2089 * and reuse it as the new spot in the
2097 /* not a symlink, we may be done */
2098 lock_ReleaseWrite(&tscp->rw);
2106 cm_ReleaseSCache(dirScp);
2114 cm_ReleaseSCache(dirScp);
2117 } /* end of a component */
2120 } /* we have a component */
2121 } /* big while loop over all components */
2125 cm_ReleaseSCache(dirScp);
2131 cm_ReleaseSCache(tscp);
2133 #ifdef DEBUG_REFCOUNT
2134 afsi_log("%s:%d cm_NameI code 0x%x outScpp 0x%p ref %d", file, line, code, *outScpp, (*outScpp) ? (*outScpp)->refCount : 0);
2136 osi_Log2(afsd_logp,"cm_NameI code 0x%x outScpp 0x%p", code, *outScpp);
2140 /* called with a dir, and a vnode within the dir that happens to be a symlink.
2141 * We chase the link, and return a held pointer to the target, if it exists,
2142 * in *outScpp. If we succeed, we return 0, otherwise we return an error code
2143 * and do not hold or return a target vnode.
2145 * This is very similar to calling cm_NameI with the last component of a name,
2146 * which happens to be a symlink, except that we've already passed by the name.
2148 * This function is typically called by the directory listing functions, which
2149 * encounter symlinks but need to return the proper file length so programs
2150 * like "more" work properly when they make use of the attributes retrieved from
2153 * The input vnode should not be locked when this function is called.
2155 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
2156 cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
2160 cm_scache_t *newRootScp;
2164 osi_Log1(afsd_logp, "Evaluating symlink scp 0x%p", linkScp);
2166 code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
2170 /* now, if newRootScp is NULL, we're really being told that the symlink
2171 * is relative to the current directory (dscp).
2173 if (newRootScp == NULL) {
2175 cm_HoldSCache(dscp);
2178 code = cm_NameI(newRootScp, spacep->wdata,
2179 CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
2180 userp, NULL, reqp, outScpp);
2182 if (code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH)
2183 code = CM_ERROR_NOSUCHPATH;
2185 /* this stuff is allocated no matter what happened on the namei call,
2187 cm_FreeSpace(spacep);
2188 cm_ReleaseSCache(newRootScp);
2190 if (linkScp == *outScpp) {
2191 cm_ReleaseSCache(*outScpp);
2193 code = CM_ERROR_NOSUCHPATH;
2199 /* for a given entry, make sure that it isn't in the stat cache, and then
2200 * add it to the list of file IDs to be obtained.
2202 * Don't bother adding it if we already have a vnode. Note that the dir
2203 * is locked, so we have to be careful checking the vnode we're thinking of
2204 * processing, to avoid deadlocks.
2206 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
2217 /* Don't overflow bsp. */
2218 if (bsp->counter >= CM_BULKMAX)
2219 return CM_ERROR_STOPNOW;
2221 thyper.LowPart = cm_data.buf_blockSize;
2222 thyper.HighPart = 0;
2223 thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
2225 /* thyper is now the first byte past the end of the record we're
2226 * interested in, and bsp->bufOffset is the first byte of the record
2227 * we're interested in.
2228 * Skip data in the others.
2231 if (LargeIntegerLessThan(*offp, bsp->bufOffset))
2233 if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
2234 return CM_ERROR_STOPNOW;
2235 if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
2238 cm_SetFid(&tfid, scp->fid.cell, scp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
2239 tscp = cm_FindSCache(&tfid);
2241 if (lock_TryWrite(&tscp->rw)) {
2242 /* we have an entry that we can look at */
2243 if (!(tscp->flags & CM_SCACHEFLAG_EACCESS) && cm_HaveCallback(tscp)) {
2244 /* we have a callback on it. Don't bother
2245 * fetching this stat entry, since we're happy
2246 * with the info we have.
2248 lock_ReleaseWrite(&tscp->rw);
2249 cm_ReleaseSCache(tscp);
2252 lock_ReleaseWrite(&tscp->rw);
2254 cm_ReleaseSCache(tscp);
2257 #ifdef AFS_FREELANCE_CLIENT
2258 // yj: if this is a mountpoint under root.afs then we don't want it
2259 // to be bulkstat-ed, instead, we call getSCache directly and under
2260 // getSCache, it is handled specially.
2261 if ( cm_freelanceEnabled &&
2262 tfid.cell==AFS_FAKE_ROOT_CELL_ID &&
2263 tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
2264 !(tfid.vnode==0x1 && tfid.unique==0x1) )
2266 osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
2267 return cm_GetSCache(&tfid, &tscp, NULL, NULL);
2269 #endif /* AFS_FREELANCE_CLIENT */
2272 bsp->fids[i].Volume = scp->fid.volume;
2273 bsp->fids[i].Vnode = tfid.vnode;
2274 bsp->fids[i].Unique = tfid.unique;
2279 cm_TryBulkStatRPC(cm_scache_t *dscp, cm_bulkStat_t *bbp, cm_user_t *userp, cm_req_t *reqp)
2282 AFSCBFids fidStruct;
2283 AFSBulkStats statStruct;
2285 AFSCBs callbackStruct;
2288 cm_callbackRequest_t cbReq;
2294 struct rx_connection * rxconnp;
2295 int inlinebulk = 0; /* Did we use InlineBulkStatus RPC or not? */
2297 /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
2298 * make the calls to create the entries. Handle AFSCBMAX files at a
2301 for (filex = 0; filex < bbp->counter; filex += filesThisCall) {
2302 filesThisCall = bbp->counter - filex;
2303 if (filesThisCall > AFSCBMAX)
2304 filesThisCall = AFSCBMAX;
2306 fidStruct.AFSCBFids_len = filesThisCall;
2307 fidStruct.AFSCBFids_val = &bbp->fids[filex];
2308 statStruct.AFSBulkStats_len = filesThisCall;
2309 statStruct.AFSBulkStats_val = &bbp->stats[filex];
2310 callbackStruct.AFSCBs_len = filesThisCall;
2311 callbackStruct.AFSCBs_val = &bbp->callbacks[filex];
2312 cm_StartCallbackGrantingCall(NULL, &cbReq);
2313 osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
2315 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2319 rxconnp = cm_GetRxConn(connp);
2320 if (!(connp->serverp->flags & CM_SERVERFLAG_NOINLINEBULK)) {
2321 code = RXAFS_InlineBulkStatus(rxconnp, &fidStruct,
2322 &statStruct, &callbackStruct, &volSync);
2323 if (code == RXGEN_OPCODE) {
2324 cm_SetServerNoInlineBulk(connp->serverp, 0);
2330 code = RXAFS_BulkStatus(rxconnp, &fidStruct,
2331 &statStruct, &callbackStruct, &volSync);
2333 rx_PutConnection(rxconnp);
2335 } while (cm_Analyze(connp, userp, reqp, &dscp->fid,
2336 &volSync, NULL, &cbReq, code));
2337 code = cm_MapRPCError(code, reqp);
2339 osi_Log2(afsd_logp, "CALL %sBulkStatus FAILURE code 0x%x",
2340 inlinebulk ? "Inline" : "", code);
2342 osi_Log1(afsd_logp, "CALL %sBulkStatus SUCCESS", inlinebulk ? "Inline" : "");
2344 /* may as well quit on an error, since we're not going to do
2345 * much better on the next immediate call, either.
2348 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2352 /* otherwise, we should do the merges */
2353 for (i = 0; i<filesThisCall; i++) {
2355 cm_SetFid(&tfid, dscp->fid.cell, bbp->fids[j].Volume, bbp->fids[j].Vnode, bbp->fids[j].Unique);
2356 code = cm_GetSCache(&tfid, &scp, userp, reqp);
2360 /* otherwise, if this entry has no callback info,
2363 lock_ObtainWrite(&scp->rw);
2364 /* now, we have to be extra paranoid on merging in this
2365 * information, since we didn't use cm_SyncOp before
2366 * starting the fetch to make sure that no bad races
2367 * were occurring. Specifically, we need to make sure
2368 * we don't obliterate any newer information in the
2369 * vnode than have here.
2371 * Right now, be pretty conservative: if there's a
2372 * callback or a pending call, skip it.
2374 if ((scp->cbServerp == NULL || (scp->flags & CM_SCACHEFLAG_EACCESS))
2376 (CM_SCACHEFLAG_FETCHING
2377 | CM_SCACHEFLAG_STORING
2378 | CM_SCACHEFLAG_SIZESTORING))) {
2379 cm_EndCallbackGrantingCall(scp, &cbReq,
2381 CM_CALLBACK_MAINTAINCOUNT);
2382 cm_MergeStatus(dscp, scp, &bbp->stats[j], &volSync, userp, 0);
2384 lock_ReleaseWrite(&scp->rw);
2385 cm_ReleaseSCache(scp);
2386 } /* all files in the response */
2387 /* now tell it to drop the count,
2388 * after doing the vnode processing above */
2389 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2390 } /* while there are still more files to process */
2392 /* If we did the InlineBulk RPC pull out the return code and log it */
2394 if ((&bbp->stats[0])->errorCode) {
2395 osi_Log1(afsd_logp, "cm_TryBulkStat bulk stat error: %d",
2396 (&bbp->stats[0])->errorCode);
2397 code = (&bbp->stats[0])->errorCode;
2404 /* called with a write locked scp and a pointer to a buffer. Make bulk stat
2405 * calls on all undeleted files in the page of the directory specified.
2408 cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
2414 osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%p", dscp);
2416 /* should be on a buffer boundary */
2417 osi_assertx((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0, "invalid offset");
2419 bbp = malloc(sizeof(cm_bulkStat_t));
2420 memset(bbp, 0, sizeof(cm_bulkStat_t));
2421 bbp->bufOffset = *offsetp;
2423 lock_ReleaseWrite(&dscp->rw);
2424 /* first, assemble the file IDs we need to stat */
2425 code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) bbp, offsetp, userp, reqp, NULL);
2427 /* if we failed, bail out early */
2428 if (code && code != CM_ERROR_STOPNOW) {
2430 lock_ObtainWrite(&dscp->rw);
2434 code = cm_TryBulkStatRPC(dscp, bbp, userp, reqp);
2435 osi_Log1(afsd_logp, "END cm_TryBulkStat code 0x%x", code);
2437 lock_ObtainWrite(&dscp->rw);
2442 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
2446 /* initialize store back mask as inexpensive local variable */
2448 memset(statusp, 0, sizeof(AFSStoreStatus));
2450 /* copy out queued info from scache first, if scp passed in */
2452 if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
2453 statusp->ClientModTime = scp->clientModTime;
2454 mask |= AFS_SETMODTIME;
2455 scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
2460 /* now add in our locally generated request */
2461 if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
2462 statusp->ClientModTime = attrp->clientModTime;
2463 mask |= AFS_SETMODTIME;
2465 if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
2466 statusp->UnixModeBits = attrp->unixModeBits;
2467 mask |= AFS_SETMODE;
2469 if (attrp->mask & CM_ATTRMASK_OWNER) {
2470 statusp->Owner = attrp->owner;
2471 mask |= AFS_SETOWNER;
2473 if (attrp->mask & CM_ATTRMASK_GROUP) {
2474 statusp->Group = attrp->group;
2475 mask |= AFS_SETGROUP;
2478 statusp->Mask = mask;
2481 /* set the file size, and make sure that all relevant buffers have been
2482 * truncated. Ensure that any partially truncated buffers have been zeroed
2483 * to the end of the buffer.
2485 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
2491 /* start by locking out buffer creation */
2492 lock_ObtainWrite(&scp->bufCreateLock);
2494 /* verify that this is a file, not a dir or a symlink */
2495 lock_ObtainWrite(&scp->rw);
2496 code = cm_SyncOp(scp, NULL, userp, reqp, 0,
2497 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2500 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2502 if (scp->fileType != CM_SCACHETYPE_FILE) {
2503 code = CM_ERROR_ISDIR;
2508 if (LargeIntegerLessThan(*sizep, scp->length))
2513 lock_ReleaseWrite(&scp->rw);
2515 /* can't hold scp->rw lock here, since we may wait for a storeback to
2516 * finish if the buffer package is cleaning a buffer by storing it to
2520 buf_Truncate(scp, userp, reqp, sizep);
2522 /* now ensure that file length is short enough, and update truncPos */
2523 lock_ObtainWrite(&scp->rw);
2525 /* make sure we have a callback (so we have the right value for the
2526 * length), and wait for it to be safe to do a truncate.
2528 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
2529 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2530 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2532 /* If we only have 'i' bits, then we should still be able to set
2533 the size of a file we created. */
2534 if (code == CM_ERROR_NOACCESS && scp->creator == userp) {
2535 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
2536 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2537 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2543 if (LargeIntegerLessThan(*sizep, scp->length)) {
2544 /* a real truncation. If truncPos is not set yet, or is bigger
2545 * than where we're truncating the file, set truncPos to this
2550 if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
2551 || LargeIntegerLessThan(*sizep, scp->length)) {
2553 scp->truncPos = *sizep;
2554 scp->mask |= CM_SCACHEMASK_TRUNCPOS;
2556 /* in either case, the new file size has been changed */
2557 scp->length = *sizep;
2558 scp->mask |= CM_SCACHEMASK_LENGTH;
2560 else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
2561 /* really extending the file */
2562 scp->length = *sizep;
2563 scp->mask |= CM_SCACHEMASK_LENGTH;
2566 /* done successfully */
2569 cm_SyncOpDone(scp, NULL,
2570 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2571 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2574 lock_ReleaseWrite(&scp->rw);
2575 lock_ReleaseWrite(&scp->bufCreateLock);
2580 /* set the file size or other attributes (but not both at once) */
2581 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
2585 AFSFetchStatus afsOutStatus;
2589 AFSStoreStatus afsInStatus;
2590 struct rx_connection * rxconnp;
2592 /* handle file length setting */
2593 if (attrp->mask & CM_ATTRMASK_LENGTH)
2594 return cm_SetLength(scp, &attrp->length, userp, reqp);
2596 lock_ObtainWrite(&scp->rw);
2597 /* otherwise, we have to make an RPC to get the status */
2598 code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
2600 lock_ReleaseWrite(&scp->rw);
2603 lock_ConvertWToR(&scp->rw);
2605 /* make the attr structure */
2606 cm_StatusFromAttr(&afsInStatus, scp, attrp);
2608 tfid.Volume = scp->fid.volume;
2609 tfid.Vnode = scp->fid.vnode;
2610 tfid.Unique = scp->fid.unique;
2611 lock_ReleaseRead(&scp->rw);
2613 /* now make the RPC */
2614 osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%p", scp);
2616 code = cm_ConnFromFID(&scp->fid, userp, reqp, &connp);
2620 rxconnp = cm_GetRxConn(connp);
2621 code = RXAFS_StoreStatus(rxconnp, &tfid,
2622 &afsInStatus, &afsOutStatus, &volSync);
2623 rx_PutConnection(rxconnp);
2625 } while (cm_Analyze(connp, userp, reqp,
2626 &scp->fid, &volSync, NULL, NULL, code));
2627 code = cm_MapRPCError(code, reqp);
2630 osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
2632 osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
2634 lock_ObtainWrite(&scp->rw);
2635 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
2637 cm_MergeStatus(NULL, scp, &afsOutStatus, &volSync, userp,
2638 CM_MERGEFLAG_FORCE|CM_MERGEFLAG_STOREDATA);
2640 /* if we're changing the mode bits, discard the ACL cache,
2641 * since we changed the mode bits.
2643 if (afsInStatus.Mask & AFS_SETMODE)
2644 cm_FreeAllACLEnts(scp);
2645 lock_ReleaseWrite(&scp->rw);
2649 long cm_Create(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
2650 cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
2655 cm_callbackRequest_t cbReq;
2658 cm_scache_t *scp = NULL;
2660 AFSStoreStatus inStatus;
2661 AFSFetchStatus updatedDirStatus;
2662 AFSFetchStatus newFileStatus;
2663 AFSCallBack newFileCallback;
2665 struct rx_connection * rxconnp;
2667 fschar_t * fnamep = NULL;
2669 /* can't create names with @sys in them; must expand it manually first.
2670 * return "invalid request" if they try.
2672 if (cm_ExpandSysName(cnamep, NULL, 0, 0)) {
2673 return CM_ERROR_ATSYS;
2676 #ifdef AFS_FREELANCE_CLIENT
2677 /* Freelance root volume does not hold files */
2678 if (cm_freelanceEnabled &&
2679 dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
2680 dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
2682 return CM_ERROR_NOACCESS;
2684 #endif /* AFS_FREELANCE_CLIENT */
2686 /* before starting the RPC, mark that we're changing the file data, so
2687 * that someone who does a chmod will know to wait until our call
2690 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2691 lock_ObtainWrite(&dscp->rw);
2692 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2693 lock_ReleaseWrite(&dscp->rw);
2695 cm_StartCallbackGrantingCall(NULL, &cbReq);
2697 cm_EndDirOp(&dirop);
2704 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
2706 cm_StatusFromAttr(&inStatus, NULL, attrp);
2708 /* try the RPC now */
2709 osi_Log1(afsd_logp, "CALL CreateFile scp 0x%p", dscp);
2711 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2715 dirAFSFid.Volume = dscp->fid.volume;
2716 dirAFSFid.Vnode = dscp->fid.vnode;
2717 dirAFSFid.Unique = dscp->fid.unique;
2719 rxconnp = cm_GetRxConn(connp);
2720 code = RXAFS_CreateFile(connp->rxconnp, &dirAFSFid, fnamep,
2721 &inStatus, &newAFSFid, &newFileStatus,
2722 &updatedDirStatus, &newFileCallback,
2724 rx_PutConnection(rxconnp);
2726 } while (cm_Analyze(connp, userp, reqp,
2727 &dscp->fid, &volSync, NULL, &cbReq, code));
2728 code = cm_MapRPCError(code, reqp);
2731 osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
2733 osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
2736 lock_ObtainWrite(&dirop.scp->dirlock);
2737 dirop.lockType = CM_DIRLOCK_WRITE;
2739 lock_ObtainWrite(&dscp->rw);
2740 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2742 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
2744 lock_ReleaseWrite(&dscp->rw);
2746 /* now try to create the file's entry, too, but be careful to
2747 * make sure that we don't merge in old info. Since we weren't locking
2748 * out any requests during the file's creation, we may have pretty old
2752 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
2753 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2755 lock_ObtainWrite(&scp->rw);
2756 scp->creator = userp; /* remember who created it */
2757 if (!cm_HaveCallback(scp)) {
2758 cm_MergeStatus(dscp, scp, &newFileStatus, &volSync,
2760 cm_EndCallbackGrantingCall(scp, &cbReq,
2761 &newFileCallback, 0);
2764 lock_ReleaseWrite(&scp->rw);
2768 /* make sure we end things properly */
2770 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2772 if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
2773 cm_DirCreateEntry(&dirop, fnamep, &newFid);
2775 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
2778 cm_EndDirOp(&dirop);
2787 cm_ReleaseSCache(scp);
2792 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
2796 code = buf_CleanVnode(scp, userp, reqp);
2798 lock_ObtainWrite(&scp->rw);
2800 if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
2801 | CM_SCACHEMASK_CLIENTMODTIME
2802 | CM_SCACHEMASK_LENGTH))
2803 code = cm_StoreMini(scp, userp, reqp);
2805 if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) {
2806 code = (scp->flags & CM_SCACHEFLAG_OVERQUOTA) ? CM_ERROR_QUOTA : CM_ERROR_SPACE;
2807 scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE);
2810 lock_ReleaseWrite(&scp->rw);
2815 long cm_MakeDir(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
2816 cm_user_t *userp, cm_req_t *reqp, cm_scache_t **scpp)
2821 cm_callbackRequest_t cbReq;
2824 cm_scache_t *scp = NULL;
2826 AFSStoreStatus inStatus;
2827 AFSFetchStatus updatedDirStatus;
2828 AFSFetchStatus newDirStatus;
2829 AFSCallBack newDirCallback;
2831 struct rx_connection * rxconnp;
2833 fschar_t * fnamep = NULL;
2835 /* can't create names with @sys in them; must expand it manually first.
2836 * return "invalid request" if they try.
2838 if (cm_ExpandSysName(cnamep, NULL, 0, 0)) {
2839 return CM_ERROR_ATSYS;
2842 #ifdef AFS_FREELANCE_CLIENT
2843 /* Freelance root volume does not hold subdirectories */
2844 if (cm_freelanceEnabled &&
2845 dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
2846 dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
2848 return CM_ERROR_NOACCESS;
2850 #endif /* AFS_FREELANCE_CLIENT */
2852 /* before starting the RPC, mark that we're changing the directory
2853 * data, so that someone who does a chmod on the dir will wait until
2854 * our call completes.
2856 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2857 lock_ObtainWrite(&dscp->rw);
2858 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2859 lock_ReleaseWrite(&dscp->rw);
2861 cm_StartCallbackGrantingCall(NULL, &cbReq);
2863 cm_EndDirOp(&dirop);
2870 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
2871 cm_StatusFromAttr(&inStatus, NULL, attrp);
2873 /* try the RPC now */
2874 osi_Log1(afsd_logp, "CALL MakeDir scp 0x%p", dscp);
2876 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2880 dirAFSFid.Volume = dscp->fid.volume;
2881 dirAFSFid.Vnode = dscp->fid.vnode;
2882 dirAFSFid.Unique = dscp->fid.unique;
2884 rxconnp = cm_GetRxConn(connp);
2885 code = RXAFS_MakeDir(connp->rxconnp, &dirAFSFid, fnamep,
2886 &inStatus, &newAFSFid, &newDirStatus,
2887 &updatedDirStatus, &newDirCallback,
2889 rx_PutConnection(rxconnp);
2891 } while (cm_Analyze(connp, userp, reqp,
2892 &dscp->fid, &volSync, NULL, &cbReq, code));
2893 code = cm_MapRPCError(code, reqp);
2896 osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
2898 osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
2901 lock_ObtainWrite(&dirop.scp->dirlock);
2902 dirop.lockType = CM_DIRLOCK_WRITE;
2904 lock_ObtainWrite(&dscp->rw);
2905 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2907 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
2909 lock_ReleaseWrite(&dscp->rw);
2911 /* now try to create the new dir's entry, too, but be careful to
2912 * make sure that we don't merge in old info. Since we weren't locking
2913 * out any requests during the file's creation, we may have pretty old
2917 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
2918 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2920 lock_ObtainWrite(&scp->rw);
2921 if (!cm_HaveCallback(scp)) {
2922 cm_MergeStatus(dscp, scp, &newDirStatus, &volSync,
2924 cm_EndCallbackGrantingCall(scp, &cbReq,
2925 &newDirCallback, 0);
2928 lock_ReleaseWrite(&scp->rw);
2932 /* make sure we end things properly */
2934 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2936 if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
2937 cm_DirCreateEntry(&dirop, fnamep, &newFid);
2939 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
2942 cm_EndDirOp(&dirop);
2950 cm_ReleaseSCache(scp);
2953 /* and return error code */
2957 long cm_Link(cm_scache_t *dscp, clientchar_t *cnamep, cm_scache_t *sscp, long flags,
2958 cm_user_t *userp, cm_req_t *reqp)
2963 AFSFid existingAFSFid;
2964 AFSFetchStatus updatedDirStatus;
2965 AFSFetchStatus newLinkStatus;
2967 struct rx_connection * rxconnp;
2969 fschar_t * fnamep = NULL;
2971 if (dscp->fid.cell != sscp->fid.cell ||
2972 dscp->fid.volume != sscp->fid.volume) {
2973 return CM_ERROR_CROSSDEVLINK;
2976 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2977 lock_ObtainWrite(&dscp->rw);
2978 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2979 lock_ReleaseWrite(&dscp->rw);
2981 cm_EndDirOp(&dirop);
2986 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
2988 /* try the RPC now */
2989 osi_Log1(afsd_logp, "CALL Link scp 0x%p", dscp);
2991 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2994 dirAFSFid.Volume = dscp->fid.volume;
2995 dirAFSFid.Vnode = dscp->fid.vnode;
2996 dirAFSFid.Unique = dscp->fid.unique;
2998 existingAFSFid.Volume = sscp->fid.volume;
2999 existingAFSFid.Vnode = sscp->fid.vnode;
3000 existingAFSFid.Unique = sscp->fid.unique;
3002 rxconnp = cm_GetRxConn(connp);
3003 code = RXAFS_Link(rxconnp, &dirAFSFid, fnamep, &existingAFSFid,
3004 &newLinkStatus, &updatedDirStatus, &volSync);
3005 rx_PutConnection(rxconnp);
3006 osi_Log1(afsd_logp," RXAFS_Link returns 0x%x", code);
3008 } while (cm_Analyze(connp, userp, reqp,
3009 &dscp->fid, &volSync, NULL, NULL, code));
3011 code = cm_MapRPCError(code, reqp);
3014 osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
3016 osi_Log0(afsd_logp, "CALL Link SUCCESS");
3019 lock_ObtainWrite(&dirop.scp->dirlock);
3020 dirop.lockType = CM_DIRLOCK_WRITE;
3022 lock_ObtainWrite(&dscp->rw);
3023 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3025 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
3027 lock_ReleaseWrite(&dscp->rw);
3030 if (cm_CheckDirOpForSingleChange(&dirop)) {
3031 cm_DirCreateEntry(&dirop, fnamep, &sscp->fid);
3033 cm_BPlusDirCreateEntry(&dirop, cnamep, &sscp->fid);
3037 cm_EndDirOp(&dirop);
3044 long cm_SymLink(cm_scache_t *dscp, clientchar_t *cnamep, fschar_t *contentsp, long flags,
3045 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
3053 AFSStoreStatus inStatus;
3054 AFSFetchStatus updatedDirStatus;
3055 AFSFetchStatus newLinkStatus;
3057 struct rx_connection * rxconnp;
3059 fschar_t *fnamep = NULL;
3061 /* before starting the RPC, mark that we're changing the directory data,
3062 * so that someone who does a chmod on the dir will wait until our
3065 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
3066 lock_ObtainWrite(&dscp->rw);
3067 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3068 lock_ReleaseWrite(&dscp->rw);
3070 cm_EndDirOp(&dirop);
3075 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3077 cm_StatusFromAttr(&inStatus, NULL, attrp);
3079 /* try the RPC now */
3080 osi_Log1(afsd_logp, "CALL Symlink scp 0x%p", dscp);
3082 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3086 dirAFSFid.Volume = dscp->fid.volume;
3087 dirAFSFid.Vnode = dscp->fid.vnode;
3088 dirAFSFid.Unique = dscp->fid.unique;
3090 rxconnp = cm_GetRxConn(connp);
3091 code = RXAFS_Symlink(rxconnp, &dirAFSFid, fnamep, contentsp,
3092 &inStatus, &newAFSFid, &newLinkStatus,
3093 &updatedDirStatus, &volSync);
3094 rx_PutConnection(rxconnp);
3096 } while (cm_Analyze(connp, userp, reqp,
3097 &dscp->fid, &volSync, NULL, NULL, code));
3098 code = cm_MapRPCError(code, reqp);
3101 osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
3103 osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
3106 lock_ObtainWrite(&dirop.scp->dirlock);
3107 dirop.lockType = CM_DIRLOCK_WRITE;
3109 lock_ObtainWrite(&dscp->rw);
3110 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3112 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
3114 lock_ReleaseWrite(&dscp->rw);
3117 if (cm_CheckDirOpForSingleChange(&dirop)) {
3118 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3120 cm_DirCreateEntry(&dirop, fnamep, &newFid);
3122 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
3126 cm_EndDirOp(&dirop);
3128 /* now try to create the new dir's entry, too, but be careful to
3129 * make sure that we don't merge in old info. Since we weren't locking
3130 * out any requests during the file's creation, we may have pretty old
3134 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3135 code = cm_GetSCache(&newFid, &scp, userp, reqp);
3137 lock_ObtainWrite(&scp->rw);
3138 if (!cm_HaveCallback(scp)) {
3139 cm_MergeStatus(dscp, scp, &newLinkStatus, &volSync,
3142 lock_ReleaseWrite(&scp->rw);
3143 cm_ReleaseSCache(scp);
3149 /* and return error code */
3153 /*! \brief Remove a directory
3155 Encapsulates a call to RXAFS_RemoveDir().
3157 \param[in] dscp cm_scache_t for the directory containing the
3158 directory to be removed.
3160 \param[in] fnamep This will be the original name of the directory
3161 as known to the file server. It will be passed in to RXAFS_RemoveDir().
3162 This parameter is optional. If it is not provided the value
3165 \param[in] cnamep Normalized name used to update the local
3168 \param[in] userp cm_user_t for the request.
3170 \param[in] reqp Request tracker.
3172 long cm_RemoveDir(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t *cnamep, cm_user_t *userp, cm_req_t *reqp)
3178 AFSFetchStatus updatedDirStatus;
3180 struct rx_connection * rxconnp;
3182 cm_scache_t *scp = NULL;
3183 int free_fnamep = FALSE;
3185 if (fnamep == NULL) {
3188 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ, &dirop);
3190 code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
3193 cm_EndDirOp(&dirop);
3200 code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
3204 /* before starting the RPC, mark that we're changing the directory data,
3205 * so that someone who does a chmod on the dir will wait until our
3208 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
3209 lock_ObtainWrite(&dscp->rw);
3210 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3211 lock_ReleaseWrite(&dscp->rw);
3213 cm_EndDirOp(&dirop);
3218 /* try the RPC now */
3219 osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%p", dscp);
3221 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3225 dirAFSFid.Volume = dscp->fid.volume;
3226 dirAFSFid.Vnode = dscp->fid.vnode;
3227 dirAFSFid.Unique = dscp->fid.unique;
3229 rxconnp = cm_GetRxConn(connp);
3230 code = RXAFS_RemoveDir(rxconnp, &dirAFSFid, fnamep,
3231 &updatedDirStatus, &volSync);
3232 rx_PutConnection(rxconnp);
3234 } while (cm_Analyze(connp, userp, reqp,
3235 &dscp->fid, &volSync, NULL, NULL, code));
3236 code = cm_MapRPCErrorRmdir(code, reqp);
3239 osi_Log1(afsd_logp, "CALL RemoveDir FAILURE, code 0x%x", code);
3241 osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
3244 lock_ObtainWrite(&dirop.scp->dirlock);
3245 dirop.lockType = CM_DIRLOCK_WRITE;
3247 lock_ObtainWrite(&dscp->rw);
3248 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3250 cm_dnlcRemove(dscp, cnamep);
3251 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
3253 lock_ReleaseWrite(&dscp->rw);
3256 if (cm_CheckDirOpForSingleChange(&dirop) && cnamep != NULL) {
3257 cm_DirDeleteEntry(&dirop, fnamep);
3259 cm_BPlusDirDeleteEntry(&dirop, cnamep);
3263 cm_EndDirOp(&dirop);
3266 cm_ReleaseSCache(scp);
3268 lock_ObtainWrite(&scp->rw);
3269 scp->flags |= CM_SCACHEFLAG_DELETED;
3270 lock_ReleaseWrite(&scp->rw);
3278 /* and return error code */
3282 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
3284 /* grab mutex on contents */
3285 lock_ObtainWrite(&scp->rw);
3287 /* reset the prefetch info */
3288 scp->prefetch.base.LowPart = 0; /* base */
3289 scp->prefetch.base.HighPart = 0;
3290 scp->prefetch.end.LowPart = 0; /* and end */
3291 scp->prefetch.end.HighPart = 0;
3293 /* release mutex on contents */
3294 lock_ReleaseWrite(&scp->rw);
3300 /*! \brief Rename a file or directory
3302 Encapsulates a RXAFS_Rename() call.
3304 \param[in] oldDscp cm_scache_t for the directory containing the old
3307 \param[in] oldNamep The original old name known to the file server.
3308 This is the name that will be passed into the RXAFS_Rename().
3309 If it is not provided, it will be looked up.
3311 \param[in] normalizedOldNamep Normalized old name. This is used for
3312 updating local directory caches.
3314 \param[in] newDscp cm_scache_t for the directory containing the new
3317 \param[in] newNamep New name. Normalized.
3319 \param[in] userp cm_user_t for the request.
3321 \param[in,out] reqp Request tracker.
3324 long cm_Rename(cm_scache_t *oldDscp, fschar_t *oldNamep, clientchar_t *cOldNamep,
3325 cm_scache_t *newDscp, clientchar_t *cNewNamep, cm_user_t *userp,
3330 AFSFid oldDirAFSFid;
3331 AFSFid newDirAFSFid;
3333 AFSFetchStatus updatedOldDirStatus;
3334 AFSFetchStatus updatedNewDirStatus;
3337 struct rx_connection * rxconnp;
3338 cm_dirOp_t oldDirOp;
3341 cm_dirOp_t newDirOp;
3342 fschar_t * newNamep = NULL;
3343 int free_oldNamep = FALSE;
3344 cm_scache_t *oldScp = NULL, *newScp = NULL;
3346 if (cOldNamep == NULL || cNewNamep == NULL ||
3347 cm_ClientStrLen(cOldNamep) == 0 ||
3348 cm_ClientStrLen(cNewNamep) == 0)
3349 return CM_ERROR_INVAL;
3352 * Before we permit the operation, make sure that we do not already have
3353 * an object in the destination directory that has a case-insensitive match
3354 * for this name UNLESS the matching object is the object we are renaming.
3356 code = cm_Lookup(oldDscp, cOldNamep, 0, userp, reqp, &oldScp);
3358 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S old name lookup failed",
3359 oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3363 code = cm_Lookup(newDscp, cNewNamep, CM_FLAG_CASEFOLD, userp, reqp, &newScp);
3365 /* found a matching object with the new name */
3366 if (cm_FidCmp(&oldScp->fid, &newScp->fid)) {
3367 /* and they don't match so return an error */
3368 osi_Log2(afsd_logp, "cm_Rename newDscp 0x%p cNewName %S new name already exists",
3369 newDscp, osi_LogSaveStringW(afsd_logp, cNewNamep));
3370 code = CM_ERROR_EXISTS;
3372 cm_ReleaseSCache(newScp);
3374 } else if (code == CM_ERROR_AMBIGUOUS_FILENAME) {
3375 code = CM_ERROR_EXISTS;
3382 if (oldNamep == NULL) {
3385 code = cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_READ, &oldDirOp);
3387 code = cm_BPlusDirLookupOriginalName(&oldDirOp, cOldNamep, &oldNamep);
3389 free_oldNamep = TRUE;
3390 cm_EndDirOp(&oldDirOp);
3394 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S Original Name lookup failed",
3395 oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3401 /* before starting the RPC, mark that we're changing the directory data,
3402 * so that someone who does a chmod on the dir will wait until our call
3403 * completes. We do this in vnode order so that we don't deadlock,
3404 * which makes the code a little verbose.
3406 if (oldDscp == newDscp) {
3407 /* check for identical names */
3408 if (cm_ClientStrCmp(cOldNamep, cNewNamep) == 0) {
3409 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_RENAME_IDENTICAL",
3411 code = CM_ERROR_RENAME_IDENTICAL;
3416 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3417 lock_ObtainWrite(&oldDscp->rw);
3418 cm_dnlcRemove(oldDscp, cOldNamep);
3419 cm_dnlcRemove(oldDscp, cNewNamep);
3420 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3421 CM_SCACHESYNC_STOREDATA);
3422 lock_ReleaseWrite(&oldDscp->rw);
3424 cm_EndDirOp(&oldDirOp);
3428 /* two distinct dir vnodes */
3430 if (oldDscp->fid.cell != newDscp->fid.cell ||
3431 oldDscp->fid.volume != newDscp->fid.volume) {
3432 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_CROSSDEVLINK",
3434 code = CM_ERROR_CROSSDEVLINK;
3438 /* shouldn't happen that we have distinct vnodes for two
3439 * different files, but could due to deliberate attack, or
3440 * stale info. Avoid deadlocks and quit now.
3442 if (oldDscp->fid.vnode == newDscp->fid.vnode) {
3443 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p vnode collision",
3445 code = CM_ERROR_CROSSDEVLINK;
3449 if (oldDscp->fid.vnode < newDscp->fid.vnode) {
3450 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3451 lock_ObtainWrite(&oldDscp->rw);
3452 cm_dnlcRemove(oldDscp, cOldNamep);
3453 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3454 CM_SCACHESYNC_STOREDATA);
3455 lock_ReleaseWrite(&oldDscp->rw);
3457 cm_EndDirOp(&oldDirOp);
3459 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE, &newDirOp);
3460 lock_ObtainWrite(&newDscp->rw);
3461 cm_dnlcRemove(newDscp, cNewNamep);
3462 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3463 CM_SCACHESYNC_STOREDATA);
3464 lock_ReleaseWrite(&newDscp->rw);
3466 cm_EndDirOp(&newDirOp);
3468 /* cleanup first one */
3469 lock_ObtainWrite(&oldDscp->rw);
3470 cm_SyncOpDone(oldDscp, NULL,
3471 CM_SCACHESYNC_STOREDATA);
3472 lock_ReleaseWrite(&oldDscp->rw);
3473 cm_EndDirOp(&oldDirOp);
3478 /* lock the new vnode entry first */
3479 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE, &newDirOp);
3480 lock_ObtainWrite(&newDscp->rw);
3481 cm_dnlcRemove(newDscp, cNewNamep);
3482 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3483 CM_SCACHESYNC_STOREDATA);
3484 lock_ReleaseWrite(&newDscp->rw);
3486 cm_EndDirOp(&newDirOp);
3488 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3489 lock_ObtainWrite(&oldDscp->rw);
3490 cm_dnlcRemove(oldDscp, cOldNamep);
3491 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3492 CM_SCACHESYNC_STOREDATA);
3493 lock_ReleaseWrite(&oldDscp->rw);
3495 cm_EndDirOp(&oldDirOp);
3497 /* cleanup first one */
3498 lock_ObtainWrite(&newDscp->rw);
3499 cm_SyncOpDone(newDscp, NULL,
3500 CM_SCACHESYNC_STOREDATA);
3501 lock_ReleaseWrite(&newDscp->rw);
3502 cm_EndDirOp(&newDirOp);
3506 } /* two distinct vnodes */
3513 newNamep = cm_ClientStringToFsStringAlloc(cNewNamep, -1, NULL);
3515 /* try the RPC now */
3516 osi_Log2(afsd_logp, "CALL Rename old scp 0x%p new scp 0x%p",
3519 code = cm_ConnFromFID(&oldDscp->fid, userp, reqp, &connp);
3523 oldDirAFSFid.Volume = oldDscp->fid.volume;
3524 oldDirAFSFid.Vnode = oldDscp->fid.vnode;
3525 oldDirAFSFid.Unique = oldDscp->fid.unique;
3526 newDirAFSFid.Volume = newDscp->fid.volume;
3527 newDirAFSFid.Vnode = newDscp->fid.vnode;
3528 newDirAFSFid.Unique = newDscp->fid.unique;
3530 rxconnp = cm_GetRxConn(connp);
3531 code = RXAFS_Rename(rxconnp, &oldDirAFSFid, oldNamep,
3532 &newDirAFSFid, newNamep,
3533 &updatedOldDirStatus, &updatedNewDirStatus,
3535 rx_PutConnection(rxconnp);
3537 } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
3538 &volSync, NULL, NULL, code));
3539 code = cm_MapRPCError(code, reqp);
3542 osi_Log1(afsd_logp, "CALL Rename FAILURE, code 0x%x", code);
3544 osi_Log0(afsd_logp, "CALL Rename SUCCESS");
3546 /* update the individual stat cache entries for the directories */
3548 lock_ObtainWrite(&oldDirOp.scp->dirlock);
3549 oldDirOp.lockType = CM_DIRLOCK_WRITE;
3551 lock_ObtainWrite(&oldDscp->rw);
3552 cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
3555 cm_MergeStatus(NULL, oldDscp, &updatedOldDirStatus, &volSync,
3556 userp, CM_MERGEFLAG_DIROP);
3557 lock_ReleaseWrite(&oldDscp->rw);
3559 if (code == 0 && cm_CheckDirOpForSingleChange(&oldDirOp)) {
3561 diropCode = cm_BPlusDirLookup(&oldDirOp, cOldNamep, &fileFid);
3562 if (diropCode == CM_ERROR_INEXACT_MATCH)
3564 else if (diropCode == EINVAL)
3566 diropCode = cm_DirLookup(&oldDirOp, oldNamep, &fileFid);
3568 if (diropCode == 0) {
3570 diropCode = cm_DirCreateEntry(&oldDirOp, newNamep, &fileFid);
3572 cm_BPlusDirCreateEntry(&oldDirOp, cNewNamep, &fileFid);
3576 if (diropCode == 0) {
3577 diropCode = cm_DirDeleteEntry(&oldDirOp, oldNamep);
3579 cm_BPlusDirDeleteEntry(&oldDirOp, cOldNamep);
3584 cm_EndDirOp(&oldDirOp);
3586 /* and update it for the new one, too, if necessary */
3589 lock_ObtainWrite(&newDirOp.scp->dirlock);
3590 newDirOp.lockType = CM_DIRLOCK_WRITE;
3592 lock_ObtainWrite(&newDscp->rw);
3593 cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
3595 cm_MergeStatus(NULL, newDscp, &updatedNewDirStatus, &volSync,
3596 userp, CM_MERGEFLAG_DIROP);
3597 lock_ReleaseWrite(&newDscp->rw);
3601 * The following optimization does not work.
3602 * When the file server processed a RXAFS_Rename() request the
3603 * FID of the object being moved between directories is not
3604 * preserved. The client does not know the new FID nor the
3605 * version number of the target. Not only can we not create
3606 * the directory entry in the new directory, but we can't
3607 * preserve the cached data for the file. It must be re-read
3608 * from the file server. - jaltman, 2009/02/20
3611 /* we only make the local change if we successfully made
3612 the change in the old directory AND there was only one
3613 change in the new directory */
3614 if (diropCode == 0 && cm_CheckDirOpForSingleChange(&newDirOp)) {
3615 cm_DirCreateEntry(&newDirOp, newNamep, &fileFid);
3617 cm_BPlusDirCreateEntry(&newDirOp, cNewNamep, &fileFid);
3622 cm_EndDirOp(&newDirOp);
3626 * After the rename the file server has invalidated the callbacks
3627 * on the file that was moved nor do we have a directory reference
3630 lock_ObtainWrite(&oldScp->rw);
3631 cm_DiscardSCache(oldScp);
3632 lock_ReleaseWrite(&oldScp->rw);
3636 cm_ReleaseSCache(oldScp);
3643 /* and return error code */
3647 /* Byte range locks:
3649 The OpenAFS Windows client has to fake byte range locks given no
3650 server side support for such locks. This is implemented as keyed
3651 byte range locks on the cache manager.
3653 Keyed byte range locks:
3655 Each cm_scache_t structure keeps track of a list of keyed locks.
3656 The key for a lock identifies an owner of a set of locks (referred
3657 to as a client). Each key is represented by a value. The set of
3658 key values used within a specific cm_scache_t structure form a
3659 namespace that has a scope of just that cm_scache_t structure. The
3660 same key value can be used with another cm_scache_t structure and
3661 correspond to a completely different client. However it is
3662 advantageous for the SMB or IFS layer to make sure that there is a
3663 1-1 mapping between client and keys over all cm_scache_t objects.
3665 Assume a client C has key Key(C) (although, since the scope of the
3666 key is a cm_scache_t, the key can be Key(C,S), where S is the
3667 cm_scache_t. But assume a 1-1 relation between keys and clients).
3668 A byte range (O,+L) denotes byte addresses (O) through (O+L-1)
3669 inclusive (a.k.a. [O,O+L-1]). The function Key(x) is implemented
3670 through cm_generateKey() function for both SMB and IFS.
3672 The list of locks for a cm_scache_t object S is maintained in
3673 S->fileLocks. The cache manager will set a lock on the AFS file
3674 server in order to assert the locks in S->fileLocks. If only
3675 shared locks are in place for S, then the cache manager will obtain
3676 a LockRead lock, while if there are any exclusive locks, it will
3677 obtain a LockWrite lock. If the exclusive locks are all released
3678 while the shared locks remain, then the cache manager will
3679 downgrade the lock from LockWrite to LockRead. Similarly, if an
3680 exclusive lock is obtained when only shared locks exist, then the
3681 cache manager will try to upgrade the lock from LockRead to
3684 Each lock L owned by client C maintains a key L->key such that
3685 L->key == Key(C), the effective range defined by L->LOffset and
3686 L->LLength such that the range of bytes affected by the lock is
3687 (L->LOffset, +L->LLength), a type maintained in L->LockType which
3688 is either exclusive or shared.
3692 A lock exists iff it is in S->fileLocks for some cm_scache_t
3693 S. Existing locks are in one of the following states: ACTIVE,
3694 WAITLOCK, WAITUNLOCK, LOST, DELETED.
3696 The following sections describe each lock and the associated
3699 1. ACTIVE: A lock L is ACTIVE iff the cache manager has asserted
3700 the lock with the AFS file server. This type of lock can be
3701 exercised by a client to read or write to the locked region (as
3704 1.1 ACTIVE->LOST: When the AFS file server fails to extend a
3705 server lock that was required to assert the lock. Before
3706 marking the lock as lost, the cache manager checks if the file
3707 has changed on the server. If the file has not changed, then
3708 the cache manager will attempt to obtain a new server lock
3709 that is sufficient to assert the client side locks for the
3710 file. If any of these fail, the lock is marked as LOST.
3711 Otherwise, it is left as ACTIVE.
3713 1.2 ACTIVE->DELETED: Lock is released.
3715 2. WAITLOCK: A lock is in a WAITLOCK state if the cache manager
3716 grants the lock but the lock is yet to be asserted with the AFS
3717 file server. Once the file server grants the lock, the state
3718 will transition to an ACTIVE lock.
3720 2.1 WAITLOCK->ACTIVE: The server granted the lock.
3722 2.2 WAITLOCK->DELETED: Lock is abandoned, or timed out during
3725 2.3 WAITLOCK->LOST: One or more locks from this client were
3726 marked as LOST. No further locks will be granted to this
3727 client until all lost locks are removed.
3729 3. WAITUNLOCK: A lock is in a WAITUNLOCK state if the cache manager
3730 receives a request for a lock that conflicts with an existing
3731 ACTIVE or WAITLOCK lock. The lock will be placed in the queue
3732 and will be granted at such time the conflicting locks are
3733 removed, at which point the state will transition to either
3736 3.1 WAITUNLOCK->ACTIVE: The conflicting lock was removed. The
3737 current serverLock is sufficient to assert this lock, or a
3738 sufficient serverLock is obtained.
3740 3.2 WAITUNLOCK->WAITLOCK: The conflicting lock was removed,
3741 however the required serverLock is yet to be asserted with the
3744 3.3 WAITUNLOCK->DELETED: The lock is abandoned, timed out or
3747 3.5 WAITUNLOCK->LOST: One or more locks from this client were
3748 marked as LOST. No further locks will be granted to this
3749 client until all lost locks are removed.
3751 4. LOST: A lock L is LOST if the server lock that was required to
3752 assert the lock could not be obtained or if it could not be
3753 extended, or if other locks by the same client were LOST.
3754 Essentially, once a lock is LOST, the contract between the cache
3755 manager and that specific client is no longer valid.
3757 The cache manager rechecks the server lock once every minute and
3758 extends it as appropriate. If this is not done for 5 minutes,
3759 the AFS file server will release the lock (the 5 minute timeout
3760 is based on current file server code and is fairly arbitrary).
3761 Once released, the lock cannot be re-obtained without verifying
3762 that the contents of the file hasn't been modified since the
3763 time the lock was released. Re-obtaining the lock without
3764 verifying this may lead to data corruption. If the lock can not
3765 be obtained safely, then all active locks for the cm_scache_t
3768 4.1 LOST->DELETED: The lock is released.
3770 5. DELETED: The lock is no longer relevant. Eventually, it will
3771 get removed from the cm_scache_t. In the meantime, it will be
3772 treated as if it does not exist.
3774 5.1 DELETED->not exist: The lock is removed from the
3777 The following are classifications of locks based on their state.
3779 6* A lock L is ACCEPTED if it is ACTIVE or WAITLOCK. These locks
3780 have been accepted by the cache manager, but may or may not have
3781 been granted back to the client.
3783 7* A lock L is QUEUED if it is ACTIVE, WAITLOCK or WAITUNLOCK.
3785 8* A lock L is WAITING if it is WAITLOCK or WAITUNLOCK.
3789 A client C can READ range (Offset,+Length) of a file represented by
3790 cm_scache_t S iff (1):
3792 1. for all _a_ in (Offset,+Length), all of the following is true:
3794 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3795 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3798 1.2 For each LOST lock L in S->fileLocks such that _a_ in
3799 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3802 (When locks are lost on an cm_scache_t, all locks are lost. By
3803 4.2 (below), if there is an exclusive LOST lock, then there
3804 can't be any overlapping ACTIVE locks.)
3806 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
3808 2. for all _a_ in (Offset,+Length), one of the following is true:
3810 2.1 Byte _a_ of S is unowned (as specified in 1.1) AND there
3811 does not exist a LOST lock L such that _a_ in
3812 (L->LOffset,+L->LLength).
3814 2.2 Byte _a_ of S is owned by C under lock L (as specified in
3815 1.2) AND L->LockType is exclusive.
3817 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
3819 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
3822 3.1 If L->LockType is exclusive then there does NOT exist a
3823 ACCEPTED lock M in S->fileLocks such that _a_ in
3824 (M->LOffset,+M->LLength).
3826 (If we count all QUEUED locks then we hit cases such as
3827 cascading waiting locks where the locks later on in the queue
3828 can be granted without compromising file integrity. On the
3829 other hand if only ACCEPTED locks are considered, then locks
3830 that were received earlier may end up waiting for locks that
3831 were received later to be unlocked. The choice of ACCEPTED
3832 locks was made to mimic the Windows byte range lock
3835 3.2 If L->LockType is shared then for each ACCEPTED lock M in
3836 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
3837 M->LockType is shared.
3839 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
3841 4.1 M->key != Key(C)
3843 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
3844 and (M->LOffset,+M->LLength) do not intersect.
3846 (Note: If a client loses a lock, it loses all locks.
3847 Subsequently, it will not be allowed to obtain any more locks
3848 until all existing LOST locks that belong to the client are
3849 released. Once all locks are released by a single client,
3850 there exists no further contract between the client and AFS
3851 about the contents of the file, hence the client can then
3852 proceed to obtain new locks and establish a new contract.
3854 This doesn't quite work as you think it should, because most
3855 applications aren't built to deal with losing locks they
3856 thought they once had. For now, we don't have a good
3857 solution to lost locks.
3859 Also, for consistency reasons, we have to hold off on
3860 granting locks that overlap exclusive LOST locks.)
3862 A client C can only unlock locks L in S->fileLocks which have
3865 The representation and invariants are as follows:
3867 - Each cm_scache_t structure keeps:
3869 - A queue of byte-range locks (cm_scache_t::fileLocks) which
3870 are of type cm_file_lock_t.
3872 - A record of the highest server-side lock that has been
3873 obtained for this object (cm_scache_t::serverLock), which is
3874 one of (-1), LockRead, LockWrite.
3876 - A count of ACCEPTED exclusive and shared locks that are in the
3877 queue (cm_scache_t::sharedLocks and
3878 cm_scache_t::exclusiveLocks)
3880 - Each cm_file_lock_t structure keeps:
3882 - The type of lock (cm_file_lock_t::LockType)
3884 - The key associated with the lock (cm_file_lock_t::key)
3886 - The offset and length of the lock (cm_file_lock_t::LOffset
3887 and cm_file_lock_t::LLength)
3889 - The state of the lock.
3891 - Time of issuance or last successful extension
3893 Semantic invariants:
3895 I1. The number of ACCEPTED locks in S->fileLocks are
3896 (S->sharedLocks + S->exclusiveLocks)
3898 External invariants:
3900 I3. S->serverLock is the lock that we have asserted with the
3901 AFS file server for this cm_scache_t.
3903 I4. S->serverLock == LockRead iff there is at least one ACTIVE
3904 shared lock, but no ACTIVE exclusive locks.
3906 I5. S->serverLock == LockWrite iff there is at least one ACTIVE
3909 I6. If L is a LOST lock, then for each lock M in S->fileLocks,
3910 M->key == L->key IMPLIES M is LOST or DELETED.
3915 #define IS_LOCK_ACTIVE(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == 0)
3917 #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)
3919 #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)
3921 #define IS_LOCK_LOST(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_LOST)
3923 #define IS_LOCK_DELETED(lockp) (((lockp)->flags & CM_FILELOCK_FLAG_DELETED) == CM_FILELOCK_FLAG_DELETED)
3926 #define IS_LOCK_ACCEPTED(lockp) (IS_LOCK_ACTIVE(lockp) || IS_LOCK_WAITLOCK(lockp))
3929 #define IS_LOCK_CLIENTONLY(lockp) ((((lockp)->scp->flags & CM_SCACHEFLAG_RO) == CM_SCACHEFLAG_RO) || (((lockp)->flags & CM_FILELOCK_FLAG_CLIENTONLY) == CM_FILELOCK_FLAG_CLIENTONLY))
3932 #define INTERSECT_RANGE(r1,r2) (((r2).offset+(r2).length) > (r1).offset && ((r1).offset +(r1).length) > (r2).offset)
3935 #define CONTAINS_RANGE(r1,r2) (((r2).offset+(r2).length) <= ((r1).offset+(r1).length) && (r1).offset <= (r2).offset)
3937 #if defined(VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS) && !defined(LOCK_TESTING)
3938 #define SCP_SUPPORTS_BRLOCKS(scp) ((scp)->cbServerp && ((scp)->cbServerp->capabilities & VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS))
3940 #define SCP_SUPPORTS_BRLOCKS(scp) (1)
3943 #define SERVERLOCKS_ENABLED(scp) (!((scp)->flags & CM_SCACHEFLAG_RO) && cm_enableServerLocks && SCP_SUPPORTS_BRLOCKS(scp))
3945 #if defined(VICED_CAPABILITY_WRITELOCKACL)
3946 #define SCP_SUPPORTS_WRITELOCKACL(scp) ((scp)->cbServerp && ((scp->cbServerp->capabilities & VICED_CAPABILITY_WRITELOCKACL)))
3948 #define SCP_SUPPORTS_WRITELOCKACL(scp) (0)
3950 /* This should really be defined in any build that this code is being
3952 #error VICED_CAPABILITY_WRITELOCKACL not defined.
3955 static void cm_LockRangeSubtract(cm_range_t * pos, const cm_range_t * neg)
3957 afs_int64 int_begin;
3960 int_begin = MAX(pos->offset, neg->offset);
3961 int_end = MIN(pos->offset+pos->length, neg->offset+neg->length);
3963 if (int_begin < int_end) {
3964 if (int_begin == pos->offset) {
3965 pos->length = pos->offset + pos->length - int_end;
3966 pos->offset = int_end;
3967 } else if (int_end == pos->offset + pos->length) {
3968 pos->length = int_begin - pos->offset;
3971 /* We only subtract ranges if the resulting range is
3972 contiguous. If we try to support non-contigous ranges, we
3973 aren't actually improving performance. */
3977 /* Called with scp->rw held. Returns 0 if all is clear to read the
3978 specified range by the client identified by key.
3980 long cm_LockCheckRead(cm_scache_t *scp,
3981 LARGE_INTEGER LOffset,
3982 LARGE_INTEGER LLength,
3985 #ifndef ADVISORY_LOCKS
3987 cm_file_lock_t *fileLock;
3991 int substract_ranges = FALSE;
3993 range.offset = LOffset.QuadPart;
3994 range.length = LLength.QuadPart;
3998 1. for all _a_ in (Offset,+Length), all of the following is true:
4000 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
4001 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
4004 1.2 For each LOST lock L in S->fileLocks such that _a_ in
4005 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
4010 lock_ObtainRead(&cm_scacheLock);
4012 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
4014 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4016 if (INTERSECT_RANGE(range, fileLock->range)) {
4017 if (IS_LOCK_ACTIVE(fileLock)) {
4018 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4020 /* If there is an active lock for this client, it
4021 is safe to substract ranges.*/
4022 cm_LockRangeSubtract(&range, &fileLock->range);
4023 substract_ranges = TRUE;
4025 if (fileLock->lockType != LockRead) {
4026 code = CM_ERROR_LOCK_CONFLICT;
4030 /* even if the entire range is locked for reading,
4031 we still can't grant the lock at this point
4032 because the client may have lost locks. That
4033 is, unless we have already seen an active lock
4034 belonging to the client, in which case there
4035 can't be any lost locks for this client. */
4036 if (substract_ranges)
4037 cm_LockRangeSubtract(&range, &fileLock->range);
4039 } else if (IS_LOCK_LOST(fileLock) &&
4040 (cm_KeyEquals(&fileLock->key, &key, 0) || fileLock->lockType == LockWrite)) {
4041 code = CM_ERROR_BADFD;
4047 lock_ReleaseRead(&cm_scacheLock);
4049 osi_Log4(afsd_logp, "cm_LockCheckRead scp 0x%x offset %d length %d code 0x%x",
4050 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
4061 /* Called with scp->rw held. Returns 0 if all is clear to write the
4062 specified range by the client identified by key.
4064 long cm_LockCheckWrite(cm_scache_t *scp,
4065 LARGE_INTEGER LOffset,
4066 LARGE_INTEGER LLength,
4069 #ifndef ADVISORY_LOCKS
4071 cm_file_lock_t *fileLock;
4076 range.offset = LOffset.QuadPart;
4077 range.length = LLength.QuadPart;
4080 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
4082 2. for all _a_ in (Offset,+Length), one of the following is true:
4084 2.1 Byte _a_ of S is unowned AND there does not exist a LOST
4085 lock L such that _a_ in (L->LOffset,+L->LLength).
4087 2.2 Byte _a_ of S is owned by C under lock L AND L->LockType is
4091 lock_ObtainRead(&cm_scacheLock);
4093 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
4095 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4097 if (INTERSECT_RANGE(range, fileLock->range)) {
4098 if (IS_LOCK_ACTIVE(fileLock)) {
4099 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4100 if (fileLock->lockType == LockWrite) {
4102 /* if there is an active lock for this client, it
4103 is safe to substract ranges */
4104 cm_LockRangeSubtract(&range, &fileLock->range);
4106 code = CM_ERROR_LOCK_CONFLICT;
4110 code = CM_ERROR_LOCK_CONFLICT;
4113 } else if (IS_LOCK_LOST(fileLock)) {
4114 code = CM_ERROR_BADFD;
4120 lock_ReleaseRead(&cm_scacheLock);
4122 osi_Log4(afsd_logp, "cm_LockCheckWrite scp 0x%x offset %d length %d code 0x%x",
4123 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
4134 /* Called with cm_scacheLock write locked */
4135 static cm_file_lock_t * cm_GetFileLock(void) {
4138 l = (cm_file_lock_t *) cm_freeFileLocks;
4140 osi_QRemove(&cm_freeFileLocks, &l->q);
4142 l = malloc(sizeof(cm_file_lock_t));
4143 osi_assertx(l, "null cm_file_lock_t");
4146 memset(l, 0, sizeof(cm_file_lock_t));
4151 /* Called with cm_scacheLock write locked */
4152 static void cm_PutFileLock(cm_file_lock_t *l) {
4153 osi_QAdd(&cm_freeFileLocks, &l->q);
4156 /* called with scp->rw held. May release it during processing, but
4157 leaves it held on exit. */
4158 long cm_IntSetLock(cm_scache_t * scp, cm_user_t * userp, int lockType,
4164 struct rx_connection * rxconnp;
4166 afs_uint32 reqflags = reqp->flags;
4168 tfid.Volume = scp->fid.volume;
4169 tfid.Vnode = scp->fid.vnode;
4170 tfid.Unique = scp->fid.unique;
4173 osi_Log2(afsd_logp, "CALL SetLock scp 0x%p for lock %d", scp, lockType);
4175 reqp->flags |= CM_REQ_NORETRY;
4176 lock_ReleaseWrite(&scp->rw);
4179 code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4183 rxconnp = cm_GetRxConn(connp);
4184 code = RXAFS_SetLock(rxconnp, &tfid, lockType,
4186 rx_PutConnection(rxconnp);
4188 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
4191 code = cm_MapRPCError(code, reqp);
4193 osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
4195 osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
4198 lock_ObtainWrite(&scp->rw);
4199 reqp->flags = reqflags;
4203 /* called with scp->rw held. Releases it during processing */
4204 long cm_IntReleaseLock(cm_scache_t * scp, cm_user_t * userp,
4210 struct rx_connection * rxconnp;
4213 tfid.Volume = scp->fid.volume;
4214 tfid.Vnode = scp->fid.vnode;
4215 tfid.Unique = scp->fid.unique;
4218 lock_ReleaseWrite(&scp->rw);
4220 osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
4223 code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4227 rxconnp = cm_GetRxConn(connp);
4228 code = RXAFS_ReleaseLock(rxconnp, &tfid, &volSync);
4229 rx_PutConnection(rxconnp);
4231 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
4233 code = cm_MapRPCError(code, reqp);
4236 "CALL ReleaseLock FAILURE, code 0x%x", code);
4239 "CALL ReleaseLock SUCCESS");
4241 lock_ObtainWrite(&scp->rw);
4246 /* called with scp->rw held. May release it during processing, but
4247 will exit with lock held.
4251 - 0 if the user has permission to get the specified lock for the scp
4253 - CM_ERROR_NOACCESS if not
4255 Any other error from cm_SyncOp will be sent down untranslated.
4257 If CM_ERROR_NOACCESS is returned and lock_type is LockRead, then
4258 phas_insert (if non-NULL) will receive a boolean value indicating
4259 whether the user has INSERT permission or not.
4261 long cm_LockCheckPerms(cm_scache_t * scp,
4268 long code = 0, code2 = 0;
4270 /* lock permissions are slightly tricky because of the 'i' bit.
4271 If the user has PRSFS_LOCK, she can read-lock the file. If the
4272 user has PRSFS_WRITE, she can write-lock the file. However, if
4273 the user has PRSFS_INSERT, then she can write-lock new files,
4274 but not old ones. Since we don't have information about
4275 whether a file is new or not, we assume that if the user owns
4276 the scp, then she has the permissions that are granted by
4279 osi_Log3(afsd_logp, "cm_LockCheckPerms for scp[0x%p] type[%d] user[0x%p]",
4280 scp, lock_type, userp);
4282 if (lock_type == LockRead)
4283 rights |= PRSFS_LOCK;
4284 else if (lock_type == LockWrite)
4285 rights |= PRSFS_WRITE | PRSFS_LOCK;
4288 osi_assertx(FALSE, "invalid lock type");
4293 *phas_insert = FALSE;
4295 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
4296 CM_SCACHESYNC_GETSTATUS |
4297 CM_SCACHESYNC_NEEDCALLBACK);
4299 if (phas_insert && scp->creator == userp) {
4301 /* If this file was created by the user, then we check for
4302 PRSFS_INSERT. If the file server is recent enough, then
4303 this should be sufficient for her to get a write-lock (but
4304 not necessarily a read-lock). VICED_CAPABILITY_WRITELOCKACL
4305 indicates whether a file server supports getting write
4306 locks when the user only has PRSFS_INSERT.
4308 If the file was not created by the user we skip the check
4309 because the INSERT bit will not apply to this user even
4313 code2 = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
4314 CM_SCACHESYNC_GETSTATUS |
4315 CM_SCACHESYNC_NEEDCALLBACK);
4317 if (code2 == CM_ERROR_NOACCESS) {
4318 osi_Log0(afsd_logp, "cm_LockCheckPerms user has no INSERT bits");
4320 *phas_insert = TRUE;
4321 osi_Log0(afsd_logp, "cm_LockCheckPerms user has INSERT bits");
4325 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
4327 osi_Log1(afsd_logp, "cm_LockCheckPerms returning code %d", code);
4332 /* called with scp->rw held */
4333 long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
4334 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
4336 int allowWait, cm_user_t *userp, cm_req_t *reqp,
4337 cm_file_lock_t **lockpp)
4340 int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
4341 cm_file_lock_t *fileLock;
4344 int wait_unlock = FALSE;
4345 int force_client_lock = FALSE;
4347 osi_Log4(afsd_logp, "cm_Lock scp 0x%x type 0x%x offset %d length %d",
4348 scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
4349 osi_Log4(afsd_logp, "... allowWait %d key <0x%x, 0x%x, 0x%x>", allowWait,
4350 key.process_id, key.session_id, key.file_id);
4353 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
4355 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
4358 3.1 If L->LockType is exclusive then there does NOT exist a
4359 ACCEPTED lock M in S->fileLocks such that _a_ in
4360 (M->LOffset,+M->LLength).
4362 3.2 If L->LockType is shared then for each ACCEPTED lock M in
4363 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
4364 M->LockType is shared.
4366 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
4368 4.1 M->key != Key(C)
4370 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
4371 and (M->LOffset,+M->LLength) do not intersect.
4374 range.offset = LOffset.QuadPart;
4375 range.length = LLength.QuadPart;
4377 lock_ObtainRead(&cm_scacheLock);
4379 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4381 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4383 if (IS_LOCK_LOST(fileLock)) {
4384 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4385 code = CM_ERROR_BADFD;
4387 } else if (fileLock->lockType == LockWrite && INTERSECT_RANGE(range, fileLock->range)) {
4388 code = CM_ERROR_WOULDBLOCK;
4394 /* we don't need to check for deleted locks here since deleted
4395 locks are dequeued from scp->fileLocks */
4396 if (IS_LOCK_ACCEPTED(fileLock) &&
4397 INTERSECT_RANGE(range, fileLock->range)) {
4399 if ((sLockType & LOCKING_ANDX_SHARED_LOCK) == 0 ||
4400 fileLock->lockType != LockRead) {
4402 code = CM_ERROR_WOULDBLOCK;
4408 lock_ReleaseRead(&cm_scacheLock);
4410 if (code == 0 && SERVERLOCKS_ENABLED(scp)) {
4411 if (Which == scp->serverLock ||
4412 (Which == LockRead && scp->serverLock == LockWrite)) {
4416 /* we already have the lock we need */
4417 osi_Log3(afsd_logp, " we already have the correct lock. exclusives[%d], shared[%d], serverLock[%d]",
4418 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4420 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4422 /* special case: if we don't have permission to read-lock
4423 the file, then we force a clientside lock. This is to
4424 compensate for applications that obtain a read-lock for
4425 reading files off of directories that don't grant
4426 read-locks to the user. */
4427 if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4429 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4430 osi_Log0(afsd_logp, " User has no read-lock perms, but has INSERT perms.");
4433 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4434 force_client_lock = TRUE;
4438 } else if ((scp->exclusiveLocks > 0) ||
4439 (scp->sharedLocks > 0 && scp->serverLock != LockRead)) {
4442 /* We are already waiting for some other lock. We should
4443 wait for the daemon to catch up instead of generating a
4444 flood of SetLock calls. */
4445 osi_Log3(afsd_logp, " already waiting for other lock. exclusives[%d], shared[%d], serverLock[%d]",
4446 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4448 /* see if we have permission to create the lock in the
4450 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4452 code = CM_ERROR_WOULDBLOCK;
4453 else if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4455 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4457 " User has no read-lock perms, but has INSERT perms.");
4458 code = CM_ERROR_WOULDBLOCK;
4461 " User has no read-lock perms. Forcing client-side lock");
4462 force_client_lock = TRUE;
4466 /* leave any other codes as-is */
4470 int check_data_version = FALSE;
4473 /* first check if we have permission to elevate or obtain
4475 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4477 if (code == CM_ERROR_NOACCESS && Which == LockRead &&
4478 (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp))) {
4479 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4480 force_client_lock = TRUE;
4485 /* has_insert => (Which == LockRead, code == CM_ERROR_NOACCESS) */
4487 if (scp->serverLock == LockRead && Which == LockWrite) {
4489 /* We want to escalate the lock to a LockWrite.
4490 * Unfortunately that's not really possible without
4491 * letting go of the current lock. But for now we do
4495 " attempting to UPGRADE from LockRead to LockWrite.");
4497 " dataVersion on scp: %I64d", scp->dataVersion);
4499 /* we assume at this point (because scp->serverLock
4500 was valid) that we had a valid server lock. */
4501 scp->lockDataVersion = scp->dataVersion;
4502 check_data_version = TRUE;
4504 code = cm_IntReleaseLock(scp, userp, reqp);
4507 /* We couldn't release the lock */
4510 scp->serverLock = -1;
4514 /* We need to obtain a server lock of type Which in order
4515 * to assert this file lock */
4516 #ifndef AGGRESSIVE_LOCKS
4519 newLock = LockWrite;
4522 code = cm_IntSetLock(scp, userp, newLock, reqp);
4524 #ifdef AGGRESSIVE_LOCKS
4525 if ((code == CM_ERROR_WOULDBLOCK ||
4526 code == CM_ERROR_NOACCESS) && newLock != Which) {
4527 /* we wanted LockRead. We tried LockWrite. Now try
4532 osi_assertx(newLock == LockRead, "lock type not read");
4534 code = cm_IntSetLock(scp, userp, newLock, reqp);
4538 if (code == CM_ERROR_NOACCESS) {
4539 if (Which == LockRead) {
4540 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4542 /* We requested a read-lock, but we have permission to
4543 * get a write-lock. Try that */
4545 tcode = cm_LockCheckPerms(scp, LockWrite, userp, reqp, NULL);
4548 newLock = LockWrite;
4550 osi_Log0(afsd_logp, " User has 'i' perms and the request was for a LockRead. Trying to get a LockWrite instead");
4552 code = cm_IntSetLock(scp, userp, newLock, reqp);
4555 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4556 force_client_lock = TRUE;
4558 } else if (Which == LockWrite &&
4559 scp->creator == userp && !SCP_SUPPORTS_WRITELOCKACL(scp)) {
4562 /* Special case: if the lock request was for a
4563 * LockWrite and the user owns the file and we weren't
4564 * allowed to obtain the serverlock, we either lost a
4565 * race (the permissions changed from under us), or we
4566 * have 'i' bits, but we aren't allowed to lock the
4569 /* check if we lost a race... */
4570 tcode = cm_LockCheckPerms(scp, Which, userp, reqp, NULL);
4573 osi_Log0(afsd_logp, " User has 'i' perms but can't obtain write locks. Using client-side locks.");
4574 force_client_lock = TRUE;
4579 if (code == 0 && check_data_version &&
4580 scp->dataVersion != scp->lockDataVersion) {
4581 /* We lost a race. Although we successfully obtained
4582 * a lock, someone modified the file in between. The
4583 * locks have all been technically lost. */
4586 " Data version mismatch while upgrading lock.");
4588 " Data versions before=%I64d, after=%I64d",
4589 scp->lockDataVersion,
4592 " Releasing stale lock for scp 0x%x", scp);
4594 code = cm_IntReleaseLock(scp, userp, reqp);
4596 scp->serverLock = -1;
4598 code = CM_ERROR_INVAL;
4599 } else if (code == 0) {
4600 scp->serverLock = newLock;
4601 scp->lockDataVersion = scp->dataVersion;
4605 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4606 scp->serverLock == -1) {
4607 /* Oops. We lost the lock. */
4608 cm_LockMarkSCacheLost(scp);
4611 } else if (code == 0) { /* server locks not enabled */
4613 " Skipping server lock for scp");
4618 if (code != 0 && !force_client_lock) {
4619 /* Special case error translations
4621 Applications don't expect certain errors from a
4622 LockFile/UnlockFile call. We need to translate some error
4623 code to codes that apps expect and handle. */
4625 /* We shouldn't actually need to handle this case since we
4626 simulate locks for RO scps anyway. */
4627 if (code == CM_ERROR_READONLY) {
4628 osi_Log0(afsd_logp, " Reinterpreting CM_ERROR_READONLY as CM_ERROR_NOACCESS");
4629 code = CM_ERROR_NOACCESS;
4633 if (code == 0 || (code == CM_ERROR_WOULDBLOCK && allowWait) ||
4634 force_client_lock) {
4636 /* clear the error if we are forcing a client lock, so we
4637 don't get confused later. */
4638 if (force_client_lock && code != CM_ERROR_WOULDBLOCK)
4641 lock_ObtainWrite(&cm_scacheLock);
4642 fileLock = cm_GetFileLock();
4643 lock_ReleaseWrite(&cm_scacheLock);
4645 fileLock->fid = scp->fid;
4647 fileLock->key = key;
4648 fileLock->lockType = Which;
4650 fileLock->userp = userp;
4651 fileLock->range = range;
4652 fileLock->flags = (code == 0 ? 0 :
4654 CM_FILELOCK_FLAG_WAITUNLOCK :
4655 CM_FILELOCK_FLAG_WAITLOCK));
4657 if (force_client_lock || !SERVERLOCKS_ENABLED(scp))
4658 fileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
4660 fileLock->lastUpdate = (code == 0 && !force_client_lock) ? time(NULL) : 0;
4662 lock_ObtainWrite(&cm_scacheLock);
4663 osi_QAddT(&scp->fileLocksH, &scp->fileLocksT, &fileLock->fileq);
4664 cm_HoldSCacheNoLock(scp);
4665 fileLock->scp = scp;
4666 osi_QAdd(&cm_allFileLocks, &fileLock->q);
4667 lock_ReleaseWrite(&cm_scacheLock);
4673 if (IS_LOCK_CLIENTONLY(fileLock)) {
4675 } else if (IS_LOCK_ACCEPTED(fileLock)) {
4676 if (Which == LockRead)
4679 scp->exclusiveLocks++;
4683 "cm_Lock Lock added 0x%p flags 0x%x to scp [0x%p]",
4684 fileLock, fileLock->flags, scp);
4686 " exclusives[%d] shared[%d] client[%d] serverLock[%d]",
4687 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4688 (int)(signed char) scp->serverLock);
4691 "cm_Lock Rejecting lock (code = 0x%x)", code);
4697 /* Called with scp->rw held */
4698 long cm_UnlockByKey(cm_scache_t * scp,
4705 cm_file_lock_t *fileLock;
4706 osi_queue_t *q, *qn;
4709 osi_Log4(afsd_logp, "cm_UnlockByKey scp 0x%p key <0x%x,0x%x,0x%x",
4710 scp, key.process_id, key.session_id, key.file_id);
4711 osi_Log1(afsd_logp, " flags=0x%x", flags);
4713 lock_ObtainWrite(&cm_scacheLock);
4715 for (q = scp->fileLocksH; q; q = qn) {
4718 fileLock = (cm_file_lock_t *)
4719 ((char *) q - offsetof(cm_file_lock_t, fileq));
4722 osi_Log4(afsd_logp, " Checking lock[0x%x] range[%d,+%d] type[%d]",
4724 (unsigned long) fileLock->range.offset,
4725 (unsigned long) fileLock->range.length,
4726 fileLock->lockType);
4727 osi_Log4(afsd_logp, " key<0x%x, 0x%x, 0x%x> flags[0x%x]",
4728 fileLock->key.process_id, fileLock->key.session_id, fileLock->key.file_id,
4731 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
4732 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
4733 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4735 fileLock->fid.volume,
4736 fileLock->fid.vnode,
4737 fileLock->fid.unique);
4738 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4739 fileLock->scp->fid.cell,
4740 fileLock->scp->fid.volume,
4741 fileLock->scp->fid.vnode,
4742 fileLock->scp->fid.unique);
4743 osi_assertx(FALSE, "invalid fid value");
4747 if (!IS_LOCK_DELETED(fileLock) &&
4748 cm_KeyEquals(&fileLock->key, &key, flags)) {
4749 osi_Log3(afsd_logp, "...Unlock range [%d,+%d] type %d",
4750 fileLock->range.offset,
4751 fileLock->range.length,
4752 fileLock->lockType);
4754 if (scp->fileLocksT == q)
4755 scp->fileLocksT = osi_QPrev(q);
4756 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
4758 if (IS_LOCK_CLIENTONLY(fileLock)) {
4760 } else if (IS_LOCK_ACCEPTED(fileLock)) {
4761 if (fileLock->lockType == LockRead)
4764 scp->exclusiveLocks--;
4767 fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
4769 cm_ReleaseUser(fileLock->userp);
4770 cm_ReleaseSCacheNoLock(scp);
4772 fileLock->userp = NULL;
4773 fileLock->scp = NULL;
4779 lock_ReleaseWrite(&cm_scacheLock);
4781 if (n_unlocks == 0) {
4782 osi_Log0(afsd_logp, "cm_UnlockByKey no locks found");
4783 osi_Log3(afsd_logp, " Leaving scp with exclusives[%d], shared[%d], serverLock[%d]",
4784 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4789 osi_Log1(afsd_logp, "cm_UnlockByKey done with %d locks", n_unlocks);
4791 osi_assertx(scp->sharedLocks >= 0, "scp->sharedLocks < 0");
4792 osi_assertx(scp->exclusiveLocks >= 0, "scp->exclusiveLocks < 0");
4793 osi_assertx(scp->clientLocks >= 0, "scp->clientLocks < 0");
4795 if (!SERVERLOCKS_ENABLED(scp)) {
4796 osi_Log0(afsd_logp, " Skipping server lock for scp");
4800 /* Ideally we would go through the rest of the locks to determine
4801 * if one or more locks that were formerly in WAITUNLOCK can now
4802 * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
4803 * scp->sharedLocks accordingly. However, the retrying of locks
4804 * in that manner is done cm_RetryLock() manually.
4807 if (scp->serverLock == LockWrite &&
4808 scp->exclusiveLocks == 0 &&
4809 scp->sharedLocks > 0) {
4811 /* The serverLock should be downgraded to LockRead */
4812 osi_Log0(afsd_logp, " DOWNGRADE lock from LockWrite to LockRead");
4814 /* since scp->serverLock looked sane, we are going to assume
4815 that we have a valid server lock. */
4816 scp->lockDataVersion = scp->dataVersion;
4817 osi_Log1(afsd_logp, " dataVersion on scp = %I64d", scp->dataVersion);
4819 code = cm_IntReleaseLock(scp, userp, reqp);
4822 /* so we couldn't release it. Just let the lock be for now */
4826 scp->serverLock = -1;
4829 code = cm_IntSetLock(scp, userp, LockRead, reqp);
4831 if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
4832 scp->serverLock = LockRead;
4833 } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
4834 /* We lost a race condition. Although we have a valid
4835 lock on the file, the data has changed and essentially
4836 we have lost the lock we had during the transition. */
4838 osi_Log0(afsd_logp, "Data version mismatch during lock downgrade");
4839 osi_Log2(afsd_logp, " Data versions before=%I64d, after=%I64d",
4840 scp->lockDataVersion,
4843 code = cm_IntReleaseLock(scp, userp, reqp);
4845 code = CM_ERROR_INVAL;
4846 scp->serverLock = -1;
4850 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4851 (scp->serverLock == -1)) {
4853 cm_LockMarkSCacheLost(scp);
4856 /* failure here has no bearing on the return value of
4860 } else if (scp->serverLock != (-1) &&
4861 scp->exclusiveLocks == 0 &&
4862 scp->sharedLocks == 0) {
4863 /* The serverLock should be released entirely */
4865 code = cm_IntReleaseLock(scp, userp, reqp);
4868 scp->serverLock = (-1);
4873 osi_Log1(afsd_logp, "cm_UnlockByKey code 0x%x", code);
4874 osi_Log4(afsd_logp, " Leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
4875 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4876 (int)(signed char) scp->serverLock);
4881 long cm_Unlock(cm_scache_t *scp,
4882 unsigned char sLockType,
4883 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
4890 int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
4891 cm_file_lock_t *fileLock;
4893 int release_userp = FALSE;
4894 int exact_match = !(flags & CM_UNLOCK_FLAG_MATCH_RANGE);
4896 LARGE_INTEGER RangeEnd;
4898 osi_Log4(afsd_logp, "cm_Unlock scp 0x%p type 0x%x offset %d length %d",
4899 scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
4900 osi_Log4(afsd_logp, "... key <0x%x,0x%x,0x%x> flags 0x%x",
4901 key.process_id, key.session_id, key.file_id, flags);
4904 RangeEnd.QuadPart = LOffset.QuadPart + LLength.QuadPart;
4907 lock_ObtainRead(&cm_scacheLock);
4909 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4910 fileLock = (cm_file_lock_t *)
4911 ((char *) q - offsetof(cm_file_lock_t, fileq));
4914 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
4915 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
4916 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4918 fileLock->fid.volume,
4919 fileLock->fid.vnode,
4920 fileLock->fid.unique);
4921 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4922 fileLock->scp->fid.cell,
4923 fileLock->scp->fid.volume,
4924 fileLock->scp->fid.vnode,
4925 fileLock->scp->fid.unique);
4926 osi_assertx(FALSE, "invalid fid value");
4930 if (!IS_LOCK_DELETED(fileLock) &&
4931 cm_KeyEquals(&fileLock->key, &key, 0) &&
4932 fileLock->range.offset == LOffset.QuadPart &&
4933 fileLock->range.length == LLength.QuadPart) {
4939 if (!IS_LOCK_DELETED(fileLock) &&
4940 cm_KeyEquals(&fileLock->key, &key, 0) &&
4941 fileLock->range.offset >= LOffset.QuadPart &&
4942 fileLock->range.offset < RangeEnd.QuadPart &&
4943 (fileLock->range.offset + fileLock->range.length) <= RangeEnd.QuadPart) {
4951 lock_ReleaseRead(&cm_scacheLock);
4953 if (lock_found && !exact_match) {
4957 osi_Log0(afsd_logp, "cm_Unlock lock not found; failure");
4959 /* The lock didn't exist anyway. *shrug* */
4960 return CM_ERROR_RANGE_NOT_LOCKED;
4964 /* discard lock record */
4965 lock_ConvertRToW(&cm_scacheLock);
4966 if (scp->fileLocksT == q)
4967 scp->fileLocksT = osi_QPrev(q);
4968 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
4971 * Don't delete it here; let the daemon delete it, to simplify
4972 * the daemon's traversal of the list.
4975 if (IS_LOCK_CLIENTONLY(fileLock)) {
4977 } else if (IS_LOCK_ACCEPTED(fileLock)) {
4978 if (fileLock->lockType == LockRead)
4981 scp->exclusiveLocks--;
4984 fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
4985 if (userp != NULL) {
4986 cm_ReleaseUser(fileLock->userp);
4988 userp = fileLock->userp;
4989 release_userp = TRUE;
4991 fileLock->userp = NULL;
4992 cm_ReleaseSCacheNoLock(scp);
4993 fileLock->scp = NULL;
4994 lock_ReleaseWrite(&cm_scacheLock);
4996 if (!SERVERLOCKS_ENABLED(scp)) {
4997 osi_Log0(afsd_logp, " Skipping server locks for scp");
5001 /* Ideally we would go through the rest of the locks to determine
5002 * if one or more locks that were formerly in WAITUNLOCK can now
5003 * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
5004 * scp->sharedLocks accordingly. However, the retrying of locks
5005 * in that manner is done cm_RetryLock() manually.
5008 if (scp->serverLock == LockWrite &&
5009 scp->exclusiveLocks == 0 &&
5010 scp->sharedLocks > 0) {
5012 /* The serverLock should be downgraded to LockRead */
5013 osi_Log0(afsd_logp, " DOWNGRADE lock from LockWrite to LockRead");
5015 /* Since we already had a lock, we assume that there is a
5016 valid server lock. */
5017 scp->lockDataVersion = scp->dataVersion;
5018 osi_Log1(afsd_logp, " dataVersion on scp is %I64d", scp->dataVersion);
5020 /* before we downgrade, make sure that we have enough
5021 permissions to get the read lock. */
5022 code = cm_LockCheckPerms(scp, LockRead, userp, reqp, NULL);
5025 osi_Log0(afsd_logp, " SKIPPING downgrade because user doesn't have perms to get downgraded lock");
5031 code = cm_IntReleaseLock(scp, userp, reqp);
5034 /* so we couldn't release it. Just let the lock be for now */
5038 scp->serverLock = -1;
5041 code = cm_IntSetLock(scp, userp, LockRead, reqp);
5043 if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
5044 scp->serverLock = LockRead;
5045 } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
5046 /* Lost a race. We obtained a new lock, but that is
5047 meaningless since someone modified the file
5051 "Data version mismatch while downgrading lock");
5053 " Data versions before=%I64d, after=%I64d",
5054 scp->lockDataVersion,
5057 code = cm_IntReleaseLock(scp, userp, reqp);
5059 scp->serverLock = -1;
5060 code = CM_ERROR_INVAL;
5064 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
5065 (scp->serverLock == -1)) {
5067 cm_LockMarkSCacheLost(scp);
5070 /* failure here has no bearing on the return value of
5074 } else if (scp->serverLock != (-1) &&
5075 scp->exclusiveLocks == 0 &&
5076 scp->sharedLocks == 0) {
5077 /* The serverLock should be released entirely */
5079 code = cm_IntReleaseLock(scp, userp, reqp);
5082 scp->serverLock = (-1);
5086 if (release_userp) {
5087 cm_ReleaseUser(userp);
5088 release_userp = FALSE;
5092 goto try_again; /* might be more than one lock in the range */
5096 osi_Log1(afsd_logp, "cm_Unlock code 0x%x", code);
5097 osi_Log4(afsd_logp, " leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
5098 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
5099 (int)(signed char) scp->serverLock);
5104 /* called with scp->rw held */
5105 void cm_LockMarkSCacheLost(cm_scache_t * scp)
5107 cm_file_lock_t *fileLock;
5110 osi_Log1(afsd_logp, "cm_LockMarkSCacheLost scp 0x%x", scp);
5112 /* cm_scacheLock needed because we are modifying fileLock->flags */
5113 lock_ObtainWrite(&cm_scacheLock);
5115 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5117 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
5119 if (IS_LOCK_ACTIVE(fileLock) &&
5120 !IS_LOCK_CLIENTONLY(fileLock)) {
5121 if (fileLock->lockType == LockRead)
5124 scp->exclusiveLocks--;
5126 fileLock->flags |= CM_FILELOCK_FLAG_LOST;
5130 scp->serverLock = -1;
5131 scp->lockDataVersion = CM_SCACHE_VERSION_BAD;
5132 lock_ReleaseWrite(&cm_scacheLock);
5135 /* Called with no relevant locks held */
5136 void cm_CheckLocks()
5138 osi_queue_t *q, *nq;
5139 cm_file_lock_t *fileLock;
5145 struct rx_connection * rxconnp;
5150 lock_ObtainWrite(&cm_scacheLock);
5152 cm_lockRefreshCycle++;
5154 osi_Log1(afsd_logp, "cm_CheckLocks starting lock check cycle %d", cm_lockRefreshCycle);
5156 for (q = cm_allFileLocks; q; q = nq) {
5157 fileLock = (cm_file_lock_t *) q;
5161 if (IS_LOCK_DELETED(fileLock)) {
5163 osi_QRemove(&cm_allFileLocks, q);
5164 cm_PutFileLock(fileLock);
5166 } else if (IS_LOCK_ACTIVE(fileLock) && !IS_LOCK_CLIENTONLY(fileLock)) {
5168 /* Server locks must have been enabled for us to have
5169 received an active non-client-only lock. */
5170 osi_assertx(cm_enableServerLocks, "!cm_enableServerLocks");
5172 scp = fileLock->scp;
5173 osi_assertx(scp != NULL, "null cm_scache_t");
5175 cm_HoldSCacheNoLock(scp);
5178 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5179 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5180 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5182 fileLock->fid.volume,
5183 fileLock->fid.vnode,
5184 fileLock->fid.unique);
5185 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5186 fileLock->scp->fid.cell,
5187 fileLock->scp->fid.volume,
5188 fileLock->scp->fid.vnode,
5189 fileLock->scp->fid.unique);
5190 osi_assertx(FALSE, "invalid fid");
5193 /* Server locks are extended once per scp per refresh
5195 if (scp->lastRefreshCycle != cm_lockRefreshCycle) {
5197 int scp_done = FALSE;
5199 osi_Log1(afsd_logp, "cm_CheckLocks Updating scp 0x%x", scp);
5201 lock_ReleaseWrite(&cm_scacheLock);
5202 lock_ObtainWrite(&scp->rw);
5204 /* did the lock change while we weren't holding the lock? */
5205 if (!IS_LOCK_ACTIVE(fileLock))
5206 goto post_syncopdone;
5208 code = cm_SyncOp(scp, NULL, fileLock->userp, &req, 0,
5209 CM_SCACHESYNC_NEEDCALLBACK
5210 | CM_SCACHESYNC_GETSTATUS
5211 | CM_SCACHESYNC_LOCK);
5215 "cm_CheckLocks SyncOp failure code 0x%x", code);
5216 goto post_syncopdone;
5219 /* cm_SyncOp releases scp->rw during which the lock
5220 may get released. */
5221 if (!IS_LOCK_ACTIVE(fileLock))
5222 goto pre_syncopdone;
5224 if (scp->serverLock != -1 && !(scp->flags & CM_SCACHEFLAG_DELETED)) {
5228 tfid.Volume = scp->fid.volume;
5229 tfid.Vnode = scp->fid.vnode;
5230 tfid.Unique = scp->fid.unique;
5232 userp = fileLock->userp;
5234 osi_Log3(afsd_logp, "CALL ExtendLock lock 0x%p for scp=0x%p with lock %d",
5237 (int) scp->serverLock);
5239 lock_ReleaseWrite(&scp->rw);
5242 code = cm_ConnFromFID(&cfid, userp,
5247 rxconnp = cm_GetRxConn(connp);
5248 code = RXAFS_ExtendLock(rxconnp, &tfid,
5250 rx_PutConnection(rxconnp);
5252 osi_Log1(afsd_logp, " ExtendLock returns %d", code);
5254 } while (cm_Analyze(connp, userp, &req,
5255 &cfid, &volSync, NULL, NULL,
5258 code = cm_MapRPCError(code, &req);
5260 lock_ObtainWrite(&scp->rw);
5263 osi_Log1(afsd_logp, "CALL ExtendLock FAILURE, code 0x%x", code);
5265 osi_Log0(afsd_logp, "CALL ExtendLock SUCCESS");
5266 scp->lockDataVersion = scp->dataVersion;
5269 if ((code == EINVAL || code == CM_ERROR_INVAL) &&
5270 scp->lockDataVersion == scp->dataVersion) {
5274 (scp->exclusiveLocks > 0) ? LockWrite: LockRead;
5276 /* we might still have a chance to obtain a
5279 code = cm_IntSetLock(scp, userp, lockType, &req);
5282 code = CM_ERROR_INVAL;
5283 } else if (scp->lockDataVersion != scp->dataVersion) {
5285 /* now check if we still have the file at
5286 the right data version. */
5288 "Data version mismatch on scp 0x%p",
5291 " Data versions: before=%I64d, after=%I64d",
5292 scp->lockDataVersion,
5295 code = cm_IntReleaseLock(scp, userp, &req);
5297 code = CM_ERROR_INVAL;
5301 if (code == EINVAL || code == CM_ERROR_INVAL ||
5302 code == CM_ERROR_BADFD) {
5303 cm_LockMarkSCacheLost(scp);
5307 /* interestingly, we have found an active lock
5308 belonging to an scache that has no
5310 cm_LockMarkSCacheLost(scp);
5317 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5320 lock_ReleaseWrite(&scp->rw);
5322 lock_ObtainWrite(&cm_scacheLock);
5325 fileLock->lastUpdate = time(NULL);
5329 scp->lastRefreshCycle = cm_lockRefreshCycle;
5332 /* we have already refreshed the locks on this scp */
5333 fileLock->lastUpdate = time(NULL);
5336 cm_ReleaseSCacheNoLock(scp);
5338 } else if (IS_LOCK_ACTIVE(fileLock) && IS_LOCK_CLIENTONLY(fileLock)) {
5339 /* TODO: Check callbacks */
5343 lock_ReleaseWrite(&cm_scacheLock);
5344 osi_Log1(afsd_logp, "cm_CheckLocks completes lock check cycle %d", cm_lockRefreshCycle);
5347 /* NOT called with scp->rw held. */
5348 long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
5351 cm_scache_t *scp = NULL;
5352 cm_file_lock_t *fileLock;
5356 int force_client_lock = FALSE;
5357 int has_insert = FALSE;
5358 int check_data_version = FALSE;
5362 if (client_is_dead) {
5363 code = CM_ERROR_TIMEDOUT;
5367 lock_ObtainRead(&cm_scacheLock);
5369 osi_Log2(afsd_logp, "cm_RetryLock checking lock %p (scp=%p)", oldFileLock, oldFileLock->scp);
5370 osi_Log4(afsd_logp, " offset(%x:%x) length(%x:%x)",
5371 (unsigned)(oldFileLock->range.offset >> 32),
5372 (unsigned)(oldFileLock->range.offset & 0xffffffff),
5373 (unsigned)(oldFileLock->range.length >> 32),
5374 (unsigned)(oldFileLock->range.length & 0xffffffff));
5375 osi_Log4(afsd_logp, " key<0x%x,0x%x,0x%x> flags=%x",
5376 oldFileLock->key.process_id, oldFileLock->key.session_id, oldFileLock->key.file_id,
5377 (unsigned)(oldFileLock->flags));
5379 /* if the lock has already been granted, then we have nothing to do */
5380 if (IS_LOCK_ACTIVE(oldFileLock)) {
5381 lock_ReleaseRead(&cm_scacheLock);
5382 osi_Log0(afsd_logp, "cm_RetryLock lock already granted");
5386 /* we can't do anything with lost or deleted locks at the moment. */
5387 if (IS_LOCK_LOST(oldFileLock) || IS_LOCK_DELETED(oldFileLock)) {
5388 code = CM_ERROR_BADFD;
5389 osi_Log0(afsd_logp, "cm_RetryLock lock is lost or deleted");
5390 lock_ReleaseRead(&cm_scacheLock);
5394 scp = oldFileLock->scp;
5396 osi_assertx(scp != NULL, "null cm_scache_t");
5398 lock_ReleaseRead(&cm_scacheLock);
5399 lock_ObtainWrite(&scp->rw);
5401 code = cm_LockCheckPerms(scp, oldFileLock->lockType,
5405 if (code == CM_ERROR_NOACCESS && oldFileLock->lockType == LockRead) {
5406 if (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp)) {
5407 force_client_lock = TRUE;
5411 lock_ReleaseWrite(&scp->rw);
5415 lock_ObtainWrite(&cm_scacheLock);
5417 /* Check if we already have a sufficient server lock to allow this
5418 lock to go through. */
5419 if (IS_LOCK_WAITLOCK(oldFileLock) &&
5420 (!SERVERLOCKS_ENABLED(scp) ||
5421 scp->serverLock == oldFileLock->lockType ||
5422 scp->serverLock == LockWrite)) {
5424 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5426 if (SERVERLOCKS_ENABLED(scp)) {
5427 osi_Log1(afsd_logp, "cm_RetryLock Server lock (%d) is sufficient for lock. Granting",
5428 (int) scp->serverLock);
5430 osi_Log0(afsd_logp, "cm_RetryLock skipping server lock for scp");
5433 lock_ReleaseWrite(&cm_scacheLock);
5434 lock_ReleaseWrite(&scp->rw);
5439 if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5441 /* check if the conflicting locks have dissappeared already */
5442 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5444 fileLock = (cm_file_lock_t *)
5445 ((char *) q - offsetof(cm_file_lock_t, fileq));
5447 if (IS_LOCK_LOST(fileLock)) {
5448 if (cm_KeyEquals(&fileLock->key, &oldFileLock->key, 0)) {
5449 code = CM_ERROR_BADFD;
5450 oldFileLock->flags |= CM_FILELOCK_FLAG_LOST;
5451 osi_Log1(afsd_logp, " found lost lock %p for same key. Marking lock as lost",
5454 } else if (fileLock->lockType == LockWrite &&
5455 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5456 osi_Log1(afsd_logp, " found conflicting LOST lock %p", fileLock);
5457 code = CM_ERROR_WOULDBLOCK;
5462 if (IS_LOCK_ACCEPTED(fileLock) &&
5463 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5465 if (oldFileLock->lockType != LockRead ||
5466 fileLock->lockType != LockRead) {
5468 osi_Log1(afsd_logp, " found conflicting lock %p", fileLock);
5469 code = CM_ERROR_WOULDBLOCK;
5477 lock_ReleaseWrite(&cm_scacheLock);
5478 lock_ReleaseWrite(&scp->rw);
5483 /* when we get here, the lock is either a WAITUNLOCK or WAITLOCK.
5484 If it is WAITUNLOCK, then we didn't find any conflicting lock
5485 but we haven't verfied whether the serverLock is sufficient to
5486 assert it. If it is WAITLOCK, then the serverLock is
5487 insufficient to assert it. Eitherway, we are ready to accept
5488 the lock as either ACTIVE or WAITLOCK depending on the
5491 /* First, promote the WAITUNLOCK to a WAITLOCK */
5492 if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5493 if (oldFileLock->lockType == LockRead)
5496 scp->exclusiveLocks++;
5498 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITUNLOCK;
5499 oldFileLock->flags |= CM_FILELOCK_FLAG_WAITLOCK;
5502 osi_assertx(IS_LOCK_WAITLOCK(oldFileLock), "!IS_LOCK_WAITLOCK");
5504 if (force_client_lock ||
5505 !SERVERLOCKS_ENABLED(scp) ||
5506 scp->serverLock == oldFileLock->lockType ||
5507 (oldFileLock->lockType == LockRead &&
5508 scp->serverLock == LockWrite)) {
5510 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5512 if ((force_client_lock ||
5513 !SERVERLOCKS_ENABLED(scp)) &&
5514 !IS_LOCK_CLIENTONLY(oldFileLock)) {
5516 oldFileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
5518 if (oldFileLock->lockType == LockRead)
5521 scp->exclusiveLocks--;
5526 lock_ReleaseWrite(&cm_scacheLock);
5527 lock_ReleaseWrite(&scp->rw);
5534 code = cm_SyncOp(scp, NULL, oldFileLock->userp, &req, 0,
5535 CM_SCACHESYNC_NEEDCALLBACK
5536 | CM_SCACHESYNC_GETSTATUS
5537 | CM_SCACHESYNC_LOCK);
5539 osi_Log1(afsd_logp, "cm_RetryLock SyncOp failure code 0x%x", code);
5540 lock_ReleaseWrite(&cm_scacheLock);
5541 goto post_syncopdone;
5544 if (!IS_LOCK_WAITLOCK(oldFileLock))
5545 goto pre_syncopdone;
5547 userp = oldFileLock->userp;
5549 #ifndef AGGRESSIVE_LOCKS
5550 newLock = oldFileLock->lockType;
5552 newLock = LockWrite;
5556 /* if has_insert is non-zero, then:
5557 - the lock a LockRead
5558 - we don't have permission to get a LockRead
5559 - we do have permission to get a LockWrite
5560 - the server supports VICED_CAPABILITY_WRITELOCKACL
5563 newLock = LockWrite;
5566 lock_ReleaseWrite(&cm_scacheLock);
5568 /* when we get here, either we have a read-lock and want a
5569 write-lock or we don't have any locks and we want some
5572 if (scp->serverLock == LockRead) {
5574 osi_assertx(newLock == LockWrite, "!LockWrite");
5576 osi_Log0(afsd_logp, " Attempting to UPGRADE from LockRead to LockWrite");
5578 scp->lockDataVersion = scp->dataVersion;
5579 check_data_version = TRUE;
5581 code = cm_IntReleaseLock(scp, userp, &req);
5584 goto pre_syncopdone;
5586 scp->serverLock = -1;
5589 code = cm_IntSetLock(scp, userp, newLock, &req);
5592 if (scp->dataVersion != scp->lockDataVersion) {
5593 /* we lost a race. too bad */
5596 " Data version mismatch while upgrading lock.");
5598 " Data versions before=%I64d, after=%I64d",
5599 scp->lockDataVersion,
5602 " Releasing stale lock for scp 0x%x", scp);
5604 code = cm_IntReleaseLock(scp, userp, &req);
5606 scp->serverLock = -1;
5608 code = CM_ERROR_INVAL;
5610 cm_LockMarkSCacheLost(scp);
5612 scp->serverLock = newLock;
5617 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5623 if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
5624 lock_ObtainWrite(&cm_scacheLock);
5625 if (scp->fileLocksT == &oldFileLock->fileq)
5626 scp->fileLocksT = osi_QPrev(&oldFileLock->fileq);
5627 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, &oldFileLock->fileq);
5628 lock_ReleaseWrite(&cm_scacheLock);
5630 lock_ReleaseWrite(&scp->rw);
5633 lock_ObtainWrite(&cm_scacheLock);
5635 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5636 } else if (code != CM_ERROR_WOULDBLOCK) {
5637 oldFileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5638 cm_ReleaseUser(oldFileLock->userp);
5639 oldFileLock->userp = NULL;
5640 if (oldFileLock->scp) {
5641 cm_ReleaseSCacheNoLock(oldFileLock->scp);
5642 oldFileLock->scp = NULL;
5645 lock_ReleaseWrite(&cm_scacheLock);
5650 cm_key_t cm_GenerateKey(afs_uint16 session_id, afs_offs_t process_id, afs_uint16 file_id)
5654 key.process_id = process_id;
5655 key.session_id = session_id;
5656 key.file_id = file_id;
5661 int cm_KeyEquals(cm_key_t *k1, cm_key_t *k2, int flags)
5663 return (k1->session_id == k2->session_id) && (k1->file_id == k2->file_id) &&
5664 ((flags & CM_UNLOCK_BY_FID) || (k1->process_id == k2->process_id));
5667 void cm_ReleaseAllLocks(void)
5673 cm_file_lock_t *fileLock;
5676 for (i = 0; i < cm_data.scacheHashTableSize; i++)
5678 for ( scp = cm_data.scacheHashTablep[i]; scp; scp = scp->nextp ) {
5679 while (scp->fileLocksH != NULL) {
5680 lock_ObtainWrite(&scp->rw);
5681 lock_ObtainWrite(&cm_scacheLock);
5682 if (!scp->fileLocksH) {
5683 lock_ReleaseWrite(&cm_scacheLock);
5684 lock_ReleaseWrite(&scp->rw);
5687 fileLock = (cm_file_lock_t *)((char *) scp->fileLocksH - offsetof(cm_file_lock_t, fileq));
5688 userp = fileLock->userp;
5690 key = fileLock->key;
5691 cm_HoldSCacheNoLock(scp);
5692 lock_ReleaseWrite(&cm_scacheLock);
5693 cm_UnlockByKey(scp, key, 0, userp, &req);
5694 cm_ReleaseSCache(scp);
5695 cm_ReleaseUser(userp);
5696 lock_ReleaseWrite(&scp->rw);