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>
15 #include <afs/unified_afs.h>
34 extern void afsi_log(char *pattern, ...);
37 int cm_enableServerLocks = 1;
39 int cm_followBackupPath = 0;
42 * Case-folding array. This was constructed by inspecting of SMBtrace output.
43 * I do not know anything more about it.
45 unsigned char cm_foldUpper[256] = {
46 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
47 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
48 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
49 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
50 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
51 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
52 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
53 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
54 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
55 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
56 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
57 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
58 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
59 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
60 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
61 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
62 0x80, 0x9a, 0x90, 0x41, 0x8e, 0x41, 0x8f, 0x80,
63 0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x8e, 0x8f,
64 0x90, 0x92, 0x92, 0x4f, 0x99, 0x4f, 0x55, 0x55,
65 0x59, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
66 0x41, 0x49, 0x4f, 0x55, 0xa5, 0xa5, 0x56, 0xa7,
67 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
68 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
69 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
70 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
71 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
72 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
73 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
74 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
75 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
76 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
77 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
81 * Case-insensitive string comparison. We used to use stricmp, but it doesn't
82 * know about 8-bit characters (e.g. 129 is lowercase u-umlaut, 154 is
83 * upper-case u-umlaut).
85 int cm_stricmp(const char *str1, const char *str2)
97 c1 = (char) cm_foldUpper[(unsigned char)(*str1++)];
98 c2 = (char) cm_foldUpper[(unsigned char)(*str2++)];
108 /* return success if we can open this file in this mode */
109 long cm_CheckOpen(cm_scache_t *scp, int openMode, int trunc, cm_user_t *userp,
117 rights |= PRSFS_READ;
118 if (openMode == 1 || openMode == 2 || trunc)
119 rights |= PRSFS_WRITE;
121 lock_ObtainWrite(&scp->rw);
123 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
124 CM_SCACHESYNC_GETSTATUS
125 | CM_SCACHESYNC_NEEDCALLBACK
126 | CM_SCACHESYNC_LOCK);
129 ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
130 scp->fileType == CM_SCACHETYPE_FILE) {
133 unsigned int sLockType;
134 LARGE_INTEGER LOffset, LLength;
136 /* Check if there's some sort of lock on the file at the
139 key = cm_GenerateKey(CM_SESSION_CMINT,0,0);
141 if (rights & PRSFS_WRITE)
144 sLockType = LOCKING_ANDX_SHARED_LOCK;
146 LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
147 LOffset.LowPart = CM_FLSHARE_OFFSET_LOW;
148 LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
149 LLength.LowPart = CM_FLSHARE_LENGTH_LOW;
151 code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
154 cm_Unlock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp);
156 /* In this case, we allow the file open to go through even
157 though we can't enforce mandatory locking on the
159 if (code == CM_ERROR_NOACCESS &&
160 !(rights & PRSFS_WRITE))
163 if (code == CM_ERROR_LOCK_NOT_GRANTED)
164 code = CM_ERROR_SHARING_VIOLATION;
168 } else if (code != 0) {
172 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
176 lock_ReleaseWrite(&scp->rw);
181 /* return success if we can open this file in this mode */
182 long cm_CheckNTOpen(cm_scache_t *scp,
183 unsigned int desiredAccess,
184 unsigned int shareAccess,
185 unsigned int createDisp,
186 afs_offs_t process_id,
187 afs_offs_t handle_id,
188 cm_user_t *userp, cm_req_t *reqp,
189 cm_lock_data_t **ldpp)
193 afs_uint16 session_id;
195 osi_assertx(ldpp != NULL, "null cm_lock_data_t");
198 /* compute the session id */
199 if (reqp->flags & CM_REQ_SOURCE_SMB)
200 session_id = CM_SESSION_SMB;
201 else if (reqp->flags & CM_REQ_SOURCE_REDIR)
202 session_id = CM_SESSION_IFS;
204 session_id = CM_SESSION_CMINT;
206 /* Ignore the SYNCHRONIZE privilege */
207 desiredAccess &= ~SYNCHRONIZE;
209 /* Always allow delete; the RPC will tell us if it's OK */
212 if (desiredAccess == DELETE)
215 /* Always allow reading attributes (Hidden, System, Readonly, ...) */
216 if (desiredAccess == FILE_READ_ATTRIBUTES)
219 if (desiredAccess & (AFS_ACCESS_READ|AFS_ACCESS_EXECUTE))
220 rights |= (scp->fileType == CM_SCACHETYPE_DIRECTORY ? PRSFS_LOOKUP : PRSFS_READ);
222 /* We used to require PRSFS_WRITE if createDisp was 4
223 (OPEN_ALWAYS) even if AFS_ACCESS_WRITE was not requested.
224 However, we don't need to do that since the existence of the
225 scp implies that we don't need to create it. */
226 if (desiredAccess & AFS_ACCESS_WRITE)
227 rights |= PRSFS_WRITE;
229 if (desiredAccess & DELETE)
230 rights |= PRSFS_DELETE;
232 lock_ObtainWrite(&scp->rw);
234 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
235 CM_SCACHESYNC_GETSTATUS
236 | CM_SCACHESYNC_NEEDCALLBACK
237 | CM_SCACHESYNC_LOCK);
240 * If the open will fail because the volume is readonly, then we will
241 * return an access denied error instead. This is to help brain-dead
242 * apps run correctly on replicated volumes.
243 * See defect 10007 for more information.
245 if (code == CM_ERROR_READONLY)
246 code = CM_ERROR_NOACCESS;
249 !(shareAccess & FILE_SHARE_WRITE) &&
250 ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
251 scp->fileType == CM_SCACHETYPE_FILE) {
253 unsigned int sLockType;
254 LARGE_INTEGER LOffset, LLength;
256 /* Check if there's some sort of lock on the file at the
259 if (rights & PRSFS_WRITE)
262 sLockType = LOCKING_ANDX_SHARED_LOCK;
264 key = cm_GenerateKey(session_id, process_id, 0);
266 /* single byte lock at offset 0x0100 0000 0000 0000 */
267 LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
268 LOffset.LowPart = CM_FLSHARE_OFFSET_LOW;
269 LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
270 LLength.LowPart = CM_FLSHARE_LENGTH_LOW;
272 code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
275 (*ldpp) = (cm_lock_data_t *)malloc(sizeof(cm_lock_data_t));
282 (*ldpp)->sLockType = sLockType;
283 (*ldpp)->LOffset.HighPart = LOffset.HighPart;
284 (*ldpp)->LOffset.LowPart = LOffset.LowPart;
285 (*ldpp)->LLength.HighPart = LLength.HighPart;
286 (*ldpp)->LLength.LowPart = LLength.LowPart;
289 * In this case, we allow the file open to go through even
290 * though we can't enforce mandatory locking on the
292 if (code == CM_ERROR_NOACCESS &&
293 !(rights & PRSFS_WRITE))
296 if (code == CM_ERROR_LOCK_NOT_GRANTED)
297 code = CM_ERROR_SHARING_VIOLATION;
300 } else if (code != 0) {
305 lock_ReleaseWrite(&scp->rw);
308 osi_Log3(afsd_logp,"cm_CheckNTOpen scp 0x%p ldp 0x%p code 0x%x", scp, *ldpp, code);
312 extern long cm_CheckNTOpenDone(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp,
313 cm_lock_data_t ** ldpp)
315 osi_Log2(afsd_logp,"cm_CheckNTOpenDone scp 0x%p ldp 0x%p", scp, ldpp ? *ldpp : 0);
316 lock_ObtainWrite(&scp->rw);
318 cm_Unlock(scp, (*ldpp)->sLockType, (*ldpp)->LOffset, (*ldpp)->LLength,
319 (*ldpp)->key, 0, userp, reqp);
323 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
324 lock_ReleaseWrite(&scp->rw);
328 * When CAP_NT_SMBS has been negotiated, deletion (of files or directories) is
329 * done in three steps:
330 * (1) open for deletion (NT_CREATE_AND_X)
331 * (2) set for deletion on close (NT_TRANSACTION2, SET_FILE_INFO)
333 * We must not do the RPC until step 3. But if we are going to return an error
334 * code (e.g. directory not empty), we must return it by step 2, otherwise most
335 * clients will not notice it. So we do a preliminary check. For deleting
336 * files, this is almost free, since we have already done the RPC to get the
337 * parent directory's status bits. But for deleting directories, we must do an
338 * additional RPC to get the directory's data to check if it is empty. Sigh.
340 long cm_CheckNTDelete(cm_scache_t *dscp, cm_scache_t *scp, cm_user_t *userp,
346 cm_dirEntry_t *dep = 0;
347 unsigned short *hashTable;
349 int BeyondPage = 0, HaveDot = 0, HaveDotDot = 0;
352 /* First check permissions */
353 lock_ObtainWrite(&scp->rw);
354 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_DELETE,
355 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
357 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
358 lock_ReleaseWrite(&scp->rw);
362 /* If deleting directory, must be empty */
364 if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
367 thyper.HighPart = 0; thyper.LowPart = 0;
368 code = buf_Get(scp, &thyper, reqp, &bufferp);
372 lock_ObtainMutex(&bufferp->mx);
373 lock_ObtainWrite(&scp->rw);
376 code = cm_SyncOp(scp, bufferp, userp, reqp, 0,
377 CM_SCACHESYNC_NEEDCALLBACK
379 | CM_SCACHESYNC_BUFLOCKED);
383 if (cm_HaveBuffer(scp, bufferp, 1))
386 /* otherwise, load the buffer and try again */
387 lock_ReleaseMutex(&bufferp->mx);
388 code = cm_GetBuffer(scp, bufferp, NULL, userp, reqp);
389 lock_ReleaseWrite(&scp->rw);
390 lock_ObtainMutex(&bufferp->mx);
391 lock_ObtainWrite(&scp->rw);
392 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
397 lock_ReleaseWrite(&scp->rw);
400 /* We try to determine emptiness without looking beyond the first page,
401 * and without assuming "." and ".." are present and are on the first
402 * page (though these assumptions might, after all, be reasonable).
404 hashTable = (unsigned short *)(bufferp->datap + (32 * 5));
405 for (i=0; i<128; i++) {
406 idx = ntohs(hashTable[i]);
412 dep = (cm_dirEntry_t *)(bufferp->datap + (32 * idx));
413 if (strcmp(dep->name, ".") == 0)
415 else if (strcmp(dep->name, "..") == 0)
418 code = CM_ERROR_NOTEMPTY;
421 idx = ntohs(dep->next);
424 if (BeyondPage && HaveDot && HaveDotDot)
425 code = CM_ERROR_NOTEMPTY;
429 lock_ReleaseMutex(&bufferp->mx);
430 buf_Release(bufferp);
432 lock_ReleaseWrite(&scp->rw);
437 * Iterate through all entries in a directory.
438 * When the function funcp is called, the buffer is locked but the
439 * directory vnode is not.
441 * If the retscp parameter is not NULL, the parmp must be a
442 * cm_lookupSearch_t object.
444 long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
445 osi_hyper_t *startOffsetp, cm_user_t *userp, cm_req_t *reqp,
446 cm_scache_t **retscp)
450 cm_dirEntry_t *dep = 0;
453 osi_hyper_t dirLength;
454 osi_hyper_t bufferOffset;
455 osi_hyper_t curOffset;
459 cm_pageHeader_t *pageHeaderp;
461 long nextEntryCookie;
462 int numDirChunks; /* # of 32 byte dir chunks in this entry */
464 /* get the directory size */
465 lock_ObtainWrite(&scp->rw);
466 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_LOOKUP,
467 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
468 lock_ReleaseWrite(&scp->rw);
472 if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
473 return CM_ERROR_NOTDIR;
475 if (retscp) /* if this is a lookup call */
477 cm_lookupSearch_t* sp = parmp;
480 #ifdef AFS_FREELANCE_CLIENT
481 /* Freelance entries never end up in the DNLC because they
482 * do not have an associated cm_server_t
484 !(cm_freelanceEnabled &&
485 sp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
486 sp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
487 #else /* !AFS_FREELANCE_CLIENT */
492 int casefold = sp->caseFold;
493 sp->caseFold = 0; /* we have a strong preference for exact matches */
494 if ( *retscp = cm_dnlcLookup(scp, sp)) /* dnlc hit */
496 sp->caseFold = casefold;
499 sp->caseFold = casefold;
503 * see if we can find it using the directory hash tables.
504 * we can only do exact matches, since the hash is case
507 if (funcp != (cm_DirFuncp_t)cm_BPlusDirFoo)
516 code = cm_BeginDirOp(scp, userp, reqp, CM_DIRLOCK_READ,
517 CM_DIROP_FLAG_NONE, &dirop);
521 code = cm_BPlusDirLookup(&dirop, sp->nsearchNamep, &sp->fid);
526 code = cm_DirLookup(&dirop, sp->searchNamep, &sp->fid);
534 sp->ExactFound = TRUE;
535 *retscp = NULL; /* force caller to call cm_GetSCache() */
540 if (sp->caseFold && code == CM_ERROR_INEXACT_MATCH) {
543 sp->ExactFound = FALSE;
544 *retscp = NULL; /* force caller to call cm_GetSCache() */
548 return CM_ERROR_BPLUS_NOMATCH;
555 * XXX We only get the length once. It might change when we drop the
558 dirLength = scp->length;
561 bufferOffset.LowPart = bufferOffset.HighPart = 0;
563 curOffset = *startOffsetp;
565 curOffset.HighPart = 0;
566 curOffset.LowPart = 0;
570 /* make sure that curOffset.LowPart doesn't point to the first
571 * 32 bytes in the 2nd through last dir page, and that it
572 * doesn't point at the first 13 32-byte chunks in the first
573 * dir page, since those are dir and page headers, and don't
574 * contain useful information.
576 temp = curOffset.LowPart & (2048-1);
577 if (curOffset.HighPart == 0 && curOffset.LowPart < 2048) {
578 /* we're in the first page */
579 if (temp < 13*32) temp = 13*32;
582 /* we're in a later dir page */
583 if (temp < 32) temp = 32;
586 /* make sure the low order 5 bits are zero */
589 /* now put temp bits back ito curOffset.LowPart */
590 curOffset.LowPart &= ~(2048-1);
591 curOffset.LowPart |= temp;
593 /* check if we've passed the dir's EOF */
594 if (LargeIntegerGreaterThanOrEqualTo(curOffset, dirLength))
597 /* see if we can use the bufferp we have now; compute in which
598 * page the current offset would be, and check whether that's
599 * the offset of the buffer we have. If not, get the buffer.
601 thyper.HighPart = curOffset.HighPart;
602 thyper.LowPart = curOffset.LowPart & ~(cm_data.buf_blockSize-1);
603 if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
606 lock_ReleaseMutex(&bufferp->mx);
607 buf_Release(bufferp);
611 code = buf_Get(scp, &thyper, reqp, &bufferp);
613 /* if buf_Get() fails we do not have a buffer object to lock */
618 lock_ObtainMutex(&bufferp->mx);
619 bufferOffset = thyper;
621 /* now get the data in the cache */
623 lock_ObtainWrite(&scp->rw);
624 code = cm_SyncOp(scp, bufferp, userp, reqp,
626 CM_SCACHESYNC_NEEDCALLBACK
628 | CM_SCACHESYNC_BUFLOCKED);
630 lock_ReleaseWrite(&scp->rw);
633 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
635 if (cm_HaveBuffer(scp, bufferp, 1)) {
636 lock_ReleaseWrite(&scp->rw);
640 /* otherwise, load the buffer and try again */
641 lock_ReleaseMutex(&bufferp->mx);
642 code = cm_GetBuffer(scp, bufferp, NULL, userp,
644 lock_ReleaseWrite(&scp->rw);
645 lock_ObtainMutex(&bufferp->mx);
650 lock_ReleaseMutex(&bufferp->mx);
651 buf_Release(bufferp);
655 } /* if (wrong buffer) ... */
657 /* now we have the buffer containing the entry we're interested
658 * in; copy it out if it represents a non-deleted entry.
660 entryInDir = curOffset.LowPart & (2048-1);
661 entryInBuffer = curOffset.LowPart & (cm_data.buf_blockSize - 1);
663 /* page header will help tell us which entries are free. Page
664 * header can change more often than once per buffer, since
665 * AFS 3 dir page size may be less than (but not more than) a
666 * buffer package buffer.
668 /* only look intra-buffer */
669 temp = curOffset.LowPart & (cm_data.buf_blockSize - 1);
670 temp &= ~(2048 - 1); /* turn off intra-page bits */
671 pageHeaderp = (cm_pageHeader_t *) (bufferp->datap + temp);
673 /* now determine which entry we're looking at in the page. If
674 * it is free (there's a free bitmap at the start of the dir),
675 * we should skip these 32 bytes.
677 slotInPage = (entryInDir & 0x7e0) >> 5;
678 if (!(pageHeaderp->freeBitmap[slotInPage>>3]
679 & (1 << (slotInPage & 0x7)))) {
680 /* this entry is free */
681 numDirChunks = 1; /* only skip this guy */
685 tp = bufferp->datap + entryInBuffer;
686 dep = (cm_dirEntry_t *) tp; /* now points to AFS3 dir entry */
689 * here are some consistency checks
691 if (dep->flag != CM_DIR_FFIRST ||
692 strlen(dep->name) > 256) {
693 code = CM_ERROR_INVAL;
695 "cm_ApplyDir invalid directory entry for scp %p bufp %p",
697 osi_Log4(afsd_logp,"... cell %u vol %u vnode %u uniq %u",
698 scp->fid.cell, scp->fid.volume, scp->fid.vnode, scp->fid.unique);
699 bufferp->dataVersion = CM_BUF_VERSION_BAD;
703 /* while we're here, compute the next entry's location, too,
704 * since we'll need it when writing out the cookie into the
705 * dir listing stream.
707 numDirChunks = cm_NameEntries(dep->name, NULL);
709 /* compute the offset of the cookie representing the next entry */
710 nextEntryCookie = curOffset.LowPart
711 + (CM_DIR_CHUNKSIZE * numDirChunks);
713 if (dep->fid.vnode != 0) {
714 /* this is one of the entries to use: it is not deleted */
715 code = (*funcp)(scp, dep, parmp, &curOffset);
718 } /* if we're including this name */
721 /* and adjust curOffset to be where the new cookie is */
723 thyper.LowPart = CM_DIR_CHUNKSIZE * numDirChunks;
724 curOffset = LargeIntegerAdd(thyper, curOffset);
725 } /* while copying data for dir listing */
727 /* release the mutex */
729 lock_ReleaseMutex(&bufferp->mx);
730 buf_Release(bufferp);
735 int cm_NoneUpper(normchar_t *s)
739 if (c >= 'A' && c <= 'Z')
744 int cm_NoneLower(normchar_t *s)
748 if (c >= 'a' && c <= 'z')
753 long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
756 cm_lookupSearch_t *sp;
758 normchar_t matchName[MAX_PATH];
759 int looking_for_short_name = FALSE;
761 sp = (cm_lookupSearch_t *) rockp;
763 if (cm_FsStringToNormString(dep->name, -1, matchName, lengthof(matchName)) == 0) {
764 /* Can't normalize FS string. */
769 match = cm_NormStrCmpI(matchName, sp->nsearchNamep);
771 match = cm_NormStrCmp(matchName, sp->nsearchNamep);
775 && !cm_Is8Dot3(matchName)) {
777 cm_Gen8Dot3NameInt(dep->name, &dep->fid, matchName, NULL);
779 match = cm_NormStrCmpI(matchName, sp->nsearchNamep);
781 match = cm_NormStrCmp(matchName, sp->nsearchNamep);
782 looking_for_short_name = TRUE;
792 if (!sp->caseFold || looking_for_short_name) {
793 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
794 return CM_ERROR_STOPNOW;
798 * If we get here, we are doing a case-insensitive search, and we
799 * have found a match. Now we determine what kind of match it is:
800 * exact, lower-case, upper-case, or none of the above. This is done
801 * in order to choose among matches, if there are more than one.
804 /* Exact matches are the best. */
805 match = cm_NormStrCmp(matchName, sp->nsearchNamep);
808 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
809 return CM_ERROR_STOPNOW;
812 /* Lower-case matches are next. */
815 if (cm_NoneUpper(matchName)) {
820 /* Upper-case matches are next. */
823 if (cm_NoneLower(matchName)) {
828 /* General matches are last. */
834 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
838 /* read the contents of a mount point into the appropriate string.
839 * called with write locked scp, and returns with locked scp.
841 long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
845 if (scp->mountPointStringp[0])
848 #ifdef AFS_FREELANCE_CLIENT
849 /* File servers do not have data for freelance entries */
850 if (cm_freelanceEnabled &&
851 scp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
852 scp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
854 code = cm_FreelanceFetchMountPointString(scp);
856 #endif /* AFS_FREELANCE_CLIENT */
858 char temp[MOUNTPOINTLEN];
861 /* otherwise, we have to read it in */
862 offset.LowPart = offset.HighPart = 0;
863 code = cm_GetData(scp, &offset, temp, MOUNTPOINTLEN, userp, reqp);
868 * scp->length is the actual length of the mount point string.
869 * It is current because cm_GetData merged the most up to date
870 * status info into scp and has not dropped the rwlock since.
872 if (scp->length.LowPart > MOUNTPOINTLEN - 1)
873 return CM_ERROR_TOOBIG;
874 if (scp->length.LowPart == 0)
875 return CM_ERROR_INVAL;
877 /* convert the terminating dot to a NUL */
878 temp[scp->length.LowPart - 1] = 0;
879 memcpy(scp->mountPointStringp, temp, scp->length.LowPart);
886 /* called with a locked scp and chases the mount point, yielding outScpp.
887 * scp remains write locked, just for simplicity of describing the interface.
889 long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
890 cm_req_t *reqp, cm_scache_t **outScpp)
892 fschar_t *cellNamep = NULL;
893 fschar_t *volNamep = NULL;
897 cm_volume_t *volp = NULL;
906 if (scp->mountRootFid.cell != 0 && scp->mountRootGen >= cm_data.mountRootGen) {
907 tfid = scp->mountRootFid;
908 lock_ReleaseWrite(&scp->rw);
909 code = cm_GetSCache(&tfid, NULL, outScpp, userp, reqp);
910 lock_ObtainWrite(&scp->rw);
914 /* parse the volume name */
915 mpNamep = scp->mountPointStringp;
917 return CM_ERROR_NOSUCHPATH;
918 mtType = *scp->mountPointStringp;
920 cp = cm_FsStrChr(mpNamep, _FS(':'));
922 /* cellular mount point */
923 cellNamep = (fschar_t *)malloc((cp - mpNamep) * sizeof(fschar_t));
924 cm_FsStrCpyN(cellNamep, cp - mpNamep, mpNamep + 1, cp - mpNamep - 1);
925 volNamep = cm_FsStrDup(cp+1);
927 /* now look up the cell */
928 lock_ReleaseWrite(&scp->rw);
929 cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE);
930 lock_ObtainWrite(&scp->rw);
933 volNamep = cm_FsStrDup(mpNamep + 1);
935 #ifdef AFS_FREELANCE_CLIENT
937 * Mount points in the Freelance cell should default
938 * to the workstation cell.
940 if (cm_freelanceEnabled &&
941 scp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
942 scp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
944 fschar_t rootCellName[256]="";
945 cm_GetRootCellName(rootCellName);
946 cellp = cm_GetCell(rootCellName, 0);
948 #endif /* AFS_FREELANCE_CLIENT */
949 cellp = cm_FindCellByID(scp->fid.cell, 0);
953 code = CM_ERROR_NOSUCHCELL;
957 vnLength = cm_FsStrLen(volNamep);
958 if (vnLength >= 8 && cm_FsStrCmp(volNamep + vnLength - 7, ".backup") == 0)
959 targetType = BACKVOL;
960 else if (vnLength >= 10
961 && cm_FsStrCmp(volNamep + vnLength - 9, ".readonly") == 0)
966 /* check for backups within backups */
967 if (targetType == BACKVOL
968 && (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
969 == CM_SCACHEFLAG_RO) {
970 code = CM_ERROR_NOSUCHVOLUME;
974 /* now we need to get the volume */
975 lock_ReleaseWrite(&scp->rw);
976 if (cm_VolNameIsID(volNamep)) {
977 code = cm_FindVolumeByID(cellp, atoi(volNamep), userp, reqp,
978 CM_GETVOL_FLAG_CREATE, &volp);
980 code = cm_FindVolumeByName(cellp, volNamep, userp, reqp,
981 CM_GETVOL_FLAG_CREATE, &volp);
983 lock_ObtainWrite(&scp->rw);
986 afs_uint32 cell, volume;
987 cm_vol_state_t *statep;
989 cell = cellp->cellID;
991 /* if the mt pt originates in a .backup volume (not a .readonly)
992 * and FollowBackupPath is active, and if there is a .backup
993 * volume for the target, then use the .backup of the target
994 * instead of the read-write.
996 if (cm_followBackupPath &&
997 volp->vol[BACKVOL].ID != 0 &&
998 (dscp->flags & (CM_SCACHEFLAG_RO|CM_SCACHEFLAG_PURERO)) == CM_SCACHEFLAG_RO &&
999 (targetType == RWVOL || targetType == ROVOL && volp->vol[ROVOL].ID == 0)
1001 targetType = BACKVOL;
1003 /* if the mt pt is in a read-only volume (not just a
1004 * backup), and if there is a read-only volume for the
1005 * target, and if this is a targetType '#' mount point, use
1006 * the read-only, otherwise use the one specified.
1008 else if (mtType == '#' && targetType == RWVOL &&
1009 (scp->flags & CM_SCACHEFLAG_PURERO) &&
1010 volp->vol[ROVOL].ID != 0) {
1014 lock_ObtainWrite(&volp->rw);
1015 statep = cm_VolumeStateByType(volp, targetType);
1016 volume = statep->ID;
1017 statep->dotdotFid = dscp->fid;
1018 lock_ReleaseWrite(&volp->rw);
1020 /* the rest of the fid is a magic number */
1021 cm_SetFid(&scp->mountRootFid, cell, volume, 1, 1);
1022 scp->mountRootGen = cm_data.mountRootGen;
1024 tfid = scp->mountRootFid;
1025 lock_ReleaseWrite(&scp->rw);
1026 code = cm_GetSCache(&tfid, NULL, outScpp, userp, reqp);
1027 lock_ObtainWrite(&scp->rw);
1040 long cm_LookupInternal(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_user_t *userp,
1041 cm_req_t *reqp, cm_scache_t **outScpp)
1044 int dnlcHit = 1; /* did we hit in the dnlc? yes, we did */
1045 cm_scache_t *tscp = NULL;
1046 cm_scache_t *mountedScp;
1047 cm_lookupSearch_t rock;
1049 normchar_t *nnamep = NULL;
1050 fschar_t *fnamep = NULL;
1055 memset(&rock, 0, sizeof(rock));
1057 if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
1058 && cm_ClientStrCmp(cnamep, _C("..")) == 0) {
1059 if (dscp->dotdotFid.volume == 0)
1060 return CM_ERROR_NOSUCHVOLUME;
1061 rock.fid = dscp->dotdotFid;
1063 } else if (cm_ClientStrCmp(cnamep, _C(".")) == 0) {
1064 rock.fid = dscp->fid;
1068 nnamep = cm_ClientStringToNormStringAlloc(cnamep, -1, NULL);
1070 code = CM_ERROR_NOSUCHFILE;
1073 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
1075 code = CM_ERROR_NOSUCHFILE;
1080 if (flags & CM_FLAG_NOMOUNTCHASE) {
1081 /* In this case, we should go and call cm_Dir* functions
1082 directly since the following cm_ApplyDir() function will
1090 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ,
1091 CM_DIROP_FLAG_NONE, &dirop);
1094 code = cm_BPlusDirLookup(&dirop, nnamep, &rock.fid);
1099 code = cm_DirLookup(&dirop, fnamep, &rock.fid);
1101 cm_EndDirOp(&dirop);
1111 if (code == CM_ERROR_INEXACT_MATCH && (flags & CM_FLAG_CASEFOLD)) {
1118 code = CM_ERROR_BPLUS_NOMATCH;
1124 rock.fid.cell = dscp->fid.cell;
1125 rock.fid.volume = dscp->fid.volume;
1126 rock.searchNamep = fnamep;
1127 rock.nsearchNamep = nnamep;
1128 rock.caseFold = (flags & CM_FLAG_CASEFOLD);
1129 rock.hasTilde = ((cm_ClientStrChr(cnamep, '~') != NULL) ? 1 : 0);
1131 /* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
1132 code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
1133 (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
1135 /* code == 0 means we fell off the end of the dir, while stopnow means
1136 * that we stopped early, probably because we found the entry we're
1137 * looking for. Any other non-zero code is an error.
1139 if (code && code != CM_ERROR_STOPNOW && code != CM_ERROR_BPLUS_NOMATCH) {
1140 /* if the cm_scache_t we are searching in is not a directory
1141 * we must return path not found because the error
1142 * is to describe the final component not an intermediary
1144 if (code == CM_ERROR_NOTDIR) {
1145 if (flags & CM_FLAG_CHECKPATH)
1146 code = CM_ERROR_NOSUCHPATH;
1148 code = CM_ERROR_NOSUCHFILE;
1154 getroot = (dscp==cm_data.rootSCachep) ;
1156 if (!cm_freelanceEnabled || !getroot) {
1157 if (flags & CM_FLAG_CHECKPATH)
1158 code = CM_ERROR_NOSUCHPATH;
1160 code = CM_ERROR_NOSUCHFILE;
1163 else if (!cm_ClientStrChr(cnamep, '#') &&
1164 !cm_ClientStrChr(cnamep, '%') &&
1165 cm_ClientStrCmpI(cnamep, _C("srvsvc")) &&
1166 cm_ClientStrCmpI(cnamep, _C("wkssvc")) &&
1167 cm_ClientStrCmpI(cnamep, _C("ipc$")))
1169 /* nonexistent dir on freelance root, so add it */
1170 fschar_t fullname[CELL_MAXNAMELEN + 1] = "."; /* +1 so that when we skip the . the size is still CELL_MAXNAMELEN */
1174 osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %S",
1175 osi_LogSaveClientString(afsd_logp,cnamep));
1178 * There is an ugly behavior where a share name "foo" will be searched
1179 * for as "fo". If the searched for name differs by an already existing
1180 * symlink or mount point in the Freelance directory, do not add the
1181 * new value automatically.
1185 fnlen = strlen(fnamep);
1186 if ( fnamep[fnlen-1] == '.') {
1187 fnamep[fnlen-1] = '\0';
1192 if (cnamep[0] == '.') {
1193 if (cm_GetCell_Gen(&fnamep[1], &fullname[1], CM_FLAG_CREATE)) {
1195 code = cm_FreelanceAddMount(fullname, &fullname[1], "root.cell", 1, &rock.fid);
1196 if ( cm_FsStrCmpI(&fnamep[1], &fullname[1])) {
1198 * Do not permit symlinks that are one of:
1199 * . the cellname followed by a dot
1200 * . the cellname minus a single character
1201 * . a substring of the cellname that does not consist of full components
1203 if ( cm_strnicmp_utf8(&fnamep[1], fullname, (int)fnlen-1) == 0 &&
1204 (fnlen-1 == strlen(fullname)-1 || fullname[fnlen-1] != '.'))
1206 /* do not add; substitute fullname for the search */
1208 fnamep = malloc(strlen(fullname)+2);
1210 strncpy(&fnamep[1], fullname, strlen(fullname)+1);
1213 code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
1218 if (cm_GetCell_Gen(fnamep, fullname, CM_FLAG_CREATE)) {
1220 code = cm_FreelanceAddMount(fullname, fullname, "root.cell", 0, &rock.fid);
1221 if ( cm_FsStrCmpI(fnamep, fullname)) {
1223 * Do not permit symlinks that are one of:
1224 * . the cellname followed by a dot
1225 * . the cellname minus a single character
1226 * . a substring of the cellname that does not consist of full components
1228 if ( cm_strnicmp_utf8(fnamep, fullname, (int)fnlen-1) == 0 &&
1229 (fnlen == strlen(fullname)-1 || fullname[fnlen] != '.'))
1231 /* do not add; substitute fullname for the search */
1233 fnamep = strdup(fullname);
1237 code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
1246 nnamep = cm_FsStringToNormStringAlloc(fnamep, -1, NULL);
1250 if (!found || code) { /* add mount point failed, so give up */
1251 if (flags & CM_FLAG_CHECKPATH)
1252 code = CM_ERROR_NOSUCHPATH;
1254 code = CM_ERROR_NOSUCHFILE;
1257 tscp = NULL; /* to force call of cm_GetSCache */
1259 if (flags & CM_FLAG_CHECKPATH)
1260 code = CM_ERROR_NOSUCHPATH;
1262 code = CM_ERROR_NOSUCHFILE;
1268 if ( !tscp ) /* we did not find it in the dnlc */
1271 code = cm_GetSCache(&rock.fid, &dscp->fid, &tscp, userp, reqp);
1275 /* tscp is now held */
1277 lock_ObtainWrite(&tscp->rw);
1280 * Do not get status if we do not already have a callback.
1281 * The process of reading the mount point string will obtain status information
1282 * in a single RPC. No reason to add a second round trip.
1284 * If we do have a callback, use cm_SyncOp to get status in case the
1285 * current cm_user_t is not the same as the one that obtained the
1286 * mount point string contents.
1288 if (cm_HaveCallback(tscp)) {
1289 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1290 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1292 lock_ReleaseWrite(&tscp->rw);
1293 cm_ReleaseSCache(tscp);
1296 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1298 /* tscp is now locked */
1300 if (!(flags & CM_FLAG_NOMOUNTCHASE)
1301 && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
1302 /* mount points are funny: they have a volume name to mount
1305 code = cm_ReadMountPoint(tscp, userp, reqp);
1307 code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
1309 lock_ReleaseWrite(&tscp->rw);
1310 cm_ReleaseSCache(tscp);
1317 lock_ReleaseWrite(&tscp->rw);
1320 /* copy back pointer */
1323 /* insert scache in dnlc */
1324 if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
1325 /* lock the directory entry to prevent racing callback revokes */
1326 lock_ObtainRead(&dscp->rw);
1327 if ( dscp->cbServerp != NULL && dscp->cbExpires > 0 ) {
1328 /* TODO: reuse nnamep from above */
1331 nnamep = cm_ClientStringToNormStringAlloc(cnamep, -1, NULL);
1333 cm_dnlcEnter(dscp, nnamep, tscp);
1335 lock_ReleaseRead(&dscp->rw);
1352 int cm_ExpandSysName(cm_req_t * reqp, clientchar_t *inp, clientchar_t *outp, long outSizeCch, unsigned int index)
1357 int use_sysname64 = 0;
1359 if (cm_sysName64Count > 0 && reqp && (reqp->flags & CM_REQ_WOW64) && (reqp->flags & CM_REQ_SOURCE_REDIR))
1363 tp = cm_ClientStrRChr(inp, '@');
1365 return 0; /* no @sys */
1367 if (cm_ClientStrCmp(tp, _C("@sys")) != 0)
1368 return 0; /* no @sys */
1370 /* caller just wants to know if this is a valid @sys type of name */
1375 if (use_sysname64 && index >= cm_sysName64Count)
1379 if (index >= cm_sysNameCount)
1382 /* otherwise generate the properly expanded @sys name */
1383 prefixCount = (int)(tp - inp);
1385 cm_ClientStrCpyN(outp, outSizeCch, inp, prefixCount); /* copy out "a." from "a.@sys" */
1386 outp[prefixCount] = 0; /* null terminate the "a." */
1389 cm_ClientStrCat(outp, outSizeCch, cm_sysName64List[index]);
1392 cm_ClientStrCat(outp, outSizeCch, cm_sysNameList[index]);
1397 long cm_EvaluateVolumeReference(clientchar_t * namep, long flags, cm_user_t * userp,
1398 cm_req_t *reqp, cm_scache_t ** outScpp)
1400 afs_uint32 code = 0;
1401 fschar_t cellName[CELL_MAXNAMELEN];
1402 fschar_t volumeName[VL_MAXNAMELEN];
1406 fschar_t * fnamep = NULL;
1408 cm_cell_t * cellp = NULL;
1409 cm_volume_t * volp = NULL;
1413 int mountType = RWVOL;
1415 osi_Log1(afsd_logp, "cm_EvaluateVolumeReference for string [%S]",
1416 osi_LogSaveClientString(afsd_logp, namep));
1418 if (cm_ClientStrCmpNI(namep, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH) != 0) {
1419 goto _exit_invalid_path;
1422 /* namep is assumed to look like the following:
1424 @vol:<cellname>%<volume>\0
1426 @vol:<cellname>#<volume>\0
1430 fnamep = cm_ClientStringToFsStringAlloc(namep, -1, NULL);
1431 cp = fnamep + CM_PREFIX_VOL_CCH; /* cp points to cell name, hopefully */
1432 tp = cm_FsStrChr(cp, '%');
1434 tp = cm_FsStrChr(cp, '#');
1436 (len = tp - cp) == 0 ||
1437 len > CELL_MAXNAMELEN)
1438 goto _exit_invalid_path;
1439 cm_FsStrCpyN(cellName, lengthof(cellName), cp, len);
1444 cp = tp+1; /* cp now points to volume, supposedly */
1445 cm_FsStrCpy(volumeName, lengthof(volumeName), cp);
1447 /* OK, now we have the cell and the volume */
1448 osi_Log2(afsd_logp, " Found cell [%s] and volume [%s]",
1449 osi_LogSaveFsString(afsd_logp, cellName),
1450 osi_LogSaveFsString(afsd_logp, volumeName));
1452 cellp = cm_GetCell(cellName, CM_FLAG_CREATE);
1453 if (cellp == NULL) {
1454 goto _exit_invalid_path;
1457 len = cm_FsStrLen(volumeName);
1458 if (len >= 8 && cm_FsStrCmp(volumeName + len - 7, ".backup") == 0)
1460 else if (len >= 10 &&
1461 cm_FsStrCmp(volumeName + len - 9, ".readonly") == 0)
1466 if (cm_VolNameIsID(volumeName)) {
1467 code = cm_FindVolumeByID(cellp, atoi(volumeName), userp, reqp,
1468 CM_GETVOL_FLAG_CREATE, &volp);
1470 code = cm_FindVolumeByName(cellp, volumeName, userp, reqp,
1471 CM_GETVOL_FLAG_CREATE, &volp);
1477 if (volType == BACKVOL)
1478 volume = volp->vol[BACKVOL].ID;
1479 else if (volType == ROVOL ||
1480 (volType == RWVOL && mountType == ROVOL && volp->vol[ROVOL].ID != 0))
1481 volume = volp->vol[ROVOL].ID;
1483 volume = volp->vol[RWVOL].ID;
1485 cm_SetFid(&fid, cellp->cellID, volume, 1, 1);
1487 code = cm_GetSCache(&fid, NULL, outScpp, userp, reqp);
1500 if (flags & CM_FLAG_CHECKPATH)
1501 return CM_ERROR_NOSUCHPATH;
1503 return CM_ERROR_NOSUCHFILE;
1506 #ifdef DEBUG_REFCOUNT
1507 long cm_LookupDbg(cm_scache_t *dscp, clientchar_t *namep, long flags, cm_user_t *userp,
1508 cm_req_t *reqp, cm_scache_t **outScpp, char * file, long line)
1510 long cm_Lookup(cm_scache_t *dscp, clientchar_t *namep, long flags, cm_user_t *userp,
1511 cm_req_t *reqp, cm_scache_t **outScpp)
1515 clientchar_t tname[AFSPATHMAX];
1516 int sysNameIndex = 0;
1517 cm_scache_t *scp = NULL;
1519 #ifdef DEBUG_REFCOUNT
1520 afsi_log("%s:%d cm_Lookup dscp 0x%p ref %d", file, line, dscp, dscp->refCount, file, line);
1521 osi_Log2(afsd_logp, "cm_Lookup dscp 0x%p ref %d", dscp, dscp->refCount);
1524 if ( cm_ClientStrCmpI(namep,_C(SMB_IOCTL_FILENAME_NOSLASH)) == 0 ) {
1525 if (flags & CM_FLAG_CHECKPATH)
1526 return CM_ERROR_NOSUCHPATH;
1528 return CM_ERROR_NOSUCHFILE;
1531 if (dscp == cm_data.rootSCachep &&
1532 cm_ClientStrCmpNI(namep, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH) == 0) {
1533 return cm_EvaluateVolumeReference(namep, flags, userp, reqp, outScpp);
1536 if (cm_ExpandSysName(reqp, namep, NULL, 0, 0) > 0) {
1537 for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
1538 code = cm_ExpandSysName(reqp, namep, tname, lengthof(tname), sysNameIndex);
1540 code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
1541 #ifdef DEBUG_REFCOUNT
1542 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);
1543 osi_Log3(afsd_logp, "cm_LookupInternal (1) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1551 cm_ReleaseSCache(scp);
1555 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1556 #ifdef DEBUG_REFCOUNT
1557 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);
1558 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1565 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1566 #ifdef DEBUG_REFCOUNT
1567 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);
1568 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1574 /* None of the possible sysName expansions could be found */
1575 if (flags & CM_FLAG_CHECKPATH)
1576 return CM_ERROR_NOSUCHPATH;
1578 return CM_ERROR_NOSUCHFILE;
1581 /*! \brief Unlink a file name
1583 Encapsulates a call to RXAFS_RemoveFile().
1585 \param[in] dscp cm_scache_t pointing at the directory containing the
1586 name to be unlinked.
1588 \param[in] fnamep Original name to be unlinked. This is the
1589 name that will be passed into the RXAFS_RemoveFile() call.
1590 This parameter is optional. If not provided, the value will
1593 \param[in] came Client name to be unlinked. This name will be used
1594 to update the local directory caches.
1596 \param[in] userp cm_user_t for the request.
1598 \param[in] reqp Request tracker.
1601 long cm_Unlink(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t * cnamep,
1602 cm_user_t *userp, cm_req_t *reqp)
1608 AFSFetchStatus newDirStatus;
1610 struct rx_connection * rxconnp;
1612 cm_scache_t *scp = NULL;
1613 int free_fnamep = FALSE;
1616 memset(&volSync, 0, sizeof(volSync));
1618 if (fnamep == NULL) {
1621 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ,
1622 CM_DIROP_FLAG_NONE, &dirop);
1624 code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
1627 cm_EndDirOp(&dirop);
1634 #ifdef AFS_FREELANCE_CLIENT
1635 if (cm_freelanceEnabled && dscp == cm_data.rootSCachep) {
1636 /* deleting a mount point from the root dir. */
1637 code = cm_FreelanceRemoveMount(fnamep);
1642 code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
1646 /* Check for RO volume */
1647 if (dscp->flags & CM_SCACHEFLAG_RO) {
1648 code = CM_ERROR_READONLY;
1652 /* make sure we don't screw up the dir status during the merge */
1653 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE,
1654 CM_DIROP_FLAG_NONE, &dirop);
1656 lock_ObtainWrite(&dscp->rw);
1657 sflags = CM_SCACHESYNC_STOREDATA;
1658 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1659 lock_ReleaseWrite(&dscp->rw);
1661 cm_EndDirOp(&dirop);
1666 InterlockedIncrement(&dscp->activeRPCs);
1668 afsFid.Volume = dscp->fid.volume;
1669 afsFid.Vnode = dscp->fid.vnode;
1670 afsFid.Unique = dscp->fid.unique;
1672 osi_Log1(afsd_logp, "CALL RemoveFile scp 0x%p", dscp);
1674 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
1678 rxconnp = cm_GetRxConn(connp);
1679 code = RXAFS_RemoveFile(rxconnp, &afsFid, fnamep,
1680 &newDirStatus, &volSync);
1681 rx_PutConnection(rxconnp);
1683 } while (cm_Analyze(connp, userp, reqp, &dscp->fid, 1, &volSync, NULL, NULL, code));
1684 code = cm_MapRPCError(code, reqp);
1687 osi_Log1(afsd_logp, "CALL RemoveFile FAILURE, code 0x%x", code);
1689 osi_Log0(afsd_logp, "CALL RemoveFile SUCCESS");
1692 lock_ObtainWrite(&dirop.scp->dirlock);
1693 dirop.lockType = CM_DIRLOCK_WRITE;
1695 lock_ObtainWrite(&dscp->rw);
1696 cm_dnlcRemove(dscp, cnamep);
1698 cm_MergeStatus(NULL, dscp, &newDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
1700 if (cm_CheckDirOpForSingleChange(&dirop) && cnamep) {
1701 lock_ReleaseWrite(&dscp->rw);
1702 cm_DirDeleteEntry(&dirop, fnamep);
1704 cm_BPlusDirDeleteEntry(&dirop, cnamep);
1706 lock_ObtainWrite(&dscp->rw);
1709 InterlockedDecrement(&scp->activeRPCs);
1710 if (code == CM_ERROR_NOSUCHFILE) {
1711 /* windows would not have allowed the request to delete the file
1712 * if it did not believe the file existed. therefore, we must
1713 * have an inconsistent view of the world.
1715 dscp->cbServerp = NULL;
1719 cm_SyncOpDone(dscp, NULL, sflags);
1720 lock_ReleaseWrite(&dscp->rw);
1722 cm_EndDirOp(&dirop);
1724 if (invalidate && RDR_Initialized &&
1725 scp->fileType != CM_SCACHETYPE_FILE && scp->fileType != CM_SCACHETYPE_DIRECTORY)
1726 RDR_InvalidateObject(dscp->fid.cell, dscp->fid.volume, dscp->fid.vnode,
1727 dscp->fid.unique, dscp->fid.hash,
1728 dscp->fileType, AFS_INVALIDATE_DATA_VERSION);
1731 cm_ReleaseSCache(scp);
1733 lock_ObtainWrite(&scp->rw);
1734 if (--scp->linkCount == 0) {
1735 scp->flags |= CM_SCACHEFLAG_DELETED;
1736 lock_ObtainWrite(&cm_scacheLock);
1737 cm_AdjustScacheLRU(scp);
1738 cm_RemoveSCacheFromHashTable(scp);
1739 lock_ReleaseWrite(&cm_scacheLock);
1741 cm_DiscardSCache(scp);
1742 lock_ReleaseWrite(&scp->rw);
1743 if (RDR_Initialized && !(reqp->flags & CM_REQ_SOURCE_REDIR) &&
1744 !RDR_InvalidateObject(scp->fid.cell, scp->fid.volume, scp->fid.vnode,
1745 scp->fid.unique, scp->fid.hash,
1746 scp->fileType, AFS_INVALIDATE_DELETED))
1747 buf_ClearRDRFlag(scp, "unlink");
1758 /* called with a write locked vnode, and fills in the link info.
1759 * returns this the vnode still write locked.
1761 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1765 lock_AssertWrite(&linkScp->rw);
1766 if (!linkScp->mountPointStringp[0]) {
1768 #ifdef AFS_FREELANCE_CLIENT
1769 /* File servers do not have data for freelance entries */
1770 if (cm_freelanceEnabled &&
1771 linkScp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
1772 linkScp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
1774 code = cm_FreelanceFetchMountPointString(linkScp);
1776 #endif /* AFS_FREELANCE_CLIENT */
1778 char temp[MOUNTPOINTLEN];
1781 /* read the link data from the file server */
1782 offset.LowPart = offset.HighPart = 0;
1783 code = cm_GetData(linkScp, &offset, temp, MOUNTPOINTLEN, userp, reqp);
1788 * linkScp->length is the actual length of the symlink target string.
1789 * It is current because cm_GetData merged the most up to date
1790 * status info into scp and has not dropped the rwlock since.
1792 if (linkScp->length.LowPart > MOUNTPOINTLEN - 1)
1793 return CM_ERROR_TOOBIG;
1794 if (linkScp->length.LowPart == 0)
1795 return CM_ERROR_INVAL;
1797 /* make sure we are NUL terminated */
1798 temp[linkScp->length.LowPart] = 0;
1799 memcpy(linkScp->mountPointStringp, temp, linkScp->length.LowPart + 1);
1802 if ( !strnicmp(linkScp->mountPointStringp, "msdfs:", strlen("msdfs:")) )
1803 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1805 } /* don't have symlink contents cached */
1810 /* called with a held vnode and a path suffix, with the held vnode being a
1811 * symbolic link. Our goal is to generate a new path to interpret, and return
1812 * this new path in newSpaceBufferp. If the new vnode is relative to a dir
1813 * other than the directory containing the symbolic link, then the new root is
1814 * returned in *newRootScpp, otherwise a null is returned there.
1816 long cm_AssembleLink(cm_scache_t *linkScp, fschar_t *pathSuffixp,
1817 cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1818 cm_user_t *userp, cm_req_t *reqp)
1825 *newRootScpp = NULL;
1826 *newSpaceBufferp = NULL;
1828 lock_ObtainWrite(&linkScp->rw);
1830 * Do not get status if we do not already have a callback.
1831 * The process of reading the symlink string will obtain status information
1832 * in a single RPC. No reason to add a second round trip.
1834 * If we do have a callback, use cm_SyncOp to get status in case the
1835 * current cm_user_t is not the same as the one that obtained the
1836 * symlink string contents.
1838 if (cm_HaveCallback(linkScp)) {
1839 code = cm_SyncOp(linkScp, NULL, userp, reqp, 0,
1840 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1842 lock_ReleaseWrite(&linkScp->rw);
1843 cm_ReleaseSCache(linkScp);
1846 cm_SyncOpDone(linkScp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1848 code = cm_HandleLink(linkScp, userp, reqp);
1852 /* if we may overflow the buffer, bail out; buffer is signficantly
1853 * bigger than max path length, so we don't really have to worry about
1854 * being a little conservative here.
1856 if (cm_FsStrLen(linkScp->mountPointStringp) + cm_FsStrLen(pathSuffixp) + 2
1857 >= CM_UTILS_SPACESIZE) {
1858 code = CM_ERROR_TOOBIG;
1862 tsp = cm_GetSpace();
1863 linkp = linkScp->mountPointStringp;
1864 if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1865 if (strlen(linkp) > cm_mountRootLen)
1866 StringCbCopyA((char *) tsp->data, sizeof(tsp->data), linkp+cm_mountRootLen+1);
1869 *newRootScpp = cm_RootSCachep(userp, reqp);
1870 cm_HoldSCache(*newRootScpp);
1871 } else if (linkp[0] == '\\' && linkp[1] == '\\') {
1872 if (!strnicmp(&linkp[2], cm_NetbiosName, (len = (long)strlen(cm_NetbiosName))))
1874 char * p = &linkp[len + 3];
1875 if (strnicmp(p, "all", 3) == 0)
1878 StringCbCopyA(tsp->data, sizeof(tsp->data), p);
1879 for (p = tsp->data; *p; p++) {
1883 *newRootScpp = cm_RootSCachep(userp, reqp);
1884 cm_HoldSCache(*newRootScpp);
1886 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1887 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1888 code = CM_ERROR_PATH_NOT_COVERED;
1890 } else if ( linkScp->fileType == CM_SCACHETYPE_DFSLINK ||
1891 !strnicmp(linkp, "msdfs:", (len = (long)strlen("msdfs:"))) ) {
1892 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1893 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1894 code = CM_ERROR_PATH_NOT_COVERED;
1895 } else if (*linkp == '\\' || *linkp == '/') {
1897 /* formerly, this was considered to be from the AFS root,
1898 * but this seems to create problems. instead, we will just
1899 * reject the link */
1900 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp+1);
1901 *newRootScpp = cm_RootSCachep(userp, reqp);
1902 cm_HoldSCache(*newRootScpp);
1904 /* we still copy the link data into the response so that
1905 * the user can see what the link points to
1907 linkScp->fileType = CM_SCACHETYPE_INVALID;
1908 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1909 code = CM_ERROR_NOSUCHPATH;
1912 /* a relative link */
1913 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1915 if (pathSuffixp[0] != 0) { /* if suffix string is non-null */
1916 StringCchCatA(tsp->data,lengthof(tsp->data), "\\");
1917 StringCchCatA(tsp->data,lengthof(tsp->data), pathSuffixp);
1921 clientchar_t * cpath = cm_FsStringToClientStringAlloc(tsp->data, -1, NULL);
1922 if (cpath != NULL) {
1923 cm_ClientStrCpy(tsp->wdata, lengthof(tsp->wdata), cpath);
1925 *newSpaceBufferp = tsp;
1927 code = CM_ERROR_NOSUCHPATH;
1934 if (code == CM_ERROR_PATH_NOT_COVERED && reqp->tidPathp && reqp->relPathp) {
1935 cm_VolStatus_Notify_DFS_Mapping(linkScp, reqp->tidPathp, reqp->relPathp);
1940 lock_ReleaseWrite(&linkScp->rw);
1943 #ifdef DEBUG_REFCOUNT
1944 long cm_NameIDbg(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
1945 cm_user_t *userp, clientchar_t *tidPathp, cm_req_t *reqp,
1946 cm_scache_t **outScpp,
1947 char * file, long line)
1949 long cm_NameI(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
1950 cm_user_t *userp, clientchar_t *tidPathp,
1951 cm_req_t *reqp, cm_scache_t **outScpp)
1955 clientchar_t *tp; /* ptr moving through input buffer */
1956 clientchar_t tc; /* temp char */
1957 int haveComponent; /* has new component started? */
1958 clientchar_t component[AFSPATHMAX]; /* this is the new component */
1959 clientchar_t *cp; /* component name being assembled */
1960 cm_scache_t *tscp; /* current location in the hierarchy */
1961 cm_scache_t *nscp; /* next dude down */
1962 cm_scache_t *dirScp; /* last dir we searched */
1963 cm_scache_t *linkScp; /* new root for the symlink we just
1965 cm_space_t *psp; /* space for current path, if we've hit
1967 cm_space_t *tempsp; /* temp vbl */
1968 clientchar_t *restp; /* rest of the pathname to interpret */
1969 int symlinkCount; /* count of # of symlinks traversed */
1970 int extraFlag; /* avoid chasing mt pts for dir cmd */
1971 int phase = 1; /* 1 = tidPathp, 2 = pathp */
1972 #define MAX_FID_COUNT 512
1973 cm_fid_t fids[MAX_FID_COUNT]; /* array of fids processed in this path walk */
1974 int fid_count = 0; /* number of fids processed in this path walk */
1979 #ifdef DEBUG_REFCOUNT
1980 afsi_log("%s:%d cm_NameI rootscp 0x%p ref %d", file, line, rootSCachep, rootSCachep->refCount);
1981 osi_Log4(afsd_logp,"cm_NameI rootscp 0x%p path %S tidpath %S flags 0x%x",
1982 rootSCachep, pathp ? pathp : L"<NULL>", tidPathp ? tidPathp : L"<NULL>",
1997 cm_HoldSCache(tscp);
2005 /* map Unix slashes into DOS ones so we can interpret Unix
2011 if (!haveComponent) {
2014 } else if (tc == 0) {
2028 /* we have a component here */
2029 if (tc == 0 || tc == '\\') {
2030 /* end of the component; we're at the last
2031 * component if tc == 0. However, if the last
2032 * is a symlink, we have more to do.
2034 *cp++ = 0; /* add null termination */
2036 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
2037 extraFlag = CM_FLAG_NOMOUNTCHASE;
2038 code = cm_Lookup(tscp, component,
2040 userp, reqp, &nscp);
2043 if (!cm_ClientStrCmp(component,_C("..")) ||
2044 !cm_ClientStrCmp(component,_C("."))) {
2046 * roll back the fid list until we find the
2047 * fid that matches where we are now. Its not
2048 * necessarily one or two fids because they
2049 * might have been symlinks or mount points or
2050 * both that were crossed.
2052 for ( i=fid_count-1; i>=0; i--) {
2053 if (!cm_FidCmp(&nscp->fid, &fids[i]))
2058 /* add the new fid to the list */
2059 if (fid_count == MAX_FID_COUNT) {
2060 code = CM_ERROR_TOO_MANY_SYMLINKS;
2061 cm_ReleaseSCache(nscp);
2065 fids[fid_count++] = nscp->fid;
2070 cm_ReleaseSCache(tscp);
2072 cm_ReleaseSCache(dirScp);
2075 if ((code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH) &&
2076 tscp->fileType == CM_SCACHETYPE_SYMLINK) {
2077 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_NOSUCHPATH");
2078 return CM_ERROR_NOSUCHPATH;
2080 osi_Log1(afsd_logp,"cm_NameI code 0x%x", code);
2085 haveComponent = 0; /* component done */
2087 cm_ReleaseSCache(dirScp);
2088 dirScp = tscp; /* for some symlinks */
2089 tscp = nscp; /* already held */
2091 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
2094 cm_ReleaseSCache(dirScp);
2100 /* now, if tscp is a symlink, we should follow it and
2101 * assemble the path again.
2103 lock_ObtainWrite(&tscp->rw);
2104 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
2105 CM_SCACHESYNC_GETSTATUS
2106 | CM_SCACHESYNC_NEEDCALLBACK);
2108 lock_ReleaseWrite(&tscp->rw);
2109 cm_ReleaseSCache(tscp);
2112 cm_ReleaseSCache(dirScp);
2117 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2119 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
2120 /* this is a symlink; assemble a new buffer */
2121 lock_ReleaseWrite(&tscp->rw);
2122 if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
2123 cm_ReleaseSCache(tscp);
2126 cm_ReleaseSCache(dirScp);
2131 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_TOO_MANY_SYMLINKS");
2132 return CM_ERROR_TOO_MANY_SYMLINKS;
2142 /* TODO: make this better */
2143 frestp = cm_ClientStringToFsStringAlloc(restp, -1, NULL);
2144 code = cm_AssembleLink(tscp, frestp, &linkScp, &tempsp, userp, reqp);
2148 if (code == 0 && linkScp != NULL) {
2149 if (linkScp == cm_data.rootSCachep) {
2153 for ( i=0; i<fid_count; i++) {
2154 if ( !cm_FidCmp(&linkScp->fid, &fids[i]) ) {
2155 code = CM_ERROR_TOO_MANY_SYMLINKS;
2156 cm_ReleaseSCache(linkScp);
2162 if (i == fid_count && fid_count < MAX_FID_COUNT) {
2163 fids[fid_count++] = linkScp->fid;
2168 /* something went wrong */
2169 cm_ReleaseSCache(tscp);
2172 cm_ReleaseSCache(dirScp);
2178 /* otherwise, tempsp has the new path,
2179 * and linkScp is the new root from
2180 * which to interpret that path.
2181 * Continue with the namei processing,
2182 * also doing the bookkeeping for the
2183 * space allocation and tracking the
2184 * vnode reference counts.
2190 cm_ReleaseSCache(tscp);
2195 * now, if linkScp is null, that's
2196 * AssembleLink's way of telling us that
2197 * the sym link is relative to the dir
2198 * containing the link. We have a ref
2199 * to it in dirScp, and we hold it now
2200 * and reuse it as the new spot in the
2208 /* not a symlink, we may be done */
2209 lock_ReleaseWrite(&tscp->rw);
2217 cm_ReleaseSCache(dirScp);
2225 cm_ReleaseSCache(dirScp);
2228 } /* end of a component */
2231 } /* we have a component */
2232 } /* big while loop over all components */
2236 cm_ReleaseSCache(dirScp);
2242 cm_ReleaseSCache(tscp);
2244 #ifdef DEBUG_REFCOUNT
2245 afsi_log("%s:%d cm_NameI code 0x%x outScpp 0x%p ref %d", file, line, code, *outScpp, (*outScpp) ? (*outScpp)->refCount : 0);
2247 osi_Log2(afsd_logp,"cm_NameI code 0x%x outScpp 0x%p", code, *outScpp);
2251 /* called with a dir, and a vnode within the dir that happens to be a symlink.
2252 * We chase the link, and return a held pointer to the target, if it exists,
2253 * in *outScpp. If we succeed, we return 0, otherwise we return an error code
2254 * and do not hold or return a target vnode.
2256 * This is very similar to calling cm_NameI with the last component of a name,
2257 * which happens to be a symlink, except that we've already passed by the name.
2259 * This function is typically called by the directory listing functions, which
2260 * encounter symlinks but need to return the proper file length so programs
2261 * like "more" work properly when they make use of the attributes retrieved from
2264 * The input vnode should not be locked when this function is called.
2266 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
2267 cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
2271 cm_scache_t *newRootScp;
2275 osi_Log1(afsd_logp, "Evaluating symlink scp 0x%p", linkScp);
2277 code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
2281 /* now, if newRootScp is NULL, we're really being told that the symlink
2282 * is relative to the current directory (dscp).
2284 if (newRootScp == NULL) {
2286 cm_HoldSCache(dscp);
2289 code = cm_NameI(newRootScp, spacep->wdata,
2290 CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
2291 userp, NULL, reqp, outScpp);
2293 if (code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH)
2294 code = CM_ERROR_NOSUCHPATH;
2296 /* this stuff is allocated no matter what happened on the namei call,
2298 cm_FreeSpace(spacep);
2299 cm_ReleaseSCache(newRootScp);
2301 if (linkScp == *outScpp) {
2302 cm_ReleaseSCache(*outScpp);
2304 code = CM_ERROR_NOSUCHPATH;
2310 /* for a given entry, make sure that it isn't in the stat cache, and then
2311 * add it to the list of file IDs to be obtained.
2313 * Don't bother adding it if we already have a vnode. Note that the dir
2314 * is locked, so we have to be careful checking the vnode we're thinking of
2315 * processing, to avoid deadlocks.
2317 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
2328 /* Don't overflow bsp. */
2329 if (bsp->counter >= CM_BULKMAX)
2330 return CM_ERROR_STOPNOW;
2332 thyper.LowPart = cm_data.buf_blockSize;
2333 thyper.HighPart = 0;
2334 thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
2336 /* thyper is now the first byte past the end of the record we're
2337 * interested in, and bsp->bufOffset is the first byte of the record
2338 * we're interested in.
2339 * Skip data in the others.
2342 if (LargeIntegerLessThan(*offp, bsp->bufOffset))
2344 if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
2345 return CM_ERROR_STOPNOW;
2346 if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
2349 cm_SetFid(&tfid, scp->fid.cell, scp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
2350 tscp = cm_FindSCache(&tfid);
2352 if (lock_TryWrite(&tscp->rw)) {
2353 /* we have an entry that we can look at */
2354 if (!cm_EAccesFindEntry(bsp->userp, &tscp->fid) && cm_HaveCallback(tscp)) {
2355 /* we have a callback on it. Don't bother
2356 * fetching this stat entry, since we're happy
2357 * with the info we have.
2359 lock_ReleaseWrite(&tscp->rw);
2360 cm_ReleaseSCache(tscp);
2363 lock_ReleaseWrite(&tscp->rw);
2365 cm_ReleaseSCache(tscp);
2368 #ifdef AFS_FREELANCE_CLIENT
2369 // yj: if this is a mountpoint under root.afs then we don't want it
2370 // to be bulkstat-ed, instead, we call getSCache directly and under
2371 // getSCache, it is handled specially.
2372 if ( cm_freelanceEnabled &&
2373 tfid.cell==AFS_FAKE_ROOT_CELL_ID &&
2374 tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
2375 !(tfid.vnode==0x1 && tfid.unique==0x1) )
2377 osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
2378 return cm_GetSCache(&tfid, NULL, &tscp, NULL, NULL);
2380 #endif /* AFS_FREELANCE_CLIENT */
2383 bsp->fids[i].Volume = scp->fid.volume;
2384 bsp->fids[i].Vnode = tfid.vnode;
2385 bsp->fids[i].Unique = tfid.unique;
2390 cm_TryBulkStatRPC(cm_scache_t *dscp, cm_bulkStat_t *bbp, cm_user_t *userp, cm_req_t *reqp)
2393 AFSCBFids fidStruct;
2394 AFSBulkStats statStruct;
2396 AFSCBs callbackStruct;
2399 cm_callbackRequest_t cbReq;
2406 struct rx_connection * rxconnp;
2407 int inlinebulk; /* Did we use InlineBulkStatus RPC or not? */
2409 memset(&volSync, 0, sizeof(volSync));
2411 /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
2412 * make the calls to create the entries. Handle AFSCBMAX files at a
2415 for (filex = 0; filex < bbp->counter; filex += filesThisCall) {
2416 filesThisCall = bbp->counter - filex;
2417 if (filesThisCall > AFSCBMAX)
2418 filesThisCall = AFSCBMAX;
2420 fidStruct.AFSCBFids_len = filesThisCall;
2421 fidStruct.AFSCBFids_val = &bbp->fids[filex];
2422 statStruct.AFSBulkStats_len = filesThisCall;
2423 statStruct.AFSBulkStats_val = &bbp->stats[filex];
2424 callbackStruct.AFSCBs_len = filesThisCall;
2425 callbackStruct.AFSCBs_val = &bbp->callbacks[filex];
2426 cm_StartCallbackGrantingCall(NULL, &cbReq);
2427 osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
2430 * Whenever cm_Analyze is called for a RXAFS_ RPC there must
2431 * be a FID provided. However, the error code from RXAFS_BulkStatus
2432 * or RXAFS_InlinkBulkStatus does not apply to any FID. Therefore,
2433 * we generate an invalid FID to match with the RPC error.
2435 cm_SetFid(&tfid, dscp->fid.cell, dscp->fid.volume, 0, 0);
2440 code = cm_ConnFromFID(&tfid, userp, reqp, &connp);
2444 rxconnp = cm_GetRxConn(connp);
2445 if (!(connp->serverp->flags & CM_SERVERFLAG_NOINLINEBULK)) {
2446 code = RXAFS_InlineBulkStatus(rxconnp, &fidStruct,
2447 &statStruct, &callbackStruct, &volSync);
2448 if (code == RXGEN_OPCODE) {
2449 cm_SetServerNoInlineBulk(connp->serverp, 0);
2455 code = RXAFS_BulkStatus(rxconnp, &fidStruct,
2456 &statStruct, &callbackStruct, &volSync);
2458 rx_PutConnection(rxconnp);
2461 * If InlineBulk RPC was called and it succeeded,
2462 * then pull out the return code from the status info
2463 * and use it for cm_Analyze so that we can failover to other
2464 * .readonly volume instances. But only do it for errors that
2465 * are volume global.
2467 if (inlinebulk && code == 0 && (&bbp->stats[0])->errorCode) {
2468 osi_Log1(afsd_logp, "cm_TryBulkStat inline-bulk stat error: %d",
2469 (&bbp->stats[0])->errorCode);
2470 switch ((&bbp->stats[0])->errorCode) {
2479 code = (&bbp->stats[0])->errorCode;
2482 /* Rx and Rxkad errors are volume global */
2483 if ( (&bbp->stats[0])->errorCode >= -64 && (&bbp->stats[0])->errorCode < 0 ||
2484 (&bbp->stats[0])->errorCode >= ERROR_TABLE_BASE_RXK && (&bbp->stats[0])->errorCode < ERROR_TABLE_BASE_RXK + 256)
2485 code = (&bbp->stats[0])->errorCode;
2488 } while (cm_Analyze(connp, userp, reqp, &tfid, 0, &volSync, NULL, &cbReq, code));
2489 code = cm_MapRPCError(code, reqp);
2492 * might as well quit on an error, since we're not going to do
2493 * much better on the next immediate call, either.
2496 osi_Log2(afsd_logp, "CALL %sBulkStatus FAILURE code 0x%x",
2497 inlinebulk ? "Inline" : "", code);
2498 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2503 * The bulk RPC has succeeded or at least not failed with a
2504 * volume global error result. For items that have inlineBulk
2505 * errors we must call cm_Analyze in order to perform required
2506 * logging of errors.
2508 * If the RPC was not inline bulk or the entry either has no error
2509 * the status must be merged.
2511 osi_Log1(afsd_logp, "CALL %sBulkStatus SUCCESS", inlinebulk ? "Inline" : "");
2513 for (i = 0; i<filesThisCall; i++) {
2515 cm_SetFid(&tfid, dscp->fid.cell, bbp->fids[j].Volume, bbp->fids[j].Vnode, bbp->fids[j].Unique);
2517 if (inlinebulk && (&bbp->stats[j])->errorCode) {
2518 cm_req_t treq = *reqp;
2519 cm_Analyze(NULL, userp, &treq, &tfid, 0, &volSync, NULL, &cbReq, (&bbp->stats[j])->errorCode);
2520 switch ((&bbp->stats[j])->errorCode) {
2525 cm_EAccesAddEntry(userp, &tfid, &dscp->fid);
2528 code = cm_GetSCache(&tfid, &dscp->fid, &scp, userp, reqp);
2533 * otherwise, if this entry has no callback info,
2534 * merge in this. If there is existing callback info
2535 * we skip the merge because the existing data must be
2536 * current (we have a callback) and the response from
2537 * a non-inline bulk rpc might actually be wrong.
2539 * now, we have to be extra paranoid on merging in this
2540 * information, since we didn't use cm_SyncOp before
2541 * starting the fetch to make sure that no bad races
2542 * were occurring. Specifically, we need to make sure
2543 * we don't obliterate any newer information in the
2544 * vnode than have here.
2546 * Right now, be pretty conservative: if there's a
2547 * callback or a pending call, skip it.
2548 * However, if the prior attempt to obtain status
2549 * was refused access or the volume is .readonly,
2550 * take the data in any case since we have nothing
2551 * better for the in flight directory enumeration that
2552 * resulted in this function being called.
2554 lock_ObtainRead(&scp->rw);
2555 if ((scp->cbServerp == NULL &&
2556 !(scp->flags & (CM_SCACHEFLAG_FETCHING | CM_SCACHEFLAG_STORING | CM_SCACHEFLAG_SIZESTORING))) ||
2557 (scp->flags & CM_SCACHEFLAG_PURERO) ||
2558 cm_EAccesFindEntry(userp, &scp->fid))
2560 lock_ConvertRToW(&scp->rw);
2561 lostRace = cm_EndCallbackGrantingCall(scp, &cbReq,
2564 CM_CALLBACK_MAINTAINCOUNT);
2565 InterlockedIncrement(&scp->activeRPCs);
2567 cm_MergeStatus(dscp, scp, &bbp->stats[j], &volSync, userp, reqp, 0);
2568 lock_ReleaseWrite(&scp->rw);
2570 lock_ReleaseRead(&scp->rw);
2572 cm_ReleaseSCache(scp);
2574 } /* all files in the response */
2575 /* now tell it to drop the count,
2576 * after doing the vnode processing above */
2577 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2578 } /* while there are still more files to process */
2583 /* called with a write locked scp and a pointer to a buffer. Make bulk stat
2584 * calls on all undeleted files in the page of the directory specified.
2587 cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
2593 osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%p", dscp);
2595 /* should be on a buffer boundary */
2596 osi_assertx((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0, "invalid offset");
2598 bbp = malloc(sizeof(cm_bulkStat_t));
2599 memset(bbp, 0, sizeof(cm_bulkStat_t));
2601 bbp->bufOffset = *offsetp;
2603 lock_ReleaseWrite(&dscp->rw);
2604 /* first, assemble the file IDs we need to stat */
2605 code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) bbp, offsetp, userp, reqp, NULL);
2607 /* if we failed, bail out early */
2608 if (code && code != CM_ERROR_STOPNOW) {
2610 lock_ObtainWrite(&dscp->rw);
2614 code = cm_TryBulkStatRPC(dscp, bbp, userp, reqp);
2615 osi_Log1(afsd_logp, "END cm_TryBulkStat code 0x%x", code);
2617 lock_ObtainWrite(&dscp->rw);
2622 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
2626 /* initialize store back mask as inexpensive local variable */
2628 memset(statusp, 0, sizeof(AFSStoreStatus));
2630 /* copy out queued info from scache first, if scp passed in */
2632 if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
2633 statusp->ClientModTime = scp->clientModTime;
2634 mask |= AFS_SETMODTIME;
2635 scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
2640 /* now add in our locally generated request */
2641 if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
2642 statusp->ClientModTime = attrp->clientModTime;
2643 mask |= AFS_SETMODTIME;
2645 if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
2646 statusp->UnixModeBits = attrp->unixModeBits;
2647 mask |= AFS_SETMODE;
2649 if (attrp->mask & CM_ATTRMASK_OWNER) {
2650 statusp->Owner = attrp->owner;
2651 mask |= AFS_SETOWNER;
2653 if (attrp->mask & CM_ATTRMASK_GROUP) {
2654 statusp->Group = attrp->group;
2655 mask |= AFS_SETGROUP;
2658 statusp->Mask = mask;
2662 cm_IsSpaceAvailable(cm_fid_t * fidp, osi_hyper_t *sizep, cm_user_t *userp, cm_req_t *reqp)
2667 struct rx_connection * rxconnp;
2668 AFSFetchVolumeStatus volStat;
2669 cm_volume_t *volp = NULL;
2674 char volName[32]="(unknown)";
2675 char offLineMsg[256]="server temporarily inaccessible";
2676 char motd[256]="server temporarily inaccessible";
2677 osi_hyper_t freespace;
2679 if (fidp->cell==AFS_FAKE_ROOT_CELL_ID &&
2680 fidp->volume==AFS_FAKE_ROOT_VOL_ID)
2685 volp = cm_GetVolumeByFID(fidp);
2690 volType = cm_VolumeType(volp, fidp->volume);
2691 if (volType == ROVOL || volType == BACKVOL) {
2697 OfflineMsg = offLineMsg;
2701 code = cm_ConnFromFID(fidp, userp, reqp, &connp);
2704 rxconnp = cm_GetRxConn(connp);
2705 code = RXAFS_GetVolumeStatus(rxconnp, fidp->volume,
2706 &volStat, &Name, &OfflineMsg, &MOTD);
2707 rx_PutConnection(rxconnp);
2709 } while (cm_Analyze(connp, userp, reqp, fidp, 0, NULL, NULL, NULL, code));
2710 code = cm_MapRPCError(code, reqp);
2712 if (volStat.MaxQuota) {
2713 freespace.QuadPart = 1024 * (afs_int64)min(volStat.MaxQuota - volStat.BlocksInUse, volStat.PartBlocksAvail);
2715 freespace.QuadPart = 1024 * (afs_int64)volStat.PartBlocksAvail;
2717 spaceAvail = LargeIntegerGreaterThanOrEqualTo(freespace, *sizep);
2719 /* the rpc failed, assume there is space and we can fail it later. */
2728 /* set the file size, and make sure that all relevant buffers have been
2729 * truncated. Ensure that any partially truncated buffers have been zeroed
2730 * to the end of the buffer.
2732 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
2738 /* start by locking out buffer creation */
2739 lock_ObtainWrite(&scp->bufCreateLock);
2741 /* verify that this is a file, not a dir or a symlink */
2742 lock_ObtainWrite(&scp->rw);
2743 code = cm_SyncOp(scp, NULL, userp, reqp, 0,
2744 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2747 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2749 if (scp->fileType != CM_SCACHETYPE_FILE) {
2750 code = CM_ERROR_ISDIR;
2755 if (LargeIntegerLessThan(*sizep, scp->length))
2760 lock_ReleaseWrite(&scp->rw);
2762 /* can't hold scp->rw lock here, since we may wait for a storeback to
2763 * finish if the buffer package is cleaning a buffer by storing it to
2767 buf_Truncate(scp, userp, reqp, sizep);
2769 /* now ensure that file length is short enough, and update truncPos */
2770 lock_ObtainWrite(&scp->rw);
2772 /* make sure we have a callback (so we have the right value for the
2773 * length), and wait for it to be safe to do a truncate.
2775 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
2776 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2777 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2779 /* If we only have 'i' bits, then we should still be able to set
2780 the size of a file we created. */
2781 if (code == CM_ERROR_NOACCESS && scp->creator == userp) {
2782 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
2783 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2784 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2790 if (LargeIntegerLessThan(*sizep, scp->length)) {
2791 /* a real truncation. If truncPos is not set yet, or is bigger
2792 * than where we're truncating the file, set truncPos to this
2797 if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
2798 || LargeIntegerLessThan(*sizep, scp->length)) {
2800 scp->truncPos = *sizep;
2801 scp->mask |= CM_SCACHEMASK_TRUNCPOS;
2803 /* in either case, the new file size has been changed */
2804 scp->length = *sizep;
2805 scp->mask |= CM_SCACHEMASK_LENGTH;
2807 else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
2808 /* really extending the file */
2809 /* Check to see if we have sufficient quota */
2810 if (cm_IsSpaceAvailable(&scp->fid, sizep, userp, reqp)) {
2811 scp->length = *sizep;
2812 scp->mask |= CM_SCACHEMASK_LENGTH;
2814 code = CM_ERROR_SPACE;
2819 /* done successfully */
2823 cm_SyncOpDone(scp, NULL,
2824 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2825 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2828 lock_ReleaseWrite(&scp->rw);
2829 lock_ReleaseWrite(&scp->bufCreateLock);
2834 /* set the file size or other attributes (but not both at once) */
2835 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
2839 AFSFetchStatus afsOutStatus;
2843 AFSStoreStatus afsInStatus;
2844 struct rx_connection * rxconnp;
2846 memset(&volSync, 0, sizeof(volSync));
2848 /* handle file length setting */
2849 if (attrp->mask & CM_ATTRMASK_LENGTH)
2850 return cm_SetLength(scp, &attrp->length, userp, reqp);
2852 lock_ObtainWrite(&scp->rw);
2853 /* Check for RO volume */
2854 if (scp->flags & CM_SCACHEFLAG_RO) {
2855 code = CM_ERROR_READONLY;
2856 lock_ReleaseWrite(&scp->rw);
2860 /* otherwise, we have to make an RPC to get the status */
2861 code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
2863 lock_ReleaseWrite(&scp->rw);
2866 lock_ConvertWToR(&scp->rw);
2868 /* make the attr structure */
2869 cm_StatusFromAttr(&afsInStatus, scp, attrp);
2871 tfid.Volume = scp->fid.volume;
2872 tfid.Vnode = scp->fid.vnode;
2873 tfid.Unique = scp->fid.unique;
2874 lock_ReleaseRead(&scp->rw);
2876 /* now make the RPC */
2877 InterlockedIncrement(&scp->activeRPCs);
2879 osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%p", scp);
2881 code = cm_ConnFromFID(&scp->fid, userp, reqp, &connp);
2885 rxconnp = cm_GetRxConn(connp);
2886 code = RXAFS_StoreStatus(rxconnp, &tfid,
2887 &afsInStatus, &afsOutStatus, &volSync);
2888 rx_PutConnection(rxconnp);
2890 } while (cm_Analyze(connp, userp, reqp,
2891 &scp->fid, 1, &volSync, NULL, NULL, code));
2892 code = cm_MapRPCError(code, reqp);
2895 osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
2897 osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
2899 lock_ObtainWrite(&scp->rw);
2901 cm_MergeStatus(NULL, scp, &afsOutStatus, &volSync, userp, reqp,
2902 CM_MERGEFLAG_FORCE|CM_MERGEFLAG_STOREDATA);
2904 InterlockedDecrement(&scp->activeRPCs);
2905 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
2907 /* if we're changing the mode bits, discard the ACL cache,
2908 * since we changed the mode bits.
2910 if (afsInStatus.Mask & AFS_SETMODE)
2911 cm_FreeAllACLEnts(scp);
2912 lock_ReleaseWrite(&scp->rw);
2916 long cm_Create(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
2917 cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
2922 cm_callbackRequest_t cbReq;
2925 cm_scache_t *scp = NULL;
2928 AFSStoreStatus inStatus;
2929 AFSFetchStatus updatedDirStatus;
2930 AFSFetchStatus newFileStatus;
2931 AFSCallBack newFileCallback;
2933 struct rx_connection * rxconnp;
2935 fschar_t * fnamep = NULL;
2937 memset(&volSync, 0, sizeof(volSync));
2939 /* can't create names with @sys in them; must expand it manually first.
2940 * return "invalid request" if they try.
2942 if (cm_ExpandSysName(NULL, cnamep, NULL, 0, 0)) {
2943 return CM_ERROR_ATSYS;
2946 #ifdef AFS_FREELANCE_CLIENT
2947 /* Freelance root volume does not hold files */
2948 if (cm_freelanceEnabled &&
2949 dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
2950 dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
2952 return CM_ERROR_NOACCESS;
2954 #endif /* AFS_FREELANCE_CLIENT */
2956 /* Check for RO volume */
2957 if (dscp->flags & CM_SCACHEFLAG_RO)
2958 return CM_ERROR_READONLY;
2960 /* before starting the RPC, mark that we're changing the file data, so
2961 * that someone who does a chmod will know to wait until our call
2964 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
2966 lock_ObtainWrite(&dscp->rw);
2967 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2968 lock_ReleaseWrite(&dscp->rw);
2970 cm_StartCallbackGrantingCall(NULL, &cbReq);
2972 cm_EndDirOp(&dirop);
2979 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
2981 cm_StatusFromAttr(&inStatus, NULL, attrp);
2983 /* try the RPC now */
2984 InterlockedIncrement(&dscp->activeRPCs);
2985 osi_Log1(afsd_logp, "CALL CreateFile scp 0x%p", dscp);
2987 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2991 dirAFSFid.Volume = dscp->fid.volume;
2992 dirAFSFid.Vnode = dscp->fid.vnode;
2993 dirAFSFid.Unique = dscp->fid.unique;
2995 rxconnp = cm_GetRxConn(connp);
2996 code = RXAFS_CreateFile(connp->rxconnp, &dirAFSFid, fnamep,
2997 &inStatus, &newAFSFid, &newFileStatus,
2998 &updatedDirStatus, &newFileCallback,
3000 rx_PutConnection(rxconnp);
3002 } while (cm_Analyze(connp, userp, reqp,
3003 &dscp->fid, 1, &volSync, NULL, &cbReq, code));
3004 code = cm_MapRPCError(code, reqp);
3007 osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
3009 osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
3012 lock_ObtainWrite(&dirop.scp->dirlock);
3013 dirop.lockType = CM_DIRLOCK_WRITE;
3015 lock_ObtainWrite(&dscp->rw);
3017 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3018 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3019 if (cm_CheckDirOpForSingleChange(&dirop)) {
3020 lock_ReleaseWrite(&dscp->rw);
3021 cm_DirCreateEntry(&dirop, fnamep, &newFid);
3023 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
3025 lock_ObtainWrite(&dscp->rw);
3028 InterlockedDecrement(&dscp->activeRPCs);
3030 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3031 lock_ReleaseWrite(&dscp->rw);
3033 /* now try to create the file's entry, too, but be careful to
3034 * make sure that we don't merge in old info. Since we weren't locking
3035 * out any requests during the file's creation, we may have pretty old
3039 code = cm_GetSCache(&newFid, &dscp->fid, &scp, userp, reqp);
3041 lock_ObtainWrite(&scp->rw);
3042 scp->creator = userp; /* remember who created it */
3043 if (!cm_HaveCallback(scp)) {
3044 lostRace = cm_EndCallbackGrantingCall(scp, &cbReq,
3045 &newFileCallback, &volSync, 0);
3046 InterlockedIncrement(&scp->activeRPCs);
3048 cm_MergeStatus(dscp, scp, &newFileStatus, &volSync,
3052 lock_ReleaseWrite(&scp->rw);
3056 /* make sure we end things properly */
3058 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
3060 cm_EndDirOp(&dirop);
3069 cm_ReleaseSCache(scp);
3075 * locked if TRUE means write-locked
3076 * else the cm_scache_t rw must not be held
3078 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp, afs_uint32 locked)
3083 lock_ReleaseWrite(&scp->rw);
3085 osi_Log2(afsd_logp, "cm_FSync scp 0x%p userp 0x%p", scp, userp);
3087 code = buf_CleanVnode(scp, userp, reqp);
3089 lock_ObtainWrite(&scp->rw);
3091 if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
3092 | CM_SCACHEMASK_CLIENTMODTIME
3093 | CM_SCACHEMASK_LENGTH))
3094 code = cm_StoreMini(scp, userp, reqp);
3096 if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) {
3097 code = (scp->flags & CM_SCACHEFLAG_OVERQUOTA) ? CM_ERROR_QUOTA : CM_ERROR_SPACE;
3098 scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE);
3102 lock_ReleaseWrite(&scp->rw);
3103 } else if (locked) {
3104 lock_ObtainWrite(&scp->rw);
3109 long cm_MakeDir(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
3110 cm_user_t *userp, cm_req_t *reqp, cm_scache_t **scpp)
3115 cm_callbackRequest_t cbReq;
3118 cm_scache_t *scp = NULL;
3121 AFSStoreStatus inStatus;
3122 AFSFetchStatus updatedDirStatus;
3123 AFSFetchStatus newDirStatus;
3124 AFSCallBack newDirCallback;
3126 struct rx_connection * rxconnp;
3128 fschar_t * fnamep = NULL;
3130 memset(&volSync, 0, sizeof(volSync));
3132 /* can't create names with @sys in them; must expand it manually first.
3133 * return "invalid request" if they try.
3135 if (cm_ExpandSysName(NULL, cnamep, NULL, 0, 0)) {
3136 return CM_ERROR_ATSYS;
3139 #ifdef AFS_FREELANCE_CLIENT
3140 /* Freelance root volume does not hold subdirectories */
3141 if (cm_freelanceEnabled &&
3142 dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
3143 dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
3145 return CM_ERROR_NOACCESS;
3147 #endif /* AFS_FREELANCE_CLIENT */
3149 /* Check for RO volume */
3150 if (dscp->flags & CM_SCACHEFLAG_RO)
3151 return CM_ERROR_READONLY;
3153 /* before starting the RPC, mark that we're changing the directory
3154 * data, so that someone who does a chmod on the dir will wait until
3155 * our call completes.
3157 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3159 lock_ObtainWrite(&dscp->rw);
3160 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3161 lock_ReleaseWrite(&dscp->rw);
3163 cm_StartCallbackGrantingCall(NULL, &cbReq);
3165 cm_EndDirOp(&dirop);
3172 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3173 cm_StatusFromAttr(&inStatus, NULL, attrp);
3175 /* try the RPC now */
3176 InterlockedIncrement(&dscp->activeRPCs);
3177 osi_Log1(afsd_logp, "CALL MakeDir scp 0x%p", dscp);
3179 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3183 dirAFSFid.Volume = dscp->fid.volume;
3184 dirAFSFid.Vnode = dscp->fid.vnode;
3185 dirAFSFid.Unique = dscp->fid.unique;
3187 rxconnp = cm_GetRxConn(connp);
3188 code = RXAFS_MakeDir(connp->rxconnp, &dirAFSFid, fnamep,
3189 &inStatus, &newAFSFid, &newDirStatus,
3190 &updatedDirStatus, &newDirCallback,
3192 rx_PutConnection(rxconnp);
3194 } while (cm_Analyze(connp, userp, reqp,
3195 &dscp->fid, 1, &volSync, NULL, &cbReq, code));
3196 code = cm_MapRPCError(code, reqp);
3199 osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
3201 osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
3204 lock_ObtainWrite(&dirop.scp->dirlock);
3205 dirop.lockType = CM_DIRLOCK_WRITE;
3207 lock_ObtainWrite(&dscp->rw);
3209 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3210 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3211 if (cm_CheckDirOpForSingleChange(&dirop)) {
3212 lock_ReleaseWrite(&dscp->rw);
3213 cm_DirCreateEntry(&dirop, fnamep, &newFid);
3215 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
3217 lock_ObtainWrite(&dscp->rw);
3220 InterlockedDecrement(&dscp->activeRPCs);
3222 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3223 lock_ReleaseWrite(&dscp->rw);
3225 /* now try to create the new dir's entry, too, but be careful to
3226 * make sure that we don't merge in old info. Since we weren't locking
3227 * out any requests during the file's creation, we may have pretty old
3231 code = cm_GetSCache(&newFid, &dscp->fid, &scp, userp, reqp);
3233 lock_ObtainWrite(&scp->rw);
3234 if (!cm_HaveCallback(scp)) {
3235 lostRace = cm_EndCallbackGrantingCall(scp, &cbReq,
3236 &newDirCallback, &volSync, 0);
3237 InterlockedIncrement(&scp->activeRPCs);
3239 cm_MergeStatus(dscp, scp, &newDirStatus, &volSync,
3243 lock_ReleaseWrite(&scp->rw);
3247 /* make sure we end things properly */
3249 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
3251 cm_EndDirOp(&dirop);
3259 cm_ReleaseSCache(scp);
3262 /* and return error code */
3266 long cm_Link(cm_scache_t *dscp, clientchar_t *cnamep, cm_scache_t *sscp, long flags,
3267 cm_user_t *userp, cm_req_t *reqp)
3272 AFSFid existingAFSFid;
3273 AFSFetchStatus updatedDirStatus;
3274 AFSFetchStatus newLinkStatus;
3276 struct rx_connection * rxconnp;
3278 fschar_t * fnamep = NULL;
3281 memset(&volSync, 0, sizeof(volSync));
3283 if (dscp->fid.cell != sscp->fid.cell ||
3284 dscp->fid.volume != sscp->fid.volume) {
3285 return CM_ERROR_CROSSDEVLINK;
3288 /* Check for RO volume */
3289 if (dscp->flags & CM_SCACHEFLAG_RO)
3290 return CM_ERROR_READONLY;
3292 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3294 lock_ObtainWrite(&dscp->rw);
3295 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3296 lock_ReleaseWrite(&dscp->rw);
3298 cm_EndDirOp(&dirop);
3303 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3305 /* try the RPC now */
3306 InterlockedIncrement(&dscp->activeRPCs);
3307 osi_Log1(afsd_logp, "CALL Link scp 0x%p", dscp);
3309 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3312 dirAFSFid.Volume = dscp->fid.volume;
3313 dirAFSFid.Vnode = dscp->fid.vnode;
3314 dirAFSFid.Unique = dscp->fid.unique;
3316 existingAFSFid.Volume = sscp->fid.volume;
3317 existingAFSFid.Vnode = sscp->fid.vnode;
3318 existingAFSFid.Unique = sscp->fid.unique;
3320 rxconnp = cm_GetRxConn(connp);
3321 code = RXAFS_Link(rxconnp, &dirAFSFid, fnamep, &existingAFSFid,
3322 &newLinkStatus, &updatedDirStatus, &volSync);
3323 rx_PutConnection(rxconnp);
3324 osi_Log1(afsd_logp," RXAFS_Link returns 0x%x", code);
3326 } while (cm_Analyze(connp, userp, reqp, &dscp->fid, 1, &volSync, NULL, NULL, code));
3328 code = cm_MapRPCError(code, reqp);
3331 osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
3333 osi_Log0(afsd_logp, "CALL Link SUCCESS");
3336 lock_ObtainWrite(&dirop.scp->dirlock);
3337 dirop.lockType = CM_DIRLOCK_WRITE;
3339 lock_ObtainWrite(&dscp->rw);
3341 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3344 if (cm_CheckDirOpForSingleChange(&dirop)) {
3345 lock_ReleaseWrite(&dscp->rw);
3346 cm_DirCreateEntry(&dirop, fnamep, &sscp->fid);
3348 cm_BPlusDirCreateEntry(&dirop, cnamep, &sscp->fid);
3350 lock_ObtainWrite(&dscp->rw);
3353 InterlockedDecrement(&dscp->activeRPCs);
3355 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3356 lock_ReleaseWrite(&dscp->rw);
3358 cm_EndDirOp(&dirop);
3360 if (invalidate && RDR_Initialized)
3361 RDR_InvalidateObject(dscp->fid.cell, dscp->fid.volume, dscp->fid.vnode,
3362 dscp->fid.unique, dscp->fid.hash,
3363 dscp->fileType, AFS_INVALIDATE_DATA_VERSION);
3365 /* Update the linked object status */
3367 lock_ObtainWrite(&sscp->rw);
3368 InterlockedIncrement(&sscp->activeRPCs);
3369 cm_MergeStatus(NULL, sscp, &newLinkStatus, &volSync, userp, reqp, 0);
3370 lock_ReleaseWrite(&sscp->rw);
3378 long cm_SymLink(cm_scache_t *dscp, clientchar_t *cnamep, fschar_t *contentsp, long flags,
3379 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp, cm_scache_t **scpp)
3387 AFSStoreStatus inStatus;
3388 AFSFetchStatus updatedDirStatus;
3389 AFSFetchStatus newLinkStatus;
3391 struct rx_connection * rxconnp;
3393 fschar_t *fnamep = NULL;
3398 /* Check for RO volume */
3399 if (dscp->flags & CM_SCACHEFLAG_RO)
3400 return CM_ERROR_READONLY;
3402 memset(&volSync, 0, sizeof(volSync));
3404 /* before starting the RPC, mark that we're changing the directory data,
3405 * so that someone who does a chmod on the dir will wait until our
3408 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3410 lock_ObtainWrite(&dscp->rw);
3411 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3412 lock_ReleaseWrite(&dscp->rw);
3414 cm_EndDirOp(&dirop);
3419 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3421 cm_StatusFromAttr(&inStatus, NULL, attrp);
3423 /* try the RPC now */
3424 InterlockedIncrement(&dscp->activeRPCs);
3425 osi_Log1(afsd_logp, "CALL Symlink scp 0x%p", dscp);
3427 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3431 dirAFSFid.Volume = dscp->fid.volume;
3432 dirAFSFid.Vnode = dscp->fid.vnode;
3433 dirAFSFid.Unique = dscp->fid.unique;
3435 rxconnp = cm_GetRxConn(connp);
3436 code = RXAFS_Symlink(rxconnp, &dirAFSFid, fnamep, contentsp,
3437 &inStatus, &newAFSFid, &newLinkStatus,
3438 &updatedDirStatus, &volSync);
3439 rx_PutConnection(rxconnp);
3441 } while (cm_Analyze(connp, userp, reqp,
3442 &dscp->fid, 1, &volSync, NULL, NULL, code));
3443 code = cm_MapRPCError(code, reqp);
3446 osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
3448 osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
3451 lock_ObtainWrite(&dirop.scp->dirlock);
3452 dirop.lockType = CM_DIRLOCK_WRITE;
3454 lock_ObtainWrite(&dscp->rw);
3456 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3457 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3458 if (cm_CheckDirOpForSingleChange(&dirop)) {
3459 lock_ReleaseWrite(&dscp->rw);
3460 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3462 cm_DirCreateEntry(&dirop, fnamep, &newFid);
3464 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
3466 lock_ObtainWrite(&dscp->rw);
3469 InterlockedDecrement(&dscp->activeRPCs);
3471 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3472 lock_ReleaseWrite(&dscp->rw);
3474 cm_EndDirOp(&dirop);
3476 /* now try to create the new dir's entry, too, but be careful to
3477 * make sure that we don't merge in old info. Since we weren't locking
3478 * out any requests during the file's creation, we may have pretty old
3482 code = cm_GetSCache(&newFid, &dscp->fid, &scp, userp, reqp);
3484 lock_ObtainWrite(&scp->rw);
3485 if (!cm_HaveCallback(scp)) {
3486 InterlockedIncrement(&scp->activeRPCs);
3487 cm_MergeStatus(dscp, scp, &newLinkStatus, &volSync,
3490 lock_ReleaseWrite(&scp->rw);
3495 cm_ReleaseSCache(scp);
3502 /* and return error code */
3506 /*! \brief Remove a directory
3508 Encapsulates a call to RXAFS_RemoveDir().
3510 \param[in] dscp cm_scache_t for the directory containing the
3511 directory to be removed.
3513 \param[in] fnamep This will be the original name of the directory
3514 as known to the file server. It will be passed in to RXAFS_RemoveDir().
3515 This parameter is optional. If it is not provided the value
3518 \param[in] cnamep Normalized name used to update the local
3521 \param[in] userp cm_user_t for the request.
3523 \param[in] reqp Request tracker.
3525 long cm_RemoveDir(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t *cnamep, cm_user_t *userp, cm_req_t *reqp)
3531 AFSFetchStatus updatedDirStatus;
3533 struct rx_connection * rxconnp;
3535 cm_scache_t *scp = NULL;
3536 int free_fnamep = FALSE;
3538 memset(&volSync, 0, sizeof(volSync));
3540 if (fnamep == NULL) {
3543 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ,
3544 CM_DIROP_FLAG_NONE, &dirop);
3546 code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
3549 cm_EndDirOp(&dirop);
3556 code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
3560 /* Check for RO volume */
3561 if (dscp->flags & CM_SCACHEFLAG_RO) {
3562 code = CM_ERROR_READONLY;
3566 /* before starting the RPC, mark that we're changing the directory data,
3567 * so that someone who does a chmod on the dir will wait until our
3570 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3572 lock_ObtainWrite(&dscp->rw);
3573 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3574 lock_ReleaseWrite(&dscp->rw);
3576 cm_EndDirOp(&dirop);
3581 /* try the RPC now */
3582 InterlockedIncrement(&dscp->activeRPCs);
3583 osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%p", dscp);
3585 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3589 dirAFSFid.Volume = dscp->fid.volume;
3590 dirAFSFid.Vnode = dscp->fid.vnode;
3591 dirAFSFid.Unique = dscp->fid.unique;
3593 rxconnp = cm_GetRxConn(connp);
3594 code = RXAFS_RemoveDir(rxconnp, &dirAFSFid, fnamep,
3595 &updatedDirStatus, &volSync);
3596 rx_PutConnection(rxconnp);
3598 } while (cm_Analyze(connp, userp, reqp,
3599 &dscp->fid, 1, &volSync, NULL, NULL, code));
3600 code = cm_MapRPCErrorRmdir(code, reqp);
3603 osi_Log1(afsd_logp, "CALL RemoveDir FAILURE, code 0x%x", code);
3605 osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
3608 lock_ObtainWrite(&dirop.scp->dirlock);
3609 dirop.lockType = CM_DIRLOCK_WRITE;
3611 lock_ObtainWrite(&dscp->rw);
3613 cm_dnlcRemove(dscp, cnamep);
3614 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3615 if (cm_CheckDirOpForSingleChange(&dirop) && cnamep != NULL) {
3616 lock_ReleaseWrite(&dscp->rw);
3617 cm_DirDeleteEntry(&dirop, fnamep);
3619 cm_BPlusDirDeleteEntry(&dirop, cnamep);
3621 lock_ObtainWrite(&dscp->rw);
3624 InterlockedDecrement(&dscp->activeRPCs);
3626 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3627 lock_ReleaseWrite(&dscp->rw);
3629 cm_EndDirOp(&dirop);
3632 cm_ReleaseSCache(scp);
3634 lock_ObtainWrite(&scp->rw);
3635 scp->flags |= CM_SCACHEFLAG_DELETED;
3636 lock_ObtainWrite(&cm_scacheLock);
3637 cm_AdjustScacheLRU(scp);
3638 cm_RemoveSCacheFromHashTable(scp);
3639 lock_ReleaseWrite(&cm_scacheLock);
3640 lock_ReleaseWrite(&scp->rw);
3641 if (RDR_Initialized && !(reqp->flags & CM_REQ_SOURCE_REDIR) &&
3642 !RDR_InvalidateObject(scp->fid.cell, scp->fid.volume, scp->fid.vnode,
3643 scp->fid.unique, scp->fid.hash,
3644 scp->fileType, AFS_INVALIDATE_DELETED))
3645 buf_ClearRDRFlag(scp, "rmdir");
3653 /* and return error code */
3657 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
3659 /* grab mutex on contents */
3660 lock_ObtainWrite(&scp->rw);
3662 /* reset the prefetch info */
3663 scp->prefetch.base.LowPart = 0; /* base */
3664 scp->prefetch.base.HighPart = 0;
3665 scp->prefetch.end.LowPart = 0; /* and end */
3666 scp->prefetch.end.HighPart = 0;
3668 /* release mutex on contents */
3669 lock_ReleaseWrite(&scp->rw);
3675 /*! \brief Rename a file or directory
3677 Encapsulates a RXAFS_Rename() call.
3679 \param[in] oldDscp cm_scache_t for the directory containing the old
3682 \param[in] oldNamep The original old name known to the file server.
3683 This is the name that will be passed into the RXAFS_Rename().
3684 If it is not provided, it will be looked up.
3686 \param[in] normalizedOldNamep Normalized old name. This is used for
3687 updating local directory caches.
3689 \param[in] newDscp cm_scache_t for the directory containing the new
3692 \param[in] newNamep New name. Normalized.
3694 \param[in] userp cm_user_t for the request.
3696 \param[in,out] reqp Request tracker.
3699 long cm_Rename(cm_scache_t *oldDscp, fschar_t *oldNamep, clientchar_t *cOldNamep,
3700 cm_scache_t *newDscp, clientchar_t *cNewNamep, cm_user_t *userp,
3705 AFSFid oldDirAFSFid;
3706 AFSFid newDirAFSFid;
3707 AFSFetchStatus updatedOldDirStatus;
3708 AFSFetchStatus updatedNewDirStatus;
3711 int bTargetExists = 0;
3712 struct rx_connection * rxconnp;
3713 cm_dirOp_t oldDirOp;
3716 cm_dirOp_t newDirOp;
3717 fschar_t * newNamep = NULL;
3718 int free_oldNamep = FALSE;
3719 cm_scache_t *oldScp = NULL, *oldTargetScp = NULL;
3720 int rpc_skipped = 0;
3722 memset(&volSync, 0, sizeof(volSync));
3724 if (cOldNamep == NULL || cNewNamep == NULL ||
3725 cm_ClientStrLen(cOldNamep) == 0 ||
3726 cm_ClientStrLen(cNewNamep) == 0)
3727 return CM_ERROR_INVAL;
3729 /* check for identical names */
3730 if (oldDscp == newDscp &&
3731 cm_ClientStrCmp(cOldNamep, cNewNamep) == 0) {
3732 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_RENAME_IDENTICAL",
3734 return CM_ERROR_RENAME_IDENTICAL;
3737 /* Check for RO volume */
3738 if ((oldDscp->flags & CM_SCACHEFLAG_RO) || (newDscp->flags & CM_SCACHEFLAG_RO)) {
3739 return CM_ERROR_READONLY;
3742 if (oldNamep == NULL) {
3745 code = cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_READ,
3746 CM_DIROP_FLAG_NONE, &oldDirOp);
3748 code = cm_BPlusDirLookupOriginalName(&oldDirOp, cOldNamep, &oldNamep);
3750 free_oldNamep = TRUE;
3751 cm_EndDirOp(&oldDirOp);
3755 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S Original Name lookup failed",
3756 oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3761 /* before starting the RPC, mark that we're changing the directory data,
3762 * so that someone who does a chmod on the dir will wait until our call
3763 * completes. We do this in vnode order so that we don't deadlock,
3764 * which makes the code a little verbose.
3766 if (oldDscp == newDscp) {
3768 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE,
3769 CM_DIROP_FLAG_NONE, &oldDirOp);
3770 lock_ObtainWrite(&oldDscp->rw);
3771 cm_dnlcRemove(oldDscp, cOldNamep);
3772 cm_dnlcRemove(oldDscp, cNewNamep);
3773 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3774 CM_SCACHESYNC_STOREDATA);
3775 lock_ReleaseWrite(&oldDscp->rw);
3777 cm_EndDirOp(&oldDirOp);
3781 /* two distinct dir vnodes */
3783 if (oldDscp->fid.cell != newDscp->fid.cell ||
3784 oldDscp->fid.volume != newDscp->fid.volume) {
3785 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_CROSSDEVLINK",
3787 code = CM_ERROR_CROSSDEVLINK;
3791 /* shouldn't happen that we have distinct vnodes for two
3792 * different files, but could due to deliberate attack, or
3793 * stale info. Avoid deadlocks and quit now.
3795 if (oldDscp->fid.vnode == newDscp->fid.vnode) {
3796 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p vnode collision",
3798 code = CM_ERROR_CROSSDEVLINK;
3802 if (oldDscp->fid.vnode < newDscp->fid.vnode) {
3803 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE,
3804 CM_DIROP_FLAG_NONE, &oldDirOp);
3805 lock_ObtainWrite(&oldDscp->rw);
3806 cm_dnlcRemove(oldDscp, cOldNamep);
3807 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3808 CM_SCACHESYNC_STOREDATA);
3809 lock_ReleaseWrite(&oldDscp->rw);
3811 cm_EndDirOp(&oldDirOp);
3813 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE,
3814 CM_DIROP_FLAG_NONE, &newDirOp);
3815 lock_ObtainWrite(&newDscp->rw);
3816 cm_dnlcRemove(newDscp, cNewNamep);
3817 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3818 CM_SCACHESYNC_STOREDATA);
3819 lock_ReleaseWrite(&newDscp->rw);
3821 cm_EndDirOp(&newDirOp);
3823 /* cleanup first one */
3824 lock_ObtainWrite(&oldDscp->rw);
3825 cm_SyncOpDone(oldDscp, NULL,
3826 CM_SCACHESYNC_STOREDATA);
3827 lock_ReleaseWrite(&oldDscp->rw);
3828 cm_EndDirOp(&oldDirOp);
3833 /* lock the new vnode entry first */
3834 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE,
3835 CM_DIROP_FLAG_NONE, &newDirOp);
3836 lock_ObtainWrite(&newDscp->rw);
3837 cm_dnlcRemove(newDscp, cNewNamep);
3838 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3839 CM_SCACHESYNC_STOREDATA);
3840 lock_ReleaseWrite(&newDscp->rw);
3842 cm_EndDirOp(&newDirOp);
3844 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE,
3845 CM_DIROP_FLAG_NONE, &oldDirOp);
3846 lock_ObtainWrite(&oldDscp->rw);
3847 cm_dnlcRemove(oldDscp, cOldNamep);
3848 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3849 CM_SCACHESYNC_STOREDATA);
3850 lock_ReleaseWrite(&oldDscp->rw);
3852 cm_EndDirOp(&oldDirOp);
3854 /* cleanup first one */
3855 lock_ObtainWrite(&newDscp->rw);
3856 cm_SyncOpDone(newDscp, NULL,
3857 CM_SCACHESYNC_STOREDATA);
3858 lock_ReleaseWrite(&newDscp->rw);
3859 cm_EndDirOp(&newDirOp);
3863 } /* two distinct vnodes */
3869 * The source and destination directories are now locked and no other local
3870 * changes can occur.
3872 * Before we permit the operation, make sure that we do not already have
3873 * an object in the destination directory that has a case-insensitive match
3874 * for this name UNLESS the matching object is the object we are renaming.
3876 code = cm_Lookup(oldDscp, cOldNamep, 0, userp, reqp, &oldScp);
3878 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S old name lookup failed",
3879 oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3884 /* Case sensitive lookup. If this succeeds we are done. */
3885 code = cm_Lookup(newDscp, cNewNamep, 0, userp, reqp, &oldTargetScp);
3888 * Case insensitive lookup. If this succeeds, it could have found the
3889 * same file with a name that differs only by case or it could be a
3890 * different file entirely.
3892 code = cm_Lookup(newDscp, cNewNamep, CM_FLAG_CASEFOLD, userp, reqp, &oldTargetScp);
3894 /* found a matching object with the new name */
3895 if (cm_FidCmp(&oldScp->fid, &oldTargetScp->fid)) {
3896 /* and they don't match so return an error */
3897 osi_Log2(afsd_logp, "cm_Rename newDscp 0x%p cNewName %S new name already exists",
3898 newDscp, osi_LogSaveStringW(afsd_logp, cNewNamep));
3899 code = CM_ERROR_EXISTS;
3901 cm_ReleaseSCache(oldTargetScp);
3902 oldTargetScp = NULL;
3903 } else if (code == CM_ERROR_AMBIGUOUS_FILENAME) {
3904 code = CM_ERROR_EXISTS;
3906 /* The target does not exist. Clear the error and perform the rename. */
3918 newNamep = cm_ClientStringToFsStringAlloc(cNewNamep, -1, NULL);
3920 /* try the RPC now */
3921 InterlockedIncrement(&oldDscp->activeRPCs);
3923 InterlockedIncrement(&newDscp->activeRPCs);
3924 osi_Log2(afsd_logp, "CALL Rename old scp 0x%p new scp 0x%p",
3927 code = cm_ConnFromFID(&oldDscp->fid, userp, reqp, &connp);
3931 oldDirAFSFid.Volume = oldDscp->fid.volume;
3932 oldDirAFSFid.Vnode = oldDscp->fid.vnode;
3933 oldDirAFSFid.Unique = oldDscp->fid.unique;
3934 newDirAFSFid.Volume = newDscp->fid.volume;
3935 newDirAFSFid.Vnode = newDscp->fid.vnode;
3936 newDirAFSFid.Unique = newDscp->fid.unique;
3938 rxconnp = cm_GetRxConn(connp);
3939 code = RXAFS_Rename(rxconnp, &oldDirAFSFid, oldNamep,
3940 &newDirAFSFid, newNamep,
3941 &updatedOldDirStatus, &updatedNewDirStatus,
3943 rx_PutConnection(rxconnp);
3945 } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid, 1,
3946 &volSync, NULL, NULL, code));
3947 code = cm_MapRPCError(code, reqp);
3950 osi_Log1(afsd_logp, "CALL Rename FAILURE, code 0x%x", code);
3952 osi_Log0(afsd_logp, "CALL Rename SUCCESS");
3955 /* update the individual stat cache entries for the directories */
3957 lock_ObtainWrite(&oldDirOp.scp->dirlock);
3958 oldDirOp.lockType = CM_DIRLOCK_WRITE;
3961 lock_ObtainWrite(&oldDscp->rw);
3963 cm_MergeStatus(NULL, oldDscp, &updatedOldDirStatus, &volSync,
3964 userp, reqp, CM_MERGEFLAG_DIROP);
3965 if (cm_CheckDirOpForSingleChange(&oldDirOp)) {
3966 lock_ReleaseWrite(&oldDscp->rw);
3967 if (bTargetExists && oneDir) {
3968 diropCode = cm_DirDeleteEntry(&oldDirOp, newNamep);
3970 cm_BPlusDirDeleteEntry(&oldDirOp, cNewNamep);
3975 diropCode = cm_BPlusDirLookup(&oldDirOp, cOldNamep, &fileFid);
3976 if (diropCode == CM_ERROR_INEXACT_MATCH)
3978 else if (diropCode == EINVAL)
3980 diropCode = cm_DirLookup(&oldDirOp, oldNamep, &fileFid);
3982 if (diropCode == 0) {
3984 diropCode = cm_DirCreateEntry(&oldDirOp, newNamep, &fileFid);
3986 cm_BPlusDirCreateEntry(&oldDirOp, cNewNamep, &fileFid);
3990 if (diropCode == 0) {
3991 diropCode = cm_DirDeleteEntry(&oldDirOp, oldNamep);
3993 cm_BPlusDirDeleteEntry(&oldDirOp, cOldNamep);
3997 lock_ObtainWrite(&oldDscp->rw);
4001 InterlockedDecrement(&oldDscp->activeRPCs);
4003 cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
4004 lock_ReleaseWrite(&oldDscp->rw);
4006 cm_EndDirOp(&oldDirOp);
4008 /* and update it for the new one, too, if necessary */
4011 lock_ObtainWrite(&newDirOp.scp->dirlock);
4012 newDirOp.lockType = CM_DIRLOCK_WRITE;
4014 lock_ObtainWrite(&newDscp->rw);
4016 cm_MergeStatus(NULL, newDscp, &updatedNewDirStatus, &volSync,
4017 userp, reqp, CM_MERGEFLAG_DIROP);
4020 * we only make the local change if we successfully made
4021 * the change in the old directory AND there was only one
4022 * change in the new directory
4024 if (diropCode == 0 && cm_CheckDirOpForSingleChange(&newDirOp)) {
4025 lock_ReleaseWrite(&newDscp->rw);
4027 if (bTargetExists && !oneDir) {
4028 diropCode = cm_DirDeleteEntry(&newDirOp, newNamep);
4030 cm_BPlusDirDeleteEntry(&newDirOp, cNewNamep);
4034 cm_DirCreateEntry(&newDirOp, newNamep, &fileFid);
4036 cm_BPlusDirCreateEntry(&newDirOp, cNewNamep, &fileFid);
4038 lock_ObtainWrite(&newDscp->rw);
4042 InterlockedIncrement(&newDscp->activeRPCs);
4044 cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
4045 lock_ReleaseWrite(&newDscp->rw);
4047 cm_EndDirOp(&newDirOp);
4052 * After the rename the file server has invalidated the callbacks
4053 * on the file that was moved and destroyed any target file.
4055 lock_ObtainWrite(&oldScp->rw);
4056 cm_DiscardSCache(oldScp);
4057 lock_ReleaseWrite(&oldScp->rw);
4059 if (RDR_Initialized)
4060 RDR_InvalidateObject(oldScp->fid.cell, oldScp->fid.volume, oldScp->fid.vnode, oldScp->fid.unique,
4061 oldScp->fid.hash, oldScp->fileType, AFS_INVALIDATE_CALLBACK);
4064 lock_ObtainWrite(&oldTargetScp->rw);
4065 cm_DiscardSCache(oldTargetScp);
4066 lock_ReleaseWrite(&oldTargetScp->rw);
4068 if (RDR_Initialized)
4069 RDR_InvalidateObject(oldTargetScp->fid.cell, oldTargetScp->fid.volume, oldTargetScp->fid.vnode, oldTargetScp->fid.unique,
4070 oldTargetScp->fid.hash, oldTargetScp->fileType, AFS_INVALIDATE_CALLBACK);
4076 cm_ReleaseSCache(oldScp);
4079 cm_ReleaseSCache(oldTargetScp);
4086 /* and return error code */
4090 /* Byte range locks:
4092 The OpenAFS Windows client has to fake byte range locks given no
4093 server side support for such locks. This is implemented as keyed
4094 byte range locks on the cache manager.
4096 Keyed byte range locks:
4098 Each cm_scache_t structure keeps track of a list of keyed locks.
4099 The key for a lock identifies an owner of a set of locks (referred
4100 to as a client). Each key is represented by a value. The set of
4101 key values used within a specific cm_scache_t structure form a
4102 namespace that has a scope of just that cm_scache_t structure. The
4103 same key value can be used with another cm_scache_t structure and
4104 correspond to a completely different client. However it is
4105 advantageous for the SMB or IFS layer to make sure that there is a
4106 1-1 mapping between client and keys over all cm_scache_t objects.
4108 Assume a client C has key Key(C) (although, since the scope of the
4109 key is a cm_scache_t, the key can be Key(C,S), where S is the
4110 cm_scache_t. But assume a 1-1 relation between keys and clients).
4111 A byte range (O,+L) denotes byte addresses (O) through (O+L-1)
4112 inclusive (a.k.a. [O,O+L-1]). The function Key(x) is implemented
4113 through cm_generateKey() function for both SMB and IFS.
4115 The list of locks for a cm_scache_t object S is maintained in
4116 S->fileLocks. The cache manager will set a lock on the AFS file
4117 server in order to assert the locks in S->fileLocks. If only
4118 shared locks are in place for S, then the cache manager will obtain
4119 a LockRead lock, while if there are any exclusive locks, it will
4120 obtain a LockWrite lock. If the exclusive locks are all released
4121 while the shared locks remain, then the cache manager will
4122 downgrade the lock from LockWrite to LockRead. Similarly, if an
4123 exclusive lock is obtained when only shared locks exist, then the
4124 cache manager will try to upgrade the lock from LockRead to
4127 Each lock L owned by client C maintains a key L->key such that
4128 L->key == Key(C), the effective range defined by L->LOffset and
4129 L->LLength such that the range of bytes affected by the lock is
4130 (L->LOffset, +L->LLength), a type maintained in L->LockType which
4131 is either exclusive or shared.
4135 A lock exists iff it is in S->fileLocks for some cm_scache_t
4136 S. Existing locks are in one of the following states: ACTIVE,
4137 WAITLOCK, WAITUNLOCK, LOST, DELETED.
4139 The following sections describe each lock and the associated
4142 1. ACTIVE: A lock L is ACTIVE iff the cache manager has asserted
4143 the lock with the AFS file server. This type of lock can be
4144 exercised by a client to read or write to the locked region (as
4147 1.1 ACTIVE->LOST: When the AFS file server fails to extend a
4148 server lock that was required to assert the lock. Before
4149 marking the lock as lost, the cache manager checks if the file
4150 has changed on the server. If the file has not changed, then
4151 the cache manager will attempt to obtain a new server lock
4152 that is sufficient to assert the client side locks for the
4153 file. If any of these fail, the lock is marked as LOST.
4154 Otherwise, it is left as ACTIVE.
4156 1.2 ACTIVE->DELETED: Lock is released.
4158 2. WAITLOCK: A lock is in a WAITLOCK state if the cache manager
4159 grants the lock but the lock is yet to be asserted with the AFS
4160 file server. Once the file server grants the lock, the state
4161 will transition to an ACTIVE lock.
4163 2.1 WAITLOCK->ACTIVE: The server granted the lock.
4165 2.2 WAITLOCK->DELETED: Lock is abandoned, or timed out during
4168 2.3 WAITLOCK->LOST: One or more locks from this client were
4169 marked as LOST. No further locks will be granted to this
4170 client until all lost locks are removed.
4172 3. WAITUNLOCK: A lock is in a WAITUNLOCK state if the cache manager
4173 receives a request for a lock that conflicts with an existing
4174 ACTIVE or WAITLOCK lock. The lock will be placed in the queue
4175 and will be granted at such time the conflicting locks are
4176 removed, at which point the state will transition to either
4179 3.1 WAITUNLOCK->ACTIVE: The conflicting lock was removed. The
4180 current serverLock is sufficient to assert this lock, or a
4181 sufficient serverLock is obtained.
4183 3.2 WAITUNLOCK->WAITLOCK: The conflicting lock was removed,
4184 however the required serverLock is yet to be asserted with the
4187 3.3 WAITUNLOCK->DELETED: The lock is abandoned, timed out or
4190 3.5 WAITUNLOCK->LOST: One or more locks from this client were
4191 marked as LOST. No further locks will be granted to this
4192 client until all lost locks are removed.
4194 4. LOST: A lock L is LOST if the server lock that was required to
4195 assert the lock could not be obtained or if it could not be
4196 extended, or if other locks by the same client were LOST.
4197 Essentially, once a lock is LOST, the contract between the cache
4198 manager and that specific client is no longer valid.
4200 The cache manager rechecks the server lock once every minute and
4201 extends it as appropriate. If this is not done for 5 minutes,
4202 the AFS file server will release the lock (the 5 minute timeout
4203 is based on current file server code and is fairly arbitrary).
4204 Once released, the lock cannot be re-obtained without verifying
4205 that the contents of the file hasn't been modified since the
4206 time the lock was released. Re-obtaining the lock without
4207 verifying this may lead to data corruption. If the lock can not
4208 be obtained safely, then all active locks for the cm_scache_t
4211 4.1 LOST->DELETED: The lock is released.
4213 5. DELETED: The lock is no longer relevant. Eventually, it will
4214 get removed from the cm_scache_t. In the meantime, it will be
4215 treated as if it does not exist.
4217 5.1 DELETED->not exist: The lock is removed from the
4220 The following are classifications of locks based on their state.
4222 6* A lock L is ACCEPTED if it is ACTIVE or WAITLOCK. These locks
4223 have been accepted by the cache manager, but may or may not have
4224 been granted back to the client.
4226 7* A lock L is QUEUED if it is ACTIVE, WAITLOCK or WAITUNLOCK.
4228 8* A lock L is WAITING if it is WAITLOCK or WAITUNLOCK.
4232 A client C can READ range (Offset,+Length) of a file represented by
4233 cm_scache_t S iff (1):
4235 1. for all _a_ in (Offset,+Length), all of the following is true:
4237 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
4238 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
4241 1.2 For each LOST lock L in S->fileLocks such that _a_ in
4242 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
4245 (When locks are lost on an cm_scache_t, all locks are lost. By
4246 4.2 (below), if there is an exclusive LOST lock, then there
4247 can't be any overlapping ACTIVE locks.)
4249 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
4251 2. for all _a_ in (Offset,+Length), one of the following is true:
4253 2.1 Byte _a_ of S is unowned (as specified in 1.1) AND there
4254 does not exist a LOST lock L such that _a_ in
4255 (L->LOffset,+L->LLength).
4257 2.2 Byte _a_ of S is owned by C under lock L (as specified in
4258 1.2) AND L->LockType is exclusive.
4260 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
4262 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
4265 3.1 If L->LockType is exclusive then there does NOT exist a
4266 ACCEPTED lock M in S->fileLocks such that _a_ in
4267 (M->LOffset,+M->LLength).
4269 (If we count all QUEUED locks then we hit cases such as
4270 cascading waiting locks where the locks later on in the queue
4271 can be granted without compromising file integrity. On the
4272 other hand if only ACCEPTED locks are considered, then locks
4273 that were received earlier may end up waiting for locks that
4274 were received later to be unlocked. The choice of ACCEPTED
4275 locks was made to mimic the Windows byte range lock
4278 3.2 If L->LockType is shared then for each ACCEPTED lock M in
4279 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
4280 M->LockType is shared.
4282 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
4284 4.1 M->key != Key(C)
4286 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
4287 and (M->LOffset,+M->LLength) do not intersect.
4289 (Note: If a client loses a lock, it loses all locks.
4290 Subsequently, it will not be allowed to obtain any more locks
4291 until all existing LOST locks that belong to the client are
4292 released. Once all locks are released by a single client,
4293 there exists no further contract between the client and AFS
4294 about the contents of the file, hence the client can then
4295 proceed to obtain new locks and establish a new contract.
4297 This doesn't quite work as you think it should, because most
4298 applications aren't built to deal with losing locks they
4299 thought they once had. For now, we don't have a good
4300 solution to lost locks.
4302 Also, for consistency reasons, we have to hold off on
4303 granting locks that overlap exclusive LOST locks.)
4305 A client C can only unlock locks L in S->fileLocks which have
4308 The representation and invariants are as follows:
4310 - Each cm_scache_t structure keeps:
4312 - A queue of byte-range locks (cm_scache_t::fileLocks) which
4313 are of type cm_file_lock_t.
4315 - A record of the highest server-side lock that has been
4316 obtained for this object (cm_scache_t::serverLock), which is
4317 one of (-1), LockRead, LockWrite.
4319 - A count of ACCEPTED exclusive and shared locks that are in the
4320 queue (cm_scache_t::sharedLocks and
4321 cm_scache_t::exclusiveLocks)
4323 - Each cm_file_lock_t structure keeps:
4325 - The type of lock (cm_file_lock_t::LockType)
4327 - The key associated with the lock (cm_file_lock_t::key)
4329 - The offset and length of the lock (cm_file_lock_t::LOffset
4330 and cm_file_lock_t::LLength)
4332 - The state of the lock.
4334 - Time of issuance or last successful extension
4336 Semantic invariants:
4338 I1. The number of ACCEPTED locks in S->fileLocks are
4339 (S->sharedLocks + S->exclusiveLocks)
4341 External invariants:
4343 I3. S->serverLock is the lock that we have asserted with the
4344 AFS file server for this cm_scache_t.
4346 I4. S->serverLock == LockRead iff there is at least one ACTIVE
4347 shared lock, but no ACTIVE exclusive locks.
4349 I5. S->serverLock == LockWrite iff there is at least one ACTIVE
4352 I6. If L is a LOST lock, then for each lock M in S->fileLocks,
4353 M->key == L->key IMPLIES M is LOST or DELETED.
4358 #define IS_LOCK_ACTIVE(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == 0)
4360 #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)
4362 #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)
4364 #define IS_LOCK_LOST(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_LOST)
4366 #define IS_LOCK_DELETED(lockp) (((lockp)->flags & CM_FILELOCK_FLAG_DELETED) == CM_FILELOCK_FLAG_DELETED)
4369 #define IS_LOCK_ACCEPTED(lockp) (IS_LOCK_ACTIVE(lockp) || IS_LOCK_WAITLOCK(lockp))
4372 #define IS_LOCK_CLIENTONLY(lockp) ((((lockp)->scp->flags & CM_SCACHEFLAG_RO) == CM_SCACHEFLAG_RO) || (((lockp)->flags & CM_FILELOCK_FLAG_CLIENTONLY) == CM_FILELOCK_FLAG_CLIENTONLY))
4375 #define INTERSECT_RANGE(r1,r2) (((r2).offset+(r2).length) > (r1).offset && ((r1).offset +(r1).length) > (r2).offset)
4378 #define CONTAINS_RANGE(r1,r2) (((r2).offset+(r2).length) <= ((r1).offset+(r1).length) && (r1).offset <= (r2).offset)
4380 #if defined(VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS) && !defined(LOCK_TESTING)
4381 #define SCP_SUPPORTS_BRLOCKS(scp) ((scp)->cbServerp && ((scp)->cbServerp->capabilities & VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS))
4383 #define SCP_SUPPORTS_BRLOCKS(scp) (1)
4386 #define SERVERLOCKS_ENABLED(scp) (!((scp)->flags & CM_SCACHEFLAG_RO) && cm_enableServerLocks && SCP_SUPPORTS_BRLOCKS(scp))
4388 #if defined(VICED_CAPABILITY_WRITELOCKACL)
4389 #define SCP_SUPPORTS_WRITELOCKACL(scp) ((scp)->cbServerp && ((scp->cbServerp->capabilities & VICED_CAPABILITY_WRITELOCKACL)))
4391 #define SCP_SUPPORTS_WRITELOCKACL(scp) (0)
4393 /* This should really be defined in any build that this code is being
4395 #error VICED_CAPABILITY_WRITELOCKACL not defined.
4398 static void cm_LockRangeSubtract(cm_range_t * pos, const cm_range_t * neg)
4400 afs_int64 int_begin;
4403 int_begin = max(pos->offset, neg->offset);
4404 int_end = min(pos->offset+pos->length, neg->offset+neg->length);
4406 if (int_begin < int_end) {
4407 if (int_begin == pos->offset) {
4408 pos->length = pos->offset + pos->length - int_end;
4409 pos->offset = int_end;
4410 } else if (int_end == pos->offset + pos->length) {
4411 pos->length = int_begin - pos->offset;
4414 /* We only subtract ranges if the resulting range is
4415 contiguous. If we try to support non-contigous ranges, we
4416 aren't actually improving performance. */
4420 /* Called with scp->rw held. Returns 0 if all is clear to read the
4421 specified range by the client identified by key.
4423 long cm_LockCheckRead(cm_scache_t *scp,
4424 LARGE_INTEGER LOffset,
4425 LARGE_INTEGER LLength,
4428 #ifndef ADVISORY_LOCKS
4430 cm_file_lock_t *fileLock;
4434 int substract_ranges = FALSE;
4436 range.offset = LOffset.QuadPart;
4437 range.length = LLength.QuadPart;
4441 1. for all _a_ in (Offset,+Length), all of the following is true:
4443 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
4444 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
4447 1.2 For each LOST lock L in S->fileLocks such that _a_ in
4448 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
4453 lock_ObtainRead(&cm_scacheLock);
4455 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
4457 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4459 if (INTERSECT_RANGE(range, fileLock->range)) {
4460 if (IS_LOCK_ACTIVE(fileLock)) {
4461 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4463 /* If there is an active lock for this client, it
4464 is safe to substract ranges.*/
4465 cm_LockRangeSubtract(&range, &fileLock->range);
4466 substract_ranges = TRUE;
4468 if (fileLock->lockType != LockRead) {
4469 code = CM_ERROR_LOCK_CONFLICT;
4473 /* even if the entire range is locked for reading,
4474 we still can't grant the lock at this point
4475 because the client may have lost locks. That
4476 is, unless we have already seen an active lock
4477 belonging to the client, in which case there
4478 can't be any lost locks for this client. */
4479 if (substract_ranges)
4480 cm_LockRangeSubtract(&range, &fileLock->range);
4482 } else if (IS_LOCK_LOST(fileLock) &&
4483 (cm_KeyEquals(&fileLock->key, &key, 0) || fileLock->lockType == LockWrite)) {
4484 code = CM_ERROR_BADFD;
4490 lock_ReleaseRead(&cm_scacheLock);
4492 osi_Log4(afsd_logp, "cm_LockCheckRead scp 0x%x offset %d length %d code 0x%x",
4493 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
4504 /* Called with scp->rw held. Returns 0 if all is clear to write the
4505 specified range by the client identified by key.
4507 long cm_LockCheckWrite(cm_scache_t *scp,
4508 LARGE_INTEGER LOffset,
4509 LARGE_INTEGER LLength,
4512 #ifndef ADVISORY_LOCKS
4514 cm_file_lock_t *fileLock;
4519 range.offset = LOffset.QuadPart;
4520 range.length = LLength.QuadPart;
4523 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
4525 2. for all _a_ in (Offset,+Length), one of the following is true:
4527 2.1 Byte _a_ of S is unowned AND there does not exist a LOST
4528 lock L such that _a_ in (L->LOffset,+L->LLength).
4530 2.2 Byte _a_ of S is owned by C under lock L AND L->LockType is
4534 lock_ObtainRead(&cm_scacheLock);
4536 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
4538 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4540 if (INTERSECT_RANGE(range, fileLock->range)) {
4541 if (IS_LOCK_ACTIVE(fileLock)) {
4542 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4543 if (fileLock->lockType == LockWrite) {
4545 /* if there is an active lock for this client, it
4546 is safe to substract ranges */
4547 cm_LockRangeSubtract(&range, &fileLock->range);
4549 code = CM_ERROR_LOCK_CONFLICT;
4553 code = CM_ERROR_LOCK_CONFLICT;
4556 } else if (IS_LOCK_LOST(fileLock)) {
4557 code = CM_ERROR_BADFD;
4563 lock_ReleaseRead(&cm_scacheLock);
4565 osi_Log4(afsd_logp, "cm_LockCheckWrite scp 0x%x offset %d length %d code 0x%x",
4566 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
4577 /* Called with cm_scacheLock write locked */
4578 static cm_file_lock_t * cm_GetFileLock(void) {
4581 l = (cm_file_lock_t *) cm_freeFileLocks;
4583 osi_QRemove(&cm_freeFileLocks, &l->q);
4585 l = malloc(sizeof(cm_file_lock_t));
4586 osi_assertx(l, "null cm_file_lock_t");
4589 memset(l, 0, sizeof(cm_file_lock_t));
4594 /* Called with cm_scacheLock write locked */
4595 static void cm_PutFileLock(cm_file_lock_t *l) {
4596 osi_QAdd(&cm_freeFileLocks, &l->q);
4599 /* called with scp->rw held. May release it during processing, but
4600 leaves it held on exit. */
4601 long cm_IntSetLock(cm_scache_t * scp, cm_user_t * userp, int lockType,
4607 struct rx_connection * rxconnp;
4609 afs_uint32 reqflags = reqp->flags;
4611 osi_Log2(afsd_logp, "CALL SetLock scp 0x%p for lock %d", scp, lockType);
4615 * The file server prior to 1.6.2 does not report an accurate value
4616 * and callbacks are not issued if the lock is dropped due to expiration.
4618 if ((lockType != LOCKING_ANDX_SHARED_LOCK && scp->fsLockCount != 0) ||
4619 (lockType == LOCKING_ANDX_SHARED_LOCK && scp->fsLockCount < 0))
4621 code = CM_ERROR_LOCK_NOT_GRANTED;
4622 osi_Log2(afsd_logp, "CALL SetLock FAILURE, fsLockCount %d code 0x%x", scp->fsLockCount, code);
4627 memset(&volSync, 0, sizeof(volSync));
4629 tfid.Volume = scp->fid.volume;
4630 tfid.Vnode = scp->fid.vnode;
4631 tfid.Unique = scp->fid.unique;
4634 reqp->flags |= CM_REQ_NORETRY;
4635 lock_ReleaseWrite(&scp->rw);
4638 code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4642 rxconnp = cm_GetRxConn(connp);
4643 code = RXAFS_SetLock(rxconnp, &tfid, lockType,
4645 rx_PutConnection(rxconnp);
4647 } while (cm_Analyze(connp, userp, reqp, &cfid, 1, &volSync,
4650 code = cm_MapRPCError(code, reqp);
4652 osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
4654 osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
4657 reqp->flags = reqflags;
4659 lock_ObtainWrite(&scp->rw);
4662 * The file server does not return a status structure so we must
4663 * locally track the file server lock count to the best of our
4666 if (lockType == LockWrite)
4667 scp->fsLockCount = -1;
4674 /* called with scp->rw held. Releases it during processing */
4675 long cm_IntReleaseLock(cm_scache_t * scp, cm_user_t * userp,
4681 struct rx_connection * rxconnp;
4684 if (scp->flags & CM_SCACHEFLAG_DELETED) {
4685 osi_Log1(afsd_logp, "CALL ReleaseLock on Deleted Vnode scp 0x%p", scp);
4689 memset(&volSync, 0, sizeof(volSync));
4691 tfid.Volume = scp->fid.volume;
4692 tfid.Vnode = scp->fid.vnode;
4693 tfid.Unique = scp->fid.unique;
4696 lock_ReleaseWrite(&scp->rw);
4698 osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
4701 code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4705 rxconnp = cm_GetRxConn(connp);
4706 code = RXAFS_ReleaseLock(rxconnp, &tfid, &volSync);
4707 rx_PutConnection(rxconnp);
4709 } while (cm_Analyze(connp, userp, reqp, &cfid, 1, &volSync,
4711 code = cm_MapRPCError(code, reqp);
4714 "CALL ReleaseLock FAILURE, code 0x%x", code);
4717 "CALL ReleaseLock SUCCESS");
4719 lock_ObtainWrite(&scp->rw);
4722 * The file server does not return a status structure so we must
4723 * locally track the file server lock count to the best of our
4727 if (scp->fsLockCount < 0)
4728 scp->fsLockCount = 0;
4731 return (code != CM_ERROR_BADFD ? code : 0);
4734 /* called with scp->rw held. May release it during processing, but
4735 will exit with lock held.
4739 - 0 if the user has permission to get the specified lock for the scp
4741 - CM_ERROR_NOACCESS if not
4743 Any other error from cm_SyncOp will be sent down untranslated.
4745 If CM_ERROR_NOACCESS is returned and lock_type is LockRead, then
4746 phas_insert (if non-NULL) will receive a boolean value indicating
4747 whether the user has INSERT permission or not.
4749 long cm_LockCheckPerms(cm_scache_t * scp,
4756 long code = 0, code2 = 0;
4758 /* lock permissions are slightly tricky because of the 'i' bit.
4759 If the user has PRSFS_LOCK, she can read-lock the file. If the
4760 user has PRSFS_WRITE, she can write-lock the file. However, if
4761 the user has PRSFS_INSERT, then she can write-lock new files,
4762 but not old ones. Since we don't have information about
4763 whether a file is new or not, we assume that if the user owns
4764 the scp, then she has the permissions that are granted by
4767 osi_Log3(afsd_logp, "cm_LockCheckPerms for scp[0x%p] type[%d] user[0x%p]",
4768 scp, lock_type, userp);
4770 if (lock_type == LockRead)
4771 rights |= PRSFS_LOCK;
4772 else if (lock_type == LockWrite)
4773 rights |= PRSFS_WRITE | PRSFS_LOCK;
4776 osi_assertx(FALSE, "invalid lock type");
4781 *phas_insert = FALSE;
4783 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
4784 CM_SCACHESYNC_GETSTATUS |
4785 CM_SCACHESYNC_NEEDCALLBACK);
4787 if (phas_insert && scp->creator == userp) {
4789 /* If this file was created by the user, then we check for
4790 PRSFS_INSERT. If the file server is recent enough, then
4791 this should be sufficient for her to get a write-lock (but
4792 not necessarily a read-lock). VICED_CAPABILITY_WRITELOCKACL
4793 indicates whether a file server supports getting write
4794 locks when the user only has PRSFS_INSERT.
4796 If the file was not created by the user we skip the check
4797 because the INSERT bit will not apply to this user even
4801 code2 = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
4802 CM_SCACHESYNC_GETSTATUS |
4803 CM_SCACHESYNC_NEEDCALLBACK);
4805 if (code2 == CM_ERROR_NOACCESS) {
4806 osi_Log0(afsd_logp, "cm_LockCheckPerms user has no INSERT bits");
4808 *phas_insert = TRUE;
4809 osi_Log0(afsd_logp, "cm_LockCheckPerms user has INSERT bits");
4813 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
4815 osi_Log1(afsd_logp, "cm_LockCheckPerms returning code %d", code);
4820 /* called with scp->rw held */
4821 long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
4822 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
4824 int allowWait, cm_user_t *userp, cm_req_t *reqp,
4825 cm_file_lock_t **lockpp)
4828 int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
4829 cm_file_lock_t *fileLock;
4832 int wait_unlock = FALSE;
4833 int force_client_lock = FALSE;
4835 osi_Log4(afsd_logp, "cm_Lock scp 0x%x type 0x%x offset %d length %d",
4836 scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
4837 osi_Log4(afsd_logp, "... allowWait %d key <0x%x, 0x%x, 0x%x>", allowWait,
4838 key.process_id, key.session_id, key.file_id);
4841 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
4843 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
4846 3.1 If L->LockType is exclusive then there does NOT exist a
4847 ACCEPTED lock M in S->fileLocks such that _a_ in
4848 (M->LOffset,+M->LLength).
4850 3.2 If L->LockType is shared then for each ACCEPTED lock M in
4851 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
4852 M->LockType is shared.
4854 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
4856 4.1 M->key != Key(C)
4858 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
4859 and (M->LOffset,+M->LLength) do not intersect.
4862 range.offset = LOffset.QuadPart;
4863 range.length = LLength.QuadPart;
4865 lock_ObtainRead(&cm_scacheLock);
4867 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4869 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4871 if (IS_LOCK_LOST(fileLock)) {
4872 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4873 code = CM_ERROR_BADFD;
4875 } else if (fileLock->lockType == LockWrite && INTERSECT_RANGE(range, fileLock->range)) {
4876 code = CM_ERROR_WOULDBLOCK;
4882 /* we don't need to check for deleted locks here since deleted
4883 locks are dequeued from scp->fileLocks */
4884 if (IS_LOCK_ACCEPTED(fileLock) &&
4885 INTERSECT_RANGE(range, fileLock->range)) {
4887 if ((sLockType & LOCKING_ANDX_SHARED_LOCK) == 0 ||
4888 fileLock->lockType != LockRead) {
4890 code = CM_ERROR_WOULDBLOCK;
4896 lock_ReleaseRead(&cm_scacheLock);
4898 if (code == 0 && SERVERLOCKS_ENABLED(scp)) {
4899 if (Which == scp->serverLock ||
4900 (Which == LockRead && scp->serverLock == LockWrite)) {
4904 /* we already have the lock we need */
4905 osi_Log3(afsd_logp, " we already have the correct lock. exclusives[%d], shared[%d], serverLock[%d]",
4906 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4908 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4910 /* special case: if we don't have permission to read-lock
4911 the file, then we force a clientside lock. This is to
4912 compensate for applications that obtain a read-lock for
4913 reading files off of directories that don't grant
4914 read-locks to the user. */
4915 if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4917 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4918 osi_Log0(afsd_logp, " User has no read-lock perms, but has INSERT perms.");
4921 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4922 force_client_lock = TRUE;
4926 } else if ((scp->exclusiveLocks > 0) ||
4927 (scp->sharedLocks > 0 && scp->serverLock != LockRead)) {
4930 /* We are already waiting for some other lock. We should
4931 wait for the daemon to catch up instead of generating a
4932 flood of SetLock calls. */
4933 osi_Log3(afsd_logp, " already waiting for other lock. exclusives[%d], shared[%d], serverLock[%d]",
4934 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4936 /* see if we have permission to create the lock in the
4938 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4940 code = CM_ERROR_WOULDBLOCK;
4941 else if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4943 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4945 " User has no read-lock perms, but has INSERT perms.");
4946 code = CM_ERROR_WOULDBLOCK;
4949 " User has no read-lock perms. Forcing client-side lock");
4950 force_client_lock = TRUE;
4954 /* leave any other codes as-is */
4958 int check_data_version = FALSE;
4961 /* first check if we have permission to elevate or obtain
4963 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4965 if (code == CM_ERROR_NOACCESS && Which == LockRead &&
4966 (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp))) {
4967 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4968 force_client_lock = TRUE;
4973 /* has_insert => (Which == LockRead, code == CM_ERROR_NOACCESS) */
4975 if (scp->serverLock == LockRead && Which == LockWrite) {
4977 /* We want to escalate the lock to a LockWrite.
4978 * Unfortunately that's not really possible without
4979 * letting go of the current lock. But for now we do
4983 " attempting to UPGRADE from LockRead to LockWrite.");
4985 " dataVersion on scp: %I64d", scp->dataVersion);
4987 /* we assume at this point (because scp->serverLock
4988 was valid) that we had a valid server lock. */
4989 scp->lockDataVersion = scp->dataVersion;
4990 check_data_version = TRUE;
4992 code = cm_IntReleaseLock(scp, userp, reqp);
4995 /* We couldn't release the lock */
4998 scp->serverLock = -1;
5002 /* We need to obtain a server lock of type Which in order
5003 * to assert this file lock */
5004 #ifndef AGGRESSIVE_LOCKS
5007 newLock = LockWrite;
5010 code = cm_IntSetLock(scp, userp, newLock, reqp);
5012 #ifdef AGGRESSIVE_LOCKS
5013 if ((code == CM_ERROR_WOULDBLOCK ||
5014 code == CM_ERROR_NOACCESS) && newLock != Which) {
5015 /* we wanted LockRead. We tried LockWrite. Now try
5020 osi_assertx(newLock == LockRead, "lock type not read");
5022 code = cm_IntSetLock(scp, userp, newLock, reqp);
5026 if (code == CM_ERROR_NOACCESS) {
5027 if (Which == LockRead) {
5028 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
5030 /* We requested a read-lock, but we have permission to
5031 * get a write-lock. Try that */
5033 tcode = cm_LockCheckPerms(scp, LockWrite, userp, reqp, NULL);
5036 newLock = LockWrite;
5038 osi_Log0(afsd_logp, " User has 'i' perms and the request was for a LockRead. Trying to get a LockWrite instead");
5040 code = cm_IntSetLock(scp, userp, newLock, reqp);
5043 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
5044 force_client_lock = TRUE;
5046 } else if (Which == LockWrite &&
5047 scp->creator == userp && !SCP_SUPPORTS_WRITELOCKACL(scp)) {
5050 /* Special case: if the lock request was for a
5051 * LockWrite and the user owns the file and we weren't
5052 * allowed to obtain the serverlock, we either lost a
5053 * race (the permissions changed from under us), or we
5054 * have 'i' bits, but we aren't allowed to lock the
5057 /* check if we lost a race... */
5058 tcode = cm_LockCheckPerms(scp, Which, userp, reqp, NULL);
5061 osi_Log0(afsd_logp, " User has 'i' perms but can't obtain write locks. Using client-side locks.");
5062 force_client_lock = TRUE;
5067 if (code == 0 && check_data_version &&
5068 scp->dataVersion != scp->lockDataVersion) {
5069 /* We lost a race. Although we successfully obtained
5070 * a lock, someone modified the file in between. The
5071 * locks have all been technically lost. */
5074 " Data version mismatch while upgrading lock.");
5076 " Data versions before=%I64d, after=%I64d",
5077 scp->lockDataVersion,
5080 " Releasing stale lock for scp 0x%x", scp);
5082 code = cm_IntReleaseLock(scp, userp, reqp);
5084 scp->serverLock = -1;
5086 code = CM_ERROR_INVAL;
5087 } else if (code == 0) {
5088 scp->serverLock = newLock;
5089 scp->lockDataVersion = scp->dataVersion;
5093 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
5094 scp->serverLock == -1) {
5095 /* Oops. We lost the lock. */
5096 cm_LockMarkSCacheLost(scp);
5099 } else if (code == 0) { /* server locks not enabled */
5101 " Skipping server lock for scp");
5106 if (code != 0 && !force_client_lock) {
5107 /* Special case error translations
5109 Applications don't expect certain errors from a
5110 LockFile/UnlockFile call. We need to translate some error
5111 code to codes that apps expect and handle. */
5113 /* We shouldn't actually need to handle this case since we
5114 simulate locks for RO scps anyway. */
5115 if (code == CM_ERROR_READONLY) {
5116 osi_Log0(afsd_logp, " Reinterpreting CM_ERROR_READONLY as CM_ERROR_NOACCESS");
5117 code = CM_ERROR_NOACCESS;
5121 if (code == 0 || (code == CM_ERROR_WOULDBLOCK && allowWait) ||
5122 force_client_lock) {
5124 /* clear the error if we are forcing a client lock, so we
5125 don't get confused later. */
5126 if (force_client_lock && code != CM_ERROR_WOULDBLOCK)
5131 lock_ObtainWrite(&cm_scacheLock);
5132 fileLock = cm_GetFileLock();
5134 fileLock->fid = scp->fid;
5136 fileLock->key = key;
5137 fileLock->lockType = Which;
5138 fileLock->userp = userp;
5139 fileLock->range = range;
5140 fileLock->flags = (code == 0 ? 0 :
5142 CM_FILELOCK_FLAG_WAITUNLOCK :
5143 CM_FILELOCK_FLAG_WAITLOCK));
5145 if (force_client_lock || !SERVERLOCKS_ENABLED(scp))
5146 fileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
5148 fileLock->lastUpdate = (code == 0 && !force_client_lock) ? time(NULL) : 0;
5150 osi_QAddT(&scp->fileLocksH, &scp->fileLocksT, &fileLock->fileq);
5151 cm_HoldSCacheNoLock(scp);
5152 fileLock->scp = scp;
5153 osi_QAdd(&cm_allFileLocks, &fileLock->q);
5154 lock_ReleaseWrite(&cm_scacheLock);
5160 if (IS_LOCK_CLIENTONLY(fileLock)) {
5162 } else if (IS_LOCK_ACCEPTED(fileLock)) {
5163 if (Which == LockRead)
5166 scp->exclusiveLocks++;
5170 "cm_Lock Lock added 0x%p flags 0x%x to scp [0x%p]",
5171 fileLock, fileLock->flags, scp);
5173 " exclusives[%d] shared[%d] client[%d] serverLock[%d]",
5174 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
5175 (int)(signed char) scp->serverLock);
5178 "cm_Lock Rejecting lock (code = 0x%x)", code);
5181 /* Convert from would block to lock not granted */
5182 if (code == CM_ERROR_WOULDBLOCK)
5183 code = CM_ERROR_LOCK_NOT_GRANTED;
5189 cm_IntUnlock(cm_scache_t * scp,
5195 osi_assertx(scp->sharedLocks >= 0, "scp->sharedLocks < 0");
5196 osi_assertx(scp->exclusiveLocks >= 0, "scp->exclusiveLocks < 0");
5197 osi_assertx(scp->clientLocks >= 0, "scp->clientLocks < 0");
5199 if (!SERVERLOCKS_ENABLED(scp)) {
5200 osi_Log0(afsd_logp, " Skipping server lock for scp");
5204 /* Ideally we would go through the rest of the locks to determine
5205 * if one or more locks that were formerly in WAITUNLOCK can now
5206 * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
5207 * scp->sharedLocks accordingly. However, the retrying of locks
5208 * in that manner is done cm_RetryLock() manually.
5211 if (scp->serverLock == LockWrite &&
5212 scp->exclusiveLocks == 0 &&
5213 scp->sharedLocks > 0) {
5214 /* The serverLock should be downgraded to LockRead */
5215 osi_Log0(afsd_logp, " DOWNGRADE lock from LockWrite to LockRead");
5217 /* Make sure there are no dirty buffers left. */
5218 code = cm_FSync(scp, userp, reqp, TRUE);
5220 /* since scp->serverLock looked sane, we are going to assume
5221 that we have a valid server lock. */
5222 scp->lockDataVersion = scp->dataVersion;
5223 osi_Log1(afsd_logp, " dataVersion on scp = %I64d", scp->dataVersion);
5225 /* before we downgrade, make sure that we have enough
5226 permissions to get the read lock. */
5227 code = cm_LockCheckPerms(scp, LockRead, userp, reqp, NULL);
5230 osi_Log0(afsd_logp, " SKIPPING downgrade because user doesn't have perms to get downgraded lock");
5236 code = cm_IntReleaseLock(scp, userp, reqp);
5239 /* so we couldn't release it. Just let the lock be for now */
5243 scp->serverLock = -1;
5246 code = cm_IntSetLock(scp, userp, LockRead, reqp);
5248 if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
5249 scp->serverLock = LockRead;
5250 } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
5251 /* We lost a race condition. Although we have a valid
5252 lock on the file, the data has changed and essentially
5253 we have lost the lock we had during the transition. */
5255 osi_Log0(afsd_logp, "Data version mismatch during lock downgrade");
5256 osi_Log2(afsd_logp, " Data versions before=%I64d, after=%I64d",
5257 scp->lockDataVersion,
5260 code = cm_IntReleaseLock(scp, userp, reqp);
5262 code = CM_ERROR_INVAL;
5263 scp->serverLock = -1;
5267 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
5268 (scp->serverLock == -1)) {
5270 cm_LockMarkSCacheLost(scp);
5273 /* failure here has no bearing on the return value of cm_Unlock() */
5276 } else if (scp->serverLock != (-1) &&
5277 scp->exclusiveLocks == 0 &&
5278 scp->sharedLocks == 0) {
5279 /* The serverLock should be released entirely */
5281 if (scp->serverLock == LockWrite) {
5282 osi_Log0(afsd_logp, " RELEASE LockWrite -> LockNone");
5284 /* Make sure there are no dirty buffers left. */
5285 code = cm_FSync(scp, userp, reqp, TRUE);
5287 osi_Log0(afsd_logp, " RELEASE LockRead -> LockNone");
5290 code = cm_IntReleaseLock(scp, userp, reqp);
5293 scp->serverLock = (-1);
5299 /* Called with scp->rw held */
5300 long cm_UnlockByKey(cm_scache_t * scp,
5307 cm_file_lock_t *fileLock;
5308 osi_queue_t *q, *qn;
5311 osi_Log4(afsd_logp, "cm_UnlockByKey scp 0x%p key <0x%x,0x%x,0x%x",
5312 scp, key.process_id, key.session_id, key.file_id);
5313 osi_Log1(afsd_logp, " flags=0x%x", flags);
5315 lock_ObtainWrite(&cm_scacheLock);
5317 for (q = scp->fileLocksH; q; q = qn) {
5320 fileLock = (cm_file_lock_t *)
5321 ((char *) q - offsetof(cm_file_lock_t, fileq));
5324 osi_Log4(afsd_logp, " Checking lock[0x%x] range[%d,+%d] type[%d]",
5326 (unsigned long) fileLock->range.offset,
5327 (unsigned long) fileLock->range.length,
5328 fileLock->lockType);
5329 osi_Log4(afsd_logp, " key<0x%x, 0x%x, 0x%x> flags[0x%x]",
5330 fileLock->key.process_id, fileLock->key.session_id, fileLock->key.file_id,
5333 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5334 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5335 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5337 fileLock->fid.volume,
5338 fileLock->fid.vnode,
5339 fileLock->fid.unique);
5340 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5341 fileLock->scp->fid.cell,
5342 fileLock->scp->fid.volume,
5343 fileLock->scp->fid.vnode,
5344 fileLock->scp->fid.unique);
5345 osi_assertx(FALSE, "invalid fid value");
5349 if (!IS_LOCK_DELETED(fileLock) &&
5350 cm_KeyEquals(&fileLock->key, &key, flags)) {
5351 osi_Log3(afsd_logp, "...Unlock range [%d,+%d] type %d",
5352 fileLock->range.offset,
5353 fileLock->range.length,
5354 fileLock->lockType);
5356 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
5358 if (IS_LOCK_CLIENTONLY(fileLock)) {
5360 } else if (IS_LOCK_ACCEPTED(fileLock)) {
5361 if (fileLock->lockType == LockRead)
5364 scp->exclusiveLocks--;
5367 fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5369 cm_ReleaseUser(fileLock->userp);
5370 cm_ReleaseSCacheNoLock(scp);
5372 fileLock->userp = NULL;
5373 fileLock->scp = NULL;
5379 lock_ReleaseWrite(&cm_scacheLock);
5381 if (n_unlocks == 0) {
5382 osi_Log0(afsd_logp, "cm_UnlockByKey no locks found");
5383 osi_Log3(afsd_logp, " Leaving scp with exclusives[%d], shared[%d], serverLock[%d]",
5384 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
5389 code = cm_IntUnlock(scp, userp, reqp);
5390 osi_Log1(afsd_logp, "cm_UnlockByKey code 0x%x", code);
5392 osi_Log4(afsd_logp, " Leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
5393 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
5394 (int)(signed char) scp->serverLock);
5399 /* Called with scp->rw held */
5400 long cm_Unlock(cm_scache_t *scp,
5401 unsigned char sLockType,
5402 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
5409 int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
5410 cm_file_lock_t *fileLock;
5412 int release_userp = FALSE;
5413 int exact_match = !(flags & CM_UNLOCK_FLAG_MATCH_RANGE);
5415 LARGE_INTEGER RangeEnd;
5417 osi_Log4(afsd_logp, "cm_Unlock scp 0x%p type 0x%x offset 0x%x length 0x%x",
5418 scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
5419 osi_Log4(afsd_logp, "... key <0x%x,0x%x,0x%x> flags 0x%x",
5420 key.process_id, key.session_id, key.file_id, flags);
5423 RangeEnd.QuadPart = LOffset.QuadPart + LLength.QuadPart;
5426 lock_ObtainRead(&cm_scacheLock);
5428 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5429 fileLock = (cm_file_lock_t *)
5430 ((char *) q - offsetof(cm_file_lock_t, fileq));
5433 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5434 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5435 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5437 fileLock->fid.volume,
5438 fileLock->fid.vnode,
5439 fileLock->fid.unique);
5440 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5441 fileLock->scp->fid.cell,
5442 fileLock->scp->fid.volume,
5443 fileLock->scp->fid.vnode,
5444 fileLock->scp->fid.unique);
5445 osi_assertx(FALSE, "invalid fid value");
5449 if (!IS_LOCK_DELETED(fileLock) &&
5450 cm_KeyEquals(&fileLock->key, &key, 0) &&
5451 fileLock->range.offset == LOffset.QuadPart &&
5452 fileLock->range.length == LLength.QuadPart) {
5458 if (!IS_LOCK_DELETED(fileLock) &&
5459 cm_KeyEquals(&fileLock->key, &key, 0) &&
5460 fileLock->range.offset >= LOffset.QuadPart &&
5461 fileLock->range.offset < RangeEnd.QuadPart &&
5462 (fileLock->range.offset + fileLock->range.length) <= RangeEnd.QuadPart) {
5470 lock_ReleaseRead(&cm_scacheLock);
5472 if (lock_found && !exact_match) {
5476 osi_Log0(afsd_logp, "cm_Unlock lock not found; failure");
5478 /* The lock didn't exist anyway. *shrug* */
5479 return CM_ERROR_RANGE_NOT_LOCKED;
5483 /* discard lock record */
5484 lock_ConvertRToW(&cm_scacheLock);
5485 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
5488 * Don't delete it here; let the daemon delete it, to simplify
5489 * the daemon's traversal of the list.
5492 if (IS_LOCK_CLIENTONLY(fileLock)) {
5494 } else if (IS_LOCK_ACCEPTED(fileLock)) {
5495 if (fileLock->lockType == LockRead)
5498 scp->exclusiveLocks--;
5501 fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5503 if (userp != NULL) {
5504 cm_ReleaseUser(fileLock->userp);
5506 userp = fileLock->userp;
5507 release_userp = TRUE;
5509 cm_ReleaseSCacheNoLock(scp);
5510 fileLock->userp = NULL;
5511 fileLock->scp = NULL;
5512 lock_ReleaseWrite(&cm_scacheLock);
5514 code = cm_IntUnlock(scp, userp, reqp);
5516 if (release_userp) {
5517 cm_ReleaseUser(userp);
5518 release_userp = FALSE;
5522 osi_Log1(afsd_logp, "cm_Unlock not exact match, searching for next lock, code 0x%x", code);
5523 goto try_again; /* might be more than one lock in the range */
5528 osi_Log1(afsd_logp, "cm_Unlock code 0x%x", code);
5529 osi_Log4(afsd_logp, " leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
5530 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
5531 (int)(signed char) scp->serverLock);
5536 /* called with scp->rw held */
5537 void cm_LockMarkSCacheLost(cm_scache_t * scp)
5539 cm_file_lock_t *fileLock;
5542 osi_Log1(afsd_logp, "cm_LockMarkSCacheLost scp 0x%x", scp);
5544 /* cm_scacheLock needed because we are modifying fileLock->flags */
5545 lock_ObtainWrite(&cm_scacheLock);
5547 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5549 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
5551 if (IS_LOCK_ACTIVE(fileLock) &&
5552 !IS_LOCK_CLIENTONLY(fileLock)) {
5553 if (fileLock->lockType == LockRead)
5556 scp->exclusiveLocks--;
5558 fileLock->flags |= CM_FILELOCK_FLAG_LOST;
5562 scp->serverLock = -1;
5563 scp->lockDataVersion = CM_SCACHE_VERSION_BAD;
5564 lock_ReleaseWrite(&cm_scacheLock);
5567 /* Called with no relevant locks held */
5568 void cm_CheckLocks()
5570 osi_queue_t *q, *nq;
5571 cm_file_lock_t *fileLock;
5577 struct rx_connection * rxconnp;
5580 memset(&volSync, 0, sizeof(volSync));
5584 lock_ObtainWrite(&cm_scacheLock);
5586 cm_lockRefreshCycle++;
5588 osi_Log1(afsd_logp, "cm_CheckLocks starting lock check cycle %d", cm_lockRefreshCycle);
5590 for (q = cm_allFileLocks; q; q = nq) {
5591 fileLock = (cm_file_lock_t *) q;
5595 if (IS_LOCK_DELETED(fileLock)) {
5596 cm_user_t *userp = fileLock->userp;
5597 cm_scache_t *scp = fileLock->scp;
5598 fileLock->userp = NULL;
5599 fileLock->scp = NULL;
5602 lock_ReleaseWrite(&cm_scacheLock);
5603 lock_ObtainWrite(&scp->rw);
5604 code = cm_IntUnlock(scp, userp, &req);
5605 lock_ReleaseWrite(&scp->rw);
5607 cm_ReleaseUser(userp);
5608 lock_ObtainWrite(&cm_scacheLock);
5609 cm_ReleaseSCacheNoLock(scp);
5611 osi_QRemove(&cm_allFileLocks, q);
5612 cm_PutFileLock(fileLock);
5614 } else if (IS_LOCK_ACTIVE(fileLock) && !IS_LOCK_CLIENTONLY(fileLock)) {
5616 /* Server locks must have been enabled for us to have
5617 received an active non-client-only lock. */
5618 osi_assertx(cm_enableServerLocks, "!cm_enableServerLocks");
5620 scp = fileLock->scp;
5621 osi_assertx(scp != NULL, "null cm_scache_t");
5623 cm_HoldSCacheNoLock(scp);
5626 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5627 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5628 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5630 fileLock->fid.volume,
5631 fileLock->fid.vnode,
5632 fileLock->fid.unique);
5633 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5634 fileLock->scp->fid.cell,
5635 fileLock->scp->fid.volume,
5636 fileLock->scp->fid.vnode,
5637 fileLock->scp->fid.unique);
5638 osi_assertx(FALSE, "invalid fid");
5641 /* Server locks are extended once per scp per refresh
5643 if (scp->lastRefreshCycle != cm_lockRefreshCycle) {
5645 int scp_done = FALSE;
5647 osi_Log1(afsd_logp, "cm_CheckLocks Updating scp 0x%x", scp);
5649 lock_ReleaseWrite(&cm_scacheLock);
5650 lock_ObtainWrite(&scp->rw);
5652 /* did the lock change while we weren't holding the lock? */
5653 if (!IS_LOCK_ACTIVE(fileLock))
5654 goto post_syncopdone;
5656 code = cm_SyncOp(scp, NULL, fileLock->userp, &req, 0,
5657 CM_SCACHESYNC_NEEDCALLBACK
5658 | CM_SCACHESYNC_GETSTATUS
5659 | CM_SCACHESYNC_LOCK);
5663 "cm_CheckLocks SyncOp failure code 0x%x", code);
5664 goto post_syncopdone;
5667 /* cm_SyncOp releases scp->rw during which the lock
5668 may get released. */
5669 if (!IS_LOCK_ACTIVE(fileLock))
5670 goto pre_syncopdone;
5672 if (scp->serverLock != -1 && !(scp->flags & CM_SCACHEFLAG_DELETED)) {
5676 tfid.Volume = scp->fid.volume;
5677 tfid.Vnode = scp->fid.vnode;
5678 tfid.Unique = scp->fid.unique;
5680 userp = fileLock->userp;
5682 osi_Log3(afsd_logp, "CALL ExtendLock lock 0x%p for scp=0x%p with lock %d",
5685 (int) scp->serverLock);
5687 lock_ReleaseWrite(&scp->rw);
5690 code = cm_ConnFromFID(&cfid, userp,
5695 rxconnp = cm_GetRxConn(connp);
5696 code = RXAFS_ExtendLock(rxconnp, &tfid,
5698 rx_PutConnection(rxconnp);
5700 osi_Log1(afsd_logp, " ExtendLock returns %d", code);
5702 } while (cm_Analyze(connp, userp, &req,
5703 &cfid, 1, &volSync, NULL, NULL,
5706 code = cm_MapRPCError(code, &req);
5708 lock_ObtainWrite(&scp->rw);
5711 osi_Log1(afsd_logp, "CALL ExtendLock FAILURE, code 0x%x", code);
5712 scp->fsLockCount = 0;
5714 osi_Log0(afsd_logp, "CALL ExtendLock SUCCESS");
5715 scp->lockDataVersion = scp->dataVersion;
5718 if ((code == EINVAL || code == CM_ERROR_INVAL) &&
5719 scp->lockDataVersion == scp->dataVersion) {
5723 (scp->exclusiveLocks > 0) ? LockWrite: LockRead;
5725 /* we might still have a chance to obtain a
5728 code = cm_IntSetLock(scp, userp, lockType, &req);
5731 code = CM_ERROR_INVAL;
5732 } else if (scp->lockDataVersion != scp->dataVersion) {
5734 /* now check if we still have the file at
5735 the right data version. */
5737 "Data version mismatch on scp 0x%p",
5740 " Data versions: before=%I64d, after=%I64d",
5741 scp->lockDataVersion,
5744 code = cm_IntReleaseLock(scp, userp, &req);
5746 code = CM_ERROR_INVAL;
5750 if (code == EINVAL || code == CM_ERROR_INVAL ||
5751 code == CM_ERROR_BADFD) {
5752 cm_LockMarkSCacheLost(scp);
5756 /* interestingly, we have found an active lock
5757 belonging to an scache that has no
5759 cm_LockMarkSCacheLost(scp);
5766 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5769 lock_ReleaseWrite(&scp->rw);
5771 lock_ObtainWrite(&cm_scacheLock);
5774 fileLock->lastUpdate = time(NULL);
5778 scp->lastRefreshCycle = cm_lockRefreshCycle;
5781 /* we have already refreshed the locks on this scp */
5782 fileLock->lastUpdate = time(NULL);
5785 cm_ReleaseSCacheNoLock(scp);
5787 } else if (IS_LOCK_ACTIVE(fileLock) && IS_LOCK_CLIENTONLY(fileLock)) {
5788 /* TODO: Check callbacks */
5792 lock_ReleaseWrite(&cm_scacheLock);
5793 osi_Log1(afsd_logp, "cm_CheckLocks completes lock check cycle %d", cm_lockRefreshCycle);
5796 /* NOT called with scp->rw held. */
5797 long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
5800 cm_scache_t *scp = NULL;
5801 cm_file_lock_t *fileLock;
5805 int force_client_lock = FALSE;
5806 int has_insert = FALSE;
5807 int check_data_version = FALSE;
5811 if (client_is_dead) {
5812 code = CM_ERROR_TIMEDOUT;
5816 lock_ObtainRead(&cm_scacheLock);
5818 osi_Log2(afsd_logp, "cm_RetryLock checking lock %p (scp=%p)", oldFileLock, oldFileLock->scp);
5819 osi_Log4(afsd_logp, " offset(%x:%x) length(%x:%x)",
5820 (unsigned)(oldFileLock->range.offset >> 32),
5821 (unsigned)(oldFileLock->range.offset & 0xffffffff),
5822 (unsigned)(oldFileLock->range.length >> 32),
5823 (unsigned)(oldFileLock->range.length & 0xffffffff));
5824 osi_Log4(afsd_logp, " key<0x%x,0x%x,0x%x> flags=%x",
5825 oldFileLock->key.process_id, oldFileLock->key.session_id, oldFileLock->key.file_id,
5826 (unsigned)(oldFileLock->flags));
5828 /* if the lock has already been granted, then we have nothing to do */
5829 if (IS_LOCK_ACTIVE(oldFileLock)) {
5830 lock_ReleaseRead(&cm_scacheLock);
5831 osi_Log0(afsd_logp, "cm_RetryLock lock already granted");
5835 /* we can't do anything with lost or deleted locks at the moment. */
5836 if (IS_LOCK_LOST(oldFileLock) || IS_LOCK_DELETED(oldFileLock)) {
5837 code = CM_ERROR_BADFD;
5838 osi_Log0(afsd_logp, "cm_RetryLock lock is lost or deleted");
5839 lock_ReleaseRead(&cm_scacheLock);
5843 scp = oldFileLock->scp;
5845 osi_assertx(scp != NULL, "null cm_scache_t");
5847 lock_ReleaseRead(&cm_scacheLock);
5848 lock_ObtainWrite(&scp->rw);
5850 code = cm_LockCheckPerms(scp, oldFileLock->lockType,
5854 if (code == CM_ERROR_NOACCESS && oldFileLock->lockType == LockRead) {
5855 if (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp)) {
5856 force_client_lock = TRUE;
5860 lock_ReleaseWrite(&scp->rw);
5864 lock_ObtainWrite(&cm_scacheLock);
5866 /* Check if we already have a sufficient server lock to allow this
5867 lock to go through. */
5868 if (IS_LOCK_WAITLOCK(oldFileLock) &&
5869 (!SERVERLOCKS_ENABLED(scp) ||
5870 scp->serverLock == oldFileLock->lockType ||
5871 scp->serverLock == LockWrite)) {
5873 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5875 if (SERVERLOCKS_ENABLED(scp)) {
5876 osi_Log1(afsd_logp, "cm_RetryLock Server lock (%d) is sufficient for lock. Granting",
5877 (int) scp->serverLock);
5879 osi_Log0(afsd_logp, "cm_RetryLock skipping server lock for scp");
5882 lock_ReleaseWrite(&cm_scacheLock);
5883 lock_ReleaseWrite(&scp->rw);
5888 if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5890 /* check if the conflicting locks have dissappeared already */
5891 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5893 fileLock = (cm_file_lock_t *)
5894 ((char *) q - offsetof(cm_file_lock_t, fileq));
5896 if (IS_LOCK_LOST(fileLock)) {
5897 if (cm_KeyEquals(&fileLock->key, &oldFileLock->key, 0)) {
5898 code = CM_ERROR_BADFD;
5899 oldFileLock->flags |= CM_FILELOCK_FLAG_LOST;
5900 osi_Log1(afsd_logp, " found lost lock %p for same key. Marking lock as lost",
5903 } else if (fileLock->lockType == LockWrite &&
5904 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5905 osi_Log1(afsd_logp, " found conflicting LOST lock %p", fileLock);
5906 code = CM_ERROR_WOULDBLOCK;
5911 if (IS_LOCK_ACCEPTED(fileLock) &&
5912 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5914 if (oldFileLock->lockType != LockRead ||
5915 fileLock->lockType != LockRead) {
5917 osi_Log1(afsd_logp, " found conflicting lock %p", fileLock);
5918 code = CM_ERROR_WOULDBLOCK;
5926 lock_ReleaseWrite(&cm_scacheLock);
5927 lock_ReleaseWrite(&scp->rw);
5932 /* when we get here, the lock is either a WAITUNLOCK or WAITLOCK.
5933 If it is WAITUNLOCK, then we didn't find any conflicting lock
5934 but we haven't verfied whether the serverLock is sufficient to
5935 assert it. If it is WAITLOCK, then the serverLock is
5936 insufficient to assert it. Eitherway, we are ready to accept
5937 the lock as either ACTIVE or WAITLOCK depending on the
5940 /* First, promote the WAITUNLOCK to a WAITLOCK */
5941 if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5942 if (oldFileLock->lockType == LockRead)
5945 scp->exclusiveLocks++;
5947 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITUNLOCK;
5948 oldFileLock->flags |= CM_FILELOCK_FLAG_WAITLOCK;
5951 osi_assertx(IS_LOCK_WAITLOCK(oldFileLock), "!IS_LOCK_WAITLOCK");
5953 if (force_client_lock ||
5954 !SERVERLOCKS_ENABLED(scp) ||
5955 scp->serverLock == oldFileLock->lockType ||
5956 (oldFileLock->lockType == LockRead &&
5957 scp->serverLock == LockWrite)) {
5959 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5961 if ((force_client_lock ||
5962 !SERVERLOCKS_ENABLED(scp)) &&
5963 !IS_LOCK_CLIENTONLY(oldFileLock)) {
5965 oldFileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
5967 if (oldFileLock->lockType == LockRead)
5970 scp->exclusiveLocks--;
5975 lock_ReleaseWrite(&cm_scacheLock);
5976 lock_ReleaseWrite(&scp->rw);
5983 code = cm_SyncOp(scp, NULL, oldFileLock->userp, &req, 0,
5984 CM_SCACHESYNC_NEEDCALLBACK
5985 | CM_SCACHESYNC_GETSTATUS
5986 | CM_SCACHESYNC_LOCK);
5988 osi_Log1(afsd_logp, "cm_RetryLock SyncOp failure code 0x%x", code);
5989 lock_ReleaseWrite(&cm_scacheLock);
5990 goto post_syncopdone;
5993 if (!IS_LOCK_WAITLOCK(oldFileLock))
5994 goto pre_syncopdone;
5996 userp = oldFileLock->userp;
5998 #ifndef AGGRESSIVE_LOCKS
5999 newLock = oldFileLock->lockType;
6001 newLock = LockWrite;
6005 /* if has_insert is non-zero, then:
6006 - the lock a LockRead
6007 - we don't have permission to get a LockRead
6008 - we do have permission to get a LockWrite
6009 - the server supports VICED_CAPABILITY_WRITELOCKACL
6012 newLock = LockWrite;
6015 lock_ReleaseWrite(&cm_scacheLock);
6017 /* when we get here, either we have a read-lock and want a
6018 write-lock or we don't have any locks and we want some
6021 if (scp->serverLock == LockRead) {
6023 osi_assertx(newLock == LockWrite, "!LockWrite");
6025 osi_Log0(afsd_logp, " Attempting to UPGRADE from LockRead to LockWrite");
6027 scp->lockDataVersion = scp->dataVersion;
6028 check_data_version = TRUE;
6030 code = cm_IntReleaseLock(scp, userp, &req);
6033 goto pre_syncopdone;
6035 scp->serverLock = -1;
6038 code = cm_IntSetLock(scp, userp, newLock, &req);
6041 if (scp->dataVersion != scp->lockDataVersion) {
6042 /* we lost a race. too bad */
6045 " Data version mismatch while upgrading lock.");
6047 " Data versions before=%I64d, after=%I64d",
6048 scp->lockDataVersion,
6051 " Releasing stale lock for scp 0x%x", scp);
6053 code = cm_IntReleaseLock(scp, userp, &req);
6055 scp->serverLock = -1;
6057 code = CM_ERROR_INVAL;
6059 cm_LockMarkSCacheLost(scp);
6061 scp->serverLock = newLock;
6066 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
6072 if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
6073 lock_ObtainWrite(&cm_scacheLock);
6074 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, &oldFileLock->fileq);
6075 lock_ReleaseWrite(&cm_scacheLock);
6077 lock_ReleaseWrite(&scp->rw);
6080 lock_ObtainWrite(&cm_scacheLock);
6082 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
6083 } else if (code != CM_ERROR_WOULDBLOCK) {
6084 oldFileLock->flags |= CM_FILELOCK_FLAG_DELETED;
6085 cm_ReleaseUser(oldFileLock->userp);
6086 oldFileLock->userp = NULL;
6087 if (oldFileLock->scp) {
6088 cm_ReleaseSCacheNoLock(oldFileLock->scp);
6089 oldFileLock->scp = NULL;
6092 lock_ReleaseWrite(&cm_scacheLock);
6097 cm_key_t cm_GenerateKey(afs_uint16 session_id, afs_offs_t process_id, afs_uint64 file_id)
6101 key.process_id = process_id;
6102 key.session_id = session_id;
6103 key.file_id = file_id;
6108 int cm_KeyEquals(cm_key_t *k1, cm_key_t *k2, int flags)
6110 return (k1->session_id == k2->session_id) && (k1->file_id == k2->file_id) &&
6111 ((flags & CM_UNLOCK_FLAG_BY_FID) || (k1->process_id == k2->process_id));
6114 void cm_ReleaseAllLocks(void)
6120 cm_file_lock_t *fileLock;
6123 for (i = 0; i < cm_data.scacheHashTableSize; i++)
6125 for ( scp = cm_data.scacheHashTablep[i]; scp; scp = scp->nextp ) {
6126 while (scp->fileLocksH != NULL) {
6127 lock_ObtainWrite(&scp->rw);
6128 lock_ObtainWrite(&cm_scacheLock);
6129 if (!scp->fileLocksH) {
6130 lock_ReleaseWrite(&cm_scacheLock);
6131 lock_ReleaseWrite(&scp->rw);
6134 fileLock = (cm_file_lock_t *)((char *) scp->fileLocksH - offsetof(cm_file_lock_t, fileq));
6135 userp = fileLock->userp;
6137 key = fileLock->key;
6138 cm_HoldSCacheNoLock(scp);
6139 lock_ReleaseWrite(&cm_scacheLock);
6140 cm_UnlockByKey(scp, key, 0, userp, &req);
6141 cm_ReleaseSCache(scp);
6142 cm_ReleaseUser(userp);
6143 lock_ReleaseWrite(&scp->rw);