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, 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 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
295 lock_ReleaseWrite(&scp->rw);
297 osi_Log3(afsd_logp,"cm_CheckNTOpen scp 0x%p ldp 0x%p code 0x%x", scp, *ldpp, code);
301 extern long cm_CheckNTOpenDone(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp,
302 cm_lock_data_t ** ldpp)
304 osi_Log2(afsd_logp,"cm_CheckNTOpenDone scp 0x%p ldp 0x%p", scp, *ldpp);
306 lock_ObtainWrite(&scp->rw);
307 cm_Unlock(scp, (*ldpp)->sLockType, (*ldpp)->LOffset, (*ldpp)->LLength,
308 (*ldpp)->key, userp, reqp);
309 lock_ReleaseWrite(&scp->rw);
316 * When CAP_NT_SMBS has been negotiated, deletion (of files or directories) is
317 * done in three steps:
318 * (1) open for deletion (NT_CREATE_AND_X)
319 * (2) set for deletion on close (NT_TRANSACTION2, SET_FILE_INFO)
321 * We must not do the RPC until step 3. But if we are going to return an error
322 * code (e.g. directory not empty), we must return it by step 2, otherwise most
323 * clients will not notice it. So we do a preliminary check. For deleting
324 * files, this is almost free, since we have already done the RPC to get the
325 * parent directory's status bits. But for deleting directories, we must do an
326 * additional RPC to get the directory's data to check if it is empty. Sigh.
328 long cm_CheckNTDelete(cm_scache_t *dscp, cm_scache_t *scp, cm_user_t *userp,
334 cm_dirEntry_t *dep = 0;
335 unsigned short *hashTable;
337 int BeyondPage = 0, HaveDot = 0, HaveDotDot = 0;
340 /* First check permissions */
341 lock_ObtainWrite(&scp->rw);
342 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_DELETE,
343 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
345 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
346 lock_ReleaseWrite(&scp->rw);
350 /* If deleting directory, must be empty */
352 if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
355 thyper.HighPart = 0; thyper.LowPart = 0;
356 code = buf_Get(scp, &thyper, &bufferp);
360 lock_ObtainMutex(&bufferp->mx);
361 lock_ObtainWrite(&scp->rw);
364 code = cm_SyncOp(scp, bufferp, userp, reqp, 0,
365 CM_SCACHESYNC_NEEDCALLBACK
367 | CM_SCACHESYNC_BUFLOCKED);
371 if (cm_HaveBuffer(scp, bufferp, 1))
374 /* otherwise, load the buffer and try again */
375 lock_ReleaseMutex(&bufferp->mx);
376 code = cm_GetBuffer(scp, bufferp, NULL, userp, reqp);
377 lock_ReleaseWrite(&scp->rw);
378 lock_ObtainMutex(&bufferp->mx);
379 lock_ObtainWrite(&scp->rw);
380 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
385 lock_ReleaseWrite(&scp->rw);
388 /* We try to determine emptiness without looking beyond the first page,
389 * and without assuming "." and ".." are present and are on the first
390 * page (though these assumptions might, after all, be reasonable).
392 hashTable = (unsigned short *)(bufferp->datap + (32 * 5));
393 for (i=0; i<128; i++) {
394 idx = ntohs(hashTable[i]);
400 dep = (cm_dirEntry_t *)(bufferp->datap + (32 * idx));
401 if (strcmp(dep->name, ".") == 0)
403 else if (strcmp(dep->name, "..") == 0)
406 code = CM_ERROR_NOTEMPTY;
409 idx = ntohs(dep->next);
412 if (BeyondPage && HaveDot && HaveDotDot)
413 code = CM_ERROR_NOTEMPTY;
417 lock_ReleaseMutex(&bufferp->mx);
418 buf_Release(bufferp);
420 lock_ReleaseWrite(&scp->rw);
425 * Iterate through all entries in a directory.
426 * When the function funcp is called, the buffer is locked but the
427 * directory vnode is not.
429 * If the retscp parameter is not NULL, the parmp must be a
430 * cm_lookupSearch_t object.
432 long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
433 osi_hyper_t *startOffsetp, cm_user_t *userp, cm_req_t *reqp,
434 cm_scache_t **retscp)
438 cm_dirEntry_t *dep = 0;
441 osi_hyper_t dirLength;
442 osi_hyper_t bufferOffset;
443 osi_hyper_t curOffset;
447 cm_pageHeader_t *pageHeaderp;
449 long nextEntryCookie;
450 int numDirChunks; /* # of 32 byte dir chunks in this entry */
452 /* get the directory size */
453 lock_ObtainWrite(&scp->rw);
454 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_LOOKUP,
455 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
456 lock_ReleaseWrite(&scp->rw);
460 if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
461 return CM_ERROR_NOTDIR;
463 if (retscp) /* if this is a lookup call */
465 cm_lookupSearch_t* sp = parmp;
468 #ifdef AFS_FREELANCE_CLIENT
469 /* Freelance entries never end up in the DNLC because they
470 * do not have an associated cm_server_t
472 !(cm_freelanceEnabled &&
473 sp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
474 sp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
475 #else /* !AFS_FREELANCE_CLIENT */
480 int casefold = sp->caseFold;
481 sp->caseFold = 0; /* we have a strong preference for exact matches */
482 if ( *retscp = cm_dnlcLookup(scp, sp)) /* dnlc hit */
484 sp->caseFold = casefold;
487 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
500 code = cm_BeginDirOp(scp, userp, reqp, CM_DIRLOCK_READ, &dirop);
504 code = cm_BPlusDirLookup(&dirop, sp->nsearchNamep, &sp->fid);
509 code = cm_DirLookup(&dirop, sp->searchNamep, &sp->fid);
517 sp->ExactFound = TRUE;
518 *retscp = NULL; /* force caller to call cm_GetSCache() */
523 if (sp->caseFold && code == CM_ERROR_INEXACT_MATCH) {
526 sp->ExactFound = FALSE;
527 *retscp = NULL; /* force caller to call cm_GetSCache() */
531 return CM_ERROR_BPLUS_NOMATCH;
539 * XXX We only get the length once. It might change when we drop the
542 dirLength = scp->length;
545 bufferOffset.LowPart = bufferOffset.HighPart = 0;
547 curOffset = *startOffsetp;
549 curOffset.HighPart = 0;
550 curOffset.LowPart = 0;
554 /* make sure that curOffset.LowPart doesn't point to the first
555 * 32 bytes in the 2nd through last dir page, and that it
556 * doesn't point at the first 13 32-byte chunks in the first
557 * dir page, since those are dir and page headers, and don't
558 * contain useful information.
560 temp = curOffset.LowPart & (2048-1);
561 if (curOffset.HighPart == 0 && curOffset.LowPart < 2048) {
562 /* we're in the first page */
563 if (temp < 13*32) temp = 13*32;
566 /* we're in a later dir page */
567 if (temp < 32) temp = 32;
570 /* make sure the low order 5 bits are zero */
573 /* now put temp bits back ito curOffset.LowPart */
574 curOffset.LowPart &= ~(2048-1);
575 curOffset.LowPart |= temp;
577 /* check if we've passed the dir's EOF */
578 if (LargeIntegerGreaterThanOrEqualTo(curOffset, dirLength))
581 /* see if we can use the bufferp we have now; compute in which
582 * page the current offset would be, and check whether that's
583 * the offset of the buffer we have. If not, get the buffer.
585 thyper.HighPart = curOffset.HighPart;
586 thyper.LowPart = curOffset.LowPart & ~(cm_data.buf_blockSize-1);
587 if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
590 lock_ReleaseMutex(&bufferp->mx);
591 buf_Release(bufferp);
595 code = buf_Get(scp, &thyper, &bufferp);
597 /* if buf_Get() fails we do not have a buffer object to lock */
602 lock_ObtainMutex(&bufferp->mx);
603 bufferOffset = thyper;
605 /* now get the data in the cache */
607 lock_ObtainWrite(&scp->rw);
608 code = cm_SyncOp(scp, bufferp, userp, reqp,
610 CM_SCACHESYNC_NEEDCALLBACK
612 | CM_SCACHESYNC_BUFLOCKED);
614 lock_ReleaseWrite(&scp->rw);
617 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
619 if (cm_HaveBuffer(scp, bufferp, 1)) {
620 lock_ReleaseWrite(&scp->rw);
624 /* otherwise, load the buffer and try again */
625 lock_ReleaseMutex(&bufferp->mx);
626 code = cm_GetBuffer(scp, bufferp, NULL, userp,
628 lock_ReleaseWrite(&scp->rw);
629 lock_ObtainMutex(&bufferp->mx);
634 lock_ReleaseMutex(&bufferp->mx);
635 buf_Release(bufferp);
639 } /* if (wrong buffer) ... */
641 /* now we have the buffer containing the entry we're interested
642 * in; copy it out if it represents a non-deleted entry.
644 entryInDir = curOffset.LowPart & (2048-1);
645 entryInBuffer = curOffset.LowPart & (cm_data.buf_blockSize - 1);
647 /* page header will help tell us which entries are free. Page
648 * header can change more often than once per buffer, since
649 * AFS 3 dir page size may be less than (but not more than) a
650 * buffer package buffer.
652 /* only look intra-buffer */
653 temp = curOffset.LowPart & (cm_data.buf_blockSize - 1);
654 temp &= ~(2048 - 1); /* turn off intra-page bits */
655 pageHeaderp = (cm_pageHeader_t *) (bufferp->datap + temp);
657 /* now determine which entry we're looking at in the page. If
658 * it is free (there's a free bitmap at the start of the dir),
659 * we should skip these 32 bytes.
661 slotInPage = (entryInDir & 0x7e0) >> 5;
662 if (!(pageHeaderp->freeBitmap[slotInPage>>3]
663 & (1 << (slotInPage & 0x7)))) {
664 /* this entry is free */
665 numDirChunks = 1; /* only skip this guy */
669 tp = bufferp->datap + entryInBuffer;
670 dep = (cm_dirEntry_t *) tp; /* now points to AFS3 dir entry */
672 /* while we're here, compute the next entry's location, too,
673 * since we'll need it when writing out the cookie into the
674 * dir listing stream.
676 numDirChunks = cm_NameEntries(dep->name, NULL);
678 /* compute the offset of the cookie representing the next entry */
679 nextEntryCookie = curOffset.LowPart
680 + (CM_DIR_CHUNKSIZE * numDirChunks);
682 if (dep->fid.vnode != 0) {
683 /* this is one of the entries to use: it is not deleted */
684 code = (*funcp)(scp, dep, parmp, &curOffset);
687 } /* if we're including this name */
690 /* and adjust curOffset to be where the new cookie is */
692 thyper.LowPart = CM_DIR_CHUNKSIZE * numDirChunks;
693 curOffset = LargeIntegerAdd(thyper, curOffset);
694 } /* while copying data for dir listing */
696 /* release the mutex */
698 lock_ReleaseMutex(&bufferp->mx);
699 buf_Release(bufferp);
704 int cm_NoneUpper(normchar_t *s)
708 if (c >= 'A' && c <= 'Z')
713 int cm_NoneLower(normchar_t *s)
717 if (c >= 'a' && c <= 'z')
722 long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
725 cm_lookupSearch_t *sp;
727 normchar_t matchName[MAX_PATH];
728 int looking_for_short_name = FALSE;
730 sp = (cm_lookupSearch_t *) rockp;
732 cm_FsStringToNormString(dep->name, -1, matchName, lengthof(matchName));
734 match = cm_NormStrCmpI(matchName, sp->nsearchNamep);
736 match = cm_NormStrCmp(matchName, sp->nsearchNamep);
740 && !cm_Is8Dot3(matchName)) {
742 cm_Gen8Dot3NameInt(dep->name, &dep->fid, matchName, NULL);
744 match = cm_NormStrCmpI(matchName, sp->nsearchNamep);
746 match = cm_NormStrCmp(matchName, sp->nsearchNamep);
747 looking_for_short_name = TRUE;
757 if (!sp->caseFold || looking_for_short_name) {
758 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
759 return CM_ERROR_STOPNOW;
763 * If we get here, we are doing a case-insensitive search, and we
764 * have found a match. Now we determine what kind of match it is:
765 * exact, lower-case, upper-case, or none of the above. This is done
766 * in order to choose among matches, if there are more than one.
769 /* Exact matches are the best. */
770 match = cm_NormStrCmp(matchName, sp->nsearchNamep);
773 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
774 return CM_ERROR_STOPNOW;
777 /* Lower-case matches are next. */
780 if (cm_NoneUpper(matchName)) {
785 /* Upper-case matches are next. */
788 if (cm_NoneLower(matchName)) {
793 /* General matches are last. */
799 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
803 /* read the contents of a mount point into the appropriate string.
804 * called with write locked scp, and returns with locked scp.
806 long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
813 if (scp->mountPointStringp[0])
816 /* otherwise, we have to read it in */
817 lock_ReleaseWrite(&scp->rw);
819 thyper.LowPart = thyper.HighPart = 0;
820 code = buf_Get(scp, &thyper, &bufp);
822 lock_ObtainWrite(&scp->rw);
827 code = cm_SyncOp(scp, bufp, userp, reqp, 0,
828 CM_SCACHESYNC_READ | CM_SCACHESYNC_NEEDCALLBACK);
832 cm_SyncOpDone(scp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
834 if (cm_HaveBuffer(scp, bufp, 0))
837 /* otherwise load buffer */
838 code = cm_GetBuffer(scp, bufp, NULL, userp, reqp);
842 /* locked, has callback, has valid data in buffer */
843 if ((tlen = scp->length.LowPart) > MOUNTPOINTLEN - 1)
844 return CM_ERROR_TOOBIG;
846 code = CM_ERROR_INVAL;
850 /* someone else did the work while we were out */
851 if (scp->mountPointStringp[0]) {
856 /* otherwise, copy out the link */
857 memcpy(scp->mountPointStringp, bufp->datap, tlen);
859 /* now make it null-terminated. Note that the original contents of a
860 * link that is a mount point is "#volname." where "." is there just to
861 * be turned into a null. That is, we can trash the last char of the
862 * link without damaging the vol name. This is a stupid convention,
863 * but that's the protocol.
865 scp->mountPointStringp[tlen-1] = 0;
875 /* called with a locked scp and chases the mount point, yielding outScpp.
876 * scp remains write locked, just for simplicity of describing the interface.
878 long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
879 cm_req_t *reqp, cm_scache_t **outScpp)
881 fschar_t *cellNamep = NULL;
882 fschar_t *volNamep = NULL;
887 cm_volume_t *volp = NULL;
896 if (scp->mountRootFid.cell != 0 && scp->mountRootGen >= cm_data.mountRootGen) {
897 tfid = scp->mountRootFid;
898 lock_ReleaseWrite(&scp->rw);
899 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
900 lock_ObtainWrite(&scp->rw);
904 /* parse the volume name */
905 mpNamep = scp->mountPointStringp;
907 return CM_ERROR_NOSUCHPATH;
908 tlen = cm_FsStrLen(scp->mountPointStringp);
909 mtType = *scp->mountPointStringp;
911 cp = cm_FsStrChr(mpNamep, _FS(':'));
913 /* cellular mount point */
914 cellNamep = (fschar_t *)malloc((cp - mpNamep) * sizeof(fschar_t));
915 cm_FsStrCpyN(cellNamep, cp - mpNamep, mpNamep + 1, cp - mpNamep - 1);
916 volNamep = cm_FsStrDup(cp+1);
918 /* now look up the cell */
919 lock_ReleaseWrite(&scp->rw);
920 cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE);
921 lock_ObtainWrite(&scp->rw);
924 volNamep = cm_FsStrDup(mpNamep + 1);
926 cellp = cm_FindCellByID(scp->fid.cell, 0);
930 code = CM_ERROR_NOSUCHCELL;
934 vnLength = cm_FsStrLen(volNamep);
935 if (vnLength >= 8 && cm_FsStrCmp(volNamep + vnLength - 7, ".backup") == 0)
936 targetType = BACKVOL;
937 else if (vnLength >= 10
938 && cm_FsStrCmp(volNamep + vnLength - 9, ".readonly") == 0)
943 /* check for backups within backups */
944 if (targetType == BACKVOL
945 && (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
946 == CM_SCACHEFLAG_RO) {
947 code = CM_ERROR_NOSUCHVOLUME;
951 /* now we need to get the volume */
952 lock_ReleaseWrite(&scp->rw);
953 if (cm_VolNameIsID(volNamep)) {
954 code = cm_FindVolumeByID(cellp, atoi(volNamep), userp, reqp,
955 CM_GETVOL_FLAG_CREATE, &volp);
957 code = cm_FindVolumeByName(cellp, volNamep, userp, reqp,
958 CM_GETVOL_FLAG_CREATE, &volp);
960 lock_ObtainWrite(&scp->rw);
963 afs_uint32 cell, volume;
964 cm_vol_state_t *statep;
966 cell = cellp->cellID;
968 /* if the mt pt originates in a .backup volume (not a .readonly)
969 * and FollowBackupPath is active, and if there is a .backup
970 * volume for the target, then use the .backup of the target
971 * instead of the read-write.
973 if (cm_followBackupPath &&
974 volp->vol[BACKVOL].ID != 0 &&
975 (dscp->flags & (CM_SCACHEFLAG_RO|CM_SCACHEFLAG_PURERO)) == CM_SCACHEFLAG_RO &&
976 (targetType == RWVOL || targetType == ROVOL && volp->vol[ROVOL].ID == 0)
978 targetType = BACKVOL;
980 /* if the mt pt is in a read-only volume (not just a
981 * backup), and if there is a read-only volume for the
982 * target, and if this is a targetType '#' mount point, use
983 * the read-only, otherwise use the one specified.
985 else if (mtType == '#' && targetType == RWVOL &&
986 (scp->flags & CM_SCACHEFLAG_PURERO) &&
987 volp->vol[ROVOL].ID != 0) {
991 lock_ObtainWrite(&volp->rw);
992 statep = cm_VolumeStateByType(volp, targetType);
994 statep->dotdotFid = dscp->fid;
995 lock_ReleaseWrite(&volp->rw);
997 /* the rest of the fid is a magic number */
998 cm_SetFid(&scp->mountRootFid, cell, volume, 1, 1);
999 scp->mountRootGen = cm_data.mountRootGen;
1001 tfid = scp->mountRootFid;
1002 lock_ReleaseWrite(&scp->rw);
1003 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
1004 lock_ObtainWrite(&scp->rw);
1017 long cm_LookupInternal(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_user_t *userp,
1018 cm_req_t *reqp, cm_scache_t **outScpp)
1021 int dnlcHit = 1; /* did we hit in the dnlc? yes, we did */
1022 cm_scache_t *tscp = NULL;
1023 cm_scache_t *mountedScp;
1024 cm_lookupSearch_t rock;
1026 normchar_t *nnamep = NULL;
1027 fschar_t *fnamep = NULL;
1031 memset(&rock, 0, sizeof(rock));
1033 if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
1034 && cm_ClientStrCmp(cnamep, _C("..")) == 0) {
1035 if (dscp->dotdotFid.volume == 0)
1036 return CM_ERROR_NOSUCHVOLUME;
1037 rock.fid = dscp->dotdotFid;
1039 } else if (cm_ClientStrCmp(cnamep, _C(".")) == 0) {
1040 rock.fid = dscp->fid;
1044 nnamep = cm_ClientStringToNormStringAlloc(cnamep, -1, NULL);
1045 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
1047 if (flags & CM_FLAG_NOMOUNTCHASE) {
1048 /* In this case, we should go and call cm_Dir* functions
1049 directly since the following cm_ApplyDir() function will
1057 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ, &dirop);
1060 code = cm_BPlusDirLookup(&dirop, nnamep, &rock.fid);
1065 code = cm_DirLookup(&dirop, fnamep, &rock.fid);
1067 cm_EndDirOp(&dirop);
1077 if (code == CM_ERROR_INEXACT_MATCH && (flags & CM_FLAG_CASEFOLD)) {
1084 return CM_ERROR_BPLUS_NOMATCH;
1089 rock.fid.cell = dscp->fid.cell;
1090 rock.fid.volume = dscp->fid.volume;
1091 rock.searchNamep = fnamep;
1092 rock.nsearchNamep = nnamep;
1093 rock.caseFold = (flags & CM_FLAG_CASEFOLD);
1094 rock.hasTilde = ((cm_ClientStrChr(cnamep, '~') != NULL) ? 1 : 0);
1096 /* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
1097 code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
1098 (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
1100 /* code == 0 means we fell off the end of the dir, while stopnow means
1101 * that we stopped early, probably because we found the entry we're
1102 * looking for. Any other non-zero code is an error.
1104 if (code && code != CM_ERROR_STOPNOW) {
1105 /* if the cm_scache_t we are searching in is not a directory
1106 * we must return path not found because the error
1107 * is to describe the final component not an intermediary
1109 if (code == CM_ERROR_NOTDIR) {
1110 if (flags & CM_FLAG_CHECKPATH)
1111 code = CM_ERROR_NOSUCHPATH;
1113 code = CM_ERROR_NOSUCHFILE;
1118 getroot = (dscp==cm_data.rootSCachep) ;
1120 if (!cm_freelanceEnabled || !getroot) {
1121 if (flags & CM_FLAG_CHECKPATH)
1122 code = CM_ERROR_NOSUCHPATH;
1124 code = CM_ERROR_NOSUCHFILE;
1127 else if (!cm_ClientStrChr(cnamep, '#') &&
1128 !cm_ClientStrChr(cnamep, '%') &&
1129 cm_ClientStrCmpI(cnamep, _C("srvsvc")) &&
1130 cm_ClientStrCmpI(cnamep, _C("wkssvc")) &&
1131 cm_ClientStrCmpI(cnamep, _C("ipc$")))
1133 /* nonexistent dir on freelance root, so add it */
1134 fschar_t fullname[CELL_MAXNAMELEN + 1] = "."; /* +1 so that when we skip the . the size is still CELL_MAXNAMELEN */
1137 osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %S",
1138 osi_LogSaveClientString(afsd_logp,cnamep));
1141 * There is an ugly behavior where a share name "foo" will be searched
1142 * for as "fo". If the searched for name differs by an already existing
1143 * symlink or mount point in the Freelance directory, do not add the
1144 * new value automatically.
1148 if (cnamep[0] == '.') {
1149 if (cm_GetCell_Gen(&fnamep[1], &fullname[1], CM_FLAG_CREATE)) {
1151 if (!cm_FreelanceMountPointExists(fullname, 0))
1152 code = cm_FreelanceAddMount(fullname, &fullname[1], "root.cell.",
1154 if ( cm_FsStrCmpI(&fnamep[1], &fullname[1]) &&
1155 !cm_FreelanceMountPointExists(fnamep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0) &&
1156 !cm_FreelanceSymlinkExists(fnamep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0))
1157 code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
1160 if (cm_GetCell_Gen(fnamep, fullname, CM_FLAG_CREATE)) {
1162 if (!cm_FreelanceMountPointExists(fullname, 0))
1163 code = cm_FreelanceAddMount(fullname, fullname, "root.cell.", 0, &rock.fid);
1164 if ( cm_FsStrCmpI(fnamep, fullname) &&
1165 !cm_FreelanceMountPointExists(fnamep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0) &&
1166 !cm_FreelanceSymlinkExists(fnamep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0))
1167 code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
1170 if (!found || code < 0) { /* add mount point failed, so give up */
1171 if (flags & CM_FLAG_CHECKPATH)
1172 code = CM_ERROR_NOSUCHPATH;
1174 code = CM_ERROR_NOSUCHFILE;
1177 tscp = NULL; /* to force call of cm_GetSCache */
1179 if (flags & CM_FLAG_CHECKPATH)
1180 code = CM_ERROR_NOSUCHPATH;
1182 code = CM_ERROR_NOSUCHFILE;
1188 if ( !tscp ) /* we did not find it in the dnlc */
1191 code = cm_GetSCache(&rock.fid, &tscp, userp, reqp);
1195 /* tscp is now held */
1197 lock_ObtainWrite(&tscp->rw);
1198 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1199 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1201 lock_ReleaseWrite(&tscp->rw);
1202 cm_ReleaseSCache(tscp);
1205 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1206 /* tscp is now locked */
1208 if (!(flags & CM_FLAG_NOMOUNTCHASE)
1209 && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
1210 /* mount points are funny: they have a volume name to mount
1213 code = cm_ReadMountPoint(tscp, userp, reqp);
1215 code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
1217 lock_ReleaseWrite(&tscp->rw);
1218 cm_ReleaseSCache(tscp);
1225 lock_ReleaseWrite(&tscp->rw);
1228 /* copy back pointer */
1231 /* insert scache in dnlc */
1232 if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
1233 /* lock the directory entry to prevent racing callback revokes */
1234 lock_ObtainRead(&dscp->rw);
1235 if ( dscp->cbServerp != NULL && dscp->cbExpires > 0 ) {
1236 /* TODO: reuse nnamep from above */
1239 nnamep = cm_ClientStringToNormStringAlloc(cnamep, -1, NULL);
1240 cm_dnlcEnter(dscp, nnamep, tscp);
1242 lock_ReleaseRead(&dscp->rw);
1259 int cm_ExpandSysName(clientchar_t *inp, clientchar_t *outp, long outSizeCch, unsigned int index)
1264 tp = cm_ClientStrRChr(inp, '@');
1266 return 0; /* no @sys */
1268 if (cm_ClientStrCmp(tp, _C("@sys")) != 0)
1269 return 0; /* no @sys */
1271 /* caller just wants to know if this is a valid @sys type of name */
1275 if (index >= cm_sysNameCount)
1278 /* otherwise generate the properly expanded @sys name */
1279 prefixCount = (int)(tp - inp);
1281 cm_ClientStrCpyN(outp, outSizeCch, inp, prefixCount); /* copy out "a." from "a.@sys" */
1282 outp[prefixCount] = 0; /* null terminate the "a." */
1283 cm_ClientStrCat(outp, outSizeCch, cm_sysNameList[index]);/* append i386_nt40 */
1287 long cm_EvaluateVolumeReference(clientchar_t * namep, long flags, cm_user_t * userp,
1288 cm_req_t *reqp, cm_scache_t ** outScpp)
1290 afs_uint32 code = 0;
1291 fschar_t cellName[CELL_MAXNAMELEN];
1292 fschar_t volumeName[VL_MAXNAMELEN];
1296 fschar_t * fnamep = NULL;
1298 cm_cell_t * cellp = NULL;
1299 cm_volume_t * volp = NULL;
1303 int mountType = RWVOL;
1305 osi_Log1(afsd_logp, "cm_EvaluateVolumeReference for string [%S]",
1306 osi_LogSaveClientString(afsd_logp, namep));
1308 if (cm_ClientStrCmpNI(namep, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH) != 0) {
1309 goto _exit_invalid_path;
1312 /* namep is assumed to look like the following:
1314 @vol:<cellname>%<volume>\0
1316 @vol:<cellname>#<volume>\0
1320 fnamep = cm_ClientStringToFsStringAlloc(namep, -1, NULL);
1321 cp = fnamep + CM_PREFIX_VOL_CCH; /* cp points to cell name, hopefully */
1322 tp = cm_FsStrChr(cp, '%');
1324 tp = cm_FsStrChr(cp, '#');
1326 (len = tp - cp) == 0 ||
1327 len > CELL_MAXNAMELEN)
1328 goto _exit_invalid_path;
1329 cm_FsStrCpyN(cellName, lengthof(cellName), cp, len);
1334 cp = tp+1; /* cp now points to volume, supposedly */
1335 cm_FsStrCpy(volumeName, lengthof(volumeName), cp);
1337 /* OK, now we have the cell and the volume */
1338 osi_Log2(afsd_logp, " Found cell [%s] and volume [%s]",
1339 osi_LogSaveFsString(afsd_logp, cellName),
1340 osi_LogSaveFsString(afsd_logp, volumeName));
1342 cellp = cm_GetCell(cellName, CM_FLAG_CREATE);
1343 if (cellp == NULL) {
1344 goto _exit_invalid_path;
1347 len = cm_FsStrLen(volumeName);
1348 if (len >= 8 && cm_FsStrCmp(volumeName + len - 7, ".backup") == 0)
1350 else if (len >= 10 &&
1351 cm_FsStrCmp(volumeName + len - 9, ".readonly") == 0)
1356 if (cm_VolNameIsID(volumeName)) {
1357 code = cm_FindVolumeByID(cellp, atoi(volumeName), userp, reqp,
1358 CM_GETVOL_FLAG_CREATE, &volp);
1360 code = cm_FindVolumeByName(cellp, volumeName, userp, reqp,
1361 CM_GETVOL_FLAG_CREATE, &volp);
1367 if (volType == BACKVOL)
1368 volume = volp->vol[BACKVOL].ID;
1369 else if (volType == ROVOL ||
1370 (volType == RWVOL && mountType == ROVOL && volp->vol[ROVOL].ID != 0))
1371 volume = volp->vol[ROVOL].ID;
1373 volume = volp->vol[RWVOL].ID;
1375 cm_SetFid(&fid, cellp->cellID, volume, 1, 1);
1377 code = cm_GetSCache(&fid, outScpp, userp, reqp);
1390 if (flags & CM_FLAG_CHECKPATH)
1391 return CM_ERROR_NOSUCHPATH;
1393 return CM_ERROR_NOSUCHFILE;
1396 #ifdef DEBUG_REFCOUNT
1397 long cm_LookupDbg(cm_scache_t *dscp, clientchar_t *namep, long flags, cm_user_t *userp,
1398 cm_req_t *reqp, cm_scache_t **outScpp, char * file, long line)
1400 long cm_Lookup(cm_scache_t *dscp, clientchar_t *namep, long flags, cm_user_t *userp,
1401 cm_req_t *reqp, cm_scache_t **outScpp)
1405 clientchar_t tname[AFSPATHMAX];
1406 int sysNameIndex = 0;
1407 cm_scache_t *scp = NULL;
1409 #ifdef DEBUG_REFCOUNT
1410 afsi_log("%s:%d cm_Lookup dscp 0x%p ref %d", file, line, dscp, dscp->refCount, file, line);
1411 osi_Log2(afsd_logp, "cm_Lookup dscp 0x%p ref %d", dscp, dscp->refCount);
1414 if ( cm_ClientStrCmpI(namep,_C(SMB_IOCTL_FILENAME_NOSLASH)) == 0 ) {
1415 if (flags & CM_FLAG_CHECKPATH)
1416 return CM_ERROR_NOSUCHPATH;
1418 return CM_ERROR_NOSUCHFILE;
1421 if (dscp == cm_data.rootSCachep &&
1422 cm_ClientStrCmpNI(namep, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH) == 0) {
1423 return cm_EvaluateVolumeReference(namep, flags, userp, reqp, outScpp);
1426 if (cm_ExpandSysName(namep, NULL, 0, 0) > 0) {
1427 for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
1428 code = cm_ExpandSysName(namep, tname, lengthof(tname), sysNameIndex);
1430 code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
1431 #ifdef DEBUG_REFCOUNT
1432 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);
1433 osi_Log3(afsd_logp, "cm_LookupInternal (1) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1441 cm_ReleaseSCache(scp);
1445 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1446 #ifdef DEBUG_REFCOUNT
1447 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);
1448 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1455 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1456 #ifdef DEBUG_REFCOUNT
1457 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);
1458 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1464 /* None of the possible sysName expansions could be found */
1465 if (flags & CM_FLAG_CHECKPATH)
1466 return CM_ERROR_NOSUCHPATH;
1468 return CM_ERROR_NOSUCHFILE;
1471 /*! \brief Unlink a file name
1473 Encapsulates a call to RXAFS_RemoveFile().
1475 \param[in] dscp cm_scache_t pointing at the directory containing the
1476 name to be unlinked.
1478 \param[in] fnamep Original name to be unlinked. This is the
1479 name that will be passed into the RXAFS_RemoveFile() call.
1480 This parameter is optional. If not provided, the value will
1483 \param[in] came Client name to be unlinked. This name will be used
1484 to update the local directory caches.
1486 \param[in] userp cm_user_t for the request.
1488 \param[in] reqp Request tracker.
1491 long cm_Unlink(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t * cnamep,
1492 cm_user_t *userp, cm_req_t *reqp)
1498 AFSFetchStatus newDirStatus;
1500 struct rx_connection * rxconnp;
1502 cm_scache_t *scp = NULL;
1503 int free_fnamep = FALSE;
1505 if (fnamep == NULL) {
1508 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ, &dirop);
1510 code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
1513 cm_EndDirOp(&dirop);
1520 #ifdef AFS_FREELANCE_CLIENT
1521 if (cm_freelanceEnabled && dscp == cm_data.rootSCachep) {
1522 /* deleting a mount point from the root dir. */
1523 code = cm_FreelanceRemoveMount(fnamep);
1528 code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
1530 /* make sure we don't screw up the dir status during the merge */
1531 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
1533 lock_ObtainWrite(&dscp->rw);
1534 sflags = CM_SCACHESYNC_STOREDATA;
1535 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1536 lock_ReleaseWrite(&dscp->rw);
1538 cm_EndDirOp(&dirop);
1543 afsFid.Volume = dscp->fid.volume;
1544 afsFid.Vnode = dscp->fid.vnode;
1545 afsFid.Unique = dscp->fid.unique;
1547 osi_Log1(afsd_logp, "CALL RemoveFile scp 0x%p", dscp);
1549 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
1553 rxconnp = cm_GetRxConn(connp);
1554 code = RXAFS_RemoveFile(rxconnp, &afsFid, fnamep,
1555 &newDirStatus, &volSync);
1556 rx_PutConnection(rxconnp);
1558 } while (cm_Analyze(connp, userp, reqp, &dscp->fid, &volSync, NULL, NULL, code));
1559 code = cm_MapRPCError(code, reqp);
1562 osi_Log1(afsd_logp, "CALL RemoveFile FAILURE, code 0x%x", code);
1564 osi_Log0(afsd_logp, "CALL RemoveFile SUCCESS");
1567 lock_ObtainWrite(&dirop.scp->dirlock);
1568 dirop.lockType = CM_DIRLOCK_WRITE;
1570 lock_ObtainWrite(&dscp->rw);
1571 cm_dnlcRemove(dscp, cnamep);
1572 cm_SyncOpDone(dscp, NULL, sflags);
1574 cm_MergeStatus(NULL, dscp, &newDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
1575 } else if (code == CM_ERROR_NOSUCHFILE) {
1576 /* windows would not have allowed the request to delete the file
1577 * if it did not believe the file existed. therefore, we must
1578 * have an inconsistent view of the world.
1580 dscp->cbServerp = NULL;
1582 lock_ReleaseWrite(&dscp->rw);
1584 if (code == 0 && cm_CheckDirOpForSingleChange(&dirop) && cnamep) {
1585 cm_DirDeleteEntry(&dirop, fnamep);
1587 cm_BPlusDirDeleteEntry(&dirop, cnamep);
1590 cm_EndDirOp(&dirop);
1593 cm_ReleaseSCache(scp);
1595 lock_ObtainWrite(&scp->rw);
1596 scp->flags |= CM_SCACHEFLAG_DELETED;
1597 lock_ReleaseWrite(&scp->rw);
1608 /* called with a write locked vnode, and fills in the link info.
1609 * returns this the vnode still write locked.
1611 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1618 lock_AssertWrite(&linkScp->rw);
1619 if (!linkScp->mountPointStringp[0]) {
1620 /* read the link data */
1621 lock_ReleaseWrite(&linkScp->rw);
1622 thyper.LowPart = thyper.HighPart = 0;
1623 code = buf_Get(linkScp, &thyper, &bufp);
1624 lock_ObtainWrite(&linkScp->rw);
1628 code = cm_SyncOp(linkScp, bufp, userp, reqp, 0,
1629 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1634 cm_SyncOpDone(linkScp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1636 if (cm_HaveBuffer(linkScp, bufp, 0))
1639 code = cm_GetBuffer(linkScp, bufp, NULL, userp, reqp);
1644 } /* while loop to get the data */
1646 /* now if we still have no link read in,
1647 * copy the data from the buffer */
1648 if ((temp = linkScp->length.LowPart) >= MOUNTPOINTLEN) {
1650 return CM_ERROR_TOOBIG;
1653 /* otherwise, it fits; make sure it is still null (could have
1654 * lost race with someone else referencing this link above),
1655 * and if so, copy in the data.
1657 if (!linkScp->mountPointStringp[0]) {
1658 strncpy(linkScp->mountPointStringp, bufp->datap, temp);
1659 linkScp->mountPointStringp[temp] = 0; /* null terminate */
1661 if ( !strnicmp(linkScp->mountPointStringp, "msdfs:", strlen("msdfs:")) )
1662 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1665 } /* don't have sym link contents cached */
1670 /* called with a held vnode and a path suffix, with the held vnode being a
1671 * symbolic link. Our goal is to generate a new path to interpret, and return
1672 * this new path in newSpaceBufferp. If the new vnode is relative to a dir
1673 * other than the directory containing the symbolic link, then the new root is
1674 * returned in *newRootScpp, otherwise a null is returned there.
1676 long cm_AssembleLink(cm_scache_t *linkScp, fschar_t *pathSuffixp,
1677 cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1678 cm_user_t *userp, cm_req_t *reqp)
1685 *newRootScpp = NULL;
1686 *newSpaceBufferp = NULL;
1688 lock_ObtainWrite(&linkScp->rw);
1689 code = cm_HandleLink(linkScp, userp, reqp);
1693 /* if we may overflow the buffer, bail out; buffer is signficantly
1694 * bigger than max path length, so we don't really have to worry about
1695 * being a little conservative here.
1697 if (cm_FsStrLen(linkScp->mountPointStringp) + cm_FsStrLen(pathSuffixp) + 2
1698 >= CM_UTILS_SPACESIZE) {
1699 code = CM_ERROR_TOOBIG;
1703 tsp = cm_GetSpace();
1704 linkp = linkScp->mountPointStringp;
1705 if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1706 if (strlen(linkp) > cm_mountRootLen)
1707 StringCbCopyA((char *) tsp->data, sizeof(tsp->data), linkp+cm_mountRootLen+1);
1710 *newRootScpp = cm_data.rootSCachep;
1711 cm_HoldSCache(cm_data.rootSCachep);
1712 } else if (linkp[0] == '\\' && linkp[1] == '\\') {
1713 if (!strnicmp(&linkp[2], cm_NetbiosName, (len = (long)strlen(cm_NetbiosName))))
1715 char * p = &linkp[len + 3];
1716 if (strnicmp(p, "all", 3) == 0)
1719 StringCbCopyA(tsp->data, sizeof(tsp->data), p);
1720 for (p = tsp->data; *p; p++) {
1724 *newRootScpp = cm_data.rootSCachep;
1725 cm_HoldSCache(cm_data.rootSCachep);
1727 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1728 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1729 code = CM_ERROR_PATH_NOT_COVERED;
1731 } else if ( linkScp->fileType == CM_SCACHETYPE_DFSLINK ||
1732 !strnicmp(linkp, "msdfs:", (len = (long)strlen("msdfs:"))) ) {
1733 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1734 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1735 code = CM_ERROR_PATH_NOT_COVERED;
1736 } else if (*linkp == '\\' || *linkp == '/') {
1738 /* formerly, this was considered to be from the AFS root,
1739 * but this seems to create problems. instead, we will just
1740 * reject the link */
1741 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp+1);
1742 *newRootScpp = cm_data.rootSCachep;
1743 cm_HoldSCache(cm_data.rootSCachep);
1745 /* we still copy the link data into the response so that
1746 * the user can see what the link points to
1748 linkScp->fileType = CM_SCACHETYPE_INVALID;
1749 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1750 code = CM_ERROR_NOSUCHPATH;
1753 /* a relative link */
1754 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1756 if (pathSuffixp[0] != 0) { /* if suffix string is non-null */
1757 StringCchCatA(tsp->data,lengthof(tsp->data), "\\");
1758 StringCchCatA(tsp->data,lengthof(tsp->data), pathSuffixp);
1761 clientchar_t * cpath = cm_FsStringToClientStringAlloc(tsp->data, -1, NULL);
1762 cm_ClientStrCpy(tsp->wdata, lengthof(tsp->wdata), cpath);
1764 *newSpaceBufferp = tsp;
1768 if (code == CM_ERROR_PATH_NOT_COVERED && reqp->tidPathp && reqp->relPathp) {
1769 cm_VolStatus_Notify_DFS_Mapping(linkScp, reqp->tidPathp, reqp->relPathp);
1774 lock_ReleaseWrite(&linkScp->rw);
1777 #ifdef DEBUG_REFCOUNT
1778 long cm_NameIDbg(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
1779 cm_user_t *userp, clientchar_t *tidPathp, cm_req_t *reqp,
1780 cm_scache_t **outScpp,
1781 char * file, long line)
1783 long cm_NameI(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
1784 cm_user_t *userp, clientchar_t *tidPathp,
1785 cm_req_t *reqp, cm_scache_t **outScpp)
1789 clientchar_t *tp; /* ptr moving through input buffer */
1790 clientchar_t tc; /* temp char */
1791 int haveComponent; /* has new component started? */
1792 clientchar_t component[AFSPATHMAX]; /* this is the new component */
1793 clientchar_t *cp; /* component name being assembled */
1794 cm_scache_t *tscp; /* current location in the hierarchy */
1795 cm_scache_t *nscp; /* next dude down */
1796 cm_scache_t *dirScp; /* last dir we searched */
1797 cm_scache_t *linkScp; /* new root for the symlink we just
1799 cm_space_t *psp; /* space for current path, if we've hit
1801 cm_space_t *tempsp; /* temp vbl */
1802 clientchar_t *restp; /* rest of the pathname to interpret */
1803 int symlinkCount; /* count of # of symlinks traversed */
1804 int extraFlag; /* avoid chasing mt pts for dir cmd */
1805 int phase = 1; /* 1 = tidPathp, 2 = pathp */
1806 #define MAX_FID_COUNT 512
1807 cm_fid_t fids[MAX_FID_COUNT]; /* array of fids processed in this path walk */
1808 int fid_count = 0; /* number of fids processed in this path walk */
1813 #ifdef DEBUG_REFCOUNT
1814 afsi_log("%s:%d cm_NameI rootscp 0x%p ref %d", file, line, rootSCachep, rootSCachep->refCount);
1815 osi_Log4(afsd_logp,"cm_NameI rootscp 0x%p path %S tidpath %S flags 0x%x",
1816 rootSCachep, pathp ? pathp : L"<NULL>", tidPathp ? tidPathp : L"<NULL>",
1831 cm_HoldSCache(tscp);
1839 /* map Unix slashes into DOS ones so we can interpret Unix
1845 if (!haveComponent) {
1848 } else if (tc == 0) {
1862 /* we have a component here */
1863 if (tc == 0 || tc == '\\') {
1864 /* end of the component; we're at the last
1865 * component if tc == 0. However, if the last
1866 * is a symlink, we have more to do.
1868 *cp++ = 0; /* add null termination */
1870 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
1871 extraFlag = CM_FLAG_NOMOUNTCHASE;
1872 code = cm_Lookup(tscp, component,
1874 userp, reqp, &nscp);
1877 if (!cm_ClientStrCmp(component,_C("..")) ||
1878 !cm_ClientStrCmp(component,_C("."))) {
1880 * roll back the fid list until we find the
1881 * fid that matches where we are now. Its not
1882 * necessarily one or two fids because they
1883 * might have been symlinks or mount points or
1884 * both that were crossed.
1886 for ( i=fid_count-1; i>=0; i--) {
1887 if (!cm_FidCmp(&nscp->fid, &fids[i]))
1892 /* add the new fid to the list */
1893 for ( i=0; i<fid_count; i++) {
1894 if ( !cm_FidCmp(&nscp->fid, &fids[i]) ) {
1895 code = CM_ERROR_TOO_MANY_SYMLINKS;
1896 cm_ReleaseSCache(nscp);
1901 if (i == fid_count && fid_count < MAX_FID_COUNT) {
1902 fids[fid_count++] = nscp->fid;
1908 cm_ReleaseSCache(tscp);
1910 cm_ReleaseSCache(dirScp);
1913 if ((code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH) &&
1914 tscp->fileType == CM_SCACHETYPE_SYMLINK) {
1915 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_NOSUCHPATH");
1916 return CM_ERROR_NOSUCHPATH;
1918 osi_Log1(afsd_logp,"cm_NameI code 0x%x", code);
1923 haveComponent = 0; /* component done */
1925 cm_ReleaseSCache(dirScp);
1926 dirScp = tscp; /* for some symlinks */
1927 tscp = nscp; /* already held */
1929 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
1932 cm_ReleaseSCache(dirScp);
1938 /* now, if tscp is a symlink, we should follow it and
1939 * assemble the path again.
1941 lock_ObtainWrite(&tscp->rw);
1942 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1943 CM_SCACHESYNC_GETSTATUS
1944 | CM_SCACHESYNC_NEEDCALLBACK);
1946 lock_ReleaseWrite(&tscp->rw);
1947 cm_ReleaseSCache(tscp);
1950 cm_ReleaseSCache(dirScp);
1955 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1957 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
1958 /* this is a symlink; assemble a new buffer */
1959 lock_ReleaseWrite(&tscp->rw);
1960 if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
1961 cm_ReleaseSCache(tscp);
1964 cm_ReleaseSCache(dirScp);
1969 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_TOO_MANY_SYMLINKS");
1970 return CM_ERROR_TOO_MANY_SYMLINKS;
1980 /* TODO: make this better */
1981 frestp = cm_ClientStringToFsStringAlloc(restp, -1, NULL);
1982 code = cm_AssembleLink(tscp, frestp, &linkScp, &tempsp, userp, reqp);
1986 if (code == 0 && linkScp != NULL) {
1987 if (linkScp == cm_data.rootSCachep)
1990 for ( i=0; i<fid_count; i++) {
1991 if ( !cm_FidCmp(&linkScp->fid, &fids[i]) ) {
1992 code = CM_ERROR_TOO_MANY_SYMLINKS;
1993 cm_ReleaseSCache(linkScp);
1999 if (i == fid_count && fid_count < MAX_FID_COUNT) {
2000 fids[fid_count++] = linkScp->fid;
2005 /* something went wrong */
2006 cm_ReleaseSCache(tscp);
2009 cm_ReleaseSCache(dirScp);
2015 /* otherwise, tempsp has the new path,
2016 * and linkScp is the new root from
2017 * which to interpret that path.
2018 * Continue with the namei processing,
2019 * also doing the bookkeeping for the
2020 * space allocation and tracking the
2021 * vnode reference counts.
2027 cm_ReleaseSCache(tscp);
2032 * now, if linkScp is null, that's
2033 * AssembleLink's way of telling us that
2034 * the sym link is relative to the dir
2035 * containing the link. We have a ref
2036 * to it in dirScp, and we hold it now
2037 * and reuse it as the new spot in the
2045 /* not a symlink, we may be done */
2046 lock_ReleaseWrite(&tscp->rw);
2054 cm_ReleaseSCache(dirScp);
2062 cm_ReleaseSCache(dirScp);
2065 } /* end of a component */
2068 } /* we have a component */
2069 } /* big while loop over all components */
2073 cm_ReleaseSCache(dirScp);
2079 cm_ReleaseSCache(tscp);
2081 #ifdef DEBUG_REFCOUNT
2082 afsi_log("%s:%d cm_NameI code 0x%x outScpp 0x%p ref %d", file, line, code, *outScpp, (*outScpp) ? (*outScpp)->refCount : 0);
2084 osi_Log2(afsd_logp,"cm_NameI code 0x%x outScpp 0x%p", code, *outScpp);
2088 /* called with a dir, and a vnode within the dir that happens to be a symlink.
2089 * We chase the link, and return a held pointer to the target, if it exists,
2090 * in *outScpp. If we succeed, we return 0, otherwise we return an error code
2091 * and do not hold or return a target vnode.
2093 * This is very similar to calling cm_NameI with the last component of a name,
2094 * which happens to be a symlink, except that we've already passed by the name.
2096 * This function is typically called by the directory listing functions, which
2097 * encounter symlinks but need to return the proper file length so programs
2098 * like "more" work properly when they make use of the attributes retrieved from
2101 * The input vnode should not be locked when this function is called.
2103 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
2104 cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
2108 cm_scache_t *newRootScp;
2112 osi_Log1(afsd_logp, "Evaluating symlink scp 0x%p", linkScp);
2114 code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
2118 /* now, if newRootScp is NULL, we're really being told that the symlink
2119 * is relative to the current directory (dscp).
2121 if (newRootScp == NULL) {
2123 cm_HoldSCache(dscp);
2126 code = cm_NameI(newRootScp, spacep->wdata,
2127 CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
2128 userp, NULL, reqp, outScpp);
2130 if (code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH)
2131 code = CM_ERROR_NOSUCHPATH;
2133 /* this stuff is allocated no matter what happened on the namei call,
2135 cm_FreeSpace(spacep);
2136 cm_ReleaseSCache(newRootScp);
2138 if (linkScp == *outScpp) {
2139 cm_ReleaseSCache(*outScpp);
2141 code = CM_ERROR_NOSUCHPATH;
2147 /* for a given entry, make sure that it isn't in the stat cache, and then
2148 * add it to the list of file IDs to be obtained.
2150 * Don't bother adding it if we already have a vnode. Note that the dir
2151 * is locked, so we have to be careful checking the vnode we're thinking of
2152 * processing, to avoid deadlocks.
2154 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
2165 /* Don't overflow bsp. */
2166 if (bsp->counter >= CM_BULKMAX)
2167 return CM_ERROR_STOPNOW;
2169 thyper.LowPart = cm_data.buf_blockSize;
2170 thyper.HighPart = 0;
2171 thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
2173 /* thyper is now the first byte past the end of the record we're
2174 * interested in, and bsp->bufOffset is the first byte of the record
2175 * we're interested in.
2176 * Skip data in the others.
2179 if (LargeIntegerLessThan(*offp, bsp->bufOffset))
2181 if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
2182 return CM_ERROR_STOPNOW;
2183 if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
2186 cm_SetFid(&tfid, scp->fid.cell, scp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
2187 tscp = cm_FindSCache(&tfid);
2189 if (lock_TryWrite(&tscp->rw)) {
2190 /* we have an entry that we can look at */
2191 if (!(tscp->flags & CM_SCACHEFLAG_EACCESS) && cm_HaveCallback(tscp)) {
2192 /* we have a callback on it. Don't bother
2193 * fetching this stat entry, since we're happy
2194 * with the info we have.
2196 lock_ReleaseWrite(&tscp->rw);
2197 cm_ReleaseSCache(tscp);
2200 lock_ReleaseWrite(&tscp->rw);
2202 cm_ReleaseSCache(tscp);
2205 #ifdef AFS_FREELANCE_CLIENT
2206 // yj: if this is a mountpoint under root.afs then we don't want it
2207 // to be bulkstat-ed, instead, we call getSCache directly and under
2208 // getSCache, it is handled specially.
2209 if ( cm_freelanceEnabled &&
2210 tfid.cell==AFS_FAKE_ROOT_CELL_ID &&
2211 tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
2212 !(tfid.vnode==0x1 && tfid.unique==0x1) )
2214 osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
2215 return cm_GetSCache(&tfid, &tscp, NULL, NULL);
2217 #endif /* AFS_FREELANCE_CLIENT */
2220 bsp->fids[i].Volume = scp->fid.volume;
2221 bsp->fids[i].Vnode = tfid.vnode;
2222 bsp->fids[i].Unique = tfid.unique;
2227 cm_TryBulkStatRPC(cm_scache_t *dscp, cm_bulkStat_t *bbp, cm_user_t *userp, cm_req_t *reqp)
2230 AFSCBFids fidStruct;
2231 AFSBulkStats statStruct;
2233 AFSCBs callbackStruct;
2236 cm_callbackRequest_t cbReq;
2242 struct rx_connection * rxconnp;
2243 int inlinebulk = 0; /* Did we use InlineBulkStatus RPC or not? */
2245 /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
2246 * make the calls to create the entries. Handle AFSCBMAX files at a
2249 for (filex = 0; filex < bbp->counter; filex += filesThisCall) {
2250 filesThisCall = bbp->counter - filex;
2251 if (filesThisCall > AFSCBMAX)
2252 filesThisCall = AFSCBMAX;
2254 fidStruct.AFSCBFids_len = filesThisCall;
2255 fidStruct.AFSCBFids_val = &bbp->fids[filex];
2256 statStruct.AFSBulkStats_len = filesThisCall;
2257 statStruct.AFSBulkStats_val = &bbp->stats[filex];
2258 callbackStruct.AFSCBs_len = filesThisCall;
2259 callbackStruct.AFSCBs_val = &bbp->callbacks[filex];
2260 cm_StartCallbackGrantingCall(NULL, &cbReq);
2261 osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
2263 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2267 rxconnp = cm_GetRxConn(connp);
2268 if (!(connp->serverp->flags & CM_SERVERFLAG_NOINLINEBULK)) {
2269 code = RXAFS_InlineBulkStatus(rxconnp, &fidStruct,
2270 &statStruct, &callbackStruct, &volSync);
2271 if (code == RXGEN_OPCODE) {
2272 cm_SetServerNoInlineBulk(connp->serverp, 0);
2278 code = RXAFS_BulkStatus(rxconnp, &fidStruct,
2279 &statStruct, &callbackStruct, &volSync);
2281 rx_PutConnection(rxconnp);
2283 } while (cm_Analyze(connp, userp, reqp, &dscp->fid,
2284 &volSync, NULL, &cbReq, code));
2285 code = cm_MapRPCError(code, reqp);
2287 osi_Log2(afsd_logp, "CALL %sBulkStatus FAILURE code 0x%x",
2288 inlinebulk ? "Inline" : "", code);
2290 osi_Log1(afsd_logp, "CALL %sBulkStatus SUCCESS", inlinebulk ? "Inline" : "");
2292 /* may as well quit on an error, since we're not going to do
2293 * much better on the next immediate call, either.
2296 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2300 /* otherwise, we should do the merges */
2301 for (i = 0; i<filesThisCall; i++) {
2303 cm_SetFid(&tfid, dscp->fid.cell, bbp->fids[j].Volume, bbp->fids[j].Vnode, bbp->fids[j].Unique);
2304 code = cm_GetSCache(&tfid, &scp, userp, reqp);
2308 /* otherwise, if this entry has no callback info,
2311 lock_ObtainWrite(&scp->rw);
2312 /* now, we have to be extra paranoid on merging in this
2313 * information, since we didn't use cm_SyncOp before
2314 * starting the fetch to make sure that no bad races
2315 * were occurring. Specifically, we need to make sure
2316 * we don't obliterate any newer information in the
2317 * vnode than have here.
2319 * Right now, be pretty conservative: if there's a
2320 * callback or a pending call, skip it.
2322 if ((scp->cbServerp == NULL || (scp->flags & CM_SCACHEFLAG_EACCESS))
2324 (CM_SCACHEFLAG_FETCHING
2325 | CM_SCACHEFLAG_STORING
2326 | CM_SCACHEFLAG_SIZESTORING))) {
2327 cm_EndCallbackGrantingCall(scp, &cbReq,
2329 CM_CALLBACK_MAINTAINCOUNT);
2330 cm_MergeStatus(dscp, scp, &bbp->stats[j], &volSync, userp, 0);
2332 lock_ReleaseWrite(&scp->rw);
2333 cm_ReleaseSCache(scp);
2334 } /* all files in the response */
2335 /* now tell it to drop the count,
2336 * after doing the vnode processing above */
2337 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2338 } /* while there are still more files to process */
2340 /* If we did the InlineBulk RPC pull out the return code and log it */
2342 if ((&bbp->stats[0])->errorCode) {
2343 osi_Log1(afsd_logp, "cm_TryBulkStat bulk stat error: %d",
2344 (&bbp->stats[0])->errorCode);
2345 code = (&bbp->stats[0])->errorCode;
2352 /* called with a write locked scp and a pointer to a buffer. Make bulk stat
2353 * calls on all undeleted files in the page of the directory specified.
2356 cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
2362 osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%p", dscp);
2364 /* should be on a buffer boundary */
2365 osi_assertx((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0, "invalid offset");
2367 bbp = malloc(sizeof(cm_bulkStat_t));
2368 memset(bbp, 0, sizeof(cm_bulkStat_t));
2369 bbp->bufOffset = *offsetp;
2371 lock_ReleaseWrite(&dscp->rw);
2372 /* first, assemble the file IDs we need to stat */
2373 code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) bbp, offsetp, userp, reqp, NULL);
2375 /* if we failed, bail out early */
2376 if (code && code != CM_ERROR_STOPNOW) {
2378 lock_ObtainWrite(&dscp->rw);
2382 code = cm_TryBulkStatRPC(dscp, bbp, userp, reqp);
2383 osi_Log1(afsd_logp, "END cm_TryBulkStat code 0x%x", code);
2385 lock_ObtainWrite(&dscp->rw);
2390 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
2394 /* initialize store back mask as inexpensive local variable */
2396 memset(statusp, 0, sizeof(AFSStoreStatus));
2398 /* copy out queued info from scache first, if scp passed in */
2400 if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
2401 statusp->ClientModTime = scp->clientModTime;
2402 mask |= AFS_SETMODTIME;
2403 scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
2408 /* now add in our locally generated request */
2409 if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
2410 statusp->ClientModTime = attrp->clientModTime;
2411 mask |= AFS_SETMODTIME;
2413 if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
2414 statusp->UnixModeBits = attrp->unixModeBits;
2415 mask |= AFS_SETMODE;
2417 if (attrp->mask & CM_ATTRMASK_OWNER) {
2418 statusp->Owner = attrp->owner;
2419 mask |= AFS_SETOWNER;
2421 if (attrp->mask & CM_ATTRMASK_GROUP) {
2422 statusp->Group = attrp->group;
2423 mask |= AFS_SETGROUP;
2426 statusp->Mask = mask;
2429 /* set the file size, and make sure that all relevant buffers have been
2430 * truncated. Ensure that any partially truncated buffers have been zeroed
2431 * to the end of the buffer.
2433 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
2439 /* start by locking out buffer creation */
2440 lock_ObtainWrite(&scp->bufCreateLock);
2442 /* verify that this is a file, not a dir or a symlink */
2443 lock_ObtainWrite(&scp->rw);
2444 code = cm_SyncOp(scp, NULL, userp, reqp, 0,
2445 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2448 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2450 if (scp->fileType != CM_SCACHETYPE_FILE) {
2451 code = CM_ERROR_ISDIR;
2456 if (LargeIntegerLessThan(*sizep, scp->length))
2461 lock_ReleaseWrite(&scp->rw);
2463 /* can't hold scp->rw lock here, since we may wait for a storeback to
2464 * finish if the buffer package is cleaning a buffer by storing it to
2468 buf_Truncate(scp, userp, reqp, sizep);
2470 /* now ensure that file length is short enough, and update truncPos */
2471 lock_ObtainWrite(&scp->rw);
2473 /* make sure we have a callback (so we have the right value for the
2474 * length), and wait for it to be safe to do a truncate.
2476 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
2477 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2478 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2480 /* If we only have 'i' bits, then we should still be able to set
2481 the size of a file we created. */
2482 if (code == CM_ERROR_NOACCESS && scp->creator == userp) {
2483 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
2484 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2485 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2491 if (LargeIntegerLessThan(*sizep, scp->length)) {
2492 /* a real truncation. If truncPos is not set yet, or is bigger
2493 * than where we're truncating the file, set truncPos to this
2498 if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
2499 || LargeIntegerLessThan(*sizep, scp->length)) {
2501 scp->truncPos = *sizep;
2502 scp->mask |= CM_SCACHEMASK_TRUNCPOS;
2504 /* in either case, the new file size has been changed */
2505 scp->length = *sizep;
2506 scp->mask |= CM_SCACHEMASK_LENGTH;
2508 else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
2509 /* really extending the file */
2510 scp->length = *sizep;
2511 scp->mask |= CM_SCACHEMASK_LENGTH;
2514 /* done successfully */
2517 cm_SyncOpDone(scp, NULL,
2518 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2519 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2522 lock_ReleaseWrite(&scp->rw);
2523 lock_ReleaseWrite(&scp->bufCreateLock);
2528 /* set the file size or other attributes (but not both at once) */
2529 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
2533 AFSFetchStatus afsOutStatus;
2537 AFSStoreStatus afsInStatus;
2538 struct rx_connection * rxconnp;
2540 /* handle file length setting */
2541 if (attrp->mask & CM_ATTRMASK_LENGTH)
2542 return cm_SetLength(scp, &attrp->length, userp, reqp);
2544 lock_ObtainWrite(&scp->rw);
2545 /* otherwise, we have to make an RPC to get the status */
2546 code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
2548 lock_ReleaseWrite(&scp->rw);
2551 lock_ConvertWToR(&scp->rw);
2553 /* make the attr structure */
2554 cm_StatusFromAttr(&afsInStatus, scp, attrp);
2556 tfid.Volume = scp->fid.volume;
2557 tfid.Vnode = scp->fid.vnode;
2558 tfid.Unique = scp->fid.unique;
2559 lock_ReleaseRead(&scp->rw);
2561 /* now make the RPC */
2562 osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%p", scp);
2564 code = cm_ConnFromFID(&scp->fid, userp, reqp, &connp);
2568 rxconnp = cm_GetRxConn(connp);
2569 code = RXAFS_StoreStatus(rxconnp, &tfid,
2570 &afsInStatus, &afsOutStatus, &volSync);
2571 rx_PutConnection(rxconnp);
2573 } while (cm_Analyze(connp, userp, reqp,
2574 &scp->fid, &volSync, NULL, NULL, code));
2575 code = cm_MapRPCError(code, reqp);
2578 osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
2580 osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
2582 lock_ObtainWrite(&scp->rw);
2583 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
2585 cm_MergeStatus(NULL, scp, &afsOutStatus, &volSync, userp,
2586 CM_MERGEFLAG_FORCE|CM_MERGEFLAG_STOREDATA);
2588 /* if we're changing the mode bits, discard the ACL cache,
2589 * since we changed the mode bits.
2591 if (afsInStatus.Mask & AFS_SETMODE)
2592 cm_FreeAllACLEnts(scp);
2593 lock_ReleaseWrite(&scp->rw);
2597 long cm_Create(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
2598 cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
2603 cm_callbackRequest_t cbReq;
2606 cm_scache_t *scp = NULL;
2608 AFSStoreStatus inStatus;
2609 AFSFetchStatus updatedDirStatus;
2610 AFSFetchStatus newFileStatus;
2611 AFSCallBack newFileCallback;
2613 struct rx_connection * rxconnp;
2615 fschar_t * fnamep = NULL;
2617 /* can't create names with @sys in them; must expand it manually first.
2618 * return "invalid request" if they try.
2620 if (cm_ExpandSysName(cnamep, NULL, 0, 0)) {
2621 return CM_ERROR_ATSYS;
2624 #ifdef AFS_FREELANCE_CLIENT
2625 /* Freelance root volume does not hold files */
2626 if (cm_freelanceEnabled &&
2627 dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
2628 dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
2630 return CM_ERROR_NOACCESS;
2632 #endif /* AFS_FREELANCE_CLIENT */
2634 /* before starting the RPC, mark that we're changing the file data, so
2635 * that someone who does a chmod will know to wait until our call
2638 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2639 lock_ObtainWrite(&dscp->rw);
2640 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2641 lock_ReleaseWrite(&dscp->rw);
2643 cm_StartCallbackGrantingCall(NULL, &cbReq);
2645 cm_EndDirOp(&dirop);
2652 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
2654 cm_StatusFromAttr(&inStatus, NULL, attrp);
2656 /* try the RPC now */
2657 osi_Log1(afsd_logp, "CALL CreateFile scp 0x%p", dscp);
2659 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2663 dirAFSFid.Volume = dscp->fid.volume;
2664 dirAFSFid.Vnode = dscp->fid.vnode;
2665 dirAFSFid.Unique = dscp->fid.unique;
2667 rxconnp = cm_GetRxConn(connp);
2668 code = RXAFS_CreateFile(connp->rxconnp, &dirAFSFid, fnamep,
2669 &inStatus, &newAFSFid, &newFileStatus,
2670 &updatedDirStatus, &newFileCallback,
2672 rx_PutConnection(rxconnp);
2674 } while (cm_Analyze(connp, userp, reqp,
2675 &dscp->fid, &volSync, NULL, &cbReq, code));
2676 code = cm_MapRPCError(code, reqp);
2679 osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
2681 osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
2684 lock_ObtainWrite(&dirop.scp->dirlock);
2685 dirop.lockType = CM_DIRLOCK_WRITE;
2687 lock_ObtainWrite(&dscp->rw);
2688 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2690 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
2692 lock_ReleaseWrite(&dscp->rw);
2694 /* now try to create the file's entry, too, but be careful to
2695 * make sure that we don't merge in old info. Since we weren't locking
2696 * out any requests during the file's creation, we may have pretty old
2700 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
2701 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2703 lock_ObtainWrite(&scp->rw);
2704 scp->creator = userp; /* remember who created it */
2705 if (!cm_HaveCallback(scp)) {
2706 cm_MergeStatus(dscp, scp, &newFileStatus, &volSync,
2708 cm_EndCallbackGrantingCall(scp, &cbReq,
2709 &newFileCallback, 0);
2712 lock_ReleaseWrite(&scp->rw);
2716 /* make sure we end things properly */
2718 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2720 if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
2721 cm_DirCreateEntry(&dirop, fnamep, &newFid);
2723 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
2726 cm_EndDirOp(&dirop);
2735 cm_ReleaseSCache(scp);
2740 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
2744 code = buf_CleanVnode(scp, userp, reqp);
2746 lock_ObtainWrite(&scp->rw);
2748 if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
2749 | CM_SCACHEMASK_CLIENTMODTIME
2750 | CM_SCACHEMASK_LENGTH))
2751 code = cm_StoreMini(scp, userp, reqp);
2753 if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) {
2754 code = (scp->flags & CM_SCACHEFLAG_OVERQUOTA) ? CM_ERROR_QUOTA : CM_ERROR_SPACE;
2755 scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE);
2758 lock_ReleaseWrite(&scp->rw);
2763 long cm_MakeDir(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
2764 cm_user_t *userp, cm_req_t *reqp, cm_scache_t **scpp)
2769 cm_callbackRequest_t cbReq;
2772 cm_scache_t *scp = NULL;
2774 AFSStoreStatus inStatus;
2775 AFSFetchStatus updatedDirStatus;
2776 AFSFetchStatus newDirStatus;
2777 AFSCallBack newDirCallback;
2779 struct rx_connection * rxconnp;
2781 fschar_t * fnamep = NULL;
2783 /* can't create names with @sys in them; must expand it manually first.
2784 * return "invalid request" if they try.
2786 if (cm_ExpandSysName(cnamep, NULL, 0, 0)) {
2787 return CM_ERROR_ATSYS;
2790 #ifdef AFS_FREELANCE_CLIENT
2791 /* Freelance root volume does not hold subdirectories */
2792 if (cm_freelanceEnabled &&
2793 dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
2794 dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
2796 return CM_ERROR_NOACCESS;
2798 #endif /* AFS_FREELANCE_CLIENT */
2800 /* before starting the RPC, mark that we're changing the directory
2801 * data, so that someone who does a chmod on the dir will wait until
2802 * our call completes.
2804 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2805 lock_ObtainWrite(&dscp->rw);
2806 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2807 lock_ReleaseWrite(&dscp->rw);
2809 cm_StartCallbackGrantingCall(NULL, &cbReq);
2811 cm_EndDirOp(&dirop);
2818 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
2819 cm_StatusFromAttr(&inStatus, NULL, attrp);
2821 /* try the RPC now */
2822 osi_Log1(afsd_logp, "CALL MakeDir scp 0x%p", dscp);
2824 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2828 dirAFSFid.Volume = dscp->fid.volume;
2829 dirAFSFid.Vnode = dscp->fid.vnode;
2830 dirAFSFid.Unique = dscp->fid.unique;
2832 rxconnp = cm_GetRxConn(connp);
2833 code = RXAFS_MakeDir(connp->rxconnp, &dirAFSFid, fnamep,
2834 &inStatus, &newAFSFid, &newDirStatus,
2835 &updatedDirStatus, &newDirCallback,
2837 rx_PutConnection(rxconnp);
2839 } while (cm_Analyze(connp, userp, reqp,
2840 &dscp->fid, &volSync, NULL, &cbReq, code));
2841 code = cm_MapRPCError(code, reqp);
2844 osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
2846 osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
2849 lock_ObtainWrite(&dirop.scp->dirlock);
2850 dirop.lockType = CM_DIRLOCK_WRITE;
2852 lock_ObtainWrite(&dscp->rw);
2853 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2855 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
2857 lock_ReleaseWrite(&dscp->rw);
2859 /* now try to create the new dir's entry, too, but be careful to
2860 * make sure that we don't merge in old info. Since we weren't locking
2861 * out any requests during the file's creation, we may have pretty old
2865 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
2866 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2868 lock_ObtainWrite(&scp->rw);
2869 if (!cm_HaveCallback(scp)) {
2870 cm_MergeStatus(dscp, scp, &newDirStatus, &volSync,
2872 cm_EndCallbackGrantingCall(scp, &cbReq,
2873 &newDirCallback, 0);
2876 lock_ReleaseWrite(&scp->rw);
2880 /* make sure we end things properly */
2882 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2884 if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
2885 cm_DirCreateEntry(&dirop, fnamep, &newFid);
2887 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
2890 cm_EndDirOp(&dirop);
2898 cm_ReleaseSCache(scp);
2901 /* and return error code */
2905 long cm_Link(cm_scache_t *dscp, clientchar_t *cnamep, cm_scache_t *sscp, long flags,
2906 cm_user_t *userp, cm_req_t *reqp)
2911 AFSFid existingAFSFid;
2912 AFSFetchStatus updatedDirStatus;
2913 AFSFetchStatus newLinkStatus;
2915 struct rx_connection * rxconnp;
2917 fschar_t * fnamep = NULL;
2919 if (dscp->fid.cell != sscp->fid.cell ||
2920 dscp->fid.volume != sscp->fid.volume) {
2921 return CM_ERROR_CROSSDEVLINK;
2924 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2925 lock_ObtainWrite(&dscp->rw);
2926 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2927 lock_ReleaseWrite(&dscp->rw);
2929 cm_EndDirOp(&dirop);
2934 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
2936 /* try the RPC now */
2937 osi_Log1(afsd_logp, "CALL Link scp 0x%p", dscp);
2939 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2942 dirAFSFid.Volume = dscp->fid.volume;
2943 dirAFSFid.Vnode = dscp->fid.vnode;
2944 dirAFSFid.Unique = dscp->fid.unique;
2946 existingAFSFid.Volume = sscp->fid.volume;
2947 existingAFSFid.Vnode = sscp->fid.vnode;
2948 existingAFSFid.Unique = sscp->fid.unique;
2950 rxconnp = cm_GetRxConn(connp);
2951 code = RXAFS_Link(rxconnp, &dirAFSFid, fnamep, &existingAFSFid,
2952 &newLinkStatus, &updatedDirStatus, &volSync);
2953 rx_PutConnection(rxconnp);
2954 osi_Log1(afsd_logp," RXAFS_Link returns 0x%x", code);
2956 } while (cm_Analyze(connp, userp, reqp,
2957 &dscp->fid, &volSync, NULL, NULL, code));
2959 code = cm_MapRPCError(code, reqp);
2962 osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
2964 osi_Log0(afsd_logp, "CALL Link SUCCESS");
2967 lock_ObtainWrite(&dirop.scp->dirlock);
2968 dirop.lockType = CM_DIRLOCK_WRITE;
2970 lock_ObtainWrite(&dscp->rw);
2971 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2973 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
2975 lock_ReleaseWrite(&dscp->rw);
2978 if (cm_CheckDirOpForSingleChange(&dirop)) {
2979 cm_DirCreateEntry(&dirop, fnamep, &sscp->fid);
2981 cm_BPlusDirCreateEntry(&dirop, cnamep, &sscp->fid);
2985 cm_EndDirOp(&dirop);
2992 long cm_SymLink(cm_scache_t *dscp, clientchar_t *cnamep, fschar_t *contentsp, long flags,
2993 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
3001 AFSStoreStatus inStatus;
3002 AFSFetchStatus updatedDirStatus;
3003 AFSFetchStatus newLinkStatus;
3005 struct rx_connection * rxconnp;
3007 fschar_t *fnamep = NULL;
3009 /* before starting the RPC, mark that we're changing the directory data,
3010 * so that someone who does a chmod on the dir will wait until our
3013 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
3014 lock_ObtainWrite(&dscp->rw);
3015 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3016 lock_ReleaseWrite(&dscp->rw);
3018 cm_EndDirOp(&dirop);
3023 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3025 cm_StatusFromAttr(&inStatus, NULL, attrp);
3027 /* try the RPC now */
3028 osi_Log1(afsd_logp, "CALL Symlink scp 0x%p", dscp);
3030 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3034 dirAFSFid.Volume = dscp->fid.volume;
3035 dirAFSFid.Vnode = dscp->fid.vnode;
3036 dirAFSFid.Unique = dscp->fid.unique;
3038 rxconnp = cm_GetRxConn(connp);
3039 code = RXAFS_Symlink(rxconnp, &dirAFSFid, fnamep, contentsp,
3040 &inStatus, &newAFSFid, &newLinkStatus,
3041 &updatedDirStatus, &volSync);
3042 rx_PutConnection(rxconnp);
3044 } while (cm_Analyze(connp, userp, reqp,
3045 &dscp->fid, &volSync, NULL, NULL, code));
3046 code = cm_MapRPCError(code, reqp);
3049 osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
3051 osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
3054 lock_ObtainWrite(&dirop.scp->dirlock);
3055 dirop.lockType = CM_DIRLOCK_WRITE;
3057 lock_ObtainWrite(&dscp->rw);
3058 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3060 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
3062 lock_ReleaseWrite(&dscp->rw);
3065 if (cm_CheckDirOpForSingleChange(&dirop)) {
3066 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3068 cm_DirCreateEntry(&dirop, fnamep, &newFid);
3070 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
3074 cm_EndDirOp(&dirop);
3076 /* now try to create the new dir's entry, too, but be careful to
3077 * make sure that we don't merge in old info. Since we weren't locking
3078 * out any requests during the file's creation, we may have pretty old
3082 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3083 code = cm_GetSCache(&newFid, &scp, userp, reqp);
3085 lock_ObtainWrite(&scp->rw);
3086 if (!cm_HaveCallback(scp)) {
3087 cm_MergeStatus(dscp, scp, &newLinkStatus, &volSync,
3090 lock_ReleaseWrite(&scp->rw);
3091 cm_ReleaseSCache(scp);
3097 /* and return error code */
3101 /*! \brief Remove a directory
3103 Encapsulates a call to RXAFS_RemoveDir().
3105 \param[in] dscp cm_scache_t for the directory containing the
3106 directory to be removed.
3108 \param[in] fnamep This will be the original name of the directory
3109 as known to the file server. It will be passed in to RXAFS_RemoveDir().
3110 This parameter is optional. If it is not provided the value
3113 \param[in] cnamep Normalized name used to update the local
3116 \param[in] userp cm_user_t for the request.
3118 \param[in] reqp Request tracker.
3120 long cm_RemoveDir(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t *cnamep, cm_user_t *userp, cm_req_t *reqp)
3126 AFSFetchStatus updatedDirStatus;
3128 struct rx_connection * rxconnp;
3130 cm_scache_t *scp = NULL;
3131 int free_fnamep = FALSE;
3133 if (fnamep == NULL) {
3136 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ, &dirop);
3138 code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
3141 cm_EndDirOp(&dirop);
3148 code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
3152 /* before starting the RPC, mark that we're changing the directory data,
3153 * so that someone who does a chmod on the dir will wait until our
3156 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
3157 lock_ObtainWrite(&dscp->rw);
3158 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3159 lock_ReleaseWrite(&dscp->rw);
3161 cm_EndDirOp(&dirop);
3166 /* try the RPC now */
3167 osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%p", dscp);
3169 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3173 dirAFSFid.Volume = dscp->fid.volume;
3174 dirAFSFid.Vnode = dscp->fid.vnode;
3175 dirAFSFid.Unique = dscp->fid.unique;
3177 rxconnp = cm_GetRxConn(connp);
3178 code = RXAFS_RemoveDir(rxconnp, &dirAFSFid, fnamep,
3179 &updatedDirStatus, &volSync);
3180 rx_PutConnection(rxconnp);
3182 } while (cm_Analyze(connp, userp, reqp,
3183 &dscp->fid, &volSync, NULL, NULL, code));
3184 code = cm_MapRPCErrorRmdir(code, reqp);
3187 osi_Log1(afsd_logp, "CALL RemoveDir FAILURE, code 0x%x", code);
3189 osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
3192 lock_ObtainWrite(&dirop.scp->dirlock);
3193 dirop.lockType = CM_DIRLOCK_WRITE;
3195 lock_ObtainWrite(&dscp->rw);
3196 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3198 cm_dnlcRemove(dscp, cnamep);
3199 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
3201 lock_ReleaseWrite(&dscp->rw);
3204 if (cm_CheckDirOpForSingleChange(&dirop) && cnamep != NULL) {
3205 cm_DirDeleteEntry(&dirop, fnamep);
3207 cm_BPlusDirDeleteEntry(&dirop, cnamep);
3211 cm_EndDirOp(&dirop);
3214 cm_ReleaseSCache(scp);
3216 lock_ObtainWrite(&scp->rw);
3217 scp->flags |= CM_SCACHEFLAG_DELETED;
3218 lock_ReleaseWrite(&scp->rw);
3226 /* and return error code */
3230 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
3232 /* grab mutex on contents */
3233 lock_ObtainWrite(&scp->rw);
3235 /* reset the prefetch info */
3236 scp->prefetch.base.LowPart = 0; /* base */
3237 scp->prefetch.base.HighPart = 0;
3238 scp->prefetch.end.LowPart = 0; /* and end */
3239 scp->prefetch.end.HighPart = 0;
3241 /* release mutex on contents */
3242 lock_ReleaseWrite(&scp->rw);
3248 /*! \brief Rename a file or directory
3250 Encapsulates a RXAFS_Rename() call.
3252 \param[in] oldDscp cm_scache_t for the directory containing the old
3255 \param[in] oldNamep The original old name known to the file server.
3256 This is the name that will be passed into the RXAFS_Rename().
3257 If it is not provided, it will be looked up.
3259 \param[in] normalizedOldNamep Normalized old name. This is used for
3260 updating local directory caches.
3262 \param[in] newDscp cm_scache_t for the directory containing the new
3265 \param[in] newNamep New name. Normalized.
3267 \param[in] userp cm_user_t for the request.
3269 \param[in,out] reqp Request tracker.
3272 long cm_Rename(cm_scache_t *oldDscp, fschar_t *oldNamep, clientchar_t *cOldNamep,
3273 cm_scache_t *newDscp, clientchar_t *cNewNamep, cm_user_t *userp,
3278 AFSFid oldDirAFSFid;
3279 AFSFid newDirAFSFid;
3281 AFSFetchStatus updatedOldDirStatus;
3282 AFSFetchStatus updatedNewDirStatus;
3285 struct rx_connection * rxconnp;
3286 cm_dirOp_t oldDirOp;
3289 cm_dirOp_t newDirOp;
3290 fschar_t * newNamep = NULL;
3291 int free_oldNamep = FALSE;
3293 if (cOldNamep == NULL || cNewNamep == NULL ||
3294 cm_ClientStrLen(cOldNamep) == 0 ||
3295 cm_ClientStrLen(cNewNamep) == 0)
3296 return CM_ERROR_INVAL;
3298 if (oldNamep == NULL) {
3301 code = cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_READ, &oldDirOp);
3303 code = cm_BPlusDirLookupOriginalName(&oldDirOp, cOldNamep, &oldNamep);
3305 free_oldNamep = TRUE;
3306 cm_EndDirOp(&oldDirOp);
3310 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S Original Name lookup failed",
3311 oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3317 /* before starting the RPC, mark that we're changing the directory data,
3318 * so that someone who does a chmod on the dir will wait until our call
3319 * completes. We do this in vnode order so that we don't deadlock,
3320 * which makes the code a little verbose.
3322 if (oldDscp == newDscp) {
3323 /* check for identical names */
3324 if (cm_ClientStrCmp(cOldNamep, cNewNamep) == 0) {
3325 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_RENAME_IDENTICAL",
3327 code = CM_ERROR_RENAME_IDENTICAL;
3332 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3333 lock_ObtainWrite(&oldDscp->rw);
3334 cm_dnlcRemove(oldDscp, cOldNamep);
3335 cm_dnlcRemove(oldDscp, cNewNamep);
3336 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3337 CM_SCACHESYNC_STOREDATA);
3338 lock_ReleaseWrite(&oldDscp->rw);
3340 cm_EndDirOp(&oldDirOp);
3344 /* two distinct dir vnodes */
3346 if (oldDscp->fid.cell != newDscp->fid.cell ||
3347 oldDscp->fid.volume != newDscp->fid.volume) {
3348 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_CROSSDEVLINK",
3350 code = CM_ERROR_CROSSDEVLINK;
3354 /* shouldn't happen that we have distinct vnodes for two
3355 * different files, but could due to deliberate attack, or
3356 * stale info. Avoid deadlocks and quit now.
3358 if (oldDscp->fid.vnode == newDscp->fid.vnode) {
3359 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p vnode collision",
3361 code = CM_ERROR_CROSSDEVLINK;
3365 if (oldDscp->fid.vnode < newDscp->fid.vnode) {
3366 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3367 lock_ObtainWrite(&oldDscp->rw);
3368 cm_dnlcRemove(oldDscp, cOldNamep);
3369 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3370 CM_SCACHESYNC_STOREDATA);
3371 lock_ReleaseWrite(&oldDscp->rw);
3373 cm_EndDirOp(&oldDirOp);
3375 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE, &newDirOp);
3376 lock_ObtainWrite(&newDscp->rw);
3377 cm_dnlcRemove(newDscp, cNewNamep);
3378 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3379 CM_SCACHESYNC_STOREDATA);
3380 lock_ReleaseWrite(&newDscp->rw);
3382 cm_EndDirOp(&newDirOp);
3384 /* cleanup first one */
3385 lock_ObtainWrite(&oldDscp->rw);
3386 cm_SyncOpDone(oldDscp, NULL,
3387 CM_SCACHESYNC_STOREDATA);
3388 lock_ReleaseWrite(&oldDscp->rw);
3389 cm_EndDirOp(&oldDirOp);
3394 /* lock the new vnode entry first */
3395 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE, &newDirOp);
3396 lock_ObtainWrite(&newDscp->rw);
3397 cm_dnlcRemove(newDscp, cNewNamep);
3398 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3399 CM_SCACHESYNC_STOREDATA);
3400 lock_ReleaseWrite(&newDscp->rw);
3402 cm_EndDirOp(&newDirOp);
3404 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3405 lock_ObtainWrite(&oldDscp->rw);
3406 cm_dnlcRemove(oldDscp, cOldNamep);
3407 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3408 CM_SCACHESYNC_STOREDATA);
3409 lock_ReleaseWrite(&oldDscp->rw);
3411 cm_EndDirOp(&oldDirOp);
3413 /* cleanup first one */
3414 lock_ObtainWrite(&newDscp->rw);
3415 cm_SyncOpDone(newDscp, NULL,
3416 CM_SCACHESYNC_STOREDATA);
3417 lock_ReleaseWrite(&newDscp->rw);
3418 cm_EndDirOp(&newDirOp);
3422 } /* two distinct vnodes */
3429 newNamep = cm_ClientStringToFsStringAlloc(cNewNamep, -1, NULL);
3431 /* try the RPC now */
3432 osi_Log2(afsd_logp, "CALL Rename old scp 0x%p new scp 0x%p",
3435 code = cm_ConnFromFID(&oldDscp->fid, userp, reqp, &connp);
3439 oldDirAFSFid.Volume = oldDscp->fid.volume;
3440 oldDirAFSFid.Vnode = oldDscp->fid.vnode;
3441 oldDirAFSFid.Unique = oldDscp->fid.unique;
3442 newDirAFSFid.Volume = newDscp->fid.volume;
3443 newDirAFSFid.Vnode = newDscp->fid.vnode;
3444 newDirAFSFid.Unique = newDscp->fid.unique;
3446 rxconnp = cm_GetRxConn(connp);
3447 code = RXAFS_Rename(rxconnp, &oldDirAFSFid, oldNamep,
3448 &newDirAFSFid, newNamep,
3449 &updatedOldDirStatus, &updatedNewDirStatus,
3451 rx_PutConnection(rxconnp);
3453 } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
3454 &volSync, NULL, NULL, code));
3455 code = cm_MapRPCError(code, reqp);
3458 osi_Log1(afsd_logp, "CALL Rename FAILURE, code 0x%x", code);
3460 osi_Log0(afsd_logp, "CALL Rename SUCCESS");
3462 /* update the individual stat cache entries for the directories */
3464 lock_ObtainWrite(&oldDirOp.scp->dirlock);
3465 oldDirOp.lockType = CM_DIRLOCK_WRITE;
3467 lock_ObtainWrite(&oldDscp->rw);
3468 cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
3471 cm_MergeStatus(NULL, oldDscp, &updatedOldDirStatus, &volSync,
3472 userp, CM_MERGEFLAG_DIROP);
3473 lock_ReleaseWrite(&oldDscp->rw);
3476 if (cm_CheckDirOpForSingleChange(&oldDirOp)) {
3479 diropCode = cm_BPlusDirLookup(&oldDirOp, cOldNamep, &fileFid);
3480 if (diropCode == CM_ERROR_INEXACT_MATCH)
3482 else if (diropCode == EINVAL)
3484 diropCode = cm_DirLookup(&oldDirOp, oldNamep, &fileFid);
3486 if (diropCode == 0) {
3488 diropCode = cm_DirCreateEntry(&oldDirOp, newNamep, &fileFid);
3490 cm_BPlusDirCreateEntry(&oldDirOp, cNewNamep, &fileFid);
3494 if (diropCode == 0) {
3495 diropCode = cm_DirDeleteEntry(&oldDirOp, oldNamep);
3497 cm_BPlusDirDeleteEntry(&oldDirOp, cOldNamep);
3503 cm_EndDirOp(&oldDirOp);
3505 /* and update it for the new one, too, if necessary */
3508 lock_ObtainWrite(&newDirOp.scp->dirlock);
3509 newDirOp.lockType = CM_DIRLOCK_WRITE;
3511 lock_ObtainWrite(&newDscp->rw);
3512 cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
3514 cm_MergeStatus(NULL, newDscp, &updatedNewDirStatus, &volSync,
3515 userp, CM_MERGEFLAG_DIROP);
3516 lock_ReleaseWrite(&newDscp->rw);
3519 /* we only make the local change if we successfully made
3520 the change in the old directory AND there was only one
3521 change in the new directory */
3522 if (diropCode == 0 && cm_CheckDirOpForSingleChange(&newDirOp)) {
3523 cm_DirCreateEntry(&newDirOp, newNamep, &fileFid);
3525 cm_BPlusDirCreateEntry(&newDirOp, cNewNamep, &fileFid);
3529 cm_EndDirOp(&newDirOp);
3538 /* and return error code */
3542 /* Byte range locks:
3544 The OpenAFS Windows client has to fake byte range locks given no
3545 server side support for such locks. This is implemented as keyed
3546 byte range locks on the cache manager.
3548 Keyed byte range locks:
3550 Each cm_scache_t structure keeps track of a list of keyed locks.
3551 The key for a lock identifies an owner of a set of locks (referred
3552 to as a client). Each key is represented by a value. The set of
3553 key values used within a specific cm_scache_t structure form a
3554 namespace that has a scope of just that cm_scache_t structure. The
3555 same key value can be used with another cm_scache_t structure and
3556 correspond to a completely different client. However it is
3557 advantageous for the SMB or IFS layer to make sure that there is a
3558 1-1 mapping between client and keys over all cm_scache_t objects.
3560 Assume a client C has key Key(C) (although, since the scope of the
3561 key is a cm_scache_t, the key can be Key(C,S), where S is the
3562 cm_scache_t. But assume a 1-1 relation between keys and clients).
3563 A byte range (O,+L) denotes byte addresses (O) through (O+L-1)
3564 inclusive (a.k.a. [O,O+L-1]). The function Key(x) is implemented
3565 through cm_generateKey() function for both SMB and IFS.
3567 The list of locks for a cm_scache_t object S is maintained in
3568 S->fileLocks. The cache manager will set a lock on the AFS file
3569 server in order to assert the locks in S->fileLocks. If only
3570 shared locks are in place for S, then the cache manager will obtain
3571 a LockRead lock, while if there are any exclusive locks, it will
3572 obtain a LockWrite lock. If the exclusive locks are all released
3573 while the shared locks remain, then the cache manager will
3574 downgrade the lock from LockWrite to LockRead. Similarly, if an
3575 exclusive lock is obtained when only shared locks exist, then the
3576 cache manager will try to upgrade the lock from LockRead to
3579 Each lock L owned by client C maintains a key L->key such that
3580 L->key == Key(C), the effective range defined by L->LOffset and
3581 L->LLength such that the range of bytes affected by the lock is
3582 (L->LOffset, +L->LLength), a type maintained in L->LockType which
3583 is either exclusive or shared.
3587 A lock exists iff it is in S->fileLocks for some cm_scache_t
3588 S. Existing locks are in one of the following states: ACTIVE,
3589 WAITLOCK, WAITUNLOCK, LOST, DELETED.
3591 The following sections describe each lock and the associated
3594 1. ACTIVE: A lock L is ACTIVE iff the cache manager has asserted
3595 the lock with the AFS file server. This type of lock can be
3596 exercised by a client to read or write to the locked region (as
3599 1.1 ACTIVE->LOST: When the AFS file server fails to extend a
3600 server lock that was required to assert the lock. Before
3601 marking the lock as lost, the cache manager checks if the file
3602 has changed on the server. If the file has not changed, then
3603 the cache manager will attempt to obtain a new server lock
3604 that is sufficient to assert the client side locks for the
3605 file. If any of these fail, the lock is marked as LOST.
3606 Otherwise, it is left as ACTIVE.
3608 1.2 ACTIVE->DELETED: Lock is released.
3610 2. WAITLOCK: A lock is in a WAITLOCK state if the cache manager
3611 grants the lock but the lock is yet to be asserted with the AFS
3612 file server. Once the file server grants the lock, the state
3613 will transition to an ACTIVE lock.
3615 2.1 WAITLOCK->ACTIVE: The server granted the lock.
3617 2.2 WAITLOCK->DELETED: Lock is abandoned, or timed out during
3620 2.3 WAITLOCK->LOST: One or more locks from this client were
3621 marked as LOST. No further locks will be granted to this
3622 client until all lost locks are removed.
3624 3. WAITUNLOCK: A lock is in a WAITUNLOCK state if the cache manager
3625 receives a request for a lock that conflicts with an existing
3626 ACTIVE or WAITLOCK lock. The lock will be placed in the queue
3627 and will be granted at such time the conflicting locks are
3628 removed, at which point the state will transition to either
3631 3.1 WAITUNLOCK->ACTIVE: The conflicting lock was removed. The
3632 current serverLock is sufficient to assert this lock, or a
3633 sufficient serverLock is obtained.
3635 3.2 WAITUNLOCK->WAITLOCK: The conflicting lock was removed,
3636 however the required serverLock is yet to be asserted with the
3639 3.3 WAITUNLOCK->DELETED: The lock is abandoned, timed out or
3642 3.5 WAITUNLOCK->LOST: One or more locks from this client were
3643 marked as LOST. No further locks will be granted to this
3644 client until all lost locks are removed.
3646 4. LOST: A lock L is LOST if the server lock that was required to
3647 assert the lock could not be obtained or if it could not be
3648 extended, or if other locks by the same client were LOST.
3649 Essentially, once a lock is LOST, the contract between the cache
3650 manager and that specific client is no longer valid.
3652 The cache manager rechecks the server lock once every minute and
3653 extends it as appropriate. If this is not done for 5 minutes,
3654 the AFS file server will release the lock (the 5 minute timeout
3655 is based on current file server code and is fairly arbitrary).
3656 Once released, the lock cannot be re-obtained without verifying
3657 that the contents of the file hasn't been modified since the
3658 time the lock was released. Re-obtaining the lock without
3659 verifying this may lead to data corruption. If the lock can not
3660 be obtained safely, then all active locks for the cm_scache_t
3663 4.1 LOST->DELETED: The lock is released.
3665 5. DELETED: The lock is no longer relevant. Eventually, it will
3666 get removed from the cm_scache_t. In the meantime, it will be
3667 treated as if it does not exist.
3669 5.1 DELETED->not exist: The lock is removed from the
3672 The following are classifications of locks based on their state.
3674 6* A lock L is ACCEPTED if it is ACTIVE or WAITLOCK. These locks
3675 have been accepted by the cache manager, but may or may not have
3676 been granted back to the client.
3678 7* A lock L is QUEUED if it is ACTIVE, WAITLOCK or WAITUNLOCK.
3680 8* A lock L is WAITING if it is WAITLOCK or WAITUNLOCK.
3684 A client C can READ range (Offset,+Length) of a file represented by
3685 cm_scache_t S iff (1):
3687 1. for all _a_ in (Offset,+Length), all of the following is true:
3689 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3690 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3693 1.2 For each LOST lock L in S->fileLocks such that _a_ in
3694 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3697 (When locks are lost on an cm_scache_t, all locks are lost. By
3698 4.2 (below), if there is an exclusive LOST lock, then there
3699 can't be any overlapping ACTIVE locks.)
3701 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
3703 2. for all _a_ in (Offset,+Length), one of the following is true:
3705 2.1 Byte _a_ of S is unowned (as specified in 1.1) AND there
3706 does not exist a LOST lock L such that _a_ in
3707 (L->LOffset,+L->LLength).
3709 2.2 Byte _a_ of S is owned by C under lock L (as specified in
3710 1.2) AND L->LockType is exclusive.
3712 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
3714 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
3717 3.1 If L->LockType is exclusive then there does NOT exist a
3718 ACCEPTED lock M in S->fileLocks such that _a_ in
3719 (M->LOffset,+M->LLength).
3721 (If we count all QUEUED locks then we hit cases such as
3722 cascading waiting locks where the locks later on in the queue
3723 can be granted without compromising file integrity. On the
3724 other hand if only ACCEPTED locks are considered, then locks
3725 that were received earlier may end up waiting for locks that
3726 were received later to be unlocked. The choice of ACCEPTED
3727 locks was made to mimic the Windows byte range lock
3730 3.2 If L->LockType is shared then for each ACCEPTED lock M in
3731 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
3732 M->LockType is shared.
3734 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
3736 4.1 M->key != Key(C)
3738 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
3739 and (M->LOffset,+M->LLength) do not intersect.
3741 (Note: If a client loses a lock, it loses all locks.
3742 Subsequently, it will not be allowed to obtain any more locks
3743 until all existing LOST locks that belong to the client are
3744 released. Once all locks are released by a single client,
3745 there exists no further contract between the client and AFS
3746 about the contents of the file, hence the client can then
3747 proceed to obtain new locks and establish a new contract.
3749 This doesn't quite work as you think it should, because most
3750 applications aren't built to deal with losing locks they
3751 thought they once had. For now, we don't have a good
3752 solution to lost locks.
3754 Also, for consistency reasons, we have to hold off on
3755 granting locks that overlap exclusive LOST locks.)
3757 A client C can only unlock locks L in S->fileLocks which have
3760 The representation and invariants are as follows:
3762 - Each cm_scache_t structure keeps:
3764 - A queue of byte-range locks (cm_scache_t::fileLocks) which
3765 are of type cm_file_lock_t.
3767 - A record of the highest server-side lock that has been
3768 obtained for this object (cm_scache_t::serverLock), which is
3769 one of (-1), LockRead, LockWrite.
3771 - A count of ACCEPTED exclusive and shared locks that are in the
3772 queue (cm_scache_t::sharedLocks and
3773 cm_scache_t::exclusiveLocks)
3775 - Each cm_file_lock_t structure keeps:
3777 - The type of lock (cm_file_lock_t::LockType)
3779 - The key associated with the lock (cm_file_lock_t::key)
3781 - The offset and length of the lock (cm_file_lock_t::LOffset
3782 and cm_file_lock_t::LLength)
3784 - The state of the lock.
3786 - Time of issuance or last successful extension
3788 Semantic invariants:
3790 I1. The number of ACCEPTED locks in S->fileLocks are
3791 (S->sharedLocks + S->exclusiveLocks)
3793 External invariants:
3795 I3. S->serverLock is the lock that we have asserted with the
3796 AFS file server for this cm_scache_t.
3798 I4. S->serverLock == LockRead iff there is at least one ACTIVE
3799 shared lock, but no ACTIVE exclusive locks.
3801 I5. S->serverLock == LockWrite iff there is at least one ACTIVE
3804 I6. If L is a LOST lock, then for each lock M in S->fileLocks,
3805 M->key == L->key IMPLIES M is LOST or DELETED.
3810 #define IS_LOCK_ACTIVE(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == 0)
3812 #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)
3814 #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)
3816 #define IS_LOCK_LOST(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_LOST)
3818 #define IS_LOCK_DELETED(lockp) (((lockp)->flags & CM_FILELOCK_FLAG_DELETED) == CM_FILELOCK_FLAG_DELETED)
3821 #define IS_LOCK_ACCEPTED(lockp) (IS_LOCK_ACTIVE(lockp) || IS_LOCK_WAITLOCK(lockp))
3824 #define IS_LOCK_CLIENTONLY(lockp) ((((lockp)->scp->flags & CM_SCACHEFLAG_RO) == CM_SCACHEFLAG_RO) || (((lockp)->flags & CM_FILELOCK_FLAG_CLIENTONLY) == CM_FILELOCK_FLAG_CLIENTONLY))
3827 #define INTERSECT_RANGE(r1,r2) (((r2).offset+(r2).length) > (r1).offset && ((r1).offset +(r1).length) > (r2).offset)
3830 #define CONTAINS_RANGE(r1,r2) (((r2).offset+(r2).length) <= ((r1).offset+(r1).length) && (r1).offset <= (r2).offset)
3832 #if defined(VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS) && !defined(LOCK_TESTING)
3833 #define SCP_SUPPORTS_BRLOCKS(scp) ((scp)->cbServerp && ((scp)->cbServerp->capabilities & VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS))
3835 #define SCP_SUPPORTS_BRLOCKS(scp) (1)
3838 #define SERVERLOCKS_ENABLED(scp) (!((scp)->flags & CM_SCACHEFLAG_RO) && cm_enableServerLocks && SCP_SUPPORTS_BRLOCKS(scp))
3840 #if defined(VICED_CAPABILITY_WRITELOCKACL)
3841 #define SCP_SUPPORTS_WRITELOCKACL(scp) ((scp)->cbServerp && ((scp->cbServerp->capabilities & VICED_CAPABILITY_WRITELOCKACL)))
3843 #define SCP_SUPPORTS_WRITELOCKACL(scp) (0)
3845 /* This should really be defined in any build that this code is being
3847 #error VICED_CAPABILITY_WRITELOCKACL not defined.
3850 static void cm_LockRangeSubtract(cm_range_t * pos, const cm_range_t * neg)
3852 afs_int64 int_begin;
3855 int_begin = MAX(pos->offset, neg->offset);
3856 int_end = MIN(pos->offset+pos->length, neg->offset+neg->length);
3858 if (int_begin < int_end) {
3859 if (int_begin == pos->offset) {
3860 pos->length = pos->offset + pos->length - int_end;
3861 pos->offset = int_end;
3862 } else if (int_end == pos->offset + pos->length) {
3863 pos->length = int_begin - pos->offset;
3866 /* We only subtract ranges if the resulting range is
3867 contiguous. If we try to support non-contigous ranges, we
3868 aren't actually improving performance. */
3872 /* Called with scp->rw held. Returns 0 if all is clear to read the
3873 specified range by the client identified by key.
3875 long cm_LockCheckRead(cm_scache_t *scp,
3876 LARGE_INTEGER LOffset,
3877 LARGE_INTEGER LLength,
3880 #ifndef ADVISORY_LOCKS
3882 cm_file_lock_t *fileLock;
3886 int substract_ranges = FALSE;
3888 range.offset = LOffset.QuadPart;
3889 range.length = LLength.QuadPart;
3893 1. for all _a_ in (Offset,+Length), all of the following is true:
3895 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3896 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3899 1.2 For each LOST lock L in S->fileLocks such that _a_ in
3900 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3905 lock_ObtainRead(&cm_scacheLock);
3907 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
3909 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
3911 if (INTERSECT_RANGE(range, fileLock->range)) {
3912 if (IS_LOCK_ACTIVE(fileLock)) {
3913 if (fileLock->key == key) {
3915 /* If there is an active lock for this client, it
3916 is safe to substract ranges.*/
3917 cm_LockRangeSubtract(&range, &fileLock->range);
3918 substract_ranges = TRUE;
3920 if (fileLock->lockType != LockRead) {
3921 code = CM_ERROR_LOCK_CONFLICT;
3925 /* even if the entire range is locked for reading,
3926 we still can't grant the lock at this point
3927 because the client may have lost locks. That
3928 is, unless we have already seen an active lock
3929 belonging to the client, in which case there
3930 can't be any lost locks for this client. */
3931 if (substract_ranges)
3932 cm_LockRangeSubtract(&range, &fileLock->range);
3934 } else if (IS_LOCK_LOST(fileLock) &&
3935 (fileLock->key == key || fileLock->lockType == LockWrite)) {
3936 code = CM_ERROR_BADFD;
3942 lock_ReleaseRead(&cm_scacheLock);
3944 osi_Log4(afsd_logp, "cm_LockCheckRead scp 0x%x offset %d length %d code 0x%x",
3945 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
3956 /* Called with scp->rw held. Returns 0 if all is clear to write the
3957 specified range by the client identified by key.
3959 long cm_LockCheckWrite(cm_scache_t *scp,
3960 LARGE_INTEGER LOffset,
3961 LARGE_INTEGER LLength,
3964 #ifndef ADVISORY_LOCKS
3966 cm_file_lock_t *fileLock;
3971 range.offset = LOffset.QuadPart;
3972 range.length = LLength.QuadPart;
3975 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
3977 2. for all _a_ in (Offset,+Length), one of the following is true:
3979 2.1 Byte _a_ of S is unowned AND there does not exist a LOST
3980 lock L such that _a_ in (L->LOffset,+L->LLength).
3982 2.2 Byte _a_ of S is owned by C under lock L AND L->LockType is
3986 lock_ObtainRead(&cm_scacheLock);
3988 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
3990 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
3992 if (INTERSECT_RANGE(range, fileLock->range)) {
3993 if (IS_LOCK_ACTIVE(fileLock)) {
3994 if (fileLock->key == key) {
3995 if (fileLock->lockType == LockWrite) {
3997 /* if there is an active lock for this client, it
3998 is safe to substract ranges */
3999 cm_LockRangeSubtract(&range, &fileLock->range);
4001 code = CM_ERROR_LOCK_CONFLICT;
4005 code = CM_ERROR_LOCK_CONFLICT;
4008 } else if (IS_LOCK_LOST(fileLock)) {
4009 code = CM_ERROR_BADFD;
4015 lock_ReleaseRead(&cm_scacheLock);
4017 osi_Log4(afsd_logp, "cm_LockCheckWrite scp 0x%x offset %d length %d code 0x%x",
4018 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
4029 /* Called with cm_scacheLock write locked */
4030 static cm_file_lock_t * cm_GetFileLock(void) {
4033 l = (cm_file_lock_t *) cm_freeFileLocks;
4035 osi_QRemove(&cm_freeFileLocks, &l->q);
4037 l = malloc(sizeof(cm_file_lock_t));
4038 osi_assertx(l, "null cm_file_lock_t");
4041 memset(l, 0, sizeof(cm_file_lock_t));
4046 /* Called with cm_scacheLock write locked */
4047 static void cm_PutFileLock(cm_file_lock_t *l) {
4048 osi_QAdd(&cm_freeFileLocks, &l->q);
4051 /* called with scp->rw held. May release it during processing, but
4052 leaves it held on exit. */
4053 long cm_IntSetLock(cm_scache_t * scp, cm_user_t * userp, int lockType,
4059 struct rx_connection * rxconnp;
4061 afs_uint32 reqflags = reqp->flags;
4063 tfid.Volume = scp->fid.volume;
4064 tfid.Vnode = scp->fid.vnode;
4065 tfid.Unique = scp->fid.unique;
4068 osi_Log2(afsd_logp, "CALL SetLock scp 0x%p for lock %d", scp, lockType);
4070 reqp->flags |= CM_REQ_NORETRY;
4071 lock_ReleaseWrite(&scp->rw);
4074 code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4078 rxconnp = cm_GetRxConn(connp);
4079 code = RXAFS_SetLock(rxconnp, &tfid, lockType,
4081 rx_PutConnection(rxconnp);
4083 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
4086 code = cm_MapRPCError(code, reqp);
4088 osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
4090 osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
4093 lock_ObtainWrite(&scp->rw);
4094 reqp->flags = reqflags;
4098 /* called with scp->rw held. Releases it during processing */
4099 long cm_IntReleaseLock(cm_scache_t * scp, cm_user_t * userp,
4105 struct rx_connection * rxconnp;
4108 tfid.Volume = scp->fid.volume;
4109 tfid.Vnode = scp->fid.vnode;
4110 tfid.Unique = scp->fid.unique;
4113 lock_ReleaseWrite(&scp->rw);
4115 osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
4118 code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4122 rxconnp = cm_GetRxConn(connp);
4123 code = RXAFS_ReleaseLock(rxconnp, &tfid, &volSync);
4124 rx_PutConnection(rxconnp);
4126 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
4128 code = cm_MapRPCError(code, reqp);
4131 "CALL ReleaseLock FAILURE, code 0x%x", code);
4134 "CALL ReleaseLock SUCCESS");
4136 lock_ObtainWrite(&scp->rw);
4141 /* called with scp->rw held. May release it during processing, but
4142 will exit with lock held.
4146 - 0 if the user has permission to get the specified lock for the scp
4148 - CM_ERROR_NOACCESS if not
4150 Any other error from cm_SyncOp will be sent down untranslated.
4152 If CM_ERROR_NOACCESS is returned and lock_type is LockRead, then
4153 phas_insert (if non-NULL) will receive a boolean value indicating
4154 whether the user has INSERT permission or not.
4156 long cm_LockCheckPerms(cm_scache_t * scp,
4163 long code = 0, code2 = 0;
4165 /* lock permissions are slightly tricky because of the 'i' bit.
4166 If the user has PRSFS_LOCK, she can read-lock the file. If the
4167 user has PRSFS_WRITE, she can write-lock the file. However, if
4168 the user has PRSFS_INSERT, then she can write-lock new files,
4169 but not old ones. Since we don't have information about
4170 whether a file is new or not, we assume that if the user owns
4171 the scp, then she has the permissions that are granted by
4174 osi_Log3(afsd_logp, "cm_LockCheckPerms for scp[0x%p] type[%d] user[0x%p]",
4175 scp, lock_type, userp);
4177 if (lock_type == LockRead)
4178 rights |= PRSFS_LOCK;
4179 else if (lock_type == LockWrite)
4180 rights |= PRSFS_WRITE | PRSFS_LOCK;
4183 osi_assertx(FALSE, "invalid lock type");
4188 *phas_insert = FALSE;
4190 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
4191 CM_SCACHESYNC_GETSTATUS |
4192 CM_SCACHESYNC_NEEDCALLBACK);
4194 if (phas_insert && scp->creator == userp) {
4196 /* If this file was created by the user, then we check for
4197 PRSFS_INSERT. If the file server is recent enough, then
4198 this should be sufficient for her to get a write-lock (but
4199 not necessarily a read-lock). VICED_CAPABILITY_WRITELOCKACL
4200 indicates whether a file server supports getting write
4201 locks when the user only has PRSFS_INSERT.
4203 If the file was not created by the user we skip the check
4204 because the INSERT bit will not apply to this user even
4208 code2 = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
4209 CM_SCACHESYNC_GETSTATUS |
4210 CM_SCACHESYNC_NEEDCALLBACK);
4212 if (code2 == CM_ERROR_NOACCESS) {
4213 osi_Log0(afsd_logp, "cm_LockCheckPerms user has no INSERT bits");
4215 *phas_insert = TRUE;
4216 osi_Log0(afsd_logp, "cm_LockCheckPerms user has INSERT bits");
4220 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
4222 osi_Log1(afsd_logp, "cm_LockCheckPerms returning code %d", code);
4227 /* called with scp->rw held */
4228 long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
4229 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
4231 int allowWait, cm_user_t *userp, cm_req_t *reqp,
4232 cm_file_lock_t **lockpp)
4235 int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
4236 cm_file_lock_t *fileLock;
4239 int wait_unlock = FALSE;
4240 int force_client_lock = FALSE;
4242 osi_Log4(afsd_logp, "cm_Lock scp 0x%x type 0x%x offset %d length %d",
4243 scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
4244 osi_Log3(afsd_logp, "... allowWait %d key 0x%x:%x", allowWait,
4245 (unsigned long)(key >> 32), (unsigned long)(key & 0xffffffff));
4248 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
4250 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
4253 3.1 If L->LockType is exclusive then there does NOT exist a
4254 ACCEPTED lock M in S->fileLocks such that _a_ in
4255 (M->LOffset,+M->LLength).
4257 3.2 If L->LockType is shared then for each ACCEPTED lock M in
4258 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
4259 M->LockType is shared.
4261 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
4263 4.1 M->key != Key(C)
4265 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
4266 and (M->LOffset,+M->LLength) do not intersect.
4269 range.offset = LOffset.QuadPart;
4270 range.length = LLength.QuadPart;
4272 lock_ObtainRead(&cm_scacheLock);
4274 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4276 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4278 if (IS_LOCK_LOST(fileLock)) {
4279 if (fileLock->key == key) {
4280 code = CM_ERROR_BADFD;
4282 } else if (fileLock->lockType == LockWrite && INTERSECT_RANGE(range, fileLock->range)) {
4283 code = CM_ERROR_WOULDBLOCK;
4289 /* we don't need to check for deleted locks here since deleted
4290 locks are dequeued from scp->fileLocks */
4291 if (IS_LOCK_ACCEPTED(fileLock) &&
4292 INTERSECT_RANGE(range, fileLock->range)) {
4294 if ((sLockType & LOCKING_ANDX_SHARED_LOCK) == 0 ||
4295 fileLock->lockType != LockRead) {
4297 code = CM_ERROR_WOULDBLOCK;
4303 lock_ReleaseRead(&cm_scacheLock);
4305 if (code == 0 && SERVERLOCKS_ENABLED(scp)) {
4306 if (Which == scp->serverLock ||
4307 (Which == LockRead && scp->serverLock == LockWrite)) {
4311 /* we already have the lock we need */
4312 osi_Log3(afsd_logp, " we already have the correct lock. exclusives[%d], shared[%d], serverLock[%d]",
4313 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4315 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4317 /* special case: if we don't have permission to read-lock
4318 the file, then we force a clientside lock. This is to
4319 compensate for applications that obtain a read-lock for
4320 reading files off of directories that don't grant
4321 read-locks to the user. */
4322 if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4324 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4325 osi_Log0(afsd_logp, " User has no read-lock perms, but has INSERT perms.");
4328 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4329 force_client_lock = TRUE;
4333 } else if ((scp->exclusiveLocks > 0) ||
4334 (scp->sharedLocks > 0 && scp->serverLock != LockRead)) {
4337 /* We are already waiting for some other lock. We should
4338 wait for the daemon to catch up instead of generating a
4339 flood of SetLock calls. */
4340 osi_Log3(afsd_logp, " already waiting for other lock. exclusives[%d], shared[%d], serverLock[%d]",
4341 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4343 /* see if we have permission to create the lock in the
4345 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4347 code = CM_ERROR_WOULDBLOCK;
4348 else if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4350 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4352 " User has no read-lock perms, but has INSERT perms.");
4353 code = CM_ERROR_WOULDBLOCK;
4356 " User has no read-lock perms. Forcing client-side lock");
4357 force_client_lock = TRUE;
4361 /* leave any other codes as-is */
4365 int check_data_version = FALSE;
4368 /* first check if we have permission to elevate or obtain
4370 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4372 if (code == CM_ERROR_NOACCESS && Which == LockRead &&
4373 (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp))) {
4374 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4375 force_client_lock = TRUE;
4380 /* has_insert => (Which == LockRead, code == CM_ERROR_NOACCESS) */
4382 if (scp->serverLock == LockRead && Which == LockWrite) {
4384 /* We want to escalate the lock to a LockWrite.
4385 * Unfortunately that's not really possible without
4386 * letting go of the current lock. But for now we do
4390 " attempting to UPGRADE from LockRead to LockWrite.");
4392 " dataVersion on scp: %I64d", scp->dataVersion);
4394 /* we assume at this point (because scp->serverLock
4395 was valid) that we had a valid server lock. */
4396 scp->lockDataVersion = scp->dataVersion;
4397 check_data_version = TRUE;
4399 code = cm_IntReleaseLock(scp, userp, reqp);
4402 /* We couldn't release the lock */
4405 scp->serverLock = -1;
4409 /* We need to obtain a server lock of type Which in order
4410 * to assert this file lock */
4411 #ifndef AGGRESSIVE_LOCKS
4414 newLock = LockWrite;
4417 code = cm_IntSetLock(scp, userp, newLock, reqp);
4419 #ifdef AGGRESSIVE_LOCKS
4420 if ((code == CM_ERROR_WOULDBLOCK ||
4421 code == CM_ERROR_NOACCESS) && newLock != Which) {
4422 /* we wanted LockRead. We tried LockWrite. Now try
4427 osi_assertx(newLock == LockRead, "lock type not read");
4429 code = cm_IntSetLock(scp, userp, newLock, reqp);
4433 if (code == CM_ERROR_NOACCESS) {
4434 if (Which == LockRead) {
4435 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4437 /* We requested a read-lock, but we have permission to
4438 * get a write-lock. Try that */
4440 tcode = cm_LockCheckPerms(scp, LockWrite, userp, reqp, NULL);
4443 newLock = LockWrite;
4445 osi_Log0(afsd_logp, " User has 'i' perms and the request was for a LockRead. Trying to get a LockWrite instead");
4447 code = cm_IntSetLock(scp, userp, newLock, reqp);
4450 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4451 force_client_lock = TRUE;
4453 } else if (Which == LockWrite &&
4454 scp->creator == userp && !SCP_SUPPORTS_WRITELOCKACL(scp)) {
4457 /* Special case: if the lock request was for a
4458 * LockWrite and the user owns the file and we weren't
4459 * allowed to obtain the serverlock, we either lost a
4460 * race (the permissions changed from under us), or we
4461 * have 'i' bits, but we aren't allowed to lock the
4464 /* check if we lost a race... */
4465 tcode = cm_LockCheckPerms(scp, Which, userp, reqp, NULL);
4468 osi_Log0(afsd_logp, " User has 'i' perms but can't obtain write locks. Using client-side locks.");
4469 force_client_lock = TRUE;
4474 if (code == 0 && check_data_version &&
4475 scp->dataVersion != scp->lockDataVersion) {
4476 /* We lost a race. Although we successfully obtained
4477 * a lock, someone modified the file in between. The
4478 * locks have all been technically lost. */
4481 " Data version mismatch while upgrading lock.");
4483 " Data versions before=%I64d, after=%I64d",
4484 scp->lockDataVersion,
4487 " Releasing stale lock for scp 0x%x", scp);
4489 code = cm_IntReleaseLock(scp, userp, reqp);
4491 scp->serverLock = -1;
4493 code = CM_ERROR_INVAL;
4494 } else if (code == 0) {
4495 scp->serverLock = newLock;
4496 scp->lockDataVersion = scp->dataVersion;
4500 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4501 scp->serverLock == -1) {
4502 /* Oops. We lost the lock. */
4503 cm_LockMarkSCacheLost(scp);
4506 } else if (code == 0) { /* server locks not enabled */
4508 " Skipping server lock for scp");
4513 if (code != 0 && !force_client_lock) {
4514 /* Special case error translations
4516 Applications don't expect certain errors from a
4517 LockFile/UnlockFile call. We need to translate some error
4518 code to codes that apps expect and handle. */
4520 /* We shouldn't actually need to handle this case since we
4521 simulate locks for RO scps anyway. */
4522 if (code == CM_ERROR_READONLY) {
4523 osi_Log0(afsd_logp, " Reinterpreting CM_ERROR_READONLY as CM_ERROR_NOACCESS");
4524 code = CM_ERROR_NOACCESS;
4528 if (code == 0 || (code == CM_ERROR_WOULDBLOCK && allowWait) ||
4529 force_client_lock) {
4531 /* clear the error if we are forcing a client lock, so we
4532 don't get confused later. */
4533 if (force_client_lock && code != CM_ERROR_WOULDBLOCK)
4536 lock_ObtainWrite(&cm_scacheLock);
4537 fileLock = cm_GetFileLock();
4538 lock_ReleaseWrite(&cm_scacheLock);
4540 fileLock->fid = scp->fid;
4542 fileLock->key = key;
4543 fileLock->lockType = Which;
4545 fileLock->userp = userp;
4546 fileLock->range = range;
4547 fileLock->flags = (code == 0 ? 0 :
4549 CM_FILELOCK_FLAG_WAITUNLOCK :
4550 CM_FILELOCK_FLAG_WAITLOCK));
4552 if (force_client_lock || !SERVERLOCKS_ENABLED(scp))
4553 fileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
4555 fileLock->lastUpdate = (code == 0 && !force_client_lock) ? time(NULL) : 0;
4557 lock_ObtainWrite(&cm_scacheLock);
4558 osi_QAddT(&scp->fileLocksH, &scp->fileLocksT, &fileLock->fileq);
4559 cm_HoldSCacheNoLock(scp);
4560 fileLock->scp = scp;
4561 osi_QAdd(&cm_allFileLocks, &fileLock->q);
4562 lock_ReleaseWrite(&cm_scacheLock);
4568 if (IS_LOCK_CLIENTONLY(fileLock)) {
4570 } else if (IS_LOCK_ACCEPTED(fileLock)) {
4571 if (Which == LockRead)
4574 scp->exclusiveLocks++;
4578 "cm_Lock Lock added 0x%p flags 0x%x to scp [0x%p]",
4579 fileLock, fileLock->flags, scp);
4581 " exclusives[%d] shared[%d] client[%d] serverLock[%d]",
4582 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4583 (int)(signed char) scp->serverLock);
4586 "cm_Lock Rejecting lock (code = 0x%x)", code);
4592 static int cm_KeyEquals(cm_key_t k1, cm_key_t k2, int flags);
4594 /* Called with scp->rw held */
4595 long cm_UnlockByKey(cm_scache_t * scp,
4602 cm_file_lock_t *fileLock;
4603 osi_queue_t *q, *qn;
4606 osi_Log4(afsd_logp, "cm_UnlockByKey scp 0x%p key 0x%x:%x flags=0x%x",
4608 (unsigned long)(key >> 32),
4609 (unsigned long)(key & 0xffffffff),
4612 lock_ObtainWrite(&cm_scacheLock);
4614 for (q = scp->fileLocksH; q; q = qn) {
4617 fileLock = (cm_file_lock_t *)
4618 ((char *) q - offsetof(cm_file_lock_t, fileq));
4621 osi_Log4(afsd_logp, " Checking lock[0x%x] range[%d,+%d] type[%d]",
4623 (unsigned long) fileLock->range.offset,
4624 (unsigned long) fileLock->range.length,
4625 fileLock->lockType);
4626 osi_Log3(afsd_logp, " key[0x%x:%x] flags[0x%x]",
4627 (unsigned long)(fileLock->key >> 32),
4628 (unsigned long)(fileLock->key & 0xffffffff),
4631 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
4632 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
4633 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4635 fileLock->fid.volume,
4636 fileLock->fid.vnode,
4637 fileLock->fid.unique);
4638 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4639 fileLock->scp->fid.cell,
4640 fileLock->scp->fid.volume,
4641 fileLock->scp->fid.vnode,
4642 fileLock->scp->fid.unique);
4643 osi_assertx(FALSE, "invalid fid value");
4647 if (!IS_LOCK_DELETED(fileLock) &&
4648 cm_KeyEquals(fileLock->key, key, flags)) {
4649 osi_Log3(afsd_logp, "...Unlock range [%d,+%d] type %d",
4650 fileLock->range.offset,
4651 fileLock->range.length,
4652 fileLock->lockType);
4654 if (scp->fileLocksT == q)
4655 scp->fileLocksT = osi_QPrev(q);
4656 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
4658 if (IS_LOCK_CLIENTONLY(fileLock)) {
4660 } else if (IS_LOCK_ACCEPTED(fileLock)) {
4661 if (fileLock->lockType == LockRead)
4664 scp->exclusiveLocks--;
4667 fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
4669 cm_ReleaseUser(fileLock->userp);
4670 cm_ReleaseSCacheNoLock(scp);
4672 fileLock->userp = NULL;
4673 fileLock->scp = NULL;
4679 lock_ReleaseWrite(&cm_scacheLock);
4681 if (n_unlocks == 0) {
4682 osi_Log0(afsd_logp, "cm_UnlockByKey no locks found");
4683 osi_Log3(afsd_logp, " Leaving scp with exclusives[%d], shared[%d], serverLock[%d]",
4684 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4689 osi_Log1(afsd_logp, "cm_UnlockByKey done with %d locks", n_unlocks);
4691 osi_assertx(scp->sharedLocks >= 0, "scp->sharedLocks < 0");
4692 osi_assertx(scp->exclusiveLocks >= 0, "scp->exclusiveLocks < 0");
4693 osi_assertx(scp->clientLocks >= 0, "scp->clientLocks < 0");
4695 if (!SERVERLOCKS_ENABLED(scp)) {
4696 osi_Log0(afsd_logp, " Skipping server lock for scp");
4700 /* Ideally we would go through the rest of the locks to determine
4701 * if one or more locks that were formerly in WAITUNLOCK can now
4702 * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
4703 * scp->sharedLocks accordingly. However, the retrying of locks
4704 * in that manner is done cm_RetryLock() manually.
4707 if (scp->serverLock == LockWrite &&
4708 scp->exclusiveLocks == 0 &&
4709 scp->sharedLocks > 0) {
4711 /* The serverLock should be downgraded to LockRead */
4712 osi_Log0(afsd_logp, " DOWNGRADE lock from LockWrite to LockRead");
4714 /* since scp->serverLock looked sane, we are going to assume
4715 that we have a valid server lock. */
4716 scp->lockDataVersion = scp->dataVersion;
4717 osi_Log1(afsd_logp, " dataVersion on scp = %I64d", scp->dataVersion);
4719 code = cm_IntReleaseLock(scp, userp, reqp);
4722 /* so we couldn't release it. Just let the lock be for now */
4726 scp->serverLock = -1;
4729 code = cm_IntSetLock(scp, userp, LockRead, reqp);
4731 if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
4732 scp->serverLock = LockRead;
4733 } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
4734 /* We lost a race condition. Although we have a valid
4735 lock on the file, the data has changed and essentially
4736 we have lost the lock we had during the transition. */
4738 osi_Log0(afsd_logp, "Data version mismatch during lock downgrade");
4739 osi_Log2(afsd_logp, " Data versions before=%I64d, after=%I64d",
4740 scp->lockDataVersion,
4743 code = cm_IntReleaseLock(scp, userp, reqp);
4745 code = CM_ERROR_INVAL;
4746 scp->serverLock = -1;
4750 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4751 (scp->serverLock == -1)) {
4753 cm_LockMarkSCacheLost(scp);
4756 /* failure here has no bearing on the return value of
4760 } else if (scp->serverLock != (-1) &&
4761 scp->exclusiveLocks == 0 &&
4762 scp->sharedLocks == 0) {
4763 /* The serverLock should be released entirely */
4765 code = cm_IntReleaseLock(scp, userp, reqp);
4768 scp->serverLock = (-1);
4773 osi_Log1(afsd_logp, "cm_UnlockByKey code 0x%x", code);
4774 osi_Log4(afsd_logp, " Leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
4775 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4776 (int)(signed char) scp->serverLock);
4781 long cm_Unlock(cm_scache_t *scp,
4782 unsigned char sLockType,
4783 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
4789 int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
4790 cm_file_lock_t *fileLock;
4792 int release_userp = FALSE;
4794 osi_Log4(afsd_logp, "cm_Unlock scp 0x%p type 0x%x offset %d length %d",
4795 scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
4796 osi_Log2(afsd_logp, "... key 0x%x:%x",
4797 (unsigned long) (key >> 32), (unsigned long) (key & 0xffffffff));
4799 lock_ObtainRead(&cm_scacheLock);
4801 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4802 fileLock = (cm_file_lock_t *)
4803 ((char *) q - offsetof(cm_file_lock_t, fileq));
4806 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
4807 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
4808 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4810 fileLock->fid.volume,
4811 fileLock->fid.vnode,
4812 fileLock->fid.unique);
4813 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4814 fileLock->scp->fid.cell,
4815 fileLock->scp->fid.volume,
4816 fileLock->scp->fid.vnode,
4817 fileLock->scp->fid.unique);
4818 osi_assertx(FALSE, "invalid fid value");
4821 if (!IS_LOCK_DELETED(fileLock) &&
4822 fileLock->key == key &&
4823 fileLock->range.offset == LOffset.QuadPart &&
4824 fileLock->range.length == LLength.QuadPart) {
4830 osi_Log0(afsd_logp, "cm_Unlock lock not found; failure");
4832 lock_ReleaseRead(&cm_scacheLock);
4834 /* The lock didn't exist anyway. *shrug* */
4835 return CM_ERROR_RANGE_NOT_LOCKED;
4838 /* discard lock record */
4839 lock_ConvertRToW(&cm_scacheLock);
4840 if (scp->fileLocksT == q)
4841 scp->fileLocksT = osi_QPrev(q);
4842 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
4845 * Don't delete it here; let the daemon delete it, to simplify
4846 * the daemon's traversal of the list.
4849 if (IS_LOCK_CLIENTONLY(fileLock)) {
4851 } else if (IS_LOCK_ACCEPTED(fileLock)) {
4852 if (fileLock->lockType == LockRead)
4855 scp->exclusiveLocks--;
4858 fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
4859 if (userp != NULL) {
4860 cm_ReleaseUser(fileLock->userp);
4862 userp = fileLock->userp;
4863 release_userp = TRUE;
4865 fileLock->userp = NULL;
4866 cm_ReleaseSCacheNoLock(scp);
4867 fileLock->scp = NULL;
4868 lock_ReleaseWrite(&cm_scacheLock);
4870 if (!SERVERLOCKS_ENABLED(scp)) {
4871 osi_Log0(afsd_logp, " Skipping server locks for scp");
4875 /* Ideally we would go through the rest of the locks to determine
4876 * if one or more locks that were formerly in WAITUNLOCK can now
4877 * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
4878 * scp->sharedLocks accordingly. However, the retrying of locks
4879 * in that manner is done cm_RetryLock() manually.
4882 if (scp->serverLock == LockWrite &&
4883 scp->exclusiveLocks == 0 &&
4884 scp->sharedLocks > 0) {
4886 /* The serverLock should be downgraded to LockRead */
4887 osi_Log0(afsd_logp, " DOWNGRADE lock from LockWrite to LockRead");
4889 /* Since we already had a lock, we assume that there is a
4890 valid server lock. */
4891 scp->lockDataVersion = scp->dataVersion;
4892 osi_Log1(afsd_logp, " dataVersion on scp is %I64d", scp->dataVersion);
4894 /* before we downgrade, make sure that we have enough
4895 permissions to get the read lock. */
4896 code = cm_LockCheckPerms(scp, LockRead, userp, reqp, NULL);
4899 osi_Log0(afsd_logp, " SKIPPING downgrade because user doesn't have perms to get downgraded lock");
4905 code = cm_IntReleaseLock(scp, userp, reqp);
4908 /* so we couldn't release it. Just let the lock be for now */
4912 scp->serverLock = -1;
4915 code = cm_IntSetLock(scp, userp, LockRead, reqp);
4917 if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
4918 scp->serverLock = LockRead;
4919 } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
4920 /* Lost a race. We obtained a new lock, but that is
4921 meaningless since someone modified the file
4925 "Data version mismatch while downgrading lock");
4927 " Data versions before=%I64d, after=%I64d",
4928 scp->lockDataVersion,
4931 code = cm_IntReleaseLock(scp, userp, reqp);
4933 scp->serverLock = -1;
4934 code = CM_ERROR_INVAL;
4938 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4939 (scp->serverLock == -1)) {
4941 cm_LockMarkSCacheLost(scp);
4944 /* failure here has no bearing on the return value of
4948 } else if (scp->serverLock != (-1) &&
4949 scp->exclusiveLocks == 0 &&
4950 scp->sharedLocks == 0) {
4951 /* The serverLock should be released entirely */
4953 code = cm_IntReleaseLock(scp, userp, reqp);
4956 scp->serverLock = (-1);
4961 cm_ReleaseUser(userp);
4965 osi_Log1(afsd_logp, "cm_Unlock code 0x%x", code);
4966 osi_Log4(afsd_logp, " leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
4967 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4968 (int)(signed char) scp->serverLock);
4973 /* called with scp->rw held */
4974 void cm_LockMarkSCacheLost(cm_scache_t * scp)
4976 cm_file_lock_t *fileLock;
4979 osi_Log1(afsd_logp, "cm_LockMarkSCacheLost scp 0x%x", scp);
4981 /* cm_scacheLock needed because we are modifying fileLock->flags */
4982 lock_ObtainWrite(&cm_scacheLock);
4984 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4986 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4988 if (IS_LOCK_ACTIVE(fileLock) &&
4989 !IS_LOCK_CLIENTONLY(fileLock)) {
4990 if (fileLock->lockType == LockRead)
4993 scp->exclusiveLocks--;
4995 fileLock->flags |= CM_FILELOCK_FLAG_LOST;
4999 scp->serverLock = -1;
5000 scp->lockDataVersion = -1;
5001 lock_ReleaseWrite(&cm_scacheLock);
5004 /* Called with no relevant locks held */
5005 void cm_CheckLocks()
5007 osi_queue_t *q, *nq;
5008 cm_file_lock_t *fileLock;
5014 struct rx_connection * rxconnp;
5019 lock_ObtainWrite(&cm_scacheLock);
5021 cm_lockRefreshCycle++;
5023 osi_Log1(afsd_logp, "cm_CheckLocks starting lock check cycle %d", cm_lockRefreshCycle);
5025 for (q = cm_allFileLocks; q; q = nq) {
5026 fileLock = (cm_file_lock_t *) q;
5030 if (IS_LOCK_DELETED(fileLock)) {
5032 osi_QRemove(&cm_allFileLocks, q);
5033 cm_PutFileLock(fileLock);
5035 } else if (IS_LOCK_ACTIVE(fileLock) && !IS_LOCK_CLIENTONLY(fileLock)) {
5037 /* Server locks must have been enabled for us to have
5038 received an active non-client-only lock. */
5039 osi_assertx(cm_enableServerLocks, "!cm_enableServerLocks");
5041 scp = fileLock->scp;
5042 osi_assertx(scp != NULL, "null cm_scache_t");
5044 cm_HoldSCacheNoLock(scp);
5047 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5048 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5049 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5051 fileLock->fid.volume,
5052 fileLock->fid.vnode,
5053 fileLock->fid.unique);
5054 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5055 fileLock->scp->fid.cell,
5056 fileLock->scp->fid.volume,
5057 fileLock->scp->fid.vnode,
5058 fileLock->scp->fid.unique);
5059 osi_assertx(FALSE, "invalid fid");
5062 /* Server locks are extended once per scp per refresh
5064 if (scp->lastRefreshCycle != cm_lockRefreshCycle) {
5066 int scp_done = FALSE;
5068 osi_Log1(afsd_logp, "cm_CheckLocks Updating scp 0x%x", scp);
5070 lock_ReleaseWrite(&cm_scacheLock);
5071 lock_ObtainWrite(&scp->rw);
5073 /* did the lock change while we weren't holding the lock? */
5074 if (!IS_LOCK_ACTIVE(fileLock))
5075 goto post_syncopdone;
5077 code = cm_SyncOp(scp, NULL, fileLock->userp, &req, 0,
5078 CM_SCACHESYNC_NEEDCALLBACK
5079 | CM_SCACHESYNC_GETSTATUS
5080 | CM_SCACHESYNC_LOCK);
5084 "cm_CheckLocks SyncOp failure code 0x%x", code);
5085 goto post_syncopdone;
5088 /* cm_SyncOp releases scp->rw during which the lock
5089 may get released. */
5090 if (!IS_LOCK_ACTIVE(fileLock))
5091 goto pre_syncopdone;
5093 if (scp->serverLock != -1 && !(scp->flags & CM_SCACHEFLAG_DELETED)) {
5097 tfid.Volume = scp->fid.volume;
5098 tfid.Vnode = scp->fid.vnode;
5099 tfid.Unique = scp->fid.unique;
5101 userp = fileLock->userp;
5103 osi_Log3(afsd_logp, "CALL ExtendLock lock 0x%p for scp=0x%p with lock %d",
5106 (int) scp->serverLock);
5108 lock_ReleaseWrite(&scp->rw);
5111 code = cm_ConnFromFID(&cfid, userp,
5116 rxconnp = cm_GetRxConn(connp);
5117 code = RXAFS_ExtendLock(rxconnp, &tfid,
5119 rx_PutConnection(rxconnp);
5121 osi_Log1(afsd_logp, " ExtendLock returns %d", code);
5123 } while (cm_Analyze(connp, userp, &req,
5124 &cfid, &volSync, NULL, NULL,
5127 code = cm_MapRPCError(code, &req);
5129 lock_ObtainWrite(&scp->rw);
5132 osi_Log1(afsd_logp, "CALL ExtendLock FAILURE, code 0x%x", code);
5134 osi_Log0(afsd_logp, "CALL ExtendLock SUCCESS");
5135 scp->lockDataVersion = scp->dataVersion;
5138 if ((code == EINVAL || code == CM_ERROR_INVAL) &&
5139 scp->lockDataVersion == scp->dataVersion) {
5143 (scp->exclusiveLocks > 0) ? LockWrite: LockRead;
5145 /* we might still have a chance to obtain a
5148 code = cm_IntSetLock(scp, userp, lockType, &req);
5151 code = CM_ERROR_INVAL;
5152 } else if (scp->lockDataVersion != scp->dataVersion) {
5154 /* now check if we still have the file at
5155 the right data version. */
5157 "Data version mismatch on scp 0x%p",
5160 " Data versions: before=%I64d, after=%I64d",
5161 scp->lockDataVersion,
5164 code = cm_IntReleaseLock(scp, userp, &req);
5166 code = CM_ERROR_INVAL;
5170 if (code == EINVAL || code == CM_ERROR_INVAL ||
5171 code == CM_ERROR_BADFD) {
5172 cm_LockMarkSCacheLost(scp);
5176 /* interestingly, we have found an active lock
5177 belonging to an scache that has no
5179 cm_LockMarkSCacheLost(scp);
5186 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5189 lock_ReleaseWrite(&scp->rw);
5191 lock_ObtainWrite(&cm_scacheLock);
5194 fileLock->lastUpdate = time(NULL);
5198 scp->lastRefreshCycle = cm_lockRefreshCycle;
5201 /* we have already refreshed the locks on this scp */
5202 fileLock->lastUpdate = time(NULL);
5205 cm_ReleaseSCacheNoLock(scp);
5207 } else if (IS_LOCK_ACTIVE(fileLock) && IS_LOCK_CLIENTONLY(fileLock)) {
5208 /* TODO: Check callbacks */
5212 lock_ReleaseWrite(&cm_scacheLock);
5213 osi_Log1(afsd_logp, "cm_CheckLocks completes lock check cycle %d", cm_lockRefreshCycle);
5216 /* NOT called with scp->rw held. */
5217 long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
5220 cm_scache_t *scp = NULL;
5221 cm_file_lock_t *fileLock;
5225 int force_client_lock = FALSE;
5226 int has_insert = FALSE;
5227 int check_data_version = FALSE;
5231 if (client_is_dead) {
5232 code = CM_ERROR_TIMEDOUT;
5236 lock_ObtainRead(&cm_scacheLock);
5238 osi_Log2(afsd_logp, "cm_RetryLock checking lock %p (scp=%p)", oldFileLock, oldFileLock->scp);
5239 osi_Log4(afsd_logp, " offset(%x:%x) length(%x:%x)",
5240 (unsigned)(oldFileLock->range.offset >> 32),
5241 (unsigned)(oldFileLock->range.offset & 0xffffffff),
5242 (unsigned)(oldFileLock->range.length >> 32),
5243 (unsigned)(oldFileLock->range.length & 0xffffffff));
5244 osi_Log3(afsd_logp, " key(%x:%x) flags=%x",
5245 (unsigned)(oldFileLock->key >> 32),
5246 (unsigned)(oldFileLock->key & 0xffffffff),
5247 (unsigned)(oldFileLock->flags));
5249 /* if the lock has already been granted, then we have nothing to do */
5250 if (IS_LOCK_ACTIVE(oldFileLock)) {
5251 lock_ReleaseRead(&cm_scacheLock);
5252 osi_Log0(afsd_logp, "cm_RetryLock lock already granted");
5256 /* we can't do anything with lost or deleted locks at the moment. */
5257 if (IS_LOCK_LOST(oldFileLock) || IS_LOCK_DELETED(oldFileLock)) {
5258 code = CM_ERROR_BADFD;
5259 osi_Log0(afsd_logp, "cm_RetryLock lock is lost or deleted");
5260 lock_ReleaseRead(&cm_scacheLock);
5264 scp = oldFileLock->scp;
5266 osi_assertx(scp != NULL, "null cm_scache_t");
5268 lock_ReleaseRead(&cm_scacheLock);
5269 lock_ObtainWrite(&scp->rw);
5271 code = cm_LockCheckPerms(scp, oldFileLock->lockType,
5275 if (code == CM_ERROR_NOACCESS && oldFileLock->lockType == LockRead) {
5276 if (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp)) {
5277 force_client_lock = TRUE;
5281 lock_ReleaseWrite(&scp->rw);
5285 lock_ObtainWrite(&cm_scacheLock);
5287 /* Check if we already have a sufficient server lock to allow this
5288 lock to go through. */
5289 if (IS_LOCK_WAITLOCK(oldFileLock) &&
5290 (!SERVERLOCKS_ENABLED(scp) ||
5291 scp->serverLock == oldFileLock->lockType ||
5292 scp->serverLock == LockWrite)) {
5294 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5296 if (SERVERLOCKS_ENABLED(scp)) {
5297 osi_Log1(afsd_logp, "cm_RetryLock Server lock (%d) is sufficient for lock. Granting",
5298 (int) scp->serverLock);
5300 osi_Log0(afsd_logp, "cm_RetryLock skipping server lock for scp");
5303 lock_ReleaseWrite(&cm_scacheLock);
5304 lock_ReleaseWrite(&scp->rw);
5309 if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5311 /* check if the conflicting locks have dissappeared already */
5312 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5314 fileLock = (cm_file_lock_t *)
5315 ((char *) q - offsetof(cm_file_lock_t, fileq));
5317 if (IS_LOCK_LOST(fileLock)) {
5318 if (fileLock->key == oldFileLock->key) {
5319 code = CM_ERROR_BADFD;
5320 oldFileLock->flags |= CM_FILELOCK_FLAG_LOST;
5321 osi_Log1(afsd_logp, " found lost lock %p for same key. Marking lock as lost",
5324 } else if (fileLock->lockType == LockWrite &&
5325 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5326 osi_Log1(afsd_logp, " found conflicting LOST lock %p", fileLock);
5327 code = CM_ERROR_WOULDBLOCK;
5332 if (IS_LOCK_ACCEPTED(fileLock) &&
5333 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5335 if (oldFileLock->lockType != LockRead ||
5336 fileLock->lockType != LockRead) {
5338 osi_Log1(afsd_logp, " found conflicting lock %p", fileLock);
5339 code = CM_ERROR_WOULDBLOCK;
5347 lock_ReleaseWrite(&cm_scacheLock);
5348 lock_ReleaseWrite(&scp->rw);
5353 /* when we get here, the lock is either a WAITUNLOCK or WAITLOCK.
5354 If it is WAITUNLOCK, then we didn't find any conflicting lock
5355 but we haven't verfied whether the serverLock is sufficient to
5356 assert it. If it is WAITLOCK, then the serverLock is
5357 insufficient to assert it. Eitherway, we are ready to accept
5358 the lock as either ACTIVE or WAITLOCK depending on the
5361 /* First, promote the WAITUNLOCK to a WAITLOCK */
5362 if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5363 if (oldFileLock->lockType == LockRead)
5366 scp->exclusiveLocks++;
5368 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITUNLOCK;
5369 oldFileLock->flags |= CM_FILELOCK_FLAG_WAITLOCK;
5372 osi_assertx(IS_LOCK_WAITLOCK(oldFileLock), "!IS_LOCK_WAITLOCK");
5374 if (force_client_lock ||
5375 !SERVERLOCKS_ENABLED(scp) ||
5376 scp->serverLock == oldFileLock->lockType ||
5377 (oldFileLock->lockType == LockRead &&
5378 scp->serverLock == LockWrite)) {
5380 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5382 if ((force_client_lock ||
5383 !SERVERLOCKS_ENABLED(scp)) &&
5384 !IS_LOCK_CLIENTONLY(oldFileLock)) {
5386 oldFileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
5388 if (oldFileLock->lockType == LockRead)
5391 scp->exclusiveLocks--;
5396 lock_ReleaseWrite(&cm_scacheLock);
5397 lock_ReleaseWrite(&scp->rw);
5404 code = cm_SyncOp(scp, NULL, oldFileLock->userp, &req, 0,
5405 CM_SCACHESYNC_NEEDCALLBACK
5406 | CM_SCACHESYNC_GETSTATUS
5407 | CM_SCACHESYNC_LOCK);
5409 osi_Log1(afsd_logp, "cm_RetryLock SyncOp failure code 0x%x", code);
5410 lock_ReleaseWrite(&cm_scacheLock);
5411 goto post_syncopdone;
5414 if (!IS_LOCK_WAITLOCK(oldFileLock))
5415 goto pre_syncopdone;
5417 userp = oldFileLock->userp;
5419 #ifndef AGGRESSIVE_LOCKS
5420 newLock = oldFileLock->lockType;
5422 newLock = LockWrite;
5426 /* if has_insert is non-zero, then:
5427 - the lock a LockRead
5428 - we don't have permission to get a LockRead
5429 - we do have permission to get a LockWrite
5430 - the server supports VICED_CAPABILITY_WRITELOCKACL
5433 newLock = LockWrite;
5436 lock_ReleaseWrite(&cm_scacheLock);
5438 /* when we get here, either we have a read-lock and want a
5439 write-lock or we don't have any locks and we want some
5442 if (scp->serverLock == LockRead) {
5444 osi_assertx(newLock == LockWrite, "!LockWrite");
5446 osi_Log0(afsd_logp, " Attempting to UPGRADE from LockRead to LockWrite");
5448 scp->lockDataVersion = scp->dataVersion;
5449 check_data_version = TRUE;
5451 code = cm_IntReleaseLock(scp, userp, &req);
5454 goto pre_syncopdone;
5456 scp->serverLock = -1;
5459 code = cm_IntSetLock(scp, userp, newLock, &req);
5462 if (scp->dataVersion != scp->lockDataVersion) {
5463 /* we lost a race. too bad */
5466 " Data version mismatch while upgrading lock.");
5468 " Data versions before=%I64d, after=%I64d",
5469 scp->lockDataVersion,
5472 " Releasing stale lock for scp 0x%x", scp);
5474 code = cm_IntReleaseLock(scp, userp, &req);
5476 scp->serverLock = -1;
5478 code = CM_ERROR_INVAL;
5480 cm_LockMarkSCacheLost(scp);
5482 scp->serverLock = newLock;
5487 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5493 if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
5494 lock_ObtainWrite(&cm_scacheLock);
5495 if (scp->fileLocksT == &oldFileLock->fileq)
5496 scp->fileLocksT = osi_QPrev(&oldFileLock->fileq);
5497 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, &oldFileLock->fileq);
5498 lock_ReleaseWrite(&cm_scacheLock);
5500 lock_ReleaseWrite(&scp->rw);
5503 lock_ObtainWrite(&cm_scacheLock);
5505 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5506 } else if (code != CM_ERROR_WOULDBLOCK) {
5507 oldFileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5508 cm_ReleaseUser(oldFileLock->userp);
5509 oldFileLock->userp = NULL;
5510 if (oldFileLock->scp) {
5511 cm_ReleaseSCacheNoLock(oldFileLock->scp);
5512 oldFileLock->scp = NULL;
5515 lock_ReleaseWrite(&cm_scacheLock);
5520 cm_key_t cm_GenerateKey(unsigned int session_id, unsigned long process_id, unsigned int file_id)
5523 osi_assertx((process_id & 0xffffffff) == process_id, "unexpected process_id");
5524 osi_assertx((session_id & 0xffff) == session_id, "unexpected session_id");
5525 osi_assertx((file_id & 0xffff) == file_id, "unexpected file_id");
5529 (((cm_key_t) (process_id & 0xffffffff)) << 32) |
5530 (((cm_key_t) (session_id & 0xffff)) << 16) |
5531 (((cm_key_t) (file_id & 0xffff)));
5534 static int cm_KeyEquals(cm_key_t k1, cm_key_t k2, int flags)
5536 if (flags & CM_UNLOCK_BY_FID) {
5537 return ((k1 & 0xffffffff) == (k2 & 0xffffffff));
5543 void cm_ReleaseAllLocks(void)
5549 cm_file_lock_t *fileLock;
5552 for (i = 0; i < cm_data.scacheHashTableSize; i++)
5554 for ( scp = cm_data.scacheHashTablep[i]; scp; scp = scp->nextp ) {
5555 while (scp->fileLocksH != NULL) {
5556 lock_ObtainWrite(&scp->rw);
5557 lock_ObtainWrite(&cm_scacheLock);
5558 if (!scp->fileLocksH) {
5559 lock_ReleaseWrite(&cm_scacheLock);
5560 lock_ReleaseWrite(&scp->rw);
5563 fileLock = (cm_file_lock_t *)((char *) scp->fileLocksH - offsetof(cm_file_lock_t, fileq));
5564 userp = fileLock->userp;
5566 key = fileLock->key;
5567 cm_HoldSCacheNoLock(scp);
5568 lock_ReleaseWrite(&cm_scacheLock);
5569 cm_UnlockByKey(scp, key, 0, userp, &req);
5570 cm_ReleaseSCache(scp);
5571 cm_ReleaseUser(userp);
5572 lock_ReleaseWrite(&scp->rw);