2 * Copyright 2000, International Business Machines Corporation and others.
5 * This software has been released under the terms of the IBM Public
6 * License. For details, see the LICENSE file in the top-level source
7 * directory or online at http://www.openafs.org/dl/license10.html
10 #include <afs/param.h>
30 extern void afsi_log(char *pattern, ...);
33 int cm_enableServerLocks = 1;
35 int cm_followBackupPath = 0;
38 * Case-folding array. This was constructed by inspecting of SMBtrace output.
39 * I do not know anything more about it.
41 unsigned char cm_foldUpper[256] = {
42 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
43 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
44 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
45 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
46 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
47 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
48 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
49 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
50 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
51 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
52 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
53 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
54 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
55 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
56 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
57 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
58 0x80, 0x9a, 0x90, 0x41, 0x8e, 0x41, 0x8f, 0x80,
59 0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x8e, 0x8f,
60 0x90, 0x92, 0x92, 0x4f, 0x99, 0x4f, 0x55, 0x55,
61 0x59, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
62 0x41, 0x49, 0x4f, 0x55, 0xa5, 0xa5, 0x56, 0xa7,
63 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
64 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
65 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
66 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
67 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
68 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
69 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
70 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
71 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
72 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
73 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
77 * Case-insensitive string comparison. We used to use stricmp, but it doesn't
78 * know about 8-bit characters (e.g. 129 is lowercase u-umlaut, 154 is
79 * upper-case u-umlaut).
81 int cm_stricmp(const char *str1, const char *str2)
93 c1 = (char) cm_foldUpper[(unsigned char)(*str1++)];
94 c2 = (char) cm_foldUpper[(unsigned char)(*str2++)];
104 /* return success if we can open this file in this mode */
105 long cm_CheckOpen(cm_scache_t *scp, int openMode, int trunc, cm_user_t *userp,
113 rights |= PRSFS_READ;
114 if (openMode == 1 || openMode == 2 || trunc)
115 rights |= PRSFS_WRITE;
117 lock_ObtainWrite(&scp->rw);
119 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
120 CM_SCACHESYNC_GETSTATUS
121 | CM_SCACHESYNC_NEEDCALLBACK
122 | CM_SCACHESYNC_LOCK);
125 ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
126 scp->fileType == CM_SCACHETYPE_FILE) {
129 unsigned int sLockType;
130 LARGE_INTEGER LOffset, LLength;
132 /* Check if there's some sort of lock on the file at the
135 key = cm_GenerateKey(CM_SESSION_CMINT,0,0);
137 if (rights & PRSFS_WRITE)
140 sLockType = LOCKING_ANDX_SHARED_LOCK;
142 LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
143 LOffset.LowPart = CM_FLSHARE_OFFSET_LOW;
144 LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
145 LLength.LowPart = CM_FLSHARE_LENGTH_LOW;
147 code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
150 cm_Unlock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp);
152 /* In this case, we allow the file open to go through even
153 though we can't enforce mandatory locking on the
155 if (code == CM_ERROR_NOACCESS &&
156 !(rights & PRSFS_WRITE))
160 case CM_ERROR_ALLOFFLINE:
161 case CM_ERROR_ALLDOWN:
162 case CM_ERROR_ALLBUSY:
163 case CM_ERROR_TIMEDOUT:
165 case CM_ERROR_WOULDBLOCK:
168 code = CM_ERROR_SHARING_VIOLATION;
173 } else if (code != 0) {
177 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
181 lock_ReleaseWrite(&scp->rw);
186 /* return success if we can open this file in this mode */
187 long cm_CheckNTOpen(cm_scache_t *scp, unsigned int desiredAccess,
188 unsigned int createDisp, cm_user_t *userp, cm_req_t *reqp,
189 cm_lock_data_t **ldpp)
194 osi_assertx(ldpp != NULL, "null cm_lock_data_t");
197 /* Always allow delete; the RPC will tell us if it's OK */
198 if (desiredAccess == DELETE)
203 if (desiredAccess & (AFS_ACCESS_READ|AFS_ACCESS_EXECUTE))
204 rights |= (scp->fileType == CM_SCACHETYPE_DIRECTORY ? PRSFS_LOOKUP : PRSFS_READ);
206 /* We used to require PRSFS_WRITE if createDisp was 4
207 (OPEN_ALWAYS) even if AFS_ACCESS_WRITE was not requested.
208 However, we don't need to do that since the existence of the
209 scp implies that we don't need to create it. */
210 if (desiredAccess & AFS_ACCESS_WRITE)
211 rights |= PRSFS_WRITE;
213 lock_ObtainWrite(&scp->rw);
215 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
216 CM_SCACHESYNC_GETSTATUS
217 | CM_SCACHESYNC_NEEDCALLBACK
218 | CM_SCACHESYNC_LOCK);
221 * If the open will fail because the volume is readonly, then we will
222 * return an access denied error instead. This is to help brain-dead
223 * apps run correctly on replicated volumes.
224 * See defect 10007 for more information.
226 if (code == CM_ERROR_READONLY)
227 code = CM_ERROR_NOACCESS;
230 ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
231 scp->fileType == CM_SCACHETYPE_FILE) {
233 unsigned int sLockType;
234 LARGE_INTEGER LOffset, LLength;
236 /* Check if there's some sort of lock on the file at the
239 key = cm_GenerateKey(CM_SESSION_CMINT,0,0);
240 if (rights & PRSFS_WRITE)
243 sLockType = LOCKING_ANDX_SHARED_LOCK;
245 /* single byte lock at offset 0x0100 0000 0000 0000 */
246 LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
247 LOffset.LowPart = CM_FLSHARE_OFFSET_LOW;
248 LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
249 LLength.LowPart = CM_FLSHARE_LENGTH_LOW;
251 code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
254 (*ldpp) = (cm_lock_data_t *)malloc(sizeof(cm_lock_data_t));
261 (*ldpp)->sLockType = sLockType;
262 (*ldpp)->LOffset.HighPart = LOffset.HighPart;
263 (*ldpp)->LOffset.LowPart = LOffset.LowPart;
264 (*ldpp)->LLength.HighPart = LLength.HighPart;
265 (*ldpp)->LLength.LowPart = LLength.LowPart;
267 /* In this case, we allow the file open to go through even
268 though we can't enforce mandatory locking on the
270 if (code == CM_ERROR_NOACCESS &&
271 !(rights & PRSFS_WRITE))
275 case CM_ERROR_ALLOFFLINE:
276 case CM_ERROR_ALLDOWN:
277 case CM_ERROR_ALLBUSY:
278 case CM_ERROR_TIMEDOUT:
280 case CM_ERROR_WOULDBLOCK:
283 code = CM_ERROR_SHARING_VIOLATION;
287 } else if (code != 0) {
292 lock_ReleaseWrite(&scp->rw);
294 osi_Log3(afsd_logp,"cm_CheckNTOpen scp 0x%p ldp 0x%p code 0x%x", scp, *ldpp, code);
298 extern long cm_CheckNTOpenDone(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp,
299 cm_lock_data_t ** ldpp)
301 osi_Log2(afsd_logp,"cm_CheckNTOpenDone scp 0x%p ldp 0x%p", scp, *ldpp);
302 lock_ObtainWrite(&scp->rw);
304 cm_Unlock(scp, (*ldpp)->sLockType, (*ldpp)->LOffset, (*ldpp)->LLength,
305 (*ldpp)->key, 0, userp, reqp);
309 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
310 lock_ReleaseWrite(&scp->rw);
314 * When CAP_NT_SMBS has been negotiated, deletion (of files or directories) is
315 * done in three steps:
316 * (1) open for deletion (NT_CREATE_AND_X)
317 * (2) set for deletion on close (NT_TRANSACTION2, SET_FILE_INFO)
319 * We must not do the RPC until step 3. But if we are going to return an error
320 * code (e.g. directory not empty), we must return it by step 2, otherwise most
321 * clients will not notice it. So we do a preliminary check. For deleting
322 * files, this is almost free, since we have already done the RPC to get the
323 * parent directory's status bits. But for deleting directories, we must do an
324 * additional RPC to get the directory's data to check if it is empty. Sigh.
326 long cm_CheckNTDelete(cm_scache_t *dscp, cm_scache_t *scp, cm_user_t *userp,
332 cm_dirEntry_t *dep = 0;
333 unsigned short *hashTable;
335 int BeyondPage = 0, HaveDot = 0, HaveDotDot = 0;
338 /* First check permissions */
339 lock_ObtainWrite(&scp->rw);
340 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_DELETE,
341 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
343 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
344 lock_ReleaseWrite(&scp->rw);
348 /* If deleting directory, must be empty */
350 if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
353 thyper.HighPart = 0; thyper.LowPart = 0;
354 code = buf_Get(scp, &thyper, reqp, &bufferp);
358 lock_ObtainMutex(&bufferp->mx);
359 lock_ObtainWrite(&scp->rw);
362 code = cm_SyncOp(scp, bufferp, userp, reqp, 0,
363 CM_SCACHESYNC_NEEDCALLBACK
365 | CM_SCACHESYNC_BUFLOCKED);
369 if (cm_HaveBuffer(scp, bufferp, 1))
372 /* otherwise, load the buffer and try again */
373 lock_ReleaseMutex(&bufferp->mx);
374 code = cm_GetBuffer(scp, bufferp, NULL, userp, reqp);
375 lock_ReleaseWrite(&scp->rw);
376 lock_ObtainMutex(&bufferp->mx);
377 lock_ObtainWrite(&scp->rw);
378 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
383 lock_ReleaseWrite(&scp->rw);
386 /* We try to determine emptiness without looking beyond the first page,
387 * and without assuming "." and ".." are present and are on the first
388 * page (though these assumptions might, after all, be reasonable).
390 hashTable = (unsigned short *)(bufferp->datap + (32 * 5));
391 for (i=0; i<128; i++) {
392 idx = ntohs(hashTable[i]);
398 dep = (cm_dirEntry_t *)(bufferp->datap + (32 * idx));
399 if (strcmp(dep->name, ".") == 0)
401 else if (strcmp(dep->name, "..") == 0)
404 code = CM_ERROR_NOTEMPTY;
407 idx = ntohs(dep->next);
410 if (BeyondPage && HaveDot && HaveDotDot)
411 code = CM_ERROR_NOTEMPTY;
415 lock_ReleaseMutex(&bufferp->mx);
416 buf_Release(bufferp);
418 lock_ReleaseWrite(&scp->rw);
423 * Iterate through all entries in a directory.
424 * When the function funcp is called, the buffer is locked but the
425 * directory vnode is not.
427 * If the retscp parameter is not NULL, the parmp must be a
428 * cm_lookupSearch_t object.
430 long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
431 osi_hyper_t *startOffsetp, cm_user_t *userp, cm_req_t *reqp,
432 cm_scache_t **retscp)
436 cm_dirEntry_t *dep = 0;
439 osi_hyper_t dirLength;
440 osi_hyper_t bufferOffset;
441 osi_hyper_t curOffset;
445 cm_pageHeader_t *pageHeaderp;
447 long nextEntryCookie;
448 int numDirChunks; /* # of 32 byte dir chunks in this entry */
450 /* get the directory size */
451 lock_ObtainWrite(&scp->rw);
452 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_LOOKUP,
453 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
454 lock_ReleaseWrite(&scp->rw);
458 if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
459 return CM_ERROR_NOTDIR;
461 if (retscp) /* if this is a lookup call */
463 cm_lookupSearch_t* sp = parmp;
466 #ifdef AFS_FREELANCE_CLIENT
467 /* Freelance entries never end up in the DNLC because they
468 * do not have an associated cm_server_t
470 !(cm_freelanceEnabled &&
471 sp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
472 sp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
473 #else /* !AFS_FREELANCE_CLIENT */
478 int casefold = sp->caseFold;
479 sp->caseFold = 0; /* we have a strong preference for exact matches */
480 if ( *retscp = cm_dnlcLookup(scp, sp)) /* dnlc hit */
482 sp->caseFold = casefold;
485 sp->caseFold = casefold;
489 * see if we can find it using the directory hash tables.
490 * we can only do exact matches, since the hash is case
493 if (funcp != (cm_DirFuncp_t)cm_BPlusDirFoo)
502 code = cm_BeginDirOp(scp, userp, reqp, CM_DIRLOCK_READ, &dirop);
506 code = cm_BPlusDirLookup(&dirop, sp->nsearchNamep, &sp->fid);
511 code = cm_DirLookup(&dirop, sp->searchNamep, &sp->fid);
519 sp->ExactFound = TRUE;
520 *retscp = NULL; /* force caller to call cm_GetSCache() */
525 if (sp->caseFold && code == CM_ERROR_INEXACT_MATCH) {
528 sp->ExactFound = FALSE;
529 *retscp = NULL; /* force caller to call cm_GetSCache() */
533 return CM_ERROR_BPLUS_NOMATCH;
540 * XXX We only get the length once. It might change when we drop the
543 dirLength = scp->length;
546 bufferOffset.LowPart = bufferOffset.HighPart = 0;
548 curOffset = *startOffsetp;
550 curOffset.HighPart = 0;
551 curOffset.LowPart = 0;
555 /* make sure that curOffset.LowPart doesn't point to the first
556 * 32 bytes in the 2nd through last dir page, and that it
557 * doesn't point at the first 13 32-byte chunks in the first
558 * dir page, since those are dir and page headers, and don't
559 * contain useful information.
561 temp = curOffset.LowPart & (2048-1);
562 if (curOffset.HighPart == 0 && curOffset.LowPart < 2048) {
563 /* we're in the first page */
564 if (temp < 13*32) temp = 13*32;
567 /* we're in a later dir page */
568 if (temp < 32) temp = 32;
571 /* make sure the low order 5 bits are zero */
574 /* now put temp bits back ito curOffset.LowPart */
575 curOffset.LowPart &= ~(2048-1);
576 curOffset.LowPart |= temp;
578 /* check if we've passed the dir's EOF */
579 if (LargeIntegerGreaterThanOrEqualTo(curOffset, dirLength))
582 /* see if we can use the bufferp we have now; compute in which
583 * page the current offset would be, and check whether that's
584 * the offset of the buffer we have. If not, get the buffer.
586 thyper.HighPart = curOffset.HighPart;
587 thyper.LowPart = curOffset.LowPart & ~(cm_data.buf_blockSize-1);
588 if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
591 lock_ReleaseMutex(&bufferp->mx);
592 buf_Release(bufferp);
596 code = buf_Get(scp, &thyper, reqp, &bufferp);
598 /* if buf_Get() fails we do not have a buffer object to lock */
603 lock_ObtainMutex(&bufferp->mx);
604 bufferOffset = thyper;
606 /* now get the data in the cache */
608 lock_ObtainWrite(&scp->rw);
609 code = cm_SyncOp(scp, bufferp, userp, reqp,
611 CM_SCACHESYNC_NEEDCALLBACK
613 | CM_SCACHESYNC_BUFLOCKED);
615 lock_ReleaseWrite(&scp->rw);
618 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
620 if (cm_HaveBuffer(scp, bufferp, 1)) {
621 lock_ReleaseWrite(&scp->rw);
625 /* otherwise, load the buffer and try again */
626 lock_ReleaseMutex(&bufferp->mx);
627 code = cm_GetBuffer(scp, bufferp, NULL, userp,
629 lock_ReleaseWrite(&scp->rw);
630 lock_ObtainMutex(&bufferp->mx);
635 lock_ReleaseMutex(&bufferp->mx);
636 buf_Release(bufferp);
640 } /* if (wrong buffer) ... */
642 /* now we have the buffer containing the entry we're interested
643 * in; copy it out if it represents a non-deleted entry.
645 entryInDir = curOffset.LowPart & (2048-1);
646 entryInBuffer = curOffset.LowPart & (cm_data.buf_blockSize - 1);
648 /* page header will help tell us which entries are free. Page
649 * header can change more often than once per buffer, since
650 * AFS 3 dir page size may be less than (but not more than) a
651 * buffer package buffer.
653 /* only look intra-buffer */
654 temp = curOffset.LowPart & (cm_data.buf_blockSize - 1);
655 temp &= ~(2048 - 1); /* turn off intra-page bits */
656 pageHeaderp = (cm_pageHeader_t *) (bufferp->datap + temp);
658 /* now determine which entry we're looking at in the page. If
659 * it is free (there's a free bitmap at the start of the dir),
660 * we should skip these 32 bytes.
662 slotInPage = (entryInDir & 0x7e0) >> 5;
663 if (!(pageHeaderp->freeBitmap[slotInPage>>3]
664 & (1 << (slotInPage & 0x7)))) {
665 /* this entry is free */
666 numDirChunks = 1; /* only skip this guy */
670 tp = bufferp->datap + entryInBuffer;
671 dep = (cm_dirEntry_t *) tp; /* now points to AFS3 dir entry */
673 /* while we're here, compute the next entry's location, too,
674 * since we'll need it when writing out the cookie into the
675 * dir listing stream.
677 numDirChunks = cm_NameEntries(dep->name, NULL);
679 /* compute the offset of the cookie representing the next entry */
680 nextEntryCookie = curOffset.LowPart
681 + (CM_DIR_CHUNKSIZE * numDirChunks);
683 if (dep->fid.vnode != 0) {
684 /* this is one of the entries to use: it is not deleted */
685 code = (*funcp)(scp, dep, parmp, &curOffset);
688 } /* if we're including this name */
691 /* and adjust curOffset to be where the new cookie is */
693 thyper.LowPart = CM_DIR_CHUNKSIZE * numDirChunks;
694 curOffset = LargeIntegerAdd(thyper, curOffset);
695 } /* while copying data for dir listing */
697 /* release the mutex */
699 lock_ReleaseMutex(&bufferp->mx);
700 buf_Release(bufferp);
705 int cm_NoneUpper(normchar_t *s)
709 if (c >= 'A' && c <= 'Z')
714 int cm_NoneLower(normchar_t *s)
718 if (c >= 'a' && c <= 'z')
723 long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
726 cm_lookupSearch_t *sp;
728 normchar_t matchName[MAX_PATH];
729 int looking_for_short_name = FALSE;
731 sp = (cm_lookupSearch_t *) rockp;
733 if (cm_FsStringToNormString(dep->name, -1, matchName, lengthof(matchName)) == 0) {
734 /* Can't normalize FS string. */
739 match = cm_NormStrCmpI(matchName, sp->nsearchNamep);
741 match = cm_NormStrCmp(matchName, sp->nsearchNamep);
745 && !cm_Is8Dot3(matchName)) {
747 cm_Gen8Dot3NameInt(dep->name, &dep->fid, matchName, NULL);
749 match = cm_NormStrCmpI(matchName, sp->nsearchNamep);
751 match = cm_NormStrCmp(matchName, sp->nsearchNamep);
752 looking_for_short_name = TRUE;
762 if (!sp->caseFold || looking_for_short_name) {
763 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
764 return CM_ERROR_STOPNOW;
768 * If we get here, we are doing a case-insensitive search, and we
769 * have found a match. Now we determine what kind of match it is:
770 * exact, lower-case, upper-case, or none of the above. This is done
771 * in order to choose among matches, if there are more than one.
774 /* Exact matches are the best. */
775 match = cm_NormStrCmp(matchName, sp->nsearchNamep);
778 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
779 return CM_ERROR_STOPNOW;
782 /* Lower-case matches are next. */
785 if (cm_NoneUpper(matchName)) {
790 /* Upper-case matches are next. */
793 if (cm_NoneLower(matchName)) {
798 /* General matches are last. */
804 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
808 /* read the contents of a mount point into the appropriate string.
809 * called with write locked scp, and returns with locked scp.
811 long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
814 cm_buf_t *bufp = NULL;
818 if (scp->mountPointStringp[0])
821 #ifdef AFS_FREELANCE_CLIENT
822 /* File servers do not have data for freelance entries */
823 if (cm_freelanceEnabled &&
824 scp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
825 scp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
827 code = cm_FreelanceFetchMountPointString(scp);
829 #endif /* AFS_FREELANCE_CLIENT */
831 /* otherwise, we have to read it in */
832 lock_ReleaseWrite(&scp->rw);
834 thyper.LowPart = thyper.HighPart = 0;
835 code = buf_Get(scp, &thyper, reqp, &bufp);
837 lock_ObtainWrite(&scp->rw);
842 code = cm_SyncOp(scp, bufp, userp, reqp, 0,
843 CM_SCACHESYNC_READ | CM_SCACHESYNC_NEEDCALLBACK);
847 cm_SyncOpDone(scp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
849 if (cm_HaveBuffer(scp, bufp, 0))
852 /* otherwise load buffer */
853 code = cm_GetBuffer(scp, bufp, NULL, userp, reqp);
857 /* locked, has callback, has valid data in buffer */
858 if ((tlen = scp->length.LowPart) > MOUNTPOINTLEN - 1)
859 return CM_ERROR_TOOBIG;
861 code = CM_ERROR_INVAL;
865 /* someone else did the work while we were out */
866 if (scp->mountPointStringp[0]) {
871 /* otherwise, copy out the link */
872 memcpy(scp->mountPointStringp, bufp->datap, tlen);
874 /* now make it null-terminated. Note that the original contents of a
875 * link that is a mount point is "#volname." where "." is there just to
876 * be turned into a null. That is, we can trash the last char of the
877 * link without damaging the vol name. This is a stupid convention,
878 * but that's the protocol.
880 scp->mountPointStringp[tlen-1] = 0;
891 /* called with a locked scp and chases the mount point, yielding outScpp.
892 * scp remains write locked, just for simplicity of describing the interface.
894 long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
895 cm_req_t *reqp, cm_scache_t **outScpp)
897 fschar_t *cellNamep = NULL;
898 fschar_t *volNamep = NULL;
902 cm_volume_t *volp = NULL;
911 if (scp->mountRootFid.cell != 0 && scp->mountRootGen >= cm_data.mountRootGen) {
912 tfid = scp->mountRootFid;
913 lock_ReleaseWrite(&scp->rw);
914 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
915 lock_ObtainWrite(&scp->rw);
919 /* parse the volume name */
920 mpNamep = scp->mountPointStringp;
922 return CM_ERROR_NOSUCHPATH;
923 mtType = *scp->mountPointStringp;
925 cp = cm_FsStrChr(mpNamep, _FS(':'));
927 /* cellular mount point */
928 cellNamep = (fschar_t *)malloc((cp - mpNamep) * sizeof(fschar_t));
929 cm_FsStrCpyN(cellNamep, cp - mpNamep, mpNamep + 1, cp - mpNamep - 1);
930 volNamep = cm_FsStrDup(cp+1);
932 /* now look up the cell */
933 lock_ReleaseWrite(&scp->rw);
934 cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE);
935 lock_ObtainWrite(&scp->rw);
938 volNamep = cm_FsStrDup(mpNamep + 1);
940 #ifdef AFS_FREELANCE_CLIENT
942 * Mount points in the Freelance cell should default
943 * to the workstation cell.
945 if (cm_freelanceEnabled &&
946 scp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
947 scp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
949 fschar_t rootCellName[256]="";
950 cm_GetRootCellName(rootCellName);
951 cellp = cm_GetCell(rootCellName, 0);
953 #endif /* AFS_FREELANCE_CLIENT */
954 cellp = cm_FindCellByID(scp->fid.cell, 0);
958 code = CM_ERROR_NOSUCHCELL;
962 vnLength = cm_FsStrLen(volNamep);
963 if (vnLength >= 8 && cm_FsStrCmp(volNamep + vnLength - 7, ".backup") == 0)
964 targetType = BACKVOL;
965 else if (vnLength >= 10
966 && cm_FsStrCmp(volNamep + vnLength - 9, ".readonly") == 0)
971 /* check for backups within backups */
972 if (targetType == BACKVOL
973 && (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
974 == CM_SCACHEFLAG_RO) {
975 code = CM_ERROR_NOSUCHVOLUME;
979 /* now we need to get the volume */
980 lock_ReleaseWrite(&scp->rw);
981 if (cm_VolNameIsID(volNamep)) {
982 code = cm_FindVolumeByID(cellp, atoi(volNamep), userp, reqp,
983 CM_GETVOL_FLAG_CREATE, &volp);
985 code = cm_FindVolumeByName(cellp, volNamep, userp, reqp,
986 CM_GETVOL_FLAG_CREATE, &volp);
988 lock_ObtainWrite(&scp->rw);
991 afs_uint32 cell, volume;
992 cm_vol_state_t *statep;
994 cell = cellp->cellID;
996 /* if the mt pt originates in a .backup volume (not a .readonly)
997 * and FollowBackupPath is active, and if there is a .backup
998 * volume for the target, then use the .backup of the target
999 * instead of the read-write.
1001 if (cm_followBackupPath &&
1002 volp->vol[BACKVOL].ID != 0 &&
1003 (dscp->flags & (CM_SCACHEFLAG_RO|CM_SCACHEFLAG_PURERO)) == CM_SCACHEFLAG_RO &&
1004 (targetType == RWVOL || targetType == ROVOL && volp->vol[ROVOL].ID == 0)
1006 targetType = BACKVOL;
1008 /* if the mt pt is in a read-only volume (not just a
1009 * backup), and if there is a read-only volume for the
1010 * target, and if this is a targetType '#' mount point, use
1011 * the read-only, otherwise use the one specified.
1013 else if (mtType == '#' && targetType == RWVOL &&
1014 (scp->flags & CM_SCACHEFLAG_PURERO) &&
1015 volp->vol[ROVOL].ID != 0) {
1019 lock_ObtainWrite(&volp->rw);
1020 statep = cm_VolumeStateByType(volp, targetType);
1021 volume = statep->ID;
1022 statep->dotdotFid = dscp->fid;
1023 lock_ReleaseWrite(&volp->rw);
1025 /* the rest of the fid is a magic number */
1026 cm_SetFid(&scp->mountRootFid, cell, volume, 1, 1);
1027 scp->mountRootGen = cm_data.mountRootGen;
1029 tfid = scp->mountRootFid;
1030 lock_ReleaseWrite(&scp->rw);
1031 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
1032 lock_ObtainWrite(&scp->rw);
1045 long cm_LookupInternal(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_user_t *userp,
1046 cm_req_t *reqp, cm_scache_t **outScpp)
1049 int dnlcHit = 1; /* did we hit in the dnlc? yes, we did */
1050 cm_scache_t *tscp = NULL;
1051 cm_scache_t *mountedScp;
1052 cm_lookupSearch_t rock;
1054 normchar_t *nnamep = NULL;
1055 fschar_t *fnamep = NULL;
1059 memset(&rock, 0, sizeof(rock));
1061 if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
1062 && cm_ClientStrCmp(cnamep, _C("..")) == 0) {
1063 if (dscp->dotdotFid.volume == 0)
1064 return CM_ERROR_NOSUCHVOLUME;
1065 rock.fid = dscp->dotdotFid;
1067 } else if (cm_ClientStrCmp(cnamep, _C(".")) == 0) {
1068 rock.fid = dscp->fid;
1072 nnamep = cm_ClientStringToNormStringAlloc(cnamep, -1, NULL);
1074 code = CM_ERROR_NOSUCHFILE;
1077 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
1079 code = CM_ERROR_NOSUCHFILE;
1083 if (flags & CM_FLAG_NOMOUNTCHASE) {
1084 /* In this case, we should go and call cm_Dir* functions
1085 directly since the following cm_ApplyDir() function will
1093 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ, &dirop);
1096 code = cm_BPlusDirLookup(&dirop, nnamep, &rock.fid);
1101 code = cm_DirLookup(&dirop, fnamep, &rock.fid);
1103 cm_EndDirOp(&dirop);
1113 if (code == CM_ERROR_INEXACT_MATCH && (flags & CM_FLAG_CASEFOLD)) {
1120 return CM_ERROR_BPLUS_NOMATCH;
1125 rock.fid.cell = dscp->fid.cell;
1126 rock.fid.volume = dscp->fid.volume;
1127 rock.searchNamep = fnamep;
1128 rock.nsearchNamep = nnamep;
1129 rock.caseFold = (flags & CM_FLAG_CASEFOLD);
1130 rock.hasTilde = ((cm_ClientStrChr(cnamep, '~') != NULL) ? 1 : 0);
1132 /* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
1133 code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
1134 (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
1136 /* code == 0 means we fell off the end of the dir, while stopnow means
1137 * that we stopped early, probably because we found the entry we're
1138 * looking for. Any other non-zero code is an error.
1140 if (code && code != CM_ERROR_STOPNOW) {
1141 /* if the cm_scache_t we are searching in is not a directory
1142 * we must return path not found because the error
1143 * is to describe the final component not an intermediary
1145 if (code == CM_ERROR_NOTDIR) {
1146 if (flags & CM_FLAG_CHECKPATH)
1147 code = CM_ERROR_NOSUCHPATH;
1149 code = CM_ERROR_NOSUCHFILE;
1154 getroot = (dscp==cm_data.rootSCachep) ;
1156 if (!cm_freelanceEnabled || !getroot) {
1157 if (flags & CM_FLAG_CHECKPATH)
1158 code = CM_ERROR_NOSUCHPATH;
1160 code = CM_ERROR_NOSUCHFILE;
1163 else if (!cm_ClientStrChr(cnamep, '#') &&
1164 !cm_ClientStrChr(cnamep, '%') &&
1165 cm_ClientStrCmpI(cnamep, _C("srvsvc")) &&
1166 cm_ClientStrCmpI(cnamep, _C("wkssvc")) &&
1167 cm_ClientStrCmpI(cnamep, _C("ipc$")))
1169 /* nonexistent dir on freelance root, so add it */
1170 fschar_t fullname[CELL_MAXNAMELEN + 1] = "."; /* +1 so that when we skip the . the size is still CELL_MAXNAMELEN */
1173 osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %S",
1174 osi_LogSaveClientString(afsd_logp,cnamep));
1177 * There is an ugly behavior where a share name "foo" will be searched
1178 * for as "fo". If the searched for name differs by an already existing
1179 * symlink or mount point in the Freelance directory, do not add the
1180 * new value automatically.
1184 if (cnamep[0] == '.') {
1185 if (cm_GetCell_Gen(&fnamep[1], &fullname[1], CM_FLAG_CREATE)) {
1187 if (!cm_FreelanceMountPointExists(fullname, 0))
1188 code = cm_FreelanceAddMount(fullname, &fullname[1], "root.cell.",
1190 if ( cm_FsStrCmpI(&fnamep[1], &fullname[1]) &&
1191 !cm_FreelanceMountPointExists(fnamep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0) &&
1192 !cm_FreelanceSymlinkExists(fnamep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0))
1193 code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
1196 if (cm_GetCell_Gen(fnamep, fullname, CM_FLAG_CREATE)) {
1198 if (!cm_FreelanceMountPointExists(fullname, 0))
1199 code = cm_FreelanceAddMount(fullname, fullname, "root.cell.", 0, &rock.fid);
1200 if ( cm_FsStrCmpI(fnamep, fullname) &&
1201 !cm_FreelanceMountPointExists(fnamep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0) &&
1202 !cm_FreelanceSymlinkExists(fnamep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0))
1203 code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
1206 if (!found || code) { /* add mount point failed, so give up */
1207 if (flags & CM_FLAG_CHECKPATH)
1208 code = CM_ERROR_NOSUCHPATH;
1210 code = CM_ERROR_NOSUCHFILE;
1213 tscp = NULL; /* to force call of cm_GetSCache */
1215 if (flags & CM_FLAG_CHECKPATH)
1216 code = CM_ERROR_NOSUCHPATH;
1218 code = CM_ERROR_NOSUCHFILE;
1224 if ( !tscp ) /* we did not find it in the dnlc */
1227 code = cm_GetSCache(&rock.fid, &tscp, userp, reqp);
1231 /* tscp is now held */
1233 lock_ObtainWrite(&tscp->rw);
1234 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1235 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1237 lock_ReleaseWrite(&tscp->rw);
1238 cm_ReleaseSCache(tscp);
1241 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1242 /* tscp is now locked */
1244 if (!(flags & CM_FLAG_NOMOUNTCHASE)
1245 && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
1246 /* mount points are funny: they have a volume name to mount
1249 code = cm_ReadMountPoint(tscp, userp, reqp);
1251 code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
1253 lock_ReleaseWrite(&tscp->rw);
1254 cm_ReleaseSCache(tscp);
1261 lock_ReleaseWrite(&tscp->rw);
1264 /* copy back pointer */
1267 /* insert scache in dnlc */
1268 if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
1269 /* lock the directory entry to prevent racing callback revokes */
1270 lock_ObtainRead(&dscp->rw);
1271 if ( dscp->cbServerp != NULL && dscp->cbExpires > 0 ) {
1272 /* TODO: reuse nnamep from above */
1275 nnamep = cm_ClientStringToNormStringAlloc(cnamep, -1, NULL);
1277 cm_dnlcEnter(dscp, nnamep, tscp);
1279 lock_ReleaseRead(&dscp->rw);
1296 int cm_ExpandSysName(clientchar_t *inp, clientchar_t *outp, long outSizeCch, unsigned int index)
1301 tp = cm_ClientStrRChr(inp, '@');
1303 return 0; /* no @sys */
1305 if (cm_ClientStrCmp(tp, _C("@sys")) != 0)
1306 return 0; /* no @sys */
1308 /* caller just wants to know if this is a valid @sys type of name */
1312 if (index >= cm_sysNameCount)
1315 /* otherwise generate the properly expanded @sys name */
1316 prefixCount = (int)(tp - inp);
1318 cm_ClientStrCpyN(outp, outSizeCch, inp, prefixCount); /* copy out "a." from "a.@sys" */
1319 outp[prefixCount] = 0; /* null terminate the "a." */
1320 cm_ClientStrCat(outp, outSizeCch, cm_sysNameList[index]);/* append i386_nt40 */
1324 long cm_EvaluateVolumeReference(clientchar_t * namep, long flags, cm_user_t * userp,
1325 cm_req_t *reqp, cm_scache_t ** outScpp)
1327 afs_uint32 code = 0;
1328 fschar_t cellName[CELL_MAXNAMELEN];
1329 fschar_t volumeName[VL_MAXNAMELEN];
1333 fschar_t * fnamep = NULL;
1335 cm_cell_t * cellp = NULL;
1336 cm_volume_t * volp = NULL;
1340 int mountType = RWVOL;
1342 osi_Log1(afsd_logp, "cm_EvaluateVolumeReference for string [%S]",
1343 osi_LogSaveClientString(afsd_logp, namep));
1345 if (cm_ClientStrCmpNI(namep, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH) != 0) {
1346 goto _exit_invalid_path;
1349 /* namep is assumed to look like the following:
1351 @vol:<cellname>%<volume>\0
1353 @vol:<cellname>#<volume>\0
1357 fnamep = cm_ClientStringToFsStringAlloc(namep, -1, NULL);
1358 cp = fnamep + CM_PREFIX_VOL_CCH; /* cp points to cell name, hopefully */
1359 tp = cm_FsStrChr(cp, '%');
1361 tp = cm_FsStrChr(cp, '#');
1363 (len = tp - cp) == 0 ||
1364 len > CELL_MAXNAMELEN)
1365 goto _exit_invalid_path;
1366 cm_FsStrCpyN(cellName, lengthof(cellName), cp, len);
1371 cp = tp+1; /* cp now points to volume, supposedly */
1372 cm_FsStrCpy(volumeName, lengthof(volumeName), cp);
1374 /* OK, now we have the cell and the volume */
1375 osi_Log2(afsd_logp, " Found cell [%s] and volume [%s]",
1376 osi_LogSaveFsString(afsd_logp, cellName),
1377 osi_LogSaveFsString(afsd_logp, volumeName));
1379 cellp = cm_GetCell(cellName, CM_FLAG_CREATE);
1380 if (cellp == NULL) {
1381 goto _exit_invalid_path;
1384 len = cm_FsStrLen(volumeName);
1385 if (len >= 8 && cm_FsStrCmp(volumeName + len - 7, ".backup") == 0)
1387 else if (len >= 10 &&
1388 cm_FsStrCmp(volumeName + len - 9, ".readonly") == 0)
1393 if (cm_VolNameIsID(volumeName)) {
1394 code = cm_FindVolumeByID(cellp, atoi(volumeName), userp, reqp,
1395 CM_GETVOL_FLAG_CREATE, &volp);
1397 code = cm_FindVolumeByName(cellp, volumeName, userp, reqp,
1398 CM_GETVOL_FLAG_CREATE, &volp);
1404 if (volType == BACKVOL)
1405 volume = volp->vol[BACKVOL].ID;
1406 else if (volType == ROVOL ||
1407 (volType == RWVOL && mountType == ROVOL && volp->vol[ROVOL].ID != 0))
1408 volume = volp->vol[ROVOL].ID;
1410 volume = volp->vol[RWVOL].ID;
1412 cm_SetFid(&fid, cellp->cellID, volume, 1, 1);
1414 code = cm_GetSCache(&fid, outScpp, userp, reqp);
1427 if (flags & CM_FLAG_CHECKPATH)
1428 return CM_ERROR_NOSUCHPATH;
1430 return CM_ERROR_NOSUCHFILE;
1433 #ifdef DEBUG_REFCOUNT
1434 long cm_LookupDbg(cm_scache_t *dscp, clientchar_t *namep, long flags, cm_user_t *userp,
1435 cm_req_t *reqp, cm_scache_t **outScpp, char * file, long line)
1437 long cm_Lookup(cm_scache_t *dscp, clientchar_t *namep, long flags, cm_user_t *userp,
1438 cm_req_t *reqp, cm_scache_t **outScpp)
1442 clientchar_t tname[AFSPATHMAX];
1443 int sysNameIndex = 0;
1444 cm_scache_t *scp = NULL;
1446 #ifdef DEBUG_REFCOUNT
1447 afsi_log("%s:%d cm_Lookup dscp 0x%p ref %d", file, line, dscp, dscp->refCount, file, line);
1448 osi_Log2(afsd_logp, "cm_Lookup dscp 0x%p ref %d", dscp, dscp->refCount);
1451 if ( cm_ClientStrCmpI(namep,_C(SMB_IOCTL_FILENAME_NOSLASH)) == 0 ) {
1452 if (flags & CM_FLAG_CHECKPATH)
1453 return CM_ERROR_NOSUCHPATH;
1455 return CM_ERROR_NOSUCHFILE;
1458 if (dscp == cm_data.rootSCachep &&
1459 cm_ClientStrCmpNI(namep, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH) == 0) {
1460 return cm_EvaluateVolumeReference(namep, flags, userp, reqp, outScpp);
1463 if (cm_ExpandSysName(namep, NULL, 0, 0) > 0) {
1464 for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
1465 code = cm_ExpandSysName(namep, tname, lengthof(tname), sysNameIndex);
1467 code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
1468 #ifdef DEBUG_REFCOUNT
1469 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);
1470 osi_Log3(afsd_logp, "cm_LookupInternal (1) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1478 cm_ReleaseSCache(scp);
1482 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1483 #ifdef DEBUG_REFCOUNT
1484 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);
1485 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1492 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1493 #ifdef DEBUG_REFCOUNT
1494 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);
1495 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1501 /* None of the possible sysName expansions could be found */
1502 if (flags & CM_FLAG_CHECKPATH)
1503 return CM_ERROR_NOSUCHPATH;
1505 return CM_ERROR_NOSUCHFILE;
1508 /*! \brief Unlink a file name
1510 Encapsulates a call to RXAFS_RemoveFile().
1512 \param[in] dscp cm_scache_t pointing at the directory containing the
1513 name to be unlinked.
1515 \param[in] fnamep Original name to be unlinked. This is the
1516 name that will be passed into the RXAFS_RemoveFile() call.
1517 This parameter is optional. If not provided, the value will
1520 \param[in] came Client name to be unlinked. This name will be used
1521 to update the local directory caches.
1523 \param[in] userp cm_user_t for the request.
1525 \param[in] reqp Request tracker.
1528 long cm_Unlink(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t * cnamep,
1529 cm_user_t *userp, cm_req_t *reqp)
1535 AFSFetchStatus newDirStatus;
1537 struct rx_connection * rxconnp;
1539 cm_scache_t *scp = NULL;
1540 int free_fnamep = FALSE;
1542 memset(&volSync, 0, sizeof(volSync));
1544 if (fnamep == NULL) {
1547 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ, &dirop);
1549 code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
1552 cm_EndDirOp(&dirop);
1559 #ifdef AFS_FREELANCE_CLIENT
1560 if (cm_freelanceEnabled && dscp == cm_data.rootSCachep) {
1561 /* deleting a mount point from the root dir. */
1562 code = cm_FreelanceRemoveMount(fnamep);
1567 code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
1569 /* make sure we don't screw up the dir status during the merge */
1570 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
1572 lock_ObtainWrite(&dscp->rw);
1573 sflags = CM_SCACHESYNC_STOREDATA;
1574 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1575 lock_ReleaseWrite(&dscp->rw);
1577 cm_EndDirOp(&dirop);
1582 afsFid.Volume = dscp->fid.volume;
1583 afsFid.Vnode = dscp->fid.vnode;
1584 afsFid.Unique = dscp->fid.unique;
1586 osi_Log1(afsd_logp, "CALL RemoveFile scp 0x%p", dscp);
1588 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
1592 rxconnp = cm_GetRxConn(connp);
1593 code = RXAFS_RemoveFile(rxconnp, &afsFid, fnamep,
1594 &newDirStatus, &volSync);
1595 rx_PutConnection(rxconnp);
1597 } while (cm_Analyze(connp, userp, reqp, &dscp->fid, &volSync, NULL, NULL, code));
1598 code = cm_MapRPCError(code, reqp);
1601 osi_Log1(afsd_logp, "CALL RemoveFile FAILURE, code 0x%x", code);
1603 osi_Log0(afsd_logp, "CALL RemoveFile SUCCESS");
1606 lock_ObtainWrite(&dirop.scp->dirlock);
1607 dirop.lockType = CM_DIRLOCK_WRITE;
1609 lock_ObtainWrite(&dscp->rw);
1610 cm_dnlcRemove(dscp, cnamep);
1611 cm_SyncOpDone(dscp, NULL, sflags);
1613 cm_MergeStatus(NULL, dscp, &newDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
1614 } else if (code == CM_ERROR_NOSUCHFILE) {
1615 /* windows would not have allowed the request to delete the file
1616 * if it did not believe the file existed. therefore, we must
1617 * have an inconsistent view of the world.
1619 dscp->cbServerp = NULL;
1621 lock_ReleaseWrite(&dscp->rw);
1623 if (code == 0 && cm_CheckDirOpForSingleChange(&dirop) && cnamep) {
1624 cm_DirDeleteEntry(&dirop, fnamep);
1626 cm_BPlusDirDeleteEntry(&dirop, cnamep);
1629 cm_EndDirOp(&dirop);
1632 cm_ReleaseSCache(scp);
1634 lock_ObtainWrite(&scp->rw);
1635 if (--scp->linkCount == 0)
1636 scp->flags |= CM_SCACHEFLAG_DELETED;
1637 cm_DiscardSCache(scp);
1638 lock_ReleaseWrite(&scp->rw);
1649 /* called with a write locked vnode, and fills in the link info.
1650 * returns this the vnode still write locked.
1652 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1659 lock_AssertWrite(&linkScp->rw);
1660 if (!linkScp->mountPointStringp[0]) {
1662 #ifdef AFS_FREELANCE_CLIENT
1663 /* File servers do not have data for freelance entries */
1664 if (cm_freelanceEnabled &&
1665 linkScp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
1666 linkScp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
1668 code = cm_FreelanceFetchMountPointString(linkScp);
1670 #endif /* AFS_FREELANCE_CLIENT */
1672 /* read the link data from the file server*/
1673 lock_ReleaseWrite(&linkScp->rw);
1674 thyper.LowPart = thyper.HighPart = 0;
1675 code = buf_Get(linkScp, &thyper, reqp, &bufp);
1676 lock_ObtainWrite(&linkScp->rw);
1680 code = cm_SyncOp(linkScp, bufp, userp, reqp, 0,
1681 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1686 cm_SyncOpDone(linkScp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1688 if (cm_HaveBuffer(linkScp, bufp, 0))
1691 code = cm_GetBuffer(linkScp, bufp, NULL, userp, reqp);
1696 } /* while loop to get the data */
1698 /* now if we still have no link read in,
1699 * copy the data from the buffer */
1700 if ((temp = linkScp->length.LowPart) >= MOUNTPOINTLEN) {
1702 return CM_ERROR_TOOBIG;
1705 /* otherwise, it fits; make sure it is still null (could have
1706 * lost race with someone else referencing this link above),
1707 * and if so, copy in the data.
1709 if (!linkScp->mountPointStringp[0]) {
1710 strncpy(linkScp->mountPointStringp, bufp->datap, temp);
1711 linkScp->mountPointStringp[temp] = 0; /* null terminate */
1716 if ( !strnicmp(linkScp->mountPointStringp, "msdfs:", strlen("msdfs:")) )
1717 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1719 } /* don't have sym link contents cached */
1724 /* called with a held vnode and a path suffix, with the held vnode being a
1725 * symbolic link. Our goal is to generate a new path to interpret, and return
1726 * this new path in newSpaceBufferp. If the new vnode is relative to a dir
1727 * other than the directory containing the symbolic link, then the new root is
1728 * returned in *newRootScpp, otherwise a null is returned there.
1730 long cm_AssembleLink(cm_scache_t *linkScp, fschar_t *pathSuffixp,
1731 cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1732 cm_user_t *userp, cm_req_t *reqp)
1739 *newRootScpp = NULL;
1740 *newSpaceBufferp = NULL;
1742 lock_ObtainWrite(&linkScp->rw);
1743 code = cm_HandleLink(linkScp, userp, reqp);
1747 /* if we may overflow the buffer, bail out; buffer is signficantly
1748 * bigger than max path length, so we don't really have to worry about
1749 * being a little conservative here.
1751 if (cm_FsStrLen(linkScp->mountPointStringp) + cm_FsStrLen(pathSuffixp) + 2
1752 >= CM_UTILS_SPACESIZE) {
1753 code = CM_ERROR_TOOBIG;
1757 tsp = cm_GetSpace();
1758 linkp = linkScp->mountPointStringp;
1759 if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1760 if (strlen(linkp) > cm_mountRootLen)
1761 StringCbCopyA((char *) tsp->data, sizeof(tsp->data), linkp+cm_mountRootLen+1);
1764 *newRootScpp = cm_data.rootSCachep;
1765 cm_HoldSCache(cm_data.rootSCachep);
1766 } else if (linkp[0] == '\\' && linkp[1] == '\\') {
1767 if (!strnicmp(&linkp[2], cm_NetbiosName, (len = (long)strlen(cm_NetbiosName))))
1769 char * p = &linkp[len + 3];
1770 if (strnicmp(p, "all", 3) == 0)
1773 StringCbCopyA(tsp->data, sizeof(tsp->data), p);
1774 for (p = tsp->data; *p; p++) {
1778 *newRootScpp = cm_data.rootSCachep;
1779 cm_HoldSCache(cm_data.rootSCachep);
1781 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1782 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1783 code = CM_ERROR_PATH_NOT_COVERED;
1785 } else if ( linkScp->fileType == CM_SCACHETYPE_DFSLINK ||
1786 !strnicmp(linkp, "msdfs:", (len = (long)strlen("msdfs:"))) ) {
1787 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1788 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1789 code = CM_ERROR_PATH_NOT_COVERED;
1790 } else if (*linkp == '\\' || *linkp == '/') {
1792 /* formerly, this was considered to be from the AFS root,
1793 * but this seems to create problems. instead, we will just
1794 * reject the link */
1795 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp+1);
1796 *newRootScpp = cm_data.rootSCachep;
1797 cm_HoldSCache(cm_data.rootSCachep);
1799 /* we still copy the link data into the response so that
1800 * the user can see what the link points to
1802 linkScp->fileType = CM_SCACHETYPE_INVALID;
1803 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1804 code = CM_ERROR_NOSUCHPATH;
1807 /* a relative link */
1808 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1810 if (pathSuffixp[0] != 0) { /* if suffix string is non-null */
1811 StringCchCatA(tsp->data,lengthof(tsp->data), "\\");
1812 StringCchCatA(tsp->data,lengthof(tsp->data), pathSuffixp);
1816 clientchar_t * cpath = cm_FsStringToClientStringAlloc(tsp->data, -1, NULL);
1817 if (cpath != NULL) {
1818 cm_ClientStrCpy(tsp->wdata, lengthof(tsp->wdata), cpath);
1820 *newSpaceBufferp = tsp;
1822 code = CM_ERROR_NOSUCHPATH;
1829 if (code == CM_ERROR_PATH_NOT_COVERED && reqp->tidPathp && reqp->relPathp) {
1830 cm_VolStatus_Notify_DFS_Mapping(linkScp, reqp->tidPathp, reqp->relPathp);
1835 lock_ReleaseWrite(&linkScp->rw);
1838 #ifdef DEBUG_REFCOUNT
1839 long cm_NameIDbg(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
1840 cm_user_t *userp, clientchar_t *tidPathp, cm_req_t *reqp,
1841 cm_scache_t **outScpp,
1842 char * file, long line)
1844 long cm_NameI(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
1845 cm_user_t *userp, clientchar_t *tidPathp,
1846 cm_req_t *reqp, cm_scache_t **outScpp)
1850 clientchar_t *tp; /* ptr moving through input buffer */
1851 clientchar_t tc; /* temp char */
1852 int haveComponent; /* has new component started? */
1853 clientchar_t component[AFSPATHMAX]; /* this is the new component */
1854 clientchar_t *cp; /* component name being assembled */
1855 cm_scache_t *tscp; /* current location in the hierarchy */
1856 cm_scache_t *nscp; /* next dude down */
1857 cm_scache_t *dirScp; /* last dir we searched */
1858 cm_scache_t *linkScp; /* new root for the symlink we just
1860 cm_space_t *psp; /* space for current path, if we've hit
1862 cm_space_t *tempsp; /* temp vbl */
1863 clientchar_t *restp; /* rest of the pathname to interpret */
1864 int symlinkCount; /* count of # of symlinks traversed */
1865 int extraFlag; /* avoid chasing mt pts for dir cmd */
1866 int phase = 1; /* 1 = tidPathp, 2 = pathp */
1867 #define MAX_FID_COUNT 512
1868 cm_fid_t fids[MAX_FID_COUNT]; /* array of fids processed in this path walk */
1869 int fid_count = 0; /* number of fids processed in this path walk */
1874 #ifdef DEBUG_REFCOUNT
1875 afsi_log("%s:%d cm_NameI rootscp 0x%p ref %d", file, line, rootSCachep, rootSCachep->refCount);
1876 osi_Log4(afsd_logp,"cm_NameI rootscp 0x%p path %S tidpath %S flags 0x%x",
1877 rootSCachep, pathp ? pathp : L"<NULL>", tidPathp ? tidPathp : L"<NULL>",
1892 cm_HoldSCache(tscp);
1900 /* map Unix slashes into DOS ones so we can interpret Unix
1906 if (!haveComponent) {
1909 } else if (tc == 0) {
1923 /* we have a component here */
1924 if (tc == 0 || tc == '\\') {
1925 /* end of the component; we're at the last
1926 * component if tc == 0. However, if the last
1927 * is a symlink, we have more to do.
1929 *cp++ = 0; /* add null termination */
1931 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
1932 extraFlag = CM_FLAG_NOMOUNTCHASE;
1933 code = cm_Lookup(tscp, component,
1935 userp, reqp, &nscp);
1938 if (!cm_ClientStrCmp(component,_C("..")) ||
1939 !cm_ClientStrCmp(component,_C("."))) {
1941 * roll back the fid list until we find the
1942 * fid that matches where we are now. Its not
1943 * necessarily one or two fids because they
1944 * might have been symlinks or mount points or
1945 * both that were crossed.
1947 for ( i=fid_count-1; i>=0; i--) {
1948 if (!cm_FidCmp(&nscp->fid, &fids[i]))
1953 /* add the new fid to the list */
1954 if (fid_count == MAX_FID_COUNT) {
1955 code = CM_ERROR_TOO_MANY_SYMLINKS;
1956 cm_ReleaseSCache(nscp);
1960 fids[fid_count++] = nscp->fid;
1965 cm_ReleaseSCache(tscp);
1967 cm_ReleaseSCache(dirScp);
1970 if ((code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH) &&
1971 tscp->fileType == CM_SCACHETYPE_SYMLINK) {
1972 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_NOSUCHPATH");
1973 return CM_ERROR_NOSUCHPATH;
1975 osi_Log1(afsd_logp,"cm_NameI code 0x%x", code);
1980 haveComponent = 0; /* component done */
1982 cm_ReleaseSCache(dirScp);
1983 dirScp = tscp; /* for some symlinks */
1984 tscp = nscp; /* already held */
1986 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
1989 cm_ReleaseSCache(dirScp);
1995 /* now, if tscp is a symlink, we should follow it and
1996 * assemble the path again.
1998 lock_ObtainWrite(&tscp->rw);
1999 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
2000 CM_SCACHESYNC_GETSTATUS
2001 | CM_SCACHESYNC_NEEDCALLBACK);
2003 lock_ReleaseWrite(&tscp->rw);
2004 cm_ReleaseSCache(tscp);
2007 cm_ReleaseSCache(dirScp);
2012 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2014 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
2015 /* this is a symlink; assemble a new buffer */
2016 lock_ReleaseWrite(&tscp->rw);
2017 if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
2018 cm_ReleaseSCache(tscp);
2021 cm_ReleaseSCache(dirScp);
2026 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_TOO_MANY_SYMLINKS");
2027 return CM_ERROR_TOO_MANY_SYMLINKS;
2037 /* TODO: make this better */
2038 frestp = cm_ClientStringToFsStringAlloc(restp, -1, NULL);
2039 code = cm_AssembleLink(tscp, frestp, &linkScp, &tempsp, userp, reqp);
2043 if (code == 0 && linkScp != NULL) {
2044 if (linkScp == cm_data.rootSCachep) {
2048 for ( i=0; i<fid_count; i++) {
2049 if ( !cm_FidCmp(&linkScp->fid, &fids[i]) ) {
2050 code = CM_ERROR_TOO_MANY_SYMLINKS;
2051 cm_ReleaseSCache(linkScp);
2057 if (i == fid_count && fid_count < MAX_FID_COUNT) {
2058 fids[fid_count++] = linkScp->fid;
2063 /* something went wrong */
2064 cm_ReleaseSCache(tscp);
2067 cm_ReleaseSCache(dirScp);
2073 /* otherwise, tempsp has the new path,
2074 * and linkScp is the new root from
2075 * which to interpret that path.
2076 * Continue with the namei processing,
2077 * also doing the bookkeeping for the
2078 * space allocation and tracking the
2079 * vnode reference counts.
2085 cm_ReleaseSCache(tscp);
2090 * now, if linkScp is null, that's
2091 * AssembleLink's way of telling us that
2092 * the sym link is relative to the dir
2093 * containing the link. We have a ref
2094 * to it in dirScp, and we hold it now
2095 * and reuse it as the new spot in the
2103 /* not a symlink, we may be done */
2104 lock_ReleaseWrite(&tscp->rw);
2112 cm_ReleaseSCache(dirScp);
2120 cm_ReleaseSCache(dirScp);
2123 } /* end of a component */
2126 } /* we have a component */
2127 } /* big while loop over all components */
2131 cm_ReleaseSCache(dirScp);
2137 cm_ReleaseSCache(tscp);
2139 #ifdef DEBUG_REFCOUNT
2140 afsi_log("%s:%d cm_NameI code 0x%x outScpp 0x%p ref %d", file, line, code, *outScpp, (*outScpp) ? (*outScpp)->refCount : 0);
2142 osi_Log2(afsd_logp,"cm_NameI code 0x%x outScpp 0x%p", code, *outScpp);
2146 /* called with a dir, and a vnode within the dir that happens to be a symlink.
2147 * We chase the link, and return a held pointer to the target, if it exists,
2148 * in *outScpp. If we succeed, we return 0, otherwise we return an error code
2149 * and do not hold or return a target vnode.
2151 * This is very similar to calling cm_NameI with the last component of a name,
2152 * which happens to be a symlink, except that we've already passed by the name.
2154 * This function is typically called by the directory listing functions, which
2155 * encounter symlinks but need to return the proper file length so programs
2156 * like "more" work properly when they make use of the attributes retrieved from
2159 * The input vnode should not be locked when this function is called.
2161 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
2162 cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
2166 cm_scache_t *newRootScp;
2170 osi_Log1(afsd_logp, "Evaluating symlink scp 0x%p", linkScp);
2172 code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
2176 /* now, if newRootScp is NULL, we're really being told that the symlink
2177 * is relative to the current directory (dscp).
2179 if (newRootScp == NULL) {
2181 cm_HoldSCache(dscp);
2184 code = cm_NameI(newRootScp, spacep->wdata,
2185 CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
2186 userp, NULL, reqp, outScpp);
2188 if (code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH)
2189 code = CM_ERROR_NOSUCHPATH;
2191 /* this stuff is allocated no matter what happened on the namei call,
2193 cm_FreeSpace(spacep);
2194 cm_ReleaseSCache(newRootScp);
2196 if (linkScp == *outScpp) {
2197 cm_ReleaseSCache(*outScpp);
2199 code = CM_ERROR_NOSUCHPATH;
2205 /* for a given entry, make sure that it isn't in the stat cache, and then
2206 * add it to the list of file IDs to be obtained.
2208 * Don't bother adding it if we already have a vnode. Note that the dir
2209 * is locked, so we have to be careful checking the vnode we're thinking of
2210 * processing, to avoid deadlocks.
2212 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
2223 /* Don't overflow bsp. */
2224 if (bsp->counter >= CM_BULKMAX)
2225 return CM_ERROR_STOPNOW;
2227 thyper.LowPart = cm_data.buf_blockSize;
2228 thyper.HighPart = 0;
2229 thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
2231 /* thyper is now the first byte past the end of the record we're
2232 * interested in, and bsp->bufOffset is the first byte of the record
2233 * we're interested in.
2234 * Skip data in the others.
2237 if (LargeIntegerLessThan(*offp, bsp->bufOffset))
2239 if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
2240 return CM_ERROR_STOPNOW;
2241 if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
2244 cm_SetFid(&tfid, scp->fid.cell, scp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
2245 tscp = cm_FindSCache(&tfid);
2247 if (lock_TryWrite(&tscp->rw)) {
2248 /* we have an entry that we can look at */
2249 if (!(tscp->flags & CM_SCACHEFLAG_EACCESS) && cm_HaveCallback(tscp)) {
2250 /* we have a callback on it. Don't bother
2251 * fetching this stat entry, since we're happy
2252 * with the info we have.
2254 lock_ReleaseWrite(&tscp->rw);
2255 cm_ReleaseSCache(tscp);
2258 lock_ReleaseWrite(&tscp->rw);
2260 cm_ReleaseSCache(tscp);
2263 #ifdef AFS_FREELANCE_CLIENT
2264 // yj: if this is a mountpoint under root.afs then we don't want it
2265 // to be bulkstat-ed, instead, we call getSCache directly and under
2266 // getSCache, it is handled specially.
2267 if ( cm_freelanceEnabled &&
2268 tfid.cell==AFS_FAKE_ROOT_CELL_ID &&
2269 tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
2270 !(tfid.vnode==0x1 && tfid.unique==0x1) )
2272 osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
2273 return cm_GetSCache(&tfid, &tscp, NULL, NULL);
2275 #endif /* AFS_FREELANCE_CLIENT */
2278 bsp->fids[i].Volume = scp->fid.volume;
2279 bsp->fids[i].Vnode = tfid.vnode;
2280 bsp->fids[i].Unique = tfid.unique;
2285 cm_TryBulkStatRPC(cm_scache_t *dscp, cm_bulkStat_t *bbp, cm_user_t *userp, cm_req_t *reqp)
2288 AFSCBFids fidStruct;
2289 AFSBulkStats statStruct;
2291 AFSCBs callbackStruct;
2294 cm_callbackRequest_t cbReq;
2300 struct rx_connection * rxconnp;
2301 int inlinebulk = 0; /* Did we use InlineBulkStatus RPC or not? */
2303 memset(&volSync, 0, sizeof(volSync));
2305 /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
2306 * make the calls to create the entries. Handle AFSCBMAX files at a
2309 for (filex = 0; filex < bbp->counter; filex += filesThisCall) {
2310 filesThisCall = bbp->counter - filex;
2311 if (filesThisCall > AFSCBMAX)
2312 filesThisCall = AFSCBMAX;
2314 fidStruct.AFSCBFids_len = filesThisCall;
2315 fidStruct.AFSCBFids_val = &bbp->fids[filex];
2316 statStruct.AFSBulkStats_len = filesThisCall;
2317 statStruct.AFSBulkStats_val = &bbp->stats[filex];
2318 callbackStruct.AFSCBs_len = filesThisCall;
2319 callbackStruct.AFSCBs_val = &bbp->callbacks[filex];
2320 cm_StartCallbackGrantingCall(NULL, &cbReq);
2321 osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
2323 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2327 rxconnp = cm_GetRxConn(connp);
2328 if (!(connp->serverp->flags & CM_SERVERFLAG_NOINLINEBULK)) {
2329 code = RXAFS_InlineBulkStatus(rxconnp, &fidStruct,
2330 &statStruct, &callbackStruct, &volSync);
2331 if (code == RXGEN_OPCODE) {
2332 cm_SetServerNoInlineBulk(connp->serverp, 0);
2338 code = RXAFS_BulkStatus(rxconnp, &fidStruct,
2339 &statStruct, &callbackStruct, &volSync);
2341 rx_PutConnection(rxconnp);
2343 } while (cm_Analyze(connp, userp, reqp, &dscp->fid,
2344 &volSync, NULL, &cbReq, code));
2345 code = cm_MapRPCError(code, reqp);
2347 /* may as well quit on an error, since we're not going to do
2348 * much better on the next immediate call, either.
2351 osi_Log2(afsd_logp, "CALL %sBulkStatus FAILURE code 0x%x",
2352 inlinebulk ? "Inline" : "", code);
2353 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2356 osi_Log1(afsd_logp, "CALL %sBulkStatus SUCCESS", inlinebulk ? "Inline" : "");
2359 /* otherwise, we should do the merges */
2360 for (i = 0; i<filesThisCall; i++) {
2362 cm_SetFid(&tfid, dscp->fid.cell, bbp->fids[j].Volume, bbp->fids[j].Vnode, bbp->fids[j].Unique);
2363 code = cm_GetSCache(&tfid, &scp, userp, reqp);
2367 /* otherwise, if this entry has no callback info,
2370 lock_ObtainWrite(&scp->rw);
2371 /* now, we have to be extra paranoid on merging in this
2372 * information, since we didn't use cm_SyncOp before
2373 * starting the fetch to make sure that no bad races
2374 * were occurring. Specifically, we need to make sure
2375 * we don't obliterate any newer information in the
2376 * vnode than have here.
2378 * Right now, be pretty conservative: if there's a
2379 * callback or a pending call, skip it.
2380 * However, if the prior attempt to obtain status
2381 * was refused access or the volume is .readonly,
2382 * take the data in any case since we have nothing
2383 * better for the in flight directory enumeration that
2384 * resulted in this function being called.
2386 if ((scp->cbServerp == NULL &&
2387 !(scp->flags & (CM_SCACHEFLAG_FETCHING | CM_SCACHEFLAG_STORING | CM_SCACHEFLAG_SIZESTORING))) ||
2388 (scp->flags & CM_SCACHEFLAG_PURERO) ||
2389 (scp->flags & CM_SCACHEFLAG_EACCESS)) {
2390 cm_EndCallbackGrantingCall(scp, &cbReq,
2393 CM_CALLBACK_MAINTAINCOUNT);
2394 cm_MergeStatus(dscp, scp, &bbp->stats[j], &volSync, userp, reqp, 0);
2396 lock_ReleaseWrite(&scp->rw);
2397 cm_ReleaseSCache(scp);
2398 } /* all files in the response */
2399 /* now tell it to drop the count,
2400 * after doing the vnode processing above */
2401 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2402 } /* while there are still more files to process */
2404 /* If we did the InlineBulk RPC pull out the return code and log it */
2406 if ((&bbp->stats[0])->errorCode) {
2407 osi_Log1(afsd_logp, "cm_TryBulkStat bulk stat error: %d",
2408 (&bbp->stats[0])->errorCode);
2409 code = (&bbp->stats[0])->errorCode;
2416 /* called with a write locked scp and a pointer to a buffer. Make bulk stat
2417 * calls on all undeleted files in the page of the directory specified.
2420 cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
2426 osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%p", dscp);
2428 /* should be on a buffer boundary */
2429 osi_assertx((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0, "invalid offset");
2431 bbp = malloc(sizeof(cm_bulkStat_t));
2432 memset(bbp, 0, sizeof(cm_bulkStat_t));
2433 bbp->bufOffset = *offsetp;
2435 lock_ReleaseWrite(&dscp->rw);
2436 /* first, assemble the file IDs we need to stat */
2437 code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) bbp, offsetp, userp, reqp, NULL);
2439 /* if we failed, bail out early */
2440 if (code && code != CM_ERROR_STOPNOW) {
2442 lock_ObtainWrite(&dscp->rw);
2446 code = cm_TryBulkStatRPC(dscp, bbp, userp, reqp);
2447 osi_Log1(afsd_logp, "END cm_TryBulkStat code 0x%x", code);
2449 lock_ObtainWrite(&dscp->rw);
2454 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
2458 /* initialize store back mask as inexpensive local variable */
2460 memset(statusp, 0, sizeof(AFSStoreStatus));
2462 /* copy out queued info from scache first, if scp passed in */
2464 if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
2465 statusp->ClientModTime = scp->clientModTime;
2466 mask |= AFS_SETMODTIME;
2467 scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
2472 /* now add in our locally generated request */
2473 if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
2474 statusp->ClientModTime = attrp->clientModTime;
2475 mask |= AFS_SETMODTIME;
2477 if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
2478 statusp->UnixModeBits = attrp->unixModeBits;
2479 mask |= AFS_SETMODE;
2481 if (attrp->mask & CM_ATTRMASK_OWNER) {
2482 statusp->Owner = attrp->owner;
2483 mask |= AFS_SETOWNER;
2485 if (attrp->mask & CM_ATTRMASK_GROUP) {
2486 statusp->Group = attrp->group;
2487 mask |= AFS_SETGROUP;
2490 statusp->Mask = mask;
2493 /* set the file size, and make sure that all relevant buffers have been
2494 * truncated. Ensure that any partially truncated buffers have been zeroed
2495 * to the end of the buffer.
2497 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
2503 /* start by locking out buffer creation */
2504 lock_ObtainWrite(&scp->bufCreateLock);
2506 /* verify that this is a file, not a dir or a symlink */
2507 lock_ObtainWrite(&scp->rw);
2508 code = cm_SyncOp(scp, NULL, userp, reqp, 0,
2509 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2512 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2514 if (scp->fileType != CM_SCACHETYPE_FILE) {
2515 code = CM_ERROR_ISDIR;
2520 if (LargeIntegerLessThan(*sizep, scp->length))
2525 lock_ReleaseWrite(&scp->rw);
2527 /* can't hold scp->rw lock here, since we may wait for a storeback to
2528 * finish if the buffer package is cleaning a buffer by storing it to
2532 buf_Truncate(scp, userp, reqp, sizep);
2534 /* now ensure that file length is short enough, and update truncPos */
2535 lock_ObtainWrite(&scp->rw);
2537 /* make sure we have a callback (so we have the right value for the
2538 * length), and wait for it to be safe to do a truncate.
2540 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
2541 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2542 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2544 /* If we only have 'i' bits, then we should still be able to set
2545 the size of a file we created. */
2546 if (code == CM_ERROR_NOACCESS && scp->creator == userp) {
2547 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
2548 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2549 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2555 if (LargeIntegerLessThan(*sizep, scp->length)) {
2556 /* a real truncation. If truncPos is not set yet, or is bigger
2557 * than where we're truncating the file, set truncPos to this
2562 if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
2563 || LargeIntegerLessThan(*sizep, scp->length)) {
2565 scp->truncPos = *sizep;
2566 scp->mask |= CM_SCACHEMASK_TRUNCPOS;
2568 /* in either case, the new file size has been changed */
2569 scp->length = *sizep;
2570 scp->mask |= CM_SCACHEMASK_LENGTH;
2572 else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
2573 /* really extending the file */
2574 scp->length = *sizep;
2575 scp->mask |= CM_SCACHEMASK_LENGTH;
2578 /* done successfully */
2581 cm_SyncOpDone(scp, NULL,
2582 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2583 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2586 lock_ReleaseWrite(&scp->rw);
2587 lock_ReleaseWrite(&scp->bufCreateLock);
2592 /* set the file size or other attributes (but not both at once) */
2593 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
2597 AFSFetchStatus afsOutStatus;
2601 AFSStoreStatus afsInStatus;
2602 struct rx_connection * rxconnp;
2604 memset(&volSync, 0, sizeof(volSync));
2606 /* handle file length setting */
2607 if (attrp->mask & CM_ATTRMASK_LENGTH)
2608 return cm_SetLength(scp, &attrp->length, userp, reqp);
2610 lock_ObtainWrite(&scp->rw);
2611 /* otherwise, we have to make an RPC to get the status */
2612 code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
2614 lock_ReleaseWrite(&scp->rw);
2617 lock_ConvertWToR(&scp->rw);
2619 /* make the attr structure */
2620 cm_StatusFromAttr(&afsInStatus, scp, attrp);
2622 tfid.Volume = scp->fid.volume;
2623 tfid.Vnode = scp->fid.vnode;
2624 tfid.Unique = scp->fid.unique;
2625 lock_ReleaseRead(&scp->rw);
2627 /* now make the RPC */
2628 osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%p", scp);
2630 code = cm_ConnFromFID(&scp->fid, userp, reqp, &connp);
2634 rxconnp = cm_GetRxConn(connp);
2635 code = RXAFS_StoreStatus(rxconnp, &tfid,
2636 &afsInStatus, &afsOutStatus, &volSync);
2637 rx_PutConnection(rxconnp);
2639 } while (cm_Analyze(connp, userp, reqp,
2640 &scp->fid, &volSync, NULL, NULL, code));
2641 code = cm_MapRPCError(code, reqp);
2644 osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
2646 osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
2648 lock_ObtainWrite(&scp->rw);
2649 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
2651 cm_MergeStatus(NULL, scp, &afsOutStatus, &volSync, userp, reqp,
2652 CM_MERGEFLAG_FORCE|CM_MERGEFLAG_STOREDATA);
2654 /* if we're changing the mode bits, discard the ACL cache,
2655 * since we changed the mode bits.
2657 if (afsInStatus.Mask & AFS_SETMODE)
2658 cm_FreeAllACLEnts(scp);
2659 lock_ReleaseWrite(&scp->rw);
2663 long cm_Create(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
2664 cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
2669 cm_callbackRequest_t cbReq;
2672 cm_scache_t *scp = NULL;
2674 AFSStoreStatus inStatus;
2675 AFSFetchStatus updatedDirStatus;
2676 AFSFetchStatus newFileStatus;
2677 AFSCallBack newFileCallback;
2679 struct rx_connection * rxconnp;
2681 fschar_t * fnamep = NULL;
2683 memset(&volSync, 0, sizeof(volSync));
2685 /* can't create names with @sys in them; must expand it manually first.
2686 * return "invalid request" if they try.
2688 if (cm_ExpandSysName(cnamep, NULL, 0, 0)) {
2689 return CM_ERROR_ATSYS;
2692 #ifdef AFS_FREELANCE_CLIENT
2693 /* Freelance root volume does not hold files */
2694 if (cm_freelanceEnabled &&
2695 dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
2696 dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
2698 return CM_ERROR_NOACCESS;
2700 #endif /* AFS_FREELANCE_CLIENT */
2702 /* before starting the RPC, mark that we're changing the file data, so
2703 * that someone who does a chmod will know to wait until our call
2706 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2707 lock_ObtainWrite(&dscp->rw);
2708 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2709 lock_ReleaseWrite(&dscp->rw);
2711 cm_StartCallbackGrantingCall(NULL, &cbReq);
2713 cm_EndDirOp(&dirop);
2720 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
2722 cm_StatusFromAttr(&inStatus, NULL, attrp);
2724 /* try the RPC now */
2725 osi_Log1(afsd_logp, "CALL CreateFile scp 0x%p", dscp);
2727 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2731 dirAFSFid.Volume = dscp->fid.volume;
2732 dirAFSFid.Vnode = dscp->fid.vnode;
2733 dirAFSFid.Unique = dscp->fid.unique;
2735 rxconnp = cm_GetRxConn(connp);
2736 code = RXAFS_CreateFile(connp->rxconnp, &dirAFSFid, fnamep,
2737 &inStatus, &newAFSFid, &newFileStatus,
2738 &updatedDirStatus, &newFileCallback,
2740 rx_PutConnection(rxconnp);
2742 } while (cm_Analyze(connp, userp, reqp,
2743 &dscp->fid, &volSync, NULL, &cbReq, code));
2744 code = cm_MapRPCError(code, reqp);
2747 osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
2749 osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
2752 lock_ObtainWrite(&dirop.scp->dirlock);
2753 dirop.lockType = CM_DIRLOCK_WRITE;
2755 lock_ObtainWrite(&dscp->rw);
2756 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2758 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
2760 lock_ReleaseWrite(&dscp->rw);
2762 /* now try to create the file's entry, too, but be careful to
2763 * make sure that we don't merge in old info. Since we weren't locking
2764 * out any requests during the file's creation, we may have pretty old
2768 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
2769 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2771 lock_ObtainWrite(&scp->rw);
2772 scp->creator = userp; /* remember who created it */
2773 if (!cm_HaveCallback(scp)) {
2774 cm_EndCallbackGrantingCall(scp, &cbReq,
2775 &newFileCallback, &volSync, 0);
2776 cm_MergeStatus(dscp, scp, &newFileStatus, &volSync,
2780 lock_ReleaseWrite(&scp->rw);
2784 /* make sure we end things properly */
2786 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2788 if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
2789 cm_DirCreateEntry(&dirop, fnamep, &newFid);
2791 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
2794 cm_EndDirOp(&dirop);
2803 cm_ReleaseSCache(scp);
2809 * locked if TRUE means write-locked
2810 * else the cm_scache_t rw must not be held
2812 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp, afs_uint32 locked)
2817 lock_ReleaseWrite(&scp->rw);
2818 code = buf_CleanVnode(scp, userp, reqp);
2820 lock_ObtainWrite(&scp->rw);
2822 if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
2823 | CM_SCACHEMASK_CLIENTMODTIME
2824 | CM_SCACHEMASK_LENGTH))
2825 code = cm_StoreMini(scp, userp, reqp);
2827 if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) {
2828 code = (scp->flags & CM_SCACHEFLAG_OVERQUOTA) ? CM_ERROR_QUOTA : CM_ERROR_SPACE;
2829 scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE);
2833 lock_ReleaseWrite(&scp->rw);
2834 } else if (locked) {
2835 lock_ObtainWrite(&scp->rw);
2840 long cm_MakeDir(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
2841 cm_user_t *userp, cm_req_t *reqp, cm_scache_t **scpp)
2846 cm_callbackRequest_t cbReq;
2849 cm_scache_t *scp = NULL;
2851 AFSStoreStatus inStatus;
2852 AFSFetchStatus updatedDirStatus;
2853 AFSFetchStatus newDirStatus;
2854 AFSCallBack newDirCallback;
2856 struct rx_connection * rxconnp;
2858 fschar_t * fnamep = NULL;
2860 memset(&volSync, 0, sizeof(volSync));
2862 /* can't create names with @sys in them; must expand it manually first.
2863 * return "invalid request" if they try.
2865 if (cm_ExpandSysName(cnamep, NULL, 0, 0)) {
2866 return CM_ERROR_ATSYS;
2869 #ifdef AFS_FREELANCE_CLIENT
2870 /* Freelance root volume does not hold subdirectories */
2871 if (cm_freelanceEnabled &&
2872 dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
2873 dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
2875 return CM_ERROR_NOACCESS;
2877 #endif /* AFS_FREELANCE_CLIENT */
2879 /* before starting the RPC, mark that we're changing the directory
2880 * data, so that someone who does a chmod on the dir will wait until
2881 * our call completes.
2883 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2884 lock_ObtainWrite(&dscp->rw);
2885 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2886 lock_ReleaseWrite(&dscp->rw);
2888 cm_StartCallbackGrantingCall(NULL, &cbReq);
2890 cm_EndDirOp(&dirop);
2897 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
2898 cm_StatusFromAttr(&inStatus, NULL, attrp);
2900 /* try the RPC now */
2901 osi_Log1(afsd_logp, "CALL MakeDir scp 0x%p", dscp);
2903 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2907 dirAFSFid.Volume = dscp->fid.volume;
2908 dirAFSFid.Vnode = dscp->fid.vnode;
2909 dirAFSFid.Unique = dscp->fid.unique;
2911 rxconnp = cm_GetRxConn(connp);
2912 code = RXAFS_MakeDir(connp->rxconnp, &dirAFSFid, fnamep,
2913 &inStatus, &newAFSFid, &newDirStatus,
2914 &updatedDirStatus, &newDirCallback,
2916 rx_PutConnection(rxconnp);
2918 } while (cm_Analyze(connp, userp, reqp,
2919 &dscp->fid, &volSync, NULL, &cbReq, code));
2920 code = cm_MapRPCError(code, reqp);
2923 osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
2925 osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
2928 lock_ObtainWrite(&dirop.scp->dirlock);
2929 dirop.lockType = CM_DIRLOCK_WRITE;
2931 lock_ObtainWrite(&dscp->rw);
2932 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2934 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
2936 lock_ReleaseWrite(&dscp->rw);
2938 /* now try to create the new dir's entry, too, but be careful to
2939 * make sure that we don't merge in old info. Since we weren't locking
2940 * out any requests during the file's creation, we may have pretty old
2944 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
2945 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2947 lock_ObtainWrite(&scp->rw);
2948 if (!cm_HaveCallback(scp)) {
2949 cm_EndCallbackGrantingCall(scp, &cbReq,
2950 &newDirCallback, &volSync, 0);
2951 cm_MergeStatus(dscp, scp, &newDirStatus, &volSync,
2955 lock_ReleaseWrite(&scp->rw);
2959 /* make sure we end things properly */
2961 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2963 if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
2964 cm_DirCreateEntry(&dirop, fnamep, &newFid);
2966 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
2969 cm_EndDirOp(&dirop);
2977 cm_ReleaseSCache(scp);
2980 /* and return error code */
2984 long cm_Link(cm_scache_t *dscp, clientchar_t *cnamep, cm_scache_t *sscp, long flags,
2985 cm_user_t *userp, cm_req_t *reqp)
2990 AFSFid existingAFSFid;
2991 AFSFetchStatus updatedDirStatus;
2992 AFSFetchStatus newLinkStatus;
2994 struct rx_connection * rxconnp;
2996 fschar_t * fnamep = NULL;
2998 memset(&volSync, 0, sizeof(volSync));
3000 if (dscp->fid.cell != sscp->fid.cell ||
3001 dscp->fid.volume != sscp->fid.volume) {
3002 return CM_ERROR_CROSSDEVLINK;
3005 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
3006 lock_ObtainWrite(&dscp->rw);
3007 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3008 lock_ReleaseWrite(&dscp->rw);
3010 cm_EndDirOp(&dirop);
3015 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3017 /* try the RPC now */
3018 osi_Log1(afsd_logp, "CALL Link scp 0x%p", dscp);
3020 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3023 dirAFSFid.Volume = dscp->fid.volume;
3024 dirAFSFid.Vnode = dscp->fid.vnode;
3025 dirAFSFid.Unique = dscp->fid.unique;
3027 existingAFSFid.Volume = sscp->fid.volume;
3028 existingAFSFid.Vnode = sscp->fid.vnode;
3029 existingAFSFid.Unique = sscp->fid.unique;
3031 rxconnp = cm_GetRxConn(connp);
3032 code = RXAFS_Link(rxconnp, &dirAFSFid, fnamep, &existingAFSFid,
3033 &newLinkStatus, &updatedDirStatus, &volSync);
3034 rx_PutConnection(rxconnp);
3035 osi_Log1(afsd_logp," RXAFS_Link returns 0x%x", code);
3037 } while (cm_Analyze(connp, userp, reqp,
3038 &dscp->fid, &volSync, NULL, NULL, code));
3040 code = cm_MapRPCError(code, reqp);
3043 osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
3045 osi_Log0(afsd_logp, "CALL Link SUCCESS");
3048 lock_ObtainWrite(&dirop.scp->dirlock);
3049 dirop.lockType = CM_DIRLOCK_WRITE;
3051 lock_ObtainWrite(&dscp->rw);
3052 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3054 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3056 lock_ReleaseWrite(&dscp->rw);
3059 if (cm_CheckDirOpForSingleChange(&dirop)) {
3060 cm_DirCreateEntry(&dirop, fnamep, &sscp->fid);
3062 cm_BPlusDirCreateEntry(&dirop, cnamep, &sscp->fid);
3066 cm_EndDirOp(&dirop);
3068 /* Update the linked object status */
3070 lock_ObtainWrite(&sscp->rw);
3071 cm_MergeStatus(NULL, sscp, &newLinkStatus, &volSync, userp, reqp, 0);
3072 lock_ReleaseWrite(&sscp->rw);
3080 long cm_SymLink(cm_scache_t *dscp, clientchar_t *cnamep, fschar_t *contentsp, long flags,
3081 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
3089 AFSStoreStatus inStatus;
3090 AFSFetchStatus updatedDirStatus;
3091 AFSFetchStatus newLinkStatus;
3093 struct rx_connection * rxconnp;
3095 fschar_t *fnamep = NULL;
3097 memset(&volSync, 0, sizeof(volSync));
3099 /* before starting the RPC, mark that we're changing the directory data,
3100 * so that someone who does a chmod on the dir will wait until our
3103 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
3104 lock_ObtainWrite(&dscp->rw);
3105 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3106 lock_ReleaseWrite(&dscp->rw);
3108 cm_EndDirOp(&dirop);
3113 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3115 cm_StatusFromAttr(&inStatus, NULL, attrp);
3117 /* try the RPC now */
3118 osi_Log1(afsd_logp, "CALL Symlink scp 0x%p", dscp);
3120 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3124 dirAFSFid.Volume = dscp->fid.volume;
3125 dirAFSFid.Vnode = dscp->fid.vnode;
3126 dirAFSFid.Unique = dscp->fid.unique;
3128 rxconnp = cm_GetRxConn(connp);
3129 code = RXAFS_Symlink(rxconnp, &dirAFSFid, fnamep, contentsp,
3130 &inStatus, &newAFSFid, &newLinkStatus,
3131 &updatedDirStatus, &volSync);
3132 rx_PutConnection(rxconnp);
3134 } while (cm_Analyze(connp, userp, reqp,
3135 &dscp->fid, &volSync, NULL, NULL, code));
3136 code = cm_MapRPCError(code, reqp);
3139 osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
3141 osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
3144 lock_ObtainWrite(&dirop.scp->dirlock);
3145 dirop.lockType = CM_DIRLOCK_WRITE;
3147 lock_ObtainWrite(&dscp->rw);
3148 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3150 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3152 lock_ReleaseWrite(&dscp->rw);
3155 if (cm_CheckDirOpForSingleChange(&dirop)) {
3156 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3158 cm_DirCreateEntry(&dirop, fnamep, &newFid);
3160 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
3164 cm_EndDirOp(&dirop);
3166 /* now try to create the new dir's entry, too, but be careful to
3167 * make sure that we don't merge in old info. Since we weren't locking
3168 * out any requests during the file's creation, we may have pretty old
3172 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3173 code = cm_GetSCache(&newFid, &scp, userp, reqp);
3175 lock_ObtainWrite(&scp->rw);
3176 if (!cm_HaveCallback(scp)) {
3177 cm_MergeStatus(dscp, scp, &newLinkStatus, &volSync,
3180 lock_ReleaseWrite(&scp->rw);
3181 cm_ReleaseSCache(scp);
3187 /* and return error code */
3191 /*! \brief Remove a directory
3193 Encapsulates a call to RXAFS_RemoveDir().
3195 \param[in] dscp cm_scache_t for the directory containing the
3196 directory to be removed.
3198 \param[in] fnamep This will be the original name of the directory
3199 as known to the file server. It will be passed in to RXAFS_RemoveDir().
3200 This parameter is optional. If it is not provided the value
3203 \param[in] cnamep Normalized name used to update the local
3206 \param[in] userp cm_user_t for the request.
3208 \param[in] reqp Request tracker.
3210 long cm_RemoveDir(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t *cnamep, cm_user_t *userp, cm_req_t *reqp)
3216 AFSFetchStatus updatedDirStatus;
3218 struct rx_connection * rxconnp;
3220 cm_scache_t *scp = NULL;
3221 int free_fnamep = FALSE;
3223 memset(&volSync, 0, sizeof(volSync));
3225 if (fnamep == NULL) {
3228 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ, &dirop);
3230 code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
3233 cm_EndDirOp(&dirop);
3240 code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
3244 /* before starting the RPC, mark that we're changing the directory data,
3245 * so that someone who does a chmod on the dir will wait until our
3248 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
3249 lock_ObtainWrite(&dscp->rw);
3250 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3251 lock_ReleaseWrite(&dscp->rw);
3253 cm_EndDirOp(&dirop);
3258 /* try the RPC now */
3259 osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%p", dscp);
3261 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3265 dirAFSFid.Volume = dscp->fid.volume;
3266 dirAFSFid.Vnode = dscp->fid.vnode;
3267 dirAFSFid.Unique = dscp->fid.unique;
3269 rxconnp = cm_GetRxConn(connp);
3270 code = RXAFS_RemoveDir(rxconnp, &dirAFSFid, fnamep,
3271 &updatedDirStatus, &volSync);
3272 rx_PutConnection(rxconnp);
3274 } while (cm_Analyze(connp, userp, reqp,
3275 &dscp->fid, &volSync, NULL, NULL, code));
3276 code = cm_MapRPCErrorRmdir(code, reqp);
3279 osi_Log1(afsd_logp, "CALL RemoveDir FAILURE, code 0x%x", code);
3281 osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
3284 lock_ObtainWrite(&dirop.scp->dirlock);
3285 dirop.lockType = CM_DIRLOCK_WRITE;
3287 lock_ObtainWrite(&dscp->rw);
3288 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3290 cm_dnlcRemove(dscp, cnamep);
3291 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3293 lock_ReleaseWrite(&dscp->rw);
3296 if (cm_CheckDirOpForSingleChange(&dirop) && cnamep != NULL) {
3297 cm_DirDeleteEntry(&dirop, fnamep);
3299 cm_BPlusDirDeleteEntry(&dirop, cnamep);
3303 cm_EndDirOp(&dirop);
3306 cm_ReleaseSCache(scp);
3308 lock_ObtainWrite(&scp->rw);
3309 scp->flags |= CM_SCACHEFLAG_DELETED;
3310 lock_ReleaseWrite(&scp->rw);
3318 /* and return error code */
3322 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
3324 /* grab mutex on contents */
3325 lock_ObtainWrite(&scp->rw);
3327 /* reset the prefetch info */
3328 scp->prefetch.base.LowPart = 0; /* base */
3329 scp->prefetch.base.HighPart = 0;
3330 scp->prefetch.end.LowPart = 0; /* and end */
3331 scp->prefetch.end.HighPart = 0;
3333 /* release mutex on contents */
3334 lock_ReleaseWrite(&scp->rw);
3340 /*! \brief Rename a file or directory
3342 Encapsulates a RXAFS_Rename() call.
3344 \param[in] oldDscp cm_scache_t for the directory containing the old
3347 \param[in] oldNamep The original old name known to the file server.
3348 This is the name that will be passed into the RXAFS_Rename().
3349 If it is not provided, it will be looked up.
3351 \param[in] normalizedOldNamep Normalized old name. This is used for
3352 updating local directory caches.
3354 \param[in] newDscp cm_scache_t for the directory containing the new
3357 \param[in] newNamep New name. Normalized.
3359 \param[in] userp cm_user_t for the request.
3361 \param[in,out] reqp Request tracker.
3364 long cm_Rename(cm_scache_t *oldDscp, fschar_t *oldNamep, clientchar_t *cOldNamep,
3365 cm_scache_t *newDscp, clientchar_t *cNewNamep, cm_user_t *userp,
3370 AFSFid oldDirAFSFid;
3371 AFSFid newDirAFSFid;
3373 AFSFetchStatus updatedOldDirStatus;
3374 AFSFetchStatus updatedNewDirStatus;
3377 struct rx_connection * rxconnp;
3378 cm_dirOp_t oldDirOp;
3381 cm_dirOp_t newDirOp;
3382 fschar_t * newNamep = NULL;
3383 int free_oldNamep = FALSE;
3384 cm_scache_t *oldScp = NULL, *newScp = NULL;
3386 memset(&volSync, 0, sizeof(volSync));
3388 if (cOldNamep == NULL || cNewNamep == NULL ||
3389 cm_ClientStrLen(cOldNamep) == 0 ||
3390 cm_ClientStrLen(cNewNamep) == 0)
3391 return CM_ERROR_INVAL;
3394 * Before we permit the operation, make sure that we do not already have
3395 * an object in the destination directory that has a case-insensitive match
3396 * for this name UNLESS the matching object is the object we are renaming.
3398 code = cm_Lookup(oldDscp, cOldNamep, 0, userp, reqp, &oldScp);
3400 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S old name lookup failed",
3401 oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3405 code = cm_Lookup(newDscp, cNewNamep, CM_FLAG_CASEFOLD, userp, reqp, &newScp);
3407 /* found a matching object with the new name */
3408 if (cm_FidCmp(&oldScp->fid, &newScp->fid)) {
3409 /* and they don't match so return an error */
3410 osi_Log2(afsd_logp, "cm_Rename newDscp 0x%p cNewName %S new name already exists",
3411 newDscp, osi_LogSaveStringW(afsd_logp, cNewNamep));
3412 code = CM_ERROR_EXISTS;
3414 cm_ReleaseSCache(newScp);
3416 } else if (code == CM_ERROR_AMBIGUOUS_FILENAME) {
3417 code = CM_ERROR_EXISTS;
3424 if (oldNamep == NULL) {
3427 code = cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_READ, &oldDirOp);
3429 code = cm_BPlusDirLookupOriginalName(&oldDirOp, cOldNamep, &oldNamep);
3431 free_oldNamep = TRUE;
3432 cm_EndDirOp(&oldDirOp);
3436 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S Original Name lookup failed",
3437 oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3443 /* before starting the RPC, mark that we're changing the directory data,
3444 * so that someone who does a chmod on the dir will wait until our call
3445 * completes. We do this in vnode order so that we don't deadlock,
3446 * which makes the code a little verbose.
3448 if (oldDscp == newDscp) {
3449 /* check for identical names */
3450 if (cm_ClientStrCmp(cOldNamep, cNewNamep) == 0) {
3451 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_RENAME_IDENTICAL",
3453 code = CM_ERROR_RENAME_IDENTICAL;
3458 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3459 lock_ObtainWrite(&oldDscp->rw);
3460 cm_dnlcRemove(oldDscp, cOldNamep);
3461 cm_dnlcRemove(oldDscp, cNewNamep);
3462 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3463 CM_SCACHESYNC_STOREDATA);
3464 lock_ReleaseWrite(&oldDscp->rw);
3466 cm_EndDirOp(&oldDirOp);
3470 /* two distinct dir vnodes */
3472 if (oldDscp->fid.cell != newDscp->fid.cell ||
3473 oldDscp->fid.volume != newDscp->fid.volume) {
3474 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_CROSSDEVLINK",
3476 code = CM_ERROR_CROSSDEVLINK;
3480 /* shouldn't happen that we have distinct vnodes for two
3481 * different files, but could due to deliberate attack, or
3482 * stale info. Avoid deadlocks and quit now.
3484 if (oldDscp->fid.vnode == newDscp->fid.vnode) {
3485 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p vnode collision",
3487 code = CM_ERROR_CROSSDEVLINK;
3491 if (oldDscp->fid.vnode < newDscp->fid.vnode) {
3492 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3493 lock_ObtainWrite(&oldDscp->rw);
3494 cm_dnlcRemove(oldDscp, cOldNamep);
3495 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3496 CM_SCACHESYNC_STOREDATA);
3497 lock_ReleaseWrite(&oldDscp->rw);
3499 cm_EndDirOp(&oldDirOp);
3501 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE, &newDirOp);
3502 lock_ObtainWrite(&newDscp->rw);
3503 cm_dnlcRemove(newDscp, cNewNamep);
3504 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3505 CM_SCACHESYNC_STOREDATA);
3506 lock_ReleaseWrite(&newDscp->rw);
3508 cm_EndDirOp(&newDirOp);
3510 /* cleanup first one */
3511 lock_ObtainWrite(&oldDscp->rw);
3512 cm_SyncOpDone(oldDscp, NULL,
3513 CM_SCACHESYNC_STOREDATA);
3514 lock_ReleaseWrite(&oldDscp->rw);
3515 cm_EndDirOp(&oldDirOp);
3520 /* lock the new vnode entry first */
3521 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE, &newDirOp);
3522 lock_ObtainWrite(&newDscp->rw);
3523 cm_dnlcRemove(newDscp, cNewNamep);
3524 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3525 CM_SCACHESYNC_STOREDATA);
3526 lock_ReleaseWrite(&newDscp->rw);
3528 cm_EndDirOp(&newDirOp);
3530 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3531 lock_ObtainWrite(&oldDscp->rw);
3532 cm_dnlcRemove(oldDscp, cOldNamep);
3533 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3534 CM_SCACHESYNC_STOREDATA);
3535 lock_ReleaseWrite(&oldDscp->rw);
3537 cm_EndDirOp(&oldDirOp);
3539 /* cleanup first one */
3540 lock_ObtainWrite(&newDscp->rw);
3541 cm_SyncOpDone(newDscp, NULL,
3542 CM_SCACHESYNC_STOREDATA);
3543 lock_ReleaseWrite(&newDscp->rw);
3544 cm_EndDirOp(&newDirOp);
3548 } /* two distinct vnodes */
3555 newNamep = cm_ClientStringToFsStringAlloc(cNewNamep, -1, NULL);
3557 /* try the RPC now */
3558 osi_Log2(afsd_logp, "CALL Rename old scp 0x%p new scp 0x%p",
3561 code = cm_ConnFromFID(&oldDscp->fid, userp, reqp, &connp);
3565 oldDirAFSFid.Volume = oldDscp->fid.volume;
3566 oldDirAFSFid.Vnode = oldDscp->fid.vnode;
3567 oldDirAFSFid.Unique = oldDscp->fid.unique;
3568 newDirAFSFid.Volume = newDscp->fid.volume;
3569 newDirAFSFid.Vnode = newDscp->fid.vnode;
3570 newDirAFSFid.Unique = newDscp->fid.unique;
3572 rxconnp = cm_GetRxConn(connp);
3573 code = RXAFS_Rename(rxconnp, &oldDirAFSFid, oldNamep,
3574 &newDirAFSFid, newNamep,
3575 &updatedOldDirStatus, &updatedNewDirStatus,
3577 rx_PutConnection(rxconnp);
3579 } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
3580 &volSync, NULL, NULL, code));
3581 code = cm_MapRPCError(code, reqp);
3584 osi_Log1(afsd_logp, "CALL Rename FAILURE, code 0x%x", code);
3586 osi_Log0(afsd_logp, "CALL Rename SUCCESS");
3588 /* update the individual stat cache entries for the directories */
3590 lock_ObtainWrite(&oldDirOp.scp->dirlock);
3591 oldDirOp.lockType = CM_DIRLOCK_WRITE;
3593 lock_ObtainWrite(&oldDscp->rw);
3594 cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
3597 cm_MergeStatus(NULL, oldDscp, &updatedOldDirStatus, &volSync,
3598 userp, reqp, CM_MERGEFLAG_DIROP);
3599 lock_ReleaseWrite(&oldDscp->rw);
3601 if (code == 0 && cm_CheckDirOpForSingleChange(&oldDirOp)) {
3603 diropCode = cm_BPlusDirLookup(&oldDirOp, cOldNamep, &fileFid);
3604 if (diropCode == CM_ERROR_INEXACT_MATCH)
3606 else if (diropCode == EINVAL)
3608 diropCode = cm_DirLookup(&oldDirOp, oldNamep, &fileFid);
3610 if (diropCode == 0) {
3612 diropCode = cm_DirCreateEntry(&oldDirOp, newNamep, &fileFid);
3614 cm_BPlusDirCreateEntry(&oldDirOp, cNewNamep, &fileFid);
3618 if (diropCode == 0) {
3619 diropCode = cm_DirDeleteEntry(&oldDirOp, oldNamep);
3621 cm_BPlusDirDeleteEntry(&oldDirOp, cOldNamep);
3626 cm_EndDirOp(&oldDirOp);
3628 /* and update it for the new one, too, if necessary */
3631 lock_ObtainWrite(&newDirOp.scp->dirlock);
3632 newDirOp.lockType = CM_DIRLOCK_WRITE;
3634 lock_ObtainWrite(&newDscp->rw);
3635 cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
3637 cm_MergeStatus(NULL, newDscp, &updatedNewDirStatus, &volSync,
3638 userp, reqp, CM_MERGEFLAG_DIROP);
3639 lock_ReleaseWrite(&newDscp->rw);
3643 * The following optimization does not work.
3644 * When the file server processed a RXAFS_Rename() request the
3645 * FID of the object being moved between directories is not
3646 * preserved. The client does not know the new FID nor the
3647 * version number of the target. Not only can we not create
3648 * the directory entry in the new directory, but we can't
3649 * preserve the cached data for the file. It must be re-read
3650 * from the file server. - jaltman, 2009/02/20
3653 /* we only make the local change if we successfully made
3654 the change in the old directory AND there was only one
3655 change in the new directory */
3656 if (diropCode == 0 && cm_CheckDirOpForSingleChange(&newDirOp)) {
3657 cm_DirCreateEntry(&newDirOp, newNamep, &fileFid);
3659 cm_BPlusDirCreateEntry(&newDirOp, cNewNamep, &fileFid);
3664 cm_EndDirOp(&newDirOp);
3668 * After the rename the file server has invalidated the callbacks
3669 * on the file that was moved nor do we have a directory reference
3672 lock_ObtainWrite(&oldScp->rw);
3673 cm_DiscardSCache(oldScp);
3674 lock_ReleaseWrite(&oldScp->rw);
3678 cm_ReleaseSCache(oldScp);
3685 /* and return error code */
3689 /* Byte range locks:
3691 The OpenAFS Windows client has to fake byte range locks given no
3692 server side support for such locks. This is implemented as keyed
3693 byte range locks on the cache manager.
3695 Keyed byte range locks:
3697 Each cm_scache_t structure keeps track of a list of keyed locks.
3698 The key for a lock identifies an owner of a set of locks (referred
3699 to as a client). Each key is represented by a value. The set of
3700 key values used within a specific cm_scache_t structure form a
3701 namespace that has a scope of just that cm_scache_t structure. The
3702 same key value can be used with another cm_scache_t structure and
3703 correspond to a completely different client. However it is
3704 advantageous for the SMB or IFS layer to make sure that there is a
3705 1-1 mapping between client and keys over all cm_scache_t objects.
3707 Assume a client C has key Key(C) (although, since the scope of the
3708 key is a cm_scache_t, the key can be Key(C,S), where S is the
3709 cm_scache_t. But assume a 1-1 relation between keys and clients).
3710 A byte range (O,+L) denotes byte addresses (O) through (O+L-1)
3711 inclusive (a.k.a. [O,O+L-1]). The function Key(x) is implemented
3712 through cm_generateKey() function for both SMB and IFS.
3714 The list of locks for a cm_scache_t object S is maintained in
3715 S->fileLocks. The cache manager will set a lock on the AFS file
3716 server in order to assert the locks in S->fileLocks. If only
3717 shared locks are in place for S, then the cache manager will obtain
3718 a LockRead lock, while if there are any exclusive locks, it will
3719 obtain a LockWrite lock. If the exclusive locks are all released
3720 while the shared locks remain, then the cache manager will
3721 downgrade the lock from LockWrite to LockRead. Similarly, if an
3722 exclusive lock is obtained when only shared locks exist, then the
3723 cache manager will try to upgrade the lock from LockRead to
3726 Each lock L owned by client C maintains a key L->key such that
3727 L->key == Key(C), the effective range defined by L->LOffset and
3728 L->LLength such that the range of bytes affected by the lock is
3729 (L->LOffset, +L->LLength), a type maintained in L->LockType which
3730 is either exclusive or shared.
3734 A lock exists iff it is in S->fileLocks for some cm_scache_t
3735 S. Existing locks are in one of the following states: ACTIVE,
3736 WAITLOCK, WAITUNLOCK, LOST, DELETED.
3738 The following sections describe each lock and the associated
3741 1. ACTIVE: A lock L is ACTIVE iff the cache manager has asserted
3742 the lock with the AFS file server. This type of lock can be
3743 exercised by a client to read or write to the locked region (as
3746 1.1 ACTIVE->LOST: When the AFS file server fails to extend a
3747 server lock that was required to assert the lock. Before
3748 marking the lock as lost, the cache manager checks if the file
3749 has changed on the server. If the file has not changed, then
3750 the cache manager will attempt to obtain a new server lock
3751 that is sufficient to assert the client side locks for the
3752 file. If any of these fail, the lock is marked as LOST.
3753 Otherwise, it is left as ACTIVE.
3755 1.2 ACTIVE->DELETED: Lock is released.
3757 2. WAITLOCK: A lock is in a WAITLOCK state if the cache manager
3758 grants the lock but the lock is yet to be asserted with the AFS
3759 file server. Once the file server grants the lock, the state
3760 will transition to an ACTIVE lock.
3762 2.1 WAITLOCK->ACTIVE: The server granted the lock.
3764 2.2 WAITLOCK->DELETED: Lock is abandoned, or timed out during
3767 2.3 WAITLOCK->LOST: One or more locks from this client were
3768 marked as LOST. No further locks will be granted to this
3769 client until all lost locks are removed.
3771 3. WAITUNLOCK: A lock is in a WAITUNLOCK state if the cache manager
3772 receives a request for a lock that conflicts with an existing
3773 ACTIVE or WAITLOCK lock. The lock will be placed in the queue
3774 and will be granted at such time the conflicting locks are
3775 removed, at which point the state will transition to either
3778 3.1 WAITUNLOCK->ACTIVE: The conflicting lock was removed. The
3779 current serverLock is sufficient to assert this lock, or a
3780 sufficient serverLock is obtained.
3782 3.2 WAITUNLOCK->WAITLOCK: The conflicting lock was removed,
3783 however the required serverLock is yet to be asserted with the
3786 3.3 WAITUNLOCK->DELETED: The lock is abandoned, timed out or
3789 3.5 WAITUNLOCK->LOST: One or more locks from this client were
3790 marked as LOST. No further locks will be granted to this
3791 client until all lost locks are removed.
3793 4. LOST: A lock L is LOST if the server lock that was required to
3794 assert the lock could not be obtained or if it could not be
3795 extended, or if other locks by the same client were LOST.
3796 Essentially, once a lock is LOST, the contract between the cache
3797 manager and that specific client is no longer valid.
3799 The cache manager rechecks the server lock once every minute and
3800 extends it as appropriate. If this is not done for 5 minutes,
3801 the AFS file server will release the lock (the 5 minute timeout
3802 is based on current file server code and is fairly arbitrary).
3803 Once released, the lock cannot be re-obtained without verifying
3804 that the contents of the file hasn't been modified since the
3805 time the lock was released. Re-obtaining the lock without
3806 verifying this may lead to data corruption. If the lock can not
3807 be obtained safely, then all active locks for the cm_scache_t
3810 4.1 LOST->DELETED: The lock is released.
3812 5. DELETED: The lock is no longer relevant. Eventually, it will
3813 get removed from the cm_scache_t. In the meantime, it will be
3814 treated as if it does not exist.
3816 5.1 DELETED->not exist: The lock is removed from the
3819 The following are classifications of locks based on their state.
3821 6* A lock L is ACCEPTED if it is ACTIVE or WAITLOCK. These locks
3822 have been accepted by the cache manager, but may or may not have
3823 been granted back to the client.
3825 7* A lock L is QUEUED if it is ACTIVE, WAITLOCK or WAITUNLOCK.
3827 8* A lock L is WAITING if it is WAITLOCK or WAITUNLOCK.
3831 A client C can READ range (Offset,+Length) of a file represented by
3832 cm_scache_t S iff (1):
3834 1. for all _a_ in (Offset,+Length), all of the following is true:
3836 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3837 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3840 1.2 For each LOST lock L in S->fileLocks such that _a_ in
3841 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3844 (When locks are lost on an cm_scache_t, all locks are lost. By
3845 4.2 (below), if there is an exclusive LOST lock, then there
3846 can't be any overlapping ACTIVE locks.)
3848 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
3850 2. for all _a_ in (Offset,+Length), one of the following is true:
3852 2.1 Byte _a_ of S is unowned (as specified in 1.1) AND there
3853 does not exist a LOST lock L such that _a_ in
3854 (L->LOffset,+L->LLength).
3856 2.2 Byte _a_ of S is owned by C under lock L (as specified in
3857 1.2) AND L->LockType is exclusive.
3859 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
3861 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
3864 3.1 If L->LockType is exclusive then there does NOT exist a
3865 ACCEPTED lock M in S->fileLocks such that _a_ in
3866 (M->LOffset,+M->LLength).
3868 (If we count all QUEUED locks then we hit cases such as
3869 cascading waiting locks where the locks later on in the queue
3870 can be granted without compromising file integrity. On the
3871 other hand if only ACCEPTED locks are considered, then locks
3872 that were received earlier may end up waiting for locks that
3873 were received later to be unlocked. The choice of ACCEPTED
3874 locks was made to mimic the Windows byte range lock
3877 3.2 If L->LockType is shared then for each ACCEPTED lock M in
3878 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
3879 M->LockType is shared.
3881 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
3883 4.1 M->key != Key(C)
3885 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
3886 and (M->LOffset,+M->LLength) do not intersect.
3888 (Note: If a client loses a lock, it loses all locks.
3889 Subsequently, it will not be allowed to obtain any more locks
3890 until all existing LOST locks that belong to the client are
3891 released. Once all locks are released by a single client,
3892 there exists no further contract between the client and AFS
3893 about the contents of the file, hence the client can then
3894 proceed to obtain new locks and establish a new contract.
3896 This doesn't quite work as you think it should, because most
3897 applications aren't built to deal with losing locks they
3898 thought they once had. For now, we don't have a good
3899 solution to lost locks.
3901 Also, for consistency reasons, we have to hold off on
3902 granting locks that overlap exclusive LOST locks.)
3904 A client C can only unlock locks L in S->fileLocks which have
3907 The representation and invariants are as follows:
3909 - Each cm_scache_t structure keeps:
3911 - A queue of byte-range locks (cm_scache_t::fileLocks) which
3912 are of type cm_file_lock_t.
3914 - A record of the highest server-side lock that has been
3915 obtained for this object (cm_scache_t::serverLock), which is
3916 one of (-1), LockRead, LockWrite.
3918 - A count of ACCEPTED exclusive and shared locks that are in the
3919 queue (cm_scache_t::sharedLocks and
3920 cm_scache_t::exclusiveLocks)
3922 - Each cm_file_lock_t structure keeps:
3924 - The type of lock (cm_file_lock_t::LockType)
3926 - The key associated with the lock (cm_file_lock_t::key)
3928 - The offset and length of the lock (cm_file_lock_t::LOffset
3929 and cm_file_lock_t::LLength)
3931 - The state of the lock.
3933 - Time of issuance or last successful extension
3935 Semantic invariants:
3937 I1. The number of ACCEPTED locks in S->fileLocks are
3938 (S->sharedLocks + S->exclusiveLocks)
3940 External invariants:
3942 I3. S->serverLock is the lock that we have asserted with the
3943 AFS file server for this cm_scache_t.
3945 I4. S->serverLock == LockRead iff there is at least one ACTIVE
3946 shared lock, but no ACTIVE exclusive locks.
3948 I5. S->serverLock == LockWrite iff there is at least one ACTIVE
3951 I6. If L is a LOST lock, then for each lock M in S->fileLocks,
3952 M->key == L->key IMPLIES M is LOST or DELETED.
3957 #define IS_LOCK_ACTIVE(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == 0)
3959 #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)
3961 #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)
3963 #define IS_LOCK_LOST(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_LOST)
3965 #define IS_LOCK_DELETED(lockp) (((lockp)->flags & CM_FILELOCK_FLAG_DELETED) == CM_FILELOCK_FLAG_DELETED)
3968 #define IS_LOCK_ACCEPTED(lockp) (IS_LOCK_ACTIVE(lockp) || IS_LOCK_WAITLOCK(lockp))
3971 #define IS_LOCK_CLIENTONLY(lockp) ((((lockp)->scp->flags & CM_SCACHEFLAG_RO) == CM_SCACHEFLAG_RO) || (((lockp)->flags & CM_FILELOCK_FLAG_CLIENTONLY) == CM_FILELOCK_FLAG_CLIENTONLY))
3974 #define INTERSECT_RANGE(r1,r2) (((r2).offset+(r2).length) > (r1).offset && ((r1).offset +(r1).length) > (r2).offset)
3977 #define CONTAINS_RANGE(r1,r2) (((r2).offset+(r2).length) <= ((r1).offset+(r1).length) && (r1).offset <= (r2).offset)
3979 #if defined(VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS) && !defined(LOCK_TESTING)
3980 #define SCP_SUPPORTS_BRLOCKS(scp) ((scp)->cbServerp && ((scp)->cbServerp->capabilities & VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS))
3982 #define SCP_SUPPORTS_BRLOCKS(scp) (1)
3985 #define SERVERLOCKS_ENABLED(scp) (!((scp)->flags & CM_SCACHEFLAG_RO) && cm_enableServerLocks && SCP_SUPPORTS_BRLOCKS(scp))
3987 #if defined(VICED_CAPABILITY_WRITELOCKACL)
3988 #define SCP_SUPPORTS_WRITELOCKACL(scp) ((scp)->cbServerp && ((scp->cbServerp->capabilities & VICED_CAPABILITY_WRITELOCKACL)))
3990 #define SCP_SUPPORTS_WRITELOCKACL(scp) (0)
3992 /* This should really be defined in any build that this code is being
3994 #error VICED_CAPABILITY_WRITELOCKACL not defined.
3997 static void cm_LockRangeSubtract(cm_range_t * pos, const cm_range_t * neg)
3999 afs_int64 int_begin;
4002 int_begin = MAX(pos->offset, neg->offset);
4003 int_end = MIN(pos->offset+pos->length, neg->offset+neg->length);
4005 if (int_begin < int_end) {
4006 if (int_begin == pos->offset) {
4007 pos->length = pos->offset + pos->length - int_end;
4008 pos->offset = int_end;
4009 } else if (int_end == pos->offset + pos->length) {
4010 pos->length = int_begin - pos->offset;
4013 /* We only subtract ranges if the resulting range is
4014 contiguous. If we try to support non-contigous ranges, we
4015 aren't actually improving performance. */
4019 /* Called with scp->rw held. Returns 0 if all is clear to read the
4020 specified range by the client identified by key.
4022 long cm_LockCheckRead(cm_scache_t *scp,
4023 LARGE_INTEGER LOffset,
4024 LARGE_INTEGER LLength,
4027 #ifndef ADVISORY_LOCKS
4029 cm_file_lock_t *fileLock;
4033 int substract_ranges = FALSE;
4035 range.offset = LOffset.QuadPart;
4036 range.length = LLength.QuadPart;
4040 1. for all _a_ in (Offset,+Length), all of the following is true:
4042 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
4043 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
4046 1.2 For each LOST lock L in S->fileLocks such that _a_ in
4047 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
4052 lock_ObtainRead(&cm_scacheLock);
4054 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
4056 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4058 if (INTERSECT_RANGE(range, fileLock->range)) {
4059 if (IS_LOCK_ACTIVE(fileLock)) {
4060 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4062 /* If there is an active lock for this client, it
4063 is safe to substract ranges.*/
4064 cm_LockRangeSubtract(&range, &fileLock->range);
4065 substract_ranges = TRUE;
4067 if (fileLock->lockType != LockRead) {
4068 code = CM_ERROR_LOCK_CONFLICT;
4072 /* even if the entire range is locked for reading,
4073 we still can't grant the lock at this point
4074 because the client may have lost locks. That
4075 is, unless we have already seen an active lock
4076 belonging to the client, in which case there
4077 can't be any lost locks for this client. */
4078 if (substract_ranges)
4079 cm_LockRangeSubtract(&range, &fileLock->range);
4081 } else if (IS_LOCK_LOST(fileLock) &&
4082 (cm_KeyEquals(&fileLock->key, &key, 0) || fileLock->lockType == LockWrite)) {
4083 code = CM_ERROR_BADFD;
4089 lock_ReleaseRead(&cm_scacheLock);
4091 osi_Log4(afsd_logp, "cm_LockCheckRead scp 0x%x offset %d length %d code 0x%x",
4092 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
4103 /* Called with scp->rw held. Returns 0 if all is clear to write the
4104 specified range by the client identified by key.
4106 long cm_LockCheckWrite(cm_scache_t *scp,
4107 LARGE_INTEGER LOffset,
4108 LARGE_INTEGER LLength,
4111 #ifndef ADVISORY_LOCKS
4113 cm_file_lock_t *fileLock;
4118 range.offset = LOffset.QuadPart;
4119 range.length = LLength.QuadPart;
4122 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
4124 2. for all _a_ in (Offset,+Length), one of the following is true:
4126 2.1 Byte _a_ of S is unowned AND there does not exist a LOST
4127 lock L such that _a_ in (L->LOffset,+L->LLength).
4129 2.2 Byte _a_ of S is owned by C under lock L AND L->LockType is
4133 lock_ObtainRead(&cm_scacheLock);
4135 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
4137 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4139 if (INTERSECT_RANGE(range, fileLock->range)) {
4140 if (IS_LOCK_ACTIVE(fileLock)) {
4141 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4142 if (fileLock->lockType == LockWrite) {
4144 /* if there is an active lock for this client, it
4145 is safe to substract ranges */
4146 cm_LockRangeSubtract(&range, &fileLock->range);
4148 code = CM_ERROR_LOCK_CONFLICT;
4152 code = CM_ERROR_LOCK_CONFLICT;
4155 } else if (IS_LOCK_LOST(fileLock)) {
4156 code = CM_ERROR_BADFD;
4162 lock_ReleaseRead(&cm_scacheLock);
4164 osi_Log4(afsd_logp, "cm_LockCheckWrite scp 0x%x offset %d length %d code 0x%x",
4165 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
4176 /* Called with cm_scacheLock write locked */
4177 static cm_file_lock_t * cm_GetFileLock(void) {
4180 l = (cm_file_lock_t *) cm_freeFileLocks;
4182 osi_QRemove(&cm_freeFileLocks, &l->q);
4184 l = malloc(sizeof(cm_file_lock_t));
4185 osi_assertx(l, "null cm_file_lock_t");
4188 memset(l, 0, sizeof(cm_file_lock_t));
4193 /* Called with cm_scacheLock write locked */
4194 static void cm_PutFileLock(cm_file_lock_t *l) {
4195 osi_QAdd(&cm_freeFileLocks, &l->q);
4198 /* called with scp->rw held. May release it during processing, but
4199 leaves it held on exit. */
4200 long cm_IntSetLock(cm_scache_t * scp, cm_user_t * userp, int lockType,
4206 struct rx_connection * rxconnp;
4208 afs_uint32 reqflags = reqp->flags;
4210 memset(&volSync, 0, sizeof(volSync));
4212 tfid.Volume = scp->fid.volume;
4213 tfid.Vnode = scp->fid.vnode;
4214 tfid.Unique = scp->fid.unique;
4217 osi_Log2(afsd_logp, "CALL SetLock scp 0x%p for lock %d", scp, lockType);
4219 reqp->flags |= CM_REQ_NORETRY;
4220 lock_ReleaseWrite(&scp->rw);
4223 code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4227 rxconnp = cm_GetRxConn(connp);
4228 code = RXAFS_SetLock(rxconnp, &tfid, lockType,
4230 rx_PutConnection(rxconnp);
4232 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
4235 code = cm_MapRPCError(code, reqp);
4237 osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
4239 osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
4242 lock_ObtainWrite(&scp->rw);
4243 reqp->flags = reqflags;
4247 /* called with scp->rw held. Releases it during processing */
4248 long cm_IntReleaseLock(cm_scache_t * scp, cm_user_t * userp,
4254 struct rx_connection * rxconnp;
4257 memset(&volSync, 0, sizeof(volSync));
4259 tfid.Volume = scp->fid.volume;
4260 tfid.Vnode = scp->fid.vnode;
4261 tfid.Unique = scp->fid.unique;
4264 lock_ReleaseWrite(&scp->rw);
4266 osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
4269 code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4273 rxconnp = cm_GetRxConn(connp);
4274 code = RXAFS_ReleaseLock(rxconnp, &tfid, &volSync);
4275 rx_PutConnection(rxconnp);
4277 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
4279 code = cm_MapRPCError(code, reqp);
4282 "CALL ReleaseLock FAILURE, code 0x%x", code);
4285 "CALL ReleaseLock SUCCESS");
4287 lock_ObtainWrite(&scp->rw);
4292 /* called with scp->rw held. May release it during processing, but
4293 will exit with lock held.
4297 - 0 if the user has permission to get the specified lock for the scp
4299 - CM_ERROR_NOACCESS if not
4301 Any other error from cm_SyncOp will be sent down untranslated.
4303 If CM_ERROR_NOACCESS is returned and lock_type is LockRead, then
4304 phas_insert (if non-NULL) will receive a boolean value indicating
4305 whether the user has INSERT permission or not.
4307 long cm_LockCheckPerms(cm_scache_t * scp,
4314 long code = 0, code2 = 0;
4316 /* lock permissions are slightly tricky because of the 'i' bit.
4317 If the user has PRSFS_LOCK, she can read-lock the file. If the
4318 user has PRSFS_WRITE, she can write-lock the file. However, if
4319 the user has PRSFS_INSERT, then she can write-lock new files,
4320 but not old ones. Since we don't have information about
4321 whether a file is new or not, we assume that if the user owns
4322 the scp, then she has the permissions that are granted by
4325 osi_Log3(afsd_logp, "cm_LockCheckPerms for scp[0x%p] type[%d] user[0x%p]",
4326 scp, lock_type, userp);
4328 if (lock_type == LockRead)
4329 rights |= PRSFS_LOCK;
4330 else if (lock_type == LockWrite)
4331 rights |= PRSFS_WRITE | PRSFS_LOCK;
4334 osi_assertx(FALSE, "invalid lock type");
4339 *phas_insert = FALSE;
4341 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
4342 CM_SCACHESYNC_GETSTATUS |
4343 CM_SCACHESYNC_NEEDCALLBACK);
4345 if (phas_insert && scp->creator == userp) {
4347 /* If this file was created by the user, then we check for
4348 PRSFS_INSERT. If the file server is recent enough, then
4349 this should be sufficient for her to get a write-lock (but
4350 not necessarily a read-lock). VICED_CAPABILITY_WRITELOCKACL
4351 indicates whether a file server supports getting write
4352 locks when the user only has PRSFS_INSERT.
4354 If the file was not created by the user we skip the check
4355 because the INSERT bit will not apply to this user even
4359 code2 = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
4360 CM_SCACHESYNC_GETSTATUS |
4361 CM_SCACHESYNC_NEEDCALLBACK);
4363 if (code2 == CM_ERROR_NOACCESS) {
4364 osi_Log0(afsd_logp, "cm_LockCheckPerms user has no INSERT bits");
4366 *phas_insert = TRUE;
4367 osi_Log0(afsd_logp, "cm_LockCheckPerms user has INSERT bits");
4371 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
4373 osi_Log1(afsd_logp, "cm_LockCheckPerms returning code %d", code);
4378 /* called with scp->rw held */
4379 long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
4380 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
4382 int allowWait, cm_user_t *userp, cm_req_t *reqp,
4383 cm_file_lock_t **lockpp)
4386 int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
4387 cm_file_lock_t *fileLock;
4390 int wait_unlock = FALSE;
4391 int force_client_lock = FALSE;
4393 osi_Log4(afsd_logp, "cm_Lock scp 0x%x type 0x%x offset %d length %d",
4394 scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
4395 osi_Log4(afsd_logp, "... allowWait %d key <0x%x, 0x%x, 0x%x>", allowWait,
4396 key.process_id, key.session_id, key.file_id);
4399 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
4401 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
4404 3.1 If L->LockType is exclusive then there does NOT exist a
4405 ACCEPTED lock M in S->fileLocks such that _a_ in
4406 (M->LOffset,+M->LLength).
4408 3.2 If L->LockType is shared then for each ACCEPTED lock M in
4409 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
4410 M->LockType is shared.
4412 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
4414 4.1 M->key != Key(C)
4416 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
4417 and (M->LOffset,+M->LLength) do not intersect.
4420 range.offset = LOffset.QuadPart;
4421 range.length = LLength.QuadPart;
4423 lock_ObtainRead(&cm_scacheLock);
4425 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4427 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4429 if (IS_LOCK_LOST(fileLock)) {
4430 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4431 code = CM_ERROR_BADFD;
4433 } else if (fileLock->lockType == LockWrite && INTERSECT_RANGE(range, fileLock->range)) {
4434 code = CM_ERROR_WOULDBLOCK;
4440 /* we don't need to check for deleted locks here since deleted
4441 locks are dequeued from scp->fileLocks */
4442 if (IS_LOCK_ACCEPTED(fileLock) &&
4443 INTERSECT_RANGE(range, fileLock->range)) {
4445 if ((sLockType & LOCKING_ANDX_SHARED_LOCK) == 0 ||
4446 fileLock->lockType != LockRead) {
4448 code = CM_ERROR_WOULDBLOCK;
4454 lock_ReleaseRead(&cm_scacheLock);
4456 if (code == 0 && SERVERLOCKS_ENABLED(scp)) {
4457 if (Which == scp->serverLock ||
4458 (Which == LockRead && scp->serverLock == LockWrite)) {
4462 /* we already have the lock we need */
4463 osi_Log3(afsd_logp, " we already have the correct lock. exclusives[%d], shared[%d], serverLock[%d]",
4464 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4466 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4468 /* special case: if we don't have permission to read-lock
4469 the file, then we force a clientside lock. This is to
4470 compensate for applications that obtain a read-lock for
4471 reading files off of directories that don't grant
4472 read-locks to the user. */
4473 if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4475 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4476 osi_Log0(afsd_logp, " User has no read-lock perms, but has INSERT perms.");
4479 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4480 force_client_lock = TRUE;
4484 } else if ((scp->exclusiveLocks > 0) ||
4485 (scp->sharedLocks > 0 && scp->serverLock != LockRead)) {
4488 /* We are already waiting for some other lock. We should
4489 wait for the daemon to catch up instead of generating a
4490 flood of SetLock calls. */
4491 osi_Log3(afsd_logp, " already waiting for other lock. exclusives[%d], shared[%d], serverLock[%d]",
4492 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4494 /* see if we have permission to create the lock in the
4496 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4498 code = CM_ERROR_WOULDBLOCK;
4499 else if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4501 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4503 " User has no read-lock perms, but has INSERT perms.");
4504 code = CM_ERROR_WOULDBLOCK;
4507 " User has no read-lock perms. Forcing client-side lock");
4508 force_client_lock = TRUE;
4512 /* leave any other codes as-is */
4516 int check_data_version = FALSE;
4519 /* first check if we have permission to elevate or obtain
4521 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4523 if (code == CM_ERROR_NOACCESS && Which == LockRead &&
4524 (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp))) {
4525 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4526 force_client_lock = TRUE;
4531 /* has_insert => (Which == LockRead, code == CM_ERROR_NOACCESS) */
4533 if (scp->serverLock == LockRead && Which == LockWrite) {
4535 /* We want to escalate the lock to a LockWrite.
4536 * Unfortunately that's not really possible without
4537 * letting go of the current lock. But for now we do
4541 " attempting to UPGRADE from LockRead to LockWrite.");
4543 " dataVersion on scp: %I64d", scp->dataVersion);
4545 /* we assume at this point (because scp->serverLock
4546 was valid) that we had a valid server lock. */
4547 scp->lockDataVersion = scp->dataVersion;
4548 check_data_version = TRUE;
4550 code = cm_IntReleaseLock(scp, userp, reqp);
4553 /* We couldn't release the lock */
4556 scp->serverLock = -1;
4560 /* We need to obtain a server lock of type Which in order
4561 * to assert this file lock */
4562 #ifndef AGGRESSIVE_LOCKS
4565 newLock = LockWrite;
4568 code = cm_IntSetLock(scp, userp, newLock, reqp);
4570 #ifdef AGGRESSIVE_LOCKS
4571 if ((code == CM_ERROR_WOULDBLOCK ||
4572 code == CM_ERROR_NOACCESS) && newLock != Which) {
4573 /* we wanted LockRead. We tried LockWrite. Now try
4578 osi_assertx(newLock == LockRead, "lock type not read");
4580 code = cm_IntSetLock(scp, userp, newLock, reqp);
4584 if (code == CM_ERROR_NOACCESS) {
4585 if (Which == LockRead) {
4586 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4588 /* We requested a read-lock, but we have permission to
4589 * get a write-lock. Try that */
4591 tcode = cm_LockCheckPerms(scp, LockWrite, userp, reqp, NULL);
4594 newLock = LockWrite;
4596 osi_Log0(afsd_logp, " User has 'i' perms and the request was for a LockRead. Trying to get a LockWrite instead");
4598 code = cm_IntSetLock(scp, userp, newLock, reqp);
4601 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4602 force_client_lock = TRUE;
4604 } else if (Which == LockWrite &&
4605 scp->creator == userp && !SCP_SUPPORTS_WRITELOCKACL(scp)) {
4608 /* Special case: if the lock request was for a
4609 * LockWrite and the user owns the file and we weren't
4610 * allowed to obtain the serverlock, we either lost a
4611 * race (the permissions changed from under us), or we
4612 * have 'i' bits, but we aren't allowed to lock the
4615 /* check if we lost a race... */
4616 tcode = cm_LockCheckPerms(scp, Which, userp, reqp, NULL);
4619 osi_Log0(afsd_logp, " User has 'i' perms but can't obtain write locks. Using client-side locks.");
4620 force_client_lock = TRUE;
4625 if (code == 0 && check_data_version &&
4626 scp->dataVersion != scp->lockDataVersion) {
4627 /* We lost a race. Although we successfully obtained
4628 * a lock, someone modified the file in between. The
4629 * locks have all been technically lost. */
4632 " Data version mismatch while upgrading lock.");
4634 " Data versions before=%I64d, after=%I64d",
4635 scp->lockDataVersion,
4638 " Releasing stale lock for scp 0x%x", scp);
4640 code = cm_IntReleaseLock(scp, userp, reqp);
4642 scp->serverLock = -1;
4644 code = CM_ERROR_INVAL;
4645 } else if (code == 0) {
4646 scp->serverLock = newLock;
4647 scp->lockDataVersion = scp->dataVersion;
4651 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4652 scp->serverLock == -1) {
4653 /* Oops. We lost the lock. */
4654 cm_LockMarkSCacheLost(scp);
4657 } else if (code == 0) { /* server locks not enabled */
4659 " Skipping server lock for scp");
4664 if (code != 0 && !force_client_lock) {
4665 /* Special case error translations
4667 Applications don't expect certain errors from a
4668 LockFile/UnlockFile call. We need to translate some error
4669 code to codes that apps expect and handle. */
4671 /* We shouldn't actually need to handle this case since we
4672 simulate locks for RO scps anyway. */
4673 if (code == CM_ERROR_READONLY) {
4674 osi_Log0(afsd_logp, " Reinterpreting CM_ERROR_READONLY as CM_ERROR_NOACCESS");
4675 code = CM_ERROR_NOACCESS;
4679 if (code == 0 || (code == CM_ERROR_WOULDBLOCK && allowWait) ||
4680 force_client_lock) {
4682 /* clear the error if we are forcing a client lock, so we
4683 don't get confused later. */
4684 if (force_client_lock && code != CM_ERROR_WOULDBLOCK)
4687 lock_ObtainWrite(&cm_scacheLock);
4688 fileLock = cm_GetFileLock();
4689 lock_ReleaseWrite(&cm_scacheLock);
4691 fileLock->fid = scp->fid;
4693 fileLock->key = key;
4694 fileLock->lockType = Which;
4696 fileLock->userp = userp;
4697 fileLock->range = range;
4698 fileLock->flags = (code == 0 ? 0 :
4700 CM_FILELOCK_FLAG_WAITUNLOCK :
4701 CM_FILELOCK_FLAG_WAITLOCK));
4703 if (force_client_lock || !SERVERLOCKS_ENABLED(scp))
4704 fileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
4706 fileLock->lastUpdate = (code == 0 && !force_client_lock) ? time(NULL) : 0;
4708 lock_ObtainWrite(&cm_scacheLock);
4709 osi_QAddT(&scp->fileLocksH, &scp->fileLocksT, &fileLock->fileq);
4710 cm_HoldSCacheNoLock(scp);
4711 fileLock->scp = scp;
4712 osi_QAdd(&cm_allFileLocks, &fileLock->q);
4713 lock_ReleaseWrite(&cm_scacheLock);
4719 if (IS_LOCK_CLIENTONLY(fileLock)) {
4721 } else if (IS_LOCK_ACCEPTED(fileLock)) {
4722 if (Which == LockRead)
4725 scp->exclusiveLocks++;
4729 "cm_Lock Lock added 0x%p flags 0x%x to scp [0x%p]",
4730 fileLock, fileLock->flags, scp);
4732 " exclusives[%d] shared[%d] client[%d] serverLock[%d]",
4733 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4734 (int)(signed char) scp->serverLock);
4737 "cm_Lock Rejecting lock (code = 0x%x)", code);
4740 /* Convert from would block to lock not granted */
4741 if (code == CM_ERROR_WOULDBLOCK)
4742 code = CM_ERROR_LOCK_NOT_GRANTED;
4747 /* Called with scp->rw held */
4748 long cm_UnlockByKey(cm_scache_t * scp,
4755 cm_file_lock_t *fileLock;
4756 osi_queue_t *q, *qn;
4759 osi_Log4(afsd_logp, "cm_UnlockByKey scp 0x%p key <0x%x,0x%x,0x%x",
4760 scp, key.process_id, key.session_id, key.file_id);
4761 osi_Log1(afsd_logp, " flags=0x%x", flags);
4763 lock_ObtainWrite(&cm_scacheLock);
4765 for (q = scp->fileLocksH; q; q = qn) {
4768 fileLock = (cm_file_lock_t *)
4769 ((char *) q - offsetof(cm_file_lock_t, fileq));
4772 osi_Log4(afsd_logp, " Checking lock[0x%x] range[%d,+%d] type[%d]",
4774 (unsigned long) fileLock->range.offset,
4775 (unsigned long) fileLock->range.length,
4776 fileLock->lockType);
4777 osi_Log4(afsd_logp, " key<0x%x, 0x%x, 0x%x> flags[0x%x]",
4778 fileLock->key.process_id, fileLock->key.session_id, fileLock->key.file_id,
4781 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
4782 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
4783 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4785 fileLock->fid.volume,
4786 fileLock->fid.vnode,
4787 fileLock->fid.unique);
4788 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4789 fileLock->scp->fid.cell,
4790 fileLock->scp->fid.volume,
4791 fileLock->scp->fid.vnode,
4792 fileLock->scp->fid.unique);
4793 osi_assertx(FALSE, "invalid fid value");
4797 if (!IS_LOCK_DELETED(fileLock) &&
4798 cm_KeyEquals(&fileLock->key, &key, flags)) {
4799 osi_Log3(afsd_logp, "...Unlock range [%d,+%d] type %d",
4800 fileLock->range.offset,
4801 fileLock->range.length,
4802 fileLock->lockType);
4804 if (scp->fileLocksT == q)
4805 scp->fileLocksT = osi_QPrev(q);
4806 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
4808 if (IS_LOCK_CLIENTONLY(fileLock)) {
4810 } else if (IS_LOCK_ACCEPTED(fileLock)) {
4811 if (fileLock->lockType == LockRead)
4814 scp->exclusiveLocks--;
4817 fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
4819 cm_ReleaseUser(fileLock->userp);
4820 cm_ReleaseSCacheNoLock(scp);
4822 fileLock->userp = NULL;
4823 fileLock->scp = NULL;
4829 lock_ReleaseWrite(&cm_scacheLock);
4831 if (n_unlocks == 0) {
4832 osi_Log0(afsd_logp, "cm_UnlockByKey no locks found");
4833 osi_Log3(afsd_logp, " Leaving scp with exclusives[%d], shared[%d], serverLock[%d]",
4834 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4839 osi_Log1(afsd_logp, "cm_UnlockByKey done with %d locks", n_unlocks);
4841 osi_assertx(scp->sharedLocks >= 0, "scp->sharedLocks < 0");
4842 osi_assertx(scp->exclusiveLocks >= 0, "scp->exclusiveLocks < 0");
4843 osi_assertx(scp->clientLocks >= 0, "scp->clientLocks < 0");
4845 if (!SERVERLOCKS_ENABLED(scp)) {
4846 osi_Log0(afsd_logp, " Skipping server lock for scp");
4850 /* Ideally we would go through the rest of the locks to determine
4851 * if one or more locks that were formerly in WAITUNLOCK can now
4852 * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
4853 * scp->sharedLocks accordingly. However, the retrying of locks
4854 * in that manner is done cm_RetryLock() manually.
4857 if (scp->serverLock == LockWrite &&
4858 scp->exclusiveLocks == 0 &&
4859 scp->sharedLocks > 0) {
4860 /* The serverLock should be downgraded to LockRead */
4861 osi_Log0(afsd_logp, " DOWNGRADE lock from LockWrite to LockRead");
4863 /* Make sure there are no dirty buffers left. */
4864 code = cm_FSync(scp, userp, reqp, TRUE);
4866 /* since scp->serverLock looked sane, we are going to assume
4867 that we have a valid server lock. */
4868 scp->lockDataVersion = scp->dataVersion;
4869 osi_Log1(afsd_logp, " dataVersion on scp = %I64d", scp->dataVersion);
4871 code = cm_IntReleaseLock(scp, userp, reqp);
4874 /* so we couldn't release it. Just let the lock be for now */
4878 scp->serverLock = -1;
4881 code = cm_IntSetLock(scp, userp, LockRead, reqp);
4883 if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
4884 scp->serverLock = LockRead;
4885 } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
4886 /* We lost a race condition. Although we have a valid
4887 lock on the file, the data has changed and essentially
4888 we have lost the lock we had during the transition. */
4890 osi_Log0(afsd_logp, "Data version mismatch during lock downgrade");
4891 osi_Log2(afsd_logp, " Data versions before=%I64d, after=%I64d",
4892 scp->lockDataVersion,
4895 code = cm_IntReleaseLock(scp, userp, reqp);
4897 code = CM_ERROR_INVAL;
4898 scp->serverLock = -1;
4902 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4903 (scp->serverLock == -1)) {
4905 cm_LockMarkSCacheLost(scp);
4908 /* failure here has no bearing on the return value of
4912 } else if (scp->serverLock != (-1) &&
4913 scp->exclusiveLocks == 0 &&
4914 scp->sharedLocks == 0) {
4915 /* The serverLock should be released entirely */
4917 if (scp->serverLock == LockWrite) {
4918 /* Make sure there are no dirty buffers left. */
4919 code = cm_FSync(scp, userp, reqp, TRUE);
4922 code = cm_IntReleaseLock(scp, userp, reqp);
4925 scp->serverLock = (-1);
4930 osi_Log1(afsd_logp, "cm_UnlockByKey code 0x%x", code);
4931 osi_Log4(afsd_logp, " Leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
4932 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4933 (int)(signed char) scp->serverLock);
4938 long cm_Unlock(cm_scache_t *scp,
4939 unsigned char sLockType,
4940 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
4947 int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
4948 cm_file_lock_t *fileLock;
4950 int release_userp = FALSE;
4951 int exact_match = !(flags & CM_UNLOCK_FLAG_MATCH_RANGE);
4953 LARGE_INTEGER RangeEnd;
4955 osi_Log4(afsd_logp, "cm_Unlock scp 0x%p type 0x%x offset %d length %d",
4956 scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
4957 osi_Log4(afsd_logp, "... key <0x%x,0x%x,0x%x> flags 0x%x",
4958 key.process_id, key.session_id, key.file_id, flags);
4961 RangeEnd.QuadPart = LOffset.QuadPart + LLength.QuadPart;
4964 lock_ObtainRead(&cm_scacheLock);
4966 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4967 fileLock = (cm_file_lock_t *)
4968 ((char *) q - offsetof(cm_file_lock_t, fileq));
4971 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
4972 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
4973 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4975 fileLock->fid.volume,
4976 fileLock->fid.vnode,
4977 fileLock->fid.unique);
4978 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4979 fileLock->scp->fid.cell,
4980 fileLock->scp->fid.volume,
4981 fileLock->scp->fid.vnode,
4982 fileLock->scp->fid.unique);
4983 osi_assertx(FALSE, "invalid fid value");
4987 if (!IS_LOCK_DELETED(fileLock) &&
4988 cm_KeyEquals(&fileLock->key, &key, 0) &&
4989 fileLock->range.offset == LOffset.QuadPart &&
4990 fileLock->range.length == LLength.QuadPart) {
4996 if (!IS_LOCK_DELETED(fileLock) &&
4997 cm_KeyEquals(&fileLock->key, &key, 0) &&
4998 fileLock->range.offset >= LOffset.QuadPart &&
4999 fileLock->range.offset < RangeEnd.QuadPart &&
5000 (fileLock->range.offset + fileLock->range.length) <= RangeEnd.QuadPart) {
5008 lock_ReleaseRead(&cm_scacheLock);
5010 if (lock_found && !exact_match) {
5014 osi_Log0(afsd_logp, "cm_Unlock lock not found; failure");
5016 /* The lock didn't exist anyway. *shrug* */
5017 return CM_ERROR_RANGE_NOT_LOCKED;
5021 /* discard lock record */
5022 lock_ConvertRToW(&cm_scacheLock);
5023 if (scp->fileLocksT == q)
5024 scp->fileLocksT = osi_QPrev(q);
5025 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
5028 * Don't delete it here; let the daemon delete it, to simplify
5029 * the daemon's traversal of the list.
5032 if (IS_LOCK_CLIENTONLY(fileLock)) {
5034 } else if (IS_LOCK_ACCEPTED(fileLock)) {
5035 if (fileLock->lockType == LockRead)
5038 scp->exclusiveLocks--;
5041 fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5042 if (userp != NULL) {
5043 cm_ReleaseUser(fileLock->userp);
5045 userp = fileLock->userp;
5046 release_userp = TRUE;
5048 fileLock->userp = NULL;
5049 cm_ReleaseSCacheNoLock(scp);
5050 fileLock->scp = NULL;
5051 lock_ReleaseWrite(&cm_scacheLock);
5053 if (!SERVERLOCKS_ENABLED(scp)) {
5054 osi_Log0(afsd_logp, " Skipping server locks for scp");
5058 /* Ideally we would go through the rest of the locks to determine
5059 * if one or more locks that were formerly in WAITUNLOCK can now
5060 * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
5061 * scp->sharedLocks accordingly. However, the retrying of locks
5062 * in that manner is done cm_RetryLock() manually.
5065 if (scp->serverLock == LockWrite &&
5066 scp->exclusiveLocks == 0 &&
5067 scp->sharedLocks > 0) {
5069 /* The serverLock should be downgraded to LockRead */
5070 osi_Log0(afsd_logp, " DOWNGRADE lock from LockWrite to LockRead");
5072 /* Make sure there are no dirty buffers left. */
5073 code = cm_FSync(scp, userp, reqp, TRUE);
5075 /* Since we already had a lock, we assume that there is a
5076 valid server lock. */
5077 scp->lockDataVersion = scp->dataVersion;
5078 osi_Log1(afsd_logp, " dataVersion on scp is %I64d", scp->dataVersion);
5080 /* before we downgrade, make sure that we have enough
5081 permissions to get the read lock. */
5082 code = cm_LockCheckPerms(scp, LockRead, userp, reqp, NULL);
5085 osi_Log0(afsd_logp, " SKIPPING downgrade because user doesn't have perms to get downgraded lock");
5091 code = cm_IntReleaseLock(scp, userp, reqp);
5094 /* so we couldn't release it. Just let the lock be for now */
5098 scp->serverLock = -1;
5101 code = cm_IntSetLock(scp, userp, LockRead, reqp);
5103 if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
5104 scp->serverLock = LockRead;
5105 } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
5106 /* Lost a race. We obtained a new lock, but that is
5107 meaningless since someone modified the file
5111 "Data version mismatch while downgrading lock");
5113 " Data versions before=%I64d, after=%I64d",
5114 scp->lockDataVersion,
5117 code = cm_IntReleaseLock(scp, userp, reqp);
5119 scp->serverLock = -1;
5120 code = CM_ERROR_INVAL;
5124 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
5125 (scp->serverLock == -1)) {
5127 cm_LockMarkSCacheLost(scp);
5130 /* failure here has no bearing on the return value of
5134 } else if (scp->serverLock != (-1) &&
5135 scp->exclusiveLocks == 0 &&
5136 scp->sharedLocks == 0) {
5137 /* The serverLock should be released entirely */
5139 if (scp->serverLock == LockWrite) {
5140 /* Make sure there are no dirty buffers left. */
5141 code = cm_FSync(scp, userp, reqp, TRUE);
5144 code = cm_IntReleaseLock(scp, userp, reqp);
5147 scp->serverLock = (-1);
5151 if (release_userp) {
5152 cm_ReleaseUser(userp);
5153 release_userp = FALSE;
5157 goto try_again; /* might be more than one lock in the range */
5161 osi_Log1(afsd_logp, "cm_Unlock code 0x%x", code);
5162 osi_Log4(afsd_logp, " leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
5163 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
5164 (int)(signed char) scp->serverLock);
5169 /* called with scp->rw held */
5170 void cm_LockMarkSCacheLost(cm_scache_t * scp)
5172 cm_file_lock_t *fileLock;
5175 osi_Log1(afsd_logp, "cm_LockMarkSCacheLost scp 0x%x", scp);
5177 /* cm_scacheLock needed because we are modifying fileLock->flags */
5178 lock_ObtainWrite(&cm_scacheLock);
5180 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5182 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
5184 if (IS_LOCK_ACTIVE(fileLock) &&
5185 !IS_LOCK_CLIENTONLY(fileLock)) {
5186 if (fileLock->lockType == LockRead)
5189 scp->exclusiveLocks--;
5191 fileLock->flags |= CM_FILELOCK_FLAG_LOST;
5195 scp->serverLock = -1;
5196 scp->lockDataVersion = CM_SCACHE_VERSION_BAD;
5197 lock_ReleaseWrite(&cm_scacheLock);
5200 /* Called with no relevant locks held */
5201 void cm_CheckLocks()
5203 osi_queue_t *q, *nq;
5204 cm_file_lock_t *fileLock;
5210 struct rx_connection * rxconnp;
5213 memset(&volSync, 0, sizeof(volSync));
5217 lock_ObtainWrite(&cm_scacheLock);
5219 cm_lockRefreshCycle++;
5221 osi_Log1(afsd_logp, "cm_CheckLocks starting lock check cycle %d", cm_lockRefreshCycle);
5223 for (q = cm_allFileLocks; q; q = nq) {
5224 fileLock = (cm_file_lock_t *) q;
5228 if (IS_LOCK_DELETED(fileLock)) {
5230 osi_QRemove(&cm_allFileLocks, q);
5231 cm_PutFileLock(fileLock);
5233 } else if (IS_LOCK_ACTIVE(fileLock) && !IS_LOCK_CLIENTONLY(fileLock)) {
5235 /* Server locks must have been enabled for us to have
5236 received an active non-client-only lock. */
5237 osi_assertx(cm_enableServerLocks, "!cm_enableServerLocks");
5239 scp = fileLock->scp;
5240 osi_assertx(scp != NULL, "null cm_scache_t");
5242 cm_HoldSCacheNoLock(scp);
5245 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5246 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5247 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5249 fileLock->fid.volume,
5250 fileLock->fid.vnode,
5251 fileLock->fid.unique);
5252 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5253 fileLock->scp->fid.cell,
5254 fileLock->scp->fid.volume,
5255 fileLock->scp->fid.vnode,
5256 fileLock->scp->fid.unique);
5257 osi_assertx(FALSE, "invalid fid");
5260 /* Server locks are extended once per scp per refresh
5262 if (scp->lastRefreshCycle != cm_lockRefreshCycle) {
5264 int scp_done = FALSE;
5266 osi_Log1(afsd_logp, "cm_CheckLocks Updating scp 0x%x", scp);
5268 lock_ReleaseWrite(&cm_scacheLock);
5269 lock_ObtainWrite(&scp->rw);
5271 /* did the lock change while we weren't holding the lock? */
5272 if (!IS_LOCK_ACTIVE(fileLock))
5273 goto post_syncopdone;
5275 code = cm_SyncOp(scp, NULL, fileLock->userp, &req, 0,
5276 CM_SCACHESYNC_NEEDCALLBACK
5277 | CM_SCACHESYNC_GETSTATUS
5278 | CM_SCACHESYNC_LOCK);
5282 "cm_CheckLocks SyncOp failure code 0x%x", code);
5283 goto post_syncopdone;
5286 /* cm_SyncOp releases scp->rw during which the lock
5287 may get released. */
5288 if (!IS_LOCK_ACTIVE(fileLock))
5289 goto pre_syncopdone;
5291 if (scp->serverLock != -1 && !(scp->flags & CM_SCACHEFLAG_DELETED)) {
5295 tfid.Volume = scp->fid.volume;
5296 tfid.Vnode = scp->fid.vnode;
5297 tfid.Unique = scp->fid.unique;
5299 userp = fileLock->userp;
5301 osi_Log3(afsd_logp, "CALL ExtendLock lock 0x%p for scp=0x%p with lock %d",
5304 (int) scp->serverLock);
5306 lock_ReleaseWrite(&scp->rw);
5309 code = cm_ConnFromFID(&cfid, userp,
5314 rxconnp = cm_GetRxConn(connp);
5315 code = RXAFS_ExtendLock(rxconnp, &tfid,
5317 rx_PutConnection(rxconnp);
5319 osi_Log1(afsd_logp, " ExtendLock returns %d", code);
5321 } while (cm_Analyze(connp, userp, &req,
5322 &cfid, &volSync, NULL, NULL,
5325 code = cm_MapRPCError(code, &req);
5327 lock_ObtainWrite(&scp->rw);
5330 osi_Log1(afsd_logp, "CALL ExtendLock FAILURE, code 0x%x", code);
5332 osi_Log0(afsd_logp, "CALL ExtendLock SUCCESS");
5333 scp->lockDataVersion = scp->dataVersion;
5336 if ((code == EINVAL || code == CM_ERROR_INVAL) &&
5337 scp->lockDataVersion == scp->dataVersion) {
5341 (scp->exclusiveLocks > 0) ? LockWrite: LockRead;
5343 /* we might still have a chance to obtain a
5346 code = cm_IntSetLock(scp, userp, lockType, &req);
5349 code = CM_ERROR_INVAL;
5350 } else if (scp->lockDataVersion != scp->dataVersion) {
5352 /* now check if we still have the file at
5353 the right data version. */
5355 "Data version mismatch on scp 0x%p",
5358 " Data versions: before=%I64d, after=%I64d",
5359 scp->lockDataVersion,
5362 code = cm_IntReleaseLock(scp, userp, &req);
5364 code = CM_ERROR_INVAL;
5368 if (code == EINVAL || code == CM_ERROR_INVAL ||
5369 code == CM_ERROR_BADFD) {
5370 cm_LockMarkSCacheLost(scp);
5374 /* interestingly, we have found an active lock
5375 belonging to an scache that has no
5377 cm_LockMarkSCacheLost(scp);
5384 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5387 lock_ReleaseWrite(&scp->rw);
5389 lock_ObtainWrite(&cm_scacheLock);
5392 fileLock->lastUpdate = time(NULL);
5396 scp->lastRefreshCycle = cm_lockRefreshCycle;
5399 /* we have already refreshed the locks on this scp */
5400 fileLock->lastUpdate = time(NULL);
5403 cm_ReleaseSCacheNoLock(scp);
5405 } else if (IS_LOCK_ACTIVE(fileLock) && IS_LOCK_CLIENTONLY(fileLock)) {
5406 /* TODO: Check callbacks */
5410 lock_ReleaseWrite(&cm_scacheLock);
5411 osi_Log1(afsd_logp, "cm_CheckLocks completes lock check cycle %d", cm_lockRefreshCycle);
5414 /* NOT called with scp->rw held. */
5415 long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
5418 cm_scache_t *scp = NULL;
5419 cm_file_lock_t *fileLock;
5423 int force_client_lock = FALSE;
5424 int has_insert = FALSE;
5425 int check_data_version = FALSE;
5429 if (client_is_dead) {
5430 code = CM_ERROR_TIMEDOUT;
5434 lock_ObtainRead(&cm_scacheLock);
5436 osi_Log2(afsd_logp, "cm_RetryLock checking lock %p (scp=%p)", oldFileLock, oldFileLock->scp);
5437 osi_Log4(afsd_logp, " offset(%x:%x) length(%x:%x)",
5438 (unsigned)(oldFileLock->range.offset >> 32),
5439 (unsigned)(oldFileLock->range.offset & 0xffffffff),
5440 (unsigned)(oldFileLock->range.length >> 32),
5441 (unsigned)(oldFileLock->range.length & 0xffffffff));
5442 osi_Log4(afsd_logp, " key<0x%x,0x%x,0x%x> flags=%x",
5443 oldFileLock->key.process_id, oldFileLock->key.session_id, oldFileLock->key.file_id,
5444 (unsigned)(oldFileLock->flags));
5446 /* if the lock has already been granted, then we have nothing to do */
5447 if (IS_LOCK_ACTIVE(oldFileLock)) {
5448 lock_ReleaseRead(&cm_scacheLock);
5449 osi_Log0(afsd_logp, "cm_RetryLock lock already granted");
5453 /* we can't do anything with lost or deleted locks at the moment. */
5454 if (IS_LOCK_LOST(oldFileLock) || IS_LOCK_DELETED(oldFileLock)) {
5455 code = CM_ERROR_BADFD;
5456 osi_Log0(afsd_logp, "cm_RetryLock lock is lost or deleted");
5457 lock_ReleaseRead(&cm_scacheLock);
5461 scp = oldFileLock->scp;
5463 osi_assertx(scp != NULL, "null cm_scache_t");
5465 lock_ReleaseRead(&cm_scacheLock);
5466 lock_ObtainWrite(&scp->rw);
5468 code = cm_LockCheckPerms(scp, oldFileLock->lockType,
5472 if (code == CM_ERROR_NOACCESS && oldFileLock->lockType == LockRead) {
5473 if (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp)) {
5474 force_client_lock = TRUE;
5478 lock_ReleaseWrite(&scp->rw);
5482 lock_ObtainWrite(&cm_scacheLock);
5484 /* Check if we already have a sufficient server lock to allow this
5485 lock to go through. */
5486 if (IS_LOCK_WAITLOCK(oldFileLock) &&
5487 (!SERVERLOCKS_ENABLED(scp) ||
5488 scp->serverLock == oldFileLock->lockType ||
5489 scp->serverLock == LockWrite)) {
5491 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5493 if (SERVERLOCKS_ENABLED(scp)) {
5494 osi_Log1(afsd_logp, "cm_RetryLock Server lock (%d) is sufficient for lock. Granting",
5495 (int) scp->serverLock);
5497 osi_Log0(afsd_logp, "cm_RetryLock skipping server lock for scp");
5500 lock_ReleaseWrite(&cm_scacheLock);
5501 lock_ReleaseWrite(&scp->rw);
5506 if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5508 /* check if the conflicting locks have dissappeared already */
5509 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5511 fileLock = (cm_file_lock_t *)
5512 ((char *) q - offsetof(cm_file_lock_t, fileq));
5514 if (IS_LOCK_LOST(fileLock)) {
5515 if (cm_KeyEquals(&fileLock->key, &oldFileLock->key, 0)) {
5516 code = CM_ERROR_BADFD;
5517 oldFileLock->flags |= CM_FILELOCK_FLAG_LOST;
5518 osi_Log1(afsd_logp, " found lost lock %p for same key. Marking lock as lost",
5521 } else if (fileLock->lockType == LockWrite &&
5522 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5523 osi_Log1(afsd_logp, " found conflicting LOST lock %p", fileLock);
5524 code = CM_ERROR_WOULDBLOCK;
5529 if (IS_LOCK_ACCEPTED(fileLock) &&
5530 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5532 if (oldFileLock->lockType != LockRead ||
5533 fileLock->lockType != LockRead) {
5535 osi_Log1(afsd_logp, " found conflicting lock %p", fileLock);
5536 code = CM_ERROR_WOULDBLOCK;
5544 lock_ReleaseWrite(&cm_scacheLock);
5545 lock_ReleaseWrite(&scp->rw);
5550 /* when we get here, the lock is either a WAITUNLOCK or WAITLOCK.
5551 If it is WAITUNLOCK, then we didn't find any conflicting lock
5552 but we haven't verfied whether the serverLock is sufficient to
5553 assert it. If it is WAITLOCK, then the serverLock is
5554 insufficient to assert it. Eitherway, we are ready to accept
5555 the lock as either ACTIVE or WAITLOCK depending on the
5558 /* First, promote the WAITUNLOCK to a WAITLOCK */
5559 if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5560 if (oldFileLock->lockType == LockRead)
5563 scp->exclusiveLocks++;
5565 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITUNLOCK;
5566 oldFileLock->flags |= CM_FILELOCK_FLAG_WAITLOCK;
5569 osi_assertx(IS_LOCK_WAITLOCK(oldFileLock), "!IS_LOCK_WAITLOCK");
5571 if (force_client_lock ||
5572 !SERVERLOCKS_ENABLED(scp) ||
5573 scp->serverLock == oldFileLock->lockType ||
5574 (oldFileLock->lockType == LockRead &&
5575 scp->serverLock == LockWrite)) {
5577 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5579 if ((force_client_lock ||
5580 !SERVERLOCKS_ENABLED(scp)) &&
5581 !IS_LOCK_CLIENTONLY(oldFileLock)) {
5583 oldFileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
5585 if (oldFileLock->lockType == LockRead)
5588 scp->exclusiveLocks--;
5593 lock_ReleaseWrite(&cm_scacheLock);
5594 lock_ReleaseWrite(&scp->rw);
5601 code = cm_SyncOp(scp, NULL, oldFileLock->userp, &req, 0,
5602 CM_SCACHESYNC_NEEDCALLBACK
5603 | CM_SCACHESYNC_GETSTATUS
5604 | CM_SCACHESYNC_LOCK);
5606 osi_Log1(afsd_logp, "cm_RetryLock SyncOp failure code 0x%x", code);
5607 lock_ReleaseWrite(&cm_scacheLock);
5608 goto post_syncopdone;
5611 if (!IS_LOCK_WAITLOCK(oldFileLock))
5612 goto pre_syncopdone;
5614 userp = oldFileLock->userp;
5616 #ifndef AGGRESSIVE_LOCKS
5617 newLock = oldFileLock->lockType;
5619 newLock = LockWrite;
5623 /* if has_insert is non-zero, then:
5624 - the lock a LockRead
5625 - we don't have permission to get a LockRead
5626 - we do have permission to get a LockWrite
5627 - the server supports VICED_CAPABILITY_WRITELOCKACL
5630 newLock = LockWrite;
5633 lock_ReleaseWrite(&cm_scacheLock);
5635 /* when we get here, either we have a read-lock and want a
5636 write-lock or we don't have any locks and we want some
5639 if (scp->serverLock == LockRead) {
5641 osi_assertx(newLock == LockWrite, "!LockWrite");
5643 osi_Log0(afsd_logp, " Attempting to UPGRADE from LockRead to LockWrite");
5645 scp->lockDataVersion = scp->dataVersion;
5646 check_data_version = TRUE;
5648 code = cm_IntReleaseLock(scp, userp, &req);
5651 goto pre_syncopdone;
5653 scp->serverLock = -1;
5656 code = cm_IntSetLock(scp, userp, newLock, &req);
5659 if (scp->dataVersion != scp->lockDataVersion) {
5660 /* we lost a race. too bad */
5663 " Data version mismatch while upgrading lock.");
5665 " Data versions before=%I64d, after=%I64d",
5666 scp->lockDataVersion,
5669 " Releasing stale lock for scp 0x%x", scp);
5671 code = cm_IntReleaseLock(scp, userp, &req);
5673 scp->serverLock = -1;
5675 code = CM_ERROR_INVAL;
5677 cm_LockMarkSCacheLost(scp);
5679 scp->serverLock = newLock;
5684 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5690 if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
5691 lock_ObtainWrite(&cm_scacheLock);
5692 if (scp->fileLocksT == &oldFileLock->fileq)
5693 scp->fileLocksT = osi_QPrev(&oldFileLock->fileq);
5694 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, &oldFileLock->fileq);
5695 lock_ReleaseWrite(&cm_scacheLock);
5697 lock_ReleaseWrite(&scp->rw);
5700 lock_ObtainWrite(&cm_scacheLock);
5702 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5703 } else if (code != CM_ERROR_WOULDBLOCK) {
5704 oldFileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5705 cm_ReleaseUser(oldFileLock->userp);
5706 oldFileLock->userp = NULL;
5707 if (oldFileLock->scp) {
5708 cm_ReleaseSCacheNoLock(oldFileLock->scp);
5709 oldFileLock->scp = NULL;
5712 lock_ReleaseWrite(&cm_scacheLock);
5717 cm_key_t cm_GenerateKey(afs_uint16 session_id, afs_offs_t process_id, afs_uint16 file_id)
5721 key.process_id = process_id;
5722 key.session_id = session_id;
5723 key.file_id = file_id;
5728 int cm_KeyEquals(cm_key_t *k1, cm_key_t *k2, int flags)
5730 return (k1->session_id == k2->session_id) && (k1->file_id == k2->file_id) &&
5731 ((flags & CM_UNLOCK_BY_FID) || (k1->process_id == k2->process_id));
5734 void cm_ReleaseAllLocks(void)
5740 cm_file_lock_t *fileLock;
5743 for (i = 0; i < cm_data.scacheHashTableSize; i++)
5745 for ( scp = cm_data.scacheHashTablep[i]; scp; scp = scp->nextp ) {
5746 while (scp->fileLocksH != NULL) {
5747 lock_ObtainWrite(&scp->rw);
5748 lock_ObtainWrite(&cm_scacheLock);
5749 if (!scp->fileLocksH) {
5750 lock_ReleaseWrite(&cm_scacheLock);
5751 lock_ReleaseWrite(&scp->rw);
5754 fileLock = (cm_file_lock_t *)((char *) scp->fileLocksH - offsetof(cm_file_lock_t, fileq));
5755 userp = fileLock->userp;
5757 key = fileLock->key;
5758 cm_HoldSCacheNoLock(scp);
5759 lock_ReleaseWrite(&cm_scacheLock);
5760 cm_UnlockByKey(scp, key, 0, userp, &req);
5761 cm_ReleaseSCache(scp);
5762 cm_ReleaseUser(userp);
5763 lock_ReleaseWrite(&scp->rw);