2 * Copyright 2000, International Business Machines Corporation and others.
5 * This software has been released under the terms of the IBM Public
6 * License. For details, see the LICENSE file in the top-level source
7 * directory or online at http://www.openafs.org/dl/license10.html
10 #include <afsconfig.h>
11 #include <afs/param.h>
33 extern void afsi_log(char *pattern, ...);
36 int cm_enableServerLocks = 1;
38 int cm_followBackupPath = 0;
41 * Case-folding array. This was constructed by inspecting of SMBtrace output.
42 * I do not know anything more about it.
44 unsigned char cm_foldUpper[256] = {
45 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
46 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
47 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
48 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
49 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
50 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
51 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
52 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
53 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
54 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
55 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
56 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
57 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
58 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
59 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
60 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
61 0x80, 0x9a, 0x90, 0x41, 0x8e, 0x41, 0x8f, 0x80,
62 0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x8e, 0x8f,
63 0x90, 0x92, 0x92, 0x4f, 0x99, 0x4f, 0x55, 0x55,
64 0x59, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
65 0x41, 0x49, 0x4f, 0x55, 0xa5, 0xa5, 0x56, 0xa7,
66 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
67 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
68 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
69 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
70 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
71 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
72 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
73 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
74 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
75 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
76 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
80 * Case-insensitive string comparison. We used to use stricmp, but it doesn't
81 * know about 8-bit characters (e.g. 129 is lowercase u-umlaut, 154 is
82 * upper-case u-umlaut).
84 int cm_stricmp(const char *str1, const char *str2)
96 c1 = (char) cm_foldUpper[(unsigned char)(*str1++)];
97 c2 = (char) cm_foldUpper[(unsigned char)(*str2++)];
107 /* return success if we can open this file in this mode */
108 long cm_CheckOpen(cm_scache_t *scp, int openMode, int trunc, cm_user_t *userp,
116 rights |= PRSFS_READ;
117 if (openMode == 1 || openMode == 2 || trunc)
118 rights |= PRSFS_WRITE;
120 lock_ObtainWrite(&scp->rw);
122 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
123 CM_SCACHESYNC_GETSTATUS
124 | CM_SCACHESYNC_NEEDCALLBACK
125 | CM_SCACHESYNC_LOCK);
128 ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
129 scp->fileType == CM_SCACHETYPE_FILE) {
132 unsigned int sLockType;
133 LARGE_INTEGER LOffset, LLength;
135 /* Check if there's some sort of lock on the file at the
138 key = cm_GenerateKey(CM_SESSION_CMINT,0,0);
140 if (rights & PRSFS_WRITE)
143 sLockType = LOCKING_ANDX_SHARED_LOCK;
145 LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
146 LOffset.LowPart = CM_FLSHARE_OFFSET_LOW;
147 LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
148 LLength.LowPart = CM_FLSHARE_LENGTH_LOW;
150 code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
153 cm_Unlock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp);
155 /* In this case, we allow the file open to go through even
156 though we can't enforce mandatory locking on the
158 if (code == CM_ERROR_NOACCESS &&
159 !(rights & PRSFS_WRITE))
162 if (code == CM_ERROR_LOCK_NOT_GRANTED)
163 code = CM_ERROR_SHARING_VIOLATION;
167 } else if (code != 0) {
171 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
175 lock_ReleaseWrite(&scp->rw);
180 /* return success if we can open this file in this mode */
181 long cm_CheckNTOpen(cm_scache_t *scp,
182 unsigned int desiredAccess,
183 unsigned int shareAccess,
184 unsigned int createDisp,
185 afs_offs_t process_id,
186 afs_offs_t handle_id,
187 cm_user_t *userp, cm_req_t *reqp,
188 cm_lock_data_t **ldpp)
192 afs_uint16 session_id;
194 osi_assertx(ldpp != NULL, "null cm_lock_data_t");
197 /* compute the session id */
198 if (reqp->flags & CM_REQ_SOURCE_SMB)
199 session_id = CM_SESSION_SMB;
200 else if (reqp->flags & CM_REQ_SOURCE_REDIR)
201 session_id = CM_SESSION_IFS;
203 session_id = CM_SESSION_CMINT;
205 /* Ignore the SYNCHRONIZE privilege */
206 desiredAccess &= ~SYNCHRONIZE;
208 /* Always allow delete; the RPC will tell us if it's OK */
211 if (desiredAccess == DELETE)
214 /* Always allow reading attributes (Hidden, System, Readonly, ...) */
215 if (desiredAccess == FILE_READ_ATTRIBUTES)
218 if (desiredAccess & (AFS_ACCESS_READ|AFS_ACCESS_EXECUTE))
219 rights |= (scp->fileType == CM_SCACHETYPE_DIRECTORY ? PRSFS_LOOKUP : PRSFS_READ);
221 /* We used to require PRSFS_WRITE if createDisp was 4
222 (OPEN_ALWAYS) even if AFS_ACCESS_WRITE was not requested.
223 However, we don't need to do that since the existence of the
224 scp implies that we don't need to create it. */
225 if (desiredAccess & AFS_ACCESS_WRITE)
226 rights |= PRSFS_WRITE;
228 if (desiredAccess & DELETE)
229 rights |= PRSFS_DELETE;
231 lock_ObtainWrite(&scp->rw);
233 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
234 CM_SCACHESYNC_GETSTATUS
235 | CM_SCACHESYNC_NEEDCALLBACK
236 | CM_SCACHESYNC_LOCK);
239 * If the open will fail because the volume is readonly, then we will
240 * return an access denied error instead. This is to help brain-dead
241 * apps run correctly on replicated volumes.
242 * See defect 10007 for more information.
244 if (code == CM_ERROR_READONLY)
245 code = CM_ERROR_NOACCESS;
248 !(shareAccess & FILE_SHARE_WRITE) &&
249 ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
250 scp->fileType == CM_SCACHETYPE_FILE) {
252 unsigned int sLockType;
253 LARGE_INTEGER LOffset, LLength;
255 /* Check if there's some sort of lock on the file at the
258 if (rights & PRSFS_WRITE)
261 sLockType = LOCKING_ANDX_SHARED_LOCK;
263 key = cm_GenerateKey(session_id, process_id, 0);
265 /* single byte lock at offset 0x0100 0000 0000 0000 */
266 LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
267 LOffset.LowPart = CM_FLSHARE_OFFSET_LOW;
268 LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
269 LLength.LowPart = CM_FLSHARE_LENGTH_LOW;
271 code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
274 (*ldpp) = (cm_lock_data_t *)malloc(sizeof(cm_lock_data_t));
281 (*ldpp)->sLockType = sLockType;
282 (*ldpp)->LOffset.HighPart = LOffset.HighPart;
283 (*ldpp)->LOffset.LowPart = LOffset.LowPart;
284 (*ldpp)->LLength.HighPart = LLength.HighPart;
285 (*ldpp)->LLength.LowPart = LLength.LowPart;
288 * In this case, we allow the file open to go through even
289 * though we can't enforce mandatory locking on the
291 if (code == CM_ERROR_NOACCESS &&
292 !(rights & PRSFS_WRITE))
295 if (code == CM_ERROR_LOCK_NOT_GRANTED)
296 code = CM_ERROR_SHARING_VIOLATION;
299 } else if (code != 0) {
304 lock_ReleaseWrite(&scp->rw);
307 osi_Log3(afsd_logp,"cm_CheckNTOpen scp 0x%p ldp 0x%p code 0x%x", scp, *ldpp, code);
311 extern long cm_CheckNTOpenDone(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp,
312 cm_lock_data_t ** ldpp)
314 osi_Log2(afsd_logp,"cm_CheckNTOpenDone scp 0x%p ldp 0x%p", scp, ldpp ? *ldpp : 0);
315 lock_ObtainWrite(&scp->rw);
317 cm_Unlock(scp, (*ldpp)->sLockType, (*ldpp)->LOffset, (*ldpp)->LLength,
318 (*ldpp)->key, 0, userp, reqp);
322 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
323 lock_ReleaseWrite(&scp->rw);
327 * When CAP_NT_SMBS has been negotiated, deletion (of files or directories) is
328 * done in three steps:
329 * (1) open for deletion (NT_CREATE_AND_X)
330 * (2) set for deletion on close (NT_TRANSACTION2, SET_FILE_INFO)
332 * We must not do the RPC until step 3. But if we are going to return an error
333 * code (e.g. directory not empty), we must return it by step 2, otherwise most
334 * clients will not notice it. So we do a preliminary check. For deleting
335 * files, this is almost free, since we have already done the RPC to get the
336 * parent directory's status bits. But for deleting directories, we must do an
337 * additional RPC to get the directory's data to check if it is empty. Sigh.
339 long cm_CheckNTDelete(cm_scache_t *dscp, cm_scache_t *scp, cm_user_t *userp,
345 cm_dirEntry_t *dep = 0;
346 unsigned short *hashTable;
348 int BeyondPage = 0, HaveDot = 0, HaveDotDot = 0;
351 /* First check permissions */
352 lock_ObtainWrite(&scp->rw);
353 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_DELETE,
354 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
356 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
357 lock_ReleaseWrite(&scp->rw);
361 /* If deleting directory, must be empty */
363 if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
366 thyper.HighPart = 0; thyper.LowPart = 0;
367 code = buf_Get(scp, &thyper, reqp, &bufferp);
371 lock_ObtainMutex(&bufferp->mx);
372 lock_ObtainWrite(&scp->rw);
375 code = cm_SyncOp(scp, bufferp, userp, reqp, 0,
376 CM_SCACHESYNC_NEEDCALLBACK
378 | CM_SCACHESYNC_BUFLOCKED);
382 if (cm_HaveBuffer(scp, bufferp, 1))
385 /* otherwise, load the buffer and try again */
386 lock_ReleaseMutex(&bufferp->mx);
387 code = cm_GetBuffer(scp, bufferp, NULL, userp, reqp);
388 lock_ReleaseWrite(&scp->rw);
389 lock_ObtainMutex(&bufferp->mx);
390 lock_ObtainWrite(&scp->rw);
391 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
396 lock_ReleaseWrite(&scp->rw);
399 /* We try to determine emptiness without looking beyond the first page,
400 * and without assuming "." and ".." are present and are on the first
401 * page (though these assumptions might, after all, be reasonable).
403 hashTable = (unsigned short *)(bufferp->datap + (32 * 5));
404 for (i=0; i<128; i++) {
405 idx = ntohs(hashTable[i]);
411 dep = (cm_dirEntry_t *)(bufferp->datap + (32 * idx));
412 if (strcmp(dep->name, ".") == 0)
414 else if (strcmp(dep->name, "..") == 0)
417 code = CM_ERROR_NOTEMPTY;
420 idx = ntohs(dep->next);
423 if (BeyondPage && HaveDot && HaveDotDot)
424 code = CM_ERROR_NOTEMPTY;
428 lock_ReleaseMutex(&bufferp->mx);
429 buf_Release(bufferp);
431 lock_ReleaseWrite(&scp->rw);
436 * Iterate through all entries in a directory.
437 * When the function funcp is called, the buffer is locked but the
438 * directory vnode is not.
440 * If the retscp parameter is not NULL, the parmp must be a
441 * cm_lookupSearch_t object.
443 long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
444 osi_hyper_t *startOffsetp, cm_user_t *userp, cm_req_t *reqp,
445 cm_scache_t **retscp)
449 cm_dirEntry_t *dep = 0;
452 osi_hyper_t dirLength;
453 osi_hyper_t bufferOffset;
454 osi_hyper_t curOffset;
458 cm_pageHeader_t *pageHeaderp;
460 long nextEntryCookie;
461 int numDirChunks; /* # of 32 byte dir chunks in this entry */
463 /* get the directory size */
464 lock_ObtainWrite(&scp->rw);
465 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_LOOKUP,
466 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
467 lock_ReleaseWrite(&scp->rw);
471 if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
472 return CM_ERROR_NOTDIR;
474 if (retscp) /* if this is a lookup call */
476 cm_lookupSearch_t* sp = parmp;
479 #ifdef AFS_FREELANCE_CLIENT
480 /* Freelance entries never end up in the DNLC because they
481 * do not have an associated cm_server_t
483 !(cm_freelanceEnabled &&
484 sp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
485 sp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
486 #else /* !AFS_FREELANCE_CLIENT */
491 int casefold = sp->caseFold;
492 sp->caseFold = 0; /* we have a strong preference for exact matches */
493 if ( *retscp = cm_dnlcLookup(scp, sp)) /* dnlc hit */
495 sp->caseFold = casefold;
498 sp->caseFold = casefold;
502 * see if we can find it using the directory hash tables.
503 * we can only do exact matches, since the hash is case
506 if (funcp != (cm_DirFuncp_t)cm_BPlusDirFoo)
515 code = cm_BeginDirOp(scp, userp, reqp, CM_DIRLOCK_READ,
516 CM_DIROP_FLAG_NONE, &dirop);
520 code = cm_BPlusDirLookup(&dirop, sp->nsearchNamep, &sp->fid);
525 code = cm_DirLookup(&dirop, sp->searchNamep, &sp->fid);
533 sp->ExactFound = TRUE;
534 *retscp = NULL; /* force caller to call cm_GetSCache() */
539 if (sp->caseFold && code == CM_ERROR_INEXACT_MATCH) {
542 sp->ExactFound = FALSE;
543 *retscp = NULL; /* force caller to call cm_GetSCache() */
547 return CM_ERROR_BPLUS_NOMATCH;
554 * XXX We only get the length once. It might change when we drop the
557 dirLength = scp->length;
560 bufferOffset.LowPart = bufferOffset.HighPart = 0;
562 curOffset = *startOffsetp;
564 curOffset.HighPart = 0;
565 curOffset.LowPart = 0;
569 /* make sure that curOffset.LowPart doesn't point to the first
570 * 32 bytes in the 2nd through last dir page, and that it
571 * doesn't point at the first 13 32-byte chunks in the first
572 * dir page, since those are dir and page headers, and don't
573 * contain useful information.
575 temp = curOffset.LowPart & (2048-1);
576 if (curOffset.HighPart == 0 && curOffset.LowPart < 2048) {
577 /* we're in the first page */
578 if (temp < 13*32) temp = 13*32;
581 /* we're in a later dir page */
582 if (temp < 32) temp = 32;
585 /* make sure the low order 5 bits are zero */
588 /* now put temp bits back ito curOffset.LowPart */
589 curOffset.LowPart &= ~(2048-1);
590 curOffset.LowPart |= temp;
592 /* check if we've passed the dir's EOF */
593 if (LargeIntegerGreaterThanOrEqualTo(curOffset, dirLength))
596 /* see if we can use the bufferp we have now; compute in which
597 * page the current offset would be, and check whether that's
598 * the offset of the buffer we have. If not, get the buffer.
600 thyper.HighPart = curOffset.HighPart;
601 thyper.LowPart = curOffset.LowPart & ~(cm_data.buf_blockSize-1);
602 if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
605 lock_ReleaseMutex(&bufferp->mx);
606 buf_Release(bufferp);
610 code = buf_Get(scp, &thyper, reqp, &bufferp);
612 /* if buf_Get() fails we do not have a buffer object to lock */
617 lock_ObtainMutex(&bufferp->mx);
618 bufferOffset = thyper;
620 /* now get the data in the cache */
622 lock_ObtainWrite(&scp->rw);
623 code = cm_SyncOp(scp, bufferp, userp, reqp,
625 CM_SCACHESYNC_NEEDCALLBACK
627 | CM_SCACHESYNC_BUFLOCKED);
629 lock_ReleaseWrite(&scp->rw);
632 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
634 if (cm_HaveBuffer(scp, bufferp, 1)) {
635 lock_ReleaseWrite(&scp->rw);
639 /* otherwise, load the buffer and try again */
640 lock_ReleaseMutex(&bufferp->mx);
641 code = cm_GetBuffer(scp, bufferp, NULL, userp,
643 lock_ReleaseWrite(&scp->rw);
644 lock_ObtainMutex(&bufferp->mx);
649 lock_ReleaseMutex(&bufferp->mx);
650 buf_Release(bufferp);
654 } /* if (wrong buffer) ... */
656 /* now we have the buffer containing the entry we're interested
657 * in; copy it out if it represents a non-deleted entry.
659 entryInDir = curOffset.LowPart & (2048-1);
660 entryInBuffer = curOffset.LowPart & (cm_data.buf_blockSize - 1);
662 /* page header will help tell us which entries are free. Page
663 * header can change more often than once per buffer, since
664 * AFS 3 dir page size may be less than (but not more than) a
665 * buffer package buffer.
667 /* only look intra-buffer */
668 temp = curOffset.LowPart & (cm_data.buf_blockSize - 1);
669 temp &= ~(2048 - 1); /* turn off intra-page bits */
670 pageHeaderp = (cm_pageHeader_t *) (bufferp->datap + temp);
672 /* now determine which entry we're looking at in the page. If
673 * it is free (there's a free bitmap at the start of the dir),
674 * we should skip these 32 bytes.
676 slotInPage = (entryInDir & 0x7e0) >> 5;
677 if (!(pageHeaderp->freeBitmap[slotInPage>>3]
678 & (1 << (slotInPage & 0x7)))) {
679 /* this entry is free */
680 numDirChunks = 1; /* only skip this guy */
684 tp = bufferp->datap + entryInBuffer;
685 dep = (cm_dirEntry_t *) tp; /* now points to AFS3 dir entry */
688 * here are some consistency checks
690 if (dep->flag != CM_DIR_FFIRST ||
691 strlen(dep->name) > 256) {
692 code = CM_ERROR_INVAL;
694 "cm_ApplyDir invalid directory entry for scp %p bufp %p",
696 osi_Log4(afsd_logp,"... cell %u vol %u vnode %u uniq %u",
697 scp->fid.cell, scp->fid.volume, scp->fid.vnode, scp->fid.unique);
698 bufferp->dataVersion = CM_BUF_VERSION_BAD;
702 /* while we're here, compute the next entry's location, too,
703 * since we'll need it when writing out the cookie into the
704 * dir listing stream.
706 numDirChunks = cm_NameEntries(dep->name, NULL);
708 /* compute the offset of the cookie representing the next entry */
709 nextEntryCookie = curOffset.LowPart
710 + (CM_DIR_CHUNKSIZE * numDirChunks);
712 if (dep->fid.vnode != 0) {
713 /* this is one of the entries to use: it is not deleted */
714 code = (*funcp)(scp, dep, parmp, &curOffset);
717 } /* if we're including this name */
720 /* and adjust curOffset to be where the new cookie is */
722 thyper.LowPart = CM_DIR_CHUNKSIZE * numDirChunks;
723 curOffset = LargeIntegerAdd(thyper, curOffset);
724 } /* while copying data for dir listing */
726 /* release the mutex */
728 lock_ReleaseMutex(&bufferp->mx);
729 buf_Release(bufferp);
734 int cm_NoneUpper(normchar_t *s)
738 if (c >= 'A' && c <= 'Z')
743 int cm_NoneLower(normchar_t *s)
747 if (c >= 'a' && c <= 'z')
752 long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
755 cm_lookupSearch_t *sp;
757 normchar_t matchName[MAX_PATH];
758 int looking_for_short_name = FALSE;
760 sp = (cm_lookupSearch_t *) rockp;
762 if (cm_FsStringToNormString(dep->name, -1, matchName, lengthof(matchName)) == 0) {
763 /* Can't normalize FS string. */
768 match = cm_NormStrCmpI(matchName, sp->nsearchNamep);
770 match = cm_NormStrCmp(matchName, sp->nsearchNamep);
774 && !cm_Is8Dot3(matchName)) {
776 cm_Gen8Dot3NameInt(dep->name, &dep->fid, matchName, NULL);
778 match = cm_NormStrCmpI(matchName, sp->nsearchNamep);
780 match = cm_NormStrCmp(matchName, sp->nsearchNamep);
781 looking_for_short_name = TRUE;
791 if (!sp->caseFold || looking_for_short_name) {
792 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
793 return CM_ERROR_STOPNOW;
797 * If we get here, we are doing a case-insensitive search, and we
798 * have found a match. Now we determine what kind of match it is:
799 * exact, lower-case, upper-case, or none of the above. This is done
800 * in order to choose among matches, if there are more than one.
803 /* Exact matches are the best. */
804 match = cm_NormStrCmp(matchName, sp->nsearchNamep);
807 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
808 return CM_ERROR_STOPNOW;
811 /* Lower-case matches are next. */
814 if (cm_NoneUpper(matchName)) {
819 /* Upper-case matches are next. */
822 if (cm_NoneLower(matchName)) {
827 /* General matches are last. */
833 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
837 /* read the contents of a mount point into the appropriate string.
838 * called with write locked scp, and returns with locked scp.
840 long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
844 if (scp->mountPointStringp[0])
847 #ifdef AFS_FREELANCE_CLIENT
848 /* File servers do not have data for freelance entries */
849 if (cm_freelanceEnabled &&
850 scp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
851 scp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
853 code = cm_FreelanceFetchMountPointString(scp);
855 #endif /* AFS_FREELANCE_CLIENT */
857 char temp[MOUNTPOINTLEN];
860 /* otherwise, we have to read it in */
861 offset.LowPart = offset.HighPart = 0;
862 code = cm_GetData(scp, &offset, temp, MOUNTPOINTLEN, userp, reqp);
867 * scp->length is the actual length of the mount point string.
868 * It is current because cm_GetData merged the most up to date
869 * status info into scp and has not dropped the rwlock since.
871 if (scp->length.LowPart > MOUNTPOINTLEN - 1)
872 return CM_ERROR_TOOBIG;
873 if (scp->length.LowPart == 0)
874 return CM_ERROR_INVAL;
876 /* convert the terminating dot to a NUL */
877 temp[scp->length.LowPart - 1] = 0;
878 memcpy(scp->mountPointStringp, temp, scp->length.LowPart);
885 /* called with a locked scp and chases the mount point, yielding outScpp.
886 * scp remains write locked, just for simplicity of describing the interface.
888 long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
889 cm_req_t *reqp, cm_scache_t **outScpp)
891 fschar_t *cellNamep = NULL;
892 fschar_t *volNamep = NULL;
896 cm_volume_t *volp = NULL;
905 if (scp->mountRootFid.cell != 0 && scp->mountRootGen >= cm_data.mountRootGen) {
906 tfid = scp->mountRootFid;
907 lock_ReleaseWrite(&scp->rw);
908 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
909 lock_ObtainWrite(&scp->rw);
913 /* parse the volume name */
914 mpNamep = scp->mountPointStringp;
916 return CM_ERROR_NOSUCHPATH;
917 mtType = *scp->mountPointStringp;
919 cp = cm_FsStrChr(mpNamep, _FS(':'));
921 /* cellular mount point */
922 cellNamep = (fschar_t *)malloc((cp - mpNamep) * sizeof(fschar_t));
923 cm_FsStrCpyN(cellNamep, cp - mpNamep, mpNamep + 1, cp - mpNamep - 1);
924 volNamep = cm_FsStrDup(cp+1);
926 /* now look up the cell */
927 lock_ReleaseWrite(&scp->rw);
928 cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE);
929 lock_ObtainWrite(&scp->rw);
932 volNamep = cm_FsStrDup(mpNamep + 1);
934 #ifdef AFS_FREELANCE_CLIENT
936 * Mount points in the Freelance cell should default
937 * to the workstation cell.
939 if (cm_freelanceEnabled &&
940 scp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
941 scp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
943 fschar_t rootCellName[256]="";
944 cm_GetRootCellName(rootCellName);
945 cellp = cm_GetCell(rootCellName, 0);
947 #endif /* AFS_FREELANCE_CLIENT */
948 cellp = cm_FindCellByID(scp->fid.cell, 0);
952 code = CM_ERROR_NOSUCHCELL;
956 vnLength = cm_FsStrLen(volNamep);
957 if (vnLength >= 8 && cm_FsStrCmp(volNamep + vnLength - 7, ".backup") == 0)
958 targetType = BACKVOL;
959 else if (vnLength >= 10
960 && cm_FsStrCmp(volNamep + vnLength - 9, ".readonly") == 0)
965 /* check for backups within backups */
966 if (targetType == BACKVOL
967 && (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
968 == CM_SCACHEFLAG_RO) {
969 code = CM_ERROR_NOSUCHVOLUME;
973 /* now we need to get the volume */
974 lock_ReleaseWrite(&scp->rw);
975 if (cm_VolNameIsID(volNamep)) {
976 code = cm_FindVolumeByID(cellp, atoi(volNamep), userp, reqp,
977 CM_GETVOL_FLAG_CREATE, &volp);
979 code = cm_FindVolumeByName(cellp, volNamep, userp, reqp,
980 CM_GETVOL_FLAG_CREATE, &volp);
982 lock_ObtainWrite(&scp->rw);
985 afs_uint32 cell, volume;
986 cm_vol_state_t *statep;
988 cell = cellp->cellID;
990 /* if the mt pt originates in a .backup volume (not a .readonly)
991 * and FollowBackupPath is active, and if there is a .backup
992 * volume for the target, then use the .backup of the target
993 * instead of the read-write.
995 if (cm_followBackupPath &&
996 volp->vol[BACKVOL].ID != 0 &&
997 (dscp->flags & (CM_SCACHEFLAG_RO|CM_SCACHEFLAG_PURERO)) == CM_SCACHEFLAG_RO &&
998 (targetType == RWVOL || targetType == ROVOL && volp->vol[ROVOL].ID == 0)
1000 targetType = BACKVOL;
1002 /* if the mt pt is in a read-only volume (not just a
1003 * backup), and if there is a read-only volume for the
1004 * target, and if this is a targetType '#' mount point, use
1005 * the read-only, otherwise use the one specified.
1007 else if (mtType == '#' && targetType == RWVOL &&
1008 (scp->flags & CM_SCACHEFLAG_PURERO) &&
1009 volp->vol[ROVOL].ID != 0) {
1013 lock_ObtainWrite(&volp->rw);
1014 statep = cm_VolumeStateByType(volp, targetType);
1015 volume = statep->ID;
1016 statep->dotdotFid = dscp->fid;
1017 lock_ReleaseWrite(&volp->rw);
1019 /* the rest of the fid is a magic number */
1020 cm_SetFid(&scp->mountRootFid, cell, volume, 1, 1);
1021 scp->mountRootGen = cm_data.mountRootGen;
1023 tfid = scp->mountRootFid;
1024 lock_ReleaseWrite(&scp->rw);
1025 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
1026 lock_ObtainWrite(&scp->rw);
1039 long cm_LookupInternal(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_user_t *userp,
1040 cm_req_t *reqp, cm_scache_t **outScpp)
1043 int dnlcHit = 1; /* did we hit in the dnlc? yes, we did */
1044 cm_scache_t *tscp = NULL;
1045 cm_scache_t *mountedScp;
1046 cm_lookupSearch_t rock;
1048 normchar_t *nnamep = NULL;
1049 fschar_t *fnamep = NULL;
1054 memset(&rock, 0, sizeof(rock));
1056 if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
1057 && cm_ClientStrCmp(cnamep, _C("..")) == 0) {
1058 if (dscp->dotdotFid.volume == 0)
1059 return CM_ERROR_NOSUCHVOLUME;
1060 rock.fid = dscp->dotdotFid;
1062 } else if (cm_ClientStrCmp(cnamep, _C(".")) == 0) {
1063 rock.fid = dscp->fid;
1067 nnamep = cm_ClientStringToNormStringAlloc(cnamep, -1, NULL);
1069 code = CM_ERROR_NOSUCHFILE;
1072 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
1074 code = CM_ERROR_NOSUCHFILE;
1079 if (flags & CM_FLAG_NOMOUNTCHASE) {
1080 /* In this case, we should go and call cm_Dir* functions
1081 directly since the following cm_ApplyDir() function will
1089 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ,
1090 CM_DIROP_FLAG_NONE, &dirop);
1093 code = cm_BPlusDirLookup(&dirop, nnamep, &rock.fid);
1098 code = cm_DirLookup(&dirop, fnamep, &rock.fid);
1100 cm_EndDirOp(&dirop);
1110 if (code == CM_ERROR_INEXACT_MATCH && (flags & CM_FLAG_CASEFOLD)) {
1117 code = CM_ERROR_BPLUS_NOMATCH;
1123 rock.fid.cell = dscp->fid.cell;
1124 rock.fid.volume = dscp->fid.volume;
1125 rock.searchNamep = fnamep;
1126 rock.nsearchNamep = nnamep;
1127 rock.caseFold = (flags & CM_FLAG_CASEFOLD);
1128 rock.hasTilde = ((cm_ClientStrChr(cnamep, '~') != NULL) ? 1 : 0);
1130 /* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
1131 code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
1132 (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
1134 /* code == 0 means we fell off the end of the dir, while stopnow means
1135 * that we stopped early, probably because we found the entry we're
1136 * looking for. Any other non-zero code is an error.
1138 if (code && code != CM_ERROR_STOPNOW && code != CM_ERROR_BPLUS_NOMATCH) {
1139 /* if the cm_scache_t we are searching in is not a directory
1140 * we must return path not found because the error
1141 * is to describe the final component not an intermediary
1143 if (code == CM_ERROR_NOTDIR) {
1144 if (flags & CM_FLAG_CHECKPATH)
1145 code = CM_ERROR_NOSUCHPATH;
1147 code = CM_ERROR_NOSUCHFILE;
1153 getroot = (dscp==cm_data.rootSCachep) ;
1155 if (!cm_freelanceEnabled || !getroot) {
1156 if (flags & CM_FLAG_CHECKPATH)
1157 code = CM_ERROR_NOSUCHPATH;
1159 code = CM_ERROR_NOSUCHFILE;
1162 else if (!cm_ClientStrChr(cnamep, '#') &&
1163 !cm_ClientStrChr(cnamep, '%') &&
1164 cm_ClientStrCmpI(cnamep, _C("srvsvc")) &&
1165 cm_ClientStrCmpI(cnamep, _C("wkssvc")) &&
1166 cm_ClientStrCmpI(cnamep, _C("ipc$")))
1168 /* nonexistent dir on freelance root, so add it */
1169 fschar_t fullname[CELL_MAXNAMELEN + 1] = "."; /* +1 so that when we skip the . the size is still CELL_MAXNAMELEN */
1173 osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %S",
1174 osi_LogSaveClientString(afsd_logp,cnamep));
1177 * There is an ugly behavior where a share name "foo" will be searched
1178 * for as "fo". If the searched for name differs by an already existing
1179 * symlink or mount point in the Freelance directory, do not add the
1180 * new value automatically.
1184 fnlen = strlen(fnamep);
1185 if ( fnamep[fnlen-1] == '.') {
1186 fnamep[fnlen-1] = '\0';
1191 if (cnamep[0] == '.') {
1192 if (cm_GetCell_Gen(&fnamep[1], &fullname[1], CM_FLAG_CREATE)) {
1194 code = cm_FreelanceAddMount(fullname, &fullname[1], "root.cell", 1, &rock.fid);
1195 if ( cm_FsStrCmpI(&fnamep[1], &fullname[1])) {
1197 * Do not permit symlinks that are one of:
1198 * . the cellname followed by a dot
1199 * . the cellname minus a single character
1200 * . a substring of the cellname that does not consist of full components
1202 if ( cm_strnicmp_utf8(&fnamep[1], fullname, (int)fnlen-1) == 0 &&
1203 (fnlen-1 == strlen(fullname)-1 || fullname[fnlen-1] != '.'))
1205 /* do not add; substitute fullname for the search */
1207 fnamep = malloc(strlen(fullname)+2);
1209 strncpy(&fnamep[1], fullname, strlen(fullname)+1);
1212 code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
1217 if (cm_GetCell_Gen(fnamep, fullname, CM_FLAG_CREATE)) {
1219 code = cm_FreelanceAddMount(fullname, fullname, "root.cell", 0, &rock.fid);
1220 if ( cm_FsStrCmpI(fnamep, fullname)) {
1222 * Do not permit symlinks that are one of:
1223 * . the cellname followed by a dot
1224 * . the cellname minus a single character
1225 * . a substring of the cellname that does not consist of full components
1227 if ( cm_strnicmp_utf8(fnamep, fullname, (int)fnlen-1) == 0 &&
1228 (fnlen == strlen(fullname)-1 || fullname[fnlen] != '.'))
1230 /* do not add; substitute fullname for the search */
1232 fnamep = strdup(fullname);
1236 code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
1245 nnamep = cm_FsStringToNormStringAlloc(fnamep, -1, NULL);
1249 if (!found || code) { /* add mount point failed, so give up */
1250 if (flags & CM_FLAG_CHECKPATH)
1251 code = CM_ERROR_NOSUCHPATH;
1253 code = CM_ERROR_NOSUCHFILE;
1256 tscp = NULL; /* to force call of cm_GetSCache */
1258 if (flags & CM_FLAG_CHECKPATH)
1259 code = CM_ERROR_NOSUCHPATH;
1261 code = CM_ERROR_NOSUCHFILE;
1267 if ( !tscp ) /* we did not find it in the dnlc */
1270 code = cm_GetSCache(&rock.fid, &tscp, userp, reqp);
1274 /* tscp is now held */
1276 lock_ObtainWrite(&tscp->rw);
1279 * Do not get status if we do not already have a callback.
1280 * The process of reading the mount point string will obtain status information
1281 * in a single RPC. No reason to add a second round trip.
1283 * If we do have a callback, use cm_SyncOp to get status in case the
1284 * current cm_user_t is not the same as the one that obtained the
1285 * mount point string contents.
1287 if (cm_HaveCallback(tscp)) {
1288 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1289 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1291 lock_ReleaseWrite(&tscp->rw);
1292 cm_ReleaseSCache(tscp);
1295 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1297 /* tscp is now locked */
1299 if (!(flags & CM_FLAG_NOMOUNTCHASE)
1300 && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
1301 /* mount points are funny: they have a volume name to mount
1304 code = cm_ReadMountPoint(tscp, userp, reqp);
1306 code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
1308 lock_ReleaseWrite(&tscp->rw);
1309 cm_ReleaseSCache(tscp);
1316 lock_ReleaseWrite(&tscp->rw);
1319 /* copy back pointer */
1322 /* insert scache in dnlc */
1323 if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
1324 /* lock the directory entry to prevent racing callback revokes */
1325 lock_ObtainRead(&dscp->rw);
1326 if ( dscp->cbServerp != NULL && dscp->cbExpires > 0 ) {
1327 /* TODO: reuse nnamep from above */
1330 nnamep = cm_ClientStringToNormStringAlloc(cnamep, -1, NULL);
1332 cm_dnlcEnter(dscp, nnamep, tscp);
1334 lock_ReleaseRead(&dscp->rw);
1351 int cm_ExpandSysName(cm_req_t * reqp, clientchar_t *inp, clientchar_t *outp, long outSizeCch, unsigned int index)
1356 int use_sysname64 = 0;
1358 if (cm_sysName64Count > 0 && reqp && (reqp->flags & CM_REQ_WOW64) && (reqp->flags & CM_REQ_SOURCE_REDIR))
1362 tp = cm_ClientStrRChr(inp, '@');
1364 return 0; /* no @sys */
1366 if (cm_ClientStrCmp(tp, _C("@sys")) != 0)
1367 return 0; /* no @sys */
1369 /* caller just wants to know if this is a valid @sys type of name */
1374 if (use_sysname64 && index >= cm_sysName64Count)
1378 if (index >= cm_sysNameCount)
1381 /* otherwise generate the properly expanded @sys name */
1382 prefixCount = (int)(tp - inp);
1384 cm_ClientStrCpyN(outp, outSizeCch, inp, prefixCount); /* copy out "a." from "a.@sys" */
1385 outp[prefixCount] = 0; /* null terminate the "a." */
1388 cm_ClientStrCat(outp, outSizeCch, cm_sysName64List[index]);
1391 cm_ClientStrCat(outp, outSizeCch, cm_sysNameList[index]);
1396 long cm_EvaluateVolumeReference(clientchar_t * namep, long flags, cm_user_t * userp,
1397 cm_req_t *reqp, cm_scache_t ** outScpp)
1399 afs_uint32 code = 0;
1400 fschar_t cellName[CELL_MAXNAMELEN];
1401 fschar_t volumeName[VL_MAXNAMELEN];
1405 fschar_t * fnamep = NULL;
1407 cm_cell_t * cellp = NULL;
1408 cm_volume_t * volp = NULL;
1412 int mountType = RWVOL;
1414 osi_Log1(afsd_logp, "cm_EvaluateVolumeReference for string [%S]",
1415 osi_LogSaveClientString(afsd_logp, namep));
1417 if (cm_ClientStrCmpNI(namep, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH) != 0) {
1418 goto _exit_invalid_path;
1421 /* namep is assumed to look like the following:
1423 @vol:<cellname>%<volume>\0
1425 @vol:<cellname>#<volume>\0
1429 fnamep = cm_ClientStringToFsStringAlloc(namep, -1, NULL);
1430 cp = fnamep + CM_PREFIX_VOL_CCH; /* cp points to cell name, hopefully */
1431 tp = cm_FsStrChr(cp, '%');
1433 tp = cm_FsStrChr(cp, '#');
1435 (len = tp - cp) == 0 ||
1436 len > CELL_MAXNAMELEN)
1437 goto _exit_invalid_path;
1438 cm_FsStrCpyN(cellName, lengthof(cellName), cp, len);
1443 cp = tp+1; /* cp now points to volume, supposedly */
1444 cm_FsStrCpy(volumeName, lengthof(volumeName), cp);
1446 /* OK, now we have the cell and the volume */
1447 osi_Log2(afsd_logp, " Found cell [%s] and volume [%s]",
1448 osi_LogSaveFsString(afsd_logp, cellName),
1449 osi_LogSaveFsString(afsd_logp, volumeName));
1451 cellp = cm_GetCell(cellName, CM_FLAG_CREATE);
1452 if (cellp == NULL) {
1453 goto _exit_invalid_path;
1456 len = cm_FsStrLen(volumeName);
1457 if (len >= 8 && cm_FsStrCmp(volumeName + len - 7, ".backup") == 0)
1459 else if (len >= 10 &&
1460 cm_FsStrCmp(volumeName + len - 9, ".readonly") == 0)
1465 if (cm_VolNameIsID(volumeName)) {
1466 code = cm_FindVolumeByID(cellp, atoi(volumeName), userp, reqp,
1467 CM_GETVOL_FLAG_CREATE, &volp);
1469 code = cm_FindVolumeByName(cellp, volumeName, userp, reqp,
1470 CM_GETVOL_FLAG_CREATE, &volp);
1476 if (volType == BACKVOL)
1477 volume = volp->vol[BACKVOL].ID;
1478 else if (volType == ROVOL ||
1479 (volType == RWVOL && mountType == ROVOL && volp->vol[ROVOL].ID != 0))
1480 volume = volp->vol[ROVOL].ID;
1482 volume = volp->vol[RWVOL].ID;
1484 cm_SetFid(&fid, cellp->cellID, volume, 1, 1);
1486 code = cm_GetSCache(&fid, outScpp, userp, reqp);
1499 if (flags & CM_FLAG_CHECKPATH)
1500 return CM_ERROR_NOSUCHPATH;
1502 return CM_ERROR_NOSUCHFILE;
1505 #ifdef DEBUG_REFCOUNT
1506 long cm_LookupDbg(cm_scache_t *dscp, clientchar_t *namep, long flags, cm_user_t *userp,
1507 cm_req_t *reqp, cm_scache_t **outScpp, char * file, long line)
1509 long cm_Lookup(cm_scache_t *dscp, clientchar_t *namep, long flags, cm_user_t *userp,
1510 cm_req_t *reqp, cm_scache_t **outScpp)
1514 clientchar_t tname[AFSPATHMAX];
1515 int sysNameIndex = 0;
1516 cm_scache_t *scp = NULL;
1518 #ifdef DEBUG_REFCOUNT
1519 afsi_log("%s:%d cm_Lookup dscp 0x%p ref %d", file, line, dscp, dscp->refCount, file, line);
1520 osi_Log2(afsd_logp, "cm_Lookup dscp 0x%p ref %d", dscp, dscp->refCount);
1523 if ( cm_ClientStrCmpI(namep,_C(SMB_IOCTL_FILENAME_NOSLASH)) == 0 ) {
1524 if (flags & CM_FLAG_CHECKPATH)
1525 return CM_ERROR_NOSUCHPATH;
1527 return CM_ERROR_NOSUCHFILE;
1530 if (dscp == cm_data.rootSCachep &&
1531 cm_ClientStrCmpNI(namep, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH) == 0) {
1532 return cm_EvaluateVolumeReference(namep, flags, userp, reqp, outScpp);
1535 if (cm_ExpandSysName(reqp, namep, NULL, 0, 0) > 0) {
1536 for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
1537 code = cm_ExpandSysName(reqp, namep, tname, lengthof(tname), sysNameIndex);
1539 code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
1540 #ifdef DEBUG_REFCOUNT
1541 afsi_log("%s:%d cm_LookupInternal (1) code 0x%x dscp 0x%p ref %d scp 0x%p ref %d", file, line, code, dscp, dscp->refCount, scp, scp ? scp->refCount : 0);
1542 osi_Log3(afsd_logp, "cm_LookupInternal (1) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1550 cm_ReleaseSCache(scp);
1554 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1555 #ifdef DEBUG_REFCOUNT
1556 afsi_log("%s:%d cm_LookupInternal (2) code 0x%x dscp 0x%p ref %d scp 0x%p ref %d", file, line, code, dscp, dscp->refCount, scp, scp ? scp->refCount : 0);
1557 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1564 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1565 #ifdef DEBUG_REFCOUNT
1566 afsi_log("%s:%d cm_LookupInternal (2) code 0x%x dscp 0x%p ref %d scp 0x%p ref %d", file, line, code, dscp, dscp->refCount, scp, scp ? scp->refCount : 0);
1567 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1573 /* None of the possible sysName expansions could be found */
1574 if (flags & CM_FLAG_CHECKPATH)
1575 return CM_ERROR_NOSUCHPATH;
1577 return CM_ERROR_NOSUCHFILE;
1580 /*! \brief Unlink a file name
1582 Encapsulates a call to RXAFS_RemoveFile().
1584 \param[in] dscp cm_scache_t pointing at the directory containing the
1585 name to be unlinked.
1587 \param[in] fnamep Original name to be unlinked. This is the
1588 name that will be passed into the RXAFS_RemoveFile() call.
1589 This parameter is optional. If not provided, the value will
1592 \param[in] came Client name to be unlinked. This name will be used
1593 to update the local directory caches.
1595 \param[in] userp cm_user_t for the request.
1597 \param[in] reqp Request tracker.
1600 long cm_Unlink(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t * cnamep,
1601 cm_user_t *userp, cm_req_t *reqp)
1607 AFSFetchStatus newDirStatus;
1609 struct rx_connection * rxconnp;
1611 cm_scache_t *scp = NULL;
1612 int free_fnamep = FALSE;
1614 memset(&volSync, 0, sizeof(volSync));
1616 if (fnamep == NULL) {
1619 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ,
1620 CM_DIROP_FLAG_NONE, &dirop);
1622 code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
1625 cm_EndDirOp(&dirop);
1632 #ifdef AFS_FREELANCE_CLIENT
1633 if (cm_freelanceEnabled && dscp == cm_data.rootSCachep) {
1634 /* deleting a mount point from the root dir. */
1635 code = cm_FreelanceRemoveMount(fnamep);
1640 code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
1644 /* Check for RO volume */
1645 if (dscp->flags & CM_SCACHEFLAG_RO) {
1646 code = CM_ERROR_READONLY;
1650 /* make sure we don't screw up the dir status during the merge */
1651 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE,
1652 CM_DIROP_FLAG_NONE, &dirop);
1654 lock_ObtainWrite(&dscp->rw);
1655 sflags = CM_SCACHESYNC_STOREDATA;
1656 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1657 lock_ReleaseWrite(&dscp->rw);
1659 cm_EndDirOp(&dirop);
1664 afsFid.Volume = dscp->fid.volume;
1665 afsFid.Vnode = dscp->fid.vnode;
1666 afsFid.Unique = dscp->fid.unique;
1668 osi_Log1(afsd_logp, "CALL RemoveFile scp 0x%p", dscp);
1670 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
1674 rxconnp = cm_GetRxConn(connp);
1675 code = RXAFS_RemoveFile(rxconnp, &afsFid, fnamep,
1676 &newDirStatus, &volSync);
1677 rx_PutConnection(rxconnp);
1679 } while (cm_Analyze(connp, userp, reqp, &dscp->fid, &volSync, NULL, NULL, code));
1680 code = cm_MapRPCError(code, reqp);
1683 osi_Log1(afsd_logp, "CALL RemoveFile FAILURE, code 0x%x", code);
1685 osi_Log0(afsd_logp, "CALL RemoveFile SUCCESS");
1688 lock_ObtainWrite(&dirop.scp->dirlock);
1689 dirop.lockType = CM_DIRLOCK_WRITE;
1691 lock_ObtainWrite(&dscp->rw);
1692 cm_dnlcRemove(dscp, cnamep);
1694 cm_MergeStatus(NULL, dscp, &newDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
1695 if (RDR_Initialized &&
1696 scp->fileType != CM_SCACHETYPE_FILE && scp->fileType != CM_SCACHETYPE_DIRECTORY)
1697 RDR_InvalidateObject(dscp->fid.cell, dscp->fid.volume, dscp->fid.vnode,
1698 dscp->fid.unique, dscp->fid.hash,
1699 dscp->fileType, AFS_INVALIDATE_DATA_VERSION);
1700 } else if (code == CM_ERROR_NOSUCHFILE) {
1701 /* windows would not have allowed the request to delete the file
1702 * if it did not believe the file existed. therefore, we must
1703 * have an inconsistent view of the world.
1705 dscp->cbServerp = NULL;
1707 cm_SyncOpDone(dscp, NULL, sflags);
1708 lock_ReleaseWrite(&dscp->rw);
1710 if (code == 0 && cm_CheckDirOpForSingleChange(&dirop) && cnamep) {
1711 cm_DirDeleteEntry(&dirop, fnamep);
1713 cm_BPlusDirDeleteEntry(&dirop, cnamep);
1716 cm_EndDirOp(&dirop);
1719 cm_ReleaseSCache(scp);
1721 lock_ObtainWrite(&scp->rw);
1722 if (--scp->linkCount == 0) {
1723 scp->flags |= CM_SCACHEFLAG_DELETED;
1724 lock_ObtainWrite(&cm_scacheLock);
1725 cm_AdjustScacheLRU(scp);
1726 cm_RemoveSCacheFromHashTable(scp);
1727 lock_ReleaseWrite(&cm_scacheLock);
1729 cm_DiscardSCache(scp);
1730 lock_ReleaseWrite(&scp->rw);
1731 if (RDR_Initialized && !(reqp->flags & CM_REQ_SOURCE_REDIR) &&
1732 !RDR_InvalidateObject(scp->fid.cell, scp->fid.volume, scp->fid.vnode,
1733 scp->fid.unique, scp->fid.hash,
1734 scp->fileType, AFS_INVALIDATE_DELETED))
1735 buf_ClearRDRFlag(scp, "unlink");
1746 /* called with a write locked vnode, and fills in the link info.
1747 * returns this the vnode still write locked.
1749 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1753 lock_AssertWrite(&linkScp->rw);
1754 if (!linkScp->mountPointStringp[0]) {
1756 #ifdef AFS_FREELANCE_CLIENT
1757 /* File servers do not have data for freelance entries */
1758 if (cm_freelanceEnabled &&
1759 linkScp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
1760 linkScp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
1762 code = cm_FreelanceFetchMountPointString(linkScp);
1764 #endif /* AFS_FREELANCE_CLIENT */
1766 char temp[MOUNTPOINTLEN];
1769 /* read the link data from the file server */
1770 offset.LowPart = offset.HighPart = 0;
1771 code = cm_GetData(linkScp, &offset, temp, MOUNTPOINTLEN, userp, reqp);
1776 * linkScp->length is the actual length of the symlink target string.
1777 * It is current because cm_GetData merged the most up to date
1778 * status info into scp and has not dropped the rwlock since.
1780 if (linkScp->length.LowPart > MOUNTPOINTLEN - 1)
1781 return CM_ERROR_TOOBIG;
1782 if (linkScp->length.LowPart == 0)
1783 return CM_ERROR_INVAL;
1785 /* make sure we are NUL terminated */
1786 temp[linkScp->length.LowPart] = 0;
1787 memcpy(linkScp->mountPointStringp, temp, linkScp->length.LowPart + 1);
1790 if ( !strnicmp(linkScp->mountPointStringp, "msdfs:", strlen("msdfs:")) )
1791 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1793 } /* don't have symlink contents cached */
1798 /* called with a held vnode and a path suffix, with the held vnode being a
1799 * symbolic link. Our goal is to generate a new path to interpret, and return
1800 * this new path in newSpaceBufferp. If the new vnode is relative to a dir
1801 * other than the directory containing the symbolic link, then the new root is
1802 * returned in *newRootScpp, otherwise a null is returned there.
1804 long cm_AssembleLink(cm_scache_t *linkScp, fschar_t *pathSuffixp,
1805 cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1806 cm_user_t *userp, cm_req_t *reqp)
1813 *newRootScpp = NULL;
1814 *newSpaceBufferp = NULL;
1816 lock_ObtainWrite(&linkScp->rw);
1818 * Do not get status if we do not already have a callback.
1819 * The process of reading the symlink string will obtain status information
1820 * in a single RPC. No reason to add a second round trip.
1822 * If we do have a callback, use cm_SyncOp to get status in case the
1823 * current cm_user_t is not the same as the one that obtained the
1824 * symlink string contents.
1826 if (cm_HaveCallback(linkScp)) {
1827 code = cm_SyncOp(linkScp, NULL, userp, reqp, 0,
1828 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1830 lock_ReleaseWrite(&linkScp->rw);
1831 cm_ReleaseSCache(linkScp);
1834 cm_SyncOpDone(linkScp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1836 code = cm_HandleLink(linkScp, userp, reqp);
1840 /* if we may overflow the buffer, bail out; buffer is signficantly
1841 * bigger than max path length, so we don't really have to worry about
1842 * being a little conservative here.
1844 if (cm_FsStrLen(linkScp->mountPointStringp) + cm_FsStrLen(pathSuffixp) + 2
1845 >= CM_UTILS_SPACESIZE) {
1846 code = CM_ERROR_TOOBIG;
1850 tsp = cm_GetSpace();
1851 linkp = linkScp->mountPointStringp;
1852 if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1853 if (strlen(linkp) > cm_mountRootLen)
1854 StringCbCopyA((char *) tsp->data, sizeof(tsp->data), linkp+cm_mountRootLen+1);
1857 *newRootScpp = cm_RootSCachep(userp, reqp);
1858 cm_HoldSCache(*newRootScpp);
1859 } else if (linkp[0] == '\\' && linkp[1] == '\\') {
1860 if (!strnicmp(&linkp[2], cm_NetbiosName, (len = (long)strlen(cm_NetbiosName))))
1862 char * p = &linkp[len + 3];
1863 if (strnicmp(p, "all", 3) == 0)
1866 StringCbCopyA(tsp->data, sizeof(tsp->data), p);
1867 for (p = tsp->data; *p; p++) {
1871 *newRootScpp = cm_RootSCachep(userp, reqp);
1872 cm_HoldSCache(*newRootScpp);
1874 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1875 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1876 code = CM_ERROR_PATH_NOT_COVERED;
1878 } else if ( linkScp->fileType == CM_SCACHETYPE_DFSLINK ||
1879 !strnicmp(linkp, "msdfs:", (len = (long)strlen("msdfs:"))) ) {
1880 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1881 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1882 code = CM_ERROR_PATH_NOT_COVERED;
1883 } else if (*linkp == '\\' || *linkp == '/') {
1885 /* formerly, this was considered to be from the AFS root,
1886 * but this seems to create problems. instead, we will just
1887 * reject the link */
1888 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp+1);
1889 *newRootScpp = cm_RootSCachep(userp, reqp);
1890 cm_HoldSCache(*newRootScpp);
1892 /* we still copy the link data into the response so that
1893 * the user can see what the link points to
1895 linkScp->fileType = CM_SCACHETYPE_INVALID;
1896 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1897 code = CM_ERROR_NOSUCHPATH;
1900 /* a relative link */
1901 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1903 if (pathSuffixp[0] != 0) { /* if suffix string is non-null */
1904 StringCchCatA(tsp->data,lengthof(tsp->data), "\\");
1905 StringCchCatA(tsp->data,lengthof(tsp->data), pathSuffixp);
1909 clientchar_t * cpath = cm_FsStringToClientStringAlloc(tsp->data, -1, NULL);
1910 if (cpath != NULL) {
1911 cm_ClientStrCpy(tsp->wdata, lengthof(tsp->wdata), cpath);
1913 *newSpaceBufferp = tsp;
1915 code = CM_ERROR_NOSUCHPATH;
1922 if (code == CM_ERROR_PATH_NOT_COVERED && reqp->tidPathp && reqp->relPathp) {
1923 cm_VolStatus_Notify_DFS_Mapping(linkScp, reqp->tidPathp, reqp->relPathp);
1928 lock_ReleaseWrite(&linkScp->rw);
1931 #ifdef DEBUG_REFCOUNT
1932 long cm_NameIDbg(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
1933 cm_user_t *userp, clientchar_t *tidPathp, cm_req_t *reqp,
1934 cm_scache_t **outScpp,
1935 char * file, long line)
1937 long cm_NameI(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
1938 cm_user_t *userp, clientchar_t *tidPathp,
1939 cm_req_t *reqp, cm_scache_t **outScpp)
1943 clientchar_t *tp; /* ptr moving through input buffer */
1944 clientchar_t tc; /* temp char */
1945 int haveComponent; /* has new component started? */
1946 clientchar_t component[AFSPATHMAX]; /* this is the new component */
1947 clientchar_t *cp; /* component name being assembled */
1948 cm_scache_t *tscp; /* current location in the hierarchy */
1949 cm_scache_t *nscp; /* next dude down */
1950 cm_scache_t *dirScp; /* last dir we searched */
1951 cm_scache_t *linkScp; /* new root for the symlink we just
1953 cm_space_t *psp; /* space for current path, if we've hit
1955 cm_space_t *tempsp; /* temp vbl */
1956 clientchar_t *restp; /* rest of the pathname to interpret */
1957 int symlinkCount; /* count of # of symlinks traversed */
1958 int extraFlag; /* avoid chasing mt pts for dir cmd */
1959 int phase = 1; /* 1 = tidPathp, 2 = pathp */
1960 #define MAX_FID_COUNT 512
1961 cm_fid_t fids[MAX_FID_COUNT]; /* array of fids processed in this path walk */
1962 int fid_count = 0; /* number of fids processed in this path walk */
1967 #ifdef DEBUG_REFCOUNT
1968 afsi_log("%s:%d cm_NameI rootscp 0x%p ref %d", file, line, rootSCachep, rootSCachep->refCount);
1969 osi_Log4(afsd_logp,"cm_NameI rootscp 0x%p path %S tidpath %S flags 0x%x",
1970 rootSCachep, pathp ? pathp : L"<NULL>", tidPathp ? tidPathp : L"<NULL>",
1985 cm_HoldSCache(tscp);
1993 /* map Unix slashes into DOS ones so we can interpret Unix
1999 if (!haveComponent) {
2002 } else if (tc == 0) {
2016 /* we have a component here */
2017 if (tc == 0 || tc == '\\') {
2018 /* end of the component; we're at the last
2019 * component if tc == 0. However, if the last
2020 * is a symlink, we have more to do.
2022 *cp++ = 0; /* add null termination */
2024 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
2025 extraFlag = CM_FLAG_NOMOUNTCHASE;
2026 code = cm_Lookup(tscp, component,
2028 userp, reqp, &nscp);
2031 if (!cm_ClientStrCmp(component,_C("..")) ||
2032 !cm_ClientStrCmp(component,_C("."))) {
2034 * roll back the fid list until we find the
2035 * fid that matches where we are now. Its not
2036 * necessarily one or two fids because they
2037 * might have been symlinks or mount points or
2038 * both that were crossed.
2040 for ( i=fid_count-1; i>=0; i--) {
2041 if (!cm_FidCmp(&nscp->fid, &fids[i]))
2046 /* add the new fid to the list */
2047 if (fid_count == MAX_FID_COUNT) {
2048 code = CM_ERROR_TOO_MANY_SYMLINKS;
2049 cm_ReleaseSCache(nscp);
2053 fids[fid_count++] = nscp->fid;
2058 cm_ReleaseSCache(tscp);
2060 cm_ReleaseSCache(dirScp);
2063 if ((code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH) &&
2064 tscp->fileType == CM_SCACHETYPE_SYMLINK) {
2065 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_NOSUCHPATH");
2066 return CM_ERROR_NOSUCHPATH;
2068 osi_Log1(afsd_logp,"cm_NameI code 0x%x", code);
2073 haveComponent = 0; /* component done */
2075 cm_ReleaseSCache(dirScp);
2076 dirScp = tscp; /* for some symlinks */
2077 tscp = nscp; /* already held */
2079 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
2082 cm_ReleaseSCache(dirScp);
2088 /* now, if tscp is a symlink, we should follow it and
2089 * assemble the path again.
2091 lock_ObtainWrite(&tscp->rw);
2092 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
2093 CM_SCACHESYNC_GETSTATUS
2094 | CM_SCACHESYNC_NEEDCALLBACK);
2096 lock_ReleaseWrite(&tscp->rw);
2097 cm_ReleaseSCache(tscp);
2100 cm_ReleaseSCache(dirScp);
2105 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2107 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
2108 /* this is a symlink; assemble a new buffer */
2109 lock_ReleaseWrite(&tscp->rw);
2110 if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
2111 cm_ReleaseSCache(tscp);
2114 cm_ReleaseSCache(dirScp);
2119 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_TOO_MANY_SYMLINKS");
2120 return CM_ERROR_TOO_MANY_SYMLINKS;
2130 /* TODO: make this better */
2131 frestp = cm_ClientStringToFsStringAlloc(restp, -1, NULL);
2132 code = cm_AssembleLink(tscp, frestp, &linkScp, &tempsp, userp, reqp);
2136 if (code == 0 && linkScp != NULL) {
2137 if (linkScp == cm_data.rootSCachep) {
2141 for ( i=0; i<fid_count; i++) {
2142 if ( !cm_FidCmp(&linkScp->fid, &fids[i]) ) {
2143 code = CM_ERROR_TOO_MANY_SYMLINKS;
2144 cm_ReleaseSCache(linkScp);
2150 if (i == fid_count && fid_count < MAX_FID_COUNT) {
2151 fids[fid_count++] = linkScp->fid;
2156 /* something went wrong */
2157 cm_ReleaseSCache(tscp);
2160 cm_ReleaseSCache(dirScp);
2166 /* otherwise, tempsp has the new path,
2167 * and linkScp is the new root from
2168 * which to interpret that path.
2169 * Continue with the namei processing,
2170 * also doing the bookkeeping for the
2171 * space allocation and tracking the
2172 * vnode reference counts.
2178 cm_ReleaseSCache(tscp);
2183 * now, if linkScp is null, that's
2184 * AssembleLink's way of telling us that
2185 * the sym link is relative to the dir
2186 * containing the link. We have a ref
2187 * to it in dirScp, and we hold it now
2188 * and reuse it as the new spot in the
2196 /* not a symlink, we may be done */
2197 lock_ReleaseWrite(&tscp->rw);
2205 cm_ReleaseSCache(dirScp);
2213 cm_ReleaseSCache(dirScp);
2216 } /* end of a component */
2219 } /* we have a component */
2220 } /* big while loop over all components */
2224 cm_ReleaseSCache(dirScp);
2230 cm_ReleaseSCache(tscp);
2232 #ifdef DEBUG_REFCOUNT
2233 afsi_log("%s:%d cm_NameI code 0x%x outScpp 0x%p ref %d", file, line, code, *outScpp, (*outScpp) ? (*outScpp)->refCount : 0);
2235 osi_Log2(afsd_logp,"cm_NameI code 0x%x outScpp 0x%p", code, *outScpp);
2239 /* called with a dir, and a vnode within the dir that happens to be a symlink.
2240 * We chase the link, and return a held pointer to the target, if it exists,
2241 * in *outScpp. If we succeed, we return 0, otherwise we return an error code
2242 * and do not hold or return a target vnode.
2244 * This is very similar to calling cm_NameI with the last component of a name,
2245 * which happens to be a symlink, except that we've already passed by the name.
2247 * This function is typically called by the directory listing functions, which
2248 * encounter symlinks but need to return the proper file length so programs
2249 * like "more" work properly when they make use of the attributes retrieved from
2252 * The input vnode should not be locked when this function is called.
2254 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
2255 cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
2259 cm_scache_t *newRootScp;
2263 osi_Log1(afsd_logp, "Evaluating symlink scp 0x%p", linkScp);
2265 code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
2269 /* now, if newRootScp is NULL, we're really being told that the symlink
2270 * is relative to the current directory (dscp).
2272 if (newRootScp == NULL) {
2274 cm_HoldSCache(dscp);
2277 code = cm_NameI(newRootScp, spacep->wdata,
2278 CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
2279 userp, NULL, reqp, outScpp);
2281 if (code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH)
2282 code = CM_ERROR_NOSUCHPATH;
2284 /* this stuff is allocated no matter what happened on the namei call,
2286 cm_FreeSpace(spacep);
2287 cm_ReleaseSCache(newRootScp);
2289 if (linkScp == *outScpp) {
2290 cm_ReleaseSCache(*outScpp);
2292 code = CM_ERROR_NOSUCHPATH;
2298 /* for a given entry, make sure that it isn't in the stat cache, and then
2299 * add it to the list of file IDs to be obtained.
2301 * Don't bother adding it if we already have a vnode. Note that the dir
2302 * is locked, so we have to be careful checking the vnode we're thinking of
2303 * processing, to avoid deadlocks.
2305 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
2316 /* Don't overflow bsp. */
2317 if (bsp->counter >= CM_BULKMAX)
2318 return CM_ERROR_STOPNOW;
2320 thyper.LowPart = cm_data.buf_blockSize;
2321 thyper.HighPart = 0;
2322 thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
2324 /* thyper is now the first byte past the end of the record we're
2325 * interested in, and bsp->bufOffset is the first byte of the record
2326 * we're interested in.
2327 * Skip data in the others.
2330 if (LargeIntegerLessThan(*offp, bsp->bufOffset))
2332 if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
2333 return CM_ERROR_STOPNOW;
2334 if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
2337 cm_SetFid(&tfid, scp->fid.cell, scp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
2338 tscp = cm_FindSCache(&tfid);
2340 if (lock_TryWrite(&tscp->rw)) {
2341 /* we have an entry that we can look at */
2342 if (!(tscp->flags & CM_SCACHEFLAG_EACCESS) && cm_HaveCallback(tscp)) {
2343 /* we have a callback on it. Don't bother
2344 * fetching this stat entry, since we're happy
2345 * with the info we have.
2347 lock_ReleaseWrite(&tscp->rw);
2348 cm_ReleaseSCache(tscp);
2351 lock_ReleaseWrite(&tscp->rw);
2353 cm_ReleaseSCache(tscp);
2356 #ifdef AFS_FREELANCE_CLIENT
2357 // yj: if this is a mountpoint under root.afs then we don't want it
2358 // to be bulkstat-ed, instead, we call getSCache directly and under
2359 // getSCache, it is handled specially.
2360 if ( cm_freelanceEnabled &&
2361 tfid.cell==AFS_FAKE_ROOT_CELL_ID &&
2362 tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
2363 !(tfid.vnode==0x1 && tfid.unique==0x1) )
2365 osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
2366 return cm_GetSCache(&tfid, &tscp, NULL, NULL);
2368 #endif /* AFS_FREELANCE_CLIENT */
2371 bsp->fids[i].Volume = scp->fid.volume;
2372 bsp->fids[i].Vnode = tfid.vnode;
2373 bsp->fids[i].Unique = tfid.unique;
2378 cm_TryBulkStatRPC(cm_scache_t *dscp, cm_bulkStat_t *bbp, cm_user_t *userp, cm_req_t *reqp)
2381 AFSCBFids fidStruct;
2382 AFSBulkStats statStruct;
2384 AFSCBs callbackStruct;
2387 cm_callbackRequest_t cbReq;
2393 struct rx_connection * rxconnp;
2394 int inlinebulk; /* Did we use InlineBulkStatus RPC or not? */
2396 memset(&volSync, 0, sizeof(volSync));
2398 /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
2399 * make the calls to create the entries. Handle AFSCBMAX files at a
2402 for (filex = 0; filex < bbp->counter; filex += filesThisCall) {
2403 filesThisCall = bbp->counter - filex;
2404 if (filesThisCall > AFSCBMAX)
2405 filesThisCall = AFSCBMAX;
2407 fidStruct.AFSCBFids_len = filesThisCall;
2408 fidStruct.AFSCBFids_val = &bbp->fids[filex];
2409 statStruct.AFSBulkStats_len = filesThisCall;
2410 statStruct.AFSBulkStats_val = &bbp->stats[filex];
2411 callbackStruct.AFSCBs_len = filesThisCall;
2412 callbackStruct.AFSCBs_val = &bbp->callbacks[filex];
2413 cm_StartCallbackGrantingCall(NULL, &cbReq);
2414 osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
2417 * Whenever cm_Analyze is called for a RXAFS_ RPC there must
2418 * be a FID provided. However, the error code from RXAFS_BulkStatus
2419 * or RXAFS_InlinkBulkStatus does not apply to any FID. Therefore,
2420 * we generate an invalid FID to match with the RPC error.
2422 cm_SetFid(&tfid, dscp->fid.cell, dscp->fid.volume, 0, 0);
2427 code = cm_ConnFromFID(&tfid, userp, reqp, &connp);
2431 rxconnp = cm_GetRxConn(connp);
2432 if (!(connp->serverp->flags & CM_SERVERFLAG_NOINLINEBULK)) {
2433 code = RXAFS_InlineBulkStatus(rxconnp, &fidStruct,
2434 &statStruct, &callbackStruct, &volSync);
2435 if (code == RXGEN_OPCODE) {
2436 cm_SetServerNoInlineBulk(connp->serverp, 0);
2442 code = RXAFS_BulkStatus(rxconnp, &fidStruct,
2443 &statStruct, &callbackStruct, &volSync);
2445 rx_PutConnection(rxconnp);
2448 * If InlineBulk RPC was called and it succeeded,
2449 * then pull out the return code from the status info
2450 * and use it for cm_Analyze so that we can failover to other
2451 * .readonly volume instances. But only do it for errors that
2452 * are volume global.
2454 if (inlinebulk && code == 0 && (&bbp->stats[0])->errorCode) {
2455 osi_Log1(afsd_logp, "cm_TryBulkStat inline-bulk stat error: %d",
2456 (&bbp->stats[0])->errorCode);
2457 switch ((&bbp->stats[0])->errorCode) {
2466 code = (&bbp->stats[0])->errorCode;
2469 /* Rx and Rxkad errors are volume global */
2470 if ( (&bbp->stats[0])->errorCode >= -64 && (&bbp->stats[0])->errorCode < 0 ||
2471 (&bbp->stats[0])->errorCode >= ERROR_TABLE_BASE_RXK && (&bbp->stats[0])->errorCode < ERROR_TABLE_BASE_RXK + 256)
2472 code = (&bbp->stats[0])->errorCode;
2475 } while (cm_Analyze(connp, userp, reqp, &tfid, &volSync, NULL, &cbReq, code));
2476 code = cm_MapRPCError(code, reqp);
2479 * might as well quit on an error, since we're not going to do
2480 * much better on the next immediate call, either.
2483 osi_Log2(afsd_logp, "CALL %sBulkStatus FAILURE code 0x%x",
2484 inlinebulk ? "Inline" : "", code);
2485 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2490 * The bulk RPC has succeeded or at least not failed with a
2491 * volume global error result. For items that have inlineBulk
2492 * errors we must call cm_Analyze in order to perform required
2493 * logging of errors.
2495 * If the RPC was not inline bulk or the entry either has no error
2496 * the status must be merged.
2498 osi_Log1(afsd_logp, "CALL %sBulkStatus SUCCESS", inlinebulk ? "Inline" : "");
2500 for (i = 0; i<filesThisCall; i++) {
2502 cm_SetFid(&tfid, dscp->fid.cell, bbp->fids[j].Volume, bbp->fids[j].Vnode, bbp->fids[j].Unique);
2504 if (inlinebulk && (&bbp->stats[j])->errorCode) {
2505 cm_req_t treq = *reqp;
2506 cm_Analyze(NULL, userp, &treq, &tfid, &volSync, NULL, &cbReq, (&bbp->stats[j])->errorCode);
2508 code = cm_GetSCache(&tfid, &scp, userp, reqp);
2513 * otherwise, if this entry has no callback info,
2514 * merge in this. If there is existing callback info
2515 * we skip the merge because the existing data must be
2516 * current (we have a callback) and the response from
2517 * a non-inline bulk rpc might actually be wrong.
2519 * now, we have to be extra paranoid on merging in this
2520 * information, since we didn't use cm_SyncOp before
2521 * starting the fetch to make sure that no bad races
2522 * were occurring. Specifically, we need to make sure
2523 * we don't obliterate any newer information in the
2524 * vnode than have here.
2526 * Right now, be pretty conservative: if there's a
2527 * callback or a pending call, skip it.
2528 * However, if the prior attempt to obtain status
2529 * was refused access or the volume is .readonly,
2530 * take the data in any case since we have nothing
2531 * better for the in flight directory enumeration that
2532 * resulted in this function being called.
2534 lock_ObtainRead(&scp->rw);
2535 if ((scp->cbServerp == NULL &&
2536 !(scp->flags & (CM_SCACHEFLAG_FETCHING | CM_SCACHEFLAG_STORING | CM_SCACHEFLAG_SIZESTORING))) ||
2537 (scp->flags & CM_SCACHEFLAG_PURERO) ||
2538 (scp->flags & CM_SCACHEFLAG_EACCESS))
2540 lock_ConvertRToW(&scp->rw);
2541 cm_EndCallbackGrantingCall(scp, &cbReq,
2544 CM_CALLBACK_MAINTAINCOUNT);
2545 cm_MergeStatus(dscp, scp, &bbp->stats[j], &volSync, userp, reqp, 0);
2546 lock_ReleaseWrite(&scp->rw);
2548 lock_ReleaseRead(&scp->rw);
2550 cm_ReleaseSCache(scp);
2552 } /* all files in the response */
2553 /* now tell it to drop the count,
2554 * after doing the vnode processing above */
2555 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2556 } /* while there are still more files to process */
2561 /* called with a write locked scp and a pointer to a buffer. Make bulk stat
2562 * calls on all undeleted files in the page of the directory specified.
2565 cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
2571 osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%p", dscp);
2573 /* should be on a buffer boundary */
2574 osi_assertx((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0, "invalid offset");
2576 bbp = malloc(sizeof(cm_bulkStat_t));
2577 memset(bbp, 0, sizeof(cm_bulkStat_t));
2578 bbp->bufOffset = *offsetp;
2580 lock_ReleaseWrite(&dscp->rw);
2581 /* first, assemble the file IDs we need to stat */
2582 code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) bbp, offsetp, userp, reqp, NULL);
2584 /* if we failed, bail out early */
2585 if (code && code != CM_ERROR_STOPNOW) {
2587 lock_ObtainWrite(&dscp->rw);
2591 code = cm_TryBulkStatRPC(dscp, bbp, userp, reqp);
2592 osi_Log1(afsd_logp, "END cm_TryBulkStat code 0x%x", code);
2594 lock_ObtainWrite(&dscp->rw);
2599 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
2603 /* initialize store back mask as inexpensive local variable */
2605 memset(statusp, 0, sizeof(AFSStoreStatus));
2607 /* copy out queued info from scache first, if scp passed in */
2609 if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
2610 statusp->ClientModTime = scp->clientModTime;
2611 mask |= AFS_SETMODTIME;
2612 scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
2617 /* now add in our locally generated request */
2618 if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
2619 statusp->ClientModTime = attrp->clientModTime;
2620 mask |= AFS_SETMODTIME;
2622 if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
2623 statusp->UnixModeBits = attrp->unixModeBits;
2624 mask |= AFS_SETMODE;
2626 if (attrp->mask & CM_ATTRMASK_OWNER) {
2627 statusp->Owner = attrp->owner;
2628 mask |= AFS_SETOWNER;
2630 if (attrp->mask & CM_ATTRMASK_GROUP) {
2631 statusp->Group = attrp->group;
2632 mask |= AFS_SETGROUP;
2635 statusp->Mask = mask;
2638 /* set the file size, and make sure that all relevant buffers have been
2639 * truncated. Ensure that any partially truncated buffers have been zeroed
2640 * to the end of the buffer.
2642 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
2648 /* start by locking out buffer creation */
2649 lock_ObtainWrite(&scp->bufCreateLock);
2651 /* verify that this is a file, not a dir or a symlink */
2652 lock_ObtainWrite(&scp->rw);
2653 code = cm_SyncOp(scp, NULL, userp, reqp, 0,
2654 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2657 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2659 if (scp->fileType != CM_SCACHETYPE_FILE) {
2660 code = CM_ERROR_ISDIR;
2665 if (LargeIntegerLessThan(*sizep, scp->length))
2670 lock_ReleaseWrite(&scp->rw);
2672 /* can't hold scp->rw lock here, since we may wait for a storeback to
2673 * finish if the buffer package is cleaning a buffer by storing it to
2677 buf_Truncate(scp, userp, reqp, sizep);
2679 /* now ensure that file length is short enough, and update truncPos */
2680 lock_ObtainWrite(&scp->rw);
2682 /* make sure we have a callback (so we have the right value for the
2683 * length), and wait for it to be safe to do a truncate.
2685 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
2686 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2687 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2689 /* If we only have 'i' bits, then we should still be able to set
2690 the size of a file we created. */
2691 if (code == CM_ERROR_NOACCESS && scp->creator == userp) {
2692 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
2693 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2694 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2700 if (LargeIntegerLessThan(*sizep, scp->length)) {
2701 /* a real truncation. If truncPos is not set yet, or is bigger
2702 * than where we're truncating the file, set truncPos to this
2707 if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
2708 || LargeIntegerLessThan(*sizep, scp->length)) {
2710 scp->truncPos = *sizep;
2711 scp->mask |= CM_SCACHEMASK_TRUNCPOS;
2713 /* in either case, the new file size has been changed */
2714 scp->length = *sizep;
2715 scp->mask |= CM_SCACHEMASK_LENGTH;
2717 else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
2718 /* really extending the file */
2719 scp->length = *sizep;
2720 scp->mask |= CM_SCACHEMASK_LENGTH;
2723 /* done successfully */
2726 cm_SyncOpDone(scp, NULL,
2727 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2728 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2731 lock_ReleaseWrite(&scp->rw);
2732 lock_ReleaseWrite(&scp->bufCreateLock);
2737 /* set the file size or other attributes (but not both at once) */
2738 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
2742 AFSFetchStatus afsOutStatus;
2746 AFSStoreStatus afsInStatus;
2747 struct rx_connection * rxconnp;
2749 memset(&volSync, 0, sizeof(volSync));
2751 /* handle file length setting */
2752 if (attrp->mask & CM_ATTRMASK_LENGTH)
2753 return cm_SetLength(scp, &attrp->length, userp, reqp);
2755 lock_ObtainWrite(&scp->rw);
2756 /* Check for RO volume */
2757 if (scp->flags & CM_SCACHEFLAG_RO) {
2758 code = CM_ERROR_READONLY;
2759 lock_ReleaseWrite(&scp->rw);
2763 /* otherwise, we have to make an RPC to get the status */
2764 code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
2766 lock_ReleaseWrite(&scp->rw);
2769 lock_ConvertWToR(&scp->rw);
2771 /* make the attr structure */
2772 cm_StatusFromAttr(&afsInStatus, scp, attrp);
2774 tfid.Volume = scp->fid.volume;
2775 tfid.Vnode = scp->fid.vnode;
2776 tfid.Unique = scp->fid.unique;
2777 lock_ReleaseRead(&scp->rw);
2779 /* now make the RPC */
2780 osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%p", scp);
2782 code = cm_ConnFromFID(&scp->fid, userp, reqp, &connp);
2786 rxconnp = cm_GetRxConn(connp);
2787 code = RXAFS_StoreStatus(rxconnp, &tfid,
2788 &afsInStatus, &afsOutStatus, &volSync);
2789 rx_PutConnection(rxconnp);
2791 } while (cm_Analyze(connp, userp, reqp,
2792 &scp->fid, &volSync, NULL, NULL, code));
2793 code = cm_MapRPCError(code, reqp);
2796 osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
2798 osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
2800 lock_ObtainWrite(&scp->rw);
2802 cm_MergeStatus(NULL, scp, &afsOutStatus, &volSync, userp, reqp,
2803 CM_MERGEFLAG_FORCE|CM_MERGEFLAG_STOREDATA);
2804 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
2806 /* if we're changing the mode bits, discard the ACL cache,
2807 * since we changed the mode bits.
2809 if (afsInStatus.Mask & AFS_SETMODE)
2810 cm_FreeAllACLEnts(scp);
2811 lock_ReleaseWrite(&scp->rw);
2815 long cm_Create(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
2816 cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
2821 cm_callbackRequest_t cbReq;
2824 cm_scache_t *scp = NULL;
2826 AFSStoreStatus inStatus;
2827 AFSFetchStatus updatedDirStatus;
2828 AFSFetchStatus newFileStatus;
2829 AFSCallBack newFileCallback;
2831 struct rx_connection * rxconnp;
2833 fschar_t * fnamep = NULL;
2835 memset(&volSync, 0, sizeof(volSync));
2837 /* can't create names with @sys in them; must expand it manually first.
2838 * return "invalid request" if they try.
2840 if (cm_ExpandSysName(NULL, cnamep, NULL, 0, 0)) {
2841 return CM_ERROR_ATSYS;
2844 #ifdef AFS_FREELANCE_CLIENT
2845 /* Freelance root volume does not hold files */
2846 if (cm_freelanceEnabled &&
2847 dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
2848 dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
2850 return CM_ERROR_NOACCESS;
2852 #endif /* AFS_FREELANCE_CLIENT */
2854 /* Check for RO volume */
2855 if (dscp->flags & CM_SCACHEFLAG_RO)
2856 return CM_ERROR_READONLY;
2858 /* before starting the RPC, mark that we're changing the file data, so
2859 * that someone who does a chmod will know to wait until our call
2862 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
2864 lock_ObtainWrite(&dscp->rw);
2865 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2866 lock_ReleaseWrite(&dscp->rw);
2868 cm_StartCallbackGrantingCall(NULL, &cbReq);
2870 cm_EndDirOp(&dirop);
2877 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
2879 cm_StatusFromAttr(&inStatus, NULL, attrp);
2881 /* try the RPC now */
2882 osi_Log1(afsd_logp, "CALL CreateFile scp 0x%p", dscp);
2884 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2888 dirAFSFid.Volume = dscp->fid.volume;
2889 dirAFSFid.Vnode = dscp->fid.vnode;
2890 dirAFSFid.Unique = dscp->fid.unique;
2892 rxconnp = cm_GetRxConn(connp);
2893 code = RXAFS_CreateFile(connp->rxconnp, &dirAFSFid, fnamep,
2894 &inStatus, &newAFSFid, &newFileStatus,
2895 &updatedDirStatus, &newFileCallback,
2897 rx_PutConnection(rxconnp);
2899 } while (cm_Analyze(connp, userp, reqp,
2900 &dscp->fid, &volSync, NULL, &cbReq, code));
2901 code = cm_MapRPCError(code, reqp);
2904 osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
2906 osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
2909 lock_ObtainWrite(&dirop.scp->dirlock);
2910 dirop.lockType = CM_DIRLOCK_WRITE;
2912 lock_ObtainWrite(&dscp->rw);
2914 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
2915 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2916 lock_ReleaseWrite(&dscp->rw);
2918 /* now try to create the file's entry, too, but be careful to
2919 * make sure that we don't merge in old info. Since we weren't locking
2920 * out any requests during the file's creation, we may have pretty old
2924 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
2925 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2927 lock_ObtainWrite(&scp->rw);
2928 scp->creator = userp; /* remember who created it */
2929 if (!cm_HaveCallback(scp)) {
2930 cm_EndCallbackGrantingCall(scp, &cbReq,
2931 &newFileCallback, &volSync, 0);
2932 cm_MergeStatus(dscp, scp, &newFileStatus, &volSync,
2936 lock_ReleaseWrite(&scp->rw);
2940 /* make sure we end things properly */
2942 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2944 if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
2945 cm_DirCreateEntry(&dirop, fnamep, &newFid);
2947 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
2950 cm_EndDirOp(&dirop);
2959 cm_ReleaseSCache(scp);
2965 * locked if TRUE means write-locked
2966 * else the cm_scache_t rw must not be held
2968 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp, afs_uint32 locked)
2973 lock_ReleaseWrite(&scp->rw);
2975 osi_Log2(afsd_logp, "cm_FSync scp 0x%p userp 0x%p", scp, userp);
2977 code = buf_CleanVnode(scp, userp, reqp);
2979 lock_ObtainWrite(&scp->rw);
2981 if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
2982 | CM_SCACHEMASK_CLIENTMODTIME
2983 | CM_SCACHEMASK_LENGTH))
2984 code = cm_StoreMini(scp, userp, reqp);
2986 if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) {
2987 code = (scp->flags & CM_SCACHEFLAG_OVERQUOTA) ? CM_ERROR_QUOTA : CM_ERROR_SPACE;
2988 scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE);
2992 lock_ReleaseWrite(&scp->rw);
2993 } else if (locked) {
2994 lock_ObtainWrite(&scp->rw);
2999 long cm_MakeDir(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
3000 cm_user_t *userp, cm_req_t *reqp, cm_scache_t **scpp)
3005 cm_callbackRequest_t cbReq;
3008 cm_scache_t *scp = NULL;
3010 AFSStoreStatus inStatus;
3011 AFSFetchStatus updatedDirStatus;
3012 AFSFetchStatus newDirStatus;
3013 AFSCallBack newDirCallback;
3015 struct rx_connection * rxconnp;
3017 fschar_t * fnamep = NULL;
3019 memset(&volSync, 0, sizeof(volSync));
3021 /* can't create names with @sys in them; must expand it manually first.
3022 * return "invalid request" if they try.
3024 if (cm_ExpandSysName(NULL, cnamep, NULL, 0, 0)) {
3025 return CM_ERROR_ATSYS;
3028 #ifdef AFS_FREELANCE_CLIENT
3029 /* Freelance root volume does not hold subdirectories */
3030 if (cm_freelanceEnabled &&
3031 dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
3032 dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
3034 return CM_ERROR_NOACCESS;
3036 #endif /* AFS_FREELANCE_CLIENT */
3038 /* Check for RO volume */
3039 if (dscp->flags & CM_SCACHEFLAG_RO)
3040 return CM_ERROR_READONLY;
3042 /* before starting the RPC, mark that we're changing the directory
3043 * data, so that someone who does a chmod on the dir will wait until
3044 * our call completes.
3046 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3048 lock_ObtainWrite(&dscp->rw);
3049 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3050 lock_ReleaseWrite(&dscp->rw);
3052 cm_StartCallbackGrantingCall(NULL, &cbReq);
3054 cm_EndDirOp(&dirop);
3061 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3062 cm_StatusFromAttr(&inStatus, NULL, attrp);
3064 /* try the RPC now */
3065 osi_Log1(afsd_logp, "CALL MakeDir scp 0x%p", dscp);
3067 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3071 dirAFSFid.Volume = dscp->fid.volume;
3072 dirAFSFid.Vnode = dscp->fid.vnode;
3073 dirAFSFid.Unique = dscp->fid.unique;
3075 rxconnp = cm_GetRxConn(connp);
3076 code = RXAFS_MakeDir(connp->rxconnp, &dirAFSFid, fnamep,
3077 &inStatus, &newAFSFid, &newDirStatus,
3078 &updatedDirStatus, &newDirCallback,
3080 rx_PutConnection(rxconnp);
3082 } while (cm_Analyze(connp, userp, reqp,
3083 &dscp->fid, &volSync, NULL, &cbReq, code));
3084 code = cm_MapRPCError(code, reqp);
3087 osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
3089 osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
3092 lock_ObtainWrite(&dirop.scp->dirlock);
3093 dirop.lockType = CM_DIRLOCK_WRITE;
3095 lock_ObtainWrite(&dscp->rw);
3097 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3098 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3099 lock_ReleaseWrite(&dscp->rw);
3101 /* now try to create the new dir's entry, too, but be careful to
3102 * make sure that we don't merge in old info. Since we weren't locking
3103 * out any requests during the file's creation, we may have pretty old
3107 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3108 code = cm_GetSCache(&newFid, &scp, userp, reqp);
3110 lock_ObtainWrite(&scp->rw);
3111 if (!cm_HaveCallback(scp)) {
3112 cm_EndCallbackGrantingCall(scp, &cbReq,
3113 &newDirCallback, &volSync, 0);
3114 cm_MergeStatus(dscp, scp, &newDirStatus, &volSync,
3118 lock_ReleaseWrite(&scp->rw);
3122 /* make sure we end things properly */
3124 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
3126 if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
3127 cm_DirCreateEntry(&dirop, fnamep, &newFid);
3129 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
3132 cm_EndDirOp(&dirop);
3140 cm_ReleaseSCache(scp);
3143 /* and return error code */
3147 long cm_Link(cm_scache_t *dscp, clientchar_t *cnamep, cm_scache_t *sscp, long flags,
3148 cm_user_t *userp, cm_req_t *reqp)
3153 AFSFid existingAFSFid;
3154 AFSFetchStatus updatedDirStatus;
3155 AFSFetchStatus newLinkStatus;
3157 struct rx_connection * rxconnp;
3159 fschar_t * fnamep = NULL;
3161 memset(&volSync, 0, sizeof(volSync));
3163 if (dscp->fid.cell != sscp->fid.cell ||
3164 dscp->fid.volume != sscp->fid.volume) {
3165 return CM_ERROR_CROSSDEVLINK;
3168 /* Check for RO volume */
3169 if (dscp->flags & CM_SCACHEFLAG_RO)
3170 return CM_ERROR_READONLY;
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_EndDirOp(&dirop);
3183 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3185 /* try the RPC now */
3186 osi_Log1(afsd_logp, "CALL Link scp 0x%p", dscp);
3188 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3191 dirAFSFid.Volume = dscp->fid.volume;
3192 dirAFSFid.Vnode = dscp->fid.vnode;
3193 dirAFSFid.Unique = dscp->fid.unique;
3195 existingAFSFid.Volume = sscp->fid.volume;
3196 existingAFSFid.Vnode = sscp->fid.vnode;
3197 existingAFSFid.Unique = sscp->fid.unique;
3199 rxconnp = cm_GetRxConn(connp);
3200 code = RXAFS_Link(rxconnp, &dirAFSFid, fnamep, &existingAFSFid,
3201 &newLinkStatus, &updatedDirStatus, &volSync);
3202 rx_PutConnection(rxconnp);
3203 osi_Log1(afsd_logp," RXAFS_Link returns 0x%x", code);
3205 } while (cm_Analyze(connp, userp, reqp,
3206 &dscp->fid, &volSync, NULL, NULL, code));
3208 code = cm_MapRPCError(code, reqp);
3211 osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
3213 osi_Log0(afsd_logp, "CALL Link SUCCESS");
3216 lock_ObtainWrite(&dirop.scp->dirlock);
3217 dirop.lockType = CM_DIRLOCK_WRITE;
3219 lock_ObtainWrite(&dscp->rw);
3221 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3222 if (RDR_Initialized)
3223 RDR_InvalidateObject(dscp->fid.cell, dscp->fid.volume, dscp->fid.vnode,
3224 dscp->fid.unique, dscp->fid.hash,
3225 dscp->fileType, AFS_INVALIDATE_DATA_VERSION);
3227 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3228 lock_ReleaseWrite(&dscp->rw);
3231 if (cm_CheckDirOpForSingleChange(&dirop)) {
3232 cm_DirCreateEntry(&dirop, fnamep, &sscp->fid);
3234 cm_BPlusDirCreateEntry(&dirop, cnamep, &sscp->fid);
3238 cm_EndDirOp(&dirop);
3240 /* Update the linked object status */
3242 lock_ObtainWrite(&sscp->rw);
3243 cm_MergeStatus(NULL, sscp, &newLinkStatus, &volSync, userp, reqp, 0);
3244 lock_ReleaseWrite(&sscp->rw);
3252 long cm_SymLink(cm_scache_t *dscp, clientchar_t *cnamep, fschar_t *contentsp, long flags,
3253 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp, cm_scache_t **scpp)
3261 AFSStoreStatus inStatus;
3262 AFSFetchStatus updatedDirStatus;
3263 AFSFetchStatus newLinkStatus;
3265 struct rx_connection * rxconnp;
3267 fschar_t *fnamep = NULL;
3272 /* Check for RO volume */
3273 if (dscp->flags & CM_SCACHEFLAG_RO)
3274 return CM_ERROR_READONLY;
3276 memset(&volSync, 0, sizeof(volSync));
3278 /* before starting the RPC, mark that we're changing the directory data,
3279 * so that someone who does a chmod on the dir will wait until our
3282 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3284 lock_ObtainWrite(&dscp->rw);
3285 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3286 lock_ReleaseWrite(&dscp->rw);
3288 cm_EndDirOp(&dirop);
3293 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3295 cm_StatusFromAttr(&inStatus, NULL, attrp);
3297 /* try the RPC now */
3298 osi_Log1(afsd_logp, "CALL Symlink scp 0x%p", dscp);
3300 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3304 dirAFSFid.Volume = dscp->fid.volume;
3305 dirAFSFid.Vnode = dscp->fid.vnode;
3306 dirAFSFid.Unique = dscp->fid.unique;
3308 rxconnp = cm_GetRxConn(connp);
3309 code = RXAFS_Symlink(rxconnp, &dirAFSFid, fnamep, contentsp,
3310 &inStatus, &newAFSFid, &newLinkStatus,
3311 &updatedDirStatus, &volSync);
3312 rx_PutConnection(rxconnp);
3314 } while (cm_Analyze(connp, userp, reqp,
3315 &dscp->fid, &volSync, NULL, NULL, code));
3316 code = cm_MapRPCError(code, reqp);
3319 osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
3321 osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
3324 lock_ObtainWrite(&dirop.scp->dirlock);
3325 dirop.lockType = CM_DIRLOCK_WRITE;
3327 lock_ObtainWrite(&dscp->rw);
3329 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3330 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3331 lock_ReleaseWrite(&dscp->rw);
3334 if (cm_CheckDirOpForSingleChange(&dirop)) {
3335 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3337 cm_DirCreateEntry(&dirop, fnamep, &newFid);
3339 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
3343 cm_EndDirOp(&dirop);
3345 /* now try to create the new dir's entry, too, but be careful to
3346 * make sure that we don't merge in old info. Since we weren't locking
3347 * out any requests during the file's creation, we may have pretty old
3351 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3352 code = cm_GetSCache(&newFid, &scp, userp, reqp);
3354 lock_ObtainWrite(&scp->rw);
3355 if (!cm_HaveCallback(scp)) {
3356 cm_MergeStatus(dscp, scp, &newLinkStatus, &volSync,
3359 lock_ReleaseWrite(&scp->rw);
3364 cm_ReleaseSCache(scp);
3371 /* and return error code */
3375 /*! \brief Remove a directory
3377 Encapsulates a call to RXAFS_RemoveDir().
3379 \param[in] dscp cm_scache_t for the directory containing the
3380 directory to be removed.
3382 \param[in] fnamep This will be the original name of the directory
3383 as known to the file server. It will be passed in to RXAFS_RemoveDir().
3384 This parameter is optional. If it is not provided the value
3387 \param[in] cnamep Normalized name used to update the local
3390 \param[in] userp cm_user_t for the request.
3392 \param[in] reqp Request tracker.
3394 long cm_RemoveDir(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t *cnamep, cm_user_t *userp, cm_req_t *reqp)
3400 AFSFetchStatus updatedDirStatus;
3402 struct rx_connection * rxconnp;
3404 cm_scache_t *scp = NULL;
3405 int free_fnamep = FALSE;
3407 memset(&volSync, 0, sizeof(volSync));
3409 if (fnamep == NULL) {
3412 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ,
3413 CM_DIROP_FLAG_NONE, &dirop);
3415 code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
3418 cm_EndDirOp(&dirop);
3425 code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
3429 /* Check for RO volume */
3430 if (dscp->flags & CM_SCACHEFLAG_RO) {
3431 code = CM_ERROR_READONLY;
3435 /* before starting the RPC, mark that we're changing the directory data,
3436 * so that someone who does a chmod on the dir will wait until our
3439 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3441 lock_ObtainWrite(&dscp->rw);
3442 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3443 lock_ReleaseWrite(&dscp->rw);
3445 cm_EndDirOp(&dirop);
3450 /* try the RPC now */
3451 osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%p", dscp);
3453 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3457 dirAFSFid.Volume = dscp->fid.volume;
3458 dirAFSFid.Vnode = dscp->fid.vnode;
3459 dirAFSFid.Unique = dscp->fid.unique;
3461 rxconnp = cm_GetRxConn(connp);
3462 code = RXAFS_RemoveDir(rxconnp, &dirAFSFid, fnamep,
3463 &updatedDirStatus, &volSync);
3464 rx_PutConnection(rxconnp);
3466 } while (cm_Analyze(connp, userp, reqp,
3467 &dscp->fid, &volSync, NULL, NULL, code));
3468 code = cm_MapRPCErrorRmdir(code, reqp);
3471 osi_Log1(afsd_logp, "CALL RemoveDir FAILURE, code 0x%x", code);
3473 osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
3476 lock_ObtainWrite(&dirop.scp->dirlock);
3477 dirop.lockType = CM_DIRLOCK_WRITE;
3479 lock_ObtainWrite(&dscp->rw);
3481 cm_dnlcRemove(dscp, cnamep);
3482 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3484 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3485 lock_ReleaseWrite(&dscp->rw);
3488 if (cm_CheckDirOpForSingleChange(&dirop) && cnamep != NULL) {
3489 cm_DirDeleteEntry(&dirop, fnamep);
3491 cm_BPlusDirDeleteEntry(&dirop, cnamep);
3495 cm_EndDirOp(&dirop);
3498 cm_ReleaseSCache(scp);
3500 lock_ObtainWrite(&scp->rw);
3501 scp->flags |= CM_SCACHEFLAG_DELETED;
3502 lock_ObtainWrite(&cm_scacheLock);
3503 cm_AdjustScacheLRU(scp);
3504 cm_RemoveSCacheFromHashTable(scp);
3505 lock_ReleaseWrite(&cm_scacheLock);
3506 lock_ReleaseWrite(&scp->rw);
3507 if (RDR_Initialized && !(reqp->flags & CM_REQ_SOURCE_REDIR) &&
3508 !RDR_InvalidateObject(scp->fid.cell, scp->fid.volume, scp->fid.vnode,
3509 scp->fid.unique, scp->fid.hash,
3510 scp->fileType, AFS_INVALIDATE_DELETED))
3511 buf_ClearRDRFlag(scp, "rmdir");
3519 /* and return error code */
3523 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
3525 /* grab mutex on contents */
3526 lock_ObtainWrite(&scp->rw);
3528 /* reset the prefetch info */
3529 scp->prefetch.base.LowPart = 0; /* base */
3530 scp->prefetch.base.HighPart = 0;
3531 scp->prefetch.end.LowPart = 0; /* and end */
3532 scp->prefetch.end.HighPart = 0;
3534 /* release mutex on contents */
3535 lock_ReleaseWrite(&scp->rw);
3541 /*! \brief Rename a file or directory
3543 Encapsulates a RXAFS_Rename() call.
3545 \param[in] oldDscp cm_scache_t for the directory containing the old
3548 \param[in] oldNamep The original old name known to the file server.
3549 This is the name that will be passed into the RXAFS_Rename().
3550 If it is not provided, it will be looked up.
3552 \param[in] normalizedOldNamep Normalized old name. This is used for
3553 updating local directory caches.
3555 \param[in] newDscp cm_scache_t for the directory containing the new
3558 \param[in] newNamep New name. Normalized.
3560 \param[in] userp cm_user_t for the request.
3562 \param[in,out] reqp Request tracker.
3565 long cm_Rename(cm_scache_t *oldDscp, fschar_t *oldNamep, clientchar_t *cOldNamep,
3566 cm_scache_t *newDscp, clientchar_t *cNewNamep, cm_user_t *userp,
3571 AFSFid oldDirAFSFid;
3572 AFSFid newDirAFSFid;
3574 AFSFetchStatus updatedOldDirStatus;
3575 AFSFetchStatus updatedNewDirStatus;
3578 struct rx_connection * rxconnp;
3579 cm_dirOp_t oldDirOp;
3582 cm_dirOp_t newDirOp;
3583 fschar_t * newNamep = NULL;
3584 int free_oldNamep = FALSE;
3585 cm_scache_t *oldScp = NULL, *newScp = NULL;
3587 memset(&volSync, 0, sizeof(volSync));
3589 if (cOldNamep == NULL || cNewNamep == NULL ||
3590 cm_ClientStrLen(cOldNamep) == 0 ||
3591 cm_ClientStrLen(cNewNamep) == 0)
3592 return CM_ERROR_INVAL;
3595 * Before we permit the operation, make sure that we do not already have
3596 * an object in the destination directory that has a case-insensitive match
3597 * for this name UNLESS the matching object is the object we are renaming.
3599 code = cm_Lookup(oldDscp, cOldNamep, 0, userp, reqp, &oldScp);
3601 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S old name lookup failed",
3602 oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3606 /* Case sensitive lookup. If this succeeds we are done. */
3607 code = cm_Lookup(newDscp, cNewNamep, 0, userp, reqp, &newScp);
3610 * Case insensitive lookup. If this succeeds, it could have found the
3611 * same file with a name that differs only by case or it could be a
3612 * different file entirely.
3614 code = cm_Lookup(newDscp, cNewNamep, CM_FLAG_CASEFOLD, userp, reqp, &newScp);
3616 /* found a matching object with the new name */
3617 if (cm_FidCmp(&oldScp->fid, &newScp->fid)) {
3618 /* and they don't match so return an error */
3619 osi_Log2(afsd_logp, "cm_Rename newDscp 0x%p cNewName %S new name already exists",
3620 newDscp, osi_LogSaveStringW(afsd_logp, cNewNamep));
3621 code = CM_ERROR_EXISTS;
3623 cm_ReleaseSCache(newScp);
3625 } else if (code == CM_ERROR_AMBIGUOUS_FILENAME) {
3626 code = CM_ERROR_EXISTS;
3628 /* The target does not exist. Clear the error and perform the rename. */
3633 /* Check for RO volume */
3635 (oldDscp->flags & CM_SCACHEFLAG_RO) || (newDscp->flags & CM_SCACHEFLAG_RO)) {
3636 code = CM_ERROR_READONLY;
3642 if (oldNamep == NULL) {
3645 code = cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_READ,
3646 CM_DIROP_FLAG_NONE, &oldDirOp);
3648 code = cm_BPlusDirLookupOriginalName(&oldDirOp, cOldNamep, &oldNamep);
3650 free_oldNamep = TRUE;
3651 cm_EndDirOp(&oldDirOp);
3655 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S Original Name lookup failed",
3656 oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3662 /* before starting the RPC, mark that we're changing the directory data,
3663 * so that someone who does a chmod on the dir will wait until our call
3664 * completes. We do this in vnode order so that we don't deadlock,
3665 * which makes the code a little verbose.
3667 if (oldDscp == newDscp) {
3668 /* check for identical names */
3669 if (cm_ClientStrCmp(cOldNamep, cNewNamep) == 0) {
3670 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_RENAME_IDENTICAL",
3672 code = CM_ERROR_RENAME_IDENTICAL;
3677 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE,
3678 CM_DIROP_FLAG_NONE, &oldDirOp);
3679 lock_ObtainWrite(&oldDscp->rw);
3680 cm_dnlcRemove(oldDscp, cOldNamep);
3681 cm_dnlcRemove(oldDscp, cNewNamep);
3682 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3683 CM_SCACHESYNC_STOREDATA);
3684 lock_ReleaseWrite(&oldDscp->rw);
3686 cm_EndDirOp(&oldDirOp);
3690 /* two distinct dir vnodes */
3692 if (oldDscp->fid.cell != newDscp->fid.cell ||
3693 oldDscp->fid.volume != newDscp->fid.volume) {
3694 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_CROSSDEVLINK",
3696 code = CM_ERROR_CROSSDEVLINK;
3700 /* shouldn't happen that we have distinct vnodes for two
3701 * different files, but could due to deliberate attack, or
3702 * stale info. Avoid deadlocks and quit now.
3704 if (oldDscp->fid.vnode == newDscp->fid.vnode) {
3705 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p vnode collision",
3707 code = CM_ERROR_CROSSDEVLINK;
3711 if (oldDscp->fid.vnode < newDscp->fid.vnode) {
3712 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE,
3713 CM_DIROP_FLAG_NONE, &oldDirOp);
3714 lock_ObtainWrite(&oldDscp->rw);
3715 cm_dnlcRemove(oldDscp, cOldNamep);
3716 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3717 CM_SCACHESYNC_STOREDATA);
3718 lock_ReleaseWrite(&oldDscp->rw);
3720 cm_EndDirOp(&oldDirOp);
3722 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE,
3723 CM_DIROP_FLAG_NONE, &newDirOp);
3724 lock_ObtainWrite(&newDscp->rw);
3725 cm_dnlcRemove(newDscp, cNewNamep);
3726 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3727 CM_SCACHESYNC_STOREDATA);
3728 lock_ReleaseWrite(&newDscp->rw);
3730 cm_EndDirOp(&newDirOp);
3732 /* cleanup first one */
3733 lock_ObtainWrite(&oldDscp->rw);
3734 cm_SyncOpDone(oldDscp, NULL,
3735 CM_SCACHESYNC_STOREDATA);
3736 lock_ReleaseWrite(&oldDscp->rw);
3737 cm_EndDirOp(&oldDirOp);
3742 /* lock the new vnode entry first */
3743 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE,
3744 CM_DIROP_FLAG_NONE, &newDirOp);
3745 lock_ObtainWrite(&newDscp->rw);
3746 cm_dnlcRemove(newDscp, cNewNamep);
3747 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3748 CM_SCACHESYNC_STOREDATA);
3749 lock_ReleaseWrite(&newDscp->rw);
3751 cm_EndDirOp(&newDirOp);
3753 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE,
3754 CM_DIROP_FLAG_NONE, &oldDirOp);
3755 lock_ObtainWrite(&oldDscp->rw);
3756 cm_dnlcRemove(oldDscp, cOldNamep);
3757 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3758 CM_SCACHESYNC_STOREDATA);
3759 lock_ReleaseWrite(&oldDscp->rw);
3761 cm_EndDirOp(&oldDirOp);
3763 /* cleanup first one */
3764 lock_ObtainWrite(&newDscp->rw);
3765 cm_SyncOpDone(newDscp, NULL,
3766 CM_SCACHESYNC_STOREDATA);
3767 lock_ReleaseWrite(&newDscp->rw);
3768 cm_EndDirOp(&newDirOp);
3772 } /* two distinct vnodes */
3779 newNamep = cm_ClientStringToFsStringAlloc(cNewNamep, -1, NULL);
3781 /* try the RPC now */
3782 osi_Log2(afsd_logp, "CALL Rename old scp 0x%p new scp 0x%p",
3785 code = cm_ConnFromFID(&oldDscp->fid, userp, reqp, &connp);
3789 oldDirAFSFid.Volume = oldDscp->fid.volume;
3790 oldDirAFSFid.Vnode = oldDscp->fid.vnode;
3791 oldDirAFSFid.Unique = oldDscp->fid.unique;
3792 newDirAFSFid.Volume = newDscp->fid.volume;
3793 newDirAFSFid.Vnode = newDscp->fid.vnode;
3794 newDirAFSFid.Unique = newDscp->fid.unique;
3796 rxconnp = cm_GetRxConn(connp);
3797 code = RXAFS_Rename(rxconnp, &oldDirAFSFid, oldNamep,
3798 &newDirAFSFid, newNamep,
3799 &updatedOldDirStatus, &updatedNewDirStatus,
3801 rx_PutConnection(rxconnp);
3803 } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
3804 &volSync, NULL, NULL, code));
3805 code = cm_MapRPCError(code, reqp);
3808 osi_Log1(afsd_logp, "CALL Rename FAILURE, code 0x%x", code);
3810 osi_Log0(afsd_logp, "CALL Rename SUCCESS");
3812 /* update the individual stat cache entries for the directories */
3814 lock_ObtainWrite(&oldDirOp.scp->dirlock);
3815 oldDirOp.lockType = CM_DIRLOCK_WRITE;
3817 lock_ObtainWrite(&oldDscp->rw);
3820 cm_MergeStatus(NULL, oldDscp, &updatedOldDirStatus, &volSync,
3821 userp, reqp, CM_MERGEFLAG_DIROP);
3822 cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
3823 lock_ReleaseWrite(&oldDscp->rw);
3825 if (code == 0 && cm_CheckDirOpForSingleChange(&oldDirOp)) {
3827 diropCode = cm_BPlusDirLookup(&oldDirOp, cOldNamep, &fileFid);
3828 if (diropCode == CM_ERROR_INEXACT_MATCH)
3830 else if (diropCode == EINVAL)
3832 diropCode = cm_DirLookup(&oldDirOp, oldNamep, &fileFid);
3834 if (diropCode == 0) {
3836 diropCode = cm_DirCreateEntry(&oldDirOp, newNamep, &fileFid);
3838 cm_BPlusDirCreateEntry(&oldDirOp, cNewNamep, &fileFid);
3842 if (diropCode == 0) {
3843 diropCode = cm_DirDeleteEntry(&oldDirOp, oldNamep);
3845 cm_BPlusDirDeleteEntry(&oldDirOp, cOldNamep);
3850 cm_EndDirOp(&oldDirOp);
3852 /* and update it for the new one, too, if necessary */
3855 lock_ObtainWrite(&newDirOp.scp->dirlock);
3856 newDirOp.lockType = CM_DIRLOCK_WRITE;
3858 lock_ObtainWrite(&newDscp->rw);
3860 cm_MergeStatus(NULL, newDscp, &updatedNewDirStatus, &volSync,
3861 userp, reqp, CM_MERGEFLAG_DIROP);
3862 cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
3863 lock_ReleaseWrite(&newDscp->rw);
3867 * The following optimization does not work.
3868 * When the file server processed a RXAFS_Rename() request the
3869 * FID of the object being moved between directories is not
3870 * preserved. The client does not know the new FID nor the
3871 * version number of the target. Not only can we not create
3872 * the directory entry in the new directory, but we can't
3873 * preserve the cached data for the file. It must be re-read
3874 * from the file server. - jaltman, 2009/02/20
3877 /* we only make the local change if we successfully made
3878 the change in the old directory AND there was only one
3879 change in the new directory */
3880 if (diropCode == 0 && cm_CheckDirOpForSingleChange(&newDirOp)) {
3881 cm_DirCreateEntry(&newDirOp, newNamep, &fileFid);
3883 cm_BPlusDirCreateEntry(&newDirOp, cNewNamep, &fileFid);
3888 cm_EndDirOp(&newDirOp);
3892 * After the rename the file server has invalidated the callbacks
3893 * on the file that was moved nor do we have a directory reference
3896 lock_ObtainWrite(&oldScp->rw);
3897 cm_DiscardSCache(oldScp);
3898 lock_ReleaseWrite(&oldScp->rw);
3900 if (RDR_Initialized)
3901 RDR_InvalidateObject(oldScp->fid.cell, oldScp->fid.volume, oldScp->fid.vnode, oldScp->fid.unique,
3902 oldScp->fid.hash, oldScp->fileType, AFS_INVALIDATE_CALLBACK);
3905 cm_ReleaseSCache(oldScp);
3912 /* and return error code */
3916 /* Byte range locks:
3918 The OpenAFS Windows client has to fake byte range locks given no
3919 server side support for such locks. This is implemented as keyed
3920 byte range locks on the cache manager.
3922 Keyed byte range locks:
3924 Each cm_scache_t structure keeps track of a list of keyed locks.
3925 The key for a lock identifies an owner of a set of locks (referred
3926 to as a client). Each key is represented by a value. The set of
3927 key values used within a specific cm_scache_t structure form a
3928 namespace that has a scope of just that cm_scache_t structure. The
3929 same key value can be used with another cm_scache_t structure and
3930 correspond to a completely different client. However it is
3931 advantageous for the SMB or IFS layer to make sure that there is a
3932 1-1 mapping between client and keys over all cm_scache_t objects.
3934 Assume a client C has key Key(C) (although, since the scope of the
3935 key is a cm_scache_t, the key can be Key(C,S), where S is the
3936 cm_scache_t. But assume a 1-1 relation between keys and clients).
3937 A byte range (O,+L) denotes byte addresses (O) through (O+L-1)
3938 inclusive (a.k.a. [O,O+L-1]). The function Key(x) is implemented
3939 through cm_generateKey() function for both SMB and IFS.
3941 The list of locks for a cm_scache_t object S is maintained in
3942 S->fileLocks. The cache manager will set a lock on the AFS file
3943 server in order to assert the locks in S->fileLocks. If only
3944 shared locks are in place for S, then the cache manager will obtain
3945 a LockRead lock, while if there are any exclusive locks, it will
3946 obtain a LockWrite lock. If the exclusive locks are all released
3947 while the shared locks remain, then the cache manager will
3948 downgrade the lock from LockWrite to LockRead. Similarly, if an
3949 exclusive lock is obtained when only shared locks exist, then the
3950 cache manager will try to upgrade the lock from LockRead to
3953 Each lock L owned by client C maintains a key L->key such that
3954 L->key == Key(C), the effective range defined by L->LOffset and
3955 L->LLength such that the range of bytes affected by the lock is
3956 (L->LOffset, +L->LLength), a type maintained in L->LockType which
3957 is either exclusive or shared.
3961 A lock exists iff it is in S->fileLocks for some cm_scache_t
3962 S. Existing locks are in one of the following states: ACTIVE,
3963 WAITLOCK, WAITUNLOCK, LOST, DELETED.
3965 The following sections describe each lock and the associated
3968 1. ACTIVE: A lock L is ACTIVE iff the cache manager has asserted
3969 the lock with the AFS file server. This type of lock can be
3970 exercised by a client to read or write to the locked region (as
3973 1.1 ACTIVE->LOST: When the AFS file server fails to extend a
3974 server lock that was required to assert the lock. Before
3975 marking the lock as lost, the cache manager checks if the file
3976 has changed on the server. If the file has not changed, then
3977 the cache manager will attempt to obtain a new server lock
3978 that is sufficient to assert the client side locks for the
3979 file. If any of these fail, the lock is marked as LOST.
3980 Otherwise, it is left as ACTIVE.
3982 1.2 ACTIVE->DELETED: Lock is released.
3984 2. WAITLOCK: A lock is in a WAITLOCK state if the cache manager
3985 grants the lock but the lock is yet to be asserted with the AFS
3986 file server. Once the file server grants the lock, the state
3987 will transition to an ACTIVE lock.
3989 2.1 WAITLOCK->ACTIVE: The server granted the lock.
3991 2.2 WAITLOCK->DELETED: Lock is abandoned, or timed out during
3994 2.3 WAITLOCK->LOST: One or more locks from this client were
3995 marked as LOST. No further locks will be granted to this
3996 client until all lost locks are removed.
3998 3. WAITUNLOCK: A lock is in a WAITUNLOCK state if the cache manager
3999 receives a request for a lock that conflicts with an existing
4000 ACTIVE or WAITLOCK lock. The lock will be placed in the queue
4001 and will be granted at such time the conflicting locks are
4002 removed, at which point the state will transition to either
4005 3.1 WAITUNLOCK->ACTIVE: The conflicting lock was removed. The
4006 current serverLock is sufficient to assert this lock, or a
4007 sufficient serverLock is obtained.
4009 3.2 WAITUNLOCK->WAITLOCK: The conflicting lock was removed,
4010 however the required serverLock is yet to be asserted with the
4013 3.3 WAITUNLOCK->DELETED: The lock is abandoned, timed out or
4016 3.5 WAITUNLOCK->LOST: One or more locks from this client were
4017 marked as LOST. No further locks will be granted to this
4018 client until all lost locks are removed.
4020 4. LOST: A lock L is LOST if the server lock that was required to
4021 assert the lock could not be obtained or if it could not be
4022 extended, or if other locks by the same client were LOST.
4023 Essentially, once a lock is LOST, the contract between the cache
4024 manager and that specific client is no longer valid.
4026 The cache manager rechecks the server lock once every minute and
4027 extends it as appropriate. If this is not done for 5 minutes,
4028 the AFS file server will release the lock (the 5 minute timeout
4029 is based on current file server code and is fairly arbitrary).
4030 Once released, the lock cannot be re-obtained without verifying
4031 that the contents of the file hasn't been modified since the
4032 time the lock was released. Re-obtaining the lock without
4033 verifying this may lead to data corruption. If the lock can not
4034 be obtained safely, then all active locks for the cm_scache_t
4037 4.1 LOST->DELETED: The lock is released.
4039 5. DELETED: The lock is no longer relevant. Eventually, it will
4040 get removed from the cm_scache_t. In the meantime, it will be
4041 treated as if it does not exist.
4043 5.1 DELETED->not exist: The lock is removed from the
4046 The following are classifications of locks based on their state.
4048 6* A lock L is ACCEPTED if it is ACTIVE or WAITLOCK. These locks
4049 have been accepted by the cache manager, but may or may not have
4050 been granted back to the client.
4052 7* A lock L is QUEUED if it is ACTIVE, WAITLOCK or WAITUNLOCK.
4054 8* A lock L is WAITING if it is WAITLOCK or WAITUNLOCK.
4058 A client C can READ range (Offset,+Length) of a file represented by
4059 cm_scache_t S iff (1):
4061 1. for all _a_ in (Offset,+Length), all of the following is true:
4063 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
4064 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
4067 1.2 For each LOST lock L in S->fileLocks such that _a_ in
4068 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
4071 (When locks are lost on an cm_scache_t, all locks are lost. By
4072 4.2 (below), if there is an exclusive LOST lock, then there
4073 can't be any overlapping ACTIVE locks.)
4075 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
4077 2. for all _a_ in (Offset,+Length), one of the following is true:
4079 2.1 Byte _a_ of S is unowned (as specified in 1.1) AND there
4080 does not exist a LOST lock L such that _a_ in
4081 (L->LOffset,+L->LLength).
4083 2.2 Byte _a_ of S is owned by C under lock L (as specified in
4084 1.2) AND L->LockType is exclusive.
4086 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
4088 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
4091 3.1 If L->LockType is exclusive then there does NOT exist a
4092 ACCEPTED lock M in S->fileLocks such that _a_ in
4093 (M->LOffset,+M->LLength).
4095 (If we count all QUEUED locks then we hit cases such as
4096 cascading waiting locks where the locks later on in the queue
4097 can be granted without compromising file integrity. On the
4098 other hand if only ACCEPTED locks are considered, then locks
4099 that were received earlier may end up waiting for locks that
4100 were received later to be unlocked. The choice of ACCEPTED
4101 locks was made to mimic the Windows byte range lock
4104 3.2 If L->LockType is shared then for each ACCEPTED lock M in
4105 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
4106 M->LockType is shared.
4108 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
4110 4.1 M->key != Key(C)
4112 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
4113 and (M->LOffset,+M->LLength) do not intersect.
4115 (Note: If a client loses a lock, it loses all locks.
4116 Subsequently, it will not be allowed to obtain any more locks
4117 until all existing LOST locks that belong to the client are
4118 released. Once all locks are released by a single client,
4119 there exists no further contract between the client and AFS
4120 about the contents of the file, hence the client can then
4121 proceed to obtain new locks and establish a new contract.
4123 This doesn't quite work as you think it should, because most
4124 applications aren't built to deal with losing locks they
4125 thought they once had. For now, we don't have a good
4126 solution to lost locks.
4128 Also, for consistency reasons, we have to hold off on
4129 granting locks that overlap exclusive LOST locks.)
4131 A client C can only unlock locks L in S->fileLocks which have
4134 The representation and invariants are as follows:
4136 - Each cm_scache_t structure keeps:
4138 - A queue of byte-range locks (cm_scache_t::fileLocks) which
4139 are of type cm_file_lock_t.
4141 - A record of the highest server-side lock that has been
4142 obtained for this object (cm_scache_t::serverLock), which is
4143 one of (-1), LockRead, LockWrite.
4145 - A count of ACCEPTED exclusive and shared locks that are in the
4146 queue (cm_scache_t::sharedLocks and
4147 cm_scache_t::exclusiveLocks)
4149 - Each cm_file_lock_t structure keeps:
4151 - The type of lock (cm_file_lock_t::LockType)
4153 - The key associated with the lock (cm_file_lock_t::key)
4155 - The offset and length of the lock (cm_file_lock_t::LOffset
4156 and cm_file_lock_t::LLength)
4158 - The state of the lock.
4160 - Time of issuance or last successful extension
4162 Semantic invariants:
4164 I1. The number of ACCEPTED locks in S->fileLocks are
4165 (S->sharedLocks + S->exclusiveLocks)
4167 External invariants:
4169 I3. S->serverLock is the lock that we have asserted with the
4170 AFS file server for this cm_scache_t.
4172 I4. S->serverLock == LockRead iff there is at least one ACTIVE
4173 shared lock, but no ACTIVE exclusive locks.
4175 I5. S->serverLock == LockWrite iff there is at least one ACTIVE
4178 I6. If L is a LOST lock, then for each lock M in S->fileLocks,
4179 M->key == L->key IMPLIES M is LOST or DELETED.
4184 #define IS_LOCK_ACTIVE(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == 0)
4186 #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)
4188 #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)
4190 #define IS_LOCK_LOST(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_LOST)
4192 #define IS_LOCK_DELETED(lockp) (((lockp)->flags & CM_FILELOCK_FLAG_DELETED) == CM_FILELOCK_FLAG_DELETED)
4195 #define IS_LOCK_ACCEPTED(lockp) (IS_LOCK_ACTIVE(lockp) || IS_LOCK_WAITLOCK(lockp))
4198 #define IS_LOCK_CLIENTONLY(lockp) ((((lockp)->scp->flags & CM_SCACHEFLAG_RO) == CM_SCACHEFLAG_RO) || (((lockp)->flags & CM_FILELOCK_FLAG_CLIENTONLY) == CM_FILELOCK_FLAG_CLIENTONLY))
4201 #define INTERSECT_RANGE(r1,r2) (((r2).offset+(r2).length) > (r1).offset && ((r1).offset +(r1).length) > (r2).offset)
4204 #define CONTAINS_RANGE(r1,r2) (((r2).offset+(r2).length) <= ((r1).offset+(r1).length) && (r1).offset <= (r2).offset)
4206 #if defined(VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS) && !defined(LOCK_TESTING)
4207 #define SCP_SUPPORTS_BRLOCKS(scp) ((scp)->cbServerp && ((scp)->cbServerp->capabilities & VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS))
4209 #define SCP_SUPPORTS_BRLOCKS(scp) (1)
4212 #define SERVERLOCKS_ENABLED(scp) (!((scp)->flags & CM_SCACHEFLAG_RO) && cm_enableServerLocks && SCP_SUPPORTS_BRLOCKS(scp))
4214 #if defined(VICED_CAPABILITY_WRITELOCKACL)
4215 #define SCP_SUPPORTS_WRITELOCKACL(scp) ((scp)->cbServerp && ((scp->cbServerp->capabilities & VICED_CAPABILITY_WRITELOCKACL)))
4217 #define SCP_SUPPORTS_WRITELOCKACL(scp) (0)
4219 /* This should really be defined in any build that this code is being
4221 #error VICED_CAPABILITY_WRITELOCKACL not defined.
4224 static void cm_LockRangeSubtract(cm_range_t * pos, const cm_range_t * neg)
4226 afs_int64 int_begin;
4229 int_begin = MAX(pos->offset, neg->offset);
4230 int_end = MIN(pos->offset+pos->length, neg->offset+neg->length);
4232 if (int_begin < int_end) {
4233 if (int_begin == pos->offset) {
4234 pos->length = pos->offset + pos->length - int_end;
4235 pos->offset = int_end;
4236 } else if (int_end == pos->offset + pos->length) {
4237 pos->length = int_begin - pos->offset;
4240 /* We only subtract ranges if the resulting range is
4241 contiguous. If we try to support non-contigous ranges, we
4242 aren't actually improving performance. */
4246 /* Called with scp->rw held. Returns 0 if all is clear to read the
4247 specified range by the client identified by key.
4249 long cm_LockCheckRead(cm_scache_t *scp,
4250 LARGE_INTEGER LOffset,
4251 LARGE_INTEGER LLength,
4254 #ifndef ADVISORY_LOCKS
4256 cm_file_lock_t *fileLock;
4260 int substract_ranges = FALSE;
4262 range.offset = LOffset.QuadPart;
4263 range.length = LLength.QuadPart;
4267 1. for all _a_ in (Offset,+Length), all of the following is true:
4269 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
4270 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
4273 1.2 For each LOST lock L in S->fileLocks such that _a_ in
4274 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
4279 lock_ObtainRead(&cm_scacheLock);
4281 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
4283 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4285 if (INTERSECT_RANGE(range, fileLock->range)) {
4286 if (IS_LOCK_ACTIVE(fileLock)) {
4287 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4289 /* If there is an active lock for this client, it
4290 is safe to substract ranges.*/
4291 cm_LockRangeSubtract(&range, &fileLock->range);
4292 substract_ranges = TRUE;
4294 if (fileLock->lockType != LockRead) {
4295 code = CM_ERROR_LOCK_CONFLICT;
4299 /* even if the entire range is locked for reading,
4300 we still can't grant the lock at this point
4301 because the client may have lost locks. That
4302 is, unless we have already seen an active lock
4303 belonging to the client, in which case there
4304 can't be any lost locks for this client. */
4305 if (substract_ranges)
4306 cm_LockRangeSubtract(&range, &fileLock->range);
4308 } else if (IS_LOCK_LOST(fileLock) &&
4309 (cm_KeyEquals(&fileLock->key, &key, 0) || fileLock->lockType == LockWrite)) {
4310 code = CM_ERROR_BADFD;
4316 lock_ReleaseRead(&cm_scacheLock);
4318 osi_Log4(afsd_logp, "cm_LockCheckRead scp 0x%x offset %d length %d code 0x%x",
4319 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
4330 /* Called with scp->rw held. Returns 0 if all is clear to write the
4331 specified range by the client identified by key.
4333 long cm_LockCheckWrite(cm_scache_t *scp,
4334 LARGE_INTEGER LOffset,
4335 LARGE_INTEGER LLength,
4338 #ifndef ADVISORY_LOCKS
4340 cm_file_lock_t *fileLock;
4345 range.offset = LOffset.QuadPart;
4346 range.length = LLength.QuadPart;
4349 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
4351 2. for all _a_ in (Offset,+Length), one of the following is true:
4353 2.1 Byte _a_ of S is unowned AND there does not exist a LOST
4354 lock L such that _a_ in (L->LOffset,+L->LLength).
4356 2.2 Byte _a_ of S is owned by C under lock L AND L->LockType is
4360 lock_ObtainRead(&cm_scacheLock);
4362 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
4364 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4366 if (INTERSECT_RANGE(range, fileLock->range)) {
4367 if (IS_LOCK_ACTIVE(fileLock)) {
4368 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4369 if (fileLock->lockType == LockWrite) {
4371 /* if there is an active lock for this client, it
4372 is safe to substract ranges */
4373 cm_LockRangeSubtract(&range, &fileLock->range);
4375 code = CM_ERROR_LOCK_CONFLICT;
4379 code = CM_ERROR_LOCK_CONFLICT;
4382 } else if (IS_LOCK_LOST(fileLock)) {
4383 code = CM_ERROR_BADFD;
4389 lock_ReleaseRead(&cm_scacheLock);
4391 osi_Log4(afsd_logp, "cm_LockCheckWrite scp 0x%x offset %d length %d code 0x%x",
4392 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
4403 /* Called with cm_scacheLock write locked */
4404 static cm_file_lock_t * cm_GetFileLock(void) {
4407 l = (cm_file_lock_t *) cm_freeFileLocks;
4409 osi_QRemove(&cm_freeFileLocks, &l->q);
4411 l = malloc(sizeof(cm_file_lock_t));
4412 osi_assertx(l, "null cm_file_lock_t");
4415 memset(l, 0, sizeof(cm_file_lock_t));
4420 /* Called with cm_scacheLock write locked */
4421 static void cm_PutFileLock(cm_file_lock_t *l) {
4422 osi_QAdd(&cm_freeFileLocks, &l->q);
4425 /* called with scp->rw held. May release it during processing, but
4426 leaves it held on exit. */
4427 long cm_IntSetLock(cm_scache_t * scp, cm_user_t * userp, int lockType,
4433 struct rx_connection * rxconnp;
4435 afs_uint32 reqflags = reqp->flags;
4437 osi_Log2(afsd_logp, "CALL SetLock scp 0x%p for lock %d", scp, lockType);
4439 if ((lockType != LOCKING_ANDX_SHARED_LOCK && scp->fsLockCount != 0) ||
4440 (lockType == LOCKING_ANDX_SHARED_LOCK && scp->fsLockCount < 0))
4442 code = CM_ERROR_LOCK_NOT_GRANTED;
4443 osi_Log2(afsd_logp, "CALL SetLock FAILURE, fsLockCount %d code 0x%x", scp->fsLockCount, code);
4447 memset(&volSync, 0, sizeof(volSync));
4449 tfid.Volume = scp->fid.volume;
4450 tfid.Vnode = scp->fid.vnode;
4451 tfid.Unique = scp->fid.unique;
4454 reqp->flags |= CM_REQ_NORETRY;
4455 lock_ReleaseWrite(&scp->rw);
4458 code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4462 rxconnp = cm_GetRxConn(connp);
4463 code = RXAFS_SetLock(rxconnp, &tfid, lockType,
4465 rx_PutConnection(rxconnp);
4467 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
4470 code = cm_MapRPCError(code, reqp);
4472 osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
4474 osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
4477 reqp->flags = reqflags;
4479 lock_ObtainWrite(&scp->rw);
4482 * The file server does not return a status structure so we must
4483 * locally track the file server lock count to the best of our
4486 if (lockType == LockWrite)
4487 scp->fsLockCount = -1;
4494 /* called with scp->rw held. Releases it during processing */
4495 long cm_IntReleaseLock(cm_scache_t * scp, cm_user_t * userp,
4501 struct rx_connection * rxconnp;
4504 if (scp->flags & CM_SCACHEFLAG_DELETED) {
4505 osi_Log1(afsd_logp, "CALL ReleaseLock on Deleted Vnode scp 0x%p", scp);
4509 memset(&volSync, 0, sizeof(volSync));
4511 tfid.Volume = scp->fid.volume;
4512 tfid.Vnode = scp->fid.vnode;
4513 tfid.Unique = scp->fid.unique;
4516 lock_ReleaseWrite(&scp->rw);
4518 osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
4521 code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4525 rxconnp = cm_GetRxConn(connp);
4526 code = RXAFS_ReleaseLock(rxconnp, &tfid, &volSync);
4527 rx_PutConnection(rxconnp);
4529 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
4531 code = cm_MapRPCError(code, reqp);
4534 "CALL ReleaseLock FAILURE, code 0x%x", code);
4537 "CALL ReleaseLock SUCCESS");
4539 lock_ObtainWrite(&scp->rw);
4542 * The file server does not return a status structure so we must
4543 * locally track the file server lock count to the best of our
4547 if (scp->fsLockCount < 0)
4548 scp->fsLockCount = 0;
4551 return (code != CM_ERROR_BADFD ? code : 0);
4554 /* called with scp->rw held. May release it during processing, but
4555 will exit with lock held.
4559 - 0 if the user has permission to get the specified lock for the scp
4561 - CM_ERROR_NOACCESS if not
4563 Any other error from cm_SyncOp will be sent down untranslated.
4565 If CM_ERROR_NOACCESS is returned and lock_type is LockRead, then
4566 phas_insert (if non-NULL) will receive a boolean value indicating
4567 whether the user has INSERT permission or not.
4569 long cm_LockCheckPerms(cm_scache_t * scp,
4576 long code = 0, code2 = 0;
4578 /* lock permissions are slightly tricky because of the 'i' bit.
4579 If the user has PRSFS_LOCK, she can read-lock the file. If the
4580 user has PRSFS_WRITE, she can write-lock the file. However, if
4581 the user has PRSFS_INSERT, then she can write-lock new files,
4582 but not old ones. Since we don't have information about
4583 whether a file is new or not, we assume that if the user owns
4584 the scp, then she has the permissions that are granted by
4587 osi_Log3(afsd_logp, "cm_LockCheckPerms for scp[0x%p] type[%d] user[0x%p]",
4588 scp, lock_type, userp);
4590 if (lock_type == LockRead)
4591 rights |= PRSFS_LOCK;
4592 else if (lock_type == LockWrite)
4593 rights |= PRSFS_WRITE | PRSFS_LOCK;
4596 osi_assertx(FALSE, "invalid lock type");
4601 *phas_insert = FALSE;
4603 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
4604 CM_SCACHESYNC_GETSTATUS |
4605 CM_SCACHESYNC_NEEDCALLBACK);
4607 if (phas_insert && scp->creator == userp) {
4609 /* If this file was created by the user, then we check for
4610 PRSFS_INSERT. If the file server is recent enough, then
4611 this should be sufficient for her to get a write-lock (but
4612 not necessarily a read-lock). VICED_CAPABILITY_WRITELOCKACL
4613 indicates whether a file server supports getting write
4614 locks when the user only has PRSFS_INSERT.
4616 If the file was not created by the user we skip the check
4617 because the INSERT bit will not apply to this user even
4621 code2 = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
4622 CM_SCACHESYNC_GETSTATUS |
4623 CM_SCACHESYNC_NEEDCALLBACK);
4625 if (code2 == CM_ERROR_NOACCESS) {
4626 osi_Log0(afsd_logp, "cm_LockCheckPerms user has no INSERT bits");
4628 *phas_insert = TRUE;
4629 osi_Log0(afsd_logp, "cm_LockCheckPerms user has INSERT bits");
4633 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
4635 osi_Log1(afsd_logp, "cm_LockCheckPerms returning code %d", code);
4640 /* called with scp->rw held */
4641 long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
4642 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
4644 int allowWait, cm_user_t *userp, cm_req_t *reqp,
4645 cm_file_lock_t **lockpp)
4648 int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
4649 cm_file_lock_t *fileLock;
4652 int wait_unlock = FALSE;
4653 int force_client_lock = FALSE;
4655 osi_Log4(afsd_logp, "cm_Lock scp 0x%x type 0x%x offset %d length %d",
4656 scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
4657 osi_Log4(afsd_logp, "... allowWait %d key <0x%x, 0x%x, 0x%x>", allowWait,
4658 key.process_id, key.session_id, key.file_id);
4661 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
4663 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
4666 3.1 If L->LockType is exclusive then there does NOT exist a
4667 ACCEPTED lock M in S->fileLocks such that _a_ in
4668 (M->LOffset,+M->LLength).
4670 3.2 If L->LockType is shared then for each ACCEPTED lock M in
4671 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
4672 M->LockType is shared.
4674 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
4676 4.1 M->key != Key(C)
4678 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
4679 and (M->LOffset,+M->LLength) do not intersect.
4682 range.offset = LOffset.QuadPart;
4683 range.length = LLength.QuadPart;
4685 lock_ObtainRead(&cm_scacheLock);
4687 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4689 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4691 if (IS_LOCK_LOST(fileLock)) {
4692 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4693 code = CM_ERROR_BADFD;
4695 } else if (fileLock->lockType == LockWrite && INTERSECT_RANGE(range, fileLock->range)) {
4696 code = CM_ERROR_WOULDBLOCK;
4702 /* we don't need to check for deleted locks here since deleted
4703 locks are dequeued from scp->fileLocks */
4704 if (IS_LOCK_ACCEPTED(fileLock) &&
4705 INTERSECT_RANGE(range, fileLock->range)) {
4707 if ((sLockType & LOCKING_ANDX_SHARED_LOCK) == 0 ||
4708 fileLock->lockType != LockRead) {
4710 code = CM_ERROR_WOULDBLOCK;
4716 lock_ReleaseRead(&cm_scacheLock);
4718 if (code == 0 && SERVERLOCKS_ENABLED(scp)) {
4719 if (Which == scp->serverLock ||
4720 (Which == LockRead && scp->serverLock == LockWrite)) {
4724 /* we already have the lock we need */
4725 osi_Log3(afsd_logp, " we already have the correct lock. exclusives[%d], shared[%d], serverLock[%d]",
4726 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4728 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4730 /* special case: if we don't have permission to read-lock
4731 the file, then we force a clientside lock. This is to
4732 compensate for applications that obtain a read-lock for
4733 reading files off of directories that don't grant
4734 read-locks to the user. */
4735 if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4737 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4738 osi_Log0(afsd_logp, " User has no read-lock perms, but has INSERT perms.");
4741 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4742 force_client_lock = TRUE;
4746 } else if ((scp->exclusiveLocks > 0) ||
4747 (scp->sharedLocks > 0 && scp->serverLock != LockRead)) {
4750 /* We are already waiting for some other lock. We should
4751 wait for the daemon to catch up instead of generating a
4752 flood of SetLock calls. */
4753 osi_Log3(afsd_logp, " already waiting for other lock. exclusives[%d], shared[%d], serverLock[%d]",
4754 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4756 /* see if we have permission to create the lock in the
4758 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4760 code = CM_ERROR_WOULDBLOCK;
4761 else if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4763 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4765 " User has no read-lock perms, but has INSERT perms.");
4766 code = CM_ERROR_WOULDBLOCK;
4769 " User has no read-lock perms. Forcing client-side lock");
4770 force_client_lock = TRUE;
4774 /* leave any other codes as-is */
4778 int check_data_version = FALSE;
4781 /* first check if we have permission to elevate or obtain
4783 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4785 if (code == CM_ERROR_NOACCESS && Which == LockRead &&
4786 (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp))) {
4787 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4788 force_client_lock = TRUE;
4793 /* has_insert => (Which == LockRead, code == CM_ERROR_NOACCESS) */
4795 if (scp->serverLock == LockRead && Which == LockWrite) {
4797 /* We want to escalate the lock to a LockWrite.
4798 * Unfortunately that's not really possible without
4799 * letting go of the current lock. But for now we do
4803 " attempting to UPGRADE from LockRead to LockWrite.");
4805 " dataVersion on scp: %I64d", scp->dataVersion);
4807 /* we assume at this point (because scp->serverLock
4808 was valid) that we had a valid server lock. */
4809 scp->lockDataVersion = scp->dataVersion;
4810 check_data_version = TRUE;
4812 code = cm_IntReleaseLock(scp, userp, reqp);
4815 /* We couldn't release the lock */
4818 scp->serverLock = -1;
4822 /* We need to obtain a server lock of type Which in order
4823 * to assert this file lock */
4824 #ifndef AGGRESSIVE_LOCKS
4827 newLock = LockWrite;
4830 code = cm_IntSetLock(scp, userp, newLock, reqp);
4832 #ifdef AGGRESSIVE_LOCKS
4833 if ((code == CM_ERROR_WOULDBLOCK ||
4834 code == CM_ERROR_NOACCESS) && newLock != Which) {
4835 /* we wanted LockRead. We tried LockWrite. Now try
4840 osi_assertx(newLock == LockRead, "lock type not read");
4842 code = cm_IntSetLock(scp, userp, newLock, reqp);
4846 if (code == CM_ERROR_NOACCESS) {
4847 if (Which == LockRead) {
4848 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4850 /* We requested a read-lock, but we have permission to
4851 * get a write-lock. Try that */
4853 tcode = cm_LockCheckPerms(scp, LockWrite, userp, reqp, NULL);
4856 newLock = LockWrite;
4858 osi_Log0(afsd_logp, " User has 'i' perms and the request was for a LockRead. Trying to get a LockWrite instead");
4860 code = cm_IntSetLock(scp, userp, newLock, reqp);
4863 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4864 force_client_lock = TRUE;
4866 } else if (Which == LockWrite &&
4867 scp->creator == userp && !SCP_SUPPORTS_WRITELOCKACL(scp)) {
4870 /* Special case: if the lock request was for a
4871 * LockWrite and the user owns the file and we weren't
4872 * allowed to obtain the serverlock, we either lost a
4873 * race (the permissions changed from under us), or we
4874 * have 'i' bits, but we aren't allowed to lock the
4877 /* check if we lost a race... */
4878 tcode = cm_LockCheckPerms(scp, Which, userp, reqp, NULL);
4881 osi_Log0(afsd_logp, " User has 'i' perms but can't obtain write locks. Using client-side locks.");
4882 force_client_lock = TRUE;
4887 if (code == 0 && check_data_version &&
4888 scp->dataVersion != scp->lockDataVersion) {
4889 /* We lost a race. Although we successfully obtained
4890 * a lock, someone modified the file in between. The
4891 * locks have all been technically lost. */
4894 " Data version mismatch while upgrading lock.");
4896 " Data versions before=%I64d, after=%I64d",
4897 scp->lockDataVersion,
4900 " Releasing stale lock for scp 0x%x", scp);
4902 code = cm_IntReleaseLock(scp, userp, reqp);
4904 scp->serverLock = -1;
4906 code = CM_ERROR_INVAL;
4907 } else if (code == 0) {
4908 scp->serverLock = newLock;
4909 scp->lockDataVersion = scp->dataVersion;
4913 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4914 scp->serverLock == -1) {
4915 /* Oops. We lost the lock. */
4916 cm_LockMarkSCacheLost(scp);
4919 } else if (code == 0) { /* server locks not enabled */
4921 " Skipping server lock for scp");
4926 if (code != 0 && !force_client_lock) {
4927 /* Special case error translations
4929 Applications don't expect certain errors from a
4930 LockFile/UnlockFile call. We need to translate some error
4931 code to codes that apps expect and handle. */
4933 /* We shouldn't actually need to handle this case since we
4934 simulate locks for RO scps anyway. */
4935 if (code == CM_ERROR_READONLY) {
4936 osi_Log0(afsd_logp, " Reinterpreting CM_ERROR_READONLY as CM_ERROR_NOACCESS");
4937 code = CM_ERROR_NOACCESS;
4941 if (code == 0 || (code == CM_ERROR_WOULDBLOCK && allowWait) ||
4942 force_client_lock) {
4944 /* clear the error if we are forcing a client lock, so we
4945 don't get confused later. */
4946 if (force_client_lock && code != CM_ERROR_WOULDBLOCK)
4951 lock_ObtainWrite(&cm_scacheLock);
4952 fileLock = cm_GetFileLock();
4954 fileLock->fid = scp->fid;
4956 fileLock->key = key;
4957 fileLock->lockType = Which;
4958 fileLock->userp = userp;
4959 fileLock->range = range;
4960 fileLock->flags = (code == 0 ? 0 :
4962 CM_FILELOCK_FLAG_WAITUNLOCK :
4963 CM_FILELOCK_FLAG_WAITLOCK));
4965 if (force_client_lock || !SERVERLOCKS_ENABLED(scp))
4966 fileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
4968 fileLock->lastUpdate = (code == 0 && !force_client_lock) ? time(NULL) : 0;
4970 osi_QAddT(&scp->fileLocksH, &scp->fileLocksT, &fileLock->fileq);
4971 cm_HoldSCacheNoLock(scp);
4972 fileLock->scp = scp;
4973 osi_QAdd(&cm_allFileLocks, &fileLock->q);
4974 lock_ReleaseWrite(&cm_scacheLock);
4980 if (IS_LOCK_CLIENTONLY(fileLock)) {
4982 } else if (IS_LOCK_ACCEPTED(fileLock)) {
4983 if (Which == LockRead)
4986 scp->exclusiveLocks++;
4990 "cm_Lock Lock added 0x%p flags 0x%x to scp [0x%p]",
4991 fileLock, fileLock->flags, scp);
4993 " exclusives[%d] shared[%d] client[%d] serverLock[%d]",
4994 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4995 (int)(signed char) scp->serverLock);
4998 "cm_Lock Rejecting lock (code = 0x%x)", code);
5001 /* Convert from would block to lock not granted */
5002 if (code == CM_ERROR_WOULDBLOCK)
5003 code = CM_ERROR_LOCK_NOT_GRANTED;
5009 cm_IntUnlock(cm_scache_t * scp,
5015 osi_assertx(scp->sharedLocks >= 0, "scp->sharedLocks < 0");
5016 osi_assertx(scp->exclusiveLocks >= 0, "scp->exclusiveLocks < 0");
5017 osi_assertx(scp->clientLocks >= 0, "scp->clientLocks < 0");
5019 if (!SERVERLOCKS_ENABLED(scp)) {
5020 osi_Log0(afsd_logp, " Skipping server lock for scp");
5024 /* Ideally we would go through the rest of the locks to determine
5025 * if one or more locks that were formerly in WAITUNLOCK can now
5026 * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
5027 * scp->sharedLocks accordingly. However, the retrying of locks
5028 * in that manner is done cm_RetryLock() manually.
5031 if (scp->serverLock == LockWrite &&
5032 scp->exclusiveLocks == 0 &&
5033 scp->sharedLocks > 0) {
5034 /* The serverLock should be downgraded to LockRead */
5035 osi_Log0(afsd_logp, " DOWNGRADE lock from LockWrite to LockRead");
5037 /* Make sure there are no dirty buffers left. */
5038 code = cm_FSync(scp, userp, reqp, TRUE);
5040 /* since scp->serverLock looked sane, we are going to assume
5041 that we have a valid server lock. */
5042 scp->lockDataVersion = scp->dataVersion;
5043 osi_Log1(afsd_logp, " dataVersion on scp = %I64d", scp->dataVersion);
5045 /* before we downgrade, make sure that we have enough
5046 permissions to get the read lock. */
5047 code = cm_LockCheckPerms(scp, LockRead, userp, reqp, NULL);
5050 osi_Log0(afsd_logp, " SKIPPING downgrade because user doesn't have perms to get downgraded lock");
5056 code = cm_IntReleaseLock(scp, userp, reqp);
5059 /* so we couldn't release it. Just let the lock be for now */
5063 scp->serverLock = -1;
5066 code = cm_IntSetLock(scp, userp, LockRead, reqp);
5068 if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
5069 scp->serverLock = LockRead;
5070 } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
5071 /* We lost a race condition. Although we have a valid
5072 lock on the file, the data has changed and essentially
5073 we have lost the lock we had during the transition. */
5075 osi_Log0(afsd_logp, "Data version mismatch during lock downgrade");
5076 osi_Log2(afsd_logp, " Data versions before=%I64d, after=%I64d",
5077 scp->lockDataVersion,
5080 code = cm_IntReleaseLock(scp, userp, reqp);
5082 code = CM_ERROR_INVAL;
5083 scp->serverLock = -1;
5087 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
5088 (scp->serverLock == -1)) {
5090 cm_LockMarkSCacheLost(scp);
5093 /* failure here has no bearing on the return value of cm_Unlock() */
5096 } else if (scp->serverLock != (-1) &&
5097 scp->exclusiveLocks == 0 &&
5098 scp->sharedLocks == 0) {
5099 /* The serverLock should be released entirely */
5101 if (scp->serverLock == LockWrite) {
5102 osi_Log0(afsd_logp, " RELEASE LockWrite -> LockNone");
5104 /* Make sure there are no dirty buffers left. */
5105 code = cm_FSync(scp, userp, reqp, TRUE);
5107 osi_Log0(afsd_logp, " RELEASE LockRead -> LockNone");
5110 code = cm_IntReleaseLock(scp, userp, reqp);
5113 scp->serverLock = (-1);
5119 /* Called with scp->rw held */
5120 long cm_UnlockByKey(cm_scache_t * scp,
5127 cm_file_lock_t *fileLock;
5128 osi_queue_t *q, *qn;
5131 osi_Log4(afsd_logp, "cm_UnlockByKey scp 0x%p key <0x%x,0x%x,0x%x",
5132 scp, key.process_id, key.session_id, key.file_id);
5133 osi_Log1(afsd_logp, " flags=0x%x", flags);
5135 lock_ObtainWrite(&cm_scacheLock);
5137 for (q = scp->fileLocksH; q; q = qn) {
5140 fileLock = (cm_file_lock_t *)
5141 ((char *) q - offsetof(cm_file_lock_t, fileq));
5144 osi_Log4(afsd_logp, " Checking lock[0x%x] range[%d,+%d] type[%d]",
5146 (unsigned long) fileLock->range.offset,
5147 (unsigned long) fileLock->range.length,
5148 fileLock->lockType);
5149 osi_Log4(afsd_logp, " key<0x%x, 0x%x, 0x%x> flags[0x%x]",
5150 fileLock->key.process_id, fileLock->key.session_id, fileLock->key.file_id,
5153 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5154 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5155 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5157 fileLock->fid.volume,
5158 fileLock->fid.vnode,
5159 fileLock->fid.unique);
5160 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5161 fileLock->scp->fid.cell,
5162 fileLock->scp->fid.volume,
5163 fileLock->scp->fid.vnode,
5164 fileLock->scp->fid.unique);
5165 osi_assertx(FALSE, "invalid fid value");
5169 if (!IS_LOCK_DELETED(fileLock) &&
5170 cm_KeyEquals(&fileLock->key, &key, flags)) {
5171 osi_Log3(afsd_logp, "...Unlock range [%d,+%d] type %d",
5172 fileLock->range.offset,
5173 fileLock->range.length,
5174 fileLock->lockType);
5176 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
5178 if (IS_LOCK_CLIENTONLY(fileLock)) {
5180 } else if (IS_LOCK_ACCEPTED(fileLock)) {
5181 if (fileLock->lockType == LockRead)
5184 scp->exclusiveLocks--;
5187 fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5189 cm_ReleaseUser(fileLock->userp);
5190 cm_ReleaseSCacheNoLock(scp);
5192 fileLock->userp = NULL;
5193 fileLock->scp = NULL;
5199 lock_ReleaseWrite(&cm_scacheLock);
5201 if (n_unlocks == 0) {
5202 osi_Log0(afsd_logp, "cm_UnlockByKey no locks found");
5203 osi_Log3(afsd_logp, " Leaving scp with exclusives[%d], shared[%d], serverLock[%d]",
5204 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
5209 code = cm_IntUnlock(scp, userp, reqp);
5210 osi_Log1(afsd_logp, "cm_UnlockByKey code 0x%x", code);
5212 osi_Log4(afsd_logp, " Leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
5213 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
5214 (int)(signed char) scp->serverLock);
5219 /* Called with scp->rw held */
5220 long cm_Unlock(cm_scache_t *scp,
5221 unsigned char sLockType,
5222 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
5229 int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
5230 cm_file_lock_t *fileLock;
5232 int release_userp = FALSE;
5233 int exact_match = !(flags & CM_UNLOCK_FLAG_MATCH_RANGE);
5235 LARGE_INTEGER RangeEnd;
5237 osi_Log4(afsd_logp, "cm_Unlock scp 0x%p type 0x%x offset 0x%x length 0x%x",
5238 scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
5239 osi_Log4(afsd_logp, "... key <0x%x,0x%x,0x%x> flags 0x%x",
5240 key.process_id, key.session_id, key.file_id, flags);
5243 RangeEnd.QuadPart = LOffset.QuadPart + LLength.QuadPart;
5246 lock_ObtainRead(&cm_scacheLock);
5248 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5249 fileLock = (cm_file_lock_t *)
5250 ((char *) q - offsetof(cm_file_lock_t, fileq));
5253 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5254 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5255 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5257 fileLock->fid.volume,
5258 fileLock->fid.vnode,
5259 fileLock->fid.unique);
5260 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5261 fileLock->scp->fid.cell,
5262 fileLock->scp->fid.volume,
5263 fileLock->scp->fid.vnode,
5264 fileLock->scp->fid.unique);
5265 osi_assertx(FALSE, "invalid fid value");
5269 if (!IS_LOCK_DELETED(fileLock) &&
5270 cm_KeyEquals(&fileLock->key, &key, 0) &&
5271 fileLock->range.offset == LOffset.QuadPart &&
5272 fileLock->range.length == LLength.QuadPart) {
5278 if (!IS_LOCK_DELETED(fileLock) &&
5279 cm_KeyEquals(&fileLock->key, &key, 0) &&
5280 fileLock->range.offset >= LOffset.QuadPart &&
5281 fileLock->range.offset < RangeEnd.QuadPart &&
5282 (fileLock->range.offset + fileLock->range.length) <= RangeEnd.QuadPart) {
5290 lock_ReleaseRead(&cm_scacheLock);
5292 if (lock_found && !exact_match) {
5296 osi_Log0(afsd_logp, "cm_Unlock lock not found; failure");
5298 /* The lock didn't exist anyway. *shrug* */
5299 return CM_ERROR_RANGE_NOT_LOCKED;
5303 /* discard lock record */
5304 lock_ConvertRToW(&cm_scacheLock);
5305 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
5308 * Don't delete it here; let the daemon delete it, to simplify
5309 * the daemon's traversal of the list.
5312 if (IS_LOCK_CLIENTONLY(fileLock)) {
5314 } else if (IS_LOCK_ACCEPTED(fileLock)) {
5315 if (fileLock->lockType == LockRead)
5318 scp->exclusiveLocks--;
5321 fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5323 if (userp != NULL) {
5324 cm_ReleaseUser(fileLock->userp);
5326 userp = fileLock->userp;
5327 release_userp = TRUE;
5329 cm_ReleaseSCacheNoLock(scp);
5330 fileLock->userp = NULL;
5331 fileLock->scp = NULL;
5332 lock_ReleaseWrite(&cm_scacheLock);
5334 code = cm_IntUnlock(scp, userp, reqp);
5336 if (release_userp) {
5337 cm_ReleaseUser(userp);
5338 release_userp = FALSE;
5342 osi_Log1(afsd_logp, "cm_Unlock not exact match, searching for next lock, code 0x%x", code);
5343 goto try_again; /* might be more than one lock in the range */
5348 osi_Log1(afsd_logp, "cm_Unlock code 0x%x", code);
5349 osi_Log4(afsd_logp, " leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
5350 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
5351 (int)(signed char) scp->serverLock);
5356 /* called with scp->rw held */
5357 void cm_LockMarkSCacheLost(cm_scache_t * scp)
5359 cm_file_lock_t *fileLock;
5362 osi_Log1(afsd_logp, "cm_LockMarkSCacheLost scp 0x%x", scp);
5364 /* cm_scacheLock needed because we are modifying fileLock->flags */
5365 lock_ObtainWrite(&cm_scacheLock);
5367 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5369 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
5371 if (IS_LOCK_ACTIVE(fileLock) &&
5372 !IS_LOCK_CLIENTONLY(fileLock)) {
5373 if (fileLock->lockType == LockRead)
5376 scp->exclusiveLocks--;
5378 fileLock->flags |= CM_FILELOCK_FLAG_LOST;
5382 scp->serverLock = -1;
5383 scp->lockDataVersion = CM_SCACHE_VERSION_BAD;
5384 lock_ReleaseWrite(&cm_scacheLock);
5387 /* Called with no relevant locks held */
5388 void cm_CheckLocks()
5390 osi_queue_t *q, *nq;
5391 cm_file_lock_t *fileLock;
5397 struct rx_connection * rxconnp;
5400 memset(&volSync, 0, sizeof(volSync));
5404 lock_ObtainWrite(&cm_scacheLock);
5406 cm_lockRefreshCycle++;
5408 osi_Log1(afsd_logp, "cm_CheckLocks starting lock check cycle %d", cm_lockRefreshCycle);
5410 for (q = cm_allFileLocks; q; q = nq) {
5411 fileLock = (cm_file_lock_t *) q;
5415 if (IS_LOCK_DELETED(fileLock)) {
5416 cm_user_t *userp = fileLock->userp;
5417 cm_scache_t *scp = fileLock->scp;
5418 fileLock->userp = NULL;
5419 fileLock->scp = NULL;
5422 lock_ReleaseWrite(&cm_scacheLock);
5423 lock_ObtainWrite(&scp->rw);
5424 code = cm_IntUnlock(scp, userp, &req);
5425 lock_ReleaseWrite(&scp->rw);
5427 cm_ReleaseUser(userp);
5428 lock_ObtainWrite(&cm_scacheLock);
5429 cm_ReleaseSCacheNoLock(scp);
5431 osi_QRemove(&cm_allFileLocks, q);
5432 cm_PutFileLock(fileLock);
5434 } else if (IS_LOCK_ACTIVE(fileLock) && !IS_LOCK_CLIENTONLY(fileLock)) {
5436 /* Server locks must have been enabled for us to have
5437 received an active non-client-only lock. */
5438 osi_assertx(cm_enableServerLocks, "!cm_enableServerLocks");
5440 scp = fileLock->scp;
5441 osi_assertx(scp != NULL, "null cm_scache_t");
5443 cm_HoldSCacheNoLock(scp);
5446 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5447 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5448 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5450 fileLock->fid.volume,
5451 fileLock->fid.vnode,
5452 fileLock->fid.unique);
5453 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5454 fileLock->scp->fid.cell,
5455 fileLock->scp->fid.volume,
5456 fileLock->scp->fid.vnode,
5457 fileLock->scp->fid.unique);
5458 osi_assertx(FALSE, "invalid fid");
5461 /* Server locks are extended once per scp per refresh
5463 if (scp->lastRefreshCycle != cm_lockRefreshCycle) {
5465 int scp_done = FALSE;
5467 osi_Log1(afsd_logp, "cm_CheckLocks Updating scp 0x%x", scp);
5469 lock_ReleaseWrite(&cm_scacheLock);
5470 lock_ObtainWrite(&scp->rw);
5472 /* did the lock change while we weren't holding the lock? */
5473 if (!IS_LOCK_ACTIVE(fileLock))
5474 goto post_syncopdone;
5476 code = cm_SyncOp(scp, NULL, fileLock->userp, &req, 0,
5477 CM_SCACHESYNC_NEEDCALLBACK
5478 | CM_SCACHESYNC_GETSTATUS
5479 | CM_SCACHESYNC_LOCK);
5483 "cm_CheckLocks SyncOp failure code 0x%x", code);
5484 goto post_syncopdone;
5487 /* cm_SyncOp releases scp->rw during which the lock
5488 may get released. */
5489 if (!IS_LOCK_ACTIVE(fileLock))
5490 goto pre_syncopdone;
5492 if (scp->serverLock != -1 && !(scp->flags & CM_SCACHEFLAG_DELETED)) {
5496 tfid.Volume = scp->fid.volume;
5497 tfid.Vnode = scp->fid.vnode;
5498 tfid.Unique = scp->fid.unique;
5500 userp = fileLock->userp;
5502 osi_Log3(afsd_logp, "CALL ExtendLock lock 0x%p for scp=0x%p with lock %d",
5505 (int) scp->serverLock);
5507 lock_ReleaseWrite(&scp->rw);
5510 code = cm_ConnFromFID(&cfid, userp,
5515 rxconnp = cm_GetRxConn(connp);
5516 code = RXAFS_ExtendLock(rxconnp, &tfid,
5518 rx_PutConnection(rxconnp);
5520 osi_Log1(afsd_logp, " ExtendLock returns %d", code);
5522 } while (cm_Analyze(connp, userp, &req,
5523 &cfid, &volSync, NULL, NULL,
5526 code = cm_MapRPCError(code, &req);
5528 lock_ObtainWrite(&scp->rw);
5531 osi_Log1(afsd_logp, "CALL ExtendLock FAILURE, code 0x%x", code);
5532 scp->fsLockCount = 0;
5534 osi_Log0(afsd_logp, "CALL ExtendLock SUCCESS");
5535 scp->lockDataVersion = scp->dataVersion;
5538 if ((code == EINVAL || code == CM_ERROR_INVAL) &&
5539 scp->lockDataVersion == scp->dataVersion) {
5543 (scp->exclusiveLocks > 0) ? LockWrite: LockRead;
5545 /* we might still have a chance to obtain a
5548 code = cm_IntSetLock(scp, userp, lockType, &req);
5551 code = CM_ERROR_INVAL;
5552 } else if (scp->lockDataVersion != scp->dataVersion) {
5554 /* now check if we still have the file at
5555 the right data version. */
5557 "Data version mismatch on scp 0x%p",
5560 " Data versions: before=%I64d, after=%I64d",
5561 scp->lockDataVersion,
5564 code = cm_IntReleaseLock(scp, userp, &req);
5566 code = CM_ERROR_INVAL;
5570 if (code == EINVAL || code == CM_ERROR_INVAL ||
5571 code == CM_ERROR_BADFD) {
5572 cm_LockMarkSCacheLost(scp);
5576 /* interestingly, we have found an active lock
5577 belonging to an scache that has no
5579 cm_LockMarkSCacheLost(scp);
5586 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5589 lock_ReleaseWrite(&scp->rw);
5591 lock_ObtainWrite(&cm_scacheLock);
5594 fileLock->lastUpdate = time(NULL);
5598 scp->lastRefreshCycle = cm_lockRefreshCycle;
5601 /* we have already refreshed the locks on this scp */
5602 fileLock->lastUpdate = time(NULL);
5605 cm_ReleaseSCacheNoLock(scp);
5607 } else if (IS_LOCK_ACTIVE(fileLock) && IS_LOCK_CLIENTONLY(fileLock)) {
5608 /* TODO: Check callbacks */
5612 lock_ReleaseWrite(&cm_scacheLock);
5613 osi_Log1(afsd_logp, "cm_CheckLocks completes lock check cycle %d", cm_lockRefreshCycle);
5616 /* NOT called with scp->rw held. */
5617 long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
5620 cm_scache_t *scp = NULL;
5621 cm_file_lock_t *fileLock;
5625 int force_client_lock = FALSE;
5626 int has_insert = FALSE;
5627 int check_data_version = FALSE;
5631 if (client_is_dead) {
5632 code = CM_ERROR_TIMEDOUT;
5636 lock_ObtainRead(&cm_scacheLock);
5638 osi_Log2(afsd_logp, "cm_RetryLock checking lock %p (scp=%p)", oldFileLock, oldFileLock->scp);
5639 osi_Log4(afsd_logp, " offset(%x:%x) length(%x:%x)",
5640 (unsigned)(oldFileLock->range.offset >> 32),
5641 (unsigned)(oldFileLock->range.offset & 0xffffffff),
5642 (unsigned)(oldFileLock->range.length >> 32),
5643 (unsigned)(oldFileLock->range.length & 0xffffffff));
5644 osi_Log4(afsd_logp, " key<0x%x,0x%x,0x%x> flags=%x",
5645 oldFileLock->key.process_id, oldFileLock->key.session_id, oldFileLock->key.file_id,
5646 (unsigned)(oldFileLock->flags));
5648 /* if the lock has already been granted, then we have nothing to do */
5649 if (IS_LOCK_ACTIVE(oldFileLock)) {
5650 lock_ReleaseRead(&cm_scacheLock);
5651 osi_Log0(afsd_logp, "cm_RetryLock lock already granted");
5655 /* we can't do anything with lost or deleted locks at the moment. */
5656 if (IS_LOCK_LOST(oldFileLock) || IS_LOCK_DELETED(oldFileLock)) {
5657 code = CM_ERROR_BADFD;
5658 osi_Log0(afsd_logp, "cm_RetryLock lock is lost or deleted");
5659 lock_ReleaseRead(&cm_scacheLock);
5663 scp = oldFileLock->scp;
5665 osi_assertx(scp != NULL, "null cm_scache_t");
5667 lock_ReleaseRead(&cm_scacheLock);
5668 lock_ObtainWrite(&scp->rw);
5670 code = cm_LockCheckPerms(scp, oldFileLock->lockType,
5674 if (code == CM_ERROR_NOACCESS && oldFileLock->lockType == LockRead) {
5675 if (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp)) {
5676 force_client_lock = TRUE;
5680 lock_ReleaseWrite(&scp->rw);
5684 lock_ObtainWrite(&cm_scacheLock);
5686 /* Check if we already have a sufficient server lock to allow this
5687 lock to go through. */
5688 if (IS_LOCK_WAITLOCK(oldFileLock) &&
5689 (!SERVERLOCKS_ENABLED(scp) ||
5690 scp->serverLock == oldFileLock->lockType ||
5691 scp->serverLock == LockWrite)) {
5693 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5695 if (SERVERLOCKS_ENABLED(scp)) {
5696 osi_Log1(afsd_logp, "cm_RetryLock Server lock (%d) is sufficient for lock. Granting",
5697 (int) scp->serverLock);
5699 osi_Log0(afsd_logp, "cm_RetryLock skipping server lock for scp");
5702 lock_ReleaseWrite(&cm_scacheLock);
5703 lock_ReleaseWrite(&scp->rw);
5708 if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5710 /* check if the conflicting locks have dissappeared already */
5711 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5713 fileLock = (cm_file_lock_t *)
5714 ((char *) q - offsetof(cm_file_lock_t, fileq));
5716 if (IS_LOCK_LOST(fileLock)) {
5717 if (cm_KeyEquals(&fileLock->key, &oldFileLock->key, 0)) {
5718 code = CM_ERROR_BADFD;
5719 oldFileLock->flags |= CM_FILELOCK_FLAG_LOST;
5720 osi_Log1(afsd_logp, " found lost lock %p for same key. Marking lock as lost",
5723 } else if (fileLock->lockType == LockWrite &&
5724 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5725 osi_Log1(afsd_logp, " found conflicting LOST lock %p", fileLock);
5726 code = CM_ERROR_WOULDBLOCK;
5731 if (IS_LOCK_ACCEPTED(fileLock) &&
5732 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5734 if (oldFileLock->lockType != LockRead ||
5735 fileLock->lockType != LockRead) {
5737 osi_Log1(afsd_logp, " found conflicting lock %p", fileLock);
5738 code = CM_ERROR_WOULDBLOCK;
5746 lock_ReleaseWrite(&cm_scacheLock);
5747 lock_ReleaseWrite(&scp->rw);
5752 /* when we get here, the lock is either a WAITUNLOCK or WAITLOCK.
5753 If it is WAITUNLOCK, then we didn't find any conflicting lock
5754 but we haven't verfied whether the serverLock is sufficient to
5755 assert it. If it is WAITLOCK, then the serverLock is
5756 insufficient to assert it. Eitherway, we are ready to accept
5757 the lock as either ACTIVE or WAITLOCK depending on the
5760 /* First, promote the WAITUNLOCK to a WAITLOCK */
5761 if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5762 if (oldFileLock->lockType == LockRead)
5765 scp->exclusiveLocks++;
5767 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITUNLOCK;
5768 oldFileLock->flags |= CM_FILELOCK_FLAG_WAITLOCK;
5771 osi_assertx(IS_LOCK_WAITLOCK(oldFileLock), "!IS_LOCK_WAITLOCK");
5773 if (force_client_lock ||
5774 !SERVERLOCKS_ENABLED(scp) ||
5775 scp->serverLock == oldFileLock->lockType ||
5776 (oldFileLock->lockType == LockRead &&
5777 scp->serverLock == LockWrite)) {
5779 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5781 if ((force_client_lock ||
5782 !SERVERLOCKS_ENABLED(scp)) &&
5783 !IS_LOCK_CLIENTONLY(oldFileLock)) {
5785 oldFileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
5787 if (oldFileLock->lockType == LockRead)
5790 scp->exclusiveLocks--;
5795 lock_ReleaseWrite(&cm_scacheLock);
5796 lock_ReleaseWrite(&scp->rw);
5803 code = cm_SyncOp(scp, NULL, oldFileLock->userp, &req, 0,
5804 CM_SCACHESYNC_NEEDCALLBACK
5805 | CM_SCACHESYNC_GETSTATUS
5806 | CM_SCACHESYNC_LOCK);
5808 osi_Log1(afsd_logp, "cm_RetryLock SyncOp failure code 0x%x", code);
5809 lock_ReleaseWrite(&cm_scacheLock);
5810 goto post_syncopdone;
5813 if (!IS_LOCK_WAITLOCK(oldFileLock))
5814 goto pre_syncopdone;
5816 userp = oldFileLock->userp;
5818 #ifndef AGGRESSIVE_LOCKS
5819 newLock = oldFileLock->lockType;
5821 newLock = LockWrite;
5825 /* if has_insert is non-zero, then:
5826 - the lock a LockRead
5827 - we don't have permission to get a LockRead
5828 - we do have permission to get a LockWrite
5829 - the server supports VICED_CAPABILITY_WRITELOCKACL
5832 newLock = LockWrite;
5835 lock_ReleaseWrite(&cm_scacheLock);
5837 /* when we get here, either we have a read-lock and want a
5838 write-lock or we don't have any locks and we want some
5841 if (scp->serverLock == LockRead) {
5843 osi_assertx(newLock == LockWrite, "!LockWrite");
5845 osi_Log0(afsd_logp, " Attempting to UPGRADE from LockRead to LockWrite");
5847 scp->lockDataVersion = scp->dataVersion;
5848 check_data_version = TRUE;
5850 code = cm_IntReleaseLock(scp, userp, &req);
5853 goto pre_syncopdone;
5855 scp->serverLock = -1;
5858 code = cm_IntSetLock(scp, userp, newLock, &req);
5861 if (scp->dataVersion != scp->lockDataVersion) {
5862 /* we lost a race. too bad */
5865 " Data version mismatch while upgrading lock.");
5867 " Data versions before=%I64d, after=%I64d",
5868 scp->lockDataVersion,
5871 " Releasing stale lock for scp 0x%x", scp);
5873 code = cm_IntReleaseLock(scp, userp, &req);
5875 scp->serverLock = -1;
5877 code = CM_ERROR_INVAL;
5879 cm_LockMarkSCacheLost(scp);
5881 scp->serverLock = newLock;
5886 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5892 if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
5893 lock_ObtainWrite(&cm_scacheLock);
5894 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, &oldFileLock->fileq);
5895 lock_ReleaseWrite(&cm_scacheLock);
5897 lock_ReleaseWrite(&scp->rw);
5900 lock_ObtainWrite(&cm_scacheLock);
5902 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5903 } else if (code != CM_ERROR_WOULDBLOCK) {
5904 oldFileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5905 cm_ReleaseUser(oldFileLock->userp);
5906 oldFileLock->userp = NULL;
5907 if (oldFileLock->scp) {
5908 cm_ReleaseSCacheNoLock(oldFileLock->scp);
5909 oldFileLock->scp = NULL;
5912 lock_ReleaseWrite(&cm_scacheLock);
5917 cm_key_t cm_GenerateKey(afs_uint16 session_id, afs_offs_t process_id, afs_uint64 file_id)
5921 key.process_id = process_id;
5922 key.session_id = session_id;
5923 key.file_id = file_id;
5928 int cm_KeyEquals(cm_key_t *k1, cm_key_t *k2, int flags)
5930 return (k1->session_id == k2->session_id) && (k1->file_id == k2->file_id) &&
5931 ((flags & CM_UNLOCK_FLAG_BY_FID) || (k1->process_id == k2->process_id));
5934 void cm_ReleaseAllLocks(void)
5940 cm_file_lock_t *fileLock;
5943 for (i = 0; i < cm_data.scacheHashTableSize; i++)
5945 for ( scp = cm_data.scacheHashTablep[i]; scp; scp = scp->nextp ) {
5946 while (scp->fileLocksH != NULL) {
5947 lock_ObtainWrite(&scp->rw);
5948 lock_ObtainWrite(&cm_scacheLock);
5949 if (!scp->fileLocksH) {
5950 lock_ReleaseWrite(&cm_scacheLock);
5951 lock_ReleaseWrite(&scp->rw);
5954 fileLock = (cm_file_lock_t *)((char *) scp->fileLocksH - offsetof(cm_file_lock_t, fileq));
5955 userp = fileLock->userp;
5957 key = fileLock->key;
5958 cm_HoldSCacheNoLock(scp);
5959 lock_ReleaseWrite(&cm_scacheLock);
5960 cm_UnlockByKey(scp, key, 0, userp, &req);
5961 cm_ReleaseSCache(scp);
5962 cm_ReleaseUser(userp);
5963 lock_ReleaseWrite(&scp->rw);