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 <afsconfig.h>
11 #include <afs/param.h>
33 extern void afsi_log(char *pattern, ...);
36 int cm_enableServerLocks = 1;
38 int cm_followBackupPath = 0;
41 * Case-folding array. This was constructed by inspecting of SMBtrace output.
42 * I do not know anything more about it.
44 unsigned char cm_foldUpper[256] = {
45 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
46 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
47 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
48 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
49 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
50 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
51 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
52 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
53 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
54 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
55 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
56 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
57 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
58 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
59 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
60 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
61 0x80, 0x9a, 0x90, 0x41, 0x8e, 0x41, 0x8f, 0x80,
62 0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x8e, 0x8f,
63 0x90, 0x92, 0x92, 0x4f, 0x99, 0x4f, 0x55, 0x55,
64 0x59, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
65 0x41, 0x49, 0x4f, 0x55, 0xa5, 0xa5, 0x56, 0xa7,
66 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
67 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
68 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
69 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
70 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
71 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
72 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
73 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
74 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
75 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
76 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
80 * Case-insensitive string comparison. We used to use stricmp, but it doesn't
81 * know about 8-bit characters (e.g. 129 is lowercase u-umlaut, 154 is
82 * upper-case u-umlaut).
84 int cm_stricmp(const char *str1, const char *str2)
96 c1 = (char) cm_foldUpper[(unsigned char)(*str1++)];
97 c2 = (char) cm_foldUpper[(unsigned char)(*str2++)];
107 /* return success if we can open this file in this mode */
108 long cm_CheckOpen(cm_scache_t *scp, int openMode, int trunc, cm_user_t *userp,
116 rights |= PRSFS_READ;
117 if (openMode == 1 || openMode == 2 || trunc)
118 rights |= PRSFS_WRITE;
120 lock_ObtainWrite(&scp->rw);
122 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
123 CM_SCACHESYNC_GETSTATUS
124 | CM_SCACHESYNC_NEEDCALLBACK
125 | CM_SCACHESYNC_LOCK);
128 ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
129 scp->fileType == CM_SCACHETYPE_FILE) {
132 unsigned int sLockType;
133 LARGE_INTEGER LOffset, LLength;
135 /* Check if there's some sort of lock on the file at the
138 key = cm_GenerateKey(CM_SESSION_CMINT,0,0);
140 if (rights & PRSFS_WRITE)
143 sLockType = LOCKING_ANDX_SHARED_LOCK;
145 LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
146 LOffset.LowPart = CM_FLSHARE_OFFSET_LOW;
147 LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
148 LLength.LowPart = CM_FLSHARE_LENGTH_LOW;
150 code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
153 cm_Unlock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp);
155 /* In this case, we allow the file open to go through even
156 though we can't enforce mandatory locking on the
158 if (code == CM_ERROR_NOACCESS &&
159 !(rights & PRSFS_WRITE))
162 if (code == CM_ERROR_LOCK_NOT_GRANTED)
163 code = CM_ERROR_SHARING_VIOLATION;
167 } else if (code != 0) {
171 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
175 lock_ReleaseWrite(&scp->rw);
180 /* return success if we can open this file in this mode */
181 long cm_CheckNTOpen(cm_scache_t *scp,
182 unsigned int desiredAccess,
183 unsigned int shareAccess,
184 unsigned int createDisp,
185 afs_offs_t process_id,
186 afs_offs_t handle_id,
187 cm_user_t *userp, cm_req_t *reqp,
188 cm_lock_data_t **ldpp)
192 afs_uint16 session_id;
194 osi_assertx(ldpp != NULL, "null cm_lock_data_t");
197 /* compute the session id */
198 if (reqp->flags & CM_REQ_SOURCE_SMB)
199 session_id = CM_SESSION_SMB;
200 else if (reqp->flags & CM_REQ_SOURCE_REDIR)
201 session_id = CM_SESSION_IFS;
203 session_id = CM_SESSION_CMINT;
205 /* Ignore the SYNCHRONIZE privilege */
206 desiredAccess &= ~SYNCHRONIZE;
208 /* Always allow delete; the RPC will tell us if it's OK */
211 if (desiredAccess == DELETE)
214 /* Always allow reading attributes (Hidden, System, Readonly, ...) */
215 if (desiredAccess == FILE_READ_ATTRIBUTES)
218 if (desiredAccess & (AFS_ACCESS_READ|AFS_ACCESS_EXECUTE))
219 rights |= (scp->fileType == CM_SCACHETYPE_DIRECTORY ? PRSFS_LOOKUP : PRSFS_READ);
221 /* We used to require PRSFS_WRITE if createDisp was 4
222 (OPEN_ALWAYS) even if AFS_ACCESS_WRITE was not requested.
223 However, we don't need to do that since the existence of the
224 scp implies that we don't need to create it. */
225 if (desiredAccess & AFS_ACCESS_WRITE)
226 rights |= PRSFS_WRITE;
228 if (desiredAccess & DELETE)
229 rights |= PRSFS_DELETE;
231 lock_ObtainWrite(&scp->rw);
233 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
234 CM_SCACHESYNC_GETSTATUS
235 | CM_SCACHESYNC_NEEDCALLBACK
236 | CM_SCACHESYNC_LOCK);
239 * If the open will fail because the volume is readonly, then we will
240 * return an access denied error instead. This is to help brain-dead
241 * apps run correctly on replicated volumes.
242 * See defect 10007 for more information.
244 if (code == CM_ERROR_READONLY)
245 code = CM_ERROR_NOACCESS;
248 !(shareAccess & FILE_SHARE_WRITE) &&
249 ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
250 scp->fileType == CM_SCACHETYPE_FILE) {
252 unsigned int sLockType;
253 LARGE_INTEGER LOffset, LLength;
255 /* Check if there's some sort of lock on the file at the
258 if (rights & PRSFS_WRITE)
261 sLockType = LOCKING_ANDX_SHARED_LOCK;
263 key = cm_GenerateKey(session_id, process_id, 0);
265 /* single byte lock at offset 0x0100 0000 0000 0000 */
266 LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
267 LOffset.LowPart = CM_FLSHARE_OFFSET_LOW;
268 LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
269 LLength.LowPart = CM_FLSHARE_LENGTH_LOW;
271 code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
274 (*ldpp) = (cm_lock_data_t *)malloc(sizeof(cm_lock_data_t));
281 (*ldpp)->sLockType = sLockType;
282 (*ldpp)->LOffset.HighPart = LOffset.HighPart;
283 (*ldpp)->LOffset.LowPart = LOffset.LowPart;
284 (*ldpp)->LLength.HighPart = LLength.HighPart;
285 (*ldpp)->LLength.LowPart = LLength.LowPart;
288 * In this case, we allow the file open to go through even
289 * though we can't enforce mandatory locking on the
291 if (code == CM_ERROR_NOACCESS &&
292 !(rights & PRSFS_WRITE))
295 if (code == CM_ERROR_LOCK_NOT_GRANTED)
296 code = CM_ERROR_SHARING_VIOLATION;
299 } else if (code != 0) {
304 lock_ReleaseWrite(&scp->rw);
307 osi_Log3(afsd_logp,"cm_CheckNTOpen scp 0x%p ldp 0x%p code 0x%x", scp, *ldpp, code);
311 extern long cm_CheckNTOpenDone(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp,
312 cm_lock_data_t ** ldpp)
314 osi_Log2(afsd_logp,"cm_CheckNTOpenDone scp 0x%p ldp 0x%p", scp, ldpp ? *ldpp : 0);
315 lock_ObtainWrite(&scp->rw);
317 cm_Unlock(scp, (*ldpp)->sLockType, (*ldpp)->LOffset, (*ldpp)->LLength,
318 (*ldpp)->key, 0, userp, reqp);
322 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
323 lock_ReleaseWrite(&scp->rw);
327 * When CAP_NT_SMBS has been negotiated, deletion (of files or directories) is
328 * done in three steps:
329 * (1) open for deletion (NT_CREATE_AND_X)
330 * (2) set for deletion on close (NT_TRANSACTION2, SET_FILE_INFO)
332 * We must not do the RPC until step 3. But if we are going to return an error
333 * code (e.g. directory not empty), we must return it by step 2, otherwise most
334 * clients will not notice it. So we do a preliminary check. For deleting
335 * files, this is almost free, since we have already done the RPC to get the
336 * parent directory's status bits. But for deleting directories, we must do an
337 * additional RPC to get the directory's data to check if it is empty. Sigh.
339 long cm_CheckNTDelete(cm_scache_t *dscp, cm_scache_t *scp, cm_user_t *userp,
345 cm_dirEntry_t *dep = 0;
346 unsigned short *hashTable;
348 int BeyondPage = 0, HaveDot = 0, HaveDotDot = 0;
351 /* First check permissions */
352 lock_ObtainWrite(&scp->rw);
353 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_DELETE,
354 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
356 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
357 lock_ReleaseWrite(&scp->rw);
361 /* If deleting directory, must be empty */
363 if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
366 thyper.HighPart = 0; thyper.LowPart = 0;
367 code = buf_Get(scp, &thyper, reqp, &bufferp);
371 lock_ObtainMutex(&bufferp->mx);
372 lock_ObtainWrite(&scp->rw);
375 code = cm_SyncOp(scp, bufferp, userp, reqp, 0,
376 CM_SCACHESYNC_NEEDCALLBACK
378 | CM_SCACHESYNC_BUFLOCKED);
382 if (cm_HaveBuffer(scp, bufferp, 1))
385 /* otherwise, load the buffer and try again */
386 lock_ReleaseMutex(&bufferp->mx);
387 code = cm_GetBuffer(scp, bufferp, NULL, userp, reqp);
388 lock_ReleaseWrite(&scp->rw);
389 lock_ObtainMutex(&bufferp->mx);
390 lock_ObtainWrite(&scp->rw);
391 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
396 lock_ReleaseWrite(&scp->rw);
399 /* We try to determine emptiness without looking beyond the first page,
400 * and without assuming "." and ".." are present and are on the first
401 * page (though these assumptions might, after all, be reasonable).
403 hashTable = (unsigned short *)(bufferp->datap + (32 * 5));
404 for (i=0; i<128; i++) {
405 idx = ntohs(hashTable[i]);
411 dep = (cm_dirEntry_t *)(bufferp->datap + (32 * idx));
412 if (strcmp(dep->name, ".") == 0)
414 else if (strcmp(dep->name, "..") == 0)
417 code = CM_ERROR_NOTEMPTY;
420 idx = ntohs(dep->next);
423 if (BeyondPage && HaveDot && HaveDotDot)
424 code = CM_ERROR_NOTEMPTY;
428 lock_ReleaseMutex(&bufferp->mx);
429 buf_Release(bufferp);
431 lock_ReleaseWrite(&scp->rw);
436 * Iterate through all entries in a directory.
437 * When the function funcp is called, the buffer is locked but the
438 * directory vnode is not.
440 * If the retscp parameter is not NULL, the parmp must be a
441 * cm_lookupSearch_t object.
443 long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
444 osi_hyper_t *startOffsetp, cm_user_t *userp, cm_req_t *reqp,
445 cm_scache_t **retscp)
449 cm_dirEntry_t *dep = 0;
452 osi_hyper_t dirLength;
453 osi_hyper_t bufferOffset;
454 osi_hyper_t curOffset;
458 cm_pageHeader_t *pageHeaderp;
460 long nextEntryCookie;
461 int numDirChunks; /* # of 32 byte dir chunks in this entry */
463 /* get the directory size */
464 lock_ObtainWrite(&scp->rw);
465 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_LOOKUP,
466 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
467 lock_ReleaseWrite(&scp->rw);
471 if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
472 return CM_ERROR_NOTDIR;
474 if (retscp) /* if this is a lookup call */
476 cm_lookupSearch_t* sp = parmp;
479 #ifdef AFS_FREELANCE_CLIENT
480 /* Freelance entries never end up in the DNLC because they
481 * do not have an associated cm_server_t
483 !(cm_freelanceEnabled &&
484 sp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
485 sp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
486 #else /* !AFS_FREELANCE_CLIENT */
491 int casefold = sp->caseFold;
492 sp->caseFold = 0; /* we have a strong preference for exact matches */
493 if ( *retscp = cm_dnlcLookup(scp, sp)) /* dnlc hit */
495 sp->caseFold = casefold;
498 sp->caseFold = casefold;
502 * see if we can find it using the directory hash tables.
503 * we can only do exact matches, since the hash is case
506 if (funcp != (cm_DirFuncp_t)cm_BPlusDirFoo)
515 code = cm_BeginDirOp(scp, userp, reqp, CM_DIRLOCK_READ,
516 CM_DIROP_FLAG_NONE, &dirop);
520 code = cm_BPlusDirLookup(&dirop, sp->nsearchNamep, &sp->fid);
525 code = cm_DirLookup(&dirop, sp->searchNamep, &sp->fid);
533 sp->ExactFound = TRUE;
534 *retscp = NULL; /* force caller to call cm_GetSCache() */
539 if (sp->caseFold && code == CM_ERROR_INEXACT_MATCH) {
542 sp->ExactFound = FALSE;
543 *retscp = NULL; /* force caller to call cm_GetSCache() */
547 return CM_ERROR_BPLUS_NOMATCH;
554 * XXX We only get the length once. It might change when we drop the
557 dirLength = scp->length;
560 bufferOffset.LowPart = bufferOffset.HighPart = 0;
562 curOffset = *startOffsetp;
564 curOffset.HighPart = 0;
565 curOffset.LowPart = 0;
569 /* make sure that curOffset.LowPart doesn't point to the first
570 * 32 bytes in the 2nd through last dir page, and that it
571 * doesn't point at the first 13 32-byte chunks in the first
572 * dir page, since those are dir and page headers, and don't
573 * contain useful information.
575 temp = curOffset.LowPart & (2048-1);
576 if (curOffset.HighPart == 0 && curOffset.LowPart < 2048) {
577 /* we're in the first page */
578 if (temp < 13*32) temp = 13*32;
581 /* we're in a later dir page */
582 if (temp < 32) temp = 32;
585 /* make sure the low order 5 bits are zero */
588 /* now put temp bits back ito curOffset.LowPart */
589 curOffset.LowPart &= ~(2048-1);
590 curOffset.LowPart |= temp;
592 /* check if we've passed the dir's EOF */
593 if (LargeIntegerGreaterThanOrEqualTo(curOffset, dirLength))
596 /* see if we can use the bufferp we have now; compute in which
597 * page the current offset would be, and check whether that's
598 * the offset of the buffer we have. If not, get the buffer.
600 thyper.HighPart = curOffset.HighPart;
601 thyper.LowPart = curOffset.LowPart & ~(cm_data.buf_blockSize-1);
602 if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
605 lock_ReleaseMutex(&bufferp->mx);
606 buf_Release(bufferp);
610 code = buf_Get(scp, &thyper, reqp, &bufferp);
612 /* if buf_Get() fails we do not have a buffer object to lock */
617 lock_ObtainMutex(&bufferp->mx);
618 bufferOffset = thyper;
620 /* now get the data in the cache */
622 lock_ObtainWrite(&scp->rw);
623 code = cm_SyncOp(scp, bufferp, userp, reqp,
625 CM_SCACHESYNC_NEEDCALLBACK
627 | CM_SCACHESYNC_BUFLOCKED);
629 lock_ReleaseWrite(&scp->rw);
632 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
634 if (cm_HaveBuffer(scp, bufferp, 1)) {
635 lock_ReleaseWrite(&scp->rw);
639 /* otherwise, load the buffer and try again */
640 lock_ReleaseMutex(&bufferp->mx);
641 code = cm_GetBuffer(scp, bufferp, NULL, userp,
643 lock_ReleaseWrite(&scp->rw);
644 lock_ObtainMutex(&bufferp->mx);
649 lock_ReleaseMutex(&bufferp->mx);
650 buf_Release(bufferp);
654 } /* if (wrong buffer) ... */
656 /* now we have the buffer containing the entry we're interested
657 * in; copy it out if it represents a non-deleted entry.
659 entryInDir = curOffset.LowPart & (2048-1);
660 entryInBuffer = curOffset.LowPart & (cm_data.buf_blockSize - 1);
662 /* page header will help tell us which entries are free. Page
663 * header can change more often than once per buffer, since
664 * AFS 3 dir page size may be less than (but not more than) a
665 * buffer package buffer.
667 /* only look intra-buffer */
668 temp = curOffset.LowPart & (cm_data.buf_blockSize - 1);
669 temp &= ~(2048 - 1); /* turn off intra-page bits */
670 pageHeaderp = (cm_pageHeader_t *) (bufferp->datap + temp);
672 /* now determine which entry we're looking at in the page. If
673 * it is free (there's a free bitmap at the start of the dir),
674 * we should skip these 32 bytes.
676 slotInPage = (entryInDir & 0x7e0) >> 5;
677 if (!(pageHeaderp->freeBitmap[slotInPage>>3]
678 & (1 << (slotInPage & 0x7)))) {
679 /* this entry is free */
680 numDirChunks = 1; /* only skip this guy */
684 tp = bufferp->datap + entryInBuffer;
685 dep = (cm_dirEntry_t *) tp; /* now points to AFS3 dir entry */
688 * here are some consistency checks
690 if (dep->flag != CM_DIR_FFIRST ||
691 strlen(dep->name) > 256) {
692 code = CM_ERROR_INVAL;
694 "cm_ApplyDir invalid directory entry for scp %p bufp %p",
696 osi_Log4(afsd_logp,"... cell %u vol %u vnode %u uniq %u",
697 scp->fid.cell, scp->fid.volume, scp->fid.vnode, scp->fid.unique);
698 bufferp->dataVersion = CM_BUF_VERSION_BAD;
702 /* while we're here, compute the next entry's location, too,
703 * since we'll need it when writing out the cookie into the
704 * dir listing stream.
706 numDirChunks = cm_NameEntries(dep->name, NULL);
708 /* compute the offset of the cookie representing the next entry */
709 nextEntryCookie = curOffset.LowPart
710 + (CM_DIR_CHUNKSIZE * numDirChunks);
712 if (dep->fid.vnode != 0) {
713 /* this is one of the entries to use: it is not deleted */
714 code = (*funcp)(scp, dep, parmp, &curOffset);
717 } /* if we're including this name */
720 /* and adjust curOffset to be where the new cookie is */
722 thyper.LowPart = CM_DIR_CHUNKSIZE * numDirChunks;
723 curOffset = LargeIntegerAdd(thyper, curOffset);
724 } /* while copying data for dir listing */
726 /* release the mutex */
728 lock_ReleaseMutex(&bufferp->mx);
729 buf_Release(bufferp);
734 int cm_NoneUpper(normchar_t *s)
738 if (c >= 'A' && c <= 'Z')
743 int cm_NoneLower(normchar_t *s)
747 if (c >= 'a' && c <= 'z')
752 long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
755 cm_lookupSearch_t *sp;
757 normchar_t matchName[MAX_PATH];
758 int looking_for_short_name = FALSE;
760 sp = (cm_lookupSearch_t *) rockp;
762 if (cm_FsStringToNormString(dep->name, -1, matchName, lengthof(matchName)) == 0) {
763 /* Can't normalize FS string. */
768 match = cm_NormStrCmpI(matchName, sp->nsearchNamep);
770 match = cm_NormStrCmp(matchName, sp->nsearchNamep);
774 && !cm_Is8Dot3(matchName)) {
776 cm_Gen8Dot3NameInt(dep->name, &dep->fid, matchName, NULL);
778 match = cm_NormStrCmpI(matchName, sp->nsearchNamep);
780 match = cm_NormStrCmp(matchName, sp->nsearchNamep);
781 looking_for_short_name = TRUE;
791 if (!sp->caseFold || looking_for_short_name) {
792 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
793 return CM_ERROR_STOPNOW;
797 * If we get here, we are doing a case-insensitive search, and we
798 * have found a match. Now we determine what kind of match it is:
799 * exact, lower-case, upper-case, or none of the above. This is done
800 * in order to choose among matches, if there are more than one.
803 /* Exact matches are the best. */
804 match = cm_NormStrCmp(matchName, sp->nsearchNamep);
807 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
808 return CM_ERROR_STOPNOW;
811 /* Lower-case matches are next. */
814 if (cm_NoneUpper(matchName)) {
819 /* Upper-case matches are next. */
822 if (cm_NoneLower(matchName)) {
827 /* General matches are last. */
833 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
837 /* read the contents of a mount point into the appropriate string.
838 * called with write locked scp, and returns with locked scp.
840 long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
844 if (scp->mountPointStringp[0])
847 #ifdef AFS_FREELANCE_CLIENT
848 /* File servers do not have data for freelance entries */
849 if (cm_freelanceEnabled &&
850 scp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
851 scp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
853 code = cm_FreelanceFetchMountPointString(scp);
855 #endif /* AFS_FREELANCE_CLIENT */
857 char temp[MOUNTPOINTLEN];
860 /* otherwise, we have to read it in */
861 offset.LowPart = offset.HighPart = 0;
862 code = cm_GetData(scp, &offset, temp, MOUNTPOINTLEN, userp, reqp);
867 * scp->length is the actual length of the mount point string.
868 * It is current because cm_GetData merged the most up to date
869 * status info into scp and has not dropped the rwlock since.
871 if (scp->length.LowPart > MOUNTPOINTLEN - 1)
872 return CM_ERROR_TOOBIG;
873 if (scp->length.LowPart == 0)
874 return CM_ERROR_INVAL;
876 /* convert the terminating dot to a NUL */
877 temp[scp->length.LowPart - 1] = 0;
878 memcpy(scp->mountPointStringp, temp, scp->length.LowPart);
885 /* called with a locked scp and chases the mount point, yielding outScpp.
886 * scp remains write locked, just for simplicity of describing the interface.
888 long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
889 cm_req_t *reqp, cm_scache_t **outScpp)
891 fschar_t *cellNamep = NULL;
892 fschar_t *volNamep = NULL;
896 cm_volume_t *volp = NULL;
905 if (scp->mountRootFid.cell != 0 && scp->mountRootGen >= cm_data.mountRootGen) {
906 tfid = scp->mountRootFid;
907 lock_ReleaseWrite(&scp->rw);
908 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
909 lock_ObtainWrite(&scp->rw);
913 /* parse the volume name */
914 mpNamep = scp->mountPointStringp;
916 return CM_ERROR_NOSUCHPATH;
917 mtType = *scp->mountPointStringp;
919 cp = cm_FsStrChr(mpNamep, _FS(':'));
921 /* cellular mount point */
922 cellNamep = (fschar_t *)malloc((cp - mpNamep) * sizeof(fschar_t));
923 cm_FsStrCpyN(cellNamep, cp - mpNamep, mpNamep + 1, cp - mpNamep - 1);
924 volNamep = cm_FsStrDup(cp+1);
926 /* now look up the cell */
927 lock_ReleaseWrite(&scp->rw);
928 cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE);
929 lock_ObtainWrite(&scp->rw);
932 volNamep = cm_FsStrDup(mpNamep + 1);
934 #ifdef AFS_FREELANCE_CLIENT
936 * Mount points in the Freelance cell should default
937 * to the workstation cell.
939 if (cm_freelanceEnabled &&
940 scp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
941 scp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
943 fschar_t rootCellName[256]="";
944 cm_GetRootCellName(rootCellName);
945 cellp = cm_GetCell(rootCellName, 0);
947 #endif /* AFS_FREELANCE_CLIENT */
948 cellp = cm_FindCellByID(scp->fid.cell, 0);
952 code = CM_ERROR_NOSUCHCELL;
956 vnLength = cm_FsStrLen(volNamep);
957 if (vnLength >= 8 && cm_FsStrCmp(volNamep + vnLength - 7, ".backup") == 0)
958 targetType = BACKVOL;
959 else if (vnLength >= 10
960 && cm_FsStrCmp(volNamep + vnLength - 9, ".readonly") == 0)
965 /* check for backups within backups */
966 if (targetType == BACKVOL
967 && (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
968 == CM_SCACHEFLAG_RO) {
969 code = CM_ERROR_NOSUCHVOLUME;
973 /* now we need to get the volume */
974 lock_ReleaseWrite(&scp->rw);
975 if (cm_VolNameIsID(volNamep)) {
976 code = cm_FindVolumeByID(cellp, atoi(volNamep), userp, reqp,
977 CM_GETVOL_FLAG_CREATE, &volp);
979 code = cm_FindVolumeByName(cellp, volNamep, userp, reqp,
980 CM_GETVOL_FLAG_CREATE, &volp);
982 lock_ObtainWrite(&scp->rw);
985 afs_uint32 cell, volume;
986 cm_vol_state_t *statep;
988 cell = cellp->cellID;
990 /* if the mt pt originates in a .backup volume (not a .readonly)
991 * and FollowBackupPath is active, and if there is a .backup
992 * volume for the target, then use the .backup of the target
993 * instead of the read-write.
995 if (cm_followBackupPath &&
996 volp->vol[BACKVOL].ID != 0 &&
997 (dscp->flags & (CM_SCACHEFLAG_RO|CM_SCACHEFLAG_PURERO)) == CM_SCACHEFLAG_RO &&
998 (targetType == RWVOL || targetType == ROVOL && volp->vol[ROVOL].ID == 0)
1000 targetType = BACKVOL;
1002 /* if the mt pt is in a read-only volume (not just a
1003 * backup), and if there is a read-only volume for the
1004 * target, and if this is a targetType '#' mount point, use
1005 * the read-only, otherwise use the one specified.
1007 else if (mtType == '#' && targetType == RWVOL &&
1008 (scp->flags & CM_SCACHEFLAG_PURERO) &&
1009 volp->vol[ROVOL].ID != 0) {
1013 lock_ObtainWrite(&volp->rw);
1014 statep = cm_VolumeStateByType(volp, targetType);
1015 volume = statep->ID;
1016 statep->dotdotFid = dscp->fid;
1017 lock_ReleaseWrite(&volp->rw);
1019 /* the rest of the fid is a magic number */
1020 cm_SetFid(&scp->mountRootFid, cell, volume, 1, 1);
1021 scp->mountRootGen = cm_data.mountRootGen;
1023 tfid = scp->mountRootFid;
1024 lock_ReleaseWrite(&scp->rw);
1025 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
1026 lock_ObtainWrite(&scp->rw);
1039 long cm_LookupInternal(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_user_t *userp,
1040 cm_req_t *reqp, cm_scache_t **outScpp)
1043 int dnlcHit = 1; /* did we hit in the dnlc? yes, we did */
1044 cm_scache_t *tscp = NULL;
1045 cm_scache_t *mountedScp;
1046 cm_lookupSearch_t rock;
1048 normchar_t *nnamep = NULL;
1049 fschar_t *fnamep = NULL;
1054 memset(&rock, 0, sizeof(rock));
1056 if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
1057 && cm_ClientStrCmp(cnamep, _C("..")) == 0) {
1058 if (dscp->dotdotFid.volume == 0)
1059 return CM_ERROR_NOSUCHVOLUME;
1060 rock.fid = dscp->dotdotFid;
1062 } else if (cm_ClientStrCmp(cnamep, _C(".")) == 0) {
1063 rock.fid = dscp->fid;
1067 nnamep = cm_ClientStringToNormStringAlloc(cnamep, -1, NULL);
1069 code = CM_ERROR_NOSUCHFILE;
1072 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
1074 code = CM_ERROR_NOSUCHFILE;
1079 if (flags & CM_FLAG_NOMOUNTCHASE) {
1080 /* In this case, we should go and call cm_Dir* functions
1081 directly since the following cm_ApplyDir() function will
1089 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ,
1090 CM_DIROP_FLAG_NONE, &dirop);
1093 code = cm_BPlusDirLookup(&dirop, nnamep, &rock.fid);
1098 code = cm_DirLookup(&dirop, fnamep, &rock.fid);
1100 cm_EndDirOp(&dirop);
1110 if (code == CM_ERROR_INEXACT_MATCH && (flags & CM_FLAG_CASEFOLD)) {
1117 code = CM_ERROR_BPLUS_NOMATCH;
1123 rock.fid.cell = dscp->fid.cell;
1124 rock.fid.volume = dscp->fid.volume;
1125 rock.searchNamep = fnamep;
1126 rock.nsearchNamep = nnamep;
1127 rock.caseFold = (flags & CM_FLAG_CASEFOLD);
1128 rock.hasTilde = ((cm_ClientStrChr(cnamep, '~') != NULL) ? 1 : 0);
1130 /* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
1131 code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
1132 (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
1134 /* code == 0 means we fell off the end of the dir, while stopnow means
1135 * that we stopped early, probably because we found the entry we're
1136 * looking for. Any other non-zero code is an error.
1138 if (code && code != CM_ERROR_STOPNOW && code != CM_ERROR_BPLUS_NOMATCH) {
1139 /* if the cm_scache_t we are searching in is not a directory
1140 * we must return path not found because the error
1141 * is to describe the final component not an intermediary
1143 if (code == CM_ERROR_NOTDIR) {
1144 if (flags & CM_FLAG_CHECKPATH)
1145 code = CM_ERROR_NOSUCHPATH;
1147 code = CM_ERROR_NOSUCHFILE;
1153 getroot = (dscp==cm_data.rootSCachep) ;
1155 if (!cm_freelanceEnabled || !getroot) {
1156 if (flags & CM_FLAG_CHECKPATH)
1157 code = CM_ERROR_NOSUCHPATH;
1159 code = CM_ERROR_NOSUCHFILE;
1162 else if (!cm_ClientStrChr(cnamep, '#') &&
1163 !cm_ClientStrChr(cnamep, '%') &&
1164 cm_ClientStrCmpI(cnamep, _C("srvsvc")) &&
1165 cm_ClientStrCmpI(cnamep, _C("wkssvc")) &&
1166 cm_ClientStrCmpI(cnamep, _C("ipc$")))
1168 /* nonexistent dir on freelance root, so add it */
1169 fschar_t fullname[CELL_MAXNAMELEN + 1] = "."; /* +1 so that when we skip the . the size is still CELL_MAXNAMELEN */
1173 osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %S",
1174 osi_LogSaveClientString(afsd_logp,cnamep));
1177 * There is an ugly behavior where a share name "foo" will be searched
1178 * for as "fo". If the searched for name differs by an already existing
1179 * symlink or mount point in the Freelance directory, do not add the
1180 * new value automatically.
1184 fnlen = strlen(fnamep);
1185 if ( fnamep[fnlen-1] == '.') {
1186 fnamep[fnlen-1] = '\0';
1191 if (cnamep[0] == '.') {
1192 if (cm_GetCell_Gen(&fnamep[1], &fullname[1], CM_FLAG_CREATE)) {
1194 code = cm_FreelanceAddMount(fullname, &fullname[1], "root.cell", 1, &rock.fid);
1195 if ( cm_FsStrCmpI(&fnamep[1], &fullname[1])) {
1197 * Do not permit symlinks that are one of:
1198 * . the cellname followed by a dot
1199 * . the cellname minus a single character
1200 * . a substring of the cellname that does not consist of full components
1202 if ( cm_strnicmp_utf8(&fnamep[1], fullname, (int)fnlen-1) == 0 &&
1203 (fnlen-1 == strlen(fullname)-1 || fullname[fnlen-1] != '.'))
1205 /* do not add; substitute fullname for the search */
1207 fnamep = malloc(strlen(fullname)+2);
1209 strncpy(&fnamep[1], fullname, strlen(fullname)+1);
1212 code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
1217 if (cm_GetCell_Gen(fnamep, fullname, CM_FLAG_CREATE)) {
1219 code = cm_FreelanceAddMount(fullname, fullname, "root.cell", 0, &rock.fid);
1220 if ( cm_FsStrCmpI(fnamep, fullname)) {
1222 * Do not permit symlinks that are one of:
1223 * . the cellname followed by a dot
1224 * . the cellname minus a single character
1225 * . a substring of the cellname that does not consist of full components
1227 if ( cm_strnicmp_utf8(fnamep, fullname, (int)fnlen-1) == 0 &&
1228 (fnlen == strlen(fullname)-1 || fullname[fnlen] != '.'))
1230 /* do not add; substitute fullname for the search */
1232 fnamep = strdup(fullname);
1236 code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
1245 nnamep = cm_FsStringToNormStringAlloc(fnamep, -1, NULL);
1249 if (!found || code) { /* add mount point failed, so give up */
1250 if (flags & CM_FLAG_CHECKPATH)
1251 code = CM_ERROR_NOSUCHPATH;
1253 code = CM_ERROR_NOSUCHFILE;
1256 tscp = NULL; /* to force call of cm_GetSCache */
1258 if (flags & CM_FLAG_CHECKPATH)
1259 code = CM_ERROR_NOSUCHPATH;
1261 code = CM_ERROR_NOSUCHFILE;
1267 if ( !tscp ) /* we did not find it in the dnlc */
1270 code = cm_GetSCache(&rock.fid, &tscp, userp, reqp);
1274 /* tscp is now held */
1276 lock_ObtainWrite(&tscp->rw);
1279 * Do not get status if we do not already have a callback.
1280 * The process of reading the mount point string will obtain status information
1281 * in a single RPC. No reason to add a second round trip.
1283 * If we do have a callback, use cm_SyncOp to get status in case the
1284 * current cm_user_t is not the same as the one that obtained the
1285 * mount point string contents.
1287 if (cm_HaveCallback(tscp)) {
1288 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1289 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1291 lock_ReleaseWrite(&tscp->rw);
1292 cm_ReleaseSCache(tscp);
1295 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1297 /* tscp is now locked */
1299 if (!(flags & CM_FLAG_NOMOUNTCHASE)
1300 && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
1301 /* mount points are funny: they have a volume name to mount
1304 code = cm_ReadMountPoint(tscp, userp, reqp);
1306 code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
1308 lock_ReleaseWrite(&tscp->rw);
1309 cm_ReleaseSCache(tscp);
1316 lock_ReleaseWrite(&tscp->rw);
1319 /* copy back pointer */
1322 /* insert scache in dnlc */
1323 if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
1324 /* lock the directory entry to prevent racing callback revokes */
1325 lock_ObtainRead(&dscp->rw);
1326 if ( dscp->cbServerp != NULL && dscp->cbExpires > 0 ) {
1327 /* TODO: reuse nnamep from above */
1330 nnamep = cm_ClientStringToNormStringAlloc(cnamep, -1, NULL);
1332 cm_dnlcEnter(dscp, nnamep, tscp);
1334 lock_ReleaseRead(&dscp->rw);
1351 int cm_ExpandSysName(cm_req_t * reqp, clientchar_t *inp, clientchar_t *outp, long outSizeCch, unsigned int index)
1356 int use_sysname64 = 0;
1358 if (cm_sysName64Count > 0 && reqp && (reqp->flags & CM_REQ_WOW64) && (reqp->flags & CM_REQ_SOURCE_REDIR))
1362 tp = cm_ClientStrRChr(inp, '@');
1364 return 0; /* no @sys */
1366 if (cm_ClientStrCmp(tp, _C("@sys")) != 0)
1367 return 0; /* no @sys */
1369 /* caller just wants to know if this is a valid @sys type of name */
1374 if (use_sysname64 && index >= cm_sysName64Count)
1378 if (index >= cm_sysNameCount)
1381 /* otherwise generate the properly expanded @sys name */
1382 prefixCount = (int)(tp - inp);
1384 cm_ClientStrCpyN(outp, outSizeCch, inp, prefixCount); /* copy out "a." from "a.@sys" */
1385 outp[prefixCount] = 0; /* null terminate the "a." */
1388 cm_ClientStrCat(outp, outSizeCch, cm_sysName64List[index]);
1391 cm_ClientStrCat(outp, outSizeCch, cm_sysNameList[index]);
1396 long cm_EvaluateVolumeReference(clientchar_t * namep, long flags, cm_user_t * userp,
1397 cm_req_t *reqp, cm_scache_t ** outScpp)
1399 afs_uint32 code = 0;
1400 fschar_t cellName[CELL_MAXNAMELEN];
1401 fschar_t volumeName[VL_MAXNAMELEN];
1405 fschar_t * fnamep = NULL;
1407 cm_cell_t * cellp = NULL;
1408 cm_volume_t * volp = NULL;
1412 int mountType = RWVOL;
1414 osi_Log1(afsd_logp, "cm_EvaluateVolumeReference for string [%S]",
1415 osi_LogSaveClientString(afsd_logp, namep));
1417 if (cm_ClientStrCmpNI(namep, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH) != 0) {
1418 goto _exit_invalid_path;
1421 /* namep is assumed to look like the following:
1423 @vol:<cellname>%<volume>\0
1425 @vol:<cellname>#<volume>\0
1429 fnamep = cm_ClientStringToFsStringAlloc(namep, -1, NULL);
1430 cp = fnamep + CM_PREFIX_VOL_CCH; /* cp points to cell name, hopefully */
1431 tp = cm_FsStrChr(cp, '%');
1433 tp = cm_FsStrChr(cp, '#');
1435 (len = tp - cp) == 0 ||
1436 len > CELL_MAXNAMELEN)
1437 goto _exit_invalid_path;
1438 cm_FsStrCpyN(cellName, lengthof(cellName), cp, len);
1443 cp = tp+1; /* cp now points to volume, supposedly */
1444 cm_FsStrCpy(volumeName, lengthof(volumeName), cp);
1446 /* OK, now we have the cell and the volume */
1447 osi_Log2(afsd_logp, " Found cell [%s] and volume [%s]",
1448 osi_LogSaveFsString(afsd_logp, cellName),
1449 osi_LogSaveFsString(afsd_logp, volumeName));
1451 cellp = cm_GetCell(cellName, CM_FLAG_CREATE);
1452 if (cellp == NULL) {
1453 goto _exit_invalid_path;
1456 len = cm_FsStrLen(volumeName);
1457 if (len >= 8 && cm_FsStrCmp(volumeName + len - 7, ".backup") == 0)
1459 else if (len >= 10 &&
1460 cm_FsStrCmp(volumeName + len - 9, ".readonly") == 0)
1465 if (cm_VolNameIsID(volumeName)) {
1466 code = cm_FindVolumeByID(cellp, atoi(volumeName), userp, reqp,
1467 CM_GETVOL_FLAG_CREATE, &volp);
1469 code = cm_FindVolumeByName(cellp, volumeName, userp, reqp,
1470 CM_GETVOL_FLAG_CREATE, &volp);
1476 if (volType == BACKVOL)
1477 volume = volp->vol[BACKVOL].ID;
1478 else if (volType == ROVOL ||
1479 (volType == RWVOL && mountType == ROVOL && volp->vol[ROVOL].ID != 0))
1480 volume = volp->vol[ROVOL].ID;
1482 volume = volp->vol[RWVOL].ID;
1484 cm_SetFid(&fid, cellp->cellID, volume, 1, 1);
1486 code = cm_GetSCache(&fid, outScpp, userp, reqp);
1499 if (flags & CM_FLAG_CHECKPATH)
1500 return CM_ERROR_NOSUCHPATH;
1502 return CM_ERROR_NOSUCHFILE;
1505 #ifdef DEBUG_REFCOUNT
1506 long cm_LookupDbg(cm_scache_t *dscp, clientchar_t *namep, long flags, cm_user_t *userp,
1507 cm_req_t *reqp, cm_scache_t **outScpp, char * file, long line)
1509 long cm_Lookup(cm_scache_t *dscp, clientchar_t *namep, long flags, cm_user_t *userp,
1510 cm_req_t *reqp, cm_scache_t **outScpp)
1514 clientchar_t tname[AFSPATHMAX];
1515 int sysNameIndex = 0;
1516 cm_scache_t *scp = NULL;
1518 #ifdef DEBUG_REFCOUNT
1519 afsi_log("%s:%d cm_Lookup dscp 0x%p ref %d", file, line, dscp, dscp->refCount, file, line);
1520 osi_Log2(afsd_logp, "cm_Lookup dscp 0x%p ref %d", dscp, dscp->refCount);
1523 if ( cm_ClientStrCmpI(namep,_C(SMB_IOCTL_FILENAME_NOSLASH)) == 0 ) {
1524 if (flags & CM_FLAG_CHECKPATH)
1525 return CM_ERROR_NOSUCHPATH;
1527 return CM_ERROR_NOSUCHFILE;
1530 if (dscp == cm_data.rootSCachep &&
1531 cm_ClientStrCmpNI(namep, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH) == 0) {
1532 return cm_EvaluateVolumeReference(namep, flags, userp, reqp, outScpp);
1535 if (cm_ExpandSysName(reqp, namep, NULL, 0, 0) > 0) {
1536 for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
1537 code = cm_ExpandSysName(reqp, namep, tname, lengthof(tname), sysNameIndex);
1539 code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
1540 #ifdef DEBUG_REFCOUNT
1541 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);
1542 osi_Log3(afsd_logp, "cm_LookupInternal (1) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1550 cm_ReleaseSCache(scp);
1554 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1555 #ifdef DEBUG_REFCOUNT
1556 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);
1557 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1564 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1565 #ifdef DEBUG_REFCOUNT
1566 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);
1567 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1573 /* None of the possible sysName expansions could be found */
1574 if (flags & CM_FLAG_CHECKPATH)
1575 return CM_ERROR_NOSUCHPATH;
1577 return CM_ERROR_NOSUCHFILE;
1580 /*! \brief Unlink a file name
1582 Encapsulates a call to RXAFS_RemoveFile().
1584 \param[in] dscp cm_scache_t pointing at the directory containing the
1585 name to be unlinked.
1587 \param[in] fnamep Original name to be unlinked. This is the
1588 name that will be passed into the RXAFS_RemoveFile() call.
1589 This parameter is optional. If not provided, the value will
1592 \param[in] came Client name to be unlinked. This name will be used
1593 to update the local directory caches.
1595 \param[in] userp cm_user_t for the request.
1597 \param[in] reqp Request tracker.
1600 long cm_Unlink(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t * cnamep,
1601 cm_user_t *userp, cm_req_t *reqp)
1607 AFSFetchStatus newDirStatus;
1609 struct rx_connection * rxconnp;
1611 cm_scache_t *scp = NULL;
1612 int free_fnamep = FALSE;
1615 memset(&volSync, 0, sizeof(volSync));
1617 if (fnamep == NULL) {
1620 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ,
1621 CM_DIROP_FLAG_NONE, &dirop);
1623 code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
1626 cm_EndDirOp(&dirop);
1633 #ifdef AFS_FREELANCE_CLIENT
1634 if (cm_freelanceEnabled && dscp == cm_data.rootSCachep) {
1635 /* deleting a mount point from the root dir. */
1636 code = cm_FreelanceRemoveMount(fnamep);
1641 code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
1645 /* Check for RO volume */
1646 if (dscp->flags & CM_SCACHEFLAG_RO) {
1647 code = CM_ERROR_READONLY;
1651 /* make sure we don't screw up the dir status during the merge */
1652 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE,
1653 CM_DIROP_FLAG_NONE, &dirop);
1655 lock_ObtainWrite(&dscp->rw);
1656 sflags = CM_SCACHESYNC_STOREDATA;
1657 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1658 lock_ReleaseWrite(&dscp->rw);
1660 cm_EndDirOp(&dirop);
1665 InterlockedIncrement(&dscp->activeRPCs);
1667 afsFid.Volume = dscp->fid.volume;
1668 afsFid.Vnode = dscp->fid.vnode;
1669 afsFid.Unique = dscp->fid.unique;
1671 osi_Log1(afsd_logp, "CALL RemoveFile scp 0x%p", dscp);
1673 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
1677 rxconnp = cm_GetRxConn(connp);
1678 code = RXAFS_RemoveFile(rxconnp, &afsFid, fnamep,
1679 &newDirStatus, &volSync);
1680 rx_PutConnection(rxconnp);
1682 } while (cm_Analyze(connp, userp, reqp, &dscp->fid, &volSync, NULL, NULL, code));
1683 code = cm_MapRPCError(code, reqp);
1686 osi_Log1(afsd_logp, "CALL RemoveFile FAILURE, code 0x%x", code);
1688 osi_Log0(afsd_logp, "CALL RemoveFile SUCCESS");
1691 lock_ObtainWrite(&dirop.scp->dirlock);
1692 dirop.lockType = CM_DIRLOCK_WRITE;
1694 lock_ObtainWrite(&dscp->rw);
1695 cm_dnlcRemove(dscp, cnamep);
1697 cm_MergeStatus(NULL, dscp, &newDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
1700 InterlockedDecrement(&scp->activeRPCs);
1701 if (code == CM_ERROR_NOSUCHFILE) {
1702 /* windows would not have allowed the request to delete the file
1703 * if it did not believe the file existed. therefore, we must
1704 * have an inconsistent view of the world.
1706 dscp->cbServerp = NULL;
1709 cm_SyncOpDone(dscp, NULL, sflags);
1710 lock_ReleaseWrite(&dscp->rw);
1712 if (code == 0 && cm_CheckDirOpForSingleChange(&dirop) && cnamep) {
1713 cm_DirDeleteEntry(&dirop, fnamep);
1715 cm_BPlusDirDeleteEntry(&dirop, cnamep);
1718 cm_EndDirOp(&dirop);
1720 if (invalidate && RDR_Initialized &&
1721 scp->fileType != CM_SCACHETYPE_FILE && scp->fileType != CM_SCACHETYPE_DIRECTORY)
1722 RDR_InvalidateObject(dscp->fid.cell, dscp->fid.volume, dscp->fid.vnode,
1723 dscp->fid.unique, dscp->fid.hash,
1724 dscp->fileType, AFS_INVALIDATE_DATA_VERSION);
1727 cm_ReleaseSCache(scp);
1729 lock_ObtainWrite(&scp->rw);
1730 if (--scp->linkCount == 0) {
1731 scp->flags |= CM_SCACHEFLAG_DELETED;
1732 lock_ObtainWrite(&cm_scacheLock);
1733 cm_AdjustScacheLRU(scp);
1734 cm_RemoveSCacheFromHashTable(scp);
1735 lock_ReleaseWrite(&cm_scacheLock);
1737 cm_DiscardSCache(scp);
1738 lock_ReleaseWrite(&scp->rw);
1739 if (RDR_Initialized && !(reqp->flags & CM_REQ_SOURCE_REDIR) &&
1740 !RDR_InvalidateObject(scp->fid.cell, scp->fid.volume, scp->fid.vnode,
1741 scp->fid.unique, scp->fid.hash,
1742 scp->fileType, AFS_INVALIDATE_DELETED))
1743 buf_ClearRDRFlag(scp, "unlink");
1754 /* called with a write locked vnode, and fills in the link info.
1755 * returns this the vnode still write locked.
1757 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1761 lock_AssertWrite(&linkScp->rw);
1762 if (!linkScp->mountPointStringp[0]) {
1764 #ifdef AFS_FREELANCE_CLIENT
1765 /* File servers do not have data for freelance entries */
1766 if (cm_freelanceEnabled &&
1767 linkScp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
1768 linkScp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
1770 code = cm_FreelanceFetchMountPointString(linkScp);
1772 #endif /* AFS_FREELANCE_CLIENT */
1774 char temp[MOUNTPOINTLEN];
1777 /* read the link data from the file server */
1778 offset.LowPart = offset.HighPart = 0;
1779 code = cm_GetData(linkScp, &offset, temp, MOUNTPOINTLEN, userp, reqp);
1784 * linkScp->length is the actual length of the symlink target string.
1785 * It is current because cm_GetData merged the most up to date
1786 * status info into scp and has not dropped the rwlock since.
1788 if (linkScp->length.LowPart > MOUNTPOINTLEN - 1)
1789 return CM_ERROR_TOOBIG;
1790 if (linkScp->length.LowPart == 0)
1791 return CM_ERROR_INVAL;
1793 /* make sure we are NUL terminated */
1794 temp[linkScp->length.LowPart] = 0;
1795 memcpy(linkScp->mountPointStringp, temp, linkScp->length.LowPart + 1);
1798 if ( !strnicmp(linkScp->mountPointStringp, "msdfs:", strlen("msdfs:")) )
1799 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1801 } /* don't have symlink contents cached */
1806 /* called with a held vnode and a path suffix, with the held vnode being a
1807 * symbolic link. Our goal is to generate a new path to interpret, and return
1808 * this new path in newSpaceBufferp. If the new vnode is relative to a dir
1809 * other than the directory containing the symbolic link, then the new root is
1810 * returned in *newRootScpp, otherwise a null is returned there.
1812 long cm_AssembleLink(cm_scache_t *linkScp, fschar_t *pathSuffixp,
1813 cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1814 cm_user_t *userp, cm_req_t *reqp)
1821 *newRootScpp = NULL;
1822 *newSpaceBufferp = NULL;
1824 lock_ObtainWrite(&linkScp->rw);
1826 * Do not get status if we do not already have a callback.
1827 * The process of reading the symlink string will obtain status information
1828 * in a single RPC. No reason to add a second round trip.
1830 * If we do have a callback, use cm_SyncOp to get status in case the
1831 * current cm_user_t is not the same as the one that obtained the
1832 * symlink string contents.
1834 if (cm_HaveCallback(linkScp)) {
1835 code = cm_SyncOp(linkScp, NULL, userp, reqp, 0,
1836 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1838 lock_ReleaseWrite(&linkScp->rw);
1839 cm_ReleaseSCache(linkScp);
1842 cm_SyncOpDone(linkScp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1844 code = cm_HandleLink(linkScp, userp, reqp);
1848 /* if we may overflow the buffer, bail out; buffer is signficantly
1849 * bigger than max path length, so we don't really have to worry about
1850 * being a little conservative here.
1852 if (cm_FsStrLen(linkScp->mountPointStringp) + cm_FsStrLen(pathSuffixp) + 2
1853 >= CM_UTILS_SPACESIZE) {
1854 code = CM_ERROR_TOOBIG;
1858 tsp = cm_GetSpace();
1859 linkp = linkScp->mountPointStringp;
1860 if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1861 if (strlen(linkp) > cm_mountRootLen)
1862 StringCbCopyA((char *) tsp->data, sizeof(tsp->data), linkp+cm_mountRootLen+1);
1865 *newRootScpp = cm_RootSCachep(userp, reqp);
1866 cm_HoldSCache(*newRootScpp);
1867 } else if (linkp[0] == '\\' && linkp[1] == '\\') {
1868 if (!strnicmp(&linkp[2], cm_NetbiosName, (len = (long)strlen(cm_NetbiosName))))
1870 char * p = &linkp[len + 3];
1871 if (strnicmp(p, "all", 3) == 0)
1874 StringCbCopyA(tsp->data, sizeof(tsp->data), p);
1875 for (p = tsp->data; *p; p++) {
1879 *newRootScpp = cm_RootSCachep(userp, reqp);
1880 cm_HoldSCache(*newRootScpp);
1882 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1883 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1884 code = CM_ERROR_PATH_NOT_COVERED;
1886 } else if ( linkScp->fileType == CM_SCACHETYPE_DFSLINK ||
1887 !strnicmp(linkp, "msdfs:", (len = (long)strlen("msdfs:"))) ) {
1888 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1889 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1890 code = CM_ERROR_PATH_NOT_COVERED;
1891 } else if (*linkp == '\\' || *linkp == '/') {
1893 /* formerly, this was considered to be from the AFS root,
1894 * but this seems to create problems. instead, we will just
1895 * reject the link */
1896 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp+1);
1897 *newRootScpp = cm_RootSCachep(userp, reqp);
1898 cm_HoldSCache(*newRootScpp);
1900 /* we still copy the link data into the response so that
1901 * the user can see what the link points to
1903 linkScp->fileType = CM_SCACHETYPE_INVALID;
1904 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1905 code = CM_ERROR_NOSUCHPATH;
1908 /* a relative link */
1909 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1911 if (pathSuffixp[0] != 0) { /* if suffix string is non-null */
1912 StringCchCatA(tsp->data,lengthof(tsp->data), "\\");
1913 StringCchCatA(tsp->data,lengthof(tsp->data), pathSuffixp);
1917 clientchar_t * cpath = cm_FsStringToClientStringAlloc(tsp->data, -1, NULL);
1918 if (cpath != NULL) {
1919 cm_ClientStrCpy(tsp->wdata, lengthof(tsp->wdata), cpath);
1921 *newSpaceBufferp = tsp;
1923 code = CM_ERROR_NOSUCHPATH;
1930 if (code == CM_ERROR_PATH_NOT_COVERED && reqp->tidPathp && reqp->relPathp) {
1931 cm_VolStatus_Notify_DFS_Mapping(linkScp, reqp->tidPathp, reqp->relPathp);
1936 lock_ReleaseWrite(&linkScp->rw);
1939 #ifdef DEBUG_REFCOUNT
1940 long cm_NameIDbg(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
1941 cm_user_t *userp, clientchar_t *tidPathp, cm_req_t *reqp,
1942 cm_scache_t **outScpp,
1943 char * file, long line)
1945 long cm_NameI(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
1946 cm_user_t *userp, clientchar_t *tidPathp,
1947 cm_req_t *reqp, cm_scache_t **outScpp)
1951 clientchar_t *tp; /* ptr moving through input buffer */
1952 clientchar_t tc; /* temp char */
1953 int haveComponent; /* has new component started? */
1954 clientchar_t component[AFSPATHMAX]; /* this is the new component */
1955 clientchar_t *cp; /* component name being assembled */
1956 cm_scache_t *tscp; /* current location in the hierarchy */
1957 cm_scache_t *nscp; /* next dude down */
1958 cm_scache_t *dirScp; /* last dir we searched */
1959 cm_scache_t *linkScp; /* new root for the symlink we just
1961 cm_space_t *psp; /* space for current path, if we've hit
1963 cm_space_t *tempsp; /* temp vbl */
1964 clientchar_t *restp; /* rest of the pathname to interpret */
1965 int symlinkCount; /* count of # of symlinks traversed */
1966 int extraFlag; /* avoid chasing mt pts for dir cmd */
1967 int phase = 1; /* 1 = tidPathp, 2 = pathp */
1968 #define MAX_FID_COUNT 512
1969 cm_fid_t fids[MAX_FID_COUNT]; /* array of fids processed in this path walk */
1970 int fid_count = 0; /* number of fids processed in this path walk */
1975 #ifdef DEBUG_REFCOUNT
1976 afsi_log("%s:%d cm_NameI rootscp 0x%p ref %d", file, line, rootSCachep, rootSCachep->refCount);
1977 osi_Log4(afsd_logp,"cm_NameI rootscp 0x%p path %S tidpath %S flags 0x%x",
1978 rootSCachep, pathp ? pathp : L"<NULL>", tidPathp ? tidPathp : L"<NULL>",
1993 cm_HoldSCache(tscp);
2001 /* map Unix slashes into DOS ones so we can interpret Unix
2007 if (!haveComponent) {
2010 } else if (tc == 0) {
2024 /* we have a component here */
2025 if (tc == 0 || tc == '\\') {
2026 /* end of the component; we're at the last
2027 * component if tc == 0. However, if the last
2028 * is a symlink, we have more to do.
2030 *cp++ = 0; /* add null termination */
2032 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
2033 extraFlag = CM_FLAG_NOMOUNTCHASE;
2034 code = cm_Lookup(tscp, component,
2036 userp, reqp, &nscp);
2039 if (!cm_ClientStrCmp(component,_C("..")) ||
2040 !cm_ClientStrCmp(component,_C("."))) {
2042 * roll back the fid list until we find the
2043 * fid that matches where we are now. Its not
2044 * necessarily one or two fids because they
2045 * might have been symlinks or mount points or
2046 * both that were crossed.
2048 for ( i=fid_count-1; i>=0; i--) {
2049 if (!cm_FidCmp(&nscp->fid, &fids[i]))
2054 /* add the new fid to the list */
2055 if (fid_count == MAX_FID_COUNT) {
2056 code = CM_ERROR_TOO_MANY_SYMLINKS;
2057 cm_ReleaseSCache(nscp);
2061 fids[fid_count++] = nscp->fid;
2066 cm_ReleaseSCache(tscp);
2068 cm_ReleaseSCache(dirScp);
2071 if ((code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH) &&
2072 tscp->fileType == CM_SCACHETYPE_SYMLINK) {
2073 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_NOSUCHPATH");
2074 return CM_ERROR_NOSUCHPATH;
2076 osi_Log1(afsd_logp,"cm_NameI code 0x%x", code);
2081 haveComponent = 0; /* component done */
2083 cm_ReleaseSCache(dirScp);
2084 dirScp = tscp; /* for some symlinks */
2085 tscp = nscp; /* already held */
2087 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
2090 cm_ReleaseSCache(dirScp);
2096 /* now, if tscp is a symlink, we should follow it and
2097 * assemble the path again.
2099 lock_ObtainWrite(&tscp->rw);
2100 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
2101 CM_SCACHESYNC_GETSTATUS
2102 | CM_SCACHESYNC_NEEDCALLBACK);
2104 lock_ReleaseWrite(&tscp->rw);
2105 cm_ReleaseSCache(tscp);
2108 cm_ReleaseSCache(dirScp);
2113 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2115 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
2116 /* this is a symlink; assemble a new buffer */
2117 lock_ReleaseWrite(&tscp->rw);
2118 if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
2119 cm_ReleaseSCache(tscp);
2122 cm_ReleaseSCache(dirScp);
2127 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_TOO_MANY_SYMLINKS");
2128 return CM_ERROR_TOO_MANY_SYMLINKS;
2138 /* TODO: make this better */
2139 frestp = cm_ClientStringToFsStringAlloc(restp, -1, NULL);
2140 code = cm_AssembleLink(tscp, frestp, &linkScp, &tempsp, userp, reqp);
2144 if (code == 0 && linkScp != NULL) {
2145 if (linkScp == cm_data.rootSCachep) {
2149 for ( i=0; i<fid_count; i++) {
2150 if ( !cm_FidCmp(&linkScp->fid, &fids[i]) ) {
2151 code = CM_ERROR_TOO_MANY_SYMLINKS;
2152 cm_ReleaseSCache(linkScp);
2158 if (i == fid_count && fid_count < MAX_FID_COUNT) {
2159 fids[fid_count++] = linkScp->fid;
2164 /* something went wrong */
2165 cm_ReleaseSCache(tscp);
2168 cm_ReleaseSCache(dirScp);
2174 /* otherwise, tempsp has the new path,
2175 * and linkScp is the new root from
2176 * which to interpret that path.
2177 * Continue with the namei processing,
2178 * also doing the bookkeeping for the
2179 * space allocation and tracking the
2180 * vnode reference counts.
2186 cm_ReleaseSCache(tscp);
2191 * now, if linkScp is null, that's
2192 * AssembleLink's way of telling us that
2193 * the sym link is relative to the dir
2194 * containing the link. We have a ref
2195 * to it in dirScp, and we hold it now
2196 * and reuse it as the new spot in the
2204 /* not a symlink, we may be done */
2205 lock_ReleaseWrite(&tscp->rw);
2213 cm_ReleaseSCache(dirScp);
2221 cm_ReleaseSCache(dirScp);
2224 } /* end of a component */
2227 } /* we have a component */
2228 } /* big while loop over all components */
2232 cm_ReleaseSCache(dirScp);
2238 cm_ReleaseSCache(tscp);
2240 #ifdef DEBUG_REFCOUNT
2241 afsi_log("%s:%d cm_NameI code 0x%x outScpp 0x%p ref %d", file, line, code, *outScpp, (*outScpp) ? (*outScpp)->refCount : 0);
2243 osi_Log2(afsd_logp,"cm_NameI code 0x%x outScpp 0x%p", code, *outScpp);
2247 /* called with a dir, and a vnode within the dir that happens to be a symlink.
2248 * We chase the link, and return a held pointer to the target, if it exists,
2249 * in *outScpp. If we succeed, we return 0, otherwise we return an error code
2250 * and do not hold or return a target vnode.
2252 * This is very similar to calling cm_NameI with the last component of a name,
2253 * which happens to be a symlink, except that we've already passed by the name.
2255 * This function is typically called by the directory listing functions, which
2256 * encounter symlinks but need to return the proper file length so programs
2257 * like "more" work properly when they make use of the attributes retrieved from
2260 * The input vnode should not be locked when this function is called.
2262 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
2263 cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
2267 cm_scache_t *newRootScp;
2271 osi_Log1(afsd_logp, "Evaluating symlink scp 0x%p", linkScp);
2273 code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
2277 /* now, if newRootScp is NULL, we're really being told that the symlink
2278 * is relative to the current directory (dscp).
2280 if (newRootScp == NULL) {
2282 cm_HoldSCache(dscp);
2285 code = cm_NameI(newRootScp, spacep->wdata,
2286 CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
2287 userp, NULL, reqp, outScpp);
2289 if (code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH)
2290 code = CM_ERROR_NOSUCHPATH;
2292 /* this stuff is allocated no matter what happened on the namei call,
2294 cm_FreeSpace(spacep);
2295 cm_ReleaseSCache(newRootScp);
2297 if (linkScp == *outScpp) {
2298 cm_ReleaseSCache(*outScpp);
2300 code = CM_ERROR_NOSUCHPATH;
2306 /* for a given entry, make sure that it isn't in the stat cache, and then
2307 * add it to the list of file IDs to be obtained.
2309 * Don't bother adding it if we already have a vnode. Note that the dir
2310 * is locked, so we have to be careful checking the vnode we're thinking of
2311 * processing, to avoid deadlocks.
2313 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
2324 /* Don't overflow bsp. */
2325 if (bsp->counter >= CM_BULKMAX)
2326 return CM_ERROR_STOPNOW;
2328 thyper.LowPart = cm_data.buf_blockSize;
2329 thyper.HighPart = 0;
2330 thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
2332 /* thyper is now the first byte past the end of the record we're
2333 * interested in, and bsp->bufOffset is the first byte of the record
2334 * we're interested in.
2335 * Skip data in the others.
2338 if (LargeIntegerLessThan(*offp, bsp->bufOffset))
2340 if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
2341 return CM_ERROR_STOPNOW;
2342 if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
2345 cm_SetFid(&tfid, scp->fid.cell, scp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
2346 tscp = cm_FindSCache(&tfid);
2348 if (lock_TryWrite(&tscp->rw)) {
2349 /* we have an entry that we can look at */
2350 if (!(tscp->flags & CM_SCACHEFLAG_EACCESS) && cm_HaveCallback(tscp)) {
2351 /* we have a callback on it. Don't bother
2352 * fetching this stat entry, since we're happy
2353 * with the info we have.
2355 lock_ReleaseWrite(&tscp->rw);
2356 cm_ReleaseSCache(tscp);
2359 lock_ReleaseWrite(&tscp->rw);
2361 cm_ReleaseSCache(tscp);
2364 #ifdef AFS_FREELANCE_CLIENT
2365 // yj: if this is a mountpoint under root.afs then we don't want it
2366 // to be bulkstat-ed, instead, we call getSCache directly and under
2367 // getSCache, it is handled specially.
2368 if ( cm_freelanceEnabled &&
2369 tfid.cell==AFS_FAKE_ROOT_CELL_ID &&
2370 tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
2371 !(tfid.vnode==0x1 && tfid.unique==0x1) )
2373 osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
2374 return cm_GetSCache(&tfid, &tscp, NULL, NULL);
2376 #endif /* AFS_FREELANCE_CLIENT */
2379 bsp->fids[i].Volume = scp->fid.volume;
2380 bsp->fids[i].Vnode = tfid.vnode;
2381 bsp->fids[i].Unique = tfid.unique;
2386 cm_TryBulkStatRPC(cm_scache_t *dscp, cm_bulkStat_t *bbp, cm_user_t *userp, cm_req_t *reqp)
2389 AFSCBFids fidStruct;
2390 AFSBulkStats statStruct;
2392 AFSCBs callbackStruct;
2395 cm_callbackRequest_t cbReq;
2401 struct rx_connection * rxconnp;
2402 int inlinebulk; /* Did we use InlineBulkStatus RPC or not? */
2404 memset(&volSync, 0, sizeof(volSync));
2406 /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
2407 * make the calls to create the entries. Handle AFSCBMAX files at a
2410 for (filex = 0; filex < bbp->counter; filex += filesThisCall) {
2411 filesThisCall = bbp->counter - filex;
2412 if (filesThisCall > AFSCBMAX)
2413 filesThisCall = AFSCBMAX;
2415 fidStruct.AFSCBFids_len = filesThisCall;
2416 fidStruct.AFSCBFids_val = &bbp->fids[filex];
2417 statStruct.AFSBulkStats_len = filesThisCall;
2418 statStruct.AFSBulkStats_val = &bbp->stats[filex];
2419 callbackStruct.AFSCBs_len = filesThisCall;
2420 callbackStruct.AFSCBs_val = &bbp->callbacks[filex];
2421 cm_StartCallbackGrantingCall(NULL, &cbReq);
2422 osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
2425 * Whenever cm_Analyze is called for a RXAFS_ RPC there must
2426 * be a FID provided. However, the error code from RXAFS_BulkStatus
2427 * or RXAFS_InlinkBulkStatus does not apply to any FID. Therefore,
2428 * we generate an invalid FID to match with the RPC error.
2430 cm_SetFid(&tfid, dscp->fid.cell, dscp->fid.volume, 0, 0);
2435 code = cm_ConnFromFID(&tfid, userp, reqp, &connp);
2439 rxconnp = cm_GetRxConn(connp);
2440 if (!(connp->serverp->flags & CM_SERVERFLAG_NOINLINEBULK)) {
2441 code = RXAFS_InlineBulkStatus(rxconnp, &fidStruct,
2442 &statStruct, &callbackStruct, &volSync);
2443 if (code == RXGEN_OPCODE) {
2444 cm_SetServerNoInlineBulk(connp->serverp, 0);
2450 code = RXAFS_BulkStatus(rxconnp, &fidStruct,
2451 &statStruct, &callbackStruct, &volSync);
2453 rx_PutConnection(rxconnp);
2456 * If InlineBulk RPC was called and it succeeded,
2457 * then pull out the return code from the status info
2458 * and use it for cm_Analyze so that we can failover to other
2459 * .readonly volume instances. But only do it for errors that
2460 * are volume global.
2462 if (inlinebulk && code == 0 && (&bbp->stats[0])->errorCode) {
2463 osi_Log1(afsd_logp, "cm_TryBulkStat inline-bulk stat error: %d",
2464 (&bbp->stats[0])->errorCode);
2465 switch ((&bbp->stats[0])->errorCode) {
2474 code = (&bbp->stats[0])->errorCode;
2477 /* Rx and Rxkad errors are volume global */
2478 if ( (&bbp->stats[0])->errorCode >= -64 && (&bbp->stats[0])->errorCode < 0 ||
2479 (&bbp->stats[0])->errorCode >= ERROR_TABLE_BASE_RXK && (&bbp->stats[0])->errorCode < ERROR_TABLE_BASE_RXK + 256)
2480 code = (&bbp->stats[0])->errorCode;
2483 } while (cm_Analyze(connp, userp, reqp, &tfid, &volSync, NULL, &cbReq, code));
2484 code = cm_MapRPCError(code, reqp);
2487 * might as well quit on an error, since we're not going to do
2488 * much better on the next immediate call, either.
2491 osi_Log2(afsd_logp, "CALL %sBulkStatus FAILURE code 0x%x",
2492 inlinebulk ? "Inline" : "", code);
2493 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2498 * The bulk RPC has succeeded or at least not failed with a
2499 * volume global error result. For items that have inlineBulk
2500 * errors we must call cm_Analyze in order to perform required
2501 * logging of errors.
2503 * If the RPC was not inline bulk or the entry either has no error
2504 * the status must be merged.
2506 osi_Log1(afsd_logp, "CALL %sBulkStatus SUCCESS", inlinebulk ? "Inline" : "");
2508 for (i = 0; i<filesThisCall; i++) {
2510 cm_SetFid(&tfid, dscp->fid.cell, bbp->fids[j].Volume, bbp->fids[j].Vnode, bbp->fids[j].Unique);
2512 if (inlinebulk && (&bbp->stats[j])->errorCode) {
2513 cm_req_t treq = *reqp;
2514 cm_Analyze(NULL, userp, &treq, &tfid, &volSync, NULL, &cbReq, (&bbp->stats[j])->errorCode);
2516 code = cm_GetSCache(&tfid, &scp, userp, reqp);
2521 * otherwise, if this entry has no callback info,
2522 * merge in this. If there is existing callback info
2523 * we skip the merge because the existing data must be
2524 * current (we have a callback) and the response from
2525 * a non-inline bulk rpc might actually be wrong.
2527 * now, we have to be extra paranoid on merging in this
2528 * information, since we didn't use cm_SyncOp before
2529 * starting the fetch to make sure that no bad races
2530 * were occurring. Specifically, we need to make sure
2531 * we don't obliterate any newer information in the
2532 * vnode than have here.
2534 * Right now, be pretty conservative: if there's a
2535 * callback or a pending call, skip it.
2536 * However, if the prior attempt to obtain status
2537 * was refused access or the volume is .readonly,
2538 * take the data in any case since we have nothing
2539 * better for the in flight directory enumeration that
2540 * resulted in this function being called.
2542 lock_ObtainRead(&scp->rw);
2543 if ((scp->cbServerp == NULL &&
2544 !(scp->flags & (CM_SCACHEFLAG_FETCHING | CM_SCACHEFLAG_STORING | CM_SCACHEFLAG_SIZESTORING))) ||
2545 (scp->flags & CM_SCACHEFLAG_PURERO) ||
2546 (scp->flags & CM_SCACHEFLAG_EACCESS))
2548 lock_ConvertRToW(&scp->rw);
2549 cm_EndCallbackGrantingCall(scp, &cbReq,
2552 CM_CALLBACK_MAINTAINCOUNT);
2553 InterlockedIncrement(&scp->activeRPCs);
2554 cm_MergeStatus(dscp, scp, &bbp->stats[j], &volSync, userp, reqp, 0);
2555 lock_ReleaseWrite(&scp->rw);
2557 lock_ReleaseRead(&scp->rw);
2559 cm_ReleaseSCache(scp);
2561 } /* all files in the response */
2562 /* now tell it to drop the count,
2563 * after doing the vnode processing above */
2564 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2565 } /* while there are still more files to process */
2570 /* called with a write locked scp and a pointer to a buffer. Make bulk stat
2571 * calls on all undeleted files in the page of the directory specified.
2574 cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
2580 osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%p", dscp);
2582 /* should be on a buffer boundary */
2583 osi_assertx((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0, "invalid offset");
2585 bbp = malloc(sizeof(cm_bulkStat_t));
2586 memset(bbp, 0, sizeof(cm_bulkStat_t));
2587 bbp->bufOffset = *offsetp;
2589 lock_ReleaseWrite(&dscp->rw);
2590 /* first, assemble the file IDs we need to stat */
2591 code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) bbp, offsetp, userp, reqp, NULL);
2593 /* if we failed, bail out early */
2594 if (code && code != CM_ERROR_STOPNOW) {
2596 lock_ObtainWrite(&dscp->rw);
2600 code = cm_TryBulkStatRPC(dscp, bbp, userp, reqp);
2601 osi_Log1(afsd_logp, "END cm_TryBulkStat code 0x%x", code);
2603 lock_ObtainWrite(&dscp->rw);
2608 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
2612 /* initialize store back mask as inexpensive local variable */
2614 memset(statusp, 0, sizeof(AFSStoreStatus));
2616 /* copy out queued info from scache first, if scp passed in */
2618 if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
2619 statusp->ClientModTime = scp->clientModTime;
2620 mask |= AFS_SETMODTIME;
2621 scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
2626 /* now add in our locally generated request */
2627 if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
2628 statusp->ClientModTime = attrp->clientModTime;
2629 mask |= AFS_SETMODTIME;
2631 if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
2632 statusp->UnixModeBits = attrp->unixModeBits;
2633 mask |= AFS_SETMODE;
2635 if (attrp->mask & CM_ATTRMASK_OWNER) {
2636 statusp->Owner = attrp->owner;
2637 mask |= AFS_SETOWNER;
2639 if (attrp->mask & CM_ATTRMASK_GROUP) {
2640 statusp->Group = attrp->group;
2641 mask |= AFS_SETGROUP;
2644 statusp->Mask = mask;
2647 /* set the file size, and make sure that all relevant buffers have been
2648 * truncated. Ensure that any partially truncated buffers have been zeroed
2649 * to the end of the buffer.
2651 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
2657 /* start by locking out buffer creation */
2658 lock_ObtainWrite(&scp->bufCreateLock);
2660 /* verify that this is a file, not a dir or a symlink */
2661 lock_ObtainWrite(&scp->rw);
2662 code = cm_SyncOp(scp, NULL, userp, reqp, 0,
2663 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2666 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2668 if (scp->fileType != CM_SCACHETYPE_FILE) {
2669 code = CM_ERROR_ISDIR;
2674 if (LargeIntegerLessThan(*sizep, scp->length))
2679 lock_ReleaseWrite(&scp->rw);
2681 /* can't hold scp->rw lock here, since we may wait for a storeback to
2682 * finish if the buffer package is cleaning a buffer by storing it to
2686 buf_Truncate(scp, userp, reqp, sizep);
2688 /* now ensure that file length is short enough, and update truncPos */
2689 lock_ObtainWrite(&scp->rw);
2691 /* make sure we have a callback (so we have the right value for the
2692 * length), and wait for it to be safe to do a truncate.
2694 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
2695 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2696 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2698 /* If we only have 'i' bits, then we should still be able to set
2699 the size of a file we created. */
2700 if (code == CM_ERROR_NOACCESS && scp->creator == userp) {
2701 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
2702 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2703 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2709 if (LargeIntegerLessThan(*sizep, scp->length)) {
2710 /* a real truncation. If truncPos is not set yet, or is bigger
2711 * than where we're truncating the file, set truncPos to this
2716 if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
2717 || LargeIntegerLessThan(*sizep, scp->length)) {
2719 scp->truncPos = *sizep;
2720 scp->mask |= CM_SCACHEMASK_TRUNCPOS;
2722 /* in either case, the new file size has been changed */
2723 scp->length = *sizep;
2724 scp->mask |= CM_SCACHEMASK_LENGTH;
2726 else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
2727 /* really extending the file */
2728 scp->length = *sizep;
2729 scp->mask |= CM_SCACHEMASK_LENGTH;
2732 /* done successfully */
2735 cm_SyncOpDone(scp, NULL,
2736 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2737 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2740 lock_ReleaseWrite(&scp->rw);
2741 lock_ReleaseWrite(&scp->bufCreateLock);
2746 /* set the file size or other attributes (but not both at once) */
2747 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
2751 AFSFetchStatus afsOutStatus;
2755 AFSStoreStatus afsInStatus;
2756 struct rx_connection * rxconnp;
2758 memset(&volSync, 0, sizeof(volSync));
2760 /* handle file length setting */
2761 if (attrp->mask & CM_ATTRMASK_LENGTH)
2762 return cm_SetLength(scp, &attrp->length, userp, reqp);
2764 lock_ObtainWrite(&scp->rw);
2765 /* Check for RO volume */
2766 if (scp->flags & CM_SCACHEFLAG_RO) {
2767 code = CM_ERROR_READONLY;
2768 lock_ReleaseWrite(&scp->rw);
2772 /* otherwise, we have to make an RPC to get the status */
2773 code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
2775 lock_ReleaseWrite(&scp->rw);
2778 lock_ConvertWToR(&scp->rw);
2780 /* make the attr structure */
2781 cm_StatusFromAttr(&afsInStatus, scp, attrp);
2783 tfid.Volume = scp->fid.volume;
2784 tfid.Vnode = scp->fid.vnode;
2785 tfid.Unique = scp->fid.unique;
2786 lock_ReleaseRead(&scp->rw);
2788 /* now make the RPC */
2789 InterlockedIncrement(&scp->activeRPCs);
2791 osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%p", scp);
2793 code = cm_ConnFromFID(&scp->fid, userp, reqp, &connp);
2797 rxconnp = cm_GetRxConn(connp);
2798 code = RXAFS_StoreStatus(rxconnp, &tfid,
2799 &afsInStatus, &afsOutStatus, &volSync);
2800 rx_PutConnection(rxconnp);
2802 } while (cm_Analyze(connp, userp, reqp,
2803 &scp->fid, &volSync, NULL, NULL, code));
2804 code = cm_MapRPCError(code, reqp);
2807 osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
2809 osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
2811 lock_ObtainWrite(&scp->rw);
2813 cm_MergeStatus(NULL, scp, &afsOutStatus, &volSync, userp, reqp,
2814 CM_MERGEFLAG_FORCE|CM_MERGEFLAG_STOREDATA);
2816 InterlockedDecrement(&scp->activeRPCs);
2817 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
2819 /* if we're changing the mode bits, discard the ACL cache,
2820 * since we changed the mode bits.
2822 if (afsInStatus.Mask & AFS_SETMODE)
2823 cm_FreeAllACLEnts(scp);
2824 lock_ReleaseWrite(&scp->rw);
2828 long cm_Create(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
2829 cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
2834 cm_callbackRequest_t cbReq;
2837 cm_scache_t *scp = NULL;
2839 AFSStoreStatus inStatus;
2840 AFSFetchStatus updatedDirStatus;
2841 AFSFetchStatus newFileStatus;
2842 AFSCallBack newFileCallback;
2844 struct rx_connection * rxconnp;
2846 fschar_t * fnamep = NULL;
2848 memset(&volSync, 0, sizeof(volSync));
2850 /* can't create names with @sys in them; must expand it manually first.
2851 * return "invalid request" if they try.
2853 if (cm_ExpandSysName(NULL, cnamep, NULL, 0, 0)) {
2854 return CM_ERROR_ATSYS;
2857 #ifdef AFS_FREELANCE_CLIENT
2858 /* Freelance root volume does not hold files */
2859 if (cm_freelanceEnabled &&
2860 dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
2861 dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
2863 return CM_ERROR_NOACCESS;
2865 #endif /* AFS_FREELANCE_CLIENT */
2867 /* Check for RO volume */
2868 if (dscp->flags & CM_SCACHEFLAG_RO)
2869 return CM_ERROR_READONLY;
2871 /* before starting the RPC, mark that we're changing the file data, so
2872 * that someone who does a chmod will know to wait until our call
2875 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
2877 lock_ObtainWrite(&dscp->rw);
2878 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2879 lock_ReleaseWrite(&dscp->rw);
2881 cm_StartCallbackGrantingCall(NULL, &cbReq);
2883 cm_EndDirOp(&dirop);
2890 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
2892 cm_StatusFromAttr(&inStatus, NULL, attrp);
2894 /* try the RPC now */
2895 InterlockedIncrement(&dscp->activeRPCs);
2896 osi_Log1(afsd_logp, "CALL CreateFile scp 0x%p", dscp);
2898 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2902 dirAFSFid.Volume = dscp->fid.volume;
2903 dirAFSFid.Vnode = dscp->fid.vnode;
2904 dirAFSFid.Unique = dscp->fid.unique;
2906 rxconnp = cm_GetRxConn(connp);
2907 code = RXAFS_CreateFile(connp->rxconnp, &dirAFSFid, fnamep,
2908 &inStatus, &newAFSFid, &newFileStatus,
2909 &updatedDirStatus, &newFileCallback,
2911 rx_PutConnection(rxconnp);
2913 } while (cm_Analyze(connp, userp, reqp,
2914 &dscp->fid, &volSync, NULL, &cbReq, code));
2915 code = cm_MapRPCError(code, reqp);
2918 osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
2920 osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
2923 lock_ObtainWrite(&dirop.scp->dirlock);
2924 dirop.lockType = CM_DIRLOCK_WRITE;
2926 lock_ObtainWrite(&dscp->rw);
2928 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
2930 InterlockedDecrement(&dscp->activeRPCs);
2931 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2932 lock_ReleaseWrite(&dscp->rw);
2934 /* now try to create the file's entry, too, but be careful to
2935 * make sure that we don't merge in old info. Since we weren't locking
2936 * out any requests during the file's creation, we may have pretty old
2940 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
2941 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2943 lock_ObtainWrite(&scp->rw);
2944 scp->creator = userp; /* remember who created it */
2945 if (!cm_HaveCallback(scp)) {
2946 cm_EndCallbackGrantingCall(scp, &cbReq,
2947 &newFileCallback, &volSync, 0);
2948 InterlockedIncrement(&scp->activeRPCs);
2949 cm_MergeStatus(dscp, scp, &newFileStatus, &volSync,
2953 lock_ReleaseWrite(&scp->rw);
2957 /* make sure we end things properly */
2959 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2961 if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
2962 cm_DirCreateEntry(&dirop, fnamep, &newFid);
2964 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
2967 cm_EndDirOp(&dirop);
2976 cm_ReleaseSCache(scp);
2982 * locked if TRUE means write-locked
2983 * else the cm_scache_t rw must not be held
2985 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp, afs_uint32 locked)
2990 lock_ReleaseWrite(&scp->rw);
2992 osi_Log2(afsd_logp, "cm_FSync scp 0x%p userp 0x%p", scp, userp);
2994 code = buf_CleanVnode(scp, userp, reqp);
2996 lock_ObtainWrite(&scp->rw);
2998 if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
2999 | CM_SCACHEMASK_CLIENTMODTIME
3000 | CM_SCACHEMASK_LENGTH))
3001 code = cm_StoreMini(scp, userp, reqp);
3003 if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) {
3004 code = (scp->flags & CM_SCACHEFLAG_OVERQUOTA) ? CM_ERROR_QUOTA : CM_ERROR_SPACE;
3005 scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE);
3009 lock_ReleaseWrite(&scp->rw);
3010 } else if (locked) {
3011 lock_ObtainWrite(&scp->rw);
3016 long cm_MakeDir(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
3017 cm_user_t *userp, cm_req_t *reqp, cm_scache_t **scpp)
3022 cm_callbackRequest_t cbReq;
3025 cm_scache_t *scp = NULL;
3027 AFSStoreStatus inStatus;
3028 AFSFetchStatus updatedDirStatus;
3029 AFSFetchStatus newDirStatus;
3030 AFSCallBack newDirCallback;
3032 struct rx_connection * rxconnp;
3034 fschar_t * fnamep = NULL;
3036 memset(&volSync, 0, sizeof(volSync));
3038 /* can't create names with @sys in them; must expand it manually first.
3039 * return "invalid request" if they try.
3041 if (cm_ExpandSysName(NULL, cnamep, NULL, 0, 0)) {
3042 return CM_ERROR_ATSYS;
3045 #ifdef AFS_FREELANCE_CLIENT
3046 /* Freelance root volume does not hold subdirectories */
3047 if (cm_freelanceEnabled &&
3048 dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
3049 dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
3051 return CM_ERROR_NOACCESS;
3053 #endif /* AFS_FREELANCE_CLIENT */
3055 /* Check for RO volume */
3056 if (dscp->flags & CM_SCACHEFLAG_RO)
3057 return CM_ERROR_READONLY;
3059 /* before starting the RPC, mark that we're changing the directory
3060 * data, so that someone who does a chmod on the dir will wait until
3061 * our call completes.
3063 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3065 lock_ObtainWrite(&dscp->rw);
3066 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3067 lock_ReleaseWrite(&dscp->rw);
3069 cm_StartCallbackGrantingCall(NULL, &cbReq);
3071 cm_EndDirOp(&dirop);
3078 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3079 cm_StatusFromAttr(&inStatus, NULL, attrp);
3081 /* try the RPC now */
3082 InterlockedIncrement(&dscp->activeRPCs);
3083 osi_Log1(afsd_logp, "CALL MakeDir scp 0x%p", dscp);
3085 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3089 dirAFSFid.Volume = dscp->fid.volume;
3090 dirAFSFid.Vnode = dscp->fid.vnode;
3091 dirAFSFid.Unique = dscp->fid.unique;
3093 rxconnp = cm_GetRxConn(connp);
3094 code = RXAFS_MakeDir(connp->rxconnp, &dirAFSFid, fnamep,
3095 &inStatus, &newAFSFid, &newDirStatus,
3096 &updatedDirStatus, &newDirCallback,
3098 rx_PutConnection(rxconnp);
3100 } while (cm_Analyze(connp, userp, reqp,
3101 &dscp->fid, &volSync, NULL, &cbReq, code));
3102 code = cm_MapRPCError(code, reqp);
3105 osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
3107 osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
3110 lock_ObtainWrite(&dirop.scp->dirlock);
3111 dirop.lockType = CM_DIRLOCK_WRITE;
3113 lock_ObtainWrite(&dscp->rw);
3115 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3117 InterlockedDecrement(&dscp->activeRPCs);
3118 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3119 lock_ReleaseWrite(&dscp->rw);
3121 /* now try to create the new dir's entry, too, but be careful to
3122 * make sure that we don't merge in old info. Since we weren't locking
3123 * out any requests during the file's creation, we may have pretty old
3127 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3128 code = cm_GetSCache(&newFid, &scp, userp, reqp);
3130 lock_ObtainWrite(&scp->rw);
3131 if (!cm_HaveCallback(scp)) {
3132 cm_EndCallbackGrantingCall(scp, &cbReq,
3133 &newDirCallback, &volSync, 0);
3134 InterlockedIncrement(&scp->activeRPCs);
3135 cm_MergeStatus(dscp, scp, &newDirStatus, &volSync,
3139 lock_ReleaseWrite(&scp->rw);
3143 /* make sure we end things properly */
3145 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
3147 if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
3148 cm_DirCreateEntry(&dirop, fnamep, &newFid);
3150 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
3153 cm_EndDirOp(&dirop);
3161 cm_ReleaseSCache(scp);
3164 /* and return error code */
3168 long cm_Link(cm_scache_t *dscp, clientchar_t *cnamep, cm_scache_t *sscp, long flags,
3169 cm_user_t *userp, cm_req_t *reqp)
3174 AFSFid existingAFSFid;
3175 AFSFetchStatus updatedDirStatus;
3176 AFSFetchStatus newLinkStatus;
3178 struct rx_connection * rxconnp;
3180 fschar_t * fnamep = NULL;
3183 memset(&volSync, 0, sizeof(volSync));
3185 if (dscp->fid.cell != sscp->fid.cell ||
3186 dscp->fid.volume != sscp->fid.volume) {
3187 return CM_ERROR_CROSSDEVLINK;
3190 /* Check for RO volume */
3191 if (dscp->flags & CM_SCACHEFLAG_RO)
3192 return CM_ERROR_READONLY;
3194 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3196 lock_ObtainWrite(&dscp->rw);
3197 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3198 lock_ReleaseWrite(&dscp->rw);
3200 cm_EndDirOp(&dirop);
3205 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3207 /* try the RPC now */
3208 InterlockedIncrement(&dscp->activeRPCs);
3209 osi_Log1(afsd_logp, "CALL Link scp 0x%p", dscp);
3211 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3214 dirAFSFid.Volume = dscp->fid.volume;
3215 dirAFSFid.Vnode = dscp->fid.vnode;
3216 dirAFSFid.Unique = dscp->fid.unique;
3218 existingAFSFid.Volume = sscp->fid.volume;
3219 existingAFSFid.Vnode = sscp->fid.vnode;
3220 existingAFSFid.Unique = sscp->fid.unique;
3222 rxconnp = cm_GetRxConn(connp);
3223 code = RXAFS_Link(rxconnp, &dirAFSFid, fnamep, &existingAFSFid,
3224 &newLinkStatus, &updatedDirStatus, &volSync);
3225 rx_PutConnection(rxconnp);
3226 osi_Log1(afsd_logp," RXAFS_Link returns 0x%x", code);
3228 } while (cm_Analyze(connp, userp, reqp,
3229 &dscp->fid, &volSync, NULL, NULL, code));
3231 code = cm_MapRPCError(code, reqp);
3234 osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
3236 osi_Log0(afsd_logp, "CALL Link SUCCESS");
3239 lock_ObtainWrite(&dirop.scp->dirlock);
3240 dirop.lockType = CM_DIRLOCK_WRITE;
3242 lock_ObtainWrite(&dscp->rw);
3244 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3247 InterlockedDecrement(&dscp->activeRPCs);
3249 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3250 lock_ReleaseWrite(&dscp->rw);
3253 if (cm_CheckDirOpForSingleChange(&dirop)) {
3254 cm_DirCreateEntry(&dirop, fnamep, &sscp->fid);
3256 cm_BPlusDirCreateEntry(&dirop, cnamep, &sscp->fid);
3260 cm_EndDirOp(&dirop);
3262 if (invalidate && RDR_Initialized)
3263 RDR_InvalidateObject(dscp->fid.cell, dscp->fid.volume, dscp->fid.vnode,
3264 dscp->fid.unique, dscp->fid.hash,
3265 dscp->fileType, AFS_INVALIDATE_DATA_VERSION);
3267 /* Update the linked object status */
3269 lock_ObtainWrite(&sscp->rw);
3270 InterlockedIncrement(&sscp->activeRPCs);
3271 cm_MergeStatus(NULL, sscp, &newLinkStatus, &volSync, userp, reqp, 0);
3272 lock_ReleaseWrite(&sscp->rw);
3280 long cm_SymLink(cm_scache_t *dscp, clientchar_t *cnamep, fschar_t *contentsp, long flags,
3281 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp, cm_scache_t **scpp)
3289 AFSStoreStatus inStatus;
3290 AFSFetchStatus updatedDirStatus;
3291 AFSFetchStatus newLinkStatus;
3293 struct rx_connection * rxconnp;
3295 fschar_t *fnamep = NULL;
3300 /* Check for RO volume */
3301 if (dscp->flags & CM_SCACHEFLAG_RO)
3302 return CM_ERROR_READONLY;
3304 memset(&volSync, 0, sizeof(volSync));
3306 /* before starting the RPC, mark that we're changing the directory data,
3307 * so that someone who does a chmod on the dir will wait until our
3310 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3312 lock_ObtainWrite(&dscp->rw);
3313 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3314 lock_ReleaseWrite(&dscp->rw);
3316 cm_EndDirOp(&dirop);
3321 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3323 cm_StatusFromAttr(&inStatus, NULL, attrp);
3325 /* try the RPC now */
3326 InterlockedIncrement(&dscp->activeRPCs);
3327 osi_Log1(afsd_logp, "CALL Symlink scp 0x%p", dscp);
3329 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3333 dirAFSFid.Volume = dscp->fid.volume;
3334 dirAFSFid.Vnode = dscp->fid.vnode;
3335 dirAFSFid.Unique = dscp->fid.unique;
3337 rxconnp = cm_GetRxConn(connp);
3338 code = RXAFS_Symlink(rxconnp, &dirAFSFid, fnamep, contentsp,
3339 &inStatus, &newAFSFid, &newLinkStatus,
3340 &updatedDirStatus, &volSync);
3341 rx_PutConnection(rxconnp);
3343 } while (cm_Analyze(connp, userp, reqp,
3344 &dscp->fid, &volSync, NULL, NULL, code));
3345 code = cm_MapRPCError(code, reqp);
3348 osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
3350 osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
3353 lock_ObtainWrite(&dirop.scp->dirlock);
3354 dirop.lockType = CM_DIRLOCK_WRITE;
3356 lock_ObtainWrite(&dscp->rw);
3358 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3360 InterlockedDecrement(&dscp->activeRPCs);
3361 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3362 lock_ReleaseWrite(&dscp->rw);
3365 if (cm_CheckDirOpForSingleChange(&dirop)) {
3366 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3368 cm_DirCreateEntry(&dirop, fnamep, &newFid);
3370 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
3374 cm_EndDirOp(&dirop);
3376 /* now try to create the new dir's entry, too, but be careful to
3377 * make sure that we don't merge in old info. Since we weren't locking
3378 * out any requests during the file's creation, we may have pretty old
3382 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3383 code = cm_GetSCache(&newFid, &scp, userp, reqp);
3385 lock_ObtainWrite(&scp->rw);
3386 if (!cm_HaveCallback(scp)) {
3387 InterlockedIncrement(&scp->activeRPCs);
3388 cm_MergeStatus(dscp, scp, &newLinkStatus, &volSync,
3391 lock_ReleaseWrite(&scp->rw);
3396 cm_ReleaseSCache(scp);
3403 /* and return error code */
3407 /*! \brief Remove a directory
3409 Encapsulates a call to RXAFS_RemoveDir().
3411 \param[in] dscp cm_scache_t for the directory containing the
3412 directory to be removed.
3414 \param[in] fnamep This will be the original name of the directory
3415 as known to the file server. It will be passed in to RXAFS_RemoveDir().
3416 This parameter is optional. If it is not provided the value
3419 \param[in] cnamep Normalized name used to update the local
3422 \param[in] userp cm_user_t for the request.
3424 \param[in] reqp Request tracker.
3426 long cm_RemoveDir(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t *cnamep, cm_user_t *userp, cm_req_t *reqp)
3432 AFSFetchStatus updatedDirStatus;
3434 struct rx_connection * rxconnp;
3436 cm_scache_t *scp = NULL;
3437 int free_fnamep = FALSE;
3439 memset(&volSync, 0, sizeof(volSync));
3441 if (fnamep == NULL) {
3444 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ,
3445 CM_DIROP_FLAG_NONE, &dirop);
3447 code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
3450 cm_EndDirOp(&dirop);
3457 code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
3461 /* Check for RO volume */
3462 if (dscp->flags & CM_SCACHEFLAG_RO) {
3463 code = CM_ERROR_READONLY;
3467 /* before starting the RPC, mark that we're changing the directory data,
3468 * so that someone who does a chmod on the dir will wait until our
3471 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3473 lock_ObtainWrite(&dscp->rw);
3474 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3475 lock_ReleaseWrite(&dscp->rw);
3477 cm_EndDirOp(&dirop);
3482 /* try the RPC now */
3483 InterlockedIncrement(&dscp->activeRPCs);
3484 osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%p", dscp);
3486 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3490 dirAFSFid.Volume = dscp->fid.volume;
3491 dirAFSFid.Vnode = dscp->fid.vnode;
3492 dirAFSFid.Unique = dscp->fid.unique;
3494 rxconnp = cm_GetRxConn(connp);
3495 code = RXAFS_RemoveDir(rxconnp, &dirAFSFid, fnamep,
3496 &updatedDirStatus, &volSync);
3497 rx_PutConnection(rxconnp);
3499 } while (cm_Analyze(connp, userp, reqp,
3500 &dscp->fid, &volSync, NULL, NULL, code));
3501 code = cm_MapRPCErrorRmdir(code, reqp);
3504 osi_Log1(afsd_logp, "CALL RemoveDir FAILURE, code 0x%x", code);
3506 osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
3509 lock_ObtainWrite(&dirop.scp->dirlock);
3510 dirop.lockType = CM_DIRLOCK_WRITE;
3512 lock_ObtainWrite(&dscp->rw);
3514 cm_dnlcRemove(dscp, cnamep);
3515 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3517 InterlockedDecrement(&dscp->activeRPCs);
3519 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3520 lock_ReleaseWrite(&dscp->rw);
3523 if (cm_CheckDirOpForSingleChange(&dirop) && cnamep != NULL) {
3524 cm_DirDeleteEntry(&dirop, fnamep);
3526 cm_BPlusDirDeleteEntry(&dirop, cnamep);
3530 cm_EndDirOp(&dirop);
3533 cm_ReleaseSCache(scp);
3535 lock_ObtainWrite(&scp->rw);
3536 scp->flags |= CM_SCACHEFLAG_DELETED;
3537 lock_ObtainWrite(&cm_scacheLock);
3538 cm_AdjustScacheLRU(scp);
3539 cm_RemoveSCacheFromHashTable(scp);
3540 lock_ReleaseWrite(&cm_scacheLock);
3541 lock_ReleaseWrite(&scp->rw);
3542 if (RDR_Initialized && !(reqp->flags & CM_REQ_SOURCE_REDIR) &&
3543 !RDR_InvalidateObject(scp->fid.cell, scp->fid.volume, scp->fid.vnode,
3544 scp->fid.unique, scp->fid.hash,
3545 scp->fileType, AFS_INVALIDATE_DELETED))
3546 buf_ClearRDRFlag(scp, "rmdir");
3554 /* and return error code */
3558 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
3560 /* grab mutex on contents */
3561 lock_ObtainWrite(&scp->rw);
3563 /* reset the prefetch info */
3564 scp->prefetch.base.LowPart = 0; /* base */
3565 scp->prefetch.base.HighPart = 0;
3566 scp->prefetch.end.LowPart = 0; /* and end */
3567 scp->prefetch.end.HighPart = 0;
3569 /* release mutex on contents */
3570 lock_ReleaseWrite(&scp->rw);
3576 /*! \brief Rename a file or directory
3578 Encapsulates a RXAFS_Rename() call.
3580 \param[in] oldDscp cm_scache_t for the directory containing the old
3583 \param[in] oldNamep The original old name known to the file server.
3584 This is the name that will be passed into the RXAFS_Rename().
3585 If it is not provided, it will be looked up.
3587 \param[in] normalizedOldNamep Normalized old name. This is used for
3588 updating local directory caches.
3590 \param[in] newDscp cm_scache_t for the directory containing the new
3593 \param[in] newNamep New name. Normalized.
3595 \param[in] userp cm_user_t for the request.
3597 \param[in,out] reqp Request tracker.
3600 long cm_Rename(cm_scache_t *oldDscp, fschar_t *oldNamep, clientchar_t *cOldNamep,
3601 cm_scache_t *newDscp, clientchar_t *cNewNamep, cm_user_t *userp,
3606 AFSFid oldDirAFSFid;
3607 AFSFid newDirAFSFid;
3609 AFSFetchStatus updatedOldDirStatus;
3610 AFSFetchStatus updatedNewDirStatus;
3613 struct rx_connection * rxconnp;
3614 cm_dirOp_t oldDirOp;
3617 cm_dirOp_t newDirOp;
3618 fschar_t * newNamep = NULL;
3619 int free_oldNamep = FALSE;
3620 cm_scache_t *oldScp = NULL, *newScp = NULL;
3622 memset(&volSync, 0, sizeof(volSync));
3624 if (cOldNamep == NULL || cNewNamep == NULL ||
3625 cm_ClientStrLen(cOldNamep) == 0 ||
3626 cm_ClientStrLen(cNewNamep) == 0)
3627 return CM_ERROR_INVAL;
3630 * Before we permit the operation, make sure that we do not already have
3631 * an object in the destination directory that has a case-insensitive match
3632 * for this name UNLESS the matching object is the object we are renaming.
3634 code = cm_Lookup(oldDscp, cOldNamep, 0, userp, reqp, &oldScp);
3636 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S old name lookup failed",
3637 oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3641 /* Case sensitive lookup. If this succeeds we are done. */
3642 code = cm_Lookup(newDscp, cNewNamep, 0, userp, reqp, &newScp);
3645 * Case insensitive lookup. If this succeeds, it could have found the
3646 * same file with a name that differs only by case or it could be a
3647 * different file entirely.
3649 code = cm_Lookup(newDscp, cNewNamep, CM_FLAG_CASEFOLD, userp, reqp, &newScp);
3651 /* found a matching object with the new name */
3652 if (cm_FidCmp(&oldScp->fid, &newScp->fid)) {
3653 /* and they don't match so return an error */
3654 osi_Log2(afsd_logp, "cm_Rename newDscp 0x%p cNewName %S new name already exists",
3655 newDscp, osi_LogSaveStringW(afsd_logp, cNewNamep));
3656 code = CM_ERROR_EXISTS;
3658 cm_ReleaseSCache(newScp);
3660 } else if (code == CM_ERROR_AMBIGUOUS_FILENAME) {
3661 code = CM_ERROR_EXISTS;
3663 /* The target does not exist. Clear the error and perform the rename. */
3668 /* Check for RO volume */
3670 (oldDscp->flags & CM_SCACHEFLAG_RO) || (newDscp->flags & CM_SCACHEFLAG_RO)) {
3671 code = CM_ERROR_READONLY;
3677 if (oldNamep == NULL) {
3680 code = cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_READ,
3681 CM_DIROP_FLAG_NONE, &oldDirOp);
3683 code = cm_BPlusDirLookupOriginalName(&oldDirOp, cOldNamep, &oldNamep);
3685 free_oldNamep = TRUE;
3686 cm_EndDirOp(&oldDirOp);
3690 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S Original Name lookup failed",
3691 oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3697 /* before starting the RPC, mark that we're changing the directory data,
3698 * so that someone who does a chmod on the dir will wait until our call
3699 * completes. We do this in vnode order so that we don't deadlock,
3700 * which makes the code a little verbose.
3702 if (oldDscp == newDscp) {
3703 /* check for identical names */
3704 if (cm_ClientStrCmp(cOldNamep, cNewNamep) == 0) {
3705 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_RENAME_IDENTICAL",
3707 code = CM_ERROR_RENAME_IDENTICAL;
3712 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE,
3713 CM_DIROP_FLAG_NONE, &oldDirOp);
3714 lock_ObtainWrite(&oldDscp->rw);
3715 cm_dnlcRemove(oldDscp, cOldNamep);
3716 cm_dnlcRemove(oldDscp, cNewNamep);
3717 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3718 CM_SCACHESYNC_STOREDATA);
3719 lock_ReleaseWrite(&oldDscp->rw);
3721 cm_EndDirOp(&oldDirOp);
3725 /* two distinct dir vnodes */
3727 if (oldDscp->fid.cell != newDscp->fid.cell ||
3728 oldDscp->fid.volume != newDscp->fid.volume) {
3729 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_CROSSDEVLINK",
3731 code = CM_ERROR_CROSSDEVLINK;
3735 /* shouldn't happen that we have distinct vnodes for two
3736 * different files, but could due to deliberate attack, or
3737 * stale info. Avoid deadlocks and quit now.
3739 if (oldDscp->fid.vnode == newDscp->fid.vnode) {
3740 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p vnode collision",
3742 code = CM_ERROR_CROSSDEVLINK;
3746 if (oldDscp->fid.vnode < newDscp->fid.vnode) {
3747 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE,
3748 CM_DIROP_FLAG_NONE, &oldDirOp);
3749 lock_ObtainWrite(&oldDscp->rw);
3750 cm_dnlcRemove(oldDscp, cOldNamep);
3751 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3752 CM_SCACHESYNC_STOREDATA);
3753 lock_ReleaseWrite(&oldDscp->rw);
3755 cm_EndDirOp(&oldDirOp);
3757 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE,
3758 CM_DIROP_FLAG_NONE, &newDirOp);
3759 lock_ObtainWrite(&newDscp->rw);
3760 cm_dnlcRemove(newDscp, cNewNamep);
3761 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3762 CM_SCACHESYNC_STOREDATA);
3763 lock_ReleaseWrite(&newDscp->rw);
3765 cm_EndDirOp(&newDirOp);
3767 /* cleanup first one */
3768 lock_ObtainWrite(&oldDscp->rw);
3769 cm_SyncOpDone(oldDscp, NULL,
3770 CM_SCACHESYNC_STOREDATA);
3771 lock_ReleaseWrite(&oldDscp->rw);
3772 cm_EndDirOp(&oldDirOp);
3777 /* lock the new vnode entry first */
3778 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE,
3779 CM_DIROP_FLAG_NONE, &newDirOp);
3780 lock_ObtainWrite(&newDscp->rw);
3781 cm_dnlcRemove(newDscp, cNewNamep);
3782 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3783 CM_SCACHESYNC_STOREDATA);
3784 lock_ReleaseWrite(&newDscp->rw);
3786 cm_EndDirOp(&newDirOp);
3788 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE,
3789 CM_DIROP_FLAG_NONE, &oldDirOp);
3790 lock_ObtainWrite(&oldDscp->rw);
3791 cm_dnlcRemove(oldDscp, cOldNamep);
3792 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3793 CM_SCACHESYNC_STOREDATA);
3794 lock_ReleaseWrite(&oldDscp->rw);
3796 cm_EndDirOp(&oldDirOp);
3798 /* cleanup first one */
3799 lock_ObtainWrite(&newDscp->rw);
3800 cm_SyncOpDone(newDscp, NULL,
3801 CM_SCACHESYNC_STOREDATA);
3802 lock_ReleaseWrite(&newDscp->rw);
3803 cm_EndDirOp(&newDirOp);
3807 } /* two distinct vnodes */
3814 newNamep = cm_ClientStringToFsStringAlloc(cNewNamep, -1, NULL);
3816 /* try the RPC now */
3817 InterlockedIncrement(&oldDscp->activeRPCs);
3819 InterlockedIncrement(&newDscp->activeRPCs);
3820 osi_Log2(afsd_logp, "CALL Rename old scp 0x%p new scp 0x%p",
3823 code = cm_ConnFromFID(&oldDscp->fid, userp, reqp, &connp);
3827 oldDirAFSFid.Volume = oldDscp->fid.volume;
3828 oldDirAFSFid.Vnode = oldDscp->fid.vnode;
3829 oldDirAFSFid.Unique = oldDscp->fid.unique;
3830 newDirAFSFid.Volume = newDscp->fid.volume;
3831 newDirAFSFid.Vnode = newDscp->fid.vnode;
3832 newDirAFSFid.Unique = newDscp->fid.unique;
3834 rxconnp = cm_GetRxConn(connp);
3835 code = RXAFS_Rename(rxconnp, &oldDirAFSFid, oldNamep,
3836 &newDirAFSFid, newNamep,
3837 &updatedOldDirStatus, &updatedNewDirStatus,
3839 rx_PutConnection(rxconnp);
3841 } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
3842 &volSync, NULL, NULL, code));
3843 code = cm_MapRPCError(code, reqp);
3846 osi_Log1(afsd_logp, "CALL Rename FAILURE, code 0x%x", code);
3848 osi_Log0(afsd_logp, "CALL Rename SUCCESS");
3850 /* update the individual stat cache entries for the directories */
3852 lock_ObtainWrite(&oldDirOp.scp->dirlock);
3853 oldDirOp.lockType = CM_DIRLOCK_WRITE;
3855 lock_ObtainWrite(&oldDscp->rw);
3858 cm_MergeStatus(NULL, oldDscp, &updatedOldDirStatus, &volSync,
3859 userp, reqp, CM_MERGEFLAG_DIROP);
3861 InterlockedDecrement(&oldDscp->activeRPCs);
3862 cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
3863 lock_ReleaseWrite(&oldDscp->rw);
3865 if (code == 0 && cm_CheckDirOpForSingleChange(&oldDirOp)) {
3867 diropCode = cm_BPlusDirLookup(&oldDirOp, cOldNamep, &fileFid);
3868 if (diropCode == CM_ERROR_INEXACT_MATCH)
3870 else if (diropCode == EINVAL)
3872 diropCode = cm_DirLookup(&oldDirOp, oldNamep, &fileFid);
3874 if (diropCode == 0) {
3876 diropCode = cm_DirCreateEntry(&oldDirOp, newNamep, &fileFid);
3878 cm_BPlusDirCreateEntry(&oldDirOp, cNewNamep, &fileFid);
3882 if (diropCode == 0) {
3883 diropCode = cm_DirDeleteEntry(&oldDirOp, oldNamep);
3885 cm_BPlusDirDeleteEntry(&oldDirOp, cOldNamep);
3890 cm_EndDirOp(&oldDirOp);
3892 /* and update it for the new one, too, if necessary */
3895 lock_ObtainWrite(&newDirOp.scp->dirlock);
3896 newDirOp.lockType = CM_DIRLOCK_WRITE;
3898 lock_ObtainWrite(&newDscp->rw);
3900 cm_MergeStatus(NULL, newDscp, &updatedNewDirStatus, &volSync,
3901 userp, reqp, CM_MERGEFLAG_DIROP);
3903 InterlockedIncrement(&newDscp->activeRPCs);
3904 cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
3905 lock_ReleaseWrite(&newDscp->rw);
3909 * The following optimization does not work.
3910 * When the file server processed a RXAFS_Rename() request the
3911 * FID of the object being moved between directories is not
3912 * preserved. The client does not know the new FID nor the
3913 * version number of the target. Not only can we not create
3914 * the directory entry in the new directory, but we can't
3915 * preserve the cached data for the file. It must be re-read
3916 * from the file server. - jaltman, 2009/02/20
3919 /* we only make the local change if we successfully made
3920 the change in the old directory AND there was only one
3921 change in the new directory */
3922 if (diropCode == 0 && cm_CheckDirOpForSingleChange(&newDirOp)) {
3923 cm_DirCreateEntry(&newDirOp, newNamep, &fileFid);
3925 cm_BPlusDirCreateEntry(&newDirOp, cNewNamep, &fileFid);
3930 cm_EndDirOp(&newDirOp);
3934 * After the rename the file server has invalidated the callbacks
3935 * on the file that was moved nor do we have a directory reference
3938 lock_ObtainWrite(&oldScp->rw);
3939 cm_DiscardSCache(oldScp);
3940 lock_ReleaseWrite(&oldScp->rw);
3942 if (RDR_Initialized)
3943 RDR_InvalidateObject(oldScp->fid.cell, oldScp->fid.volume, oldScp->fid.vnode, oldScp->fid.unique,
3944 oldScp->fid.hash, oldScp->fileType, AFS_INVALIDATE_CALLBACK);
3947 cm_ReleaseSCache(oldScp);
3954 /* and return error code */
3958 /* Byte range locks:
3960 The OpenAFS Windows client has to fake byte range locks given no
3961 server side support for such locks. This is implemented as keyed
3962 byte range locks on the cache manager.
3964 Keyed byte range locks:
3966 Each cm_scache_t structure keeps track of a list of keyed locks.
3967 The key for a lock identifies an owner of a set of locks (referred
3968 to as a client). Each key is represented by a value. The set of
3969 key values used within a specific cm_scache_t structure form a
3970 namespace that has a scope of just that cm_scache_t structure. The
3971 same key value can be used with another cm_scache_t structure and
3972 correspond to a completely different client. However it is
3973 advantageous for the SMB or IFS layer to make sure that there is a
3974 1-1 mapping between client and keys over all cm_scache_t objects.
3976 Assume a client C has key Key(C) (although, since the scope of the
3977 key is a cm_scache_t, the key can be Key(C,S), where S is the
3978 cm_scache_t. But assume a 1-1 relation between keys and clients).
3979 A byte range (O,+L) denotes byte addresses (O) through (O+L-1)
3980 inclusive (a.k.a. [O,O+L-1]). The function Key(x) is implemented
3981 through cm_generateKey() function for both SMB and IFS.
3983 The list of locks for a cm_scache_t object S is maintained in
3984 S->fileLocks. The cache manager will set a lock on the AFS file
3985 server in order to assert the locks in S->fileLocks. If only
3986 shared locks are in place for S, then the cache manager will obtain
3987 a LockRead lock, while if there are any exclusive locks, it will
3988 obtain a LockWrite lock. If the exclusive locks are all released
3989 while the shared locks remain, then the cache manager will
3990 downgrade the lock from LockWrite to LockRead. Similarly, if an
3991 exclusive lock is obtained when only shared locks exist, then the
3992 cache manager will try to upgrade the lock from LockRead to
3995 Each lock L owned by client C maintains a key L->key such that
3996 L->key == Key(C), the effective range defined by L->LOffset and
3997 L->LLength such that the range of bytes affected by the lock is
3998 (L->LOffset, +L->LLength), a type maintained in L->LockType which
3999 is either exclusive or shared.
4003 A lock exists iff it is in S->fileLocks for some cm_scache_t
4004 S. Existing locks are in one of the following states: ACTIVE,
4005 WAITLOCK, WAITUNLOCK, LOST, DELETED.
4007 The following sections describe each lock and the associated
4010 1. ACTIVE: A lock L is ACTIVE iff the cache manager has asserted
4011 the lock with the AFS file server. This type of lock can be
4012 exercised by a client to read or write to the locked region (as
4015 1.1 ACTIVE->LOST: When the AFS file server fails to extend a
4016 server lock that was required to assert the lock. Before
4017 marking the lock as lost, the cache manager checks if the file
4018 has changed on the server. If the file has not changed, then
4019 the cache manager will attempt to obtain a new server lock
4020 that is sufficient to assert the client side locks for the
4021 file. If any of these fail, the lock is marked as LOST.
4022 Otherwise, it is left as ACTIVE.
4024 1.2 ACTIVE->DELETED: Lock is released.
4026 2. WAITLOCK: A lock is in a WAITLOCK state if the cache manager
4027 grants the lock but the lock is yet to be asserted with the AFS
4028 file server. Once the file server grants the lock, the state
4029 will transition to an ACTIVE lock.
4031 2.1 WAITLOCK->ACTIVE: The server granted the lock.
4033 2.2 WAITLOCK->DELETED: Lock is abandoned, or timed out during
4036 2.3 WAITLOCK->LOST: One or more locks from this client were
4037 marked as LOST. No further locks will be granted to this
4038 client until all lost locks are removed.
4040 3. WAITUNLOCK: A lock is in a WAITUNLOCK state if the cache manager
4041 receives a request for a lock that conflicts with an existing
4042 ACTIVE or WAITLOCK lock. The lock will be placed in the queue
4043 and will be granted at such time the conflicting locks are
4044 removed, at which point the state will transition to either
4047 3.1 WAITUNLOCK->ACTIVE: The conflicting lock was removed. The
4048 current serverLock is sufficient to assert this lock, or a
4049 sufficient serverLock is obtained.
4051 3.2 WAITUNLOCK->WAITLOCK: The conflicting lock was removed,
4052 however the required serverLock is yet to be asserted with the
4055 3.3 WAITUNLOCK->DELETED: The lock is abandoned, timed out or
4058 3.5 WAITUNLOCK->LOST: One or more locks from this client were
4059 marked as LOST. No further locks will be granted to this
4060 client until all lost locks are removed.
4062 4. LOST: A lock L is LOST if the server lock that was required to
4063 assert the lock could not be obtained or if it could not be
4064 extended, or if other locks by the same client were LOST.
4065 Essentially, once a lock is LOST, the contract between the cache
4066 manager and that specific client is no longer valid.
4068 The cache manager rechecks the server lock once every minute and
4069 extends it as appropriate. If this is not done for 5 minutes,
4070 the AFS file server will release the lock (the 5 minute timeout
4071 is based on current file server code and is fairly arbitrary).
4072 Once released, the lock cannot be re-obtained without verifying
4073 that the contents of the file hasn't been modified since the
4074 time the lock was released. Re-obtaining the lock without
4075 verifying this may lead to data corruption. If the lock can not
4076 be obtained safely, then all active locks for the cm_scache_t
4079 4.1 LOST->DELETED: The lock is released.
4081 5. DELETED: The lock is no longer relevant. Eventually, it will
4082 get removed from the cm_scache_t. In the meantime, it will be
4083 treated as if it does not exist.
4085 5.1 DELETED->not exist: The lock is removed from the
4088 The following are classifications of locks based on their state.
4090 6* A lock L is ACCEPTED if it is ACTIVE or WAITLOCK. These locks
4091 have been accepted by the cache manager, but may or may not have
4092 been granted back to the client.
4094 7* A lock L is QUEUED if it is ACTIVE, WAITLOCK or WAITUNLOCK.
4096 8* A lock L is WAITING if it is WAITLOCK or WAITUNLOCK.
4100 A client C can READ range (Offset,+Length) of a file represented by
4101 cm_scache_t S iff (1):
4103 1. for all _a_ in (Offset,+Length), all of the following is true:
4105 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
4106 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
4109 1.2 For each LOST lock L in S->fileLocks such that _a_ in
4110 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
4113 (When locks are lost on an cm_scache_t, all locks are lost. By
4114 4.2 (below), if there is an exclusive LOST lock, then there
4115 can't be any overlapping ACTIVE locks.)
4117 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
4119 2. for all _a_ in (Offset,+Length), one of the following is true:
4121 2.1 Byte _a_ of S is unowned (as specified in 1.1) AND there
4122 does not exist a LOST lock L such that _a_ in
4123 (L->LOffset,+L->LLength).
4125 2.2 Byte _a_ of S is owned by C under lock L (as specified in
4126 1.2) AND L->LockType is exclusive.
4128 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
4130 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
4133 3.1 If L->LockType is exclusive then there does NOT exist a
4134 ACCEPTED lock M in S->fileLocks such that _a_ in
4135 (M->LOffset,+M->LLength).
4137 (If we count all QUEUED locks then we hit cases such as
4138 cascading waiting locks where the locks later on in the queue
4139 can be granted without compromising file integrity. On the
4140 other hand if only ACCEPTED locks are considered, then locks
4141 that were received earlier may end up waiting for locks that
4142 were received later to be unlocked. The choice of ACCEPTED
4143 locks was made to mimic the Windows byte range lock
4146 3.2 If L->LockType is shared then for each ACCEPTED lock M in
4147 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
4148 M->LockType is shared.
4150 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
4152 4.1 M->key != Key(C)
4154 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
4155 and (M->LOffset,+M->LLength) do not intersect.
4157 (Note: If a client loses a lock, it loses all locks.
4158 Subsequently, it will not be allowed to obtain any more locks
4159 until all existing LOST locks that belong to the client are
4160 released. Once all locks are released by a single client,
4161 there exists no further contract between the client and AFS
4162 about the contents of the file, hence the client can then
4163 proceed to obtain new locks and establish a new contract.
4165 This doesn't quite work as you think it should, because most
4166 applications aren't built to deal with losing locks they
4167 thought they once had. For now, we don't have a good
4168 solution to lost locks.
4170 Also, for consistency reasons, we have to hold off on
4171 granting locks that overlap exclusive LOST locks.)
4173 A client C can only unlock locks L in S->fileLocks which have
4176 The representation and invariants are as follows:
4178 - Each cm_scache_t structure keeps:
4180 - A queue of byte-range locks (cm_scache_t::fileLocks) which
4181 are of type cm_file_lock_t.
4183 - A record of the highest server-side lock that has been
4184 obtained for this object (cm_scache_t::serverLock), which is
4185 one of (-1), LockRead, LockWrite.
4187 - A count of ACCEPTED exclusive and shared locks that are in the
4188 queue (cm_scache_t::sharedLocks and
4189 cm_scache_t::exclusiveLocks)
4191 - Each cm_file_lock_t structure keeps:
4193 - The type of lock (cm_file_lock_t::LockType)
4195 - The key associated with the lock (cm_file_lock_t::key)
4197 - The offset and length of the lock (cm_file_lock_t::LOffset
4198 and cm_file_lock_t::LLength)
4200 - The state of the lock.
4202 - Time of issuance or last successful extension
4204 Semantic invariants:
4206 I1. The number of ACCEPTED locks in S->fileLocks are
4207 (S->sharedLocks + S->exclusiveLocks)
4209 External invariants:
4211 I3. S->serverLock is the lock that we have asserted with the
4212 AFS file server for this cm_scache_t.
4214 I4. S->serverLock == LockRead iff there is at least one ACTIVE
4215 shared lock, but no ACTIVE exclusive locks.
4217 I5. S->serverLock == LockWrite iff there is at least one ACTIVE
4220 I6. If L is a LOST lock, then for each lock M in S->fileLocks,
4221 M->key == L->key IMPLIES M is LOST or DELETED.
4226 #define IS_LOCK_ACTIVE(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == 0)
4228 #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)
4230 #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)
4232 #define IS_LOCK_LOST(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_LOST)
4234 #define IS_LOCK_DELETED(lockp) (((lockp)->flags & CM_FILELOCK_FLAG_DELETED) == CM_FILELOCK_FLAG_DELETED)
4237 #define IS_LOCK_ACCEPTED(lockp) (IS_LOCK_ACTIVE(lockp) || IS_LOCK_WAITLOCK(lockp))
4240 #define IS_LOCK_CLIENTONLY(lockp) ((((lockp)->scp->flags & CM_SCACHEFLAG_RO) == CM_SCACHEFLAG_RO) || (((lockp)->flags & CM_FILELOCK_FLAG_CLIENTONLY) == CM_FILELOCK_FLAG_CLIENTONLY))
4243 #define INTERSECT_RANGE(r1,r2) (((r2).offset+(r2).length) > (r1).offset && ((r1).offset +(r1).length) > (r2).offset)
4246 #define CONTAINS_RANGE(r1,r2) (((r2).offset+(r2).length) <= ((r1).offset+(r1).length) && (r1).offset <= (r2).offset)
4248 #if defined(VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS) && !defined(LOCK_TESTING)
4249 #define SCP_SUPPORTS_BRLOCKS(scp) ((scp)->cbServerp && ((scp)->cbServerp->capabilities & VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS))
4251 #define SCP_SUPPORTS_BRLOCKS(scp) (1)
4254 #define SERVERLOCKS_ENABLED(scp) (!((scp)->flags & CM_SCACHEFLAG_RO) && cm_enableServerLocks && SCP_SUPPORTS_BRLOCKS(scp))
4256 #if defined(VICED_CAPABILITY_WRITELOCKACL)
4257 #define SCP_SUPPORTS_WRITELOCKACL(scp) ((scp)->cbServerp && ((scp->cbServerp->capabilities & VICED_CAPABILITY_WRITELOCKACL)))
4259 #define SCP_SUPPORTS_WRITELOCKACL(scp) (0)
4261 /* This should really be defined in any build that this code is being
4263 #error VICED_CAPABILITY_WRITELOCKACL not defined.
4266 static void cm_LockRangeSubtract(cm_range_t * pos, const cm_range_t * neg)
4268 afs_int64 int_begin;
4271 int_begin = MAX(pos->offset, neg->offset);
4272 int_end = MIN(pos->offset+pos->length, neg->offset+neg->length);
4274 if (int_begin < int_end) {
4275 if (int_begin == pos->offset) {
4276 pos->length = pos->offset + pos->length - int_end;
4277 pos->offset = int_end;
4278 } else if (int_end == pos->offset + pos->length) {
4279 pos->length = int_begin - pos->offset;
4282 /* We only subtract ranges if the resulting range is
4283 contiguous. If we try to support non-contigous ranges, we
4284 aren't actually improving performance. */
4288 /* Called with scp->rw held. Returns 0 if all is clear to read the
4289 specified range by the client identified by key.
4291 long cm_LockCheckRead(cm_scache_t *scp,
4292 LARGE_INTEGER LOffset,
4293 LARGE_INTEGER LLength,
4296 #ifndef ADVISORY_LOCKS
4298 cm_file_lock_t *fileLock;
4302 int substract_ranges = FALSE;
4304 range.offset = LOffset.QuadPart;
4305 range.length = LLength.QuadPart;
4309 1. for all _a_ in (Offset,+Length), all of the following is true:
4311 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
4312 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
4315 1.2 For each LOST lock L in S->fileLocks such that _a_ in
4316 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
4321 lock_ObtainRead(&cm_scacheLock);
4323 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
4325 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4327 if (INTERSECT_RANGE(range, fileLock->range)) {
4328 if (IS_LOCK_ACTIVE(fileLock)) {
4329 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4331 /* If there is an active lock for this client, it
4332 is safe to substract ranges.*/
4333 cm_LockRangeSubtract(&range, &fileLock->range);
4334 substract_ranges = TRUE;
4336 if (fileLock->lockType != LockRead) {
4337 code = CM_ERROR_LOCK_CONFLICT;
4341 /* even if the entire range is locked for reading,
4342 we still can't grant the lock at this point
4343 because the client may have lost locks. That
4344 is, unless we have already seen an active lock
4345 belonging to the client, in which case there
4346 can't be any lost locks for this client. */
4347 if (substract_ranges)
4348 cm_LockRangeSubtract(&range, &fileLock->range);
4350 } else if (IS_LOCK_LOST(fileLock) &&
4351 (cm_KeyEquals(&fileLock->key, &key, 0) || fileLock->lockType == LockWrite)) {
4352 code = CM_ERROR_BADFD;
4358 lock_ReleaseRead(&cm_scacheLock);
4360 osi_Log4(afsd_logp, "cm_LockCheckRead scp 0x%x offset %d length %d code 0x%x",
4361 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
4372 /* Called with scp->rw held. Returns 0 if all is clear to write the
4373 specified range by the client identified by key.
4375 long cm_LockCheckWrite(cm_scache_t *scp,
4376 LARGE_INTEGER LOffset,
4377 LARGE_INTEGER LLength,
4380 #ifndef ADVISORY_LOCKS
4382 cm_file_lock_t *fileLock;
4387 range.offset = LOffset.QuadPart;
4388 range.length = LLength.QuadPart;
4391 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
4393 2. for all _a_ in (Offset,+Length), one of the following is true:
4395 2.1 Byte _a_ of S is unowned AND there does not exist a LOST
4396 lock L such that _a_ in (L->LOffset,+L->LLength).
4398 2.2 Byte _a_ of S is owned by C under lock L AND L->LockType is
4402 lock_ObtainRead(&cm_scacheLock);
4404 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
4406 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4408 if (INTERSECT_RANGE(range, fileLock->range)) {
4409 if (IS_LOCK_ACTIVE(fileLock)) {
4410 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4411 if (fileLock->lockType == LockWrite) {
4413 /* if there is an active lock for this client, it
4414 is safe to substract ranges */
4415 cm_LockRangeSubtract(&range, &fileLock->range);
4417 code = CM_ERROR_LOCK_CONFLICT;
4421 code = CM_ERROR_LOCK_CONFLICT;
4424 } else if (IS_LOCK_LOST(fileLock)) {
4425 code = CM_ERROR_BADFD;
4431 lock_ReleaseRead(&cm_scacheLock);
4433 osi_Log4(afsd_logp, "cm_LockCheckWrite scp 0x%x offset %d length %d code 0x%x",
4434 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
4445 /* Called with cm_scacheLock write locked */
4446 static cm_file_lock_t * cm_GetFileLock(void) {
4449 l = (cm_file_lock_t *) cm_freeFileLocks;
4451 osi_QRemove(&cm_freeFileLocks, &l->q);
4453 l = malloc(sizeof(cm_file_lock_t));
4454 osi_assertx(l, "null cm_file_lock_t");
4457 memset(l, 0, sizeof(cm_file_lock_t));
4462 /* Called with cm_scacheLock write locked */
4463 static void cm_PutFileLock(cm_file_lock_t *l) {
4464 osi_QAdd(&cm_freeFileLocks, &l->q);
4467 /* called with scp->rw held. May release it during processing, but
4468 leaves it held on exit. */
4469 long cm_IntSetLock(cm_scache_t * scp, cm_user_t * userp, int lockType,
4475 struct rx_connection * rxconnp;
4477 afs_uint32 reqflags = reqp->flags;
4479 osi_Log2(afsd_logp, "CALL SetLock scp 0x%p for lock %d", scp, lockType);
4481 if ((lockType != LOCKING_ANDX_SHARED_LOCK && scp->fsLockCount != 0) ||
4482 (lockType == LOCKING_ANDX_SHARED_LOCK && scp->fsLockCount < 0))
4484 code = CM_ERROR_LOCK_NOT_GRANTED;
4485 osi_Log2(afsd_logp, "CALL SetLock FAILURE, fsLockCount %d code 0x%x", scp->fsLockCount, code);
4489 memset(&volSync, 0, sizeof(volSync));
4491 tfid.Volume = scp->fid.volume;
4492 tfid.Vnode = scp->fid.vnode;
4493 tfid.Unique = scp->fid.unique;
4496 reqp->flags |= CM_REQ_NORETRY;
4497 lock_ReleaseWrite(&scp->rw);
4500 code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4504 rxconnp = cm_GetRxConn(connp);
4505 code = RXAFS_SetLock(rxconnp, &tfid, lockType,
4507 rx_PutConnection(rxconnp);
4509 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
4512 code = cm_MapRPCError(code, reqp);
4514 osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
4516 osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
4519 reqp->flags = reqflags;
4521 lock_ObtainWrite(&scp->rw);
4524 * The file server does not return a status structure so we must
4525 * locally track the file server lock count to the best of our
4528 if (lockType == LockWrite)
4529 scp->fsLockCount = -1;
4536 /* called with scp->rw held. Releases it during processing */
4537 long cm_IntReleaseLock(cm_scache_t * scp, cm_user_t * userp,
4543 struct rx_connection * rxconnp;
4546 if (scp->flags & CM_SCACHEFLAG_DELETED) {
4547 osi_Log1(afsd_logp, "CALL ReleaseLock on Deleted Vnode scp 0x%p", scp);
4551 memset(&volSync, 0, sizeof(volSync));
4553 tfid.Volume = scp->fid.volume;
4554 tfid.Vnode = scp->fid.vnode;
4555 tfid.Unique = scp->fid.unique;
4558 lock_ReleaseWrite(&scp->rw);
4560 osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
4563 code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4567 rxconnp = cm_GetRxConn(connp);
4568 code = RXAFS_ReleaseLock(rxconnp, &tfid, &volSync);
4569 rx_PutConnection(rxconnp);
4571 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
4573 code = cm_MapRPCError(code, reqp);
4576 "CALL ReleaseLock FAILURE, code 0x%x", code);
4579 "CALL ReleaseLock SUCCESS");
4581 lock_ObtainWrite(&scp->rw);
4584 * The file server does not return a status structure so we must
4585 * locally track the file server lock count to the best of our
4589 if (scp->fsLockCount < 0)
4590 scp->fsLockCount = 0;
4593 return (code != CM_ERROR_BADFD ? code : 0);
4596 /* called with scp->rw held. May release it during processing, but
4597 will exit with lock held.
4601 - 0 if the user has permission to get the specified lock for the scp
4603 - CM_ERROR_NOACCESS if not
4605 Any other error from cm_SyncOp will be sent down untranslated.
4607 If CM_ERROR_NOACCESS is returned and lock_type is LockRead, then
4608 phas_insert (if non-NULL) will receive a boolean value indicating
4609 whether the user has INSERT permission or not.
4611 long cm_LockCheckPerms(cm_scache_t * scp,
4618 long code = 0, code2 = 0;
4620 /* lock permissions are slightly tricky because of the 'i' bit.
4621 If the user has PRSFS_LOCK, she can read-lock the file. If the
4622 user has PRSFS_WRITE, she can write-lock the file. However, if
4623 the user has PRSFS_INSERT, then she can write-lock new files,
4624 but not old ones. Since we don't have information about
4625 whether a file is new or not, we assume that if the user owns
4626 the scp, then she has the permissions that are granted by
4629 osi_Log3(afsd_logp, "cm_LockCheckPerms for scp[0x%p] type[%d] user[0x%p]",
4630 scp, lock_type, userp);
4632 if (lock_type == LockRead)
4633 rights |= PRSFS_LOCK;
4634 else if (lock_type == LockWrite)
4635 rights |= PRSFS_WRITE | PRSFS_LOCK;
4638 osi_assertx(FALSE, "invalid lock type");
4643 *phas_insert = FALSE;
4645 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
4646 CM_SCACHESYNC_GETSTATUS |
4647 CM_SCACHESYNC_NEEDCALLBACK);
4649 if (phas_insert && scp->creator == userp) {
4651 /* If this file was created by the user, then we check for
4652 PRSFS_INSERT. If the file server is recent enough, then
4653 this should be sufficient for her to get a write-lock (but
4654 not necessarily a read-lock). VICED_CAPABILITY_WRITELOCKACL
4655 indicates whether a file server supports getting write
4656 locks when the user only has PRSFS_INSERT.
4658 If the file was not created by the user we skip the check
4659 because the INSERT bit will not apply to this user even
4663 code2 = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
4664 CM_SCACHESYNC_GETSTATUS |
4665 CM_SCACHESYNC_NEEDCALLBACK);
4667 if (code2 == CM_ERROR_NOACCESS) {
4668 osi_Log0(afsd_logp, "cm_LockCheckPerms user has no INSERT bits");
4670 *phas_insert = TRUE;
4671 osi_Log0(afsd_logp, "cm_LockCheckPerms user has INSERT bits");
4675 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
4677 osi_Log1(afsd_logp, "cm_LockCheckPerms returning code %d", code);
4682 /* called with scp->rw held */
4683 long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
4684 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
4686 int allowWait, cm_user_t *userp, cm_req_t *reqp,
4687 cm_file_lock_t **lockpp)
4690 int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
4691 cm_file_lock_t *fileLock;
4694 int wait_unlock = FALSE;
4695 int force_client_lock = FALSE;
4697 osi_Log4(afsd_logp, "cm_Lock scp 0x%x type 0x%x offset %d length %d",
4698 scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
4699 osi_Log4(afsd_logp, "... allowWait %d key <0x%x, 0x%x, 0x%x>", allowWait,
4700 key.process_id, key.session_id, key.file_id);
4703 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
4705 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
4708 3.1 If L->LockType is exclusive then there does NOT exist a
4709 ACCEPTED lock M in S->fileLocks such that _a_ in
4710 (M->LOffset,+M->LLength).
4712 3.2 If L->LockType is shared then for each ACCEPTED lock M in
4713 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
4714 M->LockType is shared.
4716 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
4718 4.1 M->key != Key(C)
4720 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
4721 and (M->LOffset,+M->LLength) do not intersect.
4724 range.offset = LOffset.QuadPart;
4725 range.length = LLength.QuadPart;
4727 lock_ObtainRead(&cm_scacheLock);
4729 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4731 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4733 if (IS_LOCK_LOST(fileLock)) {
4734 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4735 code = CM_ERROR_BADFD;
4737 } else if (fileLock->lockType == LockWrite && INTERSECT_RANGE(range, fileLock->range)) {
4738 code = CM_ERROR_WOULDBLOCK;
4744 /* we don't need to check for deleted locks here since deleted
4745 locks are dequeued from scp->fileLocks */
4746 if (IS_LOCK_ACCEPTED(fileLock) &&
4747 INTERSECT_RANGE(range, fileLock->range)) {
4749 if ((sLockType & LOCKING_ANDX_SHARED_LOCK) == 0 ||
4750 fileLock->lockType != LockRead) {
4752 code = CM_ERROR_WOULDBLOCK;
4758 lock_ReleaseRead(&cm_scacheLock);
4760 if (code == 0 && SERVERLOCKS_ENABLED(scp)) {
4761 if (Which == scp->serverLock ||
4762 (Which == LockRead && scp->serverLock == LockWrite)) {
4766 /* we already have the lock we need */
4767 osi_Log3(afsd_logp, " we already have the correct lock. exclusives[%d], shared[%d], serverLock[%d]",
4768 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4770 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4772 /* special case: if we don't have permission to read-lock
4773 the file, then we force a clientside lock. This is to
4774 compensate for applications that obtain a read-lock for
4775 reading files off of directories that don't grant
4776 read-locks to the user. */
4777 if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4779 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4780 osi_Log0(afsd_logp, " User has no read-lock perms, but has INSERT perms.");
4783 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4784 force_client_lock = TRUE;
4788 } else if ((scp->exclusiveLocks > 0) ||
4789 (scp->sharedLocks > 0 && scp->serverLock != LockRead)) {
4792 /* We are already waiting for some other lock. We should
4793 wait for the daemon to catch up instead of generating a
4794 flood of SetLock calls. */
4795 osi_Log3(afsd_logp, " already waiting for other lock. exclusives[%d], shared[%d], serverLock[%d]",
4796 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4798 /* see if we have permission to create the lock in the
4800 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4802 code = CM_ERROR_WOULDBLOCK;
4803 else if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4805 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4807 " User has no read-lock perms, but has INSERT perms.");
4808 code = CM_ERROR_WOULDBLOCK;
4811 " User has no read-lock perms. Forcing client-side lock");
4812 force_client_lock = TRUE;
4816 /* leave any other codes as-is */
4820 int check_data_version = FALSE;
4823 /* first check if we have permission to elevate or obtain
4825 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4827 if (code == CM_ERROR_NOACCESS && Which == LockRead &&
4828 (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp))) {
4829 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4830 force_client_lock = TRUE;
4835 /* has_insert => (Which == LockRead, code == CM_ERROR_NOACCESS) */
4837 if (scp->serverLock == LockRead && Which == LockWrite) {
4839 /* We want to escalate the lock to a LockWrite.
4840 * Unfortunately that's not really possible without
4841 * letting go of the current lock. But for now we do
4845 " attempting to UPGRADE from LockRead to LockWrite.");
4847 " dataVersion on scp: %I64d", scp->dataVersion);
4849 /* we assume at this point (because scp->serverLock
4850 was valid) that we had a valid server lock. */
4851 scp->lockDataVersion = scp->dataVersion;
4852 check_data_version = TRUE;
4854 code = cm_IntReleaseLock(scp, userp, reqp);
4857 /* We couldn't release the lock */
4860 scp->serverLock = -1;
4864 /* We need to obtain a server lock of type Which in order
4865 * to assert this file lock */
4866 #ifndef AGGRESSIVE_LOCKS
4869 newLock = LockWrite;
4872 code = cm_IntSetLock(scp, userp, newLock, reqp);
4874 #ifdef AGGRESSIVE_LOCKS
4875 if ((code == CM_ERROR_WOULDBLOCK ||
4876 code == CM_ERROR_NOACCESS) && newLock != Which) {
4877 /* we wanted LockRead. We tried LockWrite. Now try
4882 osi_assertx(newLock == LockRead, "lock type not read");
4884 code = cm_IntSetLock(scp, userp, newLock, reqp);
4888 if (code == CM_ERROR_NOACCESS) {
4889 if (Which == LockRead) {
4890 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4892 /* We requested a read-lock, but we have permission to
4893 * get a write-lock. Try that */
4895 tcode = cm_LockCheckPerms(scp, LockWrite, userp, reqp, NULL);
4898 newLock = LockWrite;
4900 osi_Log0(afsd_logp, " User has 'i' perms and the request was for a LockRead. Trying to get a LockWrite instead");
4902 code = cm_IntSetLock(scp, userp, newLock, reqp);
4905 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4906 force_client_lock = TRUE;
4908 } else if (Which == LockWrite &&
4909 scp->creator == userp && !SCP_SUPPORTS_WRITELOCKACL(scp)) {
4912 /* Special case: if the lock request was for a
4913 * LockWrite and the user owns the file and we weren't
4914 * allowed to obtain the serverlock, we either lost a
4915 * race (the permissions changed from under us), or we
4916 * have 'i' bits, but we aren't allowed to lock the
4919 /* check if we lost a race... */
4920 tcode = cm_LockCheckPerms(scp, Which, userp, reqp, NULL);
4923 osi_Log0(afsd_logp, " User has 'i' perms but can't obtain write locks. Using client-side locks.");
4924 force_client_lock = TRUE;
4929 if (code == 0 && check_data_version &&
4930 scp->dataVersion != scp->lockDataVersion) {
4931 /* We lost a race. Although we successfully obtained
4932 * a lock, someone modified the file in between. The
4933 * locks have all been technically lost. */
4936 " Data version mismatch while upgrading lock.");
4938 " Data versions before=%I64d, after=%I64d",
4939 scp->lockDataVersion,
4942 " Releasing stale lock for scp 0x%x", scp);
4944 code = cm_IntReleaseLock(scp, userp, reqp);
4946 scp->serverLock = -1;
4948 code = CM_ERROR_INVAL;
4949 } else if (code == 0) {
4950 scp->serverLock = newLock;
4951 scp->lockDataVersion = scp->dataVersion;
4955 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4956 scp->serverLock == -1) {
4957 /* Oops. We lost the lock. */
4958 cm_LockMarkSCacheLost(scp);
4961 } else if (code == 0) { /* server locks not enabled */
4963 " Skipping server lock for scp");
4968 if (code != 0 && !force_client_lock) {
4969 /* Special case error translations
4971 Applications don't expect certain errors from a
4972 LockFile/UnlockFile call. We need to translate some error
4973 code to codes that apps expect and handle. */
4975 /* We shouldn't actually need to handle this case since we
4976 simulate locks for RO scps anyway. */
4977 if (code == CM_ERROR_READONLY) {
4978 osi_Log0(afsd_logp, " Reinterpreting CM_ERROR_READONLY as CM_ERROR_NOACCESS");
4979 code = CM_ERROR_NOACCESS;
4983 if (code == 0 || (code == CM_ERROR_WOULDBLOCK && allowWait) ||
4984 force_client_lock) {
4986 /* clear the error if we are forcing a client lock, so we
4987 don't get confused later. */
4988 if (force_client_lock && code != CM_ERROR_WOULDBLOCK)
4993 lock_ObtainWrite(&cm_scacheLock);
4994 fileLock = cm_GetFileLock();
4996 fileLock->fid = scp->fid;
4998 fileLock->key = key;
4999 fileLock->lockType = Which;
5000 fileLock->userp = userp;
5001 fileLock->range = range;
5002 fileLock->flags = (code == 0 ? 0 :
5004 CM_FILELOCK_FLAG_WAITUNLOCK :
5005 CM_FILELOCK_FLAG_WAITLOCK));
5007 if (force_client_lock || !SERVERLOCKS_ENABLED(scp))
5008 fileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
5010 fileLock->lastUpdate = (code == 0 && !force_client_lock) ? time(NULL) : 0;
5012 osi_QAddT(&scp->fileLocksH, &scp->fileLocksT, &fileLock->fileq);
5013 cm_HoldSCacheNoLock(scp);
5014 fileLock->scp = scp;
5015 osi_QAdd(&cm_allFileLocks, &fileLock->q);
5016 lock_ReleaseWrite(&cm_scacheLock);
5022 if (IS_LOCK_CLIENTONLY(fileLock)) {
5024 } else if (IS_LOCK_ACCEPTED(fileLock)) {
5025 if (Which == LockRead)
5028 scp->exclusiveLocks++;
5032 "cm_Lock Lock added 0x%p flags 0x%x to scp [0x%p]",
5033 fileLock, fileLock->flags, scp);
5035 " exclusives[%d] shared[%d] client[%d] serverLock[%d]",
5036 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
5037 (int)(signed char) scp->serverLock);
5040 "cm_Lock Rejecting lock (code = 0x%x)", code);
5043 /* Convert from would block to lock not granted */
5044 if (code == CM_ERROR_WOULDBLOCK)
5045 code = CM_ERROR_LOCK_NOT_GRANTED;
5051 cm_IntUnlock(cm_scache_t * scp,
5057 osi_assertx(scp->sharedLocks >= 0, "scp->sharedLocks < 0");
5058 osi_assertx(scp->exclusiveLocks >= 0, "scp->exclusiveLocks < 0");
5059 osi_assertx(scp->clientLocks >= 0, "scp->clientLocks < 0");
5061 if (!SERVERLOCKS_ENABLED(scp)) {
5062 osi_Log0(afsd_logp, " Skipping server lock for scp");
5066 /* Ideally we would go through the rest of the locks to determine
5067 * if one or more locks that were formerly in WAITUNLOCK can now
5068 * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
5069 * scp->sharedLocks accordingly. However, the retrying of locks
5070 * in that manner is done cm_RetryLock() manually.
5073 if (scp->serverLock == LockWrite &&
5074 scp->exclusiveLocks == 0 &&
5075 scp->sharedLocks > 0) {
5076 /* The serverLock should be downgraded to LockRead */
5077 osi_Log0(afsd_logp, " DOWNGRADE lock from LockWrite to LockRead");
5079 /* Make sure there are no dirty buffers left. */
5080 code = cm_FSync(scp, userp, reqp, TRUE);
5082 /* since scp->serverLock looked sane, we are going to assume
5083 that we have a valid server lock. */
5084 scp->lockDataVersion = scp->dataVersion;
5085 osi_Log1(afsd_logp, " dataVersion on scp = %I64d", scp->dataVersion);
5087 /* before we downgrade, make sure that we have enough
5088 permissions to get the read lock. */
5089 code = cm_LockCheckPerms(scp, LockRead, userp, reqp, NULL);
5092 osi_Log0(afsd_logp, " SKIPPING downgrade because user doesn't have perms to get downgraded lock");
5098 code = cm_IntReleaseLock(scp, userp, reqp);
5101 /* so we couldn't release it. Just let the lock be for now */
5105 scp->serverLock = -1;
5108 code = cm_IntSetLock(scp, userp, LockRead, reqp);
5110 if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
5111 scp->serverLock = LockRead;
5112 } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
5113 /* We lost a race condition. Although we have a valid
5114 lock on the file, the data has changed and essentially
5115 we have lost the lock we had during the transition. */
5117 osi_Log0(afsd_logp, "Data version mismatch during lock downgrade");
5118 osi_Log2(afsd_logp, " Data versions before=%I64d, after=%I64d",
5119 scp->lockDataVersion,
5122 code = cm_IntReleaseLock(scp, userp, reqp);
5124 code = CM_ERROR_INVAL;
5125 scp->serverLock = -1;
5129 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
5130 (scp->serverLock == -1)) {
5132 cm_LockMarkSCacheLost(scp);
5135 /* failure here has no bearing on the return value of cm_Unlock() */
5138 } else if (scp->serverLock != (-1) &&
5139 scp->exclusiveLocks == 0 &&
5140 scp->sharedLocks == 0) {
5141 /* The serverLock should be released entirely */
5143 if (scp->serverLock == LockWrite) {
5144 osi_Log0(afsd_logp, " RELEASE LockWrite -> LockNone");
5146 /* Make sure there are no dirty buffers left. */
5147 code = cm_FSync(scp, userp, reqp, TRUE);
5149 osi_Log0(afsd_logp, " RELEASE LockRead -> LockNone");
5152 code = cm_IntReleaseLock(scp, userp, reqp);
5155 scp->serverLock = (-1);
5161 /* Called with scp->rw held */
5162 long cm_UnlockByKey(cm_scache_t * scp,
5169 cm_file_lock_t *fileLock;
5170 osi_queue_t *q, *qn;
5173 osi_Log4(afsd_logp, "cm_UnlockByKey scp 0x%p key <0x%x,0x%x,0x%x",
5174 scp, key.process_id, key.session_id, key.file_id);
5175 osi_Log1(afsd_logp, " flags=0x%x", flags);
5177 lock_ObtainWrite(&cm_scacheLock);
5179 for (q = scp->fileLocksH; q; q = qn) {
5182 fileLock = (cm_file_lock_t *)
5183 ((char *) q - offsetof(cm_file_lock_t, fileq));
5186 osi_Log4(afsd_logp, " Checking lock[0x%x] range[%d,+%d] type[%d]",
5188 (unsigned long) fileLock->range.offset,
5189 (unsigned long) fileLock->range.length,
5190 fileLock->lockType);
5191 osi_Log4(afsd_logp, " key<0x%x, 0x%x, 0x%x> flags[0x%x]",
5192 fileLock->key.process_id, fileLock->key.session_id, fileLock->key.file_id,
5195 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5196 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5197 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5199 fileLock->fid.volume,
5200 fileLock->fid.vnode,
5201 fileLock->fid.unique);
5202 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5203 fileLock->scp->fid.cell,
5204 fileLock->scp->fid.volume,
5205 fileLock->scp->fid.vnode,
5206 fileLock->scp->fid.unique);
5207 osi_assertx(FALSE, "invalid fid value");
5211 if (!IS_LOCK_DELETED(fileLock) &&
5212 cm_KeyEquals(&fileLock->key, &key, flags)) {
5213 osi_Log3(afsd_logp, "...Unlock range [%d,+%d] type %d",
5214 fileLock->range.offset,
5215 fileLock->range.length,
5216 fileLock->lockType);
5218 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
5220 if (IS_LOCK_CLIENTONLY(fileLock)) {
5222 } else if (IS_LOCK_ACCEPTED(fileLock)) {
5223 if (fileLock->lockType == LockRead)
5226 scp->exclusiveLocks--;
5229 fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5231 cm_ReleaseUser(fileLock->userp);
5232 cm_ReleaseSCacheNoLock(scp);
5234 fileLock->userp = NULL;
5235 fileLock->scp = NULL;
5241 lock_ReleaseWrite(&cm_scacheLock);
5243 if (n_unlocks == 0) {
5244 osi_Log0(afsd_logp, "cm_UnlockByKey no locks found");
5245 osi_Log3(afsd_logp, " Leaving scp with exclusives[%d], shared[%d], serverLock[%d]",
5246 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
5251 code = cm_IntUnlock(scp, userp, reqp);
5252 osi_Log1(afsd_logp, "cm_UnlockByKey code 0x%x", code);
5254 osi_Log4(afsd_logp, " Leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
5255 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
5256 (int)(signed char) scp->serverLock);
5261 /* Called with scp->rw held */
5262 long cm_Unlock(cm_scache_t *scp,
5263 unsigned char sLockType,
5264 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
5271 int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
5272 cm_file_lock_t *fileLock;
5274 int release_userp = FALSE;
5275 int exact_match = !(flags & CM_UNLOCK_FLAG_MATCH_RANGE);
5277 LARGE_INTEGER RangeEnd;
5279 osi_Log4(afsd_logp, "cm_Unlock scp 0x%p type 0x%x offset 0x%x length 0x%x",
5280 scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
5281 osi_Log4(afsd_logp, "... key <0x%x,0x%x,0x%x> flags 0x%x",
5282 key.process_id, key.session_id, key.file_id, flags);
5285 RangeEnd.QuadPart = LOffset.QuadPart + LLength.QuadPart;
5288 lock_ObtainRead(&cm_scacheLock);
5290 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5291 fileLock = (cm_file_lock_t *)
5292 ((char *) q - offsetof(cm_file_lock_t, fileq));
5295 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5296 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5297 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5299 fileLock->fid.volume,
5300 fileLock->fid.vnode,
5301 fileLock->fid.unique);
5302 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5303 fileLock->scp->fid.cell,
5304 fileLock->scp->fid.volume,
5305 fileLock->scp->fid.vnode,
5306 fileLock->scp->fid.unique);
5307 osi_assertx(FALSE, "invalid fid value");
5311 if (!IS_LOCK_DELETED(fileLock) &&
5312 cm_KeyEquals(&fileLock->key, &key, 0) &&
5313 fileLock->range.offset == LOffset.QuadPart &&
5314 fileLock->range.length == LLength.QuadPart) {
5320 if (!IS_LOCK_DELETED(fileLock) &&
5321 cm_KeyEquals(&fileLock->key, &key, 0) &&
5322 fileLock->range.offset >= LOffset.QuadPart &&
5323 fileLock->range.offset < RangeEnd.QuadPart &&
5324 (fileLock->range.offset + fileLock->range.length) <= RangeEnd.QuadPart) {
5332 lock_ReleaseRead(&cm_scacheLock);
5334 if (lock_found && !exact_match) {
5338 osi_Log0(afsd_logp, "cm_Unlock lock not found; failure");
5340 /* The lock didn't exist anyway. *shrug* */
5341 return CM_ERROR_RANGE_NOT_LOCKED;
5345 /* discard lock record */
5346 lock_ConvertRToW(&cm_scacheLock);
5347 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
5350 * Don't delete it here; let the daemon delete it, to simplify
5351 * the daemon's traversal of the list.
5354 if (IS_LOCK_CLIENTONLY(fileLock)) {
5356 } else if (IS_LOCK_ACCEPTED(fileLock)) {
5357 if (fileLock->lockType == LockRead)
5360 scp->exclusiveLocks--;
5363 fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5365 if (userp != NULL) {
5366 cm_ReleaseUser(fileLock->userp);
5368 userp = fileLock->userp;
5369 release_userp = TRUE;
5371 cm_ReleaseSCacheNoLock(scp);
5372 fileLock->userp = NULL;
5373 fileLock->scp = NULL;
5374 lock_ReleaseWrite(&cm_scacheLock);
5376 code = cm_IntUnlock(scp, userp, reqp);
5378 if (release_userp) {
5379 cm_ReleaseUser(userp);
5380 release_userp = FALSE;
5384 osi_Log1(afsd_logp, "cm_Unlock not exact match, searching for next lock, code 0x%x", code);
5385 goto try_again; /* might be more than one lock in the range */
5390 osi_Log1(afsd_logp, "cm_Unlock code 0x%x", code);
5391 osi_Log4(afsd_logp, " leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
5392 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
5393 (int)(signed char) scp->serverLock);
5398 /* called with scp->rw held */
5399 void cm_LockMarkSCacheLost(cm_scache_t * scp)
5401 cm_file_lock_t *fileLock;
5404 osi_Log1(afsd_logp, "cm_LockMarkSCacheLost scp 0x%x", scp);
5406 /* cm_scacheLock needed because we are modifying fileLock->flags */
5407 lock_ObtainWrite(&cm_scacheLock);
5409 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5411 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
5413 if (IS_LOCK_ACTIVE(fileLock) &&
5414 !IS_LOCK_CLIENTONLY(fileLock)) {
5415 if (fileLock->lockType == LockRead)
5418 scp->exclusiveLocks--;
5420 fileLock->flags |= CM_FILELOCK_FLAG_LOST;
5424 scp->serverLock = -1;
5425 scp->lockDataVersion = CM_SCACHE_VERSION_BAD;
5426 lock_ReleaseWrite(&cm_scacheLock);
5429 /* Called with no relevant locks held */
5430 void cm_CheckLocks()
5432 osi_queue_t *q, *nq;
5433 cm_file_lock_t *fileLock;
5439 struct rx_connection * rxconnp;
5442 memset(&volSync, 0, sizeof(volSync));
5446 lock_ObtainWrite(&cm_scacheLock);
5448 cm_lockRefreshCycle++;
5450 osi_Log1(afsd_logp, "cm_CheckLocks starting lock check cycle %d", cm_lockRefreshCycle);
5452 for (q = cm_allFileLocks; q; q = nq) {
5453 fileLock = (cm_file_lock_t *) q;
5457 if (IS_LOCK_DELETED(fileLock)) {
5458 cm_user_t *userp = fileLock->userp;
5459 cm_scache_t *scp = fileLock->scp;
5460 fileLock->userp = NULL;
5461 fileLock->scp = NULL;
5464 lock_ReleaseWrite(&cm_scacheLock);
5465 lock_ObtainWrite(&scp->rw);
5466 code = cm_IntUnlock(scp, userp, &req);
5467 lock_ReleaseWrite(&scp->rw);
5469 cm_ReleaseUser(userp);
5470 lock_ObtainWrite(&cm_scacheLock);
5471 cm_ReleaseSCacheNoLock(scp);
5473 osi_QRemove(&cm_allFileLocks, q);
5474 cm_PutFileLock(fileLock);
5476 } else if (IS_LOCK_ACTIVE(fileLock) && !IS_LOCK_CLIENTONLY(fileLock)) {
5478 /* Server locks must have been enabled for us to have
5479 received an active non-client-only lock. */
5480 osi_assertx(cm_enableServerLocks, "!cm_enableServerLocks");
5482 scp = fileLock->scp;
5483 osi_assertx(scp != NULL, "null cm_scache_t");
5485 cm_HoldSCacheNoLock(scp);
5488 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5489 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5490 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5492 fileLock->fid.volume,
5493 fileLock->fid.vnode,
5494 fileLock->fid.unique);
5495 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5496 fileLock->scp->fid.cell,
5497 fileLock->scp->fid.volume,
5498 fileLock->scp->fid.vnode,
5499 fileLock->scp->fid.unique);
5500 osi_assertx(FALSE, "invalid fid");
5503 /* Server locks are extended once per scp per refresh
5505 if (scp->lastRefreshCycle != cm_lockRefreshCycle) {
5507 int scp_done = FALSE;
5509 osi_Log1(afsd_logp, "cm_CheckLocks Updating scp 0x%x", scp);
5511 lock_ReleaseWrite(&cm_scacheLock);
5512 lock_ObtainWrite(&scp->rw);
5514 /* did the lock change while we weren't holding the lock? */
5515 if (!IS_LOCK_ACTIVE(fileLock))
5516 goto post_syncopdone;
5518 code = cm_SyncOp(scp, NULL, fileLock->userp, &req, 0,
5519 CM_SCACHESYNC_NEEDCALLBACK
5520 | CM_SCACHESYNC_GETSTATUS
5521 | CM_SCACHESYNC_LOCK);
5525 "cm_CheckLocks SyncOp failure code 0x%x", code);
5526 goto post_syncopdone;
5529 /* cm_SyncOp releases scp->rw during which the lock
5530 may get released. */
5531 if (!IS_LOCK_ACTIVE(fileLock))
5532 goto pre_syncopdone;
5534 if (scp->serverLock != -1 && !(scp->flags & CM_SCACHEFLAG_DELETED)) {
5538 tfid.Volume = scp->fid.volume;
5539 tfid.Vnode = scp->fid.vnode;
5540 tfid.Unique = scp->fid.unique;
5542 userp = fileLock->userp;
5544 osi_Log3(afsd_logp, "CALL ExtendLock lock 0x%p for scp=0x%p with lock %d",
5547 (int) scp->serverLock);
5549 lock_ReleaseWrite(&scp->rw);
5552 code = cm_ConnFromFID(&cfid, userp,
5557 rxconnp = cm_GetRxConn(connp);
5558 code = RXAFS_ExtendLock(rxconnp, &tfid,
5560 rx_PutConnection(rxconnp);
5562 osi_Log1(afsd_logp, " ExtendLock returns %d", code);
5564 } while (cm_Analyze(connp, userp, &req,
5565 &cfid, &volSync, NULL, NULL,
5568 code = cm_MapRPCError(code, &req);
5570 lock_ObtainWrite(&scp->rw);
5573 osi_Log1(afsd_logp, "CALL ExtendLock FAILURE, code 0x%x", code);
5574 scp->fsLockCount = 0;
5576 osi_Log0(afsd_logp, "CALL ExtendLock SUCCESS");
5577 scp->lockDataVersion = scp->dataVersion;
5580 if ((code == EINVAL || code == CM_ERROR_INVAL) &&
5581 scp->lockDataVersion == scp->dataVersion) {
5585 (scp->exclusiveLocks > 0) ? LockWrite: LockRead;
5587 /* we might still have a chance to obtain a
5590 code = cm_IntSetLock(scp, userp, lockType, &req);
5593 code = CM_ERROR_INVAL;
5594 } else if (scp->lockDataVersion != scp->dataVersion) {
5596 /* now check if we still have the file at
5597 the right data version. */
5599 "Data version mismatch on scp 0x%p",
5602 " Data versions: before=%I64d, after=%I64d",
5603 scp->lockDataVersion,
5606 code = cm_IntReleaseLock(scp, userp, &req);
5608 code = CM_ERROR_INVAL;
5612 if (code == EINVAL || code == CM_ERROR_INVAL ||
5613 code == CM_ERROR_BADFD) {
5614 cm_LockMarkSCacheLost(scp);
5618 /* interestingly, we have found an active lock
5619 belonging to an scache that has no
5621 cm_LockMarkSCacheLost(scp);
5628 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5631 lock_ReleaseWrite(&scp->rw);
5633 lock_ObtainWrite(&cm_scacheLock);
5636 fileLock->lastUpdate = time(NULL);
5640 scp->lastRefreshCycle = cm_lockRefreshCycle;
5643 /* we have already refreshed the locks on this scp */
5644 fileLock->lastUpdate = time(NULL);
5647 cm_ReleaseSCacheNoLock(scp);
5649 } else if (IS_LOCK_ACTIVE(fileLock) && IS_LOCK_CLIENTONLY(fileLock)) {
5650 /* TODO: Check callbacks */
5654 lock_ReleaseWrite(&cm_scacheLock);
5655 osi_Log1(afsd_logp, "cm_CheckLocks completes lock check cycle %d", cm_lockRefreshCycle);
5658 /* NOT called with scp->rw held. */
5659 long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
5662 cm_scache_t *scp = NULL;
5663 cm_file_lock_t *fileLock;
5667 int force_client_lock = FALSE;
5668 int has_insert = FALSE;
5669 int check_data_version = FALSE;
5673 if (client_is_dead) {
5674 code = CM_ERROR_TIMEDOUT;
5678 lock_ObtainRead(&cm_scacheLock);
5680 osi_Log2(afsd_logp, "cm_RetryLock checking lock %p (scp=%p)", oldFileLock, oldFileLock->scp);
5681 osi_Log4(afsd_logp, " offset(%x:%x) length(%x:%x)",
5682 (unsigned)(oldFileLock->range.offset >> 32),
5683 (unsigned)(oldFileLock->range.offset & 0xffffffff),
5684 (unsigned)(oldFileLock->range.length >> 32),
5685 (unsigned)(oldFileLock->range.length & 0xffffffff));
5686 osi_Log4(afsd_logp, " key<0x%x,0x%x,0x%x> flags=%x",
5687 oldFileLock->key.process_id, oldFileLock->key.session_id, oldFileLock->key.file_id,
5688 (unsigned)(oldFileLock->flags));
5690 /* if the lock has already been granted, then we have nothing to do */
5691 if (IS_LOCK_ACTIVE(oldFileLock)) {
5692 lock_ReleaseRead(&cm_scacheLock);
5693 osi_Log0(afsd_logp, "cm_RetryLock lock already granted");
5697 /* we can't do anything with lost or deleted locks at the moment. */
5698 if (IS_LOCK_LOST(oldFileLock) || IS_LOCK_DELETED(oldFileLock)) {
5699 code = CM_ERROR_BADFD;
5700 osi_Log0(afsd_logp, "cm_RetryLock lock is lost or deleted");
5701 lock_ReleaseRead(&cm_scacheLock);
5705 scp = oldFileLock->scp;
5707 osi_assertx(scp != NULL, "null cm_scache_t");
5709 lock_ReleaseRead(&cm_scacheLock);
5710 lock_ObtainWrite(&scp->rw);
5712 code = cm_LockCheckPerms(scp, oldFileLock->lockType,
5716 if (code == CM_ERROR_NOACCESS && oldFileLock->lockType == LockRead) {
5717 if (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp)) {
5718 force_client_lock = TRUE;
5722 lock_ReleaseWrite(&scp->rw);
5726 lock_ObtainWrite(&cm_scacheLock);
5728 /* Check if we already have a sufficient server lock to allow this
5729 lock to go through. */
5730 if (IS_LOCK_WAITLOCK(oldFileLock) &&
5731 (!SERVERLOCKS_ENABLED(scp) ||
5732 scp->serverLock == oldFileLock->lockType ||
5733 scp->serverLock == LockWrite)) {
5735 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5737 if (SERVERLOCKS_ENABLED(scp)) {
5738 osi_Log1(afsd_logp, "cm_RetryLock Server lock (%d) is sufficient for lock. Granting",
5739 (int) scp->serverLock);
5741 osi_Log0(afsd_logp, "cm_RetryLock skipping server lock for scp");
5744 lock_ReleaseWrite(&cm_scacheLock);
5745 lock_ReleaseWrite(&scp->rw);
5750 if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5752 /* check if the conflicting locks have dissappeared already */
5753 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5755 fileLock = (cm_file_lock_t *)
5756 ((char *) q - offsetof(cm_file_lock_t, fileq));
5758 if (IS_LOCK_LOST(fileLock)) {
5759 if (cm_KeyEquals(&fileLock->key, &oldFileLock->key, 0)) {
5760 code = CM_ERROR_BADFD;
5761 oldFileLock->flags |= CM_FILELOCK_FLAG_LOST;
5762 osi_Log1(afsd_logp, " found lost lock %p for same key. Marking lock as lost",
5765 } else if (fileLock->lockType == LockWrite &&
5766 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5767 osi_Log1(afsd_logp, " found conflicting LOST lock %p", fileLock);
5768 code = CM_ERROR_WOULDBLOCK;
5773 if (IS_LOCK_ACCEPTED(fileLock) &&
5774 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5776 if (oldFileLock->lockType != LockRead ||
5777 fileLock->lockType != LockRead) {
5779 osi_Log1(afsd_logp, " found conflicting lock %p", fileLock);
5780 code = CM_ERROR_WOULDBLOCK;
5788 lock_ReleaseWrite(&cm_scacheLock);
5789 lock_ReleaseWrite(&scp->rw);
5794 /* when we get here, the lock is either a WAITUNLOCK or WAITLOCK.
5795 If it is WAITUNLOCK, then we didn't find any conflicting lock
5796 but we haven't verfied whether the serverLock is sufficient to
5797 assert it. If it is WAITLOCK, then the serverLock is
5798 insufficient to assert it. Eitherway, we are ready to accept
5799 the lock as either ACTIVE or WAITLOCK depending on the
5802 /* First, promote the WAITUNLOCK to a WAITLOCK */
5803 if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5804 if (oldFileLock->lockType == LockRead)
5807 scp->exclusiveLocks++;
5809 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITUNLOCK;
5810 oldFileLock->flags |= CM_FILELOCK_FLAG_WAITLOCK;
5813 osi_assertx(IS_LOCK_WAITLOCK(oldFileLock), "!IS_LOCK_WAITLOCK");
5815 if (force_client_lock ||
5816 !SERVERLOCKS_ENABLED(scp) ||
5817 scp->serverLock == oldFileLock->lockType ||
5818 (oldFileLock->lockType == LockRead &&
5819 scp->serverLock == LockWrite)) {
5821 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5823 if ((force_client_lock ||
5824 !SERVERLOCKS_ENABLED(scp)) &&
5825 !IS_LOCK_CLIENTONLY(oldFileLock)) {
5827 oldFileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
5829 if (oldFileLock->lockType == LockRead)
5832 scp->exclusiveLocks--;
5837 lock_ReleaseWrite(&cm_scacheLock);
5838 lock_ReleaseWrite(&scp->rw);
5845 code = cm_SyncOp(scp, NULL, oldFileLock->userp, &req, 0,
5846 CM_SCACHESYNC_NEEDCALLBACK
5847 | CM_SCACHESYNC_GETSTATUS
5848 | CM_SCACHESYNC_LOCK);
5850 osi_Log1(afsd_logp, "cm_RetryLock SyncOp failure code 0x%x", code);
5851 lock_ReleaseWrite(&cm_scacheLock);
5852 goto post_syncopdone;
5855 if (!IS_LOCK_WAITLOCK(oldFileLock))
5856 goto pre_syncopdone;
5858 userp = oldFileLock->userp;
5860 #ifndef AGGRESSIVE_LOCKS
5861 newLock = oldFileLock->lockType;
5863 newLock = LockWrite;
5867 /* if has_insert is non-zero, then:
5868 - the lock a LockRead
5869 - we don't have permission to get a LockRead
5870 - we do have permission to get a LockWrite
5871 - the server supports VICED_CAPABILITY_WRITELOCKACL
5874 newLock = LockWrite;
5877 lock_ReleaseWrite(&cm_scacheLock);
5879 /* when we get here, either we have a read-lock and want a
5880 write-lock or we don't have any locks and we want some
5883 if (scp->serverLock == LockRead) {
5885 osi_assertx(newLock == LockWrite, "!LockWrite");
5887 osi_Log0(afsd_logp, " Attempting to UPGRADE from LockRead to LockWrite");
5889 scp->lockDataVersion = scp->dataVersion;
5890 check_data_version = TRUE;
5892 code = cm_IntReleaseLock(scp, userp, &req);
5895 goto pre_syncopdone;
5897 scp->serverLock = -1;
5900 code = cm_IntSetLock(scp, userp, newLock, &req);
5903 if (scp->dataVersion != scp->lockDataVersion) {
5904 /* we lost a race. too bad */
5907 " Data version mismatch while upgrading lock.");
5909 " Data versions before=%I64d, after=%I64d",
5910 scp->lockDataVersion,
5913 " Releasing stale lock for scp 0x%x", scp);
5915 code = cm_IntReleaseLock(scp, userp, &req);
5917 scp->serverLock = -1;
5919 code = CM_ERROR_INVAL;
5921 cm_LockMarkSCacheLost(scp);
5923 scp->serverLock = newLock;
5928 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5934 if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
5935 lock_ObtainWrite(&cm_scacheLock);
5936 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, &oldFileLock->fileq);
5937 lock_ReleaseWrite(&cm_scacheLock);
5939 lock_ReleaseWrite(&scp->rw);
5942 lock_ObtainWrite(&cm_scacheLock);
5944 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5945 } else if (code != CM_ERROR_WOULDBLOCK) {
5946 oldFileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5947 cm_ReleaseUser(oldFileLock->userp);
5948 oldFileLock->userp = NULL;
5949 if (oldFileLock->scp) {
5950 cm_ReleaseSCacheNoLock(oldFileLock->scp);
5951 oldFileLock->scp = NULL;
5954 lock_ReleaseWrite(&cm_scacheLock);
5959 cm_key_t cm_GenerateKey(afs_uint16 session_id, afs_offs_t process_id, afs_uint64 file_id)
5963 key.process_id = process_id;
5964 key.session_id = session_id;
5965 key.file_id = file_id;
5970 int cm_KeyEquals(cm_key_t *k1, cm_key_t *k2, int flags)
5972 return (k1->session_id == k2->session_id) && (k1->file_id == k2->file_id) &&
5973 ((flags & CM_UNLOCK_FLAG_BY_FID) || (k1->process_id == k2->process_id));
5976 void cm_ReleaseAllLocks(void)
5982 cm_file_lock_t *fileLock;
5985 for (i = 0; i < cm_data.scacheHashTableSize; i++)
5987 for ( scp = cm_data.scacheHashTablep[i]; scp; scp = scp->nextp ) {
5988 while (scp->fileLocksH != NULL) {
5989 lock_ObtainWrite(&scp->rw);
5990 lock_ObtainWrite(&cm_scacheLock);
5991 if (!scp->fileLocksH) {
5992 lock_ReleaseWrite(&cm_scacheLock);
5993 lock_ReleaseWrite(&scp->rw);
5996 fileLock = (cm_file_lock_t *)((char *) scp->fileLocksH - offsetof(cm_file_lock_t, fileq));
5997 userp = fileLock->userp;
5999 key = fileLock->key;
6000 cm_HoldSCacheNoLock(scp);
6001 lock_ReleaseWrite(&cm_scacheLock);
6002 cm_UnlockByKey(scp, key, 0, userp, &req);
6003 cm_ReleaseSCache(scp);
6004 cm_ReleaseUser(userp);
6005 lock_ReleaseWrite(&scp->rw);