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>
29 extern void afsi_log(char *pattern, ...);
32 int cm_enableServerLocks = 1;
34 int cm_followBackupPath = 0;
37 * Case-folding array. This was constructed by inspecting of SMBtrace output.
38 * I do not know anything more about it.
40 unsigned char cm_foldUpper[256] = {
41 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
42 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
43 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
44 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
45 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
46 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
47 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
48 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
49 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
50 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
51 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
52 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
53 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
54 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
55 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
56 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
57 0x80, 0x9a, 0x90, 0x41, 0x8e, 0x41, 0x8f, 0x80,
58 0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x8e, 0x8f,
59 0x90, 0x92, 0x92, 0x4f, 0x99, 0x4f, 0x55, 0x55,
60 0x59, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
61 0x41, 0x49, 0x4f, 0x55, 0xa5, 0xa5, 0x56, 0xa7,
62 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
63 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
64 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
65 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
66 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
67 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
68 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
69 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
70 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
71 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
72 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
76 * Case-insensitive string comparison. We used to use stricmp, but it doesn't
77 * know about 8-bit characters (e.g. 129 is lowercase u-umlaut, 154 is
78 * upper-case u-umlaut).
80 int cm_stricmp(const char *str1, const char *str2)
92 c1 = (char) cm_foldUpper[(unsigned char)(*str1++)];
93 c2 = (char) cm_foldUpper[(unsigned char)(*str2++)];
103 /* return success if we can open this file in this mode */
104 long cm_CheckOpen(cm_scache_t *scp, int openMode, int trunc, cm_user_t *userp,
112 rights |= PRSFS_READ;
113 if (openMode == 1 || openMode == 2 || trunc)
114 rights |= PRSFS_WRITE;
116 lock_ObtainWrite(&scp->rw);
118 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
119 CM_SCACHESYNC_GETSTATUS
120 | CM_SCACHESYNC_NEEDCALLBACK
121 | CM_SCACHESYNC_LOCK);
124 ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
125 scp->fileType == CM_SCACHETYPE_FILE) {
128 unsigned int sLockType;
129 LARGE_INTEGER LOffset, LLength;
131 /* Check if there's some sort of lock on the file at the
134 key = cm_GenerateKey(CM_SESSION_CMINT,0,0);
136 if (rights & PRSFS_WRITE)
139 sLockType = LOCKING_ANDX_SHARED_LOCK;
141 LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
142 LOffset.LowPart = CM_FLSHARE_OFFSET_LOW;
143 LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
144 LLength.LowPart = CM_FLSHARE_LENGTH_LOW;
146 code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
149 cm_Unlock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp);
151 /* In this case, we allow the file open to go through even
152 though we can't enforce mandatory locking on the
154 if (code == CM_ERROR_NOACCESS &&
155 !(rights & PRSFS_WRITE))
158 if (code == CM_ERROR_LOCK_NOT_GRANTED)
159 code = CM_ERROR_SHARING_VIOLATION;
163 } else if (code != 0) {
167 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
171 lock_ReleaseWrite(&scp->rw);
176 /* return success if we can open this file in this mode */
177 long cm_CheckNTOpen(cm_scache_t *scp,
178 unsigned int desiredAccess,
179 unsigned int shareAccess,
180 unsigned int createDisp,
181 afs_offs_t process_id,
182 afs_offs_t handle_id,
183 cm_user_t *userp, cm_req_t *reqp,
184 cm_lock_data_t **ldpp)
188 afs_uint16 session_id;
190 osi_assertx(ldpp != NULL, "null cm_lock_data_t");
193 /* compute the session id */
194 if (reqp->flags & CM_REQ_SOURCE_SMB)
195 session_id = CM_SESSION_SMB;
196 else if (reqp->flags & CM_REQ_SOURCE_REDIR)
197 session_id = CM_SESSION_IFS;
199 session_id = CM_SESSION_CMINT;
201 /* Ignore the SYNCHRONIZE privilege */
202 desiredAccess &= ~SYNCHRONIZE;
204 /* Always allow delete; the RPC will tell us if it's OK */
207 if (desiredAccess == DELETE)
210 /* Always allow reading attributes (Hidden, System, Readonly, ...) */
211 if (desiredAccess == FILE_READ_ATTRIBUTES)
214 if (desiredAccess & (AFS_ACCESS_READ|AFS_ACCESS_EXECUTE))
215 rights |= (scp->fileType == CM_SCACHETYPE_DIRECTORY ? PRSFS_LOOKUP : PRSFS_READ);
217 /* We used to require PRSFS_WRITE if createDisp was 4
218 (OPEN_ALWAYS) even if AFS_ACCESS_WRITE was not requested.
219 However, we don't need to do that since the existence of the
220 scp implies that we don't need to create it. */
221 if (desiredAccess & AFS_ACCESS_WRITE)
222 rights |= PRSFS_WRITE;
224 if (desiredAccess & DELETE)
225 rights |= PRSFS_DELETE;
227 lock_ObtainWrite(&scp->rw);
229 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
230 CM_SCACHESYNC_GETSTATUS
231 | CM_SCACHESYNC_NEEDCALLBACK
232 | CM_SCACHESYNC_LOCK);
235 * If the open will fail because the volume is readonly, then we will
236 * return an access denied error instead. This is to help brain-dead
237 * apps run correctly on replicated volumes.
238 * See defect 10007 for more information.
240 if (code == CM_ERROR_READONLY)
241 code = CM_ERROR_NOACCESS;
244 !(shareAccess & FILE_SHARE_WRITE) &&
245 ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
246 scp->fileType == CM_SCACHETYPE_FILE) {
248 unsigned int sLockType;
249 LARGE_INTEGER LOffset, LLength;
251 /* Check if there's some sort of lock on the file at the
254 if (rights & PRSFS_WRITE)
257 sLockType = LOCKING_ANDX_SHARED_LOCK;
259 key = cm_GenerateKey(session_id, process_id, 0);
261 /* single byte lock at offset 0x0100 0000 0000 0000 */
262 LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
263 LOffset.LowPart = CM_FLSHARE_OFFSET_LOW;
264 LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
265 LLength.LowPart = CM_FLSHARE_LENGTH_LOW;
267 code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
270 (*ldpp) = (cm_lock_data_t *)malloc(sizeof(cm_lock_data_t));
277 (*ldpp)->sLockType = sLockType;
278 (*ldpp)->LOffset.HighPart = LOffset.HighPart;
279 (*ldpp)->LOffset.LowPart = LOffset.LowPart;
280 (*ldpp)->LLength.HighPart = LLength.HighPart;
281 (*ldpp)->LLength.LowPart = LLength.LowPart;
284 * In this case, we allow the file open to go through even
285 * though we can't enforce mandatory locking on the
287 if (code == CM_ERROR_NOACCESS &&
288 !(rights & PRSFS_WRITE))
291 if (code == CM_ERROR_LOCK_NOT_GRANTED)
292 code = CM_ERROR_SHARING_VIOLATION;
295 } else if (code != 0) {
300 lock_ReleaseWrite(&scp->rw);
303 osi_Log3(afsd_logp,"cm_CheckNTOpen scp 0x%p ldp 0x%p code 0x%x", scp, *ldpp, code);
307 extern long cm_CheckNTOpenDone(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp,
308 cm_lock_data_t ** ldpp)
310 osi_Log2(afsd_logp,"cm_CheckNTOpenDone scp 0x%p ldp 0x%p", scp, ldpp ? *ldpp : 0);
311 lock_ObtainWrite(&scp->rw);
313 cm_Unlock(scp, (*ldpp)->sLockType, (*ldpp)->LOffset, (*ldpp)->LLength,
314 (*ldpp)->key, 0, userp, reqp);
318 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
319 lock_ReleaseWrite(&scp->rw);
323 * When CAP_NT_SMBS has been negotiated, deletion (of files or directories) is
324 * done in three steps:
325 * (1) open for deletion (NT_CREATE_AND_X)
326 * (2) set for deletion on close (NT_TRANSACTION2, SET_FILE_INFO)
328 * We must not do the RPC until step 3. But if we are going to return an error
329 * code (e.g. directory not empty), we must return it by step 2, otherwise most
330 * clients will not notice it. So we do a preliminary check. For deleting
331 * files, this is almost free, since we have already done the RPC to get the
332 * parent directory's status bits. But for deleting directories, we must do an
333 * additional RPC to get the directory's data to check if it is empty. Sigh.
335 long cm_CheckNTDelete(cm_scache_t *dscp, cm_scache_t *scp, cm_user_t *userp,
341 cm_dirEntry_t *dep = 0;
342 unsigned short *hashTable;
344 int BeyondPage = 0, HaveDot = 0, HaveDotDot = 0;
347 /* First check permissions */
348 lock_ObtainWrite(&scp->rw);
349 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_DELETE,
350 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
352 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
353 lock_ReleaseWrite(&scp->rw);
357 /* If deleting directory, must be empty */
359 if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
362 thyper.HighPart = 0; thyper.LowPart = 0;
363 code = buf_Get(scp, &thyper, reqp, 0, &bufferp);
367 lock_ObtainMutex(&bufferp->mx);
368 lock_ObtainWrite(&scp->rw);
371 code = cm_SyncOp(scp, bufferp, userp, reqp, 0,
372 CM_SCACHESYNC_NEEDCALLBACK
374 | CM_SCACHESYNC_BUFLOCKED);
378 if (cm_HaveBuffer(scp, bufferp, 1))
381 /* otherwise, load the buffer and try again */
382 lock_ReleaseMutex(&bufferp->mx);
383 code = cm_GetBuffer(scp, bufferp, NULL, userp, reqp);
384 lock_ReleaseWrite(&scp->rw);
385 lock_ObtainMutex(&bufferp->mx);
386 lock_ObtainWrite(&scp->rw);
387 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
392 lock_ReleaseWrite(&scp->rw);
395 /* We try to determine emptiness without looking beyond the first page,
396 * and without assuming "." and ".." are present and are on the first
397 * page (though these assumptions might, after all, be reasonable).
399 hashTable = (unsigned short *)(bufferp->datap + (32 * 5));
400 for (i=0; i<128; i++) {
401 idx = ntohs(hashTable[i]);
407 dep = (cm_dirEntry_t *)(bufferp->datap + (32 * idx));
408 if (strcmp(dep->name, ".") == 0)
410 else if (strcmp(dep->name, "..") == 0)
413 code = CM_ERROR_NOTEMPTY;
416 idx = ntohs(dep->next);
419 if (BeyondPage && HaveDot && HaveDotDot)
420 code = CM_ERROR_NOTEMPTY;
424 lock_ReleaseMutex(&bufferp->mx);
425 buf_Release(bufferp);
427 lock_ReleaseWrite(&scp->rw);
432 * Iterate through all entries in a directory.
433 * When the function funcp is called, the buffer is locked but the
434 * directory vnode is not.
436 * If the retscp parameter is not NULL, the parmp must be a
437 * cm_lookupSearch_t object.
439 long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
440 osi_hyper_t *startOffsetp, cm_user_t *userp, cm_req_t *reqp,
441 cm_scache_t **retscp)
445 cm_dirEntry_t *dep = 0;
448 osi_hyper_t dirLength;
449 osi_hyper_t bufferOffset;
450 osi_hyper_t curOffset;
454 cm_pageHeader_t *pageHeaderp;
456 long nextEntryCookie;
457 int numDirChunks; /* # of 32 byte dir chunks in this entry */
459 /* get the directory size */
460 lock_ObtainWrite(&scp->rw);
461 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_LOOKUP,
462 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
463 lock_ReleaseWrite(&scp->rw);
467 if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
468 return CM_ERROR_NOTDIR;
470 if (retscp) /* if this is a lookup call */
472 cm_lookupSearch_t* sp = parmp;
475 #ifdef AFS_FREELANCE_CLIENT
476 /* Freelance entries never end up in the DNLC because they
477 * do not have an associated cm_server_t
479 !(cm_freelanceEnabled &&
480 sp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
481 sp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
482 #else /* !AFS_FREELANCE_CLIENT */
487 int casefold = sp->caseFold;
488 sp->caseFold = 0; /* we have a strong preference for exact matches */
489 if ( *retscp = cm_dnlcLookup(scp, sp)) /* dnlc hit */
491 sp->caseFold = casefold;
494 sp->caseFold = casefold;
498 * see if we can find it using the directory hash tables.
499 * we can only do exact matches, since the hash is case
502 if (funcp != (cm_DirFuncp_t)cm_BPlusDirFoo)
511 code = cm_BeginDirOp(scp, userp, reqp, CM_DIRLOCK_READ,
512 CM_DIROP_FLAG_NONE, &dirop);
516 code = cm_BPlusDirLookup(&dirop, sp->nsearchNamep, &sp->fid);
521 code = cm_DirLookup(&dirop, sp->searchNamep, &sp->fid);
529 sp->ExactFound = TRUE;
530 *retscp = NULL; /* force caller to call cm_GetSCache() */
535 if (sp->caseFold && code == CM_ERROR_INEXACT_MATCH) {
538 sp->ExactFound = FALSE;
539 *retscp = NULL; /* force caller to call cm_GetSCache() */
543 return CM_ERROR_BPLUS_NOMATCH;
550 * XXX We only get the length once. It might change when we drop the
553 dirLength = scp->length;
556 bufferOffset.LowPart = bufferOffset.HighPart = 0;
558 curOffset = *startOffsetp;
560 curOffset.HighPart = 0;
561 curOffset.LowPart = 0;
565 /* make sure that curOffset.LowPart doesn't point to the first
566 * 32 bytes in the 2nd through last dir page, and that it
567 * doesn't point at the first 13 32-byte chunks in the first
568 * dir page, since those are dir and page headers, and don't
569 * contain useful information.
571 temp = curOffset.LowPart & (2048-1);
572 if (curOffset.HighPart == 0 && curOffset.LowPart < 2048) {
573 /* we're in the first page */
574 if (temp < 13*32) temp = 13*32;
577 /* we're in a later dir page */
578 if (temp < 32) temp = 32;
581 /* make sure the low order 5 bits are zero */
584 /* now put temp bits back ito curOffset.LowPart */
585 curOffset.LowPart &= ~(2048-1);
586 curOffset.LowPart |= temp;
588 /* check if we've passed the dir's EOF */
589 if (LargeIntegerGreaterThanOrEqualTo(curOffset, dirLength))
592 /* see if we can use the bufferp we have now; compute in which
593 * page the current offset would be, and check whether that's
594 * the offset of the buffer we have. If not, get the buffer.
596 thyper.HighPart = curOffset.HighPart;
597 thyper.LowPart = curOffset.LowPart & ~(cm_data.buf_blockSize-1);
598 if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
601 lock_ReleaseMutex(&bufferp->mx);
602 buf_Release(bufferp);
606 code = buf_Get(scp, &thyper, reqp, 0, &bufferp);
608 /* if buf_Get() fails we do not have a buffer object to lock */
613 lock_ObtainMutex(&bufferp->mx);
614 bufferOffset = thyper;
616 /* now get the data in the cache */
618 lock_ObtainWrite(&scp->rw);
619 code = cm_SyncOp(scp, bufferp, userp, reqp,
621 CM_SCACHESYNC_NEEDCALLBACK
623 | CM_SCACHESYNC_BUFLOCKED);
625 lock_ReleaseWrite(&scp->rw);
628 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
630 if (cm_HaveBuffer(scp, bufferp, 1)) {
631 lock_ReleaseWrite(&scp->rw);
635 /* otherwise, load the buffer and try again */
636 lock_ReleaseMutex(&bufferp->mx);
637 code = cm_GetBuffer(scp, bufferp, NULL, userp,
639 lock_ReleaseWrite(&scp->rw);
640 lock_ObtainMutex(&bufferp->mx);
645 lock_ReleaseMutex(&bufferp->mx);
646 buf_Release(bufferp);
650 } /* if (wrong buffer) ... */
652 /* now we have the buffer containing the entry we're interested
653 * in; copy it out if it represents a non-deleted entry.
655 entryInDir = curOffset.LowPart & (2048-1);
656 entryInBuffer = curOffset.LowPart & (cm_data.buf_blockSize - 1);
658 /* page header will help tell us which entries are free. Page
659 * header can change more often than once per buffer, since
660 * AFS 3 dir page size may be less than (but not more than) a
661 * buffer package buffer.
663 /* only look intra-buffer */
664 temp = curOffset.LowPart & (cm_data.buf_blockSize - 1);
665 temp &= ~(2048 - 1); /* turn off intra-page bits */
666 pageHeaderp = (cm_pageHeader_t *) (bufferp->datap + temp);
668 /* now determine which entry we're looking at in the page. If
669 * it is free (there's a free bitmap at the start of the dir),
670 * we should skip these 32 bytes.
672 slotInPage = (entryInDir & 0x7e0) >> 5;
673 if (!(pageHeaderp->freeBitmap[slotInPage>>3]
674 & (1 << (slotInPage & 0x7)))) {
675 /* this entry is free */
676 numDirChunks = 1; /* only skip this guy */
680 tp = bufferp->datap + entryInBuffer;
681 dep = (cm_dirEntry_t *) tp; /* now points to AFS3 dir entry */
684 * here are some consistency checks
686 if (dep->flag != CM_DIR_FFIRST ||
687 strlen(dep->name) > 256) {
688 code = CM_ERROR_INVAL;
690 "cm_ApplyDir invalid directory entry for scp %p bufp %p",
692 osi_Log4(afsd_logp,"... cell %u vol %u vnode %u uniq %u",
693 scp->fid.cell, scp->fid.volume, scp->fid.vnode, scp->fid.unique);
694 bufferp->dataVersion = CM_BUF_VERSION_BAD;
698 /* while we're here, compute the next entry's location, too,
699 * since we'll need it when writing out the cookie into the
700 * dir listing stream.
702 numDirChunks = cm_NameEntries(dep->name, NULL);
704 /* compute the offset of the cookie representing the next entry */
705 nextEntryCookie = curOffset.LowPart
706 + (CM_DIR_CHUNKSIZE * numDirChunks);
708 if (dep->fid.vnode != 0) {
709 /* this is one of the entries to use: it is not deleted */
710 code = (*funcp)(scp, dep, parmp, &curOffset);
713 } /* if we're including this name */
716 /* and adjust curOffset to be where the new cookie is */
718 thyper.LowPart = CM_DIR_CHUNKSIZE * numDirChunks;
719 curOffset = LargeIntegerAdd(thyper, curOffset);
720 } /* while copying data for dir listing */
722 /* release the mutex */
724 lock_ReleaseMutex(&bufferp->mx);
725 buf_Release(bufferp);
730 int cm_NoneUpper(normchar_t *s)
734 if (c >= 'A' && c <= 'Z')
739 int cm_NoneLower(normchar_t *s)
743 if (c >= 'a' && c <= 'z')
748 long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
751 cm_lookupSearch_t *sp;
753 normchar_t matchName[MAX_PATH];
754 int looking_for_short_name = FALSE;
756 sp = (cm_lookupSearch_t *) rockp;
758 if (cm_FsStringToNormString(dep->name, -1, matchName, lengthof(matchName)) == 0) {
759 /* Can't normalize FS string. */
764 match = cm_NormStrCmpI(matchName, sp->nsearchNamep);
766 match = cm_NormStrCmp(matchName, sp->nsearchNamep);
771 && !cm_Is8Dot3(matchName)) {
773 cm_Gen8Dot3NameInt(dep->name, &dep->fid, matchName, NULL);
775 match = cm_NormStrCmpI(matchName, sp->nsearchNamep);
777 match = cm_NormStrCmp(matchName, sp->nsearchNamep);
778 looking_for_short_name = TRUE;
788 if (!sp->caseFold || looking_for_short_name) {
789 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
790 return CM_ERROR_STOPNOW;
794 * If we get here, we are doing a case-insensitive search, and we
795 * have found a match. Now we determine what kind of match it is:
796 * exact, lower-case, upper-case, or none of the above. This is done
797 * in order to choose among matches, if there are more than one.
800 /* Exact matches are the best. */
801 match = cm_NormStrCmp(matchName, sp->nsearchNamep);
804 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
805 return CM_ERROR_STOPNOW;
808 /* Lower-case matches are next. */
811 if (cm_NoneUpper(matchName)) {
816 /* Upper-case matches are next. */
819 if (cm_NoneLower(matchName)) {
824 /* General matches are last. */
830 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
834 /* read the contents of a mount point into the appropriate string.
835 * called with write locked scp, and returns with locked scp.
837 long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
841 code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_FETCHDATA);
845 if (scp->mountPointStringp[0] &&
846 scp->mpDataVersion == scp->dataVersion) {
851 #ifdef AFS_FREELANCE_CLIENT
852 /* File servers do not have data for freelance entries */
853 if (cm_freelanceEnabled &&
854 scp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
855 scp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
857 code = cm_FreelanceFetchMountPointString(scp);
859 #endif /* AFS_FREELANCE_CLIENT */
861 char temp[MOUNTPOINTLEN];
864 /* otherwise, we have to read it in */
865 offset.LowPart = offset.HighPart = 0;
866 code = cm_GetData(scp, &offset, temp, MOUNTPOINTLEN, userp, reqp);
871 * scp->length is the actual length of the mount point string.
872 * It is current because cm_GetData merged the most up to date
873 * status info into scp and has not dropped the rwlock since.
875 if (scp->length.LowPart > MOUNTPOINTLEN - 1) {
876 code = CM_ERROR_TOOBIG;
880 if (scp->length.LowPart == 0) {
881 code = CM_ERROR_INVAL;
885 /* convert the terminating dot to a NUL */
886 temp[scp->length.LowPart - 1] = 0;
887 memcpy(scp->mountPointStringp, temp, scp->length.LowPart);
888 scp->mpDataVersion = scp->dataVersion;
892 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_FETCHDATA);
898 /* called with a locked scp and chases the mount point, yielding outScpp.
899 * scp remains write locked, just for simplicity of describing the interface.
901 long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
902 cm_req_t *reqp, cm_scache_t **outScpp)
904 fschar_t *cellNamep = NULL;
905 fschar_t *volNamep = NULL;
909 cm_volume_t *volp = NULL;
918 if (scp->mountRootFid.cell != 0 && scp->mountRootGen >= cm_data.mountRootGen) {
919 tfid = scp->mountRootFid;
920 lock_ReleaseWrite(&scp->rw);
921 code = cm_GetSCache(&tfid, NULL, outScpp, userp, reqp);
922 lock_ObtainWrite(&scp->rw);
926 /* parse the volume name */
927 mpNamep = scp->mountPointStringp;
929 return CM_ERROR_NOSUCHPATH;
930 mtType = *scp->mountPointStringp;
932 cp = cm_FsStrChr(mpNamep, _FS(':'));
934 /* cellular mount point */
935 cellNamep = (fschar_t *)malloc((cp - mpNamep) * sizeof(fschar_t));
936 cm_FsStrCpyN(cellNamep, cp - mpNamep, mpNamep + 1, cp - mpNamep - 1);
937 volNamep = cm_FsStrDup(cp+1);
939 /* now look up the cell */
940 lock_ReleaseWrite(&scp->rw);
941 cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE|CM_FLAG_NOPROBE);
942 lock_ObtainWrite(&scp->rw);
945 volNamep = cm_FsStrDup(mpNamep + 1);
947 #ifdef AFS_FREELANCE_CLIENT
949 * Mount points in the Freelance cell should default
950 * to the workstation cell.
952 if (cm_freelanceEnabled &&
953 scp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
954 scp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
956 fschar_t rootCellName[256]="";
957 cm_GetRootCellName(rootCellName);
958 cellp = cm_GetCell(rootCellName, 0);
960 #endif /* AFS_FREELANCE_CLIENT */
961 cellp = cm_FindCellByID(scp->fid.cell, 0);
965 code = CM_ERROR_NOSUCHCELL;
969 vnLength = cm_FsStrLen(volNamep);
970 if (vnLength >= 8 && cm_FsStrCmp(volNamep + vnLength - 7, ".backup") == 0)
971 targetType = BACKVOL;
972 else if (vnLength >= 10
973 && cm_FsStrCmp(volNamep + vnLength - 9, ".readonly") == 0)
978 /* check for backups within backups */
979 if (targetType == BACKVOL
980 && (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
981 == CM_SCACHEFLAG_RO) {
982 code = CM_ERROR_NOSUCHVOLUME;
986 /* now we need to get the volume */
987 lock_ReleaseWrite(&scp->rw);
988 if (cm_VolNameIsID(volNamep)) {
989 code = cm_FindVolumeByID(cellp, atoi(volNamep), userp, reqp,
990 CM_GETVOL_FLAG_CREATE, &volp);
992 code = cm_FindVolumeByName(cellp, volNamep, userp, reqp,
993 CM_GETVOL_FLAG_CREATE, &volp);
995 lock_ObtainWrite(&scp->rw);
998 afs_uint32 cell, volume;
999 cm_vol_state_t *statep;
1001 cell = cellp->cellID;
1003 /* if the mt pt originates in a .backup volume (not a .readonly)
1004 * and FollowBackupPath is active, and if there is a .backup
1005 * volume for the target, then use the .backup of the target
1006 * instead of the read-write.
1008 if (cm_followBackupPath &&
1009 volp->vol[BACKVOL].ID != 0 &&
1010 (dscp->flags & (CM_SCACHEFLAG_RO|CM_SCACHEFLAG_PURERO)) == CM_SCACHEFLAG_RO &&
1011 (targetType == RWVOL || targetType == ROVOL && volp->vol[ROVOL].ID == 0)
1013 targetType = BACKVOL;
1015 /* if the mt pt is in a read-only volume (not just a
1016 * backup), and if there is a read-only volume for the
1017 * target, and if this is a targetType '#' mount point, use
1018 * the read-only, otherwise use the one specified.
1020 else if (mtType == '#' && targetType == RWVOL &&
1021 (scp->flags & CM_SCACHEFLAG_PURERO) &&
1022 volp->vol[ROVOL].ID != 0) {
1026 lock_ObtainWrite(&volp->rw);
1027 statep = cm_VolumeStateByType(volp, targetType);
1028 volume = statep->ID;
1029 statep->dotdotFid = dscp->fid;
1030 lock_ReleaseWrite(&volp->rw);
1032 /* the rest of the fid is a magic number */
1033 cm_SetFid(&scp->mountRootFid, cell, volume, 1, 1);
1034 scp->mountRootGen = cm_data.mountRootGen;
1036 tfid = scp->mountRootFid;
1037 lock_ReleaseWrite(&scp->rw);
1038 code = cm_GetSCache(&tfid, NULL, outScpp, userp, reqp);
1039 lock_ObtainWrite(&scp->rw);
1052 long cm_LookupInternal(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_user_t *userp,
1053 cm_req_t *reqp, cm_scache_t **outScpp)
1056 int dnlcHit = 1; /* did we hit in the dnlc? yes, we did */
1057 cm_scache_t *tscp = NULL;
1058 cm_scache_t *mountedScp;
1059 cm_lookupSearch_t rock;
1061 normchar_t *nnamep = NULL;
1062 fschar_t *fnamep = NULL;
1067 memset(&rock, 0, sizeof(rock));
1069 if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
1070 && cm_ClientStrCmp(cnamep, _C("..")) == 0) {
1071 if (dscp->dotdotFid.volume == 0)
1072 return CM_ERROR_NOSUCHVOLUME;
1073 rock.fid = dscp->dotdotFid;
1075 } else if (cm_ClientStrCmp(cnamep, _C(".")) == 0) {
1076 rock.fid = dscp->fid;
1080 nnamep = cm_ClientStringToNormStringAlloc(cnamep, -1, NULL);
1082 code = CM_ERROR_NOSUCHFILE;
1085 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
1087 code = CM_ERROR_NOSUCHFILE;
1092 if (flags & CM_FLAG_NOMOUNTCHASE) {
1093 /* In this case, we should go and call cm_Dir* functions
1094 directly since the following cm_ApplyDir() function will
1102 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ,
1103 CM_DIROP_FLAG_NONE, &dirop);
1106 code = cm_BPlusDirLookup(&dirop, nnamep, &rock.fid);
1111 code = cm_DirLookup(&dirop, fnamep, &rock.fid);
1113 cm_EndDirOp(&dirop);
1123 if (code == CM_ERROR_INEXACT_MATCH && (flags & CM_FLAG_CASEFOLD)) {
1130 code = CM_ERROR_BPLUS_NOMATCH;
1136 rock.fid.cell = dscp->fid.cell;
1137 rock.fid.volume = dscp->fid.volume;
1138 rock.searchNamep = fnamep;
1139 rock.nsearchNamep = nnamep;
1140 rock.caseFold = (flags & CM_FLAG_CASEFOLD);
1141 rock.hasTilde = ((cm_ClientStrChr(cnamep, '~') != NULL) ? 1 : 0);
1143 /* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
1144 code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
1145 (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
1147 /* code == 0 means we fell off the end of the dir, while stopnow means
1148 * that we stopped early, probably because we found the entry we're
1149 * looking for. Any other non-zero code is an error.
1151 if (code && code != CM_ERROR_STOPNOW) {
1152 /* if the cm_scache_t we are searching in is not a directory
1153 * we must return path not found because the error
1154 * is to describe the final component not an intermediary
1156 if (code == CM_ERROR_NOTDIR) {
1157 if (flags & CM_FLAG_CHECKPATH)
1158 code = CM_ERROR_NOSUCHPATH;
1160 code = CM_ERROR_NOSUCHFILE;
1166 getroot = (dscp==cm_data.rootSCachep) ;
1168 if (!(cm_freelanceEnabled && cm_freelanceDiscovery) || !getroot) {
1169 if (flags & CM_FLAG_CHECKPATH)
1170 code = CM_ERROR_NOSUCHPATH;
1172 code = CM_ERROR_NOSUCHFILE;
1175 else if (!cm_ClientStrChr(cnamep, '#') &&
1176 !cm_ClientStrChr(cnamep, '%') &&
1177 cm_ClientStrCmpI(cnamep, _C("srvsvc")) &&
1178 cm_ClientStrCmpI(cnamep, _C("wkssvc")) &&
1179 cm_ClientStrCmpI(cnamep, _C("ipc$")))
1181 /* nonexistent dir on freelance root, so add it */
1182 fschar_t fullname[CELL_MAXNAMELEN + 1] = "."; /* +1 so that when we skip the . the size is still CELL_MAXNAMELEN */
1186 osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %S",
1187 osi_LogSaveClientString(afsd_logp,cnamep));
1190 * There is an ugly behavior where a share name "foo" will be searched
1191 * for as "fo". If the searched for name differs by an already existing
1192 * symlink or mount point in the Freelance directory, do not add the
1193 * new value automatically.
1197 fnlen = strlen(fnamep);
1198 if ( fnamep[fnlen-1] == '.') {
1199 fnamep[fnlen-1] = '\0';
1204 if (cnamep[0] == '.') {
1205 if (cm_GetCell_Gen(&fnamep[1], &fullname[1], CM_FLAG_CREATE)) {
1207 code = cm_FreelanceAddMount(fullname, &fullname[1], "root.cell", 1, &rock.fid);
1208 if ( cm_FsStrCmpI(&fnamep[1], &fullname[1])) {
1210 * Do not permit symlinks that are one of:
1211 * . the cellname followed by a dot
1212 * . the cellname minus a single character
1213 * . a substring of the cellname that does not consist of full components
1215 if ( cm_strnicmp_utf8(&fnamep[1], fullname, (int)fnlen-1) == 0 &&
1216 (fnlen-1 == strlen(fullname)-1 || fullname[fnlen-1] != '.'))
1218 /* do not add; substitute fullname for the search */
1220 fnamep = malloc(strlen(fullname)+2);
1222 strncpy(&fnamep[1], fullname, strlen(fullname)+1);
1225 code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
1230 if (cm_GetCell_Gen(fnamep, fullname, CM_FLAG_CREATE)) {
1232 code = cm_FreelanceAddMount(fullname, fullname, "root.cell", 0, &rock.fid);
1233 if ( cm_FsStrCmpI(fnamep, fullname)) {
1235 * Do not permit symlinks that are one of:
1236 * . the cellname followed by a dot
1237 * . the cellname minus a single character
1238 * . a substring of the cellname that does not consist of full components
1240 if ( cm_strnicmp_utf8(fnamep, fullname, (int)fnlen-1) == 0 &&
1241 (fnlen == strlen(fullname)-1 || fullname[fnlen] != '.'))
1243 /* do not add; substitute fullname for the search */
1245 fnamep = strdup(fullname);
1249 code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
1258 nnamep = cm_FsStringToNormStringAlloc(fnamep, -1, NULL);
1262 if (!found || code) { /* add mount point failed, so give up */
1263 if (flags & CM_FLAG_CHECKPATH)
1264 code = CM_ERROR_NOSUCHPATH;
1266 code = CM_ERROR_NOSUCHFILE;
1269 tscp = NULL; /* to force call of cm_GetSCache */
1271 if (flags & CM_FLAG_CHECKPATH)
1272 code = CM_ERROR_NOSUCHPATH;
1274 code = CM_ERROR_NOSUCHFILE;
1280 if ( !tscp ) /* we did not find it in the dnlc */
1283 code = cm_GetSCache(&rock.fid, &dscp->fid, &tscp, userp, reqp);
1287 /* tscp is now held */
1289 lock_ObtainWrite(&tscp->rw);
1292 * Do not get status if we do not already have a callback or know the type.
1293 * The process of reading the mount point string will obtain status information
1294 * in a single RPC. No reason to add a second round trip.
1296 * If we do have a callback, use cm_SyncOp to get status in case the
1297 * current cm_user_t is not the same as the one that obtained the
1298 * mount point string contents.
1300 if (cm_HaveCallback(tscp) || tscp->fileType == CM_SCACHETYPE_UNKNOWN) {
1301 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1302 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1304 lock_ReleaseWrite(&tscp->rw);
1305 cm_ReleaseSCache(tscp);
1308 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1310 /* tscp is now locked */
1312 if (!(flags & CM_FLAG_NOMOUNTCHASE)
1313 && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
1314 /* mount points are funny: they have a volume name to mount
1317 code = cm_ReadMountPoint(tscp, userp, reqp);
1319 code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
1321 lock_ReleaseWrite(&tscp->rw);
1322 cm_ReleaseSCache(tscp);
1329 lock_ReleaseWrite(&tscp->rw);
1332 /* copy back pointer */
1335 /* insert scache in dnlc */
1336 if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
1337 /* lock the directory entry to prevent racing callback revokes */
1338 lock_ObtainRead(&dscp->rw);
1339 if ( dscp->cbServerp != NULL && dscp->cbExpires > 0 ) {
1340 /* TODO: reuse nnamep from above */
1343 nnamep = cm_ClientStringToNormStringAlloc(cnamep, -1, NULL);
1345 cm_dnlcEnter(dscp, nnamep, tscp);
1347 lock_ReleaseRead(&dscp->rw);
1364 int cm_ExpandSysName(cm_req_t * reqp, clientchar_t *inp, clientchar_t *outp, long outSizeCch, unsigned int index)
1369 int use_sysname64 = 0;
1371 if (cm_sysName64Count > 0 && reqp && !(reqp->flags & CM_REQ_WOW64) && (reqp->flags & CM_REQ_SOURCE_REDIR))
1375 tp = cm_ClientStrRChr(inp, '@');
1377 return 0; /* no @sys */
1379 if (cm_ClientStrCmp(tp, _C("@sys")) != 0)
1380 return 0; /* no @sys */
1382 /* caller just wants to know if this is a valid @sys type of name */
1387 if (use_sysname64 && index >= cm_sysName64Count)
1391 if (index >= cm_sysNameCount)
1394 /* otherwise generate the properly expanded @sys name */
1395 prefixCount = (int)(tp - inp);
1397 cm_ClientStrCpyN(outp, outSizeCch, inp, prefixCount); /* copy out "a." from "a.@sys" */
1398 outp[prefixCount] = 0; /* null terminate the "a." */
1401 cm_ClientStrCat(outp, outSizeCch, cm_sysName64List[index]);
1404 cm_ClientStrCat(outp, outSizeCch, cm_sysNameList[index]);
1409 long cm_EvaluateVolumeReference(clientchar_t * namep, long flags, cm_user_t * userp,
1410 cm_req_t *reqp, cm_scache_t ** outScpp)
1412 afs_uint32 code = 0;
1413 fschar_t cellName[CELL_MAXNAMELEN];
1414 fschar_t volumeName[VL_MAXNAMELEN];
1418 fschar_t * fnamep = NULL;
1420 cm_cell_t * cellp = NULL;
1421 cm_volume_t * volp = NULL;
1425 int mountType = RWVOL;
1427 osi_Log1(afsd_logp, "cm_EvaluateVolumeReference for string [%S]",
1428 osi_LogSaveClientString(afsd_logp, namep));
1430 if (cm_ClientStrCmpNI(namep, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH) != 0) {
1431 goto _exit_invalid_path;
1434 /* namep is assumed to look like the following:
1436 @vol:<cellname>%<volume>\0
1438 @vol:<cellname>#<volume>\0
1442 fnamep = cm_ClientStringToFsStringAlloc(namep, -1, NULL);
1443 cp = fnamep + CM_PREFIX_VOL_CCH; /* cp points to cell name, hopefully */
1444 tp = cm_FsStrChr(cp, '%');
1446 tp = cm_FsStrChr(cp, '#');
1448 (len = tp - cp) == 0 ||
1449 len > CELL_MAXNAMELEN)
1450 goto _exit_invalid_path;
1451 cm_FsStrCpyN(cellName, lengthof(cellName), cp, len);
1456 cp = tp+1; /* cp now points to volume, supposedly */
1457 cm_FsStrCpy(volumeName, lengthof(volumeName), cp);
1459 /* OK, now we have the cell and the volume */
1460 osi_Log2(afsd_logp, " Found cell [%s] and volume [%s]",
1461 osi_LogSaveFsString(afsd_logp, cellName),
1462 osi_LogSaveFsString(afsd_logp, volumeName));
1464 cellp = cm_GetCell(cellName, CM_FLAG_CREATE);
1465 if (cellp == NULL) {
1466 goto _exit_invalid_path;
1469 len = cm_FsStrLen(volumeName);
1470 if (len >= 8 && cm_FsStrCmp(volumeName + len - 7, ".backup") == 0)
1472 else if (len >= 10 &&
1473 cm_FsStrCmp(volumeName + len - 9, ".readonly") == 0)
1478 if (cm_VolNameIsID(volumeName)) {
1479 code = cm_FindVolumeByID(cellp, atoi(volumeName), userp, reqp,
1480 CM_GETVOL_FLAG_CREATE, &volp);
1482 code = cm_FindVolumeByName(cellp, volumeName, userp, reqp,
1483 CM_GETVOL_FLAG_CREATE, &volp);
1489 if (volType == BACKVOL)
1490 volume = volp->vol[BACKVOL].ID;
1491 else if (volType == ROVOL ||
1492 (volType == RWVOL && mountType == ROVOL && volp->vol[ROVOL].ID != 0))
1493 volume = volp->vol[ROVOL].ID;
1495 volume = volp->vol[RWVOL].ID;
1497 cm_SetFid(&fid, cellp->cellID, volume, 1, 1);
1499 code = cm_GetSCache(&fid, NULL, outScpp, userp, reqp);
1512 if (flags & CM_FLAG_CHECKPATH)
1513 return CM_ERROR_NOSUCHPATH;
1515 return CM_ERROR_NOSUCHFILE;
1518 #ifdef DEBUG_REFCOUNT
1519 long cm_LookupDbg(cm_scache_t *dscp, clientchar_t *namep, long flags, cm_user_t *userp,
1520 cm_req_t *reqp, cm_scache_t **outScpp, char * file, long line)
1522 long cm_Lookup(cm_scache_t *dscp, clientchar_t *namep, long flags, cm_user_t *userp,
1523 cm_req_t *reqp, cm_scache_t **outScpp)
1527 clientchar_t tname[AFSPATHMAX];
1528 int sysNameIndex = 0;
1529 cm_scache_t *scp = NULL;
1531 #ifdef DEBUG_REFCOUNT
1532 afsi_log("%s:%d cm_Lookup dscp 0x%p ref %d", file, line, dscp, dscp->refCount, file, line);
1533 osi_Log2(afsd_logp, "cm_Lookup dscp 0x%p ref %d", dscp, dscp->refCount);
1536 if ( cm_ClientStrCmpI(namep,_C(SMB_IOCTL_FILENAME_NOSLASH)) == 0 ) {
1537 if (flags & CM_FLAG_CHECKPATH)
1538 return CM_ERROR_NOSUCHPATH;
1540 return CM_ERROR_NOSUCHFILE;
1543 if (dscp == cm_data.rootSCachep &&
1544 cm_ClientStrCmpNI(namep, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH) == 0) {
1545 return cm_EvaluateVolumeReference(namep, flags, userp, reqp, outScpp);
1548 if (cm_ExpandSysName(reqp, namep, NULL, 0, 0) > 0) {
1549 for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
1550 code = cm_ExpandSysName(reqp, namep, tname, lengthof(tname), sysNameIndex);
1552 code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
1553 #ifdef DEBUG_REFCOUNT
1554 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);
1555 osi_Log3(afsd_logp, "cm_LookupInternal (1) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1563 cm_ReleaseSCache(scp);
1567 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1568 #ifdef DEBUG_REFCOUNT
1569 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);
1570 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1577 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1578 #ifdef DEBUG_REFCOUNT
1579 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);
1580 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1586 /* None of the possible sysName expansions could be found */
1587 if (flags & CM_FLAG_CHECKPATH)
1588 return CM_ERROR_NOSUCHPATH;
1590 return CM_ERROR_NOSUCHFILE;
1593 /*! \brief Unlink a file name
1595 Encapsulates a call to RXAFS_RemoveFile().
1597 \param[in] dscp cm_scache_t pointing at the directory containing the
1598 name to be unlinked.
1600 \param[in] fnamep Original name to be unlinked. This is the
1601 name that will be passed into the RXAFS_RemoveFile() call.
1602 This parameter is optional. If not provided, the value will
1605 \param[in] came Client name to be unlinked. This name will be used
1606 to update the local directory caches.
1608 \param[in] userp cm_user_t for the request.
1610 \param[in] reqp Request tracker.
1613 long cm_Unlink(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t * cnamep,
1614 cm_user_t *userp, cm_req_t *reqp)
1620 AFSFetchStatus newDirStatus;
1622 struct rx_connection * rxconnp;
1624 cm_scache_t *scp = NULL;
1625 int free_fnamep = FALSE;
1628 memset(&volSync, 0, sizeof(volSync));
1630 if (fnamep == NULL) {
1633 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ,
1634 CM_DIROP_FLAG_NONE, &dirop);
1636 code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
1639 cm_EndDirOp(&dirop);
1646 #ifdef AFS_FREELANCE_CLIENT
1647 if (cm_freelanceEnabled && dscp == cm_data.rootSCachep) {
1648 /* deleting a mount point from the root dir. */
1649 code = cm_FreelanceRemoveMount(fnamep);
1654 code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
1658 /* Check for RO volume */
1659 if (dscp->flags & CM_SCACHEFLAG_RO) {
1660 code = CM_ERROR_READONLY;
1664 /* make sure we don't screw up the dir status during the merge */
1665 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE,
1666 CM_DIROP_FLAG_NONE, &dirop);
1668 lock_ObtainWrite(&dscp->rw);
1669 sflags = CM_SCACHESYNC_STOREDATA;
1670 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1671 lock_ReleaseWrite(&dscp->rw);
1673 cm_EndDirOp(&dirop);
1678 InterlockedIncrement(&dscp->activeRPCs);
1680 afsFid.Volume = dscp->fid.volume;
1681 afsFid.Vnode = dscp->fid.vnode;
1682 afsFid.Unique = dscp->fid.unique;
1684 osi_Log1(afsd_logp, "CALL RemoveFile scp 0x%p", dscp);
1686 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
1690 rxconnp = cm_GetRxConn(connp);
1691 code = RXAFS_RemoveFile(rxconnp, &afsFid, fnamep,
1692 &newDirStatus, &volSync);
1693 rx_PutConnection(rxconnp);
1695 } while (cm_Analyze(connp, userp, reqp, &dscp->fid, NULL, 1, &newDirStatus, &volSync, NULL, NULL, code));
1696 code = cm_MapRPCError(code, reqp);
1699 osi_Log1(afsd_logp, "CALL RemoveFile FAILURE, code 0x%x", code);
1701 osi_Log0(afsd_logp, "CALL RemoveFile SUCCESS");
1704 lock_ObtainWrite(&dirop.scp->dirlock);
1705 dirop.lockType = CM_DIRLOCK_WRITE;
1707 lock_ObtainWrite(&dscp->rw);
1708 cm_dnlcRemove(dscp, cnamep);
1710 code = cm_MergeStatus(NULL, dscp, &newDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
1712 if (cm_CheckDirOpForSingleChange(&dirop) && cnamep) {
1713 lock_ReleaseWrite(&dscp->rw);
1714 cm_DirDeleteEntry(&dirop, fnamep);
1716 cm_BPlusDirDeleteEntry(&dirop, cnamep);
1718 lock_ObtainWrite(&dscp->rw);
1721 InterlockedDecrement(&scp->activeRPCs);
1722 if (code == CM_ERROR_NOSUCHFILE) {
1723 /* windows would not have allowed the request to delete the file
1724 * if it did not believe the file existed. therefore, we must
1725 * have an inconsistent view of the world.
1727 dscp->cbServerp = NULL;
1731 cm_SyncOpDone(dscp, NULL, sflags);
1732 lock_ReleaseWrite(&dscp->rw);
1734 cm_EndDirOp(&dirop);
1736 if (invalidate && RDR_Initialized &&
1737 scp->fileType != CM_SCACHETYPE_FILE && scp->fileType != CM_SCACHETYPE_DIRECTORY)
1738 RDR_InvalidateObject(dscp->fid.cell, dscp->fid.volume, dscp->fid.vnode,
1739 dscp->fid.unique, dscp->fid.hash,
1740 dscp->fileType, AFS_INVALIDATE_DATA_VERSION);
1744 lock_ObtainWrite(&scp->rw);
1745 if (--scp->linkCount == 0) {
1746 scp->flags |= CM_SCACHEFLAG_DELETED;
1747 lock_ObtainWrite(&cm_scacheLock);
1748 cm_AdjustScacheLRU(scp);
1749 cm_RemoveSCacheFromHashTable(scp);
1750 lock_ReleaseWrite(&cm_scacheLock);
1752 cm_DiscardSCache(scp);
1753 lock_ReleaseWrite(&scp->rw);
1754 if (RDR_Initialized && !(reqp->flags & CM_REQ_SOURCE_REDIR))
1755 RDR_InvalidateObject(scp->fid.cell, scp->fid.volume, scp->fid.vnode,
1756 scp->fid.unique, scp->fid.hash,
1757 scp->fileType, AFS_INVALIDATE_DELETED);
1759 cm_ReleaseSCache(scp);
1769 /* called with a write locked vnode, and fills in the link info.
1770 * returns this the vnode still write locked.
1772 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1776 lock_AssertWrite(&linkScp->rw);
1777 if (!linkScp->mountPointStringp[0] ||
1778 linkScp->mpDataVersion != linkScp->dataVersion) {
1780 #ifdef AFS_FREELANCE_CLIENT
1781 /* File servers do not have data for freelance entries */
1782 if (cm_freelanceEnabled &&
1783 linkScp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
1784 linkScp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
1786 code = cm_FreelanceFetchMountPointString(linkScp);
1788 #endif /* AFS_FREELANCE_CLIENT */
1790 char temp[MOUNTPOINTLEN];
1793 /* read the link data from the file server */
1794 offset.LowPart = offset.HighPart = 0;
1795 code = cm_GetData(linkScp, &offset, temp, MOUNTPOINTLEN, userp, reqp);
1800 * linkScp->length is the actual length of the symlink target string.
1801 * It is current because cm_GetData merged the most up to date
1802 * status info into scp and has not dropped the rwlock since.
1804 if (linkScp->length.LowPart > MOUNTPOINTLEN - 1)
1805 return CM_ERROR_TOOBIG;
1806 if (linkScp->length.LowPart == 0)
1807 return CM_ERROR_INVAL;
1809 /* make sure we are NUL terminated */
1810 temp[linkScp->length.LowPart] = 0;
1811 memcpy(linkScp->mountPointStringp, temp, linkScp->length.LowPart + 1);
1812 linkScp->mpDataVersion = linkScp->dataVersion;
1815 if ( !strnicmp(linkScp->mountPointStringp, "msdfs:", strlen("msdfs:")) )
1816 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1818 } /* don't have symlink contents cached */
1823 /* called with a held vnode and a path suffix, with the held vnode being a
1824 * symbolic link. Our goal is to generate a new path to interpret, and return
1825 * this new path in newSpaceBufferp. If the new vnode is relative to a dir
1826 * other than the directory containing the symbolic link, then the new root is
1827 * returned in *newRootScpp, otherwise a null is returned there.
1829 long cm_AssembleLink(cm_scache_t *linkScp, fschar_t *pathSuffixp,
1830 cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1831 cm_user_t *userp, cm_req_t *reqp)
1838 *newRootScpp = NULL;
1839 *newSpaceBufferp = NULL;
1841 lock_ObtainWrite(&linkScp->rw);
1843 * Do not get status if we do not already have a callback.
1844 * The process of reading the symlink string will obtain status information
1845 * in a single RPC. No reason to add a second round trip.
1847 * If we do have a callback, use cm_SyncOp to get status in case the
1848 * current cm_user_t is not the same as the one that obtained the
1849 * symlink string contents.
1851 if (cm_HaveCallback(linkScp)) {
1852 code = cm_SyncOp(linkScp, NULL, userp, reqp, 0,
1853 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1855 lock_ReleaseWrite(&linkScp->rw);
1856 cm_ReleaseSCache(linkScp);
1859 cm_SyncOpDone(linkScp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1861 code = cm_HandleLink(linkScp, userp, reqp);
1865 /* if we may overflow the buffer, bail out; buffer is signficantly
1866 * bigger than max path length, so we don't really have to worry about
1867 * being a little conservative here.
1869 if (cm_FsStrLen(linkScp->mountPointStringp) + cm_FsStrLen(pathSuffixp) + 2
1870 >= CM_UTILS_SPACESIZE) {
1871 code = CM_ERROR_TOOBIG;
1875 tsp = cm_GetSpace();
1876 linkp = linkScp->mountPointStringp;
1877 if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1878 if (strlen(linkp) > cm_mountRootLen)
1879 StringCbCopyA((char *) tsp->data, sizeof(tsp->data), linkp+cm_mountRootLen+1);
1882 *newRootScpp = cm_RootSCachep(userp, reqp);
1883 cm_HoldSCache(*newRootScpp);
1884 } else if (linkp[0] == '\\' && linkp[1] == '\\') {
1885 if (!strnicmp(&linkp[2], cm_NetbiosName, (len = (long)strlen(cm_NetbiosName))))
1887 char * p = &linkp[len + 3];
1888 if (strnicmp(p, "all", 3) == 0)
1891 StringCbCopyA(tsp->data, sizeof(tsp->data), p);
1892 for (p = tsp->data; *p; p++) {
1896 *newRootScpp = cm_RootSCachep(userp, reqp);
1897 cm_HoldSCache(*newRootScpp);
1899 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1900 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1901 code = CM_ERROR_PATH_NOT_COVERED;
1903 } else if ( linkScp->fileType == CM_SCACHETYPE_DFSLINK ||
1904 !strnicmp(linkp, "msdfs:", (len = (long)strlen("msdfs:"))) ) {
1905 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1906 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1907 code = CM_ERROR_PATH_NOT_COVERED;
1908 } else if (*linkp == '\\' || *linkp == '/') {
1910 /* formerly, this was considered to be from the AFS root,
1911 * but this seems to create problems. instead, we will just
1912 * reject the link */
1913 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp+1);
1914 *newRootScpp = cm_RootSCachep(userp, reqp);
1915 cm_HoldSCache(*newRootScpp);
1917 /* we still copy the link data into the response so that
1918 * the user can see what the link points to
1920 linkScp->fileType = CM_SCACHETYPE_INVALID;
1921 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1922 code = CM_ERROR_NOSUCHPATH;
1925 /* a relative link */
1926 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1928 if (pathSuffixp[0] != 0) { /* if suffix string is non-null */
1929 StringCchCatA(tsp->data,lengthof(tsp->data), "\\");
1930 StringCchCatA(tsp->data,lengthof(tsp->data), pathSuffixp);
1934 clientchar_t * cpath = cm_FsStringToClientStringAlloc(tsp->data, -1, NULL);
1935 if (cpath != NULL) {
1936 cm_ClientStrCpy(tsp->wdata, lengthof(tsp->wdata), cpath);
1938 *newSpaceBufferp = tsp;
1940 code = CM_ERROR_NOSUCHPATH;
1947 if (code == CM_ERROR_PATH_NOT_COVERED && reqp->tidPathp && reqp->relPathp) {
1948 cm_VolStatus_Notify_DFS_Mapping(linkScp, reqp->tidPathp, reqp->relPathp);
1953 lock_ReleaseWrite(&linkScp->rw);
1956 #ifdef DEBUG_REFCOUNT
1957 long cm_NameIDbg(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
1958 cm_user_t *userp, clientchar_t *tidPathp, cm_req_t *reqp,
1959 cm_scache_t **outScpp,
1960 char * file, long line)
1962 long cm_NameI(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
1963 cm_user_t *userp, clientchar_t *tidPathp,
1964 cm_req_t *reqp, cm_scache_t **outScpp)
1968 clientchar_t *tp; /* ptr moving through input buffer */
1969 clientchar_t tc; /* temp char */
1970 int haveComponent; /* has new component started? */
1971 clientchar_t component[AFSPATHMAX]; /* this is the new component */
1972 clientchar_t *cp; /* component name being assembled */
1973 cm_scache_t *tscp; /* current location in the hierarchy */
1974 cm_scache_t *nscp; /* next dude down */
1975 cm_scache_t *dirScp; /* last dir we searched */
1976 cm_scache_t *linkScp; /* new root for the symlink we just
1978 cm_space_t *psp; /* space for current path, if we've hit
1980 cm_space_t *tempsp; /* temp vbl */
1981 clientchar_t *restp; /* rest of the pathname to interpret */
1982 int symlinkCount; /* count of # of symlinks traversed */
1983 int extraFlag; /* avoid chasing mt pts for dir cmd */
1984 int phase = 1; /* 1 = tidPathp, 2 = pathp */
1985 cm_fid_t fids[MAX_FID_COUNT]; /* array of fids processed in this path walk */
1986 int fid_count = 0; /* number of fids processed in this path walk */
1991 #ifdef DEBUG_REFCOUNT
1992 afsi_log("%s:%d cm_NameI rootscp 0x%p ref %d", file, line, rootSCachep, rootSCachep->refCount);
1993 osi_Log4(afsd_logp,"cm_NameI rootscp 0x%p path %S tidpath %S flags 0x%x",
1994 rootSCachep, pathp ? pathp : L"<NULL>", tidPathp ? tidPathp : L"<NULL>",
2009 cm_HoldSCache(tscp);
2017 /* map Unix slashes into DOS ones so we can interpret Unix
2023 if (!haveComponent) {
2026 } else if (tc == 0) {
2040 /* we have a component here */
2041 if (tc == 0 || tc == '\\') {
2042 /* end of the component; we're at the last
2043 * component if tc == 0. However, if the last
2044 * is a symlink, we have more to do.
2046 *cp++ = 0; /* add null termination */
2049 if (tscp == cm_RootSCachep(userp, reqp)) {
2050 code = cm_Lookup(tscp, component, CM_FLAG_CHECKPATH, userp, reqp, &nscp);
2052 if ((code == CM_ERROR_NOSUCHPATH || code == CM_ERROR_NOSUCHFILE ||
2053 code == CM_ERROR_BPLUS_NOMATCH) &&
2054 tscp == cm_data.rootSCachep) {
2056 clientchar_t volref[AFSPATHMAX];
2058 if (wcschr(component, '%') != NULL || wcschr(component, '#') != NULL) {
2060 * A volume reference: <cell>{%,#}<volume> -> @vol:<cell>{%,#}<volume>
2062 cm_ClientStrCpyN(volref, AFSPATHMAX, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH);
2063 cm_ClientStrCat(volref, AFSPATHMAX, component);
2065 code = cm_EvaluateVolumeReference(volref, CM_FLAG_CHECKPATH, userp, reqp, &nscp);
2067 #ifdef AFS_FREELANCE_CLIENT
2068 else if (tscp->fid.cell == AFS_FAKE_ROOT_CELL_ID && tscp->fid.volume == AFS_FAKE_ROOT_VOL_ID &&
2069 tscp->fid.vnode == 1 && tscp->fid.unique == 1) {
2071 * If this is the Freelance volume root directory then treat unrecognized
2072 * names as cell names and attempt to find the appropriate "root.cell".
2074 cm_ClientStrCpyN(volref, AFSPATHMAX, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH);
2075 if (component[0] == L'.') {
2076 cm_ClientStrCat(volref, AFSPATHMAX, &component[1]);
2077 cm_ClientStrCatN(volref, AFSPATHMAX, L"%", sizeof(WCHAR));
2079 cm_ClientStrCat(volref, AFSPATHMAX, component);
2080 cm_ClientStrCatN(volref, AFSPATHMAX, L"#", sizeof(WCHAR));
2082 cm_ClientStrCatN(volref, AFSPATHMAX, L"root.cell", 9 * sizeof(WCHAR));
2084 code = cm_EvaluateVolumeReference(volref, CM_FLAG_CHECKPATH, userp, reqp, &nscp);
2089 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
2090 extraFlag = CM_FLAG_NOMOUNTCHASE;
2091 code = cm_Lookup(tscp, component,
2093 userp, reqp, &nscp);
2096 if (!cm_ClientStrCmp(component,_C("..")) ||
2097 !cm_ClientStrCmp(component,_C("."))) {
2099 * roll back the fid list until we find the
2100 * fid that matches where we are now. Its not
2101 * necessarily one or two fids because they
2102 * might have been symlinks or mount points or
2103 * both that were crossed.
2105 for ( i=fid_count-1; i>=0; i--) {
2106 if (!cm_FidCmp(&nscp->fid, &fids[i]))
2111 /* add the new fid to the list */
2112 if (fid_count == MAX_FID_COUNT) {
2113 code = CM_ERROR_TOO_MANY_SYMLINKS;
2114 cm_ReleaseSCache(nscp);
2118 fids[fid_count++] = nscp->fid;
2123 cm_ReleaseSCache(tscp);
2125 cm_ReleaseSCache(dirScp);
2128 if ((code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH) &&
2129 tscp->fileType == CM_SCACHETYPE_SYMLINK) {
2130 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_NOSUCHPATH");
2131 return CM_ERROR_NOSUCHPATH;
2133 osi_Log1(afsd_logp,"cm_NameI code 0x%x", code);
2138 haveComponent = 0; /* component done */
2140 cm_ReleaseSCache(dirScp);
2141 dirScp = tscp; /* for some symlinks */
2142 tscp = nscp; /* already held */
2144 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
2147 cm_ReleaseSCache(dirScp);
2153 /* now, if tscp is a symlink, we should follow it and
2154 * assemble the path again.
2156 lock_ObtainWrite(&tscp->rw);
2157 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
2158 CM_SCACHESYNC_GETSTATUS
2159 | CM_SCACHESYNC_NEEDCALLBACK);
2161 lock_ReleaseWrite(&tscp->rw);
2162 cm_ReleaseSCache(tscp);
2165 cm_ReleaseSCache(dirScp);
2170 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2172 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
2173 /* this is a symlink; assemble a new buffer */
2174 lock_ReleaseWrite(&tscp->rw);
2175 if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
2176 cm_ReleaseSCache(tscp);
2179 cm_ReleaseSCache(dirScp);
2184 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_TOO_MANY_SYMLINKS");
2185 return CM_ERROR_TOO_MANY_SYMLINKS;
2195 /* TODO: make this better */
2196 frestp = cm_ClientStringToFsStringAlloc(restp, -1, NULL);
2197 code = cm_AssembleLink(tscp, frestp, &linkScp, &tempsp, userp, reqp);
2201 if (code == 0 && linkScp != NULL) {
2202 if (linkScp == cm_data.rootSCachep) {
2206 for ( i=0; i<fid_count; i++) {
2207 if ( !cm_FidCmp(&linkScp->fid, &fids[i]) ) {
2208 code = CM_ERROR_TOO_MANY_SYMLINKS;
2209 cm_ReleaseSCache(linkScp);
2215 if (i == fid_count && fid_count < MAX_FID_COUNT) {
2216 fids[fid_count++] = linkScp->fid;
2221 /* something went wrong */
2222 cm_ReleaseSCache(tscp);
2225 cm_ReleaseSCache(dirScp);
2231 /* otherwise, tempsp has the new path,
2232 * and linkScp is the new root from
2233 * which to interpret that path.
2234 * Continue with the namei processing,
2235 * also doing the bookkeeping for the
2236 * space allocation and tracking the
2237 * vnode reference counts.
2243 cm_ReleaseSCache(tscp);
2248 * now, if linkScp is null, that's
2249 * AssembleLink's way of telling us that
2250 * the sym link is relative to the dir
2251 * containing the link. We have a ref
2252 * to it in dirScp, and we hold it now
2253 * and reuse it as the new spot in the
2261 /* not a symlink, we may be done */
2262 lock_ReleaseWrite(&tscp->rw);
2270 cm_ReleaseSCache(dirScp);
2278 cm_ReleaseSCache(dirScp);
2281 } /* end of a component */
2284 } /* we have a component */
2285 } /* big while loop over all components */
2289 cm_ReleaseSCache(dirScp);
2295 cm_ReleaseSCache(tscp);
2297 #ifdef DEBUG_REFCOUNT
2298 afsi_log("%s:%d cm_NameI code 0x%x outScpp 0x%p ref %d", file, line, code, *outScpp, (*outScpp) ? (*outScpp)->refCount : 0);
2300 osi_Log2(afsd_logp,"cm_NameI code 0x%x outScpp 0x%p", code, *outScpp);
2304 /* called with a dir, and a vnode within the dir that happens to be a symlink.
2305 * We chase the link, and return a held pointer to the target, if it exists,
2306 * in *outScpp. If we succeed, we return 0, otherwise we return an error code
2307 * and do not hold or return a target vnode.
2309 * This is very similar to calling cm_NameI with the last component of a name,
2310 * which happens to be a symlink, except that we've already passed by the name.
2312 * This function is typically called by the directory listing functions, which
2313 * encounter symlinks but need to return the proper file length so programs
2314 * like "more" work properly when they make use of the attributes retrieved from
2317 * The input vnode should not be locked when this function is called.
2319 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
2320 cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
2324 cm_scache_t *newRootScp;
2328 osi_Log1(afsd_logp, "Evaluating symlink scp 0x%p", linkScp);
2330 code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
2334 /* now, if newRootScp is NULL, we're really being told that the symlink
2335 * is relative to the current directory (dscp).
2337 if (newRootScp == NULL) {
2339 cm_HoldSCache(dscp);
2342 code = cm_NameI(newRootScp, spacep->wdata,
2343 CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
2344 userp, NULL, reqp, outScpp);
2346 if (code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH)
2347 code = CM_ERROR_NOSUCHPATH;
2349 /* this stuff is allocated no matter what happened on the namei call,
2351 cm_FreeSpace(spacep);
2352 cm_ReleaseSCache(newRootScp);
2354 if (linkScp == *outScpp) {
2355 cm_ReleaseSCache(*outScpp);
2357 code = CM_ERROR_NOSUCHPATH;
2363 /* for a given entry, make sure that it isn't in the stat cache, and then
2364 * add it to the list of file IDs to be obtained.
2366 * Don't bother adding it if we already have a vnode. Note that the dir
2367 * is locked, so we have to be careful checking the vnode we're thinking of
2368 * processing, to avoid deadlocks.
2370 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
2381 /* Don't overflow bsp. */
2382 if (bsp->counter >= CM_BULKMAX)
2383 return CM_ERROR_STOPNOW;
2385 thyper.LowPart = cm_data.buf_blockSize;
2386 thyper.HighPart = 0;
2387 thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
2389 /* thyper is now the first byte past the end of the record we're
2390 * interested in, and bsp->bufOffset is the first byte of the record
2391 * we're interested in.
2392 * Skip data in the others.
2395 if (LargeIntegerLessThan(*offp, bsp->bufOffset))
2397 if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
2398 return CM_ERROR_STOPNOW;
2399 if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
2402 cm_SetFid(&tfid, scp->fid.cell, scp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
2403 tscp = cm_FindSCache(&tfid);
2405 if (lock_TryWrite(&tscp->rw)) {
2406 /* we have an entry that we can look at */
2407 if (!cm_EAccesFindEntry(bsp->userp, &tscp->fid) && cm_HaveCallback(tscp)) {
2408 /* we have a callback on it. Don't bother
2409 * fetching this stat entry, since we're happy
2410 * with the info we have.
2412 lock_ReleaseWrite(&tscp->rw);
2413 cm_ReleaseSCache(tscp);
2416 lock_ReleaseWrite(&tscp->rw);
2418 cm_ReleaseSCache(tscp);
2421 #ifdef AFS_FREELANCE_CLIENT
2422 // yj: if this is a mountpoint under root.afs then we don't want it
2423 // to be bulkstat-ed, instead, we call getSCache directly and under
2424 // getSCache, it is handled specially.
2425 if ( cm_freelanceEnabled &&
2426 tfid.cell==AFS_FAKE_ROOT_CELL_ID &&
2427 tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
2428 !(tfid.vnode==0x1 && tfid.unique==0x1) )
2430 osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
2431 return cm_GetSCache(&tfid, NULL, &tscp, NULL, NULL);
2433 #endif /* AFS_FREELANCE_CLIENT */
2436 bsp->fids[i].Volume = scp->fid.volume;
2437 bsp->fids[i].Vnode = tfid.vnode;
2438 bsp->fids[i].Unique = tfid.unique;
2443 cm_TryBulkStatRPC(cm_scache_t *dscp, cm_bulkStat_t *bbp, cm_user_t *userp, cm_req_t *reqp)
2446 AFSCBFids fidStruct;
2447 AFSBulkStats statStruct;
2449 AFSCBs callbackStruct;
2452 cm_callbackRequest_t cbReq;
2459 struct rx_connection * rxconnp;
2460 int inlinebulk; /* Did we use InlineBulkStatus RPC or not? */
2462 memset(&volSync, 0, sizeof(volSync));
2464 /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
2465 * make the calls to create the entries. Handle AFSCBMAX files at a
2468 for (filex = 0; filex < bbp->counter; filex += filesThisCall) {
2469 filesThisCall = bbp->counter - filex;
2470 if (filesThisCall > AFSCBMAX)
2471 filesThisCall = AFSCBMAX;
2473 fidStruct.AFSCBFids_len = filesThisCall;
2474 fidStruct.AFSCBFids_val = &bbp->fids[filex];
2475 statStruct.AFSBulkStats_len = filesThisCall;
2476 statStruct.AFSBulkStats_val = &bbp->stats[filex];
2477 callbackStruct.AFSCBs_len = filesThisCall;
2478 callbackStruct.AFSCBs_val = &bbp->callbacks[filex];
2479 cm_StartCallbackGrantingCall(NULL, &cbReq);
2480 osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
2483 * Whenever cm_Analyze is called for a RXAFS_ RPC there must
2484 * be a FID provided. However, the error code from RXAFS_BulkStatus
2485 * or RXAFS_InlinkBulkStatus does not apply to any FID. Therefore,
2486 * we generate an invalid FID to match with the RPC error.
2488 cm_SetFid(&tfid, dscp->fid.cell, dscp->fid.volume, 0, 0);
2493 code = cm_ConnFromFID(&tfid, userp, reqp, &connp);
2497 rxconnp = cm_GetRxConn(connp);
2498 if (SERVERHASINLINEBULK(connp)) {
2499 code = RXAFS_InlineBulkStatus(rxconnp, &fidStruct,
2500 &statStruct, &callbackStruct, &volSync);
2501 if (code == RXGEN_OPCODE) {
2502 SET_SERVERHASNOINLINEBULK(connp);
2509 * It is important to note that RXAFS_BulkStatus is quite braindead.
2510 * The AFS 3.6 file server implementation returns arrays that are
2511 * sized to hold responses for all of the requested FIDs but it only
2512 * populates their contents up to the point where it detects an error.
2513 * Unfortunately, it does inform the caller which entries were filled
2514 * and which were not. The caller has no ability to determine which
2515 * FID the RPC return code applies to or which of the FIDs valid status
2516 * info and callbacks have been issued for. As a result, when an
2517 * error is returned, none of the data received can be trusted.
2519 code = RXAFS_BulkStatus(rxconnp, &fidStruct,
2520 &statStruct, &callbackStruct, &volSync);
2522 rx_PutConnection(rxconnp);
2525 * If InlineBulk RPC was called and it succeeded,
2526 * then pull out the return code from the status info
2527 * and use it for cm_Analyze so that we can failover to other
2528 * .readonly volume instances. But only do it for errors that
2529 * are volume global.
2531 if (inlinebulk && code == 0 && (&bbp->stats[0])->errorCode) {
2532 osi_Log1(afsd_logp, "cm_TryBulkStat inline-bulk stat error: %d",
2533 (&bbp->stats[0])->errorCode);
2534 switch ((&bbp->stats[0])->errorCode) {
2543 code = (&bbp->stats[0])->errorCode;
2546 /* Rx and Rxkad errors are volume global */
2547 if ( (&bbp->stats[0])->errorCode >= -64 && (&bbp->stats[0])->errorCode < 0 ||
2548 (&bbp->stats[0])->errorCode >= ERROR_TABLE_BASE_RXK && (&bbp->stats[0])->errorCode < ERROR_TABLE_BASE_RXK + 256)
2549 code = (&bbp->stats[0])->errorCode;
2552 } while (cm_Analyze(connp, userp, reqp, &tfid, NULL, 0, &bbp->stats[0], &volSync, NULL, &cbReq, code));
2553 code = cm_MapRPCError(code, reqp);
2556 * might as well quit on an error, since we're not going to do
2557 * much better on the next immediate call, either.
2560 osi_Log2(afsd_logp, "CALL %sBulkStatus FAILURE code 0x%x",
2561 inlinebulk ? "Inline" : "", code);
2564 * Since an error occurred and it is impossible to determine
2565 * the context in which the returned error code should be
2566 * interpreted, we return the CM_ERROR_BULKSTAT_FAILURE error
2567 * which indicates that Bulk Stat cannot be used for the
2568 * current request. The caller should fallback to using
2569 * individual RXAFS_FetchStatus calls.
2571 code = CM_ERROR_BULKSTAT_FAILURE;
2573 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2578 * The bulk RPC has succeeded or at least not failed with a
2579 * volume global error result. For items that have inlineBulk
2580 * errors we must call cm_Analyze in order to perform required
2581 * logging of errors.
2583 * If the RPC was not inline bulk or the entry either has no error
2584 * the status must be merged.
2586 osi_Log1(afsd_logp, "CALL %sBulkStatus SUCCESS", inlinebulk ? "Inline" : "");
2588 for (i = 0; i<filesThisCall; i++) {
2590 cm_SetFid(&tfid, dscp->fid.cell, bbp->fids[j].Volume, bbp->fids[j].Vnode, bbp->fids[j].Unique);
2592 if (inlinebulk && (&bbp->stats[j])->errorCode) {
2593 cm_req_t treq = *reqp;
2594 cm_Analyze(NULL, userp, &treq, &tfid, NULL, 0, &bbp->stats[j], &volSync,
2595 NULL, &cbReq, (&bbp->stats[j])->errorCode);
2596 switch ((&bbp->stats[j])->errorCode) {
2601 cm_EAccesAddEntry(userp, &tfid, &dscp->fid);
2604 code = cm_GetSCache(&tfid, &dscp->fid, &scp, userp, reqp);
2609 * otherwise, if this entry has no callback info,
2610 * merge in this. If there is existing callback info
2611 * we skip the merge because the existing data must be
2612 * current (we have a callback) and the response from
2613 * a non-inline bulk rpc might actually be wrong.
2615 * now, we have to be extra paranoid on merging in this
2616 * information, since we didn't use cm_SyncOp before
2617 * starting the fetch to make sure that no bad races
2618 * were occurring. Specifically, we need to make sure
2619 * we don't obliterate any newer information in the
2620 * vnode than have here.
2622 * Right now, be pretty conservative: if there's a
2623 * callback or a pending call, skip it.
2624 * However, if the prior attempt to obtain status
2625 * was refused access or the volume is .readonly,
2626 * take the data in any case since we have nothing
2627 * better for the in flight directory enumeration that
2628 * resulted in this function being called.
2630 lock_ObtainRead(&scp->rw);
2631 if ((scp->cbServerp == NULL &&
2632 !(scp->flags & (CM_SCACHEFLAG_FETCHING | CM_SCACHEFLAG_STORING | CM_SCACHEFLAG_SIZESTORING))) ||
2633 (scp->flags & CM_SCACHEFLAG_PURERO) ||
2634 cm_EAccesFindEntry(userp, &scp->fid))
2636 lock_ConvertRToW(&scp->rw);
2637 lostRace = cm_EndCallbackGrantingCall(scp, &cbReq,
2640 CM_CALLBACK_MAINTAINCOUNT|CM_CALLBACK_BULKSTAT);
2641 InterlockedIncrement(&scp->activeRPCs);
2643 code = cm_MergeStatus(dscp, scp, &bbp->stats[j], &volSync, userp, reqp, CM_MERGEFLAG_BULKSTAT);
2644 lock_ReleaseWrite(&scp->rw);
2646 lock_ReleaseRead(&scp->rw);
2648 cm_ReleaseSCache(scp);
2650 } /* all files in the response */
2651 /* now tell it to drop the count,
2652 * after doing the vnode processing above */
2653 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2654 } /* while there are still more files to process */
2659 /* called with a write locked scp and a pointer to a buffer. Make bulk stat
2660 * calls on all undeleted files in the page of the directory specified.
2663 cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
2669 osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%p", dscp);
2671 /* should be on a buffer boundary */
2672 osi_assertx((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0, "invalid offset");
2674 bbp = malloc(sizeof(cm_bulkStat_t));
2675 memset(bbp, 0, sizeof(cm_bulkStat_t));
2677 bbp->bufOffset = *offsetp;
2679 lock_ReleaseWrite(&dscp->rw);
2680 /* first, assemble the file IDs we need to stat */
2681 code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) bbp, offsetp, userp, reqp, NULL);
2683 /* if we failed, bail out early */
2684 if (code && code != CM_ERROR_STOPNOW) {
2686 lock_ObtainWrite(&dscp->rw);
2690 code = cm_TryBulkStatRPC(dscp, bbp, userp, reqp);
2691 osi_Log1(afsd_logp, "END cm_TryBulkStat code 0x%x", code);
2693 lock_ObtainWrite(&dscp->rw);
2698 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
2702 /* initialize store back mask as inexpensive local variable */
2704 memset(statusp, 0, sizeof(AFSStoreStatus));
2706 /* copy out queued info from scache first, if scp passed in */
2708 if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
2709 statusp->ClientModTime = scp->clientModTime;
2710 mask |= AFS_SETMODTIME;
2711 scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
2716 /* now add in our locally generated request */
2717 if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
2718 statusp->ClientModTime = attrp->clientModTime;
2719 mask |= AFS_SETMODTIME;
2721 if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
2722 statusp->UnixModeBits = attrp->unixModeBits;
2723 mask |= AFS_SETMODE;
2725 if (attrp->mask & CM_ATTRMASK_OWNER) {
2726 statusp->Owner = attrp->owner;
2727 mask |= AFS_SETOWNER;
2729 if (attrp->mask & CM_ATTRMASK_GROUP) {
2730 statusp->Group = attrp->group;
2731 mask |= AFS_SETGROUP;
2734 statusp->Mask = mask;
2738 cm_IsSpaceAvailable(cm_fid_t * fidp, osi_hyper_t *sizep, cm_user_t *userp, cm_req_t *reqp)
2743 struct rx_connection * rxconnp;
2744 AFSFetchVolumeStatus volStat;
2745 cm_volume_t *volp = NULL;
2750 char volName[32]="(unknown)";
2751 char offLineMsg[256]="server temporarily inaccessible";
2752 char motd[256]="server temporarily inaccessible";
2753 osi_hyper_t freespace;
2757 if (fidp->cell==AFS_FAKE_ROOT_CELL_ID &&
2758 fidp->volume==AFS_FAKE_ROOT_VOL_ID)
2763 volp = cm_GetVolumeByFID(fidp);
2768 volType = cm_VolumeType(volp, fidp->volume);
2769 if (volType == ROVOL || volType == BACKVOL) {
2774 cm_SetFid(&vfid, fidp->cell, fidp->volume, 1, 1);
2775 code = cm_GetSCache(&vfid, NULL, &vscp, userp, reqp);
2777 lock_ObtainWrite(&vscp->rw);
2778 code = cm_SyncOp(vscp, NULL, userp, reqp, PRSFS_READ,
2779 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2780 lock_ReleaseWrite(&vscp->rw);
2783 OfflineMsg = offLineMsg;
2787 code = cm_ConnFromFID(&vfid, userp, reqp, &connp);
2790 rxconnp = cm_GetRxConn(connp);
2791 code = RXAFS_GetVolumeStatus(rxconnp, fidp->volume,
2792 &volStat, &Name, &OfflineMsg, &MOTD);
2793 rx_PutConnection(rxconnp);
2795 } while (cm_Analyze(connp, userp, reqp, &vfid, NULL, 0, NULL, NULL, NULL, NULL, code));
2796 code = cm_MapRPCError(code, reqp);
2799 lock_ObtainWrite(&vscp->rw);
2800 cm_SyncOpDone(vscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2801 lock_ReleaseWrite(&vscp->rw);
2802 cm_ReleaseSCache(vscp);
2806 if (volStat.MaxQuota) {
2807 freespace.QuadPart = 1024 * (afs_int64)min(volStat.MaxQuota - volStat.BlocksInUse, volStat.PartBlocksAvail);
2809 freespace.QuadPart = 1024 * (afs_int64)volStat.PartBlocksAvail;
2811 spaceAvail = LargeIntegerGreaterThanOrEqualTo(freespace, *sizep);
2813 /* the rpc failed, assume there is space and we can fail it later. */
2822 /* set the file size, and make sure that all relevant buffers have been
2823 * truncated. Ensure that any partially truncated buffers have been zeroed
2824 * to the end of the buffer.
2826 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
2833 /* start by locking out buffer creation */
2834 lock_ObtainWrite(&scp->bufCreateLock);
2836 /* verify that this is a file, not a dir or a symlink */
2837 lock_ObtainWrite(&scp->rw);
2838 code = cm_SyncOp(scp, NULL, userp, reqp, 0,
2839 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2842 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2844 if (scp->fileType != CM_SCACHETYPE_FILE) {
2845 code = CM_ERROR_ISDIR;
2850 if (LargeIntegerLessThan(*sizep, scp->length))
2855 lock_ReleaseWrite(&scp->rw);
2857 /* can't hold scp->rw lock here, since we may wait for a storeback to
2858 * finish if the buffer package is cleaning a buffer by storing it to
2862 buf_Truncate(scp, userp, reqp, sizep);
2864 /* now ensure that file length is short enough, and update truncPos */
2865 lock_ObtainWrite(&scp->rw);
2867 /* make sure we have a callback (so we have the right value for the
2868 * length), and wait for it to be safe to do a truncate.
2870 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
2871 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2872 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2874 /* If we only have 'i' bits, then we should still be able to set
2875 the size of a file we created. */
2876 if (code == CM_ERROR_NOACCESS && scp->creator == userp) {
2877 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
2878 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2879 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2885 if (LargeIntegerLessThan(*sizep, scp->length)) {
2886 /* a real truncation. If truncPos is not set yet, or is bigger
2887 * than where we're truncating the file, set truncPos to this
2892 if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
2893 || LargeIntegerLessThan(*sizep, scp->length)) {
2895 scp->truncPos = *sizep;
2896 scp->mask |= CM_SCACHEMASK_TRUNCPOS;
2898 /* in either case, the new file size has been changed */
2899 scp->length = *sizep;
2900 scp->mask |= CM_SCACHEMASK_LENGTH;
2902 else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
2904 * Really extending the file so must check to see if we
2905 * have sufficient quota. cm_IsSpaceAvailable() obtains
2906 * the cm_scache.rw lock on the volume root directory.
2907 * vnode 1 < scp->fid.vnode therefore calling cm_IsSpaceAvailable
2908 * while holding scp->rw is a lock order violation.
2909 * Dropping it is ok because we are holding scp->bufCreateLock
2910 * which prevents the size of the file from changing.
2912 lock_ReleaseWrite(&scp->rw);
2913 available = cm_IsSpaceAvailable(&scp->fid, sizep, userp, reqp);
2914 lock_ObtainWrite(&scp->rw);
2916 scp->length = *sizep;
2917 scp->mask |= CM_SCACHEMASK_LENGTH;
2919 code = CM_ERROR_SPACE;
2924 /* done successfully */
2928 cm_SyncOpDone(scp, NULL,
2929 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2930 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2933 lock_ReleaseWrite(&scp->rw);
2934 lock_ReleaseWrite(&scp->bufCreateLock);
2939 /* set the file size or other attributes (but not both at once) */
2940 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
2944 AFSFetchStatus afsOutStatus;
2948 AFSStoreStatus afsInStatus;
2949 struct rx_connection * rxconnp;
2951 memset(&volSync, 0, sizeof(volSync));
2953 /* handle file length setting */
2954 if (attrp->mask & CM_ATTRMASK_LENGTH)
2955 return cm_SetLength(scp, &attrp->length, userp, reqp);
2957 lock_ObtainWrite(&scp->rw);
2958 /* Check for RO volume */
2959 if (scp->flags & CM_SCACHEFLAG_RO) {
2960 code = CM_ERROR_READONLY;
2961 lock_ReleaseWrite(&scp->rw);
2965 /* otherwise, we have to make an RPC to get the status */
2966 code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
2968 lock_ReleaseWrite(&scp->rw);
2971 lock_ConvertWToR(&scp->rw);
2973 /* make the attr structure */
2974 cm_StatusFromAttr(&afsInStatus, scp, attrp);
2976 tfid.Volume = scp->fid.volume;
2977 tfid.Vnode = scp->fid.vnode;
2978 tfid.Unique = scp->fid.unique;
2979 lock_ReleaseRead(&scp->rw);
2981 /* now make the RPC */
2982 InterlockedIncrement(&scp->activeRPCs);
2984 osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%p", scp);
2986 code = cm_ConnFromFID(&scp->fid, userp, reqp, &connp);
2990 rxconnp = cm_GetRxConn(connp);
2991 code = RXAFS_StoreStatus(rxconnp, &tfid,
2992 &afsInStatus, &afsOutStatus, &volSync);
2993 rx_PutConnection(rxconnp);
2995 } while (cm_Analyze(connp, userp, reqp,
2996 &scp->fid, NULL, 1, &afsOutStatus, &volSync, NULL, NULL, code));
2997 code = cm_MapRPCError(code, reqp);
3000 osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
3002 osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
3004 lock_ObtainWrite(&scp->rw);
3006 code = cm_MergeStatus( NULL, scp, &afsOutStatus, &volSync, userp, reqp,
3007 CM_MERGEFLAG_FORCE|CM_MERGEFLAG_STOREDATA);
3009 InterlockedDecrement(&scp->activeRPCs);
3010 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
3012 /* if we're changing the mode bits, discard the ACL cache,
3013 * since we changed the mode bits.
3015 if (afsInStatus.Mask & AFS_SETMODE)
3016 cm_FreeAllACLEnts(scp);
3017 lock_ReleaseWrite(&scp->rw);
3021 long cm_Create(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
3022 cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
3027 cm_callbackRequest_t cbReq;
3030 cm_scache_t *scp = NULL;
3033 AFSStoreStatus inStatus;
3034 AFSFetchStatus updatedDirStatus;
3035 AFSFetchStatus newFileStatus;
3036 AFSCallBack newFileCallback;
3038 struct rx_connection * rxconnp;
3040 fschar_t * fnamep = NULL;
3042 memset(&volSync, 0, sizeof(volSync));
3044 /* can't create names with @sys in them; must expand it manually first.
3045 * return "invalid request" if they try.
3047 if (cm_ExpandSysName(NULL, cnamep, NULL, 0, 0)) {
3048 return CM_ERROR_ATSYS;
3051 #ifdef AFS_FREELANCE_CLIENT
3052 /* Freelance root volume does not hold files */
3053 if (cm_freelanceEnabled &&
3054 dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
3055 dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
3057 return CM_ERROR_NOACCESS;
3059 #endif /* AFS_FREELANCE_CLIENT */
3061 /* Check for RO volume */
3062 if (dscp->flags & CM_SCACHEFLAG_RO)
3063 return CM_ERROR_READONLY;
3065 /* before starting the RPC, mark that we're changing the file data, so
3066 * that someone who does a chmod will know to wait until our call
3069 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3071 lock_ObtainWrite(&dscp->rw);
3072 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3073 lock_ReleaseWrite(&dscp->rw);
3075 cm_StartCallbackGrantingCall(NULL, &cbReq);
3077 cm_EndDirOp(&dirop);
3084 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3086 cm_StatusFromAttr(&inStatus, NULL, attrp);
3088 /* try the RPC now */
3089 InterlockedIncrement(&dscp->activeRPCs);
3090 osi_Log1(afsd_logp, "CALL CreateFile scp 0x%p", dscp);
3092 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3096 dirAFSFid.Volume = dscp->fid.volume;
3097 dirAFSFid.Vnode = dscp->fid.vnode;
3098 dirAFSFid.Unique = dscp->fid.unique;
3100 rxconnp = cm_GetRxConn(connp);
3101 code = RXAFS_CreateFile(connp->rxconnp, &dirAFSFid, fnamep,
3102 &inStatus, &newAFSFid, &newFileStatus,
3103 &updatedDirStatus, &newFileCallback,
3105 rx_PutConnection(rxconnp);
3107 } while (cm_Analyze(connp, userp, reqp,
3108 &dscp->fid, NULL, 1, &updatedDirStatus, &volSync, NULL, &cbReq, code));
3109 code = cm_MapRPCError(code, reqp);
3112 osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
3114 osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
3117 lock_ObtainWrite(&dirop.scp->dirlock);
3118 dirop.lockType = CM_DIRLOCK_WRITE;
3120 lock_ObtainWrite(&dscp->rw);
3122 code = cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3123 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3124 if (cm_CheckDirOpForSingleChange(&dirop)) {
3125 lock_ReleaseWrite(&dscp->rw);
3126 cm_DirCreateEntry(&dirop, fnamep, &newFid);
3128 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
3130 lock_ObtainWrite(&dscp->rw);
3133 InterlockedDecrement(&dscp->activeRPCs);
3135 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3136 lock_ReleaseWrite(&dscp->rw);
3138 /* now try to create the file's entry, too, but be careful to
3139 * make sure that we don't merge in old info. Since we weren't locking
3140 * out any requests during the file's creation, we may have pretty old
3144 code = cm_GetSCache(&newFid, &dscp->fid, &scp, userp, reqp);
3146 lock_ObtainWrite(&scp->rw);
3147 scp->creator = userp; /* remember who created it */
3148 if (!cm_HaveCallback(scp)) {
3149 lostRace = cm_EndCallbackGrantingCall(scp, &cbReq,
3150 &newFileCallback, &volSync, 0);
3151 InterlockedIncrement(&scp->activeRPCs);
3153 code = cm_MergeStatus( dscp, scp, &newFileStatus, &volSync,
3157 lock_ReleaseWrite(&scp->rw);
3161 /* make sure we end things properly */
3163 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
3165 cm_EndDirOp(&dirop);
3174 cm_ReleaseSCache(scp);
3180 * locked if TRUE means write-locked
3181 * else the cm_scache_t rw must not be held
3183 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp, afs_uint32 locked)
3188 lock_ReleaseWrite(&scp->rw);
3190 osi_Log2(afsd_logp, "cm_FSync scp 0x%p userp 0x%p", scp, userp);
3192 code = buf_CleanVnode(scp, userp, reqp);
3194 lock_ObtainWrite(&scp->rw);
3196 if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
3197 | CM_SCACHEMASK_CLIENTMODTIME
3198 | CM_SCACHEMASK_LENGTH))
3199 code = cm_StoreMini(scp, userp, reqp);
3201 if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) {
3202 code = (scp->flags & CM_SCACHEFLAG_OVERQUOTA) ? CM_ERROR_QUOTA : CM_ERROR_SPACE;
3203 scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE);
3207 lock_ReleaseWrite(&scp->rw);
3208 } else if (locked) {
3209 lock_ObtainWrite(&scp->rw);
3214 long cm_MakeDir(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
3215 cm_user_t *userp, cm_req_t *reqp, cm_scache_t **scpp)
3220 cm_callbackRequest_t cbReq;
3223 cm_scache_t *scp = NULL;
3226 AFSStoreStatus inStatus;
3227 AFSFetchStatus updatedDirStatus;
3228 AFSFetchStatus newDirStatus;
3229 AFSCallBack newDirCallback;
3231 struct rx_connection * rxconnp;
3233 fschar_t * fnamep = NULL;
3235 memset(&volSync, 0, sizeof(volSync));
3237 /* can't create names with @sys in them; must expand it manually first.
3238 * return "invalid request" if they try.
3240 if (cm_ExpandSysName(NULL, cnamep, NULL, 0, 0)) {
3241 return CM_ERROR_ATSYS;
3244 #ifdef AFS_FREELANCE_CLIENT
3245 /* Freelance root volume does not hold subdirectories */
3246 if (cm_freelanceEnabled &&
3247 dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
3248 dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
3250 return CM_ERROR_NOACCESS;
3252 #endif /* AFS_FREELANCE_CLIENT */
3254 /* Check for RO volume */
3255 if (dscp->flags & CM_SCACHEFLAG_RO)
3256 return CM_ERROR_READONLY;
3258 /* before starting the RPC, mark that we're changing the directory
3259 * data, so that someone who does a chmod on the dir will wait until
3260 * our call completes.
3262 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3264 lock_ObtainWrite(&dscp->rw);
3265 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3266 lock_ReleaseWrite(&dscp->rw);
3268 cm_StartCallbackGrantingCall(NULL, &cbReq);
3270 cm_EndDirOp(&dirop);
3277 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3278 cm_StatusFromAttr(&inStatus, NULL, attrp);
3280 /* try the RPC now */
3281 InterlockedIncrement(&dscp->activeRPCs);
3282 osi_Log1(afsd_logp, "CALL MakeDir scp 0x%p", dscp);
3284 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3288 dirAFSFid.Volume = dscp->fid.volume;
3289 dirAFSFid.Vnode = dscp->fid.vnode;
3290 dirAFSFid.Unique = dscp->fid.unique;
3292 rxconnp = cm_GetRxConn(connp);
3293 code = RXAFS_MakeDir(connp->rxconnp, &dirAFSFid, fnamep,
3294 &inStatus, &newAFSFid, &newDirStatus,
3295 &updatedDirStatus, &newDirCallback,
3297 rx_PutConnection(rxconnp);
3299 } while (cm_Analyze(connp, userp, reqp,
3300 &dscp->fid, NULL, 1, &updatedDirStatus, &volSync, NULL, &cbReq, code));
3301 code = cm_MapRPCError(code, reqp);
3304 osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
3306 osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
3309 lock_ObtainWrite(&dirop.scp->dirlock);
3310 dirop.lockType = CM_DIRLOCK_WRITE;
3312 lock_ObtainWrite(&dscp->rw);
3314 code = cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3315 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3316 if (cm_CheckDirOpForSingleChange(&dirop)) {
3317 lock_ReleaseWrite(&dscp->rw);
3318 cm_DirCreateEntry(&dirop, fnamep, &newFid);
3320 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
3322 lock_ObtainWrite(&dscp->rw);
3325 InterlockedDecrement(&dscp->activeRPCs);
3327 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3328 lock_ReleaseWrite(&dscp->rw);
3330 /* now try to create the new dir's entry, too, but be careful to
3331 * make sure that we don't merge in old info. Since we weren't locking
3332 * out any requests during the file's creation, we may have pretty old
3336 code = cm_GetSCache(&newFid, &dscp->fid, &scp, userp, reqp);
3338 lock_ObtainWrite(&scp->rw);
3339 if (!cm_HaveCallback(scp)) {
3340 lostRace = cm_EndCallbackGrantingCall(scp, &cbReq,
3341 &newDirCallback, &volSync, 0);
3342 InterlockedIncrement(&scp->activeRPCs);
3344 code = cm_MergeStatus( dscp, scp, &newDirStatus, &volSync,
3348 lock_ReleaseWrite(&scp->rw);
3352 /* make sure we end things properly */
3354 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
3356 cm_EndDirOp(&dirop);
3364 cm_ReleaseSCache(scp);
3367 /* and return error code */
3371 long cm_Link(cm_scache_t *dscp, clientchar_t *cnamep, cm_scache_t *sscp, long flags,
3372 cm_user_t *userp, cm_req_t *reqp)
3377 AFSFid existingAFSFid;
3378 AFSFetchStatus updatedDirStatus;
3379 AFSFetchStatus newLinkStatus;
3381 struct rx_connection * rxconnp;
3383 fschar_t * fnamep = NULL;
3386 memset(&volSync, 0, sizeof(volSync));
3388 if (dscp->fid.cell != sscp->fid.cell ||
3389 dscp->fid.volume != sscp->fid.volume) {
3390 return CM_ERROR_CROSSDEVLINK;
3393 /* Check for RO volume */
3394 if (dscp->flags & CM_SCACHEFLAG_RO)
3395 return CM_ERROR_READONLY;
3397 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3399 lock_ObtainWrite(&dscp->rw);
3400 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3401 lock_ReleaseWrite(&dscp->rw);
3403 cm_EndDirOp(&dirop);
3408 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3410 /* try the RPC now */
3411 InterlockedIncrement(&dscp->activeRPCs);
3412 osi_Log1(afsd_logp, "CALL Link scp 0x%p", dscp);
3414 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3417 dirAFSFid.Volume = dscp->fid.volume;
3418 dirAFSFid.Vnode = dscp->fid.vnode;
3419 dirAFSFid.Unique = dscp->fid.unique;
3421 existingAFSFid.Volume = sscp->fid.volume;
3422 existingAFSFid.Vnode = sscp->fid.vnode;
3423 existingAFSFid.Unique = sscp->fid.unique;
3425 rxconnp = cm_GetRxConn(connp);
3426 code = RXAFS_Link(rxconnp, &dirAFSFid, fnamep, &existingAFSFid,
3427 &newLinkStatus, &updatedDirStatus, &volSync);
3428 rx_PutConnection(rxconnp);
3429 osi_Log1(afsd_logp," RXAFS_Link returns 0x%x", code);
3431 } while (cm_Analyze(connp, userp, reqp, &dscp->fid, NULL, 1, &updatedDirStatus, &volSync, NULL, NULL, code));
3433 code = cm_MapRPCError(code, reqp);
3436 osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
3438 osi_Log0(afsd_logp, "CALL Link SUCCESS");
3441 lock_ObtainWrite(&dirop.scp->dirlock);
3442 dirop.lockType = CM_DIRLOCK_WRITE;
3444 lock_ObtainWrite(&dscp->rw);
3446 code = cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3449 if (cm_CheckDirOpForSingleChange(&dirop)) {
3450 lock_ReleaseWrite(&dscp->rw);
3451 cm_DirCreateEntry(&dirop, fnamep, &sscp->fid);
3453 cm_BPlusDirCreateEntry(&dirop, cnamep, &sscp->fid);
3455 lock_ObtainWrite(&dscp->rw);
3458 InterlockedDecrement(&dscp->activeRPCs);
3460 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3461 lock_ReleaseWrite(&dscp->rw);
3463 cm_EndDirOp(&dirop);
3465 if (invalidate && RDR_Initialized)
3466 RDR_InvalidateObject(dscp->fid.cell, dscp->fid.volume, dscp->fid.vnode,
3467 dscp->fid.unique, dscp->fid.hash,
3468 dscp->fileType, AFS_INVALIDATE_DATA_VERSION);
3470 /* Update the linked object status */
3472 lock_ObtainWrite(&sscp->rw);
3473 InterlockedIncrement(&sscp->activeRPCs);
3474 code = cm_MergeStatus(NULL, sscp, &newLinkStatus, &volSync, userp, reqp, 0);
3475 lock_ReleaseWrite(&sscp->rw);
3483 long cm_SymLink(cm_scache_t *dscp, clientchar_t *cnamep, fschar_t *contentsp, long flags,
3484 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp, cm_scache_t **scpp)
3492 AFSStoreStatus inStatus;
3493 AFSFetchStatus updatedDirStatus;
3494 AFSFetchStatus newLinkStatus;
3496 struct rx_connection * rxconnp;
3498 fschar_t *fnamep = NULL;
3503 /* Check for RO volume */
3504 if (dscp->flags & CM_SCACHEFLAG_RO)
3505 return CM_ERROR_READONLY;
3507 memset(&volSync, 0, sizeof(volSync));
3509 /* before starting the RPC, mark that we're changing the directory data,
3510 * so that someone who does a chmod on the dir will wait until our
3513 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3515 lock_ObtainWrite(&dscp->rw);
3516 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3517 lock_ReleaseWrite(&dscp->rw);
3519 cm_EndDirOp(&dirop);
3524 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3526 cm_StatusFromAttr(&inStatus, NULL, attrp);
3528 /* try the RPC now */
3529 InterlockedIncrement(&dscp->activeRPCs);
3530 osi_Log1(afsd_logp, "CALL Symlink scp 0x%p", dscp);
3532 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3536 dirAFSFid.Volume = dscp->fid.volume;
3537 dirAFSFid.Vnode = dscp->fid.vnode;
3538 dirAFSFid.Unique = dscp->fid.unique;
3540 rxconnp = cm_GetRxConn(connp);
3541 code = RXAFS_Symlink(rxconnp, &dirAFSFid, fnamep, contentsp,
3542 &inStatus, &newAFSFid, &newLinkStatus,
3543 &updatedDirStatus, &volSync);
3544 rx_PutConnection(rxconnp);
3546 } while (cm_Analyze(connp, userp, reqp,
3547 &dscp->fid, NULL, 1, &updatedDirStatus, &volSync, NULL, NULL, code));
3548 code = cm_MapRPCError(code, reqp);
3551 osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
3553 osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
3556 lock_ObtainWrite(&dirop.scp->dirlock);
3557 dirop.lockType = CM_DIRLOCK_WRITE;
3559 lock_ObtainWrite(&dscp->rw);
3561 code = cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3562 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3563 if (cm_CheckDirOpForSingleChange(&dirop)) {
3564 lock_ReleaseWrite(&dscp->rw);
3565 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3567 cm_DirCreateEntry(&dirop, fnamep, &newFid);
3569 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
3571 lock_ObtainWrite(&dscp->rw);
3574 InterlockedDecrement(&dscp->activeRPCs);
3576 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3577 lock_ReleaseWrite(&dscp->rw);
3579 cm_EndDirOp(&dirop);
3581 /* now try to create the new dir's entry, too, but be careful to
3582 * make sure that we don't merge in old info. Since we weren't locking
3583 * out any requests during the file's creation, we may have pretty old
3587 code = cm_GetSCache(&newFid, &dscp->fid, &scp, userp, reqp);
3589 lock_ObtainWrite(&scp->rw);
3590 if (!cm_HaveCallback(scp)) {
3591 InterlockedIncrement(&scp->activeRPCs);
3592 code = cm_MergeStatus( dscp, scp, &newLinkStatus, &volSync,
3595 lock_ReleaseWrite(&scp->rw);
3600 cm_ReleaseSCache(scp);
3607 /* and return error code */
3611 /*! \brief Remove a directory
3613 Encapsulates a call to RXAFS_RemoveDir().
3615 \param[in] dscp cm_scache_t for the directory containing the
3616 directory to be removed.
3618 \param[in] fnamep This will be the original name of the directory
3619 as known to the file server. It will be passed in to RXAFS_RemoveDir().
3620 This parameter is optional. If it is not provided the value
3623 \param[in] cnamep Normalized name used to update the local
3626 \param[in] userp cm_user_t for the request.
3628 \param[in] reqp Request tracker.
3630 long cm_RemoveDir(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t *cnamep, cm_user_t *userp, cm_req_t *reqp)
3636 AFSFetchStatus updatedDirStatus;
3638 struct rx_connection * rxconnp;
3640 cm_scache_t *scp = NULL;
3641 int free_fnamep = FALSE;
3643 memset(&volSync, 0, sizeof(volSync));
3645 if (fnamep == NULL) {
3648 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ,
3649 CM_DIROP_FLAG_NONE, &dirop);
3651 code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
3654 cm_EndDirOp(&dirop);
3661 code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
3665 /* Check for RO volume */
3666 if (dscp->flags & CM_SCACHEFLAG_RO) {
3667 code = CM_ERROR_READONLY;
3671 /* before starting the RPC, mark that we're changing the directory data,
3672 * so that someone who does a chmod on the dir will wait until our
3675 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3677 lock_ObtainWrite(&dscp->rw);
3678 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3679 lock_ReleaseWrite(&dscp->rw);
3681 cm_EndDirOp(&dirop);
3686 /* try the RPC now */
3687 InterlockedIncrement(&dscp->activeRPCs);
3688 osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%p", dscp);
3690 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3694 dirAFSFid.Volume = dscp->fid.volume;
3695 dirAFSFid.Vnode = dscp->fid.vnode;
3696 dirAFSFid.Unique = dscp->fid.unique;
3698 rxconnp = cm_GetRxConn(connp);
3699 code = RXAFS_RemoveDir(rxconnp, &dirAFSFid, fnamep,
3700 &updatedDirStatus, &volSync);
3701 rx_PutConnection(rxconnp);
3703 } while (cm_Analyze(connp, userp, reqp,
3704 &dscp->fid, NULL, 1, &updatedDirStatus, &volSync, NULL, NULL, code));
3705 code = cm_MapRPCErrorRmdir(code, reqp);
3708 osi_Log1(afsd_logp, "CALL RemoveDir FAILURE, code 0x%x", code);
3710 osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
3713 lock_ObtainWrite(&dirop.scp->dirlock);
3714 dirop.lockType = CM_DIRLOCK_WRITE;
3716 lock_ObtainWrite(&dscp->rw);
3718 cm_dnlcRemove(dscp, cnamep);
3719 code = cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3720 if (cm_CheckDirOpForSingleChange(&dirop) && cnamep != NULL) {
3721 lock_ReleaseWrite(&dscp->rw);
3722 cm_DirDeleteEntry(&dirop, fnamep);
3724 cm_BPlusDirDeleteEntry(&dirop, cnamep);
3726 lock_ObtainWrite(&dscp->rw);
3729 InterlockedDecrement(&dscp->activeRPCs);
3731 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3732 lock_ReleaseWrite(&dscp->rw);
3734 cm_EndDirOp(&dirop);
3738 lock_ObtainWrite(&scp->rw);
3739 scp->flags |= CM_SCACHEFLAG_DELETED;
3740 lock_ObtainWrite(&cm_scacheLock);
3741 cm_AdjustScacheLRU(scp);
3742 cm_RemoveSCacheFromHashTable(scp);
3743 lock_ReleaseWrite(&cm_scacheLock);
3744 lock_ReleaseWrite(&scp->rw);
3745 if (RDR_Initialized && !(reqp->flags & CM_REQ_SOURCE_REDIR))
3746 RDR_InvalidateObject(scp->fid.cell, scp->fid.volume, scp->fid.vnode,
3747 scp->fid.unique, scp->fid.hash,
3748 scp->fileType, AFS_INVALIDATE_DELETED);
3750 cm_ReleaseSCache(scp);
3757 /* and return error code */
3761 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
3763 /* grab mutex on contents */
3764 lock_ObtainWrite(&scp->rw);
3766 /* reset the prefetch info */
3767 scp->prefetch.base.LowPart = 0; /* base */
3768 scp->prefetch.base.HighPart = 0;
3769 scp->prefetch.end.LowPart = 0; /* and end */
3770 scp->prefetch.end.HighPart = 0;
3772 /* release mutex on contents */
3773 lock_ReleaseWrite(&scp->rw);
3779 /*! \brief Rename a file or directory
3781 Encapsulates a RXAFS_Rename() call.
3783 \param[in] oldDscp cm_scache_t for the directory containing the old
3786 \param[in] oldNamep The original old name known to the file server.
3787 This is the name that will be passed into the RXAFS_Rename().
3788 If it is not provided, it will be looked up.
3790 \param[in] normalizedOldNamep Normalized old name. This is used for
3791 updating local directory caches.
3793 \param[in] newDscp cm_scache_t for the directory containing the new
3796 \param[in] newNamep New name. Normalized.
3798 \param[in] userp cm_user_t for the request.
3800 \param[in,out] reqp Request tracker.
3803 long cm_Rename(cm_scache_t *oldDscp, fschar_t *oldNamep, clientchar_t *cOldNamep,
3804 cm_scache_t *newDscp, clientchar_t *cNewNamep, cm_user_t *userp,
3809 AFSFid oldDirAFSFid;
3810 AFSFid newDirAFSFid;
3811 AFSFetchStatus updatedOldDirStatus;
3812 AFSFetchStatus updatedNewDirStatus;
3815 int bTargetExists = 0;
3816 struct rx_connection * rxconnp;
3817 cm_dirOp_t oldDirOp;
3820 cm_dirOp_t newDirOp;
3821 fschar_t * newNamep = NULL;
3822 int free_oldNamep = FALSE;
3823 cm_scache_t *oldScp = NULL, *oldTargetScp = NULL;
3824 int rpc_skipped = 0;
3826 memset(&volSync, 0, sizeof(volSync));
3828 if (cOldNamep == NULL || cNewNamep == NULL ||
3829 cm_ClientStrLen(cOldNamep) == 0 ||
3830 cm_ClientStrLen(cNewNamep) == 0)
3831 return CM_ERROR_INVAL;
3833 /* check for identical names */
3834 if (oldDscp == newDscp &&
3835 cm_ClientStrCmp(cOldNamep, cNewNamep) == 0) {
3836 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_RENAME_IDENTICAL",
3838 return CM_ERROR_RENAME_IDENTICAL;
3841 /* Check for RO volume */
3842 if ((oldDscp->flags & CM_SCACHEFLAG_RO) || (newDscp->flags & CM_SCACHEFLAG_RO)) {
3843 return CM_ERROR_READONLY;
3846 if (oldNamep == NULL) {
3849 code = cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_READ,
3850 CM_DIROP_FLAG_NONE, &oldDirOp);
3852 code = cm_BPlusDirLookupOriginalName(&oldDirOp, cOldNamep, &oldNamep);
3854 free_oldNamep = TRUE;
3855 cm_EndDirOp(&oldDirOp);
3859 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S Original Name lookup failed",
3860 oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3865 /* before starting the RPC, mark that we're changing the directory data,
3866 * so that someone who does a chmod on the dir will wait until our call
3867 * completes. We do this in vnode order so that we don't deadlock,
3868 * which makes the code a little verbose.
3870 if (oldDscp == newDscp) {
3872 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE,
3873 CM_DIROP_FLAG_NONE, &oldDirOp);
3874 lock_ObtainWrite(&oldDscp->rw);
3875 cm_dnlcRemove(oldDscp, cOldNamep);
3876 cm_dnlcRemove(oldDscp, cNewNamep);
3877 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3878 CM_SCACHESYNC_STOREDATA);
3879 lock_ReleaseWrite(&oldDscp->rw);
3881 cm_EndDirOp(&oldDirOp);
3885 /* two distinct dir vnodes */
3887 if (oldDscp->fid.cell != newDscp->fid.cell ||
3888 oldDscp->fid.volume != newDscp->fid.volume) {
3889 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_CROSSDEVLINK",
3891 code = CM_ERROR_CROSSDEVLINK;
3895 /* shouldn't happen that we have distinct vnodes for two
3896 * different files, but could due to deliberate attack, or
3897 * stale info. Avoid deadlocks and quit now.
3899 if (oldDscp->fid.vnode == newDscp->fid.vnode) {
3900 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p vnode collision",
3902 code = CM_ERROR_CROSSDEVLINK;
3906 if (oldDscp->fid.vnode < newDscp->fid.vnode) {
3907 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE,
3908 CM_DIROP_FLAG_NONE, &oldDirOp);
3909 lock_ObtainWrite(&oldDscp->rw);
3910 cm_dnlcRemove(oldDscp, cOldNamep);
3911 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3912 CM_SCACHESYNC_STOREDATA);
3913 lock_ReleaseWrite(&oldDscp->rw);
3915 cm_EndDirOp(&oldDirOp);
3917 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE,
3918 CM_DIROP_FLAG_NONE, &newDirOp);
3919 lock_ObtainWrite(&newDscp->rw);
3920 cm_dnlcRemove(newDscp, cNewNamep);
3921 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3922 CM_SCACHESYNC_STOREDATA);
3923 lock_ReleaseWrite(&newDscp->rw);
3925 cm_EndDirOp(&newDirOp);
3927 /* cleanup first one */
3928 lock_ObtainWrite(&oldDscp->rw);
3929 cm_SyncOpDone(oldDscp, NULL,
3930 CM_SCACHESYNC_STOREDATA);
3931 lock_ReleaseWrite(&oldDscp->rw);
3932 cm_EndDirOp(&oldDirOp);
3937 /* lock the new vnode entry first */
3938 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE,
3939 CM_DIROP_FLAG_NONE, &newDirOp);
3940 lock_ObtainWrite(&newDscp->rw);
3941 cm_dnlcRemove(newDscp, cNewNamep);
3942 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3943 CM_SCACHESYNC_STOREDATA);
3944 lock_ReleaseWrite(&newDscp->rw);
3946 cm_EndDirOp(&newDirOp);
3948 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE,
3949 CM_DIROP_FLAG_NONE, &oldDirOp);
3950 lock_ObtainWrite(&oldDscp->rw);
3951 cm_dnlcRemove(oldDscp, cOldNamep);
3952 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3953 CM_SCACHESYNC_STOREDATA);
3954 lock_ReleaseWrite(&oldDscp->rw);
3956 cm_EndDirOp(&oldDirOp);
3958 /* cleanup first one */
3959 lock_ObtainWrite(&newDscp->rw);
3960 cm_SyncOpDone(newDscp, NULL,
3961 CM_SCACHESYNC_STOREDATA);
3962 lock_ReleaseWrite(&newDscp->rw);
3963 cm_EndDirOp(&newDirOp);
3967 } /* two distinct vnodes */
3973 * The source and destination directories are now locked and no other local
3974 * changes can occur.
3976 * Before we permit the operation, make sure that we do not already have
3977 * an object in the destination directory that has a case-insensitive match
3978 * for this name UNLESS the matching object is the object we are renaming.
3980 code = cm_Lookup(oldDscp, cOldNamep, 0, userp, reqp, &oldScp);
3982 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S old name lookup failed",
3983 oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3988 /* Case sensitive lookup. If this succeeds we are done. */
3989 code = cm_Lookup(newDscp, cNewNamep, 0, userp, reqp, &oldTargetScp);
3992 * Case insensitive lookup. If this succeeds, it could have found the
3993 * same file with a name that differs only by case or it could be a
3994 * different file entirely.
3996 code = cm_Lookup(newDscp, cNewNamep, CM_FLAG_CASEFOLD, userp, reqp, &oldTargetScp);
3998 /* found a matching object with the new name */
3999 if (cm_FidCmp(&oldScp->fid, &oldTargetScp->fid)) {
4000 /* and they don't match so return an error */
4001 osi_Log2(afsd_logp, "cm_Rename newDscp 0x%p cNewName %S new name already exists",
4002 newDscp, osi_LogSaveStringW(afsd_logp, cNewNamep));
4003 code = CM_ERROR_EXISTS;
4005 cm_ReleaseSCache(oldTargetScp);
4006 oldTargetScp = NULL;
4007 } else if (code == CM_ERROR_AMBIGUOUS_FILENAME) {
4008 code = CM_ERROR_EXISTS;
4010 /* The target does not exist. Clear the error and perform the rename. */
4022 newNamep = cm_ClientStringToFsStringAlloc(cNewNamep, -1, NULL);
4024 /* try the RPC now */
4025 InterlockedIncrement(&oldDscp->activeRPCs);
4027 InterlockedIncrement(&newDscp->activeRPCs);
4028 osi_Log2(afsd_logp, "CALL Rename old scp 0x%p new scp 0x%p",
4031 code = cm_ConnFromFID(&oldDscp->fid, userp, reqp, &connp);
4035 oldDirAFSFid.Volume = oldDscp->fid.volume;
4036 oldDirAFSFid.Vnode = oldDscp->fid.vnode;
4037 oldDirAFSFid.Unique = oldDscp->fid.unique;
4038 newDirAFSFid.Volume = newDscp->fid.volume;
4039 newDirAFSFid.Vnode = newDscp->fid.vnode;
4040 newDirAFSFid.Unique = newDscp->fid.unique;
4042 rxconnp = cm_GetRxConn(connp);
4043 code = RXAFS_Rename(rxconnp, &oldDirAFSFid, oldNamep,
4044 &newDirAFSFid, newNamep,
4045 &updatedOldDirStatus, &updatedNewDirStatus,
4047 rx_PutConnection(rxconnp);
4049 } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid, NULL, 1,
4050 &updatedOldDirStatus, &volSync, NULL, NULL, code));
4051 code = cm_MapRPCError(code, reqp);
4054 osi_Log1(afsd_logp, "CALL Rename FAILURE, code 0x%x", code);
4056 osi_Log0(afsd_logp, "CALL Rename SUCCESS");
4059 /* update the individual stat cache entries for the directories */
4061 lock_ObtainWrite(&oldDirOp.scp->dirlock);
4062 oldDirOp.lockType = CM_DIRLOCK_WRITE;
4065 lock_ObtainWrite(&oldDscp->rw);
4067 code = cm_MergeStatus( NULL, oldDscp, &updatedOldDirStatus, &volSync,
4068 userp, reqp, CM_MERGEFLAG_DIROP);
4069 if (cm_CheckDirOpForSingleChange(&oldDirOp)) {
4070 lock_ReleaseWrite(&oldDscp->rw);
4071 if (bTargetExists && oneDir) {
4072 diropCode = cm_DirDeleteEntry(&oldDirOp, newNamep);
4074 cm_BPlusDirDeleteEntry(&oldDirOp, cNewNamep);
4079 diropCode = cm_BPlusDirLookup(&oldDirOp, cOldNamep, &fileFid);
4080 if (diropCode == CM_ERROR_INEXACT_MATCH)
4082 else if (diropCode == EINVAL)
4084 diropCode = cm_DirLookup(&oldDirOp, oldNamep, &fileFid);
4086 if (diropCode == 0) {
4088 diropCode = cm_DirCreateEntry(&oldDirOp, newNamep, &fileFid);
4090 cm_BPlusDirCreateEntry(&oldDirOp, cNewNamep, &fileFid);
4094 if (diropCode == 0) {
4095 diropCode = cm_DirDeleteEntry(&oldDirOp, oldNamep);
4097 cm_BPlusDirDeleteEntry(&oldDirOp, cOldNamep);
4101 lock_ObtainWrite(&oldDscp->rw);
4105 InterlockedDecrement(&oldDscp->activeRPCs);
4107 cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
4108 lock_ReleaseWrite(&oldDscp->rw);
4110 cm_EndDirOp(&oldDirOp);
4112 /* and update it for the new one, too, if necessary */
4115 lock_ObtainWrite(&newDirOp.scp->dirlock);
4116 newDirOp.lockType = CM_DIRLOCK_WRITE;
4118 lock_ObtainWrite(&newDscp->rw);
4120 code = cm_MergeStatus( NULL, newDscp, &updatedNewDirStatus, &volSync,
4121 userp, reqp, CM_MERGEFLAG_DIROP);
4124 * we only make the local change if we successfully made
4125 * the change in the old directory AND there was only one
4126 * change in the new directory
4128 if (diropCode == 0 && cm_CheckDirOpForSingleChange(&newDirOp)) {
4129 lock_ReleaseWrite(&newDscp->rw);
4131 if (bTargetExists && !oneDir) {
4132 diropCode = cm_DirDeleteEntry(&newDirOp, newNamep);
4134 cm_BPlusDirDeleteEntry(&newDirOp, cNewNamep);
4138 cm_DirCreateEntry(&newDirOp, newNamep, &fileFid);
4140 cm_BPlusDirCreateEntry(&newDirOp, cNewNamep, &fileFid);
4142 lock_ObtainWrite(&newDscp->rw);
4146 InterlockedIncrement(&newDscp->activeRPCs);
4148 cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
4149 lock_ReleaseWrite(&newDscp->rw);
4151 cm_EndDirOp(&newDirOp);
4156 * After the rename the file server has invalidated the callbacks
4157 * on the file that was moved and destroyed any target file.
4159 lock_ObtainWrite(&oldScp->rw);
4160 cm_DiscardSCache(oldScp);
4161 lock_ReleaseWrite(&oldScp->rw);
4163 if (RDR_Initialized)
4164 RDR_InvalidateObject(oldScp->fid.cell, oldScp->fid.volume, oldScp->fid.vnode, oldScp->fid.unique,
4165 oldScp->fid.hash, oldScp->fileType, AFS_INVALIDATE_CALLBACK);
4168 lock_ObtainWrite(&oldTargetScp->rw);
4169 cm_DiscardSCache(oldTargetScp);
4170 lock_ReleaseWrite(&oldTargetScp->rw);
4172 if (RDR_Initialized)
4173 RDR_InvalidateObject(oldTargetScp->fid.cell, oldTargetScp->fid.volume, oldTargetScp->fid.vnode, oldTargetScp->fid.unique,
4174 oldTargetScp->fid.hash, oldTargetScp->fileType, AFS_INVALIDATE_CALLBACK);
4180 cm_ReleaseSCache(oldScp);
4183 cm_ReleaseSCache(oldTargetScp);
4190 /* and return error code */
4194 /* Byte range locks:
4196 The OpenAFS Windows client has to fake byte range locks given no
4197 server side support for such locks. This is implemented as keyed
4198 byte range locks on the cache manager.
4200 Keyed byte range locks:
4202 Each cm_scache_t structure keeps track of a list of keyed locks.
4203 The key for a lock identifies an owner of a set of locks (referred
4204 to as a client). Each key is represented by a value. The set of
4205 key values used within a specific cm_scache_t structure form a
4206 namespace that has a scope of just that cm_scache_t structure. The
4207 same key value can be used with another cm_scache_t structure and
4208 correspond to a completely different client. However it is
4209 advantageous for the SMB or IFS layer to make sure that there is a
4210 1-1 mapping between client and keys over all cm_scache_t objects.
4212 Assume a client C has key Key(C) (although, since the scope of the
4213 key is a cm_scache_t, the key can be Key(C,S), where S is the
4214 cm_scache_t. But assume a 1-1 relation between keys and clients).
4215 A byte range (O,+L) denotes byte addresses (O) through (O+L-1)
4216 inclusive (a.k.a. [O,O+L-1]). The function Key(x) is implemented
4217 through cm_generateKey() function for both SMB and IFS.
4219 The list of locks for a cm_scache_t object S is maintained in
4220 S->fileLocks. The cache manager will set a lock on the AFS file
4221 server in order to assert the locks in S->fileLocks. If only
4222 shared locks are in place for S, then the cache manager will obtain
4223 a LockRead lock, while if there are any exclusive locks, it will
4224 obtain a LockWrite lock. If the exclusive locks are all released
4225 while the shared locks remain, then the cache manager will
4226 downgrade the lock from LockWrite to LockRead. Similarly, if an
4227 exclusive lock is obtained when only shared locks exist, then the
4228 cache manager will try to upgrade the lock from LockRead to
4231 Each lock L owned by client C maintains a key L->key such that
4232 L->key == Key(C), the effective range defined by L->LOffset and
4233 L->LLength such that the range of bytes affected by the lock is
4234 (L->LOffset, +L->LLength), a type maintained in L->LockType which
4235 is either exclusive or shared.
4239 A lock exists iff it is in S->fileLocks for some cm_scache_t
4240 S. Existing locks are in one of the following states: ACTIVE,
4241 WAITLOCK, WAITUNLOCK, LOST, DELETED.
4243 The following sections describe each lock and the associated
4246 1. ACTIVE: A lock L is ACTIVE iff the cache manager has asserted
4247 the lock with the AFS file server. This type of lock can be
4248 exercised by a client to read or write to the locked region (as
4251 1.1 ACTIVE->LOST: When the AFS file server fails to extend a
4252 server lock that was required to assert the lock. Before
4253 marking the lock as lost, the cache manager checks if the file
4254 has changed on the server. If the file has not changed, then
4255 the cache manager will attempt to obtain a new server lock
4256 that is sufficient to assert the client side locks for the
4257 file. If any of these fail, the lock is marked as LOST.
4258 Otherwise, it is left as ACTIVE.
4260 1.2 ACTIVE->DELETED: Lock is released.
4262 2. WAITLOCK: A lock is in a WAITLOCK state if the cache manager
4263 grants the lock but the lock is yet to be asserted with the AFS
4264 file server. Once the file server grants the lock, the state
4265 will transition to an ACTIVE lock.
4267 2.1 WAITLOCK->ACTIVE: The server granted the lock.
4269 2.2 WAITLOCK->DELETED: Lock is abandoned, or timed out during
4272 2.3 WAITLOCK->LOST: One or more locks from this client were
4273 marked as LOST. No further locks will be granted to this
4274 client until all lost locks are removed.
4276 3. WAITUNLOCK: A lock is in a WAITUNLOCK state if the cache manager
4277 receives a request for a lock that conflicts with an existing
4278 ACTIVE or WAITLOCK lock. The lock will be placed in the queue
4279 and will be granted at such time the conflicting locks are
4280 removed, at which point the state will transition to either
4283 3.1 WAITUNLOCK->ACTIVE: The conflicting lock was removed. The
4284 current serverLock is sufficient to assert this lock, or a
4285 sufficient serverLock is obtained.
4287 3.2 WAITUNLOCK->WAITLOCK: The conflicting lock was removed,
4288 however the required serverLock is yet to be asserted with the
4291 3.3 WAITUNLOCK->DELETED: The lock is abandoned, timed out or
4294 3.5 WAITUNLOCK->LOST: One or more locks from this client were
4295 marked as LOST. No further locks will be granted to this
4296 client until all lost locks are removed.
4298 4. LOST: A lock L is LOST if the server lock that was required to
4299 assert the lock could not be obtained or if it could not be
4300 extended, or if other locks by the same client were LOST.
4301 Essentially, once a lock is LOST, the contract between the cache
4302 manager and that specific client is no longer valid.
4304 The cache manager rechecks the server lock once every minute and
4305 extends it as appropriate. If this is not done for 5 minutes,
4306 the AFS file server will release the lock (the 5 minute timeout
4307 is based on current file server code and is fairly arbitrary).
4308 Once released, the lock cannot be re-obtained without verifying
4309 that the contents of the file hasn't been modified since the
4310 time the lock was released. Re-obtaining the lock without
4311 verifying this may lead to data corruption. If the lock can not
4312 be obtained safely, then all active locks for the cm_scache_t
4315 4.1 LOST->DELETED: The lock is released.
4317 5. DELETED: The lock is no longer relevant. Eventually, it will
4318 get removed from the cm_scache_t. In the meantime, it will be
4319 treated as if it does not exist.
4321 5.1 DELETED->not exist: The lock is removed from the
4324 The following are classifications of locks based on their state.
4326 6* A lock L is ACCEPTED if it is ACTIVE or WAITLOCK. These locks
4327 have been accepted by the cache manager, but may or may not have
4328 been granted back to the client.
4330 7* A lock L is QUEUED if it is ACTIVE, WAITLOCK or WAITUNLOCK.
4332 8* A lock L is WAITING if it is WAITLOCK or WAITUNLOCK.
4336 A client C can READ range (Offset,+Length) of a file represented by
4337 cm_scache_t S iff (1):
4339 1. for all _a_ in (Offset,+Length), all of the following is true:
4341 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
4342 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
4345 1.2 For each LOST lock L in S->fileLocks such that _a_ in
4346 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
4349 (When locks are lost on an cm_scache_t, all locks are lost. By
4350 4.2 (below), if there is an exclusive LOST lock, then there
4351 can't be any overlapping ACTIVE locks.)
4353 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
4355 2. for all _a_ in (Offset,+Length), one of the following is true:
4357 2.1 Byte _a_ of S is unowned (as specified in 1.1) AND there
4358 does not exist a LOST lock L such that _a_ in
4359 (L->LOffset,+L->LLength).
4361 2.2 Byte _a_ of S is owned by C under lock L (as specified in
4362 1.2) AND L->LockType is exclusive.
4364 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
4366 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
4369 3.1 If L->LockType is exclusive then there does NOT exist a
4370 ACCEPTED lock M in S->fileLocks such that _a_ in
4371 (M->LOffset,+M->LLength).
4373 (If we count all QUEUED locks then we hit cases such as
4374 cascading waiting locks where the locks later on in the queue
4375 can be granted without compromising file integrity. On the
4376 other hand if only ACCEPTED locks are considered, then locks
4377 that were received earlier may end up waiting for locks that
4378 were received later to be unlocked. The choice of ACCEPTED
4379 locks was made to mimic the Windows byte range lock
4382 3.2 If L->LockType is shared then for each ACCEPTED lock M in
4383 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
4384 M->LockType is shared.
4386 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
4388 4.1 M->key != Key(C)
4390 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
4391 and (M->LOffset,+M->LLength) do not intersect.
4393 (Note: If a client loses a lock, it loses all locks.
4394 Subsequently, it will not be allowed to obtain any more locks
4395 until all existing LOST locks that belong to the client are
4396 released. Once all locks are released by a single client,
4397 there exists no further contract between the client and AFS
4398 about the contents of the file, hence the client can then
4399 proceed to obtain new locks and establish a new contract.
4401 This doesn't quite work as you think it should, because most
4402 applications aren't built to deal with losing locks they
4403 thought they once had. For now, we don't have a good
4404 solution to lost locks.
4406 Also, for consistency reasons, we have to hold off on
4407 granting locks that overlap exclusive LOST locks.)
4409 A client C can only unlock locks L in S->fileLocks which have
4412 The representation and invariants are as follows:
4414 - Each cm_scache_t structure keeps:
4416 - A queue of byte-range locks (cm_scache_t::fileLocks) which
4417 are of type cm_file_lock_t.
4419 - A record of the highest server-side lock that has been
4420 obtained for this object (cm_scache_t::serverLock), which is
4421 one of (-1), LockRead, LockWrite.
4423 - A count of ACCEPTED exclusive and shared locks that are in the
4424 queue (cm_scache_t::sharedLocks and
4425 cm_scache_t::exclusiveLocks)
4427 - Each cm_file_lock_t structure keeps:
4429 - The type of lock (cm_file_lock_t::LockType)
4431 - The key associated with the lock (cm_file_lock_t::key)
4433 - The offset and length of the lock (cm_file_lock_t::LOffset
4434 and cm_file_lock_t::LLength)
4436 - The state of the lock.
4438 - Time of issuance or last successful extension
4440 Semantic invariants:
4442 I1. The number of ACCEPTED locks in S->fileLocks are
4443 (S->sharedLocks + S->exclusiveLocks)
4445 External invariants:
4447 I3. S->serverLock is the lock that we have asserted with the
4448 AFS file server for this cm_scache_t.
4450 I4. S->serverLock == LockRead iff there is at least one ACTIVE
4451 shared lock, but no ACTIVE exclusive locks.
4453 I5. S->serverLock == LockWrite iff there is at least one ACTIVE
4456 I6. If L is a LOST lock, then for each lock M in S->fileLocks,
4457 M->key == L->key IMPLIES M is LOST or DELETED.
4462 #define IS_LOCK_ACTIVE(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == 0)
4464 #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)
4466 #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)
4468 #define IS_LOCK_LOST(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_LOST)
4470 #define IS_LOCK_DELETED(lockp) (((lockp)->flags & CM_FILELOCK_FLAG_DELETED) == CM_FILELOCK_FLAG_DELETED)
4473 #define IS_LOCK_ACCEPTED(lockp) (IS_LOCK_ACTIVE(lockp) || IS_LOCK_WAITLOCK(lockp))
4476 #define IS_LOCK_CLIENTONLY(lockp) ((((lockp)->scp->flags & CM_SCACHEFLAG_RO) == CM_SCACHEFLAG_RO) || (((lockp)->flags & CM_FILELOCK_FLAG_CLIENTONLY) == CM_FILELOCK_FLAG_CLIENTONLY))
4479 #define INTERSECT_RANGE(r1,r2) (((r2).offset+(r2).length) > (r1).offset && ((r1).offset +(r1).length) > (r2).offset)
4482 #define CONTAINS_RANGE(r1,r2) (((r2).offset+(r2).length) <= ((r1).offset+(r1).length) && (r1).offset <= (r2).offset)
4484 #if defined(VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS) && !defined(LOCK_TESTING)
4485 #define SCP_SUPPORTS_BRLOCKS(scp) ((scp)->cbServerp && ((scp)->cbServerp->capabilities & VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS))
4487 #define SCP_SUPPORTS_BRLOCKS(scp) (1)
4490 #define SERVERLOCKS_ENABLED(scp) (!((scp)->flags & CM_SCACHEFLAG_RO) && cm_enableServerLocks && SCP_SUPPORTS_BRLOCKS(scp))
4492 #if defined(VICED_CAPABILITY_WRITELOCKACL)
4493 #define SCP_SUPPORTS_WRITELOCKACL(scp) ((scp)->cbServerp && ((scp->cbServerp->capabilities & VICED_CAPABILITY_WRITELOCKACL)))
4495 #define SCP_SUPPORTS_WRITELOCKACL(scp) (0)
4497 /* This should really be defined in any build that this code is being
4499 #error VICED_CAPABILITY_WRITELOCKACL not defined.
4502 static void cm_LockRangeSubtract(cm_range_t * pos, const cm_range_t * neg)
4504 afs_int64 int_begin;
4507 int_begin = max(pos->offset, neg->offset);
4508 int_end = min(pos->offset+pos->length, neg->offset+neg->length);
4510 if (int_begin < int_end) {
4511 if (int_begin == pos->offset) {
4512 pos->length = pos->offset + pos->length - int_end;
4513 pos->offset = int_end;
4514 } else if (int_end == pos->offset + pos->length) {
4515 pos->length = int_begin - pos->offset;
4518 /* We only subtract ranges if the resulting range is
4519 contiguous. If we try to support non-contigous ranges, we
4520 aren't actually improving performance. */
4524 /* Called with scp->rw held. Returns 0 if all is clear to read the
4525 specified range by the client identified by key.
4527 long cm_LockCheckRead(cm_scache_t *scp,
4528 LARGE_INTEGER LOffset,
4529 LARGE_INTEGER LLength,
4532 #ifndef ADVISORY_LOCKS
4534 cm_file_lock_t *fileLock;
4538 int substract_ranges = FALSE;
4540 range.offset = LOffset.QuadPart;
4541 range.length = LLength.QuadPart;
4545 1. for all _a_ in (Offset,+Length), all of the following is true:
4547 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
4548 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
4551 1.2 For each LOST lock L in S->fileLocks such that _a_ in
4552 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
4557 lock_ObtainRead(&cm_scacheLock);
4559 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
4561 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4563 if (INTERSECT_RANGE(range, fileLock->range)) {
4564 if (IS_LOCK_ACTIVE(fileLock)) {
4565 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4567 /* If there is an active lock for this client, it
4568 is safe to substract ranges.*/
4569 cm_LockRangeSubtract(&range, &fileLock->range);
4570 substract_ranges = TRUE;
4572 if (fileLock->lockType != LockRead) {
4573 code = CM_ERROR_LOCK_CONFLICT;
4577 /* even if the entire range is locked for reading,
4578 we still can't grant the lock at this point
4579 because the client may have lost locks. That
4580 is, unless we have already seen an active lock
4581 belonging to the client, in which case there
4582 can't be any lost locks for this client. */
4583 if (substract_ranges)
4584 cm_LockRangeSubtract(&range, &fileLock->range);
4586 } else if (IS_LOCK_LOST(fileLock) &&
4587 (cm_KeyEquals(&fileLock->key, &key, 0) || fileLock->lockType == LockWrite)) {
4588 code = CM_ERROR_BADFD;
4594 lock_ReleaseRead(&cm_scacheLock);
4596 osi_Log4(afsd_logp, "cm_LockCheckRead scp 0x%x offset %d length %d code 0x%x",
4597 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
4608 /* Called with scp->rw held. Returns 0 if all is clear to write the
4609 specified range by the client identified by key.
4611 long cm_LockCheckWrite(cm_scache_t *scp,
4612 LARGE_INTEGER LOffset,
4613 LARGE_INTEGER LLength,
4616 #ifndef ADVISORY_LOCKS
4618 cm_file_lock_t *fileLock;
4623 range.offset = LOffset.QuadPart;
4624 range.length = LLength.QuadPart;
4627 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
4629 2. for all _a_ in (Offset,+Length), one of the following is true:
4631 2.1 Byte _a_ of S is unowned AND there does not exist a LOST
4632 lock L such that _a_ in (L->LOffset,+L->LLength).
4634 2.2 Byte _a_ of S is owned by C under lock L AND L->LockType is
4638 lock_ObtainRead(&cm_scacheLock);
4640 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
4642 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4644 if (INTERSECT_RANGE(range, fileLock->range)) {
4645 if (IS_LOCK_ACTIVE(fileLock)) {
4646 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4647 if (fileLock->lockType == LockWrite) {
4649 /* if there is an active lock for this client, it
4650 is safe to substract ranges */
4651 cm_LockRangeSubtract(&range, &fileLock->range);
4653 code = CM_ERROR_LOCK_CONFLICT;
4657 code = CM_ERROR_LOCK_CONFLICT;
4660 } else if (IS_LOCK_LOST(fileLock)) {
4661 code = CM_ERROR_BADFD;
4667 lock_ReleaseRead(&cm_scacheLock);
4669 osi_Log4(afsd_logp, "cm_LockCheckWrite scp 0x%x offset %d length %d code 0x%x",
4670 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
4681 /* Called with cm_scacheLock write locked */
4682 static cm_file_lock_t * cm_GetFileLock(void) {
4685 l = (cm_file_lock_t *) cm_freeFileLocks;
4687 osi_QRemove(&cm_freeFileLocks, &l->q);
4689 l = malloc(sizeof(cm_file_lock_t));
4690 osi_assertx(l, "null cm_file_lock_t");
4693 memset(l, 0, sizeof(cm_file_lock_t));
4698 /* Called with cm_scacheLock write locked */
4699 static void cm_PutFileLock(cm_file_lock_t *l) {
4700 osi_QAdd(&cm_freeFileLocks, &l->q);
4703 /* called with scp->rw held. May release it during processing, but
4704 leaves it held on exit. */
4705 long cm_IntSetLock(cm_scache_t * scp, cm_user_t * userp, int lockType,
4711 struct rx_connection * rxconnp;
4713 afs_uint32 reqflags = reqp->flags;
4715 osi_Log2(afsd_logp, "CALL SetLock scp 0x%p for lock %d", scp, lockType);
4719 * The file server prior to 1.6.2 does not report an accurate value
4720 * and callbacks are not issued if the lock is dropped due to expiration.
4722 if ((lockType != LOCKING_ANDX_SHARED_LOCK && scp->fsLockCount != 0) ||
4723 (lockType == LOCKING_ANDX_SHARED_LOCK && scp->fsLockCount < 0))
4725 code = CM_ERROR_LOCK_NOT_GRANTED;
4726 osi_Log2(afsd_logp, "CALL SetLock FAILURE, fsLockCount %d code 0x%x", scp->fsLockCount, code);
4731 memset(&volSync, 0, sizeof(volSync));
4733 tfid.Volume = scp->fid.volume;
4734 tfid.Vnode = scp->fid.vnode;
4735 tfid.Unique = scp->fid.unique;
4738 reqp->flags |= CM_REQ_NORETRY;
4739 lock_ReleaseWrite(&scp->rw);
4742 code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4746 rxconnp = cm_GetRxConn(connp);
4747 code = RXAFS_SetLock(rxconnp, &tfid, lockType,
4749 rx_PutConnection(rxconnp);
4751 } while (cm_Analyze(connp, userp, reqp, &cfid, NULL, 1, NULL, &volSync,
4754 code = cm_MapRPCError(code, reqp);
4756 osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
4758 osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
4761 reqp->flags = reqflags;
4763 lock_ObtainWrite(&scp->rw);
4766 * The file server does not return a status structure so we must
4767 * locally track the file server lock count to the best of our
4770 if (lockType == LockWrite)
4771 scp->fsLockCount = -1;
4778 /* called with scp->rw held. Releases it during processing */
4779 long cm_IntReleaseLock(cm_scache_t * scp, cm_user_t * userp,
4785 struct rx_connection * rxconnp;
4788 if (scp->flags & CM_SCACHEFLAG_DELETED) {
4789 osi_Log1(afsd_logp, "CALL ReleaseLock on Deleted Vnode scp 0x%p", scp);
4793 memset(&volSync, 0, sizeof(volSync));
4795 tfid.Volume = scp->fid.volume;
4796 tfid.Vnode = scp->fid.vnode;
4797 tfid.Unique = scp->fid.unique;
4800 lock_ReleaseWrite(&scp->rw);
4802 osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
4805 code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4809 rxconnp = cm_GetRxConn(connp);
4810 code = RXAFS_ReleaseLock(rxconnp, &tfid, &volSync);
4811 rx_PutConnection(rxconnp);
4813 } while (cm_Analyze(connp, userp, reqp, &cfid, NULL, 1, NULL, &volSync,
4815 code = cm_MapRPCError(code, reqp);
4818 "CALL ReleaseLock FAILURE, code 0x%x", code);
4821 "CALL ReleaseLock SUCCESS");
4823 lock_ObtainWrite(&scp->rw);
4826 * The file server does not return a status structure so we must
4827 * locally track the file server lock count to the best of our
4831 if (scp->fsLockCount < 0)
4832 scp->fsLockCount = 0;
4835 return (code != CM_ERROR_BADFD ? code : 0);
4838 /* called with scp->rw held. May release it during processing, but
4839 will exit with lock held.
4843 - 0 if the user has permission to get the specified lock for the scp
4845 - CM_ERROR_NOACCESS if not
4847 Any other error from cm_SyncOp will be sent down untranslated.
4849 If CM_ERROR_NOACCESS is returned and lock_type is LockRead, then
4850 phas_insert (if non-NULL) will receive a boolean value indicating
4851 whether the user has INSERT permission or not.
4853 long cm_LockCheckPerms(cm_scache_t * scp,
4860 long code = 0, code2 = 0;
4862 /* lock permissions are slightly tricky because of the 'i' bit.
4863 If the user has PRSFS_LOCK, she can read-lock the file. If the
4864 user has PRSFS_WRITE, she can write-lock the file. However, if
4865 the user has PRSFS_INSERT, then she can write-lock new files,
4866 but not old ones. Since we don't have information about
4867 whether a file is new or not, we assume that if the user owns
4868 the scp, then she has the permissions that are granted by
4871 osi_Log3(afsd_logp, "cm_LockCheckPerms for scp[0x%p] type[%d] user[0x%p]",
4872 scp, lock_type, userp);
4874 if (lock_type == LockRead)
4875 rights |= PRSFS_LOCK;
4876 else if (lock_type == LockWrite)
4877 rights |= PRSFS_WRITE | PRSFS_LOCK;
4880 osi_assertx(FALSE, "invalid lock type");
4885 *phas_insert = FALSE;
4887 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
4888 CM_SCACHESYNC_GETSTATUS |
4889 CM_SCACHESYNC_NEEDCALLBACK);
4891 if (phas_insert && scp->creator == userp) {
4893 /* If this file was created by the user, then we check for
4894 PRSFS_INSERT. If the file server is recent enough, then
4895 this should be sufficient for her to get a write-lock (but
4896 not necessarily a read-lock). VICED_CAPABILITY_WRITELOCKACL
4897 indicates whether a file server supports getting write
4898 locks when the user only has PRSFS_INSERT.
4900 If the file was not created by the user we skip the check
4901 because the INSERT bit will not apply to this user even
4905 code2 = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
4906 CM_SCACHESYNC_GETSTATUS |
4907 CM_SCACHESYNC_NEEDCALLBACK);
4909 if (code2 == CM_ERROR_NOACCESS) {
4910 osi_Log0(afsd_logp, "cm_LockCheckPerms user has no INSERT bits");
4912 *phas_insert = TRUE;
4913 osi_Log0(afsd_logp, "cm_LockCheckPerms user has INSERT bits");
4917 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
4919 osi_Log1(afsd_logp, "cm_LockCheckPerms returning code %d", code);
4924 /* called with scp->rw held */
4925 long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
4926 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
4928 int allowWait, cm_user_t *userp, cm_req_t *reqp,
4929 cm_file_lock_t **lockpp)
4932 int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
4933 cm_file_lock_t *fileLock;
4936 int wait_unlock = FALSE;
4937 int force_client_lock = FALSE;
4939 osi_Log4(afsd_logp, "cm_Lock scp 0x%x type 0x%x offset %d length %d",
4940 scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
4941 osi_Log4(afsd_logp, "... allowWait %d key <0x%x, 0x%x, 0x%x>", allowWait,
4942 key.process_id, key.session_id, key.file_id);
4945 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
4947 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
4950 3.1 If L->LockType is exclusive then there does NOT exist a
4951 ACCEPTED lock M in S->fileLocks such that _a_ in
4952 (M->LOffset,+M->LLength).
4954 3.2 If L->LockType is shared then for each ACCEPTED lock M in
4955 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
4956 M->LockType is shared.
4958 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
4960 4.1 M->key != Key(C)
4962 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
4963 and (M->LOffset,+M->LLength) do not intersect.
4966 range.offset = LOffset.QuadPart;
4967 range.length = LLength.QuadPart;
4969 lock_ObtainRead(&cm_scacheLock);
4971 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4973 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4975 if (IS_LOCK_LOST(fileLock)) {
4976 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4977 code = CM_ERROR_BADFD;
4979 } else if (fileLock->lockType == LockWrite && INTERSECT_RANGE(range, fileLock->range)) {
4980 code = CM_ERROR_WOULDBLOCK;
4986 /* we don't need to check for deleted locks here since deleted
4987 locks are dequeued from scp->fileLocks */
4988 if (IS_LOCK_ACCEPTED(fileLock) &&
4989 INTERSECT_RANGE(range, fileLock->range)) {
4991 if ((sLockType & LOCKING_ANDX_SHARED_LOCK) == 0 ||
4992 fileLock->lockType != LockRead) {
4994 code = CM_ERROR_WOULDBLOCK;
5000 lock_ReleaseRead(&cm_scacheLock);
5002 if (code == 0 && SERVERLOCKS_ENABLED(scp)) {
5003 if (Which == scp->serverLock ||
5004 (Which == LockRead && scp->serverLock == LockWrite)) {
5008 /* we already have the lock we need */
5009 osi_Log3(afsd_logp, " we already have the correct lock. exclusives[%d], shared[%d], serverLock[%d]",
5010 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
5012 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
5014 /* special case: if we don't have permission to read-lock
5015 the file, then we force a clientside lock. This is to
5016 compensate for applications that obtain a read-lock for
5017 reading files off of directories that don't grant
5018 read-locks to the user. */
5019 if (code == CM_ERROR_NOACCESS && Which == LockRead) {
5021 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
5022 osi_Log0(afsd_logp, " User has no read-lock perms, but has INSERT perms.");
5025 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
5026 force_client_lock = TRUE;
5030 } else if ((scp->exclusiveLocks > 0) ||
5031 (scp->sharedLocks > 0 && scp->serverLock != LockRead)) {
5034 /* We are already waiting for some other lock. We should
5035 wait for the daemon to catch up instead of generating a
5036 flood of SetLock calls. */
5037 osi_Log3(afsd_logp, " already waiting for other lock. exclusives[%d], shared[%d], serverLock[%d]",
5038 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
5040 /* see if we have permission to create the lock in the
5042 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
5044 code = CM_ERROR_WOULDBLOCK;
5045 else if (code == CM_ERROR_NOACCESS && Which == LockRead) {
5047 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
5049 " User has no read-lock perms, but has INSERT perms.");
5050 code = CM_ERROR_WOULDBLOCK;
5053 " User has no read-lock perms. Forcing client-side lock");
5054 force_client_lock = TRUE;
5058 /* leave any other codes as-is */
5062 int check_data_version = FALSE;
5065 /* first check if we have permission to elevate or obtain
5067 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
5069 if (code == CM_ERROR_NOACCESS && Which == LockRead &&
5070 (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp))) {
5071 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
5072 force_client_lock = TRUE;
5077 /* has_insert => (Which == LockRead, code == CM_ERROR_NOACCESS) */
5079 if (scp->serverLock == LockRead && Which == LockWrite) {
5081 /* We want to escalate the lock to a LockWrite.
5082 * Unfortunately that's not really possible without
5083 * letting go of the current lock. But for now we do
5087 " attempting to UPGRADE from LockRead to LockWrite.");
5089 " dataVersion on scp: %I64d", scp->dataVersion);
5091 /* we assume at this point (because scp->serverLock
5092 was valid) that we had a valid server lock. */
5093 scp->lockDataVersion = scp->dataVersion;
5094 check_data_version = TRUE;
5096 code = cm_IntReleaseLock(scp, userp, reqp);
5099 /* We couldn't release the lock */
5102 scp->serverLock = -1;
5106 /* We need to obtain a server lock of type Which in order
5107 * to assert this file lock */
5108 #ifndef AGGRESSIVE_LOCKS
5111 newLock = LockWrite;
5114 code = cm_IntSetLock(scp, userp, newLock, reqp);
5116 #ifdef AGGRESSIVE_LOCKS
5117 if ((code == CM_ERROR_WOULDBLOCK ||
5118 code == CM_ERROR_NOACCESS) && newLock != Which) {
5119 /* we wanted LockRead. We tried LockWrite. Now try
5124 osi_assertx(newLock == LockRead, "lock type not read");
5126 code = cm_IntSetLock(scp, userp, newLock, reqp);
5130 if (code == CM_ERROR_NOACCESS) {
5131 if (Which == LockRead) {
5132 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
5134 /* We requested a read-lock, but we have permission to
5135 * get a write-lock. Try that */
5137 tcode = cm_LockCheckPerms(scp, LockWrite, userp, reqp, NULL);
5140 newLock = LockWrite;
5142 osi_Log0(afsd_logp, " User has 'i' perms and the request was for a LockRead. Trying to get a LockWrite instead");
5144 code = cm_IntSetLock(scp, userp, newLock, reqp);
5147 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
5148 force_client_lock = TRUE;
5150 } else if (Which == LockWrite &&
5151 scp->creator == userp && !SCP_SUPPORTS_WRITELOCKACL(scp)) {
5154 /* Special case: if the lock request was for a
5155 * LockWrite and the user owns the file and we weren't
5156 * allowed to obtain the serverlock, we either lost a
5157 * race (the permissions changed from under us), or we
5158 * have 'i' bits, but we aren't allowed to lock the
5161 /* check if we lost a race... */
5162 tcode = cm_LockCheckPerms(scp, Which, userp, reqp, NULL);
5165 osi_Log0(afsd_logp, " User has 'i' perms but can't obtain write locks. Using client-side locks.");
5166 force_client_lock = TRUE;
5171 if (code == 0 && check_data_version &&
5172 scp->dataVersion != scp->lockDataVersion) {
5173 /* We lost a race. Although we successfully obtained
5174 * a lock, someone modified the file in between. The
5175 * locks have all been technically lost. */
5178 " Data version mismatch while upgrading lock.");
5180 " Data versions before=%I64d, after=%I64d",
5181 scp->lockDataVersion,
5184 " Releasing stale lock for scp 0x%x", scp);
5186 code = cm_IntReleaseLock(scp, userp, reqp);
5188 scp->serverLock = -1;
5190 code = CM_ERROR_INVAL;
5191 } else if (code == 0) {
5192 scp->serverLock = newLock;
5193 scp->lockDataVersion = scp->dataVersion;
5197 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
5198 scp->serverLock == -1) {
5199 /* Oops. We lost the lock. */
5200 cm_LockMarkSCacheLost(scp);
5203 } else if (code == 0) { /* server locks not enabled */
5205 " Skipping server lock for scp");
5210 if (code != 0 && !force_client_lock) {
5211 /* Special case error translations
5213 Applications don't expect certain errors from a
5214 LockFile/UnlockFile call. We need to translate some error
5215 code to codes that apps expect and handle. */
5217 /* We shouldn't actually need to handle this case since we
5218 simulate locks for RO scps anyway. */
5219 if (code == CM_ERROR_READONLY) {
5220 osi_Log0(afsd_logp, " Reinterpreting CM_ERROR_READONLY as CM_ERROR_NOACCESS");
5221 code = CM_ERROR_NOACCESS;
5225 if (code == 0 || (code == CM_ERROR_WOULDBLOCK && allowWait) ||
5226 force_client_lock) {
5228 /* clear the error if we are forcing a client lock, so we
5229 don't get confused later. */
5230 if (force_client_lock && code != CM_ERROR_WOULDBLOCK)
5235 lock_ObtainWrite(&cm_scacheLock);
5236 fileLock = cm_GetFileLock();
5238 fileLock->fid = scp->fid;
5240 fileLock->key = key;
5241 fileLock->lockType = Which;
5242 fileLock->userp = userp;
5243 fileLock->range = range;
5244 fileLock->flags = (code == 0 ? 0 :
5246 CM_FILELOCK_FLAG_WAITUNLOCK :
5247 CM_FILELOCK_FLAG_WAITLOCK));
5249 if (force_client_lock || !SERVERLOCKS_ENABLED(scp))
5250 fileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
5252 fileLock->lastUpdate = (code == 0 && !force_client_lock) ? time(NULL) : 0;
5254 osi_QAddT(&scp->fileLocksH, &scp->fileLocksT, &fileLock->fileq);
5255 cm_HoldSCacheNoLock(scp);
5256 fileLock->scp = scp;
5257 osi_QAdd(&cm_allFileLocks, &fileLock->q);
5258 lock_ReleaseWrite(&cm_scacheLock);
5264 if (IS_LOCK_CLIENTONLY(fileLock)) {
5266 } else if (IS_LOCK_ACCEPTED(fileLock)) {
5267 if (Which == LockRead)
5270 scp->exclusiveLocks++;
5274 "cm_Lock Lock added 0x%p flags 0x%x to scp [0x%p]",
5275 fileLock, fileLock->flags, scp);
5277 " exclusives[%d] shared[%d] client[%d] serverLock[%d]",
5278 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
5279 (int)(signed char) scp->serverLock);
5282 "cm_Lock Rejecting lock (code = 0x%x)", code);
5285 /* Convert from would block to lock not granted */
5286 if (code == CM_ERROR_WOULDBLOCK)
5287 code = CM_ERROR_LOCK_NOT_GRANTED;
5293 cm_IntUnlock(cm_scache_t * scp,
5299 osi_assertx(scp->sharedLocks >= 0, "scp->sharedLocks < 0");
5300 osi_assertx(scp->exclusiveLocks >= 0, "scp->exclusiveLocks < 0");
5301 osi_assertx(scp->clientLocks >= 0, "scp->clientLocks < 0");
5303 if (!SERVERLOCKS_ENABLED(scp)) {
5304 osi_Log0(afsd_logp, " Skipping server lock for scp");
5308 /* Ideally we would go through the rest of the locks to determine
5309 * if one or more locks that were formerly in WAITUNLOCK can now
5310 * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
5311 * scp->sharedLocks accordingly. However, the retrying of locks
5312 * in that manner is done cm_RetryLock() manually.
5315 if (scp->serverLock == LockWrite &&
5316 scp->exclusiveLocks == 0 &&
5317 scp->sharedLocks > 0) {
5318 /* The serverLock should be downgraded to LockRead */
5319 osi_Log0(afsd_logp, " DOWNGRADE lock from LockWrite to LockRead");
5321 /* Make sure there are no dirty buffers left. */
5322 code = cm_FSync(scp, userp, reqp, TRUE);
5324 /* since scp->serverLock looked sane, we are going to assume
5325 that we have a valid server lock. */
5326 scp->lockDataVersion = scp->dataVersion;
5327 osi_Log1(afsd_logp, " dataVersion on scp = %I64d", scp->dataVersion);
5329 /* before we downgrade, make sure that we have enough
5330 permissions to get the read lock. */
5331 code = cm_LockCheckPerms(scp, LockRead, userp, reqp, NULL);
5334 osi_Log0(afsd_logp, " SKIPPING downgrade because user doesn't have perms to get downgraded lock");
5340 code = cm_IntReleaseLock(scp, userp, reqp);
5343 /* so we couldn't release it. Just let the lock be for now */
5347 scp->serverLock = -1;
5350 code = cm_IntSetLock(scp, userp, LockRead, reqp);
5352 if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
5353 scp->serverLock = LockRead;
5354 } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
5355 /* We lost a race condition. Although we have a valid
5356 lock on the file, the data has changed and essentially
5357 we have lost the lock we had during the transition. */
5359 osi_Log0(afsd_logp, "Data version mismatch during lock downgrade");
5360 osi_Log2(afsd_logp, " Data versions before=%I64d, after=%I64d",
5361 scp->lockDataVersion,
5364 code = cm_IntReleaseLock(scp, userp, reqp);
5366 code = CM_ERROR_INVAL;
5367 scp->serverLock = -1;
5371 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
5372 (scp->serverLock == -1)) {
5374 cm_LockMarkSCacheLost(scp);
5377 /* failure here has no bearing on the return value of cm_Unlock() */
5380 } else if (scp->serverLock != (-1) &&
5381 scp->exclusiveLocks == 0 &&
5382 scp->sharedLocks == 0) {
5383 /* The serverLock should be released entirely */
5385 if (scp->serverLock == LockWrite) {
5386 osi_Log0(afsd_logp, " RELEASE LockWrite -> LockNone");
5388 /* Make sure there are no dirty buffers left. */
5389 code = cm_FSync(scp, userp, reqp, TRUE);
5391 osi_Log0(afsd_logp, " RELEASE LockRead -> LockNone");
5394 code = cm_IntReleaseLock(scp, userp, reqp);
5397 scp->serverLock = (-1);
5403 /* Called with scp->rw held */
5404 long cm_UnlockByKey(cm_scache_t * scp,
5411 cm_file_lock_t *fileLock;
5412 osi_queue_t *q, *qn;
5415 osi_Log4(afsd_logp, "cm_UnlockByKey scp 0x%p key <0x%x,0x%x,0x%x",
5416 scp, key.process_id, key.session_id, key.file_id);
5417 osi_Log1(afsd_logp, " flags=0x%x", flags);
5419 lock_ObtainWrite(&cm_scacheLock);
5421 for (q = scp->fileLocksH; q; q = qn) {
5424 fileLock = (cm_file_lock_t *)
5425 ((char *) q - offsetof(cm_file_lock_t, fileq));
5428 osi_Log4(afsd_logp, " Checking lock[0x%x] range[%d,+%d] type[%d]",
5430 (unsigned long) fileLock->range.offset,
5431 (unsigned long) fileLock->range.length,
5432 fileLock->lockType);
5433 osi_Log4(afsd_logp, " key<0x%x, 0x%x, 0x%x> flags[0x%x]",
5434 fileLock->key.process_id, fileLock->key.session_id, fileLock->key.file_id,
5437 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5438 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5439 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5441 fileLock->fid.volume,
5442 fileLock->fid.vnode,
5443 fileLock->fid.unique);
5444 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5445 fileLock->scp->fid.cell,
5446 fileLock->scp->fid.volume,
5447 fileLock->scp->fid.vnode,
5448 fileLock->scp->fid.unique);
5449 osi_assertx(FALSE, "invalid fid value");
5453 if (!IS_LOCK_DELETED(fileLock) &&
5454 cm_KeyEquals(&fileLock->key, &key, flags)) {
5455 osi_Log3(afsd_logp, "...Unlock range [%d,+%d] type %d",
5456 fileLock->range.offset,
5457 fileLock->range.length,
5458 fileLock->lockType);
5460 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
5462 if (IS_LOCK_CLIENTONLY(fileLock)) {
5464 } else if (IS_LOCK_ACCEPTED(fileLock)) {
5465 if (fileLock->lockType == LockRead)
5468 scp->exclusiveLocks--;
5471 fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5473 cm_ReleaseUser(fileLock->userp);
5474 cm_ReleaseSCacheNoLock(scp);
5476 fileLock->userp = NULL;
5477 fileLock->scp = NULL;
5483 lock_ReleaseWrite(&cm_scacheLock);
5485 if (n_unlocks == 0) {
5486 osi_Log0(afsd_logp, "cm_UnlockByKey no locks found");
5487 osi_Log3(afsd_logp, " Leaving scp with exclusives[%d], shared[%d], serverLock[%d]",
5488 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
5493 code = cm_IntUnlock(scp, userp, reqp);
5494 osi_Log1(afsd_logp, "cm_UnlockByKey code 0x%x", code);
5496 osi_Log4(afsd_logp, " Leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
5497 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
5498 (int)(signed char) scp->serverLock);
5503 /* Called with scp->rw held */
5504 long cm_Unlock(cm_scache_t *scp,
5505 unsigned char sLockType,
5506 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
5513 int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
5514 cm_file_lock_t *fileLock;
5516 int release_userp = FALSE;
5517 int exact_match = !(flags & CM_UNLOCK_FLAG_MATCH_RANGE);
5519 LARGE_INTEGER RangeEnd;
5521 osi_Log4(afsd_logp, "cm_Unlock scp 0x%p type 0x%x offset 0x%x length 0x%x",
5522 scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
5523 osi_Log4(afsd_logp, "... key <0x%x,0x%x,0x%x> flags 0x%x",
5524 key.process_id, key.session_id, key.file_id, flags);
5527 RangeEnd.QuadPart = LOffset.QuadPart + LLength.QuadPart;
5530 lock_ObtainRead(&cm_scacheLock);
5532 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5533 fileLock = (cm_file_lock_t *)
5534 ((char *) q - offsetof(cm_file_lock_t, fileq));
5537 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5538 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5539 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5541 fileLock->fid.volume,
5542 fileLock->fid.vnode,
5543 fileLock->fid.unique);
5544 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5545 fileLock->scp->fid.cell,
5546 fileLock->scp->fid.volume,
5547 fileLock->scp->fid.vnode,
5548 fileLock->scp->fid.unique);
5549 osi_assertx(FALSE, "invalid fid value");
5553 if (!IS_LOCK_DELETED(fileLock) &&
5554 cm_KeyEquals(&fileLock->key, &key, 0) &&
5555 fileLock->range.offset == LOffset.QuadPart &&
5556 fileLock->range.length == LLength.QuadPart) {
5562 if (!IS_LOCK_DELETED(fileLock) &&
5563 cm_KeyEquals(&fileLock->key, &key, 0) &&
5564 fileLock->range.offset >= LOffset.QuadPart &&
5565 fileLock->range.offset < RangeEnd.QuadPart &&
5566 (fileLock->range.offset + fileLock->range.length) <= RangeEnd.QuadPart) {
5574 lock_ReleaseRead(&cm_scacheLock);
5576 if (lock_found && !exact_match) {
5580 osi_Log0(afsd_logp, "cm_Unlock lock not found; failure");
5582 /* The lock didn't exist anyway. *shrug* */
5583 return CM_ERROR_RANGE_NOT_LOCKED;
5587 /* discard lock record */
5588 lock_ConvertRToW(&cm_scacheLock);
5589 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
5592 * Don't delete it here; let the daemon delete it, to simplify
5593 * the daemon's traversal of the list.
5596 if (IS_LOCK_CLIENTONLY(fileLock)) {
5598 } else if (IS_LOCK_ACCEPTED(fileLock)) {
5599 if (fileLock->lockType == LockRead)
5602 scp->exclusiveLocks--;
5605 fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5607 if (userp != NULL) {
5608 cm_ReleaseUser(fileLock->userp);
5610 userp = fileLock->userp;
5611 release_userp = TRUE;
5613 cm_ReleaseSCacheNoLock(scp);
5614 fileLock->userp = NULL;
5615 fileLock->scp = NULL;
5616 lock_ReleaseWrite(&cm_scacheLock);
5618 code = cm_IntUnlock(scp, userp, reqp);
5620 if (release_userp) {
5621 cm_ReleaseUser(userp);
5622 release_userp = FALSE;
5626 osi_Log1(afsd_logp, "cm_Unlock not exact match, searching for next lock, code 0x%x", code);
5627 goto try_again; /* might be more than one lock in the range */
5632 osi_Log1(afsd_logp, "cm_Unlock code 0x%x", code);
5633 osi_Log4(afsd_logp, " leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
5634 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
5635 (int)(signed char) scp->serverLock);
5640 /* called with scp->rw held */
5641 void cm_LockMarkSCacheLost(cm_scache_t * scp)
5643 cm_file_lock_t *fileLock;
5646 osi_Log1(afsd_logp, "cm_LockMarkSCacheLost scp 0x%x", scp);
5648 /* cm_scacheLock needed because we are modifying fileLock->flags */
5649 lock_ObtainWrite(&cm_scacheLock);
5651 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5653 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
5655 if (IS_LOCK_ACTIVE(fileLock) &&
5656 !IS_LOCK_CLIENTONLY(fileLock)) {
5657 if (fileLock->lockType == LockRead)
5660 scp->exclusiveLocks--;
5662 fileLock->flags |= CM_FILELOCK_FLAG_LOST;
5666 scp->serverLock = -1;
5667 scp->lockDataVersion = CM_SCACHE_VERSION_BAD;
5668 lock_ReleaseWrite(&cm_scacheLock);
5671 /* Called with no relevant locks held */
5672 void cm_CheckLocks()
5674 osi_queue_t *q, *nq;
5675 cm_file_lock_t *fileLock;
5681 struct rx_connection * rxconnp;
5684 memset(&volSync, 0, sizeof(volSync));
5688 lock_ObtainWrite(&cm_scacheLock);
5690 cm_lockRefreshCycle++;
5692 osi_Log1(afsd_logp, "cm_CheckLocks starting lock check cycle %d", cm_lockRefreshCycle);
5694 for (q = cm_allFileLocks; q; q = nq) {
5695 fileLock = (cm_file_lock_t *) q;
5699 if (IS_LOCK_DELETED(fileLock)) {
5700 cm_user_t *userp = fileLock->userp;
5701 cm_scache_t *scp = fileLock->scp;
5702 fileLock->userp = NULL;
5703 fileLock->scp = NULL;
5706 lock_ReleaseWrite(&cm_scacheLock);
5707 lock_ObtainWrite(&scp->rw);
5708 code = cm_IntUnlock(scp, userp, &req);
5709 lock_ReleaseWrite(&scp->rw);
5711 cm_ReleaseUser(userp);
5712 lock_ObtainWrite(&cm_scacheLock);
5713 cm_ReleaseSCacheNoLock(scp);
5715 osi_QRemove(&cm_allFileLocks, q);
5716 cm_PutFileLock(fileLock);
5718 } else if (IS_LOCK_ACTIVE(fileLock) && !IS_LOCK_CLIENTONLY(fileLock)) {
5720 /* Server locks must have been enabled for us to have
5721 received an active non-client-only lock. */
5722 osi_assertx(cm_enableServerLocks, "!cm_enableServerLocks");
5724 scp = fileLock->scp;
5725 osi_assertx(scp != NULL, "null cm_scache_t");
5727 cm_HoldSCacheNoLock(scp);
5730 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5731 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5732 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5734 fileLock->fid.volume,
5735 fileLock->fid.vnode,
5736 fileLock->fid.unique);
5737 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5738 fileLock->scp->fid.cell,
5739 fileLock->scp->fid.volume,
5740 fileLock->scp->fid.vnode,
5741 fileLock->scp->fid.unique);
5742 osi_assertx(FALSE, "invalid fid");
5745 /* Server locks are extended once per scp per refresh
5747 if (scp->lastRefreshCycle != cm_lockRefreshCycle) {
5749 int scp_done = FALSE;
5751 osi_Log1(afsd_logp, "cm_CheckLocks Updating scp 0x%x", scp);
5753 lock_ReleaseWrite(&cm_scacheLock);
5754 lock_ObtainWrite(&scp->rw);
5756 /* did the lock change while we weren't holding the lock? */
5757 if (!IS_LOCK_ACTIVE(fileLock))
5758 goto post_syncopdone;
5760 code = cm_SyncOp(scp, NULL, fileLock->userp, &req, 0,
5761 CM_SCACHESYNC_NEEDCALLBACK
5762 | CM_SCACHESYNC_GETSTATUS
5763 | CM_SCACHESYNC_LOCK);
5767 "cm_CheckLocks SyncOp failure code 0x%x", code);
5768 goto post_syncopdone;
5771 /* cm_SyncOp releases scp->rw during which the lock
5772 may get released. */
5773 if (!IS_LOCK_ACTIVE(fileLock))
5774 goto pre_syncopdone;
5776 if (scp->serverLock != -1 && !(scp->flags & CM_SCACHEFLAG_DELETED)) {
5780 tfid.Volume = scp->fid.volume;
5781 tfid.Vnode = scp->fid.vnode;
5782 tfid.Unique = scp->fid.unique;
5784 userp = fileLock->userp;
5786 osi_Log3(afsd_logp, "CALL ExtendLock lock 0x%p for scp=0x%p with lock %d",
5789 (int) scp->serverLock);
5791 lock_ReleaseWrite(&scp->rw);
5794 code = cm_ConnFromFID(&cfid, userp,
5799 rxconnp = cm_GetRxConn(connp);
5800 code = RXAFS_ExtendLock(rxconnp, &tfid,
5802 rx_PutConnection(rxconnp);
5804 osi_Log1(afsd_logp, " ExtendLock returns %d", code);
5806 } while (cm_Analyze(connp, userp, &req,
5807 &cfid, NULL, 1, NULL, &volSync, NULL, NULL,
5810 code = cm_MapRPCError(code, &req);
5812 lock_ObtainWrite(&scp->rw);
5815 osi_Log1(afsd_logp, "CALL ExtendLock FAILURE, code 0x%x", code);
5816 scp->fsLockCount = 0;
5818 osi_Log0(afsd_logp, "CALL ExtendLock SUCCESS");
5819 scp->lockDataVersion = scp->dataVersion;
5822 if ((code == EINVAL || code == CM_ERROR_INVAL) &&
5823 scp->lockDataVersion == scp->dataVersion) {
5827 (scp->exclusiveLocks > 0) ? LockWrite: LockRead;
5829 /* we might still have a chance to obtain a
5832 code = cm_IntSetLock(scp, userp, lockType, &req);
5835 code = CM_ERROR_INVAL;
5836 } else if (scp->lockDataVersion != scp->dataVersion) {
5838 /* now check if we still have the file at
5839 the right data version. */
5841 "Data version mismatch on scp 0x%p",
5844 " Data versions: before=%I64d, after=%I64d",
5845 scp->lockDataVersion,
5848 code = cm_IntReleaseLock(scp, userp, &req);
5850 code = CM_ERROR_INVAL;
5854 if (code == EINVAL || code == CM_ERROR_INVAL ||
5855 code == CM_ERROR_BADFD) {
5856 cm_LockMarkSCacheLost(scp);
5860 /* interestingly, we have found an active lock
5861 belonging to an scache that has no
5863 cm_LockMarkSCacheLost(scp);
5870 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5873 lock_ReleaseWrite(&scp->rw);
5875 lock_ObtainWrite(&cm_scacheLock);
5878 fileLock->lastUpdate = time(NULL);
5882 scp->lastRefreshCycle = cm_lockRefreshCycle;
5885 /* we have already refreshed the locks on this scp */
5886 fileLock->lastUpdate = time(NULL);
5889 cm_ReleaseSCacheNoLock(scp);
5891 } else if (IS_LOCK_ACTIVE(fileLock) && IS_LOCK_CLIENTONLY(fileLock)) {
5892 /* TODO: Check callbacks */
5896 lock_ReleaseWrite(&cm_scacheLock);
5897 osi_Log1(afsd_logp, "cm_CheckLocks completes lock check cycle %d", cm_lockRefreshCycle);
5900 /* NOT called with scp->rw held. */
5901 long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
5904 cm_scache_t *scp = NULL;
5905 cm_file_lock_t *fileLock;
5909 int force_client_lock = FALSE;
5910 int has_insert = FALSE;
5911 int check_data_version = FALSE;
5915 if (client_is_dead) {
5916 code = CM_ERROR_TIMEDOUT;
5920 lock_ObtainRead(&cm_scacheLock);
5922 osi_Log2(afsd_logp, "cm_RetryLock checking lock %p (scp=%p)", oldFileLock, oldFileLock->scp);
5923 osi_Log4(afsd_logp, " offset(%x:%x) length(%x:%x)",
5924 (unsigned)(oldFileLock->range.offset >> 32),
5925 (unsigned)(oldFileLock->range.offset & 0xffffffff),
5926 (unsigned)(oldFileLock->range.length >> 32),
5927 (unsigned)(oldFileLock->range.length & 0xffffffff));
5928 osi_Log4(afsd_logp, " key<0x%x,0x%x,0x%x> flags=%x",
5929 oldFileLock->key.process_id, oldFileLock->key.session_id, oldFileLock->key.file_id,
5930 (unsigned)(oldFileLock->flags));
5932 /* if the lock has already been granted, then we have nothing to do */
5933 if (IS_LOCK_ACTIVE(oldFileLock)) {
5934 lock_ReleaseRead(&cm_scacheLock);
5935 osi_Log0(afsd_logp, "cm_RetryLock lock already granted");
5939 /* we can't do anything with lost or deleted locks at the moment. */
5940 if (IS_LOCK_LOST(oldFileLock) || IS_LOCK_DELETED(oldFileLock)) {
5941 code = CM_ERROR_BADFD;
5942 osi_Log0(afsd_logp, "cm_RetryLock lock is lost or deleted");
5943 lock_ReleaseRead(&cm_scacheLock);
5947 scp = oldFileLock->scp;
5949 osi_assertx(scp != NULL, "null cm_scache_t");
5951 lock_ReleaseRead(&cm_scacheLock);
5952 lock_ObtainWrite(&scp->rw);
5954 code = cm_LockCheckPerms(scp, oldFileLock->lockType,
5958 if (code == CM_ERROR_NOACCESS && oldFileLock->lockType == LockRead) {
5959 if (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp)) {
5960 force_client_lock = TRUE;
5964 lock_ReleaseWrite(&scp->rw);
5968 lock_ObtainWrite(&cm_scacheLock);
5970 /* Check if we already have a sufficient server lock to allow this
5971 lock to go through. */
5972 if (IS_LOCK_WAITLOCK(oldFileLock) &&
5973 (!SERVERLOCKS_ENABLED(scp) ||
5974 scp->serverLock == oldFileLock->lockType ||
5975 scp->serverLock == LockWrite)) {
5977 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5979 if (SERVERLOCKS_ENABLED(scp)) {
5980 osi_Log1(afsd_logp, "cm_RetryLock Server lock (%d) is sufficient for lock. Granting",
5981 (int) scp->serverLock);
5983 osi_Log0(afsd_logp, "cm_RetryLock skipping server lock for scp");
5986 lock_ReleaseWrite(&cm_scacheLock);
5987 lock_ReleaseWrite(&scp->rw);
5992 if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5994 /* check if the conflicting locks have dissappeared already */
5995 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5997 fileLock = (cm_file_lock_t *)
5998 ((char *) q - offsetof(cm_file_lock_t, fileq));
6000 if (IS_LOCK_LOST(fileLock)) {
6001 if (cm_KeyEquals(&fileLock->key, &oldFileLock->key, 0)) {
6002 code = CM_ERROR_BADFD;
6003 oldFileLock->flags |= CM_FILELOCK_FLAG_LOST;
6004 osi_Log1(afsd_logp, " found lost lock %p for same key. Marking lock as lost",
6007 } else if (fileLock->lockType == LockWrite &&
6008 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
6009 osi_Log1(afsd_logp, " found conflicting LOST lock %p", fileLock);
6010 code = CM_ERROR_WOULDBLOCK;
6015 if (IS_LOCK_ACCEPTED(fileLock) &&
6016 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
6018 if (oldFileLock->lockType != LockRead ||
6019 fileLock->lockType != LockRead) {
6021 osi_Log1(afsd_logp, " found conflicting lock %p", fileLock);
6022 code = CM_ERROR_WOULDBLOCK;
6030 lock_ReleaseWrite(&cm_scacheLock);
6031 lock_ReleaseWrite(&scp->rw);
6036 /* when we get here, the lock is either a WAITUNLOCK or WAITLOCK.
6037 If it is WAITUNLOCK, then we didn't find any conflicting lock
6038 but we haven't verfied whether the serverLock is sufficient to
6039 assert it. If it is WAITLOCK, then the serverLock is
6040 insufficient to assert it. Eitherway, we are ready to accept
6041 the lock as either ACTIVE or WAITLOCK depending on the
6044 /* First, promote the WAITUNLOCK to a WAITLOCK */
6045 if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
6046 if (oldFileLock->lockType == LockRead)
6049 scp->exclusiveLocks++;
6051 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITUNLOCK;
6052 oldFileLock->flags |= CM_FILELOCK_FLAG_WAITLOCK;
6055 osi_assertx(IS_LOCK_WAITLOCK(oldFileLock), "!IS_LOCK_WAITLOCK");
6057 if (force_client_lock ||
6058 !SERVERLOCKS_ENABLED(scp) ||
6059 scp->serverLock == oldFileLock->lockType ||
6060 (oldFileLock->lockType == LockRead &&
6061 scp->serverLock == LockWrite)) {
6063 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
6065 if ((force_client_lock ||
6066 !SERVERLOCKS_ENABLED(scp)) &&
6067 !IS_LOCK_CLIENTONLY(oldFileLock)) {
6069 oldFileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
6071 if (oldFileLock->lockType == LockRead)
6074 scp->exclusiveLocks--;
6079 lock_ReleaseWrite(&cm_scacheLock);
6080 lock_ReleaseWrite(&scp->rw);
6087 code = cm_SyncOp(scp, NULL, oldFileLock->userp, &req, 0,
6088 CM_SCACHESYNC_NEEDCALLBACK
6089 | CM_SCACHESYNC_GETSTATUS
6090 | CM_SCACHESYNC_LOCK);
6092 osi_Log1(afsd_logp, "cm_RetryLock SyncOp failure code 0x%x", code);
6093 lock_ReleaseWrite(&cm_scacheLock);
6094 goto post_syncopdone;
6097 if (!IS_LOCK_WAITLOCK(oldFileLock))
6098 goto pre_syncopdone;
6100 userp = oldFileLock->userp;
6102 #ifndef AGGRESSIVE_LOCKS
6103 newLock = oldFileLock->lockType;
6105 newLock = LockWrite;
6109 /* if has_insert is non-zero, then:
6110 - the lock a LockRead
6111 - we don't have permission to get a LockRead
6112 - we do have permission to get a LockWrite
6113 - the server supports VICED_CAPABILITY_WRITELOCKACL
6116 newLock = LockWrite;
6119 lock_ReleaseWrite(&cm_scacheLock);
6121 /* when we get here, either we have a read-lock and want a
6122 write-lock or we don't have any locks and we want some
6125 if (scp->serverLock == LockRead) {
6127 osi_assertx(newLock == LockWrite, "!LockWrite");
6129 osi_Log0(afsd_logp, " Attempting to UPGRADE from LockRead to LockWrite");
6131 scp->lockDataVersion = scp->dataVersion;
6132 check_data_version = TRUE;
6134 code = cm_IntReleaseLock(scp, userp, &req);
6137 goto pre_syncopdone;
6139 scp->serverLock = -1;
6142 code = cm_IntSetLock(scp, userp, newLock, &req);
6145 if (scp->dataVersion != scp->lockDataVersion) {
6146 /* we lost a race. too bad */
6149 " Data version mismatch while upgrading lock.");
6151 " Data versions before=%I64d, after=%I64d",
6152 scp->lockDataVersion,
6155 " Releasing stale lock for scp 0x%x", scp);
6157 code = cm_IntReleaseLock(scp, userp, &req);
6159 scp->serverLock = -1;
6161 code = CM_ERROR_INVAL;
6163 cm_LockMarkSCacheLost(scp);
6165 scp->serverLock = newLock;
6170 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
6176 if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
6177 lock_ObtainWrite(&cm_scacheLock);
6178 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, &oldFileLock->fileq);
6179 lock_ReleaseWrite(&cm_scacheLock);
6181 lock_ReleaseWrite(&scp->rw);
6184 lock_ObtainWrite(&cm_scacheLock);
6186 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
6187 } else if (code != CM_ERROR_WOULDBLOCK) {
6188 oldFileLock->flags |= CM_FILELOCK_FLAG_DELETED;
6189 cm_ReleaseUser(oldFileLock->userp);
6190 oldFileLock->userp = NULL;
6191 if (oldFileLock->scp) {
6192 cm_ReleaseSCacheNoLock(oldFileLock->scp);
6193 oldFileLock->scp = NULL;
6196 lock_ReleaseWrite(&cm_scacheLock);
6201 cm_key_t cm_GenerateKey(afs_uint16 session_id, afs_offs_t process_id, afs_uint64 file_id)
6205 key.process_id = process_id;
6206 key.session_id = session_id;
6207 key.file_id = file_id;
6212 int cm_KeyEquals(cm_key_t *k1, cm_key_t *k2, int flags)
6214 return (k1->session_id == k2->session_id) && (k1->file_id == k2->file_id) &&
6215 ((flags & CM_UNLOCK_FLAG_BY_FID) || (k1->process_id == k2->process_id));
6218 void cm_ReleaseAllLocks(void)
6224 cm_file_lock_t *fileLock;
6227 for (i = 0; i < cm_data.scacheHashTableSize; i++)
6229 for ( scp = cm_data.scacheHashTablep[i]; scp; scp = scp->nextp ) {
6230 while (scp->fileLocksH != NULL) {
6231 lock_ObtainWrite(&scp->rw);
6232 lock_ObtainWrite(&cm_scacheLock);
6233 if (!scp->fileLocksH) {
6234 lock_ReleaseWrite(&cm_scacheLock);
6235 lock_ReleaseWrite(&scp->rw);
6238 fileLock = (cm_file_lock_t *)((char *) scp->fileLocksH - offsetof(cm_file_lock_t, fileq));
6239 userp = fileLock->userp;
6241 key = fileLock->key;
6242 cm_HoldSCacheNoLock(scp);
6243 lock_ReleaseWrite(&cm_scacheLock);
6244 cm_UnlockByKey(scp, key, 0, userp, &req);
6245 cm_ReleaseSCache(scp);
6246 cm_ReleaseUser(userp);
6247 lock_ReleaseWrite(&scp->rw);