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, NULL, 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 cm_fid_t fids[MAX_FID_COUNT]; /* array of fids processed in this path walk */
1973 int fid_count = 0; /* number of fids processed in this path walk */
1978 #ifdef DEBUG_REFCOUNT
1979 afsi_log("%s:%d cm_NameI rootscp 0x%p ref %d", file, line, rootSCachep, rootSCachep->refCount);
1980 osi_Log4(afsd_logp,"cm_NameI rootscp 0x%p path %S tidpath %S flags 0x%x",
1981 rootSCachep, pathp ? pathp : L"<NULL>", tidPathp ? tidPathp : L"<NULL>",
1996 cm_HoldSCache(tscp);
2004 /* map Unix slashes into DOS ones so we can interpret Unix
2010 if (!haveComponent) {
2013 } else if (tc == 0) {
2027 /* we have a component here */
2028 if (tc == 0 || tc == '\\') {
2029 /* end of the component; we're at the last
2030 * component if tc == 0. However, if the last
2031 * is a symlink, we have more to do.
2033 *cp++ = 0; /* add null termination */
2035 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
2036 extraFlag = CM_FLAG_NOMOUNTCHASE;
2037 code = cm_Lookup(tscp, component,
2039 userp, reqp, &nscp);
2042 if (!cm_ClientStrCmp(component,_C("..")) ||
2043 !cm_ClientStrCmp(component,_C("."))) {
2045 * roll back the fid list until we find the
2046 * fid that matches where we are now. Its not
2047 * necessarily one or two fids because they
2048 * might have been symlinks or mount points or
2049 * both that were crossed.
2051 for ( i=fid_count-1; i>=0; i--) {
2052 if (!cm_FidCmp(&nscp->fid, &fids[i]))
2057 /* add the new fid to the list */
2058 if (fid_count == MAX_FID_COUNT) {
2059 code = CM_ERROR_TOO_MANY_SYMLINKS;
2060 cm_ReleaseSCache(nscp);
2064 fids[fid_count++] = nscp->fid;
2069 cm_ReleaseSCache(tscp);
2071 cm_ReleaseSCache(dirScp);
2074 if ((code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH) &&
2075 tscp->fileType == CM_SCACHETYPE_SYMLINK) {
2076 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_NOSUCHPATH");
2077 return CM_ERROR_NOSUCHPATH;
2079 osi_Log1(afsd_logp,"cm_NameI code 0x%x", code);
2084 haveComponent = 0; /* component done */
2086 cm_ReleaseSCache(dirScp);
2087 dirScp = tscp; /* for some symlinks */
2088 tscp = nscp; /* already held */
2090 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
2093 cm_ReleaseSCache(dirScp);
2099 /* now, if tscp is a symlink, we should follow it and
2100 * assemble the path again.
2102 lock_ObtainWrite(&tscp->rw);
2103 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
2104 CM_SCACHESYNC_GETSTATUS
2105 | CM_SCACHESYNC_NEEDCALLBACK);
2107 lock_ReleaseWrite(&tscp->rw);
2108 cm_ReleaseSCache(tscp);
2111 cm_ReleaseSCache(dirScp);
2116 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2118 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
2119 /* this is a symlink; assemble a new buffer */
2120 lock_ReleaseWrite(&tscp->rw);
2121 if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
2122 cm_ReleaseSCache(tscp);
2125 cm_ReleaseSCache(dirScp);
2130 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_TOO_MANY_SYMLINKS");
2131 return CM_ERROR_TOO_MANY_SYMLINKS;
2141 /* TODO: make this better */
2142 frestp = cm_ClientStringToFsStringAlloc(restp, -1, NULL);
2143 code = cm_AssembleLink(tscp, frestp, &linkScp, &tempsp, userp, reqp);
2147 if (code == 0 && linkScp != NULL) {
2148 if (linkScp == cm_data.rootSCachep) {
2152 for ( i=0; i<fid_count; i++) {
2153 if ( !cm_FidCmp(&linkScp->fid, &fids[i]) ) {
2154 code = CM_ERROR_TOO_MANY_SYMLINKS;
2155 cm_ReleaseSCache(linkScp);
2161 if (i == fid_count && fid_count < MAX_FID_COUNT) {
2162 fids[fid_count++] = linkScp->fid;
2167 /* something went wrong */
2168 cm_ReleaseSCache(tscp);
2171 cm_ReleaseSCache(dirScp);
2177 /* otherwise, tempsp has the new path,
2178 * and linkScp is the new root from
2179 * which to interpret that path.
2180 * Continue with the namei processing,
2181 * also doing the bookkeeping for the
2182 * space allocation and tracking the
2183 * vnode reference counts.
2189 cm_ReleaseSCache(tscp);
2194 * now, if linkScp is null, that's
2195 * AssembleLink's way of telling us that
2196 * the sym link is relative to the dir
2197 * containing the link. We have a ref
2198 * to it in dirScp, and we hold it now
2199 * and reuse it as the new spot in the
2207 /* not a symlink, we may be done */
2208 lock_ReleaseWrite(&tscp->rw);
2216 cm_ReleaseSCache(dirScp);
2224 cm_ReleaseSCache(dirScp);
2227 } /* end of a component */
2230 } /* we have a component */
2231 } /* big while loop over all components */
2235 cm_ReleaseSCache(dirScp);
2241 cm_ReleaseSCache(tscp);
2243 #ifdef DEBUG_REFCOUNT
2244 afsi_log("%s:%d cm_NameI code 0x%x outScpp 0x%p ref %d", file, line, code, *outScpp, (*outScpp) ? (*outScpp)->refCount : 0);
2246 osi_Log2(afsd_logp,"cm_NameI code 0x%x outScpp 0x%p", code, *outScpp);
2250 /* called with a dir, and a vnode within the dir that happens to be a symlink.
2251 * We chase the link, and return a held pointer to the target, if it exists,
2252 * in *outScpp. If we succeed, we return 0, otherwise we return an error code
2253 * and do not hold or return a target vnode.
2255 * This is very similar to calling cm_NameI with the last component of a name,
2256 * which happens to be a symlink, except that we've already passed by the name.
2258 * This function is typically called by the directory listing functions, which
2259 * encounter symlinks but need to return the proper file length so programs
2260 * like "more" work properly when they make use of the attributes retrieved from
2263 * The input vnode should not be locked when this function is called.
2265 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
2266 cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
2270 cm_scache_t *newRootScp;
2274 osi_Log1(afsd_logp, "Evaluating symlink scp 0x%p", linkScp);
2276 code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
2280 /* now, if newRootScp is NULL, we're really being told that the symlink
2281 * is relative to the current directory (dscp).
2283 if (newRootScp == NULL) {
2285 cm_HoldSCache(dscp);
2288 code = cm_NameI(newRootScp, spacep->wdata,
2289 CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
2290 userp, NULL, reqp, outScpp);
2292 if (code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH)
2293 code = CM_ERROR_NOSUCHPATH;
2295 /* this stuff is allocated no matter what happened on the namei call,
2297 cm_FreeSpace(spacep);
2298 cm_ReleaseSCache(newRootScp);
2300 if (linkScp == *outScpp) {
2301 cm_ReleaseSCache(*outScpp);
2303 code = CM_ERROR_NOSUCHPATH;
2309 /* for a given entry, make sure that it isn't in the stat cache, and then
2310 * add it to the list of file IDs to be obtained.
2312 * Don't bother adding it if we already have a vnode. Note that the dir
2313 * is locked, so we have to be careful checking the vnode we're thinking of
2314 * processing, to avoid deadlocks.
2316 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
2327 /* Don't overflow bsp. */
2328 if (bsp->counter >= CM_BULKMAX)
2329 return CM_ERROR_STOPNOW;
2331 thyper.LowPart = cm_data.buf_blockSize;
2332 thyper.HighPart = 0;
2333 thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
2335 /* thyper is now the first byte past the end of the record we're
2336 * interested in, and bsp->bufOffset is the first byte of the record
2337 * we're interested in.
2338 * Skip data in the others.
2341 if (LargeIntegerLessThan(*offp, bsp->bufOffset))
2343 if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
2344 return CM_ERROR_STOPNOW;
2345 if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
2348 cm_SetFid(&tfid, scp->fid.cell, scp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
2349 tscp = cm_FindSCache(&tfid);
2351 if (lock_TryWrite(&tscp->rw)) {
2352 /* we have an entry that we can look at */
2353 if (!cm_EAccesFindEntry(bsp->userp, &tscp->fid) && cm_HaveCallback(tscp)) {
2354 /* we have a callback on it. Don't bother
2355 * fetching this stat entry, since we're happy
2356 * with the info we have.
2358 lock_ReleaseWrite(&tscp->rw);
2359 cm_ReleaseSCache(tscp);
2362 lock_ReleaseWrite(&tscp->rw);
2364 cm_ReleaseSCache(tscp);
2367 #ifdef AFS_FREELANCE_CLIENT
2368 // yj: if this is a mountpoint under root.afs then we don't want it
2369 // to be bulkstat-ed, instead, we call getSCache directly and under
2370 // getSCache, it is handled specially.
2371 if ( cm_freelanceEnabled &&
2372 tfid.cell==AFS_FAKE_ROOT_CELL_ID &&
2373 tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
2374 !(tfid.vnode==0x1 && tfid.unique==0x1) )
2376 osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
2377 return cm_GetSCache(&tfid, NULL, &tscp, NULL, NULL);
2379 #endif /* AFS_FREELANCE_CLIENT */
2382 bsp->fids[i].Volume = scp->fid.volume;
2383 bsp->fids[i].Vnode = tfid.vnode;
2384 bsp->fids[i].Unique = tfid.unique;
2389 cm_TryBulkStatRPC(cm_scache_t *dscp, cm_bulkStat_t *bbp, cm_user_t *userp, cm_req_t *reqp)
2392 AFSCBFids fidStruct;
2393 AFSBulkStats statStruct;
2395 AFSCBs callbackStruct;
2398 cm_callbackRequest_t cbReq;
2405 struct rx_connection * rxconnp;
2406 int inlinebulk; /* Did we use InlineBulkStatus RPC or not? */
2408 memset(&volSync, 0, sizeof(volSync));
2410 /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
2411 * make the calls to create the entries. Handle AFSCBMAX files at a
2414 for (filex = 0; filex < bbp->counter; filex += filesThisCall) {
2415 filesThisCall = bbp->counter - filex;
2416 if (filesThisCall > AFSCBMAX)
2417 filesThisCall = AFSCBMAX;
2419 fidStruct.AFSCBFids_len = filesThisCall;
2420 fidStruct.AFSCBFids_val = &bbp->fids[filex];
2421 statStruct.AFSBulkStats_len = filesThisCall;
2422 statStruct.AFSBulkStats_val = &bbp->stats[filex];
2423 callbackStruct.AFSCBs_len = filesThisCall;
2424 callbackStruct.AFSCBs_val = &bbp->callbacks[filex];
2425 cm_StartCallbackGrantingCall(NULL, &cbReq);
2426 osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
2429 * Whenever cm_Analyze is called for a RXAFS_ RPC there must
2430 * be a FID provided. However, the error code from RXAFS_BulkStatus
2431 * or RXAFS_InlinkBulkStatus does not apply to any FID. Therefore,
2432 * we generate an invalid FID to match with the RPC error.
2434 cm_SetFid(&tfid, dscp->fid.cell, dscp->fid.volume, 0, 0);
2439 code = cm_ConnFromFID(&tfid, userp, reqp, &connp);
2443 rxconnp = cm_GetRxConn(connp);
2444 if (!(connp->serverp->flags & CM_SERVERFLAG_NOINLINEBULK)) {
2445 code = RXAFS_InlineBulkStatus(rxconnp, &fidStruct,
2446 &statStruct, &callbackStruct, &volSync);
2447 if (code == RXGEN_OPCODE) {
2448 cm_SetServerNoInlineBulk(connp->serverp, 0);
2454 code = RXAFS_BulkStatus(rxconnp, &fidStruct,
2455 &statStruct, &callbackStruct, &volSync);
2457 rx_PutConnection(rxconnp);
2460 * If InlineBulk RPC was called and it succeeded,
2461 * then pull out the return code from the status info
2462 * and use it for cm_Analyze so that we can failover to other
2463 * .readonly volume instances. But only do it for errors that
2464 * are volume global.
2466 if (inlinebulk && code == 0 && (&bbp->stats[0])->errorCode) {
2467 osi_Log1(afsd_logp, "cm_TryBulkStat inline-bulk stat error: %d",
2468 (&bbp->stats[0])->errorCode);
2469 switch ((&bbp->stats[0])->errorCode) {
2478 code = (&bbp->stats[0])->errorCode;
2481 /* Rx and Rxkad errors are volume global */
2482 if ( (&bbp->stats[0])->errorCode >= -64 && (&bbp->stats[0])->errorCode < 0 ||
2483 (&bbp->stats[0])->errorCode >= ERROR_TABLE_BASE_RXK && (&bbp->stats[0])->errorCode < ERROR_TABLE_BASE_RXK + 256)
2484 code = (&bbp->stats[0])->errorCode;
2487 } while (cm_Analyze(connp, userp, reqp, &tfid, NULL, 0, &volSync, NULL, &cbReq, code));
2488 code = cm_MapRPCError(code, reqp);
2491 * might as well quit on an error, since we're not going to do
2492 * much better on the next immediate call, either.
2495 osi_Log2(afsd_logp, "CALL %sBulkStatus FAILURE code 0x%x",
2496 inlinebulk ? "Inline" : "", code);
2497 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2502 * The bulk RPC has succeeded or at least not failed with a
2503 * volume global error result. For items that have inlineBulk
2504 * errors we must call cm_Analyze in order to perform required
2505 * logging of errors.
2507 * If the RPC was not inline bulk or the entry either has no error
2508 * the status must be merged.
2510 osi_Log1(afsd_logp, "CALL %sBulkStatus SUCCESS", inlinebulk ? "Inline" : "");
2512 for (i = 0; i<filesThisCall; i++) {
2514 cm_SetFid(&tfid, dscp->fid.cell, bbp->fids[j].Volume, bbp->fids[j].Vnode, bbp->fids[j].Unique);
2516 if (inlinebulk && (&bbp->stats[j])->errorCode) {
2517 cm_req_t treq = *reqp;
2518 cm_Analyze(NULL, userp, &treq, &tfid, NULL, 0, &volSync, NULL, &cbReq, (&bbp->stats[j])->errorCode);
2519 switch ((&bbp->stats[j])->errorCode) {
2524 cm_EAccesAddEntry(userp, &tfid, &dscp->fid);
2527 code = cm_GetSCache(&tfid, &dscp->fid, &scp, userp, reqp);
2532 * otherwise, if this entry has no callback info,
2533 * merge in this. If there is existing callback info
2534 * we skip the merge because the existing data must be
2535 * current (we have a callback) and the response from
2536 * a non-inline bulk rpc might actually be wrong.
2538 * now, we have to be extra paranoid on merging in this
2539 * information, since we didn't use cm_SyncOp before
2540 * starting the fetch to make sure that no bad races
2541 * were occurring. Specifically, we need to make sure
2542 * we don't obliterate any newer information in the
2543 * vnode than have here.
2545 * Right now, be pretty conservative: if there's a
2546 * callback or a pending call, skip it.
2547 * However, if the prior attempt to obtain status
2548 * was refused access or the volume is .readonly,
2549 * take the data in any case since we have nothing
2550 * better for the in flight directory enumeration that
2551 * resulted in this function being called.
2553 lock_ObtainRead(&scp->rw);
2554 if ((scp->cbServerp == NULL &&
2555 !(scp->flags & (CM_SCACHEFLAG_FETCHING | CM_SCACHEFLAG_STORING | CM_SCACHEFLAG_SIZESTORING))) ||
2556 (scp->flags & CM_SCACHEFLAG_PURERO) ||
2557 cm_EAccesFindEntry(userp, &scp->fid))
2559 lock_ConvertRToW(&scp->rw);
2560 lostRace = cm_EndCallbackGrantingCall(scp, &cbReq,
2563 CM_CALLBACK_MAINTAINCOUNT);
2564 InterlockedIncrement(&scp->activeRPCs);
2566 cm_MergeStatus(dscp, scp, &bbp->stats[j], &volSync, userp, reqp, 0);
2567 lock_ReleaseWrite(&scp->rw);
2569 lock_ReleaseRead(&scp->rw);
2571 cm_ReleaseSCache(scp);
2573 } /* all files in the response */
2574 /* now tell it to drop the count,
2575 * after doing the vnode processing above */
2576 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2577 } /* while there are still more files to process */
2582 /* called with a write locked scp and a pointer to a buffer. Make bulk stat
2583 * calls on all undeleted files in the page of the directory specified.
2586 cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
2592 osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%p", dscp);
2594 /* should be on a buffer boundary */
2595 osi_assertx((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0, "invalid offset");
2597 bbp = malloc(sizeof(cm_bulkStat_t));
2598 memset(bbp, 0, sizeof(cm_bulkStat_t));
2600 bbp->bufOffset = *offsetp;
2602 lock_ReleaseWrite(&dscp->rw);
2603 /* first, assemble the file IDs we need to stat */
2604 code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) bbp, offsetp, userp, reqp, NULL);
2606 /* if we failed, bail out early */
2607 if (code && code != CM_ERROR_STOPNOW) {
2609 lock_ObtainWrite(&dscp->rw);
2613 code = cm_TryBulkStatRPC(dscp, bbp, userp, reqp);
2614 osi_Log1(afsd_logp, "END cm_TryBulkStat code 0x%x", code);
2616 lock_ObtainWrite(&dscp->rw);
2621 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
2625 /* initialize store back mask as inexpensive local variable */
2627 memset(statusp, 0, sizeof(AFSStoreStatus));
2629 /* copy out queued info from scache first, if scp passed in */
2631 if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
2632 statusp->ClientModTime = scp->clientModTime;
2633 mask |= AFS_SETMODTIME;
2634 scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
2639 /* now add in our locally generated request */
2640 if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
2641 statusp->ClientModTime = attrp->clientModTime;
2642 mask |= AFS_SETMODTIME;
2644 if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
2645 statusp->UnixModeBits = attrp->unixModeBits;
2646 mask |= AFS_SETMODE;
2648 if (attrp->mask & CM_ATTRMASK_OWNER) {
2649 statusp->Owner = attrp->owner;
2650 mask |= AFS_SETOWNER;
2652 if (attrp->mask & CM_ATTRMASK_GROUP) {
2653 statusp->Group = attrp->group;
2654 mask |= AFS_SETGROUP;
2657 statusp->Mask = mask;
2661 cm_IsSpaceAvailable(cm_fid_t * fidp, osi_hyper_t *sizep, cm_user_t *userp, cm_req_t *reqp)
2666 struct rx_connection * rxconnp;
2667 AFSFetchVolumeStatus volStat;
2668 cm_volume_t *volp = NULL;
2673 char volName[32]="(unknown)";
2674 char offLineMsg[256]="server temporarily inaccessible";
2675 char motd[256]="server temporarily inaccessible";
2676 osi_hyper_t freespace;
2678 if (fidp->cell==AFS_FAKE_ROOT_CELL_ID &&
2679 fidp->volume==AFS_FAKE_ROOT_VOL_ID)
2684 volp = cm_GetVolumeByFID(fidp);
2689 volType = cm_VolumeType(volp, fidp->volume);
2690 if (volType == ROVOL || volType == BACKVOL) {
2696 OfflineMsg = offLineMsg;
2700 code = cm_ConnFromFID(fidp, userp, reqp, &connp);
2703 rxconnp = cm_GetRxConn(connp);
2704 code = RXAFS_GetVolumeStatus(rxconnp, fidp->volume,
2705 &volStat, &Name, &OfflineMsg, &MOTD);
2706 rx_PutConnection(rxconnp);
2708 } while (cm_Analyze(connp, userp, reqp, fidp, NULL, 0, NULL, NULL, NULL, code));
2709 code = cm_MapRPCError(code, reqp);
2711 if (volStat.MaxQuota) {
2712 freespace.QuadPart = 1024 * (afs_int64)min(volStat.MaxQuota - volStat.BlocksInUse, volStat.PartBlocksAvail);
2714 freespace.QuadPart = 1024 * (afs_int64)volStat.PartBlocksAvail;
2716 spaceAvail = LargeIntegerGreaterThanOrEqualTo(freespace, *sizep);
2718 /* the rpc failed, assume there is space and we can fail it later. */
2727 /* set the file size, and make sure that all relevant buffers have been
2728 * truncated. Ensure that any partially truncated buffers have been zeroed
2729 * to the end of the buffer.
2731 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
2737 /* start by locking out buffer creation */
2738 lock_ObtainWrite(&scp->bufCreateLock);
2740 /* verify that this is a file, not a dir or a symlink */
2741 lock_ObtainWrite(&scp->rw);
2742 code = cm_SyncOp(scp, NULL, userp, reqp, 0,
2743 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2746 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2748 if (scp->fileType != CM_SCACHETYPE_FILE) {
2749 code = CM_ERROR_ISDIR;
2754 if (LargeIntegerLessThan(*sizep, scp->length))
2759 lock_ReleaseWrite(&scp->rw);
2761 /* can't hold scp->rw lock here, since we may wait for a storeback to
2762 * finish if the buffer package is cleaning a buffer by storing it to
2766 buf_Truncate(scp, userp, reqp, sizep);
2768 /* now ensure that file length is short enough, and update truncPos */
2769 lock_ObtainWrite(&scp->rw);
2771 /* make sure we have a callback (so we have the right value for the
2772 * length), and wait for it to be safe to do a truncate.
2774 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
2775 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2776 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2778 /* If we only have 'i' bits, then we should still be able to set
2779 the size of a file we created. */
2780 if (code == CM_ERROR_NOACCESS && scp->creator == userp) {
2781 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
2782 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2783 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2789 if (LargeIntegerLessThan(*sizep, scp->length)) {
2790 /* a real truncation. If truncPos is not set yet, or is bigger
2791 * than where we're truncating the file, set truncPos to this
2796 if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
2797 || LargeIntegerLessThan(*sizep, scp->length)) {
2799 scp->truncPos = *sizep;
2800 scp->mask |= CM_SCACHEMASK_TRUNCPOS;
2802 /* in either case, the new file size has been changed */
2803 scp->length = *sizep;
2804 scp->mask |= CM_SCACHEMASK_LENGTH;
2806 else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
2807 /* really extending the file */
2808 /* Check to see if we have sufficient quota */
2809 if (cm_IsSpaceAvailable(&scp->fid, sizep, userp, reqp)) {
2810 scp->length = *sizep;
2811 scp->mask |= CM_SCACHEMASK_LENGTH;
2813 code = CM_ERROR_SPACE;
2818 /* done successfully */
2822 cm_SyncOpDone(scp, NULL,
2823 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2824 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2827 lock_ReleaseWrite(&scp->rw);
2828 lock_ReleaseWrite(&scp->bufCreateLock);
2833 /* set the file size or other attributes (but not both at once) */
2834 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
2838 AFSFetchStatus afsOutStatus;
2842 AFSStoreStatus afsInStatus;
2843 struct rx_connection * rxconnp;
2845 memset(&volSync, 0, sizeof(volSync));
2847 /* handle file length setting */
2848 if (attrp->mask & CM_ATTRMASK_LENGTH)
2849 return cm_SetLength(scp, &attrp->length, userp, reqp);
2851 lock_ObtainWrite(&scp->rw);
2852 /* Check for RO volume */
2853 if (scp->flags & CM_SCACHEFLAG_RO) {
2854 code = CM_ERROR_READONLY;
2855 lock_ReleaseWrite(&scp->rw);
2859 /* otherwise, we have to make an RPC to get the status */
2860 code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
2862 lock_ReleaseWrite(&scp->rw);
2865 lock_ConvertWToR(&scp->rw);
2867 /* make the attr structure */
2868 cm_StatusFromAttr(&afsInStatus, scp, attrp);
2870 tfid.Volume = scp->fid.volume;
2871 tfid.Vnode = scp->fid.vnode;
2872 tfid.Unique = scp->fid.unique;
2873 lock_ReleaseRead(&scp->rw);
2875 /* now make the RPC */
2876 InterlockedIncrement(&scp->activeRPCs);
2878 osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%p", scp);
2880 code = cm_ConnFromFID(&scp->fid, userp, reqp, &connp);
2884 rxconnp = cm_GetRxConn(connp);
2885 code = RXAFS_StoreStatus(rxconnp, &tfid,
2886 &afsInStatus, &afsOutStatus, &volSync);
2887 rx_PutConnection(rxconnp);
2889 } while (cm_Analyze(connp, userp, reqp,
2890 &scp->fid, NULL, 1, &volSync, NULL, NULL, code));
2891 code = cm_MapRPCError(code, reqp);
2894 osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
2896 osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
2898 lock_ObtainWrite(&scp->rw);
2900 cm_MergeStatus(NULL, scp, &afsOutStatus, &volSync, userp, reqp,
2901 CM_MERGEFLAG_FORCE|CM_MERGEFLAG_STOREDATA);
2903 InterlockedDecrement(&scp->activeRPCs);
2904 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
2906 /* if we're changing the mode bits, discard the ACL cache,
2907 * since we changed the mode bits.
2909 if (afsInStatus.Mask & AFS_SETMODE)
2910 cm_FreeAllACLEnts(scp);
2911 lock_ReleaseWrite(&scp->rw);
2915 long cm_Create(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
2916 cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
2921 cm_callbackRequest_t cbReq;
2924 cm_scache_t *scp = NULL;
2927 AFSStoreStatus inStatus;
2928 AFSFetchStatus updatedDirStatus;
2929 AFSFetchStatus newFileStatus;
2930 AFSCallBack newFileCallback;
2932 struct rx_connection * rxconnp;
2934 fschar_t * fnamep = NULL;
2936 memset(&volSync, 0, sizeof(volSync));
2938 /* can't create names with @sys in them; must expand it manually first.
2939 * return "invalid request" if they try.
2941 if (cm_ExpandSysName(NULL, cnamep, NULL, 0, 0)) {
2942 return CM_ERROR_ATSYS;
2945 #ifdef AFS_FREELANCE_CLIENT
2946 /* Freelance root volume does not hold files */
2947 if (cm_freelanceEnabled &&
2948 dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
2949 dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
2951 return CM_ERROR_NOACCESS;
2953 #endif /* AFS_FREELANCE_CLIENT */
2955 /* Check for RO volume */
2956 if (dscp->flags & CM_SCACHEFLAG_RO)
2957 return CM_ERROR_READONLY;
2959 /* before starting the RPC, mark that we're changing the file data, so
2960 * that someone who does a chmod will know to wait until our call
2963 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
2965 lock_ObtainWrite(&dscp->rw);
2966 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2967 lock_ReleaseWrite(&dscp->rw);
2969 cm_StartCallbackGrantingCall(NULL, &cbReq);
2971 cm_EndDirOp(&dirop);
2978 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
2980 cm_StatusFromAttr(&inStatus, NULL, attrp);
2982 /* try the RPC now */
2983 InterlockedIncrement(&dscp->activeRPCs);
2984 osi_Log1(afsd_logp, "CALL CreateFile scp 0x%p", dscp);
2986 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2990 dirAFSFid.Volume = dscp->fid.volume;
2991 dirAFSFid.Vnode = dscp->fid.vnode;
2992 dirAFSFid.Unique = dscp->fid.unique;
2994 rxconnp = cm_GetRxConn(connp);
2995 code = RXAFS_CreateFile(connp->rxconnp, &dirAFSFid, fnamep,
2996 &inStatus, &newAFSFid, &newFileStatus,
2997 &updatedDirStatus, &newFileCallback,
2999 rx_PutConnection(rxconnp);
3001 } while (cm_Analyze(connp, userp, reqp,
3002 &dscp->fid, NULL, 1, &volSync, NULL, &cbReq, code));
3003 code = cm_MapRPCError(code, reqp);
3006 osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
3008 osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
3011 lock_ObtainWrite(&dirop.scp->dirlock);
3012 dirop.lockType = CM_DIRLOCK_WRITE;
3014 lock_ObtainWrite(&dscp->rw);
3016 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3017 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3018 if (cm_CheckDirOpForSingleChange(&dirop)) {
3019 lock_ReleaseWrite(&dscp->rw);
3020 cm_DirCreateEntry(&dirop, fnamep, &newFid);
3022 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
3024 lock_ObtainWrite(&dscp->rw);
3027 InterlockedDecrement(&dscp->activeRPCs);
3029 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3030 lock_ReleaseWrite(&dscp->rw);
3032 /* now try to create the file's entry, too, but be careful to
3033 * make sure that we don't merge in old info. Since we weren't locking
3034 * out any requests during the file's creation, we may have pretty old
3038 code = cm_GetSCache(&newFid, &dscp->fid, &scp, userp, reqp);
3040 lock_ObtainWrite(&scp->rw);
3041 scp->creator = userp; /* remember who created it */
3042 if (!cm_HaveCallback(scp)) {
3043 lostRace = cm_EndCallbackGrantingCall(scp, &cbReq,
3044 &newFileCallback, &volSync, 0);
3045 InterlockedIncrement(&scp->activeRPCs);
3047 cm_MergeStatus(dscp, scp, &newFileStatus, &volSync,
3051 lock_ReleaseWrite(&scp->rw);
3055 /* make sure we end things properly */
3057 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
3059 cm_EndDirOp(&dirop);
3068 cm_ReleaseSCache(scp);
3074 * locked if TRUE means write-locked
3075 * else the cm_scache_t rw must not be held
3077 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp, afs_uint32 locked)
3082 lock_ReleaseWrite(&scp->rw);
3084 osi_Log2(afsd_logp, "cm_FSync scp 0x%p userp 0x%p", scp, userp);
3086 code = buf_CleanVnode(scp, userp, reqp);
3088 lock_ObtainWrite(&scp->rw);
3090 if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
3091 | CM_SCACHEMASK_CLIENTMODTIME
3092 | CM_SCACHEMASK_LENGTH))
3093 code = cm_StoreMini(scp, userp, reqp);
3095 if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) {
3096 code = (scp->flags & CM_SCACHEFLAG_OVERQUOTA) ? CM_ERROR_QUOTA : CM_ERROR_SPACE;
3097 scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE);
3101 lock_ReleaseWrite(&scp->rw);
3102 } else if (locked) {
3103 lock_ObtainWrite(&scp->rw);
3108 long cm_MakeDir(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
3109 cm_user_t *userp, cm_req_t *reqp, cm_scache_t **scpp)
3114 cm_callbackRequest_t cbReq;
3117 cm_scache_t *scp = NULL;
3120 AFSStoreStatus inStatus;
3121 AFSFetchStatus updatedDirStatus;
3122 AFSFetchStatus newDirStatus;
3123 AFSCallBack newDirCallback;
3125 struct rx_connection * rxconnp;
3127 fschar_t * fnamep = NULL;
3129 memset(&volSync, 0, sizeof(volSync));
3131 /* can't create names with @sys in them; must expand it manually first.
3132 * return "invalid request" if they try.
3134 if (cm_ExpandSysName(NULL, cnamep, NULL, 0, 0)) {
3135 return CM_ERROR_ATSYS;
3138 #ifdef AFS_FREELANCE_CLIENT
3139 /* Freelance root volume does not hold subdirectories */
3140 if (cm_freelanceEnabled &&
3141 dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
3142 dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
3144 return CM_ERROR_NOACCESS;
3146 #endif /* AFS_FREELANCE_CLIENT */
3148 /* Check for RO volume */
3149 if (dscp->flags & CM_SCACHEFLAG_RO)
3150 return CM_ERROR_READONLY;
3152 /* before starting the RPC, mark that we're changing the directory
3153 * data, so that someone who does a chmod on the dir will wait until
3154 * our call completes.
3156 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3158 lock_ObtainWrite(&dscp->rw);
3159 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3160 lock_ReleaseWrite(&dscp->rw);
3162 cm_StartCallbackGrantingCall(NULL, &cbReq);
3164 cm_EndDirOp(&dirop);
3171 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3172 cm_StatusFromAttr(&inStatus, NULL, attrp);
3174 /* try the RPC now */
3175 InterlockedIncrement(&dscp->activeRPCs);
3176 osi_Log1(afsd_logp, "CALL MakeDir scp 0x%p", dscp);
3178 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3182 dirAFSFid.Volume = dscp->fid.volume;
3183 dirAFSFid.Vnode = dscp->fid.vnode;
3184 dirAFSFid.Unique = dscp->fid.unique;
3186 rxconnp = cm_GetRxConn(connp);
3187 code = RXAFS_MakeDir(connp->rxconnp, &dirAFSFid, fnamep,
3188 &inStatus, &newAFSFid, &newDirStatus,
3189 &updatedDirStatus, &newDirCallback,
3191 rx_PutConnection(rxconnp);
3193 } while (cm_Analyze(connp, userp, reqp,
3194 &dscp->fid, NULL, 1, &volSync, NULL, &cbReq, code));
3195 code = cm_MapRPCError(code, reqp);
3198 osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
3200 osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
3203 lock_ObtainWrite(&dirop.scp->dirlock);
3204 dirop.lockType = CM_DIRLOCK_WRITE;
3206 lock_ObtainWrite(&dscp->rw);
3208 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3209 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3210 if (cm_CheckDirOpForSingleChange(&dirop)) {
3211 lock_ReleaseWrite(&dscp->rw);
3212 cm_DirCreateEntry(&dirop, fnamep, &newFid);
3214 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
3216 lock_ObtainWrite(&dscp->rw);
3219 InterlockedDecrement(&dscp->activeRPCs);
3221 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3222 lock_ReleaseWrite(&dscp->rw);
3224 /* now try to create the new dir's entry, too, but be careful to
3225 * make sure that we don't merge in old info. Since we weren't locking
3226 * out any requests during the file's creation, we may have pretty old
3230 code = cm_GetSCache(&newFid, &dscp->fid, &scp, userp, reqp);
3232 lock_ObtainWrite(&scp->rw);
3233 if (!cm_HaveCallback(scp)) {
3234 lostRace = cm_EndCallbackGrantingCall(scp, &cbReq,
3235 &newDirCallback, &volSync, 0);
3236 InterlockedIncrement(&scp->activeRPCs);
3238 cm_MergeStatus(dscp, scp, &newDirStatus, &volSync,
3242 lock_ReleaseWrite(&scp->rw);
3246 /* make sure we end things properly */
3248 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
3250 cm_EndDirOp(&dirop);
3258 cm_ReleaseSCache(scp);
3261 /* and return error code */
3265 long cm_Link(cm_scache_t *dscp, clientchar_t *cnamep, cm_scache_t *sscp, long flags,
3266 cm_user_t *userp, cm_req_t *reqp)
3271 AFSFid existingAFSFid;
3272 AFSFetchStatus updatedDirStatus;
3273 AFSFetchStatus newLinkStatus;
3275 struct rx_connection * rxconnp;
3277 fschar_t * fnamep = NULL;
3280 memset(&volSync, 0, sizeof(volSync));
3282 if (dscp->fid.cell != sscp->fid.cell ||
3283 dscp->fid.volume != sscp->fid.volume) {
3284 return CM_ERROR_CROSSDEVLINK;
3287 /* Check for RO volume */
3288 if (dscp->flags & CM_SCACHEFLAG_RO)
3289 return CM_ERROR_READONLY;
3291 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3293 lock_ObtainWrite(&dscp->rw);
3294 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3295 lock_ReleaseWrite(&dscp->rw);
3297 cm_EndDirOp(&dirop);
3302 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3304 /* try the RPC now */
3305 InterlockedIncrement(&dscp->activeRPCs);
3306 osi_Log1(afsd_logp, "CALL Link scp 0x%p", dscp);
3308 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3311 dirAFSFid.Volume = dscp->fid.volume;
3312 dirAFSFid.Vnode = dscp->fid.vnode;
3313 dirAFSFid.Unique = dscp->fid.unique;
3315 existingAFSFid.Volume = sscp->fid.volume;
3316 existingAFSFid.Vnode = sscp->fid.vnode;
3317 existingAFSFid.Unique = sscp->fid.unique;
3319 rxconnp = cm_GetRxConn(connp);
3320 code = RXAFS_Link(rxconnp, &dirAFSFid, fnamep, &existingAFSFid,
3321 &newLinkStatus, &updatedDirStatus, &volSync);
3322 rx_PutConnection(rxconnp);
3323 osi_Log1(afsd_logp," RXAFS_Link returns 0x%x", code);
3325 } while (cm_Analyze(connp, userp, reqp, &dscp->fid, NULL, 1, &volSync, NULL, NULL, code));
3327 code = cm_MapRPCError(code, reqp);
3330 osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
3332 osi_Log0(afsd_logp, "CALL Link SUCCESS");
3335 lock_ObtainWrite(&dirop.scp->dirlock);
3336 dirop.lockType = CM_DIRLOCK_WRITE;
3338 lock_ObtainWrite(&dscp->rw);
3340 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3343 if (cm_CheckDirOpForSingleChange(&dirop)) {
3344 lock_ReleaseWrite(&dscp->rw);
3345 cm_DirCreateEntry(&dirop, fnamep, &sscp->fid);
3347 cm_BPlusDirCreateEntry(&dirop, cnamep, &sscp->fid);
3349 lock_ObtainWrite(&dscp->rw);
3352 InterlockedDecrement(&dscp->activeRPCs);
3354 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3355 lock_ReleaseWrite(&dscp->rw);
3357 cm_EndDirOp(&dirop);
3359 if (invalidate && RDR_Initialized)
3360 RDR_InvalidateObject(dscp->fid.cell, dscp->fid.volume, dscp->fid.vnode,
3361 dscp->fid.unique, dscp->fid.hash,
3362 dscp->fileType, AFS_INVALIDATE_DATA_VERSION);
3364 /* Update the linked object status */
3366 lock_ObtainWrite(&sscp->rw);
3367 InterlockedIncrement(&sscp->activeRPCs);
3368 cm_MergeStatus(NULL, sscp, &newLinkStatus, &volSync, userp, reqp, 0);
3369 lock_ReleaseWrite(&sscp->rw);
3377 long cm_SymLink(cm_scache_t *dscp, clientchar_t *cnamep, fschar_t *contentsp, long flags,
3378 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp, cm_scache_t **scpp)
3386 AFSStoreStatus inStatus;
3387 AFSFetchStatus updatedDirStatus;
3388 AFSFetchStatus newLinkStatus;
3390 struct rx_connection * rxconnp;
3392 fschar_t *fnamep = NULL;
3397 /* Check for RO volume */
3398 if (dscp->flags & CM_SCACHEFLAG_RO)
3399 return CM_ERROR_READONLY;
3401 memset(&volSync, 0, sizeof(volSync));
3403 /* before starting the RPC, mark that we're changing the directory data,
3404 * so that someone who does a chmod on the dir will wait until our
3407 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3409 lock_ObtainWrite(&dscp->rw);
3410 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3411 lock_ReleaseWrite(&dscp->rw);
3413 cm_EndDirOp(&dirop);
3418 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3420 cm_StatusFromAttr(&inStatus, NULL, attrp);
3422 /* try the RPC now */
3423 InterlockedIncrement(&dscp->activeRPCs);
3424 osi_Log1(afsd_logp, "CALL Symlink scp 0x%p", dscp);
3426 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3430 dirAFSFid.Volume = dscp->fid.volume;
3431 dirAFSFid.Vnode = dscp->fid.vnode;
3432 dirAFSFid.Unique = dscp->fid.unique;
3434 rxconnp = cm_GetRxConn(connp);
3435 code = RXAFS_Symlink(rxconnp, &dirAFSFid, fnamep, contentsp,
3436 &inStatus, &newAFSFid, &newLinkStatus,
3437 &updatedDirStatus, &volSync);
3438 rx_PutConnection(rxconnp);
3440 } while (cm_Analyze(connp, userp, reqp,
3441 &dscp->fid, NULL, 1, &volSync, NULL, NULL, code));
3442 code = cm_MapRPCError(code, reqp);
3445 osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
3447 osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
3450 lock_ObtainWrite(&dirop.scp->dirlock);
3451 dirop.lockType = CM_DIRLOCK_WRITE;
3453 lock_ObtainWrite(&dscp->rw);
3455 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3456 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3457 if (cm_CheckDirOpForSingleChange(&dirop)) {
3458 lock_ReleaseWrite(&dscp->rw);
3459 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3461 cm_DirCreateEntry(&dirop, fnamep, &newFid);
3463 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
3465 lock_ObtainWrite(&dscp->rw);
3468 InterlockedDecrement(&dscp->activeRPCs);
3470 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3471 lock_ReleaseWrite(&dscp->rw);
3473 cm_EndDirOp(&dirop);
3475 /* now try to create the new dir's entry, too, but be careful to
3476 * make sure that we don't merge in old info. Since we weren't locking
3477 * out any requests during the file's creation, we may have pretty old
3481 code = cm_GetSCache(&newFid, &dscp->fid, &scp, userp, reqp);
3483 lock_ObtainWrite(&scp->rw);
3484 if (!cm_HaveCallback(scp)) {
3485 InterlockedIncrement(&scp->activeRPCs);
3486 cm_MergeStatus(dscp, scp, &newLinkStatus, &volSync,
3489 lock_ReleaseWrite(&scp->rw);
3494 cm_ReleaseSCache(scp);
3501 /* and return error code */
3505 /*! \brief Remove a directory
3507 Encapsulates a call to RXAFS_RemoveDir().
3509 \param[in] dscp cm_scache_t for the directory containing the
3510 directory to be removed.
3512 \param[in] fnamep This will be the original name of the directory
3513 as known to the file server. It will be passed in to RXAFS_RemoveDir().
3514 This parameter is optional. If it is not provided the value
3517 \param[in] cnamep Normalized name used to update the local
3520 \param[in] userp cm_user_t for the request.
3522 \param[in] reqp Request tracker.
3524 long cm_RemoveDir(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t *cnamep, cm_user_t *userp, cm_req_t *reqp)
3530 AFSFetchStatus updatedDirStatus;
3532 struct rx_connection * rxconnp;
3534 cm_scache_t *scp = NULL;
3535 int free_fnamep = FALSE;
3537 memset(&volSync, 0, sizeof(volSync));
3539 if (fnamep == NULL) {
3542 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ,
3543 CM_DIROP_FLAG_NONE, &dirop);
3545 code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
3548 cm_EndDirOp(&dirop);
3555 code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
3559 /* Check for RO volume */
3560 if (dscp->flags & CM_SCACHEFLAG_RO) {
3561 code = CM_ERROR_READONLY;
3565 /* before starting the RPC, mark that we're changing the directory data,
3566 * so that someone who does a chmod on the dir will wait until our
3569 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3571 lock_ObtainWrite(&dscp->rw);
3572 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3573 lock_ReleaseWrite(&dscp->rw);
3575 cm_EndDirOp(&dirop);
3580 /* try the RPC now */
3581 InterlockedIncrement(&dscp->activeRPCs);
3582 osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%p", dscp);
3584 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3588 dirAFSFid.Volume = dscp->fid.volume;
3589 dirAFSFid.Vnode = dscp->fid.vnode;
3590 dirAFSFid.Unique = dscp->fid.unique;
3592 rxconnp = cm_GetRxConn(connp);
3593 code = RXAFS_RemoveDir(rxconnp, &dirAFSFid, fnamep,
3594 &updatedDirStatus, &volSync);
3595 rx_PutConnection(rxconnp);
3597 } while (cm_Analyze(connp, userp, reqp,
3598 &dscp->fid, NULL, 1, &volSync, NULL, NULL, code));
3599 code = cm_MapRPCErrorRmdir(code, reqp);
3602 osi_Log1(afsd_logp, "CALL RemoveDir FAILURE, code 0x%x", code);
3604 osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
3607 lock_ObtainWrite(&dirop.scp->dirlock);
3608 dirop.lockType = CM_DIRLOCK_WRITE;
3610 lock_ObtainWrite(&dscp->rw);
3612 cm_dnlcRemove(dscp, cnamep);
3613 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3614 if (cm_CheckDirOpForSingleChange(&dirop) && cnamep != NULL) {
3615 lock_ReleaseWrite(&dscp->rw);
3616 cm_DirDeleteEntry(&dirop, fnamep);
3618 cm_BPlusDirDeleteEntry(&dirop, cnamep);
3620 lock_ObtainWrite(&dscp->rw);
3623 InterlockedDecrement(&dscp->activeRPCs);
3625 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3626 lock_ReleaseWrite(&dscp->rw);
3628 cm_EndDirOp(&dirop);
3631 cm_ReleaseSCache(scp);
3633 lock_ObtainWrite(&scp->rw);
3634 scp->flags |= CM_SCACHEFLAG_DELETED;
3635 lock_ObtainWrite(&cm_scacheLock);
3636 cm_AdjustScacheLRU(scp);
3637 cm_RemoveSCacheFromHashTable(scp);
3638 lock_ReleaseWrite(&cm_scacheLock);
3639 lock_ReleaseWrite(&scp->rw);
3640 if (RDR_Initialized && !(reqp->flags & CM_REQ_SOURCE_REDIR) &&
3641 !RDR_InvalidateObject(scp->fid.cell, scp->fid.volume, scp->fid.vnode,
3642 scp->fid.unique, scp->fid.hash,
3643 scp->fileType, AFS_INVALIDATE_DELETED))
3644 buf_ClearRDRFlag(scp, "rmdir");
3652 /* and return error code */
3656 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
3658 /* grab mutex on contents */
3659 lock_ObtainWrite(&scp->rw);
3661 /* reset the prefetch info */
3662 scp->prefetch.base.LowPart = 0; /* base */
3663 scp->prefetch.base.HighPart = 0;
3664 scp->prefetch.end.LowPart = 0; /* and end */
3665 scp->prefetch.end.HighPart = 0;
3667 /* release mutex on contents */
3668 lock_ReleaseWrite(&scp->rw);
3674 /*! \brief Rename a file or directory
3676 Encapsulates a RXAFS_Rename() call.
3678 \param[in] oldDscp cm_scache_t for the directory containing the old
3681 \param[in] oldNamep The original old name known to the file server.
3682 This is the name that will be passed into the RXAFS_Rename().
3683 If it is not provided, it will be looked up.
3685 \param[in] normalizedOldNamep Normalized old name. This is used for
3686 updating local directory caches.
3688 \param[in] newDscp cm_scache_t for the directory containing the new
3691 \param[in] newNamep New name. Normalized.
3693 \param[in] userp cm_user_t for the request.
3695 \param[in,out] reqp Request tracker.
3698 long cm_Rename(cm_scache_t *oldDscp, fschar_t *oldNamep, clientchar_t *cOldNamep,
3699 cm_scache_t *newDscp, clientchar_t *cNewNamep, cm_user_t *userp,
3704 AFSFid oldDirAFSFid;
3705 AFSFid newDirAFSFid;
3706 AFSFetchStatus updatedOldDirStatus;
3707 AFSFetchStatus updatedNewDirStatus;
3710 int bTargetExists = 0;
3711 struct rx_connection * rxconnp;
3712 cm_dirOp_t oldDirOp;
3715 cm_dirOp_t newDirOp;
3716 fschar_t * newNamep = NULL;
3717 int free_oldNamep = FALSE;
3718 cm_scache_t *oldScp = NULL, *oldTargetScp = NULL;
3719 int rpc_skipped = 0;
3721 memset(&volSync, 0, sizeof(volSync));
3723 if (cOldNamep == NULL || cNewNamep == NULL ||
3724 cm_ClientStrLen(cOldNamep) == 0 ||
3725 cm_ClientStrLen(cNewNamep) == 0)
3726 return CM_ERROR_INVAL;
3728 /* check for identical names */
3729 if (oldDscp == newDscp &&
3730 cm_ClientStrCmp(cOldNamep, cNewNamep) == 0) {
3731 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_RENAME_IDENTICAL",
3733 return CM_ERROR_RENAME_IDENTICAL;
3736 /* Check for RO volume */
3737 if ((oldDscp->flags & CM_SCACHEFLAG_RO) || (newDscp->flags & CM_SCACHEFLAG_RO)) {
3738 return CM_ERROR_READONLY;
3741 if (oldNamep == NULL) {
3744 code = cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_READ,
3745 CM_DIROP_FLAG_NONE, &oldDirOp);
3747 code = cm_BPlusDirLookupOriginalName(&oldDirOp, cOldNamep, &oldNamep);
3749 free_oldNamep = TRUE;
3750 cm_EndDirOp(&oldDirOp);
3754 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S Original Name lookup failed",
3755 oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3760 /* before starting the RPC, mark that we're changing the directory data,
3761 * so that someone who does a chmod on the dir will wait until our call
3762 * completes. We do this in vnode order so that we don't deadlock,
3763 * which makes the code a little verbose.
3765 if (oldDscp == newDscp) {
3767 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE,
3768 CM_DIROP_FLAG_NONE, &oldDirOp);
3769 lock_ObtainWrite(&oldDscp->rw);
3770 cm_dnlcRemove(oldDscp, cOldNamep);
3771 cm_dnlcRemove(oldDscp, cNewNamep);
3772 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3773 CM_SCACHESYNC_STOREDATA);
3774 lock_ReleaseWrite(&oldDscp->rw);
3776 cm_EndDirOp(&oldDirOp);
3780 /* two distinct dir vnodes */
3782 if (oldDscp->fid.cell != newDscp->fid.cell ||
3783 oldDscp->fid.volume != newDscp->fid.volume) {
3784 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_CROSSDEVLINK",
3786 code = CM_ERROR_CROSSDEVLINK;
3790 /* shouldn't happen that we have distinct vnodes for two
3791 * different files, but could due to deliberate attack, or
3792 * stale info. Avoid deadlocks and quit now.
3794 if (oldDscp->fid.vnode == newDscp->fid.vnode) {
3795 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p vnode collision",
3797 code = CM_ERROR_CROSSDEVLINK;
3801 if (oldDscp->fid.vnode < newDscp->fid.vnode) {
3802 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE,
3803 CM_DIROP_FLAG_NONE, &oldDirOp);
3804 lock_ObtainWrite(&oldDscp->rw);
3805 cm_dnlcRemove(oldDscp, cOldNamep);
3806 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3807 CM_SCACHESYNC_STOREDATA);
3808 lock_ReleaseWrite(&oldDscp->rw);
3810 cm_EndDirOp(&oldDirOp);
3812 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE,
3813 CM_DIROP_FLAG_NONE, &newDirOp);
3814 lock_ObtainWrite(&newDscp->rw);
3815 cm_dnlcRemove(newDscp, cNewNamep);
3816 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3817 CM_SCACHESYNC_STOREDATA);
3818 lock_ReleaseWrite(&newDscp->rw);
3820 cm_EndDirOp(&newDirOp);
3822 /* cleanup first one */
3823 lock_ObtainWrite(&oldDscp->rw);
3824 cm_SyncOpDone(oldDscp, NULL,
3825 CM_SCACHESYNC_STOREDATA);
3826 lock_ReleaseWrite(&oldDscp->rw);
3827 cm_EndDirOp(&oldDirOp);
3832 /* lock the new vnode entry first */
3833 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE,
3834 CM_DIROP_FLAG_NONE, &newDirOp);
3835 lock_ObtainWrite(&newDscp->rw);
3836 cm_dnlcRemove(newDscp, cNewNamep);
3837 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3838 CM_SCACHESYNC_STOREDATA);
3839 lock_ReleaseWrite(&newDscp->rw);
3841 cm_EndDirOp(&newDirOp);
3843 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE,
3844 CM_DIROP_FLAG_NONE, &oldDirOp);
3845 lock_ObtainWrite(&oldDscp->rw);
3846 cm_dnlcRemove(oldDscp, cOldNamep);
3847 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3848 CM_SCACHESYNC_STOREDATA);
3849 lock_ReleaseWrite(&oldDscp->rw);
3851 cm_EndDirOp(&oldDirOp);
3853 /* cleanup first one */
3854 lock_ObtainWrite(&newDscp->rw);
3855 cm_SyncOpDone(newDscp, NULL,
3856 CM_SCACHESYNC_STOREDATA);
3857 lock_ReleaseWrite(&newDscp->rw);
3858 cm_EndDirOp(&newDirOp);
3862 } /* two distinct vnodes */
3868 * The source and destination directories are now locked and no other local
3869 * changes can occur.
3871 * Before we permit the operation, make sure that we do not already have
3872 * an object in the destination directory that has a case-insensitive match
3873 * for this name UNLESS the matching object is the object we are renaming.
3875 code = cm_Lookup(oldDscp, cOldNamep, 0, userp, reqp, &oldScp);
3877 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S old name lookup failed",
3878 oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3883 /* Case sensitive lookup. If this succeeds we are done. */
3884 code = cm_Lookup(newDscp, cNewNamep, 0, userp, reqp, &oldTargetScp);
3887 * Case insensitive lookup. If this succeeds, it could have found the
3888 * same file with a name that differs only by case or it could be a
3889 * different file entirely.
3891 code = cm_Lookup(newDscp, cNewNamep, CM_FLAG_CASEFOLD, userp, reqp, &oldTargetScp);
3893 /* found a matching object with the new name */
3894 if (cm_FidCmp(&oldScp->fid, &oldTargetScp->fid)) {
3895 /* and they don't match so return an error */
3896 osi_Log2(afsd_logp, "cm_Rename newDscp 0x%p cNewName %S new name already exists",
3897 newDscp, osi_LogSaveStringW(afsd_logp, cNewNamep));
3898 code = CM_ERROR_EXISTS;
3900 cm_ReleaseSCache(oldTargetScp);
3901 oldTargetScp = NULL;
3902 } else if (code == CM_ERROR_AMBIGUOUS_FILENAME) {
3903 code = CM_ERROR_EXISTS;
3905 /* The target does not exist. Clear the error and perform the rename. */
3917 newNamep = cm_ClientStringToFsStringAlloc(cNewNamep, -1, NULL);
3919 /* try the RPC now */
3920 InterlockedIncrement(&oldDscp->activeRPCs);
3922 InterlockedIncrement(&newDscp->activeRPCs);
3923 osi_Log2(afsd_logp, "CALL Rename old scp 0x%p new scp 0x%p",
3926 code = cm_ConnFromFID(&oldDscp->fid, userp, reqp, &connp);
3930 oldDirAFSFid.Volume = oldDscp->fid.volume;
3931 oldDirAFSFid.Vnode = oldDscp->fid.vnode;
3932 oldDirAFSFid.Unique = oldDscp->fid.unique;
3933 newDirAFSFid.Volume = newDscp->fid.volume;
3934 newDirAFSFid.Vnode = newDscp->fid.vnode;
3935 newDirAFSFid.Unique = newDscp->fid.unique;
3937 rxconnp = cm_GetRxConn(connp);
3938 code = RXAFS_Rename(rxconnp, &oldDirAFSFid, oldNamep,
3939 &newDirAFSFid, newNamep,
3940 &updatedOldDirStatus, &updatedNewDirStatus,
3942 rx_PutConnection(rxconnp);
3944 } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid, NULL, 1,
3945 &volSync, NULL, NULL, code));
3946 code = cm_MapRPCError(code, reqp);
3949 osi_Log1(afsd_logp, "CALL Rename FAILURE, code 0x%x", code);
3951 osi_Log0(afsd_logp, "CALL Rename SUCCESS");
3954 /* update the individual stat cache entries for the directories */
3956 lock_ObtainWrite(&oldDirOp.scp->dirlock);
3957 oldDirOp.lockType = CM_DIRLOCK_WRITE;
3960 lock_ObtainWrite(&oldDscp->rw);
3962 cm_MergeStatus(NULL, oldDscp, &updatedOldDirStatus, &volSync,
3963 userp, reqp, CM_MERGEFLAG_DIROP);
3964 if (cm_CheckDirOpForSingleChange(&oldDirOp)) {
3965 lock_ReleaseWrite(&oldDscp->rw);
3966 if (bTargetExists && oneDir) {
3967 diropCode = cm_DirDeleteEntry(&oldDirOp, newNamep);
3969 cm_BPlusDirDeleteEntry(&oldDirOp, cNewNamep);
3974 diropCode = cm_BPlusDirLookup(&oldDirOp, cOldNamep, &fileFid);
3975 if (diropCode == CM_ERROR_INEXACT_MATCH)
3977 else if (diropCode == EINVAL)
3979 diropCode = cm_DirLookup(&oldDirOp, oldNamep, &fileFid);
3981 if (diropCode == 0) {
3983 diropCode = cm_DirCreateEntry(&oldDirOp, newNamep, &fileFid);
3985 cm_BPlusDirCreateEntry(&oldDirOp, cNewNamep, &fileFid);
3989 if (diropCode == 0) {
3990 diropCode = cm_DirDeleteEntry(&oldDirOp, oldNamep);
3992 cm_BPlusDirDeleteEntry(&oldDirOp, cOldNamep);
3996 lock_ObtainWrite(&oldDscp->rw);
4000 InterlockedDecrement(&oldDscp->activeRPCs);
4002 cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
4003 lock_ReleaseWrite(&oldDscp->rw);
4005 cm_EndDirOp(&oldDirOp);
4007 /* and update it for the new one, too, if necessary */
4010 lock_ObtainWrite(&newDirOp.scp->dirlock);
4011 newDirOp.lockType = CM_DIRLOCK_WRITE;
4013 lock_ObtainWrite(&newDscp->rw);
4015 cm_MergeStatus(NULL, newDscp, &updatedNewDirStatus, &volSync,
4016 userp, reqp, CM_MERGEFLAG_DIROP);
4019 * we only make the local change if we successfully made
4020 * the change in the old directory AND there was only one
4021 * change in the new directory
4023 if (diropCode == 0 && cm_CheckDirOpForSingleChange(&newDirOp)) {
4024 lock_ReleaseWrite(&newDscp->rw);
4026 if (bTargetExists && !oneDir) {
4027 diropCode = cm_DirDeleteEntry(&newDirOp, newNamep);
4029 cm_BPlusDirDeleteEntry(&newDirOp, cNewNamep);
4033 cm_DirCreateEntry(&newDirOp, newNamep, &fileFid);
4035 cm_BPlusDirCreateEntry(&newDirOp, cNewNamep, &fileFid);
4037 lock_ObtainWrite(&newDscp->rw);
4041 InterlockedIncrement(&newDscp->activeRPCs);
4043 cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
4044 lock_ReleaseWrite(&newDscp->rw);
4046 cm_EndDirOp(&newDirOp);
4051 * After the rename the file server has invalidated the callbacks
4052 * on the file that was moved and destroyed any target file.
4054 lock_ObtainWrite(&oldScp->rw);
4055 cm_DiscardSCache(oldScp);
4056 lock_ReleaseWrite(&oldScp->rw);
4058 if (RDR_Initialized)
4059 RDR_InvalidateObject(oldScp->fid.cell, oldScp->fid.volume, oldScp->fid.vnode, oldScp->fid.unique,
4060 oldScp->fid.hash, oldScp->fileType, AFS_INVALIDATE_CALLBACK);
4063 lock_ObtainWrite(&oldTargetScp->rw);
4064 cm_DiscardSCache(oldTargetScp);
4065 lock_ReleaseWrite(&oldTargetScp->rw);
4067 if (RDR_Initialized)
4068 RDR_InvalidateObject(oldTargetScp->fid.cell, oldTargetScp->fid.volume, oldTargetScp->fid.vnode, oldTargetScp->fid.unique,
4069 oldTargetScp->fid.hash, oldTargetScp->fileType, AFS_INVALIDATE_CALLBACK);
4075 cm_ReleaseSCache(oldScp);
4078 cm_ReleaseSCache(oldTargetScp);
4085 /* and return error code */
4089 /* Byte range locks:
4091 The OpenAFS Windows client has to fake byte range locks given no
4092 server side support for such locks. This is implemented as keyed
4093 byte range locks on the cache manager.
4095 Keyed byte range locks:
4097 Each cm_scache_t structure keeps track of a list of keyed locks.
4098 The key for a lock identifies an owner of a set of locks (referred
4099 to as a client). Each key is represented by a value. The set of
4100 key values used within a specific cm_scache_t structure form a
4101 namespace that has a scope of just that cm_scache_t structure. The
4102 same key value can be used with another cm_scache_t structure and
4103 correspond to a completely different client. However it is
4104 advantageous for the SMB or IFS layer to make sure that there is a
4105 1-1 mapping between client and keys over all cm_scache_t objects.
4107 Assume a client C has key Key(C) (although, since the scope of the
4108 key is a cm_scache_t, the key can be Key(C,S), where S is the
4109 cm_scache_t. But assume a 1-1 relation between keys and clients).
4110 A byte range (O,+L) denotes byte addresses (O) through (O+L-1)
4111 inclusive (a.k.a. [O,O+L-1]). The function Key(x) is implemented
4112 through cm_generateKey() function for both SMB and IFS.
4114 The list of locks for a cm_scache_t object S is maintained in
4115 S->fileLocks. The cache manager will set a lock on the AFS file
4116 server in order to assert the locks in S->fileLocks. If only
4117 shared locks are in place for S, then the cache manager will obtain
4118 a LockRead lock, while if there are any exclusive locks, it will
4119 obtain a LockWrite lock. If the exclusive locks are all released
4120 while the shared locks remain, then the cache manager will
4121 downgrade the lock from LockWrite to LockRead. Similarly, if an
4122 exclusive lock is obtained when only shared locks exist, then the
4123 cache manager will try to upgrade the lock from LockRead to
4126 Each lock L owned by client C maintains a key L->key such that
4127 L->key == Key(C), the effective range defined by L->LOffset and
4128 L->LLength such that the range of bytes affected by the lock is
4129 (L->LOffset, +L->LLength), a type maintained in L->LockType which
4130 is either exclusive or shared.
4134 A lock exists iff it is in S->fileLocks for some cm_scache_t
4135 S. Existing locks are in one of the following states: ACTIVE,
4136 WAITLOCK, WAITUNLOCK, LOST, DELETED.
4138 The following sections describe each lock and the associated
4141 1. ACTIVE: A lock L is ACTIVE iff the cache manager has asserted
4142 the lock with the AFS file server. This type of lock can be
4143 exercised by a client to read or write to the locked region (as
4146 1.1 ACTIVE->LOST: When the AFS file server fails to extend a
4147 server lock that was required to assert the lock. Before
4148 marking the lock as lost, the cache manager checks if the file
4149 has changed on the server. If the file has not changed, then
4150 the cache manager will attempt to obtain a new server lock
4151 that is sufficient to assert the client side locks for the
4152 file. If any of these fail, the lock is marked as LOST.
4153 Otherwise, it is left as ACTIVE.
4155 1.2 ACTIVE->DELETED: Lock is released.
4157 2. WAITLOCK: A lock is in a WAITLOCK state if the cache manager
4158 grants the lock but the lock is yet to be asserted with the AFS
4159 file server. Once the file server grants the lock, the state
4160 will transition to an ACTIVE lock.
4162 2.1 WAITLOCK->ACTIVE: The server granted the lock.
4164 2.2 WAITLOCK->DELETED: Lock is abandoned, or timed out during
4167 2.3 WAITLOCK->LOST: One or more locks from this client were
4168 marked as LOST. No further locks will be granted to this
4169 client until all lost locks are removed.
4171 3. WAITUNLOCK: A lock is in a WAITUNLOCK state if the cache manager
4172 receives a request for a lock that conflicts with an existing
4173 ACTIVE or WAITLOCK lock. The lock will be placed in the queue
4174 and will be granted at such time the conflicting locks are
4175 removed, at which point the state will transition to either
4178 3.1 WAITUNLOCK->ACTIVE: The conflicting lock was removed. The
4179 current serverLock is sufficient to assert this lock, or a
4180 sufficient serverLock is obtained.
4182 3.2 WAITUNLOCK->WAITLOCK: The conflicting lock was removed,
4183 however the required serverLock is yet to be asserted with the
4186 3.3 WAITUNLOCK->DELETED: The lock is abandoned, timed out or
4189 3.5 WAITUNLOCK->LOST: One or more locks from this client were
4190 marked as LOST. No further locks will be granted to this
4191 client until all lost locks are removed.
4193 4. LOST: A lock L is LOST if the server lock that was required to
4194 assert the lock could not be obtained or if it could not be
4195 extended, or if other locks by the same client were LOST.
4196 Essentially, once a lock is LOST, the contract between the cache
4197 manager and that specific client is no longer valid.
4199 The cache manager rechecks the server lock once every minute and
4200 extends it as appropriate. If this is not done for 5 minutes,
4201 the AFS file server will release the lock (the 5 minute timeout
4202 is based on current file server code and is fairly arbitrary).
4203 Once released, the lock cannot be re-obtained without verifying
4204 that the contents of the file hasn't been modified since the
4205 time the lock was released. Re-obtaining the lock without
4206 verifying this may lead to data corruption. If the lock can not
4207 be obtained safely, then all active locks for the cm_scache_t
4210 4.1 LOST->DELETED: The lock is released.
4212 5. DELETED: The lock is no longer relevant. Eventually, it will
4213 get removed from the cm_scache_t. In the meantime, it will be
4214 treated as if it does not exist.
4216 5.1 DELETED->not exist: The lock is removed from the
4219 The following are classifications of locks based on their state.
4221 6* A lock L is ACCEPTED if it is ACTIVE or WAITLOCK. These locks
4222 have been accepted by the cache manager, but may or may not have
4223 been granted back to the client.
4225 7* A lock L is QUEUED if it is ACTIVE, WAITLOCK or WAITUNLOCK.
4227 8* A lock L is WAITING if it is WAITLOCK or WAITUNLOCK.
4231 A client C can READ range (Offset,+Length) of a file represented by
4232 cm_scache_t S iff (1):
4234 1. for all _a_ in (Offset,+Length), all of the following is true:
4236 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
4237 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
4240 1.2 For each LOST lock L in S->fileLocks such that _a_ in
4241 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
4244 (When locks are lost on an cm_scache_t, all locks are lost. By
4245 4.2 (below), if there is an exclusive LOST lock, then there
4246 can't be any overlapping ACTIVE locks.)
4248 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
4250 2. for all _a_ in (Offset,+Length), one of the following is true:
4252 2.1 Byte _a_ of S is unowned (as specified in 1.1) AND there
4253 does not exist a LOST lock L such that _a_ in
4254 (L->LOffset,+L->LLength).
4256 2.2 Byte _a_ of S is owned by C under lock L (as specified in
4257 1.2) AND L->LockType is exclusive.
4259 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
4261 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
4264 3.1 If L->LockType is exclusive then there does NOT exist a
4265 ACCEPTED lock M in S->fileLocks such that _a_ in
4266 (M->LOffset,+M->LLength).
4268 (If we count all QUEUED locks then we hit cases such as
4269 cascading waiting locks where the locks later on in the queue
4270 can be granted without compromising file integrity. On the
4271 other hand if only ACCEPTED locks are considered, then locks
4272 that were received earlier may end up waiting for locks that
4273 were received later to be unlocked. The choice of ACCEPTED
4274 locks was made to mimic the Windows byte range lock
4277 3.2 If L->LockType is shared then for each ACCEPTED lock M in
4278 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
4279 M->LockType is shared.
4281 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
4283 4.1 M->key != Key(C)
4285 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
4286 and (M->LOffset,+M->LLength) do not intersect.
4288 (Note: If a client loses a lock, it loses all locks.
4289 Subsequently, it will not be allowed to obtain any more locks
4290 until all existing LOST locks that belong to the client are
4291 released. Once all locks are released by a single client,
4292 there exists no further contract between the client and AFS
4293 about the contents of the file, hence the client can then
4294 proceed to obtain new locks and establish a new contract.
4296 This doesn't quite work as you think it should, because most
4297 applications aren't built to deal with losing locks they
4298 thought they once had. For now, we don't have a good
4299 solution to lost locks.
4301 Also, for consistency reasons, we have to hold off on
4302 granting locks that overlap exclusive LOST locks.)
4304 A client C can only unlock locks L in S->fileLocks which have
4307 The representation and invariants are as follows:
4309 - Each cm_scache_t structure keeps:
4311 - A queue of byte-range locks (cm_scache_t::fileLocks) which
4312 are of type cm_file_lock_t.
4314 - A record of the highest server-side lock that has been
4315 obtained for this object (cm_scache_t::serverLock), which is
4316 one of (-1), LockRead, LockWrite.
4318 - A count of ACCEPTED exclusive and shared locks that are in the
4319 queue (cm_scache_t::sharedLocks and
4320 cm_scache_t::exclusiveLocks)
4322 - Each cm_file_lock_t structure keeps:
4324 - The type of lock (cm_file_lock_t::LockType)
4326 - The key associated with the lock (cm_file_lock_t::key)
4328 - The offset and length of the lock (cm_file_lock_t::LOffset
4329 and cm_file_lock_t::LLength)
4331 - The state of the lock.
4333 - Time of issuance or last successful extension
4335 Semantic invariants:
4337 I1. The number of ACCEPTED locks in S->fileLocks are
4338 (S->sharedLocks + S->exclusiveLocks)
4340 External invariants:
4342 I3. S->serverLock is the lock that we have asserted with the
4343 AFS file server for this cm_scache_t.
4345 I4. S->serverLock == LockRead iff there is at least one ACTIVE
4346 shared lock, but no ACTIVE exclusive locks.
4348 I5. S->serverLock == LockWrite iff there is at least one ACTIVE
4351 I6. If L is a LOST lock, then for each lock M in S->fileLocks,
4352 M->key == L->key IMPLIES M is LOST or DELETED.
4357 #define IS_LOCK_ACTIVE(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == 0)
4359 #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)
4361 #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)
4363 #define IS_LOCK_LOST(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_LOST)
4365 #define IS_LOCK_DELETED(lockp) (((lockp)->flags & CM_FILELOCK_FLAG_DELETED) == CM_FILELOCK_FLAG_DELETED)
4368 #define IS_LOCK_ACCEPTED(lockp) (IS_LOCK_ACTIVE(lockp) || IS_LOCK_WAITLOCK(lockp))
4371 #define IS_LOCK_CLIENTONLY(lockp) ((((lockp)->scp->flags & CM_SCACHEFLAG_RO) == CM_SCACHEFLAG_RO) || (((lockp)->flags & CM_FILELOCK_FLAG_CLIENTONLY) == CM_FILELOCK_FLAG_CLIENTONLY))
4374 #define INTERSECT_RANGE(r1,r2) (((r2).offset+(r2).length) > (r1).offset && ((r1).offset +(r1).length) > (r2).offset)
4377 #define CONTAINS_RANGE(r1,r2) (((r2).offset+(r2).length) <= ((r1).offset+(r1).length) && (r1).offset <= (r2).offset)
4379 #if defined(VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS) && !defined(LOCK_TESTING)
4380 #define SCP_SUPPORTS_BRLOCKS(scp) ((scp)->cbServerp && ((scp)->cbServerp->capabilities & VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS))
4382 #define SCP_SUPPORTS_BRLOCKS(scp) (1)
4385 #define SERVERLOCKS_ENABLED(scp) (!((scp)->flags & CM_SCACHEFLAG_RO) && cm_enableServerLocks && SCP_SUPPORTS_BRLOCKS(scp))
4387 #if defined(VICED_CAPABILITY_WRITELOCKACL)
4388 #define SCP_SUPPORTS_WRITELOCKACL(scp) ((scp)->cbServerp && ((scp->cbServerp->capabilities & VICED_CAPABILITY_WRITELOCKACL)))
4390 #define SCP_SUPPORTS_WRITELOCKACL(scp) (0)
4392 /* This should really be defined in any build that this code is being
4394 #error VICED_CAPABILITY_WRITELOCKACL not defined.
4397 static void cm_LockRangeSubtract(cm_range_t * pos, const cm_range_t * neg)
4399 afs_int64 int_begin;
4402 int_begin = max(pos->offset, neg->offset);
4403 int_end = min(pos->offset+pos->length, neg->offset+neg->length);
4405 if (int_begin < int_end) {
4406 if (int_begin == pos->offset) {
4407 pos->length = pos->offset + pos->length - int_end;
4408 pos->offset = int_end;
4409 } else if (int_end == pos->offset + pos->length) {
4410 pos->length = int_begin - pos->offset;
4413 /* We only subtract ranges if the resulting range is
4414 contiguous. If we try to support non-contigous ranges, we
4415 aren't actually improving performance. */
4419 /* Called with scp->rw held. Returns 0 if all is clear to read the
4420 specified range by the client identified by key.
4422 long cm_LockCheckRead(cm_scache_t *scp,
4423 LARGE_INTEGER LOffset,
4424 LARGE_INTEGER LLength,
4427 #ifndef ADVISORY_LOCKS
4429 cm_file_lock_t *fileLock;
4433 int substract_ranges = FALSE;
4435 range.offset = LOffset.QuadPart;
4436 range.length = LLength.QuadPart;
4440 1. for all _a_ in (Offset,+Length), all of the following is true:
4442 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
4443 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
4446 1.2 For each LOST lock L in S->fileLocks such that _a_ in
4447 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
4452 lock_ObtainRead(&cm_scacheLock);
4454 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
4456 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4458 if (INTERSECT_RANGE(range, fileLock->range)) {
4459 if (IS_LOCK_ACTIVE(fileLock)) {
4460 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4462 /* If there is an active lock for this client, it
4463 is safe to substract ranges.*/
4464 cm_LockRangeSubtract(&range, &fileLock->range);
4465 substract_ranges = TRUE;
4467 if (fileLock->lockType != LockRead) {
4468 code = CM_ERROR_LOCK_CONFLICT;
4472 /* even if the entire range is locked for reading,
4473 we still can't grant the lock at this point
4474 because the client may have lost locks. That
4475 is, unless we have already seen an active lock
4476 belonging to the client, in which case there
4477 can't be any lost locks for this client. */
4478 if (substract_ranges)
4479 cm_LockRangeSubtract(&range, &fileLock->range);
4481 } else if (IS_LOCK_LOST(fileLock) &&
4482 (cm_KeyEquals(&fileLock->key, &key, 0) || fileLock->lockType == LockWrite)) {
4483 code = CM_ERROR_BADFD;
4489 lock_ReleaseRead(&cm_scacheLock);
4491 osi_Log4(afsd_logp, "cm_LockCheckRead scp 0x%x offset %d length %d code 0x%x",
4492 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
4503 /* Called with scp->rw held. Returns 0 if all is clear to write the
4504 specified range by the client identified by key.
4506 long cm_LockCheckWrite(cm_scache_t *scp,
4507 LARGE_INTEGER LOffset,
4508 LARGE_INTEGER LLength,
4511 #ifndef ADVISORY_LOCKS
4513 cm_file_lock_t *fileLock;
4518 range.offset = LOffset.QuadPart;
4519 range.length = LLength.QuadPart;
4522 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
4524 2. for all _a_ in (Offset,+Length), one of the following is true:
4526 2.1 Byte _a_ of S is unowned AND there does not exist a LOST
4527 lock L such that _a_ in (L->LOffset,+L->LLength).
4529 2.2 Byte _a_ of S is owned by C under lock L AND L->LockType is
4533 lock_ObtainRead(&cm_scacheLock);
4535 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
4537 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4539 if (INTERSECT_RANGE(range, fileLock->range)) {
4540 if (IS_LOCK_ACTIVE(fileLock)) {
4541 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4542 if (fileLock->lockType == LockWrite) {
4544 /* if there is an active lock for this client, it
4545 is safe to substract ranges */
4546 cm_LockRangeSubtract(&range, &fileLock->range);
4548 code = CM_ERROR_LOCK_CONFLICT;
4552 code = CM_ERROR_LOCK_CONFLICT;
4555 } else if (IS_LOCK_LOST(fileLock)) {
4556 code = CM_ERROR_BADFD;
4562 lock_ReleaseRead(&cm_scacheLock);
4564 osi_Log4(afsd_logp, "cm_LockCheckWrite scp 0x%x offset %d length %d code 0x%x",
4565 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
4576 /* Called with cm_scacheLock write locked */
4577 static cm_file_lock_t * cm_GetFileLock(void) {
4580 l = (cm_file_lock_t *) cm_freeFileLocks;
4582 osi_QRemove(&cm_freeFileLocks, &l->q);
4584 l = malloc(sizeof(cm_file_lock_t));
4585 osi_assertx(l, "null cm_file_lock_t");
4588 memset(l, 0, sizeof(cm_file_lock_t));
4593 /* Called with cm_scacheLock write locked */
4594 static void cm_PutFileLock(cm_file_lock_t *l) {
4595 osi_QAdd(&cm_freeFileLocks, &l->q);
4598 /* called with scp->rw held. May release it during processing, but
4599 leaves it held on exit. */
4600 long cm_IntSetLock(cm_scache_t * scp, cm_user_t * userp, int lockType,
4606 struct rx_connection * rxconnp;
4608 afs_uint32 reqflags = reqp->flags;
4610 osi_Log2(afsd_logp, "CALL SetLock scp 0x%p for lock %d", scp, lockType);
4614 * The file server prior to 1.6.2 does not report an accurate value
4615 * and callbacks are not issued if the lock is dropped due to expiration.
4617 if ((lockType != LOCKING_ANDX_SHARED_LOCK && scp->fsLockCount != 0) ||
4618 (lockType == LOCKING_ANDX_SHARED_LOCK && scp->fsLockCount < 0))
4620 code = CM_ERROR_LOCK_NOT_GRANTED;
4621 osi_Log2(afsd_logp, "CALL SetLock FAILURE, fsLockCount %d code 0x%x", scp->fsLockCount, code);
4626 memset(&volSync, 0, sizeof(volSync));
4628 tfid.Volume = scp->fid.volume;
4629 tfid.Vnode = scp->fid.vnode;
4630 tfid.Unique = scp->fid.unique;
4633 reqp->flags |= CM_REQ_NORETRY;
4634 lock_ReleaseWrite(&scp->rw);
4637 code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4641 rxconnp = cm_GetRxConn(connp);
4642 code = RXAFS_SetLock(rxconnp, &tfid, lockType,
4644 rx_PutConnection(rxconnp);
4646 } while (cm_Analyze(connp, userp, reqp, &cfid, NULL, 1, &volSync,
4649 code = cm_MapRPCError(code, reqp);
4651 osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
4653 osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
4656 reqp->flags = reqflags;
4658 lock_ObtainWrite(&scp->rw);
4661 * The file server does not return a status structure so we must
4662 * locally track the file server lock count to the best of our
4665 if (lockType == LockWrite)
4666 scp->fsLockCount = -1;
4673 /* called with scp->rw held. Releases it during processing */
4674 long cm_IntReleaseLock(cm_scache_t * scp, cm_user_t * userp,
4680 struct rx_connection * rxconnp;
4683 if (scp->flags & CM_SCACHEFLAG_DELETED) {
4684 osi_Log1(afsd_logp, "CALL ReleaseLock on Deleted Vnode scp 0x%p", scp);
4688 memset(&volSync, 0, sizeof(volSync));
4690 tfid.Volume = scp->fid.volume;
4691 tfid.Vnode = scp->fid.vnode;
4692 tfid.Unique = scp->fid.unique;
4695 lock_ReleaseWrite(&scp->rw);
4697 osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
4700 code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4704 rxconnp = cm_GetRxConn(connp);
4705 code = RXAFS_ReleaseLock(rxconnp, &tfid, &volSync);
4706 rx_PutConnection(rxconnp);
4708 } while (cm_Analyze(connp, userp, reqp, &cfid, NULL, 1, &volSync,
4710 code = cm_MapRPCError(code, reqp);
4713 "CALL ReleaseLock FAILURE, code 0x%x", code);
4716 "CALL ReleaseLock SUCCESS");
4718 lock_ObtainWrite(&scp->rw);
4721 * The file server does not return a status structure so we must
4722 * locally track the file server lock count to the best of our
4726 if (scp->fsLockCount < 0)
4727 scp->fsLockCount = 0;
4730 return (code != CM_ERROR_BADFD ? code : 0);
4733 /* called with scp->rw held. May release it during processing, but
4734 will exit with lock held.
4738 - 0 if the user has permission to get the specified lock for the scp
4740 - CM_ERROR_NOACCESS if not
4742 Any other error from cm_SyncOp will be sent down untranslated.
4744 If CM_ERROR_NOACCESS is returned and lock_type is LockRead, then
4745 phas_insert (if non-NULL) will receive a boolean value indicating
4746 whether the user has INSERT permission or not.
4748 long cm_LockCheckPerms(cm_scache_t * scp,
4755 long code = 0, code2 = 0;
4757 /* lock permissions are slightly tricky because of the 'i' bit.
4758 If the user has PRSFS_LOCK, she can read-lock the file. If the
4759 user has PRSFS_WRITE, she can write-lock the file. However, if
4760 the user has PRSFS_INSERT, then she can write-lock new files,
4761 but not old ones. Since we don't have information about
4762 whether a file is new or not, we assume that if the user owns
4763 the scp, then she has the permissions that are granted by
4766 osi_Log3(afsd_logp, "cm_LockCheckPerms for scp[0x%p] type[%d] user[0x%p]",
4767 scp, lock_type, userp);
4769 if (lock_type == LockRead)
4770 rights |= PRSFS_LOCK;
4771 else if (lock_type == LockWrite)
4772 rights |= PRSFS_WRITE | PRSFS_LOCK;
4775 osi_assertx(FALSE, "invalid lock type");
4780 *phas_insert = FALSE;
4782 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
4783 CM_SCACHESYNC_GETSTATUS |
4784 CM_SCACHESYNC_NEEDCALLBACK);
4786 if (phas_insert && scp->creator == userp) {
4788 /* If this file was created by the user, then we check for
4789 PRSFS_INSERT. If the file server is recent enough, then
4790 this should be sufficient for her to get a write-lock (but
4791 not necessarily a read-lock). VICED_CAPABILITY_WRITELOCKACL
4792 indicates whether a file server supports getting write
4793 locks when the user only has PRSFS_INSERT.
4795 If the file was not created by the user we skip the check
4796 because the INSERT bit will not apply to this user even
4800 code2 = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
4801 CM_SCACHESYNC_GETSTATUS |
4802 CM_SCACHESYNC_NEEDCALLBACK);
4804 if (code2 == CM_ERROR_NOACCESS) {
4805 osi_Log0(afsd_logp, "cm_LockCheckPerms user has no INSERT bits");
4807 *phas_insert = TRUE;
4808 osi_Log0(afsd_logp, "cm_LockCheckPerms user has INSERT bits");
4812 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
4814 osi_Log1(afsd_logp, "cm_LockCheckPerms returning code %d", code);
4819 /* called with scp->rw held */
4820 long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
4821 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
4823 int allowWait, cm_user_t *userp, cm_req_t *reqp,
4824 cm_file_lock_t **lockpp)
4827 int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
4828 cm_file_lock_t *fileLock;
4831 int wait_unlock = FALSE;
4832 int force_client_lock = FALSE;
4834 osi_Log4(afsd_logp, "cm_Lock scp 0x%x type 0x%x offset %d length %d",
4835 scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
4836 osi_Log4(afsd_logp, "... allowWait %d key <0x%x, 0x%x, 0x%x>", allowWait,
4837 key.process_id, key.session_id, key.file_id);
4840 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
4842 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
4845 3.1 If L->LockType is exclusive then there does NOT exist a
4846 ACCEPTED lock M in S->fileLocks such that _a_ in
4847 (M->LOffset,+M->LLength).
4849 3.2 If L->LockType is shared then for each ACCEPTED lock M in
4850 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
4851 M->LockType is shared.
4853 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
4855 4.1 M->key != Key(C)
4857 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
4858 and (M->LOffset,+M->LLength) do not intersect.
4861 range.offset = LOffset.QuadPart;
4862 range.length = LLength.QuadPart;
4864 lock_ObtainRead(&cm_scacheLock);
4866 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4868 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4870 if (IS_LOCK_LOST(fileLock)) {
4871 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4872 code = CM_ERROR_BADFD;
4874 } else if (fileLock->lockType == LockWrite && INTERSECT_RANGE(range, fileLock->range)) {
4875 code = CM_ERROR_WOULDBLOCK;
4881 /* we don't need to check for deleted locks here since deleted
4882 locks are dequeued from scp->fileLocks */
4883 if (IS_LOCK_ACCEPTED(fileLock) &&
4884 INTERSECT_RANGE(range, fileLock->range)) {
4886 if ((sLockType & LOCKING_ANDX_SHARED_LOCK) == 0 ||
4887 fileLock->lockType != LockRead) {
4889 code = CM_ERROR_WOULDBLOCK;
4895 lock_ReleaseRead(&cm_scacheLock);
4897 if (code == 0 && SERVERLOCKS_ENABLED(scp)) {
4898 if (Which == scp->serverLock ||
4899 (Which == LockRead && scp->serverLock == LockWrite)) {
4903 /* we already have the lock we need */
4904 osi_Log3(afsd_logp, " we already have the correct lock. exclusives[%d], shared[%d], serverLock[%d]",
4905 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4907 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4909 /* special case: if we don't have permission to read-lock
4910 the file, then we force a clientside lock. This is to
4911 compensate for applications that obtain a read-lock for
4912 reading files off of directories that don't grant
4913 read-locks to the user. */
4914 if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4916 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4917 osi_Log0(afsd_logp, " User has no read-lock perms, but has INSERT perms.");
4920 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4921 force_client_lock = TRUE;
4925 } else if ((scp->exclusiveLocks > 0) ||
4926 (scp->sharedLocks > 0 && scp->serverLock != LockRead)) {
4929 /* We are already waiting for some other lock. We should
4930 wait for the daemon to catch up instead of generating a
4931 flood of SetLock calls. */
4932 osi_Log3(afsd_logp, " already waiting for other lock. exclusives[%d], shared[%d], serverLock[%d]",
4933 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4935 /* see if we have permission to create the lock in the
4937 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4939 code = CM_ERROR_WOULDBLOCK;
4940 else if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4942 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4944 " User has no read-lock perms, but has INSERT perms.");
4945 code = CM_ERROR_WOULDBLOCK;
4948 " User has no read-lock perms. Forcing client-side lock");
4949 force_client_lock = TRUE;
4953 /* leave any other codes as-is */
4957 int check_data_version = FALSE;
4960 /* first check if we have permission to elevate or obtain
4962 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4964 if (code == CM_ERROR_NOACCESS && Which == LockRead &&
4965 (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp))) {
4966 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4967 force_client_lock = TRUE;
4972 /* has_insert => (Which == LockRead, code == CM_ERROR_NOACCESS) */
4974 if (scp->serverLock == LockRead && Which == LockWrite) {
4976 /* We want to escalate the lock to a LockWrite.
4977 * Unfortunately that's not really possible without
4978 * letting go of the current lock. But for now we do
4982 " attempting to UPGRADE from LockRead to LockWrite.");
4984 " dataVersion on scp: %I64d", scp->dataVersion);
4986 /* we assume at this point (because scp->serverLock
4987 was valid) that we had a valid server lock. */
4988 scp->lockDataVersion = scp->dataVersion;
4989 check_data_version = TRUE;
4991 code = cm_IntReleaseLock(scp, userp, reqp);
4994 /* We couldn't release the lock */
4997 scp->serverLock = -1;
5001 /* We need to obtain a server lock of type Which in order
5002 * to assert this file lock */
5003 #ifndef AGGRESSIVE_LOCKS
5006 newLock = LockWrite;
5009 code = cm_IntSetLock(scp, userp, newLock, reqp);
5011 #ifdef AGGRESSIVE_LOCKS
5012 if ((code == CM_ERROR_WOULDBLOCK ||
5013 code == CM_ERROR_NOACCESS) && newLock != Which) {
5014 /* we wanted LockRead. We tried LockWrite. Now try
5019 osi_assertx(newLock == LockRead, "lock type not read");
5021 code = cm_IntSetLock(scp, userp, newLock, reqp);
5025 if (code == CM_ERROR_NOACCESS) {
5026 if (Which == LockRead) {
5027 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
5029 /* We requested a read-lock, but we have permission to
5030 * get a write-lock. Try that */
5032 tcode = cm_LockCheckPerms(scp, LockWrite, userp, reqp, NULL);
5035 newLock = LockWrite;
5037 osi_Log0(afsd_logp, " User has 'i' perms and the request was for a LockRead. Trying to get a LockWrite instead");
5039 code = cm_IntSetLock(scp, userp, newLock, reqp);
5042 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
5043 force_client_lock = TRUE;
5045 } else if (Which == LockWrite &&
5046 scp->creator == userp && !SCP_SUPPORTS_WRITELOCKACL(scp)) {
5049 /* Special case: if the lock request was for a
5050 * LockWrite and the user owns the file and we weren't
5051 * allowed to obtain the serverlock, we either lost a
5052 * race (the permissions changed from under us), or we
5053 * have 'i' bits, but we aren't allowed to lock the
5056 /* check if we lost a race... */
5057 tcode = cm_LockCheckPerms(scp, Which, userp, reqp, NULL);
5060 osi_Log0(afsd_logp, " User has 'i' perms but can't obtain write locks. Using client-side locks.");
5061 force_client_lock = TRUE;
5066 if (code == 0 && check_data_version &&
5067 scp->dataVersion != scp->lockDataVersion) {
5068 /* We lost a race. Although we successfully obtained
5069 * a lock, someone modified the file in between. The
5070 * locks have all been technically lost. */
5073 " Data version mismatch while upgrading lock.");
5075 " Data versions before=%I64d, after=%I64d",
5076 scp->lockDataVersion,
5079 " Releasing stale lock for scp 0x%x", scp);
5081 code = cm_IntReleaseLock(scp, userp, reqp);
5083 scp->serverLock = -1;
5085 code = CM_ERROR_INVAL;
5086 } else if (code == 0) {
5087 scp->serverLock = newLock;
5088 scp->lockDataVersion = scp->dataVersion;
5092 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
5093 scp->serverLock == -1) {
5094 /* Oops. We lost the lock. */
5095 cm_LockMarkSCacheLost(scp);
5098 } else if (code == 0) { /* server locks not enabled */
5100 " Skipping server lock for scp");
5105 if (code != 0 && !force_client_lock) {
5106 /* Special case error translations
5108 Applications don't expect certain errors from a
5109 LockFile/UnlockFile call. We need to translate some error
5110 code to codes that apps expect and handle. */
5112 /* We shouldn't actually need to handle this case since we
5113 simulate locks for RO scps anyway. */
5114 if (code == CM_ERROR_READONLY) {
5115 osi_Log0(afsd_logp, " Reinterpreting CM_ERROR_READONLY as CM_ERROR_NOACCESS");
5116 code = CM_ERROR_NOACCESS;
5120 if (code == 0 || (code == CM_ERROR_WOULDBLOCK && allowWait) ||
5121 force_client_lock) {
5123 /* clear the error if we are forcing a client lock, so we
5124 don't get confused later. */
5125 if (force_client_lock && code != CM_ERROR_WOULDBLOCK)
5130 lock_ObtainWrite(&cm_scacheLock);
5131 fileLock = cm_GetFileLock();
5133 fileLock->fid = scp->fid;
5135 fileLock->key = key;
5136 fileLock->lockType = Which;
5137 fileLock->userp = userp;
5138 fileLock->range = range;
5139 fileLock->flags = (code == 0 ? 0 :
5141 CM_FILELOCK_FLAG_WAITUNLOCK :
5142 CM_FILELOCK_FLAG_WAITLOCK));
5144 if (force_client_lock || !SERVERLOCKS_ENABLED(scp))
5145 fileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
5147 fileLock->lastUpdate = (code == 0 && !force_client_lock) ? time(NULL) : 0;
5149 osi_QAddT(&scp->fileLocksH, &scp->fileLocksT, &fileLock->fileq);
5150 cm_HoldSCacheNoLock(scp);
5151 fileLock->scp = scp;
5152 osi_QAdd(&cm_allFileLocks, &fileLock->q);
5153 lock_ReleaseWrite(&cm_scacheLock);
5159 if (IS_LOCK_CLIENTONLY(fileLock)) {
5161 } else if (IS_LOCK_ACCEPTED(fileLock)) {
5162 if (Which == LockRead)
5165 scp->exclusiveLocks++;
5169 "cm_Lock Lock added 0x%p flags 0x%x to scp [0x%p]",
5170 fileLock, fileLock->flags, scp);
5172 " exclusives[%d] shared[%d] client[%d] serverLock[%d]",
5173 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
5174 (int)(signed char) scp->serverLock);
5177 "cm_Lock Rejecting lock (code = 0x%x)", code);
5180 /* Convert from would block to lock not granted */
5181 if (code == CM_ERROR_WOULDBLOCK)
5182 code = CM_ERROR_LOCK_NOT_GRANTED;
5188 cm_IntUnlock(cm_scache_t * scp,
5194 osi_assertx(scp->sharedLocks >= 0, "scp->sharedLocks < 0");
5195 osi_assertx(scp->exclusiveLocks >= 0, "scp->exclusiveLocks < 0");
5196 osi_assertx(scp->clientLocks >= 0, "scp->clientLocks < 0");
5198 if (!SERVERLOCKS_ENABLED(scp)) {
5199 osi_Log0(afsd_logp, " Skipping server lock for scp");
5203 /* Ideally we would go through the rest of the locks to determine
5204 * if one or more locks that were formerly in WAITUNLOCK can now
5205 * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
5206 * scp->sharedLocks accordingly. However, the retrying of locks
5207 * in that manner is done cm_RetryLock() manually.
5210 if (scp->serverLock == LockWrite &&
5211 scp->exclusiveLocks == 0 &&
5212 scp->sharedLocks > 0) {
5213 /* The serverLock should be downgraded to LockRead */
5214 osi_Log0(afsd_logp, " DOWNGRADE lock from LockWrite to LockRead");
5216 /* Make sure there are no dirty buffers left. */
5217 code = cm_FSync(scp, userp, reqp, TRUE);
5219 /* since scp->serverLock looked sane, we are going to assume
5220 that we have a valid server lock. */
5221 scp->lockDataVersion = scp->dataVersion;
5222 osi_Log1(afsd_logp, " dataVersion on scp = %I64d", scp->dataVersion);
5224 /* before we downgrade, make sure that we have enough
5225 permissions to get the read lock. */
5226 code = cm_LockCheckPerms(scp, LockRead, userp, reqp, NULL);
5229 osi_Log0(afsd_logp, " SKIPPING downgrade because user doesn't have perms to get downgraded lock");
5235 code = cm_IntReleaseLock(scp, userp, reqp);
5238 /* so we couldn't release it. Just let the lock be for now */
5242 scp->serverLock = -1;
5245 code = cm_IntSetLock(scp, userp, LockRead, reqp);
5247 if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
5248 scp->serverLock = LockRead;
5249 } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
5250 /* We lost a race condition. Although we have a valid
5251 lock on the file, the data has changed and essentially
5252 we have lost the lock we had during the transition. */
5254 osi_Log0(afsd_logp, "Data version mismatch during lock downgrade");
5255 osi_Log2(afsd_logp, " Data versions before=%I64d, after=%I64d",
5256 scp->lockDataVersion,
5259 code = cm_IntReleaseLock(scp, userp, reqp);
5261 code = CM_ERROR_INVAL;
5262 scp->serverLock = -1;
5266 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
5267 (scp->serverLock == -1)) {
5269 cm_LockMarkSCacheLost(scp);
5272 /* failure here has no bearing on the return value of cm_Unlock() */
5275 } else if (scp->serverLock != (-1) &&
5276 scp->exclusiveLocks == 0 &&
5277 scp->sharedLocks == 0) {
5278 /* The serverLock should be released entirely */
5280 if (scp->serverLock == LockWrite) {
5281 osi_Log0(afsd_logp, " RELEASE LockWrite -> LockNone");
5283 /* Make sure there are no dirty buffers left. */
5284 code = cm_FSync(scp, userp, reqp, TRUE);
5286 osi_Log0(afsd_logp, " RELEASE LockRead -> LockNone");
5289 code = cm_IntReleaseLock(scp, userp, reqp);
5292 scp->serverLock = (-1);
5298 /* Called with scp->rw held */
5299 long cm_UnlockByKey(cm_scache_t * scp,
5306 cm_file_lock_t *fileLock;
5307 osi_queue_t *q, *qn;
5310 osi_Log4(afsd_logp, "cm_UnlockByKey scp 0x%p key <0x%x,0x%x,0x%x",
5311 scp, key.process_id, key.session_id, key.file_id);
5312 osi_Log1(afsd_logp, " flags=0x%x", flags);
5314 lock_ObtainWrite(&cm_scacheLock);
5316 for (q = scp->fileLocksH; q; q = qn) {
5319 fileLock = (cm_file_lock_t *)
5320 ((char *) q - offsetof(cm_file_lock_t, fileq));
5323 osi_Log4(afsd_logp, " Checking lock[0x%x] range[%d,+%d] type[%d]",
5325 (unsigned long) fileLock->range.offset,
5326 (unsigned long) fileLock->range.length,
5327 fileLock->lockType);
5328 osi_Log4(afsd_logp, " key<0x%x, 0x%x, 0x%x> flags[0x%x]",
5329 fileLock->key.process_id, fileLock->key.session_id, fileLock->key.file_id,
5332 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5333 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5334 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5336 fileLock->fid.volume,
5337 fileLock->fid.vnode,
5338 fileLock->fid.unique);
5339 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5340 fileLock->scp->fid.cell,
5341 fileLock->scp->fid.volume,
5342 fileLock->scp->fid.vnode,
5343 fileLock->scp->fid.unique);
5344 osi_assertx(FALSE, "invalid fid value");
5348 if (!IS_LOCK_DELETED(fileLock) &&
5349 cm_KeyEquals(&fileLock->key, &key, flags)) {
5350 osi_Log3(afsd_logp, "...Unlock range [%d,+%d] type %d",
5351 fileLock->range.offset,
5352 fileLock->range.length,
5353 fileLock->lockType);
5355 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
5357 if (IS_LOCK_CLIENTONLY(fileLock)) {
5359 } else if (IS_LOCK_ACCEPTED(fileLock)) {
5360 if (fileLock->lockType == LockRead)
5363 scp->exclusiveLocks--;
5366 fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5368 cm_ReleaseUser(fileLock->userp);
5369 cm_ReleaseSCacheNoLock(scp);
5371 fileLock->userp = NULL;
5372 fileLock->scp = NULL;
5378 lock_ReleaseWrite(&cm_scacheLock);
5380 if (n_unlocks == 0) {
5381 osi_Log0(afsd_logp, "cm_UnlockByKey no locks found");
5382 osi_Log3(afsd_logp, " Leaving scp with exclusives[%d], shared[%d], serverLock[%d]",
5383 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
5388 code = cm_IntUnlock(scp, userp, reqp);
5389 osi_Log1(afsd_logp, "cm_UnlockByKey code 0x%x", code);
5391 osi_Log4(afsd_logp, " Leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
5392 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
5393 (int)(signed char) scp->serverLock);
5398 /* Called with scp->rw held */
5399 long cm_Unlock(cm_scache_t *scp,
5400 unsigned char sLockType,
5401 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
5408 int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
5409 cm_file_lock_t *fileLock;
5411 int release_userp = FALSE;
5412 int exact_match = !(flags & CM_UNLOCK_FLAG_MATCH_RANGE);
5414 LARGE_INTEGER RangeEnd;
5416 osi_Log4(afsd_logp, "cm_Unlock scp 0x%p type 0x%x offset 0x%x length 0x%x",
5417 scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
5418 osi_Log4(afsd_logp, "... key <0x%x,0x%x,0x%x> flags 0x%x",
5419 key.process_id, key.session_id, key.file_id, flags);
5422 RangeEnd.QuadPart = LOffset.QuadPart + LLength.QuadPart;
5425 lock_ObtainRead(&cm_scacheLock);
5427 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5428 fileLock = (cm_file_lock_t *)
5429 ((char *) q - offsetof(cm_file_lock_t, fileq));
5432 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5433 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5434 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5436 fileLock->fid.volume,
5437 fileLock->fid.vnode,
5438 fileLock->fid.unique);
5439 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5440 fileLock->scp->fid.cell,
5441 fileLock->scp->fid.volume,
5442 fileLock->scp->fid.vnode,
5443 fileLock->scp->fid.unique);
5444 osi_assertx(FALSE, "invalid fid value");
5448 if (!IS_LOCK_DELETED(fileLock) &&
5449 cm_KeyEquals(&fileLock->key, &key, 0) &&
5450 fileLock->range.offset == LOffset.QuadPart &&
5451 fileLock->range.length == LLength.QuadPart) {
5457 if (!IS_LOCK_DELETED(fileLock) &&
5458 cm_KeyEquals(&fileLock->key, &key, 0) &&
5459 fileLock->range.offset >= LOffset.QuadPart &&
5460 fileLock->range.offset < RangeEnd.QuadPart &&
5461 (fileLock->range.offset + fileLock->range.length) <= RangeEnd.QuadPart) {
5469 lock_ReleaseRead(&cm_scacheLock);
5471 if (lock_found && !exact_match) {
5475 osi_Log0(afsd_logp, "cm_Unlock lock not found; failure");
5477 /* The lock didn't exist anyway. *shrug* */
5478 return CM_ERROR_RANGE_NOT_LOCKED;
5482 /* discard lock record */
5483 lock_ConvertRToW(&cm_scacheLock);
5484 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
5487 * Don't delete it here; let the daemon delete it, to simplify
5488 * the daemon's traversal of the list.
5491 if (IS_LOCK_CLIENTONLY(fileLock)) {
5493 } else if (IS_LOCK_ACCEPTED(fileLock)) {
5494 if (fileLock->lockType == LockRead)
5497 scp->exclusiveLocks--;
5500 fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5502 if (userp != NULL) {
5503 cm_ReleaseUser(fileLock->userp);
5505 userp = fileLock->userp;
5506 release_userp = TRUE;
5508 cm_ReleaseSCacheNoLock(scp);
5509 fileLock->userp = NULL;
5510 fileLock->scp = NULL;
5511 lock_ReleaseWrite(&cm_scacheLock);
5513 code = cm_IntUnlock(scp, userp, reqp);
5515 if (release_userp) {
5516 cm_ReleaseUser(userp);
5517 release_userp = FALSE;
5521 osi_Log1(afsd_logp, "cm_Unlock not exact match, searching for next lock, code 0x%x", code);
5522 goto try_again; /* might be more than one lock in the range */
5527 osi_Log1(afsd_logp, "cm_Unlock code 0x%x", code);
5528 osi_Log4(afsd_logp, " leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
5529 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
5530 (int)(signed char) scp->serverLock);
5535 /* called with scp->rw held */
5536 void cm_LockMarkSCacheLost(cm_scache_t * scp)
5538 cm_file_lock_t *fileLock;
5541 osi_Log1(afsd_logp, "cm_LockMarkSCacheLost scp 0x%x", scp);
5543 /* cm_scacheLock needed because we are modifying fileLock->flags */
5544 lock_ObtainWrite(&cm_scacheLock);
5546 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5548 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
5550 if (IS_LOCK_ACTIVE(fileLock) &&
5551 !IS_LOCK_CLIENTONLY(fileLock)) {
5552 if (fileLock->lockType == LockRead)
5555 scp->exclusiveLocks--;
5557 fileLock->flags |= CM_FILELOCK_FLAG_LOST;
5561 scp->serverLock = -1;
5562 scp->lockDataVersion = CM_SCACHE_VERSION_BAD;
5563 lock_ReleaseWrite(&cm_scacheLock);
5566 /* Called with no relevant locks held */
5567 void cm_CheckLocks()
5569 osi_queue_t *q, *nq;
5570 cm_file_lock_t *fileLock;
5576 struct rx_connection * rxconnp;
5579 memset(&volSync, 0, sizeof(volSync));
5583 lock_ObtainWrite(&cm_scacheLock);
5585 cm_lockRefreshCycle++;
5587 osi_Log1(afsd_logp, "cm_CheckLocks starting lock check cycle %d", cm_lockRefreshCycle);
5589 for (q = cm_allFileLocks; q; q = nq) {
5590 fileLock = (cm_file_lock_t *) q;
5594 if (IS_LOCK_DELETED(fileLock)) {
5595 cm_user_t *userp = fileLock->userp;
5596 cm_scache_t *scp = fileLock->scp;
5597 fileLock->userp = NULL;
5598 fileLock->scp = NULL;
5601 lock_ReleaseWrite(&cm_scacheLock);
5602 lock_ObtainWrite(&scp->rw);
5603 code = cm_IntUnlock(scp, userp, &req);
5604 lock_ReleaseWrite(&scp->rw);
5606 cm_ReleaseUser(userp);
5607 lock_ObtainWrite(&cm_scacheLock);
5608 cm_ReleaseSCacheNoLock(scp);
5610 osi_QRemove(&cm_allFileLocks, q);
5611 cm_PutFileLock(fileLock);
5613 } else if (IS_LOCK_ACTIVE(fileLock) && !IS_LOCK_CLIENTONLY(fileLock)) {
5615 /* Server locks must have been enabled for us to have
5616 received an active non-client-only lock. */
5617 osi_assertx(cm_enableServerLocks, "!cm_enableServerLocks");
5619 scp = fileLock->scp;
5620 osi_assertx(scp != NULL, "null cm_scache_t");
5622 cm_HoldSCacheNoLock(scp);
5625 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5626 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5627 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5629 fileLock->fid.volume,
5630 fileLock->fid.vnode,
5631 fileLock->fid.unique);
5632 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5633 fileLock->scp->fid.cell,
5634 fileLock->scp->fid.volume,
5635 fileLock->scp->fid.vnode,
5636 fileLock->scp->fid.unique);
5637 osi_assertx(FALSE, "invalid fid");
5640 /* Server locks are extended once per scp per refresh
5642 if (scp->lastRefreshCycle != cm_lockRefreshCycle) {
5644 int scp_done = FALSE;
5646 osi_Log1(afsd_logp, "cm_CheckLocks Updating scp 0x%x", scp);
5648 lock_ReleaseWrite(&cm_scacheLock);
5649 lock_ObtainWrite(&scp->rw);
5651 /* did the lock change while we weren't holding the lock? */
5652 if (!IS_LOCK_ACTIVE(fileLock))
5653 goto post_syncopdone;
5655 code = cm_SyncOp(scp, NULL, fileLock->userp, &req, 0,
5656 CM_SCACHESYNC_NEEDCALLBACK
5657 | CM_SCACHESYNC_GETSTATUS
5658 | CM_SCACHESYNC_LOCK);
5662 "cm_CheckLocks SyncOp failure code 0x%x", code);
5663 goto post_syncopdone;
5666 /* cm_SyncOp releases scp->rw during which the lock
5667 may get released. */
5668 if (!IS_LOCK_ACTIVE(fileLock))
5669 goto pre_syncopdone;
5671 if (scp->serverLock != -1 && !(scp->flags & CM_SCACHEFLAG_DELETED)) {
5675 tfid.Volume = scp->fid.volume;
5676 tfid.Vnode = scp->fid.vnode;
5677 tfid.Unique = scp->fid.unique;
5679 userp = fileLock->userp;
5681 osi_Log3(afsd_logp, "CALL ExtendLock lock 0x%p for scp=0x%p with lock %d",
5684 (int) scp->serverLock);
5686 lock_ReleaseWrite(&scp->rw);
5689 code = cm_ConnFromFID(&cfid, userp,
5694 rxconnp = cm_GetRxConn(connp);
5695 code = RXAFS_ExtendLock(rxconnp, &tfid,
5697 rx_PutConnection(rxconnp);
5699 osi_Log1(afsd_logp, " ExtendLock returns %d", code);
5701 } while (cm_Analyze(connp, userp, &req,
5702 &cfid, NULL, 1, &volSync, NULL, NULL,
5705 code = cm_MapRPCError(code, &req);
5707 lock_ObtainWrite(&scp->rw);
5710 osi_Log1(afsd_logp, "CALL ExtendLock FAILURE, code 0x%x", code);
5711 scp->fsLockCount = 0;
5713 osi_Log0(afsd_logp, "CALL ExtendLock SUCCESS");
5714 scp->lockDataVersion = scp->dataVersion;
5717 if ((code == EINVAL || code == CM_ERROR_INVAL) &&
5718 scp->lockDataVersion == scp->dataVersion) {
5722 (scp->exclusiveLocks > 0) ? LockWrite: LockRead;
5724 /* we might still have a chance to obtain a
5727 code = cm_IntSetLock(scp, userp, lockType, &req);
5730 code = CM_ERROR_INVAL;
5731 } else if (scp->lockDataVersion != scp->dataVersion) {
5733 /* now check if we still have the file at
5734 the right data version. */
5736 "Data version mismatch on scp 0x%p",
5739 " Data versions: before=%I64d, after=%I64d",
5740 scp->lockDataVersion,
5743 code = cm_IntReleaseLock(scp, userp, &req);
5745 code = CM_ERROR_INVAL;
5749 if (code == EINVAL || code == CM_ERROR_INVAL ||
5750 code == CM_ERROR_BADFD) {
5751 cm_LockMarkSCacheLost(scp);
5755 /* interestingly, we have found an active lock
5756 belonging to an scache that has no
5758 cm_LockMarkSCacheLost(scp);
5765 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5768 lock_ReleaseWrite(&scp->rw);
5770 lock_ObtainWrite(&cm_scacheLock);
5773 fileLock->lastUpdate = time(NULL);
5777 scp->lastRefreshCycle = cm_lockRefreshCycle;
5780 /* we have already refreshed the locks on this scp */
5781 fileLock->lastUpdate = time(NULL);
5784 cm_ReleaseSCacheNoLock(scp);
5786 } else if (IS_LOCK_ACTIVE(fileLock) && IS_LOCK_CLIENTONLY(fileLock)) {
5787 /* TODO: Check callbacks */
5791 lock_ReleaseWrite(&cm_scacheLock);
5792 osi_Log1(afsd_logp, "cm_CheckLocks completes lock check cycle %d", cm_lockRefreshCycle);
5795 /* NOT called with scp->rw held. */
5796 long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
5799 cm_scache_t *scp = NULL;
5800 cm_file_lock_t *fileLock;
5804 int force_client_lock = FALSE;
5805 int has_insert = FALSE;
5806 int check_data_version = FALSE;
5810 if (client_is_dead) {
5811 code = CM_ERROR_TIMEDOUT;
5815 lock_ObtainRead(&cm_scacheLock);
5817 osi_Log2(afsd_logp, "cm_RetryLock checking lock %p (scp=%p)", oldFileLock, oldFileLock->scp);
5818 osi_Log4(afsd_logp, " offset(%x:%x) length(%x:%x)",
5819 (unsigned)(oldFileLock->range.offset >> 32),
5820 (unsigned)(oldFileLock->range.offset & 0xffffffff),
5821 (unsigned)(oldFileLock->range.length >> 32),
5822 (unsigned)(oldFileLock->range.length & 0xffffffff));
5823 osi_Log4(afsd_logp, " key<0x%x,0x%x,0x%x> flags=%x",
5824 oldFileLock->key.process_id, oldFileLock->key.session_id, oldFileLock->key.file_id,
5825 (unsigned)(oldFileLock->flags));
5827 /* if the lock has already been granted, then we have nothing to do */
5828 if (IS_LOCK_ACTIVE(oldFileLock)) {
5829 lock_ReleaseRead(&cm_scacheLock);
5830 osi_Log0(afsd_logp, "cm_RetryLock lock already granted");
5834 /* we can't do anything with lost or deleted locks at the moment. */
5835 if (IS_LOCK_LOST(oldFileLock) || IS_LOCK_DELETED(oldFileLock)) {
5836 code = CM_ERROR_BADFD;
5837 osi_Log0(afsd_logp, "cm_RetryLock lock is lost or deleted");
5838 lock_ReleaseRead(&cm_scacheLock);
5842 scp = oldFileLock->scp;
5844 osi_assertx(scp != NULL, "null cm_scache_t");
5846 lock_ReleaseRead(&cm_scacheLock);
5847 lock_ObtainWrite(&scp->rw);
5849 code = cm_LockCheckPerms(scp, oldFileLock->lockType,
5853 if (code == CM_ERROR_NOACCESS && oldFileLock->lockType == LockRead) {
5854 if (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp)) {
5855 force_client_lock = TRUE;
5859 lock_ReleaseWrite(&scp->rw);
5863 lock_ObtainWrite(&cm_scacheLock);
5865 /* Check if we already have a sufficient server lock to allow this
5866 lock to go through. */
5867 if (IS_LOCK_WAITLOCK(oldFileLock) &&
5868 (!SERVERLOCKS_ENABLED(scp) ||
5869 scp->serverLock == oldFileLock->lockType ||
5870 scp->serverLock == LockWrite)) {
5872 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5874 if (SERVERLOCKS_ENABLED(scp)) {
5875 osi_Log1(afsd_logp, "cm_RetryLock Server lock (%d) is sufficient for lock. Granting",
5876 (int) scp->serverLock);
5878 osi_Log0(afsd_logp, "cm_RetryLock skipping server lock for scp");
5881 lock_ReleaseWrite(&cm_scacheLock);
5882 lock_ReleaseWrite(&scp->rw);
5887 if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5889 /* check if the conflicting locks have dissappeared already */
5890 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5892 fileLock = (cm_file_lock_t *)
5893 ((char *) q - offsetof(cm_file_lock_t, fileq));
5895 if (IS_LOCK_LOST(fileLock)) {
5896 if (cm_KeyEquals(&fileLock->key, &oldFileLock->key, 0)) {
5897 code = CM_ERROR_BADFD;
5898 oldFileLock->flags |= CM_FILELOCK_FLAG_LOST;
5899 osi_Log1(afsd_logp, " found lost lock %p for same key. Marking lock as lost",
5902 } else if (fileLock->lockType == LockWrite &&
5903 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5904 osi_Log1(afsd_logp, " found conflicting LOST lock %p", fileLock);
5905 code = CM_ERROR_WOULDBLOCK;
5910 if (IS_LOCK_ACCEPTED(fileLock) &&
5911 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5913 if (oldFileLock->lockType != LockRead ||
5914 fileLock->lockType != LockRead) {
5916 osi_Log1(afsd_logp, " found conflicting lock %p", fileLock);
5917 code = CM_ERROR_WOULDBLOCK;
5925 lock_ReleaseWrite(&cm_scacheLock);
5926 lock_ReleaseWrite(&scp->rw);
5931 /* when we get here, the lock is either a WAITUNLOCK or WAITLOCK.
5932 If it is WAITUNLOCK, then we didn't find any conflicting lock
5933 but we haven't verfied whether the serverLock is sufficient to
5934 assert it. If it is WAITLOCK, then the serverLock is
5935 insufficient to assert it. Eitherway, we are ready to accept
5936 the lock as either ACTIVE or WAITLOCK depending on the
5939 /* First, promote the WAITUNLOCK to a WAITLOCK */
5940 if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5941 if (oldFileLock->lockType == LockRead)
5944 scp->exclusiveLocks++;
5946 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITUNLOCK;
5947 oldFileLock->flags |= CM_FILELOCK_FLAG_WAITLOCK;
5950 osi_assertx(IS_LOCK_WAITLOCK(oldFileLock), "!IS_LOCK_WAITLOCK");
5952 if (force_client_lock ||
5953 !SERVERLOCKS_ENABLED(scp) ||
5954 scp->serverLock == oldFileLock->lockType ||
5955 (oldFileLock->lockType == LockRead &&
5956 scp->serverLock == LockWrite)) {
5958 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5960 if ((force_client_lock ||
5961 !SERVERLOCKS_ENABLED(scp)) &&
5962 !IS_LOCK_CLIENTONLY(oldFileLock)) {
5964 oldFileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
5966 if (oldFileLock->lockType == LockRead)
5969 scp->exclusiveLocks--;
5974 lock_ReleaseWrite(&cm_scacheLock);
5975 lock_ReleaseWrite(&scp->rw);
5982 code = cm_SyncOp(scp, NULL, oldFileLock->userp, &req, 0,
5983 CM_SCACHESYNC_NEEDCALLBACK
5984 | CM_SCACHESYNC_GETSTATUS
5985 | CM_SCACHESYNC_LOCK);
5987 osi_Log1(afsd_logp, "cm_RetryLock SyncOp failure code 0x%x", code);
5988 lock_ReleaseWrite(&cm_scacheLock);
5989 goto post_syncopdone;
5992 if (!IS_LOCK_WAITLOCK(oldFileLock))
5993 goto pre_syncopdone;
5995 userp = oldFileLock->userp;
5997 #ifndef AGGRESSIVE_LOCKS
5998 newLock = oldFileLock->lockType;
6000 newLock = LockWrite;
6004 /* if has_insert is non-zero, then:
6005 - the lock a LockRead
6006 - we don't have permission to get a LockRead
6007 - we do have permission to get a LockWrite
6008 - the server supports VICED_CAPABILITY_WRITELOCKACL
6011 newLock = LockWrite;
6014 lock_ReleaseWrite(&cm_scacheLock);
6016 /* when we get here, either we have a read-lock and want a
6017 write-lock or we don't have any locks and we want some
6020 if (scp->serverLock == LockRead) {
6022 osi_assertx(newLock == LockWrite, "!LockWrite");
6024 osi_Log0(afsd_logp, " Attempting to UPGRADE from LockRead to LockWrite");
6026 scp->lockDataVersion = scp->dataVersion;
6027 check_data_version = TRUE;
6029 code = cm_IntReleaseLock(scp, userp, &req);
6032 goto pre_syncopdone;
6034 scp->serverLock = -1;
6037 code = cm_IntSetLock(scp, userp, newLock, &req);
6040 if (scp->dataVersion != scp->lockDataVersion) {
6041 /* we lost a race. too bad */
6044 " Data version mismatch while upgrading lock.");
6046 " Data versions before=%I64d, after=%I64d",
6047 scp->lockDataVersion,
6050 " Releasing stale lock for scp 0x%x", scp);
6052 code = cm_IntReleaseLock(scp, userp, &req);
6054 scp->serverLock = -1;
6056 code = CM_ERROR_INVAL;
6058 cm_LockMarkSCacheLost(scp);
6060 scp->serverLock = newLock;
6065 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
6071 if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
6072 lock_ObtainWrite(&cm_scacheLock);
6073 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, &oldFileLock->fileq);
6074 lock_ReleaseWrite(&cm_scacheLock);
6076 lock_ReleaseWrite(&scp->rw);
6079 lock_ObtainWrite(&cm_scacheLock);
6081 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
6082 } else if (code != CM_ERROR_WOULDBLOCK) {
6083 oldFileLock->flags |= CM_FILELOCK_FLAG_DELETED;
6084 cm_ReleaseUser(oldFileLock->userp);
6085 oldFileLock->userp = NULL;
6086 if (oldFileLock->scp) {
6087 cm_ReleaseSCacheNoLock(oldFileLock->scp);
6088 oldFileLock->scp = NULL;
6091 lock_ReleaseWrite(&cm_scacheLock);
6096 cm_key_t cm_GenerateKey(afs_uint16 session_id, afs_offs_t process_id, afs_uint64 file_id)
6100 key.process_id = process_id;
6101 key.session_id = session_id;
6102 key.file_id = file_id;
6107 int cm_KeyEquals(cm_key_t *k1, cm_key_t *k2, int flags)
6109 return (k1->session_id == k2->session_id) && (k1->file_id == k2->file_id) &&
6110 ((flags & CM_UNLOCK_FLAG_BY_FID) || (k1->process_id == k2->process_id));
6113 void cm_ReleaseAllLocks(void)
6119 cm_file_lock_t *fileLock;
6122 for (i = 0; i < cm_data.scacheHashTableSize; i++)
6124 for ( scp = cm_data.scacheHashTablep[i]; scp; scp = scp->nextp ) {
6125 while (scp->fileLocksH != NULL) {
6126 lock_ObtainWrite(&scp->rw);
6127 lock_ObtainWrite(&cm_scacheLock);
6128 if (!scp->fileLocksH) {
6129 lock_ReleaseWrite(&cm_scacheLock);
6130 lock_ReleaseWrite(&scp->rw);
6133 fileLock = (cm_file_lock_t *)((char *) scp->fileLocksH - offsetof(cm_file_lock_t, fileq));
6134 userp = fileLock->userp;
6136 key = fileLock->key;
6137 cm_HoldSCacheNoLock(scp);
6138 lock_ReleaseWrite(&cm_scacheLock);
6139 cm_UnlockByKey(scp, key, 0, userp, &req);
6140 cm_ReleaseSCache(scp);
6141 cm_ReleaseUser(userp);
6142 lock_ReleaseWrite(&scp->rw);