2 * Copyright 2000, International Business Machines Corporation and others.
5 * This software has been released under the terms of the IBM Public
6 * License. For details, see the LICENSE file in the top-level source
7 * directory or online at http://www.openafs.org/dl/license10.html
10 #include <afs/param.h>
30 extern void afsi_log(char *pattern, ...);
33 int cm_enableServerLocks = 1;
35 int cm_followBackupPath = 0;
38 * Case-folding array. This was constructed by inspecting of SMBtrace output.
39 * I do not know anything more about it.
41 unsigned char cm_foldUpper[256] = {
42 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
43 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
44 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
45 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
46 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
47 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
48 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
49 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
50 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
51 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
52 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
53 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
54 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
55 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
56 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
57 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
58 0x80, 0x9a, 0x90, 0x41, 0x8e, 0x41, 0x8f, 0x80,
59 0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x8e, 0x8f,
60 0x90, 0x92, 0x92, 0x4f, 0x99, 0x4f, 0x55, 0x55,
61 0x59, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
62 0x41, 0x49, 0x4f, 0x55, 0xa5, 0xa5, 0x56, 0xa7,
63 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
64 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
65 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
66 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
67 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
68 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
69 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
70 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
71 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
72 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
73 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
77 * Case-insensitive string comparison. We used to use stricmp, but it doesn't
78 * know about 8-bit characters (e.g. 129 is lowercase u-umlaut, 154 is
79 * upper-case u-umlaut).
81 int cm_stricmp(const char *str1, const char *str2)
93 c1 = (char) cm_foldUpper[(unsigned char)(*str1++)];
94 c2 = (char) cm_foldUpper[(unsigned char)(*str2++)];
104 /* return success if we can open this file in this mode */
105 long cm_CheckOpen(cm_scache_t *scp, int openMode, int trunc, cm_user_t *userp,
113 rights |= PRSFS_READ;
114 if (openMode == 1 || openMode == 2 || trunc)
115 rights |= PRSFS_WRITE;
117 lock_ObtainWrite(&scp->rw);
119 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
120 CM_SCACHESYNC_GETSTATUS
121 | CM_SCACHESYNC_NEEDCALLBACK
122 | CM_SCACHESYNC_LOCK);
125 ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
126 scp->fileType == CM_SCACHETYPE_FILE) {
129 unsigned int sLockType;
130 LARGE_INTEGER LOffset, LLength;
132 /* Check if there's some sort of lock on the file at the
135 key = cm_GenerateKey(CM_SESSION_CMINT,0,0);
137 if (rights & PRSFS_WRITE)
140 sLockType = LOCKING_ANDX_SHARED_LOCK;
142 LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
143 LOffset.LowPart = CM_FLSHARE_OFFSET_LOW;
144 LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
145 LLength.LowPart = CM_FLSHARE_LENGTH_LOW;
147 code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
150 cm_Unlock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp);
152 /* In this case, we allow the file open to go through even
153 though we can't enforce mandatory locking on the
155 if (code == CM_ERROR_NOACCESS &&
156 !(rights & PRSFS_WRITE))
160 case CM_ERROR_ALLOFFLINE:
161 case CM_ERROR_ALLDOWN:
162 case CM_ERROR_ALLBUSY:
163 case CM_ERROR_TIMEDOUT:
165 case CM_ERROR_WOULDBLOCK:
168 code = CM_ERROR_SHARING_VIOLATION;
173 } else if (code != 0) {
177 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
181 lock_ReleaseWrite(&scp->rw);
186 /* return success if we can open this file in this mode */
187 long cm_CheckNTOpen(cm_scache_t *scp, unsigned int desiredAccess,
188 unsigned int createDisp, cm_user_t *userp, cm_req_t *reqp,
189 cm_lock_data_t **ldpp)
194 osi_assertx(ldpp != NULL, "null cm_lock_data_t");
197 /* Always allow delete; the RPC will tell us if it's OK */
198 if (desiredAccess == DELETE)
203 if (desiredAccess & (AFS_ACCESS_READ|AFS_ACCESS_EXECUTE))
204 rights |= (scp->fileType == CM_SCACHETYPE_DIRECTORY ? PRSFS_LOOKUP : PRSFS_READ);
206 /* We used to require PRSFS_WRITE if createDisp was 4
207 (OPEN_ALWAYS) even if AFS_ACCESS_WRITE was not requested.
208 However, we don't need to do that since the existence of the
209 scp implies that we don't need to create it. */
210 if (desiredAccess & AFS_ACCESS_WRITE)
211 rights |= PRSFS_WRITE;
213 lock_ObtainWrite(&scp->rw);
215 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
216 CM_SCACHESYNC_GETSTATUS
217 | CM_SCACHESYNC_NEEDCALLBACK
218 | CM_SCACHESYNC_LOCK);
221 * If the open will fail because the volume is readonly, then we will
222 * return an access denied error instead. This is to help brain-dead
223 * apps run correctly on replicated volumes.
224 * See defect 10007 for more information.
226 if (code == CM_ERROR_READONLY)
227 code = CM_ERROR_NOACCESS;
230 ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
231 scp->fileType == CM_SCACHETYPE_FILE) {
233 unsigned int sLockType;
234 LARGE_INTEGER LOffset, LLength;
236 /* Check if there's some sort of lock on the file at the
239 key = cm_GenerateKey(CM_SESSION_CMINT,0,0);
240 if (rights & PRSFS_WRITE)
243 sLockType = LOCKING_ANDX_SHARED_LOCK;
245 /* single byte lock at offset 0x0100 0000 0000 0000 */
246 LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
247 LOffset.LowPart = CM_FLSHARE_OFFSET_LOW;
248 LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
249 LLength.LowPart = CM_FLSHARE_LENGTH_LOW;
251 code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
254 (*ldpp) = (cm_lock_data_t *)malloc(sizeof(cm_lock_data_t));
261 (*ldpp)->sLockType = sLockType;
262 (*ldpp)->LOffset.HighPart = LOffset.HighPart;
263 (*ldpp)->LOffset.LowPart = LOffset.LowPart;
264 (*ldpp)->LLength.HighPart = LLength.HighPart;
265 (*ldpp)->LLength.LowPart = LLength.LowPart;
267 /* In this case, we allow the file open to go through even
268 though we can't enforce mandatory locking on the
270 if (code == CM_ERROR_NOACCESS &&
271 !(rights & PRSFS_WRITE))
275 case CM_ERROR_ALLOFFLINE:
276 case CM_ERROR_ALLDOWN:
277 case CM_ERROR_ALLBUSY:
278 case CM_ERROR_TIMEDOUT:
280 case CM_ERROR_WOULDBLOCK:
283 code = CM_ERROR_SHARING_VIOLATION;
287 } else if (code != 0) {
292 lock_ReleaseWrite(&scp->rw);
294 osi_Log3(afsd_logp,"cm_CheckNTOpen scp 0x%p ldp 0x%p code 0x%x", scp, *ldpp, code);
298 extern long cm_CheckNTOpenDone(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp,
299 cm_lock_data_t ** ldpp)
301 osi_Log2(afsd_logp,"cm_CheckNTOpenDone scp 0x%p ldp 0x%p", scp, *ldpp);
302 lock_ObtainWrite(&scp->rw);
304 cm_Unlock(scp, (*ldpp)->sLockType, (*ldpp)->LOffset, (*ldpp)->LLength,
305 (*ldpp)->key, 0, userp, reqp);
309 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
310 lock_ReleaseWrite(&scp->rw);
314 * When CAP_NT_SMBS has been negotiated, deletion (of files or directories) is
315 * done in three steps:
316 * (1) open for deletion (NT_CREATE_AND_X)
317 * (2) set for deletion on close (NT_TRANSACTION2, SET_FILE_INFO)
319 * We must not do the RPC until step 3. But if we are going to return an error
320 * code (e.g. directory not empty), we must return it by step 2, otherwise most
321 * clients will not notice it. So we do a preliminary check. For deleting
322 * files, this is almost free, since we have already done the RPC to get the
323 * parent directory's status bits. But for deleting directories, we must do an
324 * additional RPC to get the directory's data to check if it is empty. Sigh.
326 long cm_CheckNTDelete(cm_scache_t *dscp, cm_scache_t *scp, cm_user_t *userp,
332 cm_dirEntry_t *dep = 0;
333 unsigned short *hashTable;
335 int BeyondPage = 0, HaveDot = 0, HaveDotDot = 0;
338 /* First check permissions */
339 lock_ObtainWrite(&scp->rw);
340 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_DELETE,
341 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
343 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
344 lock_ReleaseWrite(&scp->rw);
348 /* If deleting directory, must be empty */
350 if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
353 thyper.HighPart = 0; thyper.LowPart = 0;
354 code = buf_Get(scp, &thyper, reqp, &bufferp);
358 lock_ObtainMutex(&bufferp->mx);
359 lock_ObtainWrite(&scp->rw);
362 code = cm_SyncOp(scp, bufferp, userp, reqp, 0,
363 CM_SCACHESYNC_NEEDCALLBACK
365 | CM_SCACHESYNC_BUFLOCKED);
369 if (cm_HaveBuffer(scp, bufferp, 1))
372 /* otherwise, load the buffer and try again */
373 lock_ReleaseMutex(&bufferp->mx);
374 code = cm_GetBuffer(scp, bufferp, NULL, userp, reqp);
375 lock_ReleaseWrite(&scp->rw);
376 lock_ObtainMutex(&bufferp->mx);
377 lock_ObtainWrite(&scp->rw);
378 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
383 lock_ReleaseWrite(&scp->rw);
386 /* We try to determine emptiness without looking beyond the first page,
387 * and without assuming "." and ".." are present and are on the first
388 * page (though these assumptions might, after all, be reasonable).
390 hashTable = (unsigned short *)(bufferp->datap + (32 * 5));
391 for (i=0; i<128; i++) {
392 idx = ntohs(hashTable[i]);
398 dep = (cm_dirEntry_t *)(bufferp->datap + (32 * idx));
399 if (strcmp(dep->name, ".") == 0)
401 else if (strcmp(dep->name, "..") == 0)
404 code = CM_ERROR_NOTEMPTY;
407 idx = ntohs(dep->next);
410 if (BeyondPage && HaveDot && HaveDotDot)
411 code = CM_ERROR_NOTEMPTY;
415 lock_ReleaseMutex(&bufferp->mx);
416 buf_Release(bufferp);
418 lock_ReleaseWrite(&scp->rw);
423 * Iterate through all entries in a directory.
424 * When the function funcp is called, the buffer is locked but the
425 * directory vnode is not.
427 * If the retscp parameter is not NULL, the parmp must be a
428 * cm_lookupSearch_t object.
430 long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
431 osi_hyper_t *startOffsetp, cm_user_t *userp, cm_req_t *reqp,
432 cm_scache_t **retscp)
436 cm_dirEntry_t *dep = 0;
439 osi_hyper_t dirLength;
440 osi_hyper_t bufferOffset;
441 osi_hyper_t curOffset;
445 cm_pageHeader_t *pageHeaderp;
447 long nextEntryCookie;
448 int numDirChunks; /* # of 32 byte dir chunks in this entry */
450 /* get the directory size */
451 lock_ObtainWrite(&scp->rw);
452 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_LOOKUP,
453 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
454 lock_ReleaseWrite(&scp->rw);
458 if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
459 return CM_ERROR_NOTDIR;
461 if (retscp) /* if this is a lookup call */
463 cm_lookupSearch_t* sp = parmp;
466 #ifdef AFS_FREELANCE_CLIENT
467 /* Freelance entries never end up in the DNLC because they
468 * do not have an associated cm_server_t
470 !(cm_freelanceEnabled &&
471 sp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
472 sp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
473 #else /* !AFS_FREELANCE_CLIENT */
478 int casefold = sp->caseFold;
479 sp->caseFold = 0; /* we have a strong preference for exact matches */
480 if ( *retscp = cm_dnlcLookup(scp, sp)) /* dnlc hit */
482 sp->caseFold = casefold;
485 sp->caseFold = casefold;
489 * see if we can find it using the directory hash tables.
490 * we can only do exact matches, since the hash is case
493 if (funcp != (cm_DirFuncp_t)cm_BPlusDirFoo)
502 code = cm_BeginDirOp(scp, userp, reqp, CM_DIRLOCK_READ,
503 CM_DIROP_FLAG_NONE, &dirop);
507 code = cm_BPlusDirLookup(&dirop, sp->nsearchNamep, &sp->fid);
512 code = cm_DirLookup(&dirop, sp->searchNamep, &sp->fid);
520 sp->ExactFound = TRUE;
521 *retscp = NULL; /* force caller to call cm_GetSCache() */
526 if (sp->caseFold && code == CM_ERROR_INEXACT_MATCH) {
529 sp->ExactFound = FALSE;
530 *retscp = NULL; /* force caller to call cm_GetSCache() */
534 return CM_ERROR_BPLUS_NOMATCH;
541 * XXX We only get the length once. It might change when we drop the
544 dirLength = scp->length;
547 bufferOffset.LowPart = bufferOffset.HighPart = 0;
549 curOffset = *startOffsetp;
551 curOffset.HighPart = 0;
552 curOffset.LowPart = 0;
556 /* make sure that curOffset.LowPart doesn't point to the first
557 * 32 bytes in the 2nd through last dir page, and that it
558 * doesn't point at the first 13 32-byte chunks in the first
559 * dir page, since those are dir and page headers, and don't
560 * contain useful information.
562 temp = curOffset.LowPart & (2048-1);
563 if (curOffset.HighPart == 0 && curOffset.LowPart < 2048) {
564 /* we're in the first page */
565 if (temp < 13*32) temp = 13*32;
568 /* we're in a later dir page */
569 if (temp < 32) temp = 32;
572 /* make sure the low order 5 bits are zero */
575 /* now put temp bits back ito curOffset.LowPart */
576 curOffset.LowPart &= ~(2048-1);
577 curOffset.LowPart |= temp;
579 /* check if we've passed the dir's EOF */
580 if (LargeIntegerGreaterThanOrEqualTo(curOffset, dirLength))
583 /* see if we can use the bufferp we have now; compute in which
584 * page the current offset would be, and check whether that's
585 * the offset of the buffer we have. If not, get the buffer.
587 thyper.HighPart = curOffset.HighPart;
588 thyper.LowPart = curOffset.LowPart & ~(cm_data.buf_blockSize-1);
589 if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
592 lock_ReleaseMutex(&bufferp->mx);
593 buf_Release(bufferp);
597 code = buf_Get(scp, &thyper, reqp, &bufferp);
599 /* if buf_Get() fails we do not have a buffer object to lock */
604 lock_ObtainMutex(&bufferp->mx);
605 bufferOffset = thyper;
607 /* now get the data in the cache */
609 lock_ObtainWrite(&scp->rw);
610 code = cm_SyncOp(scp, bufferp, userp, reqp,
612 CM_SCACHESYNC_NEEDCALLBACK
614 | CM_SCACHESYNC_BUFLOCKED);
616 lock_ReleaseWrite(&scp->rw);
619 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
621 if (cm_HaveBuffer(scp, bufferp, 1)) {
622 lock_ReleaseWrite(&scp->rw);
626 /* otherwise, load the buffer and try again */
627 lock_ReleaseMutex(&bufferp->mx);
628 code = cm_GetBuffer(scp, bufferp, NULL, userp,
630 lock_ReleaseWrite(&scp->rw);
631 lock_ObtainMutex(&bufferp->mx);
636 lock_ReleaseMutex(&bufferp->mx);
637 buf_Release(bufferp);
641 } /* if (wrong buffer) ... */
643 /* now we have the buffer containing the entry we're interested
644 * in; copy it out if it represents a non-deleted entry.
646 entryInDir = curOffset.LowPart & (2048-1);
647 entryInBuffer = curOffset.LowPart & (cm_data.buf_blockSize - 1);
649 /* page header will help tell us which entries are free. Page
650 * header can change more often than once per buffer, since
651 * AFS 3 dir page size may be less than (but not more than) a
652 * buffer package buffer.
654 /* only look intra-buffer */
655 temp = curOffset.LowPart & (cm_data.buf_blockSize - 1);
656 temp &= ~(2048 - 1); /* turn off intra-page bits */
657 pageHeaderp = (cm_pageHeader_t *) (bufferp->datap + temp);
659 /* now determine which entry we're looking at in the page. If
660 * it is free (there's a free bitmap at the start of the dir),
661 * we should skip these 32 bytes.
663 slotInPage = (entryInDir & 0x7e0) >> 5;
664 if (!(pageHeaderp->freeBitmap[slotInPage>>3]
665 & (1 << (slotInPage & 0x7)))) {
666 /* this entry is free */
667 numDirChunks = 1; /* only skip this guy */
671 tp = bufferp->datap + entryInBuffer;
672 dep = (cm_dirEntry_t *) tp; /* now points to AFS3 dir entry */
675 * here are some consistency checks
677 if (dep->flag != CM_DIR_FFIRST ||
678 strlen(dep->name) > 256) {
679 code = CM_ERROR_INVAL;
683 /* while we're here, compute the next entry's location, too,
684 * since we'll need it when writing out the cookie into the
685 * dir listing stream.
687 numDirChunks = cm_NameEntries(dep->name, NULL);
689 /* compute the offset of the cookie representing the next entry */
690 nextEntryCookie = curOffset.LowPart
691 + (CM_DIR_CHUNKSIZE * numDirChunks);
693 if (dep->fid.vnode != 0) {
694 /* this is one of the entries to use: it is not deleted */
695 code = (*funcp)(scp, dep, parmp, &curOffset);
698 } /* if we're including this name */
701 /* and adjust curOffset to be where the new cookie is */
703 thyper.LowPart = CM_DIR_CHUNKSIZE * numDirChunks;
704 curOffset = LargeIntegerAdd(thyper, curOffset);
705 } /* while copying data for dir listing */
707 /* release the mutex */
709 lock_ReleaseMutex(&bufferp->mx);
710 buf_Release(bufferp);
715 int cm_NoneUpper(normchar_t *s)
719 if (c >= 'A' && c <= 'Z')
724 int cm_NoneLower(normchar_t *s)
728 if (c >= 'a' && c <= 'z')
733 long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
736 cm_lookupSearch_t *sp;
738 normchar_t matchName[MAX_PATH];
739 int looking_for_short_name = FALSE;
741 sp = (cm_lookupSearch_t *) rockp;
743 if (cm_FsStringToNormString(dep->name, -1, matchName, lengthof(matchName)) == 0) {
744 /* Can't normalize FS string. */
749 match = cm_NormStrCmpI(matchName, sp->nsearchNamep);
751 match = cm_NormStrCmp(matchName, sp->nsearchNamep);
755 && !cm_Is8Dot3(matchName)) {
757 cm_Gen8Dot3NameInt(dep->name, &dep->fid, matchName, NULL);
759 match = cm_NormStrCmpI(matchName, sp->nsearchNamep);
761 match = cm_NormStrCmp(matchName, sp->nsearchNamep);
762 looking_for_short_name = TRUE;
772 if (!sp->caseFold || looking_for_short_name) {
773 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
774 return CM_ERROR_STOPNOW;
778 * If we get here, we are doing a case-insensitive search, and we
779 * have found a match. Now we determine what kind of match it is:
780 * exact, lower-case, upper-case, or none of the above. This is done
781 * in order to choose among matches, if there are more than one.
784 /* Exact matches are the best. */
785 match = cm_NormStrCmp(matchName, sp->nsearchNamep);
788 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
789 return CM_ERROR_STOPNOW;
792 /* Lower-case matches are next. */
795 if (cm_NoneUpper(matchName)) {
800 /* Upper-case matches are next. */
803 if (cm_NoneLower(matchName)) {
808 /* General matches are last. */
814 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
818 /* read the contents of a mount point into the appropriate string.
819 * called with write locked scp, and returns with locked scp.
821 long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
824 cm_buf_t *bufp = NULL;
828 if (scp->mountPointStringp[0])
831 #ifdef AFS_FREELANCE_CLIENT
832 /* File servers do not have data for freelance entries */
833 if (cm_freelanceEnabled &&
834 scp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
835 scp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
837 code = cm_FreelanceFetchMountPointString(scp);
839 #endif /* AFS_FREELANCE_CLIENT */
841 /* otherwise, we have to read it in */
842 lock_ReleaseWrite(&scp->rw);
844 thyper.LowPart = thyper.HighPart = 0;
845 code = buf_Get(scp, &thyper, reqp, &bufp);
847 lock_ObtainWrite(&scp->rw);
852 code = cm_SyncOp(scp, bufp, userp, reqp, 0,
853 CM_SCACHESYNC_READ | CM_SCACHESYNC_NEEDCALLBACK);
857 cm_SyncOpDone(scp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
859 if (cm_HaveBuffer(scp, bufp, 0))
862 /* otherwise load buffer */
863 code = cm_GetBuffer(scp, bufp, NULL, userp, reqp);
867 /* locked, has callback, has valid data in buffer */
868 if ((tlen = scp->length.LowPart) > MOUNTPOINTLEN - 1)
869 return CM_ERROR_TOOBIG;
871 code = CM_ERROR_INVAL;
875 /* someone else did the work while we were out */
876 if (scp->mountPointStringp[0]) {
881 /* otherwise, copy out the link */
882 memcpy(scp->mountPointStringp, bufp->datap, tlen);
884 /* now make it null-terminated. Note that the original contents of a
885 * link that is a mount point is "#volname." where "." is there just to
886 * be turned into a null. That is, we can trash the last char of the
887 * link without damaging the vol name. This is a stupid convention,
888 * but that's the protocol.
890 scp->mountPointStringp[tlen-1] = 0;
901 /* called with a locked scp and chases the mount point, yielding outScpp.
902 * scp remains write locked, just for simplicity of describing the interface.
904 long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
905 cm_req_t *reqp, cm_scache_t **outScpp)
907 fschar_t *cellNamep = NULL;
908 fschar_t *volNamep = NULL;
912 cm_volume_t *volp = NULL;
921 if (scp->mountRootFid.cell != 0 && scp->mountRootGen >= cm_data.mountRootGen) {
922 tfid = scp->mountRootFid;
923 lock_ReleaseWrite(&scp->rw);
924 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
925 lock_ObtainWrite(&scp->rw);
929 /* parse the volume name */
930 mpNamep = scp->mountPointStringp;
932 return CM_ERROR_NOSUCHPATH;
933 mtType = *scp->mountPointStringp;
935 cp = cm_FsStrChr(mpNamep, _FS(':'));
937 /* cellular mount point */
938 cellNamep = (fschar_t *)malloc((cp - mpNamep) * sizeof(fschar_t));
939 cm_FsStrCpyN(cellNamep, cp - mpNamep, mpNamep + 1, cp - mpNamep - 1);
940 volNamep = cm_FsStrDup(cp+1);
942 /* now look up the cell */
943 lock_ReleaseWrite(&scp->rw);
944 cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE);
945 lock_ObtainWrite(&scp->rw);
948 volNamep = cm_FsStrDup(mpNamep + 1);
950 #ifdef AFS_FREELANCE_CLIENT
952 * Mount points in the Freelance cell should default
953 * to the workstation cell.
955 if (cm_freelanceEnabled &&
956 scp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
957 scp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
959 fschar_t rootCellName[256]="";
960 cm_GetRootCellName(rootCellName);
961 cellp = cm_GetCell(rootCellName, 0);
963 #endif /* AFS_FREELANCE_CLIENT */
964 cellp = cm_FindCellByID(scp->fid.cell, 0);
968 code = CM_ERROR_NOSUCHCELL;
972 vnLength = cm_FsStrLen(volNamep);
973 if (vnLength >= 8 && cm_FsStrCmp(volNamep + vnLength - 7, ".backup") == 0)
974 targetType = BACKVOL;
975 else if (vnLength >= 10
976 && cm_FsStrCmp(volNamep + vnLength - 9, ".readonly") == 0)
981 /* check for backups within backups */
982 if (targetType == BACKVOL
983 && (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
984 == CM_SCACHEFLAG_RO) {
985 code = CM_ERROR_NOSUCHVOLUME;
989 /* now we need to get the volume */
990 lock_ReleaseWrite(&scp->rw);
991 if (cm_VolNameIsID(volNamep)) {
992 code = cm_FindVolumeByID(cellp, atoi(volNamep), userp, reqp,
993 CM_GETVOL_FLAG_CREATE, &volp);
995 code = cm_FindVolumeByName(cellp, volNamep, userp, reqp,
996 CM_GETVOL_FLAG_CREATE, &volp);
998 lock_ObtainWrite(&scp->rw);
1001 afs_uint32 cell, volume;
1002 cm_vol_state_t *statep;
1004 cell = cellp->cellID;
1006 /* if the mt pt originates in a .backup volume (not a .readonly)
1007 * and FollowBackupPath is active, and if there is a .backup
1008 * volume for the target, then use the .backup of the target
1009 * instead of the read-write.
1011 if (cm_followBackupPath &&
1012 volp->vol[BACKVOL].ID != 0 &&
1013 (dscp->flags & (CM_SCACHEFLAG_RO|CM_SCACHEFLAG_PURERO)) == CM_SCACHEFLAG_RO &&
1014 (targetType == RWVOL || targetType == ROVOL && volp->vol[ROVOL].ID == 0)
1016 targetType = BACKVOL;
1018 /* if the mt pt is in a read-only volume (not just a
1019 * backup), and if there is a read-only volume for the
1020 * target, and if this is a targetType '#' mount point, use
1021 * the read-only, otherwise use the one specified.
1023 else if (mtType == '#' && targetType == RWVOL &&
1024 (scp->flags & CM_SCACHEFLAG_PURERO) &&
1025 volp->vol[ROVOL].ID != 0) {
1029 lock_ObtainWrite(&volp->rw);
1030 statep = cm_VolumeStateByType(volp, targetType);
1031 volume = statep->ID;
1032 statep->dotdotFid = dscp->fid;
1033 lock_ReleaseWrite(&volp->rw);
1035 /* the rest of the fid is a magic number */
1036 cm_SetFid(&scp->mountRootFid, cell, volume, 1, 1);
1037 scp->mountRootGen = cm_data.mountRootGen;
1039 tfid = scp->mountRootFid;
1040 lock_ReleaseWrite(&scp->rw);
1041 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
1042 lock_ObtainWrite(&scp->rw);
1055 long cm_LookupInternal(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_user_t *userp,
1056 cm_req_t *reqp, cm_scache_t **outScpp)
1059 int dnlcHit = 1; /* did we hit in the dnlc? yes, we did */
1060 cm_scache_t *tscp = NULL;
1061 cm_scache_t *mountedScp;
1062 cm_lookupSearch_t rock;
1064 normchar_t *nnamep = NULL;
1065 fschar_t *fnamep = NULL;
1070 memset(&rock, 0, sizeof(rock));
1072 if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
1073 && cm_ClientStrCmp(cnamep, _C("..")) == 0) {
1074 if (dscp->dotdotFid.volume == 0)
1075 return CM_ERROR_NOSUCHVOLUME;
1076 rock.fid = dscp->dotdotFid;
1078 } else if (cm_ClientStrCmp(cnamep, _C(".")) == 0) {
1079 rock.fid = dscp->fid;
1083 nnamep = cm_ClientStringToNormStringAlloc(cnamep, -1, NULL);
1085 code = CM_ERROR_NOSUCHFILE;
1088 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
1090 code = CM_ERROR_NOSUCHFILE;
1095 if (flags & CM_FLAG_NOMOUNTCHASE) {
1096 /* In this case, we should go and call cm_Dir* functions
1097 directly since the following cm_ApplyDir() function will
1105 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ,
1106 CM_DIROP_FLAG_NONE, &dirop);
1109 code = cm_BPlusDirLookup(&dirop, nnamep, &rock.fid);
1114 code = cm_DirLookup(&dirop, fnamep, &rock.fid);
1116 cm_EndDirOp(&dirop);
1126 if (code == CM_ERROR_INEXACT_MATCH && (flags & CM_FLAG_CASEFOLD)) {
1133 code = CM_ERROR_BPLUS_NOMATCH;
1139 rock.fid.cell = dscp->fid.cell;
1140 rock.fid.volume = dscp->fid.volume;
1141 rock.searchNamep = fnamep;
1142 rock.nsearchNamep = nnamep;
1143 rock.caseFold = (flags & CM_FLAG_CASEFOLD);
1144 rock.hasTilde = ((cm_ClientStrChr(cnamep, '~') != NULL) ? 1 : 0);
1146 /* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
1147 code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
1148 (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
1150 /* code == 0 means we fell off the end of the dir, while stopnow means
1151 * that we stopped early, probably because we found the entry we're
1152 * looking for. Any other non-zero code is an error.
1154 if (code && code != CM_ERROR_STOPNOW && code != CM_ERROR_BPLUS_NOMATCH) {
1155 /* if the cm_scache_t we are searching in is not a directory
1156 * we must return path not found because the error
1157 * is to describe the final component not an intermediary
1159 if (code == CM_ERROR_NOTDIR) {
1160 if (flags & CM_FLAG_CHECKPATH)
1161 code = CM_ERROR_NOSUCHPATH;
1163 code = CM_ERROR_NOSUCHFILE;
1169 getroot = (dscp==cm_data.rootSCachep) ;
1171 if (!cm_freelanceEnabled || !getroot) {
1172 if (flags & CM_FLAG_CHECKPATH)
1173 code = CM_ERROR_NOSUCHPATH;
1175 code = CM_ERROR_NOSUCHFILE;
1178 else if (!cm_ClientStrChr(cnamep, '#') &&
1179 !cm_ClientStrChr(cnamep, '%') &&
1180 cm_ClientStrCmpI(cnamep, _C("srvsvc")) &&
1181 cm_ClientStrCmpI(cnamep, _C("wkssvc")) &&
1182 cm_ClientStrCmpI(cnamep, _C("ipc$")))
1184 /* nonexistent dir on freelance root, so add it */
1185 fschar_t fullname[CELL_MAXNAMELEN + 1] = "."; /* +1 so that when we skip the . the size is still CELL_MAXNAMELEN */
1189 osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %S",
1190 osi_LogSaveClientString(afsd_logp,cnamep));
1193 * There is an ugly behavior where a share name "foo" will be searched
1194 * for as "fo". If the searched for name differs by an already existing
1195 * symlink or mount point in the Freelance directory, do not add the
1196 * new value automatically.
1200 fnlen = strlen(fnamep);
1201 if ( fnamep[fnlen-1] == '.') {
1202 fnamep[fnlen-1] = '\0';
1207 if (cnamep[0] == '.') {
1208 if (cm_GetCell_Gen(&fnamep[1], &fullname[1], CM_FLAG_CREATE)) {
1210 code = cm_FreelanceAddMount(fullname, &fullname[1], "root.cell.", 1, &rock.fid);
1211 if ( cm_FsStrCmpI(&fnamep[1], &fullname[1])) {
1213 * Do not permit symlinks that are one of:
1214 * . the cellname followed by a dot
1215 * . the cellname minus a single character
1216 * . a substring of the cellname that does not consist of full components
1218 if ( cm_strnicmp_utf8(&fnamep[1], fullname, (int)fnlen-1) == 0 &&
1219 (fnlen-1 == strlen(fullname)-1 || fullname[fnlen-1] != '.'))
1221 /* do not add; substitute fullname for the search */
1223 fnamep = malloc(strlen(fullname)+2);
1225 strncpy(&fnamep[1], fullname, strlen(fullname)+1);
1228 code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
1233 if (cm_GetCell_Gen(fnamep, fullname, CM_FLAG_CREATE)) {
1235 code = cm_FreelanceAddMount(fullname, fullname, "root.cell.", 0, &rock.fid);
1236 if ( cm_FsStrCmpI(fnamep, fullname)) {
1238 * Do not permit symlinks that are one of:
1239 * . the cellname followed by a dot
1240 * . the cellname minus a single character
1241 * . a substring of the cellname that does not consist of full components
1243 if ( cm_strnicmp_utf8(fnamep, fullname, (int)fnlen-1) == 0 &&
1244 (fnlen == strlen(fullname)-1 || fullname[fnlen] != '.'))
1246 /* do not add; substitute fullname for the search */
1248 fnamep = strdup(fullname);
1252 code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
1261 nnamep = cm_FsStringToNormStringAlloc(fnamep, -1, NULL);
1265 if (!found || code) { /* add mount point failed, so give up */
1266 if (flags & CM_FLAG_CHECKPATH)
1267 code = CM_ERROR_NOSUCHPATH;
1269 code = CM_ERROR_NOSUCHFILE;
1272 tscp = NULL; /* to force call of cm_GetSCache */
1274 if (flags & CM_FLAG_CHECKPATH)
1275 code = CM_ERROR_NOSUCHPATH;
1277 code = CM_ERROR_NOSUCHFILE;
1283 if ( !tscp ) /* we did not find it in the dnlc */
1286 code = cm_GetSCache(&rock.fid, &tscp, userp, reqp);
1290 /* tscp is now held */
1292 lock_ObtainWrite(&tscp->rw);
1293 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1294 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1296 lock_ReleaseWrite(&tscp->rw);
1297 cm_ReleaseSCache(tscp);
1300 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1301 /* tscp is now locked */
1303 if (!(flags & CM_FLAG_NOMOUNTCHASE)
1304 && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
1305 /* mount points are funny: they have a volume name to mount
1308 code = cm_ReadMountPoint(tscp, userp, reqp);
1310 code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
1312 lock_ReleaseWrite(&tscp->rw);
1313 cm_ReleaseSCache(tscp);
1320 lock_ReleaseWrite(&tscp->rw);
1323 /* copy back pointer */
1326 /* insert scache in dnlc */
1327 if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
1328 /* lock the directory entry to prevent racing callback revokes */
1329 lock_ObtainRead(&dscp->rw);
1330 if ( dscp->cbServerp != NULL && dscp->cbExpires > 0 ) {
1331 /* TODO: reuse nnamep from above */
1334 nnamep = cm_ClientStringToNormStringAlloc(cnamep, -1, NULL);
1336 cm_dnlcEnter(dscp, nnamep, tscp);
1338 lock_ReleaseRead(&dscp->rw);
1355 int cm_ExpandSysName(clientchar_t *inp, clientchar_t *outp, long outSizeCch, unsigned int index)
1360 tp = cm_ClientStrRChr(inp, '@');
1362 return 0; /* no @sys */
1364 if (cm_ClientStrCmp(tp, _C("@sys")) != 0)
1365 return 0; /* no @sys */
1367 /* caller just wants to know if this is a valid @sys type of name */
1371 if (index >= cm_sysNameCount)
1374 /* otherwise generate the properly expanded @sys name */
1375 prefixCount = (int)(tp - inp);
1377 cm_ClientStrCpyN(outp, outSizeCch, inp, prefixCount); /* copy out "a." from "a.@sys" */
1378 outp[prefixCount] = 0; /* null terminate the "a." */
1379 cm_ClientStrCat(outp, outSizeCch, cm_sysNameList[index]);/* append i386_nt40 */
1383 long cm_EvaluateVolumeReference(clientchar_t * namep, long flags, cm_user_t * userp,
1384 cm_req_t *reqp, cm_scache_t ** outScpp)
1386 afs_uint32 code = 0;
1387 fschar_t cellName[CELL_MAXNAMELEN];
1388 fschar_t volumeName[VL_MAXNAMELEN];
1392 fschar_t * fnamep = NULL;
1394 cm_cell_t * cellp = NULL;
1395 cm_volume_t * volp = NULL;
1399 int mountType = RWVOL;
1401 osi_Log1(afsd_logp, "cm_EvaluateVolumeReference for string [%S]",
1402 osi_LogSaveClientString(afsd_logp, namep));
1404 if (cm_ClientStrCmpNI(namep, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH) != 0) {
1405 goto _exit_invalid_path;
1408 /* namep is assumed to look like the following:
1410 @vol:<cellname>%<volume>\0
1412 @vol:<cellname>#<volume>\0
1416 fnamep = cm_ClientStringToFsStringAlloc(namep, -1, NULL);
1417 cp = fnamep + CM_PREFIX_VOL_CCH; /* cp points to cell name, hopefully */
1418 tp = cm_FsStrChr(cp, '%');
1420 tp = cm_FsStrChr(cp, '#');
1422 (len = tp - cp) == 0 ||
1423 len > CELL_MAXNAMELEN)
1424 goto _exit_invalid_path;
1425 cm_FsStrCpyN(cellName, lengthof(cellName), cp, len);
1430 cp = tp+1; /* cp now points to volume, supposedly */
1431 cm_FsStrCpy(volumeName, lengthof(volumeName), cp);
1433 /* OK, now we have the cell and the volume */
1434 osi_Log2(afsd_logp, " Found cell [%s] and volume [%s]",
1435 osi_LogSaveFsString(afsd_logp, cellName),
1436 osi_LogSaveFsString(afsd_logp, volumeName));
1438 cellp = cm_GetCell(cellName, CM_FLAG_CREATE);
1439 if (cellp == NULL) {
1440 goto _exit_invalid_path;
1443 len = cm_FsStrLen(volumeName);
1444 if (len >= 8 && cm_FsStrCmp(volumeName + len - 7, ".backup") == 0)
1446 else if (len >= 10 &&
1447 cm_FsStrCmp(volumeName + len - 9, ".readonly") == 0)
1452 if (cm_VolNameIsID(volumeName)) {
1453 code = cm_FindVolumeByID(cellp, atoi(volumeName), userp, reqp,
1454 CM_GETVOL_FLAG_CREATE, &volp);
1456 code = cm_FindVolumeByName(cellp, volumeName, userp, reqp,
1457 CM_GETVOL_FLAG_CREATE, &volp);
1463 if (volType == BACKVOL)
1464 volume = volp->vol[BACKVOL].ID;
1465 else if (volType == ROVOL ||
1466 (volType == RWVOL && mountType == ROVOL && volp->vol[ROVOL].ID != 0))
1467 volume = volp->vol[ROVOL].ID;
1469 volume = volp->vol[RWVOL].ID;
1471 cm_SetFid(&fid, cellp->cellID, volume, 1, 1);
1473 code = cm_GetSCache(&fid, outScpp, userp, reqp);
1486 if (flags & CM_FLAG_CHECKPATH)
1487 return CM_ERROR_NOSUCHPATH;
1489 return CM_ERROR_NOSUCHFILE;
1492 #ifdef DEBUG_REFCOUNT
1493 long cm_LookupDbg(cm_scache_t *dscp, clientchar_t *namep, long flags, cm_user_t *userp,
1494 cm_req_t *reqp, cm_scache_t **outScpp, char * file, long line)
1496 long cm_Lookup(cm_scache_t *dscp, clientchar_t *namep, long flags, cm_user_t *userp,
1497 cm_req_t *reqp, cm_scache_t **outScpp)
1501 clientchar_t tname[AFSPATHMAX];
1502 int sysNameIndex = 0;
1503 cm_scache_t *scp = NULL;
1505 #ifdef DEBUG_REFCOUNT
1506 afsi_log("%s:%d cm_Lookup dscp 0x%p ref %d", file, line, dscp, dscp->refCount, file, line);
1507 osi_Log2(afsd_logp, "cm_Lookup dscp 0x%p ref %d", dscp, dscp->refCount);
1510 if ( cm_ClientStrCmpI(namep,_C(SMB_IOCTL_FILENAME_NOSLASH)) == 0 ) {
1511 if (flags & CM_FLAG_CHECKPATH)
1512 return CM_ERROR_NOSUCHPATH;
1514 return CM_ERROR_NOSUCHFILE;
1517 if (dscp == cm_data.rootSCachep &&
1518 cm_ClientStrCmpNI(namep, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH) == 0) {
1519 return cm_EvaluateVolumeReference(namep, flags, userp, reqp, outScpp);
1522 if (cm_ExpandSysName(namep, NULL, 0, 0) > 0) {
1523 for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
1524 code = cm_ExpandSysName(namep, tname, lengthof(tname), sysNameIndex);
1526 code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
1527 #ifdef DEBUG_REFCOUNT
1528 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);
1529 osi_Log3(afsd_logp, "cm_LookupInternal (1) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1537 cm_ReleaseSCache(scp);
1541 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1542 #ifdef DEBUG_REFCOUNT
1543 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);
1544 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1551 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1552 #ifdef DEBUG_REFCOUNT
1553 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);
1554 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1560 /* None of the possible sysName expansions could be found */
1561 if (flags & CM_FLAG_CHECKPATH)
1562 return CM_ERROR_NOSUCHPATH;
1564 return CM_ERROR_NOSUCHFILE;
1567 /*! \brief Unlink a file name
1569 Encapsulates a call to RXAFS_RemoveFile().
1571 \param[in] dscp cm_scache_t pointing at the directory containing the
1572 name to be unlinked.
1574 \param[in] fnamep Original name to be unlinked. This is the
1575 name that will be passed into the RXAFS_RemoveFile() call.
1576 This parameter is optional. If not provided, the value will
1579 \param[in] came Client name to be unlinked. This name will be used
1580 to update the local directory caches.
1582 \param[in] userp cm_user_t for the request.
1584 \param[in] reqp Request tracker.
1587 long cm_Unlink(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t * cnamep,
1588 cm_user_t *userp, cm_req_t *reqp)
1594 AFSFetchStatus newDirStatus;
1596 struct rx_connection * rxconnp;
1598 cm_scache_t *scp = NULL;
1599 int free_fnamep = FALSE;
1601 memset(&volSync, 0, sizeof(volSync));
1603 if (fnamep == NULL) {
1606 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ,
1607 CM_DIROP_FLAG_NONE, &dirop);
1609 code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
1612 cm_EndDirOp(&dirop);
1619 #ifdef AFS_FREELANCE_CLIENT
1620 if (cm_freelanceEnabled && dscp == cm_data.rootSCachep) {
1621 /* deleting a mount point from the root dir. */
1622 code = cm_FreelanceRemoveMount(fnamep);
1627 code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
1629 /* make sure we don't screw up the dir status during the merge */
1630 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE,
1631 CM_DIROP_FLAG_NONE, &dirop);
1633 lock_ObtainWrite(&dscp->rw);
1634 sflags = CM_SCACHESYNC_STOREDATA;
1635 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1636 lock_ReleaseWrite(&dscp->rw);
1638 cm_EndDirOp(&dirop);
1643 afsFid.Volume = dscp->fid.volume;
1644 afsFid.Vnode = dscp->fid.vnode;
1645 afsFid.Unique = dscp->fid.unique;
1647 osi_Log1(afsd_logp, "CALL RemoveFile scp 0x%p", dscp);
1649 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
1653 rxconnp = cm_GetRxConn(connp);
1654 code = RXAFS_RemoveFile(rxconnp, &afsFid, fnamep,
1655 &newDirStatus, &volSync);
1656 rx_PutConnection(rxconnp);
1658 } while (cm_Analyze(connp, userp, reqp, &dscp->fid, &volSync, NULL, NULL, code));
1659 code = cm_MapRPCError(code, reqp);
1662 osi_Log1(afsd_logp, "CALL RemoveFile FAILURE, code 0x%x", code);
1664 osi_Log0(afsd_logp, "CALL RemoveFile SUCCESS");
1667 lock_ObtainWrite(&dirop.scp->dirlock);
1668 dirop.lockType = CM_DIRLOCK_WRITE;
1670 lock_ObtainWrite(&dscp->rw);
1671 cm_dnlcRemove(dscp, cnamep);
1672 cm_SyncOpDone(dscp, NULL, sflags);
1674 cm_MergeStatus(NULL, dscp, &newDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
1675 } else if (code == CM_ERROR_NOSUCHFILE) {
1676 /* windows would not have allowed the request to delete the file
1677 * if it did not believe the file existed. therefore, we must
1678 * have an inconsistent view of the world.
1680 dscp->cbServerp = NULL;
1682 lock_ReleaseWrite(&dscp->rw);
1684 if (code == 0 && cm_CheckDirOpForSingleChange(&dirop) && cnamep) {
1685 cm_DirDeleteEntry(&dirop, fnamep);
1687 cm_BPlusDirDeleteEntry(&dirop, cnamep);
1690 cm_EndDirOp(&dirop);
1693 cm_ReleaseSCache(scp);
1695 lock_ObtainWrite(&scp->rw);
1696 if (--scp->linkCount == 0)
1697 scp->flags |= CM_SCACHEFLAG_DELETED;
1698 cm_DiscardSCache(scp);
1699 lock_ReleaseWrite(&scp->rw);
1710 /* called with a write locked vnode, and fills in the link info.
1711 * returns this the vnode still write locked.
1713 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1720 lock_AssertWrite(&linkScp->rw);
1721 if (!linkScp->mountPointStringp[0]) {
1723 #ifdef AFS_FREELANCE_CLIENT
1724 /* File servers do not have data for freelance entries */
1725 if (cm_freelanceEnabled &&
1726 linkScp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
1727 linkScp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
1729 code = cm_FreelanceFetchMountPointString(linkScp);
1731 #endif /* AFS_FREELANCE_CLIENT */
1733 /* read the link data from the file server*/
1734 lock_ReleaseWrite(&linkScp->rw);
1735 thyper.LowPart = thyper.HighPart = 0;
1736 code = buf_Get(linkScp, &thyper, reqp, &bufp);
1737 lock_ObtainWrite(&linkScp->rw);
1741 code = cm_SyncOp(linkScp, bufp, userp, reqp, 0,
1742 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1747 cm_SyncOpDone(linkScp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1749 if (cm_HaveBuffer(linkScp, bufp, 0))
1752 code = cm_GetBuffer(linkScp, bufp, NULL, userp, reqp);
1757 } /* while loop to get the data */
1759 /* now if we still have no link read in,
1760 * copy the data from the buffer */
1761 if ((temp = linkScp->length.LowPart) >= MOUNTPOINTLEN) {
1763 return CM_ERROR_TOOBIG;
1766 /* otherwise, it fits; make sure it is still null (could have
1767 * lost race with someone else referencing this link above),
1768 * and if so, copy in the data.
1770 if (!linkScp->mountPointStringp[0]) {
1771 strncpy(linkScp->mountPointStringp, bufp->datap, temp);
1772 linkScp->mountPointStringp[temp] = 0; /* null terminate */
1777 if ( !strnicmp(linkScp->mountPointStringp, "msdfs:", strlen("msdfs:")) )
1778 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1780 } /* don't have sym link contents cached */
1785 /* called with a held vnode and a path suffix, with the held vnode being a
1786 * symbolic link. Our goal is to generate a new path to interpret, and return
1787 * this new path in newSpaceBufferp. If the new vnode is relative to a dir
1788 * other than the directory containing the symbolic link, then the new root is
1789 * returned in *newRootScpp, otherwise a null is returned there.
1791 long cm_AssembleLink(cm_scache_t *linkScp, fschar_t *pathSuffixp,
1792 cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1793 cm_user_t *userp, cm_req_t *reqp)
1800 *newRootScpp = NULL;
1801 *newSpaceBufferp = NULL;
1803 lock_ObtainWrite(&linkScp->rw);
1804 code = cm_HandleLink(linkScp, userp, reqp);
1808 /* if we may overflow the buffer, bail out; buffer is signficantly
1809 * bigger than max path length, so we don't really have to worry about
1810 * being a little conservative here.
1812 if (cm_FsStrLen(linkScp->mountPointStringp) + cm_FsStrLen(pathSuffixp) + 2
1813 >= CM_UTILS_SPACESIZE) {
1814 code = CM_ERROR_TOOBIG;
1818 tsp = cm_GetSpace();
1819 linkp = linkScp->mountPointStringp;
1820 if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1821 if (strlen(linkp) > cm_mountRootLen)
1822 StringCbCopyA((char *) tsp->data, sizeof(tsp->data), linkp+cm_mountRootLen+1);
1825 *newRootScpp = cm_RootSCachep(userp, reqp);
1826 cm_HoldSCache(*newRootScpp);
1827 } else if (linkp[0] == '\\' && linkp[1] == '\\') {
1828 if (!strnicmp(&linkp[2], cm_NetbiosName, (len = (long)strlen(cm_NetbiosName))))
1830 char * p = &linkp[len + 3];
1831 if (strnicmp(p, "all", 3) == 0)
1834 StringCbCopyA(tsp->data, sizeof(tsp->data), p);
1835 for (p = tsp->data; *p; p++) {
1839 *newRootScpp = cm_RootSCachep(userp, reqp);
1840 cm_HoldSCache(*newRootScpp);
1842 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1843 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1844 code = CM_ERROR_PATH_NOT_COVERED;
1846 } else if ( linkScp->fileType == CM_SCACHETYPE_DFSLINK ||
1847 !strnicmp(linkp, "msdfs:", (len = (long)strlen("msdfs:"))) ) {
1848 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1849 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1850 code = CM_ERROR_PATH_NOT_COVERED;
1851 } else if (*linkp == '\\' || *linkp == '/') {
1853 /* formerly, this was considered to be from the AFS root,
1854 * but this seems to create problems. instead, we will just
1855 * reject the link */
1856 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp+1);
1857 *newRootScpp = cm_RootSCachep(userp, reqp);
1858 cm_HoldSCache(*newRootScpp);
1860 /* we still copy the link data into the response so that
1861 * the user can see what the link points to
1863 linkScp->fileType = CM_SCACHETYPE_INVALID;
1864 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1865 code = CM_ERROR_NOSUCHPATH;
1868 /* a relative link */
1869 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1871 if (pathSuffixp[0] != 0) { /* if suffix string is non-null */
1872 StringCchCatA(tsp->data,lengthof(tsp->data), "\\");
1873 StringCchCatA(tsp->data,lengthof(tsp->data), pathSuffixp);
1877 clientchar_t * cpath = cm_FsStringToClientStringAlloc(tsp->data, -1, NULL);
1878 if (cpath != NULL) {
1879 cm_ClientStrCpy(tsp->wdata, lengthof(tsp->wdata), cpath);
1881 *newSpaceBufferp = tsp;
1883 code = CM_ERROR_NOSUCHPATH;
1890 if (code == CM_ERROR_PATH_NOT_COVERED && reqp->tidPathp && reqp->relPathp) {
1891 cm_VolStatus_Notify_DFS_Mapping(linkScp, reqp->tidPathp, reqp->relPathp);
1896 lock_ReleaseWrite(&linkScp->rw);
1899 #ifdef DEBUG_REFCOUNT
1900 long cm_NameIDbg(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
1901 cm_user_t *userp, clientchar_t *tidPathp, cm_req_t *reqp,
1902 cm_scache_t **outScpp,
1903 char * file, long line)
1905 long cm_NameI(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
1906 cm_user_t *userp, clientchar_t *tidPathp,
1907 cm_req_t *reqp, cm_scache_t **outScpp)
1911 clientchar_t *tp; /* ptr moving through input buffer */
1912 clientchar_t tc; /* temp char */
1913 int haveComponent; /* has new component started? */
1914 clientchar_t component[AFSPATHMAX]; /* this is the new component */
1915 clientchar_t *cp; /* component name being assembled */
1916 cm_scache_t *tscp; /* current location in the hierarchy */
1917 cm_scache_t *nscp; /* next dude down */
1918 cm_scache_t *dirScp; /* last dir we searched */
1919 cm_scache_t *linkScp; /* new root for the symlink we just
1921 cm_space_t *psp; /* space for current path, if we've hit
1923 cm_space_t *tempsp; /* temp vbl */
1924 clientchar_t *restp; /* rest of the pathname to interpret */
1925 int symlinkCount; /* count of # of symlinks traversed */
1926 int extraFlag; /* avoid chasing mt pts for dir cmd */
1927 int phase = 1; /* 1 = tidPathp, 2 = pathp */
1928 #define MAX_FID_COUNT 512
1929 cm_fid_t fids[MAX_FID_COUNT]; /* array of fids processed in this path walk */
1930 int fid_count = 0; /* number of fids processed in this path walk */
1935 #ifdef DEBUG_REFCOUNT
1936 afsi_log("%s:%d cm_NameI rootscp 0x%p ref %d", file, line, rootSCachep, rootSCachep->refCount);
1937 osi_Log4(afsd_logp,"cm_NameI rootscp 0x%p path %S tidpath %S flags 0x%x",
1938 rootSCachep, pathp ? pathp : L"<NULL>", tidPathp ? tidPathp : L"<NULL>",
1953 cm_HoldSCache(tscp);
1961 /* map Unix slashes into DOS ones so we can interpret Unix
1967 if (!haveComponent) {
1970 } else if (tc == 0) {
1984 /* we have a component here */
1985 if (tc == 0 || tc == '\\') {
1986 /* end of the component; we're at the last
1987 * component if tc == 0. However, if the last
1988 * is a symlink, we have more to do.
1990 *cp++ = 0; /* add null termination */
1992 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
1993 extraFlag = CM_FLAG_NOMOUNTCHASE;
1994 code = cm_Lookup(tscp, component,
1996 userp, reqp, &nscp);
1999 if (!cm_ClientStrCmp(component,_C("..")) ||
2000 !cm_ClientStrCmp(component,_C("."))) {
2002 * roll back the fid list until we find the
2003 * fid that matches where we are now. Its not
2004 * necessarily one or two fids because they
2005 * might have been symlinks or mount points or
2006 * both that were crossed.
2008 for ( i=fid_count-1; i>=0; i--) {
2009 if (!cm_FidCmp(&nscp->fid, &fids[i]))
2014 /* add the new fid to the list */
2015 if (fid_count == MAX_FID_COUNT) {
2016 code = CM_ERROR_TOO_MANY_SYMLINKS;
2017 cm_ReleaseSCache(nscp);
2021 fids[fid_count++] = nscp->fid;
2026 cm_ReleaseSCache(tscp);
2028 cm_ReleaseSCache(dirScp);
2031 if ((code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH) &&
2032 tscp->fileType == CM_SCACHETYPE_SYMLINK) {
2033 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_NOSUCHPATH");
2034 return CM_ERROR_NOSUCHPATH;
2036 osi_Log1(afsd_logp,"cm_NameI code 0x%x", code);
2041 haveComponent = 0; /* component done */
2043 cm_ReleaseSCache(dirScp);
2044 dirScp = tscp; /* for some symlinks */
2045 tscp = nscp; /* already held */
2047 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
2050 cm_ReleaseSCache(dirScp);
2056 /* now, if tscp is a symlink, we should follow it and
2057 * assemble the path again.
2059 lock_ObtainWrite(&tscp->rw);
2060 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
2061 CM_SCACHESYNC_GETSTATUS
2062 | CM_SCACHESYNC_NEEDCALLBACK);
2064 lock_ReleaseWrite(&tscp->rw);
2065 cm_ReleaseSCache(tscp);
2068 cm_ReleaseSCache(dirScp);
2073 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2075 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
2076 /* this is a symlink; assemble a new buffer */
2077 lock_ReleaseWrite(&tscp->rw);
2078 if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
2079 cm_ReleaseSCache(tscp);
2082 cm_ReleaseSCache(dirScp);
2087 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_TOO_MANY_SYMLINKS");
2088 return CM_ERROR_TOO_MANY_SYMLINKS;
2098 /* TODO: make this better */
2099 frestp = cm_ClientStringToFsStringAlloc(restp, -1, NULL);
2100 code = cm_AssembleLink(tscp, frestp, &linkScp, &tempsp, userp, reqp);
2104 if (code == 0 && linkScp != NULL) {
2105 if (linkScp == cm_data.rootSCachep) {
2109 for ( i=0; i<fid_count; i++) {
2110 if ( !cm_FidCmp(&linkScp->fid, &fids[i]) ) {
2111 code = CM_ERROR_TOO_MANY_SYMLINKS;
2112 cm_ReleaseSCache(linkScp);
2118 if (i == fid_count && fid_count < MAX_FID_COUNT) {
2119 fids[fid_count++] = linkScp->fid;
2124 /* something went wrong */
2125 cm_ReleaseSCache(tscp);
2128 cm_ReleaseSCache(dirScp);
2134 /* otherwise, tempsp has the new path,
2135 * and linkScp is the new root from
2136 * which to interpret that path.
2137 * Continue with the namei processing,
2138 * also doing the bookkeeping for the
2139 * space allocation and tracking the
2140 * vnode reference counts.
2146 cm_ReleaseSCache(tscp);
2151 * now, if linkScp is null, that's
2152 * AssembleLink's way of telling us that
2153 * the sym link is relative to the dir
2154 * containing the link. We have a ref
2155 * to it in dirScp, and we hold it now
2156 * and reuse it as the new spot in the
2164 /* not a symlink, we may be done */
2165 lock_ReleaseWrite(&tscp->rw);
2173 cm_ReleaseSCache(dirScp);
2181 cm_ReleaseSCache(dirScp);
2184 } /* end of a component */
2187 } /* we have a component */
2188 } /* big while loop over all components */
2192 cm_ReleaseSCache(dirScp);
2198 cm_ReleaseSCache(tscp);
2200 #ifdef DEBUG_REFCOUNT
2201 afsi_log("%s:%d cm_NameI code 0x%x outScpp 0x%p ref %d", file, line, code, *outScpp, (*outScpp) ? (*outScpp)->refCount : 0);
2203 osi_Log2(afsd_logp,"cm_NameI code 0x%x outScpp 0x%p", code, *outScpp);
2207 /* called with a dir, and a vnode within the dir that happens to be a symlink.
2208 * We chase the link, and return a held pointer to the target, if it exists,
2209 * in *outScpp. If we succeed, we return 0, otherwise we return an error code
2210 * and do not hold or return a target vnode.
2212 * This is very similar to calling cm_NameI with the last component of a name,
2213 * which happens to be a symlink, except that we've already passed by the name.
2215 * This function is typically called by the directory listing functions, which
2216 * encounter symlinks but need to return the proper file length so programs
2217 * like "more" work properly when they make use of the attributes retrieved from
2220 * The input vnode should not be locked when this function is called.
2222 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
2223 cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
2227 cm_scache_t *newRootScp;
2231 osi_Log1(afsd_logp, "Evaluating symlink scp 0x%p", linkScp);
2233 code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
2237 /* now, if newRootScp is NULL, we're really being told that the symlink
2238 * is relative to the current directory (dscp).
2240 if (newRootScp == NULL) {
2242 cm_HoldSCache(dscp);
2245 code = cm_NameI(newRootScp, spacep->wdata,
2246 CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
2247 userp, NULL, reqp, outScpp);
2249 if (code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH)
2250 code = CM_ERROR_NOSUCHPATH;
2252 /* this stuff is allocated no matter what happened on the namei call,
2254 cm_FreeSpace(spacep);
2255 cm_ReleaseSCache(newRootScp);
2257 if (linkScp == *outScpp) {
2258 cm_ReleaseSCache(*outScpp);
2260 code = CM_ERROR_NOSUCHPATH;
2266 /* for a given entry, make sure that it isn't in the stat cache, and then
2267 * add it to the list of file IDs to be obtained.
2269 * Don't bother adding it if we already have a vnode. Note that the dir
2270 * is locked, so we have to be careful checking the vnode we're thinking of
2271 * processing, to avoid deadlocks.
2273 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
2284 /* Don't overflow bsp. */
2285 if (bsp->counter >= CM_BULKMAX)
2286 return CM_ERROR_STOPNOW;
2288 thyper.LowPart = cm_data.buf_blockSize;
2289 thyper.HighPart = 0;
2290 thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
2292 /* thyper is now the first byte past the end of the record we're
2293 * interested in, and bsp->bufOffset is the first byte of the record
2294 * we're interested in.
2295 * Skip data in the others.
2298 if (LargeIntegerLessThan(*offp, bsp->bufOffset))
2300 if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
2301 return CM_ERROR_STOPNOW;
2302 if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
2305 cm_SetFid(&tfid, scp->fid.cell, scp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
2306 tscp = cm_FindSCache(&tfid);
2308 if (lock_TryWrite(&tscp->rw)) {
2309 /* we have an entry that we can look at */
2310 if (!(tscp->flags & CM_SCACHEFLAG_EACCESS) && cm_HaveCallback(tscp)) {
2311 /* we have a callback on it. Don't bother
2312 * fetching this stat entry, since we're happy
2313 * with the info we have.
2315 lock_ReleaseWrite(&tscp->rw);
2316 cm_ReleaseSCache(tscp);
2319 lock_ReleaseWrite(&tscp->rw);
2321 cm_ReleaseSCache(tscp);
2324 #ifdef AFS_FREELANCE_CLIENT
2325 // yj: if this is a mountpoint under root.afs then we don't want it
2326 // to be bulkstat-ed, instead, we call getSCache directly and under
2327 // getSCache, it is handled specially.
2328 if ( cm_freelanceEnabled &&
2329 tfid.cell==AFS_FAKE_ROOT_CELL_ID &&
2330 tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
2331 !(tfid.vnode==0x1 && tfid.unique==0x1) )
2333 osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
2334 return cm_GetSCache(&tfid, &tscp, NULL, NULL);
2336 #endif /* AFS_FREELANCE_CLIENT */
2339 bsp->fids[i].Volume = scp->fid.volume;
2340 bsp->fids[i].Vnode = tfid.vnode;
2341 bsp->fids[i].Unique = tfid.unique;
2346 cm_TryBulkStatRPC(cm_scache_t *dscp, cm_bulkStat_t *bbp, cm_user_t *userp, cm_req_t *reqp)
2349 AFSCBFids fidStruct;
2350 AFSBulkStats statStruct;
2352 AFSCBs callbackStruct;
2355 cm_callbackRequest_t cbReq;
2361 struct rx_connection * rxconnp;
2362 int inlinebulk; /* Did we use InlineBulkStatus RPC or not? */
2364 memset(&volSync, 0, sizeof(volSync));
2366 /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
2367 * make the calls to create the entries. Handle AFSCBMAX files at a
2370 for (filex = 0; filex < bbp->counter; filex += filesThisCall) {
2371 filesThisCall = bbp->counter - filex;
2372 if (filesThisCall > AFSCBMAX)
2373 filesThisCall = AFSCBMAX;
2375 fidStruct.AFSCBFids_len = filesThisCall;
2376 fidStruct.AFSCBFids_val = &bbp->fids[filex];
2377 statStruct.AFSBulkStats_len = filesThisCall;
2378 statStruct.AFSBulkStats_val = &bbp->stats[filex];
2379 callbackStruct.AFSCBs_len = filesThisCall;
2380 callbackStruct.AFSCBs_val = &bbp->callbacks[filex];
2381 cm_StartCallbackGrantingCall(NULL, &cbReq);
2382 osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
2385 * Whenever cm_Analyze is called for a RXAFS_ RPC there must
2386 * be a FID provided. However, the error code from RXAFS_BulkStatus
2387 * or RXAFS_InlinkBulkStatus does not apply to any FID. Therefore,
2388 * we generate an invalid FID to match with the RPC error.
2390 cm_SetFid(&tfid, dscp->fid.cell, dscp->fid.volume, 0, 0);
2395 code = cm_ConnFromFID(&tfid, userp, reqp, &connp);
2399 rxconnp = cm_GetRxConn(connp);
2400 if (!(connp->serverp->flags & CM_SERVERFLAG_NOINLINEBULK)) {
2401 code = RXAFS_InlineBulkStatus(rxconnp, &fidStruct,
2402 &statStruct, &callbackStruct, &volSync);
2403 if (code == RXGEN_OPCODE) {
2404 cm_SetServerNoInlineBulk(connp->serverp, 0);
2410 code = RXAFS_BulkStatus(rxconnp, &fidStruct,
2411 &statStruct, &callbackStruct, &volSync);
2413 rx_PutConnection(rxconnp);
2416 * If InlineBulk RPC was called and it succeeded,
2417 * then pull out the return code from the status info
2418 * and use it for cm_Analyze so that we can failover to other
2419 * .readonly volume instances. But only do it for errors that
2420 * are volume global.
2422 if (inlinebulk && code == 0 && (&bbp->stats[0])->errorCode) {
2423 osi_Log1(afsd_logp, "cm_TryBulkStat inline-bulk stat error: %d",
2424 (&bbp->stats[0])->errorCode);
2425 switch ((&bbp->stats[0])->errorCode) {
2434 code = (&bbp->stats[0])->errorCode;
2437 /* Rx and Rxkad errors are volume global */
2438 if ( (&bbp->stats[0])->errorCode >= -64 && (&bbp->stats[0])->errorCode < 0 ||
2439 (&bbp->stats[0])->errorCode >= ERROR_TABLE_BASE_RXK && (&bbp->stats[0])->errorCode < ERROR_TABLE_BASE_RXK + 256)
2440 code = (&bbp->stats[0])->errorCode;
2443 } while (cm_Analyze(connp, userp, reqp, &tfid, &volSync, NULL, &cbReq, code));
2444 code = cm_MapRPCError(code, reqp);
2447 * might as well quit on an error, since we're not going to do
2448 * much better on the next immediate call, either.
2451 osi_Log2(afsd_logp, "CALL %sBulkStatus FAILURE code 0x%x",
2452 inlinebulk ? "Inline" : "", code);
2453 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2458 * The bulk RPC has succeeded or at least not failed with a
2459 * volume global error result. For items that have inlineBulk
2460 * errors we must call cm_Analyze in order to perform required
2461 * logging of errors.
2463 * If the RPC was not inline bulk or the entry either has no error
2464 * the status must be merged.
2466 osi_Log1(afsd_logp, "CALL %sBulkStatus SUCCESS", inlinebulk ? "Inline" : "");
2468 for (i = 0; i<filesThisCall; i++) {
2470 cm_SetFid(&tfid, dscp->fid.cell, bbp->fids[j].Volume, bbp->fids[j].Vnode, bbp->fids[j].Unique);
2472 if (inlinebulk && (&bbp->stats[j])->errorCode) {
2473 cm_req_t treq = *reqp;
2474 cm_Analyze(NULL, userp, &treq, &tfid, &volSync, NULL, &cbReq, (&bbp->stats[j])->errorCode);
2476 code = cm_GetSCache(&tfid, &scp, userp, reqp);
2481 * otherwise, if this entry has no callback info,
2482 * merge in this. If there is existing callback info
2483 * we skip the merge because the existing data must be
2484 * current (we have a callback) and the response from
2485 * a non-inline bulk rpc might actually be wrong.
2487 * now, we have to be extra paranoid on merging in this
2488 * information, since we didn't use cm_SyncOp before
2489 * starting the fetch to make sure that no bad races
2490 * were occurring. Specifically, we need to make sure
2491 * we don't obliterate any newer information in the
2492 * vnode than have here.
2494 * Right now, be pretty conservative: if there's a
2495 * callback or a pending call, skip it.
2496 * However, if the prior attempt to obtain status
2497 * was refused access or the volume is .readonly,
2498 * take the data in any case since we have nothing
2499 * better for the in flight directory enumeration that
2500 * resulted in this function being called.
2502 lock_ObtainRead(&scp->rw);
2503 if ((scp->cbServerp == NULL &&
2504 !(scp->flags & (CM_SCACHEFLAG_FETCHING | CM_SCACHEFLAG_STORING | CM_SCACHEFLAG_SIZESTORING))) ||
2505 (scp->flags & CM_SCACHEFLAG_PURERO) ||
2506 (scp->flags & CM_SCACHEFLAG_EACCESS))
2508 lock_ConvertRToW(&scp->rw);
2509 cm_EndCallbackGrantingCall(scp, &cbReq,
2512 CM_CALLBACK_MAINTAINCOUNT);
2513 cm_MergeStatus(dscp, scp, &bbp->stats[j], &volSync, userp, reqp, 0);
2514 lock_ReleaseWrite(&scp->rw);
2516 lock_ReleaseRead(&scp->rw);
2518 cm_ReleaseSCache(scp);
2520 } /* all files in the response */
2521 /* now tell it to drop the count,
2522 * after doing the vnode processing above */
2523 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2524 } /* while there are still more files to process */
2529 /* called with a write locked scp and a pointer to a buffer. Make bulk stat
2530 * calls on all undeleted files in the page of the directory specified.
2533 cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
2539 osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%p", dscp);
2541 /* should be on a buffer boundary */
2542 osi_assertx((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0, "invalid offset");
2544 bbp = malloc(sizeof(cm_bulkStat_t));
2545 memset(bbp, 0, sizeof(cm_bulkStat_t));
2546 bbp->bufOffset = *offsetp;
2548 lock_ReleaseWrite(&dscp->rw);
2549 /* first, assemble the file IDs we need to stat */
2550 code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) bbp, offsetp, userp, reqp, NULL);
2552 /* if we failed, bail out early */
2553 if (code && code != CM_ERROR_STOPNOW) {
2555 lock_ObtainWrite(&dscp->rw);
2559 code = cm_TryBulkStatRPC(dscp, bbp, userp, reqp);
2560 osi_Log1(afsd_logp, "END cm_TryBulkStat code 0x%x", code);
2562 lock_ObtainWrite(&dscp->rw);
2567 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
2571 /* initialize store back mask as inexpensive local variable */
2573 memset(statusp, 0, sizeof(AFSStoreStatus));
2575 /* copy out queued info from scache first, if scp passed in */
2577 if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
2578 statusp->ClientModTime = scp->clientModTime;
2579 mask |= AFS_SETMODTIME;
2580 scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
2585 /* now add in our locally generated request */
2586 if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
2587 statusp->ClientModTime = attrp->clientModTime;
2588 mask |= AFS_SETMODTIME;
2590 if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
2591 statusp->UnixModeBits = attrp->unixModeBits;
2592 mask |= AFS_SETMODE;
2594 if (attrp->mask & CM_ATTRMASK_OWNER) {
2595 statusp->Owner = attrp->owner;
2596 mask |= AFS_SETOWNER;
2598 if (attrp->mask & CM_ATTRMASK_GROUP) {
2599 statusp->Group = attrp->group;
2600 mask |= AFS_SETGROUP;
2603 statusp->Mask = mask;
2606 /* set the file size, and make sure that all relevant buffers have been
2607 * truncated. Ensure that any partially truncated buffers have been zeroed
2608 * to the end of the buffer.
2610 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
2616 /* start by locking out buffer creation */
2617 lock_ObtainWrite(&scp->bufCreateLock);
2619 /* verify that this is a file, not a dir or a symlink */
2620 lock_ObtainWrite(&scp->rw);
2621 code = cm_SyncOp(scp, NULL, userp, reqp, 0,
2622 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2625 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2627 if (scp->fileType != CM_SCACHETYPE_FILE) {
2628 code = CM_ERROR_ISDIR;
2633 if (LargeIntegerLessThan(*sizep, scp->length))
2638 lock_ReleaseWrite(&scp->rw);
2640 /* can't hold scp->rw lock here, since we may wait for a storeback to
2641 * finish if the buffer package is cleaning a buffer by storing it to
2645 buf_Truncate(scp, userp, reqp, sizep);
2647 /* now ensure that file length is short enough, and update truncPos */
2648 lock_ObtainWrite(&scp->rw);
2650 /* make sure we have a callback (so we have the right value for the
2651 * length), and wait for it to be safe to do a truncate.
2653 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
2654 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2655 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2657 /* If we only have 'i' bits, then we should still be able to set
2658 the size of a file we created. */
2659 if (code == CM_ERROR_NOACCESS && scp->creator == userp) {
2660 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
2661 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2662 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2668 if (LargeIntegerLessThan(*sizep, scp->length)) {
2669 /* a real truncation. If truncPos is not set yet, or is bigger
2670 * than where we're truncating the file, set truncPos to this
2675 if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
2676 || LargeIntegerLessThan(*sizep, scp->length)) {
2678 scp->truncPos = *sizep;
2679 scp->mask |= CM_SCACHEMASK_TRUNCPOS;
2681 /* in either case, the new file size has been changed */
2682 scp->length = *sizep;
2683 scp->mask |= CM_SCACHEMASK_LENGTH;
2685 else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
2686 /* really extending the file */
2687 scp->length = *sizep;
2688 scp->mask |= CM_SCACHEMASK_LENGTH;
2691 /* done successfully */
2694 cm_SyncOpDone(scp, NULL,
2695 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2696 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2699 lock_ReleaseWrite(&scp->rw);
2700 lock_ReleaseWrite(&scp->bufCreateLock);
2705 /* set the file size or other attributes (but not both at once) */
2706 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
2710 AFSFetchStatus afsOutStatus;
2714 AFSStoreStatus afsInStatus;
2715 struct rx_connection * rxconnp;
2717 memset(&volSync, 0, sizeof(volSync));
2719 /* handle file length setting */
2720 if (attrp->mask & CM_ATTRMASK_LENGTH)
2721 return cm_SetLength(scp, &attrp->length, userp, reqp);
2723 lock_ObtainWrite(&scp->rw);
2724 /* otherwise, we have to make an RPC to get the status */
2725 code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
2727 lock_ReleaseWrite(&scp->rw);
2730 lock_ConvertWToR(&scp->rw);
2732 /* make the attr structure */
2733 cm_StatusFromAttr(&afsInStatus, scp, attrp);
2735 tfid.Volume = scp->fid.volume;
2736 tfid.Vnode = scp->fid.vnode;
2737 tfid.Unique = scp->fid.unique;
2738 lock_ReleaseRead(&scp->rw);
2740 /* now make the RPC */
2741 osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%p", scp);
2743 code = cm_ConnFromFID(&scp->fid, userp, reqp, &connp);
2747 rxconnp = cm_GetRxConn(connp);
2748 code = RXAFS_StoreStatus(rxconnp, &tfid,
2749 &afsInStatus, &afsOutStatus, &volSync);
2750 rx_PutConnection(rxconnp);
2752 } while (cm_Analyze(connp, userp, reqp,
2753 &scp->fid, &volSync, NULL, NULL, code));
2754 code = cm_MapRPCError(code, reqp);
2757 osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
2759 osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
2761 lock_ObtainWrite(&scp->rw);
2762 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
2764 cm_MergeStatus(NULL, scp, &afsOutStatus, &volSync, userp, reqp,
2765 CM_MERGEFLAG_FORCE|CM_MERGEFLAG_STOREDATA);
2767 /* if we're changing the mode bits, discard the ACL cache,
2768 * since we changed the mode bits.
2770 if (afsInStatus.Mask & AFS_SETMODE)
2771 cm_FreeAllACLEnts(scp);
2772 lock_ReleaseWrite(&scp->rw);
2776 long cm_Create(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
2777 cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
2782 cm_callbackRequest_t cbReq;
2785 cm_scache_t *scp = NULL;
2787 AFSStoreStatus inStatus;
2788 AFSFetchStatus updatedDirStatus;
2789 AFSFetchStatus newFileStatus;
2790 AFSCallBack newFileCallback;
2792 struct rx_connection * rxconnp;
2794 fschar_t * fnamep = NULL;
2796 memset(&volSync, 0, sizeof(volSync));
2798 /* can't create names with @sys in them; must expand it manually first.
2799 * return "invalid request" if they try.
2801 if (cm_ExpandSysName(cnamep, NULL, 0, 0)) {
2802 return CM_ERROR_ATSYS;
2805 #ifdef AFS_FREELANCE_CLIENT
2806 /* Freelance root volume does not hold files */
2807 if (cm_freelanceEnabled &&
2808 dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
2809 dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
2811 return CM_ERROR_NOACCESS;
2813 #endif /* AFS_FREELANCE_CLIENT */
2815 /* before starting the RPC, mark that we're changing the file data, so
2816 * that someone who does a chmod will know to wait until our call
2819 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
2821 lock_ObtainWrite(&dscp->rw);
2822 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2823 lock_ReleaseWrite(&dscp->rw);
2825 cm_StartCallbackGrantingCall(NULL, &cbReq);
2827 cm_EndDirOp(&dirop);
2834 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
2836 cm_StatusFromAttr(&inStatus, NULL, attrp);
2838 /* try the RPC now */
2839 osi_Log1(afsd_logp, "CALL CreateFile scp 0x%p", dscp);
2841 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2845 dirAFSFid.Volume = dscp->fid.volume;
2846 dirAFSFid.Vnode = dscp->fid.vnode;
2847 dirAFSFid.Unique = dscp->fid.unique;
2849 rxconnp = cm_GetRxConn(connp);
2850 code = RXAFS_CreateFile(connp->rxconnp, &dirAFSFid, fnamep,
2851 &inStatus, &newAFSFid, &newFileStatus,
2852 &updatedDirStatus, &newFileCallback,
2854 rx_PutConnection(rxconnp);
2856 } while (cm_Analyze(connp, userp, reqp,
2857 &dscp->fid, &volSync, NULL, &cbReq, code));
2858 code = cm_MapRPCError(code, reqp);
2861 osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
2863 osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
2866 lock_ObtainWrite(&dirop.scp->dirlock);
2867 dirop.lockType = CM_DIRLOCK_WRITE;
2869 lock_ObtainWrite(&dscp->rw);
2870 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2872 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
2874 lock_ReleaseWrite(&dscp->rw);
2876 /* now try to create the file's entry, too, but be careful to
2877 * make sure that we don't merge in old info. Since we weren't locking
2878 * out any requests during the file's creation, we may have pretty old
2882 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
2883 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2885 lock_ObtainWrite(&scp->rw);
2886 scp->creator = userp; /* remember who created it */
2887 if (!cm_HaveCallback(scp)) {
2888 cm_EndCallbackGrantingCall(scp, &cbReq,
2889 &newFileCallback, &volSync, 0);
2890 cm_MergeStatus(dscp, scp, &newFileStatus, &volSync,
2894 lock_ReleaseWrite(&scp->rw);
2898 /* make sure we end things properly */
2900 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2902 if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
2903 cm_DirCreateEntry(&dirop, fnamep, &newFid);
2905 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
2908 cm_EndDirOp(&dirop);
2917 cm_ReleaseSCache(scp);
2923 * locked if TRUE means write-locked
2924 * else the cm_scache_t rw must not be held
2926 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp, afs_uint32 locked)
2931 lock_ReleaseWrite(&scp->rw);
2932 code = buf_CleanVnode(scp, userp, reqp);
2934 lock_ObtainWrite(&scp->rw);
2936 if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
2937 | CM_SCACHEMASK_CLIENTMODTIME
2938 | CM_SCACHEMASK_LENGTH))
2939 code = cm_StoreMini(scp, userp, reqp);
2941 if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) {
2942 code = (scp->flags & CM_SCACHEFLAG_OVERQUOTA) ? CM_ERROR_QUOTA : CM_ERROR_SPACE;
2943 scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE);
2947 lock_ReleaseWrite(&scp->rw);
2948 } else if (locked) {
2949 lock_ObtainWrite(&scp->rw);
2954 long cm_MakeDir(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
2955 cm_user_t *userp, cm_req_t *reqp, cm_scache_t **scpp)
2960 cm_callbackRequest_t cbReq;
2963 cm_scache_t *scp = NULL;
2965 AFSStoreStatus inStatus;
2966 AFSFetchStatus updatedDirStatus;
2967 AFSFetchStatus newDirStatus;
2968 AFSCallBack newDirCallback;
2970 struct rx_connection * rxconnp;
2972 fschar_t * fnamep = NULL;
2974 memset(&volSync, 0, sizeof(volSync));
2976 /* can't create names with @sys in them; must expand it manually first.
2977 * return "invalid request" if they try.
2979 if (cm_ExpandSysName(cnamep, NULL, 0, 0)) {
2980 return CM_ERROR_ATSYS;
2983 #ifdef AFS_FREELANCE_CLIENT
2984 /* Freelance root volume does not hold subdirectories */
2985 if (cm_freelanceEnabled &&
2986 dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
2987 dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
2989 return CM_ERROR_NOACCESS;
2991 #endif /* AFS_FREELANCE_CLIENT */
2993 /* before starting the RPC, mark that we're changing the directory
2994 * data, so that someone who does a chmod on the dir will wait until
2995 * our call completes.
2997 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
2999 lock_ObtainWrite(&dscp->rw);
3000 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3001 lock_ReleaseWrite(&dscp->rw);
3003 cm_StartCallbackGrantingCall(NULL, &cbReq);
3005 cm_EndDirOp(&dirop);
3012 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3013 cm_StatusFromAttr(&inStatus, NULL, attrp);
3015 /* try the RPC now */
3016 osi_Log1(afsd_logp, "CALL MakeDir scp 0x%p", dscp);
3018 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3022 dirAFSFid.Volume = dscp->fid.volume;
3023 dirAFSFid.Vnode = dscp->fid.vnode;
3024 dirAFSFid.Unique = dscp->fid.unique;
3026 rxconnp = cm_GetRxConn(connp);
3027 code = RXAFS_MakeDir(connp->rxconnp, &dirAFSFid, fnamep,
3028 &inStatus, &newAFSFid, &newDirStatus,
3029 &updatedDirStatus, &newDirCallback,
3031 rx_PutConnection(rxconnp);
3033 } while (cm_Analyze(connp, userp, reqp,
3034 &dscp->fid, &volSync, NULL, &cbReq, code));
3035 code = cm_MapRPCError(code, reqp);
3038 osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
3040 osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
3043 lock_ObtainWrite(&dirop.scp->dirlock);
3044 dirop.lockType = CM_DIRLOCK_WRITE;
3046 lock_ObtainWrite(&dscp->rw);
3047 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3049 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3051 lock_ReleaseWrite(&dscp->rw);
3053 /* now try to create the new dir's entry, too, but be careful to
3054 * make sure that we don't merge in old info. Since we weren't locking
3055 * out any requests during the file's creation, we may have pretty old
3059 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3060 code = cm_GetSCache(&newFid, &scp, userp, reqp);
3062 lock_ObtainWrite(&scp->rw);
3063 if (!cm_HaveCallback(scp)) {
3064 cm_EndCallbackGrantingCall(scp, &cbReq,
3065 &newDirCallback, &volSync, 0);
3066 cm_MergeStatus(dscp, scp, &newDirStatus, &volSync,
3070 lock_ReleaseWrite(&scp->rw);
3074 /* make sure we end things properly */
3076 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
3078 if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
3079 cm_DirCreateEntry(&dirop, fnamep, &newFid);
3081 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
3084 cm_EndDirOp(&dirop);
3092 cm_ReleaseSCache(scp);
3095 /* and return error code */
3099 long cm_Link(cm_scache_t *dscp, clientchar_t *cnamep, cm_scache_t *sscp, long flags,
3100 cm_user_t *userp, cm_req_t *reqp)
3105 AFSFid existingAFSFid;
3106 AFSFetchStatus updatedDirStatus;
3107 AFSFetchStatus newLinkStatus;
3109 struct rx_connection * rxconnp;
3111 fschar_t * fnamep = NULL;
3113 memset(&volSync, 0, sizeof(volSync));
3115 if (dscp->fid.cell != sscp->fid.cell ||
3116 dscp->fid.volume != sscp->fid.volume) {
3117 return CM_ERROR_CROSSDEVLINK;
3120 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3122 lock_ObtainWrite(&dscp->rw);
3123 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3124 lock_ReleaseWrite(&dscp->rw);
3126 cm_EndDirOp(&dirop);
3131 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3133 /* try the RPC now */
3134 osi_Log1(afsd_logp, "CALL Link scp 0x%p", dscp);
3136 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3139 dirAFSFid.Volume = dscp->fid.volume;
3140 dirAFSFid.Vnode = dscp->fid.vnode;
3141 dirAFSFid.Unique = dscp->fid.unique;
3143 existingAFSFid.Volume = sscp->fid.volume;
3144 existingAFSFid.Vnode = sscp->fid.vnode;
3145 existingAFSFid.Unique = sscp->fid.unique;
3147 rxconnp = cm_GetRxConn(connp);
3148 code = RXAFS_Link(rxconnp, &dirAFSFid, fnamep, &existingAFSFid,
3149 &newLinkStatus, &updatedDirStatus, &volSync);
3150 rx_PutConnection(rxconnp);
3151 osi_Log1(afsd_logp," RXAFS_Link returns 0x%x", code);
3153 } while (cm_Analyze(connp, userp, reqp,
3154 &dscp->fid, &volSync, NULL, NULL, code));
3156 code = cm_MapRPCError(code, reqp);
3159 osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
3161 osi_Log0(afsd_logp, "CALL Link SUCCESS");
3164 lock_ObtainWrite(&dirop.scp->dirlock);
3165 dirop.lockType = CM_DIRLOCK_WRITE;
3167 lock_ObtainWrite(&dscp->rw);
3168 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3170 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3172 lock_ReleaseWrite(&dscp->rw);
3175 if (cm_CheckDirOpForSingleChange(&dirop)) {
3176 cm_DirCreateEntry(&dirop, fnamep, &sscp->fid);
3178 cm_BPlusDirCreateEntry(&dirop, cnamep, &sscp->fid);
3182 cm_EndDirOp(&dirop);
3184 /* Update the linked object status */
3186 lock_ObtainWrite(&sscp->rw);
3187 cm_MergeStatus(NULL, sscp, &newLinkStatus, &volSync, userp, reqp, 0);
3188 lock_ReleaseWrite(&sscp->rw);
3196 long cm_SymLink(cm_scache_t *dscp, clientchar_t *cnamep, fschar_t *contentsp, long flags,
3197 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
3205 AFSStoreStatus inStatus;
3206 AFSFetchStatus updatedDirStatus;
3207 AFSFetchStatus newLinkStatus;
3209 struct rx_connection * rxconnp;
3211 fschar_t *fnamep = NULL;
3213 memset(&volSync, 0, sizeof(volSync));
3215 /* before starting the RPC, mark that we're changing the directory data,
3216 * so that someone who does a chmod on the dir will wait until our
3219 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3221 lock_ObtainWrite(&dscp->rw);
3222 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3223 lock_ReleaseWrite(&dscp->rw);
3225 cm_EndDirOp(&dirop);
3230 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3232 cm_StatusFromAttr(&inStatus, NULL, attrp);
3234 /* try the RPC now */
3235 osi_Log1(afsd_logp, "CALL Symlink scp 0x%p", dscp);
3237 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3241 dirAFSFid.Volume = dscp->fid.volume;
3242 dirAFSFid.Vnode = dscp->fid.vnode;
3243 dirAFSFid.Unique = dscp->fid.unique;
3245 rxconnp = cm_GetRxConn(connp);
3246 code = RXAFS_Symlink(rxconnp, &dirAFSFid, fnamep, contentsp,
3247 &inStatus, &newAFSFid, &newLinkStatus,
3248 &updatedDirStatus, &volSync);
3249 rx_PutConnection(rxconnp);
3251 } while (cm_Analyze(connp, userp, reqp,
3252 &dscp->fid, &volSync, NULL, NULL, code));
3253 code = cm_MapRPCError(code, reqp);
3256 osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
3258 osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
3261 lock_ObtainWrite(&dirop.scp->dirlock);
3262 dirop.lockType = CM_DIRLOCK_WRITE;
3264 lock_ObtainWrite(&dscp->rw);
3265 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3267 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3269 lock_ReleaseWrite(&dscp->rw);
3272 if (cm_CheckDirOpForSingleChange(&dirop)) {
3273 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3275 cm_DirCreateEntry(&dirop, fnamep, &newFid);
3277 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
3281 cm_EndDirOp(&dirop);
3283 /* now try to create the new dir's entry, too, but be careful to
3284 * make sure that we don't merge in old info. Since we weren't locking
3285 * out any requests during the file's creation, we may have pretty old
3289 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3290 code = cm_GetSCache(&newFid, &scp, userp, reqp);
3292 lock_ObtainWrite(&scp->rw);
3293 if (!cm_HaveCallback(scp)) {
3294 cm_MergeStatus(dscp, scp, &newLinkStatus, &volSync,
3297 lock_ReleaseWrite(&scp->rw);
3298 cm_ReleaseSCache(scp);
3304 /* and return error code */
3308 /*! \brief Remove a directory
3310 Encapsulates a call to RXAFS_RemoveDir().
3312 \param[in] dscp cm_scache_t for the directory containing the
3313 directory to be removed.
3315 \param[in] fnamep This will be the original name of the directory
3316 as known to the file server. It will be passed in to RXAFS_RemoveDir().
3317 This parameter is optional. If it is not provided the value
3320 \param[in] cnamep Normalized name used to update the local
3323 \param[in] userp cm_user_t for the request.
3325 \param[in] reqp Request tracker.
3327 long cm_RemoveDir(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t *cnamep, cm_user_t *userp, cm_req_t *reqp)
3333 AFSFetchStatus updatedDirStatus;
3335 struct rx_connection * rxconnp;
3337 cm_scache_t *scp = NULL;
3338 int free_fnamep = FALSE;
3340 memset(&volSync, 0, sizeof(volSync));
3342 if (fnamep == NULL) {
3345 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ,
3346 CM_DIROP_FLAG_NONE, &dirop);
3348 code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
3351 cm_EndDirOp(&dirop);
3358 code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
3362 /* before starting the RPC, mark that we're changing the directory data,
3363 * so that someone who does a chmod on the dir will wait until our
3366 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3368 lock_ObtainWrite(&dscp->rw);
3369 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3370 lock_ReleaseWrite(&dscp->rw);
3372 cm_EndDirOp(&dirop);
3377 /* try the RPC now */
3378 osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%p", dscp);
3380 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3384 dirAFSFid.Volume = dscp->fid.volume;
3385 dirAFSFid.Vnode = dscp->fid.vnode;
3386 dirAFSFid.Unique = dscp->fid.unique;
3388 rxconnp = cm_GetRxConn(connp);
3389 code = RXAFS_RemoveDir(rxconnp, &dirAFSFid, fnamep,
3390 &updatedDirStatus, &volSync);
3391 rx_PutConnection(rxconnp);
3393 } while (cm_Analyze(connp, userp, reqp,
3394 &dscp->fid, &volSync, NULL, NULL, code));
3395 code = cm_MapRPCErrorRmdir(code, reqp);
3398 osi_Log1(afsd_logp, "CALL RemoveDir FAILURE, code 0x%x", code);
3400 osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
3403 lock_ObtainWrite(&dirop.scp->dirlock);
3404 dirop.lockType = CM_DIRLOCK_WRITE;
3406 lock_ObtainWrite(&dscp->rw);
3407 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3409 cm_dnlcRemove(dscp, cnamep);
3410 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3412 lock_ReleaseWrite(&dscp->rw);
3415 if (cm_CheckDirOpForSingleChange(&dirop) && cnamep != NULL) {
3416 cm_DirDeleteEntry(&dirop, fnamep);
3418 cm_BPlusDirDeleteEntry(&dirop, cnamep);
3422 cm_EndDirOp(&dirop);
3425 cm_ReleaseSCache(scp);
3427 lock_ObtainWrite(&scp->rw);
3428 scp->flags |= CM_SCACHEFLAG_DELETED;
3429 lock_ReleaseWrite(&scp->rw);
3437 /* and return error code */
3441 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
3443 /* grab mutex on contents */
3444 lock_ObtainWrite(&scp->rw);
3446 /* reset the prefetch info */
3447 scp->prefetch.base.LowPart = 0; /* base */
3448 scp->prefetch.base.HighPart = 0;
3449 scp->prefetch.end.LowPart = 0; /* and end */
3450 scp->prefetch.end.HighPart = 0;
3452 /* release mutex on contents */
3453 lock_ReleaseWrite(&scp->rw);
3459 /*! \brief Rename a file or directory
3461 Encapsulates a RXAFS_Rename() call.
3463 \param[in] oldDscp cm_scache_t for the directory containing the old
3466 \param[in] oldNamep The original old name known to the file server.
3467 This is the name that will be passed into the RXAFS_Rename().
3468 If it is not provided, it will be looked up.
3470 \param[in] normalizedOldNamep Normalized old name. This is used for
3471 updating local directory caches.
3473 \param[in] newDscp cm_scache_t for the directory containing the new
3476 \param[in] newNamep New name. Normalized.
3478 \param[in] userp cm_user_t for the request.
3480 \param[in,out] reqp Request tracker.
3483 long cm_Rename(cm_scache_t *oldDscp, fschar_t *oldNamep, clientchar_t *cOldNamep,
3484 cm_scache_t *newDscp, clientchar_t *cNewNamep, cm_user_t *userp,
3489 AFSFid oldDirAFSFid;
3490 AFSFid newDirAFSFid;
3492 AFSFetchStatus updatedOldDirStatus;
3493 AFSFetchStatus updatedNewDirStatus;
3496 struct rx_connection * rxconnp;
3497 cm_dirOp_t oldDirOp;
3500 cm_dirOp_t newDirOp;
3501 fschar_t * newNamep = NULL;
3502 int free_oldNamep = FALSE;
3503 cm_scache_t *oldScp = NULL, *newScp = NULL;
3505 memset(&volSync, 0, sizeof(volSync));
3507 if (cOldNamep == NULL || cNewNamep == NULL ||
3508 cm_ClientStrLen(cOldNamep) == 0 ||
3509 cm_ClientStrLen(cNewNamep) == 0)
3510 return CM_ERROR_INVAL;
3513 * Before we permit the operation, make sure that we do not already have
3514 * an object in the destination directory that has a case-insensitive match
3515 * for this name UNLESS the matching object is the object we are renaming.
3517 code = cm_Lookup(oldDscp, cOldNamep, 0, userp, reqp, &oldScp);
3519 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S old name lookup failed",
3520 oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3524 code = cm_Lookup(newDscp, cNewNamep, CM_FLAG_CASEFOLD, userp, reqp, &newScp);
3526 /* found a matching object with the new name */
3527 if (cm_FidCmp(&oldScp->fid, &newScp->fid)) {
3528 /* and they don't match so return an error */
3529 osi_Log2(afsd_logp, "cm_Rename newDscp 0x%p cNewName %S new name already exists",
3530 newDscp, osi_LogSaveStringW(afsd_logp, cNewNamep));
3531 code = CM_ERROR_EXISTS;
3533 cm_ReleaseSCache(newScp);
3535 } else if (code == CM_ERROR_AMBIGUOUS_FILENAME) {
3536 code = CM_ERROR_EXISTS;
3543 if (oldNamep == NULL) {
3546 code = cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_READ,
3547 CM_DIROP_FLAG_NONE, &oldDirOp);
3549 code = cm_BPlusDirLookupOriginalName(&oldDirOp, cOldNamep, &oldNamep);
3551 free_oldNamep = TRUE;
3552 cm_EndDirOp(&oldDirOp);
3556 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S Original Name lookup failed",
3557 oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3563 /* before starting the RPC, mark that we're changing the directory data,
3564 * so that someone who does a chmod on the dir will wait until our call
3565 * completes. We do this in vnode order so that we don't deadlock,
3566 * which makes the code a little verbose.
3568 if (oldDscp == newDscp) {
3569 /* check for identical names */
3570 if (cm_ClientStrCmp(cOldNamep, cNewNamep) == 0) {
3571 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_RENAME_IDENTICAL",
3573 code = CM_ERROR_RENAME_IDENTICAL;
3578 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE,
3579 CM_DIROP_FLAG_NONE, &oldDirOp);
3580 lock_ObtainWrite(&oldDscp->rw);
3581 cm_dnlcRemove(oldDscp, cOldNamep);
3582 cm_dnlcRemove(oldDscp, cNewNamep);
3583 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3584 CM_SCACHESYNC_STOREDATA);
3585 lock_ReleaseWrite(&oldDscp->rw);
3587 cm_EndDirOp(&oldDirOp);
3591 /* two distinct dir vnodes */
3593 if (oldDscp->fid.cell != newDscp->fid.cell ||
3594 oldDscp->fid.volume != newDscp->fid.volume) {
3595 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_CROSSDEVLINK",
3597 code = CM_ERROR_CROSSDEVLINK;
3601 /* shouldn't happen that we have distinct vnodes for two
3602 * different files, but could due to deliberate attack, or
3603 * stale info. Avoid deadlocks and quit now.
3605 if (oldDscp->fid.vnode == newDscp->fid.vnode) {
3606 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p vnode collision",
3608 code = CM_ERROR_CROSSDEVLINK;
3612 if (oldDscp->fid.vnode < newDscp->fid.vnode) {
3613 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE,
3614 CM_DIROP_FLAG_NONE, &oldDirOp);
3615 lock_ObtainWrite(&oldDscp->rw);
3616 cm_dnlcRemove(oldDscp, cOldNamep);
3617 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3618 CM_SCACHESYNC_STOREDATA);
3619 lock_ReleaseWrite(&oldDscp->rw);
3621 cm_EndDirOp(&oldDirOp);
3623 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE,
3624 CM_DIROP_FLAG_NONE, &newDirOp);
3625 lock_ObtainWrite(&newDscp->rw);
3626 cm_dnlcRemove(newDscp, cNewNamep);
3627 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3628 CM_SCACHESYNC_STOREDATA);
3629 lock_ReleaseWrite(&newDscp->rw);
3631 cm_EndDirOp(&newDirOp);
3633 /* cleanup first one */
3634 lock_ObtainWrite(&oldDscp->rw);
3635 cm_SyncOpDone(oldDscp, NULL,
3636 CM_SCACHESYNC_STOREDATA);
3637 lock_ReleaseWrite(&oldDscp->rw);
3638 cm_EndDirOp(&oldDirOp);
3643 /* lock the new vnode entry first */
3644 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE,
3645 CM_DIROP_FLAG_NONE, &newDirOp);
3646 lock_ObtainWrite(&newDscp->rw);
3647 cm_dnlcRemove(newDscp, cNewNamep);
3648 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3649 CM_SCACHESYNC_STOREDATA);
3650 lock_ReleaseWrite(&newDscp->rw);
3652 cm_EndDirOp(&newDirOp);
3654 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE,
3655 CM_DIROP_FLAG_NONE, &oldDirOp);
3656 lock_ObtainWrite(&oldDscp->rw);
3657 cm_dnlcRemove(oldDscp, cOldNamep);
3658 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3659 CM_SCACHESYNC_STOREDATA);
3660 lock_ReleaseWrite(&oldDscp->rw);
3662 cm_EndDirOp(&oldDirOp);
3664 /* cleanup first one */
3665 lock_ObtainWrite(&newDscp->rw);
3666 cm_SyncOpDone(newDscp, NULL,
3667 CM_SCACHESYNC_STOREDATA);
3668 lock_ReleaseWrite(&newDscp->rw);
3669 cm_EndDirOp(&newDirOp);
3673 } /* two distinct vnodes */
3680 newNamep = cm_ClientStringToFsStringAlloc(cNewNamep, -1, NULL);
3682 /* try the RPC now */
3683 osi_Log2(afsd_logp, "CALL Rename old scp 0x%p new scp 0x%p",
3686 code = cm_ConnFromFID(&oldDscp->fid, userp, reqp, &connp);
3690 oldDirAFSFid.Volume = oldDscp->fid.volume;
3691 oldDirAFSFid.Vnode = oldDscp->fid.vnode;
3692 oldDirAFSFid.Unique = oldDscp->fid.unique;
3693 newDirAFSFid.Volume = newDscp->fid.volume;
3694 newDirAFSFid.Vnode = newDscp->fid.vnode;
3695 newDirAFSFid.Unique = newDscp->fid.unique;
3697 rxconnp = cm_GetRxConn(connp);
3698 code = RXAFS_Rename(rxconnp, &oldDirAFSFid, oldNamep,
3699 &newDirAFSFid, newNamep,
3700 &updatedOldDirStatus, &updatedNewDirStatus,
3702 rx_PutConnection(rxconnp);
3704 } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
3705 &volSync, NULL, NULL, code));
3706 code = cm_MapRPCError(code, reqp);
3709 osi_Log1(afsd_logp, "CALL Rename FAILURE, code 0x%x", code);
3711 osi_Log0(afsd_logp, "CALL Rename SUCCESS");
3713 /* update the individual stat cache entries for the directories */
3715 lock_ObtainWrite(&oldDirOp.scp->dirlock);
3716 oldDirOp.lockType = CM_DIRLOCK_WRITE;
3718 lock_ObtainWrite(&oldDscp->rw);
3719 cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
3722 cm_MergeStatus(NULL, oldDscp, &updatedOldDirStatus, &volSync,
3723 userp, reqp, CM_MERGEFLAG_DIROP);
3724 lock_ReleaseWrite(&oldDscp->rw);
3726 if (code == 0 && cm_CheckDirOpForSingleChange(&oldDirOp)) {
3728 diropCode = cm_BPlusDirLookup(&oldDirOp, cOldNamep, &fileFid);
3729 if (diropCode == CM_ERROR_INEXACT_MATCH)
3731 else if (diropCode == EINVAL)
3733 diropCode = cm_DirLookup(&oldDirOp, oldNamep, &fileFid);
3735 if (diropCode == 0) {
3737 diropCode = cm_DirCreateEntry(&oldDirOp, newNamep, &fileFid);
3739 cm_BPlusDirCreateEntry(&oldDirOp, cNewNamep, &fileFid);
3743 if (diropCode == 0) {
3744 diropCode = cm_DirDeleteEntry(&oldDirOp, oldNamep);
3746 cm_BPlusDirDeleteEntry(&oldDirOp, cOldNamep);
3751 cm_EndDirOp(&oldDirOp);
3753 /* and update it for the new one, too, if necessary */
3756 lock_ObtainWrite(&newDirOp.scp->dirlock);
3757 newDirOp.lockType = CM_DIRLOCK_WRITE;
3759 lock_ObtainWrite(&newDscp->rw);
3760 cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
3762 cm_MergeStatus(NULL, newDscp, &updatedNewDirStatus, &volSync,
3763 userp, reqp, CM_MERGEFLAG_DIROP);
3764 lock_ReleaseWrite(&newDscp->rw);
3768 * The following optimization does not work.
3769 * When the file server processed a RXAFS_Rename() request the
3770 * FID of the object being moved between directories is not
3771 * preserved. The client does not know the new FID nor the
3772 * version number of the target. Not only can we not create
3773 * the directory entry in the new directory, but we can't
3774 * preserve the cached data for the file. It must be re-read
3775 * from the file server. - jaltman, 2009/02/20
3778 /* we only make the local change if we successfully made
3779 the change in the old directory AND there was only one
3780 change in the new directory */
3781 if (diropCode == 0 && cm_CheckDirOpForSingleChange(&newDirOp)) {
3782 cm_DirCreateEntry(&newDirOp, newNamep, &fileFid);
3784 cm_BPlusDirCreateEntry(&newDirOp, cNewNamep, &fileFid);
3789 cm_EndDirOp(&newDirOp);
3793 * After the rename the file server has invalidated the callbacks
3794 * on the file that was moved nor do we have a directory reference
3797 lock_ObtainWrite(&oldScp->rw);
3798 cm_DiscardSCache(oldScp);
3799 lock_ReleaseWrite(&oldScp->rw);
3803 cm_ReleaseSCache(oldScp);
3810 /* and return error code */
3814 /* Byte range locks:
3816 The OpenAFS Windows client has to fake byte range locks given no
3817 server side support for such locks. This is implemented as keyed
3818 byte range locks on the cache manager.
3820 Keyed byte range locks:
3822 Each cm_scache_t structure keeps track of a list of keyed locks.
3823 The key for a lock identifies an owner of a set of locks (referred
3824 to as a client). Each key is represented by a value. The set of
3825 key values used within a specific cm_scache_t structure form a
3826 namespace that has a scope of just that cm_scache_t structure. The
3827 same key value can be used with another cm_scache_t structure and
3828 correspond to a completely different client. However it is
3829 advantageous for the SMB or IFS layer to make sure that there is a
3830 1-1 mapping between client and keys over all cm_scache_t objects.
3832 Assume a client C has key Key(C) (although, since the scope of the
3833 key is a cm_scache_t, the key can be Key(C,S), where S is the
3834 cm_scache_t. But assume a 1-1 relation between keys and clients).
3835 A byte range (O,+L) denotes byte addresses (O) through (O+L-1)
3836 inclusive (a.k.a. [O,O+L-1]). The function Key(x) is implemented
3837 through cm_generateKey() function for both SMB and IFS.
3839 The list of locks for a cm_scache_t object S is maintained in
3840 S->fileLocks. The cache manager will set a lock on the AFS file
3841 server in order to assert the locks in S->fileLocks. If only
3842 shared locks are in place for S, then the cache manager will obtain
3843 a LockRead lock, while if there are any exclusive locks, it will
3844 obtain a LockWrite lock. If the exclusive locks are all released
3845 while the shared locks remain, then the cache manager will
3846 downgrade the lock from LockWrite to LockRead. Similarly, if an
3847 exclusive lock is obtained when only shared locks exist, then the
3848 cache manager will try to upgrade the lock from LockRead to
3851 Each lock L owned by client C maintains a key L->key such that
3852 L->key == Key(C), the effective range defined by L->LOffset and
3853 L->LLength such that the range of bytes affected by the lock is
3854 (L->LOffset, +L->LLength), a type maintained in L->LockType which
3855 is either exclusive or shared.
3859 A lock exists iff it is in S->fileLocks for some cm_scache_t
3860 S. Existing locks are in one of the following states: ACTIVE,
3861 WAITLOCK, WAITUNLOCK, LOST, DELETED.
3863 The following sections describe each lock and the associated
3866 1. ACTIVE: A lock L is ACTIVE iff the cache manager has asserted
3867 the lock with the AFS file server. This type of lock can be
3868 exercised by a client to read or write to the locked region (as
3871 1.1 ACTIVE->LOST: When the AFS file server fails to extend a
3872 server lock that was required to assert the lock. Before
3873 marking the lock as lost, the cache manager checks if the file
3874 has changed on the server. If the file has not changed, then
3875 the cache manager will attempt to obtain a new server lock
3876 that is sufficient to assert the client side locks for the
3877 file. If any of these fail, the lock is marked as LOST.
3878 Otherwise, it is left as ACTIVE.
3880 1.2 ACTIVE->DELETED: Lock is released.
3882 2. WAITLOCK: A lock is in a WAITLOCK state if the cache manager
3883 grants the lock but the lock is yet to be asserted with the AFS
3884 file server. Once the file server grants the lock, the state
3885 will transition to an ACTIVE lock.
3887 2.1 WAITLOCK->ACTIVE: The server granted the lock.
3889 2.2 WAITLOCK->DELETED: Lock is abandoned, or timed out during
3892 2.3 WAITLOCK->LOST: One or more locks from this client were
3893 marked as LOST. No further locks will be granted to this
3894 client until all lost locks are removed.
3896 3. WAITUNLOCK: A lock is in a WAITUNLOCK state if the cache manager
3897 receives a request for a lock that conflicts with an existing
3898 ACTIVE or WAITLOCK lock. The lock will be placed in the queue
3899 and will be granted at such time the conflicting locks are
3900 removed, at which point the state will transition to either
3903 3.1 WAITUNLOCK->ACTIVE: The conflicting lock was removed. The
3904 current serverLock is sufficient to assert this lock, or a
3905 sufficient serverLock is obtained.
3907 3.2 WAITUNLOCK->WAITLOCK: The conflicting lock was removed,
3908 however the required serverLock is yet to be asserted with the
3911 3.3 WAITUNLOCK->DELETED: The lock is abandoned, timed out or
3914 3.5 WAITUNLOCK->LOST: One or more locks from this client were
3915 marked as LOST. No further locks will be granted to this
3916 client until all lost locks are removed.
3918 4. LOST: A lock L is LOST if the server lock that was required to
3919 assert the lock could not be obtained or if it could not be
3920 extended, or if other locks by the same client were LOST.
3921 Essentially, once a lock is LOST, the contract between the cache
3922 manager and that specific client is no longer valid.
3924 The cache manager rechecks the server lock once every minute and
3925 extends it as appropriate. If this is not done for 5 minutes,
3926 the AFS file server will release the lock (the 5 minute timeout
3927 is based on current file server code and is fairly arbitrary).
3928 Once released, the lock cannot be re-obtained without verifying
3929 that the contents of the file hasn't been modified since the
3930 time the lock was released. Re-obtaining the lock without
3931 verifying this may lead to data corruption. If the lock can not
3932 be obtained safely, then all active locks for the cm_scache_t
3935 4.1 LOST->DELETED: The lock is released.
3937 5. DELETED: The lock is no longer relevant. Eventually, it will
3938 get removed from the cm_scache_t. In the meantime, it will be
3939 treated as if it does not exist.
3941 5.1 DELETED->not exist: The lock is removed from the
3944 The following are classifications of locks based on their state.
3946 6* A lock L is ACCEPTED if it is ACTIVE or WAITLOCK. These locks
3947 have been accepted by the cache manager, but may or may not have
3948 been granted back to the client.
3950 7* A lock L is QUEUED if it is ACTIVE, WAITLOCK or WAITUNLOCK.
3952 8* A lock L is WAITING if it is WAITLOCK or WAITUNLOCK.
3956 A client C can READ range (Offset,+Length) of a file represented by
3957 cm_scache_t S iff (1):
3959 1. for all _a_ in (Offset,+Length), all of the following is true:
3961 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3962 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3965 1.2 For each LOST lock L in S->fileLocks such that _a_ in
3966 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3969 (When locks are lost on an cm_scache_t, all locks are lost. By
3970 4.2 (below), if there is an exclusive LOST lock, then there
3971 can't be any overlapping ACTIVE locks.)
3973 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
3975 2. for all _a_ in (Offset,+Length), one of the following is true:
3977 2.1 Byte _a_ of S is unowned (as specified in 1.1) AND there
3978 does not exist a LOST lock L such that _a_ in
3979 (L->LOffset,+L->LLength).
3981 2.2 Byte _a_ of S is owned by C under lock L (as specified in
3982 1.2) AND L->LockType is exclusive.
3984 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
3986 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
3989 3.1 If L->LockType is exclusive then there does NOT exist a
3990 ACCEPTED lock M in S->fileLocks such that _a_ in
3991 (M->LOffset,+M->LLength).
3993 (If we count all QUEUED locks then we hit cases such as
3994 cascading waiting locks where the locks later on in the queue
3995 can be granted without compromising file integrity. On the
3996 other hand if only ACCEPTED locks are considered, then locks
3997 that were received earlier may end up waiting for locks that
3998 were received later to be unlocked. The choice of ACCEPTED
3999 locks was made to mimic the Windows byte range lock
4002 3.2 If L->LockType is shared then for each ACCEPTED lock M in
4003 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
4004 M->LockType is shared.
4006 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
4008 4.1 M->key != Key(C)
4010 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
4011 and (M->LOffset,+M->LLength) do not intersect.
4013 (Note: If a client loses a lock, it loses all locks.
4014 Subsequently, it will not be allowed to obtain any more locks
4015 until all existing LOST locks that belong to the client are
4016 released. Once all locks are released by a single client,
4017 there exists no further contract between the client and AFS
4018 about the contents of the file, hence the client can then
4019 proceed to obtain new locks and establish a new contract.
4021 This doesn't quite work as you think it should, because most
4022 applications aren't built to deal with losing locks they
4023 thought they once had. For now, we don't have a good
4024 solution to lost locks.
4026 Also, for consistency reasons, we have to hold off on
4027 granting locks that overlap exclusive LOST locks.)
4029 A client C can only unlock locks L in S->fileLocks which have
4032 The representation and invariants are as follows:
4034 - Each cm_scache_t structure keeps:
4036 - A queue of byte-range locks (cm_scache_t::fileLocks) which
4037 are of type cm_file_lock_t.
4039 - A record of the highest server-side lock that has been
4040 obtained for this object (cm_scache_t::serverLock), which is
4041 one of (-1), LockRead, LockWrite.
4043 - A count of ACCEPTED exclusive and shared locks that are in the
4044 queue (cm_scache_t::sharedLocks and
4045 cm_scache_t::exclusiveLocks)
4047 - Each cm_file_lock_t structure keeps:
4049 - The type of lock (cm_file_lock_t::LockType)
4051 - The key associated with the lock (cm_file_lock_t::key)
4053 - The offset and length of the lock (cm_file_lock_t::LOffset
4054 and cm_file_lock_t::LLength)
4056 - The state of the lock.
4058 - Time of issuance or last successful extension
4060 Semantic invariants:
4062 I1. The number of ACCEPTED locks in S->fileLocks are
4063 (S->sharedLocks + S->exclusiveLocks)
4065 External invariants:
4067 I3. S->serverLock is the lock that we have asserted with the
4068 AFS file server for this cm_scache_t.
4070 I4. S->serverLock == LockRead iff there is at least one ACTIVE
4071 shared lock, but no ACTIVE exclusive locks.
4073 I5. S->serverLock == LockWrite iff there is at least one ACTIVE
4076 I6. If L is a LOST lock, then for each lock M in S->fileLocks,
4077 M->key == L->key IMPLIES M is LOST or DELETED.
4082 #define IS_LOCK_ACTIVE(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == 0)
4084 #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)
4086 #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)
4088 #define IS_LOCK_LOST(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_LOST)
4090 #define IS_LOCK_DELETED(lockp) (((lockp)->flags & CM_FILELOCK_FLAG_DELETED) == CM_FILELOCK_FLAG_DELETED)
4093 #define IS_LOCK_ACCEPTED(lockp) (IS_LOCK_ACTIVE(lockp) || IS_LOCK_WAITLOCK(lockp))
4096 #define IS_LOCK_CLIENTONLY(lockp) ((((lockp)->scp->flags & CM_SCACHEFLAG_RO) == CM_SCACHEFLAG_RO) || (((lockp)->flags & CM_FILELOCK_FLAG_CLIENTONLY) == CM_FILELOCK_FLAG_CLIENTONLY))
4099 #define INTERSECT_RANGE(r1,r2) (((r2).offset+(r2).length) > (r1).offset && ((r1).offset +(r1).length) > (r2).offset)
4102 #define CONTAINS_RANGE(r1,r2) (((r2).offset+(r2).length) <= ((r1).offset+(r1).length) && (r1).offset <= (r2).offset)
4104 #if defined(VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS) && !defined(LOCK_TESTING)
4105 #define SCP_SUPPORTS_BRLOCKS(scp) ((scp)->cbServerp && ((scp)->cbServerp->capabilities & VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS))
4107 #define SCP_SUPPORTS_BRLOCKS(scp) (1)
4110 #define SERVERLOCKS_ENABLED(scp) (!((scp)->flags & CM_SCACHEFLAG_RO) && cm_enableServerLocks && SCP_SUPPORTS_BRLOCKS(scp))
4112 #if defined(VICED_CAPABILITY_WRITELOCKACL)
4113 #define SCP_SUPPORTS_WRITELOCKACL(scp) ((scp)->cbServerp && ((scp->cbServerp->capabilities & VICED_CAPABILITY_WRITELOCKACL)))
4115 #define SCP_SUPPORTS_WRITELOCKACL(scp) (0)
4117 /* This should really be defined in any build that this code is being
4119 #error VICED_CAPABILITY_WRITELOCKACL not defined.
4122 static void cm_LockRangeSubtract(cm_range_t * pos, const cm_range_t * neg)
4124 afs_int64 int_begin;
4127 int_begin = MAX(pos->offset, neg->offset);
4128 int_end = MIN(pos->offset+pos->length, neg->offset+neg->length);
4130 if (int_begin < int_end) {
4131 if (int_begin == pos->offset) {
4132 pos->length = pos->offset + pos->length - int_end;
4133 pos->offset = int_end;
4134 } else if (int_end == pos->offset + pos->length) {
4135 pos->length = int_begin - pos->offset;
4138 /* We only subtract ranges if the resulting range is
4139 contiguous. If we try to support non-contigous ranges, we
4140 aren't actually improving performance. */
4144 /* Called with scp->rw held. Returns 0 if all is clear to read the
4145 specified range by the client identified by key.
4147 long cm_LockCheckRead(cm_scache_t *scp,
4148 LARGE_INTEGER LOffset,
4149 LARGE_INTEGER LLength,
4152 #ifndef ADVISORY_LOCKS
4154 cm_file_lock_t *fileLock;
4158 int substract_ranges = FALSE;
4160 range.offset = LOffset.QuadPart;
4161 range.length = LLength.QuadPart;
4165 1. for all _a_ in (Offset,+Length), all of the following is true:
4167 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
4168 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
4171 1.2 For each LOST lock L in S->fileLocks such that _a_ in
4172 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
4177 lock_ObtainRead(&cm_scacheLock);
4179 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
4181 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4183 if (INTERSECT_RANGE(range, fileLock->range)) {
4184 if (IS_LOCK_ACTIVE(fileLock)) {
4185 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4187 /* If there is an active lock for this client, it
4188 is safe to substract ranges.*/
4189 cm_LockRangeSubtract(&range, &fileLock->range);
4190 substract_ranges = TRUE;
4192 if (fileLock->lockType != LockRead) {
4193 code = CM_ERROR_LOCK_CONFLICT;
4197 /* even if the entire range is locked for reading,
4198 we still can't grant the lock at this point
4199 because the client may have lost locks. That
4200 is, unless we have already seen an active lock
4201 belonging to the client, in which case there
4202 can't be any lost locks for this client. */
4203 if (substract_ranges)
4204 cm_LockRangeSubtract(&range, &fileLock->range);
4206 } else if (IS_LOCK_LOST(fileLock) &&
4207 (cm_KeyEquals(&fileLock->key, &key, 0) || fileLock->lockType == LockWrite)) {
4208 code = CM_ERROR_BADFD;
4214 lock_ReleaseRead(&cm_scacheLock);
4216 osi_Log4(afsd_logp, "cm_LockCheckRead scp 0x%x offset %d length %d code 0x%x",
4217 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
4228 /* Called with scp->rw held. Returns 0 if all is clear to write the
4229 specified range by the client identified by key.
4231 long cm_LockCheckWrite(cm_scache_t *scp,
4232 LARGE_INTEGER LOffset,
4233 LARGE_INTEGER LLength,
4236 #ifndef ADVISORY_LOCKS
4238 cm_file_lock_t *fileLock;
4243 range.offset = LOffset.QuadPart;
4244 range.length = LLength.QuadPart;
4247 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
4249 2. for all _a_ in (Offset,+Length), one of the following is true:
4251 2.1 Byte _a_ of S is unowned AND there does not exist a LOST
4252 lock L such that _a_ in (L->LOffset,+L->LLength).
4254 2.2 Byte _a_ of S is owned by C under lock L AND L->LockType is
4258 lock_ObtainRead(&cm_scacheLock);
4260 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
4262 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4264 if (INTERSECT_RANGE(range, fileLock->range)) {
4265 if (IS_LOCK_ACTIVE(fileLock)) {
4266 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4267 if (fileLock->lockType == LockWrite) {
4269 /* if there is an active lock for this client, it
4270 is safe to substract ranges */
4271 cm_LockRangeSubtract(&range, &fileLock->range);
4273 code = CM_ERROR_LOCK_CONFLICT;
4277 code = CM_ERROR_LOCK_CONFLICT;
4280 } else if (IS_LOCK_LOST(fileLock)) {
4281 code = CM_ERROR_BADFD;
4287 lock_ReleaseRead(&cm_scacheLock);
4289 osi_Log4(afsd_logp, "cm_LockCheckWrite scp 0x%x offset %d length %d code 0x%x",
4290 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
4301 /* Called with cm_scacheLock write locked */
4302 static cm_file_lock_t * cm_GetFileLock(void) {
4305 l = (cm_file_lock_t *) cm_freeFileLocks;
4307 osi_QRemove(&cm_freeFileLocks, &l->q);
4309 l = malloc(sizeof(cm_file_lock_t));
4310 osi_assertx(l, "null cm_file_lock_t");
4313 memset(l, 0, sizeof(cm_file_lock_t));
4318 /* Called with cm_scacheLock write locked */
4319 static void cm_PutFileLock(cm_file_lock_t *l) {
4320 osi_QAdd(&cm_freeFileLocks, &l->q);
4323 /* called with scp->rw held. May release it during processing, but
4324 leaves it held on exit. */
4325 long cm_IntSetLock(cm_scache_t * scp, cm_user_t * userp, int lockType,
4331 struct rx_connection * rxconnp;
4333 afs_uint32 reqflags = reqp->flags;
4335 memset(&volSync, 0, sizeof(volSync));
4337 tfid.Volume = scp->fid.volume;
4338 tfid.Vnode = scp->fid.vnode;
4339 tfid.Unique = scp->fid.unique;
4342 osi_Log2(afsd_logp, "CALL SetLock scp 0x%p for lock %d", scp, lockType);
4344 reqp->flags |= CM_REQ_NORETRY;
4345 lock_ReleaseWrite(&scp->rw);
4348 code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4352 rxconnp = cm_GetRxConn(connp);
4353 code = RXAFS_SetLock(rxconnp, &tfid, lockType,
4355 rx_PutConnection(rxconnp);
4357 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
4360 code = cm_MapRPCError(code, reqp);
4362 osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
4364 osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
4367 lock_ObtainWrite(&scp->rw);
4368 reqp->flags = reqflags;
4372 /* called with scp->rw held. Releases it during processing */
4373 long cm_IntReleaseLock(cm_scache_t * scp, cm_user_t * userp,
4379 struct rx_connection * rxconnp;
4382 memset(&volSync, 0, sizeof(volSync));
4384 tfid.Volume = scp->fid.volume;
4385 tfid.Vnode = scp->fid.vnode;
4386 tfid.Unique = scp->fid.unique;
4389 lock_ReleaseWrite(&scp->rw);
4391 osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
4394 code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4398 rxconnp = cm_GetRxConn(connp);
4399 code = RXAFS_ReleaseLock(rxconnp, &tfid, &volSync);
4400 rx_PutConnection(rxconnp);
4402 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
4404 code = cm_MapRPCError(code, reqp);
4407 "CALL ReleaseLock FAILURE, code 0x%x", code);
4410 "CALL ReleaseLock SUCCESS");
4412 lock_ObtainWrite(&scp->rw);
4417 /* called with scp->rw held. May release it during processing, but
4418 will exit with lock held.
4422 - 0 if the user has permission to get the specified lock for the scp
4424 - CM_ERROR_NOACCESS if not
4426 Any other error from cm_SyncOp will be sent down untranslated.
4428 If CM_ERROR_NOACCESS is returned and lock_type is LockRead, then
4429 phas_insert (if non-NULL) will receive a boolean value indicating
4430 whether the user has INSERT permission or not.
4432 long cm_LockCheckPerms(cm_scache_t * scp,
4439 long code = 0, code2 = 0;
4441 /* lock permissions are slightly tricky because of the 'i' bit.
4442 If the user has PRSFS_LOCK, she can read-lock the file. If the
4443 user has PRSFS_WRITE, she can write-lock the file. However, if
4444 the user has PRSFS_INSERT, then she can write-lock new files,
4445 but not old ones. Since we don't have information about
4446 whether a file is new or not, we assume that if the user owns
4447 the scp, then she has the permissions that are granted by
4450 osi_Log3(afsd_logp, "cm_LockCheckPerms for scp[0x%p] type[%d] user[0x%p]",
4451 scp, lock_type, userp);
4453 if (lock_type == LockRead)
4454 rights |= PRSFS_LOCK;
4455 else if (lock_type == LockWrite)
4456 rights |= PRSFS_WRITE | PRSFS_LOCK;
4459 osi_assertx(FALSE, "invalid lock type");
4464 *phas_insert = FALSE;
4466 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
4467 CM_SCACHESYNC_GETSTATUS |
4468 CM_SCACHESYNC_NEEDCALLBACK);
4470 if (phas_insert && scp->creator == userp) {
4472 /* If this file was created by the user, then we check for
4473 PRSFS_INSERT. If the file server is recent enough, then
4474 this should be sufficient for her to get a write-lock (but
4475 not necessarily a read-lock). VICED_CAPABILITY_WRITELOCKACL
4476 indicates whether a file server supports getting write
4477 locks when the user only has PRSFS_INSERT.
4479 If the file was not created by the user we skip the check
4480 because the INSERT bit will not apply to this user even
4484 code2 = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
4485 CM_SCACHESYNC_GETSTATUS |
4486 CM_SCACHESYNC_NEEDCALLBACK);
4488 if (code2 == CM_ERROR_NOACCESS) {
4489 osi_Log0(afsd_logp, "cm_LockCheckPerms user has no INSERT bits");
4491 *phas_insert = TRUE;
4492 osi_Log0(afsd_logp, "cm_LockCheckPerms user has INSERT bits");
4496 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
4498 osi_Log1(afsd_logp, "cm_LockCheckPerms returning code %d", code);
4503 /* called with scp->rw held */
4504 long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
4505 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
4507 int allowWait, cm_user_t *userp, cm_req_t *reqp,
4508 cm_file_lock_t **lockpp)
4511 int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
4512 cm_file_lock_t *fileLock;
4515 int wait_unlock = FALSE;
4516 int force_client_lock = FALSE;
4518 osi_Log4(afsd_logp, "cm_Lock scp 0x%x type 0x%x offset %d length %d",
4519 scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
4520 osi_Log4(afsd_logp, "... allowWait %d key <0x%x, 0x%x, 0x%x>", allowWait,
4521 key.process_id, key.session_id, key.file_id);
4524 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
4526 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
4529 3.1 If L->LockType is exclusive then there does NOT exist a
4530 ACCEPTED lock M in S->fileLocks such that _a_ in
4531 (M->LOffset,+M->LLength).
4533 3.2 If L->LockType is shared then for each ACCEPTED lock M in
4534 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
4535 M->LockType is shared.
4537 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
4539 4.1 M->key != Key(C)
4541 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
4542 and (M->LOffset,+M->LLength) do not intersect.
4545 range.offset = LOffset.QuadPart;
4546 range.length = LLength.QuadPart;
4548 lock_ObtainRead(&cm_scacheLock);
4550 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4552 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4554 if (IS_LOCK_LOST(fileLock)) {
4555 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4556 code = CM_ERROR_BADFD;
4558 } else if (fileLock->lockType == LockWrite && INTERSECT_RANGE(range, fileLock->range)) {
4559 code = CM_ERROR_WOULDBLOCK;
4565 /* we don't need to check for deleted locks here since deleted
4566 locks are dequeued from scp->fileLocks */
4567 if (IS_LOCK_ACCEPTED(fileLock) &&
4568 INTERSECT_RANGE(range, fileLock->range)) {
4570 if ((sLockType & LOCKING_ANDX_SHARED_LOCK) == 0 ||
4571 fileLock->lockType != LockRead) {
4573 code = CM_ERROR_WOULDBLOCK;
4579 lock_ReleaseRead(&cm_scacheLock);
4581 if (code == 0 && SERVERLOCKS_ENABLED(scp)) {
4582 if (Which == scp->serverLock ||
4583 (Which == LockRead && scp->serverLock == LockWrite)) {
4587 /* we already have the lock we need */
4588 osi_Log3(afsd_logp, " we already have the correct lock. exclusives[%d], shared[%d], serverLock[%d]",
4589 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4591 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4593 /* special case: if we don't have permission to read-lock
4594 the file, then we force a clientside lock. This is to
4595 compensate for applications that obtain a read-lock for
4596 reading files off of directories that don't grant
4597 read-locks to the user. */
4598 if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4600 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4601 osi_Log0(afsd_logp, " User has no read-lock perms, but has INSERT perms.");
4604 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4605 force_client_lock = TRUE;
4609 } else if ((scp->exclusiveLocks > 0) ||
4610 (scp->sharedLocks > 0 && scp->serverLock != LockRead)) {
4613 /* We are already waiting for some other lock. We should
4614 wait for the daemon to catch up instead of generating a
4615 flood of SetLock calls. */
4616 osi_Log3(afsd_logp, " already waiting for other lock. exclusives[%d], shared[%d], serverLock[%d]",
4617 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4619 /* see if we have permission to create the lock in the
4621 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4623 code = CM_ERROR_WOULDBLOCK;
4624 else if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4626 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4628 " User has no read-lock perms, but has INSERT perms.");
4629 code = CM_ERROR_WOULDBLOCK;
4632 " User has no read-lock perms. Forcing client-side lock");
4633 force_client_lock = TRUE;
4637 /* leave any other codes as-is */
4641 int check_data_version = FALSE;
4644 /* first check if we have permission to elevate or obtain
4646 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4648 if (code == CM_ERROR_NOACCESS && Which == LockRead &&
4649 (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp))) {
4650 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4651 force_client_lock = TRUE;
4656 /* has_insert => (Which == LockRead, code == CM_ERROR_NOACCESS) */
4658 if (scp->serverLock == LockRead && Which == LockWrite) {
4660 /* We want to escalate the lock to a LockWrite.
4661 * Unfortunately that's not really possible without
4662 * letting go of the current lock. But for now we do
4666 " attempting to UPGRADE from LockRead to LockWrite.");
4668 " dataVersion on scp: %I64d", scp->dataVersion);
4670 /* we assume at this point (because scp->serverLock
4671 was valid) that we had a valid server lock. */
4672 scp->lockDataVersion = scp->dataVersion;
4673 check_data_version = TRUE;
4675 code = cm_IntReleaseLock(scp, userp, reqp);
4678 /* We couldn't release the lock */
4681 scp->serverLock = -1;
4685 /* We need to obtain a server lock of type Which in order
4686 * to assert this file lock */
4687 #ifndef AGGRESSIVE_LOCKS
4690 newLock = LockWrite;
4693 code = cm_IntSetLock(scp, userp, newLock, reqp);
4695 #ifdef AGGRESSIVE_LOCKS
4696 if ((code == CM_ERROR_WOULDBLOCK ||
4697 code == CM_ERROR_NOACCESS) && newLock != Which) {
4698 /* we wanted LockRead. We tried LockWrite. Now try
4703 osi_assertx(newLock == LockRead, "lock type not read");
4705 code = cm_IntSetLock(scp, userp, newLock, reqp);
4709 if (code == CM_ERROR_NOACCESS) {
4710 if (Which == LockRead) {
4711 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4713 /* We requested a read-lock, but we have permission to
4714 * get a write-lock. Try that */
4716 tcode = cm_LockCheckPerms(scp, LockWrite, userp, reqp, NULL);
4719 newLock = LockWrite;
4721 osi_Log0(afsd_logp, " User has 'i' perms and the request was for a LockRead. Trying to get a LockWrite instead");
4723 code = cm_IntSetLock(scp, userp, newLock, reqp);
4726 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4727 force_client_lock = TRUE;
4729 } else if (Which == LockWrite &&
4730 scp->creator == userp && !SCP_SUPPORTS_WRITELOCKACL(scp)) {
4733 /* Special case: if the lock request was for a
4734 * LockWrite and the user owns the file and we weren't
4735 * allowed to obtain the serverlock, we either lost a
4736 * race (the permissions changed from under us), or we
4737 * have 'i' bits, but we aren't allowed to lock the
4740 /* check if we lost a race... */
4741 tcode = cm_LockCheckPerms(scp, Which, userp, reqp, NULL);
4744 osi_Log0(afsd_logp, " User has 'i' perms but can't obtain write locks. Using client-side locks.");
4745 force_client_lock = TRUE;
4750 if (code == 0 && check_data_version &&
4751 scp->dataVersion != scp->lockDataVersion) {
4752 /* We lost a race. Although we successfully obtained
4753 * a lock, someone modified the file in between. The
4754 * locks have all been technically lost. */
4757 " Data version mismatch while upgrading lock.");
4759 " Data versions before=%I64d, after=%I64d",
4760 scp->lockDataVersion,
4763 " Releasing stale lock for scp 0x%x", scp);
4765 code = cm_IntReleaseLock(scp, userp, reqp);
4767 scp->serverLock = -1;
4769 code = CM_ERROR_INVAL;
4770 } else if (code == 0) {
4771 scp->serverLock = newLock;
4772 scp->lockDataVersion = scp->dataVersion;
4776 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4777 scp->serverLock == -1) {
4778 /* Oops. We lost the lock. */
4779 cm_LockMarkSCacheLost(scp);
4782 } else if (code == 0) { /* server locks not enabled */
4784 " Skipping server lock for scp");
4789 if (code != 0 && !force_client_lock) {
4790 /* Special case error translations
4792 Applications don't expect certain errors from a
4793 LockFile/UnlockFile call. We need to translate some error
4794 code to codes that apps expect and handle. */
4796 /* We shouldn't actually need to handle this case since we
4797 simulate locks for RO scps anyway. */
4798 if (code == CM_ERROR_READONLY) {
4799 osi_Log0(afsd_logp, " Reinterpreting CM_ERROR_READONLY as CM_ERROR_NOACCESS");
4800 code = CM_ERROR_NOACCESS;
4804 if (code == 0 || (code == CM_ERROR_WOULDBLOCK && allowWait) ||
4805 force_client_lock) {
4807 /* clear the error if we are forcing a client lock, so we
4808 don't get confused later. */
4809 if (force_client_lock && code != CM_ERROR_WOULDBLOCK)
4812 lock_ObtainWrite(&cm_scacheLock);
4813 fileLock = cm_GetFileLock();
4814 lock_ReleaseWrite(&cm_scacheLock);
4816 fileLock->fid = scp->fid;
4818 fileLock->key = key;
4819 fileLock->lockType = Which;
4821 fileLock->userp = userp;
4822 fileLock->range = range;
4823 fileLock->flags = (code == 0 ? 0 :
4825 CM_FILELOCK_FLAG_WAITUNLOCK :
4826 CM_FILELOCK_FLAG_WAITLOCK));
4828 if (force_client_lock || !SERVERLOCKS_ENABLED(scp))
4829 fileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
4831 fileLock->lastUpdate = (code == 0 && !force_client_lock) ? time(NULL) : 0;
4833 lock_ObtainWrite(&cm_scacheLock);
4834 osi_QAddT(&scp->fileLocksH, &scp->fileLocksT, &fileLock->fileq);
4835 cm_HoldSCacheNoLock(scp);
4836 fileLock->scp = scp;
4837 osi_QAdd(&cm_allFileLocks, &fileLock->q);
4838 lock_ReleaseWrite(&cm_scacheLock);
4844 if (IS_LOCK_CLIENTONLY(fileLock)) {
4846 } else if (IS_LOCK_ACCEPTED(fileLock)) {
4847 if (Which == LockRead)
4850 scp->exclusiveLocks++;
4854 "cm_Lock Lock added 0x%p flags 0x%x to scp [0x%p]",
4855 fileLock, fileLock->flags, scp);
4857 " exclusives[%d] shared[%d] client[%d] serverLock[%d]",
4858 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4859 (int)(signed char) scp->serverLock);
4862 "cm_Lock Rejecting lock (code = 0x%x)", code);
4865 /* Convert from would block to lock not granted */
4866 if (code == CM_ERROR_WOULDBLOCK)
4867 code = CM_ERROR_LOCK_NOT_GRANTED;
4872 /* Called with scp->rw held */
4873 long cm_UnlockByKey(cm_scache_t * scp,
4880 cm_file_lock_t *fileLock;
4881 osi_queue_t *q, *qn;
4884 osi_Log4(afsd_logp, "cm_UnlockByKey scp 0x%p key <0x%x,0x%x,0x%x",
4885 scp, key.process_id, key.session_id, key.file_id);
4886 osi_Log1(afsd_logp, " flags=0x%x", flags);
4888 lock_ObtainWrite(&cm_scacheLock);
4890 for (q = scp->fileLocksH; q; q = qn) {
4893 fileLock = (cm_file_lock_t *)
4894 ((char *) q - offsetof(cm_file_lock_t, fileq));
4897 osi_Log4(afsd_logp, " Checking lock[0x%x] range[%d,+%d] type[%d]",
4899 (unsigned long) fileLock->range.offset,
4900 (unsigned long) fileLock->range.length,
4901 fileLock->lockType);
4902 osi_Log4(afsd_logp, " key<0x%x, 0x%x, 0x%x> flags[0x%x]",
4903 fileLock->key.process_id, fileLock->key.session_id, fileLock->key.file_id,
4906 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
4907 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
4908 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4910 fileLock->fid.volume,
4911 fileLock->fid.vnode,
4912 fileLock->fid.unique);
4913 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4914 fileLock->scp->fid.cell,
4915 fileLock->scp->fid.volume,
4916 fileLock->scp->fid.vnode,
4917 fileLock->scp->fid.unique);
4918 osi_assertx(FALSE, "invalid fid value");
4922 if (!IS_LOCK_DELETED(fileLock) &&
4923 cm_KeyEquals(&fileLock->key, &key, flags)) {
4924 osi_Log3(afsd_logp, "...Unlock range [%d,+%d] type %d",
4925 fileLock->range.offset,
4926 fileLock->range.length,
4927 fileLock->lockType);
4929 if (scp->fileLocksT == q)
4930 scp->fileLocksT = osi_QPrev(q);
4931 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
4933 if (IS_LOCK_CLIENTONLY(fileLock)) {
4935 } else if (IS_LOCK_ACCEPTED(fileLock)) {
4936 if (fileLock->lockType == LockRead)
4939 scp->exclusiveLocks--;
4942 fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
4944 cm_ReleaseUser(fileLock->userp);
4945 cm_ReleaseSCacheNoLock(scp);
4947 fileLock->userp = NULL;
4948 fileLock->scp = NULL;
4954 lock_ReleaseWrite(&cm_scacheLock);
4956 if (n_unlocks == 0) {
4957 osi_Log0(afsd_logp, "cm_UnlockByKey no locks found");
4958 osi_Log3(afsd_logp, " Leaving scp with exclusives[%d], shared[%d], serverLock[%d]",
4959 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4964 osi_Log1(afsd_logp, "cm_UnlockByKey done with %d locks", n_unlocks);
4966 osi_assertx(scp->sharedLocks >= 0, "scp->sharedLocks < 0");
4967 osi_assertx(scp->exclusiveLocks >= 0, "scp->exclusiveLocks < 0");
4968 osi_assertx(scp->clientLocks >= 0, "scp->clientLocks < 0");
4970 if (!SERVERLOCKS_ENABLED(scp)) {
4971 osi_Log0(afsd_logp, " Skipping server lock for scp");
4975 /* Ideally we would go through the rest of the locks to determine
4976 * if one or more locks that were formerly in WAITUNLOCK can now
4977 * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
4978 * scp->sharedLocks accordingly. However, the retrying of locks
4979 * in that manner is done cm_RetryLock() manually.
4982 if (scp->serverLock == LockWrite &&
4983 scp->exclusiveLocks == 0 &&
4984 scp->sharedLocks > 0) {
4985 /* The serverLock should be downgraded to LockRead */
4986 osi_Log0(afsd_logp, " DOWNGRADE lock from LockWrite to LockRead");
4988 /* Make sure there are no dirty buffers left. */
4989 code = cm_FSync(scp, userp, reqp, TRUE);
4991 /* since scp->serverLock looked sane, we are going to assume
4992 that we have a valid server lock. */
4993 scp->lockDataVersion = scp->dataVersion;
4994 osi_Log1(afsd_logp, " dataVersion on scp = %I64d", scp->dataVersion);
4996 code = cm_IntReleaseLock(scp, userp, reqp);
4999 /* so we couldn't release it. Just let the lock be for now */
5003 scp->serverLock = -1;
5006 code = cm_IntSetLock(scp, userp, LockRead, reqp);
5008 if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
5009 scp->serverLock = LockRead;
5010 } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
5011 /* We lost a race condition. Although we have a valid
5012 lock on the file, the data has changed and essentially
5013 we have lost the lock we had during the transition. */
5015 osi_Log0(afsd_logp, "Data version mismatch during lock downgrade");
5016 osi_Log2(afsd_logp, " Data versions before=%I64d, after=%I64d",
5017 scp->lockDataVersion,
5020 code = cm_IntReleaseLock(scp, userp, reqp);
5022 code = CM_ERROR_INVAL;
5023 scp->serverLock = -1;
5027 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
5028 (scp->serverLock == -1)) {
5030 cm_LockMarkSCacheLost(scp);
5033 /* failure here has no bearing on the return value of
5037 } else if (scp->serverLock != (-1) &&
5038 scp->exclusiveLocks == 0 &&
5039 scp->sharedLocks == 0) {
5040 /* The serverLock should be released entirely */
5042 if (scp->serverLock == LockWrite) {
5043 /* Make sure there are no dirty buffers left. */
5044 code = cm_FSync(scp, userp, reqp, TRUE);
5047 code = cm_IntReleaseLock(scp, userp, reqp);
5050 scp->serverLock = (-1);
5055 osi_Log1(afsd_logp, "cm_UnlockByKey code 0x%x", code);
5056 osi_Log4(afsd_logp, " Leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
5057 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
5058 (int)(signed char) scp->serverLock);
5063 long cm_Unlock(cm_scache_t *scp,
5064 unsigned char sLockType,
5065 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
5072 int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
5073 cm_file_lock_t *fileLock;
5075 int release_userp = FALSE;
5076 int exact_match = !(flags & CM_UNLOCK_FLAG_MATCH_RANGE);
5078 LARGE_INTEGER RangeEnd;
5080 osi_Log4(afsd_logp, "cm_Unlock scp 0x%p type 0x%x offset %d length %d",
5081 scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
5082 osi_Log4(afsd_logp, "... key <0x%x,0x%x,0x%x> flags 0x%x",
5083 key.process_id, key.session_id, key.file_id, flags);
5086 RangeEnd.QuadPart = LOffset.QuadPart + LLength.QuadPart;
5089 lock_ObtainRead(&cm_scacheLock);
5091 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5092 fileLock = (cm_file_lock_t *)
5093 ((char *) q - offsetof(cm_file_lock_t, fileq));
5096 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5097 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5098 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5100 fileLock->fid.volume,
5101 fileLock->fid.vnode,
5102 fileLock->fid.unique);
5103 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5104 fileLock->scp->fid.cell,
5105 fileLock->scp->fid.volume,
5106 fileLock->scp->fid.vnode,
5107 fileLock->scp->fid.unique);
5108 osi_assertx(FALSE, "invalid fid value");
5112 if (!IS_LOCK_DELETED(fileLock) &&
5113 cm_KeyEquals(&fileLock->key, &key, 0) &&
5114 fileLock->range.offset == LOffset.QuadPart &&
5115 fileLock->range.length == LLength.QuadPart) {
5121 if (!IS_LOCK_DELETED(fileLock) &&
5122 cm_KeyEquals(&fileLock->key, &key, 0) &&
5123 fileLock->range.offset >= LOffset.QuadPart &&
5124 fileLock->range.offset < RangeEnd.QuadPart &&
5125 (fileLock->range.offset + fileLock->range.length) <= RangeEnd.QuadPart) {
5133 lock_ReleaseRead(&cm_scacheLock);
5135 if (lock_found && !exact_match) {
5139 osi_Log0(afsd_logp, "cm_Unlock lock not found; failure");
5141 /* The lock didn't exist anyway. *shrug* */
5142 return CM_ERROR_RANGE_NOT_LOCKED;
5146 /* discard lock record */
5147 lock_ConvertRToW(&cm_scacheLock);
5148 if (scp->fileLocksT == q)
5149 scp->fileLocksT = osi_QPrev(q);
5150 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
5153 * Don't delete it here; let the daemon delete it, to simplify
5154 * the daemon's traversal of the list.
5157 if (IS_LOCK_CLIENTONLY(fileLock)) {
5159 } else if (IS_LOCK_ACCEPTED(fileLock)) {
5160 if (fileLock->lockType == LockRead)
5163 scp->exclusiveLocks--;
5166 fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5167 if (userp != NULL) {
5168 cm_ReleaseUser(fileLock->userp);
5170 userp = fileLock->userp;
5171 release_userp = TRUE;
5173 fileLock->userp = NULL;
5174 cm_ReleaseSCacheNoLock(scp);
5175 fileLock->scp = NULL;
5176 lock_ReleaseWrite(&cm_scacheLock);
5178 if (!SERVERLOCKS_ENABLED(scp)) {
5179 osi_Log0(afsd_logp, " Skipping server locks for scp");
5183 /* Ideally we would go through the rest of the locks to determine
5184 * if one or more locks that were formerly in WAITUNLOCK can now
5185 * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
5186 * scp->sharedLocks accordingly. However, the retrying of locks
5187 * in that manner is done cm_RetryLock() manually.
5190 if (scp->serverLock == LockWrite &&
5191 scp->exclusiveLocks == 0 &&
5192 scp->sharedLocks > 0) {
5194 /* The serverLock should be downgraded to LockRead */
5195 osi_Log0(afsd_logp, " DOWNGRADE lock from LockWrite to LockRead");
5197 /* Make sure there are no dirty buffers left. */
5198 code = cm_FSync(scp, userp, reqp, TRUE);
5200 /* Since we already had a lock, we assume that there is a
5201 valid server lock. */
5202 scp->lockDataVersion = scp->dataVersion;
5203 osi_Log1(afsd_logp, " dataVersion on scp is %I64d", scp->dataVersion);
5205 /* before we downgrade, make sure that we have enough
5206 permissions to get the read lock. */
5207 code = cm_LockCheckPerms(scp, LockRead, userp, reqp, NULL);
5210 osi_Log0(afsd_logp, " SKIPPING downgrade because user doesn't have perms to get downgraded lock");
5216 code = cm_IntReleaseLock(scp, userp, reqp);
5219 /* so we couldn't release it. Just let the lock be for now */
5223 scp->serverLock = -1;
5226 code = cm_IntSetLock(scp, userp, LockRead, reqp);
5228 if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
5229 scp->serverLock = LockRead;
5230 } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
5231 /* Lost a race. We obtained a new lock, but that is
5232 meaningless since someone modified the file
5236 "Data version mismatch while downgrading lock");
5238 " Data versions before=%I64d, after=%I64d",
5239 scp->lockDataVersion,
5242 code = cm_IntReleaseLock(scp, userp, reqp);
5244 scp->serverLock = -1;
5245 code = CM_ERROR_INVAL;
5249 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
5250 (scp->serverLock == -1)) {
5252 cm_LockMarkSCacheLost(scp);
5255 /* failure here has no bearing on the return value of
5259 } else if (scp->serverLock != (-1) &&
5260 scp->exclusiveLocks == 0 &&
5261 scp->sharedLocks == 0) {
5262 /* The serverLock should be released entirely */
5264 if (scp->serverLock == LockWrite) {
5265 /* Make sure there are no dirty buffers left. */
5266 code = cm_FSync(scp, userp, reqp, TRUE);
5269 code = cm_IntReleaseLock(scp, userp, reqp);
5272 scp->serverLock = (-1);
5276 if (release_userp) {
5277 cm_ReleaseUser(userp);
5278 release_userp = FALSE;
5282 goto try_again; /* might be more than one lock in the range */
5286 osi_Log1(afsd_logp, "cm_Unlock code 0x%x", code);
5287 osi_Log4(afsd_logp, " leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
5288 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
5289 (int)(signed char) scp->serverLock);
5294 /* called with scp->rw held */
5295 void cm_LockMarkSCacheLost(cm_scache_t * scp)
5297 cm_file_lock_t *fileLock;
5300 osi_Log1(afsd_logp, "cm_LockMarkSCacheLost scp 0x%x", scp);
5302 /* cm_scacheLock needed because we are modifying fileLock->flags */
5303 lock_ObtainWrite(&cm_scacheLock);
5305 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5307 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
5309 if (IS_LOCK_ACTIVE(fileLock) &&
5310 !IS_LOCK_CLIENTONLY(fileLock)) {
5311 if (fileLock->lockType == LockRead)
5314 scp->exclusiveLocks--;
5316 fileLock->flags |= CM_FILELOCK_FLAG_LOST;
5320 scp->serverLock = -1;
5321 scp->lockDataVersion = CM_SCACHE_VERSION_BAD;
5322 lock_ReleaseWrite(&cm_scacheLock);
5325 /* Called with no relevant locks held */
5326 void cm_CheckLocks()
5328 osi_queue_t *q, *nq;
5329 cm_file_lock_t *fileLock;
5335 struct rx_connection * rxconnp;
5338 memset(&volSync, 0, sizeof(volSync));
5342 lock_ObtainWrite(&cm_scacheLock);
5344 cm_lockRefreshCycle++;
5346 osi_Log1(afsd_logp, "cm_CheckLocks starting lock check cycle %d", cm_lockRefreshCycle);
5348 for (q = cm_allFileLocks; q; q = nq) {
5349 fileLock = (cm_file_lock_t *) q;
5353 if (IS_LOCK_DELETED(fileLock)) {
5355 osi_QRemove(&cm_allFileLocks, q);
5356 cm_PutFileLock(fileLock);
5358 } else if (IS_LOCK_ACTIVE(fileLock) && !IS_LOCK_CLIENTONLY(fileLock)) {
5360 /* Server locks must have been enabled for us to have
5361 received an active non-client-only lock. */
5362 osi_assertx(cm_enableServerLocks, "!cm_enableServerLocks");
5364 scp = fileLock->scp;
5365 osi_assertx(scp != NULL, "null cm_scache_t");
5367 cm_HoldSCacheNoLock(scp);
5370 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5371 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5372 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5374 fileLock->fid.volume,
5375 fileLock->fid.vnode,
5376 fileLock->fid.unique);
5377 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5378 fileLock->scp->fid.cell,
5379 fileLock->scp->fid.volume,
5380 fileLock->scp->fid.vnode,
5381 fileLock->scp->fid.unique);
5382 osi_assertx(FALSE, "invalid fid");
5385 /* Server locks are extended once per scp per refresh
5387 if (scp->lastRefreshCycle != cm_lockRefreshCycle) {
5389 int scp_done = FALSE;
5391 osi_Log1(afsd_logp, "cm_CheckLocks Updating scp 0x%x", scp);
5393 lock_ReleaseWrite(&cm_scacheLock);
5394 lock_ObtainWrite(&scp->rw);
5396 /* did the lock change while we weren't holding the lock? */
5397 if (!IS_LOCK_ACTIVE(fileLock))
5398 goto post_syncopdone;
5400 code = cm_SyncOp(scp, NULL, fileLock->userp, &req, 0,
5401 CM_SCACHESYNC_NEEDCALLBACK
5402 | CM_SCACHESYNC_GETSTATUS
5403 | CM_SCACHESYNC_LOCK);
5407 "cm_CheckLocks SyncOp failure code 0x%x", code);
5408 goto post_syncopdone;
5411 /* cm_SyncOp releases scp->rw during which the lock
5412 may get released. */
5413 if (!IS_LOCK_ACTIVE(fileLock))
5414 goto pre_syncopdone;
5416 if (scp->serverLock != -1 && !(scp->flags & CM_SCACHEFLAG_DELETED)) {
5420 tfid.Volume = scp->fid.volume;
5421 tfid.Vnode = scp->fid.vnode;
5422 tfid.Unique = scp->fid.unique;
5424 userp = fileLock->userp;
5426 osi_Log3(afsd_logp, "CALL ExtendLock lock 0x%p for scp=0x%p with lock %d",
5429 (int) scp->serverLock);
5431 lock_ReleaseWrite(&scp->rw);
5434 code = cm_ConnFromFID(&cfid, userp,
5439 rxconnp = cm_GetRxConn(connp);
5440 code = RXAFS_ExtendLock(rxconnp, &tfid,
5442 rx_PutConnection(rxconnp);
5444 osi_Log1(afsd_logp, " ExtendLock returns %d", code);
5446 } while (cm_Analyze(connp, userp, &req,
5447 &cfid, &volSync, NULL, NULL,
5450 code = cm_MapRPCError(code, &req);
5452 lock_ObtainWrite(&scp->rw);
5455 osi_Log1(afsd_logp, "CALL ExtendLock FAILURE, code 0x%x", code);
5457 osi_Log0(afsd_logp, "CALL ExtendLock SUCCESS");
5458 scp->lockDataVersion = scp->dataVersion;
5461 if ((code == EINVAL || code == CM_ERROR_INVAL) &&
5462 scp->lockDataVersion == scp->dataVersion) {
5466 (scp->exclusiveLocks > 0) ? LockWrite: LockRead;
5468 /* we might still have a chance to obtain a
5471 code = cm_IntSetLock(scp, userp, lockType, &req);
5474 code = CM_ERROR_INVAL;
5475 } else if (scp->lockDataVersion != scp->dataVersion) {
5477 /* now check if we still have the file at
5478 the right data version. */
5480 "Data version mismatch on scp 0x%p",
5483 " Data versions: before=%I64d, after=%I64d",
5484 scp->lockDataVersion,
5487 code = cm_IntReleaseLock(scp, userp, &req);
5489 code = CM_ERROR_INVAL;
5493 if (code == EINVAL || code == CM_ERROR_INVAL ||
5494 code == CM_ERROR_BADFD) {
5495 cm_LockMarkSCacheLost(scp);
5499 /* interestingly, we have found an active lock
5500 belonging to an scache that has no
5502 cm_LockMarkSCacheLost(scp);
5509 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5512 lock_ReleaseWrite(&scp->rw);
5514 lock_ObtainWrite(&cm_scacheLock);
5517 fileLock->lastUpdate = time(NULL);
5521 scp->lastRefreshCycle = cm_lockRefreshCycle;
5524 /* we have already refreshed the locks on this scp */
5525 fileLock->lastUpdate = time(NULL);
5528 cm_ReleaseSCacheNoLock(scp);
5530 } else if (IS_LOCK_ACTIVE(fileLock) && IS_LOCK_CLIENTONLY(fileLock)) {
5531 /* TODO: Check callbacks */
5535 lock_ReleaseWrite(&cm_scacheLock);
5536 osi_Log1(afsd_logp, "cm_CheckLocks completes lock check cycle %d", cm_lockRefreshCycle);
5539 /* NOT called with scp->rw held. */
5540 long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
5543 cm_scache_t *scp = NULL;
5544 cm_file_lock_t *fileLock;
5548 int force_client_lock = FALSE;
5549 int has_insert = FALSE;
5550 int check_data_version = FALSE;
5554 if (client_is_dead) {
5555 code = CM_ERROR_TIMEDOUT;
5559 lock_ObtainRead(&cm_scacheLock);
5561 osi_Log2(afsd_logp, "cm_RetryLock checking lock %p (scp=%p)", oldFileLock, oldFileLock->scp);
5562 osi_Log4(afsd_logp, " offset(%x:%x) length(%x:%x)",
5563 (unsigned)(oldFileLock->range.offset >> 32),
5564 (unsigned)(oldFileLock->range.offset & 0xffffffff),
5565 (unsigned)(oldFileLock->range.length >> 32),
5566 (unsigned)(oldFileLock->range.length & 0xffffffff));
5567 osi_Log4(afsd_logp, " key<0x%x,0x%x,0x%x> flags=%x",
5568 oldFileLock->key.process_id, oldFileLock->key.session_id, oldFileLock->key.file_id,
5569 (unsigned)(oldFileLock->flags));
5571 /* if the lock has already been granted, then we have nothing to do */
5572 if (IS_LOCK_ACTIVE(oldFileLock)) {
5573 lock_ReleaseRead(&cm_scacheLock);
5574 osi_Log0(afsd_logp, "cm_RetryLock lock already granted");
5578 /* we can't do anything with lost or deleted locks at the moment. */
5579 if (IS_LOCK_LOST(oldFileLock) || IS_LOCK_DELETED(oldFileLock)) {
5580 code = CM_ERROR_BADFD;
5581 osi_Log0(afsd_logp, "cm_RetryLock lock is lost or deleted");
5582 lock_ReleaseRead(&cm_scacheLock);
5586 scp = oldFileLock->scp;
5588 osi_assertx(scp != NULL, "null cm_scache_t");
5590 lock_ReleaseRead(&cm_scacheLock);
5591 lock_ObtainWrite(&scp->rw);
5593 code = cm_LockCheckPerms(scp, oldFileLock->lockType,
5597 if (code == CM_ERROR_NOACCESS && oldFileLock->lockType == LockRead) {
5598 if (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp)) {
5599 force_client_lock = TRUE;
5603 lock_ReleaseWrite(&scp->rw);
5607 lock_ObtainWrite(&cm_scacheLock);
5609 /* Check if we already have a sufficient server lock to allow this
5610 lock to go through. */
5611 if (IS_LOCK_WAITLOCK(oldFileLock) &&
5612 (!SERVERLOCKS_ENABLED(scp) ||
5613 scp->serverLock == oldFileLock->lockType ||
5614 scp->serverLock == LockWrite)) {
5616 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5618 if (SERVERLOCKS_ENABLED(scp)) {
5619 osi_Log1(afsd_logp, "cm_RetryLock Server lock (%d) is sufficient for lock. Granting",
5620 (int) scp->serverLock);
5622 osi_Log0(afsd_logp, "cm_RetryLock skipping server lock for scp");
5625 lock_ReleaseWrite(&cm_scacheLock);
5626 lock_ReleaseWrite(&scp->rw);
5631 if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5633 /* check if the conflicting locks have dissappeared already */
5634 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5636 fileLock = (cm_file_lock_t *)
5637 ((char *) q - offsetof(cm_file_lock_t, fileq));
5639 if (IS_LOCK_LOST(fileLock)) {
5640 if (cm_KeyEquals(&fileLock->key, &oldFileLock->key, 0)) {
5641 code = CM_ERROR_BADFD;
5642 oldFileLock->flags |= CM_FILELOCK_FLAG_LOST;
5643 osi_Log1(afsd_logp, " found lost lock %p for same key. Marking lock as lost",
5646 } else if (fileLock->lockType == LockWrite &&
5647 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5648 osi_Log1(afsd_logp, " found conflicting LOST lock %p", fileLock);
5649 code = CM_ERROR_WOULDBLOCK;
5654 if (IS_LOCK_ACCEPTED(fileLock) &&
5655 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5657 if (oldFileLock->lockType != LockRead ||
5658 fileLock->lockType != LockRead) {
5660 osi_Log1(afsd_logp, " found conflicting lock %p", fileLock);
5661 code = CM_ERROR_WOULDBLOCK;
5669 lock_ReleaseWrite(&cm_scacheLock);
5670 lock_ReleaseWrite(&scp->rw);
5675 /* when we get here, the lock is either a WAITUNLOCK or WAITLOCK.
5676 If it is WAITUNLOCK, then we didn't find any conflicting lock
5677 but we haven't verfied whether the serverLock is sufficient to
5678 assert it. If it is WAITLOCK, then the serverLock is
5679 insufficient to assert it. Eitherway, we are ready to accept
5680 the lock as either ACTIVE or WAITLOCK depending on the
5683 /* First, promote the WAITUNLOCK to a WAITLOCK */
5684 if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5685 if (oldFileLock->lockType == LockRead)
5688 scp->exclusiveLocks++;
5690 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITUNLOCK;
5691 oldFileLock->flags |= CM_FILELOCK_FLAG_WAITLOCK;
5694 osi_assertx(IS_LOCK_WAITLOCK(oldFileLock), "!IS_LOCK_WAITLOCK");
5696 if (force_client_lock ||
5697 !SERVERLOCKS_ENABLED(scp) ||
5698 scp->serverLock == oldFileLock->lockType ||
5699 (oldFileLock->lockType == LockRead &&
5700 scp->serverLock == LockWrite)) {
5702 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5704 if ((force_client_lock ||
5705 !SERVERLOCKS_ENABLED(scp)) &&
5706 !IS_LOCK_CLIENTONLY(oldFileLock)) {
5708 oldFileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
5710 if (oldFileLock->lockType == LockRead)
5713 scp->exclusiveLocks--;
5718 lock_ReleaseWrite(&cm_scacheLock);
5719 lock_ReleaseWrite(&scp->rw);
5726 code = cm_SyncOp(scp, NULL, oldFileLock->userp, &req, 0,
5727 CM_SCACHESYNC_NEEDCALLBACK
5728 | CM_SCACHESYNC_GETSTATUS
5729 | CM_SCACHESYNC_LOCK);
5731 osi_Log1(afsd_logp, "cm_RetryLock SyncOp failure code 0x%x", code);
5732 lock_ReleaseWrite(&cm_scacheLock);
5733 goto post_syncopdone;
5736 if (!IS_LOCK_WAITLOCK(oldFileLock))
5737 goto pre_syncopdone;
5739 userp = oldFileLock->userp;
5741 #ifndef AGGRESSIVE_LOCKS
5742 newLock = oldFileLock->lockType;
5744 newLock = LockWrite;
5748 /* if has_insert is non-zero, then:
5749 - the lock a LockRead
5750 - we don't have permission to get a LockRead
5751 - we do have permission to get a LockWrite
5752 - the server supports VICED_CAPABILITY_WRITELOCKACL
5755 newLock = LockWrite;
5758 lock_ReleaseWrite(&cm_scacheLock);
5760 /* when we get here, either we have a read-lock and want a
5761 write-lock or we don't have any locks and we want some
5764 if (scp->serverLock == LockRead) {
5766 osi_assertx(newLock == LockWrite, "!LockWrite");
5768 osi_Log0(afsd_logp, " Attempting to UPGRADE from LockRead to LockWrite");
5770 scp->lockDataVersion = scp->dataVersion;
5771 check_data_version = TRUE;
5773 code = cm_IntReleaseLock(scp, userp, &req);
5776 goto pre_syncopdone;
5778 scp->serverLock = -1;
5781 code = cm_IntSetLock(scp, userp, newLock, &req);
5784 if (scp->dataVersion != scp->lockDataVersion) {
5785 /* we lost a race. too bad */
5788 " Data version mismatch while upgrading lock.");
5790 " Data versions before=%I64d, after=%I64d",
5791 scp->lockDataVersion,
5794 " Releasing stale lock for scp 0x%x", scp);
5796 code = cm_IntReleaseLock(scp, userp, &req);
5798 scp->serverLock = -1;
5800 code = CM_ERROR_INVAL;
5802 cm_LockMarkSCacheLost(scp);
5804 scp->serverLock = newLock;
5809 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5815 if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
5816 lock_ObtainWrite(&cm_scacheLock);
5817 if (scp->fileLocksT == &oldFileLock->fileq)
5818 scp->fileLocksT = osi_QPrev(&oldFileLock->fileq);
5819 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, &oldFileLock->fileq);
5820 lock_ReleaseWrite(&cm_scacheLock);
5822 lock_ReleaseWrite(&scp->rw);
5825 lock_ObtainWrite(&cm_scacheLock);
5827 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5828 } else if (code != CM_ERROR_WOULDBLOCK) {
5829 oldFileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5830 cm_ReleaseUser(oldFileLock->userp);
5831 oldFileLock->userp = NULL;
5832 if (oldFileLock->scp) {
5833 cm_ReleaseSCacheNoLock(oldFileLock->scp);
5834 oldFileLock->scp = NULL;
5837 lock_ReleaseWrite(&cm_scacheLock);
5842 cm_key_t cm_GenerateKey(afs_uint16 session_id, afs_offs_t process_id, afs_uint16 file_id)
5846 key.process_id = process_id;
5847 key.session_id = session_id;
5848 key.file_id = file_id;
5853 int cm_KeyEquals(cm_key_t *k1, cm_key_t *k2, int flags)
5855 return (k1->session_id == k2->session_id) && (k1->file_id == k2->file_id) &&
5856 ((flags & CM_UNLOCK_BY_FID) || (k1->process_id == k2->process_id));
5859 void cm_ReleaseAllLocks(void)
5865 cm_file_lock_t *fileLock;
5868 for (i = 0; i < cm_data.scacheHashTableSize; i++)
5870 for ( scp = cm_data.scacheHashTablep[i]; scp; scp = scp->nextp ) {
5871 while (scp->fileLocksH != NULL) {
5872 lock_ObtainWrite(&scp->rw);
5873 lock_ObtainWrite(&cm_scacheLock);
5874 if (!scp->fileLocksH) {
5875 lock_ReleaseWrite(&cm_scacheLock);
5876 lock_ReleaseWrite(&scp->rw);
5879 fileLock = (cm_file_lock_t *)((char *) scp->fileLocksH - offsetof(cm_file_lock_t, fileq));
5880 userp = fileLock->userp;
5882 key = fileLock->key;
5883 cm_HoldSCacheNoLock(scp);
5884 lock_ReleaseWrite(&cm_scacheLock);
5885 cm_UnlockByKey(scp, key, 0, userp, &req);
5886 cm_ReleaseSCache(scp);
5887 cm_ReleaseUser(userp);
5888 lock_ReleaseWrite(&scp->rw);