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))
163 case CM_ERROR_ALLOFFLINE:
164 case CM_ERROR_ALLDOWN:
165 case CM_ERROR_ALLBUSY:
166 case CM_ERROR_TIMEDOUT:
168 case CM_ERROR_WOULDBLOCK:
171 code = CM_ERROR_SHARING_VIOLATION;
176 } else if (code != 0) {
180 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
184 lock_ReleaseWrite(&scp->rw);
189 /* return success if we can open this file in this mode */
190 long cm_CheckNTOpen(cm_scache_t *scp, unsigned int desiredAccess,
191 unsigned int createDisp, cm_user_t *userp, cm_req_t *reqp,
192 cm_lock_data_t **ldpp)
197 osi_assertx(ldpp != NULL, "null cm_lock_data_t");
200 /* Always allow delete; the RPC will tell us if it's OK */
203 if (desiredAccess == DELETE)
206 if (desiredAccess & (AFS_ACCESS_READ|AFS_ACCESS_EXECUTE))
207 rights |= (scp->fileType == CM_SCACHETYPE_DIRECTORY ? PRSFS_LOOKUP : PRSFS_READ);
209 /* We used to require PRSFS_WRITE if createDisp was 4
210 (OPEN_ALWAYS) even if AFS_ACCESS_WRITE was not requested.
211 However, we don't need to do that since the existence of the
212 scp implies that we don't need to create it. */
213 if (desiredAccess & AFS_ACCESS_WRITE)
214 rights |= PRSFS_WRITE;
216 if (desiredAccess & DELETE)
217 rights |= PRSFS_DELETE;
219 lock_ObtainWrite(&scp->rw);
221 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
222 CM_SCACHESYNC_GETSTATUS
223 | CM_SCACHESYNC_NEEDCALLBACK
224 | CM_SCACHESYNC_LOCK);
227 * If the open will fail because the volume is readonly, then we will
228 * return an access denied error instead. This is to help brain-dead
229 * apps run correctly on replicated volumes.
230 * See defect 10007 for more information.
232 if (code == CM_ERROR_READONLY)
233 code = CM_ERROR_NOACCESS;
236 ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
237 scp->fileType == CM_SCACHETYPE_FILE) {
239 unsigned int sLockType;
240 LARGE_INTEGER LOffset, LLength;
242 /* Check if there's some sort of lock on the file at the
245 key = cm_GenerateKey(CM_SESSION_CMINT,0,0);
246 if (rights & PRSFS_WRITE)
249 sLockType = LOCKING_ANDX_SHARED_LOCK;
251 /* single byte lock at offset 0x0100 0000 0000 0000 */
252 LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
253 LOffset.LowPart = CM_FLSHARE_OFFSET_LOW;
254 LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
255 LLength.LowPart = CM_FLSHARE_LENGTH_LOW;
257 code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
260 (*ldpp) = (cm_lock_data_t *)malloc(sizeof(cm_lock_data_t));
267 (*ldpp)->sLockType = sLockType;
268 (*ldpp)->LOffset.HighPart = LOffset.HighPart;
269 (*ldpp)->LOffset.LowPart = LOffset.LowPart;
270 (*ldpp)->LLength.HighPart = LLength.HighPart;
271 (*ldpp)->LLength.LowPart = LLength.LowPart;
273 /* In this case, we allow the file open to go through even
274 though we can't enforce mandatory locking on the
276 if (code == CM_ERROR_NOACCESS &&
277 !(rights & PRSFS_WRITE))
281 case CM_ERROR_ALLOFFLINE:
282 case CM_ERROR_ALLDOWN:
283 case CM_ERROR_ALLBUSY:
284 case CM_ERROR_TIMEDOUT:
286 case CM_ERROR_WOULDBLOCK:
289 code = CM_ERROR_SHARING_VIOLATION;
293 } else if (code != 0) {
298 lock_ReleaseWrite(&scp->rw);
301 osi_Log3(afsd_logp,"cm_CheckNTOpen scp 0x%p ldp 0x%p code 0x%x", scp, *ldpp, code);
305 extern long cm_CheckNTOpenDone(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp,
306 cm_lock_data_t ** ldpp)
308 osi_Log2(afsd_logp,"cm_CheckNTOpenDone scp 0x%p ldp 0x%p", scp, *ldpp);
309 lock_ObtainWrite(&scp->rw);
311 cm_Unlock(scp, (*ldpp)->sLockType, (*ldpp)->LOffset, (*ldpp)->LLength,
312 (*ldpp)->key, 0, userp, reqp);
316 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
317 lock_ReleaseWrite(&scp->rw);
321 * When CAP_NT_SMBS has been negotiated, deletion (of files or directories) is
322 * done in three steps:
323 * (1) open for deletion (NT_CREATE_AND_X)
324 * (2) set for deletion on close (NT_TRANSACTION2, SET_FILE_INFO)
326 * We must not do the RPC until step 3. But if we are going to return an error
327 * code (e.g. directory not empty), we must return it by step 2, otherwise most
328 * clients will not notice it. So we do a preliminary check. For deleting
329 * files, this is almost free, since we have already done the RPC to get the
330 * parent directory's status bits. But for deleting directories, we must do an
331 * additional RPC to get the directory's data to check if it is empty. Sigh.
333 long cm_CheckNTDelete(cm_scache_t *dscp, cm_scache_t *scp, cm_user_t *userp,
339 cm_dirEntry_t *dep = 0;
340 unsigned short *hashTable;
342 int BeyondPage = 0, HaveDot = 0, HaveDotDot = 0;
345 /* First check permissions */
346 lock_ObtainWrite(&scp->rw);
347 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_DELETE,
348 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
350 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
351 lock_ReleaseWrite(&scp->rw);
355 /* If deleting directory, must be empty */
357 if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
360 thyper.HighPart = 0; thyper.LowPart = 0;
361 code = buf_Get(scp, &thyper, reqp, &bufferp);
365 lock_ObtainMutex(&bufferp->mx);
366 lock_ObtainWrite(&scp->rw);
369 code = cm_SyncOp(scp, bufferp, userp, reqp, 0,
370 CM_SCACHESYNC_NEEDCALLBACK
372 | CM_SCACHESYNC_BUFLOCKED);
376 if (cm_HaveBuffer(scp, bufferp, 1))
379 /* otherwise, load the buffer and try again */
380 lock_ReleaseMutex(&bufferp->mx);
381 code = cm_GetBuffer(scp, bufferp, NULL, userp, reqp);
382 lock_ReleaseWrite(&scp->rw);
383 lock_ObtainMutex(&bufferp->mx);
384 lock_ObtainWrite(&scp->rw);
385 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
390 lock_ReleaseWrite(&scp->rw);
393 /* We try to determine emptiness without looking beyond the first page,
394 * and without assuming "." and ".." are present and are on the first
395 * page (though these assumptions might, after all, be reasonable).
397 hashTable = (unsigned short *)(bufferp->datap + (32 * 5));
398 for (i=0; i<128; i++) {
399 idx = ntohs(hashTable[i]);
405 dep = (cm_dirEntry_t *)(bufferp->datap + (32 * idx));
406 if (strcmp(dep->name, ".") == 0)
408 else if (strcmp(dep->name, "..") == 0)
411 code = CM_ERROR_NOTEMPTY;
414 idx = ntohs(dep->next);
417 if (BeyondPage && HaveDot && HaveDotDot)
418 code = CM_ERROR_NOTEMPTY;
422 lock_ReleaseMutex(&bufferp->mx);
423 buf_Release(bufferp);
425 lock_ReleaseWrite(&scp->rw);
430 * Iterate through all entries in a directory.
431 * When the function funcp is called, the buffer is locked but the
432 * directory vnode is not.
434 * If the retscp parameter is not NULL, the parmp must be a
435 * cm_lookupSearch_t object.
437 long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
438 osi_hyper_t *startOffsetp, cm_user_t *userp, cm_req_t *reqp,
439 cm_scache_t **retscp)
443 cm_dirEntry_t *dep = 0;
446 osi_hyper_t dirLength;
447 osi_hyper_t bufferOffset;
448 osi_hyper_t curOffset;
452 cm_pageHeader_t *pageHeaderp;
454 long nextEntryCookie;
455 int numDirChunks; /* # of 32 byte dir chunks in this entry */
457 /* get the directory size */
458 lock_ObtainWrite(&scp->rw);
459 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_LOOKUP,
460 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
461 lock_ReleaseWrite(&scp->rw);
465 if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
466 return CM_ERROR_NOTDIR;
468 if (retscp) /* if this is a lookup call */
470 cm_lookupSearch_t* sp = parmp;
473 #ifdef AFS_FREELANCE_CLIENT
474 /* Freelance entries never end up in the DNLC because they
475 * do not have an associated cm_server_t
477 !(cm_freelanceEnabled &&
478 sp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
479 sp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
480 #else /* !AFS_FREELANCE_CLIENT */
485 int casefold = sp->caseFold;
486 sp->caseFold = 0; /* we have a strong preference for exact matches */
487 if ( *retscp = cm_dnlcLookup(scp, sp)) /* dnlc hit */
489 sp->caseFold = casefold;
492 sp->caseFold = casefold;
496 * see if we can find it using the directory hash tables.
497 * we can only do exact matches, since the hash is case
500 if (funcp != (cm_DirFuncp_t)cm_BPlusDirFoo)
509 code = cm_BeginDirOp(scp, userp, reqp, CM_DIRLOCK_READ,
510 CM_DIROP_FLAG_NONE, &dirop);
514 code = cm_BPlusDirLookup(&dirop, sp->nsearchNamep, &sp->fid);
519 code = cm_DirLookup(&dirop, sp->searchNamep, &sp->fid);
527 sp->ExactFound = TRUE;
528 *retscp = NULL; /* force caller to call cm_GetSCache() */
533 if (sp->caseFold && code == CM_ERROR_INEXACT_MATCH) {
536 sp->ExactFound = FALSE;
537 *retscp = NULL; /* force caller to call cm_GetSCache() */
541 return CM_ERROR_BPLUS_NOMATCH;
548 * XXX We only get the length once. It might change when we drop the
551 dirLength = scp->length;
554 bufferOffset.LowPart = bufferOffset.HighPart = 0;
556 curOffset = *startOffsetp;
558 curOffset.HighPart = 0;
559 curOffset.LowPart = 0;
563 /* make sure that curOffset.LowPart doesn't point to the first
564 * 32 bytes in the 2nd through last dir page, and that it
565 * doesn't point at the first 13 32-byte chunks in the first
566 * dir page, since those are dir and page headers, and don't
567 * contain useful information.
569 temp = curOffset.LowPart & (2048-1);
570 if (curOffset.HighPart == 0 && curOffset.LowPart < 2048) {
571 /* we're in the first page */
572 if (temp < 13*32) temp = 13*32;
575 /* we're in a later dir page */
576 if (temp < 32) temp = 32;
579 /* make sure the low order 5 bits are zero */
582 /* now put temp bits back ito curOffset.LowPart */
583 curOffset.LowPart &= ~(2048-1);
584 curOffset.LowPart |= temp;
586 /* check if we've passed the dir's EOF */
587 if (LargeIntegerGreaterThanOrEqualTo(curOffset, dirLength))
590 /* see if we can use the bufferp we have now; compute in which
591 * page the current offset would be, and check whether that's
592 * the offset of the buffer we have. If not, get the buffer.
594 thyper.HighPart = curOffset.HighPart;
595 thyper.LowPart = curOffset.LowPart & ~(cm_data.buf_blockSize-1);
596 if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
599 lock_ReleaseMutex(&bufferp->mx);
600 buf_Release(bufferp);
604 code = buf_Get(scp, &thyper, reqp, &bufferp);
606 /* if buf_Get() fails we do not have a buffer object to lock */
611 lock_ObtainMutex(&bufferp->mx);
612 bufferOffset = thyper;
614 /* now get the data in the cache */
616 lock_ObtainWrite(&scp->rw);
617 code = cm_SyncOp(scp, bufferp, userp, reqp,
619 CM_SCACHESYNC_NEEDCALLBACK
621 | CM_SCACHESYNC_BUFLOCKED);
623 lock_ReleaseWrite(&scp->rw);
626 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
628 if (cm_HaveBuffer(scp, bufferp, 1)) {
629 lock_ReleaseWrite(&scp->rw);
633 /* otherwise, load the buffer and try again */
634 lock_ReleaseMutex(&bufferp->mx);
635 code = cm_GetBuffer(scp, bufferp, NULL, userp,
637 lock_ReleaseWrite(&scp->rw);
638 lock_ObtainMutex(&bufferp->mx);
643 lock_ReleaseMutex(&bufferp->mx);
644 buf_Release(bufferp);
648 } /* if (wrong buffer) ... */
650 /* now we have the buffer containing the entry we're interested
651 * in; copy it out if it represents a non-deleted entry.
653 entryInDir = curOffset.LowPart & (2048-1);
654 entryInBuffer = curOffset.LowPart & (cm_data.buf_blockSize - 1);
656 /* page header will help tell us which entries are free. Page
657 * header can change more often than once per buffer, since
658 * AFS 3 dir page size may be less than (but not more than) a
659 * buffer package buffer.
661 /* only look intra-buffer */
662 temp = curOffset.LowPart & (cm_data.buf_blockSize - 1);
663 temp &= ~(2048 - 1); /* turn off intra-page bits */
664 pageHeaderp = (cm_pageHeader_t *) (bufferp->datap + temp);
666 /* now determine which entry we're looking at in the page. If
667 * it is free (there's a free bitmap at the start of the dir),
668 * we should skip these 32 bytes.
670 slotInPage = (entryInDir & 0x7e0) >> 5;
671 if (!(pageHeaderp->freeBitmap[slotInPage>>3]
672 & (1 << (slotInPage & 0x7)))) {
673 /* this entry is free */
674 numDirChunks = 1; /* only skip this guy */
678 tp = bufferp->datap + entryInBuffer;
679 dep = (cm_dirEntry_t *) tp; /* now points to AFS3 dir entry */
682 * here are some consistency checks
684 if (dep->flag != CM_DIR_FFIRST ||
685 strlen(dep->name) > 256) {
686 code = CM_ERROR_INVAL;
688 "cm_ApplyDir invalid directory entry for scp %p bufp %p",
690 osi_Log4(afsd_logp,"... cell %u vol %u vnode %u uniq %u",
691 scp->fid.cell, scp->fid.volume, scp->fid.vnode, scp->fid.unique);
692 bufferp->dataVersion = CM_BUF_VERSION_BAD;
696 /* while we're here, compute the next entry's location, too,
697 * since we'll need it when writing out the cookie into the
698 * dir listing stream.
700 numDirChunks = cm_NameEntries(dep->name, NULL);
702 /* compute the offset of the cookie representing the next entry */
703 nextEntryCookie = curOffset.LowPart
704 + (CM_DIR_CHUNKSIZE * numDirChunks);
706 if (dep->fid.vnode != 0) {
707 /* this is one of the entries to use: it is not deleted */
708 code = (*funcp)(scp, dep, parmp, &curOffset);
711 } /* if we're including this name */
714 /* and adjust curOffset to be where the new cookie is */
716 thyper.LowPart = CM_DIR_CHUNKSIZE * numDirChunks;
717 curOffset = LargeIntegerAdd(thyper, curOffset);
718 } /* while copying data for dir listing */
720 /* release the mutex */
722 lock_ReleaseMutex(&bufferp->mx);
723 buf_Release(bufferp);
728 int cm_NoneUpper(normchar_t *s)
732 if (c >= 'A' && c <= 'Z')
737 int cm_NoneLower(normchar_t *s)
741 if (c >= 'a' && c <= 'z')
746 long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
749 cm_lookupSearch_t *sp;
751 normchar_t matchName[MAX_PATH];
752 int looking_for_short_name = FALSE;
754 sp = (cm_lookupSearch_t *) rockp;
756 if (cm_FsStringToNormString(dep->name, -1, matchName, lengthof(matchName)) == 0) {
757 /* Can't normalize FS string. */
762 match = cm_NormStrCmpI(matchName, sp->nsearchNamep);
764 match = cm_NormStrCmp(matchName, sp->nsearchNamep);
768 && !cm_Is8Dot3(matchName)) {
770 cm_Gen8Dot3NameInt(dep->name, &dep->fid, matchName, NULL);
772 match = cm_NormStrCmpI(matchName, sp->nsearchNamep);
774 match = cm_NormStrCmp(matchName, sp->nsearchNamep);
775 looking_for_short_name = TRUE;
785 if (!sp->caseFold || looking_for_short_name) {
786 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
787 return CM_ERROR_STOPNOW;
791 * If we get here, we are doing a case-insensitive search, and we
792 * have found a match. Now we determine what kind of match it is:
793 * exact, lower-case, upper-case, or none of the above. This is done
794 * in order to choose among matches, if there are more than one.
797 /* Exact matches are the best. */
798 match = cm_NormStrCmp(matchName, sp->nsearchNamep);
801 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
802 return CM_ERROR_STOPNOW;
805 /* Lower-case matches are next. */
808 if (cm_NoneUpper(matchName)) {
813 /* Upper-case matches are next. */
816 if (cm_NoneLower(matchName)) {
821 /* General matches are last. */
827 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
831 /* read the contents of a mount point into the appropriate string.
832 * called with write locked scp, and returns with locked scp.
834 long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
838 if (scp->mountPointStringp[0])
841 #ifdef AFS_FREELANCE_CLIENT
842 /* File servers do not have data for freelance entries */
843 if (cm_freelanceEnabled &&
844 scp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
845 scp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
847 code = cm_FreelanceFetchMountPointString(scp);
849 #endif /* AFS_FREELANCE_CLIENT */
851 char temp[MOUNTPOINTLEN];
854 /* otherwise, we have to read it in */
855 offset.LowPart = offset.HighPart = 0;
856 code = cm_GetData(scp, &offset, temp, MOUNTPOINTLEN, userp, reqp);
861 * scp->length is the actual length of the mount point string.
862 * It is current because cm_GetData merged the most up to date
863 * status info into scp and has not dropped the rwlock since.
865 if (scp->length.LowPart > MOUNTPOINTLEN - 1)
866 return CM_ERROR_TOOBIG;
867 if (scp->length.LowPart == 0)
868 return CM_ERROR_INVAL;
870 /* convert the terminating dot to a NUL */
871 temp[scp->length.LowPart - 1] = 0;
872 memcpy(scp->mountPointStringp, temp, scp->length.LowPart);
879 /* called with a locked scp and chases the mount point, yielding outScpp.
880 * scp remains write locked, just for simplicity of describing the interface.
882 long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
883 cm_req_t *reqp, cm_scache_t **outScpp)
885 fschar_t *cellNamep = NULL;
886 fschar_t *volNamep = NULL;
890 cm_volume_t *volp = NULL;
899 if (scp->mountRootFid.cell != 0 && scp->mountRootGen >= cm_data.mountRootGen) {
900 tfid = scp->mountRootFid;
901 lock_ReleaseWrite(&scp->rw);
902 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
903 lock_ObtainWrite(&scp->rw);
907 /* parse the volume name */
908 mpNamep = scp->mountPointStringp;
910 return CM_ERROR_NOSUCHPATH;
911 mtType = *scp->mountPointStringp;
913 cp = cm_FsStrChr(mpNamep, _FS(':'));
915 /* cellular mount point */
916 cellNamep = (fschar_t *)malloc((cp - mpNamep) * sizeof(fschar_t));
917 cm_FsStrCpyN(cellNamep, cp - mpNamep, mpNamep + 1, cp - mpNamep - 1);
918 volNamep = cm_FsStrDup(cp+1);
920 /* now look up the cell */
921 lock_ReleaseWrite(&scp->rw);
922 cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE);
923 lock_ObtainWrite(&scp->rw);
926 volNamep = cm_FsStrDup(mpNamep + 1);
928 #ifdef AFS_FREELANCE_CLIENT
930 * Mount points in the Freelance cell should default
931 * to the workstation cell.
933 if (cm_freelanceEnabled &&
934 scp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
935 scp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
937 fschar_t rootCellName[256]="";
938 cm_GetRootCellName(rootCellName);
939 cellp = cm_GetCell(rootCellName, 0);
941 #endif /* AFS_FREELANCE_CLIENT */
942 cellp = cm_FindCellByID(scp->fid.cell, 0);
946 code = CM_ERROR_NOSUCHCELL;
950 vnLength = cm_FsStrLen(volNamep);
951 if (vnLength >= 8 && cm_FsStrCmp(volNamep + vnLength - 7, ".backup") == 0)
952 targetType = BACKVOL;
953 else if (vnLength >= 10
954 && cm_FsStrCmp(volNamep + vnLength - 9, ".readonly") == 0)
959 /* check for backups within backups */
960 if (targetType == BACKVOL
961 && (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
962 == CM_SCACHEFLAG_RO) {
963 code = CM_ERROR_NOSUCHVOLUME;
967 /* now we need to get the volume */
968 lock_ReleaseWrite(&scp->rw);
969 if (cm_VolNameIsID(volNamep)) {
970 code = cm_FindVolumeByID(cellp, atoi(volNamep), userp, reqp,
971 CM_GETVOL_FLAG_CREATE, &volp);
973 code = cm_FindVolumeByName(cellp, volNamep, userp, reqp,
974 CM_GETVOL_FLAG_CREATE, &volp);
976 lock_ObtainWrite(&scp->rw);
979 afs_uint32 cell, volume;
980 cm_vol_state_t *statep;
982 cell = cellp->cellID;
984 /* if the mt pt originates in a .backup volume (not a .readonly)
985 * and FollowBackupPath is active, and if there is a .backup
986 * volume for the target, then use the .backup of the target
987 * instead of the read-write.
989 if (cm_followBackupPath &&
990 volp->vol[BACKVOL].ID != 0 &&
991 (dscp->flags & (CM_SCACHEFLAG_RO|CM_SCACHEFLAG_PURERO)) == CM_SCACHEFLAG_RO &&
992 (targetType == RWVOL || targetType == ROVOL && volp->vol[ROVOL].ID == 0)
994 targetType = BACKVOL;
996 /* if the mt pt is in a read-only volume (not just a
997 * backup), and if there is a read-only volume for the
998 * target, and if this is a targetType '#' mount point, use
999 * the read-only, otherwise use the one specified.
1001 else if (mtType == '#' && targetType == RWVOL &&
1002 (scp->flags & CM_SCACHEFLAG_PURERO) &&
1003 volp->vol[ROVOL].ID != 0) {
1007 lock_ObtainWrite(&volp->rw);
1008 statep = cm_VolumeStateByType(volp, targetType);
1009 volume = statep->ID;
1010 statep->dotdotFid = dscp->fid;
1011 lock_ReleaseWrite(&volp->rw);
1013 /* the rest of the fid is a magic number */
1014 cm_SetFid(&scp->mountRootFid, cell, volume, 1, 1);
1015 scp->mountRootGen = cm_data.mountRootGen;
1017 tfid = scp->mountRootFid;
1018 lock_ReleaseWrite(&scp->rw);
1019 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
1020 lock_ObtainWrite(&scp->rw);
1033 long cm_LookupInternal(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_user_t *userp,
1034 cm_req_t *reqp, cm_scache_t **outScpp)
1037 int dnlcHit = 1; /* did we hit in the dnlc? yes, we did */
1038 cm_scache_t *tscp = NULL;
1039 cm_scache_t *mountedScp;
1040 cm_lookupSearch_t rock;
1042 normchar_t *nnamep = NULL;
1043 fschar_t *fnamep = NULL;
1048 memset(&rock, 0, sizeof(rock));
1050 if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
1051 && cm_ClientStrCmp(cnamep, _C("..")) == 0) {
1052 if (dscp->dotdotFid.volume == 0)
1053 return CM_ERROR_NOSUCHVOLUME;
1054 rock.fid = dscp->dotdotFid;
1056 } else if (cm_ClientStrCmp(cnamep, _C(".")) == 0) {
1057 rock.fid = dscp->fid;
1061 nnamep = cm_ClientStringToNormStringAlloc(cnamep, -1, NULL);
1063 code = CM_ERROR_NOSUCHFILE;
1066 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
1068 code = CM_ERROR_NOSUCHFILE;
1073 if (flags & CM_FLAG_NOMOUNTCHASE) {
1074 /* In this case, we should go and call cm_Dir* functions
1075 directly since the following cm_ApplyDir() function will
1083 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ,
1084 CM_DIROP_FLAG_NONE, &dirop);
1087 code = cm_BPlusDirLookup(&dirop, nnamep, &rock.fid);
1092 code = cm_DirLookup(&dirop, fnamep, &rock.fid);
1094 cm_EndDirOp(&dirop);
1104 if (code == CM_ERROR_INEXACT_MATCH && (flags & CM_FLAG_CASEFOLD)) {
1111 code = CM_ERROR_BPLUS_NOMATCH;
1117 rock.fid.cell = dscp->fid.cell;
1118 rock.fid.volume = dscp->fid.volume;
1119 rock.searchNamep = fnamep;
1120 rock.nsearchNamep = nnamep;
1121 rock.caseFold = (flags & CM_FLAG_CASEFOLD);
1122 rock.hasTilde = ((cm_ClientStrChr(cnamep, '~') != NULL) ? 1 : 0);
1124 /* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
1125 code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
1126 (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
1128 /* code == 0 means we fell off the end of the dir, while stopnow means
1129 * that we stopped early, probably because we found the entry we're
1130 * looking for. Any other non-zero code is an error.
1132 if (code && code != CM_ERROR_STOPNOW && code != CM_ERROR_BPLUS_NOMATCH) {
1133 /* if the cm_scache_t we are searching in is not a directory
1134 * we must return path not found because the error
1135 * is to describe the final component not an intermediary
1137 if (code == CM_ERROR_NOTDIR) {
1138 if (flags & CM_FLAG_CHECKPATH)
1139 code = CM_ERROR_NOSUCHPATH;
1141 code = CM_ERROR_NOSUCHFILE;
1147 getroot = (dscp==cm_data.rootSCachep) ;
1149 if (!cm_freelanceEnabled || !getroot) {
1150 if (flags & CM_FLAG_CHECKPATH)
1151 code = CM_ERROR_NOSUCHPATH;
1153 code = CM_ERROR_NOSUCHFILE;
1156 else if (!cm_ClientStrChr(cnamep, '#') &&
1157 !cm_ClientStrChr(cnamep, '%') &&
1158 cm_ClientStrCmpI(cnamep, _C("srvsvc")) &&
1159 cm_ClientStrCmpI(cnamep, _C("wkssvc")) &&
1160 cm_ClientStrCmpI(cnamep, _C("ipc$")))
1162 /* nonexistent dir on freelance root, so add it */
1163 fschar_t fullname[CELL_MAXNAMELEN + 1] = "."; /* +1 so that when we skip the . the size is still CELL_MAXNAMELEN */
1167 osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %S",
1168 osi_LogSaveClientString(afsd_logp,cnamep));
1171 * There is an ugly behavior where a share name "foo" will be searched
1172 * for as "fo". If the searched for name differs by an already existing
1173 * symlink or mount point in the Freelance directory, do not add the
1174 * new value automatically.
1178 fnlen = strlen(fnamep);
1179 if ( fnamep[fnlen-1] == '.') {
1180 fnamep[fnlen-1] = '\0';
1185 if (cnamep[0] == '.') {
1186 if (cm_GetCell_Gen(&fnamep[1], &fullname[1], CM_FLAG_CREATE)) {
1188 code = cm_FreelanceAddMount(fullname, &fullname[1], "root.cell", 1, &rock.fid);
1189 if ( cm_FsStrCmpI(&fnamep[1], &fullname[1])) {
1191 * Do not permit symlinks that are one of:
1192 * . the cellname followed by a dot
1193 * . the cellname minus a single character
1194 * . a substring of the cellname that does not consist of full components
1196 if ( cm_strnicmp_utf8(&fnamep[1], fullname, (int)fnlen-1) == 0 &&
1197 (fnlen-1 == strlen(fullname)-1 || fullname[fnlen-1] != '.'))
1199 /* do not add; substitute fullname for the search */
1201 fnamep = malloc(strlen(fullname)+2);
1203 strncpy(&fnamep[1], fullname, strlen(fullname)+1);
1206 code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
1211 if (cm_GetCell_Gen(fnamep, fullname, CM_FLAG_CREATE)) {
1213 code = cm_FreelanceAddMount(fullname, fullname, "root.cell", 0, &rock.fid);
1214 if ( cm_FsStrCmpI(fnamep, fullname)) {
1216 * Do not permit symlinks that are one of:
1217 * . the cellname followed by a dot
1218 * . the cellname minus a single character
1219 * . a substring of the cellname that does not consist of full components
1221 if ( cm_strnicmp_utf8(fnamep, fullname, (int)fnlen-1) == 0 &&
1222 (fnlen == strlen(fullname)-1 || fullname[fnlen] != '.'))
1224 /* do not add; substitute fullname for the search */
1226 fnamep = strdup(fullname);
1230 code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
1239 nnamep = cm_FsStringToNormStringAlloc(fnamep, -1, NULL);
1243 if (!found || code) { /* add mount point failed, so give up */
1244 if (flags & CM_FLAG_CHECKPATH)
1245 code = CM_ERROR_NOSUCHPATH;
1247 code = CM_ERROR_NOSUCHFILE;
1250 tscp = NULL; /* to force call of cm_GetSCache */
1252 if (flags & CM_FLAG_CHECKPATH)
1253 code = CM_ERROR_NOSUCHPATH;
1255 code = CM_ERROR_NOSUCHFILE;
1261 if ( !tscp ) /* we did not find it in the dnlc */
1264 code = cm_GetSCache(&rock.fid, &tscp, userp, reqp);
1268 /* tscp is now held */
1270 lock_ObtainWrite(&tscp->rw);
1273 * Do not get status if we do not already have a callback.
1274 * The process of reading the mount point string will obtain status information
1275 * in a single RPC. No reason to add a second round trip.
1277 * If we do have a callback, use cm_SyncOp to get status in case the
1278 * current cm_user_t is not the same as the one that obtained the
1279 * mount point string contents.
1281 if (cm_HaveCallback(tscp)) {
1282 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1283 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1285 lock_ReleaseWrite(&tscp->rw);
1286 cm_ReleaseSCache(tscp);
1289 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1291 /* tscp is now locked */
1293 if (!(flags & CM_FLAG_NOMOUNTCHASE)
1294 && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
1295 /* mount points are funny: they have a volume name to mount
1298 code = cm_ReadMountPoint(tscp, userp, reqp);
1300 code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
1302 lock_ReleaseWrite(&tscp->rw);
1303 cm_ReleaseSCache(tscp);
1310 lock_ReleaseWrite(&tscp->rw);
1313 /* copy back pointer */
1316 /* insert scache in dnlc */
1317 if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
1318 /* lock the directory entry to prevent racing callback revokes */
1319 lock_ObtainRead(&dscp->rw);
1320 if ( dscp->cbServerp != NULL && dscp->cbExpires > 0 ) {
1321 /* TODO: reuse nnamep from above */
1324 nnamep = cm_ClientStringToNormStringAlloc(cnamep, -1, NULL);
1326 cm_dnlcEnter(dscp, nnamep, tscp);
1328 lock_ReleaseRead(&dscp->rw);
1345 int cm_ExpandSysName(clientchar_t *inp, clientchar_t *outp, long outSizeCch, unsigned int index)
1350 tp = cm_ClientStrRChr(inp, '@');
1352 return 0; /* no @sys */
1354 if (cm_ClientStrCmp(tp, _C("@sys")) != 0)
1355 return 0; /* no @sys */
1357 /* caller just wants to know if this is a valid @sys type of name */
1361 if (index >= cm_sysNameCount)
1364 /* otherwise generate the properly expanded @sys name */
1365 prefixCount = (int)(tp - inp);
1367 cm_ClientStrCpyN(outp, outSizeCch, inp, prefixCount); /* copy out "a." from "a.@sys" */
1368 outp[prefixCount] = 0; /* null terminate the "a." */
1369 cm_ClientStrCat(outp, outSizeCch, cm_sysNameList[index]);/* append i386_nt40 */
1373 long cm_EvaluateVolumeReference(clientchar_t * namep, long flags, cm_user_t * userp,
1374 cm_req_t *reqp, cm_scache_t ** outScpp)
1376 afs_uint32 code = 0;
1377 fschar_t cellName[CELL_MAXNAMELEN];
1378 fschar_t volumeName[VL_MAXNAMELEN];
1382 fschar_t * fnamep = NULL;
1384 cm_cell_t * cellp = NULL;
1385 cm_volume_t * volp = NULL;
1389 int mountType = RWVOL;
1391 osi_Log1(afsd_logp, "cm_EvaluateVolumeReference for string [%S]",
1392 osi_LogSaveClientString(afsd_logp, namep));
1394 if (cm_ClientStrCmpNI(namep, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH) != 0) {
1395 goto _exit_invalid_path;
1398 /* namep is assumed to look like the following:
1400 @vol:<cellname>%<volume>\0
1402 @vol:<cellname>#<volume>\0
1406 fnamep = cm_ClientStringToFsStringAlloc(namep, -1, NULL);
1407 cp = fnamep + CM_PREFIX_VOL_CCH; /* cp points to cell name, hopefully */
1408 tp = cm_FsStrChr(cp, '%');
1410 tp = cm_FsStrChr(cp, '#');
1412 (len = tp - cp) == 0 ||
1413 len > CELL_MAXNAMELEN)
1414 goto _exit_invalid_path;
1415 cm_FsStrCpyN(cellName, lengthof(cellName), cp, len);
1420 cp = tp+1; /* cp now points to volume, supposedly */
1421 cm_FsStrCpy(volumeName, lengthof(volumeName), cp);
1423 /* OK, now we have the cell and the volume */
1424 osi_Log2(afsd_logp, " Found cell [%s] and volume [%s]",
1425 osi_LogSaveFsString(afsd_logp, cellName),
1426 osi_LogSaveFsString(afsd_logp, volumeName));
1428 cellp = cm_GetCell(cellName, CM_FLAG_CREATE);
1429 if (cellp == NULL) {
1430 goto _exit_invalid_path;
1433 len = cm_FsStrLen(volumeName);
1434 if (len >= 8 && cm_FsStrCmp(volumeName + len - 7, ".backup") == 0)
1436 else if (len >= 10 &&
1437 cm_FsStrCmp(volumeName + len - 9, ".readonly") == 0)
1442 if (cm_VolNameIsID(volumeName)) {
1443 code = cm_FindVolumeByID(cellp, atoi(volumeName), userp, reqp,
1444 CM_GETVOL_FLAG_CREATE, &volp);
1446 code = cm_FindVolumeByName(cellp, volumeName, userp, reqp,
1447 CM_GETVOL_FLAG_CREATE, &volp);
1453 if (volType == BACKVOL)
1454 volume = volp->vol[BACKVOL].ID;
1455 else if (volType == ROVOL ||
1456 (volType == RWVOL && mountType == ROVOL && volp->vol[ROVOL].ID != 0))
1457 volume = volp->vol[ROVOL].ID;
1459 volume = volp->vol[RWVOL].ID;
1461 cm_SetFid(&fid, cellp->cellID, volume, 1, 1);
1463 code = cm_GetSCache(&fid, outScpp, userp, reqp);
1476 if (flags & CM_FLAG_CHECKPATH)
1477 return CM_ERROR_NOSUCHPATH;
1479 return CM_ERROR_NOSUCHFILE;
1482 #ifdef DEBUG_REFCOUNT
1483 long cm_LookupDbg(cm_scache_t *dscp, clientchar_t *namep, long flags, cm_user_t *userp,
1484 cm_req_t *reqp, cm_scache_t **outScpp, char * file, long line)
1486 long cm_Lookup(cm_scache_t *dscp, clientchar_t *namep, long flags, cm_user_t *userp,
1487 cm_req_t *reqp, cm_scache_t **outScpp)
1491 clientchar_t tname[AFSPATHMAX];
1492 int sysNameIndex = 0;
1493 cm_scache_t *scp = NULL;
1495 #ifdef DEBUG_REFCOUNT
1496 afsi_log("%s:%d cm_Lookup dscp 0x%p ref %d", file, line, dscp, dscp->refCount, file, line);
1497 osi_Log2(afsd_logp, "cm_Lookup dscp 0x%p ref %d", dscp, dscp->refCount);
1500 if ( cm_ClientStrCmpI(namep,_C(SMB_IOCTL_FILENAME_NOSLASH)) == 0 ) {
1501 if (flags & CM_FLAG_CHECKPATH)
1502 return CM_ERROR_NOSUCHPATH;
1504 return CM_ERROR_NOSUCHFILE;
1507 if (dscp == cm_data.rootSCachep &&
1508 cm_ClientStrCmpNI(namep, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH) == 0) {
1509 return cm_EvaluateVolumeReference(namep, flags, userp, reqp, outScpp);
1512 if (cm_ExpandSysName(namep, NULL, 0, 0) > 0) {
1513 for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
1514 code = cm_ExpandSysName(namep, tname, lengthof(tname), sysNameIndex);
1516 code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
1517 #ifdef DEBUG_REFCOUNT
1518 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);
1519 osi_Log3(afsd_logp, "cm_LookupInternal (1) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1527 cm_ReleaseSCache(scp);
1531 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1532 #ifdef DEBUG_REFCOUNT
1533 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);
1534 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1541 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1542 #ifdef DEBUG_REFCOUNT
1543 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);
1544 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1550 /* None of the possible sysName expansions could be found */
1551 if (flags & CM_FLAG_CHECKPATH)
1552 return CM_ERROR_NOSUCHPATH;
1554 return CM_ERROR_NOSUCHFILE;
1557 /*! \brief Unlink a file name
1559 Encapsulates a call to RXAFS_RemoveFile().
1561 \param[in] dscp cm_scache_t pointing at the directory containing the
1562 name to be unlinked.
1564 \param[in] fnamep Original name to be unlinked. This is the
1565 name that will be passed into the RXAFS_RemoveFile() call.
1566 This parameter is optional. If not provided, the value will
1569 \param[in] came Client name to be unlinked. This name will be used
1570 to update the local directory caches.
1572 \param[in] userp cm_user_t for the request.
1574 \param[in] reqp Request tracker.
1577 long cm_Unlink(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t * cnamep,
1578 cm_user_t *userp, cm_req_t *reqp)
1584 AFSFetchStatus newDirStatus;
1586 struct rx_connection * rxconnp;
1588 cm_scache_t *scp = NULL;
1589 int free_fnamep = FALSE;
1591 memset(&volSync, 0, sizeof(volSync));
1593 if (fnamep == NULL) {
1596 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ,
1597 CM_DIROP_FLAG_NONE, &dirop);
1599 code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
1602 cm_EndDirOp(&dirop);
1609 #ifdef AFS_FREELANCE_CLIENT
1610 if (cm_freelanceEnabled && dscp == cm_data.rootSCachep) {
1611 /* deleting a mount point from the root dir. */
1612 code = cm_FreelanceRemoveMount(fnamep);
1617 code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
1621 /* Check for RO volume */
1622 if (dscp->flags & CM_SCACHEFLAG_RO) {
1623 code = CM_ERROR_READONLY;
1627 /* make sure we don't screw up the dir status during the merge */
1628 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE,
1629 CM_DIROP_FLAG_NONE, &dirop);
1631 lock_ObtainWrite(&dscp->rw);
1632 sflags = CM_SCACHESYNC_STOREDATA;
1633 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1634 lock_ReleaseWrite(&dscp->rw);
1636 cm_EndDirOp(&dirop);
1641 afsFid.Volume = dscp->fid.volume;
1642 afsFid.Vnode = dscp->fid.vnode;
1643 afsFid.Unique = dscp->fid.unique;
1645 osi_Log1(afsd_logp, "CALL RemoveFile scp 0x%p", dscp);
1647 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
1651 rxconnp = cm_GetRxConn(connp);
1652 code = RXAFS_RemoveFile(rxconnp, &afsFid, fnamep,
1653 &newDirStatus, &volSync);
1654 rx_PutConnection(rxconnp);
1656 } while (cm_Analyze(connp, userp, reqp, &dscp->fid, &volSync, NULL, NULL, code));
1657 code = cm_MapRPCError(code, reqp);
1660 osi_Log1(afsd_logp, "CALL RemoveFile FAILURE, code 0x%x", code);
1662 osi_Log0(afsd_logp, "CALL RemoveFile SUCCESS");
1665 lock_ObtainWrite(&dirop.scp->dirlock);
1666 dirop.lockType = CM_DIRLOCK_WRITE;
1668 lock_ObtainWrite(&dscp->rw);
1669 cm_dnlcRemove(dscp, cnamep);
1670 cm_SyncOpDone(dscp, NULL, sflags);
1672 cm_MergeStatus(NULL, dscp, &newDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
1673 } else if (code == CM_ERROR_NOSUCHFILE) {
1674 /* windows would not have allowed the request to delete the file
1675 * if it did not believe the file existed. therefore, we must
1676 * have an inconsistent view of the world.
1678 dscp->cbServerp = NULL;
1680 lock_ReleaseWrite(&dscp->rw);
1682 if (code == 0 && cm_CheckDirOpForSingleChange(&dirop) && cnamep) {
1683 cm_DirDeleteEntry(&dirop, fnamep);
1685 cm_BPlusDirDeleteEntry(&dirop, cnamep);
1688 cm_EndDirOp(&dirop);
1691 cm_ReleaseSCache(scp);
1693 lock_ObtainWrite(&scp->rw);
1694 if (--scp->linkCount == 0)
1695 scp->flags |= CM_SCACHEFLAG_DELETED;
1696 cm_DiscardSCache(scp);
1697 lock_ReleaseWrite(&scp->rw);
1708 /* called with a write locked vnode, and fills in the link info.
1709 * returns this the vnode still write locked.
1711 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1715 lock_AssertWrite(&linkScp->rw);
1716 if (!linkScp->mountPointStringp[0]) {
1718 #ifdef AFS_FREELANCE_CLIENT
1719 /* File servers do not have data for freelance entries */
1720 if (cm_freelanceEnabled &&
1721 linkScp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
1722 linkScp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
1724 code = cm_FreelanceFetchMountPointString(linkScp);
1726 #endif /* AFS_FREELANCE_CLIENT */
1728 char temp[MOUNTPOINTLEN];
1731 /* read the link data from the file server */
1732 offset.LowPart = offset.HighPart = 0;
1733 code = cm_GetData(linkScp, &offset, temp, MOUNTPOINTLEN, userp, reqp);
1738 * linkScp->length is the actual length of the symlink target string.
1739 * It is current because cm_GetData merged the most up to date
1740 * status info into scp and has not dropped the rwlock since.
1742 if (linkScp->length.LowPart > MOUNTPOINTLEN - 1)
1743 return CM_ERROR_TOOBIG;
1744 if (linkScp->length.LowPart == 0)
1745 return CM_ERROR_INVAL;
1747 /* make sure we are NUL terminated */
1748 temp[linkScp->length.LowPart] = 0;
1749 memcpy(linkScp->mountPointStringp, temp, linkScp->length.LowPart + 1);
1752 if ( !strnicmp(linkScp->mountPointStringp, "msdfs:", strlen("msdfs:")) )
1753 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1755 } /* don't have symlink contents cached */
1760 /* called with a held vnode and a path suffix, with the held vnode being a
1761 * symbolic link. Our goal is to generate a new path to interpret, and return
1762 * this new path in newSpaceBufferp. If the new vnode is relative to a dir
1763 * other than the directory containing the symbolic link, then the new root is
1764 * returned in *newRootScpp, otherwise a null is returned there.
1766 long cm_AssembleLink(cm_scache_t *linkScp, fschar_t *pathSuffixp,
1767 cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1768 cm_user_t *userp, cm_req_t *reqp)
1775 *newRootScpp = NULL;
1776 *newSpaceBufferp = NULL;
1778 lock_ObtainWrite(&linkScp->rw);
1780 * Do not get status if we do not already have a callback.
1781 * The process of reading the symlink string will obtain status information
1782 * in a single RPC. No reason to add a second round trip.
1784 * If we do have a callback, use cm_SyncOp to get status in case the
1785 * current cm_user_t is not the same as the one that obtained the
1786 * symlink string contents.
1788 if (cm_HaveCallback(linkScp)) {
1789 code = cm_SyncOp(linkScp, NULL, userp, reqp, 0,
1790 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1792 lock_ReleaseWrite(&linkScp->rw);
1793 cm_ReleaseSCache(linkScp);
1796 cm_SyncOpDone(linkScp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1798 code = cm_HandleLink(linkScp, userp, reqp);
1802 /* if we may overflow the buffer, bail out; buffer is signficantly
1803 * bigger than max path length, so we don't really have to worry about
1804 * being a little conservative here.
1806 if (cm_FsStrLen(linkScp->mountPointStringp) + cm_FsStrLen(pathSuffixp) + 2
1807 >= CM_UTILS_SPACESIZE) {
1808 code = CM_ERROR_TOOBIG;
1812 tsp = cm_GetSpace();
1813 linkp = linkScp->mountPointStringp;
1814 if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1815 if (strlen(linkp) > cm_mountRootLen)
1816 StringCbCopyA((char *) tsp->data, sizeof(tsp->data), linkp+cm_mountRootLen+1);
1819 *newRootScpp = cm_RootSCachep(userp, reqp);
1820 cm_HoldSCache(*newRootScpp);
1821 } else if (linkp[0] == '\\' && linkp[1] == '\\') {
1822 if (!strnicmp(&linkp[2], cm_NetbiosName, (len = (long)strlen(cm_NetbiosName))))
1824 char * p = &linkp[len + 3];
1825 if (strnicmp(p, "all", 3) == 0)
1828 StringCbCopyA(tsp->data, sizeof(tsp->data), p);
1829 for (p = tsp->data; *p; p++) {
1833 *newRootScpp = cm_RootSCachep(userp, reqp);
1834 cm_HoldSCache(*newRootScpp);
1836 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1837 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1838 code = CM_ERROR_PATH_NOT_COVERED;
1840 } else if ( linkScp->fileType == CM_SCACHETYPE_DFSLINK ||
1841 !strnicmp(linkp, "msdfs:", (len = (long)strlen("msdfs:"))) ) {
1842 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1843 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1844 code = CM_ERROR_PATH_NOT_COVERED;
1845 } else if (*linkp == '\\' || *linkp == '/') {
1847 /* formerly, this was considered to be from the AFS root,
1848 * but this seems to create problems. instead, we will just
1849 * reject the link */
1850 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp+1);
1851 *newRootScpp = cm_RootSCachep(userp, reqp);
1852 cm_HoldSCache(*newRootScpp);
1854 /* we still copy the link data into the response so that
1855 * the user can see what the link points to
1857 linkScp->fileType = CM_SCACHETYPE_INVALID;
1858 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1859 code = CM_ERROR_NOSUCHPATH;
1862 /* a relative link */
1863 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1865 if (pathSuffixp[0] != 0) { /* if suffix string is non-null */
1866 StringCchCatA(tsp->data,lengthof(tsp->data), "\\");
1867 StringCchCatA(tsp->data,lengthof(tsp->data), pathSuffixp);
1871 clientchar_t * cpath = cm_FsStringToClientStringAlloc(tsp->data, -1, NULL);
1872 if (cpath != NULL) {
1873 cm_ClientStrCpy(tsp->wdata, lengthof(tsp->wdata), cpath);
1875 *newSpaceBufferp = tsp;
1877 code = CM_ERROR_NOSUCHPATH;
1884 if (code == CM_ERROR_PATH_NOT_COVERED && reqp->tidPathp && reqp->relPathp) {
1885 cm_VolStatus_Notify_DFS_Mapping(linkScp, reqp->tidPathp, reqp->relPathp);
1890 lock_ReleaseWrite(&linkScp->rw);
1893 #ifdef DEBUG_REFCOUNT
1894 long cm_NameIDbg(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
1895 cm_user_t *userp, clientchar_t *tidPathp, cm_req_t *reqp,
1896 cm_scache_t **outScpp,
1897 char * file, long line)
1899 long cm_NameI(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
1900 cm_user_t *userp, clientchar_t *tidPathp,
1901 cm_req_t *reqp, cm_scache_t **outScpp)
1905 clientchar_t *tp; /* ptr moving through input buffer */
1906 clientchar_t tc; /* temp char */
1907 int haveComponent; /* has new component started? */
1908 clientchar_t component[AFSPATHMAX]; /* this is the new component */
1909 clientchar_t *cp; /* component name being assembled */
1910 cm_scache_t *tscp; /* current location in the hierarchy */
1911 cm_scache_t *nscp; /* next dude down */
1912 cm_scache_t *dirScp; /* last dir we searched */
1913 cm_scache_t *linkScp; /* new root for the symlink we just
1915 cm_space_t *psp; /* space for current path, if we've hit
1917 cm_space_t *tempsp; /* temp vbl */
1918 clientchar_t *restp; /* rest of the pathname to interpret */
1919 int symlinkCount; /* count of # of symlinks traversed */
1920 int extraFlag; /* avoid chasing mt pts for dir cmd */
1921 int phase = 1; /* 1 = tidPathp, 2 = pathp */
1922 #define MAX_FID_COUNT 512
1923 cm_fid_t fids[MAX_FID_COUNT]; /* array of fids processed in this path walk */
1924 int fid_count = 0; /* number of fids processed in this path walk */
1929 #ifdef DEBUG_REFCOUNT
1930 afsi_log("%s:%d cm_NameI rootscp 0x%p ref %d", file, line, rootSCachep, rootSCachep->refCount);
1931 osi_Log4(afsd_logp,"cm_NameI rootscp 0x%p path %S tidpath %S flags 0x%x",
1932 rootSCachep, pathp ? pathp : L"<NULL>", tidPathp ? tidPathp : L"<NULL>",
1947 cm_HoldSCache(tscp);
1955 /* map Unix slashes into DOS ones so we can interpret Unix
1961 if (!haveComponent) {
1964 } else if (tc == 0) {
1978 /* we have a component here */
1979 if (tc == 0 || tc == '\\') {
1980 /* end of the component; we're at the last
1981 * component if tc == 0. However, if the last
1982 * is a symlink, we have more to do.
1984 *cp++ = 0; /* add null termination */
1986 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
1987 extraFlag = CM_FLAG_NOMOUNTCHASE;
1988 code = cm_Lookup(tscp, component,
1990 userp, reqp, &nscp);
1993 if (!cm_ClientStrCmp(component,_C("..")) ||
1994 !cm_ClientStrCmp(component,_C("."))) {
1996 * roll back the fid list until we find the
1997 * fid that matches where we are now. Its not
1998 * necessarily one or two fids because they
1999 * might have been symlinks or mount points or
2000 * both that were crossed.
2002 for ( i=fid_count-1; i>=0; i--) {
2003 if (!cm_FidCmp(&nscp->fid, &fids[i]))
2008 /* add the new fid to the list */
2009 if (fid_count == MAX_FID_COUNT) {
2010 code = CM_ERROR_TOO_MANY_SYMLINKS;
2011 cm_ReleaseSCache(nscp);
2015 fids[fid_count++] = nscp->fid;
2020 cm_ReleaseSCache(tscp);
2022 cm_ReleaseSCache(dirScp);
2025 if ((code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH) &&
2026 tscp->fileType == CM_SCACHETYPE_SYMLINK) {
2027 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_NOSUCHPATH");
2028 return CM_ERROR_NOSUCHPATH;
2030 osi_Log1(afsd_logp,"cm_NameI code 0x%x", code);
2035 haveComponent = 0; /* component done */
2037 cm_ReleaseSCache(dirScp);
2038 dirScp = tscp; /* for some symlinks */
2039 tscp = nscp; /* already held */
2041 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
2044 cm_ReleaseSCache(dirScp);
2050 /* now, if tscp is a symlink, we should follow it and
2051 * assemble the path again.
2053 lock_ObtainWrite(&tscp->rw);
2054 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
2055 CM_SCACHESYNC_GETSTATUS
2056 | CM_SCACHESYNC_NEEDCALLBACK);
2058 lock_ReleaseWrite(&tscp->rw);
2059 cm_ReleaseSCache(tscp);
2062 cm_ReleaseSCache(dirScp);
2067 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2069 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
2070 /* this is a symlink; assemble a new buffer */
2071 lock_ReleaseWrite(&tscp->rw);
2072 if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
2073 cm_ReleaseSCache(tscp);
2076 cm_ReleaseSCache(dirScp);
2081 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_TOO_MANY_SYMLINKS");
2082 return CM_ERROR_TOO_MANY_SYMLINKS;
2092 /* TODO: make this better */
2093 frestp = cm_ClientStringToFsStringAlloc(restp, -1, NULL);
2094 code = cm_AssembleLink(tscp, frestp, &linkScp, &tempsp, userp, reqp);
2098 if (code == 0 && linkScp != NULL) {
2099 if (linkScp == cm_data.rootSCachep) {
2103 for ( i=0; i<fid_count; i++) {
2104 if ( !cm_FidCmp(&linkScp->fid, &fids[i]) ) {
2105 code = CM_ERROR_TOO_MANY_SYMLINKS;
2106 cm_ReleaseSCache(linkScp);
2112 if (i == fid_count && fid_count < MAX_FID_COUNT) {
2113 fids[fid_count++] = linkScp->fid;
2118 /* something went wrong */
2119 cm_ReleaseSCache(tscp);
2122 cm_ReleaseSCache(dirScp);
2128 /* otherwise, tempsp has the new path,
2129 * and linkScp is the new root from
2130 * which to interpret that path.
2131 * Continue with the namei processing,
2132 * also doing the bookkeeping for the
2133 * space allocation and tracking the
2134 * vnode reference counts.
2140 cm_ReleaseSCache(tscp);
2145 * now, if linkScp is null, that's
2146 * AssembleLink's way of telling us that
2147 * the sym link is relative to the dir
2148 * containing the link. We have a ref
2149 * to it in dirScp, and we hold it now
2150 * and reuse it as the new spot in the
2158 /* not a symlink, we may be done */
2159 lock_ReleaseWrite(&tscp->rw);
2167 cm_ReleaseSCache(dirScp);
2175 cm_ReleaseSCache(dirScp);
2178 } /* end of a component */
2181 } /* we have a component */
2182 } /* big while loop over all components */
2186 cm_ReleaseSCache(dirScp);
2192 cm_ReleaseSCache(tscp);
2194 #ifdef DEBUG_REFCOUNT
2195 afsi_log("%s:%d cm_NameI code 0x%x outScpp 0x%p ref %d", file, line, code, *outScpp, (*outScpp) ? (*outScpp)->refCount : 0);
2197 osi_Log2(afsd_logp,"cm_NameI code 0x%x outScpp 0x%p", code, *outScpp);
2201 /* called with a dir, and a vnode within the dir that happens to be a symlink.
2202 * We chase the link, and return a held pointer to the target, if it exists,
2203 * in *outScpp. If we succeed, we return 0, otherwise we return an error code
2204 * and do not hold or return a target vnode.
2206 * This is very similar to calling cm_NameI with the last component of a name,
2207 * which happens to be a symlink, except that we've already passed by the name.
2209 * This function is typically called by the directory listing functions, which
2210 * encounter symlinks but need to return the proper file length so programs
2211 * like "more" work properly when they make use of the attributes retrieved from
2214 * The input vnode should not be locked when this function is called.
2216 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
2217 cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
2221 cm_scache_t *newRootScp;
2225 osi_Log1(afsd_logp, "Evaluating symlink scp 0x%p", linkScp);
2227 code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
2231 /* now, if newRootScp is NULL, we're really being told that the symlink
2232 * is relative to the current directory (dscp).
2234 if (newRootScp == NULL) {
2236 cm_HoldSCache(dscp);
2239 code = cm_NameI(newRootScp, spacep->wdata,
2240 CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
2241 userp, NULL, reqp, outScpp);
2243 if (code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH)
2244 code = CM_ERROR_NOSUCHPATH;
2246 /* this stuff is allocated no matter what happened on the namei call,
2248 cm_FreeSpace(spacep);
2249 cm_ReleaseSCache(newRootScp);
2251 if (linkScp == *outScpp) {
2252 cm_ReleaseSCache(*outScpp);
2254 code = CM_ERROR_NOSUCHPATH;
2260 /* for a given entry, make sure that it isn't in the stat cache, and then
2261 * add it to the list of file IDs to be obtained.
2263 * Don't bother adding it if we already have a vnode. Note that the dir
2264 * is locked, so we have to be careful checking the vnode we're thinking of
2265 * processing, to avoid deadlocks.
2267 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
2278 /* Don't overflow bsp. */
2279 if (bsp->counter >= CM_BULKMAX)
2280 return CM_ERROR_STOPNOW;
2282 thyper.LowPart = cm_data.buf_blockSize;
2283 thyper.HighPart = 0;
2284 thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
2286 /* thyper is now the first byte past the end of the record we're
2287 * interested in, and bsp->bufOffset is the first byte of the record
2288 * we're interested in.
2289 * Skip data in the others.
2292 if (LargeIntegerLessThan(*offp, bsp->bufOffset))
2294 if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
2295 return CM_ERROR_STOPNOW;
2296 if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
2299 cm_SetFid(&tfid, scp->fid.cell, scp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
2300 tscp = cm_FindSCache(&tfid);
2302 if (lock_TryWrite(&tscp->rw)) {
2303 /* we have an entry that we can look at */
2304 if (!(tscp->flags & CM_SCACHEFLAG_EACCESS) && cm_HaveCallback(tscp)) {
2305 /* we have a callback on it. Don't bother
2306 * fetching this stat entry, since we're happy
2307 * with the info we have.
2309 lock_ReleaseWrite(&tscp->rw);
2310 cm_ReleaseSCache(tscp);
2313 lock_ReleaseWrite(&tscp->rw);
2315 cm_ReleaseSCache(tscp);
2318 #ifdef AFS_FREELANCE_CLIENT
2319 // yj: if this is a mountpoint under root.afs then we don't want it
2320 // to be bulkstat-ed, instead, we call getSCache directly and under
2321 // getSCache, it is handled specially.
2322 if ( cm_freelanceEnabled &&
2323 tfid.cell==AFS_FAKE_ROOT_CELL_ID &&
2324 tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
2325 !(tfid.vnode==0x1 && tfid.unique==0x1) )
2327 osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
2328 return cm_GetSCache(&tfid, &tscp, NULL, NULL);
2330 #endif /* AFS_FREELANCE_CLIENT */
2333 bsp->fids[i].Volume = scp->fid.volume;
2334 bsp->fids[i].Vnode = tfid.vnode;
2335 bsp->fids[i].Unique = tfid.unique;
2340 cm_TryBulkStatRPC(cm_scache_t *dscp, cm_bulkStat_t *bbp, cm_user_t *userp, cm_req_t *reqp)
2343 AFSCBFids fidStruct;
2344 AFSBulkStats statStruct;
2346 AFSCBs callbackStruct;
2349 cm_callbackRequest_t cbReq;
2355 struct rx_connection * rxconnp;
2356 int inlinebulk; /* Did we use InlineBulkStatus RPC or not? */
2358 memset(&volSync, 0, sizeof(volSync));
2360 /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
2361 * make the calls to create the entries. Handle AFSCBMAX files at a
2364 for (filex = 0; filex < bbp->counter; filex += filesThisCall) {
2365 filesThisCall = bbp->counter - filex;
2366 if (filesThisCall > AFSCBMAX)
2367 filesThisCall = AFSCBMAX;
2369 fidStruct.AFSCBFids_len = filesThisCall;
2370 fidStruct.AFSCBFids_val = &bbp->fids[filex];
2371 statStruct.AFSBulkStats_len = filesThisCall;
2372 statStruct.AFSBulkStats_val = &bbp->stats[filex];
2373 callbackStruct.AFSCBs_len = filesThisCall;
2374 callbackStruct.AFSCBs_val = &bbp->callbacks[filex];
2375 cm_StartCallbackGrantingCall(NULL, &cbReq);
2376 osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
2379 * Whenever cm_Analyze is called for a RXAFS_ RPC there must
2380 * be a FID provided. However, the error code from RXAFS_BulkStatus
2381 * or RXAFS_InlinkBulkStatus does not apply to any FID. Therefore,
2382 * we generate an invalid FID to match with the RPC error.
2384 cm_SetFid(&tfid, dscp->fid.cell, dscp->fid.volume, 0, 0);
2389 code = cm_ConnFromFID(&tfid, userp, reqp, &connp);
2393 rxconnp = cm_GetRxConn(connp);
2394 if (!(connp->serverp->flags & CM_SERVERFLAG_NOINLINEBULK)) {
2395 code = RXAFS_InlineBulkStatus(rxconnp, &fidStruct,
2396 &statStruct, &callbackStruct, &volSync);
2397 if (code == RXGEN_OPCODE) {
2398 cm_SetServerNoInlineBulk(connp->serverp, 0);
2404 code = RXAFS_BulkStatus(rxconnp, &fidStruct,
2405 &statStruct, &callbackStruct, &volSync);
2407 rx_PutConnection(rxconnp);
2410 * If InlineBulk RPC was called and it succeeded,
2411 * then pull out the return code from the status info
2412 * and use it for cm_Analyze so that we can failover to other
2413 * .readonly volume instances. But only do it for errors that
2414 * are volume global.
2416 if (inlinebulk && code == 0 && (&bbp->stats[0])->errorCode) {
2417 osi_Log1(afsd_logp, "cm_TryBulkStat inline-bulk stat error: %d",
2418 (&bbp->stats[0])->errorCode);
2419 switch ((&bbp->stats[0])->errorCode) {
2428 code = (&bbp->stats[0])->errorCode;
2431 /* Rx and Rxkad errors are volume global */
2432 if ( (&bbp->stats[0])->errorCode >= -64 && (&bbp->stats[0])->errorCode < 0 ||
2433 (&bbp->stats[0])->errorCode >= ERROR_TABLE_BASE_RXK && (&bbp->stats[0])->errorCode < ERROR_TABLE_BASE_RXK + 256)
2434 code = (&bbp->stats[0])->errorCode;
2437 } while (cm_Analyze(connp, userp, reqp, &tfid, &volSync, NULL, &cbReq, code));
2438 code = cm_MapRPCError(code, reqp);
2441 * might as well quit on an error, since we're not going to do
2442 * much better on the next immediate call, either.
2445 osi_Log2(afsd_logp, "CALL %sBulkStatus FAILURE code 0x%x",
2446 inlinebulk ? "Inline" : "", code);
2447 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2452 * The bulk RPC has succeeded or at least not failed with a
2453 * volume global error result. For items that have inlineBulk
2454 * errors we must call cm_Analyze in order to perform required
2455 * logging of errors.
2457 * If the RPC was not inline bulk or the entry either has no error
2458 * the status must be merged.
2460 osi_Log1(afsd_logp, "CALL %sBulkStatus SUCCESS", inlinebulk ? "Inline" : "");
2462 for (i = 0; i<filesThisCall; i++) {
2464 cm_SetFid(&tfid, dscp->fid.cell, bbp->fids[j].Volume, bbp->fids[j].Vnode, bbp->fids[j].Unique);
2466 if (inlinebulk && (&bbp->stats[j])->errorCode) {
2467 cm_req_t treq = *reqp;
2468 cm_Analyze(NULL, userp, &treq, &tfid, &volSync, NULL, &cbReq, (&bbp->stats[j])->errorCode);
2470 code = cm_GetSCache(&tfid, &scp, userp, reqp);
2475 * otherwise, if this entry has no callback info,
2476 * merge in this. If there is existing callback info
2477 * we skip the merge because the existing data must be
2478 * current (we have a callback) and the response from
2479 * a non-inline bulk rpc might actually be wrong.
2481 * now, we have to be extra paranoid on merging in this
2482 * information, since we didn't use cm_SyncOp before
2483 * starting the fetch to make sure that no bad races
2484 * were occurring. Specifically, we need to make sure
2485 * we don't obliterate any newer information in the
2486 * vnode than have here.
2488 * Right now, be pretty conservative: if there's a
2489 * callback or a pending call, skip it.
2490 * However, if the prior attempt to obtain status
2491 * was refused access or the volume is .readonly,
2492 * take the data in any case since we have nothing
2493 * better for the in flight directory enumeration that
2494 * resulted in this function being called.
2496 lock_ObtainRead(&scp->rw);
2497 if ((scp->cbServerp == NULL &&
2498 !(scp->flags & (CM_SCACHEFLAG_FETCHING | CM_SCACHEFLAG_STORING | CM_SCACHEFLAG_SIZESTORING))) ||
2499 (scp->flags & CM_SCACHEFLAG_PURERO) ||
2500 (scp->flags & CM_SCACHEFLAG_EACCESS))
2502 lock_ConvertRToW(&scp->rw);
2503 cm_EndCallbackGrantingCall(scp, &cbReq,
2506 CM_CALLBACK_MAINTAINCOUNT);
2507 cm_MergeStatus(dscp, scp, &bbp->stats[j], &volSync, userp, reqp, 0);
2508 lock_ReleaseWrite(&scp->rw);
2510 lock_ReleaseRead(&scp->rw);
2512 cm_ReleaseSCache(scp);
2514 } /* all files in the response */
2515 /* now tell it to drop the count,
2516 * after doing the vnode processing above */
2517 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2518 } /* while there are still more files to process */
2523 /* called with a write locked scp and a pointer to a buffer. Make bulk stat
2524 * calls on all undeleted files in the page of the directory specified.
2527 cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
2533 osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%p", dscp);
2535 /* should be on a buffer boundary */
2536 osi_assertx((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0, "invalid offset");
2538 bbp = malloc(sizeof(cm_bulkStat_t));
2539 memset(bbp, 0, sizeof(cm_bulkStat_t));
2540 bbp->bufOffset = *offsetp;
2542 lock_ReleaseWrite(&dscp->rw);
2543 /* first, assemble the file IDs we need to stat */
2544 code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) bbp, offsetp, userp, reqp, NULL);
2546 /* if we failed, bail out early */
2547 if (code && code != CM_ERROR_STOPNOW) {
2549 lock_ObtainWrite(&dscp->rw);
2553 code = cm_TryBulkStatRPC(dscp, bbp, userp, reqp);
2554 osi_Log1(afsd_logp, "END cm_TryBulkStat code 0x%x", code);
2556 lock_ObtainWrite(&dscp->rw);
2561 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
2565 /* initialize store back mask as inexpensive local variable */
2567 memset(statusp, 0, sizeof(AFSStoreStatus));
2569 /* copy out queued info from scache first, if scp passed in */
2571 if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
2572 statusp->ClientModTime = scp->clientModTime;
2573 mask |= AFS_SETMODTIME;
2574 scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
2579 /* now add in our locally generated request */
2580 if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
2581 statusp->ClientModTime = attrp->clientModTime;
2582 mask |= AFS_SETMODTIME;
2584 if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
2585 statusp->UnixModeBits = attrp->unixModeBits;
2586 mask |= AFS_SETMODE;
2588 if (attrp->mask & CM_ATTRMASK_OWNER) {
2589 statusp->Owner = attrp->owner;
2590 mask |= AFS_SETOWNER;
2592 if (attrp->mask & CM_ATTRMASK_GROUP) {
2593 statusp->Group = attrp->group;
2594 mask |= AFS_SETGROUP;
2597 statusp->Mask = mask;
2600 /* set the file size, and make sure that all relevant buffers have been
2601 * truncated. Ensure that any partially truncated buffers have been zeroed
2602 * to the end of the buffer.
2604 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
2610 /* start by locking out buffer creation */
2611 lock_ObtainWrite(&scp->bufCreateLock);
2613 /* verify that this is a file, not a dir or a symlink */
2614 lock_ObtainWrite(&scp->rw);
2615 code = cm_SyncOp(scp, NULL, userp, reqp, 0,
2616 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2619 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2621 if (scp->fileType != CM_SCACHETYPE_FILE) {
2622 code = CM_ERROR_ISDIR;
2627 if (LargeIntegerLessThan(*sizep, scp->length))
2632 lock_ReleaseWrite(&scp->rw);
2634 /* can't hold scp->rw lock here, since we may wait for a storeback to
2635 * finish if the buffer package is cleaning a buffer by storing it to
2639 buf_Truncate(scp, userp, reqp, sizep);
2641 /* now ensure that file length is short enough, and update truncPos */
2642 lock_ObtainWrite(&scp->rw);
2644 /* make sure we have a callback (so we have the right value for the
2645 * length), and wait for it to be safe to do a truncate.
2647 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
2648 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2649 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2651 /* If we only have 'i' bits, then we should still be able to set
2652 the size of a file we created. */
2653 if (code == CM_ERROR_NOACCESS && scp->creator == userp) {
2654 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
2655 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2656 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2662 if (LargeIntegerLessThan(*sizep, scp->length)) {
2663 /* a real truncation. If truncPos is not set yet, or is bigger
2664 * than where we're truncating the file, set truncPos to this
2669 if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
2670 || LargeIntegerLessThan(*sizep, scp->length)) {
2672 scp->truncPos = *sizep;
2673 scp->mask |= CM_SCACHEMASK_TRUNCPOS;
2675 /* in either case, the new file size has been changed */
2676 scp->length = *sizep;
2677 scp->mask |= CM_SCACHEMASK_LENGTH;
2679 else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
2680 /* really extending the file */
2681 scp->length = *sizep;
2682 scp->mask |= CM_SCACHEMASK_LENGTH;
2685 /* done successfully */
2688 cm_SyncOpDone(scp, NULL,
2689 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2690 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2693 lock_ReleaseWrite(&scp->rw);
2694 lock_ReleaseWrite(&scp->bufCreateLock);
2699 /* set the file size or other attributes (but not both at once) */
2700 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
2704 AFSFetchStatus afsOutStatus;
2708 AFSStoreStatus afsInStatus;
2709 struct rx_connection * rxconnp;
2711 memset(&volSync, 0, sizeof(volSync));
2713 /* handle file length setting */
2714 if (attrp->mask & CM_ATTRMASK_LENGTH)
2715 return cm_SetLength(scp, &attrp->length, userp, reqp);
2717 lock_ObtainWrite(&scp->rw);
2718 /* Check for RO volume */
2719 if (scp->flags & CM_SCACHEFLAG_RO) {
2720 code = CM_ERROR_READONLY;
2721 lock_ReleaseWrite(&scp->rw);
2725 /* otherwise, we have to make an RPC to get the status */
2726 code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
2728 lock_ReleaseWrite(&scp->rw);
2731 lock_ConvertWToR(&scp->rw);
2733 /* make the attr structure */
2734 cm_StatusFromAttr(&afsInStatus, scp, attrp);
2736 tfid.Volume = scp->fid.volume;
2737 tfid.Vnode = scp->fid.vnode;
2738 tfid.Unique = scp->fid.unique;
2739 lock_ReleaseRead(&scp->rw);
2741 /* now make the RPC */
2742 osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%p", scp);
2744 code = cm_ConnFromFID(&scp->fid, userp, reqp, &connp);
2748 rxconnp = cm_GetRxConn(connp);
2749 code = RXAFS_StoreStatus(rxconnp, &tfid,
2750 &afsInStatus, &afsOutStatus, &volSync);
2751 rx_PutConnection(rxconnp);
2753 } while (cm_Analyze(connp, userp, reqp,
2754 &scp->fid, &volSync, NULL, NULL, code));
2755 code = cm_MapRPCError(code, reqp);
2758 osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
2760 osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
2762 lock_ObtainWrite(&scp->rw);
2763 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
2765 cm_MergeStatus(NULL, scp, &afsOutStatus, &volSync, userp, reqp,
2766 CM_MERGEFLAG_FORCE|CM_MERGEFLAG_STOREDATA);
2768 /* if we're changing the mode bits, discard the ACL cache,
2769 * since we changed the mode bits.
2771 if (afsInStatus.Mask & AFS_SETMODE)
2772 cm_FreeAllACLEnts(scp);
2773 lock_ReleaseWrite(&scp->rw);
2777 long cm_Create(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
2778 cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
2783 cm_callbackRequest_t cbReq;
2786 cm_scache_t *scp = NULL;
2788 AFSStoreStatus inStatus;
2789 AFSFetchStatus updatedDirStatus;
2790 AFSFetchStatus newFileStatus;
2791 AFSCallBack newFileCallback;
2793 struct rx_connection * rxconnp;
2795 fschar_t * fnamep = NULL;
2797 memset(&volSync, 0, sizeof(volSync));
2799 /* can't create names with @sys in them; must expand it manually first.
2800 * return "invalid request" if they try.
2802 if (cm_ExpandSysName(cnamep, NULL, 0, 0)) {
2803 return CM_ERROR_ATSYS;
2806 #ifdef AFS_FREELANCE_CLIENT
2807 /* Freelance root volume does not hold files */
2808 if (cm_freelanceEnabled &&
2809 dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
2810 dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
2812 return CM_ERROR_NOACCESS;
2814 #endif /* AFS_FREELANCE_CLIENT */
2816 /* Check for RO volume */
2817 if (dscp->flags & CM_SCACHEFLAG_RO)
2818 return CM_ERROR_READONLY;
2820 /* before starting the RPC, mark that we're changing the file data, so
2821 * that someone who does a chmod will know to wait until our call
2824 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
2826 lock_ObtainWrite(&dscp->rw);
2827 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2828 lock_ReleaseWrite(&dscp->rw);
2830 cm_StartCallbackGrantingCall(NULL, &cbReq);
2832 cm_EndDirOp(&dirop);
2839 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
2841 cm_StatusFromAttr(&inStatus, NULL, attrp);
2843 /* try the RPC now */
2844 osi_Log1(afsd_logp, "CALL CreateFile scp 0x%p", dscp);
2846 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2850 dirAFSFid.Volume = dscp->fid.volume;
2851 dirAFSFid.Vnode = dscp->fid.vnode;
2852 dirAFSFid.Unique = dscp->fid.unique;
2854 rxconnp = cm_GetRxConn(connp);
2855 code = RXAFS_CreateFile(connp->rxconnp, &dirAFSFid, fnamep,
2856 &inStatus, &newAFSFid, &newFileStatus,
2857 &updatedDirStatus, &newFileCallback,
2859 rx_PutConnection(rxconnp);
2861 } while (cm_Analyze(connp, userp, reqp,
2862 &dscp->fid, &volSync, NULL, &cbReq, code));
2863 code = cm_MapRPCError(code, reqp);
2866 osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
2868 osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
2871 lock_ObtainWrite(&dirop.scp->dirlock);
2872 dirop.lockType = CM_DIRLOCK_WRITE;
2874 lock_ObtainWrite(&dscp->rw);
2875 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2877 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
2879 lock_ReleaseWrite(&dscp->rw);
2881 /* now try to create the file's entry, too, but be careful to
2882 * make sure that we don't merge in old info. Since we weren't locking
2883 * out any requests during the file's creation, we may have pretty old
2887 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
2888 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2890 lock_ObtainWrite(&scp->rw);
2891 scp->creator = userp; /* remember who created it */
2892 if (!cm_HaveCallback(scp)) {
2893 cm_EndCallbackGrantingCall(scp, &cbReq,
2894 &newFileCallback, &volSync, 0);
2895 cm_MergeStatus(dscp, scp, &newFileStatus, &volSync,
2899 lock_ReleaseWrite(&scp->rw);
2903 /* make sure we end things properly */
2905 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2907 if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
2908 cm_DirCreateEntry(&dirop, fnamep, &newFid);
2910 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
2913 cm_EndDirOp(&dirop);
2922 cm_ReleaseSCache(scp);
2928 * locked if TRUE means write-locked
2929 * else the cm_scache_t rw must not be held
2931 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp, afs_uint32 locked)
2936 lock_ReleaseWrite(&scp->rw);
2937 code = buf_CleanVnode(scp, userp, reqp);
2939 lock_ObtainWrite(&scp->rw);
2941 if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
2942 | CM_SCACHEMASK_CLIENTMODTIME
2943 | CM_SCACHEMASK_LENGTH))
2944 code = cm_StoreMini(scp, userp, reqp);
2946 if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) {
2947 code = (scp->flags & CM_SCACHEFLAG_OVERQUOTA) ? CM_ERROR_QUOTA : CM_ERROR_SPACE;
2948 scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE);
2952 lock_ReleaseWrite(&scp->rw);
2953 } else if (locked) {
2954 lock_ObtainWrite(&scp->rw);
2959 long cm_MakeDir(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
2960 cm_user_t *userp, cm_req_t *reqp, cm_scache_t **scpp)
2965 cm_callbackRequest_t cbReq;
2968 cm_scache_t *scp = NULL;
2970 AFSStoreStatus inStatus;
2971 AFSFetchStatus updatedDirStatus;
2972 AFSFetchStatus newDirStatus;
2973 AFSCallBack newDirCallback;
2975 struct rx_connection * rxconnp;
2977 fschar_t * fnamep = NULL;
2979 memset(&volSync, 0, sizeof(volSync));
2981 /* can't create names with @sys in them; must expand it manually first.
2982 * return "invalid request" if they try.
2984 if (cm_ExpandSysName(cnamep, NULL, 0, 0)) {
2985 return CM_ERROR_ATSYS;
2988 #ifdef AFS_FREELANCE_CLIENT
2989 /* Freelance root volume does not hold subdirectories */
2990 if (cm_freelanceEnabled &&
2991 dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
2992 dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
2994 return CM_ERROR_NOACCESS;
2996 #endif /* AFS_FREELANCE_CLIENT */
2998 /* Check for RO volume */
2999 if (dscp->flags & CM_SCACHEFLAG_RO)
3000 return CM_ERROR_READONLY;
3002 /* before starting the RPC, mark that we're changing the directory
3003 * data, so that someone who does a chmod on the dir will wait until
3004 * our call completes.
3006 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3008 lock_ObtainWrite(&dscp->rw);
3009 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3010 lock_ReleaseWrite(&dscp->rw);
3012 cm_StartCallbackGrantingCall(NULL, &cbReq);
3014 cm_EndDirOp(&dirop);
3021 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3022 cm_StatusFromAttr(&inStatus, NULL, attrp);
3024 /* try the RPC now */
3025 osi_Log1(afsd_logp, "CALL MakeDir scp 0x%p", dscp);
3027 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3031 dirAFSFid.Volume = dscp->fid.volume;
3032 dirAFSFid.Vnode = dscp->fid.vnode;
3033 dirAFSFid.Unique = dscp->fid.unique;
3035 rxconnp = cm_GetRxConn(connp);
3036 code = RXAFS_MakeDir(connp->rxconnp, &dirAFSFid, fnamep,
3037 &inStatus, &newAFSFid, &newDirStatus,
3038 &updatedDirStatus, &newDirCallback,
3040 rx_PutConnection(rxconnp);
3042 } while (cm_Analyze(connp, userp, reqp,
3043 &dscp->fid, &volSync, NULL, &cbReq, code));
3044 code = cm_MapRPCError(code, reqp);
3047 osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
3049 osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
3052 lock_ObtainWrite(&dirop.scp->dirlock);
3053 dirop.lockType = CM_DIRLOCK_WRITE;
3055 lock_ObtainWrite(&dscp->rw);
3056 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3058 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3060 lock_ReleaseWrite(&dscp->rw);
3062 /* now try to create the new dir's entry, too, but be careful to
3063 * make sure that we don't merge in old info. Since we weren't locking
3064 * out any requests during the file's creation, we may have pretty old
3068 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3069 code = cm_GetSCache(&newFid, &scp, userp, reqp);
3071 lock_ObtainWrite(&scp->rw);
3072 if (!cm_HaveCallback(scp)) {
3073 cm_EndCallbackGrantingCall(scp, &cbReq,
3074 &newDirCallback, &volSync, 0);
3075 cm_MergeStatus(dscp, scp, &newDirStatus, &volSync,
3079 lock_ReleaseWrite(&scp->rw);
3083 /* make sure we end things properly */
3085 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
3087 if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
3088 cm_DirCreateEntry(&dirop, fnamep, &newFid);
3090 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
3093 cm_EndDirOp(&dirop);
3101 cm_ReleaseSCache(scp);
3104 /* and return error code */
3108 long cm_Link(cm_scache_t *dscp, clientchar_t *cnamep, cm_scache_t *sscp, long flags,
3109 cm_user_t *userp, cm_req_t *reqp)
3114 AFSFid existingAFSFid;
3115 AFSFetchStatus updatedDirStatus;
3116 AFSFetchStatus newLinkStatus;
3118 struct rx_connection * rxconnp;
3120 fschar_t * fnamep = NULL;
3122 memset(&volSync, 0, sizeof(volSync));
3124 if (dscp->fid.cell != sscp->fid.cell ||
3125 dscp->fid.volume != sscp->fid.volume) {
3126 return CM_ERROR_CROSSDEVLINK;
3129 /* Check for RO volume */
3130 if (dscp->flags & CM_SCACHEFLAG_RO)
3131 return CM_ERROR_READONLY;
3133 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3135 lock_ObtainWrite(&dscp->rw);
3136 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3137 lock_ReleaseWrite(&dscp->rw);
3139 cm_EndDirOp(&dirop);
3144 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3146 /* try the RPC now */
3147 osi_Log1(afsd_logp, "CALL Link scp 0x%p", dscp);
3149 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3152 dirAFSFid.Volume = dscp->fid.volume;
3153 dirAFSFid.Vnode = dscp->fid.vnode;
3154 dirAFSFid.Unique = dscp->fid.unique;
3156 existingAFSFid.Volume = sscp->fid.volume;
3157 existingAFSFid.Vnode = sscp->fid.vnode;
3158 existingAFSFid.Unique = sscp->fid.unique;
3160 rxconnp = cm_GetRxConn(connp);
3161 code = RXAFS_Link(rxconnp, &dirAFSFid, fnamep, &existingAFSFid,
3162 &newLinkStatus, &updatedDirStatus, &volSync);
3163 rx_PutConnection(rxconnp);
3164 osi_Log1(afsd_logp," RXAFS_Link returns 0x%x", code);
3166 } while (cm_Analyze(connp, userp, reqp,
3167 &dscp->fid, &volSync, NULL, NULL, code));
3169 code = cm_MapRPCError(code, reqp);
3172 osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
3174 osi_Log0(afsd_logp, "CALL Link SUCCESS");
3177 lock_ObtainWrite(&dirop.scp->dirlock);
3178 dirop.lockType = CM_DIRLOCK_WRITE;
3180 lock_ObtainWrite(&dscp->rw);
3181 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3183 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3185 lock_ReleaseWrite(&dscp->rw);
3188 if (cm_CheckDirOpForSingleChange(&dirop)) {
3189 cm_DirCreateEntry(&dirop, fnamep, &sscp->fid);
3191 cm_BPlusDirCreateEntry(&dirop, cnamep, &sscp->fid);
3195 cm_EndDirOp(&dirop);
3197 /* Update the linked object status */
3199 lock_ObtainWrite(&sscp->rw);
3200 cm_MergeStatus(NULL, sscp, &newLinkStatus, &volSync, userp, reqp, 0);
3201 lock_ReleaseWrite(&sscp->rw);
3209 long cm_SymLink(cm_scache_t *dscp, clientchar_t *cnamep, fschar_t *contentsp, long flags,
3210 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
3218 AFSStoreStatus inStatus;
3219 AFSFetchStatus updatedDirStatus;
3220 AFSFetchStatus newLinkStatus;
3222 struct rx_connection * rxconnp;
3224 fschar_t *fnamep = NULL;
3226 /* Check for RO volume */
3227 if (dscp->flags & CM_SCACHEFLAG_RO)
3228 return CM_ERROR_READONLY;
3230 memset(&volSync, 0, sizeof(volSync));
3232 /* before starting the RPC, mark that we're changing the directory data,
3233 * so that someone who does a chmod on the dir will wait until our
3236 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3238 lock_ObtainWrite(&dscp->rw);
3239 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3240 lock_ReleaseWrite(&dscp->rw);
3242 cm_EndDirOp(&dirop);
3247 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3249 cm_StatusFromAttr(&inStatus, NULL, attrp);
3251 /* try the RPC now */
3252 osi_Log1(afsd_logp, "CALL Symlink scp 0x%p", dscp);
3254 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3258 dirAFSFid.Volume = dscp->fid.volume;
3259 dirAFSFid.Vnode = dscp->fid.vnode;
3260 dirAFSFid.Unique = dscp->fid.unique;
3262 rxconnp = cm_GetRxConn(connp);
3263 code = RXAFS_Symlink(rxconnp, &dirAFSFid, fnamep, contentsp,
3264 &inStatus, &newAFSFid, &newLinkStatus,
3265 &updatedDirStatus, &volSync);
3266 rx_PutConnection(rxconnp);
3268 } while (cm_Analyze(connp, userp, reqp,
3269 &dscp->fid, &volSync, NULL, NULL, code));
3270 code = cm_MapRPCError(code, reqp);
3273 osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
3275 osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
3278 lock_ObtainWrite(&dirop.scp->dirlock);
3279 dirop.lockType = CM_DIRLOCK_WRITE;
3281 lock_ObtainWrite(&dscp->rw);
3282 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3284 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3286 lock_ReleaseWrite(&dscp->rw);
3289 if (cm_CheckDirOpForSingleChange(&dirop)) {
3290 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3292 cm_DirCreateEntry(&dirop, fnamep, &newFid);
3294 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
3298 cm_EndDirOp(&dirop);
3300 /* now try to create the new dir's entry, too, but be careful to
3301 * make sure that we don't merge in old info. Since we weren't locking
3302 * out any requests during the file's creation, we may have pretty old
3306 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3307 code = cm_GetSCache(&newFid, &scp, userp, reqp);
3309 lock_ObtainWrite(&scp->rw);
3310 if (!cm_HaveCallback(scp)) {
3311 cm_MergeStatus(dscp, scp, &newLinkStatus, &volSync,
3314 lock_ReleaseWrite(&scp->rw);
3315 cm_ReleaseSCache(scp);
3321 /* and return error code */
3325 /*! \brief Remove a directory
3327 Encapsulates a call to RXAFS_RemoveDir().
3329 \param[in] dscp cm_scache_t for the directory containing the
3330 directory to be removed.
3332 \param[in] fnamep This will be the original name of the directory
3333 as known to the file server. It will be passed in to RXAFS_RemoveDir().
3334 This parameter is optional. If it is not provided the value
3337 \param[in] cnamep Normalized name used to update the local
3340 \param[in] userp cm_user_t for the request.
3342 \param[in] reqp Request tracker.
3344 long cm_RemoveDir(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t *cnamep, cm_user_t *userp, cm_req_t *reqp)
3350 AFSFetchStatus updatedDirStatus;
3352 struct rx_connection * rxconnp;
3354 cm_scache_t *scp = NULL;
3355 int free_fnamep = FALSE;
3357 memset(&volSync, 0, sizeof(volSync));
3359 if (fnamep == NULL) {
3362 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ,
3363 CM_DIROP_FLAG_NONE, &dirop);
3365 code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
3368 cm_EndDirOp(&dirop);
3375 code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
3379 /* Check for RO volume */
3380 if (dscp->flags & CM_SCACHEFLAG_RO) {
3381 code = CM_ERROR_READONLY;
3385 /* before starting the RPC, mark that we're changing the directory data,
3386 * so that someone who does a chmod on the dir will wait until our
3389 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3391 lock_ObtainWrite(&dscp->rw);
3392 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3393 lock_ReleaseWrite(&dscp->rw);
3395 cm_EndDirOp(&dirop);
3400 /* try the RPC now */
3401 osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%p", dscp);
3403 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3407 dirAFSFid.Volume = dscp->fid.volume;
3408 dirAFSFid.Vnode = dscp->fid.vnode;
3409 dirAFSFid.Unique = dscp->fid.unique;
3411 rxconnp = cm_GetRxConn(connp);
3412 code = RXAFS_RemoveDir(rxconnp, &dirAFSFid, fnamep,
3413 &updatedDirStatus, &volSync);
3414 rx_PutConnection(rxconnp);
3416 } while (cm_Analyze(connp, userp, reqp,
3417 &dscp->fid, &volSync, NULL, NULL, code));
3418 code = cm_MapRPCErrorRmdir(code, reqp);
3421 osi_Log1(afsd_logp, "CALL RemoveDir FAILURE, code 0x%x", code);
3423 osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
3426 lock_ObtainWrite(&dirop.scp->dirlock);
3427 dirop.lockType = CM_DIRLOCK_WRITE;
3429 lock_ObtainWrite(&dscp->rw);
3430 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3432 cm_dnlcRemove(dscp, cnamep);
3433 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3435 lock_ReleaseWrite(&dscp->rw);
3438 if (cm_CheckDirOpForSingleChange(&dirop) && cnamep != NULL) {
3439 cm_DirDeleteEntry(&dirop, fnamep);
3441 cm_BPlusDirDeleteEntry(&dirop, cnamep);
3445 cm_EndDirOp(&dirop);
3448 cm_ReleaseSCache(scp);
3450 lock_ObtainWrite(&scp->rw);
3451 scp->flags |= CM_SCACHEFLAG_DELETED;
3452 lock_ReleaseWrite(&scp->rw);
3460 /* and return error code */
3464 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
3466 /* grab mutex on contents */
3467 lock_ObtainWrite(&scp->rw);
3469 /* reset the prefetch info */
3470 scp->prefetch.base.LowPart = 0; /* base */
3471 scp->prefetch.base.HighPart = 0;
3472 scp->prefetch.end.LowPart = 0; /* and end */
3473 scp->prefetch.end.HighPart = 0;
3475 /* release mutex on contents */
3476 lock_ReleaseWrite(&scp->rw);
3482 /*! \brief Rename a file or directory
3484 Encapsulates a RXAFS_Rename() call.
3486 \param[in] oldDscp cm_scache_t for the directory containing the old
3489 \param[in] oldNamep The original old name known to the file server.
3490 This is the name that will be passed into the RXAFS_Rename().
3491 If it is not provided, it will be looked up.
3493 \param[in] normalizedOldNamep Normalized old name. This is used for
3494 updating local directory caches.
3496 \param[in] newDscp cm_scache_t for the directory containing the new
3499 \param[in] newNamep New name. Normalized.
3501 \param[in] userp cm_user_t for the request.
3503 \param[in,out] reqp Request tracker.
3506 long cm_Rename(cm_scache_t *oldDscp, fschar_t *oldNamep, clientchar_t *cOldNamep,
3507 cm_scache_t *newDscp, clientchar_t *cNewNamep, cm_user_t *userp,
3512 AFSFid oldDirAFSFid;
3513 AFSFid newDirAFSFid;
3515 AFSFetchStatus updatedOldDirStatus;
3516 AFSFetchStatus updatedNewDirStatus;
3519 struct rx_connection * rxconnp;
3520 cm_dirOp_t oldDirOp;
3523 cm_dirOp_t newDirOp;
3524 fschar_t * newNamep = NULL;
3525 int free_oldNamep = FALSE;
3526 cm_scache_t *oldScp = NULL, *newScp = NULL;
3528 memset(&volSync, 0, sizeof(volSync));
3530 if (cOldNamep == NULL || cNewNamep == NULL ||
3531 cm_ClientStrLen(cOldNamep) == 0 ||
3532 cm_ClientStrLen(cNewNamep) == 0)
3533 return CM_ERROR_INVAL;
3536 * Before we permit the operation, make sure that we do not already have
3537 * an object in the destination directory that has a case-insensitive match
3538 * for this name UNLESS the matching object is the object we are renaming.
3540 code = cm_Lookup(oldDscp, cOldNamep, 0, userp, reqp, &oldScp);
3542 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S old name lookup failed",
3543 oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3547 code = cm_Lookup(newDscp, cNewNamep, CM_FLAG_CASEFOLD, userp, reqp, &newScp);
3549 /* found a matching object with the new name */
3550 if (cm_FidCmp(&oldScp->fid, &newScp->fid)) {
3551 /* and they don't match so return an error */
3552 osi_Log2(afsd_logp, "cm_Rename newDscp 0x%p cNewName %S new name already exists",
3553 newDscp, osi_LogSaveStringW(afsd_logp, cNewNamep));
3554 code = CM_ERROR_EXISTS;
3556 cm_ReleaseSCache(newScp);
3558 } else if (code == CM_ERROR_AMBIGUOUS_FILENAME) {
3559 code = CM_ERROR_EXISTS;
3564 /* Check for RO volume */
3566 (oldDscp->flags & CM_SCACHEFLAG_RO) || (newDscp->flags & CM_SCACHEFLAG_RO)) {
3567 code = CM_ERROR_READONLY;
3573 if (oldNamep == NULL) {
3576 code = cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_READ,
3577 CM_DIROP_FLAG_NONE, &oldDirOp);
3579 code = cm_BPlusDirLookupOriginalName(&oldDirOp, cOldNamep, &oldNamep);
3581 free_oldNamep = TRUE;
3582 cm_EndDirOp(&oldDirOp);
3586 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S Original Name lookup failed",
3587 oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3593 /* before starting the RPC, mark that we're changing the directory data,
3594 * so that someone who does a chmod on the dir will wait until our call
3595 * completes. We do this in vnode order so that we don't deadlock,
3596 * which makes the code a little verbose.
3598 if (oldDscp == newDscp) {
3599 /* check for identical names */
3600 if (cm_ClientStrCmp(cOldNamep, cNewNamep) == 0) {
3601 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_RENAME_IDENTICAL",
3603 code = CM_ERROR_RENAME_IDENTICAL;
3608 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE,
3609 CM_DIROP_FLAG_NONE, &oldDirOp);
3610 lock_ObtainWrite(&oldDscp->rw);
3611 cm_dnlcRemove(oldDscp, cOldNamep);
3612 cm_dnlcRemove(oldDscp, cNewNamep);
3613 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3614 CM_SCACHESYNC_STOREDATA);
3615 lock_ReleaseWrite(&oldDscp->rw);
3617 cm_EndDirOp(&oldDirOp);
3621 /* two distinct dir vnodes */
3623 if (oldDscp->fid.cell != newDscp->fid.cell ||
3624 oldDscp->fid.volume != newDscp->fid.volume) {
3625 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_CROSSDEVLINK",
3627 code = CM_ERROR_CROSSDEVLINK;
3631 /* shouldn't happen that we have distinct vnodes for two
3632 * different files, but could due to deliberate attack, or
3633 * stale info. Avoid deadlocks and quit now.
3635 if (oldDscp->fid.vnode == newDscp->fid.vnode) {
3636 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p vnode collision",
3638 code = CM_ERROR_CROSSDEVLINK;
3642 if (oldDscp->fid.vnode < newDscp->fid.vnode) {
3643 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE,
3644 CM_DIROP_FLAG_NONE, &oldDirOp);
3645 lock_ObtainWrite(&oldDscp->rw);
3646 cm_dnlcRemove(oldDscp, cOldNamep);
3647 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3648 CM_SCACHESYNC_STOREDATA);
3649 lock_ReleaseWrite(&oldDscp->rw);
3651 cm_EndDirOp(&oldDirOp);
3653 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE,
3654 CM_DIROP_FLAG_NONE, &newDirOp);
3655 lock_ObtainWrite(&newDscp->rw);
3656 cm_dnlcRemove(newDscp, cNewNamep);
3657 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3658 CM_SCACHESYNC_STOREDATA);
3659 lock_ReleaseWrite(&newDscp->rw);
3661 cm_EndDirOp(&newDirOp);
3663 /* cleanup first one */
3664 lock_ObtainWrite(&oldDscp->rw);
3665 cm_SyncOpDone(oldDscp, NULL,
3666 CM_SCACHESYNC_STOREDATA);
3667 lock_ReleaseWrite(&oldDscp->rw);
3668 cm_EndDirOp(&oldDirOp);
3673 /* lock the new vnode entry first */
3674 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE,
3675 CM_DIROP_FLAG_NONE, &newDirOp);
3676 lock_ObtainWrite(&newDscp->rw);
3677 cm_dnlcRemove(newDscp, cNewNamep);
3678 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3679 CM_SCACHESYNC_STOREDATA);
3680 lock_ReleaseWrite(&newDscp->rw);
3682 cm_EndDirOp(&newDirOp);
3684 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE,
3685 CM_DIROP_FLAG_NONE, &oldDirOp);
3686 lock_ObtainWrite(&oldDscp->rw);
3687 cm_dnlcRemove(oldDscp, cOldNamep);
3688 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3689 CM_SCACHESYNC_STOREDATA);
3690 lock_ReleaseWrite(&oldDscp->rw);
3692 cm_EndDirOp(&oldDirOp);
3694 /* cleanup first one */
3695 lock_ObtainWrite(&newDscp->rw);
3696 cm_SyncOpDone(newDscp, NULL,
3697 CM_SCACHESYNC_STOREDATA);
3698 lock_ReleaseWrite(&newDscp->rw);
3699 cm_EndDirOp(&newDirOp);
3703 } /* two distinct vnodes */
3710 newNamep = cm_ClientStringToFsStringAlloc(cNewNamep, -1, NULL);
3712 /* try the RPC now */
3713 osi_Log2(afsd_logp, "CALL Rename old scp 0x%p new scp 0x%p",
3716 code = cm_ConnFromFID(&oldDscp->fid, userp, reqp, &connp);
3720 oldDirAFSFid.Volume = oldDscp->fid.volume;
3721 oldDirAFSFid.Vnode = oldDscp->fid.vnode;
3722 oldDirAFSFid.Unique = oldDscp->fid.unique;
3723 newDirAFSFid.Volume = newDscp->fid.volume;
3724 newDirAFSFid.Vnode = newDscp->fid.vnode;
3725 newDirAFSFid.Unique = newDscp->fid.unique;
3727 rxconnp = cm_GetRxConn(connp);
3728 code = RXAFS_Rename(rxconnp, &oldDirAFSFid, oldNamep,
3729 &newDirAFSFid, newNamep,
3730 &updatedOldDirStatus, &updatedNewDirStatus,
3732 rx_PutConnection(rxconnp);
3734 } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
3735 &volSync, NULL, NULL, code));
3736 code = cm_MapRPCError(code, reqp);
3739 osi_Log1(afsd_logp, "CALL Rename FAILURE, code 0x%x", code);
3741 osi_Log0(afsd_logp, "CALL Rename SUCCESS");
3743 /* update the individual stat cache entries for the directories */
3745 lock_ObtainWrite(&oldDirOp.scp->dirlock);
3746 oldDirOp.lockType = CM_DIRLOCK_WRITE;
3748 lock_ObtainWrite(&oldDscp->rw);
3749 cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
3752 cm_MergeStatus(NULL, oldDscp, &updatedOldDirStatus, &volSync,
3753 userp, reqp, CM_MERGEFLAG_DIROP);
3754 lock_ReleaseWrite(&oldDscp->rw);
3756 if (code == 0 && cm_CheckDirOpForSingleChange(&oldDirOp)) {
3758 diropCode = cm_BPlusDirLookup(&oldDirOp, cOldNamep, &fileFid);
3759 if (diropCode == CM_ERROR_INEXACT_MATCH)
3761 else if (diropCode == EINVAL)
3763 diropCode = cm_DirLookup(&oldDirOp, oldNamep, &fileFid);
3765 if (diropCode == 0) {
3767 diropCode = cm_DirCreateEntry(&oldDirOp, newNamep, &fileFid);
3769 cm_BPlusDirCreateEntry(&oldDirOp, cNewNamep, &fileFid);
3773 if (diropCode == 0) {
3774 diropCode = cm_DirDeleteEntry(&oldDirOp, oldNamep);
3776 cm_BPlusDirDeleteEntry(&oldDirOp, cOldNamep);
3781 cm_EndDirOp(&oldDirOp);
3783 /* and update it for the new one, too, if necessary */
3786 lock_ObtainWrite(&newDirOp.scp->dirlock);
3787 newDirOp.lockType = CM_DIRLOCK_WRITE;
3789 lock_ObtainWrite(&newDscp->rw);
3790 cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
3792 cm_MergeStatus(NULL, newDscp, &updatedNewDirStatus, &volSync,
3793 userp, reqp, CM_MERGEFLAG_DIROP);
3794 lock_ReleaseWrite(&newDscp->rw);
3798 * The following optimization does not work.
3799 * When the file server processed a RXAFS_Rename() request the
3800 * FID of the object being moved between directories is not
3801 * preserved. The client does not know the new FID nor the
3802 * version number of the target. Not only can we not create
3803 * the directory entry in the new directory, but we can't
3804 * preserve the cached data for the file. It must be re-read
3805 * from the file server. - jaltman, 2009/02/20
3808 /* we only make the local change if we successfully made
3809 the change in the old directory AND there was only one
3810 change in the new directory */
3811 if (diropCode == 0 && cm_CheckDirOpForSingleChange(&newDirOp)) {
3812 cm_DirCreateEntry(&newDirOp, newNamep, &fileFid);
3814 cm_BPlusDirCreateEntry(&newDirOp, cNewNamep, &fileFid);
3819 cm_EndDirOp(&newDirOp);
3823 * After the rename the file server has invalidated the callbacks
3824 * on the file that was moved nor do we have a directory reference
3827 lock_ObtainWrite(&oldScp->rw);
3828 cm_DiscardSCache(oldScp);
3829 lock_ReleaseWrite(&oldScp->rw);
3833 cm_ReleaseSCache(oldScp);
3840 /* and return error code */
3844 /* Byte range locks:
3846 The OpenAFS Windows client has to fake byte range locks given no
3847 server side support for such locks. This is implemented as keyed
3848 byte range locks on the cache manager.
3850 Keyed byte range locks:
3852 Each cm_scache_t structure keeps track of a list of keyed locks.
3853 The key for a lock identifies an owner of a set of locks (referred
3854 to as a client). Each key is represented by a value. The set of
3855 key values used within a specific cm_scache_t structure form a
3856 namespace that has a scope of just that cm_scache_t structure. The
3857 same key value can be used with another cm_scache_t structure and
3858 correspond to a completely different client. However it is
3859 advantageous for the SMB or IFS layer to make sure that there is a
3860 1-1 mapping between client and keys over all cm_scache_t objects.
3862 Assume a client C has key Key(C) (although, since the scope of the
3863 key is a cm_scache_t, the key can be Key(C,S), where S is the
3864 cm_scache_t. But assume a 1-1 relation between keys and clients).
3865 A byte range (O,+L) denotes byte addresses (O) through (O+L-1)
3866 inclusive (a.k.a. [O,O+L-1]). The function Key(x) is implemented
3867 through cm_generateKey() function for both SMB and IFS.
3869 The list of locks for a cm_scache_t object S is maintained in
3870 S->fileLocks. The cache manager will set a lock on the AFS file
3871 server in order to assert the locks in S->fileLocks. If only
3872 shared locks are in place for S, then the cache manager will obtain
3873 a LockRead lock, while if there are any exclusive locks, it will
3874 obtain a LockWrite lock. If the exclusive locks are all released
3875 while the shared locks remain, then the cache manager will
3876 downgrade the lock from LockWrite to LockRead. Similarly, if an
3877 exclusive lock is obtained when only shared locks exist, then the
3878 cache manager will try to upgrade the lock from LockRead to
3881 Each lock L owned by client C maintains a key L->key such that
3882 L->key == Key(C), the effective range defined by L->LOffset and
3883 L->LLength such that the range of bytes affected by the lock is
3884 (L->LOffset, +L->LLength), a type maintained in L->LockType which
3885 is either exclusive or shared.
3889 A lock exists iff it is in S->fileLocks for some cm_scache_t
3890 S. Existing locks are in one of the following states: ACTIVE,
3891 WAITLOCK, WAITUNLOCK, LOST, DELETED.
3893 The following sections describe each lock and the associated
3896 1. ACTIVE: A lock L is ACTIVE iff the cache manager has asserted
3897 the lock with the AFS file server. This type of lock can be
3898 exercised by a client to read or write to the locked region (as
3901 1.1 ACTIVE->LOST: When the AFS file server fails to extend a
3902 server lock that was required to assert the lock. Before
3903 marking the lock as lost, the cache manager checks if the file
3904 has changed on the server. If the file has not changed, then
3905 the cache manager will attempt to obtain a new server lock
3906 that is sufficient to assert the client side locks for the
3907 file. If any of these fail, the lock is marked as LOST.
3908 Otherwise, it is left as ACTIVE.
3910 1.2 ACTIVE->DELETED: Lock is released.
3912 2. WAITLOCK: A lock is in a WAITLOCK state if the cache manager
3913 grants the lock but the lock is yet to be asserted with the AFS
3914 file server. Once the file server grants the lock, the state
3915 will transition to an ACTIVE lock.
3917 2.1 WAITLOCK->ACTIVE: The server granted the lock.
3919 2.2 WAITLOCK->DELETED: Lock is abandoned, or timed out during
3922 2.3 WAITLOCK->LOST: One or more locks from this client were
3923 marked as LOST. No further locks will be granted to this
3924 client until all lost locks are removed.
3926 3. WAITUNLOCK: A lock is in a WAITUNLOCK state if the cache manager
3927 receives a request for a lock that conflicts with an existing
3928 ACTIVE or WAITLOCK lock. The lock will be placed in the queue
3929 and will be granted at such time the conflicting locks are
3930 removed, at which point the state will transition to either
3933 3.1 WAITUNLOCK->ACTIVE: The conflicting lock was removed. The
3934 current serverLock is sufficient to assert this lock, or a
3935 sufficient serverLock is obtained.
3937 3.2 WAITUNLOCK->WAITLOCK: The conflicting lock was removed,
3938 however the required serverLock is yet to be asserted with the
3941 3.3 WAITUNLOCK->DELETED: The lock is abandoned, timed out or
3944 3.5 WAITUNLOCK->LOST: One or more locks from this client were
3945 marked as LOST. No further locks will be granted to this
3946 client until all lost locks are removed.
3948 4. LOST: A lock L is LOST if the server lock that was required to
3949 assert the lock could not be obtained or if it could not be
3950 extended, or if other locks by the same client were LOST.
3951 Essentially, once a lock is LOST, the contract between the cache
3952 manager and that specific client is no longer valid.
3954 The cache manager rechecks the server lock once every minute and
3955 extends it as appropriate. If this is not done for 5 minutes,
3956 the AFS file server will release the lock (the 5 minute timeout
3957 is based on current file server code and is fairly arbitrary).
3958 Once released, the lock cannot be re-obtained without verifying
3959 that the contents of the file hasn't been modified since the
3960 time the lock was released. Re-obtaining the lock without
3961 verifying this may lead to data corruption. If the lock can not
3962 be obtained safely, then all active locks for the cm_scache_t
3965 4.1 LOST->DELETED: The lock is released.
3967 5. DELETED: The lock is no longer relevant. Eventually, it will
3968 get removed from the cm_scache_t. In the meantime, it will be
3969 treated as if it does not exist.
3971 5.1 DELETED->not exist: The lock is removed from the
3974 The following are classifications of locks based on their state.
3976 6* A lock L is ACCEPTED if it is ACTIVE or WAITLOCK. These locks
3977 have been accepted by the cache manager, but may or may not have
3978 been granted back to the client.
3980 7* A lock L is QUEUED if it is ACTIVE, WAITLOCK or WAITUNLOCK.
3982 8* A lock L is WAITING if it is WAITLOCK or WAITUNLOCK.
3986 A client C can READ range (Offset,+Length) of a file represented by
3987 cm_scache_t S iff (1):
3989 1. for all _a_ in (Offset,+Length), all of the following is true:
3991 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3992 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3995 1.2 For each LOST lock L in S->fileLocks such that _a_ in
3996 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3999 (When locks are lost on an cm_scache_t, all locks are lost. By
4000 4.2 (below), if there is an exclusive LOST lock, then there
4001 can't be any overlapping ACTIVE locks.)
4003 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
4005 2. for all _a_ in (Offset,+Length), one of the following is true:
4007 2.1 Byte _a_ of S is unowned (as specified in 1.1) AND there
4008 does not exist a LOST lock L such that _a_ in
4009 (L->LOffset,+L->LLength).
4011 2.2 Byte _a_ of S is owned by C under lock L (as specified in
4012 1.2) AND L->LockType is exclusive.
4014 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
4016 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
4019 3.1 If L->LockType is exclusive then there does NOT exist a
4020 ACCEPTED lock M in S->fileLocks such that _a_ in
4021 (M->LOffset,+M->LLength).
4023 (If we count all QUEUED locks then we hit cases such as
4024 cascading waiting locks where the locks later on in the queue
4025 can be granted without compromising file integrity. On the
4026 other hand if only ACCEPTED locks are considered, then locks
4027 that were received earlier may end up waiting for locks that
4028 were received later to be unlocked. The choice of ACCEPTED
4029 locks was made to mimic the Windows byte range lock
4032 3.2 If L->LockType is shared then for each ACCEPTED lock M in
4033 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
4034 M->LockType is shared.
4036 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
4038 4.1 M->key != Key(C)
4040 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
4041 and (M->LOffset,+M->LLength) do not intersect.
4043 (Note: If a client loses a lock, it loses all locks.
4044 Subsequently, it will not be allowed to obtain any more locks
4045 until all existing LOST locks that belong to the client are
4046 released. Once all locks are released by a single client,
4047 there exists no further contract between the client and AFS
4048 about the contents of the file, hence the client can then
4049 proceed to obtain new locks and establish a new contract.
4051 This doesn't quite work as you think it should, because most
4052 applications aren't built to deal with losing locks they
4053 thought they once had. For now, we don't have a good
4054 solution to lost locks.
4056 Also, for consistency reasons, we have to hold off on
4057 granting locks that overlap exclusive LOST locks.)
4059 A client C can only unlock locks L in S->fileLocks which have
4062 The representation and invariants are as follows:
4064 - Each cm_scache_t structure keeps:
4066 - A queue of byte-range locks (cm_scache_t::fileLocks) which
4067 are of type cm_file_lock_t.
4069 - A record of the highest server-side lock that has been
4070 obtained for this object (cm_scache_t::serverLock), which is
4071 one of (-1), LockRead, LockWrite.
4073 - A count of ACCEPTED exclusive and shared locks that are in the
4074 queue (cm_scache_t::sharedLocks and
4075 cm_scache_t::exclusiveLocks)
4077 - Each cm_file_lock_t structure keeps:
4079 - The type of lock (cm_file_lock_t::LockType)
4081 - The key associated with the lock (cm_file_lock_t::key)
4083 - The offset and length of the lock (cm_file_lock_t::LOffset
4084 and cm_file_lock_t::LLength)
4086 - The state of the lock.
4088 - Time of issuance or last successful extension
4090 Semantic invariants:
4092 I1. The number of ACCEPTED locks in S->fileLocks are
4093 (S->sharedLocks + S->exclusiveLocks)
4095 External invariants:
4097 I3. S->serverLock is the lock that we have asserted with the
4098 AFS file server for this cm_scache_t.
4100 I4. S->serverLock == LockRead iff there is at least one ACTIVE
4101 shared lock, but no ACTIVE exclusive locks.
4103 I5. S->serverLock == LockWrite iff there is at least one ACTIVE
4106 I6. If L is a LOST lock, then for each lock M in S->fileLocks,
4107 M->key == L->key IMPLIES M is LOST or DELETED.
4112 #define IS_LOCK_ACTIVE(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == 0)
4114 #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)
4116 #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)
4118 #define IS_LOCK_LOST(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_LOST)
4120 #define IS_LOCK_DELETED(lockp) (((lockp)->flags & CM_FILELOCK_FLAG_DELETED) == CM_FILELOCK_FLAG_DELETED)
4123 #define IS_LOCK_ACCEPTED(lockp) (IS_LOCK_ACTIVE(lockp) || IS_LOCK_WAITLOCK(lockp))
4126 #define IS_LOCK_CLIENTONLY(lockp) ((((lockp)->scp->flags & CM_SCACHEFLAG_RO) == CM_SCACHEFLAG_RO) || (((lockp)->flags & CM_FILELOCK_FLAG_CLIENTONLY) == CM_FILELOCK_FLAG_CLIENTONLY))
4129 #define INTERSECT_RANGE(r1,r2) (((r2).offset+(r2).length) > (r1).offset && ((r1).offset +(r1).length) > (r2).offset)
4132 #define CONTAINS_RANGE(r1,r2) (((r2).offset+(r2).length) <= ((r1).offset+(r1).length) && (r1).offset <= (r2).offset)
4134 #if defined(VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS) && !defined(LOCK_TESTING)
4135 #define SCP_SUPPORTS_BRLOCKS(scp) ((scp)->cbServerp && ((scp)->cbServerp->capabilities & VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS))
4137 #define SCP_SUPPORTS_BRLOCKS(scp) (1)
4140 #define SERVERLOCKS_ENABLED(scp) (!((scp)->flags & CM_SCACHEFLAG_RO) && cm_enableServerLocks && SCP_SUPPORTS_BRLOCKS(scp))
4142 #if defined(VICED_CAPABILITY_WRITELOCKACL)
4143 #define SCP_SUPPORTS_WRITELOCKACL(scp) ((scp)->cbServerp && ((scp->cbServerp->capabilities & VICED_CAPABILITY_WRITELOCKACL)))
4145 #define SCP_SUPPORTS_WRITELOCKACL(scp) (0)
4147 /* This should really be defined in any build that this code is being
4149 #error VICED_CAPABILITY_WRITELOCKACL not defined.
4152 static void cm_LockRangeSubtract(cm_range_t * pos, const cm_range_t * neg)
4154 afs_int64 int_begin;
4157 int_begin = MAX(pos->offset, neg->offset);
4158 int_end = MIN(pos->offset+pos->length, neg->offset+neg->length);
4160 if (int_begin < int_end) {
4161 if (int_begin == pos->offset) {
4162 pos->length = pos->offset + pos->length - int_end;
4163 pos->offset = int_end;
4164 } else if (int_end == pos->offset + pos->length) {
4165 pos->length = int_begin - pos->offset;
4168 /* We only subtract ranges if the resulting range is
4169 contiguous. If we try to support non-contigous ranges, we
4170 aren't actually improving performance. */
4174 /* Called with scp->rw held. Returns 0 if all is clear to read the
4175 specified range by the client identified by key.
4177 long cm_LockCheckRead(cm_scache_t *scp,
4178 LARGE_INTEGER LOffset,
4179 LARGE_INTEGER LLength,
4182 #ifndef ADVISORY_LOCKS
4184 cm_file_lock_t *fileLock;
4188 int substract_ranges = FALSE;
4190 range.offset = LOffset.QuadPart;
4191 range.length = LLength.QuadPart;
4195 1. for all _a_ in (Offset,+Length), all of the following is true:
4197 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
4198 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
4201 1.2 For each LOST lock L in S->fileLocks such that _a_ in
4202 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
4207 lock_ObtainRead(&cm_scacheLock);
4209 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
4211 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4213 if (INTERSECT_RANGE(range, fileLock->range)) {
4214 if (IS_LOCK_ACTIVE(fileLock)) {
4215 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4217 /* If there is an active lock for this client, it
4218 is safe to substract ranges.*/
4219 cm_LockRangeSubtract(&range, &fileLock->range);
4220 substract_ranges = TRUE;
4222 if (fileLock->lockType != LockRead) {
4223 code = CM_ERROR_LOCK_CONFLICT;
4227 /* even if the entire range is locked for reading,
4228 we still can't grant the lock at this point
4229 because the client may have lost locks. That
4230 is, unless we have already seen an active lock
4231 belonging to the client, in which case there
4232 can't be any lost locks for this client. */
4233 if (substract_ranges)
4234 cm_LockRangeSubtract(&range, &fileLock->range);
4236 } else if (IS_LOCK_LOST(fileLock) &&
4237 (cm_KeyEquals(&fileLock->key, &key, 0) || fileLock->lockType == LockWrite)) {
4238 code = CM_ERROR_BADFD;
4244 lock_ReleaseRead(&cm_scacheLock);
4246 osi_Log4(afsd_logp, "cm_LockCheckRead scp 0x%x offset %d length %d code 0x%x",
4247 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
4258 /* Called with scp->rw held. Returns 0 if all is clear to write the
4259 specified range by the client identified by key.
4261 long cm_LockCheckWrite(cm_scache_t *scp,
4262 LARGE_INTEGER LOffset,
4263 LARGE_INTEGER LLength,
4266 #ifndef ADVISORY_LOCKS
4268 cm_file_lock_t *fileLock;
4273 range.offset = LOffset.QuadPart;
4274 range.length = LLength.QuadPart;
4277 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
4279 2. for all _a_ in (Offset,+Length), one of the following is true:
4281 2.1 Byte _a_ of S is unowned AND there does not exist a LOST
4282 lock L such that _a_ in (L->LOffset,+L->LLength).
4284 2.2 Byte _a_ of S is owned by C under lock L AND L->LockType is
4288 lock_ObtainRead(&cm_scacheLock);
4290 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
4292 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4294 if (INTERSECT_RANGE(range, fileLock->range)) {
4295 if (IS_LOCK_ACTIVE(fileLock)) {
4296 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4297 if (fileLock->lockType == LockWrite) {
4299 /* if there is an active lock for this client, it
4300 is safe to substract ranges */
4301 cm_LockRangeSubtract(&range, &fileLock->range);
4303 code = CM_ERROR_LOCK_CONFLICT;
4307 code = CM_ERROR_LOCK_CONFLICT;
4310 } else if (IS_LOCK_LOST(fileLock)) {
4311 code = CM_ERROR_BADFD;
4317 lock_ReleaseRead(&cm_scacheLock);
4319 osi_Log4(afsd_logp, "cm_LockCheckWrite scp 0x%x offset %d length %d code 0x%x",
4320 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
4331 /* Called with cm_scacheLock write locked */
4332 static cm_file_lock_t * cm_GetFileLock(void) {
4335 l = (cm_file_lock_t *) cm_freeFileLocks;
4337 osi_QRemove(&cm_freeFileLocks, &l->q);
4339 l = malloc(sizeof(cm_file_lock_t));
4340 osi_assertx(l, "null cm_file_lock_t");
4343 memset(l, 0, sizeof(cm_file_lock_t));
4348 /* Called with cm_scacheLock write locked */
4349 static void cm_PutFileLock(cm_file_lock_t *l) {
4350 osi_QAdd(&cm_freeFileLocks, &l->q);
4353 /* called with scp->rw held. May release it during processing, but
4354 leaves it held on exit. */
4355 long cm_IntSetLock(cm_scache_t * scp, cm_user_t * userp, int lockType,
4361 struct rx_connection * rxconnp;
4363 afs_uint32 reqflags = reqp->flags;
4365 memset(&volSync, 0, sizeof(volSync));
4367 tfid.Volume = scp->fid.volume;
4368 tfid.Vnode = scp->fid.vnode;
4369 tfid.Unique = scp->fid.unique;
4372 osi_Log2(afsd_logp, "CALL SetLock scp 0x%p for lock %d", scp, lockType);
4374 reqp->flags |= CM_REQ_NORETRY;
4375 lock_ReleaseWrite(&scp->rw);
4378 code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4382 rxconnp = cm_GetRxConn(connp);
4383 code = RXAFS_SetLock(rxconnp, &tfid, lockType,
4385 rx_PutConnection(rxconnp);
4387 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
4390 code = cm_MapRPCError(code, reqp);
4392 osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
4394 osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
4397 lock_ObtainWrite(&scp->rw);
4398 reqp->flags = reqflags;
4402 /* called with scp->rw held. Releases it during processing */
4403 long cm_IntReleaseLock(cm_scache_t * scp, cm_user_t * userp,
4409 struct rx_connection * rxconnp;
4412 memset(&volSync, 0, sizeof(volSync));
4414 tfid.Volume = scp->fid.volume;
4415 tfid.Vnode = scp->fid.vnode;
4416 tfid.Unique = scp->fid.unique;
4419 lock_ReleaseWrite(&scp->rw);
4421 osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
4424 code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4428 rxconnp = cm_GetRxConn(connp);
4429 code = RXAFS_ReleaseLock(rxconnp, &tfid, &volSync);
4430 rx_PutConnection(rxconnp);
4432 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
4434 code = cm_MapRPCError(code, reqp);
4437 "CALL ReleaseLock FAILURE, code 0x%x", code);
4440 "CALL ReleaseLock SUCCESS");
4442 lock_ObtainWrite(&scp->rw);
4447 /* called with scp->rw held. May release it during processing, but
4448 will exit with lock held.
4452 - 0 if the user has permission to get the specified lock for the scp
4454 - CM_ERROR_NOACCESS if not
4456 Any other error from cm_SyncOp will be sent down untranslated.
4458 If CM_ERROR_NOACCESS is returned and lock_type is LockRead, then
4459 phas_insert (if non-NULL) will receive a boolean value indicating
4460 whether the user has INSERT permission or not.
4462 long cm_LockCheckPerms(cm_scache_t * scp,
4469 long code = 0, code2 = 0;
4471 /* lock permissions are slightly tricky because of the 'i' bit.
4472 If the user has PRSFS_LOCK, she can read-lock the file. If the
4473 user has PRSFS_WRITE, she can write-lock the file. However, if
4474 the user has PRSFS_INSERT, then she can write-lock new files,
4475 but not old ones. Since we don't have information about
4476 whether a file is new or not, we assume that if the user owns
4477 the scp, then she has the permissions that are granted by
4480 osi_Log3(afsd_logp, "cm_LockCheckPerms for scp[0x%p] type[%d] user[0x%p]",
4481 scp, lock_type, userp);
4483 if (lock_type == LockRead)
4484 rights |= PRSFS_LOCK;
4485 else if (lock_type == LockWrite)
4486 rights |= PRSFS_WRITE | PRSFS_LOCK;
4489 osi_assertx(FALSE, "invalid lock type");
4494 *phas_insert = FALSE;
4496 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
4497 CM_SCACHESYNC_GETSTATUS |
4498 CM_SCACHESYNC_NEEDCALLBACK);
4500 if (phas_insert && scp->creator == userp) {
4502 /* If this file was created by the user, then we check for
4503 PRSFS_INSERT. If the file server is recent enough, then
4504 this should be sufficient for her to get a write-lock (but
4505 not necessarily a read-lock). VICED_CAPABILITY_WRITELOCKACL
4506 indicates whether a file server supports getting write
4507 locks when the user only has PRSFS_INSERT.
4509 If the file was not created by the user we skip the check
4510 because the INSERT bit will not apply to this user even
4514 code2 = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
4515 CM_SCACHESYNC_GETSTATUS |
4516 CM_SCACHESYNC_NEEDCALLBACK);
4518 if (code2 == CM_ERROR_NOACCESS) {
4519 osi_Log0(afsd_logp, "cm_LockCheckPerms user has no INSERT bits");
4521 *phas_insert = TRUE;
4522 osi_Log0(afsd_logp, "cm_LockCheckPerms user has INSERT bits");
4526 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
4528 osi_Log1(afsd_logp, "cm_LockCheckPerms returning code %d", code);
4533 /* called with scp->rw held */
4534 long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
4535 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
4537 int allowWait, cm_user_t *userp, cm_req_t *reqp,
4538 cm_file_lock_t **lockpp)
4541 int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
4542 cm_file_lock_t *fileLock;
4545 int wait_unlock = FALSE;
4546 int force_client_lock = FALSE;
4548 osi_Log4(afsd_logp, "cm_Lock scp 0x%x type 0x%x offset %d length %d",
4549 scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
4550 osi_Log4(afsd_logp, "... allowWait %d key <0x%x, 0x%x, 0x%x>", allowWait,
4551 key.process_id, key.session_id, key.file_id);
4554 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
4556 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
4559 3.1 If L->LockType is exclusive then there does NOT exist a
4560 ACCEPTED lock M in S->fileLocks such that _a_ in
4561 (M->LOffset,+M->LLength).
4563 3.2 If L->LockType is shared then for each ACCEPTED lock M in
4564 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
4565 M->LockType is shared.
4567 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
4569 4.1 M->key != Key(C)
4571 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
4572 and (M->LOffset,+M->LLength) do not intersect.
4575 range.offset = LOffset.QuadPart;
4576 range.length = LLength.QuadPart;
4578 lock_ObtainRead(&cm_scacheLock);
4580 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4582 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4584 if (IS_LOCK_LOST(fileLock)) {
4585 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4586 code = CM_ERROR_BADFD;
4588 } else if (fileLock->lockType == LockWrite && INTERSECT_RANGE(range, fileLock->range)) {
4589 code = CM_ERROR_WOULDBLOCK;
4595 /* we don't need to check for deleted locks here since deleted
4596 locks are dequeued from scp->fileLocks */
4597 if (IS_LOCK_ACCEPTED(fileLock) &&
4598 INTERSECT_RANGE(range, fileLock->range)) {
4600 if ((sLockType & LOCKING_ANDX_SHARED_LOCK) == 0 ||
4601 fileLock->lockType != LockRead) {
4603 code = CM_ERROR_WOULDBLOCK;
4609 lock_ReleaseRead(&cm_scacheLock);
4611 if (code == 0 && SERVERLOCKS_ENABLED(scp)) {
4612 if (Which == scp->serverLock ||
4613 (Which == LockRead && scp->serverLock == LockWrite)) {
4617 /* we already have the lock we need */
4618 osi_Log3(afsd_logp, " we already have the correct lock. exclusives[%d], shared[%d], serverLock[%d]",
4619 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4621 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4623 /* special case: if we don't have permission to read-lock
4624 the file, then we force a clientside lock. This is to
4625 compensate for applications that obtain a read-lock for
4626 reading files off of directories that don't grant
4627 read-locks to the user. */
4628 if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4630 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4631 osi_Log0(afsd_logp, " User has no read-lock perms, but has INSERT perms.");
4634 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4635 force_client_lock = TRUE;
4639 } else if ((scp->exclusiveLocks > 0) ||
4640 (scp->sharedLocks > 0 && scp->serverLock != LockRead)) {
4643 /* We are already waiting for some other lock. We should
4644 wait for the daemon to catch up instead of generating a
4645 flood of SetLock calls. */
4646 osi_Log3(afsd_logp, " already waiting for other lock. exclusives[%d], shared[%d], serverLock[%d]",
4647 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4649 /* see if we have permission to create the lock in the
4651 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4653 code = CM_ERROR_WOULDBLOCK;
4654 else if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4656 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4658 " User has no read-lock perms, but has INSERT perms.");
4659 code = CM_ERROR_WOULDBLOCK;
4662 " User has no read-lock perms. Forcing client-side lock");
4663 force_client_lock = TRUE;
4667 /* leave any other codes as-is */
4671 int check_data_version = FALSE;
4674 /* first check if we have permission to elevate or obtain
4676 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4678 if (code == CM_ERROR_NOACCESS && Which == LockRead &&
4679 (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp))) {
4680 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4681 force_client_lock = TRUE;
4686 /* has_insert => (Which == LockRead, code == CM_ERROR_NOACCESS) */
4688 if (scp->serverLock == LockRead && Which == LockWrite) {
4690 /* We want to escalate the lock to a LockWrite.
4691 * Unfortunately that's not really possible without
4692 * letting go of the current lock. But for now we do
4696 " attempting to UPGRADE from LockRead to LockWrite.");
4698 " dataVersion on scp: %I64d", scp->dataVersion);
4700 /* we assume at this point (because scp->serverLock
4701 was valid) that we had a valid server lock. */
4702 scp->lockDataVersion = scp->dataVersion;
4703 check_data_version = TRUE;
4705 code = cm_IntReleaseLock(scp, userp, reqp);
4708 /* We couldn't release the lock */
4711 scp->serverLock = -1;
4715 /* We need to obtain a server lock of type Which in order
4716 * to assert this file lock */
4717 #ifndef AGGRESSIVE_LOCKS
4720 newLock = LockWrite;
4723 code = cm_IntSetLock(scp, userp, newLock, reqp);
4725 #ifdef AGGRESSIVE_LOCKS
4726 if ((code == CM_ERROR_WOULDBLOCK ||
4727 code == CM_ERROR_NOACCESS) && newLock != Which) {
4728 /* we wanted LockRead. We tried LockWrite. Now try
4733 osi_assertx(newLock == LockRead, "lock type not read");
4735 code = cm_IntSetLock(scp, userp, newLock, reqp);
4739 if (code == CM_ERROR_NOACCESS) {
4740 if (Which == LockRead) {
4741 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4743 /* We requested a read-lock, but we have permission to
4744 * get a write-lock. Try that */
4746 tcode = cm_LockCheckPerms(scp, LockWrite, userp, reqp, NULL);
4749 newLock = LockWrite;
4751 osi_Log0(afsd_logp, " User has 'i' perms and the request was for a LockRead. Trying to get a LockWrite instead");
4753 code = cm_IntSetLock(scp, userp, newLock, reqp);
4756 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4757 force_client_lock = TRUE;
4759 } else if (Which == LockWrite &&
4760 scp->creator == userp && !SCP_SUPPORTS_WRITELOCKACL(scp)) {
4763 /* Special case: if the lock request was for a
4764 * LockWrite and the user owns the file and we weren't
4765 * allowed to obtain the serverlock, we either lost a
4766 * race (the permissions changed from under us), or we
4767 * have 'i' bits, but we aren't allowed to lock the
4770 /* check if we lost a race... */
4771 tcode = cm_LockCheckPerms(scp, Which, userp, reqp, NULL);
4774 osi_Log0(afsd_logp, " User has 'i' perms but can't obtain write locks. Using client-side locks.");
4775 force_client_lock = TRUE;
4780 if (code == 0 && check_data_version &&
4781 scp->dataVersion != scp->lockDataVersion) {
4782 /* We lost a race. Although we successfully obtained
4783 * a lock, someone modified the file in between. The
4784 * locks have all been technically lost. */
4787 " Data version mismatch while upgrading lock.");
4789 " Data versions before=%I64d, after=%I64d",
4790 scp->lockDataVersion,
4793 " Releasing stale lock for scp 0x%x", scp);
4795 code = cm_IntReleaseLock(scp, userp, reqp);
4797 scp->serverLock = -1;
4799 code = CM_ERROR_INVAL;
4800 } else if (code == 0) {
4801 scp->serverLock = newLock;
4802 scp->lockDataVersion = scp->dataVersion;
4806 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4807 scp->serverLock == -1) {
4808 /* Oops. We lost the lock. */
4809 cm_LockMarkSCacheLost(scp);
4812 } else if (code == 0) { /* server locks not enabled */
4814 " Skipping server lock for scp");
4819 if (code != 0 && !force_client_lock) {
4820 /* Special case error translations
4822 Applications don't expect certain errors from a
4823 LockFile/UnlockFile call. We need to translate some error
4824 code to codes that apps expect and handle. */
4826 /* We shouldn't actually need to handle this case since we
4827 simulate locks for RO scps anyway. */
4828 if (code == CM_ERROR_READONLY) {
4829 osi_Log0(afsd_logp, " Reinterpreting CM_ERROR_READONLY as CM_ERROR_NOACCESS");
4830 code = CM_ERROR_NOACCESS;
4834 if (code == 0 || (code == CM_ERROR_WOULDBLOCK && allowWait) ||
4835 force_client_lock) {
4837 /* clear the error if we are forcing a client lock, so we
4838 don't get confused later. */
4839 if (force_client_lock && code != CM_ERROR_WOULDBLOCK)
4842 lock_ObtainWrite(&cm_scacheLock);
4843 fileLock = cm_GetFileLock();
4844 lock_ReleaseWrite(&cm_scacheLock);
4846 fileLock->fid = scp->fid;
4848 fileLock->key = key;
4849 fileLock->lockType = Which;
4851 fileLock->userp = userp;
4852 fileLock->range = range;
4853 fileLock->flags = (code == 0 ? 0 :
4855 CM_FILELOCK_FLAG_WAITUNLOCK :
4856 CM_FILELOCK_FLAG_WAITLOCK));
4858 if (force_client_lock || !SERVERLOCKS_ENABLED(scp))
4859 fileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
4861 fileLock->lastUpdate = (code == 0 && !force_client_lock) ? time(NULL) : 0;
4863 lock_ObtainWrite(&cm_scacheLock);
4864 osi_QAddT(&scp->fileLocksH, &scp->fileLocksT, &fileLock->fileq);
4865 cm_HoldSCacheNoLock(scp);
4866 fileLock->scp = scp;
4867 osi_QAdd(&cm_allFileLocks, &fileLock->q);
4868 lock_ReleaseWrite(&cm_scacheLock);
4874 if (IS_LOCK_CLIENTONLY(fileLock)) {
4876 } else if (IS_LOCK_ACCEPTED(fileLock)) {
4877 if (Which == LockRead)
4880 scp->exclusiveLocks++;
4884 "cm_Lock Lock added 0x%p flags 0x%x to scp [0x%p]",
4885 fileLock, fileLock->flags, scp);
4887 " exclusives[%d] shared[%d] client[%d] serverLock[%d]",
4888 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4889 (int)(signed char) scp->serverLock);
4892 "cm_Lock Rejecting lock (code = 0x%x)", code);
4895 /* Convert from would block to lock not granted */
4896 if (code == CM_ERROR_WOULDBLOCK)
4897 code = CM_ERROR_LOCK_NOT_GRANTED;
4902 /* Called with scp->rw held */
4903 long cm_UnlockByKey(cm_scache_t * scp,
4910 cm_file_lock_t *fileLock;
4911 osi_queue_t *q, *qn;
4914 osi_Log4(afsd_logp, "cm_UnlockByKey scp 0x%p key <0x%x,0x%x,0x%x",
4915 scp, key.process_id, key.session_id, key.file_id);
4916 osi_Log1(afsd_logp, " flags=0x%x", flags);
4918 lock_ObtainWrite(&cm_scacheLock);
4920 for (q = scp->fileLocksH; q; q = qn) {
4923 fileLock = (cm_file_lock_t *)
4924 ((char *) q - offsetof(cm_file_lock_t, fileq));
4927 osi_Log4(afsd_logp, " Checking lock[0x%x] range[%d,+%d] type[%d]",
4929 (unsigned long) fileLock->range.offset,
4930 (unsigned long) fileLock->range.length,
4931 fileLock->lockType);
4932 osi_Log4(afsd_logp, " key<0x%x, 0x%x, 0x%x> flags[0x%x]",
4933 fileLock->key.process_id, fileLock->key.session_id, fileLock->key.file_id,
4936 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
4937 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
4938 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4940 fileLock->fid.volume,
4941 fileLock->fid.vnode,
4942 fileLock->fid.unique);
4943 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4944 fileLock->scp->fid.cell,
4945 fileLock->scp->fid.volume,
4946 fileLock->scp->fid.vnode,
4947 fileLock->scp->fid.unique);
4948 osi_assertx(FALSE, "invalid fid value");
4952 if (!IS_LOCK_DELETED(fileLock) &&
4953 cm_KeyEquals(&fileLock->key, &key, flags)) {
4954 osi_Log3(afsd_logp, "...Unlock range [%d,+%d] type %d",
4955 fileLock->range.offset,
4956 fileLock->range.length,
4957 fileLock->lockType);
4959 if (scp->fileLocksT == q)
4960 scp->fileLocksT = osi_QPrev(q);
4961 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
4963 if (IS_LOCK_CLIENTONLY(fileLock)) {
4965 } else if (IS_LOCK_ACCEPTED(fileLock)) {
4966 if (fileLock->lockType == LockRead)
4969 scp->exclusiveLocks--;
4972 fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
4974 cm_ReleaseUser(fileLock->userp);
4975 cm_ReleaseSCacheNoLock(scp);
4977 fileLock->userp = NULL;
4978 fileLock->scp = NULL;
4984 lock_ReleaseWrite(&cm_scacheLock);
4986 if (n_unlocks == 0) {
4987 osi_Log0(afsd_logp, "cm_UnlockByKey no locks found");
4988 osi_Log3(afsd_logp, " Leaving scp with exclusives[%d], shared[%d], serverLock[%d]",
4989 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4994 osi_Log1(afsd_logp, "cm_UnlockByKey done with %d locks", n_unlocks);
4996 osi_assertx(scp->sharedLocks >= 0, "scp->sharedLocks < 0");
4997 osi_assertx(scp->exclusiveLocks >= 0, "scp->exclusiveLocks < 0");
4998 osi_assertx(scp->clientLocks >= 0, "scp->clientLocks < 0");
5000 if (!SERVERLOCKS_ENABLED(scp)) {
5001 osi_Log0(afsd_logp, " Skipping server lock for scp");
5005 /* Ideally we would go through the rest of the locks to determine
5006 * if one or more locks that were formerly in WAITUNLOCK can now
5007 * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
5008 * scp->sharedLocks accordingly. However, the retrying of locks
5009 * in that manner is done cm_RetryLock() manually.
5012 if (scp->serverLock == LockWrite &&
5013 scp->exclusiveLocks == 0 &&
5014 scp->sharedLocks > 0) {
5015 /* The serverLock should be downgraded to LockRead */
5016 osi_Log0(afsd_logp, " DOWNGRADE lock from LockWrite to LockRead");
5018 /* Make sure there are no dirty buffers left. */
5019 code = cm_FSync(scp, userp, reqp, TRUE);
5021 /* since scp->serverLock looked sane, we are going to assume
5022 that we have a valid server lock. */
5023 scp->lockDataVersion = scp->dataVersion;
5024 osi_Log1(afsd_logp, " dataVersion on scp = %I64d", scp->dataVersion);
5026 code = cm_IntReleaseLock(scp, userp, reqp);
5029 /* so we couldn't release it. Just let the lock be for now */
5033 scp->serverLock = -1;
5036 code = cm_IntSetLock(scp, userp, LockRead, reqp);
5038 if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
5039 scp->serverLock = LockRead;
5040 } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
5041 /* We lost a race condition. Although we have a valid
5042 lock on the file, the data has changed and essentially
5043 we have lost the lock we had during the transition. */
5045 osi_Log0(afsd_logp, "Data version mismatch during lock downgrade");
5046 osi_Log2(afsd_logp, " Data versions before=%I64d, after=%I64d",
5047 scp->lockDataVersion,
5050 code = cm_IntReleaseLock(scp, userp, reqp);
5052 code = CM_ERROR_INVAL;
5053 scp->serverLock = -1;
5057 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
5058 (scp->serverLock == -1)) {
5060 cm_LockMarkSCacheLost(scp);
5063 /* failure here has no bearing on the return value of
5067 } else if (scp->serverLock != (-1) &&
5068 scp->exclusiveLocks == 0 &&
5069 scp->sharedLocks == 0) {
5070 /* The serverLock should be released entirely */
5072 if (scp->serverLock == LockWrite) {
5073 /* Make sure there are no dirty buffers left. */
5074 code = cm_FSync(scp, userp, reqp, TRUE);
5077 code = cm_IntReleaseLock(scp, userp, reqp);
5080 scp->serverLock = (-1);
5085 osi_Log1(afsd_logp, "cm_UnlockByKey code 0x%x", code);
5086 osi_Log4(afsd_logp, " Leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
5087 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
5088 (int)(signed char) scp->serverLock);
5093 long cm_Unlock(cm_scache_t *scp,
5094 unsigned char sLockType,
5095 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
5102 int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
5103 cm_file_lock_t *fileLock;
5105 int release_userp = FALSE;
5106 int exact_match = !(flags & CM_UNLOCK_FLAG_MATCH_RANGE);
5108 LARGE_INTEGER RangeEnd;
5110 osi_Log4(afsd_logp, "cm_Unlock scp 0x%p type 0x%x offset %d length %d",
5111 scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
5112 osi_Log4(afsd_logp, "... key <0x%x,0x%x,0x%x> flags 0x%x",
5113 key.process_id, key.session_id, key.file_id, flags);
5116 RangeEnd.QuadPart = LOffset.QuadPart + LLength.QuadPart;
5119 lock_ObtainRead(&cm_scacheLock);
5121 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5122 fileLock = (cm_file_lock_t *)
5123 ((char *) q - offsetof(cm_file_lock_t, fileq));
5126 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5127 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5128 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5130 fileLock->fid.volume,
5131 fileLock->fid.vnode,
5132 fileLock->fid.unique);
5133 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5134 fileLock->scp->fid.cell,
5135 fileLock->scp->fid.volume,
5136 fileLock->scp->fid.vnode,
5137 fileLock->scp->fid.unique);
5138 osi_assertx(FALSE, "invalid fid value");
5142 if (!IS_LOCK_DELETED(fileLock) &&
5143 cm_KeyEquals(&fileLock->key, &key, 0) &&
5144 fileLock->range.offset == LOffset.QuadPart &&
5145 fileLock->range.length == LLength.QuadPart) {
5151 if (!IS_LOCK_DELETED(fileLock) &&
5152 cm_KeyEquals(&fileLock->key, &key, 0) &&
5153 fileLock->range.offset >= LOffset.QuadPart &&
5154 fileLock->range.offset < RangeEnd.QuadPart &&
5155 (fileLock->range.offset + fileLock->range.length) <= RangeEnd.QuadPart) {
5163 lock_ReleaseRead(&cm_scacheLock);
5165 if (lock_found && !exact_match) {
5169 osi_Log0(afsd_logp, "cm_Unlock lock not found; failure");
5171 /* The lock didn't exist anyway. *shrug* */
5172 return CM_ERROR_RANGE_NOT_LOCKED;
5176 /* discard lock record */
5177 lock_ConvertRToW(&cm_scacheLock);
5178 if (scp->fileLocksT == q)
5179 scp->fileLocksT = osi_QPrev(q);
5180 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
5183 * Don't delete it here; let the daemon delete it, to simplify
5184 * the daemon's traversal of the list.
5187 if (IS_LOCK_CLIENTONLY(fileLock)) {
5189 } else if (IS_LOCK_ACCEPTED(fileLock)) {
5190 if (fileLock->lockType == LockRead)
5193 scp->exclusiveLocks--;
5196 fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5197 if (userp != NULL) {
5198 cm_ReleaseUser(fileLock->userp);
5200 userp = fileLock->userp;
5201 release_userp = TRUE;
5203 fileLock->userp = NULL;
5204 cm_ReleaseSCacheNoLock(scp);
5205 fileLock->scp = NULL;
5206 lock_ReleaseWrite(&cm_scacheLock);
5208 if (!SERVERLOCKS_ENABLED(scp)) {
5209 osi_Log0(afsd_logp, " Skipping server locks for scp");
5213 /* Ideally we would go through the rest of the locks to determine
5214 * if one or more locks that were formerly in WAITUNLOCK can now
5215 * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
5216 * scp->sharedLocks accordingly. However, the retrying of locks
5217 * in that manner is done cm_RetryLock() manually.
5220 if (scp->serverLock == LockWrite &&
5221 scp->exclusiveLocks == 0 &&
5222 scp->sharedLocks > 0) {
5224 /* The serverLock should be downgraded to LockRead */
5225 osi_Log0(afsd_logp, " DOWNGRADE lock from LockWrite to LockRead");
5227 /* Make sure there are no dirty buffers left. */
5228 code = cm_FSync(scp, userp, reqp, TRUE);
5230 /* Since we already had a lock, we assume that there is a
5231 valid server lock. */
5232 scp->lockDataVersion = scp->dataVersion;
5233 osi_Log1(afsd_logp, " dataVersion on scp is %I64d", scp->dataVersion);
5235 /* before we downgrade, make sure that we have enough
5236 permissions to get the read lock. */
5237 code = cm_LockCheckPerms(scp, LockRead, userp, reqp, NULL);
5240 osi_Log0(afsd_logp, " SKIPPING downgrade because user doesn't have perms to get downgraded lock");
5246 code = cm_IntReleaseLock(scp, userp, reqp);
5249 /* so we couldn't release it. Just let the lock be for now */
5253 scp->serverLock = -1;
5256 code = cm_IntSetLock(scp, userp, LockRead, reqp);
5258 if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
5259 scp->serverLock = LockRead;
5260 } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
5261 /* Lost a race. We obtained a new lock, but that is
5262 meaningless since someone modified the file
5266 "Data version mismatch while downgrading lock");
5268 " Data versions before=%I64d, after=%I64d",
5269 scp->lockDataVersion,
5272 code = cm_IntReleaseLock(scp, userp, reqp);
5274 scp->serverLock = -1;
5275 code = CM_ERROR_INVAL;
5279 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
5280 (scp->serverLock == -1)) {
5282 cm_LockMarkSCacheLost(scp);
5285 /* failure here has no bearing on the return value of
5289 } else if (scp->serverLock != (-1) &&
5290 scp->exclusiveLocks == 0 &&
5291 scp->sharedLocks == 0) {
5292 /* The serverLock should be released entirely */
5294 if (scp->serverLock == LockWrite) {
5295 /* Make sure there are no dirty buffers left. */
5296 code = cm_FSync(scp, userp, reqp, TRUE);
5299 code = cm_IntReleaseLock(scp, userp, reqp);
5302 scp->serverLock = (-1);
5306 if (release_userp) {
5307 cm_ReleaseUser(userp);
5308 release_userp = FALSE;
5312 goto try_again; /* might be more than one lock in the range */
5316 osi_Log1(afsd_logp, "cm_Unlock code 0x%x", code);
5317 osi_Log4(afsd_logp, " leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
5318 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
5319 (int)(signed char) scp->serverLock);
5324 /* called with scp->rw held */
5325 void cm_LockMarkSCacheLost(cm_scache_t * scp)
5327 cm_file_lock_t *fileLock;
5330 osi_Log1(afsd_logp, "cm_LockMarkSCacheLost scp 0x%x", scp);
5332 /* cm_scacheLock needed because we are modifying fileLock->flags */
5333 lock_ObtainWrite(&cm_scacheLock);
5335 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5337 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
5339 if (IS_LOCK_ACTIVE(fileLock) &&
5340 !IS_LOCK_CLIENTONLY(fileLock)) {
5341 if (fileLock->lockType == LockRead)
5344 scp->exclusiveLocks--;
5346 fileLock->flags |= CM_FILELOCK_FLAG_LOST;
5350 scp->serverLock = -1;
5351 scp->lockDataVersion = CM_SCACHE_VERSION_BAD;
5352 lock_ReleaseWrite(&cm_scacheLock);
5355 /* Called with no relevant locks held */
5356 void cm_CheckLocks()
5358 osi_queue_t *q, *nq;
5359 cm_file_lock_t *fileLock;
5365 struct rx_connection * rxconnp;
5368 memset(&volSync, 0, sizeof(volSync));
5372 lock_ObtainWrite(&cm_scacheLock);
5374 cm_lockRefreshCycle++;
5376 osi_Log1(afsd_logp, "cm_CheckLocks starting lock check cycle %d", cm_lockRefreshCycle);
5378 for (q = cm_allFileLocks; q; q = nq) {
5379 fileLock = (cm_file_lock_t *) q;
5383 if (IS_LOCK_DELETED(fileLock)) {
5385 osi_QRemove(&cm_allFileLocks, q);
5386 cm_PutFileLock(fileLock);
5388 } else if (IS_LOCK_ACTIVE(fileLock) && !IS_LOCK_CLIENTONLY(fileLock)) {
5390 /* Server locks must have been enabled for us to have
5391 received an active non-client-only lock. */
5392 osi_assertx(cm_enableServerLocks, "!cm_enableServerLocks");
5394 scp = fileLock->scp;
5395 osi_assertx(scp != NULL, "null cm_scache_t");
5397 cm_HoldSCacheNoLock(scp);
5400 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5401 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5402 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5404 fileLock->fid.volume,
5405 fileLock->fid.vnode,
5406 fileLock->fid.unique);
5407 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5408 fileLock->scp->fid.cell,
5409 fileLock->scp->fid.volume,
5410 fileLock->scp->fid.vnode,
5411 fileLock->scp->fid.unique);
5412 osi_assertx(FALSE, "invalid fid");
5415 /* Server locks are extended once per scp per refresh
5417 if (scp->lastRefreshCycle != cm_lockRefreshCycle) {
5419 int scp_done = FALSE;
5421 osi_Log1(afsd_logp, "cm_CheckLocks Updating scp 0x%x", scp);
5423 lock_ReleaseWrite(&cm_scacheLock);
5424 lock_ObtainWrite(&scp->rw);
5426 /* did the lock change while we weren't holding the lock? */
5427 if (!IS_LOCK_ACTIVE(fileLock))
5428 goto post_syncopdone;
5430 code = cm_SyncOp(scp, NULL, fileLock->userp, &req, 0,
5431 CM_SCACHESYNC_NEEDCALLBACK
5432 | CM_SCACHESYNC_GETSTATUS
5433 | CM_SCACHESYNC_LOCK);
5437 "cm_CheckLocks SyncOp failure code 0x%x", code);
5438 goto post_syncopdone;
5441 /* cm_SyncOp releases scp->rw during which the lock
5442 may get released. */
5443 if (!IS_LOCK_ACTIVE(fileLock))
5444 goto pre_syncopdone;
5446 if (scp->serverLock != -1 && !(scp->flags & CM_SCACHEFLAG_DELETED)) {
5450 tfid.Volume = scp->fid.volume;
5451 tfid.Vnode = scp->fid.vnode;
5452 tfid.Unique = scp->fid.unique;
5454 userp = fileLock->userp;
5456 osi_Log3(afsd_logp, "CALL ExtendLock lock 0x%p for scp=0x%p with lock %d",
5459 (int) scp->serverLock);
5461 lock_ReleaseWrite(&scp->rw);
5464 code = cm_ConnFromFID(&cfid, userp,
5469 rxconnp = cm_GetRxConn(connp);
5470 code = RXAFS_ExtendLock(rxconnp, &tfid,
5472 rx_PutConnection(rxconnp);
5474 osi_Log1(afsd_logp, " ExtendLock returns %d", code);
5476 } while (cm_Analyze(connp, userp, &req,
5477 &cfid, &volSync, NULL, NULL,
5480 code = cm_MapRPCError(code, &req);
5482 lock_ObtainWrite(&scp->rw);
5485 osi_Log1(afsd_logp, "CALL ExtendLock FAILURE, code 0x%x", code);
5487 osi_Log0(afsd_logp, "CALL ExtendLock SUCCESS");
5488 scp->lockDataVersion = scp->dataVersion;
5491 if ((code == EINVAL || code == CM_ERROR_INVAL) &&
5492 scp->lockDataVersion == scp->dataVersion) {
5496 (scp->exclusiveLocks > 0) ? LockWrite: LockRead;
5498 /* we might still have a chance to obtain a
5501 code = cm_IntSetLock(scp, userp, lockType, &req);
5504 code = CM_ERROR_INVAL;
5505 } else if (scp->lockDataVersion != scp->dataVersion) {
5507 /* now check if we still have the file at
5508 the right data version. */
5510 "Data version mismatch on scp 0x%p",
5513 " Data versions: before=%I64d, after=%I64d",
5514 scp->lockDataVersion,
5517 code = cm_IntReleaseLock(scp, userp, &req);
5519 code = CM_ERROR_INVAL;
5523 if (code == EINVAL || code == CM_ERROR_INVAL ||
5524 code == CM_ERROR_BADFD) {
5525 cm_LockMarkSCacheLost(scp);
5529 /* interestingly, we have found an active lock
5530 belonging to an scache that has no
5532 cm_LockMarkSCacheLost(scp);
5539 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5542 lock_ReleaseWrite(&scp->rw);
5544 lock_ObtainWrite(&cm_scacheLock);
5547 fileLock->lastUpdate = time(NULL);
5551 scp->lastRefreshCycle = cm_lockRefreshCycle;
5554 /* we have already refreshed the locks on this scp */
5555 fileLock->lastUpdate = time(NULL);
5558 cm_ReleaseSCacheNoLock(scp);
5560 } else if (IS_LOCK_ACTIVE(fileLock) && IS_LOCK_CLIENTONLY(fileLock)) {
5561 /* TODO: Check callbacks */
5565 lock_ReleaseWrite(&cm_scacheLock);
5566 osi_Log1(afsd_logp, "cm_CheckLocks completes lock check cycle %d", cm_lockRefreshCycle);
5569 /* NOT called with scp->rw held. */
5570 long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
5573 cm_scache_t *scp = NULL;
5574 cm_file_lock_t *fileLock;
5578 int force_client_lock = FALSE;
5579 int has_insert = FALSE;
5580 int check_data_version = FALSE;
5584 if (client_is_dead) {
5585 code = CM_ERROR_TIMEDOUT;
5589 lock_ObtainRead(&cm_scacheLock);
5591 osi_Log2(afsd_logp, "cm_RetryLock checking lock %p (scp=%p)", oldFileLock, oldFileLock->scp);
5592 osi_Log4(afsd_logp, " offset(%x:%x) length(%x:%x)",
5593 (unsigned)(oldFileLock->range.offset >> 32),
5594 (unsigned)(oldFileLock->range.offset & 0xffffffff),
5595 (unsigned)(oldFileLock->range.length >> 32),
5596 (unsigned)(oldFileLock->range.length & 0xffffffff));
5597 osi_Log4(afsd_logp, " key<0x%x,0x%x,0x%x> flags=%x",
5598 oldFileLock->key.process_id, oldFileLock->key.session_id, oldFileLock->key.file_id,
5599 (unsigned)(oldFileLock->flags));
5601 /* if the lock has already been granted, then we have nothing to do */
5602 if (IS_LOCK_ACTIVE(oldFileLock)) {
5603 lock_ReleaseRead(&cm_scacheLock);
5604 osi_Log0(afsd_logp, "cm_RetryLock lock already granted");
5608 /* we can't do anything with lost or deleted locks at the moment. */
5609 if (IS_LOCK_LOST(oldFileLock) || IS_LOCK_DELETED(oldFileLock)) {
5610 code = CM_ERROR_BADFD;
5611 osi_Log0(afsd_logp, "cm_RetryLock lock is lost or deleted");
5612 lock_ReleaseRead(&cm_scacheLock);
5616 scp = oldFileLock->scp;
5618 osi_assertx(scp != NULL, "null cm_scache_t");
5620 lock_ReleaseRead(&cm_scacheLock);
5621 lock_ObtainWrite(&scp->rw);
5623 code = cm_LockCheckPerms(scp, oldFileLock->lockType,
5627 if (code == CM_ERROR_NOACCESS && oldFileLock->lockType == LockRead) {
5628 if (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp)) {
5629 force_client_lock = TRUE;
5633 lock_ReleaseWrite(&scp->rw);
5637 lock_ObtainWrite(&cm_scacheLock);
5639 /* Check if we already have a sufficient server lock to allow this
5640 lock to go through. */
5641 if (IS_LOCK_WAITLOCK(oldFileLock) &&
5642 (!SERVERLOCKS_ENABLED(scp) ||
5643 scp->serverLock == oldFileLock->lockType ||
5644 scp->serverLock == LockWrite)) {
5646 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5648 if (SERVERLOCKS_ENABLED(scp)) {
5649 osi_Log1(afsd_logp, "cm_RetryLock Server lock (%d) is sufficient for lock. Granting",
5650 (int) scp->serverLock);
5652 osi_Log0(afsd_logp, "cm_RetryLock skipping server lock for scp");
5655 lock_ReleaseWrite(&cm_scacheLock);
5656 lock_ReleaseWrite(&scp->rw);
5661 if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5663 /* check if the conflicting locks have dissappeared already */
5664 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5666 fileLock = (cm_file_lock_t *)
5667 ((char *) q - offsetof(cm_file_lock_t, fileq));
5669 if (IS_LOCK_LOST(fileLock)) {
5670 if (cm_KeyEquals(&fileLock->key, &oldFileLock->key, 0)) {
5671 code = CM_ERROR_BADFD;
5672 oldFileLock->flags |= CM_FILELOCK_FLAG_LOST;
5673 osi_Log1(afsd_logp, " found lost lock %p for same key. Marking lock as lost",
5676 } else if (fileLock->lockType == LockWrite &&
5677 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5678 osi_Log1(afsd_logp, " found conflicting LOST lock %p", fileLock);
5679 code = CM_ERROR_WOULDBLOCK;
5684 if (IS_LOCK_ACCEPTED(fileLock) &&
5685 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5687 if (oldFileLock->lockType != LockRead ||
5688 fileLock->lockType != LockRead) {
5690 osi_Log1(afsd_logp, " found conflicting lock %p", fileLock);
5691 code = CM_ERROR_WOULDBLOCK;
5699 lock_ReleaseWrite(&cm_scacheLock);
5700 lock_ReleaseWrite(&scp->rw);
5705 /* when we get here, the lock is either a WAITUNLOCK or WAITLOCK.
5706 If it is WAITUNLOCK, then we didn't find any conflicting lock
5707 but we haven't verfied whether the serverLock is sufficient to
5708 assert it. If it is WAITLOCK, then the serverLock is
5709 insufficient to assert it. Eitherway, we are ready to accept
5710 the lock as either ACTIVE or WAITLOCK depending on the
5713 /* First, promote the WAITUNLOCK to a WAITLOCK */
5714 if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5715 if (oldFileLock->lockType == LockRead)
5718 scp->exclusiveLocks++;
5720 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITUNLOCK;
5721 oldFileLock->flags |= CM_FILELOCK_FLAG_WAITLOCK;
5724 osi_assertx(IS_LOCK_WAITLOCK(oldFileLock), "!IS_LOCK_WAITLOCK");
5726 if (force_client_lock ||
5727 !SERVERLOCKS_ENABLED(scp) ||
5728 scp->serverLock == oldFileLock->lockType ||
5729 (oldFileLock->lockType == LockRead &&
5730 scp->serverLock == LockWrite)) {
5732 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5734 if ((force_client_lock ||
5735 !SERVERLOCKS_ENABLED(scp)) &&
5736 !IS_LOCK_CLIENTONLY(oldFileLock)) {
5738 oldFileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
5740 if (oldFileLock->lockType == LockRead)
5743 scp->exclusiveLocks--;
5748 lock_ReleaseWrite(&cm_scacheLock);
5749 lock_ReleaseWrite(&scp->rw);
5756 code = cm_SyncOp(scp, NULL, oldFileLock->userp, &req, 0,
5757 CM_SCACHESYNC_NEEDCALLBACK
5758 | CM_SCACHESYNC_GETSTATUS
5759 | CM_SCACHESYNC_LOCK);
5761 osi_Log1(afsd_logp, "cm_RetryLock SyncOp failure code 0x%x", code);
5762 lock_ReleaseWrite(&cm_scacheLock);
5763 goto post_syncopdone;
5766 if (!IS_LOCK_WAITLOCK(oldFileLock))
5767 goto pre_syncopdone;
5769 userp = oldFileLock->userp;
5771 #ifndef AGGRESSIVE_LOCKS
5772 newLock = oldFileLock->lockType;
5774 newLock = LockWrite;
5778 /* if has_insert is non-zero, then:
5779 - the lock a LockRead
5780 - we don't have permission to get a LockRead
5781 - we do have permission to get a LockWrite
5782 - the server supports VICED_CAPABILITY_WRITELOCKACL
5785 newLock = LockWrite;
5788 lock_ReleaseWrite(&cm_scacheLock);
5790 /* when we get here, either we have a read-lock and want a
5791 write-lock or we don't have any locks and we want some
5794 if (scp->serverLock == LockRead) {
5796 osi_assertx(newLock == LockWrite, "!LockWrite");
5798 osi_Log0(afsd_logp, " Attempting to UPGRADE from LockRead to LockWrite");
5800 scp->lockDataVersion = scp->dataVersion;
5801 check_data_version = TRUE;
5803 code = cm_IntReleaseLock(scp, userp, &req);
5806 goto pre_syncopdone;
5808 scp->serverLock = -1;
5811 code = cm_IntSetLock(scp, userp, newLock, &req);
5814 if (scp->dataVersion != scp->lockDataVersion) {
5815 /* we lost a race. too bad */
5818 " Data version mismatch while upgrading lock.");
5820 " Data versions before=%I64d, after=%I64d",
5821 scp->lockDataVersion,
5824 " Releasing stale lock for scp 0x%x", scp);
5826 code = cm_IntReleaseLock(scp, userp, &req);
5828 scp->serverLock = -1;
5830 code = CM_ERROR_INVAL;
5832 cm_LockMarkSCacheLost(scp);
5834 scp->serverLock = newLock;
5839 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5845 if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
5846 lock_ObtainWrite(&cm_scacheLock);
5847 if (scp->fileLocksT == &oldFileLock->fileq)
5848 scp->fileLocksT = osi_QPrev(&oldFileLock->fileq);
5849 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, &oldFileLock->fileq);
5850 lock_ReleaseWrite(&cm_scacheLock);
5852 lock_ReleaseWrite(&scp->rw);
5855 lock_ObtainWrite(&cm_scacheLock);
5857 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5858 } else if (code != CM_ERROR_WOULDBLOCK) {
5859 oldFileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5860 cm_ReleaseUser(oldFileLock->userp);
5861 oldFileLock->userp = NULL;
5862 if (oldFileLock->scp) {
5863 cm_ReleaseSCacheNoLock(oldFileLock->scp);
5864 oldFileLock->scp = NULL;
5867 lock_ReleaseWrite(&cm_scacheLock);
5872 cm_key_t cm_GenerateKey(afs_uint16 session_id, afs_offs_t process_id, afs_uint16 file_id)
5876 key.process_id = process_id;
5877 key.session_id = session_id;
5878 key.file_id = file_id;
5883 int cm_KeyEquals(cm_key_t *k1, cm_key_t *k2, int flags)
5885 return (k1->session_id == k2->session_id) && (k1->file_id == k2->file_id) &&
5886 ((flags & CM_UNLOCK_BY_FID) || (k1->process_id == k2->process_id));
5889 void cm_ReleaseAllLocks(void)
5895 cm_file_lock_t *fileLock;
5898 for (i = 0; i < cm_data.scacheHashTableSize; i++)
5900 for ( scp = cm_data.scacheHashTablep[i]; scp; scp = scp->nextp ) {
5901 while (scp->fileLocksH != NULL) {
5902 lock_ObtainWrite(&scp->rw);
5903 lock_ObtainWrite(&cm_scacheLock);
5904 if (!scp->fileLocksH) {
5905 lock_ReleaseWrite(&cm_scacheLock);
5906 lock_ReleaseWrite(&scp->rw);
5909 fileLock = (cm_file_lock_t *)((char *) scp->fileLocksH - offsetof(cm_file_lock_t, fileq));
5910 userp = fileLock->userp;
5912 key = fileLock->key;
5913 cm_HoldSCacheNoLock(scp);
5914 lock_ReleaseWrite(&cm_scacheLock);
5915 cm_UnlockByKey(scp, key, 0, userp, &req);
5916 cm_ReleaseSCache(scp);
5917 cm_ReleaseUser(userp);
5918 lock_ReleaseWrite(&scp->rw);