2 * Copyright 2000, International Business Machines Corporation and others.
5 * This software has been released under the terms of the IBM Public
6 * License. For details, see the LICENSE file in the top-level source
7 * directory or online at http://www.openafs.org/dl/license10.html
10 #include <afs/param.h>
30 extern void afsi_log(char *pattern, ...);
33 int cm_enableServerLocks = 1;
35 int cm_followBackupPath = 0;
38 * Case-folding array. This was constructed by inspecting of SMBtrace output.
39 * I do not know anything more about it.
41 unsigned char cm_foldUpper[256] = {
42 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
43 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
44 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
45 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
46 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
47 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
48 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
49 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
50 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
51 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
52 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
53 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
54 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
55 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
56 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
57 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
58 0x80, 0x9a, 0x90, 0x41, 0x8e, 0x41, 0x8f, 0x80,
59 0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x8e, 0x8f,
60 0x90, 0x92, 0x92, 0x4f, 0x99, 0x4f, 0x55, 0x55,
61 0x59, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
62 0x41, 0x49, 0x4f, 0x55, 0xa5, 0xa5, 0x56, 0xa7,
63 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
64 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
65 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
66 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
67 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
68 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
69 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
70 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
71 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
72 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
73 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
77 * Case-insensitive string comparison. We used to use stricmp, but it doesn't
78 * know about 8-bit characters (e.g. 129 is lowercase u-umlaut, 154 is
79 * upper-case u-umlaut).
81 int cm_stricmp(const char *str1, const char *str2)
93 c1 = (char) cm_foldUpper[(unsigned char)(*str1++)];
94 c2 = (char) cm_foldUpper[(unsigned char)(*str2++)];
104 /* return success if we can open this file in this mode */
105 long cm_CheckOpen(cm_scache_t *scp, int openMode, int trunc, cm_user_t *userp,
113 rights |= PRSFS_READ;
114 if (openMode == 1 || openMode == 2 || trunc)
115 rights |= PRSFS_WRITE;
117 lock_ObtainWrite(&scp->rw);
119 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
120 CM_SCACHESYNC_GETSTATUS
121 | CM_SCACHESYNC_NEEDCALLBACK
122 | CM_SCACHESYNC_LOCK);
125 ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
126 scp->fileType == CM_SCACHETYPE_FILE) {
129 unsigned int sLockType;
130 LARGE_INTEGER LOffset, LLength;
132 /* Check if there's some sort of lock on the file at the
135 key = cm_GenerateKey(CM_SESSION_CMINT,0,0);
137 if (rights & PRSFS_WRITE)
140 sLockType = LOCKING_ANDX_SHARED_LOCK;
142 LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
143 LOffset.LowPart = CM_FLSHARE_OFFSET_LOW;
144 LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
145 LLength.LowPart = CM_FLSHARE_LENGTH_LOW;
147 code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
150 cm_Unlock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp);
152 /* In this case, we allow the file open to go through even
153 though we can't enforce mandatory locking on the
155 if (code == CM_ERROR_NOACCESS &&
156 !(rights & PRSFS_WRITE))
160 case CM_ERROR_ALLOFFLINE:
161 case CM_ERROR_ALLDOWN:
162 case CM_ERROR_ALLBUSY:
163 case CM_ERROR_TIMEDOUT:
165 case CM_ERROR_WOULDBLOCK:
168 code = CM_ERROR_SHARING_VIOLATION;
173 } else if (code != 0) {
177 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
181 lock_ReleaseWrite(&scp->rw);
186 /* return success if we can open this file in this mode */
187 long cm_CheckNTOpen(cm_scache_t *scp, unsigned int desiredAccess,
188 unsigned int createDisp, cm_user_t *userp, cm_req_t *reqp,
189 cm_lock_data_t **ldpp)
194 osi_assertx(ldpp != NULL, "null cm_lock_data_t");
197 /* Always allow delete; the RPC will tell us if it's OK */
198 if (desiredAccess == DELETE)
203 if (desiredAccess & (AFS_ACCESS_READ|AFS_ACCESS_EXECUTE))
204 rights |= (scp->fileType == CM_SCACHETYPE_DIRECTORY ? PRSFS_LOOKUP : PRSFS_READ);
206 /* We used to require PRSFS_WRITE if createDisp was 4
207 (OPEN_ALWAYS) even if AFS_ACCESS_WRITE was not requested.
208 However, we don't need to do that since the existence of the
209 scp implies that we don't need to create it. */
210 if (desiredAccess & AFS_ACCESS_WRITE)
211 rights |= PRSFS_WRITE;
213 lock_ObtainWrite(&scp->rw);
215 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
216 CM_SCACHESYNC_GETSTATUS
217 | CM_SCACHESYNC_NEEDCALLBACK
218 | CM_SCACHESYNC_LOCK);
221 * If the open will fail because the volume is readonly, then we will
222 * return an access denied error instead. This is to help brain-dead
223 * apps run correctly on replicated volumes.
224 * See defect 10007 for more information.
226 if (code == CM_ERROR_READONLY)
227 code = CM_ERROR_NOACCESS;
230 ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
231 scp->fileType == CM_SCACHETYPE_FILE) {
233 unsigned int sLockType;
234 LARGE_INTEGER LOffset, LLength;
236 /* Check if there's some sort of lock on the file at the
239 key = cm_GenerateKey(CM_SESSION_CMINT,0,0);
240 if (rights & PRSFS_WRITE)
243 sLockType = LOCKING_ANDX_SHARED_LOCK;
245 /* single byte lock at offset 0x0100 0000 0000 0000 */
246 LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
247 LOffset.LowPart = CM_FLSHARE_OFFSET_LOW;
248 LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
249 LLength.LowPart = CM_FLSHARE_LENGTH_LOW;
251 code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
254 (*ldpp) = (cm_lock_data_t *)malloc(sizeof(cm_lock_data_t));
261 (*ldpp)->sLockType = sLockType;
262 (*ldpp)->LOffset.HighPart = LOffset.HighPart;
263 (*ldpp)->LOffset.LowPart = LOffset.LowPart;
264 (*ldpp)->LLength.HighPart = LLength.HighPart;
265 (*ldpp)->LLength.LowPart = LLength.LowPart;
267 /* In this case, we allow the file open to go through even
268 though we can't enforce mandatory locking on the
270 if (code == CM_ERROR_NOACCESS &&
271 !(rights & PRSFS_WRITE))
275 case CM_ERROR_ALLOFFLINE:
276 case CM_ERROR_ALLDOWN:
277 case CM_ERROR_ALLBUSY:
278 case CM_ERROR_TIMEDOUT:
280 case CM_ERROR_WOULDBLOCK:
283 code = CM_ERROR_SHARING_VIOLATION;
287 } else if (code != 0) {
292 lock_ReleaseWrite(&scp->rw);
294 osi_Log3(afsd_logp,"cm_CheckNTOpen scp 0x%p ldp 0x%p code 0x%x", scp, *ldpp, code);
298 extern long cm_CheckNTOpenDone(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp,
299 cm_lock_data_t ** ldpp)
301 osi_Log2(afsd_logp,"cm_CheckNTOpenDone scp 0x%p ldp 0x%p", scp, *ldpp);
302 lock_ObtainWrite(&scp->rw);
304 cm_Unlock(scp, (*ldpp)->sLockType, (*ldpp)->LOffset, (*ldpp)->LLength,
305 (*ldpp)->key, 0, userp, reqp);
309 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
310 lock_ReleaseWrite(&scp->rw);
314 * When CAP_NT_SMBS has been negotiated, deletion (of files or directories) is
315 * done in three steps:
316 * (1) open for deletion (NT_CREATE_AND_X)
317 * (2) set for deletion on close (NT_TRANSACTION2, SET_FILE_INFO)
319 * We must not do the RPC until step 3. But if we are going to return an error
320 * code (e.g. directory not empty), we must return it by step 2, otherwise most
321 * clients will not notice it. So we do a preliminary check. For deleting
322 * files, this is almost free, since we have already done the RPC to get the
323 * parent directory's status bits. But for deleting directories, we must do an
324 * additional RPC to get the directory's data to check if it is empty. Sigh.
326 long cm_CheckNTDelete(cm_scache_t *dscp, cm_scache_t *scp, cm_user_t *userp,
332 cm_dirEntry_t *dep = 0;
333 unsigned short *hashTable;
335 int BeyondPage = 0, HaveDot = 0, HaveDotDot = 0;
338 /* First check permissions */
339 lock_ObtainWrite(&scp->rw);
340 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_DELETE,
341 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
343 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
344 lock_ReleaseWrite(&scp->rw);
348 /* If deleting directory, must be empty */
350 if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
353 thyper.HighPart = 0; thyper.LowPart = 0;
354 code = buf_Get(scp, &thyper, reqp, &bufferp);
358 lock_ObtainMutex(&bufferp->mx);
359 lock_ObtainWrite(&scp->rw);
362 code = cm_SyncOp(scp, bufferp, userp, reqp, 0,
363 CM_SCACHESYNC_NEEDCALLBACK
365 | CM_SCACHESYNC_BUFLOCKED);
369 if (cm_HaveBuffer(scp, bufferp, 1))
372 /* otherwise, load the buffer and try again */
373 lock_ReleaseMutex(&bufferp->mx);
374 code = cm_GetBuffer(scp, bufferp, NULL, userp, reqp);
375 lock_ReleaseWrite(&scp->rw);
376 lock_ObtainMutex(&bufferp->mx);
377 lock_ObtainWrite(&scp->rw);
378 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
383 lock_ReleaseWrite(&scp->rw);
386 /* We try to determine emptiness without looking beyond the first page,
387 * and without assuming "." and ".." are present and are on the first
388 * page (though these assumptions might, after all, be reasonable).
390 hashTable = (unsigned short *)(bufferp->datap + (32 * 5));
391 for (i=0; i<128; i++) {
392 idx = ntohs(hashTable[i]);
398 dep = (cm_dirEntry_t *)(bufferp->datap + (32 * idx));
399 if (strcmp(dep->name, ".") == 0)
401 else if (strcmp(dep->name, "..") == 0)
404 code = CM_ERROR_NOTEMPTY;
407 idx = ntohs(dep->next);
410 if (BeyondPage && HaveDot && HaveDotDot)
411 code = CM_ERROR_NOTEMPTY;
415 lock_ReleaseMutex(&bufferp->mx);
416 buf_Release(bufferp);
418 lock_ReleaseWrite(&scp->rw);
423 * Iterate through all entries in a directory.
424 * When the function funcp is called, the buffer is locked but the
425 * directory vnode is not.
427 * If the retscp parameter is not NULL, the parmp must be a
428 * cm_lookupSearch_t object.
430 long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
431 osi_hyper_t *startOffsetp, cm_user_t *userp, cm_req_t *reqp,
432 cm_scache_t **retscp)
436 cm_dirEntry_t *dep = 0;
439 osi_hyper_t dirLength;
440 osi_hyper_t bufferOffset;
441 osi_hyper_t curOffset;
445 cm_pageHeader_t *pageHeaderp;
447 long nextEntryCookie;
448 int numDirChunks; /* # of 32 byte dir chunks in this entry */
450 /* get the directory size */
451 lock_ObtainWrite(&scp->rw);
452 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_LOOKUP,
453 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
454 lock_ReleaseWrite(&scp->rw);
458 if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
459 return CM_ERROR_NOTDIR;
461 if (retscp) /* if this is a lookup call */
463 cm_lookupSearch_t* sp = parmp;
466 #ifdef AFS_FREELANCE_CLIENT
467 /* Freelance entries never end up in the DNLC because they
468 * do not have an associated cm_server_t
470 !(cm_freelanceEnabled &&
471 sp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
472 sp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
473 #else /* !AFS_FREELANCE_CLIENT */
478 int casefold = sp->caseFold;
479 sp->caseFold = 0; /* we have a strong preference for exact matches */
480 if ( *retscp = cm_dnlcLookup(scp, sp)) /* dnlc hit */
482 sp->caseFold = casefold;
485 sp->caseFold = casefold;
487 /* see if we can find it using the directory hash tables.
488 we can only do exact matches, since the hash is case
498 code = cm_BeginDirOp(scp, userp, reqp, CM_DIRLOCK_READ, &dirop);
502 code = cm_BPlusDirLookup(&dirop, sp->nsearchNamep, &sp->fid);
507 code = cm_DirLookup(&dirop, sp->searchNamep, &sp->fid);
515 sp->ExactFound = TRUE;
516 *retscp = NULL; /* force caller to call cm_GetSCache() */
521 if (sp->caseFold && code == CM_ERROR_INEXACT_MATCH) {
524 sp->ExactFound = FALSE;
525 *retscp = NULL; /* force caller to call cm_GetSCache() */
529 return CM_ERROR_BPLUS_NOMATCH;
537 * XXX We only get the length once. It might change when we drop the
540 dirLength = scp->length;
543 bufferOffset.LowPart = bufferOffset.HighPart = 0;
545 curOffset = *startOffsetp;
547 curOffset.HighPart = 0;
548 curOffset.LowPart = 0;
552 /* make sure that curOffset.LowPart doesn't point to the first
553 * 32 bytes in the 2nd through last dir page, and that it
554 * doesn't point at the first 13 32-byte chunks in the first
555 * dir page, since those are dir and page headers, and don't
556 * contain useful information.
558 temp = curOffset.LowPart & (2048-1);
559 if (curOffset.HighPart == 0 && curOffset.LowPart < 2048) {
560 /* we're in the first page */
561 if (temp < 13*32) temp = 13*32;
564 /* we're in a later dir page */
565 if (temp < 32) temp = 32;
568 /* make sure the low order 5 bits are zero */
571 /* now put temp bits back ito curOffset.LowPart */
572 curOffset.LowPart &= ~(2048-1);
573 curOffset.LowPart |= temp;
575 /* check if we've passed the dir's EOF */
576 if (LargeIntegerGreaterThanOrEqualTo(curOffset, dirLength))
579 /* see if we can use the bufferp we have now; compute in which
580 * page the current offset would be, and check whether that's
581 * the offset of the buffer we have. If not, get the buffer.
583 thyper.HighPart = curOffset.HighPart;
584 thyper.LowPart = curOffset.LowPart & ~(cm_data.buf_blockSize-1);
585 if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
588 lock_ReleaseMutex(&bufferp->mx);
589 buf_Release(bufferp);
593 code = buf_Get(scp, &thyper, reqp, &bufferp);
595 /* if buf_Get() fails we do not have a buffer object to lock */
600 lock_ObtainMutex(&bufferp->mx);
601 bufferOffset = thyper;
603 /* now get the data in the cache */
605 lock_ObtainWrite(&scp->rw);
606 code = cm_SyncOp(scp, bufferp, userp, reqp,
608 CM_SCACHESYNC_NEEDCALLBACK
610 | CM_SCACHESYNC_BUFLOCKED);
612 lock_ReleaseWrite(&scp->rw);
615 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
617 if (cm_HaveBuffer(scp, bufferp, 1)) {
618 lock_ReleaseWrite(&scp->rw);
622 /* otherwise, load the buffer and try again */
623 lock_ReleaseMutex(&bufferp->mx);
624 code = cm_GetBuffer(scp, bufferp, NULL, userp,
626 lock_ReleaseWrite(&scp->rw);
627 lock_ObtainMutex(&bufferp->mx);
632 lock_ReleaseMutex(&bufferp->mx);
633 buf_Release(bufferp);
637 } /* if (wrong buffer) ... */
639 /* now we have the buffer containing the entry we're interested
640 * in; copy it out if it represents a non-deleted entry.
642 entryInDir = curOffset.LowPart & (2048-1);
643 entryInBuffer = curOffset.LowPart & (cm_data.buf_blockSize - 1);
645 /* page header will help tell us which entries are free. Page
646 * header can change more often than once per buffer, since
647 * AFS 3 dir page size may be less than (but not more than) a
648 * buffer package buffer.
650 /* only look intra-buffer */
651 temp = curOffset.LowPart & (cm_data.buf_blockSize - 1);
652 temp &= ~(2048 - 1); /* turn off intra-page bits */
653 pageHeaderp = (cm_pageHeader_t *) (bufferp->datap + temp);
655 /* now determine which entry we're looking at in the page. If
656 * it is free (there's a free bitmap at the start of the dir),
657 * we should skip these 32 bytes.
659 slotInPage = (entryInDir & 0x7e0) >> 5;
660 if (!(pageHeaderp->freeBitmap[slotInPage>>3]
661 & (1 << (slotInPage & 0x7)))) {
662 /* this entry is free */
663 numDirChunks = 1; /* only skip this guy */
667 tp = bufferp->datap + entryInBuffer;
668 dep = (cm_dirEntry_t *) tp; /* now points to AFS3 dir entry */
670 /* while we're here, compute the next entry's location, too,
671 * since we'll need it when writing out the cookie into the
672 * dir listing stream.
674 numDirChunks = cm_NameEntries(dep->name, NULL);
676 /* compute the offset of the cookie representing the next entry */
677 nextEntryCookie = curOffset.LowPart
678 + (CM_DIR_CHUNKSIZE * numDirChunks);
680 if (dep->fid.vnode != 0) {
681 /* this is one of the entries to use: it is not deleted */
682 code = (*funcp)(scp, dep, parmp, &curOffset);
685 } /* if we're including this name */
688 /* and adjust curOffset to be where the new cookie is */
690 thyper.LowPart = CM_DIR_CHUNKSIZE * numDirChunks;
691 curOffset = LargeIntegerAdd(thyper, curOffset);
692 } /* while copying data for dir listing */
694 /* release the mutex */
696 lock_ReleaseMutex(&bufferp->mx);
697 buf_Release(bufferp);
702 int cm_NoneUpper(normchar_t *s)
706 if (c >= 'A' && c <= 'Z')
711 int cm_NoneLower(normchar_t *s)
715 if (c >= 'a' && c <= 'z')
720 long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
723 cm_lookupSearch_t *sp;
725 normchar_t matchName[MAX_PATH];
726 int looking_for_short_name = FALSE;
728 sp = (cm_lookupSearch_t *) rockp;
730 if (cm_FsStringToNormString(dep->name, -1, matchName, lengthof(matchName)) == 0) {
731 /* Can't normalize FS string. */
736 match = cm_NormStrCmpI(matchName, sp->nsearchNamep);
738 match = cm_NormStrCmp(matchName, sp->nsearchNamep);
742 && !cm_Is8Dot3(matchName)) {
744 cm_Gen8Dot3NameInt(dep->name, &dep->fid, matchName, NULL);
746 match = cm_NormStrCmpI(matchName, sp->nsearchNamep);
748 match = cm_NormStrCmp(matchName, sp->nsearchNamep);
749 looking_for_short_name = TRUE;
759 if (!sp->caseFold || looking_for_short_name) {
760 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
761 return CM_ERROR_STOPNOW;
765 * If we get here, we are doing a case-insensitive search, and we
766 * have found a match. Now we determine what kind of match it is:
767 * exact, lower-case, upper-case, or none of the above. This is done
768 * in order to choose among matches, if there are more than one.
771 /* Exact matches are the best. */
772 match = cm_NormStrCmp(matchName, sp->nsearchNamep);
775 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
776 return CM_ERROR_STOPNOW;
779 /* Lower-case matches are next. */
782 if (cm_NoneUpper(matchName)) {
787 /* Upper-case matches are next. */
790 if (cm_NoneLower(matchName)) {
795 /* General matches are last. */
801 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
805 /* read the contents of a mount point into the appropriate string.
806 * called with write locked scp, and returns with locked scp.
808 long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
811 cm_buf_t *bufp = NULL;
815 if (scp->mountPointStringp[0])
818 #ifdef AFS_FREELANCE_CLIENT
819 /* File servers do not have data for freelance entries */
820 if (cm_freelanceEnabled &&
821 scp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
822 scp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
824 code = cm_FreelanceFetchMountPointString(scp);
826 #endif /* AFS_FREELANCE_CLIENT */
828 /* otherwise, we have to read it in */
829 lock_ReleaseWrite(&scp->rw);
831 thyper.LowPart = thyper.HighPart = 0;
832 code = buf_Get(scp, &thyper, reqp, &bufp);
834 lock_ObtainWrite(&scp->rw);
839 code = cm_SyncOp(scp, bufp, userp, reqp, 0,
840 CM_SCACHESYNC_READ | CM_SCACHESYNC_NEEDCALLBACK);
844 cm_SyncOpDone(scp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
846 if (cm_HaveBuffer(scp, bufp, 0))
849 /* otherwise load buffer */
850 code = cm_GetBuffer(scp, bufp, NULL, userp, reqp);
854 /* locked, has callback, has valid data in buffer */
855 if ((tlen = scp->length.LowPart) > MOUNTPOINTLEN - 1)
856 return CM_ERROR_TOOBIG;
858 code = CM_ERROR_INVAL;
862 /* someone else did the work while we were out */
863 if (scp->mountPointStringp[0]) {
868 /* otherwise, copy out the link */
869 memcpy(scp->mountPointStringp, bufp->datap, tlen);
871 /* now make it null-terminated. Note that the original contents of a
872 * link that is a mount point is "#volname." where "." is there just to
873 * be turned into a null. That is, we can trash the last char of the
874 * link without damaging the vol name. This is a stupid convention,
875 * but that's the protocol.
877 scp->mountPointStringp[tlen-1] = 0;
888 /* called with a locked scp and chases the mount point, yielding outScpp.
889 * scp remains write locked, just for simplicity of describing the interface.
891 long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
892 cm_req_t *reqp, cm_scache_t **outScpp)
894 fschar_t *cellNamep = NULL;
895 fschar_t *volNamep = NULL;
900 cm_volume_t *volp = NULL;
909 if (scp->mountRootFid.cell != 0 && scp->mountRootGen >= cm_data.mountRootGen) {
910 tfid = scp->mountRootFid;
911 lock_ReleaseWrite(&scp->rw);
912 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
913 lock_ObtainWrite(&scp->rw);
917 /* parse the volume name */
918 mpNamep = scp->mountPointStringp;
920 return CM_ERROR_NOSUCHPATH;
921 tlen = cm_FsStrLen(scp->mountPointStringp);
922 mtType = *scp->mountPointStringp;
924 cp = cm_FsStrChr(mpNamep, _FS(':'));
926 /* cellular mount point */
927 cellNamep = (fschar_t *)malloc((cp - mpNamep) * sizeof(fschar_t));
928 cm_FsStrCpyN(cellNamep, cp - mpNamep, mpNamep + 1, cp - mpNamep - 1);
929 volNamep = cm_FsStrDup(cp+1);
931 /* now look up the cell */
932 lock_ReleaseWrite(&scp->rw);
933 cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE);
934 lock_ObtainWrite(&scp->rw);
937 volNamep = cm_FsStrDup(mpNamep + 1);
939 #ifdef AFS_FREELANCE_CLIENT
941 * Mount points in the Freelance cell should default
942 * to the workstation cell.
944 if (cm_freelanceEnabled &&
945 scp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
946 scp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
948 fschar_t rootCellName[256]="";
949 cm_GetRootCellName(rootCellName);
950 cellp = cm_GetCell(rootCellName, 0);
952 #endif /* AFS_FREELANCE_CLIENT */
953 cellp = cm_FindCellByID(scp->fid.cell, 0);
957 code = CM_ERROR_NOSUCHCELL;
961 vnLength = cm_FsStrLen(volNamep);
962 if (vnLength >= 8 && cm_FsStrCmp(volNamep + vnLength - 7, ".backup") == 0)
963 targetType = BACKVOL;
964 else if (vnLength >= 10
965 && cm_FsStrCmp(volNamep + vnLength - 9, ".readonly") == 0)
970 /* check for backups within backups */
971 if (targetType == BACKVOL
972 && (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
973 == CM_SCACHEFLAG_RO) {
974 code = CM_ERROR_NOSUCHVOLUME;
978 /* now we need to get the volume */
979 lock_ReleaseWrite(&scp->rw);
980 if (cm_VolNameIsID(volNamep)) {
981 code = cm_FindVolumeByID(cellp, atoi(volNamep), userp, reqp,
982 CM_GETVOL_FLAG_CREATE, &volp);
984 code = cm_FindVolumeByName(cellp, volNamep, userp, reqp,
985 CM_GETVOL_FLAG_CREATE, &volp);
987 lock_ObtainWrite(&scp->rw);
990 afs_uint32 cell, volume;
991 cm_vol_state_t *statep;
993 cell = cellp->cellID;
995 /* if the mt pt originates in a .backup volume (not a .readonly)
996 * and FollowBackupPath is active, and if there is a .backup
997 * volume for the target, then use the .backup of the target
998 * instead of the read-write.
1000 if (cm_followBackupPath &&
1001 volp->vol[BACKVOL].ID != 0 &&
1002 (dscp->flags & (CM_SCACHEFLAG_RO|CM_SCACHEFLAG_PURERO)) == CM_SCACHEFLAG_RO &&
1003 (targetType == RWVOL || targetType == ROVOL && volp->vol[ROVOL].ID == 0)
1005 targetType = BACKVOL;
1007 /* if the mt pt is in a read-only volume (not just a
1008 * backup), and if there is a read-only volume for the
1009 * target, and if this is a targetType '#' mount point, use
1010 * the read-only, otherwise use the one specified.
1012 else if (mtType == '#' && targetType == RWVOL &&
1013 (scp->flags & CM_SCACHEFLAG_PURERO) &&
1014 volp->vol[ROVOL].ID != 0) {
1018 lock_ObtainWrite(&volp->rw);
1019 statep = cm_VolumeStateByType(volp, targetType);
1020 volume = statep->ID;
1021 statep->dotdotFid = dscp->fid;
1022 lock_ReleaseWrite(&volp->rw);
1024 /* the rest of the fid is a magic number */
1025 cm_SetFid(&scp->mountRootFid, cell, volume, 1, 1);
1026 scp->mountRootGen = cm_data.mountRootGen;
1028 tfid = scp->mountRootFid;
1029 lock_ReleaseWrite(&scp->rw);
1030 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
1031 lock_ObtainWrite(&scp->rw);
1044 long cm_LookupInternal(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_user_t *userp,
1045 cm_req_t *reqp, cm_scache_t **outScpp)
1048 int dnlcHit = 1; /* did we hit in the dnlc? yes, we did */
1049 cm_scache_t *tscp = NULL;
1050 cm_scache_t *mountedScp;
1051 cm_lookupSearch_t rock;
1053 normchar_t *nnamep = NULL;
1054 fschar_t *fnamep = NULL;
1058 memset(&rock, 0, sizeof(rock));
1060 if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
1061 && cm_ClientStrCmp(cnamep, _C("..")) == 0) {
1062 if (dscp->dotdotFid.volume == 0)
1063 return CM_ERROR_NOSUCHVOLUME;
1064 rock.fid = dscp->dotdotFid;
1066 } else if (cm_ClientStrCmp(cnamep, _C(".")) == 0) {
1067 rock.fid = dscp->fid;
1071 nnamep = cm_ClientStringToNormStringAlloc(cnamep, -1, NULL);
1073 code = CM_ERROR_NOSUCHFILE;
1076 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
1078 code = CM_ERROR_NOSUCHFILE;
1082 if (flags & CM_FLAG_NOMOUNTCHASE) {
1083 /* In this case, we should go and call cm_Dir* functions
1084 directly since the following cm_ApplyDir() function will
1092 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ, &dirop);
1095 code = cm_BPlusDirLookup(&dirop, nnamep, &rock.fid);
1100 code = cm_DirLookup(&dirop, fnamep, &rock.fid);
1102 cm_EndDirOp(&dirop);
1112 if (code == CM_ERROR_INEXACT_MATCH && (flags & CM_FLAG_CASEFOLD)) {
1119 return CM_ERROR_BPLUS_NOMATCH;
1124 rock.fid.cell = dscp->fid.cell;
1125 rock.fid.volume = dscp->fid.volume;
1126 rock.searchNamep = fnamep;
1127 rock.nsearchNamep = nnamep;
1128 rock.caseFold = (flags & CM_FLAG_CASEFOLD);
1129 rock.hasTilde = ((cm_ClientStrChr(cnamep, '~') != NULL) ? 1 : 0);
1131 /* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
1132 code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
1133 (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
1135 /* code == 0 means we fell off the end of the dir, while stopnow means
1136 * that we stopped early, probably because we found the entry we're
1137 * looking for. Any other non-zero code is an error.
1139 if (code && code != CM_ERROR_STOPNOW) {
1140 /* if the cm_scache_t we are searching in is not a directory
1141 * we must return path not found because the error
1142 * is to describe the final component not an intermediary
1144 if (code == CM_ERROR_NOTDIR) {
1145 if (flags & CM_FLAG_CHECKPATH)
1146 code = CM_ERROR_NOSUCHPATH;
1148 code = CM_ERROR_NOSUCHFILE;
1153 getroot = (dscp==cm_data.rootSCachep) ;
1155 if (!cm_freelanceEnabled || !getroot) {
1156 if (flags & CM_FLAG_CHECKPATH)
1157 code = CM_ERROR_NOSUCHPATH;
1159 code = CM_ERROR_NOSUCHFILE;
1162 else if (!cm_ClientStrChr(cnamep, '#') &&
1163 !cm_ClientStrChr(cnamep, '%') &&
1164 cm_ClientStrCmpI(cnamep, _C("srvsvc")) &&
1165 cm_ClientStrCmpI(cnamep, _C("wkssvc")) &&
1166 cm_ClientStrCmpI(cnamep, _C("ipc$")))
1168 /* nonexistent dir on freelance root, so add it */
1169 fschar_t fullname[CELL_MAXNAMELEN + 1] = "."; /* +1 so that when we skip the . the size is still CELL_MAXNAMELEN */
1172 osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %S",
1173 osi_LogSaveClientString(afsd_logp,cnamep));
1176 * There is an ugly behavior where a share name "foo" will be searched
1177 * for as "fo". If the searched for name differs by an already existing
1178 * symlink or mount point in the Freelance directory, do not add the
1179 * new value automatically.
1183 if (cnamep[0] == '.') {
1184 if (cm_GetCell_Gen(&fnamep[1], &fullname[1], CM_FLAG_CREATE)) {
1186 if (!cm_FreelanceMountPointExists(fullname, 0))
1187 code = cm_FreelanceAddMount(fullname, &fullname[1], "root.cell.",
1189 if ( cm_FsStrCmpI(&fnamep[1], &fullname[1]) &&
1190 !cm_FreelanceMountPointExists(fnamep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0) &&
1191 !cm_FreelanceSymlinkExists(fnamep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0))
1192 code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
1195 if (cm_GetCell_Gen(fnamep, fullname, CM_FLAG_CREATE)) {
1197 if (!cm_FreelanceMountPointExists(fullname, 0))
1198 code = cm_FreelanceAddMount(fullname, fullname, "root.cell.", 0, &rock.fid);
1199 if ( cm_FsStrCmpI(fnamep, fullname) &&
1200 !cm_FreelanceMountPointExists(fnamep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0) &&
1201 !cm_FreelanceSymlinkExists(fnamep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0))
1202 code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
1205 if (!found || code) { /* add mount point failed, so give up */
1206 if (flags & CM_FLAG_CHECKPATH)
1207 code = CM_ERROR_NOSUCHPATH;
1209 code = CM_ERROR_NOSUCHFILE;
1212 tscp = NULL; /* to force call of cm_GetSCache */
1214 if (flags & CM_FLAG_CHECKPATH)
1215 code = CM_ERROR_NOSUCHPATH;
1217 code = CM_ERROR_NOSUCHFILE;
1223 if ( !tscp ) /* we did not find it in the dnlc */
1226 code = cm_GetSCache(&rock.fid, &tscp, userp, reqp);
1230 /* tscp is now held */
1232 lock_ObtainWrite(&tscp->rw);
1233 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1234 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1236 lock_ReleaseWrite(&tscp->rw);
1237 cm_ReleaseSCache(tscp);
1240 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1241 /* tscp is now locked */
1243 if (!(flags & CM_FLAG_NOMOUNTCHASE)
1244 && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
1245 /* mount points are funny: they have a volume name to mount
1248 code = cm_ReadMountPoint(tscp, userp, reqp);
1250 code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
1252 lock_ReleaseWrite(&tscp->rw);
1253 cm_ReleaseSCache(tscp);
1260 lock_ReleaseWrite(&tscp->rw);
1263 /* copy back pointer */
1266 /* insert scache in dnlc */
1267 if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
1268 /* lock the directory entry to prevent racing callback revokes */
1269 lock_ObtainRead(&dscp->rw);
1270 if ( dscp->cbServerp != NULL && dscp->cbExpires > 0 ) {
1271 /* TODO: reuse nnamep from above */
1274 nnamep = cm_ClientStringToNormStringAlloc(cnamep, -1, NULL);
1276 cm_dnlcEnter(dscp, nnamep, tscp);
1278 lock_ReleaseRead(&dscp->rw);
1295 int cm_ExpandSysName(clientchar_t *inp, clientchar_t *outp, long outSizeCch, unsigned int index)
1300 tp = cm_ClientStrRChr(inp, '@');
1302 return 0; /* no @sys */
1304 if (cm_ClientStrCmp(tp, _C("@sys")) != 0)
1305 return 0; /* no @sys */
1307 /* caller just wants to know if this is a valid @sys type of name */
1311 if (index >= cm_sysNameCount)
1314 /* otherwise generate the properly expanded @sys name */
1315 prefixCount = (int)(tp - inp);
1317 cm_ClientStrCpyN(outp, outSizeCch, inp, prefixCount); /* copy out "a." from "a.@sys" */
1318 outp[prefixCount] = 0; /* null terminate the "a." */
1319 cm_ClientStrCat(outp, outSizeCch, cm_sysNameList[index]);/* append i386_nt40 */
1323 long cm_EvaluateVolumeReference(clientchar_t * namep, long flags, cm_user_t * userp,
1324 cm_req_t *reqp, cm_scache_t ** outScpp)
1326 afs_uint32 code = 0;
1327 fschar_t cellName[CELL_MAXNAMELEN];
1328 fschar_t volumeName[VL_MAXNAMELEN];
1332 fschar_t * fnamep = NULL;
1334 cm_cell_t * cellp = NULL;
1335 cm_volume_t * volp = NULL;
1339 int mountType = RWVOL;
1341 osi_Log1(afsd_logp, "cm_EvaluateVolumeReference for string [%S]",
1342 osi_LogSaveClientString(afsd_logp, namep));
1344 if (cm_ClientStrCmpNI(namep, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH) != 0) {
1345 goto _exit_invalid_path;
1348 /* namep is assumed to look like the following:
1350 @vol:<cellname>%<volume>\0
1352 @vol:<cellname>#<volume>\0
1356 fnamep = cm_ClientStringToFsStringAlloc(namep, -1, NULL);
1357 cp = fnamep + CM_PREFIX_VOL_CCH; /* cp points to cell name, hopefully */
1358 tp = cm_FsStrChr(cp, '%');
1360 tp = cm_FsStrChr(cp, '#');
1362 (len = tp - cp) == 0 ||
1363 len > CELL_MAXNAMELEN)
1364 goto _exit_invalid_path;
1365 cm_FsStrCpyN(cellName, lengthof(cellName), cp, len);
1370 cp = tp+1; /* cp now points to volume, supposedly */
1371 cm_FsStrCpy(volumeName, lengthof(volumeName), cp);
1373 /* OK, now we have the cell and the volume */
1374 osi_Log2(afsd_logp, " Found cell [%s] and volume [%s]",
1375 osi_LogSaveFsString(afsd_logp, cellName),
1376 osi_LogSaveFsString(afsd_logp, volumeName));
1378 cellp = cm_GetCell(cellName, CM_FLAG_CREATE);
1379 if (cellp == NULL) {
1380 goto _exit_invalid_path;
1383 len = cm_FsStrLen(volumeName);
1384 if (len >= 8 && cm_FsStrCmp(volumeName + len - 7, ".backup") == 0)
1386 else if (len >= 10 &&
1387 cm_FsStrCmp(volumeName + len - 9, ".readonly") == 0)
1392 if (cm_VolNameIsID(volumeName)) {
1393 code = cm_FindVolumeByID(cellp, atoi(volumeName), userp, reqp,
1394 CM_GETVOL_FLAG_CREATE, &volp);
1396 code = cm_FindVolumeByName(cellp, volumeName, userp, reqp,
1397 CM_GETVOL_FLAG_CREATE, &volp);
1403 if (volType == BACKVOL)
1404 volume = volp->vol[BACKVOL].ID;
1405 else if (volType == ROVOL ||
1406 (volType == RWVOL && mountType == ROVOL && volp->vol[ROVOL].ID != 0))
1407 volume = volp->vol[ROVOL].ID;
1409 volume = volp->vol[RWVOL].ID;
1411 cm_SetFid(&fid, cellp->cellID, volume, 1, 1);
1413 code = cm_GetSCache(&fid, outScpp, userp, reqp);
1426 if (flags & CM_FLAG_CHECKPATH)
1427 return CM_ERROR_NOSUCHPATH;
1429 return CM_ERROR_NOSUCHFILE;
1432 #ifdef DEBUG_REFCOUNT
1433 long cm_LookupDbg(cm_scache_t *dscp, clientchar_t *namep, long flags, cm_user_t *userp,
1434 cm_req_t *reqp, cm_scache_t **outScpp, char * file, long line)
1436 long cm_Lookup(cm_scache_t *dscp, clientchar_t *namep, long flags, cm_user_t *userp,
1437 cm_req_t *reqp, cm_scache_t **outScpp)
1441 clientchar_t tname[AFSPATHMAX];
1442 int sysNameIndex = 0;
1443 cm_scache_t *scp = NULL;
1445 #ifdef DEBUG_REFCOUNT
1446 afsi_log("%s:%d cm_Lookup dscp 0x%p ref %d", file, line, dscp, dscp->refCount, file, line);
1447 osi_Log2(afsd_logp, "cm_Lookup dscp 0x%p ref %d", dscp, dscp->refCount);
1450 if ( cm_ClientStrCmpI(namep,_C(SMB_IOCTL_FILENAME_NOSLASH)) == 0 ) {
1451 if (flags & CM_FLAG_CHECKPATH)
1452 return CM_ERROR_NOSUCHPATH;
1454 return CM_ERROR_NOSUCHFILE;
1457 if (dscp == cm_data.rootSCachep &&
1458 cm_ClientStrCmpNI(namep, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH) == 0) {
1459 return cm_EvaluateVolumeReference(namep, flags, userp, reqp, outScpp);
1462 if (cm_ExpandSysName(namep, NULL, 0, 0) > 0) {
1463 for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
1464 code = cm_ExpandSysName(namep, tname, lengthof(tname), sysNameIndex);
1466 code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
1467 #ifdef DEBUG_REFCOUNT
1468 afsi_log("%s:%d cm_LookupInternal (1) code 0x%x dscp 0x%p ref %d scp 0x%p ref %d", file, line, code, dscp, dscp->refCount, scp, scp ? scp->refCount : 0);
1469 osi_Log3(afsd_logp, "cm_LookupInternal (1) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1477 cm_ReleaseSCache(scp);
1481 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1482 #ifdef DEBUG_REFCOUNT
1483 afsi_log("%s:%d cm_LookupInternal (2) code 0x%x dscp 0x%p ref %d scp 0x%p ref %d", file, line, code, dscp, dscp->refCount, scp, scp ? scp->refCount : 0);
1484 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1491 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1492 #ifdef DEBUG_REFCOUNT
1493 afsi_log("%s:%d cm_LookupInternal (2) code 0x%x dscp 0x%p ref %d scp 0x%p ref %d", file, line, code, dscp, dscp->refCount, scp, scp ? scp->refCount : 0);
1494 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1500 /* None of the possible sysName expansions could be found */
1501 if (flags & CM_FLAG_CHECKPATH)
1502 return CM_ERROR_NOSUCHPATH;
1504 return CM_ERROR_NOSUCHFILE;
1507 /*! \brief Unlink a file name
1509 Encapsulates a call to RXAFS_RemoveFile().
1511 \param[in] dscp cm_scache_t pointing at the directory containing the
1512 name to be unlinked.
1514 \param[in] fnamep Original name to be unlinked. This is the
1515 name that will be passed into the RXAFS_RemoveFile() call.
1516 This parameter is optional. If not provided, the value will
1519 \param[in] came Client name to be unlinked. This name will be used
1520 to update the local directory caches.
1522 \param[in] userp cm_user_t for the request.
1524 \param[in] reqp Request tracker.
1527 long cm_Unlink(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t * cnamep,
1528 cm_user_t *userp, cm_req_t *reqp)
1534 AFSFetchStatus newDirStatus;
1536 struct rx_connection * rxconnp;
1538 cm_scache_t *scp = NULL;
1539 int free_fnamep = FALSE;
1541 memset(&volSync, 0, sizeof(volSync));
1543 if (fnamep == NULL) {
1546 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ, &dirop);
1548 code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
1551 cm_EndDirOp(&dirop);
1558 #ifdef AFS_FREELANCE_CLIENT
1559 if (cm_freelanceEnabled && dscp == cm_data.rootSCachep) {
1560 /* deleting a mount point from the root dir. */
1561 code = cm_FreelanceRemoveMount(fnamep);
1566 code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
1568 /* make sure we don't screw up the dir status during the merge */
1569 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
1571 lock_ObtainWrite(&dscp->rw);
1572 sflags = CM_SCACHESYNC_STOREDATA;
1573 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1574 lock_ReleaseWrite(&dscp->rw);
1576 cm_EndDirOp(&dirop);
1581 afsFid.Volume = dscp->fid.volume;
1582 afsFid.Vnode = dscp->fid.vnode;
1583 afsFid.Unique = dscp->fid.unique;
1585 osi_Log1(afsd_logp, "CALL RemoveFile scp 0x%p", dscp);
1587 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
1591 rxconnp = cm_GetRxConn(connp);
1592 code = RXAFS_RemoveFile(rxconnp, &afsFid, fnamep,
1593 &newDirStatus, &volSync);
1594 rx_PutConnection(rxconnp);
1596 } while (cm_Analyze(connp, userp, reqp, &dscp->fid, &volSync, NULL, NULL, code));
1597 code = cm_MapRPCError(code, reqp);
1600 osi_Log1(afsd_logp, "CALL RemoveFile FAILURE, code 0x%x", code);
1602 osi_Log0(afsd_logp, "CALL RemoveFile SUCCESS");
1605 lock_ObtainWrite(&dirop.scp->dirlock);
1606 dirop.lockType = CM_DIRLOCK_WRITE;
1608 lock_ObtainWrite(&dscp->rw);
1609 cm_dnlcRemove(dscp, cnamep);
1610 cm_SyncOpDone(dscp, NULL, sflags);
1612 cm_MergeStatus(NULL, dscp, &newDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
1613 } else if (code == CM_ERROR_NOSUCHFILE) {
1614 /* windows would not have allowed the request to delete the file
1615 * if it did not believe the file existed. therefore, we must
1616 * have an inconsistent view of the world.
1618 dscp->cbServerp = NULL;
1620 lock_ReleaseWrite(&dscp->rw);
1622 if (code == 0 && cm_CheckDirOpForSingleChange(&dirop) && cnamep) {
1623 cm_DirDeleteEntry(&dirop, fnamep);
1625 cm_BPlusDirDeleteEntry(&dirop, cnamep);
1628 cm_EndDirOp(&dirop);
1631 cm_ReleaseSCache(scp);
1633 lock_ObtainWrite(&scp->rw);
1634 if (--scp->linkCount == 0)
1635 scp->flags |= CM_SCACHEFLAG_DELETED;
1636 cm_DiscardSCache(scp);
1637 lock_ReleaseWrite(&scp->rw);
1648 /* called with a write locked vnode, and fills in the link info.
1649 * returns this the vnode still write locked.
1651 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1658 lock_AssertWrite(&linkScp->rw);
1659 if (!linkScp->mountPointStringp[0]) {
1661 #ifdef AFS_FREELANCE_CLIENT
1662 /* File servers do not have data for freelance entries */
1663 if (cm_freelanceEnabled &&
1664 linkScp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
1665 linkScp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
1667 code = cm_FreelanceFetchMountPointString(linkScp);
1669 #endif /* AFS_FREELANCE_CLIENT */
1671 /* read the link data from the file server*/
1672 lock_ReleaseWrite(&linkScp->rw);
1673 thyper.LowPart = thyper.HighPart = 0;
1674 code = buf_Get(linkScp, &thyper, reqp, &bufp);
1675 lock_ObtainWrite(&linkScp->rw);
1679 code = cm_SyncOp(linkScp, bufp, userp, reqp, 0,
1680 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1685 cm_SyncOpDone(linkScp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1687 if (cm_HaveBuffer(linkScp, bufp, 0))
1690 code = cm_GetBuffer(linkScp, bufp, NULL, userp, reqp);
1695 } /* while loop to get the data */
1697 /* now if we still have no link read in,
1698 * copy the data from the buffer */
1699 if ((temp = linkScp->length.LowPart) >= MOUNTPOINTLEN) {
1701 return CM_ERROR_TOOBIG;
1704 /* otherwise, it fits; make sure it is still null (could have
1705 * lost race with someone else referencing this link above),
1706 * and if so, copy in the data.
1708 if (!linkScp->mountPointStringp[0]) {
1709 strncpy(linkScp->mountPointStringp, bufp->datap, temp);
1710 linkScp->mountPointStringp[temp] = 0; /* null terminate */
1715 if ( !strnicmp(linkScp->mountPointStringp, "msdfs:", strlen("msdfs:")) )
1716 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1718 } /* don't have sym link contents cached */
1723 /* called with a held vnode and a path suffix, with the held vnode being a
1724 * symbolic link. Our goal is to generate a new path to interpret, and return
1725 * this new path in newSpaceBufferp. If the new vnode is relative to a dir
1726 * other than the directory containing the symbolic link, then the new root is
1727 * returned in *newRootScpp, otherwise a null is returned there.
1729 long cm_AssembleLink(cm_scache_t *linkScp, fschar_t *pathSuffixp,
1730 cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1731 cm_user_t *userp, cm_req_t *reqp)
1738 *newRootScpp = NULL;
1739 *newSpaceBufferp = NULL;
1741 lock_ObtainWrite(&linkScp->rw);
1742 code = cm_HandleLink(linkScp, userp, reqp);
1746 /* if we may overflow the buffer, bail out; buffer is signficantly
1747 * bigger than max path length, so we don't really have to worry about
1748 * being a little conservative here.
1750 if (cm_FsStrLen(linkScp->mountPointStringp) + cm_FsStrLen(pathSuffixp) + 2
1751 >= CM_UTILS_SPACESIZE) {
1752 code = CM_ERROR_TOOBIG;
1756 tsp = cm_GetSpace();
1757 linkp = linkScp->mountPointStringp;
1758 if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1759 if (strlen(linkp) > cm_mountRootLen)
1760 StringCbCopyA((char *) tsp->data, sizeof(tsp->data), linkp+cm_mountRootLen+1);
1763 *newRootScpp = cm_data.rootSCachep;
1764 cm_HoldSCache(cm_data.rootSCachep);
1765 } else if (linkp[0] == '\\' && linkp[1] == '\\') {
1766 if (!strnicmp(&linkp[2], cm_NetbiosName, (len = (long)strlen(cm_NetbiosName))))
1768 char * p = &linkp[len + 3];
1769 if (strnicmp(p, "all", 3) == 0)
1772 StringCbCopyA(tsp->data, sizeof(tsp->data), p);
1773 for (p = tsp->data; *p; p++) {
1777 *newRootScpp = cm_data.rootSCachep;
1778 cm_HoldSCache(cm_data.rootSCachep);
1780 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1781 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1782 code = CM_ERROR_PATH_NOT_COVERED;
1784 } else if ( linkScp->fileType == CM_SCACHETYPE_DFSLINK ||
1785 !strnicmp(linkp, "msdfs:", (len = (long)strlen("msdfs:"))) ) {
1786 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1787 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1788 code = CM_ERROR_PATH_NOT_COVERED;
1789 } else if (*linkp == '\\' || *linkp == '/') {
1791 /* formerly, this was considered to be from the AFS root,
1792 * but this seems to create problems. instead, we will just
1793 * reject the link */
1794 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp+1);
1795 *newRootScpp = cm_data.rootSCachep;
1796 cm_HoldSCache(cm_data.rootSCachep);
1798 /* we still copy the link data into the response so that
1799 * the user can see what the link points to
1801 linkScp->fileType = CM_SCACHETYPE_INVALID;
1802 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1803 code = CM_ERROR_NOSUCHPATH;
1806 /* a relative link */
1807 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1809 if (pathSuffixp[0] != 0) { /* if suffix string is non-null */
1810 StringCchCatA(tsp->data,lengthof(tsp->data), "\\");
1811 StringCchCatA(tsp->data,lengthof(tsp->data), pathSuffixp);
1815 clientchar_t * cpath = cm_FsStringToClientStringAlloc(tsp->data, -1, NULL);
1816 if (cpath != NULL) {
1817 cm_ClientStrCpy(tsp->wdata, lengthof(tsp->wdata), cpath);
1819 *newSpaceBufferp = tsp;
1821 code = CM_ERROR_NOSUCHPATH;
1828 if (code == CM_ERROR_PATH_NOT_COVERED && reqp->tidPathp && reqp->relPathp) {
1829 cm_VolStatus_Notify_DFS_Mapping(linkScp, reqp->tidPathp, reqp->relPathp);
1834 lock_ReleaseWrite(&linkScp->rw);
1837 #ifdef DEBUG_REFCOUNT
1838 long cm_NameIDbg(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
1839 cm_user_t *userp, clientchar_t *tidPathp, cm_req_t *reqp,
1840 cm_scache_t **outScpp,
1841 char * file, long line)
1843 long cm_NameI(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
1844 cm_user_t *userp, clientchar_t *tidPathp,
1845 cm_req_t *reqp, cm_scache_t **outScpp)
1849 clientchar_t *tp; /* ptr moving through input buffer */
1850 clientchar_t tc; /* temp char */
1851 int haveComponent; /* has new component started? */
1852 clientchar_t component[AFSPATHMAX]; /* this is the new component */
1853 clientchar_t *cp; /* component name being assembled */
1854 cm_scache_t *tscp; /* current location in the hierarchy */
1855 cm_scache_t *nscp; /* next dude down */
1856 cm_scache_t *dirScp; /* last dir we searched */
1857 cm_scache_t *linkScp; /* new root for the symlink we just
1859 cm_space_t *psp; /* space for current path, if we've hit
1861 cm_space_t *tempsp; /* temp vbl */
1862 clientchar_t *restp; /* rest of the pathname to interpret */
1863 int symlinkCount; /* count of # of symlinks traversed */
1864 int extraFlag; /* avoid chasing mt pts for dir cmd */
1865 int phase = 1; /* 1 = tidPathp, 2 = pathp */
1866 #define MAX_FID_COUNT 512
1867 cm_fid_t fids[MAX_FID_COUNT]; /* array of fids processed in this path walk */
1868 int fid_count = 0; /* number of fids processed in this path walk */
1873 #ifdef DEBUG_REFCOUNT
1874 afsi_log("%s:%d cm_NameI rootscp 0x%p ref %d", file, line, rootSCachep, rootSCachep->refCount);
1875 osi_Log4(afsd_logp,"cm_NameI rootscp 0x%p path %S tidpath %S flags 0x%x",
1876 rootSCachep, pathp ? pathp : L"<NULL>", tidPathp ? tidPathp : L"<NULL>",
1891 cm_HoldSCache(tscp);
1899 /* map Unix slashes into DOS ones so we can interpret Unix
1905 if (!haveComponent) {
1908 } else if (tc == 0) {
1922 /* we have a component here */
1923 if (tc == 0 || tc == '\\') {
1924 /* end of the component; we're at the last
1925 * component if tc == 0. However, if the last
1926 * is a symlink, we have more to do.
1928 *cp++ = 0; /* add null termination */
1930 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
1931 extraFlag = CM_FLAG_NOMOUNTCHASE;
1932 code = cm_Lookup(tscp, component,
1934 userp, reqp, &nscp);
1937 if (!cm_ClientStrCmp(component,_C("..")) ||
1938 !cm_ClientStrCmp(component,_C("."))) {
1940 * roll back the fid list until we find the
1941 * fid that matches where we are now. Its not
1942 * necessarily one or two fids because they
1943 * might have been symlinks or mount points or
1944 * both that were crossed.
1946 for ( i=fid_count-1; i>=0; i--) {
1947 if (!cm_FidCmp(&nscp->fid, &fids[i]))
1952 /* add the new fid to the list */
1953 if (fid_count == MAX_FID_COUNT) {
1954 code = CM_ERROR_TOO_MANY_SYMLINKS;
1955 cm_ReleaseSCache(nscp);
1959 fids[fid_count++] = nscp->fid;
1964 cm_ReleaseSCache(tscp);
1966 cm_ReleaseSCache(dirScp);
1969 if ((code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH) &&
1970 tscp->fileType == CM_SCACHETYPE_SYMLINK) {
1971 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_NOSUCHPATH");
1972 return CM_ERROR_NOSUCHPATH;
1974 osi_Log1(afsd_logp,"cm_NameI code 0x%x", code);
1979 haveComponent = 0; /* component done */
1981 cm_ReleaseSCache(dirScp);
1982 dirScp = tscp; /* for some symlinks */
1983 tscp = nscp; /* already held */
1985 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
1988 cm_ReleaseSCache(dirScp);
1994 /* now, if tscp is a symlink, we should follow it and
1995 * assemble the path again.
1997 lock_ObtainWrite(&tscp->rw);
1998 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1999 CM_SCACHESYNC_GETSTATUS
2000 | CM_SCACHESYNC_NEEDCALLBACK);
2002 lock_ReleaseWrite(&tscp->rw);
2003 cm_ReleaseSCache(tscp);
2006 cm_ReleaseSCache(dirScp);
2011 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2013 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
2014 /* this is a symlink; assemble a new buffer */
2015 lock_ReleaseWrite(&tscp->rw);
2016 if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
2017 cm_ReleaseSCache(tscp);
2020 cm_ReleaseSCache(dirScp);
2025 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_TOO_MANY_SYMLINKS");
2026 return CM_ERROR_TOO_MANY_SYMLINKS;
2036 /* TODO: make this better */
2037 frestp = cm_ClientStringToFsStringAlloc(restp, -1, NULL);
2038 code = cm_AssembleLink(tscp, frestp, &linkScp, &tempsp, userp, reqp);
2042 if (code == 0 && linkScp != NULL) {
2043 if (linkScp == cm_data.rootSCachep) {
2047 for ( i=0; i<fid_count; i++) {
2048 if ( !cm_FidCmp(&linkScp->fid, &fids[i]) ) {
2049 code = CM_ERROR_TOO_MANY_SYMLINKS;
2050 cm_ReleaseSCache(linkScp);
2056 if (i == fid_count && fid_count < MAX_FID_COUNT) {
2057 fids[fid_count++] = linkScp->fid;
2062 /* something went wrong */
2063 cm_ReleaseSCache(tscp);
2066 cm_ReleaseSCache(dirScp);
2072 /* otherwise, tempsp has the new path,
2073 * and linkScp is the new root from
2074 * which to interpret that path.
2075 * Continue with the namei processing,
2076 * also doing the bookkeeping for the
2077 * space allocation and tracking the
2078 * vnode reference counts.
2084 cm_ReleaseSCache(tscp);
2089 * now, if linkScp is null, that's
2090 * AssembleLink's way of telling us that
2091 * the sym link is relative to the dir
2092 * containing the link. We have a ref
2093 * to it in dirScp, and we hold it now
2094 * and reuse it as the new spot in the
2102 /* not a symlink, we may be done */
2103 lock_ReleaseWrite(&tscp->rw);
2111 cm_ReleaseSCache(dirScp);
2119 cm_ReleaseSCache(dirScp);
2122 } /* end of a component */
2125 } /* we have a component */
2126 } /* big while loop over all components */
2130 cm_ReleaseSCache(dirScp);
2136 cm_ReleaseSCache(tscp);
2138 #ifdef DEBUG_REFCOUNT
2139 afsi_log("%s:%d cm_NameI code 0x%x outScpp 0x%p ref %d", file, line, code, *outScpp, (*outScpp) ? (*outScpp)->refCount : 0);
2141 osi_Log2(afsd_logp,"cm_NameI code 0x%x outScpp 0x%p", code, *outScpp);
2145 /* called with a dir, and a vnode within the dir that happens to be a symlink.
2146 * We chase the link, and return a held pointer to the target, if it exists,
2147 * in *outScpp. If we succeed, we return 0, otherwise we return an error code
2148 * and do not hold or return a target vnode.
2150 * This is very similar to calling cm_NameI with the last component of a name,
2151 * which happens to be a symlink, except that we've already passed by the name.
2153 * This function is typically called by the directory listing functions, which
2154 * encounter symlinks but need to return the proper file length so programs
2155 * like "more" work properly when they make use of the attributes retrieved from
2158 * The input vnode should not be locked when this function is called.
2160 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
2161 cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
2165 cm_scache_t *newRootScp;
2169 osi_Log1(afsd_logp, "Evaluating symlink scp 0x%p", linkScp);
2171 code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
2175 /* now, if newRootScp is NULL, we're really being told that the symlink
2176 * is relative to the current directory (dscp).
2178 if (newRootScp == NULL) {
2180 cm_HoldSCache(dscp);
2183 code = cm_NameI(newRootScp, spacep->wdata,
2184 CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
2185 userp, NULL, reqp, outScpp);
2187 if (code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH)
2188 code = CM_ERROR_NOSUCHPATH;
2190 /* this stuff is allocated no matter what happened on the namei call,
2192 cm_FreeSpace(spacep);
2193 cm_ReleaseSCache(newRootScp);
2195 if (linkScp == *outScpp) {
2196 cm_ReleaseSCache(*outScpp);
2198 code = CM_ERROR_NOSUCHPATH;
2204 /* for a given entry, make sure that it isn't in the stat cache, and then
2205 * add it to the list of file IDs to be obtained.
2207 * Don't bother adding it if we already have a vnode. Note that the dir
2208 * is locked, so we have to be careful checking the vnode we're thinking of
2209 * processing, to avoid deadlocks.
2211 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
2222 /* Don't overflow bsp. */
2223 if (bsp->counter >= CM_BULKMAX)
2224 return CM_ERROR_STOPNOW;
2226 thyper.LowPart = cm_data.buf_blockSize;
2227 thyper.HighPart = 0;
2228 thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
2230 /* thyper is now the first byte past the end of the record we're
2231 * interested in, and bsp->bufOffset is the first byte of the record
2232 * we're interested in.
2233 * Skip data in the others.
2236 if (LargeIntegerLessThan(*offp, bsp->bufOffset))
2238 if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
2239 return CM_ERROR_STOPNOW;
2240 if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
2243 cm_SetFid(&tfid, scp->fid.cell, scp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
2244 tscp = cm_FindSCache(&tfid);
2246 if (lock_TryWrite(&tscp->rw)) {
2247 /* we have an entry that we can look at */
2248 if (!(tscp->flags & CM_SCACHEFLAG_EACCESS) && cm_HaveCallback(tscp)) {
2249 /* we have a callback on it. Don't bother
2250 * fetching this stat entry, since we're happy
2251 * with the info we have.
2253 lock_ReleaseWrite(&tscp->rw);
2254 cm_ReleaseSCache(tscp);
2257 lock_ReleaseWrite(&tscp->rw);
2259 cm_ReleaseSCache(tscp);
2262 #ifdef AFS_FREELANCE_CLIENT
2263 // yj: if this is a mountpoint under root.afs then we don't want it
2264 // to be bulkstat-ed, instead, we call getSCache directly and under
2265 // getSCache, it is handled specially.
2266 if ( cm_freelanceEnabled &&
2267 tfid.cell==AFS_FAKE_ROOT_CELL_ID &&
2268 tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
2269 !(tfid.vnode==0x1 && tfid.unique==0x1) )
2271 osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
2272 return cm_GetSCache(&tfid, &tscp, NULL, NULL);
2274 #endif /* AFS_FREELANCE_CLIENT */
2277 bsp->fids[i].Volume = scp->fid.volume;
2278 bsp->fids[i].Vnode = tfid.vnode;
2279 bsp->fids[i].Unique = tfid.unique;
2284 cm_TryBulkStatRPC(cm_scache_t *dscp, cm_bulkStat_t *bbp, cm_user_t *userp, cm_req_t *reqp)
2287 AFSCBFids fidStruct;
2288 AFSBulkStats statStruct;
2290 AFSCBs callbackStruct;
2293 cm_callbackRequest_t cbReq;
2299 struct rx_connection * rxconnp;
2300 int inlinebulk = 0; /* Did we use InlineBulkStatus RPC or not? */
2302 memset(&volSync, 0, sizeof(volSync));
2304 /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
2305 * make the calls to create the entries. Handle AFSCBMAX files at a
2308 for (filex = 0; filex < bbp->counter; filex += filesThisCall) {
2309 filesThisCall = bbp->counter - filex;
2310 if (filesThisCall > AFSCBMAX)
2311 filesThisCall = AFSCBMAX;
2313 fidStruct.AFSCBFids_len = filesThisCall;
2314 fidStruct.AFSCBFids_val = &bbp->fids[filex];
2315 statStruct.AFSBulkStats_len = filesThisCall;
2316 statStruct.AFSBulkStats_val = &bbp->stats[filex];
2317 callbackStruct.AFSCBs_len = filesThisCall;
2318 callbackStruct.AFSCBs_val = &bbp->callbacks[filex];
2319 cm_StartCallbackGrantingCall(NULL, &cbReq);
2320 osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
2322 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2326 rxconnp = cm_GetRxConn(connp);
2327 if (!(connp->serverp->flags & CM_SERVERFLAG_NOINLINEBULK)) {
2328 code = RXAFS_InlineBulkStatus(rxconnp, &fidStruct,
2329 &statStruct, &callbackStruct, &volSync);
2330 if (code == RXGEN_OPCODE) {
2331 cm_SetServerNoInlineBulk(connp->serverp, 0);
2337 code = RXAFS_BulkStatus(rxconnp, &fidStruct,
2338 &statStruct, &callbackStruct, &volSync);
2340 rx_PutConnection(rxconnp);
2342 } while (cm_Analyze(connp, userp, reqp, &dscp->fid,
2343 &volSync, NULL, &cbReq, code));
2344 code = cm_MapRPCError(code, reqp);
2346 /* may as well quit on an error, since we're not going to do
2347 * much better on the next immediate call, either.
2350 osi_Log2(afsd_logp, "CALL %sBulkStatus FAILURE code 0x%x",
2351 inlinebulk ? "Inline" : "", code);
2352 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2355 osi_Log1(afsd_logp, "CALL %sBulkStatus SUCCESS", inlinebulk ? "Inline" : "");
2358 /* otherwise, we should do the merges */
2359 for (i = 0; i<filesThisCall; i++) {
2361 cm_SetFid(&tfid, dscp->fid.cell, bbp->fids[j].Volume, bbp->fids[j].Vnode, bbp->fids[j].Unique);
2362 code = cm_GetSCache(&tfid, &scp, userp, reqp);
2366 /* otherwise, if this entry has no callback info,
2369 lock_ObtainWrite(&scp->rw);
2370 /* now, we have to be extra paranoid on merging in this
2371 * information, since we didn't use cm_SyncOp before
2372 * starting the fetch to make sure that no bad races
2373 * were occurring. Specifically, we need to make sure
2374 * we don't obliterate any newer information in the
2375 * vnode than have here.
2377 * Right now, be pretty conservative: if there's a
2378 * callback or a pending call, skip it.
2379 * However, if the prior attempt to obtain status
2380 * was refused access or the volume is .readonly,
2381 * take the data in any case since we have nothing
2382 * better for the in flight directory enumeration that
2383 * resulted in this function being called.
2385 if ((scp->cbServerp == NULL &&
2386 !(scp->flags & (CM_SCACHEFLAG_FETCHING | CM_SCACHEFLAG_STORING | CM_SCACHEFLAG_SIZESTORING))) ||
2387 (scp->flags & CM_SCACHEFLAG_PURERO) ||
2388 (scp->flags & CM_SCACHEFLAG_EACCESS)) {
2389 cm_EndCallbackGrantingCall(scp, &cbReq,
2392 CM_CALLBACK_MAINTAINCOUNT);
2393 cm_MergeStatus(dscp, scp, &bbp->stats[j], &volSync, userp, reqp, 0);
2395 lock_ReleaseWrite(&scp->rw);
2396 cm_ReleaseSCache(scp);
2397 } /* all files in the response */
2398 /* now tell it to drop the count,
2399 * after doing the vnode processing above */
2400 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2401 } /* while there are still more files to process */
2403 /* If we did the InlineBulk RPC pull out the return code and log it */
2405 if ((&bbp->stats[0])->errorCode) {
2406 osi_Log1(afsd_logp, "cm_TryBulkStat bulk stat error: %d",
2407 (&bbp->stats[0])->errorCode);
2408 code = (&bbp->stats[0])->errorCode;
2415 /* called with a write locked scp and a pointer to a buffer. Make bulk stat
2416 * calls on all undeleted files in the page of the directory specified.
2419 cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
2425 osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%p", dscp);
2427 /* should be on a buffer boundary */
2428 osi_assertx((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0, "invalid offset");
2430 bbp = malloc(sizeof(cm_bulkStat_t));
2431 memset(bbp, 0, sizeof(cm_bulkStat_t));
2432 bbp->bufOffset = *offsetp;
2434 lock_ReleaseWrite(&dscp->rw);
2435 /* first, assemble the file IDs we need to stat */
2436 code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) bbp, offsetp, userp, reqp, NULL);
2438 /* if we failed, bail out early */
2439 if (code && code != CM_ERROR_STOPNOW) {
2441 lock_ObtainWrite(&dscp->rw);
2445 code = cm_TryBulkStatRPC(dscp, bbp, userp, reqp);
2446 osi_Log1(afsd_logp, "END cm_TryBulkStat code 0x%x", code);
2448 lock_ObtainWrite(&dscp->rw);
2453 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
2457 /* initialize store back mask as inexpensive local variable */
2459 memset(statusp, 0, sizeof(AFSStoreStatus));
2461 /* copy out queued info from scache first, if scp passed in */
2463 if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
2464 statusp->ClientModTime = scp->clientModTime;
2465 mask |= AFS_SETMODTIME;
2466 scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
2471 /* now add in our locally generated request */
2472 if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
2473 statusp->ClientModTime = attrp->clientModTime;
2474 mask |= AFS_SETMODTIME;
2476 if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
2477 statusp->UnixModeBits = attrp->unixModeBits;
2478 mask |= AFS_SETMODE;
2480 if (attrp->mask & CM_ATTRMASK_OWNER) {
2481 statusp->Owner = attrp->owner;
2482 mask |= AFS_SETOWNER;
2484 if (attrp->mask & CM_ATTRMASK_GROUP) {
2485 statusp->Group = attrp->group;
2486 mask |= AFS_SETGROUP;
2489 statusp->Mask = mask;
2492 /* set the file size, and make sure that all relevant buffers have been
2493 * truncated. Ensure that any partially truncated buffers have been zeroed
2494 * to the end of the buffer.
2496 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
2502 /* start by locking out buffer creation */
2503 lock_ObtainWrite(&scp->bufCreateLock);
2505 /* verify that this is a file, not a dir or a symlink */
2506 lock_ObtainWrite(&scp->rw);
2507 code = cm_SyncOp(scp, NULL, userp, reqp, 0,
2508 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2511 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2513 if (scp->fileType != CM_SCACHETYPE_FILE) {
2514 code = CM_ERROR_ISDIR;
2519 if (LargeIntegerLessThan(*sizep, scp->length))
2524 lock_ReleaseWrite(&scp->rw);
2526 /* can't hold scp->rw lock here, since we may wait for a storeback to
2527 * finish if the buffer package is cleaning a buffer by storing it to
2531 buf_Truncate(scp, userp, reqp, sizep);
2533 /* now ensure that file length is short enough, and update truncPos */
2534 lock_ObtainWrite(&scp->rw);
2536 /* make sure we have a callback (so we have the right value for the
2537 * length), and wait for it to be safe to do a truncate.
2539 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
2540 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2541 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2543 /* If we only have 'i' bits, then we should still be able to set
2544 the size of a file we created. */
2545 if (code == CM_ERROR_NOACCESS && scp->creator == userp) {
2546 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
2547 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2548 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2554 if (LargeIntegerLessThan(*sizep, scp->length)) {
2555 /* a real truncation. If truncPos is not set yet, or is bigger
2556 * than where we're truncating the file, set truncPos to this
2561 if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
2562 || LargeIntegerLessThan(*sizep, scp->length)) {
2564 scp->truncPos = *sizep;
2565 scp->mask |= CM_SCACHEMASK_TRUNCPOS;
2567 /* in either case, the new file size has been changed */
2568 scp->length = *sizep;
2569 scp->mask |= CM_SCACHEMASK_LENGTH;
2571 else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
2572 /* really extending the file */
2573 scp->length = *sizep;
2574 scp->mask |= CM_SCACHEMASK_LENGTH;
2577 /* done successfully */
2580 cm_SyncOpDone(scp, NULL,
2581 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2582 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2585 lock_ReleaseWrite(&scp->rw);
2586 lock_ReleaseWrite(&scp->bufCreateLock);
2591 /* set the file size or other attributes (but not both at once) */
2592 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
2596 AFSFetchStatus afsOutStatus;
2600 AFSStoreStatus afsInStatus;
2601 struct rx_connection * rxconnp;
2603 memset(&volSync, 0, sizeof(volSync));
2605 /* handle file length setting */
2606 if (attrp->mask & CM_ATTRMASK_LENGTH)
2607 return cm_SetLength(scp, &attrp->length, userp, reqp);
2609 lock_ObtainWrite(&scp->rw);
2610 /* otherwise, we have to make an RPC to get the status */
2611 code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
2613 lock_ReleaseWrite(&scp->rw);
2616 lock_ConvertWToR(&scp->rw);
2618 /* make the attr structure */
2619 cm_StatusFromAttr(&afsInStatus, scp, attrp);
2621 tfid.Volume = scp->fid.volume;
2622 tfid.Vnode = scp->fid.vnode;
2623 tfid.Unique = scp->fid.unique;
2624 lock_ReleaseRead(&scp->rw);
2626 /* now make the RPC */
2627 osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%p", scp);
2629 code = cm_ConnFromFID(&scp->fid, userp, reqp, &connp);
2633 rxconnp = cm_GetRxConn(connp);
2634 code = RXAFS_StoreStatus(rxconnp, &tfid,
2635 &afsInStatus, &afsOutStatus, &volSync);
2636 rx_PutConnection(rxconnp);
2638 } while (cm_Analyze(connp, userp, reqp,
2639 &scp->fid, &volSync, NULL, NULL, code));
2640 code = cm_MapRPCError(code, reqp);
2643 osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
2645 osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
2647 lock_ObtainWrite(&scp->rw);
2648 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
2650 cm_MergeStatus(NULL, scp, &afsOutStatus, &volSync, userp, reqp,
2651 CM_MERGEFLAG_FORCE|CM_MERGEFLAG_STOREDATA);
2653 /* if we're changing the mode bits, discard the ACL cache,
2654 * since we changed the mode bits.
2656 if (afsInStatus.Mask & AFS_SETMODE)
2657 cm_FreeAllACLEnts(scp);
2658 lock_ReleaseWrite(&scp->rw);
2662 long cm_Create(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
2663 cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
2668 cm_callbackRequest_t cbReq;
2671 cm_scache_t *scp = NULL;
2673 AFSStoreStatus inStatus;
2674 AFSFetchStatus updatedDirStatus;
2675 AFSFetchStatus newFileStatus;
2676 AFSCallBack newFileCallback;
2678 struct rx_connection * rxconnp;
2680 fschar_t * fnamep = NULL;
2682 memset(&volSync, 0, sizeof(volSync));
2684 /* can't create names with @sys in them; must expand it manually first.
2685 * return "invalid request" if they try.
2687 if (cm_ExpandSysName(cnamep, NULL, 0, 0)) {
2688 return CM_ERROR_ATSYS;
2691 #ifdef AFS_FREELANCE_CLIENT
2692 /* Freelance root volume does not hold files */
2693 if (cm_freelanceEnabled &&
2694 dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
2695 dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
2697 return CM_ERROR_NOACCESS;
2699 #endif /* AFS_FREELANCE_CLIENT */
2701 /* before starting the RPC, mark that we're changing the file data, so
2702 * that someone who does a chmod will know to wait until our call
2705 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2706 lock_ObtainWrite(&dscp->rw);
2707 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2708 lock_ReleaseWrite(&dscp->rw);
2710 cm_StartCallbackGrantingCall(NULL, &cbReq);
2712 cm_EndDirOp(&dirop);
2719 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
2721 cm_StatusFromAttr(&inStatus, NULL, attrp);
2723 /* try the RPC now */
2724 osi_Log1(afsd_logp, "CALL CreateFile scp 0x%p", dscp);
2726 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2730 dirAFSFid.Volume = dscp->fid.volume;
2731 dirAFSFid.Vnode = dscp->fid.vnode;
2732 dirAFSFid.Unique = dscp->fid.unique;
2734 rxconnp = cm_GetRxConn(connp);
2735 code = RXAFS_CreateFile(connp->rxconnp, &dirAFSFid, fnamep,
2736 &inStatus, &newAFSFid, &newFileStatus,
2737 &updatedDirStatus, &newFileCallback,
2739 rx_PutConnection(rxconnp);
2741 } while (cm_Analyze(connp, userp, reqp,
2742 &dscp->fid, &volSync, NULL, &cbReq, code));
2743 code = cm_MapRPCError(code, reqp);
2746 osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
2748 osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
2751 lock_ObtainWrite(&dirop.scp->dirlock);
2752 dirop.lockType = CM_DIRLOCK_WRITE;
2754 lock_ObtainWrite(&dscp->rw);
2755 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2757 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
2759 lock_ReleaseWrite(&dscp->rw);
2761 /* now try to create the file's entry, too, but be careful to
2762 * make sure that we don't merge in old info. Since we weren't locking
2763 * out any requests during the file's creation, we may have pretty old
2767 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
2768 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2770 lock_ObtainWrite(&scp->rw);
2771 scp->creator = userp; /* remember who created it */
2772 if (!cm_HaveCallback(scp)) {
2773 cm_EndCallbackGrantingCall(scp, &cbReq,
2774 &newFileCallback, &volSync, 0);
2775 cm_MergeStatus(dscp, scp, &newFileStatus, &volSync,
2779 lock_ReleaseWrite(&scp->rw);
2783 /* make sure we end things properly */
2785 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2787 if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
2788 cm_DirCreateEntry(&dirop, fnamep, &newFid);
2790 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
2793 cm_EndDirOp(&dirop);
2802 cm_ReleaseSCache(scp);
2808 * locked if TRUE means write-locked
2809 * else the cm_scache_t rw must not be held
2811 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp, afs_uint32 locked)
2816 lock_ReleaseWrite(&scp->rw);
2817 code = buf_CleanVnode(scp, userp, reqp);
2819 lock_ObtainWrite(&scp->rw);
2821 if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
2822 | CM_SCACHEMASK_CLIENTMODTIME
2823 | CM_SCACHEMASK_LENGTH))
2824 code = cm_StoreMini(scp, userp, reqp);
2826 if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) {
2827 code = (scp->flags & CM_SCACHEFLAG_OVERQUOTA) ? CM_ERROR_QUOTA : CM_ERROR_SPACE;
2828 scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE);
2832 lock_ReleaseWrite(&scp->rw);
2833 } else if (locked) {
2834 lock_ObtainWrite(&scp->rw);
2839 long cm_MakeDir(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
2840 cm_user_t *userp, cm_req_t *reqp, cm_scache_t **scpp)
2845 cm_callbackRequest_t cbReq;
2848 cm_scache_t *scp = NULL;
2850 AFSStoreStatus inStatus;
2851 AFSFetchStatus updatedDirStatus;
2852 AFSFetchStatus newDirStatus;
2853 AFSCallBack newDirCallback;
2855 struct rx_connection * rxconnp;
2857 fschar_t * fnamep = NULL;
2859 memset(&volSync, 0, sizeof(volSync));
2861 /* can't create names with @sys in them; must expand it manually first.
2862 * return "invalid request" if they try.
2864 if (cm_ExpandSysName(cnamep, NULL, 0, 0)) {
2865 return CM_ERROR_ATSYS;
2868 #ifdef AFS_FREELANCE_CLIENT
2869 /* Freelance root volume does not hold subdirectories */
2870 if (cm_freelanceEnabled &&
2871 dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
2872 dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
2874 return CM_ERROR_NOACCESS;
2876 #endif /* AFS_FREELANCE_CLIENT */
2878 /* before starting the RPC, mark that we're changing the directory
2879 * data, so that someone who does a chmod on the dir will wait until
2880 * our call completes.
2882 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2883 lock_ObtainWrite(&dscp->rw);
2884 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2885 lock_ReleaseWrite(&dscp->rw);
2887 cm_StartCallbackGrantingCall(NULL, &cbReq);
2889 cm_EndDirOp(&dirop);
2896 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
2897 cm_StatusFromAttr(&inStatus, NULL, attrp);
2899 /* try the RPC now */
2900 osi_Log1(afsd_logp, "CALL MakeDir scp 0x%p", dscp);
2902 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2906 dirAFSFid.Volume = dscp->fid.volume;
2907 dirAFSFid.Vnode = dscp->fid.vnode;
2908 dirAFSFid.Unique = dscp->fid.unique;
2910 rxconnp = cm_GetRxConn(connp);
2911 code = RXAFS_MakeDir(connp->rxconnp, &dirAFSFid, fnamep,
2912 &inStatus, &newAFSFid, &newDirStatus,
2913 &updatedDirStatus, &newDirCallback,
2915 rx_PutConnection(rxconnp);
2917 } while (cm_Analyze(connp, userp, reqp,
2918 &dscp->fid, &volSync, NULL, &cbReq, code));
2919 code = cm_MapRPCError(code, reqp);
2922 osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
2924 osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
2927 lock_ObtainWrite(&dirop.scp->dirlock);
2928 dirop.lockType = CM_DIRLOCK_WRITE;
2930 lock_ObtainWrite(&dscp->rw);
2931 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2933 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
2935 lock_ReleaseWrite(&dscp->rw);
2937 /* now try to create the new dir's entry, too, but be careful to
2938 * make sure that we don't merge in old info. Since we weren't locking
2939 * out any requests during the file's creation, we may have pretty old
2943 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
2944 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2946 lock_ObtainWrite(&scp->rw);
2947 if (!cm_HaveCallback(scp)) {
2948 cm_EndCallbackGrantingCall(scp, &cbReq,
2949 &newDirCallback, &volSync, 0);
2950 cm_MergeStatus(dscp, scp, &newDirStatus, &volSync,
2954 lock_ReleaseWrite(&scp->rw);
2958 /* make sure we end things properly */
2960 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2962 if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
2963 cm_DirCreateEntry(&dirop, fnamep, &newFid);
2965 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
2968 cm_EndDirOp(&dirop);
2976 cm_ReleaseSCache(scp);
2979 /* and return error code */
2983 long cm_Link(cm_scache_t *dscp, clientchar_t *cnamep, cm_scache_t *sscp, long flags,
2984 cm_user_t *userp, cm_req_t *reqp)
2989 AFSFid existingAFSFid;
2990 AFSFetchStatus updatedDirStatus;
2991 AFSFetchStatus newLinkStatus;
2993 struct rx_connection * rxconnp;
2995 fschar_t * fnamep = NULL;
2997 memset(&volSync, 0, sizeof(volSync));
2999 if (dscp->fid.cell != sscp->fid.cell ||
3000 dscp->fid.volume != sscp->fid.volume) {
3001 return CM_ERROR_CROSSDEVLINK;
3004 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
3005 lock_ObtainWrite(&dscp->rw);
3006 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3007 lock_ReleaseWrite(&dscp->rw);
3009 cm_EndDirOp(&dirop);
3014 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3016 /* try the RPC now */
3017 osi_Log1(afsd_logp, "CALL Link scp 0x%p", dscp);
3019 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3022 dirAFSFid.Volume = dscp->fid.volume;
3023 dirAFSFid.Vnode = dscp->fid.vnode;
3024 dirAFSFid.Unique = dscp->fid.unique;
3026 existingAFSFid.Volume = sscp->fid.volume;
3027 existingAFSFid.Vnode = sscp->fid.vnode;
3028 existingAFSFid.Unique = sscp->fid.unique;
3030 rxconnp = cm_GetRxConn(connp);
3031 code = RXAFS_Link(rxconnp, &dirAFSFid, fnamep, &existingAFSFid,
3032 &newLinkStatus, &updatedDirStatus, &volSync);
3033 rx_PutConnection(rxconnp);
3034 osi_Log1(afsd_logp," RXAFS_Link returns 0x%x", code);
3036 } while (cm_Analyze(connp, userp, reqp,
3037 &dscp->fid, &volSync, NULL, NULL, code));
3039 code = cm_MapRPCError(code, reqp);
3042 osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
3044 osi_Log0(afsd_logp, "CALL Link SUCCESS");
3047 lock_ObtainWrite(&dirop.scp->dirlock);
3048 dirop.lockType = CM_DIRLOCK_WRITE;
3050 lock_ObtainWrite(&dscp->rw);
3051 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3053 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3055 lock_ReleaseWrite(&dscp->rw);
3058 if (cm_CheckDirOpForSingleChange(&dirop)) {
3059 cm_DirCreateEntry(&dirop, fnamep, &sscp->fid);
3061 cm_BPlusDirCreateEntry(&dirop, cnamep, &sscp->fid);
3065 cm_EndDirOp(&dirop);
3067 /* Update the linked object status */
3069 lock_ObtainWrite(&sscp->rw);
3070 cm_MergeStatus(NULL, sscp, &newLinkStatus, &volSync, userp, reqp, 0);
3071 lock_ReleaseWrite(&sscp->rw);
3079 long cm_SymLink(cm_scache_t *dscp, clientchar_t *cnamep, fschar_t *contentsp, long flags,
3080 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
3088 AFSStoreStatus inStatus;
3089 AFSFetchStatus updatedDirStatus;
3090 AFSFetchStatus newLinkStatus;
3092 struct rx_connection * rxconnp;
3094 fschar_t *fnamep = NULL;
3096 memset(&volSync, 0, sizeof(volSync));
3098 /* before starting the RPC, mark that we're changing the directory data,
3099 * so that someone who does a chmod on the dir will wait until our
3102 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
3103 lock_ObtainWrite(&dscp->rw);
3104 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3105 lock_ReleaseWrite(&dscp->rw);
3107 cm_EndDirOp(&dirop);
3112 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3114 cm_StatusFromAttr(&inStatus, NULL, attrp);
3116 /* try the RPC now */
3117 osi_Log1(afsd_logp, "CALL Symlink scp 0x%p", dscp);
3119 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3123 dirAFSFid.Volume = dscp->fid.volume;
3124 dirAFSFid.Vnode = dscp->fid.vnode;
3125 dirAFSFid.Unique = dscp->fid.unique;
3127 rxconnp = cm_GetRxConn(connp);
3128 code = RXAFS_Symlink(rxconnp, &dirAFSFid, fnamep, contentsp,
3129 &inStatus, &newAFSFid, &newLinkStatus,
3130 &updatedDirStatus, &volSync);
3131 rx_PutConnection(rxconnp);
3133 } while (cm_Analyze(connp, userp, reqp,
3134 &dscp->fid, &volSync, NULL, NULL, code));
3135 code = cm_MapRPCError(code, reqp);
3138 osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
3140 osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
3143 lock_ObtainWrite(&dirop.scp->dirlock);
3144 dirop.lockType = CM_DIRLOCK_WRITE;
3146 lock_ObtainWrite(&dscp->rw);
3147 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3149 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3151 lock_ReleaseWrite(&dscp->rw);
3154 if (cm_CheckDirOpForSingleChange(&dirop)) {
3155 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3157 cm_DirCreateEntry(&dirop, fnamep, &newFid);
3159 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
3163 cm_EndDirOp(&dirop);
3165 /* now try to create the new dir's entry, too, but be careful to
3166 * make sure that we don't merge in old info. Since we weren't locking
3167 * out any requests during the file's creation, we may have pretty old
3171 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3172 code = cm_GetSCache(&newFid, &scp, userp, reqp);
3174 lock_ObtainWrite(&scp->rw);
3175 if (!cm_HaveCallback(scp)) {
3176 cm_MergeStatus(dscp, scp, &newLinkStatus, &volSync,
3179 lock_ReleaseWrite(&scp->rw);
3180 cm_ReleaseSCache(scp);
3186 /* and return error code */
3190 /*! \brief Remove a directory
3192 Encapsulates a call to RXAFS_RemoveDir().
3194 \param[in] dscp cm_scache_t for the directory containing the
3195 directory to be removed.
3197 \param[in] fnamep This will be the original name of the directory
3198 as known to the file server. It will be passed in to RXAFS_RemoveDir().
3199 This parameter is optional. If it is not provided the value
3202 \param[in] cnamep Normalized name used to update the local
3205 \param[in] userp cm_user_t for the request.
3207 \param[in] reqp Request tracker.
3209 long cm_RemoveDir(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t *cnamep, cm_user_t *userp, cm_req_t *reqp)
3215 AFSFetchStatus updatedDirStatus;
3217 struct rx_connection * rxconnp;
3219 cm_scache_t *scp = NULL;
3220 int free_fnamep = FALSE;
3222 memset(&volSync, 0, sizeof(volSync));
3224 if (fnamep == NULL) {
3227 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ, &dirop);
3229 code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
3232 cm_EndDirOp(&dirop);
3239 code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
3243 /* before starting the RPC, mark that we're changing the directory data,
3244 * so that someone who does a chmod on the dir will wait until our
3247 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
3248 lock_ObtainWrite(&dscp->rw);
3249 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3250 lock_ReleaseWrite(&dscp->rw);
3252 cm_EndDirOp(&dirop);
3257 /* try the RPC now */
3258 osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%p", dscp);
3260 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3264 dirAFSFid.Volume = dscp->fid.volume;
3265 dirAFSFid.Vnode = dscp->fid.vnode;
3266 dirAFSFid.Unique = dscp->fid.unique;
3268 rxconnp = cm_GetRxConn(connp);
3269 code = RXAFS_RemoveDir(rxconnp, &dirAFSFid, fnamep,
3270 &updatedDirStatus, &volSync);
3271 rx_PutConnection(rxconnp);
3273 } while (cm_Analyze(connp, userp, reqp,
3274 &dscp->fid, &volSync, NULL, NULL, code));
3275 code = cm_MapRPCErrorRmdir(code, reqp);
3278 osi_Log1(afsd_logp, "CALL RemoveDir FAILURE, code 0x%x", code);
3280 osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
3283 lock_ObtainWrite(&dirop.scp->dirlock);
3284 dirop.lockType = CM_DIRLOCK_WRITE;
3286 lock_ObtainWrite(&dscp->rw);
3287 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3289 cm_dnlcRemove(dscp, cnamep);
3290 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3292 lock_ReleaseWrite(&dscp->rw);
3295 if (cm_CheckDirOpForSingleChange(&dirop) && cnamep != NULL) {
3296 cm_DirDeleteEntry(&dirop, fnamep);
3298 cm_BPlusDirDeleteEntry(&dirop, cnamep);
3302 cm_EndDirOp(&dirop);
3305 cm_ReleaseSCache(scp);
3307 lock_ObtainWrite(&scp->rw);
3308 scp->flags |= CM_SCACHEFLAG_DELETED;
3309 lock_ReleaseWrite(&scp->rw);
3317 /* and return error code */
3321 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
3323 /* grab mutex on contents */
3324 lock_ObtainWrite(&scp->rw);
3326 /* reset the prefetch info */
3327 scp->prefetch.base.LowPart = 0; /* base */
3328 scp->prefetch.base.HighPart = 0;
3329 scp->prefetch.end.LowPart = 0; /* and end */
3330 scp->prefetch.end.HighPart = 0;
3332 /* release mutex on contents */
3333 lock_ReleaseWrite(&scp->rw);
3339 /*! \brief Rename a file or directory
3341 Encapsulates a RXAFS_Rename() call.
3343 \param[in] oldDscp cm_scache_t for the directory containing the old
3346 \param[in] oldNamep The original old name known to the file server.
3347 This is the name that will be passed into the RXAFS_Rename().
3348 If it is not provided, it will be looked up.
3350 \param[in] normalizedOldNamep Normalized old name. This is used for
3351 updating local directory caches.
3353 \param[in] newDscp cm_scache_t for the directory containing the new
3356 \param[in] newNamep New name. Normalized.
3358 \param[in] userp cm_user_t for the request.
3360 \param[in,out] reqp Request tracker.
3363 long cm_Rename(cm_scache_t *oldDscp, fschar_t *oldNamep, clientchar_t *cOldNamep,
3364 cm_scache_t *newDscp, clientchar_t *cNewNamep, cm_user_t *userp,
3369 AFSFid oldDirAFSFid;
3370 AFSFid newDirAFSFid;
3372 AFSFetchStatus updatedOldDirStatus;
3373 AFSFetchStatus updatedNewDirStatus;
3376 struct rx_connection * rxconnp;
3377 cm_dirOp_t oldDirOp;
3380 cm_dirOp_t newDirOp;
3381 fschar_t * newNamep = NULL;
3382 int free_oldNamep = FALSE;
3383 cm_scache_t *oldScp = NULL, *newScp = NULL;
3385 memset(&volSync, 0, sizeof(volSync));
3387 if (cOldNamep == NULL || cNewNamep == NULL ||
3388 cm_ClientStrLen(cOldNamep) == 0 ||
3389 cm_ClientStrLen(cNewNamep) == 0)
3390 return CM_ERROR_INVAL;
3393 * Before we permit the operation, make sure that we do not already have
3394 * an object in the destination directory that has a case-insensitive match
3395 * for this name UNLESS the matching object is the object we are renaming.
3397 code = cm_Lookup(oldDscp, cOldNamep, 0, userp, reqp, &oldScp);
3399 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S old name lookup failed",
3400 oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3404 code = cm_Lookup(newDscp, cNewNamep, CM_FLAG_CASEFOLD, userp, reqp, &newScp);
3406 /* found a matching object with the new name */
3407 if (cm_FidCmp(&oldScp->fid, &newScp->fid)) {
3408 /* and they don't match so return an error */
3409 osi_Log2(afsd_logp, "cm_Rename newDscp 0x%p cNewName %S new name already exists",
3410 newDscp, osi_LogSaveStringW(afsd_logp, cNewNamep));
3411 code = CM_ERROR_EXISTS;
3413 cm_ReleaseSCache(newScp);
3415 } else if (code == CM_ERROR_AMBIGUOUS_FILENAME) {
3416 code = CM_ERROR_EXISTS;
3423 if (oldNamep == NULL) {
3426 code = cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_READ, &oldDirOp);
3428 code = cm_BPlusDirLookupOriginalName(&oldDirOp, cOldNamep, &oldNamep);
3430 free_oldNamep = TRUE;
3431 cm_EndDirOp(&oldDirOp);
3435 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S Original Name lookup failed",
3436 oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3442 /* before starting the RPC, mark that we're changing the directory data,
3443 * so that someone who does a chmod on the dir will wait until our call
3444 * completes. We do this in vnode order so that we don't deadlock,
3445 * which makes the code a little verbose.
3447 if (oldDscp == newDscp) {
3448 /* check for identical names */
3449 if (cm_ClientStrCmp(cOldNamep, cNewNamep) == 0) {
3450 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_RENAME_IDENTICAL",
3452 code = CM_ERROR_RENAME_IDENTICAL;
3457 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3458 lock_ObtainWrite(&oldDscp->rw);
3459 cm_dnlcRemove(oldDscp, cOldNamep);
3460 cm_dnlcRemove(oldDscp, cNewNamep);
3461 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3462 CM_SCACHESYNC_STOREDATA);
3463 lock_ReleaseWrite(&oldDscp->rw);
3465 cm_EndDirOp(&oldDirOp);
3469 /* two distinct dir vnodes */
3471 if (oldDscp->fid.cell != newDscp->fid.cell ||
3472 oldDscp->fid.volume != newDscp->fid.volume) {
3473 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_CROSSDEVLINK",
3475 code = CM_ERROR_CROSSDEVLINK;
3479 /* shouldn't happen that we have distinct vnodes for two
3480 * different files, but could due to deliberate attack, or
3481 * stale info. Avoid deadlocks and quit now.
3483 if (oldDscp->fid.vnode == newDscp->fid.vnode) {
3484 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p vnode collision",
3486 code = CM_ERROR_CROSSDEVLINK;
3490 if (oldDscp->fid.vnode < newDscp->fid.vnode) {
3491 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3492 lock_ObtainWrite(&oldDscp->rw);
3493 cm_dnlcRemove(oldDscp, cOldNamep);
3494 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3495 CM_SCACHESYNC_STOREDATA);
3496 lock_ReleaseWrite(&oldDscp->rw);
3498 cm_EndDirOp(&oldDirOp);
3500 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE, &newDirOp);
3501 lock_ObtainWrite(&newDscp->rw);
3502 cm_dnlcRemove(newDscp, cNewNamep);
3503 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3504 CM_SCACHESYNC_STOREDATA);
3505 lock_ReleaseWrite(&newDscp->rw);
3507 cm_EndDirOp(&newDirOp);
3509 /* cleanup first one */
3510 lock_ObtainWrite(&oldDscp->rw);
3511 cm_SyncOpDone(oldDscp, NULL,
3512 CM_SCACHESYNC_STOREDATA);
3513 lock_ReleaseWrite(&oldDscp->rw);
3514 cm_EndDirOp(&oldDirOp);
3519 /* lock the new vnode entry first */
3520 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE, &newDirOp);
3521 lock_ObtainWrite(&newDscp->rw);
3522 cm_dnlcRemove(newDscp, cNewNamep);
3523 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3524 CM_SCACHESYNC_STOREDATA);
3525 lock_ReleaseWrite(&newDscp->rw);
3527 cm_EndDirOp(&newDirOp);
3529 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3530 lock_ObtainWrite(&oldDscp->rw);
3531 cm_dnlcRemove(oldDscp, cOldNamep);
3532 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3533 CM_SCACHESYNC_STOREDATA);
3534 lock_ReleaseWrite(&oldDscp->rw);
3536 cm_EndDirOp(&oldDirOp);
3538 /* cleanup first one */
3539 lock_ObtainWrite(&newDscp->rw);
3540 cm_SyncOpDone(newDscp, NULL,
3541 CM_SCACHESYNC_STOREDATA);
3542 lock_ReleaseWrite(&newDscp->rw);
3543 cm_EndDirOp(&newDirOp);
3547 } /* two distinct vnodes */
3554 newNamep = cm_ClientStringToFsStringAlloc(cNewNamep, -1, NULL);
3556 /* try the RPC now */
3557 osi_Log2(afsd_logp, "CALL Rename old scp 0x%p new scp 0x%p",
3560 code = cm_ConnFromFID(&oldDscp->fid, userp, reqp, &connp);
3564 oldDirAFSFid.Volume = oldDscp->fid.volume;
3565 oldDirAFSFid.Vnode = oldDscp->fid.vnode;
3566 oldDirAFSFid.Unique = oldDscp->fid.unique;
3567 newDirAFSFid.Volume = newDscp->fid.volume;
3568 newDirAFSFid.Vnode = newDscp->fid.vnode;
3569 newDirAFSFid.Unique = newDscp->fid.unique;
3571 rxconnp = cm_GetRxConn(connp);
3572 code = RXAFS_Rename(rxconnp, &oldDirAFSFid, oldNamep,
3573 &newDirAFSFid, newNamep,
3574 &updatedOldDirStatus, &updatedNewDirStatus,
3576 rx_PutConnection(rxconnp);
3578 } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
3579 &volSync, NULL, NULL, code));
3580 code = cm_MapRPCError(code, reqp);
3583 osi_Log1(afsd_logp, "CALL Rename FAILURE, code 0x%x", code);
3585 osi_Log0(afsd_logp, "CALL Rename SUCCESS");
3587 /* update the individual stat cache entries for the directories */
3589 lock_ObtainWrite(&oldDirOp.scp->dirlock);
3590 oldDirOp.lockType = CM_DIRLOCK_WRITE;
3592 lock_ObtainWrite(&oldDscp->rw);
3593 cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
3596 cm_MergeStatus(NULL, oldDscp, &updatedOldDirStatus, &volSync,
3597 userp, reqp, CM_MERGEFLAG_DIROP);
3598 lock_ReleaseWrite(&oldDscp->rw);
3600 if (code == 0 && cm_CheckDirOpForSingleChange(&oldDirOp)) {
3602 diropCode = cm_BPlusDirLookup(&oldDirOp, cOldNamep, &fileFid);
3603 if (diropCode == CM_ERROR_INEXACT_MATCH)
3605 else if (diropCode == EINVAL)
3607 diropCode = cm_DirLookup(&oldDirOp, oldNamep, &fileFid);
3609 if (diropCode == 0) {
3611 diropCode = cm_DirCreateEntry(&oldDirOp, newNamep, &fileFid);
3613 cm_BPlusDirCreateEntry(&oldDirOp, cNewNamep, &fileFid);
3617 if (diropCode == 0) {
3618 diropCode = cm_DirDeleteEntry(&oldDirOp, oldNamep);
3620 cm_BPlusDirDeleteEntry(&oldDirOp, cOldNamep);
3625 cm_EndDirOp(&oldDirOp);
3627 /* and update it for the new one, too, if necessary */
3630 lock_ObtainWrite(&newDirOp.scp->dirlock);
3631 newDirOp.lockType = CM_DIRLOCK_WRITE;
3633 lock_ObtainWrite(&newDscp->rw);
3634 cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
3636 cm_MergeStatus(NULL, newDscp, &updatedNewDirStatus, &volSync,
3637 userp, reqp, CM_MERGEFLAG_DIROP);
3638 lock_ReleaseWrite(&newDscp->rw);
3642 * The following optimization does not work.
3643 * When the file server processed a RXAFS_Rename() request the
3644 * FID of the object being moved between directories is not
3645 * preserved. The client does not know the new FID nor the
3646 * version number of the target. Not only can we not create
3647 * the directory entry in the new directory, but we can't
3648 * preserve the cached data for the file. It must be re-read
3649 * from the file server. - jaltman, 2009/02/20
3652 /* we only make the local change if we successfully made
3653 the change in the old directory AND there was only one
3654 change in the new directory */
3655 if (diropCode == 0 && cm_CheckDirOpForSingleChange(&newDirOp)) {
3656 cm_DirCreateEntry(&newDirOp, newNamep, &fileFid);
3658 cm_BPlusDirCreateEntry(&newDirOp, cNewNamep, &fileFid);
3663 cm_EndDirOp(&newDirOp);
3667 * After the rename the file server has invalidated the callbacks
3668 * on the file that was moved nor do we have a directory reference
3671 lock_ObtainWrite(&oldScp->rw);
3672 cm_DiscardSCache(oldScp);
3673 lock_ReleaseWrite(&oldScp->rw);
3677 cm_ReleaseSCache(oldScp);
3684 /* and return error code */
3688 /* Byte range locks:
3690 The OpenAFS Windows client has to fake byte range locks given no
3691 server side support for such locks. This is implemented as keyed
3692 byte range locks on the cache manager.
3694 Keyed byte range locks:
3696 Each cm_scache_t structure keeps track of a list of keyed locks.
3697 The key for a lock identifies an owner of a set of locks (referred
3698 to as a client). Each key is represented by a value. The set of
3699 key values used within a specific cm_scache_t structure form a
3700 namespace that has a scope of just that cm_scache_t structure. The
3701 same key value can be used with another cm_scache_t structure and
3702 correspond to a completely different client. However it is
3703 advantageous for the SMB or IFS layer to make sure that there is a
3704 1-1 mapping between client and keys over all cm_scache_t objects.
3706 Assume a client C has key Key(C) (although, since the scope of the
3707 key is a cm_scache_t, the key can be Key(C,S), where S is the
3708 cm_scache_t. But assume a 1-1 relation between keys and clients).
3709 A byte range (O,+L) denotes byte addresses (O) through (O+L-1)
3710 inclusive (a.k.a. [O,O+L-1]). The function Key(x) is implemented
3711 through cm_generateKey() function for both SMB and IFS.
3713 The list of locks for a cm_scache_t object S is maintained in
3714 S->fileLocks. The cache manager will set a lock on the AFS file
3715 server in order to assert the locks in S->fileLocks. If only
3716 shared locks are in place for S, then the cache manager will obtain
3717 a LockRead lock, while if there are any exclusive locks, it will
3718 obtain a LockWrite lock. If the exclusive locks are all released
3719 while the shared locks remain, then the cache manager will
3720 downgrade the lock from LockWrite to LockRead. Similarly, if an
3721 exclusive lock is obtained when only shared locks exist, then the
3722 cache manager will try to upgrade the lock from LockRead to
3725 Each lock L owned by client C maintains a key L->key such that
3726 L->key == Key(C), the effective range defined by L->LOffset and
3727 L->LLength such that the range of bytes affected by the lock is
3728 (L->LOffset, +L->LLength), a type maintained in L->LockType which
3729 is either exclusive or shared.
3733 A lock exists iff it is in S->fileLocks for some cm_scache_t
3734 S. Existing locks are in one of the following states: ACTIVE,
3735 WAITLOCK, WAITUNLOCK, LOST, DELETED.
3737 The following sections describe each lock and the associated
3740 1. ACTIVE: A lock L is ACTIVE iff the cache manager has asserted
3741 the lock with the AFS file server. This type of lock can be
3742 exercised by a client to read or write to the locked region (as
3745 1.1 ACTIVE->LOST: When the AFS file server fails to extend a
3746 server lock that was required to assert the lock. Before
3747 marking the lock as lost, the cache manager checks if the file
3748 has changed on the server. If the file has not changed, then
3749 the cache manager will attempt to obtain a new server lock
3750 that is sufficient to assert the client side locks for the
3751 file. If any of these fail, the lock is marked as LOST.
3752 Otherwise, it is left as ACTIVE.
3754 1.2 ACTIVE->DELETED: Lock is released.
3756 2. WAITLOCK: A lock is in a WAITLOCK state if the cache manager
3757 grants the lock but the lock is yet to be asserted with the AFS
3758 file server. Once the file server grants the lock, the state
3759 will transition to an ACTIVE lock.
3761 2.1 WAITLOCK->ACTIVE: The server granted the lock.
3763 2.2 WAITLOCK->DELETED: Lock is abandoned, or timed out during
3766 2.3 WAITLOCK->LOST: One or more locks from this client were
3767 marked as LOST. No further locks will be granted to this
3768 client until all lost locks are removed.
3770 3. WAITUNLOCK: A lock is in a WAITUNLOCK state if the cache manager
3771 receives a request for a lock that conflicts with an existing
3772 ACTIVE or WAITLOCK lock. The lock will be placed in the queue
3773 and will be granted at such time the conflicting locks are
3774 removed, at which point the state will transition to either
3777 3.1 WAITUNLOCK->ACTIVE: The conflicting lock was removed. The
3778 current serverLock is sufficient to assert this lock, or a
3779 sufficient serverLock is obtained.
3781 3.2 WAITUNLOCK->WAITLOCK: The conflicting lock was removed,
3782 however the required serverLock is yet to be asserted with the
3785 3.3 WAITUNLOCK->DELETED: The lock is abandoned, timed out or
3788 3.5 WAITUNLOCK->LOST: One or more locks from this client were
3789 marked as LOST. No further locks will be granted to this
3790 client until all lost locks are removed.
3792 4. LOST: A lock L is LOST if the server lock that was required to
3793 assert the lock could not be obtained or if it could not be
3794 extended, or if other locks by the same client were LOST.
3795 Essentially, once a lock is LOST, the contract between the cache
3796 manager and that specific client is no longer valid.
3798 The cache manager rechecks the server lock once every minute and
3799 extends it as appropriate. If this is not done for 5 minutes,
3800 the AFS file server will release the lock (the 5 minute timeout
3801 is based on current file server code and is fairly arbitrary).
3802 Once released, the lock cannot be re-obtained without verifying
3803 that the contents of the file hasn't been modified since the
3804 time the lock was released. Re-obtaining the lock without
3805 verifying this may lead to data corruption. If the lock can not
3806 be obtained safely, then all active locks for the cm_scache_t
3809 4.1 LOST->DELETED: The lock is released.
3811 5. DELETED: The lock is no longer relevant. Eventually, it will
3812 get removed from the cm_scache_t. In the meantime, it will be
3813 treated as if it does not exist.
3815 5.1 DELETED->not exist: The lock is removed from the
3818 The following are classifications of locks based on their state.
3820 6* A lock L is ACCEPTED if it is ACTIVE or WAITLOCK. These locks
3821 have been accepted by the cache manager, but may or may not have
3822 been granted back to the client.
3824 7* A lock L is QUEUED if it is ACTIVE, WAITLOCK or WAITUNLOCK.
3826 8* A lock L is WAITING if it is WAITLOCK or WAITUNLOCK.
3830 A client C can READ range (Offset,+Length) of a file represented by
3831 cm_scache_t S iff (1):
3833 1. for all _a_ in (Offset,+Length), all of the following is true:
3835 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3836 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3839 1.2 For each LOST lock L in S->fileLocks such that _a_ in
3840 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3843 (When locks are lost on an cm_scache_t, all locks are lost. By
3844 4.2 (below), if there is an exclusive LOST lock, then there
3845 can't be any overlapping ACTIVE locks.)
3847 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
3849 2. for all _a_ in (Offset,+Length), one of the following is true:
3851 2.1 Byte _a_ of S is unowned (as specified in 1.1) AND there
3852 does not exist a LOST lock L such that _a_ in
3853 (L->LOffset,+L->LLength).
3855 2.2 Byte _a_ of S is owned by C under lock L (as specified in
3856 1.2) AND L->LockType is exclusive.
3858 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
3860 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
3863 3.1 If L->LockType is exclusive then there does NOT exist a
3864 ACCEPTED lock M in S->fileLocks such that _a_ in
3865 (M->LOffset,+M->LLength).
3867 (If we count all QUEUED locks then we hit cases such as
3868 cascading waiting locks where the locks later on in the queue
3869 can be granted without compromising file integrity. On the
3870 other hand if only ACCEPTED locks are considered, then locks
3871 that were received earlier may end up waiting for locks that
3872 were received later to be unlocked. The choice of ACCEPTED
3873 locks was made to mimic the Windows byte range lock
3876 3.2 If L->LockType is shared then for each ACCEPTED lock M in
3877 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
3878 M->LockType is shared.
3880 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
3882 4.1 M->key != Key(C)
3884 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
3885 and (M->LOffset,+M->LLength) do not intersect.
3887 (Note: If a client loses a lock, it loses all locks.
3888 Subsequently, it will not be allowed to obtain any more locks
3889 until all existing LOST locks that belong to the client are
3890 released. Once all locks are released by a single client,
3891 there exists no further contract between the client and AFS
3892 about the contents of the file, hence the client can then
3893 proceed to obtain new locks and establish a new contract.
3895 This doesn't quite work as you think it should, because most
3896 applications aren't built to deal with losing locks they
3897 thought they once had. For now, we don't have a good
3898 solution to lost locks.
3900 Also, for consistency reasons, we have to hold off on
3901 granting locks that overlap exclusive LOST locks.)
3903 A client C can only unlock locks L in S->fileLocks which have
3906 The representation and invariants are as follows:
3908 - Each cm_scache_t structure keeps:
3910 - A queue of byte-range locks (cm_scache_t::fileLocks) which
3911 are of type cm_file_lock_t.
3913 - A record of the highest server-side lock that has been
3914 obtained for this object (cm_scache_t::serverLock), which is
3915 one of (-1), LockRead, LockWrite.
3917 - A count of ACCEPTED exclusive and shared locks that are in the
3918 queue (cm_scache_t::sharedLocks and
3919 cm_scache_t::exclusiveLocks)
3921 - Each cm_file_lock_t structure keeps:
3923 - The type of lock (cm_file_lock_t::LockType)
3925 - The key associated with the lock (cm_file_lock_t::key)
3927 - The offset and length of the lock (cm_file_lock_t::LOffset
3928 and cm_file_lock_t::LLength)
3930 - The state of the lock.
3932 - Time of issuance or last successful extension
3934 Semantic invariants:
3936 I1. The number of ACCEPTED locks in S->fileLocks are
3937 (S->sharedLocks + S->exclusiveLocks)
3939 External invariants:
3941 I3. S->serverLock is the lock that we have asserted with the
3942 AFS file server for this cm_scache_t.
3944 I4. S->serverLock == LockRead iff there is at least one ACTIVE
3945 shared lock, but no ACTIVE exclusive locks.
3947 I5. S->serverLock == LockWrite iff there is at least one ACTIVE
3950 I6. If L is a LOST lock, then for each lock M in S->fileLocks,
3951 M->key == L->key IMPLIES M is LOST or DELETED.
3956 #define IS_LOCK_ACTIVE(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == 0)
3958 #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)
3960 #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)
3962 #define IS_LOCK_LOST(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_LOST)
3964 #define IS_LOCK_DELETED(lockp) (((lockp)->flags & CM_FILELOCK_FLAG_DELETED) == CM_FILELOCK_FLAG_DELETED)
3967 #define IS_LOCK_ACCEPTED(lockp) (IS_LOCK_ACTIVE(lockp) || IS_LOCK_WAITLOCK(lockp))
3970 #define IS_LOCK_CLIENTONLY(lockp) ((((lockp)->scp->flags & CM_SCACHEFLAG_RO) == CM_SCACHEFLAG_RO) || (((lockp)->flags & CM_FILELOCK_FLAG_CLIENTONLY) == CM_FILELOCK_FLAG_CLIENTONLY))
3973 #define INTERSECT_RANGE(r1,r2) (((r2).offset+(r2).length) > (r1).offset && ((r1).offset +(r1).length) > (r2).offset)
3976 #define CONTAINS_RANGE(r1,r2) (((r2).offset+(r2).length) <= ((r1).offset+(r1).length) && (r1).offset <= (r2).offset)
3978 #if defined(VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS) && !defined(LOCK_TESTING)
3979 #define SCP_SUPPORTS_BRLOCKS(scp) ((scp)->cbServerp && ((scp)->cbServerp->capabilities & VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS))
3981 #define SCP_SUPPORTS_BRLOCKS(scp) (1)
3984 #define SERVERLOCKS_ENABLED(scp) (!((scp)->flags & CM_SCACHEFLAG_RO) && cm_enableServerLocks && SCP_SUPPORTS_BRLOCKS(scp))
3986 #if defined(VICED_CAPABILITY_WRITELOCKACL)
3987 #define SCP_SUPPORTS_WRITELOCKACL(scp) ((scp)->cbServerp && ((scp->cbServerp->capabilities & VICED_CAPABILITY_WRITELOCKACL)))
3989 #define SCP_SUPPORTS_WRITELOCKACL(scp) (0)
3991 /* This should really be defined in any build that this code is being
3993 #error VICED_CAPABILITY_WRITELOCKACL not defined.
3996 static void cm_LockRangeSubtract(cm_range_t * pos, const cm_range_t * neg)
3998 afs_int64 int_begin;
4001 int_begin = MAX(pos->offset, neg->offset);
4002 int_end = MIN(pos->offset+pos->length, neg->offset+neg->length);
4004 if (int_begin < int_end) {
4005 if (int_begin == pos->offset) {
4006 pos->length = pos->offset + pos->length - int_end;
4007 pos->offset = int_end;
4008 } else if (int_end == pos->offset + pos->length) {
4009 pos->length = int_begin - pos->offset;
4012 /* We only subtract ranges if the resulting range is
4013 contiguous. If we try to support non-contigous ranges, we
4014 aren't actually improving performance. */
4018 /* Called with scp->rw held. Returns 0 if all is clear to read the
4019 specified range by the client identified by key.
4021 long cm_LockCheckRead(cm_scache_t *scp,
4022 LARGE_INTEGER LOffset,
4023 LARGE_INTEGER LLength,
4026 #ifndef ADVISORY_LOCKS
4028 cm_file_lock_t *fileLock;
4032 int substract_ranges = FALSE;
4034 range.offset = LOffset.QuadPart;
4035 range.length = LLength.QuadPart;
4039 1. for all _a_ in (Offset,+Length), all of the following is true:
4041 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
4042 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
4045 1.2 For each LOST lock L in S->fileLocks such that _a_ in
4046 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
4051 lock_ObtainRead(&cm_scacheLock);
4053 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
4055 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4057 if (INTERSECT_RANGE(range, fileLock->range)) {
4058 if (IS_LOCK_ACTIVE(fileLock)) {
4059 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4061 /* If there is an active lock for this client, it
4062 is safe to substract ranges.*/
4063 cm_LockRangeSubtract(&range, &fileLock->range);
4064 substract_ranges = TRUE;
4066 if (fileLock->lockType != LockRead) {
4067 code = CM_ERROR_LOCK_CONFLICT;
4071 /* even if the entire range is locked for reading,
4072 we still can't grant the lock at this point
4073 because the client may have lost locks. That
4074 is, unless we have already seen an active lock
4075 belonging to the client, in which case there
4076 can't be any lost locks for this client. */
4077 if (substract_ranges)
4078 cm_LockRangeSubtract(&range, &fileLock->range);
4080 } else if (IS_LOCK_LOST(fileLock) &&
4081 (cm_KeyEquals(&fileLock->key, &key, 0) || fileLock->lockType == LockWrite)) {
4082 code = CM_ERROR_BADFD;
4088 lock_ReleaseRead(&cm_scacheLock);
4090 osi_Log4(afsd_logp, "cm_LockCheckRead scp 0x%x offset %d length %d code 0x%x",
4091 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
4102 /* Called with scp->rw held. Returns 0 if all is clear to write the
4103 specified range by the client identified by key.
4105 long cm_LockCheckWrite(cm_scache_t *scp,
4106 LARGE_INTEGER LOffset,
4107 LARGE_INTEGER LLength,
4110 #ifndef ADVISORY_LOCKS
4112 cm_file_lock_t *fileLock;
4117 range.offset = LOffset.QuadPart;
4118 range.length = LLength.QuadPart;
4121 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
4123 2. for all _a_ in (Offset,+Length), one of the following is true:
4125 2.1 Byte _a_ of S is unowned AND there does not exist a LOST
4126 lock L such that _a_ in (L->LOffset,+L->LLength).
4128 2.2 Byte _a_ of S is owned by C under lock L AND L->LockType is
4132 lock_ObtainRead(&cm_scacheLock);
4134 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
4136 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4138 if (INTERSECT_RANGE(range, fileLock->range)) {
4139 if (IS_LOCK_ACTIVE(fileLock)) {
4140 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4141 if (fileLock->lockType == LockWrite) {
4143 /* if there is an active lock for this client, it
4144 is safe to substract ranges */
4145 cm_LockRangeSubtract(&range, &fileLock->range);
4147 code = CM_ERROR_LOCK_CONFLICT;
4151 code = CM_ERROR_LOCK_CONFLICT;
4154 } else if (IS_LOCK_LOST(fileLock)) {
4155 code = CM_ERROR_BADFD;
4161 lock_ReleaseRead(&cm_scacheLock);
4163 osi_Log4(afsd_logp, "cm_LockCheckWrite scp 0x%x offset %d length %d code 0x%x",
4164 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
4175 /* Called with cm_scacheLock write locked */
4176 static cm_file_lock_t * cm_GetFileLock(void) {
4179 l = (cm_file_lock_t *) cm_freeFileLocks;
4181 osi_QRemove(&cm_freeFileLocks, &l->q);
4183 l = malloc(sizeof(cm_file_lock_t));
4184 osi_assertx(l, "null cm_file_lock_t");
4187 memset(l, 0, sizeof(cm_file_lock_t));
4192 /* Called with cm_scacheLock write locked */
4193 static void cm_PutFileLock(cm_file_lock_t *l) {
4194 osi_QAdd(&cm_freeFileLocks, &l->q);
4197 /* called with scp->rw held. May release it during processing, but
4198 leaves it held on exit. */
4199 long cm_IntSetLock(cm_scache_t * scp, cm_user_t * userp, int lockType,
4205 struct rx_connection * rxconnp;
4207 afs_uint32 reqflags = reqp->flags;
4209 memset(&volSync, 0, sizeof(volSync));
4211 tfid.Volume = scp->fid.volume;
4212 tfid.Vnode = scp->fid.vnode;
4213 tfid.Unique = scp->fid.unique;
4216 osi_Log2(afsd_logp, "CALL SetLock scp 0x%p for lock %d", scp, lockType);
4218 reqp->flags |= CM_REQ_NORETRY;
4219 lock_ReleaseWrite(&scp->rw);
4222 code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4226 rxconnp = cm_GetRxConn(connp);
4227 code = RXAFS_SetLock(rxconnp, &tfid, lockType,
4229 rx_PutConnection(rxconnp);
4231 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
4234 code = cm_MapRPCError(code, reqp);
4236 osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
4238 osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
4241 lock_ObtainWrite(&scp->rw);
4242 reqp->flags = reqflags;
4246 /* called with scp->rw held. Releases it during processing */
4247 long cm_IntReleaseLock(cm_scache_t * scp, cm_user_t * userp,
4253 struct rx_connection * rxconnp;
4256 memset(&volSync, 0, sizeof(volSync));
4258 tfid.Volume = scp->fid.volume;
4259 tfid.Vnode = scp->fid.vnode;
4260 tfid.Unique = scp->fid.unique;
4263 lock_ReleaseWrite(&scp->rw);
4265 osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
4268 code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4272 rxconnp = cm_GetRxConn(connp);
4273 code = RXAFS_ReleaseLock(rxconnp, &tfid, &volSync);
4274 rx_PutConnection(rxconnp);
4276 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
4278 code = cm_MapRPCError(code, reqp);
4281 "CALL ReleaseLock FAILURE, code 0x%x", code);
4284 "CALL ReleaseLock SUCCESS");
4286 lock_ObtainWrite(&scp->rw);
4291 /* called with scp->rw held. May release it during processing, but
4292 will exit with lock held.
4296 - 0 if the user has permission to get the specified lock for the scp
4298 - CM_ERROR_NOACCESS if not
4300 Any other error from cm_SyncOp will be sent down untranslated.
4302 If CM_ERROR_NOACCESS is returned and lock_type is LockRead, then
4303 phas_insert (if non-NULL) will receive a boolean value indicating
4304 whether the user has INSERT permission or not.
4306 long cm_LockCheckPerms(cm_scache_t * scp,
4313 long code = 0, code2 = 0;
4315 /* lock permissions are slightly tricky because of the 'i' bit.
4316 If the user has PRSFS_LOCK, she can read-lock the file. If the
4317 user has PRSFS_WRITE, she can write-lock the file. However, if
4318 the user has PRSFS_INSERT, then she can write-lock new files,
4319 but not old ones. Since we don't have information about
4320 whether a file is new or not, we assume that if the user owns
4321 the scp, then she has the permissions that are granted by
4324 osi_Log3(afsd_logp, "cm_LockCheckPerms for scp[0x%p] type[%d] user[0x%p]",
4325 scp, lock_type, userp);
4327 if (lock_type == LockRead)
4328 rights |= PRSFS_LOCK;
4329 else if (lock_type == LockWrite)
4330 rights |= PRSFS_WRITE | PRSFS_LOCK;
4333 osi_assertx(FALSE, "invalid lock type");
4338 *phas_insert = FALSE;
4340 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
4341 CM_SCACHESYNC_GETSTATUS |
4342 CM_SCACHESYNC_NEEDCALLBACK);
4344 if (phas_insert && scp->creator == userp) {
4346 /* If this file was created by the user, then we check for
4347 PRSFS_INSERT. If the file server is recent enough, then
4348 this should be sufficient for her to get a write-lock (but
4349 not necessarily a read-lock). VICED_CAPABILITY_WRITELOCKACL
4350 indicates whether a file server supports getting write
4351 locks when the user only has PRSFS_INSERT.
4353 If the file was not created by the user we skip the check
4354 because the INSERT bit will not apply to this user even
4358 code2 = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
4359 CM_SCACHESYNC_GETSTATUS |
4360 CM_SCACHESYNC_NEEDCALLBACK);
4362 if (code2 == CM_ERROR_NOACCESS) {
4363 osi_Log0(afsd_logp, "cm_LockCheckPerms user has no INSERT bits");
4365 *phas_insert = TRUE;
4366 osi_Log0(afsd_logp, "cm_LockCheckPerms user has INSERT bits");
4370 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
4372 osi_Log1(afsd_logp, "cm_LockCheckPerms returning code %d", code);
4377 /* called with scp->rw held */
4378 long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
4379 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
4381 int allowWait, cm_user_t *userp, cm_req_t *reqp,
4382 cm_file_lock_t **lockpp)
4385 int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
4386 cm_file_lock_t *fileLock;
4389 int wait_unlock = FALSE;
4390 int force_client_lock = FALSE;
4392 osi_Log4(afsd_logp, "cm_Lock scp 0x%x type 0x%x offset %d length %d",
4393 scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
4394 osi_Log4(afsd_logp, "... allowWait %d key <0x%x, 0x%x, 0x%x>", allowWait,
4395 key.process_id, key.session_id, key.file_id);
4398 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
4400 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
4403 3.1 If L->LockType is exclusive then there does NOT exist a
4404 ACCEPTED lock M in S->fileLocks such that _a_ in
4405 (M->LOffset,+M->LLength).
4407 3.2 If L->LockType is shared then for each ACCEPTED lock M in
4408 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
4409 M->LockType is shared.
4411 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
4413 4.1 M->key != Key(C)
4415 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
4416 and (M->LOffset,+M->LLength) do not intersect.
4419 range.offset = LOffset.QuadPart;
4420 range.length = LLength.QuadPart;
4422 lock_ObtainRead(&cm_scacheLock);
4424 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4426 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4428 if (IS_LOCK_LOST(fileLock)) {
4429 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4430 code = CM_ERROR_BADFD;
4432 } else if (fileLock->lockType == LockWrite && INTERSECT_RANGE(range, fileLock->range)) {
4433 code = CM_ERROR_WOULDBLOCK;
4439 /* we don't need to check for deleted locks here since deleted
4440 locks are dequeued from scp->fileLocks */
4441 if (IS_LOCK_ACCEPTED(fileLock) &&
4442 INTERSECT_RANGE(range, fileLock->range)) {
4444 if ((sLockType & LOCKING_ANDX_SHARED_LOCK) == 0 ||
4445 fileLock->lockType != LockRead) {
4447 code = CM_ERROR_WOULDBLOCK;
4453 lock_ReleaseRead(&cm_scacheLock);
4455 if (code == 0 && SERVERLOCKS_ENABLED(scp)) {
4456 if (Which == scp->serverLock ||
4457 (Which == LockRead && scp->serverLock == LockWrite)) {
4461 /* we already have the lock we need */
4462 osi_Log3(afsd_logp, " we already have the correct lock. exclusives[%d], shared[%d], serverLock[%d]",
4463 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4465 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4467 /* special case: if we don't have permission to read-lock
4468 the file, then we force a clientside lock. This is to
4469 compensate for applications that obtain a read-lock for
4470 reading files off of directories that don't grant
4471 read-locks to the user. */
4472 if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4474 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4475 osi_Log0(afsd_logp, " User has no read-lock perms, but has INSERT perms.");
4478 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4479 force_client_lock = TRUE;
4483 } else if ((scp->exclusiveLocks > 0) ||
4484 (scp->sharedLocks > 0 && scp->serverLock != LockRead)) {
4487 /* We are already waiting for some other lock. We should
4488 wait for the daemon to catch up instead of generating a
4489 flood of SetLock calls. */
4490 osi_Log3(afsd_logp, " already waiting for other lock. exclusives[%d], shared[%d], serverLock[%d]",
4491 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4493 /* see if we have permission to create the lock in the
4495 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4497 code = CM_ERROR_WOULDBLOCK;
4498 else if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4500 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4502 " User has no read-lock perms, but has INSERT perms.");
4503 code = CM_ERROR_WOULDBLOCK;
4506 " User has no read-lock perms. Forcing client-side lock");
4507 force_client_lock = TRUE;
4511 /* leave any other codes as-is */
4515 int check_data_version = FALSE;
4518 /* first check if we have permission to elevate or obtain
4520 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4522 if (code == CM_ERROR_NOACCESS && Which == LockRead &&
4523 (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp))) {
4524 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4525 force_client_lock = TRUE;
4530 /* has_insert => (Which == LockRead, code == CM_ERROR_NOACCESS) */
4532 if (scp->serverLock == LockRead && Which == LockWrite) {
4534 /* We want to escalate the lock to a LockWrite.
4535 * Unfortunately that's not really possible without
4536 * letting go of the current lock. But for now we do
4540 " attempting to UPGRADE from LockRead to LockWrite.");
4542 " dataVersion on scp: %I64d", scp->dataVersion);
4544 /* we assume at this point (because scp->serverLock
4545 was valid) that we had a valid server lock. */
4546 scp->lockDataVersion = scp->dataVersion;
4547 check_data_version = TRUE;
4549 code = cm_IntReleaseLock(scp, userp, reqp);
4552 /* We couldn't release the lock */
4555 scp->serverLock = -1;
4559 /* We need to obtain a server lock of type Which in order
4560 * to assert this file lock */
4561 #ifndef AGGRESSIVE_LOCKS
4564 newLock = LockWrite;
4567 code = cm_IntSetLock(scp, userp, newLock, reqp);
4569 #ifdef AGGRESSIVE_LOCKS
4570 if ((code == CM_ERROR_WOULDBLOCK ||
4571 code == CM_ERROR_NOACCESS) && newLock != Which) {
4572 /* we wanted LockRead. We tried LockWrite. Now try
4577 osi_assertx(newLock == LockRead, "lock type not read");
4579 code = cm_IntSetLock(scp, userp, newLock, reqp);
4583 if (code == CM_ERROR_NOACCESS) {
4584 if (Which == LockRead) {
4585 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4587 /* We requested a read-lock, but we have permission to
4588 * get a write-lock. Try that */
4590 tcode = cm_LockCheckPerms(scp, LockWrite, userp, reqp, NULL);
4593 newLock = LockWrite;
4595 osi_Log0(afsd_logp, " User has 'i' perms and the request was for a LockRead. Trying to get a LockWrite instead");
4597 code = cm_IntSetLock(scp, userp, newLock, reqp);
4600 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4601 force_client_lock = TRUE;
4603 } else if (Which == LockWrite &&
4604 scp->creator == userp && !SCP_SUPPORTS_WRITELOCKACL(scp)) {
4607 /* Special case: if the lock request was for a
4608 * LockWrite and the user owns the file and we weren't
4609 * allowed to obtain the serverlock, we either lost a
4610 * race (the permissions changed from under us), or we
4611 * have 'i' bits, but we aren't allowed to lock the
4614 /* check if we lost a race... */
4615 tcode = cm_LockCheckPerms(scp, Which, userp, reqp, NULL);
4618 osi_Log0(afsd_logp, " User has 'i' perms but can't obtain write locks. Using client-side locks.");
4619 force_client_lock = TRUE;
4624 if (code == 0 && check_data_version &&
4625 scp->dataVersion != scp->lockDataVersion) {
4626 /* We lost a race. Although we successfully obtained
4627 * a lock, someone modified the file in between. The
4628 * locks have all been technically lost. */
4631 " Data version mismatch while upgrading lock.");
4633 " Data versions before=%I64d, after=%I64d",
4634 scp->lockDataVersion,
4637 " Releasing stale lock for scp 0x%x", scp);
4639 code = cm_IntReleaseLock(scp, userp, reqp);
4641 scp->serverLock = -1;
4643 code = CM_ERROR_INVAL;
4644 } else if (code == 0) {
4645 scp->serverLock = newLock;
4646 scp->lockDataVersion = scp->dataVersion;
4650 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4651 scp->serverLock == -1) {
4652 /* Oops. We lost the lock. */
4653 cm_LockMarkSCacheLost(scp);
4656 } else if (code == 0) { /* server locks not enabled */
4658 " Skipping server lock for scp");
4663 if (code != 0 && !force_client_lock) {
4664 /* Special case error translations
4666 Applications don't expect certain errors from a
4667 LockFile/UnlockFile call. We need to translate some error
4668 code to codes that apps expect and handle. */
4670 /* We shouldn't actually need to handle this case since we
4671 simulate locks for RO scps anyway. */
4672 if (code == CM_ERROR_READONLY) {
4673 osi_Log0(afsd_logp, " Reinterpreting CM_ERROR_READONLY as CM_ERROR_NOACCESS");
4674 code = CM_ERROR_NOACCESS;
4678 if (code == 0 || (code == CM_ERROR_WOULDBLOCK && allowWait) ||
4679 force_client_lock) {
4681 /* clear the error if we are forcing a client lock, so we
4682 don't get confused later. */
4683 if (force_client_lock && code != CM_ERROR_WOULDBLOCK)
4686 lock_ObtainWrite(&cm_scacheLock);
4687 fileLock = cm_GetFileLock();
4688 lock_ReleaseWrite(&cm_scacheLock);
4690 fileLock->fid = scp->fid;
4692 fileLock->key = key;
4693 fileLock->lockType = Which;
4695 fileLock->userp = userp;
4696 fileLock->range = range;
4697 fileLock->flags = (code == 0 ? 0 :
4699 CM_FILELOCK_FLAG_WAITUNLOCK :
4700 CM_FILELOCK_FLAG_WAITLOCK));
4702 if (force_client_lock || !SERVERLOCKS_ENABLED(scp))
4703 fileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
4705 fileLock->lastUpdate = (code == 0 && !force_client_lock) ? time(NULL) : 0;
4707 lock_ObtainWrite(&cm_scacheLock);
4708 osi_QAddT(&scp->fileLocksH, &scp->fileLocksT, &fileLock->fileq);
4709 cm_HoldSCacheNoLock(scp);
4710 fileLock->scp = scp;
4711 osi_QAdd(&cm_allFileLocks, &fileLock->q);
4712 lock_ReleaseWrite(&cm_scacheLock);
4718 if (IS_LOCK_CLIENTONLY(fileLock)) {
4720 } else if (IS_LOCK_ACCEPTED(fileLock)) {
4721 if (Which == LockRead)
4724 scp->exclusiveLocks++;
4728 "cm_Lock Lock added 0x%p flags 0x%x to scp [0x%p]",
4729 fileLock, fileLock->flags, scp);
4731 " exclusives[%d] shared[%d] client[%d] serverLock[%d]",
4732 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4733 (int)(signed char) scp->serverLock);
4736 "cm_Lock Rejecting lock (code = 0x%x)", code);
4739 /* Convert from would block to lock not granted */
4740 if (code == CM_ERROR_WOULDBLOCK)
4741 code = CM_ERROR_LOCK_NOT_GRANTED;
4746 /* Called with scp->rw held */
4747 long cm_UnlockByKey(cm_scache_t * scp,
4754 cm_file_lock_t *fileLock;
4755 osi_queue_t *q, *qn;
4758 osi_Log4(afsd_logp, "cm_UnlockByKey scp 0x%p key <0x%x,0x%x,0x%x",
4759 scp, key.process_id, key.session_id, key.file_id);
4760 osi_Log1(afsd_logp, " flags=0x%x", flags);
4762 lock_ObtainWrite(&cm_scacheLock);
4764 for (q = scp->fileLocksH; q; q = qn) {
4767 fileLock = (cm_file_lock_t *)
4768 ((char *) q - offsetof(cm_file_lock_t, fileq));
4771 osi_Log4(afsd_logp, " Checking lock[0x%x] range[%d,+%d] type[%d]",
4773 (unsigned long) fileLock->range.offset,
4774 (unsigned long) fileLock->range.length,
4775 fileLock->lockType);
4776 osi_Log4(afsd_logp, " key<0x%x, 0x%x, 0x%x> flags[0x%x]",
4777 fileLock->key.process_id, fileLock->key.session_id, fileLock->key.file_id,
4780 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
4781 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
4782 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4784 fileLock->fid.volume,
4785 fileLock->fid.vnode,
4786 fileLock->fid.unique);
4787 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4788 fileLock->scp->fid.cell,
4789 fileLock->scp->fid.volume,
4790 fileLock->scp->fid.vnode,
4791 fileLock->scp->fid.unique);
4792 osi_assertx(FALSE, "invalid fid value");
4796 if (!IS_LOCK_DELETED(fileLock) &&
4797 cm_KeyEquals(&fileLock->key, &key, flags)) {
4798 osi_Log3(afsd_logp, "...Unlock range [%d,+%d] type %d",
4799 fileLock->range.offset,
4800 fileLock->range.length,
4801 fileLock->lockType);
4803 if (scp->fileLocksT == q)
4804 scp->fileLocksT = osi_QPrev(q);
4805 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
4807 if (IS_LOCK_CLIENTONLY(fileLock)) {
4809 } else if (IS_LOCK_ACCEPTED(fileLock)) {
4810 if (fileLock->lockType == LockRead)
4813 scp->exclusiveLocks--;
4816 fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
4818 cm_ReleaseUser(fileLock->userp);
4819 cm_ReleaseSCacheNoLock(scp);
4821 fileLock->userp = NULL;
4822 fileLock->scp = NULL;
4828 lock_ReleaseWrite(&cm_scacheLock);
4830 if (n_unlocks == 0) {
4831 osi_Log0(afsd_logp, "cm_UnlockByKey no locks found");
4832 osi_Log3(afsd_logp, " Leaving scp with exclusives[%d], shared[%d], serverLock[%d]",
4833 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4838 osi_Log1(afsd_logp, "cm_UnlockByKey done with %d locks", n_unlocks);
4840 osi_assertx(scp->sharedLocks >= 0, "scp->sharedLocks < 0");
4841 osi_assertx(scp->exclusiveLocks >= 0, "scp->exclusiveLocks < 0");
4842 osi_assertx(scp->clientLocks >= 0, "scp->clientLocks < 0");
4844 if (!SERVERLOCKS_ENABLED(scp)) {
4845 osi_Log0(afsd_logp, " Skipping server lock for scp");
4849 /* Ideally we would go through the rest of the locks to determine
4850 * if one or more locks that were formerly in WAITUNLOCK can now
4851 * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
4852 * scp->sharedLocks accordingly. However, the retrying of locks
4853 * in that manner is done cm_RetryLock() manually.
4856 if (scp->serverLock == LockWrite &&
4857 scp->exclusiveLocks == 0 &&
4858 scp->sharedLocks > 0) {
4859 /* The serverLock should be downgraded to LockRead */
4860 osi_Log0(afsd_logp, " DOWNGRADE lock from LockWrite to LockRead");
4862 /* Make sure there are no dirty buffers left. */
4863 code = cm_FSync(scp, userp, reqp, TRUE);
4865 /* since scp->serverLock looked sane, we are going to assume
4866 that we have a valid server lock. */
4867 scp->lockDataVersion = scp->dataVersion;
4868 osi_Log1(afsd_logp, " dataVersion on scp = %I64d", scp->dataVersion);
4870 code = cm_IntReleaseLock(scp, userp, reqp);
4873 /* so we couldn't release it. Just let the lock be for now */
4877 scp->serverLock = -1;
4880 code = cm_IntSetLock(scp, userp, LockRead, reqp);
4882 if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
4883 scp->serverLock = LockRead;
4884 } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
4885 /* We lost a race condition. Although we have a valid
4886 lock on the file, the data has changed and essentially
4887 we have lost the lock we had during the transition. */
4889 osi_Log0(afsd_logp, "Data version mismatch during lock downgrade");
4890 osi_Log2(afsd_logp, " Data versions before=%I64d, after=%I64d",
4891 scp->lockDataVersion,
4894 code = cm_IntReleaseLock(scp, userp, reqp);
4896 code = CM_ERROR_INVAL;
4897 scp->serverLock = -1;
4901 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4902 (scp->serverLock == -1)) {
4904 cm_LockMarkSCacheLost(scp);
4907 /* failure here has no bearing on the return value of
4911 } else if (scp->serverLock != (-1) &&
4912 scp->exclusiveLocks == 0 &&
4913 scp->sharedLocks == 0) {
4914 /* The serverLock should be released entirely */
4916 if (scp->serverLock == LockWrite) {
4917 /* Make sure there are no dirty buffers left. */
4918 code = cm_FSync(scp, userp, reqp, TRUE);
4921 code = cm_IntReleaseLock(scp, userp, reqp);
4924 scp->serverLock = (-1);
4929 osi_Log1(afsd_logp, "cm_UnlockByKey code 0x%x", code);
4930 osi_Log4(afsd_logp, " Leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
4931 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4932 (int)(signed char) scp->serverLock);
4937 long cm_Unlock(cm_scache_t *scp,
4938 unsigned char sLockType,
4939 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
4946 int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
4947 cm_file_lock_t *fileLock;
4949 int release_userp = FALSE;
4950 int exact_match = !(flags & CM_UNLOCK_FLAG_MATCH_RANGE);
4952 LARGE_INTEGER RangeEnd;
4954 osi_Log4(afsd_logp, "cm_Unlock scp 0x%p type 0x%x offset %d length %d",
4955 scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
4956 osi_Log4(afsd_logp, "... key <0x%x,0x%x,0x%x> flags 0x%x",
4957 key.process_id, key.session_id, key.file_id, flags);
4960 RangeEnd.QuadPart = LOffset.QuadPart + LLength.QuadPart;
4963 lock_ObtainRead(&cm_scacheLock);
4965 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4966 fileLock = (cm_file_lock_t *)
4967 ((char *) q - offsetof(cm_file_lock_t, fileq));
4970 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
4971 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
4972 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4974 fileLock->fid.volume,
4975 fileLock->fid.vnode,
4976 fileLock->fid.unique);
4977 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4978 fileLock->scp->fid.cell,
4979 fileLock->scp->fid.volume,
4980 fileLock->scp->fid.vnode,
4981 fileLock->scp->fid.unique);
4982 osi_assertx(FALSE, "invalid fid value");
4986 if (!IS_LOCK_DELETED(fileLock) &&
4987 cm_KeyEquals(&fileLock->key, &key, 0) &&
4988 fileLock->range.offset == LOffset.QuadPart &&
4989 fileLock->range.length == LLength.QuadPart) {
4995 if (!IS_LOCK_DELETED(fileLock) &&
4996 cm_KeyEquals(&fileLock->key, &key, 0) &&
4997 fileLock->range.offset >= LOffset.QuadPart &&
4998 fileLock->range.offset < RangeEnd.QuadPart &&
4999 (fileLock->range.offset + fileLock->range.length) <= RangeEnd.QuadPart) {
5007 lock_ReleaseRead(&cm_scacheLock);
5009 if (lock_found && !exact_match) {
5013 osi_Log0(afsd_logp, "cm_Unlock lock not found; failure");
5015 /* The lock didn't exist anyway. *shrug* */
5016 return CM_ERROR_RANGE_NOT_LOCKED;
5020 /* discard lock record */
5021 lock_ConvertRToW(&cm_scacheLock);
5022 if (scp->fileLocksT == q)
5023 scp->fileLocksT = osi_QPrev(q);
5024 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
5027 * Don't delete it here; let the daemon delete it, to simplify
5028 * the daemon's traversal of the list.
5031 if (IS_LOCK_CLIENTONLY(fileLock)) {
5033 } else if (IS_LOCK_ACCEPTED(fileLock)) {
5034 if (fileLock->lockType == LockRead)
5037 scp->exclusiveLocks--;
5040 fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5041 if (userp != NULL) {
5042 cm_ReleaseUser(fileLock->userp);
5044 userp = fileLock->userp;
5045 release_userp = TRUE;
5047 fileLock->userp = NULL;
5048 cm_ReleaseSCacheNoLock(scp);
5049 fileLock->scp = NULL;
5050 lock_ReleaseWrite(&cm_scacheLock);
5052 if (!SERVERLOCKS_ENABLED(scp)) {
5053 osi_Log0(afsd_logp, " Skipping server locks for scp");
5057 /* Ideally we would go through the rest of the locks to determine
5058 * if one or more locks that were formerly in WAITUNLOCK can now
5059 * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
5060 * scp->sharedLocks accordingly. However, the retrying of locks
5061 * in that manner is done cm_RetryLock() manually.
5064 if (scp->serverLock == LockWrite &&
5065 scp->exclusiveLocks == 0 &&
5066 scp->sharedLocks > 0) {
5068 /* The serverLock should be downgraded to LockRead */
5069 osi_Log0(afsd_logp, " DOWNGRADE lock from LockWrite to LockRead");
5071 /* Make sure there are no dirty buffers left. */
5072 code = cm_FSync(scp, userp, reqp, TRUE);
5074 /* Since we already had a lock, we assume that there is a
5075 valid server lock. */
5076 scp->lockDataVersion = scp->dataVersion;
5077 osi_Log1(afsd_logp, " dataVersion on scp is %I64d", scp->dataVersion);
5079 /* before we downgrade, make sure that we have enough
5080 permissions to get the read lock. */
5081 code = cm_LockCheckPerms(scp, LockRead, userp, reqp, NULL);
5084 osi_Log0(afsd_logp, " SKIPPING downgrade because user doesn't have perms to get downgraded lock");
5090 code = cm_IntReleaseLock(scp, userp, reqp);
5093 /* so we couldn't release it. Just let the lock be for now */
5097 scp->serverLock = -1;
5100 code = cm_IntSetLock(scp, userp, LockRead, reqp);
5102 if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
5103 scp->serverLock = LockRead;
5104 } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
5105 /* Lost a race. We obtained a new lock, but that is
5106 meaningless since someone modified the file
5110 "Data version mismatch while downgrading lock");
5112 " Data versions before=%I64d, after=%I64d",
5113 scp->lockDataVersion,
5116 code = cm_IntReleaseLock(scp, userp, reqp);
5118 scp->serverLock = -1;
5119 code = CM_ERROR_INVAL;
5123 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
5124 (scp->serverLock == -1)) {
5126 cm_LockMarkSCacheLost(scp);
5129 /* failure here has no bearing on the return value of
5133 } else if (scp->serverLock != (-1) &&
5134 scp->exclusiveLocks == 0 &&
5135 scp->sharedLocks == 0) {
5136 /* The serverLock should be released entirely */
5138 if (scp->serverLock == LockWrite) {
5139 /* Make sure there are no dirty buffers left. */
5140 code = cm_FSync(scp, userp, reqp, TRUE);
5143 code = cm_IntReleaseLock(scp, userp, reqp);
5146 scp->serverLock = (-1);
5150 if (release_userp) {
5151 cm_ReleaseUser(userp);
5152 release_userp = FALSE;
5156 goto try_again; /* might be more than one lock in the range */
5160 osi_Log1(afsd_logp, "cm_Unlock code 0x%x", code);
5161 osi_Log4(afsd_logp, " leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
5162 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
5163 (int)(signed char) scp->serverLock);
5168 /* called with scp->rw held */
5169 void cm_LockMarkSCacheLost(cm_scache_t * scp)
5171 cm_file_lock_t *fileLock;
5174 osi_Log1(afsd_logp, "cm_LockMarkSCacheLost scp 0x%x", scp);
5176 /* cm_scacheLock needed because we are modifying fileLock->flags */
5177 lock_ObtainWrite(&cm_scacheLock);
5179 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5181 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
5183 if (IS_LOCK_ACTIVE(fileLock) &&
5184 !IS_LOCK_CLIENTONLY(fileLock)) {
5185 if (fileLock->lockType == LockRead)
5188 scp->exclusiveLocks--;
5190 fileLock->flags |= CM_FILELOCK_FLAG_LOST;
5194 scp->serverLock = -1;
5195 scp->lockDataVersion = CM_SCACHE_VERSION_BAD;
5196 lock_ReleaseWrite(&cm_scacheLock);
5199 /* Called with no relevant locks held */
5200 void cm_CheckLocks()
5202 osi_queue_t *q, *nq;
5203 cm_file_lock_t *fileLock;
5209 struct rx_connection * rxconnp;
5212 memset(&volSync, 0, sizeof(volSync));
5216 lock_ObtainWrite(&cm_scacheLock);
5218 cm_lockRefreshCycle++;
5220 osi_Log1(afsd_logp, "cm_CheckLocks starting lock check cycle %d", cm_lockRefreshCycle);
5222 for (q = cm_allFileLocks; q; q = nq) {
5223 fileLock = (cm_file_lock_t *) q;
5227 if (IS_LOCK_DELETED(fileLock)) {
5229 osi_QRemove(&cm_allFileLocks, q);
5230 cm_PutFileLock(fileLock);
5232 } else if (IS_LOCK_ACTIVE(fileLock) && !IS_LOCK_CLIENTONLY(fileLock)) {
5234 /* Server locks must have been enabled for us to have
5235 received an active non-client-only lock. */
5236 osi_assertx(cm_enableServerLocks, "!cm_enableServerLocks");
5238 scp = fileLock->scp;
5239 osi_assertx(scp != NULL, "null cm_scache_t");
5241 cm_HoldSCacheNoLock(scp);
5244 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5245 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5246 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5248 fileLock->fid.volume,
5249 fileLock->fid.vnode,
5250 fileLock->fid.unique);
5251 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5252 fileLock->scp->fid.cell,
5253 fileLock->scp->fid.volume,
5254 fileLock->scp->fid.vnode,
5255 fileLock->scp->fid.unique);
5256 osi_assertx(FALSE, "invalid fid");
5259 /* Server locks are extended once per scp per refresh
5261 if (scp->lastRefreshCycle != cm_lockRefreshCycle) {
5263 int scp_done = FALSE;
5265 osi_Log1(afsd_logp, "cm_CheckLocks Updating scp 0x%x", scp);
5267 lock_ReleaseWrite(&cm_scacheLock);
5268 lock_ObtainWrite(&scp->rw);
5270 /* did the lock change while we weren't holding the lock? */
5271 if (!IS_LOCK_ACTIVE(fileLock))
5272 goto post_syncopdone;
5274 code = cm_SyncOp(scp, NULL, fileLock->userp, &req, 0,
5275 CM_SCACHESYNC_NEEDCALLBACK
5276 | CM_SCACHESYNC_GETSTATUS
5277 | CM_SCACHESYNC_LOCK);
5281 "cm_CheckLocks SyncOp failure code 0x%x", code);
5282 goto post_syncopdone;
5285 /* cm_SyncOp releases scp->rw during which the lock
5286 may get released. */
5287 if (!IS_LOCK_ACTIVE(fileLock))
5288 goto pre_syncopdone;
5290 if (scp->serverLock != -1 && !(scp->flags & CM_SCACHEFLAG_DELETED)) {
5294 tfid.Volume = scp->fid.volume;
5295 tfid.Vnode = scp->fid.vnode;
5296 tfid.Unique = scp->fid.unique;
5298 userp = fileLock->userp;
5300 osi_Log3(afsd_logp, "CALL ExtendLock lock 0x%p for scp=0x%p with lock %d",
5303 (int) scp->serverLock);
5305 lock_ReleaseWrite(&scp->rw);
5308 code = cm_ConnFromFID(&cfid, userp,
5313 rxconnp = cm_GetRxConn(connp);
5314 code = RXAFS_ExtendLock(rxconnp, &tfid,
5316 rx_PutConnection(rxconnp);
5318 osi_Log1(afsd_logp, " ExtendLock returns %d", code);
5320 } while (cm_Analyze(connp, userp, &req,
5321 &cfid, &volSync, NULL, NULL,
5324 code = cm_MapRPCError(code, &req);
5326 lock_ObtainWrite(&scp->rw);
5329 osi_Log1(afsd_logp, "CALL ExtendLock FAILURE, code 0x%x", code);
5331 osi_Log0(afsd_logp, "CALL ExtendLock SUCCESS");
5332 scp->lockDataVersion = scp->dataVersion;
5335 if ((code == EINVAL || code == CM_ERROR_INVAL) &&
5336 scp->lockDataVersion == scp->dataVersion) {
5340 (scp->exclusiveLocks > 0) ? LockWrite: LockRead;
5342 /* we might still have a chance to obtain a
5345 code = cm_IntSetLock(scp, userp, lockType, &req);
5348 code = CM_ERROR_INVAL;
5349 } else if (scp->lockDataVersion != scp->dataVersion) {
5351 /* now check if we still have the file at
5352 the right data version. */
5354 "Data version mismatch on scp 0x%p",
5357 " Data versions: before=%I64d, after=%I64d",
5358 scp->lockDataVersion,
5361 code = cm_IntReleaseLock(scp, userp, &req);
5363 code = CM_ERROR_INVAL;
5367 if (code == EINVAL || code == CM_ERROR_INVAL ||
5368 code == CM_ERROR_BADFD) {
5369 cm_LockMarkSCacheLost(scp);
5373 /* interestingly, we have found an active lock
5374 belonging to an scache that has no
5376 cm_LockMarkSCacheLost(scp);
5383 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5386 lock_ReleaseWrite(&scp->rw);
5388 lock_ObtainWrite(&cm_scacheLock);
5391 fileLock->lastUpdate = time(NULL);
5395 scp->lastRefreshCycle = cm_lockRefreshCycle;
5398 /* we have already refreshed the locks on this scp */
5399 fileLock->lastUpdate = time(NULL);
5402 cm_ReleaseSCacheNoLock(scp);
5404 } else if (IS_LOCK_ACTIVE(fileLock) && IS_LOCK_CLIENTONLY(fileLock)) {
5405 /* TODO: Check callbacks */
5409 lock_ReleaseWrite(&cm_scacheLock);
5410 osi_Log1(afsd_logp, "cm_CheckLocks completes lock check cycle %d", cm_lockRefreshCycle);
5413 /* NOT called with scp->rw held. */
5414 long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
5417 cm_scache_t *scp = NULL;
5418 cm_file_lock_t *fileLock;
5422 int force_client_lock = FALSE;
5423 int has_insert = FALSE;
5424 int check_data_version = FALSE;
5428 if (client_is_dead) {
5429 code = CM_ERROR_TIMEDOUT;
5433 lock_ObtainRead(&cm_scacheLock);
5435 osi_Log2(afsd_logp, "cm_RetryLock checking lock %p (scp=%p)", oldFileLock, oldFileLock->scp);
5436 osi_Log4(afsd_logp, " offset(%x:%x) length(%x:%x)",
5437 (unsigned)(oldFileLock->range.offset >> 32),
5438 (unsigned)(oldFileLock->range.offset & 0xffffffff),
5439 (unsigned)(oldFileLock->range.length >> 32),
5440 (unsigned)(oldFileLock->range.length & 0xffffffff));
5441 osi_Log4(afsd_logp, " key<0x%x,0x%x,0x%x> flags=%x",
5442 oldFileLock->key.process_id, oldFileLock->key.session_id, oldFileLock->key.file_id,
5443 (unsigned)(oldFileLock->flags));
5445 /* if the lock has already been granted, then we have nothing to do */
5446 if (IS_LOCK_ACTIVE(oldFileLock)) {
5447 lock_ReleaseRead(&cm_scacheLock);
5448 osi_Log0(afsd_logp, "cm_RetryLock lock already granted");
5452 /* we can't do anything with lost or deleted locks at the moment. */
5453 if (IS_LOCK_LOST(oldFileLock) || IS_LOCK_DELETED(oldFileLock)) {
5454 code = CM_ERROR_BADFD;
5455 osi_Log0(afsd_logp, "cm_RetryLock lock is lost or deleted");
5456 lock_ReleaseRead(&cm_scacheLock);
5460 scp = oldFileLock->scp;
5462 osi_assertx(scp != NULL, "null cm_scache_t");
5464 lock_ReleaseRead(&cm_scacheLock);
5465 lock_ObtainWrite(&scp->rw);
5467 code = cm_LockCheckPerms(scp, oldFileLock->lockType,
5471 if (code == CM_ERROR_NOACCESS && oldFileLock->lockType == LockRead) {
5472 if (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp)) {
5473 force_client_lock = TRUE;
5477 lock_ReleaseWrite(&scp->rw);
5481 lock_ObtainWrite(&cm_scacheLock);
5483 /* Check if we already have a sufficient server lock to allow this
5484 lock to go through. */
5485 if (IS_LOCK_WAITLOCK(oldFileLock) &&
5486 (!SERVERLOCKS_ENABLED(scp) ||
5487 scp->serverLock == oldFileLock->lockType ||
5488 scp->serverLock == LockWrite)) {
5490 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5492 if (SERVERLOCKS_ENABLED(scp)) {
5493 osi_Log1(afsd_logp, "cm_RetryLock Server lock (%d) is sufficient for lock. Granting",
5494 (int) scp->serverLock);
5496 osi_Log0(afsd_logp, "cm_RetryLock skipping server lock for scp");
5499 lock_ReleaseWrite(&cm_scacheLock);
5500 lock_ReleaseWrite(&scp->rw);
5505 if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5507 /* check if the conflicting locks have dissappeared already */
5508 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5510 fileLock = (cm_file_lock_t *)
5511 ((char *) q - offsetof(cm_file_lock_t, fileq));
5513 if (IS_LOCK_LOST(fileLock)) {
5514 if (cm_KeyEquals(&fileLock->key, &oldFileLock->key, 0)) {
5515 code = CM_ERROR_BADFD;
5516 oldFileLock->flags |= CM_FILELOCK_FLAG_LOST;
5517 osi_Log1(afsd_logp, " found lost lock %p for same key. Marking lock as lost",
5520 } else if (fileLock->lockType == LockWrite &&
5521 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5522 osi_Log1(afsd_logp, " found conflicting LOST lock %p", fileLock);
5523 code = CM_ERROR_WOULDBLOCK;
5528 if (IS_LOCK_ACCEPTED(fileLock) &&
5529 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5531 if (oldFileLock->lockType != LockRead ||
5532 fileLock->lockType != LockRead) {
5534 osi_Log1(afsd_logp, " found conflicting lock %p", fileLock);
5535 code = CM_ERROR_WOULDBLOCK;
5543 lock_ReleaseWrite(&cm_scacheLock);
5544 lock_ReleaseWrite(&scp->rw);
5549 /* when we get here, the lock is either a WAITUNLOCK or WAITLOCK.
5550 If it is WAITUNLOCK, then we didn't find any conflicting lock
5551 but we haven't verfied whether the serverLock is sufficient to
5552 assert it. If it is WAITLOCK, then the serverLock is
5553 insufficient to assert it. Eitherway, we are ready to accept
5554 the lock as either ACTIVE or WAITLOCK depending on the
5557 /* First, promote the WAITUNLOCK to a WAITLOCK */
5558 if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5559 if (oldFileLock->lockType == LockRead)
5562 scp->exclusiveLocks++;
5564 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITUNLOCK;
5565 oldFileLock->flags |= CM_FILELOCK_FLAG_WAITLOCK;
5568 osi_assertx(IS_LOCK_WAITLOCK(oldFileLock), "!IS_LOCK_WAITLOCK");
5570 if (force_client_lock ||
5571 !SERVERLOCKS_ENABLED(scp) ||
5572 scp->serverLock == oldFileLock->lockType ||
5573 (oldFileLock->lockType == LockRead &&
5574 scp->serverLock == LockWrite)) {
5576 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5578 if ((force_client_lock ||
5579 !SERVERLOCKS_ENABLED(scp)) &&
5580 !IS_LOCK_CLIENTONLY(oldFileLock)) {
5582 oldFileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
5584 if (oldFileLock->lockType == LockRead)
5587 scp->exclusiveLocks--;
5592 lock_ReleaseWrite(&cm_scacheLock);
5593 lock_ReleaseWrite(&scp->rw);
5600 code = cm_SyncOp(scp, NULL, oldFileLock->userp, &req, 0,
5601 CM_SCACHESYNC_NEEDCALLBACK
5602 | CM_SCACHESYNC_GETSTATUS
5603 | CM_SCACHESYNC_LOCK);
5605 osi_Log1(afsd_logp, "cm_RetryLock SyncOp failure code 0x%x", code);
5606 lock_ReleaseWrite(&cm_scacheLock);
5607 goto post_syncopdone;
5610 if (!IS_LOCK_WAITLOCK(oldFileLock))
5611 goto pre_syncopdone;
5613 userp = oldFileLock->userp;
5615 #ifndef AGGRESSIVE_LOCKS
5616 newLock = oldFileLock->lockType;
5618 newLock = LockWrite;
5622 /* if has_insert is non-zero, then:
5623 - the lock a LockRead
5624 - we don't have permission to get a LockRead
5625 - we do have permission to get a LockWrite
5626 - the server supports VICED_CAPABILITY_WRITELOCKACL
5629 newLock = LockWrite;
5632 lock_ReleaseWrite(&cm_scacheLock);
5634 /* when we get here, either we have a read-lock and want a
5635 write-lock or we don't have any locks and we want some
5638 if (scp->serverLock == LockRead) {
5640 osi_assertx(newLock == LockWrite, "!LockWrite");
5642 osi_Log0(afsd_logp, " Attempting to UPGRADE from LockRead to LockWrite");
5644 scp->lockDataVersion = scp->dataVersion;
5645 check_data_version = TRUE;
5647 code = cm_IntReleaseLock(scp, userp, &req);
5650 goto pre_syncopdone;
5652 scp->serverLock = -1;
5655 code = cm_IntSetLock(scp, userp, newLock, &req);
5658 if (scp->dataVersion != scp->lockDataVersion) {
5659 /* we lost a race. too bad */
5662 " Data version mismatch while upgrading lock.");
5664 " Data versions before=%I64d, after=%I64d",
5665 scp->lockDataVersion,
5668 " Releasing stale lock for scp 0x%x", scp);
5670 code = cm_IntReleaseLock(scp, userp, &req);
5672 scp->serverLock = -1;
5674 code = CM_ERROR_INVAL;
5676 cm_LockMarkSCacheLost(scp);
5678 scp->serverLock = newLock;
5683 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5689 if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
5690 lock_ObtainWrite(&cm_scacheLock);
5691 if (scp->fileLocksT == &oldFileLock->fileq)
5692 scp->fileLocksT = osi_QPrev(&oldFileLock->fileq);
5693 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, &oldFileLock->fileq);
5694 lock_ReleaseWrite(&cm_scacheLock);
5696 lock_ReleaseWrite(&scp->rw);
5699 lock_ObtainWrite(&cm_scacheLock);
5701 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5702 } else if (code != CM_ERROR_WOULDBLOCK) {
5703 oldFileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5704 cm_ReleaseUser(oldFileLock->userp);
5705 oldFileLock->userp = NULL;
5706 if (oldFileLock->scp) {
5707 cm_ReleaseSCacheNoLock(oldFileLock->scp);
5708 oldFileLock->scp = NULL;
5711 lock_ReleaseWrite(&cm_scacheLock);
5716 cm_key_t cm_GenerateKey(afs_uint16 session_id, afs_offs_t process_id, afs_uint16 file_id)
5720 key.process_id = process_id;
5721 key.session_id = session_id;
5722 key.file_id = file_id;
5727 int cm_KeyEquals(cm_key_t *k1, cm_key_t *k2, int flags)
5729 return (k1->session_id == k2->session_id) && (k1->file_id == k2->file_id) &&
5730 ((flags & CM_UNLOCK_BY_FID) || (k1->process_id == k2->process_id));
5733 void cm_ReleaseAllLocks(void)
5739 cm_file_lock_t *fileLock;
5742 for (i = 0; i < cm_data.scacheHashTableSize; i++)
5744 for ( scp = cm_data.scacheHashTablep[i]; scp; scp = scp->nextp ) {
5745 while (scp->fileLocksH != NULL) {
5746 lock_ObtainWrite(&scp->rw);
5747 lock_ObtainWrite(&cm_scacheLock);
5748 if (!scp->fileLocksH) {
5749 lock_ReleaseWrite(&cm_scacheLock);
5750 lock_ReleaseWrite(&scp->rw);
5753 fileLock = (cm_file_lock_t *)((char *) scp->fileLocksH - offsetof(cm_file_lock_t, fileq));
5754 userp = fileLock->userp;
5756 key = fileLock->key;
5757 cm_HoldSCacheNoLock(scp);
5758 lock_ReleaseWrite(&cm_scacheLock);
5759 cm_UnlockByKey(scp, key, 0, userp, &req);
5760 cm_ReleaseSCache(scp);
5761 cm_ReleaseUser(userp);
5762 lock_ReleaseWrite(&scp->rw);