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)
839 if (scp->mountPointStringp[0])
842 #ifdef AFS_FREELANCE_CLIENT
843 /* File servers do not have data for freelance entries */
844 if (cm_freelanceEnabled &&
845 scp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
846 scp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
848 code = cm_FreelanceFetchMountPointString(scp);
850 #endif /* AFS_FREELANCE_CLIENT */
852 /* otherwise, we have to read it in */
853 thyper.LowPart = thyper.HighPart = 0;
854 code = cm_GetData(scp, &thyper, scp->mountPointStringp, MOUNTPOINTLEN, userp, reqp);
858 scp->mountPointStringp[MOUNTPOINTLEN-1] = 0; /* nul terminate */
860 if (scp->length.LowPart > MOUNTPOINTLEN - 1)
861 return CM_ERROR_TOOBIG;
862 if (scp->length.LowPart == 0)
863 return CM_ERROR_INVAL;
865 /* convert the terminating dot to a NUL */
866 scp->mountPointStringp[scp->length.LowPart - 1] = 0;
873 /* called with a locked scp and chases the mount point, yielding outScpp.
874 * scp remains write locked, just for simplicity of describing the interface.
876 long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
877 cm_req_t *reqp, cm_scache_t **outScpp)
879 fschar_t *cellNamep = NULL;
880 fschar_t *volNamep = NULL;
884 cm_volume_t *volp = NULL;
893 if (scp->mountRootFid.cell != 0 && scp->mountRootGen >= cm_data.mountRootGen) {
894 tfid = scp->mountRootFid;
895 lock_ReleaseWrite(&scp->rw);
896 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
897 lock_ObtainWrite(&scp->rw);
901 /* parse the volume name */
902 mpNamep = scp->mountPointStringp;
904 return CM_ERROR_NOSUCHPATH;
905 mtType = *scp->mountPointStringp;
907 cp = cm_FsStrChr(mpNamep, _FS(':'));
909 /* cellular mount point */
910 cellNamep = (fschar_t *)malloc((cp - mpNamep) * sizeof(fschar_t));
911 cm_FsStrCpyN(cellNamep, cp - mpNamep, mpNamep + 1, cp - mpNamep - 1);
912 volNamep = cm_FsStrDup(cp+1);
914 /* now look up the cell */
915 lock_ReleaseWrite(&scp->rw);
916 cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE);
917 lock_ObtainWrite(&scp->rw);
920 volNamep = cm_FsStrDup(mpNamep + 1);
922 #ifdef AFS_FREELANCE_CLIENT
924 * Mount points in the Freelance cell should default
925 * to the workstation cell.
927 if (cm_freelanceEnabled &&
928 scp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
929 scp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
931 fschar_t rootCellName[256]="";
932 cm_GetRootCellName(rootCellName);
933 cellp = cm_GetCell(rootCellName, 0);
935 #endif /* AFS_FREELANCE_CLIENT */
936 cellp = cm_FindCellByID(scp->fid.cell, 0);
940 code = CM_ERROR_NOSUCHCELL;
944 vnLength = cm_FsStrLen(volNamep);
945 if (vnLength >= 8 && cm_FsStrCmp(volNamep + vnLength - 7, ".backup") == 0)
946 targetType = BACKVOL;
947 else if (vnLength >= 10
948 && cm_FsStrCmp(volNamep + vnLength - 9, ".readonly") == 0)
953 /* check for backups within backups */
954 if (targetType == BACKVOL
955 && (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
956 == CM_SCACHEFLAG_RO) {
957 code = CM_ERROR_NOSUCHVOLUME;
961 /* now we need to get the volume */
962 lock_ReleaseWrite(&scp->rw);
963 if (cm_VolNameIsID(volNamep)) {
964 code = cm_FindVolumeByID(cellp, atoi(volNamep), userp, reqp,
965 CM_GETVOL_FLAG_CREATE, &volp);
967 code = cm_FindVolumeByName(cellp, volNamep, userp, reqp,
968 CM_GETVOL_FLAG_CREATE, &volp);
970 lock_ObtainWrite(&scp->rw);
973 afs_uint32 cell, volume;
974 cm_vol_state_t *statep;
976 cell = cellp->cellID;
978 /* if the mt pt originates in a .backup volume (not a .readonly)
979 * and FollowBackupPath is active, and if there is a .backup
980 * volume for the target, then use the .backup of the target
981 * instead of the read-write.
983 if (cm_followBackupPath &&
984 volp->vol[BACKVOL].ID != 0 &&
985 (dscp->flags & (CM_SCACHEFLAG_RO|CM_SCACHEFLAG_PURERO)) == CM_SCACHEFLAG_RO &&
986 (targetType == RWVOL || targetType == ROVOL && volp->vol[ROVOL].ID == 0)
988 targetType = BACKVOL;
990 /* if the mt pt is in a read-only volume (not just a
991 * backup), and if there is a read-only volume for the
992 * target, and if this is a targetType '#' mount point, use
993 * the read-only, otherwise use the one specified.
995 else if (mtType == '#' && targetType == RWVOL &&
996 (scp->flags & CM_SCACHEFLAG_PURERO) &&
997 volp->vol[ROVOL].ID != 0) {
1001 lock_ObtainWrite(&volp->rw);
1002 statep = cm_VolumeStateByType(volp, targetType);
1003 volume = statep->ID;
1004 statep->dotdotFid = dscp->fid;
1005 lock_ReleaseWrite(&volp->rw);
1007 /* the rest of the fid is a magic number */
1008 cm_SetFid(&scp->mountRootFid, cell, volume, 1, 1);
1009 scp->mountRootGen = cm_data.mountRootGen;
1011 tfid = scp->mountRootFid;
1012 lock_ReleaseWrite(&scp->rw);
1013 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
1014 lock_ObtainWrite(&scp->rw);
1027 long cm_LookupInternal(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_user_t *userp,
1028 cm_req_t *reqp, cm_scache_t **outScpp)
1031 int dnlcHit = 1; /* did we hit in the dnlc? yes, we did */
1032 cm_scache_t *tscp = NULL;
1033 cm_scache_t *mountedScp;
1034 cm_lookupSearch_t rock;
1036 normchar_t *nnamep = NULL;
1037 fschar_t *fnamep = NULL;
1042 memset(&rock, 0, sizeof(rock));
1044 if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
1045 && cm_ClientStrCmp(cnamep, _C("..")) == 0) {
1046 if (dscp->dotdotFid.volume == 0)
1047 return CM_ERROR_NOSUCHVOLUME;
1048 rock.fid = dscp->dotdotFid;
1050 } else if (cm_ClientStrCmp(cnamep, _C(".")) == 0) {
1051 rock.fid = dscp->fid;
1055 nnamep = cm_ClientStringToNormStringAlloc(cnamep, -1, NULL);
1057 code = CM_ERROR_NOSUCHFILE;
1060 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
1062 code = CM_ERROR_NOSUCHFILE;
1067 if (flags & CM_FLAG_NOMOUNTCHASE) {
1068 /* In this case, we should go and call cm_Dir* functions
1069 directly since the following cm_ApplyDir() function will
1077 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ,
1078 CM_DIROP_FLAG_NONE, &dirop);
1081 code = cm_BPlusDirLookup(&dirop, nnamep, &rock.fid);
1086 code = cm_DirLookup(&dirop, fnamep, &rock.fid);
1088 cm_EndDirOp(&dirop);
1098 if (code == CM_ERROR_INEXACT_MATCH && (flags & CM_FLAG_CASEFOLD)) {
1105 code = CM_ERROR_BPLUS_NOMATCH;
1111 rock.fid.cell = dscp->fid.cell;
1112 rock.fid.volume = dscp->fid.volume;
1113 rock.searchNamep = fnamep;
1114 rock.nsearchNamep = nnamep;
1115 rock.caseFold = (flags & CM_FLAG_CASEFOLD);
1116 rock.hasTilde = ((cm_ClientStrChr(cnamep, '~') != NULL) ? 1 : 0);
1118 /* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
1119 code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
1120 (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
1122 /* code == 0 means we fell off the end of the dir, while stopnow means
1123 * that we stopped early, probably because we found the entry we're
1124 * looking for. Any other non-zero code is an error.
1126 if (code && code != CM_ERROR_STOPNOW && code != CM_ERROR_BPLUS_NOMATCH) {
1127 /* if the cm_scache_t we are searching in is not a directory
1128 * we must return path not found because the error
1129 * is to describe the final component not an intermediary
1131 if (code == CM_ERROR_NOTDIR) {
1132 if (flags & CM_FLAG_CHECKPATH)
1133 code = CM_ERROR_NOSUCHPATH;
1135 code = CM_ERROR_NOSUCHFILE;
1141 getroot = (dscp==cm_data.rootSCachep) ;
1143 if (!cm_freelanceEnabled || !getroot) {
1144 if (flags & CM_FLAG_CHECKPATH)
1145 code = CM_ERROR_NOSUCHPATH;
1147 code = CM_ERROR_NOSUCHFILE;
1150 else if (!cm_ClientStrChr(cnamep, '#') &&
1151 !cm_ClientStrChr(cnamep, '%') &&
1152 cm_ClientStrCmpI(cnamep, _C("srvsvc")) &&
1153 cm_ClientStrCmpI(cnamep, _C("wkssvc")) &&
1154 cm_ClientStrCmpI(cnamep, _C("ipc$")))
1156 /* nonexistent dir on freelance root, so add it */
1157 fschar_t fullname[CELL_MAXNAMELEN + 1] = "."; /* +1 so that when we skip the . the size is still CELL_MAXNAMELEN */
1161 osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %S",
1162 osi_LogSaveClientString(afsd_logp,cnamep));
1165 * There is an ugly behavior where a share name "foo" will be searched
1166 * for as "fo". If the searched for name differs by an already existing
1167 * symlink or mount point in the Freelance directory, do not add the
1168 * new value automatically.
1172 fnlen = strlen(fnamep);
1173 if ( fnamep[fnlen-1] == '.') {
1174 fnamep[fnlen-1] = '\0';
1179 if (cnamep[0] == '.') {
1180 if (cm_GetCell_Gen(&fnamep[1], &fullname[1], CM_FLAG_CREATE)) {
1182 code = cm_FreelanceAddMount(fullname, &fullname[1], "root.cell", 1, &rock.fid);
1183 if ( cm_FsStrCmpI(&fnamep[1], &fullname[1])) {
1185 * Do not permit symlinks that are one of:
1186 * . the cellname followed by a dot
1187 * . the cellname minus a single character
1188 * . a substring of the cellname that does not consist of full components
1190 if ( cm_strnicmp_utf8(&fnamep[1], fullname, (int)fnlen-1) == 0 &&
1191 (fnlen-1 == strlen(fullname)-1 || fullname[fnlen-1] != '.'))
1193 /* do not add; substitute fullname for the search */
1195 fnamep = malloc(strlen(fullname)+2);
1197 strncpy(&fnamep[1], fullname, strlen(fullname)+1);
1200 code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
1205 if (cm_GetCell_Gen(fnamep, fullname, CM_FLAG_CREATE)) {
1207 code = cm_FreelanceAddMount(fullname, fullname, "root.cell", 0, &rock.fid);
1208 if ( cm_FsStrCmpI(fnamep, fullname)) {
1210 * Do not permit symlinks that are one of:
1211 * . the cellname followed by a dot
1212 * . the cellname minus a single character
1213 * . a substring of the cellname that does not consist of full components
1215 if ( cm_strnicmp_utf8(fnamep, fullname, (int)fnlen-1) == 0 &&
1216 (fnlen == strlen(fullname)-1 || fullname[fnlen] != '.'))
1218 /* do not add; substitute fullname for the search */
1220 fnamep = strdup(fullname);
1224 code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
1233 nnamep = cm_FsStringToNormStringAlloc(fnamep, -1, NULL);
1237 if (!found || code) { /* add mount point failed, so give up */
1238 if (flags & CM_FLAG_CHECKPATH)
1239 code = CM_ERROR_NOSUCHPATH;
1241 code = CM_ERROR_NOSUCHFILE;
1244 tscp = NULL; /* to force call of cm_GetSCache */
1246 if (flags & CM_FLAG_CHECKPATH)
1247 code = CM_ERROR_NOSUCHPATH;
1249 code = CM_ERROR_NOSUCHFILE;
1255 if ( !tscp ) /* we did not find it in the dnlc */
1258 code = cm_GetSCache(&rock.fid, &tscp, userp, reqp);
1262 /* tscp is now held */
1264 lock_ObtainWrite(&tscp->rw);
1267 * Do not get status if we do not already have a callback.
1268 * The process of reading the mount point string will obtain status information
1269 * in a single RPC. No reason to add a second round trip.
1271 * If we do have a callback, use cm_SyncOp to get status in case the
1272 * current cm_user_t is not the same as the one that obtained the
1273 * mount point string contents.
1275 if (cm_HaveCallback(tscp)) {
1276 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1277 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1279 lock_ReleaseWrite(&tscp->rw);
1280 cm_ReleaseSCache(tscp);
1283 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1285 /* tscp is now locked */
1287 if (!(flags & CM_FLAG_NOMOUNTCHASE)
1288 && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
1289 /* mount points are funny: they have a volume name to mount
1292 code = cm_ReadMountPoint(tscp, userp, reqp);
1294 code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
1296 lock_ReleaseWrite(&tscp->rw);
1297 cm_ReleaseSCache(tscp);
1304 lock_ReleaseWrite(&tscp->rw);
1307 /* copy back pointer */
1310 /* insert scache in dnlc */
1311 if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
1312 /* lock the directory entry to prevent racing callback revokes */
1313 lock_ObtainRead(&dscp->rw);
1314 if ( dscp->cbServerp != NULL && dscp->cbExpires > 0 ) {
1315 /* TODO: reuse nnamep from above */
1318 nnamep = cm_ClientStringToNormStringAlloc(cnamep, -1, NULL);
1320 cm_dnlcEnter(dscp, nnamep, tscp);
1322 lock_ReleaseRead(&dscp->rw);
1339 int cm_ExpandSysName(clientchar_t *inp, clientchar_t *outp, long outSizeCch, unsigned int index)
1344 tp = cm_ClientStrRChr(inp, '@');
1346 return 0; /* no @sys */
1348 if (cm_ClientStrCmp(tp, _C("@sys")) != 0)
1349 return 0; /* no @sys */
1351 /* caller just wants to know if this is a valid @sys type of name */
1355 if (index >= cm_sysNameCount)
1358 /* otherwise generate the properly expanded @sys name */
1359 prefixCount = (int)(tp - inp);
1361 cm_ClientStrCpyN(outp, outSizeCch, inp, prefixCount); /* copy out "a." from "a.@sys" */
1362 outp[prefixCount] = 0; /* null terminate the "a." */
1363 cm_ClientStrCat(outp, outSizeCch, cm_sysNameList[index]);/* append i386_nt40 */
1367 long cm_EvaluateVolumeReference(clientchar_t * namep, long flags, cm_user_t * userp,
1368 cm_req_t *reqp, cm_scache_t ** outScpp)
1370 afs_uint32 code = 0;
1371 fschar_t cellName[CELL_MAXNAMELEN];
1372 fschar_t volumeName[VL_MAXNAMELEN];
1376 fschar_t * fnamep = NULL;
1378 cm_cell_t * cellp = NULL;
1379 cm_volume_t * volp = NULL;
1383 int mountType = RWVOL;
1385 osi_Log1(afsd_logp, "cm_EvaluateVolumeReference for string [%S]",
1386 osi_LogSaveClientString(afsd_logp, namep));
1388 if (cm_ClientStrCmpNI(namep, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH) != 0) {
1389 goto _exit_invalid_path;
1392 /* namep is assumed to look like the following:
1394 @vol:<cellname>%<volume>\0
1396 @vol:<cellname>#<volume>\0
1400 fnamep = cm_ClientStringToFsStringAlloc(namep, -1, NULL);
1401 cp = fnamep + CM_PREFIX_VOL_CCH; /* cp points to cell name, hopefully */
1402 tp = cm_FsStrChr(cp, '%');
1404 tp = cm_FsStrChr(cp, '#');
1406 (len = tp - cp) == 0 ||
1407 len > CELL_MAXNAMELEN)
1408 goto _exit_invalid_path;
1409 cm_FsStrCpyN(cellName, lengthof(cellName), cp, len);
1414 cp = tp+1; /* cp now points to volume, supposedly */
1415 cm_FsStrCpy(volumeName, lengthof(volumeName), cp);
1417 /* OK, now we have the cell and the volume */
1418 osi_Log2(afsd_logp, " Found cell [%s] and volume [%s]",
1419 osi_LogSaveFsString(afsd_logp, cellName),
1420 osi_LogSaveFsString(afsd_logp, volumeName));
1422 cellp = cm_GetCell(cellName, CM_FLAG_CREATE);
1423 if (cellp == NULL) {
1424 goto _exit_invalid_path;
1427 len = cm_FsStrLen(volumeName);
1428 if (len >= 8 && cm_FsStrCmp(volumeName + len - 7, ".backup") == 0)
1430 else if (len >= 10 &&
1431 cm_FsStrCmp(volumeName + len - 9, ".readonly") == 0)
1436 if (cm_VolNameIsID(volumeName)) {
1437 code = cm_FindVolumeByID(cellp, atoi(volumeName), userp, reqp,
1438 CM_GETVOL_FLAG_CREATE, &volp);
1440 code = cm_FindVolumeByName(cellp, volumeName, userp, reqp,
1441 CM_GETVOL_FLAG_CREATE, &volp);
1447 if (volType == BACKVOL)
1448 volume = volp->vol[BACKVOL].ID;
1449 else if (volType == ROVOL ||
1450 (volType == RWVOL && mountType == ROVOL && volp->vol[ROVOL].ID != 0))
1451 volume = volp->vol[ROVOL].ID;
1453 volume = volp->vol[RWVOL].ID;
1455 cm_SetFid(&fid, cellp->cellID, volume, 1, 1);
1457 code = cm_GetSCache(&fid, outScpp, userp, reqp);
1470 if (flags & CM_FLAG_CHECKPATH)
1471 return CM_ERROR_NOSUCHPATH;
1473 return CM_ERROR_NOSUCHFILE;
1476 #ifdef DEBUG_REFCOUNT
1477 long cm_LookupDbg(cm_scache_t *dscp, clientchar_t *namep, long flags, cm_user_t *userp,
1478 cm_req_t *reqp, cm_scache_t **outScpp, char * file, long line)
1480 long cm_Lookup(cm_scache_t *dscp, clientchar_t *namep, long flags, cm_user_t *userp,
1481 cm_req_t *reqp, cm_scache_t **outScpp)
1485 clientchar_t tname[AFSPATHMAX];
1486 int sysNameIndex = 0;
1487 cm_scache_t *scp = NULL;
1489 #ifdef DEBUG_REFCOUNT
1490 afsi_log("%s:%d cm_Lookup dscp 0x%p ref %d", file, line, dscp, dscp->refCount, file, line);
1491 osi_Log2(afsd_logp, "cm_Lookup dscp 0x%p ref %d", dscp, dscp->refCount);
1494 if ( cm_ClientStrCmpI(namep,_C(SMB_IOCTL_FILENAME_NOSLASH)) == 0 ) {
1495 if (flags & CM_FLAG_CHECKPATH)
1496 return CM_ERROR_NOSUCHPATH;
1498 return CM_ERROR_NOSUCHFILE;
1501 if (dscp == cm_data.rootSCachep &&
1502 cm_ClientStrCmpNI(namep, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH) == 0) {
1503 return cm_EvaluateVolumeReference(namep, flags, userp, reqp, outScpp);
1506 if (cm_ExpandSysName(namep, NULL, 0, 0) > 0) {
1507 for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
1508 code = cm_ExpandSysName(namep, tname, lengthof(tname), sysNameIndex);
1510 code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
1511 #ifdef DEBUG_REFCOUNT
1512 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);
1513 osi_Log3(afsd_logp, "cm_LookupInternal (1) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1521 cm_ReleaseSCache(scp);
1525 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1526 #ifdef DEBUG_REFCOUNT
1527 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);
1528 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1535 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1536 #ifdef DEBUG_REFCOUNT
1537 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);
1538 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1544 /* None of the possible sysName expansions could be found */
1545 if (flags & CM_FLAG_CHECKPATH)
1546 return CM_ERROR_NOSUCHPATH;
1548 return CM_ERROR_NOSUCHFILE;
1551 /*! \brief Unlink a file name
1553 Encapsulates a call to RXAFS_RemoveFile().
1555 \param[in] dscp cm_scache_t pointing at the directory containing the
1556 name to be unlinked.
1558 \param[in] fnamep Original name to be unlinked. This is the
1559 name that will be passed into the RXAFS_RemoveFile() call.
1560 This parameter is optional. If not provided, the value will
1563 \param[in] came Client name to be unlinked. This name will be used
1564 to update the local directory caches.
1566 \param[in] userp cm_user_t for the request.
1568 \param[in] reqp Request tracker.
1571 long cm_Unlink(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t * cnamep,
1572 cm_user_t *userp, cm_req_t *reqp)
1578 AFSFetchStatus newDirStatus;
1580 struct rx_connection * rxconnp;
1582 cm_scache_t *scp = NULL;
1583 int free_fnamep = FALSE;
1585 memset(&volSync, 0, sizeof(volSync));
1587 if (fnamep == NULL) {
1590 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ,
1591 CM_DIROP_FLAG_NONE, &dirop);
1593 code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
1596 cm_EndDirOp(&dirop);
1603 #ifdef AFS_FREELANCE_CLIENT
1604 if (cm_freelanceEnabled && dscp == cm_data.rootSCachep) {
1605 /* deleting a mount point from the root dir. */
1606 code = cm_FreelanceRemoveMount(fnamep);
1611 code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
1615 /* Check for RO volume */
1616 if (dscp->flags & CM_SCACHEFLAG_RO) {
1617 code = CM_ERROR_READONLY;
1621 /* make sure we don't screw up the dir status during the merge */
1622 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE,
1623 CM_DIROP_FLAG_NONE, &dirop);
1625 lock_ObtainWrite(&dscp->rw);
1626 sflags = CM_SCACHESYNC_STOREDATA;
1627 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1628 lock_ReleaseWrite(&dscp->rw);
1630 cm_EndDirOp(&dirop);
1635 afsFid.Volume = dscp->fid.volume;
1636 afsFid.Vnode = dscp->fid.vnode;
1637 afsFid.Unique = dscp->fid.unique;
1639 osi_Log1(afsd_logp, "CALL RemoveFile scp 0x%p", dscp);
1641 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
1645 rxconnp = cm_GetRxConn(connp);
1646 code = RXAFS_RemoveFile(rxconnp, &afsFid, fnamep,
1647 &newDirStatus, &volSync);
1648 rx_PutConnection(rxconnp);
1650 } while (cm_Analyze(connp, userp, reqp, &dscp->fid, &volSync, NULL, NULL, code));
1651 code = cm_MapRPCError(code, reqp);
1654 osi_Log1(afsd_logp, "CALL RemoveFile FAILURE, code 0x%x", code);
1656 osi_Log0(afsd_logp, "CALL RemoveFile SUCCESS");
1659 lock_ObtainWrite(&dirop.scp->dirlock);
1660 dirop.lockType = CM_DIRLOCK_WRITE;
1662 lock_ObtainWrite(&dscp->rw);
1663 cm_dnlcRemove(dscp, cnamep);
1664 cm_SyncOpDone(dscp, NULL, sflags);
1666 cm_MergeStatus(NULL, dscp, &newDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
1667 } else if (code == CM_ERROR_NOSUCHFILE) {
1668 /* windows would not have allowed the request to delete the file
1669 * if it did not believe the file existed. therefore, we must
1670 * have an inconsistent view of the world.
1672 dscp->cbServerp = NULL;
1674 lock_ReleaseWrite(&dscp->rw);
1676 if (code == 0 && cm_CheckDirOpForSingleChange(&dirop) && cnamep) {
1677 cm_DirDeleteEntry(&dirop, fnamep);
1679 cm_BPlusDirDeleteEntry(&dirop, cnamep);
1682 cm_EndDirOp(&dirop);
1685 cm_ReleaseSCache(scp);
1687 lock_ObtainWrite(&scp->rw);
1688 if (--scp->linkCount == 0)
1689 scp->flags |= CM_SCACHEFLAG_DELETED;
1690 cm_DiscardSCache(scp);
1691 lock_ReleaseWrite(&scp->rw);
1702 /* called with a write locked vnode, and fills in the link info.
1703 * returns this the vnode still write locked.
1705 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1710 lock_AssertWrite(&linkScp->rw);
1711 if (!linkScp->mountPointStringp[0]) {
1713 #ifdef AFS_FREELANCE_CLIENT
1714 /* File servers do not have data for freelance entries */
1715 if (cm_freelanceEnabled &&
1716 linkScp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
1717 linkScp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
1719 code = cm_FreelanceFetchMountPointString(linkScp);
1721 #endif /* AFS_FREELANCE_CLIENT */
1723 /* read the link data from the file server */
1724 thyper.LowPart = thyper.HighPart = 0;
1725 code = cm_GetData(linkScp, &thyper, linkScp->mountPointStringp, MOUNTPOINTLEN, userp, reqp);
1729 linkScp->mountPointStringp[MOUNTPOINTLEN-1] = 0; /* null terminate */
1731 if (linkScp->length.LowPart > MOUNTPOINTLEN - 1)
1732 return CM_ERROR_TOOBIG;
1733 if (linkScp->length.LowPart == 0)
1734 return CM_ERROR_INVAL;
1737 if ( !strnicmp(linkScp->mountPointStringp, "msdfs:", strlen("msdfs:")) )
1738 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1740 } /* don't have symlink contents cached */
1745 /* called with a held vnode and a path suffix, with the held vnode being a
1746 * symbolic link. Our goal is to generate a new path to interpret, and return
1747 * this new path in newSpaceBufferp. If the new vnode is relative to a dir
1748 * other than the directory containing the symbolic link, then the new root is
1749 * returned in *newRootScpp, otherwise a null is returned there.
1751 long cm_AssembleLink(cm_scache_t *linkScp, fschar_t *pathSuffixp,
1752 cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1753 cm_user_t *userp, cm_req_t *reqp)
1760 *newRootScpp = NULL;
1761 *newSpaceBufferp = NULL;
1763 lock_ObtainWrite(&linkScp->rw);
1765 * Do not get status if we do not already have a callback.
1766 * The process of reading the symlink string will obtain status information
1767 * in a single RPC. No reason to add a second round trip.
1769 * If we do have a callback, use cm_SyncOp to get status in case the
1770 * current cm_user_t is not the same as the one that obtained the
1771 * symlink string contents.
1773 if (cm_HaveCallback(linkScp)) {
1774 code = cm_SyncOp(linkScp, NULL, userp, reqp, 0,
1775 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1777 lock_ReleaseWrite(&linkScp->rw);
1778 cm_ReleaseSCache(linkScp);
1781 cm_SyncOpDone(linkScp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1783 code = cm_HandleLink(linkScp, userp, reqp);
1787 /* if we may overflow the buffer, bail out; buffer is signficantly
1788 * bigger than max path length, so we don't really have to worry about
1789 * being a little conservative here.
1791 if (cm_FsStrLen(linkScp->mountPointStringp) + cm_FsStrLen(pathSuffixp) + 2
1792 >= CM_UTILS_SPACESIZE) {
1793 code = CM_ERROR_TOOBIG;
1797 tsp = cm_GetSpace();
1798 linkp = linkScp->mountPointStringp;
1799 if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1800 if (strlen(linkp) > cm_mountRootLen)
1801 StringCbCopyA((char *) tsp->data, sizeof(tsp->data), linkp+cm_mountRootLen+1);
1804 *newRootScpp = cm_RootSCachep(userp, reqp);
1805 cm_HoldSCache(*newRootScpp);
1806 } else if (linkp[0] == '\\' && linkp[1] == '\\') {
1807 if (!strnicmp(&linkp[2], cm_NetbiosName, (len = (long)strlen(cm_NetbiosName))))
1809 char * p = &linkp[len + 3];
1810 if (strnicmp(p, "all", 3) == 0)
1813 StringCbCopyA(tsp->data, sizeof(tsp->data), p);
1814 for (p = tsp->data; *p; p++) {
1818 *newRootScpp = cm_RootSCachep(userp, reqp);
1819 cm_HoldSCache(*newRootScpp);
1821 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1822 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1823 code = CM_ERROR_PATH_NOT_COVERED;
1825 } else if ( linkScp->fileType == CM_SCACHETYPE_DFSLINK ||
1826 !strnicmp(linkp, "msdfs:", (len = (long)strlen("msdfs:"))) ) {
1827 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1828 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1829 code = CM_ERROR_PATH_NOT_COVERED;
1830 } else if (*linkp == '\\' || *linkp == '/') {
1832 /* formerly, this was considered to be from the AFS root,
1833 * but this seems to create problems. instead, we will just
1834 * reject the link */
1835 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp+1);
1836 *newRootScpp = cm_RootSCachep(userp, reqp);
1837 cm_HoldSCache(*newRootScpp);
1839 /* we still copy the link data into the response so that
1840 * the user can see what the link points to
1842 linkScp->fileType = CM_SCACHETYPE_INVALID;
1843 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1844 code = CM_ERROR_NOSUCHPATH;
1847 /* a relative link */
1848 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1850 if (pathSuffixp[0] != 0) { /* if suffix string is non-null */
1851 StringCchCatA(tsp->data,lengthof(tsp->data), "\\");
1852 StringCchCatA(tsp->data,lengthof(tsp->data), pathSuffixp);
1856 clientchar_t * cpath = cm_FsStringToClientStringAlloc(tsp->data, -1, NULL);
1857 if (cpath != NULL) {
1858 cm_ClientStrCpy(tsp->wdata, lengthof(tsp->wdata), cpath);
1860 *newSpaceBufferp = tsp;
1862 code = CM_ERROR_NOSUCHPATH;
1869 if (code == CM_ERROR_PATH_NOT_COVERED && reqp->tidPathp && reqp->relPathp) {
1870 cm_VolStatus_Notify_DFS_Mapping(linkScp, reqp->tidPathp, reqp->relPathp);
1875 lock_ReleaseWrite(&linkScp->rw);
1878 #ifdef DEBUG_REFCOUNT
1879 long cm_NameIDbg(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
1880 cm_user_t *userp, clientchar_t *tidPathp, cm_req_t *reqp,
1881 cm_scache_t **outScpp,
1882 char * file, long line)
1884 long cm_NameI(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
1885 cm_user_t *userp, clientchar_t *tidPathp,
1886 cm_req_t *reqp, cm_scache_t **outScpp)
1890 clientchar_t *tp; /* ptr moving through input buffer */
1891 clientchar_t tc; /* temp char */
1892 int haveComponent; /* has new component started? */
1893 clientchar_t component[AFSPATHMAX]; /* this is the new component */
1894 clientchar_t *cp; /* component name being assembled */
1895 cm_scache_t *tscp; /* current location in the hierarchy */
1896 cm_scache_t *nscp; /* next dude down */
1897 cm_scache_t *dirScp; /* last dir we searched */
1898 cm_scache_t *linkScp; /* new root for the symlink we just
1900 cm_space_t *psp; /* space for current path, if we've hit
1902 cm_space_t *tempsp; /* temp vbl */
1903 clientchar_t *restp; /* rest of the pathname to interpret */
1904 int symlinkCount; /* count of # of symlinks traversed */
1905 int extraFlag; /* avoid chasing mt pts for dir cmd */
1906 int phase = 1; /* 1 = tidPathp, 2 = pathp */
1907 #define MAX_FID_COUNT 512
1908 cm_fid_t fids[MAX_FID_COUNT]; /* array of fids processed in this path walk */
1909 int fid_count = 0; /* number of fids processed in this path walk */
1914 #ifdef DEBUG_REFCOUNT
1915 afsi_log("%s:%d cm_NameI rootscp 0x%p ref %d", file, line, rootSCachep, rootSCachep->refCount);
1916 osi_Log4(afsd_logp,"cm_NameI rootscp 0x%p path %S tidpath %S flags 0x%x",
1917 rootSCachep, pathp ? pathp : L"<NULL>", tidPathp ? tidPathp : L"<NULL>",
1932 cm_HoldSCache(tscp);
1940 /* map Unix slashes into DOS ones so we can interpret Unix
1946 if (!haveComponent) {
1949 } else if (tc == 0) {
1963 /* we have a component here */
1964 if (tc == 0 || tc == '\\') {
1965 /* end of the component; we're at the last
1966 * component if tc == 0. However, if the last
1967 * is a symlink, we have more to do.
1969 *cp++ = 0; /* add null termination */
1971 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
1972 extraFlag = CM_FLAG_NOMOUNTCHASE;
1973 code = cm_Lookup(tscp, component,
1975 userp, reqp, &nscp);
1978 if (!cm_ClientStrCmp(component,_C("..")) ||
1979 !cm_ClientStrCmp(component,_C("."))) {
1981 * roll back the fid list until we find the
1982 * fid that matches where we are now. Its not
1983 * necessarily one or two fids because they
1984 * might have been symlinks or mount points or
1985 * both that were crossed.
1987 for ( i=fid_count-1; i>=0; i--) {
1988 if (!cm_FidCmp(&nscp->fid, &fids[i]))
1993 /* add the new fid to the list */
1994 if (fid_count == MAX_FID_COUNT) {
1995 code = CM_ERROR_TOO_MANY_SYMLINKS;
1996 cm_ReleaseSCache(nscp);
2000 fids[fid_count++] = nscp->fid;
2005 cm_ReleaseSCache(tscp);
2007 cm_ReleaseSCache(dirScp);
2010 if ((code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH) &&
2011 tscp->fileType == CM_SCACHETYPE_SYMLINK) {
2012 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_NOSUCHPATH");
2013 return CM_ERROR_NOSUCHPATH;
2015 osi_Log1(afsd_logp,"cm_NameI code 0x%x", code);
2020 haveComponent = 0; /* component done */
2022 cm_ReleaseSCache(dirScp);
2023 dirScp = tscp; /* for some symlinks */
2024 tscp = nscp; /* already held */
2026 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
2029 cm_ReleaseSCache(dirScp);
2035 /* now, if tscp is a symlink, we should follow it and
2036 * assemble the path again.
2038 lock_ObtainWrite(&tscp->rw);
2039 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
2040 CM_SCACHESYNC_GETSTATUS
2041 | CM_SCACHESYNC_NEEDCALLBACK);
2043 lock_ReleaseWrite(&tscp->rw);
2044 cm_ReleaseSCache(tscp);
2047 cm_ReleaseSCache(dirScp);
2052 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2054 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
2055 /* this is a symlink; assemble a new buffer */
2056 lock_ReleaseWrite(&tscp->rw);
2057 if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
2058 cm_ReleaseSCache(tscp);
2061 cm_ReleaseSCache(dirScp);
2066 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_TOO_MANY_SYMLINKS");
2067 return CM_ERROR_TOO_MANY_SYMLINKS;
2077 /* TODO: make this better */
2078 frestp = cm_ClientStringToFsStringAlloc(restp, -1, NULL);
2079 code = cm_AssembleLink(tscp, frestp, &linkScp, &tempsp, userp, reqp);
2083 if (code == 0 && linkScp != NULL) {
2084 if (linkScp == cm_data.rootSCachep) {
2088 for ( i=0; i<fid_count; i++) {
2089 if ( !cm_FidCmp(&linkScp->fid, &fids[i]) ) {
2090 code = CM_ERROR_TOO_MANY_SYMLINKS;
2091 cm_ReleaseSCache(linkScp);
2097 if (i == fid_count && fid_count < MAX_FID_COUNT) {
2098 fids[fid_count++] = linkScp->fid;
2103 /* something went wrong */
2104 cm_ReleaseSCache(tscp);
2107 cm_ReleaseSCache(dirScp);
2113 /* otherwise, tempsp has the new path,
2114 * and linkScp is the new root from
2115 * which to interpret that path.
2116 * Continue with the namei processing,
2117 * also doing the bookkeeping for the
2118 * space allocation and tracking the
2119 * vnode reference counts.
2125 cm_ReleaseSCache(tscp);
2130 * now, if linkScp is null, that's
2131 * AssembleLink's way of telling us that
2132 * the sym link is relative to the dir
2133 * containing the link. We have a ref
2134 * to it in dirScp, and we hold it now
2135 * and reuse it as the new spot in the
2143 /* not a symlink, we may be done */
2144 lock_ReleaseWrite(&tscp->rw);
2152 cm_ReleaseSCache(dirScp);
2160 cm_ReleaseSCache(dirScp);
2163 } /* end of a component */
2166 } /* we have a component */
2167 } /* big while loop over all components */
2171 cm_ReleaseSCache(dirScp);
2177 cm_ReleaseSCache(tscp);
2179 #ifdef DEBUG_REFCOUNT
2180 afsi_log("%s:%d cm_NameI code 0x%x outScpp 0x%p ref %d", file, line, code, *outScpp, (*outScpp) ? (*outScpp)->refCount : 0);
2182 osi_Log2(afsd_logp,"cm_NameI code 0x%x outScpp 0x%p", code, *outScpp);
2186 /* called with a dir, and a vnode within the dir that happens to be a symlink.
2187 * We chase the link, and return a held pointer to the target, if it exists,
2188 * in *outScpp. If we succeed, we return 0, otherwise we return an error code
2189 * and do not hold or return a target vnode.
2191 * This is very similar to calling cm_NameI with the last component of a name,
2192 * which happens to be a symlink, except that we've already passed by the name.
2194 * This function is typically called by the directory listing functions, which
2195 * encounter symlinks but need to return the proper file length so programs
2196 * like "more" work properly when they make use of the attributes retrieved from
2199 * The input vnode should not be locked when this function is called.
2201 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
2202 cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
2206 cm_scache_t *newRootScp;
2210 osi_Log1(afsd_logp, "Evaluating symlink scp 0x%p", linkScp);
2212 code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
2216 /* now, if newRootScp is NULL, we're really being told that the symlink
2217 * is relative to the current directory (dscp).
2219 if (newRootScp == NULL) {
2221 cm_HoldSCache(dscp);
2224 code = cm_NameI(newRootScp, spacep->wdata,
2225 CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
2226 userp, NULL, reqp, outScpp);
2228 if (code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH)
2229 code = CM_ERROR_NOSUCHPATH;
2231 /* this stuff is allocated no matter what happened on the namei call,
2233 cm_FreeSpace(spacep);
2234 cm_ReleaseSCache(newRootScp);
2236 if (linkScp == *outScpp) {
2237 cm_ReleaseSCache(*outScpp);
2239 code = CM_ERROR_NOSUCHPATH;
2245 /* for a given entry, make sure that it isn't in the stat cache, and then
2246 * add it to the list of file IDs to be obtained.
2248 * Don't bother adding it if we already have a vnode. Note that the dir
2249 * is locked, so we have to be careful checking the vnode we're thinking of
2250 * processing, to avoid deadlocks.
2252 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
2263 /* Don't overflow bsp. */
2264 if (bsp->counter >= CM_BULKMAX)
2265 return CM_ERROR_STOPNOW;
2267 thyper.LowPart = cm_data.buf_blockSize;
2268 thyper.HighPart = 0;
2269 thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
2271 /* thyper is now the first byte past the end of the record we're
2272 * interested in, and bsp->bufOffset is the first byte of the record
2273 * we're interested in.
2274 * Skip data in the others.
2277 if (LargeIntegerLessThan(*offp, bsp->bufOffset))
2279 if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
2280 return CM_ERROR_STOPNOW;
2281 if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
2284 cm_SetFid(&tfid, scp->fid.cell, scp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
2285 tscp = cm_FindSCache(&tfid);
2287 if (lock_TryWrite(&tscp->rw)) {
2288 /* we have an entry that we can look at */
2289 if (!(tscp->flags & CM_SCACHEFLAG_EACCESS) && cm_HaveCallback(tscp)) {
2290 /* we have a callback on it. Don't bother
2291 * fetching this stat entry, since we're happy
2292 * with the info we have.
2294 lock_ReleaseWrite(&tscp->rw);
2295 cm_ReleaseSCache(tscp);
2298 lock_ReleaseWrite(&tscp->rw);
2300 cm_ReleaseSCache(tscp);
2303 #ifdef AFS_FREELANCE_CLIENT
2304 // yj: if this is a mountpoint under root.afs then we don't want it
2305 // to be bulkstat-ed, instead, we call getSCache directly and under
2306 // getSCache, it is handled specially.
2307 if ( cm_freelanceEnabled &&
2308 tfid.cell==AFS_FAKE_ROOT_CELL_ID &&
2309 tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
2310 !(tfid.vnode==0x1 && tfid.unique==0x1) )
2312 osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
2313 return cm_GetSCache(&tfid, &tscp, NULL, NULL);
2315 #endif /* AFS_FREELANCE_CLIENT */
2318 bsp->fids[i].Volume = scp->fid.volume;
2319 bsp->fids[i].Vnode = tfid.vnode;
2320 bsp->fids[i].Unique = tfid.unique;
2325 cm_TryBulkStatRPC(cm_scache_t *dscp, cm_bulkStat_t *bbp, cm_user_t *userp, cm_req_t *reqp)
2328 AFSCBFids fidStruct;
2329 AFSBulkStats statStruct;
2331 AFSCBs callbackStruct;
2334 cm_callbackRequest_t cbReq;
2340 struct rx_connection * rxconnp;
2341 int inlinebulk; /* Did we use InlineBulkStatus RPC or not? */
2343 memset(&volSync, 0, sizeof(volSync));
2345 /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
2346 * make the calls to create the entries. Handle AFSCBMAX files at a
2349 for (filex = 0; filex < bbp->counter; filex += filesThisCall) {
2350 filesThisCall = bbp->counter - filex;
2351 if (filesThisCall > AFSCBMAX)
2352 filesThisCall = AFSCBMAX;
2354 fidStruct.AFSCBFids_len = filesThisCall;
2355 fidStruct.AFSCBFids_val = &bbp->fids[filex];
2356 statStruct.AFSBulkStats_len = filesThisCall;
2357 statStruct.AFSBulkStats_val = &bbp->stats[filex];
2358 callbackStruct.AFSCBs_len = filesThisCall;
2359 callbackStruct.AFSCBs_val = &bbp->callbacks[filex];
2360 cm_StartCallbackGrantingCall(NULL, &cbReq);
2361 osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
2364 * Whenever cm_Analyze is called for a RXAFS_ RPC there must
2365 * be a FID provided. However, the error code from RXAFS_BulkStatus
2366 * or RXAFS_InlinkBulkStatus does not apply to any FID. Therefore,
2367 * we generate an invalid FID to match with the RPC error.
2369 cm_SetFid(&tfid, dscp->fid.cell, dscp->fid.volume, 0, 0);
2374 code = cm_ConnFromFID(&tfid, userp, reqp, &connp);
2378 rxconnp = cm_GetRxConn(connp);
2379 if (!(connp->serverp->flags & CM_SERVERFLAG_NOINLINEBULK)) {
2380 code = RXAFS_InlineBulkStatus(rxconnp, &fidStruct,
2381 &statStruct, &callbackStruct, &volSync);
2382 if (code == RXGEN_OPCODE) {
2383 cm_SetServerNoInlineBulk(connp->serverp, 0);
2389 code = RXAFS_BulkStatus(rxconnp, &fidStruct,
2390 &statStruct, &callbackStruct, &volSync);
2392 rx_PutConnection(rxconnp);
2395 * If InlineBulk RPC was called and it succeeded,
2396 * then pull out the return code from the status info
2397 * and use it for cm_Analyze so that we can failover to other
2398 * .readonly volume instances. But only do it for errors that
2399 * are volume global.
2401 if (inlinebulk && code == 0 && (&bbp->stats[0])->errorCode) {
2402 osi_Log1(afsd_logp, "cm_TryBulkStat inline-bulk stat error: %d",
2403 (&bbp->stats[0])->errorCode);
2404 switch ((&bbp->stats[0])->errorCode) {
2413 code = (&bbp->stats[0])->errorCode;
2416 /* Rx and Rxkad errors are volume global */
2417 if ( (&bbp->stats[0])->errorCode >= -64 && (&bbp->stats[0])->errorCode < 0 ||
2418 (&bbp->stats[0])->errorCode >= ERROR_TABLE_BASE_RXK && (&bbp->stats[0])->errorCode < ERROR_TABLE_BASE_RXK + 256)
2419 code = (&bbp->stats[0])->errorCode;
2422 } while (cm_Analyze(connp, userp, reqp, &tfid, &volSync, NULL, &cbReq, code));
2423 code = cm_MapRPCError(code, reqp);
2426 * might as well quit on an error, since we're not going to do
2427 * much better on the next immediate call, either.
2430 osi_Log2(afsd_logp, "CALL %sBulkStatus FAILURE code 0x%x",
2431 inlinebulk ? "Inline" : "", code);
2432 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2437 * The bulk RPC has succeeded or at least not failed with a
2438 * volume global error result. For items that have inlineBulk
2439 * errors we must call cm_Analyze in order to perform required
2440 * logging of errors.
2442 * If the RPC was not inline bulk or the entry either has no error
2443 * the status must be merged.
2445 osi_Log1(afsd_logp, "CALL %sBulkStatus SUCCESS", inlinebulk ? "Inline" : "");
2447 for (i = 0; i<filesThisCall; i++) {
2449 cm_SetFid(&tfid, dscp->fid.cell, bbp->fids[j].Volume, bbp->fids[j].Vnode, bbp->fids[j].Unique);
2451 if (inlinebulk && (&bbp->stats[j])->errorCode) {
2452 cm_req_t treq = *reqp;
2453 cm_Analyze(NULL, userp, &treq, &tfid, &volSync, NULL, &cbReq, (&bbp->stats[j])->errorCode);
2455 code = cm_GetSCache(&tfid, &scp, userp, reqp);
2460 * otherwise, if this entry has no callback info,
2461 * merge in this. If there is existing callback info
2462 * we skip the merge because the existing data must be
2463 * current (we have a callback) and the response from
2464 * a non-inline bulk rpc might actually be wrong.
2466 * now, we have to be extra paranoid on merging in this
2467 * information, since we didn't use cm_SyncOp before
2468 * starting the fetch to make sure that no bad races
2469 * were occurring. Specifically, we need to make sure
2470 * we don't obliterate any newer information in the
2471 * vnode than have here.
2473 * Right now, be pretty conservative: if there's a
2474 * callback or a pending call, skip it.
2475 * However, if the prior attempt to obtain status
2476 * was refused access or the volume is .readonly,
2477 * take the data in any case since we have nothing
2478 * better for the in flight directory enumeration that
2479 * resulted in this function being called.
2481 lock_ObtainRead(&scp->rw);
2482 if ((scp->cbServerp == NULL &&
2483 !(scp->flags & (CM_SCACHEFLAG_FETCHING | CM_SCACHEFLAG_STORING | CM_SCACHEFLAG_SIZESTORING))) ||
2484 (scp->flags & CM_SCACHEFLAG_PURERO) ||
2485 (scp->flags & CM_SCACHEFLAG_EACCESS))
2487 lock_ConvertRToW(&scp->rw);
2488 cm_EndCallbackGrantingCall(scp, &cbReq,
2491 CM_CALLBACK_MAINTAINCOUNT);
2492 cm_MergeStatus(dscp, scp, &bbp->stats[j], &volSync, userp, reqp, 0);
2493 lock_ReleaseWrite(&scp->rw);
2495 lock_ReleaseRead(&scp->rw);
2497 cm_ReleaseSCache(scp);
2499 } /* all files in the response */
2500 /* now tell it to drop the count,
2501 * after doing the vnode processing above */
2502 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2503 } /* while there are still more files to process */
2508 /* called with a write locked scp and a pointer to a buffer. Make bulk stat
2509 * calls on all undeleted files in the page of the directory specified.
2512 cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
2518 osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%p", dscp);
2520 /* should be on a buffer boundary */
2521 osi_assertx((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0, "invalid offset");
2523 bbp = malloc(sizeof(cm_bulkStat_t));
2524 memset(bbp, 0, sizeof(cm_bulkStat_t));
2525 bbp->bufOffset = *offsetp;
2527 lock_ReleaseWrite(&dscp->rw);
2528 /* first, assemble the file IDs we need to stat */
2529 code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) bbp, offsetp, userp, reqp, NULL);
2531 /* if we failed, bail out early */
2532 if (code && code != CM_ERROR_STOPNOW) {
2534 lock_ObtainWrite(&dscp->rw);
2538 code = cm_TryBulkStatRPC(dscp, bbp, userp, reqp);
2539 osi_Log1(afsd_logp, "END cm_TryBulkStat code 0x%x", code);
2541 lock_ObtainWrite(&dscp->rw);
2546 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
2550 /* initialize store back mask as inexpensive local variable */
2552 memset(statusp, 0, sizeof(AFSStoreStatus));
2554 /* copy out queued info from scache first, if scp passed in */
2556 if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
2557 statusp->ClientModTime = scp->clientModTime;
2558 mask |= AFS_SETMODTIME;
2559 scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
2564 /* now add in our locally generated request */
2565 if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
2566 statusp->ClientModTime = attrp->clientModTime;
2567 mask |= AFS_SETMODTIME;
2569 if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
2570 statusp->UnixModeBits = attrp->unixModeBits;
2571 mask |= AFS_SETMODE;
2573 if (attrp->mask & CM_ATTRMASK_OWNER) {
2574 statusp->Owner = attrp->owner;
2575 mask |= AFS_SETOWNER;
2577 if (attrp->mask & CM_ATTRMASK_GROUP) {
2578 statusp->Group = attrp->group;
2579 mask |= AFS_SETGROUP;
2582 statusp->Mask = mask;
2585 /* set the file size, and make sure that all relevant buffers have been
2586 * truncated. Ensure that any partially truncated buffers have been zeroed
2587 * to the end of the buffer.
2589 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
2595 /* start by locking out buffer creation */
2596 lock_ObtainWrite(&scp->bufCreateLock);
2598 /* verify that this is a file, not a dir or a symlink */
2599 lock_ObtainWrite(&scp->rw);
2600 code = cm_SyncOp(scp, NULL, userp, reqp, 0,
2601 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2604 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2606 if (scp->fileType != CM_SCACHETYPE_FILE) {
2607 code = CM_ERROR_ISDIR;
2612 if (LargeIntegerLessThan(*sizep, scp->length))
2617 lock_ReleaseWrite(&scp->rw);
2619 /* can't hold scp->rw lock here, since we may wait for a storeback to
2620 * finish if the buffer package is cleaning a buffer by storing it to
2624 buf_Truncate(scp, userp, reqp, sizep);
2626 /* now ensure that file length is short enough, and update truncPos */
2627 lock_ObtainWrite(&scp->rw);
2629 /* make sure we have a callback (so we have the right value for the
2630 * length), and wait for it to be safe to do a truncate.
2632 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
2633 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2634 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2636 /* If we only have 'i' bits, then we should still be able to set
2637 the size of a file we created. */
2638 if (code == CM_ERROR_NOACCESS && scp->creator == userp) {
2639 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
2640 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2641 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2647 if (LargeIntegerLessThan(*sizep, scp->length)) {
2648 /* a real truncation. If truncPos is not set yet, or is bigger
2649 * than where we're truncating the file, set truncPos to this
2654 if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
2655 || LargeIntegerLessThan(*sizep, scp->length)) {
2657 scp->truncPos = *sizep;
2658 scp->mask |= CM_SCACHEMASK_TRUNCPOS;
2660 /* in either case, the new file size has been changed */
2661 scp->length = *sizep;
2662 scp->mask |= CM_SCACHEMASK_LENGTH;
2664 else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
2665 /* really extending the file */
2666 scp->length = *sizep;
2667 scp->mask |= CM_SCACHEMASK_LENGTH;
2670 /* done successfully */
2673 cm_SyncOpDone(scp, NULL,
2674 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2675 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2678 lock_ReleaseWrite(&scp->rw);
2679 lock_ReleaseWrite(&scp->bufCreateLock);
2684 /* set the file size or other attributes (but not both at once) */
2685 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
2689 AFSFetchStatus afsOutStatus;
2693 AFSStoreStatus afsInStatus;
2694 struct rx_connection * rxconnp;
2696 memset(&volSync, 0, sizeof(volSync));
2698 /* handle file length setting */
2699 if (attrp->mask & CM_ATTRMASK_LENGTH)
2700 return cm_SetLength(scp, &attrp->length, userp, reqp);
2702 lock_ObtainWrite(&scp->rw);
2703 /* Check for RO volume */
2704 if (scp->flags & CM_SCACHEFLAG_RO) {
2705 code = CM_ERROR_READONLY;
2706 lock_ReleaseWrite(&scp->rw);
2710 /* otherwise, we have to make an RPC to get the status */
2711 code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
2713 lock_ReleaseWrite(&scp->rw);
2716 lock_ConvertWToR(&scp->rw);
2718 /* make the attr structure */
2719 cm_StatusFromAttr(&afsInStatus, scp, attrp);
2721 tfid.Volume = scp->fid.volume;
2722 tfid.Vnode = scp->fid.vnode;
2723 tfid.Unique = scp->fid.unique;
2724 lock_ReleaseRead(&scp->rw);
2726 /* now make the RPC */
2727 osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%p", scp);
2729 code = cm_ConnFromFID(&scp->fid, userp, reqp, &connp);
2733 rxconnp = cm_GetRxConn(connp);
2734 code = RXAFS_StoreStatus(rxconnp, &tfid,
2735 &afsInStatus, &afsOutStatus, &volSync);
2736 rx_PutConnection(rxconnp);
2738 } while (cm_Analyze(connp, userp, reqp,
2739 &scp->fid, &volSync, NULL, NULL, code));
2740 code = cm_MapRPCError(code, reqp);
2743 osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
2745 osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
2747 lock_ObtainWrite(&scp->rw);
2748 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
2750 cm_MergeStatus(NULL, scp, &afsOutStatus, &volSync, userp, reqp,
2751 CM_MERGEFLAG_FORCE|CM_MERGEFLAG_STOREDATA);
2753 /* if we're changing the mode bits, discard the ACL cache,
2754 * since we changed the mode bits.
2756 if (afsInStatus.Mask & AFS_SETMODE)
2757 cm_FreeAllACLEnts(scp);
2758 lock_ReleaseWrite(&scp->rw);
2762 long cm_Create(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
2763 cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
2768 cm_callbackRequest_t cbReq;
2771 cm_scache_t *scp = NULL;
2773 AFSStoreStatus inStatus;
2774 AFSFetchStatus updatedDirStatus;
2775 AFSFetchStatus newFileStatus;
2776 AFSCallBack newFileCallback;
2778 struct rx_connection * rxconnp;
2780 fschar_t * fnamep = NULL;
2782 memset(&volSync, 0, sizeof(volSync));
2784 /* can't create names with @sys in them; must expand it manually first.
2785 * return "invalid request" if they try.
2787 if (cm_ExpandSysName(cnamep, NULL, 0, 0)) {
2788 return CM_ERROR_ATSYS;
2791 #ifdef AFS_FREELANCE_CLIENT
2792 /* Freelance root volume does not hold files */
2793 if (cm_freelanceEnabled &&
2794 dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
2795 dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
2797 return CM_ERROR_NOACCESS;
2799 #endif /* AFS_FREELANCE_CLIENT */
2801 /* Check for RO volume */
2802 if (dscp->flags & CM_SCACHEFLAG_RO)
2803 return CM_ERROR_READONLY;
2805 /* before starting the RPC, mark that we're changing the file data, so
2806 * that someone who does a chmod will know to wait until our call
2809 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
2811 lock_ObtainWrite(&dscp->rw);
2812 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2813 lock_ReleaseWrite(&dscp->rw);
2815 cm_StartCallbackGrantingCall(NULL, &cbReq);
2817 cm_EndDirOp(&dirop);
2824 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
2826 cm_StatusFromAttr(&inStatus, NULL, attrp);
2828 /* try the RPC now */
2829 osi_Log1(afsd_logp, "CALL CreateFile scp 0x%p", dscp);
2831 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2835 dirAFSFid.Volume = dscp->fid.volume;
2836 dirAFSFid.Vnode = dscp->fid.vnode;
2837 dirAFSFid.Unique = dscp->fid.unique;
2839 rxconnp = cm_GetRxConn(connp);
2840 code = RXAFS_CreateFile(connp->rxconnp, &dirAFSFid, fnamep,
2841 &inStatus, &newAFSFid, &newFileStatus,
2842 &updatedDirStatus, &newFileCallback,
2844 rx_PutConnection(rxconnp);
2846 } while (cm_Analyze(connp, userp, reqp,
2847 &dscp->fid, &volSync, NULL, &cbReq, code));
2848 code = cm_MapRPCError(code, reqp);
2851 osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
2853 osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
2856 lock_ObtainWrite(&dirop.scp->dirlock);
2857 dirop.lockType = CM_DIRLOCK_WRITE;
2859 lock_ObtainWrite(&dscp->rw);
2860 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2862 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
2864 lock_ReleaseWrite(&dscp->rw);
2866 /* now try to create the file's entry, too, but be careful to
2867 * make sure that we don't merge in old info. Since we weren't locking
2868 * out any requests during the file's creation, we may have pretty old
2872 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
2873 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2875 lock_ObtainWrite(&scp->rw);
2876 scp->creator = userp; /* remember who created it */
2877 if (!cm_HaveCallback(scp)) {
2878 cm_EndCallbackGrantingCall(scp, &cbReq,
2879 &newFileCallback, &volSync, 0);
2880 cm_MergeStatus(dscp, scp, &newFileStatus, &volSync,
2884 lock_ReleaseWrite(&scp->rw);
2888 /* make sure we end things properly */
2890 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2892 if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
2893 cm_DirCreateEntry(&dirop, fnamep, &newFid);
2895 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
2898 cm_EndDirOp(&dirop);
2907 cm_ReleaseSCache(scp);
2913 * locked if TRUE means write-locked
2914 * else the cm_scache_t rw must not be held
2916 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp, afs_uint32 locked)
2921 lock_ReleaseWrite(&scp->rw);
2922 code = buf_CleanVnode(scp, userp, reqp);
2924 lock_ObtainWrite(&scp->rw);
2926 if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
2927 | CM_SCACHEMASK_CLIENTMODTIME
2928 | CM_SCACHEMASK_LENGTH))
2929 code = cm_StoreMini(scp, userp, reqp);
2931 if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) {
2932 code = (scp->flags & CM_SCACHEFLAG_OVERQUOTA) ? CM_ERROR_QUOTA : CM_ERROR_SPACE;
2933 scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE);
2937 lock_ReleaseWrite(&scp->rw);
2938 } else if (locked) {
2939 lock_ObtainWrite(&scp->rw);
2944 long cm_MakeDir(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
2945 cm_user_t *userp, cm_req_t *reqp, cm_scache_t **scpp)
2950 cm_callbackRequest_t cbReq;
2953 cm_scache_t *scp = NULL;
2955 AFSStoreStatus inStatus;
2956 AFSFetchStatus updatedDirStatus;
2957 AFSFetchStatus newDirStatus;
2958 AFSCallBack newDirCallback;
2960 struct rx_connection * rxconnp;
2962 fschar_t * fnamep = NULL;
2964 memset(&volSync, 0, sizeof(volSync));
2966 /* can't create names with @sys in them; must expand it manually first.
2967 * return "invalid request" if they try.
2969 if (cm_ExpandSysName(cnamep, NULL, 0, 0)) {
2970 return CM_ERROR_ATSYS;
2973 #ifdef AFS_FREELANCE_CLIENT
2974 /* Freelance root volume does not hold subdirectories */
2975 if (cm_freelanceEnabled &&
2976 dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
2977 dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
2979 return CM_ERROR_NOACCESS;
2981 #endif /* AFS_FREELANCE_CLIENT */
2983 /* Check for RO volume */
2984 if (dscp->flags & CM_SCACHEFLAG_RO)
2985 return CM_ERROR_READONLY;
2987 /* before starting the RPC, mark that we're changing the directory
2988 * data, so that someone who does a chmod on the dir will wait until
2989 * our call completes.
2991 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
2993 lock_ObtainWrite(&dscp->rw);
2994 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2995 lock_ReleaseWrite(&dscp->rw);
2997 cm_StartCallbackGrantingCall(NULL, &cbReq);
2999 cm_EndDirOp(&dirop);
3006 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3007 cm_StatusFromAttr(&inStatus, NULL, attrp);
3009 /* try the RPC now */
3010 osi_Log1(afsd_logp, "CALL MakeDir scp 0x%p", dscp);
3012 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3016 dirAFSFid.Volume = dscp->fid.volume;
3017 dirAFSFid.Vnode = dscp->fid.vnode;
3018 dirAFSFid.Unique = dscp->fid.unique;
3020 rxconnp = cm_GetRxConn(connp);
3021 code = RXAFS_MakeDir(connp->rxconnp, &dirAFSFid, fnamep,
3022 &inStatus, &newAFSFid, &newDirStatus,
3023 &updatedDirStatus, &newDirCallback,
3025 rx_PutConnection(rxconnp);
3027 } while (cm_Analyze(connp, userp, reqp,
3028 &dscp->fid, &volSync, NULL, &cbReq, code));
3029 code = cm_MapRPCError(code, reqp);
3032 osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
3034 osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
3037 lock_ObtainWrite(&dirop.scp->dirlock);
3038 dirop.lockType = CM_DIRLOCK_WRITE;
3040 lock_ObtainWrite(&dscp->rw);
3041 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3043 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3045 lock_ReleaseWrite(&dscp->rw);
3047 /* now try to create the new dir's entry, too, but be careful to
3048 * make sure that we don't merge in old info. Since we weren't locking
3049 * out any requests during the file's creation, we may have pretty old
3053 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3054 code = cm_GetSCache(&newFid, &scp, userp, reqp);
3056 lock_ObtainWrite(&scp->rw);
3057 if (!cm_HaveCallback(scp)) {
3058 cm_EndCallbackGrantingCall(scp, &cbReq,
3059 &newDirCallback, &volSync, 0);
3060 cm_MergeStatus(dscp, scp, &newDirStatus, &volSync,
3064 lock_ReleaseWrite(&scp->rw);
3068 /* make sure we end things properly */
3070 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
3072 if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
3073 cm_DirCreateEntry(&dirop, fnamep, &newFid);
3075 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
3078 cm_EndDirOp(&dirop);
3086 cm_ReleaseSCache(scp);
3089 /* and return error code */
3093 long cm_Link(cm_scache_t *dscp, clientchar_t *cnamep, cm_scache_t *sscp, long flags,
3094 cm_user_t *userp, cm_req_t *reqp)
3099 AFSFid existingAFSFid;
3100 AFSFetchStatus updatedDirStatus;
3101 AFSFetchStatus newLinkStatus;
3103 struct rx_connection * rxconnp;
3105 fschar_t * fnamep = NULL;
3107 memset(&volSync, 0, sizeof(volSync));
3109 if (dscp->fid.cell != sscp->fid.cell ||
3110 dscp->fid.volume != sscp->fid.volume) {
3111 return CM_ERROR_CROSSDEVLINK;
3114 /* Check for RO volume */
3115 if (dscp->flags & CM_SCACHEFLAG_RO)
3116 return CM_ERROR_READONLY;
3118 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3120 lock_ObtainWrite(&dscp->rw);
3121 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3122 lock_ReleaseWrite(&dscp->rw);
3124 cm_EndDirOp(&dirop);
3129 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3131 /* try the RPC now */
3132 osi_Log1(afsd_logp, "CALL Link scp 0x%p", dscp);
3134 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3137 dirAFSFid.Volume = dscp->fid.volume;
3138 dirAFSFid.Vnode = dscp->fid.vnode;
3139 dirAFSFid.Unique = dscp->fid.unique;
3141 existingAFSFid.Volume = sscp->fid.volume;
3142 existingAFSFid.Vnode = sscp->fid.vnode;
3143 existingAFSFid.Unique = sscp->fid.unique;
3145 rxconnp = cm_GetRxConn(connp);
3146 code = RXAFS_Link(rxconnp, &dirAFSFid, fnamep, &existingAFSFid,
3147 &newLinkStatus, &updatedDirStatus, &volSync);
3148 rx_PutConnection(rxconnp);
3149 osi_Log1(afsd_logp," RXAFS_Link returns 0x%x", code);
3151 } while (cm_Analyze(connp, userp, reqp,
3152 &dscp->fid, &volSync, NULL, NULL, code));
3154 code = cm_MapRPCError(code, reqp);
3157 osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
3159 osi_Log0(afsd_logp, "CALL Link SUCCESS");
3162 lock_ObtainWrite(&dirop.scp->dirlock);
3163 dirop.lockType = CM_DIRLOCK_WRITE;
3165 lock_ObtainWrite(&dscp->rw);
3166 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3168 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3170 lock_ReleaseWrite(&dscp->rw);
3173 if (cm_CheckDirOpForSingleChange(&dirop)) {
3174 cm_DirCreateEntry(&dirop, fnamep, &sscp->fid);
3176 cm_BPlusDirCreateEntry(&dirop, cnamep, &sscp->fid);
3180 cm_EndDirOp(&dirop);
3182 /* Update the linked object status */
3184 lock_ObtainWrite(&sscp->rw);
3185 cm_MergeStatus(NULL, sscp, &newLinkStatus, &volSync, userp, reqp, 0);
3186 lock_ReleaseWrite(&sscp->rw);
3194 long cm_SymLink(cm_scache_t *dscp, clientchar_t *cnamep, fschar_t *contentsp, long flags,
3195 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
3203 AFSStoreStatus inStatus;
3204 AFSFetchStatus updatedDirStatus;
3205 AFSFetchStatus newLinkStatus;
3207 struct rx_connection * rxconnp;
3209 fschar_t *fnamep = NULL;
3211 /* Check for RO volume */
3212 if (dscp->flags & CM_SCACHEFLAG_RO)
3213 return CM_ERROR_READONLY;
3215 memset(&volSync, 0, sizeof(volSync));
3217 /* before starting the RPC, mark that we're changing the directory data,
3218 * so that someone who does a chmod on the dir will wait until our
3221 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3223 lock_ObtainWrite(&dscp->rw);
3224 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3225 lock_ReleaseWrite(&dscp->rw);
3227 cm_EndDirOp(&dirop);
3232 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3234 cm_StatusFromAttr(&inStatus, NULL, attrp);
3236 /* try the RPC now */
3237 osi_Log1(afsd_logp, "CALL Symlink scp 0x%p", dscp);
3239 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3243 dirAFSFid.Volume = dscp->fid.volume;
3244 dirAFSFid.Vnode = dscp->fid.vnode;
3245 dirAFSFid.Unique = dscp->fid.unique;
3247 rxconnp = cm_GetRxConn(connp);
3248 code = RXAFS_Symlink(rxconnp, &dirAFSFid, fnamep, contentsp,
3249 &inStatus, &newAFSFid, &newLinkStatus,
3250 &updatedDirStatus, &volSync);
3251 rx_PutConnection(rxconnp);
3253 } while (cm_Analyze(connp, userp, reqp,
3254 &dscp->fid, &volSync, NULL, NULL, code));
3255 code = cm_MapRPCError(code, reqp);
3258 osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
3260 osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
3263 lock_ObtainWrite(&dirop.scp->dirlock);
3264 dirop.lockType = CM_DIRLOCK_WRITE;
3266 lock_ObtainWrite(&dscp->rw);
3267 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3269 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3271 lock_ReleaseWrite(&dscp->rw);
3274 if (cm_CheckDirOpForSingleChange(&dirop)) {
3275 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3277 cm_DirCreateEntry(&dirop, fnamep, &newFid);
3279 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
3283 cm_EndDirOp(&dirop);
3285 /* now try to create the new dir's entry, too, but be careful to
3286 * make sure that we don't merge in old info. Since we weren't locking
3287 * out any requests during the file's creation, we may have pretty old
3291 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3292 code = cm_GetSCache(&newFid, &scp, userp, reqp);
3294 lock_ObtainWrite(&scp->rw);
3295 if (!cm_HaveCallback(scp)) {
3296 cm_MergeStatus(dscp, scp, &newLinkStatus, &volSync,
3299 lock_ReleaseWrite(&scp->rw);
3300 cm_ReleaseSCache(scp);
3306 /* and return error code */
3310 /*! \brief Remove a directory
3312 Encapsulates a call to RXAFS_RemoveDir().
3314 \param[in] dscp cm_scache_t for the directory containing the
3315 directory to be removed.
3317 \param[in] fnamep This will be the original name of the directory
3318 as known to the file server. It will be passed in to RXAFS_RemoveDir().
3319 This parameter is optional. If it is not provided the value
3322 \param[in] cnamep Normalized name used to update the local
3325 \param[in] userp cm_user_t for the request.
3327 \param[in] reqp Request tracker.
3329 long cm_RemoveDir(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t *cnamep, cm_user_t *userp, cm_req_t *reqp)
3335 AFSFetchStatus updatedDirStatus;
3337 struct rx_connection * rxconnp;
3339 cm_scache_t *scp = NULL;
3340 int free_fnamep = FALSE;
3342 memset(&volSync, 0, sizeof(volSync));
3344 if (fnamep == NULL) {
3347 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ,
3348 CM_DIROP_FLAG_NONE, &dirop);
3350 code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
3353 cm_EndDirOp(&dirop);
3360 code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
3364 /* Check for RO volume */
3365 if (dscp->flags & CM_SCACHEFLAG_RO) {
3366 code = CM_ERROR_READONLY;
3370 /* before starting the RPC, mark that we're changing the directory data,
3371 * so that someone who does a chmod on the dir will wait until our
3374 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3376 lock_ObtainWrite(&dscp->rw);
3377 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3378 lock_ReleaseWrite(&dscp->rw);
3380 cm_EndDirOp(&dirop);
3385 /* try the RPC now */
3386 osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%p", dscp);
3388 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3392 dirAFSFid.Volume = dscp->fid.volume;
3393 dirAFSFid.Vnode = dscp->fid.vnode;
3394 dirAFSFid.Unique = dscp->fid.unique;
3396 rxconnp = cm_GetRxConn(connp);
3397 code = RXAFS_RemoveDir(rxconnp, &dirAFSFid, fnamep,
3398 &updatedDirStatus, &volSync);
3399 rx_PutConnection(rxconnp);
3401 } while (cm_Analyze(connp, userp, reqp,
3402 &dscp->fid, &volSync, NULL, NULL, code));
3403 code = cm_MapRPCErrorRmdir(code, reqp);
3406 osi_Log1(afsd_logp, "CALL RemoveDir FAILURE, code 0x%x", code);
3408 osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
3411 lock_ObtainWrite(&dirop.scp->dirlock);
3412 dirop.lockType = CM_DIRLOCK_WRITE;
3414 lock_ObtainWrite(&dscp->rw);
3415 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3417 cm_dnlcRemove(dscp, cnamep);
3418 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3420 lock_ReleaseWrite(&dscp->rw);
3423 if (cm_CheckDirOpForSingleChange(&dirop) && cnamep != NULL) {
3424 cm_DirDeleteEntry(&dirop, fnamep);
3426 cm_BPlusDirDeleteEntry(&dirop, cnamep);
3430 cm_EndDirOp(&dirop);
3433 cm_ReleaseSCache(scp);
3435 lock_ObtainWrite(&scp->rw);
3436 scp->flags |= CM_SCACHEFLAG_DELETED;
3437 lock_ReleaseWrite(&scp->rw);
3445 /* and return error code */
3449 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
3451 /* grab mutex on contents */
3452 lock_ObtainWrite(&scp->rw);
3454 /* reset the prefetch info */
3455 scp->prefetch.base.LowPart = 0; /* base */
3456 scp->prefetch.base.HighPart = 0;
3457 scp->prefetch.end.LowPart = 0; /* and end */
3458 scp->prefetch.end.HighPart = 0;
3460 /* release mutex on contents */
3461 lock_ReleaseWrite(&scp->rw);
3467 /*! \brief Rename a file or directory
3469 Encapsulates a RXAFS_Rename() call.
3471 \param[in] oldDscp cm_scache_t for the directory containing the old
3474 \param[in] oldNamep The original old name known to the file server.
3475 This is the name that will be passed into the RXAFS_Rename().
3476 If it is not provided, it will be looked up.
3478 \param[in] normalizedOldNamep Normalized old name. This is used for
3479 updating local directory caches.
3481 \param[in] newDscp cm_scache_t for the directory containing the new
3484 \param[in] newNamep New name. Normalized.
3486 \param[in] userp cm_user_t for the request.
3488 \param[in,out] reqp Request tracker.
3491 long cm_Rename(cm_scache_t *oldDscp, fschar_t *oldNamep, clientchar_t *cOldNamep,
3492 cm_scache_t *newDscp, clientchar_t *cNewNamep, cm_user_t *userp,
3497 AFSFid oldDirAFSFid;
3498 AFSFid newDirAFSFid;
3500 AFSFetchStatus updatedOldDirStatus;
3501 AFSFetchStatus updatedNewDirStatus;
3504 struct rx_connection * rxconnp;
3505 cm_dirOp_t oldDirOp;
3508 cm_dirOp_t newDirOp;
3509 fschar_t * newNamep = NULL;
3510 int free_oldNamep = FALSE;
3511 cm_scache_t *oldScp = NULL, *newScp = NULL;
3513 memset(&volSync, 0, sizeof(volSync));
3515 if (cOldNamep == NULL || cNewNamep == NULL ||
3516 cm_ClientStrLen(cOldNamep) == 0 ||
3517 cm_ClientStrLen(cNewNamep) == 0)
3518 return CM_ERROR_INVAL;
3521 * Before we permit the operation, make sure that we do not already have
3522 * an object in the destination directory that has a case-insensitive match
3523 * for this name UNLESS the matching object is the object we are renaming.
3525 code = cm_Lookup(oldDscp, cOldNamep, 0, userp, reqp, &oldScp);
3527 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S old name lookup failed",
3528 oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3532 code = cm_Lookup(newDscp, cNewNamep, CM_FLAG_CASEFOLD, userp, reqp, &newScp);
3534 /* found a matching object with the new name */
3535 if (cm_FidCmp(&oldScp->fid, &newScp->fid)) {
3536 /* and they don't match so return an error */
3537 osi_Log2(afsd_logp, "cm_Rename newDscp 0x%p cNewName %S new name already exists",
3538 newDscp, osi_LogSaveStringW(afsd_logp, cNewNamep));
3539 code = CM_ERROR_EXISTS;
3541 cm_ReleaseSCache(newScp);
3543 } else if (code == CM_ERROR_AMBIGUOUS_FILENAME) {
3544 code = CM_ERROR_EXISTS;
3549 /* Check for RO volume */
3551 (oldDscp->flags & CM_SCACHEFLAG_RO) || (newDscp->flags & CM_SCACHEFLAG_RO)) {
3552 code = CM_ERROR_READONLY;
3558 if (oldNamep == NULL) {
3561 code = cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_READ,
3562 CM_DIROP_FLAG_NONE, &oldDirOp);
3564 code = cm_BPlusDirLookupOriginalName(&oldDirOp, cOldNamep, &oldNamep);
3566 free_oldNamep = TRUE;
3567 cm_EndDirOp(&oldDirOp);
3571 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S Original Name lookup failed",
3572 oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3578 /* before starting the RPC, mark that we're changing the directory data,
3579 * so that someone who does a chmod on the dir will wait until our call
3580 * completes. We do this in vnode order so that we don't deadlock,
3581 * which makes the code a little verbose.
3583 if (oldDscp == newDscp) {
3584 /* check for identical names */
3585 if (cm_ClientStrCmp(cOldNamep, cNewNamep) == 0) {
3586 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_RENAME_IDENTICAL",
3588 code = CM_ERROR_RENAME_IDENTICAL;
3593 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE,
3594 CM_DIROP_FLAG_NONE, &oldDirOp);
3595 lock_ObtainWrite(&oldDscp->rw);
3596 cm_dnlcRemove(oldDscp, cOldNamep);
3597 cm_dnlcRemove(oldDscp, cNewNamep);
3598 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3599 CM_SCACHESYNC_STOREDATA);
3600 lock_ReleaseWrite(&oldDscp->rw);
3602 cm_EndDirOp(&oldDirOp);
3606 /* two distinct dir vnodes */
3608 if (oldDscp->fid.cell != newDscp->fid.cell ||
3609 oldDscp->fid.volume != newDscp->fid.volume) {
3610 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_CROSSDEVLINK",
3612 code = CM_ERROR_CROSSDEVLINK;
3616 /* shouldn't happen that we have distinct vnodes for two
3617 * different files, but could due to deliberate attack, or
3618 * stale info. Avoid deadlocks and quit now.
3620 if (oldDscp->fid.vnode == newDscp->fid.vnode) {
3621 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p vnode collision",
3623 code = CM_ERROR_CROSSDEVLINK;
3627 if (oldDscp->fid.vnode < newDscp->fid.vnode) {
3628 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE,
3629 CM_DIROP_FLAG_NONE, &oldDirOp);
3630 lock_ObtainWrite(&oldDscp->rw);
3631 cm_dnlcRemove(oldDscp, cOldNamep);
3632 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3633 CM_SCACHESYNC_STOREDATA);
3634 lock_ReleaseWrite(&oldDscp->rw);
3636 cm_EndDirOp(&oldDirOp);
3638 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE,
3639 CM_DIROP_FLAG_NONE, &newDirOp);
3640 lock_ObtainWrite(&newDscp->rw);
3641 cm_dnlcRemove(newDscp, cNewNamep);
3642 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3643 CM_SCACHESYNC_STOREDATA);
3644 lock_ReleaseWrite(&newDscp->rw);
3646 cm_EndDirOp(&newDirOp);
3648 /* cleanup first one */
3649 lock_ObtainWrite(&oldDscp->rw);
3650 cm_SyncOpDone(oldDscp, NULL,
3651 CM_SCACHESYNC_STOREDATA);
3652 lock_ReleaseWrite(&oldDscp->rw);
3653 cm_EndDirOp(&oldDirOp);
3658 /* lock the new vnode entry first */
3659 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE,
3660 CM_DIROP_FLAG_NONE, &newDirOp);
3661 lock_ObtainWrite(&newDscp->rw);
3662 cm_dnlcRemove(newDscp, cNewNamep);
3663 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3664 CM_SCACHESYNC_STOREDATA);
3665 lock_ReleaseWrite(&newDscp->rw);
3667 cm_EndDirOp(&newDirOp);
3669 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE,
3670 CM_DIROP_FLAG_NONE, &oldDirOp);
3671 lock_ObtainWrite(&oldDscp->rw);
3672 cm_dnlcRemove(oldDscp, cOldNamep);
3673 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3674 CM_SCACHESYNC_STOREDATA);
3675 lock_ReleaseWrite(&oldDscp->rw);
3677 cm_EndDirOp(&oldDirOp);
3679 /* cleanup first one */
3680 lock_ObtainWrite(&newDscp->rw);
3681 cm_SyncOpDone(newDscp, NULL,
3682 CM_SCACHESYNC_STOREDATA);
3683 lock_ReleaseWrite(&newDscp->rw);
3684 cm_EndDirOp(&newDirOp);
3688 } /* two distinct vnodes */
3695 newNamep = cm_ClientStringToFsStringAlloc(cNewNamep, -1, NULL);
3697 /* try the RPC now */
3698 osi_Log2(afsd_logp, "CALL Rename old scp 0x%p new scp 0x%p",
3701 code = cm_ConnFromFID(&oldDscp->fid, userp, reqp, &connp);
3705 oldDirAFSFid.Volume = oldDscp->fid.volume;
3706 oldDirAFSFid.Vnode = oldDscp->fid.vnode;
3707 oldDirAFSFid.Unique = oldDscp->fid.unique;
3708 newDirAFSFid.Volume = newDscp->fid.volume;
3709 newDirAFSFid.Vnode = newDscp->fid.vnode;
3710 newDirAFSFid.Unique = newDscp->fid.unique;
3712 rxconnp = cm_GetRxConn(connp);
3713 code = RXAFS_Rename(rxconnp, &oldDirAFSFid, oldNamep,
3714 &newDirAFSFid, newNamep,
3715 &updatedOldDirStatus, &updatedNewDirStatus,
3717 rx_PutConnection(rxconnp);
3719 } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
3720 &volSync, NULL, NULL, code));
3721 code = cm_MapRPCError(code, reqp);
3724 osi_Log1(afsd_logp, "CALL Rename FAILURE, code 0x%x", code);
3726 osi_Log0(afsd_logp, "CALL Rename SUCCESS");
3728 /* update the individual stat cache entries for the directories */
3730 lock_ObtainWrite(&oldDirOp.scp->dirlock);
3731 oldDirOp.lockType = CM_DIRLOCK_WRITE;
3733 lock_ObtainWrite(&oldDscp->rw);
3734 cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
3737 cm_MergeStatus(NULL, oldDscp, &updatedOldDirStatus, &volSync,
3738 userp, reqp, CM_MERGEFLAG_DIROP);
3739 lock_ReleaseWrite(&oldDscp->rw);
3741 if (code == 0 && cm_CheckDirOpForSingleChange(&oldDirOp)) {
3743 diropCode = cm_BPlusDirLookup(&oldDirOp, cOldNamep, &fileFid);
3744 if (diropCode == CM_ERROR_INEXACT_MATCH)
3746 else if (diropCode == EINVAL)
3748 diropCode = cm_DirLookup(&oldDirOp, oldNamep, &fileFid);
3750 if (diropCode == 0) {
3752 diropCode = cm_DirCreateEntry(&oldDirOp, newNamep, &fileFid);
3754 cm_BPlusDirCreateEntry(&oldDirOp, cNewNamep, &fileFid);
3758 if (diropCode == 0) {
3759 diropCode = cm_DirDeleteEntry(&oldDirOp, oldNamep);
3761 cm_BPlusDirDeleteEntry(&oldDirOp, cOldNamep);
3766 cm_EndDirOp(&oldDirOp);
3768 /* and update it for the new one, too, if necessary */
3771 lock_ObtainWrite(&newDirOp.scp->dirlock);
3772 newDirOp.lockType = CM_DIRLOCK_WRITE;
3774 lock_ObtainWrite(&newDscp->rw);
3775 cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
3777 cm_MergeStatus(NULL, newDscp, &updatedNewDirStatus, &volSync,
3778 userp, reqp, CM_MERGEFLAG_DIROP);
3779 lock_ReleaseWrite(&newDscp->rw);
3783 * The following optimization does not work.
3784 * When the file server processed a RXAFS_Rename() request the
3785 * FID of the object being moved between directories is not
3786 * preserved. The client does not know the new FID nor the
3787 * version number of the target. Not only can we not create
3788 * the directory entry in the new directory, but we can't
3789 * preserve the cached data for the file. It must be re-read
3790 * from the file server. - jaltman, 2009/02/20
3793 /* we only make the local change if we successfully made
3794 the change in the old directory AND there was only one
3795 change in the new directory */
3796 if (diropCode == 0 && cm_CheckDirOpForSingleChange(&newDirOp)) {
3797 cm_DirCreateEntry(&newDirOp, newNamep, &fileFid);
3799 cm_BPlusDirCreateEntry(&newDirOp, cNewNamep, &fileFid);
3804 cm_EndDirOp(&newDirOp);
3808 * After the rename the file server has invalidated the callbacks
3809 * on the file that was moved nor do we have a directory reference
3812 lock_ObtainWrite(&oldScp->rw);
3813 cm_DiscardSCache(oldScp);
3814 lock_ReleaseWrite(&oldScp->rw);
3818 cm_ReleaseSCache(oldScp);
3825 /* and return error code */
3829 /* Byte range locks:
3831 The OpenAFS Windows client has to fake byte range locks given no
3832 server side support for such locks. This is implemented as keyed
3833 byte range locks on the cache manager.
3835 Keyed byte range locks:
3837 Each cm_scache_t structure keeps track of a list of keyed locks.
3838 The key for a lock identifies an owner of a set of locks (referred
3839 to as a client). Each key is represented by a value. The set of
3840 key values used within a specific cm_scache_t structure form a
3841 namespace that has a scope of just that cm_scache_t structure. The
3842 same key value can be used with another cm_scache_t structure and
3843 correspond to a completely different client. However it is
3844 advantageous for the SMB or IFS layer to make sure that there is a
3845 1-1 mapping between client and keys over all cm_scache_t objects.
3847 Assume a client C has key Key(C) (although, since the scope of the
3848 key is a cm_scache_t, the key can be Key(C,S), where S is the
3849 cm_scache_t. But assume a 1-1 relation between keys and clients).
3850 A byte range (O,+L) denotes byte addresses (O) through (O+L-1)
3851 inclusive (a.k.a. [O,O+L-1]). The function Key(x) is implemented
3852 through cm_generateKey() function for both SMB and IFS.
3854 The list of locks for a cm_scache_t object S is maintained in
3855 S->fileLocks. The cache manager will set a lock on the AFS file
3856 server in order to assert the locks in S->fileLocks. If only
3857 shared locks are in place for S, then the cache manager will obtain
3858 a LockRead lock, while if there are any exclusive locks, it will
3859 obtain a LockWrite lock. If the exclusive locks are all released
3860 while the shared locks remain, then the cache manager will
3861 downgrade the lock from LockWrite to LockRead. Similarly, if an
3862 exclusive lock is obtained when only shared locks exist, then the
3863 cache manager will try to upgrade the lock from LockRead to
3866 Each lock L owned by client C maintains a key L->key such that
3867 L->key == Key(C), the effective range defined by L->LOffset and
3868 L->LLength such that the range of bytes affected by the lock is
3869 (L->LOffset, +L->LLength), a type maintained in L->LockType which
3870 is either exclusive or shared.
3874 A lock exists iff it is in S->fileLocks for some cm_scache_t
3875 S. Existing locks are in one of the following states: ACTIVE,
3876 WAITLOCK, WAITUNLOCK, LOST, DELETED.
3878 The following sections describe each lock and the associated
3881 1. ACTIVE: A lock L is ACTIVE iff the cache manager has asserted
3882 the lock with the AFS file server. This type of lock can be
3883 exercised by a client to read or write to the locked region (as
3886 1.1 ACTIVE->LOST: When the AFS file server fails to extend a
3887 server lock that was required to assert the lock. Before
3888 marking the lock as lost, the cache manager checks if the file
3889 has changed on the server. If the file has not changed, then
3890 the cache manager will attempt to obtain a new server lock
3891 that is sufficient to assert the client side locks for the
3892 file. If any of these fail, the lock is marked as LOST.
3893 Otherwise, it is left as ACTIVE.
3895 1.2 ACTIVE->DELETED: Lock is released.
3897 2. WAITLOCK: A lock is in a WAITLOCK state if the cache manager
3898 grants the lock but the lock is yet to be asserted with the AFS
3899 file server. Once the file server grants the lock, the state
3900 will transition to an ACTIVE lock.
3902 2.1 WAITLOCK->ACTIVE: The server granted the lock.
3904 2.2 WAITLOCK->DELETED: Lock is abandoned, or timed out during
3907 2.3 WAITLOCK->LOST: One or more locks from this client were
3908 marked as LOST. No further locks will be granted to this
3909 client until all lost locks are removed.
3911 3. WAITUNLOCK: A lock is in a WAITUNLOCK state if the cache manager
3912 receives a request for a lock that conflicts with an existing
3913 ACTIVE or WAITLOCK lock. The lock will be placed in the queue
3914 and will be granted at such time the conflicting locks are
3915 removed, at which point the state will transition to either
3918 3.1 WAITUNLOCK->ACTIVE: The conflicting lock was removed. The
3919 current serverLock is sufficient to assert this lock, or a
3920 sufficient serverLock is obtained.
3922 3.2 WAITUNLOCK->WAITLOCK: The conflicting lock was removed,
3923 however the required serverLock is yet to be asserted with the
3926 3.3 WAITUNLOCK->DELETED: The lock is abandoned, timed out or
3929 3.5 WAITUNLOCK->LOST: One or more locks from this client were
3930 marked as LOST. No further locks will be granted to this
3931 client until all lost locks are removed.
3933 4. LOST: A lock L is LOST if the server lock that was required to
3934 assert the lock could not be obtained or if it could not be
3935 extended, or if other locks by the same client were LOST.
3936 Essentially, once a lock is LOST, the contract between the cache
3937 manager and that specific client is no longer valid.
3939 The cache manager rechecks the server lock once every minute and
3940 extends it as appropriate. If this is not done for 5 minutes,
3941 the AFS file server will release the lock (the 5 minute timeout
3942 is based on current file server code and is fairly arbitrary).
3943 Once released, the lock cannot be re-obtained without verifying
3944 that the contents of the file hasn't been modified since the
3945 time the lock was released. Re-obtaining the lock without
3946 verifying this may lead to data corruption. If the lock can not
3947 be obtained safely, then all active locks for the cm_scache_t
3950 4.1 LOST->DELETED: The lock is released.
3952 5. DELETED: The lock is no longer relevant. Eventually, it will
3953 get removed from the cm_scache_t. In the meantime, it will be
3954 treated as if it does not exist.
3956 5.1 DELETED->not exist: The lock is removed from the
3959 The following are classifications of locks based on their state.
3961 6* A lock L is ACCEPTED if it is ACTIVE or WAITLOCK. These locks
3962 have been accepted by the cache manager, but may or may not have
3963 been granted back to the client.
3965 7* A lock L is QUEUED if it is ACTIVE, WAITLOCK or WAITUNLOCK.
3967 8* A lock L is WAITING if it is WAITLOCK or WAITUNLOCK.
3971 A client C can READ range (Offset,+Length) of a file represented by
3972 cm_scache_t S iff (1):
3974 1. for all _a_ in (Offset,+Length), all of the following is true:
3976 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3977 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3980 1.2 For each LOST lock L in S->fileLocks such that _a_ in
3981 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3984 (When locks are lost on an cm_scache_t, all locks are lost. By
3985 4.2 (below), if there is an exclusive LOST lock, then there
3986 can't be any overlapping ACTIVE locks.)
3988 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
3990 2. for all _a_ in (Offset,+Length), one of the following is true:
3992 2.1 Byte _a_ of S is unowned (as specified in 1.1) AND there
3993 does not exist a LOST lock L such that _a_ in
3994 (L->LOffset,+L->LLength).
3996 2.2 Byte _a_ of S is owned by C under lock L (as specified in
3997 1.2) AND L->LockType is exclusive.
3999 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
4001 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
4004 3.1 If L->LockType is exclusive then there does NOT exist a
4005 ACCEPTED lock M in S->fileLocks such that _a_ in
4006 (M->LOffset,+M->LLength).
4008 (If we count all QUEUED locks then we hit cases such as
4009 cascading waiting locks where the locks later on in the queue
4010 can be granted without compromising file integrity. On the
4011 other hand if only ACCEPTED locks are considered, then locks
4012 that were received earlier may end up waiting for locks that
4013 were received later to be unlocked. The choice of ACCEPTED
4014 locks was made to mimic the Windows byte range lock
4017 3.2 If L->LockType is shared then for each ACCEPTED lock M in
4018 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
4019 M->LockType is shared.
4021 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
4023 4.1 M->key != Key(C)
4025 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
4026 and (M->LOffset,+M->LLength) do not intersect.
4028 (Note: If a client loses a lock, it loses all locks.
4029 Subsequently, it will not be allowed to obtain any more locks
4030 until all existing LOST locks that belong to the client are
4031 released. Once all locks are released by a single client,
4032 there exists no further contract between the client and AFS
4033 about the contents of the file, hence the client can then
4034 proceed to obtain new locks and establish a new contract.
4036 This doesn't quite work as you think it should, because most
4037 applications aren't built to deal with losing locks they
4038 thought they once had. For now, we don't have a good
4039 solution to lost locks.
4041 Also, for consistency reasons, we have to hold off on
4042 granting locks that overlap exclusive LOST locks.)
4044 A client C can only unlock locks L in S->fileLocks which have
4047 The representation and invariants are as follows:
4049 - Each cm_scache_t structure keeps:
4051 - A queue of byte-range locks (cm_scache_t::fileLocks) which
4052 are of type cm_file_lock_t.
4054 - A record of the highest server-side lock that has been
4055 obtained for this object (cm_scache_t::serverLock), which is
4056 one of (-1), LockRead, LockWrite.
4058 - A count of ACCEPTED exclusive and shared locks that are in the
4059 queue (cm_scache_t::sharedLocks and
4060 cm_scache_t::exclusiveLocks)
4062 - Each cm_file_lock_t structure keeps:
4064 - The type of lock (cm_file_lock_t::LockType)
4066 - The key associated with the lock (cm_file_lock_t::key)
4068 - The offset and length of the lock (cm_file_lock_t::LOffset
4069 and cm_file_lock_t::LLength)
4071 - The state of the lock.
4073 - Time of issuance or last successful extension
4075 Semantic invariants:
4077 I1. The number of ACCEPTED locks in S->fileLocks are
4078 (S->sharedLocks + S->exclusiveLocks)
4080 External invariants:
4082 I3. S->serverLock is the lock that we have asserted with the
4083 AFS file server for this cm_scache_t.
4085 I4. S->serverLock == LockRead iff there is at least one ACTIVE
4086 shared lock, but no ACTIVE exclusive locks.
4088 I5. S->serverLock == LockWrite iff there is at least one ACTIVE
4091 I6. If L is a LOST lock, then for each lock M in S->fileLocks,
4092 M->key == L->key IMPLIES M is LOST or DELETED.
4097 #define IS_LOCK_ACTIVE(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == 0)
4099 #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)
4101 #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)
4103 #define IS_LOCK_LOST(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_LOST)
4105 #define IS_LOCK_DELETED(lockp) (((lockp)->flags & CM_FILELOCK_FLAG_DELETED) == CM_FILELOCK_FLAG_DELETED)
4108 #define IS_LOCK_ACCEPTED(lockp) (IS_LOCK_ACTIVE(lockp) || IS_LOCK_WAITLOCK(lockp))
4111 #define IS_LOCK_CLIENTONLY(lockp) ((((lockp)->scp->flags & CM_SCACHEFLAG_RO) == CM_SCACHEFLAG_RO) || (((lockp)->flags & CM_FILELOCK_FLAG_CLIENTONLY) == CM_FILELOCK_FLAG_CLIENTONLY))
4114 #define INTERSECT_RANGE(r1,r2) (((r2).offset+(r2).length) > (r1).offset && ((r1).offset +(r1).length) > (r2).offset)
4117 #define CONTAINS_RANGE(r1,r2) (((r2).offset+(r2).length) <= ((r1).offset+(r1).length) && (r1).offset <= (r2).offset)
4119 #if defined(VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS) && !defined(LOCK_TESTING)
4120 #define SCP_SUPPORTS_BRLOCKS(scp) ((scp)->cbServerp && ((scp)->cbServerp->capabilities & VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS))
4122 #define SCP_SUPPORTS_BRLOCKS(scp) (1)
4125 #define SERVERLOCKS_ENABLED(scp) (!((scp)->flags & CM_SCACHEFLAG_RO) && cm_enableServerLocks && SCP_SUPPORTS_BRLOCKS(scp))
4127 #if defined(VICED_CAPABILITY_WRITELOCKACL)
4128 #define SCP_SUPPORTS_WRITELOCKACL(scp) ((scp)->cbServerp && ((scp->cbServerp->capabilities & VICED_CAPABILITY_WRITELOCKACL)))
4130 #define SCP_SUPPORTS_WRITELOCKACL(scp) (0)
4132 /* This should really be defined in any build that this code is being
4134 #error VICED_CAPABILITY_WRITELOCKACL not defined.
4137 static void cm_LockRangeSubtract(cm_range_t * pos, const cm_range_t * neg)
4139 afs_int64 int_begin;
4142 int_begin = MAX(pos->offset, neg->offset);
4143 int_end = MIN(pos->offset+pos->length, neg->offset+neg->length);
4145 if (int_begin < int_end) {
4146 if (int_begin == pos->offset) {
4147 pos->length = pos->offset + pos->length - int_end;
4148 pos->offset = int_end;
4149 } else if (int_end == pos->offset + pos->length) {
4150 pos->length = int_begin - pos->offset;
4153 /* We only subtract ranges if the resulting range is
4154 contiguous. If we try to support non-contigous ranges, we
4155 aren't actually improving performance. */
4159 /* Called with scp->rw held. Returns 0 if all is clear to read the
4160 specified range by the client identified by key.
4162 long cm_LockCheckRead(cm_scache_t *scp,
4163 LARGE_INTEGER LOffset,
4164 LARGE_INTEGER LLength,
4167 #ifndef ADVISORY_LOCKS
4169 cm_file_lock_t *fileLock;
4173 int substract_ranges = FALSE;
4175 range.offset = LOffset.QuadPart;
4176 range.length = LLength.QuadPart;
4180 1. for all _a_ in (Offset,+Length), all of the following is true:
4182 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
4183 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
4186 1.2 For each LOST lock L in S->fileLocks such that _a_ in
4187 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
4192 lock_ObtainRead(&cm_scacheLock);
4194 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
4196 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4198 if (INTERSECT_RANGE(range, fileLock->range)) {
4199 if (IS_LOCK_ACTIVE(fileLock)) {
4200 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4202 /* If there is an active lock for this client, it
4203 is safe to substract ranges.*/
4204 cm_LockRangeSubtract(&range, &fileLock->range);
4205 substract_ranges = TRUE;
4207 if (fileLock->lockType != LockRead) {
4208 code = CM_ERROR_LOCK_CONFLICT;
4212 /* even if the entire range is locked for reading,
4213 we still can't grant the lock at this point
4214 because the client may have lost locks. That
4215 is, unless we have already seen an active lock
4216 belonging to the client, in which case there
4217 can't be any lost locks for this client. */
4218 if (substract_ranges)
4219 cm_LockRangeSubtract(&range, &fileLock->range);
4221 } else if (IS_LOCK_LOST(fileLock) &&
4222 (cm_KeyEquals(&fileLock->key, &key, 0) || fileLock->lockType == LockWrite)) {
4223 code = CM_ERROR_BADFD;
4229 lock_ReleaseRead(&cm_scacheLock);
4231 osi_Log4(afsd_logp, "cm_LockCheckRead scp 0x%x offset %d length %d code 0x%x",
4232 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
4243 /* Called with scp->rw held. Returns 0 if all is clear to write the
4244 specified range by the client identified by key.
4246 long cm_LockCheckWrite(cm_scache_t *scp,
4247 LARGE_INTEGER LOffset,
4248 LARGE_INTEGER LLength,
4251 #ifndef ADVISORY_LOCKS
4253 cm_file_lock_t *fileLock;
4258 range.offset = LOffset.QuadPart;
4259 range.length = LLength.QuadPart;
4262 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
4264 2. for all _a_ in (Offset,+Length), one of the following is true:
4266 2.1 Byte _a_ of S is unowned AND there does not exist a LOST
4267 lock L such that _a_ in (L->LOffset,+L->LLength).
4269 2.2 Byte _a_ of S is owned by C under lock L AND L->LockType is
4273 lock_ObtainRead(&cm_scacheLock);
4275 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
4277 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4279 if (INTERSECT_RANGE(range, fileLock->range)) {
4280 if (IS_LOCK_ACTIVE(fileLock)) {
4281 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4282 if (fileLock->lockType == LockWrite) {
4284 /* if there is an active lock for this client, it
4285 is safe to substract ranges */
4286 cm_LockRangeSubtract(&range, &fileLock->range);
4288 code = CM_ERROR_LOCK_CONFLICT;
4292 code = CM_ERROR_LOCK_CONFLICT;
4295 } else if (IS_LOCK_LOST(fileLock)) {
4296 code = CM_ERROR_BADFD;
4302 lock_ReleaseRead(&cm_scacheLock);
4304 osi_Log4(afsd_logp, "cm_LockCheckWrite scp 0x%x offset %d length %d code 0x%x",
4305 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
4316 /* Called with cm_scacheLock write locked */
4317 static cm_file_lock_t * cm_GetFileLock(void) {
4320 l = (cm_file_lock_t *) cm_freeFileLocks;
4322 osi_QRemove(&cm_freeFileLocks, &l->q);
4324 l = malloc(sizeof(cm_file_lock_t));
4325 osi_assertx(l, "null cm_file_lock_t");
4328 memset(l, 0, sizeof(cm_file_lock_t));
4333 /* Called with cm_scacheLock write locked */
4334 static void cm_PutFileLock(cm_file_lock_t *l) {
4335 osi_QAdd(&cm_freeFileLocks, &l->q);
4338 /* called with scp->rw held. May release it during processing, but
4339 leaves it held on exit. */
4340 long cm_IntSetLock(cm_scache_t * scp, cm_user_t * userp, int lockType,
4346 struct rx_connection * rxconnp;
4348 afs_uint32 reqflags = reqp->flags;
4350 memset(&volSync, 0, sizeof(volSync));
4352 tfid.Volume = scp->fid.volume;
4353 tfid.Vnode = scp->fid.vnode;
4354 tfid.Unique = scp->fid.unique;
4357 osi_Log2(afsd_logp, "CALL SetLock scp 0x%p for lock %d", scp, lockType);
4359 reqp->flags |= CM_REQ_NORETRY;
4360 lock_ReleaseWrite(&scp->rw);
4363 code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4367 rxconnp = cm_GetRxConn(connp);
4368 code = RXAFS_SetLock(rxconnp, &tfid, lockType,
4370 rx_PutConnection(rxconnp);
4372 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
4375 code = cm_MapRPCError(code, reqp);
4377 osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
4379 osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
4382 lock_ObtainWrite(&scp->rw);
4383 reqp->flags = reqflags;
4387 /* called with scp->rw held. Releases it during processing */
4388 long cm_IntReleaseLock(cm_scache_t * scp, cm_user_t * userp,
4394 struct rx_connection * rxconnp;
4397 memset(&volSync, 0, sizeof(volSync));
4399 tfid.Volume = scp->fid.volume;
4400 tfid.Vnode = scp->fid.vnode;
4401 tfid.Unique = scp->fid.unique;
4404 lock_ReleaseWrite(&scp->rw);
4406 osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
4409 code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4413 rxconnp = cm_GetRxConn(connp);
4414 code = RXAFS_ReleaseLock(rxconnp, &tfid, &volSync);
4415 rx_PutConnection(rxconnp);
4417 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
4419 code = cm_MapRPCError(code, reqp);
4422 "CALL ReleaseLock FAILURE, code 0x%x", code);
4425 "CALL ReleaseLock SUCCESS");
4427 lock_ObtainWrite(&scp->rw);
4432 /* called with scp->rw held. May release it during processing, but
4433 will exit with lock held.
4437 - 0 if the user has permission to get the specified lock for the scp
4439 - CM_ERROR_NOACCESS if not
4441 Any other error from cm_SyncOp will be sent down untranslated.
4443 If CM_ERROR_NOACCESS is returned and lock_type is LockRead, then
4444 phas_insert (if non-NULL) will receive a boolean value indicating
4445 whether the user has INSERT permission or not.
4447 long cm_LockCheckPerms(cm_scache_t * scp,
4454 long code = 0, code2 = 0;
4456 /* lock permissions are slightly tricky because of the 'i' bit.
4457 If the user has PRSFS_LOCK, she can read-lock the file. If the
4458 user has PRSFS_WRITE, she can write-lock the file. However, if
4459 the user has PRSFS_INSERT, then she can write-lock new files,
4460 but not old ones. Since we don't have information about
4461 whether a file is new or not, we assume that if the user owns
4462 the scp, then she has the permissions that are granted by
4465 osi_Log3(afsd_logp, "cm_LockCheckPerms for scp[0x%p] type[%d] user[0x%p]",
4466 scp, lock_type, userp);
4468 if (lock_type == LockRead)
4469 rights |= PRSFS_LOCK;
4470 else if (lock_type == LockWrite)
4471 rights |= PRSFS_WRITE | PRSFS_LOCK;
4474 osi_assertx(FALSE, "invalid lock type");
4479 *phas_insert = FALSE;
4481 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
4482 CM_SCACHESYNC_GETSTATUS |
4483 CM_SCACHESYNC_NEEDCALLBACK);
4485 if (phas_insert && scp->creator == userp) {
4487 /* If this file was created by the user, then we check for
4488 PRSFS_INSERT. If the file server is recent enough, then
4489 this should be sufficient for her to get a write-lock (but
4490 not necessarily a read-lock). VICED_CAPABILITY_WRITELOCKACL
4491 indicates whether a file server supports getting write
4492 locks when the user only has PRSFS_INSERT.
4494 If the file was not created by the user we skip the check
4495 because the INSERT bit will not apply to this user even
4499 code2 = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
4500 CM_SCACHESYNC_GETSTATUS |
4501 CM_SCACHESYNC_NEEDCALLBACK);
4503 if (code2 == CM_ERROR_NOACCESS) {
4504 osi_Log0(afsd_logp, "cm_LockCheckPerms user has no INSERT bits");
4506 *phas_insert = TRUE;
4507 osi_Log0(afsd_logp, "cm_LockCheckPerms user has INSERT bits");
4511 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
4513 osi_Log1(afsd_logp, "cm_LockCheckPerms returning code %d", code);
4518 /* called with scp->rw held */
4519 long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
4520 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
4522 int allowWait, cm_user_t *userp, cm_req_t *reqp,
4523 cm_file_lock_t **lockpp)
4526 int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
4527 cm_file_lock_t *fileLock;
4530 int wait_unlock = FALSE;
4531 int force_client_lock = FALSE;
4533 osi_Log4(afsd_logp, "cm_Lock scp 0x%x type 0x%x offset %d length %d",
4534 scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
4535 osi_Log4(afsd_logp, "... allowWait %d key <0x%x, 0x%x, 0x%x>", allowWait,
4536 key.process_id, key.session_id, key.file_id);
4539 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
4541 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
4544 3.1 If L->LockType is exclusive then there does NOT exist a
4545 ACCEPTED lock M in S->fileLocks such that _a_ in
4546 (M->LOffset,+M->LLength).
4548 3.2 If L->LockType is shared then for each ACCEPTED lock M in
4549 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
4550 M->LockType is shared.
4552 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
4554 4.1 M->key != Key(C)
4556 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
4557 and (M->LOffset,+M->LLength) do not intersect.
4560 range.offset = LOffset.QuadPart;
4561 range.length = LLength.QuadPart;
4563 lock_ObtainRead(&cm_scacheLock);
4565 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4567 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4569 if (IS_LOCK_LOST(fileLock)) {
4570 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4571 code = CM_ERROR_BADFD;
4573 } else if (fileLock->lockType == LockWrite && INTERSECT_RANGE(range, fileLock->range)) {
4574 code = CM_ERROR_WOULDBLOCK;
4580 /* we don't need to check for deleted locks here since deleted
4581 locks are dequeued from scp->fileLocks */
4582 if (IS_LOCK_ACCEPTED(fileLock) &&
4583 INTERSECT_RANGE(range, fileLock->range)) {
4585 if ((sLockType & LOCKING_ANDX_SHARED_LOCK) == 0 ||
4586 fileLock->lockType != LockRead) {
4588 code = CM_ERROR_WOULDBLOCK;
4594 lock_ReleaseRead(&cm_scacheLock);
4596 if (code == 0 && SERVERLOCKS_ENABLED(scp)) {
4597 if (Which == scp->serverLock ||
4598 (Which == LockRead && scp->serverLock == LockWrite)) {
4602 /* we already have the lock we need */
4603 osi_Log3(afsd_logp, " we already have the correct lock. exclusives[%d], shared[%d], serverLock[%d]",
4604 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4606 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4608 /* special case: if we don't have permission to read-lock
4609 the file, then we force a clientside lock. This is to
4610 compensate for applications that obtain a read-lock for
4611 reading files off of directories that don't grant
4612 read-locks to the user. */
4613 if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4615 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4616 osi_Log0(afsd_logp, " User has no read-lock perms, but has INSERT perms.");
4619 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4620 force_client_lock = TRUE;
4624 } else if ((scp->exclusiveLocks > 0) ||
4625 (scp->sharedLocks > 0 && scp->serverLock != LockRead)) {
4628 /* We are already waiting for some other lock. We should
4629 wait for the daemon to catch up instead of generating a
4630 flood of SetLock calls. */
4631 osi_Log3(afsd_logp, " already waiting for other lock. exclusives[%d], shared[%d], serverLock[%d]",
4632 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4634 /* see if we have permission to create the lock in the
4636 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4638 code = CM_ERROR_WOULDBLOCK;
4639 else if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4641 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4643 " User has no read-lock perms, but has INSERT perms.");
4644 code = CM_ERROR_WOULDBLOCK;
4647 " User has no read-lock perms. Forcing client-side lock");
4648 force_client_lock = TRUE;
4652 /* leave any other codes as-is */
4656 int check_data_version = FALSE;
4659 /* first check if we have permission to elevate or obtain
4661 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4663 if (code == CM_ERROR_NOACCESS && Which == LockRead &&
4664 (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp))) {
4665 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4666 force_client_lock = TRUE;
4671 /* has_insert => (Which == LockRead, code == CM_ERROR_NOACCESS) */
4673 if (scp->serverLock == LockRead && Which == LockWrite) {
4675 /* We want to escalate the lock to a LockWrite.
4676 * Unfortunately that's not really possible without
4677 * letting go of the current lock. But for now we do
4681 " attempting to UPGRADE from LockRead to LockWrite.");
4683 " dataVersion on scp: %I64d", scp->dataVersion);
4685 /* we assume at this point (because scp->serverLock
4686 was valid) that we had a valid server lock. */
4687 scp->lockDataVersion = scp->dataVersion;
4688 check_data_version = TRUE;
4690 code = cm_IntReleaseLock(scp, userp, reqp);
4693 /* We couldn't release the lock */
4696 scp->serverLock = -1;
4700 /* We need to obtain a server lock of type Which in order
4701 * to assert this file lock */
4702 #ifndef AGGRESSIVE_LOCKS
4705 newLock = LockWrite;
4708 code = cm_IntSetLock(scp, userp, newLock, reqp);
4710 #ifdef AGGRESSIVE_LOCKS
4711 if ((code == CM_ERROR_WOULDBLOCK ||
4712 code == CM_ERROR_NOACCESS) && newLock != Which) {
4713 /* we wanted LockRead. We tried LockWrite. Now try
4718 osi_assertx(newLock == LockRead, "lock type not read");
4720 code = cm_IntSetLock(scp, userp, newLock, reqp);
4724 if (code == CM_ERROR_NOACCESS) {
4725 if (Which == LockRead) {
4726 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4728 /* We requested a read-lock, but we have permission to
4729 * get a write-lock. Try that */
4731 tcode = cm_LockCheckPerms(scp, LockWrite, userp, reqp, NULL);
4734 newLock = LockWrite;
4736 osi_Log0(afsd_logp, " User has 'i' perms and the request was for a LockRead. Trying to get a LockWrite instead");
4738 code = cm_IntSetLock(scp, userp, newLock, reqp);
4741 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4742 force_client_lock = TRUE;
4744 } else if (Which == LockWrite &&
4745 scp->creator == userp && !SCP_SUPPORTS_WRITELOCKACL(scp)) {
4748 /* Special case: if the lock request was for a
4749 * LockWrite and the user owns the file and we weren't
4750 * allowed to obtain the serverlock, we either lost a
4751 * race (the permissions changed from under us), or we
4752 * have 'i' bits, but we aren't allowed to lock the
4755 /* check if we lost a race... */
4756 tcode = cm_LockCheckPerms(scp, Which, userp, reqp, NULL);
4759 osi_Log0(afsd_logp, " User has 'i' perms but can't obtain write locks. Using client-side locks.");
4760 force_client_lock = TRUE;
4765 if (code == 0 && check_data_version &&
4766 scp->dataVersion != scp->lockDataVersion) {
4767 /* We lost a race. Although we successfully obtained
4768 * a lock, someone modified the file in between. The
4769 * locks have all been technically lost. */
4772 " Data version mismatch while upgrading lock.");
4774 " Data versions before=%I64d, after=%I64d",
4775 scp->lockDataVersion,
4778 " Releasing stale lock for scp 0x%x", scp);
4780 code = cm_IntReleaseLock(scp, userp, reqp);
4782 scp->serverLock = -1;
4784 code = CM_ERROR_INVAL;
4785 } else if (code == 0) {
4786 scp->serverLock = newLock;
4787 scp->lockDataVersion = scp->dataVersion;
4791 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4792 scp->serverLock == -1) {
4793 /* Oops. We lost the lock. */
4794 cm_LockMarkSCacheLost(scp);
4797 } else if (code == 0) { /* server locks not enabled */
4799 " Skipping server lock for scp");
4804 if (code != 0 && !force_client_lock) {
4805 /* Special case error translations
4807 Applications don't expect certain errors from a
4808 LockFile/UnlockFile call. We need to translate some error
4809 code to codes that apps expect and handle. */
4811 /* We shouldn't actually need to handle this case since we
4812 simulate locks for RO scps anyway. */
4813 if (code == CM_ERROR_READONLY) {
4814 osi_Log0(afsd_logp, " Reinterpreting CM_ERROR_READONLY as CM_ERROR_NOACCESS");
4815 code = CM_ERROR_NOACCESS;
4819 if (code == 0 || (code == CM_ERROR_WOULDBLOCK && allowWait) ||
4820 force_client_lock) {
4822 /* clear the error if we are forcing a client lock, so we
4823 don't get confused later. */
4824 if (force_client_lock && code != CM_ERROR_WOULDBLOCK)
4827 lock_ObtainWrite(&cm_scacheLock);
4828 fileLock = cm_GetFileLock();
4829 lock_ReleaseWrite(&cm_scacheLock);
4831 fileLock->fid = scp->fid;
4833 fileLock->key = key;
4834 fileLock->lockType = Which;
4836 fileLock->userp = userp;
4837 fileLock->range = range;
4838 fileLock->flags = (code == 0 ? 0 :
4840 CM_FILELOCK_FLAG_WAITUNLOCK :
4841 CM_FILELOCK_FLAG_WAITLOCK));
4843 if (force_client_lock || !SERVERLOCKS_ENABLED(scp))
4844 fileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
4846 fileLock->lastUpdate = (code == 0 && !force_client_lock) ? time(NULL) : 0;
4848 lock_ObtainWrite(&cm_scacheLock);
4849 osi_QAddT(&scp->fileLocksH, &scp->fileLocksT, &fileLock->fileq);
4850 cm_HoldSCacheNoLock(scp);
4851 fileLock->scp = scp;
4852 osi_QAdd(&cm_allFileLocks, &fileLock->q);
4853 lock_ReleaseWrite(&cm_scacheLock);
4859 if (IS_LOCK_CLIENTONLY(fileLock)) {
4861 } else if (IS_LOCK_ACCEPTED(fileLock)) {
4862 if (Which == LockRead)
4865 scp->exclusiveLocks++;
4869 "cm_Lock Lock added 0x%p flags 0x%x to scp [0x%p]",
4870 fileLock, fileLock->flags, scp);
4872 " exclusives[%d] shared[%d] client[%d] serverLock[%d]",
4873 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4874 (int)(signed char) scp->serverLock);
4877 "cm_Lock Rejecting lock (code = 0x%x)", code);
4880 /* Convert from would block to lock not granted */
4881 if (code == CM_ERROR_WOULDBLOCK)
4882 code = CM_ERROR_LOCK_NOT_GRANTED;
4887 /* Called with scp->rw held */
4888 long cm_UnlockByKey(cm_scache_t * scp,
4895 cm_file_lock_t *fileLock;
4896 osi_queue_t *q, *qn;
4899 osi_Log4(afsd_logp, "cm_UnlockByKey scp 0x%p key <0x%x,0x%x,0x%x",
4900 scp, key.process_id, key.session_id, key.file_id);
4901 osi_Log1(afsd_logp, " flags=0x%x", flags);
4903 lock_ObtainWrite(&cm_scacheLock);
4905 for (q = scp->fileLocksH; q; q = qn) {
4908 fileLock = (cm_file_lock_t *)
4909 ((char *) q - offsetof(cm_file_lock_t, fileq));
4912 osi_Log4(afsd_logp, " Checking lock[0x%x] range[%d,+%d] type[%d]",
4914 (unsigned long) fileLock->range.offset,
4915 (unsigned long) fileLock->range.length,
4916 fileLock->lockType);
4917 osi_Log4(afsd_logp, " key<0x%x, 0x%x, 0x%x> flags[0x%x]",
4918 fileLock->key.process_id, fileLock->key.session_id, fileLock->key.file_id,
4921 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
4922 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
4923 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4925 fileLock->fid.volume,
4926 fileLock->fid.vnode,
4927 fileLock->fid.unique);
4928 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4929 fileLock->scp->fid.cell,
4930 fileLock->scp->fid.volume,
4931 fileLock->scp->fid.vnode,
4932 fileLock->scp->fid.unique);
4933 osi_assertx(FALSE, "invalid fid value");
4937 if (!IS_LOCK_DELETED(fileLock) &&
4938 cm_KeyEquals(&fileLock->key, &key, flags)) {
4939 osi_Log3(afsd_logp, "...Unlock range [%d,+%d] type %d",
4940 fileLock->range.offset,
4941 fileLock->range.length,
4942 fileLock->lockType);
4944 if (scp->fileLocksT == q)
4945 scp->fileLocksT = osi_QPrev(q);
4946 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
4948 if (IS_LOCK_CLIENTONLY(fileLock)) {
4950 } else if (IS_LOCK_ACCEPTED(fileLock)) {
4951 if (fileLock->lockType == LockRead)
4954 scp->exclusiveLocks--;
4957 fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
4959 cm_ReleaseUser(fileLock->userp);
4960 cm_ReleaseSCacheNoLock(scp);
4962 fileLock->userp = NULL;
4963 fileLock->scp = NULL;
4969 lock_ReleaseWrite(&cm_scacheLock);
4971 if (n_unlocks == 0) {
4972 osi_Log0(afsd_logp, "cm_UnlockByKey no locks found");
4973 osi_Log3(afsd_logp, " Leaving scp with exclusives[%d], shared[%d], serverLock[%d]",
4974 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4979 osi_Log1(afsd_logp, "cm_UnlockByKey done with %d locks", n_unlocks);
4981 osi_assertx(scp->sharedLocks >= 0, "scp->sharedLocks < 0");
4982 osi_assertx(scp->exclusiveLocks >= 0, "scp->exclusiveLocks < 0");
4983 osi_assertx(scp->clientLocks >= 0, "scp->clientLocks < 0");
4985 if (!SERVERLOCKS_ENABLED(scp)) {
4986 osi_Log0(afsd_logp, " Skipping server lock for scp");
4990 /* Ideally we would go through the rest of the locks to determine
4991 * if one or more locks that were formerly in WAITUNLOCK can now
4992 * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
4993 * scp->sharedLocks accordingly. However, the retrying of locks
4994 * in that manner is done cm_RetryLock() manually.
4997 if (scp->serverLock == LockWrite &&
4998 scp->exclusiveLocks == 0 &&
4999 scp->sharedLocks > 0) {
5000 /* The serverLock should be downgraded to LockRead */
5001 osi_Log0(afsd_logp, " DOWNGRADE lock from LockWrite to LockRead");
5003 /* Make sure there are no dirty buffers left. */
5004 code = cm_FSync(scp, userp, reqp, TRUE);
5006 /* since scp->serverLock looked sane, we are going to assume
5007 that we have a valid server lock. */
5008 scp->lockDataVersion = scp->dataVersion;
5009 osi_Log1(afsd_logp, " dataVersion on scp = %I64d", scp->dataVersion);
5011 code = cm_IntReleaseLock(scp, userp, reqp);
5014 /* so we couldn't release it. Just let the lock be for now */
5018 scp->serverLock = -1;
5021 code = cm_IntSetLock(scp, userp, LockRead, reqp);
5023 if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
5024 scp->serverLock = LockRead;
5025 } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
5026 /* We lost a race condition. Although we have a valid
5027 lock on the file, the data has changed and essentially
5028 we have lost the lock we had during the transition. */
5030 osi_Log0(afsd_logp, "Data version mismatch during lock downgrade");
5031 osi_Log2(afsd_logp, " Data versions before=%I64d, after=%I64d",
5032 scp->lockDataVersion,
5035 code = cm_IntReleaseLock(scp, userp, reqp);
5037 code = CM_ERROR_INVAL;
5038 scp->serverLock = -1;
5042 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
5043 (scp->serverLock == -1)) {
5045 cm_LockMarkSCacheLost(scp);
5048 /* failure here has no bearing on the return value of
5052 } else if (scp->serverLock != (-1) &&
5053 scp->exclusiveLocks == 0 &&
5054 scp->sharedLocks == 0) {
5055 /* The serverLock should be released entirely */
5057 if (scp->serverLock == LockWrite) {
5058 /* Make sure there are no dirty buffers left. */
5059 code = cm_FSync(scp, userp, reqp, TRUE);
5062 code = cm_IntReleaseLock(scp, userp, reqp);
5065 scp->serverLock = (-1);
5070 osi_Log1(afsd_logp, "cm_UnlockByKey code 0x%x", code);
5071 osi_Log4(afsd_logp, " Leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
5072 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
5073 (int)(signed char) scp->serverLock);
5078 long cm_Unlock(cm_scache_t *scp,
5079 unsigned char sLockType,
5080 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
5087 int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
5088 cm_file_lock_t *fileLock;
5090 int release_userp = FALSE;
5091 int exact_match = !(flags & CM_UNLOCK_FLAG_MATCH_RANGE);
5093 LARGE_INTEGER RangeEnd;
5095 osi_Log4(afsd_logp, "cm_Unlock scp 0x%p type 0x%x offset %d length %d",
5096 scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
5097 osi_Log4(afsd_logp, "... key <0x%x,0x%x,0x%x> flags 0x%x",
5098 key.process_id, key.session_id, key.file_id, flags);
5101 RangeEnd.QuadPart = LOffset.QuadPart + LLength.QuadPart;
5104 lock_ObtainRead(&cm_scacheLock);
5106 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5107 fileLock = (cm_file_lock_t *)
5108 ((char *) q - offsetof(cm_file_lock_t, fileq));
5111 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5112 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5113 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5115 fileLock->fid.volume,
5116 fileLock->fid.vnode,
5117 fileLock->fid.unique);
5118 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5119 fileLock->scp->fid.cell,
5120 fileLock->scp->fid.volume,
5121 fileLock->scp->fid.vnode,
5122 fileLock->scp->fid.unique);
5123 osi_assertx(FALSE, "invalid fid value");
5127 if (!IS_LOCK_DELETED(fileLock) &&
5128 cm_KeyEquals(&fileLock->key, &key, 0) &&
5129 fileLock->range.offset == LOffset.QuadPart &&
5130 fileLock->range.length == LLength.QuadPart) {
5136 if (!IS_LOCK_DELETED(fileLock) &&
5137 cm_KeyEquals(&fileLock->key, &key, 0) &&
5138 fileLock->range.offset >= LOffset.QuadPart &&
5139 fileLock->range.offset < RangeEnd.QuadPart &&
5140 (fileLock->range.offset + fileLock->range.length) <= RangeEnd.QuadPart) {
5148 lock_ReleaseRead(&cm_scacheLock);
5150 if (lock_found && !exact_match) {
5154 osi_Log0(afsd_logp, "cm_Unlock lock not found; failure");
5156 /* The lock didn't exist anyway. *shrug* */
5157 return CM_ERROR_RANGE_NOT_LOCKED;
5161 /* discard lock record */
5162 lock_ConvertRToW(&cm_scacheLock);
5163 if (scp->fileLocksT == q)
5164 scp->fileLocksT = osi_QPrev(q);
5165 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
5168 * Don't delete it here; let the daemon delete it, to simplify
5169 * the daemon's traversal of the list.
5172 if (IS_LOCK_CLIENTONLY(fileLock)) {
5174 } else if (IS_LOCK_ACCEPTED(fileLock)) {
5175 if (fileLock->lockType == LockRead)
5178 scp->exclusiveLocks--;
5181 fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5182 if (userp != NULL) {
5183 cm_ReleaseUser(fileLock->userp);
5185 userp = fileLock->userp;
5186 release_userp = TRUE;
5188 fileLock->userp = NULL;
5189 cm_ReleaseSCacheNoLock(scp);
5190 fileLock->scp = NULL;
5191 lock_ReleaseWrite(&cm_scacheLock);
5193 if (!SERVERLOCKS_ENABLED(scp)) {
5194 osi_Log0(afsd_logp, " Skipping server locks for scp");
5198 /* Ideally we would go through the rest of the locks to determine
5199 * if one or more locks that were formerly in WAITUNLOCK can now
5200 * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
5201 * scp->sharedLocks accordingly. However, the retrying of locks
5202 * in that manner is done cm_RetryLock() manually.
5205 if (scp->serverLock == LockWrite &&
5206 scp->exclusiveLocks == 0 &&
5207 scp->sharedLocks > 0) {
5209 /* The serverLock should be downgraded to LockRead */
5210 osi_Log0(afsd_logp, " DOWNGRADE lock from LockWrite to LockRead");
5212 /* Make sure there are no dirty buffers left. */
5213 code = cm_FSync(scp, userp, reqp, TRUE);
5215 /* Since we already had a lock, we assume that there is a
5216 valid server lock. */
5217 scp->lockDataVersion = scp->dataVersion;
5218 osi_Log1(afsd_logp, " dataVersion on scp is %I64d", scp->dataVersion);
5220 /* before we downgrade, make sure that we have enough
5221 permissions to get the read lock. */
5222 code = cm_LockCheckPerms(scp, LockRead, userp, reqp, NULL);
5225 osi_Log0(afsd_logp, " SKIPPING downgrade because user doesn't have perms to get downgraded lock");
5231 code = cm_IntReleaseLock(scp, userp, reqp);
5234 /* so we couldn't release it. Just let the lock be for now */
5238 scp->serverLock = -1;
5241 code = cm_IntSetLock(scp, userp, LockRead, reqp);
5243 if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
5244 scp->serverLock = LockRead;
5245 } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
5246 /* Lost a race. We obtained a new lock, but that is
5247 meaningless since someone modified the file
5251 "Data version mismatch while downgrading lock");
5253 " Data versions before=%I64d, after=%I64d",
5254 scp->lockDataVersion,
5257 code = cm_IntReleaseLock(scp, userp, reqp);
5259 scp->serverLock = -1;
5260 code = CM_ERROR_INVAL;
5264 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
5265 (scp->serverLock == -1)) {
5267 cm_LockMarkSCacheLost(scp);
5270 /* failure here has no bearing on the return value of
5274 } else if (scp->serverLock != (-1) &&
5275 scp->exclusiveLocks == 0 &&
5276 scp->sharedLocks == 0) {
5277 /* The serverLock should be released entirely */
5279 if (scp->serverLock == LockWrite) {
5280 /* Make sure there are no dirty buffers left. */
5281 code = cm_FSync(scp, userp, reqp, TRUE);
5284 code = cm_IntReleaseLock(scp, userp, reqp);
5287 scp->serverLock = (-1);
5291 if (release_userp) {
5292 cm_ReleaseUser(userp);
5293 release_userp = FALSE;
5297 goto try_again; /* might be more than one lock in the range */
5301 osi_Log1(afsd_logp, "cm_Unlock code 0x%x", code);
5302 osi_Log4(afsd_logp, " leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
5303 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
5304 (int)(signed char) scp->serverLock);
5309 /* called with scp->rw held */
5310 void cm_LockMarkSCacheLost(cm_scache_t * scp)
5312 cm_file_lock_t *fileLock;
5315 osi_Log1(afsd_logp, "cm_LockMarkSCacheLost scp 0x%x", scp);
5317 /* cm_scacheLock needed because we are modifying fileLock->flags */
5318 lock_ObtainWrite(&cm_scacheLock);
5320 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5322 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
5324 if (IS_LOCK_ACTIVE(fileLock) &&
5325 !IS_LOCK_CLIENTONLY(fileLock)) {
5326 if (fileLock->lockType == LockRead)
5329 scp->exclusiveLocks--;
5331 fileLock->flags |= CM_FILELOCK_FLAG_LOST;
5335 scp->serverLock = -1;
5336 scp->lockDataVersion = CM_SCACHE_VERSION_BAD;
5337 lock_ReleaseWrite(&cm_scacheLock);
5340 /* Called with no relevant locks held */
5341 void cm_CheckLocks()
5343 osi_queue_t *q, *nq;
5344 cm_file_lock_t *fileLock;
5350 struct rx_connection * rxconnp;
5353 memset(&volSync, 0, sizeof(volSync));
5357 lock_ObtainWrite(&cm_scacheLock);
5359 cm_lockRefreshCycle++;
5361 osi_Log1(afsd_logp, "cm_CheckLocks starting lock check cycle %d", cm_lockRefreshCycle);
5363 for (q = cm_allFileLocks; q; q = nq) {
5364 fileLock = (cm_file_lock_t *) q;
5368 if (IS_LOCK_DELETED(fileLock)) {
5370 osi_QRemove(&cm_allFileLocks, q);
5371 cm_PutFileLock(fileLock);
5373 } else if (IS_LOCK_ACTIVE(fileLock) && !IS_LOCK_CLIENTONLY(fileLock)) {
5375 /* Server locks must have been enabled for us to have
5376 received an active non-client-only lock. */
5377 osi_assertx(cm_enableServerLocks, "!cm_enableServerLocks");
5379 scp = fileLock->scp;
5380 osi_assertx(scp != NULL, "null cm_scache_t");
5382 cm_HoldSCacheNoLock(scp);
5385 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5386 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5387 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5389 fileLock->fid.volume,
5390 fileLock->fid.vnode,
5391 fileLock->fid.unique);
5392 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5393 fileLock->scp->fid.cell,
5394 fileLock->scp->fid.volume,
5395 fileLock->scp->fid.vnode,
5396 fileLock->scp->fid.unique);
5397 osi_assertx(FALSE, "invalid fid");
5400 /* Server locks are extended once per scp per refresh
5402 if (scp->lastRefreshCycle != cm_lockRefreshCycle) {
5404 int scp_done = FALSE;
5406 osi_Log1(afsd_logp, "cm_CheckLocks Updating scp 0x%x", scp);
5408 lock_ReleaseWrite(&cm_scacheLock);
5409 lock_ObtainWrite(&scp->rw);
5411 /* did the lock change while we weren't holding the lock? */
5412 if (!IS_LOCK_ACTIVE(fileLock))
5413 goto post_syncopdone;
5415 code = cm_SyncOp(scp, NULL, fileLock->userp, &req, 0,
5416 CM_SCACHESYNC_NEEDCALLBACK
5417 | CM_SCACHESYNC_GETSTATUS
5418 | CM_SCACHESYNC_LOCK);
5422 "cm_CheckLocks SyncOp failure code 0x%x", code);
5423 goto post_syncopdone;
5426 /* cm_SyncOp releases scp->rw during which the lock
5427 may get released. */
5428 if (!IS_LOCK_ACTIVE(fileLock))
5429 goto pre_syncopdone;
5431 if (scp->serverLock != -1 && !(scp->flags & CM_SCACHEFLAG_DELETED)) {
5435 tfid.Volume = scp->fid.volume;
5436 tfid.Vnode = scp->fid.vnode;
5437 tfid.Unique = scp->fid.unique;
5439 userp = fileLock->userp;
5441 osi_Log3(afsd_logp, "CALL ExtendLock lock 0x%p for scp=0x%p with lock %d",
5444 (int) scp->serverLock);
5446 lock_ReleaseWrite(&scp->rw);
5449 code = cm_ConnFromFID(&cfid, userp,
5454 rxconnp = cm_GetRxConn(connp);
5455 code = RXAFS_ExtendLock(rxconnp, &tfid,
5457 rx_PutConnection(rxconnp);
5459 osi_Log1(afsd_logp, " ExtendLock returns %d", code);
5461 } while (cm_Analyze(connp, userp, &req,
5462 &cfid, &volSync, NULL, NULL,
5465 code = cm_MapRPCError(code, &req);
5467 lock_ObtainWrite(&scp->rw);
5470 osi_Log1(afsd_logp, "CALL ExtendLock FAILURE, code 0x%x", code);
5472 osi_Log0(afsd_logp, "CALL ExtendLock SUCCESS");
5473 scp->lockDataVersion = scp->dataVersion;
5476 if ((code == EINVAL || code == CM_ERROR_INVAL) &&
5477 scp->lockDataVersion == scp->dataVersion) {
5481 (scp->exclusiveLocks > 0) ? LockWrite: LockRead;
5483 /* we might still have a chance to obtain a
5486 code = cm_IntSetLock(scp, userp, lockType, &req);
5489 code = CM_ERROR_INVAL;
5490 } else if (scp->lockDataVersion != scp->dataVersion) {
5492 /* now check if we still have the file at
5493 the right data version. */
5495 "Data version mismatch on scp 0x%p",
5498 " Data versions: before=%I64d, after=%I64d",
5499 scp->lockDataVersion,
5502 code = cm_IntReleaseLock(scp, userp, &req);
5504 code = CM_ERROR_INVAL;
5508 if (code == EINVAL || code == CM_ERROR_INVAL ||
5509 code == CM_ERROR_BADFD) {
5510 cm_LockMarkSCacheLost(scp);
5514 /* interestingly, we have found an active lock
5515 belonging to an scache that has no
5517 cm_LockMarkSCacheLost(scp);
5524 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5527 lock_ReleaseWrite(&scp->rw);
5529 lock_ObtainWrite(&cm_scacheLock);
5532 fileLock->lastUpdate = time(NULL);
5536 scp->lastRefreshCycle = cm_lockRefreshCycle;
5539 /* we have already refreshed the locks on this scp */
5540 fileLock->lastUpdate = time(NULL);
5543 cm_ReleaseSCacheNoLock(scp);
5545 } else if (IS_LOCK_ACTIVE(fileLock) && IS_LOCK_CLIENTONLY(fileLock)) {
5546 /* TODO: Check callbacks */
5550 lock_ReleaseWrite(&cm_scacheLock);
5551 osi_Log1(afsd_logp, "cm_CheckLocks completes lock check cycle %d", cm_lockRefreshCycle);
5554 /* NOT called with scp->rw held. */
5555 long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
5558 cm_scache_t *scp = NULL;
5559 cm_file_lock_t *fileLock;
5563 int force_client_lock = FALSE;
5564 int has_insert = FALSE;
5565 int check_data_version = FALSE;
5569 if (client_is_dead) {
5570 code = CM_ERROR_TIMEDOUT;
5574 lock_ObtainRead(&cm_scacheLock);
5576 osi_Log2(afsd_logp, "cm_RetryLock checking lock %p (scp=%p)", oldFileLock, oldFileLock->scp);
5577 osi_Log4(afsd_logp, " offset(%x:%x) length(%x:%x)",
5578 (unsigned)(oldFileLock->range.offset >> 32),
5579 (unsigned)(oldFileLock->range.offset & 0xffffffff),
5580 (unsigned)(oldFileLock->range.length >> 32),
5581 (unsigned)(oldFileLock->range.length & 0xffffffff));
5582 osi_Log4(afsd_logp, " key<0x%x,0x%x,0x%x> flags=%x",
5583 oldFileLock->key.process_id, oldFileLock->key.session_id, oldFileLock->key.file_id,
5584 (unsigned)(oldFileLock->flags));
5586 /* if the lock has already been granted, then we have nothing to do */
5587 if (IS_LOCK_ACTIVE(oldFileLock)) {
5588 lock_ReleaseRead(&cm_scacheLock);
5589 osi_Log0(afsd_logp, "cm_RetryLock lock already granted");
5593 /* we can't do anything with lost or deleted locks at the moment. */
5594 if (IS_LOCK_LOST(oldFileLock) || IS_LOCK_DELETED(oldFileLock)) {
5595 code = CM_ERROR_BADFD;
5596 osi_Log0(afsd_logp, "cm_RetryLock lock is lost or deleted");
5597 lock_ReleaseRead(&cm_scacheLock);
5601 scp = oldFileLock->scp;
5603 osi_assertx(scp != NULL, "null cm_scache_t");
5605 lock_ReleaseRead(&cm_scacheLock);
5606 lock_ObtainWrite(&scp->rw);
5608 code = cm_LockCheckPerms(scp, oldFileLock->lockType,
5612 if (code == CM_ERROR_NOACCESS && oldFileLock->lockType == LockRead) {
5613 if (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp)) {
5614 force_client_lock = TRUE;
5618 lock_ReleaseWrite(&scp->rw);
5622 lock_ObtainWrite(&cm_scacheLock);
5624 /* Check if we already have a sufficient server lock to allow this
5625 lock to go through. */
5626 if (IS_LOCK_WAITLOCK(oldFileLock) &&
5627 (!SERVERLOCKS_ENABLED(scp) ||
5628 scp->serverLock == oldFileLock->lockType ||
5629 scp->serverLock == LockWrite)) {
5631 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5633 if (SERVERLOCKS_ENABLED(scp)) {
5634 osi_Log1(afsd_logp, "cm_RetryLock Server lock (%d) is sufficient for lock. Granting",
5635 (int) scp->serverLock);
5637 osi_Log0(afsd_logp, "cm_RetryLock skipping server lock for scp");
5640 lock_ReleaseWrite(&cm_scacheLock);
5641 lock_ReleaseWrite(&scp->rw);
5646 if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5648 /* check if the conflicting locks have dissappeared already */
5649 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5651 fileLock = (cm_file_lock_t *)
5652 ((char *) q - offsetof(cm_file_lock_t, fileq));
5654 if (IS_LOCK_LOST(fileLock)) {
5655 if (cm_KeyEquals(&fileLock->key, &oldFileLock->key, 0)) {
5656 code = CM_ERROR_BADFD;
5657 oldFileLock->flags |= CM_FILELOCK_FLAG_LOST;
5658 osi_Log1(afsd_logp, " found lost lock %p for same key. Marking lock as lost",
5661 } else if (fileLock->lockType == LockWrite &&
5662 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5663 osi_Log1(afsd_logp, " found conflicting LOST lock %p", fileLock);
5664 code = CM_ERROR_WOULDBLOCK;
5669 if (IS_LOCK_ACCEPTED(fileLock) &&
5670 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5672 if (oldFileLock->lockType != LockRead ||
5673 fileLock->lockType != LockRead) {
5675 osi_Log1(afsd_logp, " found conflicting lock %p", fileLock);
5676 code = CM_ERROR_WOULDBLOCK;
5684 lock_ReleaseWrite(&cm_scacheLock);
5685 lock_ReleaseWrite(&scp->rw);
5690 /* when we get here, the lock is either a WAITUNLOCK or WAITLOCK.
5691 If it is WAITUNLOCK, then we didn't find any conflicting lock
5692 but we haven't verfied whether the serverLock is sufficient to
5693 assert it. If it is WAITLOCK, then the serverLock is
5694 insufficient to assert it. Eitherway, we are ready to accept
5695 the lock as either ACTIVE or WAITLOCK depending on the
5698 /* First, promote the WAITUNLOCK to a WAITLOCK */
5699 if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5700 if (oldFileLock->lockType == LockRead)
5703 scp->exclusiveLocks++;
5705 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITUNLOCK;
5706 oldFileLock->flags |= CM_FILELOCK_FLAG_WAITLOCK;
5709 osi_assertx(IS_LOCK_WAITLOCK(oldFileLock), "!IS_LOCK_WAITLOCK");
5711 if (force_client_lock ||
5712 !SERVERLOCKS_ENABLED(scp) ||
5713 scp->serverLock == oldFileLock->lockType ||
5714 (oldFileLock->lockType == LockRead &&
5715 scp->serverLock == LockWrite)) {
5717 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5719 if ((force_client_lock ||
5720 !SERVERLOCKS_ENABLED(scp)) &&
5721 !IS_LOCK_CLIENTONLY(oldFileLock)) {
5723 oldFileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
5725 if (oldFileLock->lockType == LockRead)
5728 scp->exclusiveLocks--;
5733 lock_ReleaseWrite(&cm_scacheLock);
5734 lock_ReleaseWrite(&scp->rw);
5741 code = cm_SyncOp(scp, NULL, oldFileLock->userp, &req, 0,
5742 CM_SCACHESYNC_NEEDCALLBACK
5743 | CM_SCACHESYNC_GETSTATUS
5744 | CM_SCACHESYNC_LOCK);
5746 osi_Log1(afsd_logp, "cm_RetryLock SyncOp failure code 0x%x", code);
5747 lock_ReleaseWrite(&cm_scacheLock);
5748 goto post_syncopdone;
5751 if (!IS_LOCK_WAITLOCK(oldFileLock))
5752 goto pre_syncopdone;
5754 userp = oldFileLock->userp;
5756 #ifndef AGGRESSIVE_LOCKS
5757 newLock = oldFileLock->lockType;
5759 newLock = LockWrite;
5763 /* if has_insert is non-zero, then:
5764 - the lock a LockRead
5765 - we don't have permission to get a LockRead
5766 - we do have permission to get a LockWrite
5767 - the server supports VICED_CAPABILITY_WRITELOCKACL
5770 newLock = LockWrite;
5773 lock_ReleaseWrite(&cm_scacheLock);
5775 /* when we get here, either we have a read-lock and want a
5776 write-lock or we don't have any locks and we want some
5779 if (scp->serverLock == LockRead) {
5781 osi_assertx(newLock == LockWrite, "!LockWrite");
5783 osi_Log0(afsd_logp, " Attempting to UPGRADE from LockRead to LockWrite");
5785 scp->lockDataVersion = scp->dataVersion;
5786 check_data_version = TRUE;
5788 code = cm_IntReleaseLock(scp, userp, &req);
5791 goto pre_syncopdone;
5793 scp->serverLock = -1;
5796 code = cm_IntSetLock(scp, userp, newLock, &req);
5799 if (scp->dataVersion != scp->lockDataVersion) {
5800 /* we lost a race. too bad */
5803 " Data version mismatch while upgrading lock.");
5805 " Data versions before=%I64d, after=%I64d",
5806 scp->lockDataVersion,
5809 " Releasing stale lock for scp 0x%x", scp);
5811 code = cm_IntReleaseLock(scp, userp, &req);
5813 scp->serverLock = -1;
5815 code = CM_ERROR_INVAL;
5817 cm_LockMarkSCacheLost(scp);
5819 scp->serverLock = newLock;
5824 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5830 if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
5831 lock_ObtainWrite(&cm_scacheLock);
5832 if (scp->fileLocksT == &oldFileLock->fileq)
5833 scp->fileLocksT = osi_QPrev(&oldFileLock->fileq);
5834 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, &oldFileLock->fileq);
5835 lock_ReleaseWrite(&cm_scacheLock);
5837 lock_ReleaseWrite(&scp->rw);
5840 lock_ObtainWrite(&cm_scacheLock);
5842 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5843 } else if (code != CM_ERROR_WOULDBLOCK) {
5844 oldFileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5845 cm_ReleaseUser(oldFileLock->userp);
5846 oldFileLock->userp = NULL;
5847 if (oldFileLock->scp) {
5848 cm_ReleaseSCacheNoLock(oldFileLock->scp);
5849 oldFileLock->scp = NULL;
5852 lock_ReleaseWrite(&cm_scacheLock);
5857 cm_key_t cm_GenerateKey(afs_uint16 session_id, afs_offs_t process_id, afs_uint16 file_id)
5861 key.process_id = process_id;
5862 key.session_id = session_id;
5863 key.file_id = file_id;
5868 int cm_KeyEquals(cm_key_t *k1, cm_key_t *k2, int flags)
5870 return (k1->session_id == k2->session_id) && (k1->file_id == k2->file_id) &&
5871 ((flags & CM_UNLOCK_BY_FID) || (k1->process_id == k2->process_id));
5874 void cm_ReleaseAllLocks(void)
5880 cm_file_lock_t *fileLock;
5883 for (i = 0; i < cm_data.scacheHashTableSize; i++)
5885 for ( scp = cm_data.scacheHashTablep[i]; scp; scp = scp->nextp ) {
5886 while (scp->fileLocksH != NULL) {
5887 lock_ObtainWrite(&scp->rw);
5888 lock_ObtainWrite(&cm_scacheLock);
5889 if (!scp->fileLocksH) {
5890 lock_ReleaseWrite(&cm_scacheLock);
5891 lock_ReleaseWrite(&scp->rw);
5894 fileLock = (cm_file_lock_t *)((char *) scp->fileLocksH - offsetof(cm_file_lock_t, fileq));
5895 userp = fileLock->userp;
5897 key = fileLock->key;
5898 cm_HoldSCacheNoLock(scp);
5899 lock_ReleaseWrite(&cm_scacheLock);
5900 cm_UnlockByKey(scp, key, 0, userp, &req);
5901 cm_ReleaseSCache(scp);
5902 cm_ReleaseUser(userp);
5903 lock_ReleaseWrite(&scp->rw);