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, &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, &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);
770 && !cm_Is8Dot3(matchName)) {
772 cm_Gen8Dot3NameInt(dep->name, &dep->fid, matchName, NULL);
774 match = cm_NormStrCmpI(matchName, sp->nsearchNamep);
776 match = cm_NormStrCmp(matchName, sp->nsearchNamep);
777 looking_for_short_name = TRUE;
787 if (!sp->caseFold || looking_for_short_name) {
788 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
789 return CM_ERROR_STOPNOW;
793 * If we get here, we are doing a case-insensitive search, and we
794 * have found a match. Now we determine what kind of match it is:
795 * exact, lower-case, upper-case, or none of the above. This is done
796 * in order to choose among matches, if there are more than one.
799 /* Exact matches are the best. */
800 match = cm_NormStrCmp(matchName, sp->nsearchNamep);
803 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
804 return CM_ERROR_STOPNOW;
807 /* Lower-case matches are next. */
810 if (cm_NoneUpper(matchName)) {
815 /* Upper-case matches are next. */
818 if (cm_NoneLower(matchName)) {
823 /* General matches are last. */
829 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
833 /* read the contents of a mount point into the appropriate string.
834 * called with write locked scp, and returns with locked scp.
836 long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
840 if (scp->mountPointStringp[0] &&
841 scp->mpDataVersion == scp->dataVersion)
844 #ifdef AFS_FREELANCE_CLIENT
845 /* File servers do not have data for freelance entries */
846 if (cm_freelanceEnabled &&
847 scp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
848 scp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
850 code = cm_FreelanceFetchMountPointString(scp);
852 #endif /* AFS_FREELANCE_CLIENT */
854 char temp[MOUNTPOINTLEN];
857 /* otherwise, we have to read it in */
858 offset.LowPart = offset.HighPart = 0;
859 code = cm_GetData(scp, &offset, temp, MOUNTPOINTLEN, userp, reqp);
864 * scp->length is the actual length of the mount point string.
865 * It is current because cm_GetData merged the most up to date
866 * status info into scp and has not dropped the rwlock since.
868 if (scp->length.LowPart > MOUNTPOINTLEN - 1)
869 return CM_ERROR_TOOBIG;
870 if (scp->length.LowPart == 0)
871 return CM_ERROR_INVAL;
873 /* convert the terminating dot to a NUL */
874 temp[scp->length.LowPart - 1] = 0;
875 memcpy(scp->mountPointStringp, temp, scp->length.LowPart);
876 scp->mpDataVersion = scp->dataVersion;
883 /* called with a locked scp and chases the mount point, yielding outScpp.
884 * scp remains write locked, just for simplicity of describing the interface.
886 long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
887 cm_req_t *reqp, cm_scache_t **outScpp)
889 fschar_t *cellNamep = NULL;
890 fschar_t *volNamep = NULL;
894 cm_volume_t *volp = NULL;
903 if (scp->mountRootFid.cell != 0 && scp->mountRootGen >= cm_data.mountRootGen) {
904 tfid = scp->mountRootFid;
905 lock_ReleaseWrite(&scp->rw);
906 code = cm_GetSCache(&tfid, NULL, outScpp, userp, reqp);
907 lock_ObtainWrite(&scp->rw);
911 /* parse the volume name */
912 mpNamep = scp->mountPointStringp;
914 return CM_ERROR_NOSUCHPATH;
915 mtType = *scp->mountPointStringp;
917 cp = cm_FsStrChr(mpNamep, _FS(':'));
919 /* cellular mount point */
920 cellNamep = (fschar_t *)malloc((cp - mpNamep) * sizeof(fschar_t));
921 cm_FsStrCpyN(cellNamep, cp - mpNamep, mpNamep + 1, cp - mpNamep - 1);
922 volNamep = cm_FsStrDup(cp+1);
924 /* now look up the cell */
925 lock_ReleaseWrite(&scp->rw);
926 cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE|CM_FLAG_NOPROBE);
927 lock_ObtainWrite(&scp->rw);
930 volNamep = cm_FsStrDup(mpNamep + 1);
932 #ifdef AFS_FREELANCE_CLIENT
934 * Mount points in the Freelance cell should default
935 * to the workstation cell.
937 if (cm_freelanceEnabled &&
938 scp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
939 scp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
941 fschar_t rootCellName[256]="";
942 cm_GetRootCellName(rootCellName);
943 cellp = cm_GetCell(rootCellName, 0);
945 #endif /* AFS_FREELANCE_CLIENT */
946 cellp = cm_FindCellByID(scp->fid.cell, 0);
950 code = CM_ERROR_NOSUCHCELL;
954 vnLength = cm_FsStrLen(volNamep);
955 if (vnLength >= 8 && cm_FsStrCmp(volNamep + vnLength - 7, ".backup") == 0)
956 targetType = BACKVOL;
957 else if (vnLength >= 10
958 && cm_FsStrCmp(volNamep + vnLength - 9, ".readonly") == 0)
963 /* check for backups within backups */
964 if (targetType == BACKVOL
965 && (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
966 == CM_SCACHEFLAG_RO) {
967 code = CM_ERROR_NOSUCHVOLUME;
971 /* now we need to get the volume */
972 lock_ReleaseWrite(&scp->rw);
973 if (cm_VolNameIsID(volNamep)) {
974 code = cm_FindVolumeByID(cellp, atoi(volNamep), userp, reqp,
975 CM_GETVOL_FLAG_CREATE, &volp);
977 code = cm_FindVolumeByName(cellp, volNamep, userp, reqp,
978 CM_GETVOL_FLAG_CREATE, &volp);
980 lock_ObtainWrite(&scp->rw);
983 afs_uint32 cell, volume;
984 cm_vol_state_t *statep;
986 cell = cellp->cellID;
988 /* if the mt pt originates in a .backup volume (not a .readonly)
989 * and FollowBackupPath is active, and if there is a .backup
990 * volume for the target, then use the .backup of the target
991 * instead of the read-write.
993 if (cm_followBackupPath &&
994 volp->vol[BACKVOL].ID != 0 &&
995 (dscp->flags & (CM_SCACHEFLAG_RO|CM_SCACHEFLAG_PURERO)) == CM_SCACHEFLAG_RO &&
996 (targetType == RWVOL || targetType == ROVOL && volp->vol[ROVOL].ID == 0)
998 targetType = BACKVOL;
1000 /* if the mt pt is in a read-only volume (not just a
1001 * backup), and if there is a read-only volume for the
1002 * target, and if this is a targetType '#' mount point, use
1003 * the read-only, otherwise use the one specified.
1005 else if (mtType == '#' && targetType == RWVOL &&
1006 (scp->flags & CM_SCACHEFLAG_PURERO) &&
1007 volp->vol[ROVOL].ID != 0) {
1011 lock_ObtainWrite(&volp->rw);
1012 statep = cm_VolumeStateByType(volp, targetType);
1013 volume = statep->ID;
1014 statep->dotdotFid = dscp->fid;
1015 lock_ReleaseWrite(&volp->rw);
1017 /* the rest of the fid is a magic number */
1018 cm_SetFid(&scp->mountRootFid, cell, volume, 1, 1);
1019 scp->mountRootGen = cm_data.mountRootGen;
1021 tfid = scp->mountRootFid;
1022 lock_ReleaseWrite(&scp->rw);
1023 code = cm_GetSCache(&tfid, NULL, outScpp, userp, reqp);
1024 lock_ObtainWrite(&scp->rw);
1037 long cm_LookupInternal(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_user_t *userp,
1038 cm_req_t *reqp, cm_scache_t **outScpp)
1041 int dnlcHit = 1; /* did we hit in the dnlc? yes, we did */
1042 cm_scache_t *tscp = NULL;
1043 cm_scache_t *mountedScp;
1044 cm_lookupSearch_t rock;
1046 normchar_t *nnamep = NULL;
1047 fschar_t *fnamep = NULL;
1052 memset(&rock, 0, sizeof(rock));
1054 if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
1055 && cm_ClientStrCmp(cnamep, _C("..")) == 0) {
1056 if (dscp->dotdotFid.volume == 0)
1057 return CM_ERROR_NOSUCHVOLUME;
1058 rock.fid = dscp->dotdotFid;
1060 } else if (cm_ClientStrCmp(cnamep, _C(".")) == 0) {
1061 rock.fid = dscp->fid;
1065 nnamep = cm_ClientStringToNormStringAlloc(cnamep, -1, NULL);
1067 code = CM_ERROR_NOSUCHFILE;
1070 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
1072 code = CM_ERROR_NOSUCHFILE;
1077 if (flags & CM_FLAG_NOMOUNTCHASE) {
1078 /* In this case, we should go and call cm_Dir* functions
1079 directly since the following cm_ApplyDir() function will
1087 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ,
1088 CM_DIROP_FLAG_NONE, &dirop);
1091 code = cm_BPlusDirLookup(&dirop, nnamep, &rock.fid);
1096 code = cm_DirLookup(&dirop, fnamep, &rock.fid);
1098 cm_EndDirOp(&dirop);
1108 if (code == CM_ERROR_INEXACT_MATCH && (flags & CM_FLAG_CASEFOLD)) {
1115 code = CM_ERROR_BPLUS_NOMATCH;
1121 rock.fid.cell = dscp->fid.cell;
1122 rock.fid.volume = dscp->fid.volume;
1123 rock.searchNamep = fnamep;
1124 rock.nsearchNamep = nnamep;
1125 rock.caseFold = (flags & CM_FLAG_CASEFOLD);
1126 rock.hasTilde = ((cm_ClientStrChr(cnamep, '~') != NULL) ? 1 : 0);
1128 /* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
1129 code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
1130 (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
1132 /* code == 0 means we fell off the end of the dir, while stopnow means
1133 * that we stopped early, probably because we found the entry we're
1134 * looking for. Any other non-zero code is an error.
1136 if (code && code != CM_ERROR_STOPNOW && code != CM_ERROR_BPLUS_NOMATCH) {
1137 /* if the cm_scache_t we are searching in is not a directory
1138 * we must return path not found because the error
1139 * is to describe the final component not an intermediary
1141 if (code == CM_ERROR_NOTDIR) {
1142 if (flags & CM_FLAG_CHECKPATH)
1143 code = CM_ERROR_NOSUCHPATH;
1145 code = CM_ERROR_NOSUCHFILE;
1151 getroot = (dscp==cm_data.rootSCachep) ;
1153 if (!cm_freelanceEnabled || !getroot) {
1154 if (flags & CM_FLAG_CHECKPATH)
1155 code = CM_ERROR_NOSUCHPATH;
1157 code = CM_ERROR_NOSUCHFILE;
1160 else if (!cm_ClientStrChr(cnamep, '#') &&
1161 !cm_ClientStrChr(cnamep, '%') &&
1162 cm_ClientStrCmpI(cnamep, _C("srvsvc")) &&
1163 cm_ClientStrCmpI(cnamep, _C("wkssvc")) &&
1164 cm_ClientStrCmpI(cnamep, _C("ipc$")))
1166 /* nonexistent dir on freelance root, so add it */
1167 fschar_t fullname[CELL_MAXNAMELEN + 1] = "."; /* +1 so that when we skip the . the size is still CELL_MAXNAMELEN */
1171 osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %S",
1172 osi_LogSaveClientString(afsd_logp,cnamep));
1175 * There is an ugly behavior where a share name "foo" will be searched
1176 * for as "fo". If the searched for name differs by an already existing
1177 * symlink or mount point in the Freelance directory, do not add the
1178 * new value automatically.
1182 fnlen = strlen(fnamep);
1183 if ( fnamep[fnlen-1] == '.') {
1184 fnamep[fnlen-1] = '\0';
1189 if (cnamep[0] == '.') {
1190 if (cm_GetCell_Gen(&fnamep[1], &fullname[1], CM_FLAG_CREATE)) {
1192 code = cm_FreelanceAddMount(fullname, &fullname[1], "root.cell", 1, &rock.fid);
1193 if ( cm_FsStrCmpI(&fnamep[1], &fullname[1])) {
1195 * Do not permit symlinks that are one of:
1196 * . the cellname followed by a dot
1197 * . the cellname minus a single character
1198 * . a substring of the cellname that does not consist of full components
1200 if ( cm_strnicmp_utf8(&fnamep[1], fullname, (int)fnlen-1) == 0 &&
1201 (fnlen-1 == strlen(fullname)-1 || fullname[fnlen-1] != '.'))
1203 /* do not add; substitute fullname for the search */
1205 fnamep = malloc(strlen(fullname)+2);
1207 strncpy(&fnamep[1], fullname, strlen(fullname)+1);
1210 code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
1215 if (cm_GetCell_Gen(fnamep, fullname, CM_FLAG_CREATE)) {
1217 code = cm_FreelanceAddMount(fullname, fullname, "root.cell", 0, &rock.fid);
1218 if ( cm_FsStrCmpI(fnamep, fullname)) {
1220 * Do not permit symlinks that are one of:
1221 * . the cellname followed by a dot
1222 * . the cellname minus a single character
1223 * . a substring of the cellname that does not consist of full components
1225 if ( cm_strnicmp_utf8(fnamep, fullname, (int)fnlen-1) == 0 &&
1226 (fnlen == strlen(fullname)-1 || fullname[fnlen] != '.'))
1228 /* do not add; substitute fullname for the search */
1230 fnamep = strdup(fullname);
1234 code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
1243 nnamep = cm_FsStringToNormStringAlloc(fnamep, -1, NULL);
1247 if (!found || code) { /* add mount point failed, so give up */
1248 if (flags & CM_FLAG_CHECKPATH)
1249 code = CM_ERROR_NOSUCHPATH;
1251 code = CM_ERROR_NOSUCHFILE;
1254 tscp = NULL; /* to force call of cm_GetSCache */
1256 if (flags & CM_FLAG_CHECKPATH)
1257 code = CM_ERROR_NOSUCHPATH;
1259 code = CM_ERROR_NOSUCHFILE;
1265 if ( !tscp ) /* we did not find it in the dnlc */
1268 code = cm_GetSCache(&rock.fid, &dscp->fid, &tscp, userp, reqp);
1272 /* tscp is now held */
1274 lock_ObtainWrite(&tscp->rw);
1277 * Do not get status if we do not already have a callback.
1278 * The process of reading the mount point string will obtain status information
1279 * in a single RPC. No reason to add a second round trip.
1281 * If we do have a callback, use cm_SyncOp to get status in case the
1282 * current cm_user_t is not the same as the one that obtained the
1283 * mount point string contents.
1285 if (cm_HaveCallback(tscp)) {
1286 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1287 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1289 lock_ReleaseWrite(&tscp->rw);
1290 cm_ReleaseSCache(tscp);
1293 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1295 /* tscp is now locked */
1297 if (!(flags & CM_FLAG_NOMOUNTCHASE)
1298 && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
1299 /* mount points are funny: they have a volume name to mount
1302 code = cm_ReadMountPoint(tscp, userp, reqp);
1304 code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
1306 lock_ReleaseWrite(&tscp->rw);
1307 cm_ReleaseSCache(tscp);
1314 lock_ReleaseWrite(&tscp->rw);
1317 /* copy back pointer */
1320 /* insert scache in dnlc */
1321 if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
1322 /* lock the directory entry to prevent racing callback revokes */
1323 lock_ObtainRead(&dscp->rw);
1324 if ( dscp->cbServerp != NULL && dscp->cbExpires > 0 ) {
1325 /* TODO: reuse nnamep from above */
1328 nnamep = cm_ClientStringToNormStringAlloc(cnamep, -1, NULL);
1330 cm_dnlcEnter(dscp, nnamep, tscp);
1332 lock_ReleaseRead(&dscp->rw);
1349 int cm_ExpandSysName(cm_req_t * reqp, clientchar_t *inp, clientchar_t *outp, long outSizeCch, unsigned int index)
1354 int use_sysname64 = 0;
1356 if (cm_sysName64Count > 0 && reqp && (reqp->flags & CM_REQ_WOW64) && (reqp->flags & CM_REQ_SOURCE_REDIR))
1360 tp = cm_ClientStrRChr(inp, '@');
1362 return 0; /* no @sys */
1364 if (cm_ClientStrCmp(tp, _C("@sys")) != 0)
1365 return 0; /* no @sys */
1367 /* caller just wants to know if this is a valid @sys type of name */
1372 if (use_sysname64 && index >= cm_sysName64Count)
1376 if (index >= cm_sysNameCount)
1379 /* otherwise generate the properly expanded @sys name */
1380 prefixCount = (int)(tp - inp);
1382 cm_ClientStrCpyN(outp, outSizeCch, inp, prefixCount); /* copy out "a." from "a.@sys" */
1383 outp[prefixCount] = 0; /* null terminate the "a." */
1386 cm_ClientStrCat(outp, outSizeCch, cm_sysName64List[index]);
1389 cm_ClientStrCat(outp, outSizeCch, cm_sysNameList[index]);
1394 long cm_EvaluateVolumeReference(clientchar_t * namep, long flags, cm_user_t * userp,
1395 cm_req_t *reqp, cm_scache_t ** outScpp)
1397 afs_uint32 code = 0;
1398 fschar_t cellName[CELL_MAXNAMELEN];
1399 fschar_t volumeName[VL_MAXNAMELEN];
1403 fschar_t * fnamep = NULL;
1405 cm_cell_t * cellp = NULL;
1406 cm_volume_t * volp = NULL;
1410 int mountType = RWVOL;
1412 osi_Log1(afsd_logp, "cm_EvaluateVolumeReference for string [%S]",
1413 osi_LogSaveClientString(afsd_logp, namep));
1415 if (cm_ClientStrCmpNI(namep, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH) != 0) {
1416 goto _exit_invalid_path;
1419 /* namep is assumed to look like the following:
1421 @vol:<cellname>%<volume>\0
1423 @vol:<cellname>#<volume>\0
1427 fnamep = cm_ClientStringToFsStringAlloc(namep, -1, NULL);
1428 cp = fnamep + CM_PREFIX_VOL_CCH; /* cp points to cell name, hopefully */
1429 tp = cm_FsStrChr(cp, '%');
1431 tp = cm_FsStrChr(cp, '#');
1433 (len = tp - cp) == 0 ||
1434 len > CELL_MAXNAMELEN)
1435 goto _exit_invalid_path;
1436 cm_FsStrCpyN(cellName, lengthof(cellName), cp, len);
1441 cp = tp+1; /* cp now points to volume, supposedly */
1442 cm_FsStrCpy(volumeName, lengthof(volumeName), cp);
1444 /* OK, now we have the cell and the volume */
1445 osi_Log2(afsd_logp, " Found cell [%s] and volume [%s]",
1446 osi_LogSaveFsString(afsd_logp, cellName),
1447 osi_LogSaveFsString(afsd_logp, volumeName));
1449 cellp = cm_GetCell(cellName, CM_FLAG_CREATE);
1450 if (cellp == NULL) {
1451 goto _exit_invalid_path;
1454 len = cm_FsStrLen(volumeName);
1455 if (len >= 8 && cm_FsStrCmp(volumeName + len - 7, ".backup") == 0)
1457 else if (len >= 10 &&
1458 cm_FsStrCmp(volumeName + len - 9, ".readonly") == 0)
1463 if (cm_VolNameIsID(volumeName)) {
1464 code = cm_FindVolumeByID(cellp, atoi(volumeName), userp, reqp,
1465 CM_GETVOL_FLAG_CREATE, &volp);
1467 code = cm_FindVolumeByName(cellp, volumeName, userp, reqp,
1468 CM_GETVOL_FLAG_CREATE, &volp);
1474 if (volType == BACKVOL)
1475 volume = volp->vol[BACKVOL].ID;
1476 else if (volType == ROVOL ||
1477 (volType == RWVOL && mountType == ROVOL && volp->vol[ROVOL].ID != 0))
1478 volume = volp->vol[ROVOL].ID;
1480 volume = volp->vol[RWVOL].ID;
1482 cm_SetFid(&fid, cellp->cellID, volume, 1, 1);
1484 code = cm_GetSCache(&fid, NULL, outScpp, userp, reqp);
1497 if (flags & CM_FLAG_CHECKPATH)
1498 return CM_ERROR_NOSUCHPATH;
1500 return CM_ERROR_NOSUCHFILE;
1503 #ifdef DEBUG_REFCOUNT
1504 long cm_LookupDbg(cm_scache_t *dscp, clientchar_t *namep, long flags, cm_user_t *userp,
1505 cm_req_t *reqp, cm_scache_t **outScpp, char * file, long line)
1507 long cm_Lookup(cm_scache_t *dscp, clientchar_t *namep, long flags, cm_user_t *userp,
1508 cm_req_t *reqp, cm_scache_t **outScpp)
1512 clientchar_t tname[AFSPATHMAX];
1513 int sysNameIndex = 0;
1514 cm_scache_t *scp = NULL;
1516 #ifdef DEBUG_REFCOUNT
1517 afsi_log("%s:%d cm_Lookup dscp 0x%p ref %d", file, line, dscp, dscp->refCount, file, line);
1518 osi_Log2(afsd_logp, "cm_Lookup dscp 0x%p ref %d", dscp, dscp->refCount);
1521 if ( cm_ClientStrCmpI(namep,_C(SMB_IOCTL_FILENAME_NOSLASH)) == 0 ) {
1522 if (flags & CM_FLAG_CHECKPATH)
1523 return CM_ERROR_NOSUCHPATH;
1525 return CM_ERROR_NOSUCHFILE;
1528 if (dscp == cm_data.rootSCachep &&
1529 cm_ClientStrCmpNI(namep, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH) == 0) {
1530 return cm_EvaluateVolumeReference(namep, flags, userp, reqp, outScpp);
1533 if (cm_ExpandSysName(reqp, namep, NULL, 0, 0) > 0) {
1534 for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
1535 code = cm_ExpandSysName(reqp, namep, tname, lengthof(tname), sysNameIndex);
1537 code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
1538 #ifdef DEBUG_REFCOUNT
1539 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);
1540 osi_Log3(afsd_logp, "cm_LookupInternal (1) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1548 cm_ReleaseSCache(scp);
1552 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1553 #ifdef DEBUG_REFCOUNT
1554 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);
1555 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1562 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1563 #ifdef DEBUG_REFCOUNT
1564 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);
1565 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1571 /* None of the possible sysName expansions could be found */
1572 if (flags & CM_FLAG_CHECKPATH)
1573 return CM_ERROR_NOSUCHPATH;
1575 return CM_ERROR_NOSUCHFILE;
1578 /*! \brief Unlink a file name
1580 Encapsulates a call to RXAFS_RemoveFile().
1582 \param[in] dscp cm_scache_t pointing at the directory containing the
1583 name to be unlinked.
1585 \param[in] fnamep Original name to be unlinked. This is the
1586 name that will be passed into the RXAFS_RemoveFile() call.
1587 This parameter is optional. If not provided, the value will
1590 \param[in] came Client name to be unlinked. This name will be used
1591 to update the local directory caches.
1593 \param[in] userp cm_user_t for the request.
1595 \param[in] reqp Request tracker.
1598 long cm_Unlink(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t * cnamep,
1599 cm_user_t *userp, cm_req_t *reqp)
1605 AFSFetchStatus newDirStatus;
1607 struct rx_connection * rxconnp;
1609 cm_scache_t *scp = NULL;
1610 int free_fnamep = FALSE;
1613 memset(&volSync, 0, sizeof(volSync));
1615 if (fnamep == NULL) {
1618 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ,
1619 CM_DIROP_FLAG_NONE, &dirop);
1621 code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
1624 cm_EndDirOp(&dirop);
1631 #ifdef AFS_FREELANCE_CLIENT
1632 if (cm_freelanceEnabled && dscp == cm_data.rootSCachep) {
1633 /* deleting a mount point from the root dir. */
1634 code = cm_FreelanceRemoveMount(fnamep);
1639 code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
1643 /* Check for RO volume */
1644 if (dscp->flags & CM_SCACHEFLAG_RO) {
1645 code = CM_ERROR_READONLY;
1649 /* make sure we don't screw up the dir status during the merge */
1650 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE,
1651 CM_DIROP_FLAG_NONE, &dirop);
1653 lock_ObtainWrite(&dscp->rw);
1654 sflags = CM_SCACHESYNC_STOREDATA;
1655 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1656 lock_ReleaseWrite(&dscp->rw);
1658 cm_EndDirOp(&dirop);
1663 InterlockedIncrement(&dscp->activeRPCs);
1665 afsFid.Volume = dscp->fid.volume;
1666 afsFid.Vnode = dscp->fid.vnode;
1667 afsFid.Unique = dscp->fid.unique;
1669 osi_Log1(afsd_logp, "CALL RemoveFile scp 0x%p", dscp);
1671 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
1675 rxconnp = cm_GetRxConn(connp);
1676 code = RXAFS_RemoveFile(rxconnp, &afsFid, fnamep,
1677 &newDirStatus, &volSync);
1678 rx_PutConnection(rxconnp);
1680 } while (cm_Analyze(connp, userp, reqp, &dscp->fid, NULL, 1, &volSync, NULL, NULL, code));
1681 code = cm_MapRPCError(code, reqp);
1684 osi_Log1(afsd_logp, "CALL RemoveFile FAILURE, code 0x%x", code);
1686 osi_Log0(afsd_logp, "CALL RemoveFile SUCCESS");
1689 lock_ObtainWrite(&dirop.scp->dirlock);
1690 dirop.lockType = CM_DIRLOCK_WRITE;
1692 lock_ObtainWrite(&dscp->rw);
1693 cm_dnlcRemove(dscp, cnamep);
1695 cm_MergeStatus(NULL, dscp, &newDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
1697 if (cm_CheckDirOpForSingleChange(&dirop) && cnamep) {
1698 lock_ReleaseWrite(&dscp->rw);
1699 cm_DirDeleteEntry(&dirop, fnamep);
1701 cm_BPlusDirDeleteEntry(&dirop, cnamep);
1703 lock_ObtainWrite(&dscp->rw);
1706 InterlockedDecrement(&scp->activeRPCs);
1707 if (code == CM_ERROR_NOSUCHFILE) {
1708 /* windows would not have allowed the request to delete the file
1709 * if it did not believe the file existed. therefore, we must
1710 * have an inconsistent view of the world.
1712 dscp->cbServerp = NULL;
1716 cm_SyncOpDone(dscp, NULL, sflags);
1717 lock_ReleaseWrite(&dscp->rw);
1719 cm_EndDirOp(&dirop);
1721 if (invalidate && RDR_Initialized &&
1722 scp->fileType != CM_SCACHETYPE_FILE && scp->fileType != CM_SCACHETYPE_DIRECTORY)
1723 RDR_InvalidateObject(dscp->fid.cell, dscp->fid.volume, dscp->fid.vnode,
1724 dscp->fid.unique, dscp->fid.hash,
1725 dscp->fileType, AFS_INVALIDATE_DATA_VERSION);
1728 cm_ReleaseSCache(scp);
1730 lock_ObtainWrite(&scp->rw);
1731 if (--scp->linkCount == 0) {
1732 scp->flags |= CM_SCACHEFLAG_DELETED;
1733 lock_ObtainWrite(&cm_scacheLock);
1734 cm_AdjustScacheLRU(scp);
1735 cm_RemoveSCacheFromHashTable(scp);
1736 lock_ReleaseWrite(&cm_scacheLock);
1738 cm_DiscardSCache(scp);
1739 lock_ReleaseWrite(&scp->rw);
1740 if (RDR_Initialized && !(reqp->flags & CM_REQ_SOURCE_REDIR) &&
1741 !RDR_InvalidateObject(scp->fid.cell, scp->fid.volume, scp->fid.vnode,
1742 scp->fid.unique, scp->fid.hash,
1743 scp->fileType, AFS_INVALIDATE_DELETED))
1744 buf_ClearRDRFlag(scp, "unlink");
1755 /* called with a write locked vnode, and fills in the link info.
1756 * returns this the vnode still write locked.
1758 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1762 lock_AssertWrite(&linkScp->rw);
1763 if (!linkScp->mountPointStringp[0] ||
1764 linkScp->mpDataVersion != linkScp->dataVersion) {
1766 #ifdef AFS_FREELANCE_CLIENT
1767 /* File servers do not have data for freelance entries */
1768 if (cm_freelanceEnabled &&
1769 linkScp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
1770 linkScp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
1772 code = cm_FreelanceFetchMountPointString(linkScp);
1774 #endif /* AFS_FREELANCE_CLIENT */
1776 char temp[MOUNTPOINTLEN];
1779 /* read the link data from the file server */
1780 offset.LowPart = offset.HighPart = 0;
1781 code = cm_GetData(linkScp, &offset, temp, MOUNTPOINTLEN, userp, reqp);
1786 * linkScp->length is the actual length of the symlink target string.
1787 * It is current because cm_GetData merged the most up to date
1788 * status info into scp and has not dropped the rwlock since.
1790 if (linkScp->length.LowPart > MOUNTPOINTLEN - 1)
1791 return CM_ERROR_TOOBIG;
1792 if (linkScp->length.LowPart == 0)
1793 return CM_ERROR_INVAL;
1795 /* make sure we are NUL terminated */
1796 temp[linkScp->length.LowPart] = 0;
1797 memcpy(linkScp->mountPointStringp, temp, linkScp->length.LowPart + 1);
1800 if ( !strnicmp(linkScp->mountPointStringp, "msdfs:", strlen("msdfs:")) )
1801 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1803 } /* don't have symlink contents cached */
1808 /* called with a held vnode and a path suffix, with the held vnode being a
1809 * symbolic link. Our goal is to generate a new path to interpret, and return
1810 * this new path in newSpaceBufferp. If the new vnode is relative to a dir
1811 * other than the directory containing the symbolic link, then the new root is
1812 * returned in *newRootScpp, otherwise a null is returned there.
1814 long cm_AssembleLink(cm_scache_t *linkScp, fschar_t *pathSuffixp,
1815 cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1816 cm_user_t *userp, cm_req_t *reqp)
1823 *newRootScpp = NULL;
1824 *newSpaceBufferp = NULL;
1826 lock_ObtainWrite(&linkScp->rw);
1828 * Do not get status if we do not already have a callback.
1829 * The process of reading the symlink string will obtain status information
1830 * in a single RPC. No reason to add a second round trip.
1832 * If we do have a callback, use cm_SyncOp to get status in case the
1833 * current cm_user_t is not the same as the one that obtained the
1834 * symlink string contents.
1836 if (cm_HaveCallback(linkScp)) {
1837 code = cm_SyncOp(linkScp, NULL, userp, reqp, 0,
1838 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1840 lock_ReleaseWrite(&linkScp->rw);
1841 cm_ReleaseSCache(linkScp);
1844 cm_SyncOpDone(linkScp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1846 code = cm_HandleLink(linkScp, userp, reqp);
1850 /* if we may overflow the buffer, bail out; buffer is signficantly
1851 * bigger than max path length, so we don't really have to worry about
1852 * being a little conservative here.
1854 if (cm_FsStrLen(linkScp->mountPointStringp) + cm_FsStrLen(pathSuffixp) + 2
1855 >= CM_UTILS_SPACESIZE) {
1856 code = CM_ERROR_TOOBIG;
1860 tsp = cm_GetSpace();
1861 linkp = linkScp->mountPointStringp;
1862 if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1863 if (strlen(linkp) > cm_mountRootLen)
1864 StringCbCopyA((char *) tsp->data, sizeof(tsp->data), linkp+cm_mountRootLen+1);
1867 *newRootScpp = cm_RootSCachep(userp, reqp);
1868 cm_HoldSCache(*newRootScpp);
1869 } else if (linkp[0] == '\\' && linkp[1] == '\\') {
1870 if (!strnicmp(&linkp[2], cm_NetbiosName, (len = (long)strlen(cm_NetbiosName))))
1872 char * p = &linkp[len + 3];
1873 if (strnicmp(p, "all", 3) == 0)
1876 StringCbCopyA(tsp->data, sizeof(tsp->data), p);
1877 for (p = tsp->data; *p; p++) {
1881 *newRootScpp = cm_RootSCachep(userp, reqp);
1882 cm_HoldSCache(*newRootScpp);
1884 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1885 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1886 code = CM_ERROR_PATH_NOT_COVERED;
1888 } else if ( linkScp->fileType == CM_SCACHETYPE_DFSLINK ||
1889 !strnicmp(linkp, "msdfs:", (len = (long)strlen("msdfs:"))) ) {
1890 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1891 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1892 code = CM_ERROR_PATH_NOT_COVERED;
1893 } else if (*linkp == '\\' || *linkp == '/') {
1895 /* formerly, this was considered to be from the AFS root,
1896 * but this seems to create problems. instead, we will just
1897 * reject the link */
1898 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp+1);
1899 *newRootScpp = cm_RootSCachep(userp, reqp);
1900 cm_HoldSCache(*newRootScpp);
1902 /* we still copy the link data into the response so that
1903 * the user can see what the link points to
1905 linkScp->fileType = CM_SCACHETYPE_INVALID;
1906 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1907 code = CM_ERROR_NOSUCHPATH;
1910 /* a relative link */
1911 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1913 if (pathSuffixp[0] != 0) { /* if suffix string is non-null */
1914 StringCchCatA(tsp->data,lengthof(tsp->data), "\\");
1915 StringCchCatA(tsp->data,lengthof(tsp->data), pathSuffixp);
1919 clientchar_t * cpath = cm_FsStringToClientStringAlloc(tsp->data, -1, NULL);
1920 if (cpath != NULL) {
1921 cm_ClientStrCpy(tsp->wdata, lengthof(tsp->wdata), cpath);
1923 *newSpaceBufferp = tsp;
1925 code = CM_ERROR_NOSUCHPATH;
1932 if (code == CM_ERROR_PATH_NOT_COVERED && reqp->tidPathp && reqp->relPathp) {
1933 cm_VolStatus_Notify_DFS_Mapping(linkScp, reqp->tidPathp, reqp->relPathp);
1938 lock_ReleaseWrite(&linkScp->rw);
1941 #ifdef DEBUG_REFCOUNT
1942 long cm_NameIDbg(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
1943 cm_user_t *userp, clientchar_t *tidPathp, cm_req_t *reqp,
1944 cm_scache_t **outScpp,
1945 char * file, long line)
1947 long cm_NameI(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
1948 cm_user_t *userp, clientchar_t *tidPathp,
1949 cm_req_t *reqp, cm_scache_t **outScpp)
1953 clientchar_t *tp; /* ptr moving through input buffer */
1954 clientchar_t tc; /* temp char */
1955 int haveComponent; /* has new component started? */
1956 clientchar_t component[AFSPATHMAX]; /* this is the new component */
1957 clientchar_t *cp; /* component name being assembled */
1958 cm_scache_t *tscp; /* current location in the hierarchy */
1959 cm_scache_t *nscp; /* next dude down */
1960 cm_scache_t *dirScp; /* last dir we searched */
1961 cm_scache_t *linkScp; /* new root for the symlink we just
1963 cm_space_t *psp; /* space for current path, if we've hit
1965 cm_space_t *tempsp; /* temp vbl */
1966 clientchar_t *restp; /* rest of the pathname to interpret */
1967 int symlinkCount; /* count of # of symlinks traversed */
1968 int extraFlag; /* avoid chasing mt pts for dir cmd */
1969 int phase = 1; /* 1 = tidPathp, 2 = pathp */
1970 cm_fid_t fids[MAX_FID_COUNT]; /* array of fids processed in this path walk */
1971 int fid_count = 0; /* number of fids processed in this path walk */
1976 #ifdef DEBUG_REFCOUNT
1977 afsi_log("%s:%d cm_NameI rootscp 0x%p ref %d", file, line, rootSCachep, rootSCachep->refCount);
1978 osi_Log4(afsd_logp,"cm_NameI rootscp 0x%p path %S tidpath %S flags 0x%x",
1979 rootSCachep, pathp ? pathp : L"<NULL>", tidPathp ? tidPathp : L"<NULL>",
1994 cm_HoldSCache(tscp);
2002 /* map Unix slashes into DOS ones so we can interpret Unix
2008 if (!haveComponent) {
2011 } else if (tc == 0) {
2025 /* we have a component here */
2026 if (tc == 0 || tc == '\\') {
2027 /* end of the component; we're at the last
2028 * component if tc == 0. However, if the last
2029 * is a symlink, we have more to do.
2031 *cp++ = 0; /* add null termination */
2033 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
2034 extraFlag = CM_FLAG_NOMOUNTCHASE;
2035 code = cm_Lookup(tscp, component,
2037 userp, reqp, &nscp);
2040 if (!cm_ClientStrCmp(component,_C("..")) ||
2041 !cm_ClientStrCmp(component,_C("."))) {
2043 * roll back the fid list until we find the
2044 * fid that matches where we are now. Its not
2045 * necessarily one or two fids because they
2046 * might have been symlinks or mount points or
2047 * both that were crossed.
2049 for ( i=fid_count-1; i>=0; i--) {
2050 if (!cm_FidCmp(&nscp->fid, &fids[i]))
2055 /* add the new fid to the list */
2056 if (fid_count == MAX_FID_COUNT) {
2057 code = CM_ERROR_TOO_MANY_SYMLINKS;
2058 cm_ReleaseSCache(nscp);
2062 fids[fid_count++] = nscp->fid;
2067 cm_ReleaseSCache(tscp);
2069 cm_ReleaseSCache(dirScp);
2072 if ((code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH) &&
2073 tscp->fileType == CM_SCACHETYPE_SYMLINK) {
2074 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_NOSUCHPATH");
2075 return CM_ERROR_NOSUCHPATH;
2077 osi_Log1(afsd_logp,"cm_NameI code 0x%x", code);
2082 haveComponent = 0; /* component done */
2084 cm_ReleaseSCache(dirScp);
2085 dirScp = tscp; /* for some symlinks */
2086 tscp = nscp; /* already held */
2088 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
2091 cm_ReleaseSCache(dirScp);
2097 /* now, if tscp is a symlink, we should follow it and
2098 * assemble the path again.
2100 lock_ObtainWrite(&tscp->rw);
2101 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
2102 CM_SCACHESYNC_GETSTATUS
2103 | CM_SCACHESYNC_NEEDCALLBACK);
2105 lock_ReleaseWrite(&tscp->rw);
2106 cm_ReleaseSCache(tscp);
2109 cm_ReleaseSCache(dirScp);
2114 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2116 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
2117 /* this is a symlink; assemble a new buffer */
2118 lock_ReleaseWrite(&tscp->rw);
2119 if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
2120 cm_ReleaseSCache(tscp);
2123 cm_ReleaseSCache(dirScp);
2128 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_TOO_MANY_SYMLINKS");
2129 return CM_ERROR_TOO_MANY_SYMLINKS;
2139 /* TODO: make this better */
2140 frestp = cm_ClientStringToFsStringAlloc(restp, -1, NULL);
2141 code = cm_AssembleLink(tscp, frestp, &linkScp, &tempsp, userp, reqp);
2145 if (code == 0 && linkScp != NULL) {
2146 if (linkScp == cm_data.rootSCachep) {
2150 for ( i=0; i<fid_count; i++) {
2151 if ( !cm_FidCmp(&linkScp->fid, &fids[i]) ) {
2152 code = CM_ERROR_TOO_MANY_SYMLINKS;
2153 cm_ReleaseSCache(linkScp);
2159 if (i == fid_count && fid_count < MAX_FID_COUNT) {
2160 fids[fid_count++] = linkScp->fid;
2165 /* something went wrong */
2166 cm_ReleaseSCache(tscp);
2169 cm_ReleaseSCache(dirScp);
2175 /* otherwise, tempsp has the new path,
2176 * and linkScp is the new root from
2177 * which to interpret that path.
2178 * Continue with the namei processing,
2179 * also doing the bookkeeping for the
2180 * space allocation and tracking the
2181 * vnode reference counts.
2187 cm_ReleaseSCache(tscp);
2192 * now, if linkScp is null, that's
2193 * AssembleLink's way of telling us that
2194 * the sym link is relative to the dir
2195 * containing the link. We have a ref
2196 * to it in dirScp, and we hold it now
2197 * and reuse it as the new spot in the
2205 /* not a symlink, we may be done */
2206 lock_ReleaseWrite(&tscp->rw);
2214 cm_ReleaseSCache(dirScp);
2222 cm_ReleaseSCache(dirScp);
2225 } /* end of a component */
2228 } /* we have a component */
2229 } /* big while loop over all components */
2233 cm_ReleaseSCache(dirScp);
2239 cm_ReleaseSCache(tscp);
2241 #ifdef DEBUG_REFCOUNT
2242 afsi_log("%s:%d cm_NameI code 0x%x outScpp 0x%p ref %d", file, line, code, *outScpp, (*outScpp) ? (*outScpp)->refCount : 0);
2244 osi_Log2(afsd_logp,"cm_NameI code 0x%x outScpp 0x%p", code, *outScpp);
2248 /* called with a dir, and a vnode within the dir that happens to be a symlink.
2249 * We chase the link, and return a held pointer to the target, if it exists,
2250 * in *outScpp. If we succeed, we return 0, otherwise we return an error code
2251 * and do not hold or return a target vnode.
2253 * This is very similar to calling cm_NameI with the last component of a name,
2254 * which happens to be a symlink, except that we've already passed by the name.
2256 * This function is typically called by the directory listing functions, which
2257 * encounter symlinks but need to return the proper file length so programs
2258 * like "more" work properly when they make use of the attributes retrieved from
2261 * The input vnode should not be locked when this function is called.
2263 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
2264 cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
2268 cm_scache_t *newRootScp;
2272 osi_Log1(afsd_logp, "Evaluating symlink scp 0x%p", linkScp);
2274 code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
2278 /* now, if newRootScp is NULL, we're really being told that the symlink
2279 * is relative to the current directory (dscp).
2281 if (newRootScp == NULL) {
2283 cm_HoldSCache(dscp);
2286 code = cm_NameI(newRootScp, spacep->wdata,
2287 CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
2288 userp, NULL, reqp, outScpp);
2290 if (code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH)
2291 code = CM_ERROR_NOSUCHPATH;
2293 /* this stuff is allocated no matter what happened on the namei call,
2295 cm_FreeSpace(spacep);
2296 cm_ReleaseSCache(newRootScp);
2298 if (linkScp == *outScpp) {
2299 cm_ReleaseSCache(*outScpp);
2301 code = CM_ERROR_NOSUCHPATH;
2307 /* for a given entry, make sure that it isn't in the stat cache, and then
2308 * add it to the list of file IDs to be obtained.
2310 * Don't bother adding it if we already have a vnode. Note that the dir
2311 * is locked, so we have to be careful checking the vnode we're thinking of
2312 * processing, to avoid deadlocks.
2314 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
2325 /* Don't overflow bsp. */
2326 if (bsp->counter >= CM_BULKMAX)
2327 return CM_ERROR_STOPNOW;
2329 thyper.LowPart = cm_data.buf_blockSize;
2330 thyper.HighPart = 0;
2331 thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
2333 /* thyper is now the first byte past the end of the record we're
2334 * interested in, and bsp->bufOffset is the first byte of the record
2335 * we're interested in.
2336 * Skip data in the others.
2339 if (LargeIntegerLessThan(*offp, bsp->bufOffset))
2341 if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
2342 return CM_ERROR_STOPNOW;
2343 if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
2346 cm_SetFid(&tfid, scp->fid.cell, scp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
2347 tscp = cm_FindSCache(&tfid);
2349 if (lock_TryWrite(&tscp->rw)) {
2350 /* we have an entry that we can look at */
2351 if (!cm_EAccesFindEntry(bsp->userp, &tscp->fid) && cm_HaveCallback(tscp)) {
2352 /* we have a callback on it. Don't bother
2353 * fetching this stat entry, since we're happy
2354 * with the info we have.
2356 lock_ReleaseWrite(&tscp->rw);
2357 cm_ReleaseSCache(tscp);
2360 lock_ReleaseWrite(&tscp->rw);
2362 cm_ReleaseSCache(tscp);
2365 #ifdef AFS_FREELANCE_CLIENT
2366 // yj: if this is a mountpoint under root.afs then we don't want it
2367 // to be bulkstat-ed, instead, we call getSCache directly and under
2368 // getSCache, it is handled specially.
2369 if ( cm_freelanceEnabled &&
2370 tfid.cell==AFS_FAKE_ROOT_CELL_ID &&
2371 tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
2372 !(tfid.vnode==0x1 && tfid.unique==0x1) )
2374 osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
2375 return cm_GetSCache(&tfid, NULL, &tscp, NULL, NULL);
2377 #endif /* AFS_FREELANCE_CLIENT */
2380 bsp->fids[i].Volume = scp->fid.volume;
2381 bsp->fids[i].Vnode = tfid.vnode;
2382 bsp->fids[i].Unique = tfid.unique;
2387 cm_TryBulkStatRPC(cm_scache_t *dscp, cm_bulkStat_t *bbp, cm_user_t *userp, cm_req_t *reqp)
2390 AFSCBFids fidStruct;
2391 AFSBulkStats statStruct;
2393 AFSCBs callbackStruct;
2396 cm_callbackRequest_t cbReq;
2403 struct rx_connection * rxconnp;
2404 int inlinebulk; /* Did we use InlineBulkStatus RPC or not? */
2406 memset(&volSync, 0, sizeof(volSync));
2408 /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
2409 * make the calls to create the entries. Handle AFSCBMAX files at a
2412 for (filex = 0; filex < bbp->counter; filex += filesThisCall) {
2413 filesThisCall = bbp->counter - filex;
2414 if (filesThisCall > AFSCBMAX)
2415 filesThisCall = AFSCBMAX;
2417 fidStruct.AFSCBFids_len = filesThisCall;
2418 fidStruct.AFSCBFids_val = &bbp->fids[filex];
2419 statStruct.AFSBulkStats_len = filesThisCall;
2420 statStruct.AFSBulkStats_val = &bbp->stats[filex];
2421 callbackStruct.AFSCBs_len = filesThisCall;
2422 callbackStruct.AFSCBs_val = &bbp->callbacks[filex];
2423 cm_StartCallbackGrantingCall(NULL, &cbReq);
2424 osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
2427 * Whenever cm_Analyze is called for a RXAFS_ RPC there must
2428 * be a FID provided. However, the error code from RXAFS_BulkStatus
2429 * or RXAFS_InlinkBulkStatus does not apply to any FID. Therefore,
2430 * we generate an invalid FID to match with the RPC error.
2432 cm_SetFid(&tfid, dscp->fid.cell, dscp->fid.volume, 0, 0);
2437 code = cm_ConnFromFID(&tfid, userp, reqp, &connp);
2441 rxconnp = cm_GetRxConn(connp);
2442 if (!(connp->serverp->flags & CM_SERVERFLAG_NOINLINEBULK)) {
2443 code = RXAFS_InlineBulkStatus(rxconnp, &fidStruct,
2444 &statStruct, &callbackStruct, &volSync);
2445 if (code == RXGEN_OPCODE) {
2446 cm_SetServerNoInlineBulk(connp->serverp, 0);
2452 code = RXAFS_BulkStatus(rxconnp, &fidStruct,
2453 &statStruct, &callbackStruct, &volSync);
2455 rx_PutConnection(rxconnp);
2458 * If InlineBulk RPC was called and it succeeded,
2459 * then pull out the return code from the status info
2460 * and use it for cm_Analyze so that we can failover to other
2461 * .readonly volume instances. But only do it for errors that
2462 * are volume global.
2464 if (inlinebulk && code == 0 && (&bbp->stats[0])->errorCode) {
2465 osi_Log1(afsd_logp, "cm_TryBulkStat inline-bulk stat error: %d",
2466 (&bbp->stats[0])->errorCode);
2467 switch ((&bbp->stats[0])->errorCode) {
2476 code = (&bbp->stats[0])->errorCode;
2479 /* Rx and Rxkad errors are volume global */
2480 if ( (&bbp->stats[0])->errorCode >= -64 && (&bbp->stats[0])->errorCode < 0 ||
2481 (&bbp->stats[0])->errorCode >= ERROR_TABLE_BASE_RXK && (&bbp->stats[0])->errorCode < ERROR_TABLE_BASE_RXK + 256)
2482 code = (&bbp->stats[0])->errorCode;
2485 } while (cm_Analyze(connp, userp, reqp, &tfid, NULL, 0, &volSync, NULL, &cbReq, code));
2486 code = cm_MapRPCError(code, reqp);
2489 * might as well quit on an error, since we're not going to do
2490 * much better on the next immediate call, either.
2493 osi_Log2(afsd_logp, "CALL %sBulkStatus FAILURE code 0x%x",
2494 inlinebulk ? "Inline" : "", code);
2495 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2500 * The bulk RPC has succeeded or at least not failed with a
2501 * volume global error result. For items that have inlineBulk
2502 * errors we must call cm_Analyze in order to perform required
2503 * logging of errors.
2505 * If the RPC was not inline bulk or the entry either has no error
2506 * the status must be merged.
2508 osi_Log1(afsd_logp, "CALL %sBulkStatus SUCCESS", inlinebulk ? "Inline" : "");
2510 for (i = 0; i<filesThisCall; i++) {
2512 cm_SetFid(&tfid, dscp->fid.cell, bbp->fids[j].Volume, bbp->fids[j].Vnode, bbp->fids[j].Unique);
2514 if (inlinebulk && (&bbp->stats[j])->errorCode) {
2515 cm_req_t treq = *reqp;
2516 cm_Analyze(NULL, userp, &treq, &tfid, NULL, 0, &volSync, NULL, &cbReq, (&bbp->stats[j])->errorCode);
2517 switch ((&bbp->stats[j])->errorCode) {
2522 cm_EAccesAddEntry(userp, &tfid, &dscp->fid);
2525 code = cm_GetSCache(&tfid, &dscp->fid, &scp, userp, reqp);
2530 * otherwise, if this entry has no callback info,
2531 * merge in this. If there is existing callback info
2532 * we skip the merge because the existing data must be
2533 * current (we have a callback) and the response from
2534 * a non-inline bulk rpc might actually be wrong.
2536 * now, we have to be extra paranoid on merging in this
2537 * information, since we didn't use cm_SyncOp before
2538 * starting the fetch to make sure that no bad races
2539 * were occurring. Specifically, we need to make sure
2540 * we don't obliterate any newer information in the
2541 * vnode than have here.
2543 * Right now, be pretty conservative: if there's a
2544 * callback or a pending call, skip it.
2545 * However, if the prior attempt to obtain status
2546 * was refused access or the volume is .readonly,
2547 * take the data in any case since we have nothing
2548 * better for the in flight directory enumeration that
2549 * resulted in this function being called.
2551 lock_ObtainRead(&scp->rw);
2552 if ((scp->cbServerp == NULL &&
2553 !(scp->flags & (CM_SCACHEFLAG_FETCHING | CM_SCACHEFLAG_STORING | CM_SCACHEFLAG_SIZESTORING))) ||
2554 (scp->flags & CM_SCACHEFLAG_PURERO) ||
2555 cm_EAccesFindEntry(userp, &scp->fid))
2557 lock_ConvertRToW(&scp->rw);
2558 lostRace = cm_EndCallbackGrantingCall(scp, &cbReq,
2561 CM_CALLBACK_MAINTAINCOUNT|CM_CALLBACK_BULKSTAT);
2562 InterlockedIncrement(&scp->activeRPCs);
2564 cm_MergeStatus(dscp, scp, &bbp->stats[j], &volSync, userp, reqp, CM_MERGEFLAG_BULKSTAT);
2565 lock_ReleaseWrite(&scp->rw);
2567 lock_ReleaseRead(&scp->rw);
2569 cm_ReleaseSCache(scp);
2571 } /* all files in the response */
2572 /* now tell it to drop the count,
2573 * after doing the vnode processing above */
2574 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2575 } /* while there are still more files to process */
2580 /* called with a write locked scp and a pointer to a buffer. Make bulk stat
2581 * calls on all undeleted files in the page of the directory specified.
2584 cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
2590 osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%p", dscp);
2592 /* should be on a buffer boundary */
2593 osi_assertx((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0, "invalid offset");
2595 bbp = malloc(sizeof(cm_bulkStat_t));
2596 memset(bbp, 0, sizeof(cm_bulkStat_t));
2598 bbp->bufOffset = *offsetp;
2600 lock_ReleaseWrite(&dscp->rw);
2601 /* first, assemble the file IDs we need to stat */
2602 code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) bbp, offsetp, userp, reqp, NULL);
2604 /* if we failed, bail out early */
2605 if (code && code != CM_ERROR_STOPNOW) {
2607 lock_ObtainWrite(&dscp->rw);
2611 code = cm_TryBulkStatRPC(dscp, bbp, userp, reqp);
2612 osi_Log1(afsd_logp, "END cm_TryBulkStat code 0x%x", code);
2614 lock_ObtainWrite(&dscp->rw);
2619 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
2623 /* initialize store back mask as inexpensive local variable */
2625 memset(statusp, 0, sizeof(AFSStoreStatus));
2627 /* copy out queued info from scache first, if scp passed in */
2629 if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
2630 statusp->ClientModTime = scp->clientModTime;
2631 mask |= AFS_SETMODTIME;
2632 scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
2637 /* now add in our locally generated request */
2638 if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
2639 statusp->ClientModTime = attrp->clientModTime;
2640 mask |= AFS_SETMODTIME;
2642 if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
2643 statusp->UnixModeBits = attrp->unixModeBits;
2644 mask |= AFS_SETMODE;
2646 if (attrp->mask & CM_ATTRMASK_OWNER) {
2647 statusp->Owner = attrp->owner;
2648 mask |= AFS_SETOWNER;
2650 if (attrp->mask & CM_ATTRMASK_GROUP) {
2651 statusp->Group = attrp->group;
2652 mask |= AFS_SETGROUP;
2655 statusp->Mask = mask;
2659 cm_IsSpaceAvailable(cm_fid_t * fidp, osi_hyper_t *sizep, cm_user_t *userp, cm_req_t *reqp)
2664 struct rx_connection * rxconnp;
2665 AFSFetchVolumeStatus volStat;
2666 cm_volume_t *volp = NULL;
2671 char volName[32]="(unknown)";
2672 char offLineMsg[256]="server temporarily inaccessible";
2673 char motd[256]="server temporarily inaccessible";
2674 osi_hyper_t freespace;
2678 if (fidp->cell==AFS_FAKE_ROOT_CELL_ID &&
2679 fidp->volume==AFS_FAKE_ROOT_VOL_ID)
2684 volp = cm_GetVolumeByFID(fidp);
2689 volType = cm_VolumeType(volp, fidp->volume);
2690 if (volType == ROVOL || volType == BACKVOL) {
2695 cm_SetFid(&vfid, fidp->cell, fidp->volume, 1, 1);
2696 code = cm_GetSCache(&vfid, NULL, &vscp, userp, reqp);
2698 lock_ObtainWrite(&vscp->rw);
2699 code = cm_SyncOp(vscp, NULL, userp, reqp, PRSFS_READ,
2700 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2701 lock_ReleaseWrite(&vscp->rw);
2704 OfflineMsg = offLineMsg;
2708 code = cm_ConnFromFID(&vfid, userp, reqp, &connp);
2711 rxconnp = cm_GetRxConn(connp);
2712 code = RXAFS_GetVolumeStatus(rxconnp, fidp->volume,
2713 &volStat, &Name, &OfflineMsg, &MOTD);
2714 rx_PutConnection(rxconnp);
2716 } while (cm_Analyze(connp, userp, reqp, &vfid, NULL, 0, NULL, NULL, NULL, code));
2717 code = cm_MapRPCError(code, reqp);
2720 lock_ObtainWrite(&vscp->rw);
2721 cm_SyncOpDone(vscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2722 lock_ReleaseWrite(&vscp->rw);
2723 cm_ReleaseSCache(vscp);
2727 if (volStat.MaxQuota) {
2728 freespace.QuadPart = 1024 * (afs_int64)min(volStat.MaxQuota - volStat.BlocksInUse, volStat.PartBlocksAvail);
2730 freespace.QuadPart = 1024 * (afs_int64)volStat.PartBlocksAvail;
2732 spaceAvail = LargeIntegerGreaterThanOrEqualTo(freespace, *sizep);
2734 /* the rpc failed, assume there is space and we can fail it later. */
2743 /* set the file size, and make sure that all relevant buffers have been
2744 * truncated. Ensure that any partially truncated buffers have been zeroed
2745 * to the end of the buffer.
2747 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
2753 /* start by locking out buffer creation */
2754 lock_ObtainWrite(&scp->bufCreateLock);
2756 /* verify that this is a file, not a dir or a symlink */
2757 lock_ObtainWrite(&scp->rw);
2758 code = cm_SyncOp(scp, NULL, userp, reqp, 0,
2759 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2762 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2764 if (scp->fileType != CM_SCACHETYPE_FILE) {
2765 code = CM_ERROR_ISDIR;
2770 if (LargeIntegerLessThan(*sizep, scp->length))
2775 lock_ReleaseWrite(&scp->rw);
2777 /* can't hold scp->rw lock here, since we may wait for a storeback to
2778 * finish if the buffer package is cleaning a buffer by storing it to
2782 buf_Truncate(scp, userp, reqp, sizep);
2784 /* now ensure that file length is short enough, and update truncPos */
2785 lock_ObtainWrite(&scp->rw);
2787 /* make sure we have a callback (so we have the right value for the
2788 * length), and wait for it to be safe to do a truncate.
2790 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
2791 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2792 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2794 /* If we only have 'i' bits, then we should still be able to set
2795 the size of a file we created. */
2796 if (code == CM_ERROR_NOACCESS && scp->creator == userp) {
2797 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
2798 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2799 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2805 if (LargeIntegerLessThan(*sizep, scp->length)) {
2806 /* a real truncation. If truncPos is not set yet, or is bigger
2807 * than where we're truncating the file, set truncPos to this
2812 if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
2813 || LargeIntegerLessThan(*sizep, scp->length)) {
2815 scp->truncPos = *sizep;
2816 scp->mask |= CM_SCACHEMASK_TRUNCPOS;
2818 /* in either case, the new file size has been changed */
2819 scp->length = *sizep;
2820 scp->mask |= CM_SCACHEMASK_LENGTH;
2822 else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
2823 /* really extending the file */
2824 /* Check to see if we have sufficient quota */
2825 if (cm_IsSpaceAvailable(&scp->fid, sizep, userp, reqp)) {
2826 scp->length = *sizep;
2827 scp->mask |= CM_SCACHEMASK_LENGTH;
2829 code = CM_ERROR_SPACE;
2834 /* done successfully */
2838 cm_SyncOpDone(scp, NULL,
2839 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2840 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2843 lock_ReleaseWrite(&scp->rw);
2844 lock_ReleaseWrite(&scp->bufCreateLock);
2849 /* set the file size or other attributes (but not both at once) */
2850 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
2854 AFSFetchStatus afsOutStatus;
2858 AFSStoreStatus afsInStatus;
2859 struct rx_connection * rxconnp;
2861 memset(&volSync, 0, sizeof(volSync));
2863 /* handle file length setting */
2864 if (attrp->mask & CM_ATTRMASK_LENGTH)
2865 return cm_SetLength(scp, &attrp->length, userp, reqp);
2867 lock_ObtainWrite(&scp->rw);
2868 /* Check for RO volume */
2869 if (scp->flags & CM_SCACHEFLAG_RO) {
2870 code = CM_ERROR_READONLY;
2871 lock_ReleaseWrite(&scp->rw);
2875 /* otherwise, we have to make an RPC to get the status */
2876 code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
2878 lock_ReleaseWrite(&scp->rw);
2881 lock_ConvertWToR(&scp->rw);
2883 /* make the attr structure */
2884 cm_StatusFromAttr(&afsInStatus, scp, attrp);
2886 tfid.Volume = scp->fid.volume;
2887 tfid.Vnode = scp->fid.vnode;
2888 tfid.Unique = scp->fid.unique;
2889 lock_ReleaseRead(&scp->rw);
2891 /* now make the RPC */
2892 InterlockedIncrement(&scp->activeRPCs);
2894 osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%p", scp);
2896 code = cm_ConnFromFID(&scp->fid, userp, reqp, &connp);
2900 rxconnp = cm_GetRxConn(connp);
2901 code = RXAFS_StoreStatus(rxconnp, &tfid,
2902 &afsInStatus, &afsOutStatus, &volSync);
2903 rx_PutConnection(rxconnp);
2905 } while (cm_Analyze(connp, userp, reqp,
2906 &scp->fid, NULL, 1, &volSync, NULL, NULL, code));
2907 code = cm_MapRPCError(code, reqp);
2910 osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
2912 osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
2914 lock_ObtainWrite(&scp->rw);
2916 cm_MergeStatus(NULL, scp, &afsOutStatus, &volSync, userp, reqp,
2917 CM_MERGEFLAG_FORCE|CM_MERGEFLAG_STOREDATA);
2919 InterlockedDecrement(&scp->activeRPCs);
2920 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
2922 /* if we're changing the mode bits, discard the ACL cache,
2923 * since we changed the mode bits.
2925 if (afsInStatus.Mask & AFS_SETMODE)
2926 cm_FreeAllACLEnts(scp);
2927 lock_ReleaseWrite(&scp->rw);
2931 long cm_Create(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
2932 cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
2937 cm_callbackRequest_t cbReq;
2940 cm_scache_t *scp = NULL;
2943 AFSStoreStatus inStatus;
2944 AFSFetchStatus updatedDirStatus;
2945 AFSFetchStatus newFileStatus;
2946 AFSCallBack newFileCallback;
2948 struct rx_connection * rxconnp;
2950 fschar_t * fnamep = NULL;
2952 memset(&volSync, 0, sizeof(volSync));
2954 /* can't create names with @sys in them; must expand it manually first.
2955 * return "invalid request" if they try.
2957 if (cm_ExpandSysName(NULL, cnamep, NULL, 0, 0)) {
2958 return CM_ERROR_ATSYS;
2961 #ifdef AFS_FREELANCE_CLIENT
2962 /* Freelance root volume does not hold files */
2963 if (cm_freelanceEnabled &&
2964 dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
2965 dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
2967 return CM_ERROR_NOACCESS;
2969 #endif /* AFS_FREELANCE_CLIENT */
2971 /* Check for RO volume */
2972 if (dscp->flags & CM_SCACHEFLAG_RO)
2973 return CM_ERROR_READONLY;
2975 /* before starting the RPC, mark that we're changing the file data, so
2976 * that someone who does a chmod will know to wait until our call
2979 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
2981 lock_ObtainWrite(&dscp->rw);
2982 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2983 lock_ReleaseWrite(&dscp->rw);
2985 cm_StartCallbackGrantingCall(NULL, &cbReq);
2987 cm_EndDirOp(&dirop);
2994 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
2996 cm_StatusFromAttr(&inStatus, NULL, attrp);
2998 /* try the RPC now */
2999 InterlockedIncrement(&dscp->activeRPCs);
3000 osi_Log1(afsd_logp, "CALL CreateFile scp 0x%p", dscp);
3002 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3006 dirAFSFid.Volume = dscp->fid.volume;
3007 dirAFSFid.Vnode = dscp->fid.vnode;
3008 dirAFSFid.Unique = dscp->fid.unique;
3010 rxconnp = cm_GetRxConn(connp);
3011 code = RXAFS_CreateFile(connp->rxconnp, &dirAFSFid, fnamep,
3012 &inStatus, &newAFSFid, &newFileStatus,
3013 &updatedDirStatus, &newFileCallback,
3015 rx_PutConnection(rxconnp);
3017 } while (cm_Analyze(connp, userp, reqp,
3018 &dscp->fid, NULL, 1, &volSync, NULL, &cbReq, code));
3019 code = cm_MapRPCError(code, reqp);
3022 osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
3024 osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
3027 lock_ObtainWrite(&dirop.scp->dirlock);
3028 dirop.lockType = CM_DIRLOCK_WRITE;
3030 lock_ObtainWrite(&dscp->rw);
3032 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3033 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3034 if (cm_CheckDirOpForSingleChange(&dirop)) {
3035 lock_ReleaseWrite(&dscp->rw);
3036 cm_DirCreateEntry(&dirop, fnamep, &newFid);
3038 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
3040 lock_ObtainWrite(&dscp->rw);
3043 InterlockedDecrement(&dscp->activeRPCs);
3045 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3046 lock_ReleaseWrite(&dscp->rw);
3048 /* now try to create the file's entry, too, but be careful to
3049 * make sure that we don't merge in old info. Since we weren't locking
3050 * out any requests during the file's creation, we may have pretty old
3054 code = cm_GetSCache(&newFid, &dscp->fid, &scp, userp, reqp);
3056 lock_ObtainWrite(&scp->rw);
3057 scp->creator = userp; /* remember who created it */
3058 if (!cm_HaveCallback(scp)) {
3059 lostRace = cm_EndCallbackGrantingCall(scp, &cbReq,
3060 &newFileCallback, &volSync, 0);
3061 InterlockedIncrement(&scp->activeRPCs);
3063 cm_MergeStatus(dscp, scp, &newFileStatus, &volSync,
3067 lock_ReleaseWrite(&scp->rw);
3071 /* make sure we end things properly */
3073 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
3075 cm_EndDirOp(&dirop);
3084 cm_ReleaseSCache(scp);
3090 * locked if TRUE means write-locked
3091 * else the cm_scache_t rw must not be held
3093 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp, afs_uint32 locked)
3098 lock_ReleaseWrite(&scp->rw);
3100 osi_Log2(afsd_logp, "cm_FSync scp 0x%p userp 0x%p", scp, userp);
3102 code = buf_CleanVnode(scp, userp, reqp);
3104 lock_ObtainWrite(&scp->rw);
3106 if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
3107 | CM_SCACHEMASK_CLIENTMODTIME
3108 | CM_SCACHEMASK_LENGTH))
3109 code = cm_StoreMini(scp, userp, reqp);
3111 if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) {
3112 code = (scp->flags & CM_SCACHEFLAG_OVERQUOTA) ? CM_ERROR_QUOTA : CM_ERROR_SPACE;
3113 scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE);
3117 lock_ReleaseWrite(&scp->rw);
3118 } else if (locked) {
3119 lock_ObtainWrite(&scp->rw);
3124 long cm_MakeDir(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
3125 cm_user_t *userp, cm_req_t *reqp, cm_scache_t **scpp)
3130 cm_callbackRequest_t cbReq;
3133 cm_scache_t *scp = NULL;
3136 AFSStoreStatus inStatus;
3137 AFSFetchStatus updatedDirStatus;
3138 AFSFetchStatus newDirStatus;
3139 AFSCallBack newDirCallback;
3141 struct rx_connection * rxconnp;
3143 fschar_t * fnamep = NULL;
3145 memset(&volSync, 0, sizeof(volSync));
3147 /* can't create names with @sys in them; must expand it manually first.
3148 * return "invalid request" if they try.
3150 if (cm_ExpandSysName(NULL, cnamep, NULL, 0, 0)) {
3151 return CM_ERROR_ATSYS;
3154 #ifdef AFS_FREELANCE_CLIENT
3155 /* Freelance root volume does not hold subdirectories */
3156 if (cm_freelanceEnabled &&
3157 dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
3158 dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
3160 return CM_ERROR_NOACCESS;
3162 #endif /* AFS_FREELANCE_CLIENT */
3164 /* Check for RO volume */
3165 if (dscp->flags & CM_SCACHEFLAG_RO)
3166 return CM_ERROR_READONLY;
3168 /* before starting the RPC, mark that we're changing the directory
3169 * data, so that someone who does a chmod on the dir will wait until
3170 * our call completes.
3172 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3174 lock_ObtainWrite(&dscp->rw);
3175 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3176 lock_ReleaseWrite(&dscp->rw);
3178 cm_StartCallbackGrantingCall(NULL, &cbReq);
3180 cm_EndDirOp(&dirop);
3187 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3188 cm_StatusFromAttr(&inStatus, NULL, attrp);
3190 /* try the RPC now */
3191 InterlockedIncrement(&dscp->activeRPCs);
3192 osi_Log1(afsd_logp, "CALL MakeDir scp 0x%p", dscp);
3194 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3198 dirAFSFid.Volume = dscp->fid.volume;
3199 dirAFSFid.Vnode = dscp->fid.vnode;
3200 dirAFSFid.Unique = dscp->fid.unique;
3202 rxconnp = cm_GetRxConn(connp);
3203 code = RXAFS_MakeDir(connp->rxconnp, &dirAFSFid, fnamep,
3204 &inStatus, &newAFSFid, &newDirStatus,
3205 &updatedDirStatus, &newDirCallback,
3207 rx_PutConnection(rxconnp);
3209 } while (cm_Analyze(connp, userp, reqp,
3210 &dscp->fid, NULL, 1, &volSync, NULL, &cbReq, code));
3211 code = cm_MapRPCError(code, reqp);
3214 osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
3216 osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
3219 lock_ObtainWrite(&dirop.scp->dirlock);
3220 dirop.lockType = CM_DIRLOCK_WRITE;
3222 lock_ObtainWrite(&dscp->rw);
3224 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3225 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3226 if (cm_CheckDirOpForSingleChange(&dirop)) {
3227 lock_ReleaseWrite(&dscp->rw);
3228 cm_DirCreateEntry(&dirop, fnamep, &newFid);
3230 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
3232 lock_ObtainWrite(&dscp->rw);
3235 InterlockedDecrement(&dscp->activeRPCs);
3237 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3238 lock_ReleaseWrite(&dscp->rw);
3240 /* now try to create the new dir's entry, too, but be careful to
3241 * make sure that we don't merge in old info. Since we weren't locking
3242 * out any requests during the file's creation, we may have pretty old
3246 code = cm_GetSCache(&newFid, &dscp->fid, &scp, userp, reqp);
3248 lock_ObtainWrite(&scp->rw);
3249 if (!cm_HaveCallback(scp)) {
3250 lostRace = cm_EndCallbackGrantingCall(scp, &cbReq,
3251 &newDirCallback, &volSync, 0);
3252 InterlockedIncrement(&scp->activeRPCs);
3254 cm_MergeStatus(dscp, scp, &newDirStatus, &volSync,
3258 lock_ReleaseWrite(&scp->rw);
3262 /* make sure we end things properly */
3264 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
3266 cm_EndDirOp(&dirop);
3274 cm_ReleaseSCache(scp);
3277 /* and return error code */
3281 long cm_Link(cm_scache_t *dscp, clientchar_t *cnamep, cm_scache_t *sscp, long flags,
3282 cm_user_t *userp, cm_req_t *reqp)
3287 AFSFid existingAFSFid;
3288 AFSFetchStatus updatedDirStatus;
3289 AFSFetchStatus newLinkStatus;
3291 struct rx_connection * rxconnp;
3293 fschar_t * fnamep = NULL;
3296 memset(&volSync, 0, sizeof(volSync));
3298 if (dscp->fid.cell != sscp->fid.cell ||
3299 dscp->fid.volume != sscp->fid.volume) {
3300 return CM_ERROR_CROSSDEVLINK;
3303 /* Check for RO volume */
3304 if (dscp->flags & CM_SCACHEFLAG_RO)
3305 return CM_ERROR_READONLY;
3307 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3309 lock_ObtainWrite(&dscp->rw);
3310 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3311 lock_ReleaseWrite(&dscp->rw);
3313 cm_EndDirOp(&dirop);
3318 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3320 /* try the RPC now */
3321 InterlockedIncrement(&dscp->activeRPCs);
3322 osi_Log1(afsd_logp, "CALL Link scp 0x%p", dscp);
3324 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3327 dirAFSFid.Volume = dscp->fid.volume;
3328 dirAFSFid.Vnode = dscp->fid.vnode;
3329 dirAFSFid.Unique = dscp->fid.unique;
3331 existingAFSFid.Volume = sscp->fid.volume;
3332 existingAFSFid.Vnode = sscp->fid.vnode;
3333 existingAFSFid.Unique = sscp->fid.unique;
3335 rxconnp = cm_GetRxConn(connp);
3336 code = RXAFS_Link(rxconnp, &dirAFSFid, fnamep, &existingAFSFid,
3337 &newLinkStatus, &updatedDirStatus, &volSync);
3338 rx_PutConnection(rxconnp);
3339 osi_Log1(afsd_logp," RXAFS_Link returns 0x%x", code);
3341 } while (cm_Analyze(connp, userp, reqp, &dscp->fid, NULL, 1, &volSync, NULL, NULL, code));
3343 code = cm_MapRPCError(code, reqp);
3346 osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
3348 osi_Log0(afsd_logp, "CALL Link SUCCESS");
3351 lock_ObtainWrite(&dirop.scp->dirlock);
3352 dirop.lockType = CM_DIRLOCK_WRITE;
3354 lock_ObtainWrite(&dscp->rw);
3356 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3359 if (cm_CheckDirOpForSingleChange(&dirop)) {
3360 lock_ReleaseWrite(&dscp->rw);
3361 cm_DirCreateEntry(&dirop, fnamep, &sscp->fid);
3363 cm_BPlusDirCreateEntry(&dirop, cnamep, &sscp->fid);
3365 lock_ObtainWrite(&dscp->rw);
3368 InterlockedDecrement(&dscp->activeRPCs);
3370 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3371 lock_ReleaseWrite(&dscp->rw);
3373 cm_EndDirOp(&dirop);
3375 if (invalidate && RDR_Initialized)
3376 RDR_InvalidateObject(dscp->fid.cell, dscp->fid.volume, dscp->fid.vnode,
3377 dscp->fid.unique, dscp->fid.hash,
3378 dscp->fileType, AFS_INVALIDATE_DATA_VERSION);
3380 /* Update the linked object status */
3382 lock_ObtainWrite(&sscp->rw);
3383 InterlockedIncrement(&sscp->activeRPCs);
3384 cm_MergeStatus(NULL, sscp, &newLinkStatus, &volSync, userp, reqp, 0);
3385 lock_ReleaseWrite(&sscp->rw);
3393 long cm_SymLink(cm_scache_t *dscp, clientchar_t *cnamep, fschar_t *contentsp, long flags,
3394 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp, cm_scache_t **scpp)
3402 AFSStoreStatus inStatus;
3403 AFSFetchStatus updatedDirStatus;
3404 AFSFetchStatus newLinkStatus;
3406 struct rx_connection * rxconnp;
3408 fschar_t *fnamep = NULL;
3413 /* Check for RO volume */
3414 if (dscp->flags & CM_SCACHEFLAG_RO)
3415 return CM_ERROR_READONLY;
3417 memset(&volSync, 0, sizeof(volSync));
3419 /* before starting the RPC, mark that we're changing the directory data,
3420 * so that someone who does a chmod on the dir will wait until our
3423 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3425 lock_ObtainWrite(&dscp->rw);
3426 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3427 lock_ReleaseWrite(&dscp->rw);
3429 cm_EndDirOp(&dirop);
3434 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3436 cm_StatusFromAttr(&inStatus, NULL, attrp);
3438 /* try the RPC now */
3439 InterlockedIncrement(&dscp->activeRPCs);
3440 osi_Log1(afsd_logp, "CALL Symlink scp 0x%p", dscp);
3442 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3446 dirAFSFid.Volume = dscp->fid.volume;
3447 dirAFSFid.Vnode = dscp->fid.vnode;
3448 dirAFSFid.Unique = dscp->fid.unique;
3450 rxconnp = cm_GetRxConn(connp);
3451 code = RXAFS_Symlink(rxconnp, &dirAFSFid, fnamep, contentsp,
3452 &inStatus, &newAFSFid, &newLinkStatus,
3453 &updatedDirStatus, &volSync);
3454 rx_PutConnection(rxconnp);
3456 } while (cm_Analyze(connp, userp, reqp,
3457 &dscp->fid, NULL, 1, &volSync, NULL, NULL, code));
3458 code = cm_MapRPCError(code, reqp);
3461 osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
3463 osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
3466 lock_ObtainWrite(&dirop.scp->dirlock);
3467 dirop.lockType = CM_DIRLOCK_WRITE;
3469 lock_ObtainWrite(&dscp->rw);
3471 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3472 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3473 if (cm_CheckDirOpForSingleChange(&dirop)) {
3474 lock_ReleaseWrite(&dscp->rw);
3475 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3477 cm_DirCreateEntry(&dirop, fnamep, &newFid);
3479 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
3481 lock_ObtainWrite(&dscp->rw);
3484 InterlockedDecrement(&dscp->activeRPCs);
3486 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3487 lock_ReleaseWrite(&dscp->rw);
3489 cm_EndDirOp(&dirop);
3491 /* now try to create the new dir's entry, too, but be careful to
3492 * make sure that we don't merge in old info. Since we weren't locking
3493 * out any requests during the file's creation, we may have pretty old
3497 code = cm_GetSCache(&newFid, &dscp->fid, &scp, userp, reqp);
3499 lock_ObtainWrite(&scp->rw);
3500 if (!cm_HaveCallback(scp)) {
3501 InterlockedIncrement(&scp->activeRPCs);
3502 cm_MergeStatus(dscp, scp, &newLinkStatus, &volSync,
3505 lock_ReleaseWrite(&scp->rw);
3510 cm_ReleaseSCache(scp);
3517 /* and return error code */
3521 /*! \brief Remove a directory
3523 Encapsulates a call to RXAFS_RemoveDir().
3525 \param[in] dscp cm_scache_t for the directory containing the
3526 directory to be removed.
3528 \param[in] fnamep This will be the original name of the directory
3529 as known to the file server. It will be passed in to RXAFS_RemoveDir().
3530 This parameter is optional. If it is not provided the value
3533 \param[in] cnamep Normalized name used to update the local
3536 \param[in] userp cm_user_t for the request.
3538 \param[in] reqp Request tracker.
3540 long cm_RemoveDir(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t *cnamep, cm_user_t *userp, cm_req_t *reqp)
3546 AFSFetchStatus updatedDirStatus;
3548 struct rx_connection * rxconnp;
3550 cm_scache_t *scp = NULL;
3551 int free_fnamep = FALSE;
3553 memset(&volSync, 0, sizeof(volSync));
3555 if (fnamep == NULL) {
3558 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ,
3559 CM_DIROP_FLAG_NONE, &dirop);
3561 code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
3564 cm_EndDirOp(&dirop);
3571 code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
3575 /* Check for RO volume */
3576 if (dscp->flags & CM_SCACHEFLAG_RO) {
3577 code = CM_ERROR_READONLY;
3581 /* before starting the RPC, mark that we're changing the directory data,
3582 * so that someone who does a chmod on the dir will wait until our
3585 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3587 lock_ObtainWrite(&dscp->rw);
3588 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3589 lock_ReleaseWrite(&dscp->rw);
3591 cm_EndDirOp(&dirop);
3596 /* try the RPC now */
3597 InterlockedIncrement(&dscp->activeRPCs);
3598 osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%p", dscp);
3600 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3604 dirAFSFid.Volume = dscp->fid.volume;
3605 dirAFSFid.Vnode = dscp->fid.vnode;
3606 dirAFSFid.Unique = dscp->fid.unique;
3608 rxconnp = cm_GetRxConn(connp);
3609 code = RXAFS_RemoveDir(rxconnp, &dirAFSFid, fnamep,
3610 &updatedDirStatus, &volSync);
3611 rx_PutConnection(rxconnp);
3613 } while (cm_Analyze(connp, userp, reqp,
3614 &dscp->fid, NULL, 1, &volSync, NULL, NULL, code));
3615 code = cm_MapRPCErrorRmdir(code, reqp);
3618 osi_Log1(afsd_logp, "CALL RemoveDir FAILURE, code 0x%x", code);
3620 osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
3623 lock_ObtainWrite(&dirop.scp->dirlock);
3624 dirop.lockType = CM_DIRLOCK_WRITE;
3626 lock_ObtainWrite(&dscp->rw);
3628 cm_dnlcRemove(dscp, cnamep);
3629 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3630 if (cm_CheckDirOpForSingleChange(&dirop) && cnamep != NULL) {
3631 lock_ReleaseWrite(&dscp->rw);
3632 cm_DirDeleteEntry(&dirop, fnamep);
3634 cm_BPlusDirDeleteEntry(&dirop, cnamep);
3636 lock_ObtainWrite(&dscp->rw);
3639 InterlockedDecrement(&dscp->activeRPCs);
3641 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3642 lock_ReleaseWrite(&dscp->rw);
3644 cm_EndDirOp(&dirop);
3647 cm_ReleaseSCache(scp);
3649 lock_ObtainWrite(&scp->rw);
3650 scp->flags |= CM_SCACHEFLAG_DELETED;
3651 lock_ObtainWrite(&cm_scacheLock);
3652 cm_AdjustScacheLRU(scp);
3653 cm_RemoveSCacheFromHashTable(scp);
3654 lock_ReleaseWrite(&cm_scacheLock);
3655 lock_ReleaseWrite(&scp->rw);
3656 if (RDR_Initialized && !(reqp->flags & CM_REQ_SOURCE_REDIR) &&
3657 !RDR_InvalidateObject(scp->fid.cell, scp->fid.volume, scp->fid.vnode,
3658 scp->fid.unique, scp->fid.hash,
3659 scp->fileType, AFS_INVALIDATE_DELETED))
3660 buf_ClearRDRFlag(scp, "rmdir");
3668 /* and return error code */
3672 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
3674 /* grab mutex on contents */
3675 lock_ObtainWrite(&scp->rw);
3677 /* reset the prefetch info */
3678 scp->prefetch.base.LowPart = 0; /* base */
3679 scp->prefetch.base.HighPart = 0;
3680 scp->prefetch.end.LowPart = 0; /* and end */
3681 scp->prefetch.end.HighPart = 0;
3683 /* release mutex on contents */
3684 lock_ReleaseWrite(&scp->rw);
3690 /*! \brief Rename a file or directory
3692 Encapsulates a RXAFS_Rename() call.
3694 \param[in] oldDscp cm_scache_t for the directory containing the old
3697 \param[in] oldNamep The original old name known to the file server.
3698 This is the name that will be passed into the RXAFS_Rename().
3699 If it is not provided, it will be looked up.
3701 \param[in] normalizedOldNamep Normalized old name. This is used for
3702 updating local directory caches.
3704 \param[in] newDscp cm_scache_t for the directory containing the new
3707 \param[in] newNamep New name. Normalized.
3709 \param[in] userp cm_user_t for the request.
3711 \param[in,out] reqp Request tracker.
3714 long cm_Rename(cm_scache_t *oldDscp, fschar_t *oldNamep, clientchar_t *cOldNamep,
3715 cm_scache_t *newDscp, clientchar_t *cNewNamep, cm_user_t *userp,
3720 AFSFid oldDirAFSFid;
3721 AFSFid newDirAFSFid;
3722 AFSFetchStatus updatedOldDirStatus;
3723 AFSFetchStatus updatedNewDirStatus;
3726 int bTargetExists = 0;
3727 struct rx_connection * rxconnp;
3728 cm_dirOp_t oldDirOp;
3731 cm_dirOp_t newDirOp;
3732 fschar_t * newNamep = NULL;
3733 int free_oldNamep = FALSE;
3734 cm_scache_t *oldScp = NULL, *oldTargetScp = NULL;
3735 int rpc_skipped = 0;
3737 memset(&volSync, 0, sizeof(volSync));
3739 if (cOldNamep == NULL || cNewNamep == NULL ||
3740 cm_ClientStrLen(cOldNamep) == 0 ||
3741 cm_ClientStrLen(cNewNamep) == 0)
3742 return CM_ERROR_INVAL;
3744 /* check for identical names */
3745 if (oldDscp == newDscp &&
3746 cm_ClientStrCmp(cOldNamep, cNewNamep) == 0) {
3747 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_RENAME_IDENTICAL",
3749 return CM_ERROR_RENAME_IDENTICAL;
3752 /* Check for RO volume */
3753 if ((oldDscp->flags & CM_SCACHEFLAG_RO) || (newDscp->flags & CM_SCACHEFLAG_RO)) {
3754 return CM_ERROR_READONLY;
3757 if (oldNamep == NULL) {
3760 code = cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_READ,
3761 CM_DIROP_FLAG_NONE, &oldDirOp);
3763 code = cm_BPlusDirLookupOriginalName(&oldDirOp, cOldNamep, &oldNamep);
3765 free_oldNamep = TRUE;
3766 cm_EndDirOp(&oldDirOp);
3770 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S Original Name lookup failed",
3771 oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3776 /* before starting the RPC, mark that we're changing the directory data,
3777 * so that someone who does a chmod on the dir will wait until our call
3778 * completes. We do this in vnode order so that we don't deadlock,
3779 * which makes the code a little verbose.
3781 if (oldDscp == newDscp) {
3783 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE,
3784 CM_DIROP_FLAG_NONE, &oldDirOp);
3785 lock_ObtainWrite(&oldDscp->rw);
3786 cm_dnlcRemove(oldDscp, cOldNamep);
3787 cm_dnlcRemove(oldDscp, cNewNamep);
3788 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3789 CM_SCACHESYNC_STOREDATA);
3790 lock_ReleaseWrite(&oldDscp->rw);
3792 cm_EndDirOp(&oldDirOp);
3796 /* two distinct dir vnodes */
3798 if (oldDscp->fid.cell != newDscp->fid.cell ||
3799 oldDscp->fid.volume != newDscp->fid.volume) {
3800 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_CROSSDEVLINK",
3802 code = CM_ERROR_CROSSDEVLINK;
3806 /* shouldn't happen that we have distinct vnodes for two
3807 * different files, but could due to deliberate attack, or
3808 * stale info. Avoid deadlocks and quit now.
3810 if (oldDscp->fid.vnode == newDscp->fid.vnode) {
3811 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p vnode collision",
3813 code = CM_ERROR_CROSSDEVLINK;
3817 if (oldDscp->fid.vnode < newDscp->fid.vnode) {
3818 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE,
3819 CM_DIROP_FLAG_NONE, &oldDirOp);
3820 lock_ObtainWrite(&oldDscp->rw);
3821 cm_dnlcRemove(oldDscp, cOldNamep);
3822 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3823 CM_SCACHESYNC_STOREDATA);
3824 lock_ReleaseWrite(&oldDscp->rw);
3826 cm_EndDirOp(&oldDirOp);
3828 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE,
3829 CM_DIROP_FLAG_NONE, &newDirOp);
3830 lock_ObtainWrite(&newDscp->rw);
3831 cm_dnlcRemove(newDscp, cNewNamep);
3832 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3833 CM_SCACHESYNC_STOREDATA);
3834 lock_ReleaseWrite(&newDscp->rw);
3836 cm_EndDirOp(&newDirOp);
3838 /* cleanup first one */
3839 lock_ObtainWrite(&oldDscp->rw);
3840 cm_SyncOpDone(oldDscp, NULL,
3841 CM_SCACHESYNC_STOREDATA);
3842 lock_ReleaseWrite(&oldDscp->rw);
3843 cm_EndDirOp(&oldDirOp);
3848 /* lock the new vnode entry first */
3849 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE,
3850 CM_DIROP_FLAG_NONE, &newDirOp);
3851 lock_ObtainWrite(&newDscp->rw);
3852 cm_dnlcRemove(newDscp, cNewNamep);
3853 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3854 CM_SCACHESYNC_STOREDATA);
3855 lock_ReleaseWrite(&newDscp->rw);
3857 cm_EndDirOp(&newDirOp);
3859 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE,
3860 CM_DIROP_FLAG_NONE, &oldDirOp);
3861 lock_ObtainWrite(&oldDscp->rw);
3862 cm_dnlcRemove(oldDscp, cOldNamep);
3863 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3864 CM_SCACHESYNC_STOREDATA);
3865 lock_ReleaseWrite(&oldDscp->rw);
3867 cm_EndDirOp(&oldDirOp);
3869 /* cleanup first one */
3870 lock_ObtainWrite(&newDscp->rw);
3871 cm_SyncOpDone(newDscp, NULL,
3872 CM_SCACHESYNC_STOREDATA);
3873 lock_ReleaseWrite(&newDscp->rw);
3874 cm_EndDirOp(&newDirOp);
3878 } /* two distinct vnodes */
3884 * The source and destination directories are now locked and no other local
3885 * changes can occur.
3887 * Before we permit the operation, make sure that we do not already have
3888 * an object in the destination directory that has a case-insensitive match
3889 * for this name UNLESS the matching object is the object we are renaming.
3891 code = cm_Lookup(oldDscp, cOldNamep, 0, userp, reqp, &oldScp);
3893 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S old name lookup failed",
3894 oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3899 /* Case sensitive lookup. If this succeeds we are done. */
3900 code = cm_Lookup(newDscp, cNewNamep, 0, userp, reqp, &oldTargetScp);
3903 * Case insensitive lookup. If this succeeds, it could have found the
3904 * same file with a name that differs only by case or it could be a
3905 * different file entirely.
3907 code = cm_Lookup(newDscp, cNewNamep, CM_FLAG_CASEFOLD, userp, reqp, &oldTargetScp);
3909 /* found a matching object with the new name */
3910 if (cm_FidCmp(&oldScp->fid, &oldTargetScp->fid)) {
3911 /* and they don't match so return an error */
3912 osi_Log2(afsd_logp, "cm_Rename newDscp 0x%p cNewName %S new name already exists",
3913 newDscp, osi_LogSaveStringW(afsd_logp, cNewNamep));
3914 code = CM_ERROR_EXISTS;
3916 cm_ReleaseSCache(oldTargetScp);
3917 oldTargetScp = NULL;
3918 } else if (code == CM_ERROR_AMBIGUOUS_FILENAME) {
3919 code = CM_ERROR_EXISTS;
3921 /* The target does not exist. Clear the error and perform the rename. */
3933 newNamep = cm_ClientStringToFsStringAlloc(cNewNamep, -1, NULL);
3935 /* try the RPC now */
3936 InterlockedIncrement(&oldDscp->activeRPCs);
3938 InterlockedIncrement(&newDscp->activeRPCs);
3939 osi_Log2(afsd_logp, "CALL Rename old scp 0x%p new scp 0x%p",
3942 code = cm_ConnFromFID(&oldDscp->fid, userp, reqp, &connp);
3946 oldDirAFSFid.Volume = oldDscp->fid.volume;
3947 oldDirAFSFid.Vnode = oldDscp->fid.vnode;
3948 oldDirAFSFid.Unique = oldDscp->fid.unique;
3949 newDirAFSFid.Volume = newDscp->fid.volume;
3950 newDirAFSFid.Vnode = newDscp->fid.vnode;
3951 newDirAFSFid.Unique = newDscp->fid.unique;
3953 rxconnp = cm_GetRxConn(connp);
3954 code = RXAFS_Rename(rxconnp, &oldDirAFSFid, oldNamep,
3955 &newDirAFSFid, newNamep,
3956 &updatedOldDirStatus, &updatedNewDirStatus,
3958 rx_PutConnection(rxconnp);
3960 } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid, NULL, 1,
3961 &volSync, NULL, NULL, code));
3962 code = cm_MapRPCError(code, reqp);
3965 osi_Log1(afsd_logp, "CALL Rename FAILURE, code 0x%x", code);
3967 osi_Log0(afsd_logp, "CALL Rename SUCCESS");
3970 /* update the individual stat cache entries for the directories */
3972 lock_ObtainWrite(&oldDirOp.scp->dirlock);
3973 oldDirOp.lockType = CM_DIRLOCK_WRITE;
3976 lock_ObtainWrite(&oldDscp->rw);
3978 cm_MergeStatus(NULL, oldDscp, &updatedOldDirStatus, &volSync,
3979 userp, reqp, CM_MERGEFLAG_DIROP);
3980 if (cm_CheckDirOpForSingleChange(&oldDirOp)) {
3981 lock_ReleaseWrite(&oldDscp->rw);
3982 if (bTargetExists && oneDir) {
3983 diropCode = cm_DirDeleteEntry(&oldDirOp, newNamep);
3985 cm_BPlusDirDeleteEntry(&oldDirOp, cNewNamep);
3990 diropCode = cm_BPlusDirLookup(&oldDirOp, cOldNamep, &fileFid);
3991 if (diropCode == CM_ERROR_INEXACT_MATCH)
3993 else if (diropCode == EINVAL)
3995 diropCode = cm_DirLookup(&oldDirOp, oldNamep, &fileFid);
3997 if (diropCode == 0) {
3999 diropCode = cm_DirCreateEntry(&oldDirOp, newNamep, &fileFid);
4001 cm_BPlusDirCreateEntry(&oldDirOp, cNewNamep, &fileFid);
4005 if (diropCode == 0) {
4006 diropCode = cm_DirDeleteEntry(&oldDirOp, oldNamep);
4008 cm_BPlusDirDeleteEntry(&oldDirOp, cOldNamep);
4012 lock_ObtainWrite(&oldDscp->rw);
4016 InterlockedDecrement(&oldDscp->activeRPCs);
4018 cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
4019 lock_ReleaseWrite(&oldDscp->rw);
4021 cm_EndDirOp(&oldDirOp);
4023 /* and update it for the new one, too, if necessary */
4026 lock_ObtainWrite(&newDirOp.scp->dirlock);
4027 newDirOp.lockType = CM_DIRLOCK_WRITE;
4029 lock_ObtainWrite(&newDscp->rw);
4031 cm_MergeStatus(NULL, newDscp, &updatedNewDirStatus, &volSync,
4032 userp, reqp, CM_MERGEFLAG_DIROP);
4035 * we only make the local change if we successfully made
4036 * the change in the old directory AND there was only one
4037 * change in the new directory
4039 if (diropCode == 0 && cm_CheckDirOpForSingleChange(&newDirOp)) {
4040 lock_ReleaseWrite(&newDscp->rw);
4042 if (bTargetExists && !oneDir) {
4043 diropCode = cm_DirDeleteEntry(&newDirOp, newNamep);
4045 cm_BPlusDirDeleteEntry(&newDirOp, cNewNamep);
4049 cm_DirCreateEntry(&newDirOp, newNamep, &fileFid);
4051 cm_BPlusDirCreateEntry(&newDirOp, cNewNamep, &fileFid);
4053 lock_ObtainWrite(&newDscp->rw);
4057 InterlockedIncrement(&newDscp->activeRPCs);
4059 cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
4060 lock_ReleaseWrite(&newDscp->rw);
4062 cm_EndDirOp(&newDirOp);
4067 * After the rename the file server has invalidated the callbacks
4068 * on the file that was moved and destroyed any target file.
4070 lock_ObtainWrite(&oldScp->rw);
4071 cm_DiscardSCache(oldScp);
4072 lock_ReleaseWrite(&oldScp->rw);
4074 if (RDR_Initialized)
4075 RDR_InvalidateObject(oldScp->fid.cell, oldScp->fid.volume, oldScp->fid.vnode, oldScp->fid.unique,
4076 oldScp->fid.hash, oldScp->fileType, AFS_INVALIDATE_CALLBACK);
4079 lock_ObtainWrite(&oldTargetScp->rw);
4080 cm_DiscardSCache(oldTargetScp);
4081 lock_ReleaseWrite(&oldTargetScp->rw);
4083 if (RDR_Initialized)
4084 RDR_InvalidateObject(oldTargetScp->fid.cell, oldTargetScp->fid.volume, oldTargetScp->fid.vnode, oldTargetScp->fid.unique,
4085 oldTargetScp->fid.hash, oldTargetScp->fileType, AFS_INVALIDATE_CALLBACK);
4091 cm_ReleaseSCache(oldScp);
4094 cm_ReleaseSCache(oldTargetScp);
4101 /* and return error code */
4105 /* Byte range locks:
4107 The OpenAFS Windows client has to fake byte range locks given no
4108 server side support for such locks. This is implemented as keyed
4109 byte range locks on the cache manager.
4111 Keyed byte range locks:
4113 Each cm_scache_t structure keeps track of a list of keyed locks.
4114 The key for a lock identifies an owner of a set of locks (referred
4115 to as a client). Each key is represented by a value. The set of
4116 key values used within a specific cm_scache_t structure form a
4117 namespace that has a scope of just that cm_scache_t structure. The
4118 same key value can be used with another cm_scache_t structure and
4119 correspond to a completely different client. However it is
4120 advantageous for the SMB or IFS layer to make sure that there is a
4121 1-1 mapping between client and keys over all cm_scache_t objects.
4123 Assume a client C has key Key(C) (although, since the scope of the
4124 key is a cm_scache_t, the key can be Key(C,S), where S is the
4125 cm_scache_t. But assume a 1-1 relation between keys and clients).
4126 A byte range (O,+L) denotes byte addresses (O) through (O+L-1)
4127 inclusive (a.k.a. [O,O+L-1]). The function Key(x) is implemented
4128 through cm_generateKey() function for both SMB and IFS.
4130 The list of locks for a cm_scache_t object S is maintained in
4131 S->fileLocks. The cache manager will set a lock on the AFS file
4132 server in order to assert the locks in S->fileLocks. If only
4133 shared locks are in place for S, then the cache manager will obtain
4134 a LockRead lock, while if there are any exclusive locks, it will
4135 obtain a LockWrite lock. If the exclusive locks are all released
4136 while the shared locks remain, then the cache manager will
4137 downgrade the lock from LockWrite to LockRead. Similarly, if an
4138 exclusive lock is obtained when only shared locks exist, then the
4139 cache manager will try to upgrade the lock from LockRead to
4142 Each lock L owned by client C maintains a key L->key such that
4143 L->key == Key(C), the effective range defined by L->LOffset and
4144 L->LLength such that the range of bytes affected by the lock is
4145 (L->LOffset, +L->LLength), a type maintained in L->LockType which
4146 is either exclusive or shared.
4150 A lock exists iff it is in S->fileLocks for some cm_scache_t
4151 S. Existing locks are in one of the following states: ACTIVE,
4152 WAITLOCK, WAITUNLOCK, LOST, DELETED.
4154 The following sections describe each lock and the associated
4157 1. ACTIVE: A lock L is ACTIVE iff the cache manager has asserted
4158 the lock with the AFS file server. This type of lock can be
4159 exercised by a client to read or write to the locked region (as
4162 1.1 ACTIVE->LOST: When the AFS file server fails to extend a
4163 server lock that was required to assert the lock. Before
4164 marking the lock as lost, the cache manager checks if the file
4165 has changed on the server. If the file has not changed, then
4166 the cache manager will attempt to obtain a new server lock
4167 that is sufficient to assert the client side locks for the
4168 file. If any of these fail, the lock is marked as LOST.
4169 Otherwise, it is left as ACTIVE.
4171 1.2 ACTIVE->DELETED: Lock is released.
4173 2. WAITLOCK: A lock is in a WAITLOCK state if the cache manager
4174 grants the lock but the lock is yet to be asserted with the AFS
4175 file server. Once the file server grants the lock, the state
4176 will transition to an ACTIVE lock.
4178 2.1 WAITLOCK->ACTIVE: The server granted the lock.
4180 2.2 WAITLOCK->DELETED: Lock is abandoned, or timed out during
4183 2.3 WAITLOCK->LOST: One or more locks from this client were
4184 marked as LOST. No further locks will be granted to this
4185 client until all lost locks are removed.
4187 3. WAITUNLOCK: A lock is in a WAITUNLOCK state if the cache manager
4188 receives a request for a lock that conflicts with an existing
4189 ACTIVE or WAITLOCK lock. The lock will be placed in the queue
4190 and will be granted at such time the conflicting locks are
4191 removed, at which point the state will transition to either
4194 3.1 WAITUNLOCK->ACTIVE: The conflicting lock was removed. The
4195 current serverLock is sufficient to assert this lock, or a
4196 sufficient serverLock is obtained.
4198 3.2 WAITUNLOCK->WAITLOCK: The conflicting lock was removed,
4199 however the required serverLock is yet to be asserted with the
4202 3.3 WAITUNLOCK->DELETED: The lock is abandoned, timed out or
4205 3.5 WAITUNLOCK->LOST: One or more locks from this client were
4206 marked as LOST. No further locks will be granted to this
4207 client until all lost locks are removed.
4209 4. LOST: A lock L is LOST if the server lock that was required to
4210 assert the lock could not be obtained or if it could not be
4211 extended, or if other locks by the same client were LOST.
4212 Essentially, once a lock is LOST, the contract between the cache
4213 manager and that specific client is no longer valid.
4215 The cache manager rechecks the server lock once every minute and
4216 extends it as appropriate. If this is not done for 5 minutes,
4217 the AFS file server will release the lock (the 5 minute timeout
4218 is based on current file server code and is fairly arbitrary).
4219 Once released, the lock cannot be re-obtained without verifying
4220 that the contents of the file hasn't been modified since the
4221 time the lock was released. Re-obtaining the lock without
4222 verifying this may lead to data corruption. If the lock can not
4223 be obtained safely, then all active locks for the cm_scache_t
4226 4.1 LOST->DELETED: The lock is released.
4228 5. DELETED: The lock is no longer relevant. Eventually, it will
4229 get removed from the cm_scache_t. In the meantime, it will be
4230 treated as if it does not exist.
4232 5.1 DELETED->not exist: The lock is removed from the
4235 The following are classifications of locks based on their state.
4237 6* A lock L is ACCEPTED if it is ACTIVE or WAITLOCK. These locks
4238 have been accepted by the cache manager, but may or may not have
4239 been granted back to the client.
4241 7* A lock L is QUEUED if it is ACTIVE, WAITLOCK or WAITUNLOCK.
4243 8* A lock L is WAITING if it is WAITLOCK or WAITUNLOCK.
4247 A client C can READ range (Offset,+Length) of a file represented by
4248 cm_scache_t S iff (1):
4250 1. for all _a_ in (Offset,+Length), all of the following is true:
4252 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
4253 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
4256 1.2 For each LOST lock L in S->fileLocks such that _a_ in
4257 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
4260 (When locks are lost on an cm_scache_t, all locks are lost. By
4261 4.2 (below), if there is an exclusive LOST lock, then there
4262 can't be any overlapping ACTIVE locks.)
4264 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
4266 2. for all _a_ in (Offset,+Length), one of the following is true:
4268 2.1 Byte _a_ of S is unowned (as specified in 1.1) AND there
4269 does not exist a LOST lock L such that _a_ in
4270 (L->LOffset,+L->LLength).
4272 2.2 Byte _a_ of S is owned by C under lock L (as specified in
4273 1.2) AND L->LockType is exclusive.
4275 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
4277 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
4280 3.1 If L->LockType is exclusive then there does NOT exist a
4281 ACCEPTED lock M in S->fileLocks such that _a_ in
4282 (M->LOffset,+M->LLength).
4284 (If we count all QUEUED locks then we hit cases such as
4285 cascading waiting locks where the locks later on in the queue
4286 can be granted without compromising file integrity. On the
4287 other hand if only ACCEPTED locks are considered, then locks
4288 that were received earlier may end up waiting for locks that
4289 were received later to be unlocked. The choice of ACCEPTED
4290 locks was made to mimic the Windows byte range lock
4293 3.2 If L->LockType is shared then for each ACCEPTED lock M in
4294 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
4295 M->LockType is shared.
4297 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
4299 4.1 M->key != Key(C)
4301 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
4302 and (M->LOffset,+M->LLength) do not intersect.
4304 (Note: If a client loses a lock, it loses all locks.
4305 Subsequently, it will not be allowed to obtain any more locks
4306 until all existing LOST locks that belong to the client are
4307 released. Once all locks are released by a single client,
4308 there exists no further contract between the client and AFS
4309 about the contents of the file, hence the client can then
4310 proceed to obtain new locks and establish a new contract.
4312 This doesn't quite work as you think it should, because most
4313 applications aren't built to deal with losing locks they
4314 thought they once had. For now, we don't have a good
4315 solution to lost locks.
4317 Also, for consistency reasons, we have to hold off on
4318 granting locks that overlap exclusive LOST locks.)
4320 A client C can only unlock locks L in S->fileLocks which have
4323 The representation and invariants are as follows:
4325 - Each cm_scache_t structure keeps:
4327 - A queue of byte-range locks (cm_scache_t::fileLocks) which
4328 are of type cm_file_lock_t.
4330 - A record of the highest server-side lock that has been
4331 obtained for this object (cm_scache_t::serverLock), which is
4332 one of (-1), LockRead, LockWrite.
4334 - A count of ACCEPTED exclusive and shared locks that are in the
4335 queue (cm_scache_t::sharedLocks and
4336 cm_scache_t::exclusiveLocks)
4338 - Each cm_file_lock_t structure keeps:
4340 - The type of lock (cm_file_lock_t::LockType)
4342 - The key associated with the lock (cm_file_lock_t::key)
4344 - The offset and length of the lock (cm_file_lock_t::LOffset
4345 and cm_file_lock_t::LLength)
4347 - The state of the lock.
4349 - Time of issuance or last successful extension
4351 Semantic invariants:
4353 I1. The number of ACCEPTED locks in S->fileLocks are
4354 (S->sharedLocks + S->exclusiveLocks)
4356 External invariants:
4358 I3. S->serverLock is the lock that we have asserted with the
4359 AFS file server for this cm_scache_t.
4361 I4. S->serverLock == LockRead iff there is at least one ACTIVE
4362 shared lock, but no ACTIVE exclusive locks.
4364 I5. S->serverLock == LockWrite iff there is at least one ACTIVE
4367 I6. If L is a LOST lock, then for each lock M in S->fileLocks,
4368 M->key == L->key IMPLIES M is LOST or DELETED.
4373 #define IS_LOCK_ACTIVE(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == 0)
4375 #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)
4377 #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)
4379 #define IS_LOCK_LOST(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_LOST)
4381 #define IS_LOCK_DELETED(lockp) (((lockp)->flags & CM_FILELOCK_FLAG_DELETED) == CM_FILELOCK_FLAG_DELETED)
4384 #define IS_LOCK_ACCEPTED(lockp) (IS_LOCK_ACTIVE(lockp) || IS_LOCK_WAITLOCK(lockp))
4387 #define IS_LOCK_CLIENTONLY(lockp) ((((lockp)->scp->flags & CM_SCACHEFLAG_RO) == CM_SCACHEFLAG_RO) || (((lockp)->flags & CM_FILELOCK_FLAG_CLIENTONLY) == CM_FILELOCK_FLAG_CLIENTONLY))
4390 #define INTERSECT_RANGE(r1,r2) (((r2).offset+(r2).length) > (r1).offset && ((r1).offset +(r1).length) > (r2).offset)
4393 #define CONTAINS_RANGE(r1,r2) (((r2).offset+(r2).length) <= ((r1).offset+(r1).length) && (r1).offset <= (r2).offset)
4395 #if defined(VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS) && !defined(LOCK_TESTING)
4396 #define SCP_SUPPORTS_BRLOCKS(scp) ((scp)->cbServerp && ((scp)->cbServerp->capabilities & VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS))
4398 #define SCP_SUPPORTS_BRLOCKS(scp) (1)
4401 #define SERVERLOCKS_ENABLED(scp) (!((scp)->flags & CM_SCACHEFLAG_RO) && cm_enableServerLocks && SCP_SUPPORTS_BRLOCKS(scp))
4403 #if defined(VICED_CAPABILITY_WRITELOCKACL)
4404 #define SCP_SUPPORTS_WRITELOCKACL(scp) ((scp)->cbServerp && ((scp->cbServerp->capabilities & VICED_CAPABILITY_WRITELOCKACL)))
4406 #define SCP_SUPPORTS_WRITELOCKACL(scp) (0)
4408 /* This should really be defined in any build that this code is being
4410 #error VICED_CAPABILITY_WRITELOCKACL not defined.
4413 static void cm_LockRangeSubtract(cm_range_t * pos, const cm_range_t * neg)
4415 afs_int64 int_begin;
4418 int_begin = max(pos->offset, neg->offset);
4419 int_end = min(pos->offset+pos->length, neg->offset+neg->length);
4421 if (int_begin < int_end) {
4422 if (int_begin == pos->offset) {
4423 pos->length = pos->offset + pos->length - int_end;
4424 pos->offset = int_end;
4425 } else if (int_end == pos->offset + pos->length) {
4426 pos->length = int_begin - pos->offset;
4429 /* We only subtract ranges if the resulting range is
4430 contiguous. If we try to support non-contigous ranges, we
4431 aren't actually improving performance. */
4435 /* Called with scp->rw held. Returns 0 if all is clear to read the
4436 specified range by the client identified by key.
4438 long cm_LockCheckRead(cm_scache_t *scp,
4439 LARGE_INTEGER LOffset,
4440 LARGE_INTEGER LLength,
4443 #ifndef ADVISORY_LOCKS
4445 cm_file_lock_t *fileLock;
4449 int substract_ranges = FALSE;
4451 range.offset = LOffset.QuadPart;
4452 range.length = LLength.QuadPart;
4456 1. for all _a_ in (Offset,+Length), all of the following is true:
4458 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
4459 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
4462 1.2 For each LOST lock L in S->fileLocks such that _a_ in
4463 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
4468 lock_ObtainRead(&cm_scacheLock);
4470 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
4472 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4474 if (INTERSECT_RANGE(range, fileLock->range)) {
4475 if (IS_LOCK_ACTIVE(fileLock)) {
4476 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4478 /* If there is an active lock for this client, it
4479 is safe to substract ranges.*/
4480 cm_LockRangeSubtract(&range, &fileLock->range);
4481 substract_ranges = TRUE;
4483 if (fileLock->lockType != LockRead) {
4484 code = CM_ERROR_LOCK_CONFLICT;
4488 /* even if the entire range is locked for reading,
4489 we still can't grant the lock at this point
4490 because the client may have lost locks. That
4491 is, unless we have already seen an active lock
4492 belonging to the client, in which case there
4493 can't be any lost locks for this client. */
4494 if (substract_ranges)
4495 cm_LockRangeSubtract(&range, &fileLock->range);
4497 } else if (IS_LOCK_LOST(fileLock) &&
4498 (cm_KeyEquals(&fileLock->key, &key, 0) || fileLock->lockType == LockWrite)) {
4499 code = CM_ERROR_BADFD;
4505 lock_ReleaseRead(&cm_scacheLock);
4507 osi_Log4(afsd_logp, "cm_LockCheckRead scp 0x%x offset %d length %d code 0x%x",
4508 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
4519 /* Called with scp->rw held. Returns 0 if all is clear to write the
4520 specified range by the client identified by key.
4522 long cm_LockCheckWrite(cm_scache_t *scp,
4523 LARGE_INTEGER LOffset,
4524 LARGE_INTEGER LLength,
4527 #ifndef ADVISORY_LOCKS
4529 cm_file_lock_t *fileLock;
4534 range.offset = LOffset.QuadPart;
4535 range.length = LLength.QuadPart;
4538 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
4540 2. for all _a_ in (Offset,+Length), one of the following is true:
4542 2.1 Byte _a_ of S is unowned AND there does not exist a LOST
4543 lock L such that _a_ in (L->LOffset,+L->LLength).
4545 2.2 Byte _a_ of S is owned by C under lock L AND L->LockType is
4549 lock_ObtainRead(&cm_scacheLock);
4551 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
4553 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4555 if (INTERSECT_RANGE(range, fileLock->range)) {
4556 if (IS_LOCK_ACTIVE(fileLock)) {
4557 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4558 if (fileLock->lockType == LockWrite) {
4560 /* if there is an active lock for this client, it
4561 is safe to substract ranges */
4562 cm_LockRangeSubtract(&range, &fileLock->range);
4564 code = CM_ERROR_LOCK_CONFLICT;
4568 code = CM_ERROR_LOCK_CONFLICT;
4571 } else if (IS_LOCK_LOST(fileLock)) {
4572 code = CM_ERROR_BADFD;
4578 lock_ReleaseRead(&cm_scacheLock);
4580 osi_Log4(afsd_logp, "cm_LockCheckWrite scp 0x%x offset %d length %d code 0x%x",
4581 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
4592 /* Called with cm_scacheLock write locked */
4593 static cm_file_lock_t * cm_GetFileLock(void) {
4596 l = (cm_file_lock_t *) cm_freeFileLocks;
4598 osi_QRemove(&cm_freeFileLocks, &l->q);
4600 l = malloc(sizeof(cm_file_lock_t));
4601 osi_assertx(l, "null cm_file_lock_t");
4604 memset(l, 0, sizeof(cm_file_lock_t));
4609 /* Called with cm_scacheLock write locked */
4610 static void cm_PutFileLock(cm_file_lock_t *l) {
4611 osi_QAdd(&cm_freeFileLocks, &l->q);
4614 /* called with scp->rw held. May release it during processing, but
4615 leaves it held on exit. */
4616 long cm_IntSetLock(cm_scache_t * scp, cm_user_t * userp, int lockType,
4622 struct rx_connection * rxconnp;
4624 afs_uint32 reqflags = reqp->flags;
4626 osi_Log2(afsd_logp, "CALL SetLock scp 0x%p for lock %d", scp, lockType);
4630 * The file server prior to 1.6.2 does not report an accurate value
4631 * and callbacks are not issued if the lock is dropped due to expiration.
4633 if ((lockType != LOCKING_ANDX_SHARED_LOCK && scp->fsLockCount != 0) ||
4634 (lockType == LOCKING_ANDX_SHARED_LOCK && scp->fsLockCount < 0))
4636 code = CM_ERROR_LOCK_NOT_GRANTED;
4637 osi_Log2(afsd_logp, "CALL SetLock FAILURE, fsLockCount %d code 0x%x", scp->fsLockCount, code);
4642 memset(&volSync, 0, sizeof(volSync));
4644 tfid.Volume = scp->fid.volume;
4645 tfid.Vnode = scp->fid.vnode;
4646 tfid.Unique = scp->fid.unique;
4649 reqp->flags |= CM_REQ_NORETRY;
4650 lock_ReleaseWrite(&scp->rw);
4653 code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4657 rxconnp = cm_GetRxConn(connp);
4658 code = RXAFS_SetLock(rxconnp, &tfid, lockType,
4660 rx_PutConnection(rxconnp);
4662 } while (cm_Analyze(connp, userp, reqp, &cfid, NULL, 1, &volSync,
4665 code = cm_MapRPCError(code, reqp);
4667 osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
4669 osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
4672 reqp->flags = reqflags;
4674 lock_ObtainWrite(&scp->rw);
4677 * The file server does not return a status structure so we must
4678 * locally track the file server lock count to the best of our
4681 if (lockType == LockWrite)
4682 scp->fsLockCount = -1;
4689 /* called with scp->rw held. Releases it during processing */
4690 long cm_IntReleaseLock(cm_scache_t * scp, cm_user_t * userp,
4696 struct rx_connection * rxconnp;
4699 if (scp->flags & CM_SCACHEFLAG_DELETED) {
4700 osi_Log1(afsd_logp, "CALL ReleaseLock on Deleted Vnode scp 0x%p", scp);
4704 memset(&volSync, 0, sizeof(volSync));
4706 tfid.Volume = scp->fid.volume;
4707 tfid.Vnode = scp->fid.vnode;
4708 tfid.Unique = scp->fid.unique;
4711 lock_ReleaseWrite(&scp->rw);
4713 osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
4716 code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4720 rxconnp = cm_GetRxConn(connp);
4721 code = RXAFS_ReleaseLock(rxconnp, &tfid, &volSync);
4722 rx_PutConnection(rxconnp);
4724 } while (cm_Analyze(connp, userp, reqp, &cfid, NULL, 1, &volSync,
4726 code = cm_MapRPCError(code, reqp);
4729 "CALL ReleaseLock FAILURE, code 0x%x", code);
4732 "CALL ReleaseLock SUCCESS");
4734 lock_ObtainWrite(&scp->rw);
4737 * The file server does not return a status structure so we must
4738 * locally track the file server lock count to the best of our
4742 if (scp->fsLockCount < 0)
4743 scp->fsLockCount = 0;
4746 return (code != CM_ERROR_BADFD ? code : 0);
4749 /* called with scp->rw held. May release it during processing, but
4750 will exit with lock held.
4754 - 0 if the user has permission to get the specified lock for the scp
4756 - CM_ERROR_NOACCESS if not
4758 Any other error from cm_SyncOp will be sent down untranslated.
4760 If CM_ERROR_NOACCESS is returned and lock_type is LockRead, then
4761 phas_insert (if non-NULL) will receive a boolean value indicating
4762 whether the user has INSERT permission or not.
4764 long cm_LockCheckPerms(cm_scache_t * scp,
4771 long code = 0, code2 = 0;
4773 /* lock permissions are slightly tricky because of the 'i' bit.
4774 If the user has PRSFS_LOCK, she can read-lock the file. If the
4775 user has PRSFS_WRITE, she can write-lock the file. However, if
4776 the user has PRSFS_INSERT, then she can write-lock new files,
4777 but not old ones. Since we don't have information about
4778 whether a file is new or not, we assume that if the user owns
4779 the scp, then she has the permissions that are granted by
4782 osi_Log3(afsd_logp, "cm_LockCheckPerms for scp[0x%p] type[%d] user[0x%p]",
4783 scp, lock_type, userp);
4785 if (lock_type == LockRead)
4786 rights |= PRSFS_LOCK;
4787 else if (lock_type == LockWrite)
4788 rights |= PRSFS_WRITE | PRSFS_LOCK;
4791 osi_assertx(FALSE, "invalid lock type");
4796 *phas_insert = FALSE;
4798 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
4799 CM_SCACHESYNC_GETSTATUS |
4800 CM_SCACHESYNC_NEEDCALLBACK);
4802 if (phas_insert && scp->creator == userp) {
4804 /* If this file was created by the user, then we check for
4805 PRSFS_INSERT. If the file server is recent enough, then
4806 this should be sufficient for her to get a write-lock (but
4807 not necessarily a read-lock). VICED_CAPABILITY_WRITELOCKACL
4808 indicates whether a file server supports getting write
4809 locks when the user only has PRSFS_INSERT.
4811 If the file was not created by the user we skip the check
4812 because the INSERT bit will not apply to this user even
4816 code2 = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
4817 CM_SCACHESYNC_GETSTATUS |
4818 CM_SCACHESYNC_NEEDCALLBACK);
4820 if (code2 == CM_ERROR_NOACCESS) {
4821 osi_Log0(afsd_logp, "cm_LockCheckPerms user has no INSERT bits");
4823 *phas_insert = TRUE;
4824 osi_Log0(afsd_logp, "cm_LockCheckPerms user has INSERT bits");
4828 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
4830 osi_Log1(afsd_logp, "cm_LockCheckPerms returning code %d", code);
4835 /* called with scp->rw held */
4836 long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
4837 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
4839 int allowWait, cm_user_t *userp, cm_req_t *reqp,
4840 cm_file_lock_t **lockpp)
4843 int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
4844 cm_file_lock_t *fileLock;
4847 int wait_unlock = FALSE;
4848 int force_client_lock = FALSE;
4850 osi_Log4(afsd_logp, "cm_Lock scp 0x%x type 0x%x offset %d length %d",
4851 scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
4852 osi_Log4(afsd_logp, "... allowWait %d key <0x%x, 0x%x, 0x%x>", allowWait,
4853 key.process_id, key.session_id, key.file_id);
4856 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
4858 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
4861 3.1 If L->LockType is exclusive then there does NOT exist a
4862 ACCEPTED lock M in S->fileLocks such that _a_ in
4863 (M->LOffset,+M->LLength).
4865 3.2 If L->LockType is shared then for each ACCEPTED lock M in
4866 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
4867 M->LockType is shared.
4869 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
4871 4.1 M->key != Key(C)
4873 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
4874 and (M->LOffset,+M->LLength) do not intersect.
4877 range.offset = LOffset.QuadPart;
4878 range.length = LLength.QuadPart;
4880 lock_ObtainRead(&cm_scacheLock);
4882 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4884 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4886 if (IS_LOCK_LOST(fileLock)) {
4887 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4888 code = CM_ERROR_BADFD;
4890 } else if (fileLock->lockType == LockWrite && INTERSECT_RANGE(range, fileLock->range)) {
4891 code = CM_ERROR_WOULDBLOCK;
4897 /* we don't need to check for deleted locks here since deleted
4898 locks are dequeued from scp->fileLocks */
4899 if (IS_LOCK_ACCEPTED(fileLock) &&
4900 INTERSECT_RANGE(range, fileLock->range)) {
4902 if ((sLockType & LOCKING_ANDX_SHARED_LOCK) == 0 ||
4903 fileLock->lockType != LockRead) {
4905 code = CM_ERROR_WOULDBLOCK;
4911 lock_ReleaseRead(&cm_scacheLock);
4913 if (code == 0 && SERVERLOCKS_ENABLED(scp)) {
4914 if (Which == scp->serverLock ||
4915 (Which == LockRead && scp->serverLock == LockWrite)) {
4919 /* we already have the lock we need */
4920 osi_Log3(afsd_logp, " we already have the correct lock. exclusives[%d], shared[%d], serverLock[%d]",
4921 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4923 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4925 /* special case: if we don't have permission to read-lock
4926 the file, then we force a clientside lock. This is to
4927 compensate for applications that obtain a read-lock for
4928 reading files off of directories that don't grant
4929 read-locks to the user. */
4930 if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4932 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4933 osi_Log0(afsd_logp, " User has no read-lock perms, but has INSERT perms.");
4936 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4937 force_client_lock = TRUE;
4941 } else if ((scp->exclusiveLocks > 0) ||
4942 (scp->sharedLocks > 0 && scp->serverLock != LockRead)) {
4945 /* We are already waiting for some other lock. We should
4946 wait for the daemon to catch up instead of generating a
4947 flood of SetLock calls. */
4948 osi_Log3(afsd_logp, " already waiting for other lock. exclusives[%d], shared[%d], serverLock[%d]",
4949 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4951 /* see if we have permission to create the lock in the
4953 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4955 code = CM_ERROR_WOULDBLOCK;
4956 else if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4958 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4960 " User has no read-lock perms, but has INSERT perms.");
4961 code = CM_ERROR_WOULDBLOCK;
4964 " User has no read-lock perms. Forcing client-side lock");
4965 force_client_lock = TRUE;
4969 /* leave any other codes as-is */
4973 int check_data_version = FALSE;
4976 /* first check if we have permission to elevate or obtain
4978 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4980 if (code == CM_ERROR_NOACCESS && Which == LockRead &&
4981 (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp))) {
4982 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4983 force_client_lock = TRUE;
4988 /* has_insert => (Which == LockRead, code == CM_ERROR_NOACCESS) */
4990 if (scp->serverLock == LockRead && Which == LockWrite) {
4992 /* We want to escalate the lock to a LockWrite.
4993 * Unfortunately that's not really possible without
4994 * letting go of the current lock. But for now we do
4998 " attempting to UPGRADE from LockRead to LockWrite.");
5000 " dataVersion on scp: %I64d", scp->dataVersion);
5002 /* we assume at this point (because scp->serverLock
5003 was valid) that we had a valid server lock. */
5004 scp->lockDataVersion = scp->dataVersion;
5005 check_data_version = TRUE;
5007 code = cm_IntReleaseLock(scp, userp, reqp);
5010 /* We couldn't release the lock */
5013 scp->serverLock = -1;
5017 /* We need to obtain a server lock of type Which in order
5018 * to assert this file lock */
5019 #ifndef AGGRESSIVE_LOCKS
5022 newLock = LockWrite;
5025 code = cm_IntSetLock(scp, userp, newLock, reqp);
5027 #ifdef AGGRESSIVE_LOCKS
5028 if ((code == CM_ERROR_WOULDBLOCK ||
5029 code == CM_ERROR_NOACCESS) && newLock != Which) {
5030 /* we wanted LockRead. We tried LockWrite. Now try
5035 osi_assertx(newLock == LockRead, "lock type not read");
5037 code = cm_IntSetLock(scp, userp, newLock, reqp);
5041 if (code == CM_ERROR_NOACCESS) {
5042 if (Which == LockRead) {
5043 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
5045 /* We requested a read-lock, but we have permission to
5046 * get a write-lock. Try that */
5048 tcode = cm_LockCheckPerms(scp, LockWrite, userp, reqp, NULL);
5051 newLock = LockWrite;
5053 osi_Log0(afsd_logp, " User has 'i' perms and the request was for a LockRead. Trying to get a LockWrite instead");
5055 code = cm_IntSetLock(scp, userp, newLock, reqp);
5058 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
5059 force_client_lock = TRUE;
5061 } else if (Which == LockWrite &&
5062 scp->creator == userp && !SCP_SUPPORTS_WRITELOCKACL(scp)) {
5065 /* Special case: if the lock request was for a
5066 * LockWrite and the user owns the file and we weren't
5067 * allowed to obtain the serverlock, we either lost a
5068 * race (the permissions changed from under us), or we
5069 * have 'i' bits, but we aren't allowed to lock the
5072 /* check if we lost a race... */
5073 tcode = cm_LockCheckPerms(scp, Which, userp, reqp, NULL);
5076 osi_Log0(afsd_logp, " User has 'i' perms but can't obtain write locks. Using client-side locks.");
5077 force_client_lock = TRUE;
5082 if (code == 0 && check_data_version &&
5083 scp->dataVersion != scp->lockDataVersion) {
5084 /* We lost a race. Although we successfully obtained
5085 * a lock, someone modified the file in between. The
5086 * locks have all been technically lost. */
5089 " Data version mismatch while upgrading lock.");
5091 " Data versions before=%I64d, after=%I64d",
5092 scp->lockDataVersion,
5095 " Releasing stale lock for scp 0x%x", scp);
5097 code = cm_IntReleaseLock(scp, userp, reqp);
5099 scp->serverLock = -1;
5101 code = CM_ERROR_INVAL;
5102 } else if (code == 0) {
5103 scp->serverLock = newLock;
5104 scp->lockDataVersion = scp->dataVersion;
5108 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
5109 scp->serverLock == -1) {
5110 /* Oops. We lost the lock. */
5111 cm_LockMarkSCacheLost(scp);
5114 } else if (code == 0) { /* server locks not enabled */
5116 " Skipping server lock for scp");
5121 if (code != 0 && !force_client_lock) {
5122 /* Special case error translations
5124 Applications don't expect certain errors from a
5125 LockFile/UnlockFile call. We need to translate some error
5126 code to codes that apps expect and handle. */
5128 /* We shouldn't actually need to handle this case since we
5129 simulate locks for RO scps anyway. */
5130 if (code == CM_ERROR_READONLY) {
5131 osi_Log0(afsd_logp, " Reinterpreting CM_ERROR_READONLY as CM_ERROR_NOACCESS");
5132 code = CM_ERROR_NOACCESS;
5136 if (code == 0 || (code == CM_ERROR_WOULDBLOCK && allowWait) ||
5137 force_client_lock) {
5139 /* clear the error if we are forcing a client lock, so we
5140 don't get confused later. */
5141 if (force_client_lock && code != CM_ERROR_WOULDBLOCK)
5146 lock_ObtainWrite(&cm_scacheLock);
5147 fileLock = cm_GetFileLock();
5149 fileLock->fid = scp->fid;
5151 fileLock->key = key;
5152 fileLock->lockType = Which;
5153 fileLock->userp = userp;
5154 fileLock->range = range;
5155 fileLock->flags = (code == 0 ? 0 :
5157 CM_FILELOCK_FLAG_WAITUNLOCK :
5158 CM_FILELOCK_FLAG_WAITLOCK));
5160 if (force_client_lock || !SERVERLOCKS_ENABLED(scp))
5161 fileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
5163 fileLock->lastUpdate = (code == 0 && !force_client_lock) ? time(NULL) : 0;
5165 osi_QAddT(&scp->fileLocksH, &scp->fileLocksT, &fileLock->fileq);
5166 cm_HoldSCacheNoLock(scp);
5167 fileLock->scp = scp;
5168 osi_QAdd(&cm_allFileLocks, &fileLock->q);
5169 lock_ReleaseWrite(&cm_scacheLock);
5175 if (IS_LOCK_CLIENTONLY(fileLock)) {
5177 } else if (IS_LOCK_ACCEPTED(fileLock)) {
5178 if (Which == LockRead)
5181 scp->exclusiveLocks++;
5185 "cm_Lock Lock added 0x%p flags 0x%x to scp [0x%p]",
5186 fileLock, fileLock->flags, scp);
5188 " exclusives[%d] shared[%d] client[%d] serverLock[%d]",
5189 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
5190 (int)(signed char) scp->serverLock);
5193 "cm_Lock Rejecting lock (code = 0x%x)", code);
5196 /* Convert from would block to lock not granted */
5197 if (code == CM_ERROR_WOULDBLOCK)
5198 code = CM_ERROR_LOCK_NOT_GRANTED;
5204 cm_IntUnlock(cm_scache_t * scp,
5210 osi_assertx(scp->sharedLocks >= 0, "scp->sharedLocks < 0");
5211 osi_assertx(scp->exclusiveLocks >= 0, "scp->exclusiveLocks < 0");
5212 osi_assertx(scp->clientLocks >= 0, "scp->clientLocks < 0");
5214 if (!SERVERLOCKS_ENABLED(scp)) {
5215 osi_Log0(afsd_logp, " Skipping server lock for scp");
5219 /* Ideally we would go through the rest of the locks to determine
5220 * if one or more locks that were formerly in WAITUNLOCK can now
5221 * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
5222 * scp->sharedLocks accordingly. However, the retrying of locks
5223 * in that manner is done cm_RetryLock() manually.
5226 if (scp->serverLock == LockWrite &&
5227 scp->exclusiveLocks == 0 &&
5228 scp->sharedLocks > 0) {
5229 /* The serverLock should be downgraded to LockRead */
5230 osi_Log0(afsd_logp, " DOWNGRADE lock from LockWrite to LockRead");
5232 /* Make sure there are no dirty buffers left. */
5233 code = cm_FSync(scp, userp, reqp, TRUE);
5235 /* since scp->serverLock looked sane, we are going to assume
5236 that we have a valid server lock. */
5237 scp->lockDataVersion = scp->dataVersion;
5238 osi_Log1(afsd_logp, " dataVersion on scp = %I64d", scp->dataVersion);
5240 /* before we downgrade, make sure that we have enough
5241 permissions to get the read lock. */
5242 code = cm_LockCheckPerms(scp, LockRead, userp, reqp, NULL);
5245 osi_Log0(afsd_logp, " SKIPPING downgrade because user doesn't have perms to get downgraded lock");
5251 code = cm_IntReleaseLock(scp, userp, reqp);
5254 /* so we couldn't release it. Just let the lock be for now */
5258 scp->serverLock = -1;
5261 code = cm_IntSetLock(scp, userp, LockRead, reqp);
5263 if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
5264 scp->serverLock = LockRead;
5265 } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
5266 /* We lost a race condition. Although we have a valid
5267 lock on the file, the data has changed and essentially
5268 we have lost the lock we had during the transition. */
5270 osi_Log0(afsd_logp, "Data version mismatch during lock downgrade");
5271 osi_Log2(afsd_logp, " Data versions before=%I64d, after=%I64d",
5272 scp->lockDataVersion,
5275 code = cm_IntReleaseLock(scp, userp, reqp);
5277 code = CM_ERROR_INVAL;
5278 scp->serverLock = -1;
5282 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
5283 (scp->serverLock == -1)) {
5285 cm_LockMarkSCacheLost(scp);
5288 /* failure here has no bearing on the return value of cm_Unlock() */
5291 } else if (scp->serverLock != (-1) &&
5292 scp->exclusiveLocks == 0 &&
5293 scp->sharedLocks == 0) {
5294 /* The serverLock should be released entirely */
5296 if (scp->serverLock == LockWrite) {
5297 osi_Log0(afsd_logp, " RELEASE LockWrite -> LockNone");
5299 /* Make sure there are no dirty buffers left. */
5300 code = cm_FSync(scp, userp, reqp, TRUE);
5302 osi_Log0(afsd_logp, " RELEASE LockRead -> LockNone");
5305 code = cm_IntReleaseLock(scp, userp, reqp);
5308 scp->serverLock = (-1);
5314 /* Called with scp->rw held */
5315 long cm_UnlockByKey(cm_scache_t * scp,
5322 cm_file_lock_t *fileLock;
5323 osi_queue_t *q, *qn;
5326 osi_Log4(afsd_logp, "cm_UnlockByKey scp 0x%p key <0x%x,0x%x,0x%x",
5327 scp, key.process_id, key.session_id, key.file_id);
5328 osi_Log1(afsd_logp, " flags=0x%x", flags);
5330 lock_ObtainWrite(&cm_scacheLock);
5332 for (q = scp->fileLocksH; q; q = qn) {
5335 fileLock = (cm_file_lock_t *)
5336 ((char *) q - offsetof(cm_file_lock_t, fileq));
5339 osi_Log4(afsd_logp, " Checking lock[0x%x] range[%d,+%d] type[%d]",
5341 (unsigned long) fileLock->range.offset,
5342 (unsigned long) fileLock->range.length,
5343 fileLock->lockType);
5344 osi_Log4(afsd_logp, " key<0x%x, 0x%x, 0x%x> flags[0x%x]",
5345 fileLock->key.process_id, fileLock->key.session_id, fileLock->key.file_id,
5348 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5349 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5350 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5352 fileLock->fid.volume,
5353 fileLock->fid.vnode,
5354 fileLock->fid.unique);
5355 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5356 fileLock->scp->fid.cell,
5357 fileLock->scp->fid.volume,
5358 fileLock->scp->fid.vnode,
5359 fileLock->scp->fid.unique);
5360 osi_assertx(FALSE, "invalid fid value");
5364 if (!IS_LOCK_DELETED(fileLock) &&
5365 cm_KeyEquals(&fileLock->key, &key, flags)) {
5366 osi_Log3(afsd_logp, "...Unlock range [%d,+%d] type %d",
5367 fileLock->range.offset,
5368 fileLock->range.length,
5369 fileLock->lockType);
5371 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
5373 if (IS_LOCK_CLIENTONLY(fileLock)) {
5375 } else if (IS_LOCK_ACCEPTED(fileLock)) {
5376 if (fileLock->lockType == LockRead)
5379 scp->exclusiveLocks--;
5382 fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5384 cm_ReleaseUser(fileLock->userp);
5385 cm_ReleaseSCacheNoLock(scp);
5387 fileLock->userp = NULL;
5388 fileLock->scp = NULL;
5394 lock_ReleaseWrite(&cm_scacheLock);
5396 if (n_unlocks == 0) {
5397 osi_Log0(afsd_logp, "cm_UnlockByKey no locks found");
5398 osi_Log3(afsd_logp, " Leaving scp with exclusives[%d], shared[%d], serverLock[%d]",
5399 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
5404 code = cm_IntUnlock(scp, userp, reqp);
5405 osi_Log1(afsd_logp, "cm_UnlockByKey code 0x%x", code);
5407 osi_Log4(afsd_logp, " Leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
5408 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
5409 (int)(signed char) scp->serverLock);
5414 /* Called with scp->rw held */
5415 long cm_Unlock(cm_scache_t *scp,
5416 unsigned char sLockType,
5417 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
5424 int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
5425 cm_file_lock_t *fileLock;
5427 int release_userp = FALSE;
5428 int exact_match = !(flags & CM_UNLOCK_FLAG_MATCH_RANGE);
5430 LARGE_INTEGER RangeEnd;
5432 osi_Log4(afsd_logp, "cm_Unlock scp 0x%p type 0x%x offset 0x%x length 0x%x",
5433 scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
5434 osi_Log4(afsd_logp, "... key <0x%x,0x%x,0x%x> flags 0x%x",
5435 key.process_id, key.session_id, key.file_id, flags);
5438 RangeEnd.QuadPart = LOffset.QuadPart + LLength.QuadPart;
5441 lock_ObtainRead(&cm_scacheLock);
5443 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5444 fileLock = (cm_file_lock_t *)
5445 ((char *) q - offsetof(cm_file_lock_t, fileq));
5448 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5449 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5450 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5452 fileLock->fid.volume,
5453 fileLock->fid.vnode,
5454 fileLock->fid.unique);
5455 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5456 fileLock->scp->fid.cell,
5457 fileLock->scp->fid.volume,
5458 fileLock->scp->fid.vnode,
5459 fileLock->scp->fid.unique);
5460 osi_assertx(FALSE, "invalid fid value");
5464 if (!IS_LOCK_DELETED(fileLock) &&
5465 cm_KeyEquals(&fileLock->key, &key, 0) &&
5466 fileLock->range.offset == LOffset.QuadPart &&
5467 fileLock->range.length == LLength.QuadPart) {
5473 if (!IS_LOCK_DELETED(fileLock) &&
5474 cm_KeyEquals(&fileLock->key, &key, 0) &&
5475 fileLock->range.offset >= LOffset.QuadPart &&
5476 fileLock->range.offset < RangeEnd.QuadPart &&
5477 (fileLock->range.offset + fileLock->range.length) <= RangeEnd.QuadPart) {
5485 lock_ReleaseRead(&cm_scacheLock);
5487 if (lock_found && !exact_match) {
5491 osi_Log0(afsd_logp, "cm_Unlock lock not found; failure");
5493 /* The lock didn't exist anyway. *shrug* */
5494 return CM_ERROR_RANGE_NOT_LOCKED;
5498 /* discard lock record */
5499 lock_ConvertRToW(&cm_scacheLock);
5500 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
5503 * Don't delete it here; let the daemon delete it, to simplify
5504 * the daemon's traversal of the list.
5507 if (IS_LOCK_CLIENTONLY(fileLock)) {
5509 } else if (IS_LOCK_ACCEPTED(fileLock)) {
5510 if (fileLock->lockType == LockRead)
5513 scp->exclusiveLocks--;
5516 fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5518 if (userp != NULL) {
5519 cm_ReleaseUser(fileLock->userp);
5521 userp = fileLock->userp;
5522 release_userp = TRUE;
5524 cm_ReleaseSCacheNoLock(scp);
5525 fileLock->userp = NULL;
5526 fileLock->scp = NULL;
5527 lock_ReleaseWrite(&cm_scacheLock);
5529 code = cm_IntUnlock(scp, userp, reqp);
5531 if (release_userp) {
5532 cm_ReleaseUser(userp);
5533 release_userp = FALSE;
5537 osi_Log1(afsd_logp, "cm_Unlock not exact match, searching for next lock, code 0x%x", code);
5538 goto try_again; /* might be more than one lock in the range */
5543 osi_Log1(afsd_logp, "cm_Unlock code 0x%x", code);
5544 osi_Log4(afsd_logp, " leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
5545 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
5546 (int)(signed char) scp->serverLock);
5551 /* called with scp->rw held */
5552 void cm_LockMarkSCacheLost(cm_scache_t * scp)
5554 cm_file_lock_t *fileLock;
5557 osi_Log1(afsd_logp, "cm_LockMarkSCacheLost scp 0x%x", scp);
5559 /* cm_scacheLock needed because we are modifying fileLock->flags */
5560 lock_ObtainWrite(&cm_scacheLock);
5562 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5564 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
5566 if (IS_LOCK_ACTIVE(fileLock) &&
5567 !IS_LOCK_CLIENTONLY(fileLock)) {
5568 if (fileLock->lockType == LockRead)
5571 scp->exclusiveLocks--;
5573 fileLock->flags |= CM_FILELOCK_FLAG_LOST;
5577 scp->serverLock = -1;
5578 scp->lockDataVersion = CM_SCACHE_VERSION_BAD;
5579 lock_ReleaseWrite(&cm_scacheLock);
5582 /* Called with no relevant locks held */
5583 void cm_CheckLocks()
5585 osi_queue_t *q, *nq;
5586 cm_file_lock_t *fileLock;
5592 struct rx_connection * rxconnp;
5595 memset(&volSync, 0, sizeof(volSync));
5599 lock_ObtainWrite(&cm_scacheLock);
5601 cm_lockRefreshCycle++;
5603 osi_Log1(afsd_logp, "cm_CheckLocks starting lock check cycle %d", cm_lockRefreshCycle);
5605 for (q = cm_allFileLocks; q; q = nq) {
5606 fileLock = (cm_file_lock_t *) q;
5610 if (IS_LOCK_DELETED(fileLock)) {
5611 cm_user_t *userp = fileLock->userp;
5612 cm_scache_t *scp = fileLock->scp;
5613 fileLock->userp = NULL;
5614 fileLock->scp = NULL;
5617 lock_ReleaseWrite(&cm_scacheLock);
5618 lock_ObtainWrite(&scp->rw);
5619 code = cm_IntUnlock(scp, userp, &req);
5620 lock_ReleaseWrite(&scp->rw);
5622 cm_ReleaseUser(userp);
5623 lock_ObtainWrite(&cm_scacheLock);
5624 cm_ReleaseSCacheNoLock(scp);
5626 osi_QRemove(&cm_allFileLocks, q);
5627 cm_PutFileLock(fileLock);
5629 } else if (IS_LOCK_ACTIVE(fileLock) && !IS_LOCK_CLIENTONLY(fileLock)) {
5631 /* Server locks must have been enabled for us to have
5632 received an active non-client-only lock. */
5633 osi_assertx(cm_enableServerLocks, "!cm_enableServerLocks");
5635 scp = fileLock->scp;
5636 osi_assertx(scp != NULL, "null cm_scache_t");
5638 cm_HoldSCacheNoLock(scp);
5641 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5642 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5643 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5645 fileLock->fid.volume,
5646 fileLock->fid.vnode,
5647 fileLock->fid.unique);
5648 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5649 fileLock->scp->fid.cell,
5650 fileLock->scp->fid.volume,
5651 fileLock->scp->fid.vnode,
5652 fileLock->scp->fid.unique);
5653 osi_assertx(FALSE, "invalid fid");
5656 /* Server locks are extended once per scp per refresh
5658 if (scp->lastRefreshCycle != cm_lockRefreshCycle) {
5660 int scp_done = FALSE;
5662 osi_Log1(afsd_logp, "cm_CheckLocks Updating scp 0x%x", scp);
5664 lock_ReleaseWrite(&cm_scacheLock);
5665 lock_ObtainWrite(&scp->rw);
5667 /* did the lock change while we weren't holding the lock? */
5668 if (!IS_LOCK_ACTIVE(fileLock))
5669 goto post_syncopdone;
5671 code = cm_SyncOp(scp, NULL, fileLock->userp, &req, 0,
5672 CM_SCACHESYNC_NEEDCALLBACK
5673 | CM_SCACHESYNC_GETSTATUS
5674 | CM_SCACHESYNC_LOCK);
5678 "cm_CheckLocks SyncOp failure code 0x%x", code);
5679 goto post_syncopdone;
5682 /* cm_SyncOp releases scp->rw during which the lock
5683 may get released. */
5684 if (!IS_LOCK_ACTIVE(fileLock))
5685 goto pre_syncopdone;
5687 if (scp->serverLock != -1 && !(scp->flags & CM_SCACHEFLAG_DELETED)) {
5691 tfid.Volume = scp->fid.volume;
5692 tfid.Vnode = scp->fid.vnode;
5693 tfid.Unique = scp->fid.unique;
5695 userp = fileLock->userp;
5697 osi_Log3(afsd_logp, "CALL ExtendLock lock 0x%p for scp=0x%p with lock %d",
5700 (int) scp->serverLock);
5702 lock_ReleaseWrite(&scp->rw);
5705 code = cm_ConnFromFID(&cfid, userp,
5710 rxconnp = cm_GetRxConn(connp);
5711 code = RXAFS_ExtendLock(rxconnp, &tfid,
5713 rx_PutConnection(rxconnp);
5715 osi_Log1(afsd_logp, " ExtendLock returns %d", code);
5717 } while (cm_Analyze(connp, userp, &req,
5718 &cfid, NULL, 1, &volSync, NULL, NULL,
5721 code = cm_MapRPCError(code, &req);
5723 lock_ObtainWrite(&scp->rw);
5726 osi_Log1(afsd_logp, "CALL ExtendLock FAILURE, code 0x%x", code);
5727 scp->fsLockCount = 0;
5729 osi_Log0(afsd_logp, "CALL ExtendLock SUCCESS");
5730 scp->lockDataVersion = scp->dataVersion;
5733 if ((code == EINVAL || code == CM_ERROR_INVAL) &&
5734 scp->lockDataVersion == scp->dataVersion) {
5738 (scp->exclusiveLocks > 0) ? LockWrite: LockRead;
5740 /* we might still have a chance to obtain a
5743 code = cm_IntSetLock(scp, userp, lockType, &req);
5746 code = CM_ERROR_INVAL;
5747 } else if (scp->lockDataVersion != scp->dataVersion) {
5749 /* now check if we still have the file at
5750 the right data version. */
5752 "Data version mismatch on scp 0x%p",
5755 " Data versions: before=%I64d, after=%I64d",
5756 scp->lockDataVersion,
5759 code = cm_IntReleaseLock(scp, userp, &req);
5761 code = CM_ERROR_INVAL;
5765 if (code == EINVAL || code == CM_ERROR_INVAL ||
5766 code == CM_ERROR_BADFD) {
5767 cm_LockMarkSCacheLost(scp);
5771 /* interestingly, we have found an active lock
5772 belonging to an scache that has no
5774 cm_LockMarkSCacheLost(scp);
5781 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5784 lock_ReleaseWrite(&scp->rw);
5786 lock_ObtainWrite(&cm_scacheLock);
5789 fileLock->lastUpdate = time(NULL);
5793 scp->lastRefreshCycle = cm_lockRefreshCycle;
5796 /* we have already refreshed the locks on this scp */
5797 fileLock->lastUpdate = time(NULL);
5800 cm_ReleaseSCacheNoLock(scp);
5802 } else if (IS_LOCK_ACTIVE(fileLock) && IS_LOCK_CLIENTONLY(fileLock)) {
5803 /* TODO: Check callbacks */
5807 lock_ReleaseWrite(&cm_scacheLock);
5808 osi_Log1(afsd_logp, "cm_CheckLocks completes lock check cycle %d", cm_lockRefreshCycle);
5811 /* NOT called with scp->rw held. */
5812 long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
5815 cm_scache_t *scp = NULL;
5816 cm_file_lock_t *fileLock;
5820 int force_client_lock = FALSE;
5821 int has_insert = FALSE;
5822 int check_data_version = FALSE;
5826 if (client_is_dead) {
5827 code = CM_ERROR_TIMEDOUT;
5831 lock_ObtainRead(&cm_scacheLock);
5833 osi_Log2(afsd_logp, "cm_RetryLock checking lock %p (scp=%p)", oldFileLock, oldFileLock->scp);
5834 osi_Log4(afsd_logp, " offset(%x:%x) length(%x:%x)",
5835 (unsigned)(oldFileLock->range.offset >> 32),
5836 (unsigned)(oldFileLock->range.offset & 0xffffffff),
5837 (unsigned)(oldFileLock->range.length >> 32),
5838 (unsigned)(oldFileLock->range.length & 0xffffffff));
5839 osi_Log4(afsd_logp, " key<0x%x,0x%x,0x%x> flags=%x",
5840 oldFileLock->key.process_id, oldFileLock->key.session_id, oldFileLock->key.file_id,
5841 (unsigned)(oldFileLock->flags));
5843 /* if the lock has already been granted, then we have nothing to do */
5844 if (IS_LOCK_ACTIVE(oldFileLock)) {
5845 lock_ReleaseRead(&cm_scacheLock);
5846 osi_Log0(afsd_logp, "cm_RetryLock lock already granted");
5850 /* we can't do anything with lost or deleted locks at the moment. */
5851 if (IS_LOCK_LOST(oldFileLock) || IS_LOCK_DELETED(oldFileLock)) {
5852 code = CM_ERROR_BADFD;
5853 osi_Log0(afsd_logp, "cm_RetryLock lock is lost or deleted");
5854 lock_ReleaseRead(&cm_scacheLock);
5858 scp = oldFileLock->scp;
5860 osi_assertx(scp != NULL, "null cm_scache_t");
5862 lock_ReleaseRead(&cm_scacheLock);
5863 lock_ObtainWrite(&scp->rw);
5865 code = cm_LockCheckPerms(scp, oldFileLock->lockType,
5869 if (code == CM_ERROR_NOACCESS && oldFileLock->lockType == LockRead) {
5870 if (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp)) {
5871 force_client_lock = TRUE;
5875 lock_ReleaseWrite(&scp->rw);
5879 lock_ObtainWrite(&cm_scacheLock);
5881 /* Check if we already have a sufficient server lock to allow this
5882 lock to go through. */
5883 if (IS_LOCK_WAITLOCK(oldFileLock) &&
5884 (!SERVERLOCKS_ENABLED(scp) ||
5885 scp->serverLock == oldFileLock->lockType ||
5886 scp->serverLock == LockWrite)) {
5888 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5890 if (SERVERLOCKS_ENABLED(scp)) {
5891 osi_Log1(afsd_logp, "cm_RetryLock Server lock (%d) is sufficient for lock. Granting",
5892 (int) scp->serverLock);
5894 osi_Log0(afsd_logp, "cm_RetryLock skipping server lock for scp");
5897 lock_ReleaseWrite(&cm_scacheLock);
5898 lock_ReleaseWrite(&scp->rw);
5903 if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5905 /* check if the conflicting locks have dissappeared already */
5906 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5908 fileLock = (cm_file_lock_t *)
5909 ((char *) q - offsetof(cm_file_lock_t, fileq));
5911 if (IS_LOCK_LOST(fileLock)) {
5912 if (cm_KeyEquals(&fileLock->key, &oldFileLock->key, 0)) {
5913 code = CM_ERROR_BADFD;
5914 oldFileLock->flags |= CM_FILELOCK_FLAG_LOST;
5915 osi_Log1(afsd_logp, " found lost lock %p for same key. Marking lock as lost",
5918 } else if (fileLock->lockType == LockWrite &&
5919 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5920 osi_Log1(afsd_logp, " found conflicting LOST lock %p", fileLock);
5921 code = CM_ERROR_WOULDBLOCK;
5926 if (IS_LOCK_ACCEPTED(fileLock) &&
5927 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5929 if (oldFileLock->lockType != LockRead ||
5930 fileLock->lockType != LockRead) {
5932 osi_Log1(afsd_logp, " found conflicting lock %p", fileLock);
5933 code = CM_ERROR_WOULDBLOCK;
5941 lock_ReleaseWrite(&cm_scacheLock);
5942 lock_ReleaseWrite(&scp->rw);
5947 /* when we get here, the lock is either a WAITUNLOCK or WAITLOCK.
5948 If it is WAITUNLOCK, then we didn't find any conflicting lock
5949 but we haven't verfied whether the serverLock is sufficient to
5950 assert it. If it is WAITLOCK, then the serverLock is
5951 insufficient to assert it. Eitherway, we are ready to accept
5952 the lock as either ACTIVE or WAITLOCK depending on the
5955 /* First, promote the WAITUNLOCK to a WAITLOCK */
5956 if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5957 if (oldFileLock->lockType == LockRead)
5960 scp->exclusiveLocks++;
5962 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITUNLOCK;
5963 oldFileLock->flags |= CM_FILELOCK_FLAG_WAITLOCK;
5966 osi_assertx(IS_LOCK_WAITLOCK(oldFileLock), "!IS_LOCK_WAITLOCK");
5968 if (force_client_lock ||
5969 !SERVERLOCKS_ENABLED(scp) ||
5970 scp->serverLock == oldFileLock->lockType ||
5971 (oldFileLock->lockType == LockRead &&
5972 scp->serverLock == LockWrite)) {
5974 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5976 if ((force_client_lock ||
5977 !SERVERLOCKS_ENABLED(scp)) &&
5978 !IS_LOCK_CLIENTONLY(oldFileLock)) {
5980 oldFileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
5982 if (oldFileLock->lockType == LockRead)
5985 scp->exclusiveLocks--;
5990 lock_ReleaseWrite(&cm_scacheLock);
5991 lock_ReleaseWrite(&scp->rw);
5998 code = cm_SyncOp(scp, NULL, oldFileLock->userp, &req, 0,
5999 CM_SCACHESYNC_NEEDCALLBACK
6000 | CM_SCACHESYNC_GETSTATUS
6001 | CM_SCACHESYNC_LOCK);
6003 osi_Log1(afsd_logp, "cm_RetryLock SyncOp failure code 0x%x", code);
6004 lock_ReleaseWrite(&cm_scacheLock);
6005 goto post_syncopdone;
6008 if (!IS_LOCK_WAITLOCK(oldFileLock))
6009 goto pre_syncopdone;
6011 userp = oldFileLock->userp;
6013 #ifndef AGGRESSIVE_LOCKS
6014 newLock = oldFileLock->lockType;
6016 newLock = LockWrite;
6020 /* if has_insert is non-zero, then:
6021 - the lock a LockRead
6022 - we don't have permission to get a LockRead
6023 - we do have permission to get a LockWrite
6024 - the server supports VICED_CAPABILITY_WRITELOCKACL
6027 newLock = LockWrite;
6030 lock_ReleaseWrite(&cm_scacheLock);
6032 /* when we get here, either we have a read-lock and want a
6033 write-lock or we don't have any locks and we want some
6036 if (scp->serverLock == LockRead) {
6038 osi_assertx(newLock == LockWrite, "!LockWrite");
6040 osi_Log0(afsd_logp, " Attempting to UPGRADE from LockRead to LockWrite");
6042 scp->lockDataVersion = scp->dataVersion;
6043 check_data_version = TRUE;
6045 code = cm_IntReleaseLock(scp, userp, &req);
6048 goto pre_syncopdone;
6050 scp->serverLock = -1;
6053 code = cm_IntSetLock(scp, userp, newLock, &req);
6056 if (scp->dataVersion != scp->lockDataVersion) {
6057 /* we lost a race. too bad */
6060 " Data version mismatch while upgrading lock.");
6062 " Data versions before=%I64d, after=%I64d",
6063 scp->lockDataVersion,
6066 " Releasing stale lock for scp 0x%x", scp);
6068 code = cm_IntReleaseLock(scp, userp, &req);
6070 scp->serverLock = -1;
6072 code = CM_ERROR_INVAL;
6074 cm_LockMarkSCacheLost(scp);
6076 scp->serverLock = newLock;
6081 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
6087 if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
6088 lock_ObtainWrite(&cm_scacheLock);
6089 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, &oldFileLock->fileq);
6090 lock_ReleaseWrite(&cm_scacheLock);
6092 lock_ReleaseWrite(&scp->rw);
6095 lock_ObtainWrite(&cm_scacheLock);
6097 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
6098 } else if (code != CM_ERROR_WOULDBLOCK) {
6099 oldFileLock->flags |= CM_FILELOCK_FLAG_DELETED;
6100 cm_ReleaseUser(oldFileLock->userp);
6101 oldFileLock->userp = NULL;
6102 if (oldFileLock->scp) {
6103 cm_ReleaseSCacheNoLock(oldFileLock->scp);
6104 oldFileLock->scp = NULL;
6107 lock_ReleaseWrite(&cm_scacheLock);
6112 cm_key_t cm_GenerateKey(afs_uint16 session_id, afs_offs_t process_id, afs_uint64 file_id)
6116 key.process_id = process_id;
6117 key.session_id = session_id;
6118 key.file_id = file_id;
6123 int cm_KeyEquals(cm_key_t *k1, cm_key_t *k2, int flags)
6125 return (k1->session_id == k2->session_id) && (k1->file_id == k2->file_id) &&
6126 ((flags & CM_UNLOCK_FLAG_BY_FID) || (k1->process_id == k2->process_id));
6129 void cm_ReleaseAllLocks(void)
6135 cm_file_lock_t *fileLock;
6138 for (i = 0; i < cm_data.scacheHashTableSize; i++)
6140 for ( scp = cm_data.scacheHashTablep[i]; scp; scp = scp->nextp ) {
6141 while (scp->fileLocksH != NULL) {
6142 lock_ObtainWrite(&scp->rw);
6143 lock_ObtainWrite(&cm_scacheLock);
6144 if (!scp->fileLocksH) {
6145 lock_ReleaseWrite(&cm_scacheLock);
6146 lock_ReleaseWrite(&scp->rw);
6149 fileLock = (cm_file_lock_t *)((char *) scp->fileLocksH - offsetof(cm_file_lock_t, fileq));
6150 userp = fileLock->userp;
6152 key = fileLock->key;
6153 cm_HoldSCacheNoLock(scp);
6154 lock_ReleaseWrite(&cm_scacheLock);
6155 cm_UnlockByKey(scp, key, 0, userp, &req);
6156 cm_ReleaseSCache(scp);
6157 cm_ReleaseUser(userp);
6158 lock_ReleaseWrite(&scp->rw);