2 * Copyright 2000, International Business Machines Corporation and others.
5 * This software has been released under the terms of the IBM Public
6 * License. For details, see the LICENSE file in the top-level source
7 * directory or online at http://www.openafs.org/dl/license10.html
10 #include <afsconfig.h>
11 #include <afs/param.h>
33 extern void afsi_log(char *pattern, ...);
36 int cm_enableServerLocks = 1;
38 int cm_followBackupPath = 0;
41 * Case-folding array. This was constructed by inspecting of SMBtrace output.
42 * I do not know anything more about it.
44 unsigned char cm_foldUpper[256] = {
45 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
46 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
47 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
48 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
49 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
50 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
51 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
52 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
53 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
54 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
55 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
56 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
57 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
58 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
59 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
60 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
61 0x80, 0x9a, 0x90, 0x41, 0x8e, 0x41, 0x8f, 0x80,
62 0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x8e, 0x8f,
63 0x90, 0x92, 0x92, 0x4f, 0x99, 0x4f, 0x55, 0x55,
64 0x59, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
65 0x41, 0x49, 0x4f, 0x55, 0xa5, 0xa5, 0x56, 0xa7,
66 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
67 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
68 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
69 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
70 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
71 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
72 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
73 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
74 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
75 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
76 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
80 * Case-insensitive string comparison. We used to use stricmp, but it doesn't
81 * know about 8-bit characters (e.g. 129 is lowercase u-umlaut, 154 is
82 * upper-case u-umlaut).
84 int cm_stricmp(const char *str1, const char *str2)
96 c1 = (char) cm_foldUpper[(unsigned char)(*str1++)];
97 c2 = (char) cm_foldUpper[(unsigned char)(*str2++)];
107 /* return success if we can open this file in this mode */
108 long cm_CheckOpen(cm_scache_t *scp, int openMode, int trunc, cm_user_t *userp,
116 rights |= PRSFS_READ;
117 if (openMode == 1 || openMode == 2 || trunc)
118 rights |= PRSFS_WRITE;
120 lock_ObtainWrite(&scp->rw);
122 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
123 CM_SCACHESYNC_GETSTATUS
124 | CM_SCACHESYNC_NEEDCALLBACK
125 | CM_SCACHESYNC_LOCK);
128 ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
129 scp->fileType == CM_SCACHETYPE_FILE) {
132 unsigned int sLockType;
133 LARGE_INTEGER LOffset, LLength;
135 /* Check if there's some sort of lock on the file at the
138 key = cm_GenerateKey(CM_SESSION_CMINT,0,0);
140 if (rights & PRSFS_WRITE)
143 sLockType = LOCKING_ANDX_SHARED_LOCK;
145 LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
146 LOffset.LowPart = CM_FLSHARE_OFFSET_LOW;
147 LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
148 LLength.LowPart = CM_FLSHARE_LENGTH_LOW;
150 code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
153 cm_Unlock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp);
155 /* In this case, we allow the file open to go through even
156 though we can't enforce mandatory locking on the
158 if (code == CM_ERROR_NOACCESS &&
159 !(rights & PRSFS_WRITE))
163 case CM_ERROR_ALLOFFLINE:
164 case CM_ERROR_ALLDOWN:
165 case CM_ERROR_ALLBUSY:
166 case CM_ERROR_TIMEDOUT:
168 case CM_ERROR_WOULDBLOCK:
171 code = CM_ERROR_SHARING_VIOLATION;
176 } else if (code != 0) {
180 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
184 lock_ReleaseWrite(&scp->rw);
189 /* return success if we can open this file in this mode */
190 long cm_CheckNTOpen(cm_scache_t *scp, unsigned int desiredAccess,
191 unsigned int createDisp, cm_user_t *userp, cm_req_t *reqp,
192 cm_lock_data_t **ldpp)
197 osi_assertx(ldpp != NULL, "null cm_lock_data_t");
200 /* Always allow delete; the RPC will tell us if it's OK */
203 if (desiredAccess == DELETE)
206 if (desiredAccess & (AFS_ACCESS_READ|AFS_ACCESS_EXECUTE))
207 rights |= (scp->fileType == CM_SCACHETYPE_DIRECTORY ? PRSFS_LOOKUP : PRSFS_READ);
209 /* We used to require PRSFS_WRITE if createDisp was 4
210 (OPEN_ALWAYS) even if AFS_ACCESS_WRITE was not requested.
211 However, we don't need to do that since the existence of the
212 scp implies that we don't need to create it. */
213 if (desiredAccess & AFS_ACCESS_WRITE)
214 rights |= PRSFS_WRITE;
216 if (desiredAccess & DELETE)
217 rights |= PRSFS_DELETE;
219 lock_ObtainWrite(&scp->rw);
221 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
222 CM_SCACHESYNC_GETSTATUS
223 | CM_SCACHESYNC_NEEDCALLBACK
224 | CM_SCACHESYNC_LOCK);
227 * If the open will fail because the volume is readonly, then we will
228 * return an access denied error instead. This is to help brain-dead
229 * apps run correctly on replicated volumes.
230 * See defect 10007 for more information.
232 if (code == CM_ERROR_READONLY)
233 code = CM_ERROR_NOACCESS;
236 ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
237 scp->fileType == CM_SCACHETYPE_FILE) {
239 unsigned int sLockType;
240 LARGE_INTEGER LOffset, LLength;
242 /* Check if there's some sort of lock on the file at the
245 key = cm_GenerateKey(CM_SESSION_CMINT,0,0);
246 if (rights & PRSFS_WRITE)
249 sLockType = LOCKING_ANDX_SHARED_LOCK;
251 /* single byte lock at offset 0x0100 0000 0000 0000 */
252 LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
253 LOffset.LowPart = CM_FLSHARE_OFFSET_LOW;
254 LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
255 LLength.LowPart = CM_FLSHARE_LENGTH_LOW;
257 code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
260 (*ldpp) = (cm_lock_data_t *)malloc(sizeof(cm_lock_data_t));
267 (*ldpp)->sLockType = sLockType;
268 (*ldpp)->LOffset.HighPart = LOffset.HighPart;
269 (*ldpp)->LOffset.LowPart = LOffset.LowPart;
270 (*ldpp)->LLength.HighPart = LLength.HighPart;
271 (*ldpp)->LLength.LowPart = LLength.LowPart;
273 /* In this case, we allow the file open to go through even
274 though we can't enforce mandatory locking on the
276 if (code == CM_ERROR_NOACCESS &&
277 !(rights & PRSFS_WRITE))
281 case CM_ERROR_ALLOFFLINE:
282 case CM_ERROR_ALLDOWN:
283 case CM_ERROR_ALLBUSY:
284 case CM_ERROR_TIMEDOUT:
286 case CM_ERROR_WOULDBLOCK:
289 code = CM_ERROR_SHARING_VIOLATION;
293 } else if (code != 0) {
298 lock_ReleaseWrite(&scp->rw);
301 osi_Log3(afsd_logp,"cm_CheckNTOpen scp 0x%p ldp 0x%p code 0x%x", scp, *ldpp, code);
305 extern long cm_CheckNTOpenDone(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp,
306 cm_lock_data_t ** ldpp)
308 osi_Log2(afsd_logp,"cm_CheckNTOpenDone scp 0x%p ldp 0x%p", scp, *ldpp);
309 lock_ObtainWrite(&scp->rw);
311 cm_Unlock(scp, (*ldpp)->sLockType, (*ldpp)->LOffset, (*ldpp)->LLength,
312 (*ldpp)->key, 0, userp, reqp);
316 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
317 lock_ReleaseWrite(&scp->rw);
321 * When CAP_NT_SMBS has been negotiated, deletion (of files or directories) is
322 * done in three steps:
323 * (1) open for deletion (NT_CREATE_AND_X)
324 * (2) set for deletion on close (NT_TRANSACTION2, SET_FILE_INFO)
326 * We must not do the RPC until step 3. But if we are going to return an error
327 * code (e.g. directory not empty), we must return it by step 2, otherwise most
328 * clients will not notice it. So we do a preliminary check. For deleting
329 * files, this is almost free, since we have already done the RPC to get the
330 * parent directory's status bits. But for deleting directories, we must do an
331 * additional RPC to get the directory's data to check if it is empty. Sigh.
333 long cm_CheckNTDelete(cm_scache_t *dscp, cm_scache_t *scp, cm_user_t *userp,
339 cm_dirEntry_t *dep = 0;
340 unsigned short *hashTable;
342 int BeyondPage = 0, HaveDot = 0, HaveDotDot = 0;
345 /* First check permissions */
346 lock_ObtainWrite(&scp->rw);
347 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_DELETE,
348 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
350 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
351 lock_ReleaseWrite(&scp->rw);
355 /* If deleting directory, must be empty */
357 if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
360 thyper.HighPart = 0; thyper.LowPart = 0;
361 code = buf_Get(scp, &thyper, reqp, &bufferp);
365 lock_ObtainMutex(&bufferp->mx);
366 lock_ObtainWrite(&scp->rw);
369 code = cm_SyncOp(scp, bufferp, userp, reqp, 0,
370 CM_SCACHESYNC_NEEDCALLBACK
372 | CM_SCACHESYNC_BUFLOCKED);
376 if (cm_HaveBuffer(scp, bufferp, 1))
379 /* otherwise, load the buffer and try again */
380 lock_ReleaseMutex(&bufferp->mx);
381 code = cm_GetBuffer(scp, bufferp, NULL, userp, reqp);
382 lock_ReleaseWrite(&scp->rw);
383 lock_ObtainMutex(&bufferp->mx);
384 lock_ObtainWrite(&scp->rw);
385 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
390 lock_ReleaseWrite(&scp->rw);
393 /* We try to determine emptiness without looking beyond the first page,
394 * and without assuming "." and ".." are present and are on the first
395 * page (though these assumptions might, after all, be reasonable).
397 hashTable = (unsigned short *)(bufferp->datap + (32 * 5));
398 for (i=0; i<128; i++) {
399 idx = ntohs(hashTable[i]);
405 dep = (cm_dirEntry_t *)(bufferp->datap + (32 * idx));
406 if (strcmp(dep->name, ".") == 0)
408 else if (strcmp(dep->name, "..") == 0)
411 code = CM_ERROR_NOTEMPTY;
414 idx = ntohs(dep->next);
417 if (BeyondPage && HaveDot && HaveDotDot)
418 code = CM_ERROR_NOTEMPTY;
422 lock_ReleaseMutex(&bufferp->mx);
423 buf_Release(bufferp);
425 lock_ReleaseWrite(&scp->rw);
430 * Iterate through all entries in a directory.
431 * When the function funcp is called, the buffer is locked but the
432 * directory vnode is not.
434 * If the retscp parameter is not NULL, the parmp must be a
435 * cm_lookupSearch_t object.
437 long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
438 osi_hyper_t *startOffsetp, cm_user_t *userp, cm_req_t *reqp,
439 cm_scache_t **retscp)
443 cm_dirEntry_t *dep = 0;
446 osi_hyper_t dirLength;
447 osi_hyper_t bufferOffset;
448 osi_hyper_t curOffset;
452 cm_pageHeader_t *pageHeaderp;
454 long nextEntryCookie;
455 int numDirChunks; /* # of 32 byte dir chunks in this entry */
457 /* get the directory size */
458 lock_ObtainWrite(&scp->rw);
459 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_LOOKUP,
460 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
461 lock_ReleaseWrite(&scp->rw);
465 if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
466 return CM_ERROR_NOTDIR;
468 if (retscp) /* if this is a lookup call */
470 cm_lookupSearch_t* sp = parmp;
473 #ifdef AFS_FREELANCE_CLIENT
474 /* Freelance entries never end up in the DNLC because they
475 * do not have an associated cm_server_t
477 !(cm_freelanceEnabled &&
478 sp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
479 sp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
480 #else /* !AFS_FREELANCE_CLIENT */
485 int casefold = sp->caseFold;
486 sp->caseFold = 0; /* we have a strong preference for exact matches */
487 if ( *retscp = cm_dnlcLookup(scp, sp)) /* dnlc hit */
489 sp->caseFold = casefold;
492 sp->caseFold = casefold;
496 * see if we can find it using the directory hash tables.
497 * we can only do exact matches, since the hash is case
500 if (funcp != (cm_DirFuncp_t)cm_BPlusDirFoo)
509 code = cm_BeginDirOp(scp, userp, reqp, CM_DIRLOCK_READ,
510 CM_DIROP_FLAG_NONE, &dirop);
514 code = cm_BPlusDirLookup(&dirop, sp->nsearchNamep, &sp->fid);
519 code = cm_DirLookup(&dirop, sp->searchNamep, &sp->fid);
527 sp->ExactFound = TRUE;
528 *retscp = NULL; /* force caller to call cm_GetSCache() */
533 if (sp->caseFold && code == CM_ERROR_INEXACT_MATCH) {
536 sp->ExactFound = FALSE;
537 *retscp = NULL; /* force caller to call cm_GetSCache() */
541 return CM_ERROR_BPLUS_NOMATCH;
548 * XXX We only get the length once. It might change when we drop the
551 dirLength = scp->length;
554 bufferOffset.LowPart = bufferOffset.HighPart = 0;
556 curOffset = *startOffsetp;
558 curOffset.HighPart = 0;
559 curOffset.LowPart = 0;
563 /* make sure that curOffset.LowPart doesn't point to the first
564 * 32 bytes in the 2nd through last dir page, and that it
565 * doesn't point at the first 13 32-byte chunks in the first
566 * dir page, since those are dir and page headers, and don't
567 * contain useful information.
569 temp = curOffset.LowPart & (2048-1);
570 if (curOffset.HighPart == 0 && curOffset.LowPart < 2048) {
571 /* we're in the first page */
572 if (temp < 13*32) temp = 13*32;
575 /* we're in a later dir page */
576 if (temp < 32) temp = 32;
579 /* make sure the low order 5 bits are zero */
582 /* now put temp bits back ito curOffset.LowPart */
583 curOffset.LowPart &= ~(2048-1);
584 curOffset.LowPart |= temp;
586 /* check if we've passed the dir's EOF */
587 if (LargeIntegerGreaterThanOrEqualTo(curOffset, dirLength))
590 /* see if we can use the bufferp we have now; compute in which
591 * page the current offset would be, and check whether that's
592 * the offset of the buffer we have. If not, get the buffer.
594 thyper.HighPart = curOffset.HighPart;
595 thyper.LowPart = curOffset.LowPart & ~(cm_data.buf_blockSize-1);
596 if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
599 lock_ReleaseMutex(&bufferp->mx);
600 buf_Release(bufferp);
604 code = buf_Get(scp, &thyper, reqp, &bufferp);
606 /* if buf_Get() fails we do not have a buffer object to lock */
611 lock_ObtainMutex(&bufferp->mx);
612 bufferOffset = thyper;
614 /* now get the data in the cache */
616 lock_ObtainWrite(&scp->rw);
617 code = cm_SyncOp(scp, bufferp, userp, reqp,
619 CM_SCACHESYNC_NEEDCALLBACK
621 | CM_SCACHESYNC_BUFLOCKED);
623 lock_ReleaseWrite(&scp->rw);
626 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
628 if (cm_HaveBuffer(scp, bufferp, 1)) {
629 lock_ReleaseWrite(&scp->rw);
633 /* otherwise, load the buffer and try again */
634 lock_ReleaseMutex(&bufferp->mx);
635 code = cm_GetBuffer(scp, bufferp, NULL, userp,
637 lock_ReleaseWrite(&scp->rw);
638 lock_ObtainMutex(&bufferp->mx);
643 lock_ReleaseMutex(&bufferp->mx);
644 buf_Release(bufferp);
648 } /* if (wrong buffer) ... */
650 /* now we have the buffer containing the entry we're interested
651 * in; copy it out if it represents a non-deleted entry.
653 entryInDir = curOffset.LowPart & (2048-1);
654 entryInBuffer = curOffset.LowPart & (cm_data.buf_blockSize - 1);
656 /* page header will help tell us which entries are free. Page
657 * header can change more often than once per buffer, since
658 * AFS 3 dir page size may be less than (but not more than) a
659 * buffer package buffer.
661 /* only look intra-buffer */
662 temp = curOffset.LowPart & (cm_data.buf_blockSize - 1);
663 temp &= ~(2048 - 1); /* turn off intra-page bits */
664 pageHeaderp = (cm_pageHeader_t *) (bufferp->datap + temp);
666 /* now determine which entry we're looking at in the page. If
667 * it is free (there's a free bitmap at the start of the dir),
668 * we should skip these 32 bytes.
670 slotInPage = (entryInDir & 0x7e0) >> 5;
671 if (!(pageHeaderp->freeBitmap[slotInPage>>3]
672 & (1 << (slotInPage & 0x7)))) {
673 /* this entry is free */
674 numDirChunks = 1; /* only skip this guy */
678 tp = bufferp->datap + entryInBuffer;
679 dep = (cm_dirEntry_t *) tp; /* now points to AFS3 dir entry */
682 * here are some consistency checks
684 if (dep->flag != CM_DIR_FFIRST ||
685 strlen(dep->name) > 256) {
686 code = CM_ERROR_INVAL;
688 "cm_ApplyDir invalid directory entry for scp %p bufp %p",
690 osi_Log4(afsd_logp,"... cell %u vol %u vnode %u uniq %u",
691 scp->fid.cell, scp->fid.volume, scp->fid.vnode, scp->fid.unique);
692 bufferp->dataVersion = CM_BUF_VERSION_BAD;
696 /* while we're here, compute the next entry's location, too,
697 * since we'll need it when writing out the cookie into the
698 * dir listing stream.
700 numDirChunks = cm_NameEntries(dep->name, NULL);
702 /* compute the offset of the cookie representing the next entry */
703 nextEntryCookie = curOffset.LowPart
704 + (CM_DIR_CHUNKSIZE * numDirChunks);
706 if (dep->fid.vnode != 0) {
707 /* this is one of the entries to use: it is not deleted */
708 code = (*funcp)(scp, dep, parmp, &curOffset);
711 } /* if we're including this name */
714 /* and adjust curOffset to be where the new cookie is */
716 thyper.LowPart = CM_DIR_CHUNKSIZE * numDirChunks;
717 curOffset = LargeIntegerAdd(thyper, curOffset);
718 } /* while copying data for dir listing */
720 /* release the mutex */
722 lock_ReleaseMutex(&bufferp->mx);
723 buf_Release(bufferp);
728 int cm_NoneUpper(normchar_t *s)
732 if (c >= 'A' && c <= 'Z')
737 int cm_NoneLower(normchar_t *s)
741 if (c >= 'a' && c <= 'z')
746 long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
749 cm_lookupSearch_t *sp;
751 normchar_t matchName[MAX_PATH];
752 int looking_for_short_name = FALSE;
754 sp = (cm_lookupSearch_t *) rockp;
756 if (cm_FsStringToNormString(dep->name, -1, matchName, lengthof(matchName)) == 0) {
757 /* Can't normalize FS string. */
762 match = cm_NormStrCmpI(matchName, sp->nsearchNamep);
764 match = cm_NormStrCmp(matchName, sp->nsearchNamep);
768 && !cm_Is8Dot3(matchName)) {
770 cm_Gen8Dot3NameInt(dep->name, &dep->fid, matchName, NULL);
772 match = cm_NormStrCmpI(matchName, sp->nsearchNamep);
774 match = cm_NormStrCmp(matchName, sp->nsearchNamep);
775 looking_for_short_name = TRUE;
785 if (!sp->caseFold || looking_for_short_name) {
786 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
787 return CM_ERROR_STOPNOW;
791 * If we get here, we are doing a case-insensitive search, and we
792 * have found a match. Now we determine what kind of match it is:
793 * exact, lower-case, upper-case, or none of the above. This is done
794 * in order to choose among matches, if there are more than one.
797 /* Exact matches are the best. */
798 match = cm_NormStrCmp(matchName, sp->nsearchNamep);
801 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
802 return CM_ERROR_STOPNOW;
805 /* Lower-case matches are next. */
808 if (cm_NoneUpper(matchName)) {
813 /* Upper-case matches are next. */
816 if (cm_NoneLower(matchName)) {
821 /* General matches are last. */
827 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
831 /* read the contents of a mount point into the appropriate string.
832 * called with write locked scp, and returns with locked scp.
834 long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
838 if (scp->mountPointStringp[0])
841 #ifdef AFS_FREELANCE_CLIENT
842 /* File servers do not have data for freelance entries */
843 if (cm_freelanceEnabled &&
844 scp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
845 scp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
847 code = cm_FreelanceFetchMountPointString(scp);
849 #endif /* AFS_FREELANCE_CLIENT */
851 char temp[MOUNTPOINTLEN];
854 /* otherwise, we have to read it in */
855 offset.LowPart = offset.HighPart = 0;
856 code = cm_GetData(scp, &offset, temp, MOUNTPOINTLEN, userp, reqp);
861 * scp->length is the actual length of the mount point string.
862 * It is current because cm_GetData merged the most up to date
863 * status info into scp and has not dropped the rwlock since.
865 if (scp->length.LowPart > MOUNTPOINTLEN - 1)
866 return CM_ERROR_TOOBIG;
867 if (scp->length.LowPart == 0)
868 return CM_ERROR_INVAL;
870 /* convert the terminating dot to a NUL */
871 temp[scp->length.LowPart - 1] = 0;
872 memcpy(scp->mountPointStringp, temp, scp->length.LowPart);
879 /* called with a locked scp and chases the mount point, yielding outScpp.
880 * scp remains write locked, just for simplicity of describing the interface.
882 long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
883 cm_req_t *reqp, cm_scache_t **outScpp)
885 fschar_t *cellNamep = NULL;
886 fschar_t *volNamep = NULL;
890 cm_volume_t *volp = NULL;
899 if (scp->mountRootFid.cell != 0 && scp->mountRootGen >= cm_data.mountRootGen) {
900 tfid = scp->mountRootFid;
901 lock_ReleaseWrite(&scp->rw);
902 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
903 lock_ObtainWrite(&scp->rw);
907 /* parse the volume name */
908 mpNamep = scp->mountPointStringp;
910 return CM_ERROR_NOSUCHPATH;
911 mtType = *scp->mountPointStringp;
913 cp = cm_FsStrChr(mpNamep, _FS(':'));
915 /* cellular mount point */
916 cellNamep = (fschar_t *)malloc((cp - mpNamep) * sizeof(fschar_t));
917 cm_FsStrCpyN(cellNamep, cp - mpNamep, mpNamep + 1, cp - mpNamep - 1);
918 volNamep = cm_FsStrDup(cp+1);
920 /* now look up the cell */
921 lock_ReleaseWrite(&scp->rw);
922 cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE);
923 lock_ObtainWrite(&scp->rw);
926 volNamep = cm_FsStrDup(mpNamep + 1);
928 #ifdef AFS_FREELANCE_CLIENT
930 * Mount points in the Freelance cell should default
931 * to the workstation cell.
933 if (cm_freelanceEnabled &&
934 scp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
935 scp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
937 fschar_t rootCellName[256]="";
938 cm_GetRootCellName(rootCellName);
939 cellp = cm_GetCell(rootCellName, 0);
941 #endif /* AFS_FREELANCE_CLIENT */
942 cellp = cm_FindCellByID(scp->fid.cell, 0);
946 code = CM_ERROR_NOSUCHCELL;
950 vnLength = cm_FsStrLen(volNamep);
951 if (vnLength >= 8 && cm_FsStrCmp(volNamep + vnLength - 7, ".backup") == 0)
952 targetType = BACKVOL;
953 else if (vnLength >= 10
954 && cm_FsStrCmp(volNamep + vnLength - 9, ".readonly") == 0)
959 /* check for backups within backups */
960 if (targetType == BACKVOL
961 && (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
962 == CM_SCACHEFLAG_RO) {
963 code = CM_ERROR_NOSUCHVOLUME;
967 /* now we need to get the volume */
968 lock_ReleaseWrite(&scp->rw);
969 if (cm_VolNameIsID(volNamep)) {
970 code = cm_FindVolumeByID(cellp, atoi(volNamep), userp, reqp,
971 CM_GETVOL_FLAG_CREATE, &volp);
973 code = cm_FindVolumeByName(cellp, volNamep, userp, reqp,
974 CM_GETVOL_FLAG_CREATE, &volp);
976 lock_ObtainWrite(&scp->rw);
979 afs_uint32 cell, volume;
980 cm_vol_state_t *statep;
982 cell = cellp->cellID;
984 /* if the mt pt originates in a .backup volume (not a .readonly)
985 * and FollowBackupPath is active, and if there is a .backup
986 * volume for the target, then use the .backup of the target
987 * instead of the read-write.
989 if (cm_followBackupPath &&
990 volp->vol[BACKVOL].ID != 0 &&
991 (dscp->flags & (CM_SCACHEFLAG_RO|CM_SCACHEFLAG_PURERO)) == CM_SCACHEFLAG_RO &&
992 (targetType == RWVOL || targetType == ROVOL && volp->vol[ROVOL].ID == 0)
994 targetType = BACKVOL;
996 /* if the mt pt is in a read-only volume (not just a
997 * backup), and if there is a read-only volume for the
998 * target, and if this is a targetType '#' mount point, use
999 * the read-only, otherwise use the one specified.
1001 else if (mtType == '#' && targetType == RWVOL &&
1002 (scp->flags & CM_SCACHEFLAG_PURERO) &&
1003 volp->vol[ROVOL].ID != 0) {
1007 lock_ObtainWrite(&volp->rw);
1008 statep = cm_VolumeStateByType(volp, targetType);
1009 volume = statep->ID;
1010 statep->dotdotFid = dscp->fid;
1011 lock_ReleaseWrite(&volp->rw);
1013 /* the rest of the fid is a magic number */
1014 cm_SetFid(&scp->mountRootFid, cell, volume, 1, 1);
1015 scp->mountRootGen = cm_data.mountRootGen;
1017 tfid = scp->mountRootFid;
1018 lock_ReleaseWrite(&scp->rw);
1019 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
1020 lock_ObtainWrite(&scp->rw);
1033 long cm_LookupInternal(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_user_t *userp,
1034 cm_req_t *reqp, cm_scache_t **outScpp)
1037 int dnlcHit = 1; /* did we hit in the dnlc? yes, we did */
1038 cm_scache_t *tscp = NULL;
1039 cm_scache_t *mountedScp;
1040 cm_lookupSearch_t rock;
1042 normchar_t *nnamep = NULL;
1043 fschar_t *fnamep = NULL;
1048 memset(&rock, 0, sizeof(rock));
1050 if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
1051 && cm_ClientStrCmp(cnamep, _C("..")) == 0) {
1052 if (dscp->dotdotFid.volume == 0)
1053 return CM_ERROR_NOSUCHVOLUME;
1054 rock.fid = dscp->dotdotFid;
1056 } else if (cm_ClientStrCmp(cnamep, _C(".")) == 0) {
1057 rock.fid = dscp->fid;
1061 nnamep = cm_ClientStringToNormStringAlloc(cnamep, -1, NULL);
1063 code = CM_ERROR_NOSUCHFILE;
1066 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
1068 code = CM_ERROR_NOSUCHFILE;
1073 if (flags & CM_FLAG_NOMOUNTCHASE) {
1074 /* In this case, we should go and call cm_Dir* functions
1075 directly since the following cm_ApplyDir() function will
1083 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ,
1084 CM_DIROP_FLAG_NONE, &dirop);
1087 code = cm_BPlusDirLookup(&dirop, nnamep, &rock.fid);
1092 code = cm_DirLookup(&dirop, fnamep, &rock.fid);
1094 cm_EndDirOp(&dirop);
1104 if (code == CM_ERROR_INEXACT_MATCH && (flags & CM_FLAG_CASEFOLD)) {
1111 code = CM_ERROR_BPLUS_NOMATCH;
1117 rock.fid.cell = dscp->fid.cell;
1118 rock.fid.volume = dscp->fid.volume;
1119 rock.searchNamep = fnamep;
1120 rock.nsearchNamep = nnamep;
1121 rock.caseFold = (flags & CM_FLAG_CASEFOLD);
1122 rock.hasTilde = ((cm_ClientStrChr(cnamep, '~') != NULL) ? 1 : 0);
1124 /* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
1125 code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
1126 (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
1128 /* code == 0 means we fell off the end of the dir, while stopnow means
1129 * that we stopped early, probably because we found the entry we're
1130 * looking for. Any other non-zero code is an error.
1132 if (code && code != CM_ERROR_STOPNOW && code != CM_ERROR_BPLUS_NOMATCH) {
1133 /* if the cm_scache_t we are searching in is not a directory
1134 * we must return path not found because the error
1135 * is to describe the final component not an intermediary
1137 if (code == CM_ERROR_NOTDIR) {
1138 if (flags & CM_FLAG_CHECKPATH)
1139 code = CM_ERROR_NOSUCHPATH;
1141 code = CM_ERROR_NOSUCHFILE;
1147 getroot = (dscp==cm_data.rootSCachep) ;
1149 if (!cm_freelanceEnabled || !getroot) {
1150 if (flags & CM_FLAG_CHECKPATH)
1151 code = CM_ERROR_NOSUCHPATH;
1153 code = CM_ERROR_NOSUCHFILE;
1156 else if (!cm_ClientStrChr(cnamep, '#') &&
1157 !cm_ClientStrChr(cnamep, '%') &&
1158 cm_ClientStrCmpI(cnamep, _C("srvsvc")) &&
1159 cm_ClientStrCmpI(cnamep, _C("wkssvc")) &&
1160 cm_ClientStrCmpI(cnamep, _C("ipc$")))
1162 /* nonexistent dir on freelance root, so add it */
1163 fschar_t fullname[CELL_MAXNAMELEN + 1] = "."; /* +1 so that when we skip the . the size is still CELL_MAXNAMELEN */
1167 osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %S",
1168 osi_LogSaveClientString(afsd_logp,cnamep));
1171 * There is an ugly behavior where a share name "foo" will be searched
1172 * for as "fo". If the searched for name differs by an already existing
1173 * symlink or mount point in the Freelance directory, do not add the
1174 * new value automatically.
1178 fnlen = strlen(fnamep);
1179 if ( fnamep[fnlen-1] == '.') {
1180 fnamep[fnlen-1] = '\0';
1185 if (cnamep[0] == '.') {
1186 if (cm_GetCell_Gen(&fnamep[1], &fullname[1], CM_FLAG_CREATE)) {
1188 code = cm_FreelanceAddMount(fullname, &fullname[1], "root.cell", 1, &rock.fid);
1189 if ( cm_FsStrCmpI(&fnamep[1], &fullname[1])) {
1191 * Do not permit symlinks that are one of:
1192 * . the cellname followed by a dot
1193 * . the cellname minus a single character
1194 * . a substring of the cellname that does not consist of full components
1196 if ( cm_strnicmp_utf8(&fnamep[1], fullname, (int)fnlen-1) == 0 &&
1197 (fnlen-1 == strlen(fullname)-1 || fullname[fnlen-1] != '.'))
1199 /* do not add; substitute fullname for the search */
1201 fnamep = malloc(strlen(fullname)+2);
1203 strncpy(&fnamep[1], fullname, strlen(fullname)+1);
1206 code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
1211 if (cm_GetCell_Gen(fnamep, fullname, CM_FLAG_CREATE)) {
1213 code = cm_FreelanceAddMount(fullname, fullname, "root.cell", 0, &rock.fid);
1214 if ( cm_FsStrCmpI(fnamep, fullname)) {
1216 * Do not permit symlinks that are one of:
1217 * . the cellname followed by a dot
1218 * . the cellname minus a single character
1219 * . a substring of the cellname that does not consist of full components
1221 if ( cm_strnicmp_utf8(fnamep, fullname, (int)fnlen-1) == 0 &&
1222 (fnlen == strlen(fullname)-1 || fullname[fnlen] != '.'))
1224 /* do not add; substitute fullname for the search */
1226 fnamep = strdup(fullname);
1230 code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
1239 nnamep = cm_FsStringToNormStringAlloc(fnamep, -1, NULL);
1243 if (!found || code) { /* add mount point failed, so give up */
1244 if (flags & CM_FLAG_CHECKPATH)
1245 code = CM_ERROR_NOSUCHPATH;
1247 code = CM_ERROR_NOSUCHFILE;
1250 tscp = NULL; /* to force call of cm_GetSCache */
1252 if (flags & CM_FLAG_CHECKPATH)
1253 code = CM_ERROR_NOSUCHPATH;
1255 code = CM_ERROR_NOSUCHFILE;
1261 if ( !tscp ) /* we did not find it in the dnlc */
1264 code = cm_GetSCache(&rock.fid, &tscp, userp, reqp);
1268 /* tscp is now held */
1270 lock_ObtainWrite(&tscp->rw);
1273 * Do not get status if we do not already have a callback.
1274 * The process of reading the mount point string will obtain status information
1275 * in a single RPC. No reason to add a second round trip.
1277 * If we do have a callback, use cm_SyncOp to get status in case the
1278 * current cm_user_t is not the same as the one that obtained the
1279 * mount point string contents.
1281 if (cm_HaveCallback(tscp)) {
1282 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1283 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1285 lock_ReleaseWrite(&tscp->rw);
1286 cm_ReleaseSCache(tscp);
1289 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1291 /* tscp is now locked */
1293 if (!(flags & CM_FLAG_NOMOUNTCHASE)
1294 && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
1295 /* mount points are funny: they have a volume name to mount
1298 code = cm_ReadMountPoint(tscp, userp, reqp);
1300 code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
1302 lock_ReleaseWrite(&tscp->rw);
1303 cm_ReleaseSCache(tscp);
1310 lock_ReleaseWrite(&tscp->rw);
1313 /* copy back pointer */
1316 /* insert scache in dnlc */
1317 if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
1318 /* lock the directory entry to prevent racing callback revokes */
1319 lock_ObtainRead(&dscp->rw);
1320 if ( dscp->cbServerp != NULL && dscp->cbExpires > 0 ) {
1321 /* TODO: reuse nnamep from above */
1324 nnamep = cm_ClientStringToNormStringAlloc(cnamep, -1, NULL);
1326 cm_dnlcEnter(dscp, nnamep, tscp);
1328 lock_ReleaseRead(&dscp->rw);
1345 int cm_ExpandSysName(clientchar_t *inp, clientchar_t *outp, long outSizeCch, unsigned int index)
1350 tp = cm_ClientStrRChr(inp, '@');
1352 return 0; /* no @sys */
1354 if (cm_ClientStrCmp(tp, _C("@sys")) != 0)
1355 return 0; /* no @sys */
1357 /* caller just wants to know if this is a valid @sys type of name */
1361 if (index >= cm_sysNameCount)
1364 /* otherwise generate the properly expanded @sys name */
1365 prefixCount = (int)(tp - inp);
1367 cm_ClientStrCpyN(outp, outSizeCch, inp, prefixCount); /* copy out "a." from "a.@sys" */
1368 outp[prefixCount] = 0; /* null terminate the "a." */
1369 cm_ClientStrCat(outp, outSizeCch, cm_sysNameList[index]);/* append i386_nt40 */
1373 long cm_EvaluateVolumeReference(clientchar_t * namep, long flags, cm_user_t * userp,
1374 cm_req_t *reqp, cm_scache_t ** outScpp)
1376 afs_uint32 code = 0;
1377 fschar_t cellName[CELL_MAXNAMELEN];
1378 fschar_t volumeName[VL_MAXNAMELEN];
1382 fschar_t * fnamep = NULL;
1384 cm_cell_t * cellp = NULL;
1385 cm_volume_t * volp = NULL;
1389 int mountType = RWVOL;
1391 osi_Log1(afsd_logp, "cm_EvaluateVolumeReference for string [%S]",
1392 osi_LogSaveClientString(afsd_logp, namep));
1394 if (cm_ClientStrCmpNI(namep, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH) != 0) {
1395 goto _exit_invalid_path;
1398 /* namep is assumed to look like the following:
1400 @vol:<cellname>%<volume>\0
1402 @vol:<cellname>#<volume>\0
1406 fnamep = cm_ClientStringToFsStringAlloc(namep, -1, NULL);
1407 cp = fnamep + CM_PREFIX_VOL_CCH; /* cp points to cell name, hopefully */
1408 tp = cm_FsStrChr(cp, '%');
1410 tp = cm_FsStrChr(cp, '#');
1412 (len = tp - cp) == 0 ||
1413 len > CELL_MAXNAMELEN)
1414 goto _exit_invalid_path;
1415 cm_FsStrCpyN(cellName, lengthof(cellName), cp, len);
1420 cp = tp+1; /* cp now points to volume, supposedly */
1421 cm_FsStrCpy(volumeName, lengthof(volumeName), cp);
1423 /* OK, now we have the cell and the volume */
1424 osi_Log2(afsd_logp, " Found cell [%s] and volume [%s]",
1425 osi_LogSaveFsString(afsd_logp, cellName),
1426 osi_LogSaveFsString(afsd_logp, volumeName));
1428 cellp = cm_GetCell(cellName, CM_FLAG_CREATE);
1429 if (cellp == NULL) {
1430 goto _exit_invalid_path;
1433 len = cm_FsStrLen(volumeName);
1434 if (len >= 8 && cm_FsStrCmp(volumeName + len - 7, ".backup") == 0)
1436 else if (len >= 10 &&
1437 cm_FsStrCmp(volumeName + len - 9, ".readonly") == 0)
1442 if (cm_VolNameIsID(volumeName)) {
1443 code = cm_FindVolumeByID(cellp, atoi(volumeName), userp, reqp,
1444 CM_GETVOL_FLAG_CREATE, &volp);
1446 code = cm_FindVolumeByName(cellp, volumeName, userp, reqp,
1447 CM_GETVOL_FLAG_CREATE, &volp);
1453 if (volType == BACKVOL)
1454 volume = volp->vol[BACKVOL].ID;
1455 else if (volType == ROVOL ||
1456 (volType == RWVOL && mountType == ROVOL && volp->vol[ROVOL].ID != 0))
1457 volume = volp->vol[ROVOL].ID;
1459 volume = volp->vol[RWVOL].ID;
1461 cm_SetFid(&fid, cellp->cellID, volume, 1, 1);
1463 code = cm_GetSCache(&fid, outScpp, userp, reqp);
1476 if (flags & CM_FLAG_CHECKPATH)
1477 return CM_ERROR_NOSUCHPATH;
1479 return CM_ERROR_NOSUCHFILE;
1482 #ifdef DEBUG_REFCOUNT
1483 long cm_LookupDbg(cm_scache_t *dscp, clientchar_t *namep, long flags, cm_user_t *userp,
1484 cm_req_t *reqp, cm_scache_t **outScpp, char * file, long line)
1486 long cm_Lookup(cm_scache_t *dscp, clientchar_t *namep, long flags, cm_user_t *userp,
1487 cm_req_t *reqp, cm_scache_t **outScpp)
1491 clientchar_t tname[AFSPATHMAX];
1492 int sysNameIndex = 0;
1493 cm_scache_t *scp = NULL;
1495 #ifdef DEBUG_REFCOUNT
1496 afsi_log("%s:%d cm_Lookup dscp 0x%p ref %d", file, line, dscp, dscp->refCount, file, line);
1497 osi_Log2(afsd_logp, "cm_Lookup dscp 0x%p ref %d", dscp, dscp->refCount);
1500 if ( cm_ClientStrCmpI(namep,_C(SMB_IOCTL_FILENAME_NOSLASH)) == 0 ) {
1501 if (flags & CM_FLAG_CHECKPATH)
1502 return CM_ERROR_NOSUCHPATH;
1504 return CM_ERROR_NOSUCHFILE;
1507 if (dscp == cm_data.rootSCachep &&
1508 cm_ClientStrCmpNI(namep, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH) == 0) {
1509 return cm_EvaluateVolumeReference(namep, flags, userp, reqp, outScpp);
1512 if (cm_ExpandSysName(namep, NULL, 0, 0) > 0) {
1513 for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
1514 code = cm_ExpandSysName(namep, tname, lengthof(tname), sysNameIndex);
1516 code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
1517 #ifdef DEBUG_REFCOUNT
1518 afsi_log("%s:%d cm_LookupInternal (1) code 0x%x dscp 0x%p ref %d scp 0x%p ref %d", file, line, code, dscp, dscp->refCount, scp, scp ? scp->refCount : 0);
1519 osi_Log3(afsd_logp, "cm_LookupInternal (1) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1527 cm_ReleaseSCache(scp);
1531 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1532 #ifdef DEBUG_REFCOUNT
1533 afsi_log("%s:%d cm_LookupInternal (2) code 0x%x dscp 0x%p ref %d scp 0x%p ref %d", file, line, code, dscp, dscp->refCount, scp, scp ? scp->refCount : 0);
1534 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1541 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1542 #ifdef DEBUG_REFCOUNT
1543 afsi_log("%s:%d cm_LookupInternal (2) code 0x%x dscp 0x%p ref %d scp 0x%p ref %d", file, line, code, dscp, dscp->refCount, scp, scp ? scp->refCount : 0);
1544 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1550 /* None of the possible sysName expansions could be found */
1551 if (flags & CM_FLAG_CHECKPATH)
1552 return CM_ERROR_NOSUCHPATH;
1554 return CM_ERROR_NOSUCHFILE;
1557 /*! \brief Unlink a file name
1559 Encapsulates a call to RXAFS_RemoveFile().
1561 \param[in] dscp cm_scache_t pointing at the directory containing the
1562 name to be unlinked.
1564 \param[in] fnamep Original name to be unlinked. This is the
1565 name that will be passed into the RXAFS_RemoveFile() call.
1566 This parameter is optional. If not provided, the value will
1569 \param[in] came Client name to be unlinked. This name will be used
1570 to update the local directory caches.
1572 \param[in] userp cm_user_t for the request.
1574 \param[in] reqp Request tracker.
1577 long cm_Unlink(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t * cnamep,
1578 cm_user_t *userp, cm_req_t *reqp)
1584 AFSFetchStatus newDirStatus;
1586 struct rx_connection * rxconnp;
1588 cm_scache_t *scp = NULL;
1589 int free_fnamep = FALSE;
1591 memset(&volSync, 0, sizeof(volSync));
1593 if (fnamep == NULL) {
1596 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ,
1597 CM_DIROP_FLAG_NONE, &dirop);
1599 code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
1602 cm_EndDirOp(&dirop);
1609 #ifdef AFS_FREELANCE_CLIENT
1610 if (cm_freelanceEnabled && dscp == cm_data.rootSCachep) {
1611 /* deleting a mount point from the root dir. */
1612 code = cm_FreelanceRemoveMount(fnamep);
1617 code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
1621 /* Check for RO volume */
1622 if (dscp->flags & CM_SCACHEFLAG_RO) {
1623 code = CM_ERROR_READONLY;
1627 /* make sure we don't screw up the dir status during the merge */
1628 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE,
1629 CM_DIROP_FLAG_NONE, &dirop);
1631 lock_ObtainWrite(&dscp->rw);
1632 sflags = CM_SCACHESYNC_STOREDATA;
1633 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1634 lock_ReleaseWrite(&dscp->rw);
1636 cm_EndDirOp(&dirop);
1641 afsFid.Volume = dscp->fid.volume;
1642 afsFid.Vnode = dscp->fid.vnode;
1643 afsFid.Unique = dscp->fid.unique;
1645 osi_Log1(afsd_logp, "CALL RemoveFile scp 0x%p", dscp);
1647 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
1651 rxconnp = cm_GetRxConn(connp);
1652 code = RXAFS_RemoveFile(rxconnp, &afsFid, fnamep,
1653 &newDirStatus, &volSync);
1654 rx_PutConnection(rxconnp);
1656 } while (cm_Analyze(connp, userp, reqp, &dscp->fid, &volSync, NULL, NULL, code));
1657 code = cm_MapRPCError(code, reqp);
1660 osi_Log1(afsd_logp, "CALL RemoveFile FAILURE, code 0x%x", code);
1662 osi_Log0(afsd_logp, "CALL RemoveFile SUCCESS");
1665 lock_ObtainWrite(&dirop.scp->dirlock);
1666 dirop.lockType = CM_DIRLOCK_WRITE;
1668 lock_ObtainWrite(&dscp->rw);
1669 cm_dnlcRemove(dscp, cnamep);
1671 cm_MergeStatus(NULL, dscp, &newDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
1672 } else if (code == CM_ERROR_NOSUCHFILE) {
1673 /* windows would not have allowed the request to delete the file
1674 * if it did not believe the file existed. therefore, we must
1675 * have an inconsistent view of the world.
1677 dscp->cbServerp = NULL;
1679 cm_SyncOpDone(dscp, NULL, sflags);
1680 lock_ReleaseWrite(&dscp->rw);
1682 if (code == 0 && cm_CheckDirOpForSingleChange(&dirop) && cnamep) {
1683 cm_DirDeleteEntry(&dirop, fnamep);
1685 cm_BPlusDirDeleteEntry(&dirop, cnamep);
1688 cm_EndDirOp(&dirop);
1691 cm_ReleaseSCache(scp);
1693 lock_ObtainWrite(&scp->rw);
1694 if (--scp->linkCount == 0)
1695 scp->flags |= CM_SCACHEFLAG_DELETED;
1696 cm_DiscardSCache(scp);
1697 lock_ReleaseWrite(&scp->rw);
1708 /* called with a write locked vnode, and fills in the link info.
1709 * returns this the vnode still write locked.
1711 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1715 lock_AssertWrite(&linkScp->rw);
1716 if (!linkScp->mountPointStringp[0]) {
1718 #ifdef AFS_FREELANCE_CLIENT
1719 /* File servers do not have data for freelance entries */
1720 if (cm_freelanceEnabled &&
1721 linkScp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
1722 linkScp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
1724 code = cm_FreelanceFetchMountPointString(linkScp);
1726 #endif /* AFS_FREELANCE_CLIENT */
1728 char temp[MOUNTPOINTLEN];
1731 /* read the link data from the file server */
1732 offset.LowPart = offset.HighPart = 0;
1733 code = cm_GetData(linkScp, &offset, temp, MOUNTPOINTLEN, userp, reqp);
1738 * linkScp->length is the actual length of the symlink target string.
1739 * It is current because cm_GetData merged the most up to date
1740 * status info into scp and has not dropped the rwlock since.
1742 if (linkScp->length.LowPart > MOUNTPOINTLEN - 1)
1743 return CM_ERROR_TOOBIG;
1744 if (linkScp->length.LowPart == 0)
1745 return CM_ERROR_INVAL;
1747 /* make sure we are NUL terminated */
1748 temp[linkScp->length.LowPart] = 0;
1749 memcpy(linkScp->mountPointStringp, temp, linkScp->length.LowPart + 1);
1752 if ( !strnicmp(linkScp->mountPointStringp, "msdfs:", strlen("msdfs:")) )
1753 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1755 } /* don't have symlink contents cached */
1760 /* called with a held vnode and a path suffix, with the held vnode being a
1761 * symbolic link. Our goal is to generate a new path to interpret, and return
1762 * this new path in newSpaceBufferp. If the new vnode is relative to a dir
1763 * other than the directory containing the symbolic link, then the new root is
1764 * returned in *newRootScpp, otherwise a null is returned there.
1766 long cm_AssembleLink(cm_scache_t *linkScp, fschar_t *pathSuffixp,
1767 cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1768 cm_user_t *userp, cm_req_t *reqp)
1775 *newRootScpp = NULL;
1776 *newSpaceBufferp = NULL;
1778 lock_ObtainWrite(&linkScp->rw);
1780 * Do not get status if we do not already have a callback.
1781 * The process of reading the symlink string will obtain status information
1782 * in a single RPC. No reason to add a second round trip.
1784 * If we do have a callback, use cm_SyncOp to get status in case the
1785 * current cm_user_t is not the same as the one that obtained the
1786 * symlink string contents.
1788 if (cm_HaveCallback(linkScp)) {
1789 code = cm_SyncOp(linkScp, NULL, userp, reqp, 0,
1790 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1792 lock_ReleaseWrite(&linkScp->rw);
1793 cm_ReleaseSCache(linkScp);
1796 cm_SyncOpDone(linkScp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1798 code = cm_HandleLink(linkScp, userp, reqp);
1802 /* if we may overflow the buffer, bail out; buffer is signficantly
1803 * bigger than max path length, so we don't really have to worry about
1804 * being a little conservative here.
1806 if (cm_FsStrLen(linkScp->mountPointStringp) + cm_FsStrLen(pathSuffixp) + 2
1807 >= CM_UTILS_SPACESIZE) {
1808 code = CM_ERROR_TOOBIG;
1812 tsp = cm_GetSpace();
1813 linkp = linkScp->mountPointStringp;
1814 if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1815 if (strlen(linkp) > cm_mountRootLen)
1816 StringCbCopyA((char *) tsp->data, sizeof(tsp->data), linkp+cm_mountRootLen+1);
1819 *newRootScpp = cm_RootSCachep(userp, reqp);
1820 cm_HoldSCache(*newRootScpp);
1821 } else if (linkp[0] == '\\' && linkp[1] == '\\') {
1822 if (!strnicmp(&linkp[2], cm_NetbiosName, (len = (long)strlen(cm_NetbiosName))))
1824 char * p = &linkp[len + 3];
1825 if (strnicmp(p, "all", 3) == 0)
1828 StringCbCopyA(tsp->data, sizeof(tsp->data), p);
1829 for (p = tsp->data; *p; p++) {
1833 *newRootScpp = cm_RootSCachep(userp, reqp);
1834 cm_HoldSCache(*newRootScpp);
1836 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1837 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1838 code = CM_ERROR_PATH_NOT_COVERED;
1840 } else if ( linkScp->fileType == CM_SCACHETYPE_DFSLINK ||
1841 !strnicmp(linkp, "msdfs:", (len = (long)strlen("msdfs:"))) ) {
1842 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1843 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1844 code = CM_ERROR_PATH_NOT_COVERED;
1845 } else if (*linkp == '\\' || *linkp == '/') {
1847 /* formerly, this was considered to be from the AFS root,
1848 * but this seems to create problems. instead, we will just
1849 * reject the link */
1850 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp+1);
1851 *newRootScpp = cm_RootSCachep(userp, reqp);
1852 cm_HoldSCache(*newRootScpp);
1854 /* we still copy the link data into the response so that
1855 * the user can see what the link points to
1857 linkScp->fileType = CM_SCACHETYPE_INVALID;
1858 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1859 code = CM_ERROR_NOSUCHPATH;
1862 /* a relative link */
1863 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1865 if (pathSuffixp[0] != 0) { /* if suffix string is non-null */
1866 StringCchCatA(tsp->data,lengthof(tsp->data), "\\");
1867 StringCchCatA(tsp->data,lengthof(tsp->data), pathSuffixp);
1871 clientchar_t * cpath = cm_FsStringToClientStringAlloc(tsp->data, -1, NULL);
1872 if (cpath != NULL) {
1873 cm_ClientStrCpy(tsp->wdata, lengthof(tsp->wdata), cpath);
1875 *newSpaceBufferp = tsp;
1877 code = CM_ERROR_NOSUCHPATH;
1884 if (code == CM_ERROR_PATH_NOT_COVERED && reqp->tidPathp && reqp->relPathp) {
1885 cm_VolStatus_Notify_DFS_Mapping(linkScp, reqp->tidPathp, reqp->relPathp);
1890 lock_ReleaseWrite(&linkScp->rw);
1893 #ifdef DEBUG_REFCOUNT
1894 long cm_NameIDbg(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
1895 cm_user_t *userp, clientchar_t *tidPathp, cm_req_t *reqp,
1896 cm_scache_t **outScpp,
1897 char * file, long line)
1899 long cm_NameI(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
1900 cm_user_t *userp, clientchar_t *tidPathp,
1901 cm_req_t *reqp, cm_scache_t **outScpp)
1905 clientchar_t *tp; /* ptr moving through input buffer */
1906 clientchar_t tc; /* temp char */
1907 int haveComponent; /* has new component started? */
1908 clientchar_t component[AFSPATHMAX]; /* this is the new component */
1909 clientchar_t *cp; /* component name being assembled */
1910 cm_scache_t *tscp; /* current location in the hierarchy */
1911 cm_scache_t *nscp; /* next dude down */
1912 cm_scache_t *dirScp; /* last dir we searched */
1913 cm_scache_t *linkScp; /* new root for the symlink we just
1915 cm_space_t *psp; /* space for current path, if we've hit
1917 cm_space_t *tempsp; /* temp vbl */
1918 clientchar_t *restp; /* rest of the pathname to interpret */
1919 int symlinkCount; /* count of # of symlinks traversed */
1920 int extraFlag; /* avoid chasing mt pts for dir cmd */
1921 int phase = 1; /* 1 = tidPathp, 2 = pathp */
1922 #define MAX_FID_COUNT 512
1923 cm_fid_t fids[MAX_FID_COUNT]; /* array of fids processed in this path walk */
1924 int fid_count = 0; /* number of fids processed in this path walk */
1929 #ifdef DEBUG_REFCOUNT
1930 afsi_log("%s:%d cm_NameI rootscp 0x%p ref %d", file, line, rootSCachep, rootSCachep->refCount);
1931 osi_Log4(afsd_logp,"cm_NameI rootscp 0x%p path %S tidpath %S flags 0x%x",
1932 rootSCachep, pathp ? pathp : L"<NULL>", tidPathp ? tidPathp : L"<NULL>",
1947 cm_HoldSCache(tscp);
1955 /* map Unix slashes into DOS ones so we can interpret Unix
1961 if (!haveComponent) {
1964 } else if (tc == 0) {
1978 /* we have a component here */
1979 if (tc == 0 || tc == '\\') {
1980 /* end of the component; we're at the last
1981 * component if tc == 0. However, if the last
1982 * is a symlink, we have more to do.
1984 *cp++ = 0; /* add null termination */
1986 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
1987 extraFlag = CM_FLAG_NOMOUNTCHASE;
1988 code = cm_Lookup(tscp, component,
1990 userp, reqp, &nscp);
1993 if (!cm_ClientStrCmp(component,_C("..")) ||
1994 !cm_ClientStrCmp(component,_C("."))) {
1996 * roll back the fid list until we find the
1997 * fid that matches where we are now. Its not
1998 * necessarily one or two fids because they
1999 * might have been symlinks or mount points or
2000 * both that were crossed.
2002 for ( i=fid_count-1; i>=0; i--) {
2003 if (!cm_FidCmp(&nscp->fid, &fids[i]))
2008 /* add the new fid to the list */
2009 if (fid_count == MAX_FID_COUNT) {
2010 code = CM_ERROR_TOO_MANY_SYMLINKS;
2011 cm_ReleaseSCache(nscp);
2015 fids[fid_count++] = nscp->fid;
2020 cm_ReleaseSCache(tscp);
2022 cm_ReleaseSCache(dirScp);
2025 if ((code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH) &&
2026 tscp->fileType == CM_SCACHETYPE_SYMLINK) {
2027 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_NOSUCHPATH");
2028 return CM_ERROR_NOSUCHPATH;
2030 osi_Log1(afsd_logp,"cm_NameI code 0x%x", code);
2035 haveComponent = 0; /* component done */
2037 cm_ReleaseSCache(dirScp);
2038 dirScp = tscp; /* for some symlinks */
2039 tscp = nscp; /* already held */
2041 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
2044 cm_ReleaseSCache(dirScp);
2050 /* now, if tscp is a symlink, we should follow it and
2051 * assemble the path again.
2053 lock_ObtainWrite(&tscp->rw);
2054 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
2055 CM_SCACHESYNC_GETSTATUS
2056 | CM_SCACHESYNC_NEEDCALLBACK);
2058 lock_ReleaseWrite(&tscp->rw);
2059 cm_ReleaseSCache(tscp);
2062 cm_ReleaseSCache(dirScp);
2067 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2069 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
2070 /* this is a symlink; assemble a new buffer */
2071 lock_ReleaseWrite(&tscp->rw);
2072 if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
2073 cm_ReleaseSCache(tscp);
2076 cm_ReleaseSCache(dirScp);
2081 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_TOO_MANY_SYMLINKS");
2082 return CM_ERROR_TOO_MANY_SYMLINKS;
2092 /* TODO: make this better */
2093 frestp = cm_ClientStringToFsStringAlloc(restp, -1, NULL);
2094 code = cm_AssembleLink(tscp, frestp, &linkScp, &tempsp, userp, reqp);
2098 if (code == 0 && linkScp != NULL) {
2099 if (linkScp == cm_data.rootSCachep) {
2103 for ( i=0; i<fid_count; i++) {
2104 if ( !cm_FidCmp(&linkScp->fid, &fids[i]) ) {
2105 code = CM_ERROR_TOO_MANY_SYMLINKS;
2106 cm_ReleaseSCache(linkScp);
2112 if (i == fid_count && fid_count < MAX_FID_COUNT) {
2113 fids[fid_count++] = linkScp->fid;
2118 /* something went wrong */
2119 cm_ReleaseSCache(tscp);
2122 cm_ReleaseSCache(dirScp);
2128 /* otherwise, tempsp has the new path,
2129 * and linkScp is the new root from
2130 * which to interpret that path.
2131 * Continue with the namei processing,
2132 * also doing the bookkeeping for the
2133 * space allocation and tracking the
2134 * vnode reference counts.
2140 cm_ReleaseSCache(tscp);
2145 * now, if linkScp is null, that's
2146 * AssembleLink's way of telling us that
2147 * the sym link is relative to the dir
2148 * containing the link. We have a ref
2149 * to it in dirScp, and we hold it now
2150 * and reuse it as the new spot in the
2158 /* not a symlink, we may be done */
2159 lock_ReleaseWrite(&tscp->rw);
2167 cm_ReleaseSCache(dirScp);
2175 cm_ReleaseSCache(dirScp);
2178 } /* end of a component */
2181 } /* we have a component */
2182 } /* big while loop over all components */
2186 cm_ReleaseSCache(dirScp);
2192 cm_ReleaseSCache(tscp);
2194 #ifdef DEBUG_REFCOUNT
2195 afsi_log("%s:%d cm_NameI code 0x%x outScpp 0x%p ref %d", file, line, code, *outScpp, (*outScpp) ? (*outScpp)->refCount : 0);
2197 osi_Log2(afsd_logp,"cm_NameI code 0x%x outScpp 0x%p", code, *outScpp);
2201 /* called with a dir, and a vnode within the dir that happens to be a symlink.
2202 * We chase the link, and return a held pointer to the target, if it exists,
2203 * in *outScpp. If we succeed, we return 0, otherwise we return an error code
2204 * and do not hold or return a target vnode.
2206 * This is very similar to calling cm_NameI with the last component of a name,
2207 * which happens to be a symlink, except that we've already passed by the name.
2209 * This function is typically called by the directory listing functions, which
2210 * encounter symlinks but need to return the proper file length so programs
2211 * like "more" work properly when they make use of the attributes retrieved from
2214 * The input vnode should not be locked when this function is called.
2216 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
2217 cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
2221 cm_scache_t *newRootScp;
2225 osi_Log1(afsd_logp, "Evaluating symlink scp 0x%p", linkScp);
2227 code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
2231 /* now, if newRootScp is NULL, we're really being told that the symlink
2232 * is relative to the current directory (dscp).
2234 if (newRootScp == NULL) {
2236 cm_HoldSCache(dscp);
2239 code = cm_NameI(newRootScp, spacep->wdata,
2240 CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
2241 userp, NULL, reqp, outScpp);
2243 if (code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH)
2244 code = CM_ERROR_NOSUCHPATH;
2246 /* this stuff is allocated no matter what happened on the namei call,
2248 cm_FreeSpace(spacep);
2249 cm_ReleaseSCache(newRootScp);
2251 if (linkScp == *outScpp) {
2252 cm_ReleaseSCache(*outScpp);
2254 code = CM_ERROR_NOSUCHPATH;
2260 /* for a given entry, make sure that it isn't in the stat cache, and then
2261 * add it to the list of file IDs to be obtained.
2263 * Don't bother adding it if we already have a vnode. Note that the dir
2264 * is locked, so we have to be careful checking the vnode we're thinking of
2265 * processing, to avoid deadlocks.
2267 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
2278 /* Don't overflow bsp. */
2279 if (bsp->counter >= CM_BULKMAX)
2280 return CM_ERROR_STOPNOW;
2282 thyper.LowPart = cm_data.buf_blockSize;
2283 thyper.HighPart = 0;
2284 thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
2286 /* thyper is now the first byte past the end of the record we're
2287 * interested in, and bsp->bufOffset is the first byte of the record
2288 * we're interested in.
2289 * Skip data in the others.
2292 if (LargeIntegerLessThan(*offp, bsp->bufOffset))
2294 if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
2295 return CM_ERROR_STOPNOW;
2296 if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
2299 cm_SetFid(&tfid, scp->fid.cell, scp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
2300 tscp = cm_FindSCache(&tfid);
2302 if (lock_TryWrite(&tscp->rw)) {
2303 /* we have an entry that we can look at */
2304 if (!(tscp->flags & CM_SCACHEFLAG_EACCESS) && cm_HaveCallback(tscp)) {
2305 /* we have a callback on it. Don't bother
2306 * fetching this stat entry, since we're happy
2307 * with the info we have.
2309 lock_ReleaseWrite(&tscp->rw);
2310 cm_ReleaseSCache(tscp);
2313 lock_ReleaseWrite(&tscp->rw);
2315 cm_ReleaseSCache(tscp);
2318 #ifdef AFS_FREELANCE_CLIENT
2319 // yj: if this is a mountpoint under root.afs then we don't want it
2320 // to be bulkstat-ed, instead, we call getSCache directly and under
2321 // getSCache, it is handled specially.
2322 if ( cm_freelanceEnabled &&
2323 tfid.cell==AFS_FAKE_ROOT_CELL_ID &&
2324 tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
2325 !(tfid.vnode==0x1 && tfid.unique==0x1) )
2327 osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
2328 return cm_GetSCache(&tfid, &tscp, NULL, NULL);
2330 #endif /* AFS_FREELANCE_CLIENT */
2333 bsp->fids[i].Volume = scp->fid.volume;
2334 bsp->fids[i].Vnode = tfid.vnode;
2335 bsp->fids[i].Unique = tfid.unique;
2340 cm_TryBulkStatRPC(cm_scache_t *dscp, cm_bulkStat_t *bbp, cm_user_t *userp, cm_req_t *reqp)
2343 AFSCBFids fidStruct;
2344 AFSBulkStats statStruct;
2346 AFSCBs callbackStruct;
2349 cm_callbackRequest_t cbReq;
2355 struct rx_connection * rxconnp;
2356 int inlinebulk; /* Did we use InlineBulkStatus RPC or not? */
2358 memset(&volSync, 0, sizeof(volSync));
2360 /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
2361 * make the calls to create the entries. Handle AFSCBMAX files at a
2364 for (filex = 0; filex < bbp->counter; filex += filesThisCall) {
2365 filesThisCall = bbp->counter - filex;
2366 if (filesThisCall > AFSCBMAX)
2367 filesThisCall = AFSCBMAX;
2369 fidStruct.AFSCBFids_len = filesThisCall;
2370 fidStruct.AFSCBFids_val = &bbp->fids[filex];
2371 statStruct.AFSBulkStats_len = filesThisCall;
2372 statStruct.AFSBulkStats_val = &bbp->stats[filex];
2373 callbackStruct.AFSCBs_len = filesThisCall;
2374 callbackStruct.AFSCBs_val = &bbp->callbacks[filex];
2375 cm_StartCallbackGrantingCall(NULL, &cbReq);
2376 osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
2379 * Whenever cm_Analyze is called for a RXAFS_ RPC there must
2380 * be a FID provided. However, the error code from RXAFS_BulkStatus
2381 * or RXAFS_InlinkBulkStatus does not apply to any FID. Therefore,
2382 * we generate an invalid FID to match with the RPC error.
2384 cm_SetFid(&tfid, dscp->fid.cell, dscp->fid.volume, 0, 0);
2389 code = cm_ConnFromFID(&tfid, userp, reqp, &connp);
2393 rxconnp = cm_GetRxConn(connp);
2394 if (!(connp->serverp->flags & CM_SERVERFLAG_NOINLINEBULK)) {
2395 code = RXAFS_InlineBulkStatus(rxconnp, &fidStruct,
2396 &statStruct, &callbackStruct, &volSync);
2397 if (code == RXGEN_OPCODE) {
2398 cm_SetServerNoInlineBulk(connp->serverp, 0);
2404 code = RXAFS_BulkStatus(rxconnp, &fidStruct,
2405 &statStruct, &callbackStruct, &volSync);
2407 rx_PutConnection(rxconnp);
2410 * If InlineBulk RPC was called and it succeeded,
2411 * then pull out the return code from the status info
2412 * and use it for cm_Analyze so that we can failover to other
2413 * .readonly volume instances. But only do it for errors that
2414 * are volume global.
2416 if (inlinebulk && code == 0 && (&bbp->stats[0])->errorCode) {
2417 osi_Log1(afsd_logp, "cm_TryBulkStat inline-bulk stat error: %d",
2418 (&bbp->stats[0])->errorCode);
2419 switch ((&bbp->stats[0])->errorCode) {
2428 code = (&bbp->stats[0])->errorCode;
2431 /* Rx and Rxkad errors are volume global */
2432 if ( (&bbp->stats[0])->errorCode >= -64 && (&bbp->stats[0])->errorCode < 0 ||
2433 (&bbp->stats[0])->errorCode >= ERROR_TABLE_BASE_RXK && (&bbp->stats[0])->errorCode < ERROR_TABLE_BASE_RXK + 256)
2434 code = (&bbp->stats[0])->errorCode;
2437 } while (cm_Analyze(connp, userp, reqp, &tfid, &volSync, NULL, &cbReq, code));
2438 code = cm_MapRPCError(code, reqp);
2441 * might as well quit on an error, since we're not going to do
2442 * much better on the next immediate call, either.
2445 osi_Log2(afsd_logp, "CALL %sBulkStatus FAILURE code 0x%x",
2446 inlinebulk ? "Inline" : "", code);
2447 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2452 * The bulk RPC has succeeded or at least not failed with a
2453 * volume global error result. For items that have inlineBulk
2454 * errors we must call cm_Analyze in order to perform required
2455 * logging of errors.
2457 * If the RPC was not inline bulk or the entry either has no error
2458 * the status must be merged.
2460 osi_Log1(afsd_logp, "CALL %sBulkStatus SUCCESS", inlinebulk ? "Inline" : "");
2462 for (i = 0; i<filesThisCall; i++) {
2464 cm_SetFid(&tfid, dscp->fid.cell, bbp->fids[j].Volume, bbp->fids[j].Vnode, bbp->fids[j].Unique);
2466 if (inlinebulk && (&bbp->stats[j])->errorCode) {
2467 cm_req_t treq = *reqp;
2468 cm_Analyze(NULL, userp, &treq, &tfid, &volSync, NULL, &cbReq, (&bbp->stats[j])->errorCode);
2470 code = cm_GetSCache(&tfid, &scp, userp, reqp);
2475 * otherwise, if this entry has no callback info,
2476 * merge in this. If there is existing callback info
2477 * we skip the merge because the existing data must be
2478 * current (we have a callback) and the response from
2479 * a non-inline bulk rpc might actually be wrong.
2481 * now, we have to be extra paranoid on merging in this
2482 * information, since we didn't use cm_SyncOp before
2483 * starting the fetch to make sure that no bad races
2484 * were occurring. Specifically, we need to make sure
2485 * we don't obliterate any newer information in the
2486 * vnode than have here.
2488 * Right now, be pretty conservative: if there's a
2489 * callback or a pending call, skip it.
2490 * However, if the prior attempt to obtain status
2491 * was refused access or the volume is .readonly,
2492 * take the data in any case since we have nothing
2493 * better for the in flight directory enumeration that
2494 * resulted in this function being called.
2496 lock_ObtainRead(&scp->rw);
2497 if ((scp->cbServerp == NULL &&
2498 !(scp->flags & (CM_SCACHEFLAG_FETCHING | CM_SCACHEFLAG_STORING | CM_SCACHEFLAG_SIZESTORING))) ||
2499 (scp->flags & CM_SCACHEFLAG_PURERO) ||
2500 (scp->flags & CM_SCACHEFLAG_EACCESS))
2502 lock_ConvertRToW(&scp->rw);
2503 cm_EndCallbackGrantingCall(scp, &cbReq,
2506 CM_CALLBACK_MAINTAINCOUNT);
2507 cm_MergeStatus(dscp, scp, &bbp->stats[j], &volSync, userp, reqp, 0);
2508 lock_ReleaseWrite(&scp->rw);
2510 lock_ReleaseRead(&scp->rw);
2512 cm_ReleaseSCache(scp);
2514 } /* all files in the response */
2515 /* now tell it to drop the count,
2516 * after doing the vnode processing above */
2517 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2518 } /* while there are still more files to process */
2523 /* called with a write locked scp and a pointer to a buffer. Make bulk stat
2524 * calls on all undeleted files in the page of the directory specified.
2527 cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
2533 osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%p", dscp);
2535 /* should be on a buffer boundary */
2536 osi_assertx((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0, "invalid offset");
2538 bbp = malloc(sizeof(cm_bulkStat_t));
2539 memset(bbp, 0, sizeof(cm_bulkStat_t));
2540 bbp->bufOffset = *offsetp;
2542 lock_ReleaseWrite(&dscp->rw);
2543 /* first, assemble the file IDs we need to stat */
2544 code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) bbp, offsetp, userp, reqp, NULL);
2546 /* if we failed, bail out early */
2547 if (code && code != CM_ERROR_STOPNOW) {
2549 lock_ObtainWrite(&dscp->rw);
2553 code = cm_TryBulkStatRPC(dscp, bbp, userp, reqp);
2554 osi_Log1(afsd_logp, "END cm_TryBulkStat code 0x%x", code);
2556 lock_ObtainWrite(&dscp->rw);
2561 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
2565 /* initialize store back mask as inexpensive local variable */
2567 memset(statusp, 0, sizeof(AFSStoreStatus));
2569 /* copy out queued info from scache first, if scp passed in */
2571 if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
2572 statusp->ClientModTime = scp->clientModTime;
2573 mask |= AFS_SETMODTIME;
2574 scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
2579 /* now add in our locally generated request */
2580 if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
2581 statusp->ClientModTime = attrp->clientModTime;
2582 mask |= AFS_SETMODTIME;
2584 if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
2585 statusp->UnixModeBits = attrp->unixModeBits;
2586 mask |= AFS_SETMODE;
2588 if (attrp->mask & CM_ATTRMASK_OWNER) {
2589 statusp->Owner = attrp->owner;
2590 mask |= AFS_SETOWNER;
2592 if (attrp->mask & CM_ATTRMASK_GROUP) {
2593 statusp->Group = attrp->group;
2594 mask |= AFS_SETGROUP;
2597 statusp->Mask = mask;
2600 /* set the file size, and make sure that all relevant buffers have been
2601 * truncated. Ensure that any partially truncated buffers have been zeroed
2602 * to the end of the buffer.
2604 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
2610 /* start by locking out buffer creation */
2611 lock_ObtainWrite(&scp->bufCreateLock);
2613 /* verify that this is a file, not a dir or a symlink */
2614 lock_ObtainWrite(&scp->rw);
2615 code = cm_SyncOp(scp, NULL, userp, reqp, 0,
2616 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2619 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2621 if (scp->fileType != CM_SCACHETYPE_FILE) {
2622 code = CM_ERROR_ISDIR;
2627 if (LargeIntegerLessThan(*sizep, scp->length))
2632 lock_ReleaseWrite(&scp->rw);
2634 /* can't hold scp->rw lock here, since we may wait for a storeback to
2635 * finish if the buffer package is cleaning a buffer by storing it to
2639 buf_Truncate(scp, userp, reqp, sizep);
2641 /* now ensure that file length is short enough, and update truncPos */
2642 lock_ObtainWrite(&scp->rw);
2644 /* make sure we have a callback (so we have the right value for the
2645 * length), and wait for it to be safe to do a truncate.
2647 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
2648 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2649 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2651 /* If we only have 'i' bits, then we should still be able to set
2652 the size of a file we created. */
2653 if (code == CM_ERROR_NOACCESS && scp->creator == userp) {
2654 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
2655 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2656 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2662 if (LargeIntegerLessThan(*sizep, scp->length)) {
2663 /* a real truncation. If truncPos is not set yet, or is bigger
2664 * than where we're truncating the file, set truncPos to this
2669 if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
2670 || LargeIntegerLessThan(*sizep, scp->length)) {
2672 scp->truncPos = *sizep;
2673 scp->mask |= CM_SCACHEMASK_TRUNCPOS;
2675 /* in either case, the new file size has been changed */
2676 scp->length = *sizep;
2677 scp->mask |= CM_SCACHEMASK_LENGTH;
2679 else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
2680 /* really extending the file */
2681 scp->length = *sizep;
2682 scp->mask |= CM_SCACHEMASK_LENGTH;
2685 /* done successfully */
2688 cm_SyncOpDone(scp, NULL,
2689 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2690 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2693 lock_ReleaseWrite(&scp->rw);
2694 lock_ReleaseWrite(&scp->bufCreateLock);
2699 /* set the file size or other attributes (but not both at once) */
2700 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
2704 AFSFetchStatus afsOutStatus;
2708 AFSStoreStatus afsInStatus;
2709 struct rx_connection * rxconnp;
2711 memset(&volSync, 0, sizeof(volSync));
2713 /* handle file length setting */
2714 if (attrp->mask & CM_ATTRMASK_LENGTH)
2715 return cm_SetLength(scp, &attrp->length, userp, reqp);
2717 lock_ObtainWrite(&scp->rw);
2718 /* Check for RO volume */
2719 if (scp->flags & CM_SCACHEFLAG_RO) {
2720 code = CM_ERROR_READONLY;
2721 lock_ReleaseWrite(&scp->rw);
2725 /* otherwise, we have to make an RPC to get the status */
2726 code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
2728 lock_ReleaseWrite(&scp->rw);
2731 lock_ConvertWToR(&scp->rw);
2733 /* make the attr structure */
2734 cm_StatusFromAttr(&afsInStatus, scp, attrp);
2736 tfid.Volume = scp->fid.volume;
2737 tfid.Vnode = scp->fid.vnode;
2738 tfid.Unique = scp->fid.unique;
2739 lock_ReleaseRead(&scp->rw);
2741 /* now make the RPC */
2742 osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%p", scp);
2744 code = cm_ConnFromFID(&scp->fid, userp, reqp, &connp);
2748 rxconnp = cm_GetRxConn(connp);
2749 code = RXAFS_StoreStatus(rxconnp, &tfid,
2750 &afsInStatus, &afsOutStatus, &volSync);
2751 rx_PutConnection(rxconnp);
2753 } while (cm_Analyze(connp, userp, reqp,
2754 &scp->fid, &volSync, NULL, NULL, code));
2755 code = cm_MapRPCError(code, reqp);
2758 osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
2760 osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
2762 lock_ObtainWrite(&scp->rw);
2764 cm_MergeStatus(NULL, scp, &afsOutStatus, &volSync, userp, reqp,
2765 CM_MERGEFLAG_FORCE|CM_MERGEFLAG_STOREDATA);
2766 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
2768 /* if we're changing the mode bits, discard the ACL cache,
2769 * since we changed the mode bits.
2771 if (afsInStatus.Mask & AFS_SETMODE)
2772 cm_FreeAllACLEnts(scp);
2773 lock_ReleaseWrite(&scp->rw);
2777 long cm_Create(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
2778 cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
2783 cm_callbackRequest_t cbReq;
2786 cm_scache_t *scp = NULL;
2788 AFSStoreStatus inStatus;
2789 AFSFetchStatus updatedDirStatus;
2790 AFSFetchStatus newFileStatus;
2791 AFSCallBack newFileCallback;
2793 struct rx_connection * rxconnp;
2795 fschar_t * fnamep = NULL;
2797 memset(&volSync, 0, sizeof(volSync));
2799 /* can't create names with @sys in them; must expand it manually first.
2800 * return "invalid request" if they try.
2802 if (cm_ExpandSysName(cnamep, NULL, 0, 0)) {
2803 return CM_ERROR_ATSYS;
2806 #ifdef AFS_FREELANCE_CLIENT
2807 /* Freelance root volume does not hold files */
2808 if (cm_freelanceEnabled &&
2809 dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
2810 dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
2812 return CM_ERROR_NOACCESS;
2814 #endif /* AFS_FREELANCE_CLIENT */
2816 /* Check for RO volume */
2817 if (dscp->flags & CM_SCACHEFLAG_RO)
2818 return CM_ERROR_READONLY;
2820 /* before starting the RPC, mark that we're changing the file data, so
2821 * that someone who does a chmod will know to wait until our call
2824 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
2826 lock_ObtainWrite(&dscp->rw);
2827 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2828 lock_ReleaseWrite(&dscp->rw);
2830 cm_StartCallbackGrantingCall(NULL, &cbReq);
2832 cm_EndDirOp(&dirop);
2839 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
2841 cm_StatusFromAttr(&inStatus, NULL, attrp);
2843 /* try the RPC now */
2844 osi_Log1(afsd_logp, "CALL CreateFile scp 0x%p", dscp);
2846 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2850 dirAFSFid.Volume = dscp->fid.volume;
2851 dirAFSFid.Vnode = dscp->fid.vnode;
2852 dirAFSFid.Unique = dscp->fid.unique;
2854 rxconnp = cm_GetRxConn(connp);
2855 code = RXAFS_CreateFile(connp->rxconnp, &dirAFSFid, fnamep,
2856 &inStatus, &newAFSFid, &newFileStatus,
2857 &updatedDirStatus, &newFileCallback,
2859 rx_PutConnection(rxconnp);
2861 } while (cm_Analyze(connp, userp, reqp,
2862 &dscp->fid, &volSync, NULL, &cbReq, code));
2863 code = cm_MapRPCError(code, reqp);
2866 osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
2868 osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
2871 lock_ObtainWrite(&dirop.scp->dirlock);
2872 dirop.lockType = CM_DIRLOCK_WRITE;
2874 lock_ObtainWrite(&dscp->rw);
2876 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
2877 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2878 lock_ReleaseWrite(&dscp->rw);
2880 /* now try to create the file's entry, too, but be careful to
2881 * make sure that we don't merge in old info. Since we weren't locking
2882 * out any requests during the file's creation, we may have pretty old
2886 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
2887 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2889 lock_ObtainWrite(&scp->rw);
2890 scp->creator = userp; /* remember who created it */
2891 if (!cm_HaveCallback(scp)) {
2892 cm_EndCallbackGrantingCall(scp, &cbReq,
2893 &newFileCallback, &volSync, 0);
2894 cm_MergeStatus(dscp, scp, &newFileStatus, &volSync,
2898 lock_ReleaseWrite(&scp->rw);
2902 /* make sure we end things properly */
2904 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2906 if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
2907 cm_DirCreateEntry(&dirop, fnamep, &newFid);
2909 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
2912 cm_EndDirOp(&dirop);
2921 cm_ReleaseSCache(scp);
2927 * locked if TRUE means write-locked
2928 * else the cm_scache_t rw must not be held
2930 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp, afs_uint32 locked)
2935 lock_ReleaseWrite(&scp->rw);
2937 osi_Log2(afsd_logp, "cm_FSync scp 0x%p userp 0x%p", scp, userp);
2939 code = buf_CleanVnode(scp, userp, reqp);
2941 lock_ObtainWrite(&scp->rw);
2943 if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
2944 | CM_SCACHEMASK_CLIENTMODTIME
2945 | CM_SCACHEMASK_LENGTH))
2946 code = cm_StoreMini(scp, userp, reqp);
2948 if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) {
2949 code = (scp->flags & CM_SCACHEFLAG_OVERQUOTA) ? CM_ERROR_QUOTA : CM_ERROR_SPACE;
2950 scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE);
2954 lock_ReleaseWrite(&scp->rw);
2955 } else if (locked) {
2956 lock_ObtainWrite(&scp->rw);
2961 long cm_MakeDir(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
2962 cm_user_t *userp, cm_req_t *reqp, cm_scache_t **scpp)
2967 cm_callbackRequest_t cbReq;
2970 cm_scache_t *scp = NULL;
2972 AFSStoreStatus inStatus;
2973 AFSFetchStatus updatedDirStatus;
2974 AFSFetchStatus newDirStatus;
2975 AFSCallBack newDirCallback;
2977 struct rx_connection * rxconnp;
2979 fschar_t * fnamep = NULL;
2981 memset(&volSync, 0, sizeof(volSync));
2983 /* can't create names with @sys in them; must expand it manually first.
2984 * return "invalid request" if they try.
2986 if (cm_ExpandSysName(cnamep, NULL, 0, 0)) {
2987 return CM_ERROR_ATSYS;
2990 #ifdef AFS_FREELANCE_CLIENT
2991 /* Freelance root volume does not hold subdirectories */
2992 if (cm_freelanceEnabled &&
2993 dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
2994 dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
2996 return CM_ERROR_NOACCESS;
2998 #endif /* AFS_FREELANCE_CLIENT */
3000 /* Check for RO volume */
3001 if (dscp->flags & CM_SCACHEFLAG_RO)
3002 return CM_ERROR_READONLY;
3004 /* before starting the RPC, mark that we're changing the directory
3005 * data, so that someone who does a chmod on the dir will wait until
3006 * our call completes.
3008 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3010 lock_ObtainWrite(&dscp->rw);
3011 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3012 lock_ReleaseWrite(&dscp->rw);
3014 cm_StartCallbackGrantingCall(NULL, &cbReq);
3016 cm_EndDirOp(&dirop);
3023 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3024 cm_StatusFromAttr(&inStatus, NULL, attrp);
3026 /* try the RPC now */
3027 osi_Log1(afsd_logp, "CALL MakeDir scp 0x%p", dscp);
3029 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3033 dirAFSFid.Volume = dscp->fid.volume;
3034 dirAFSFid.Vnode = dscp->fid.vnode;
3035 dirAFSFid.Unique = dscp->fid.unique;
3037 rxconnp = cm_GetRxConn(connp);
3038 code = RXAFS_MakeDir(connp->rxconnp, &dirAFSFid, fnamep,
3039 &inStatus, &newAFSFid, &newDirStatus,
3040 &updatedDirStatus, &newDirCallback,
3042 rx_PutConnection(rxconnp);
3044 } while (cm_Analyze(connp, userp, reqp,
3045 &dscp->fid, &volSync, NULL, &cbReq, code));
3046 code = cm_MapRPCError(code, reqp);
3049 osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
3051 osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
3054 lock_ObtainWrite(&dirop.scp->dirlock);
3055 dirop.lockType = CM_DIRLOCK_WRITE;
3057 lock_ObtainWrite(&dscp->rw);
3059 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3060 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3061 lock_ReleaseWrite(&dscp->rw);
3063 /* now try to create the new dir's entry, too, but be careful to
3064 * make sure that we don't merge in old info. Since we weren't locking
3065 * out any requests during the file's creation, we may have pretty old
3069 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3070 code = cm_GetSCache(&newFid, &scp, userp, reqp);
3072 lock_ObtainWrite(&scp->rw);
3073 if (!cm_HaveCallback(scp)) {
3074 cm_EndCallbackGrantingCall(scp, &cbReq,
3075 &newDirCallback, &volSync, 0);
3076 cm_MergeStatus(dscp, scp, &newDirStatus, &volSync,
3080 lock_ReleaseWrite(&scp->rw);
3084 /* make sure we end things properly */
3086 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
3088 if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
3089 cm_DirCreateEntry(&dirop, fnamep, &newFid);
3091 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
3094 cm_EndDirOp(&dirop);
3102 cm_ReleaseSCache(scp);
3105 /* and return error code */
3109 long cm_Link(cm_scache_t *dscp, clientchar_t *cnamep, cm_scache_t *sscp, long flags,
3110 cm_user_t *userp, cm_req_t *reqp)
3115 AFSFid existingAFSFid;
3116 AFSFetchStatus updatedDirStatus;
3117 AFSFetchStatus newLinkStatus;
3119 struct rx_connection * rxconnp;
3121 fschar_t * fnamep = NULL;
3123 memset(&volSync, 0, sizeof(volSync));
3125 if (dscp->fid.cell != sscp->fid.cell ||
3126 dscp->fid.volume != sscp->fid.volume) {
3127 return CM_ERROR_CROSSDEVLINK;
3130 /* Check for RO volume */
3131 if (dscp->flags & CM_SCACHEFLAG_RO)
3132 return CM_ERROR_READONLY;
3134 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3136 lock_ObtainWrite(&dscp->rw);
3137 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3138 lock_ReleaseWrite(&dscp->rw);
3140 cm_EndDirOp(&dirop);
3145 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3147 /* try the RPC now */
3148 osi_Log1(afsd_logp, "CALL Link scp 0x%p", dscp);
3150 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3153 dirAFSFid.Volume = dscp->fid.volume;
3154 dirAFSFid.Vnode = dscp->fid.vnode;
3155 dirAFSFid.Unique = dscp->fid.unique;
3157 existingAFSFid.Volume = sscp->fid.volume;
3158 existingAFSFid.Vnode = sscp->fid.vnode;
3159 existingAFSFid.Unique = sscp->fid.unique;
3161 rxconnp = cm_GetRxConn(connp);
3162 code = RXAFS_Link(rxconnp, &dirAFSFid, fnamep, &existingAFSFid,
3163 &newLinkStatus, &updatedDirStatus, &volSync);
3164 rx_PutConnection(rxconnp);
3165 osi_Log1(afsd_logp," RXAFS_Link returns 0x%x", code);
3167 } while (cm_Analyze(connp, userp, reqp,
3168 &dscp->fid, &volSync, NULL, NULL, code));
3170 code = cm_MapRPCError(code, reqp);
3173 osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
3175 osi_Log0(afsd_logp, "CALL Link SUCCESS");
3178 lock_ObtainWrite(&dirop.scp->dirlock);
3179 dirop.lockType = CM_DIRLOCK_WRITE;
3181 lock_ObtainWrite(&dscp->rw);
3183 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3185 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3186 lock_ReleaseWrite(&dscp->rw);
3189 if (cm_CheckDirOpForSingleChange(&dirop)) {
3190 cm_DirCreateEntry(&dirop, fnamep, &sscp->fid);
3192 cm_BPlusDirCreateEntry(&dirop, cnamep, &sscp->fid);
3196 cm_EndDirOp(&dirop);
3198 /* Update the linked object status */
3200 lock_ObtainWrite(&sscp->rw);
3201 cm_MergeStatus(NULL, sscp, &newLinkStatus, &volSync, userp, reqp, 0);
3202 lock_ReleaseWrite(&sscp->rw);
3210 long cm_SymLink(cm_scache_t *dscp, clientchar_t *cnamep, fschar_t *contentsp, long flags,
3211 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
3219 AFSStoreStatus inStatus;
3220 AFSFetchStatus updatedDirStatus;
3221 AFSFetchStatus newLinkStatus;
3223 struct rx_connection * rxconnp;
3225 fschar_t *fnamep = NULL;
3227 /* Check for RO volume */
3228 if (dscp->flags & CM_SCACHEFLAG_RO)
3229 return CM_ERROR_READONLY;
3231 memset(&volSync, 0, sizeof(volSync));
3233 /* before starting the RPC, mark that we're changing the directory data,
3234 * so that someone who does a chmod on the dir will wait until our
3237 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3239 lock_ObtainWrite(&dscp->rw);
3240 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3241 lock_ReleaseWrite(&dscp->rw);
3243 cm_EndDirOp(&dirop);
3248 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3250 cm_StatusFromAttr(&inStatus, NULL, attrp);
3252 /* try the RPC now */
3253 osi_Log1(afsd_logp, "CALL Symlink scp 0x%p", dscp);
3255 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3259 dirAFSFid.Volume = dscp->fid.volume;
3260 dirAFSFid.Vnode = dscp->fid.vnode;
3261 dirAFSFid.Unique = dscp->fid.unique;
3263 rxconnp = cm_GetRxConn(connp);
3264 code = RXAFS_Symlink(rxconnp, &dirAFSFid, fnamep, contentsp,
3265 &inStatus, &newAFSFid, &newLinkStatus,
3266 &updatedDirStatus, &volSync);
3267 rx_PutConnection(rxconnp);
3269 } while (cm_Analyze(connp, userp, reqp,
3270 &dscp->fid, &volSync, NULL, NULL, code));
3271 code = cm_MapRPCError(code, reqp);
3274 osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
3276 osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
3279 lock_ObtainWrite(&dirop.scp->dirlock);
3280 dirop.lockType = CM_DIRLOCK_WRITE;
3282 lock_ObtainWrite(&dscp->rw);
3284 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3285 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3286 lock_ReleaseWrite(&dscp->rw);
3289 if (cm_CheckDirOpForSingleChange(&dirop)) {
3290 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3292 cm_DirCreateEntry(&dirop, fnamep, &newFid);
3294 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
3298 cm_EndDirOp(&dirop);
3300 /* now try to create the new dir's entry, too, but be careful to
3301 * make sure that we don't merge in old info. Since we weren't locking
3302 * out any requests during the file's creation, we may have pretty old
3306 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3307 code = cm_GetSCache(&newFid, &scp, userp, reqp);
3309 lock_ObtainWrite(&scp->rw);
3310 if (!cm_HaveCallback(scp)) {
3311 cm_MergeStatus(dscp, scp, &newLinkStatus, &volSync,
3314 lock_ReleaseWrite(&scp->rw);
3315 cm_ReleaseSCache(scp);
3321 /* and return error code */
3325 /*! \brief Remove a directory
3327 Encapsulates a call to RXAFS_RemoveDir().
3329 \param[in] dscp cm_scache_t for the directory containing the
3330 directory to be removed.
3332 \param[in] fnamep This will be the original name of the directory
3333 as known to the file server. It will be passed in to RXAFS_RemoveDir().
3334 This parameter is optional. If it is not provided the value
3337 \param[in] cnamep Normalized name used to update the local
3340 \param[in] userp cm_user_t for the request.
3342 \param[in] reqp Request tracker.
3344 long cm_RemoveDir(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t *cnamep, cm_user_t *userp, cm_req_t *reqp)
3350 AFSFetchStatus updatedDirStatus;
3352 struct rx_connection * rxconnp;
3354 cm_scache_t *scp = NULL;
3355 int free_fnamep = FALSE;
3357 memset(&volSync, 0, sizeof(volSync));
3359 if (fnamep == NULL) {
3362 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ,
3363 CM_DIROP_FLAG_NONE, &dirop);
3365 code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
3368 cm_EndDirOp(&dirop);
3375 code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
3379 /* Check for RO volume */
3380 if (dscp->flags & CM_SCACHEFLAG_RO) {
3381 code = CM_ERROR_READONLY;
3385 /* before starting the RPC, mark that we're changing the directory data,
3386 * so that someone who does a chmod on the dir will wait until our
3389 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3391 lock_ObtainWrite(&dscp->rw);
3392 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3393 lock_ReleaseWrite(&dscp->rw);
3395 cm_EndDirOp(&dirop);
3400 /* try the RPC now */
3401 osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%p", dscp);
3403 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3407 dirAFSFid.Volume = dscp->fid.volume;
3408 dirAFSFid.Vnode = dscp->fid.vnode;
3409 dirAFSFid.Unique = dscp->fid.unique;
3411 rxconnp = cm_GetRxConn(connp);
3412 code = RXAFS_RemoveDir(rxconnp, &dirAFSFid, fnamep,
3413 &updatedDirStatus, &volSync);
3414 rx_PutConnection(rxconnp);
3416 } while (cm_Analyze(connp, userp, reqp,
3417 &dscp->fid, &volSync, NULL, NULL, code));
3418 code = cm_MapRPCErrorRmdir(code, reqp);
3421 osi_Log1(afsd_logp, "CALL RemoveDir FAILURE, code 0x%x", code);
3423 osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
3426 lock_ObtainWrite(&dirop.scp->dirlock);
3427 dirop.lockType = CM_DIRLOCK_WRITE;
3429 lock_ObtainWrite(&dscp->rw);
3431 cm_dnlcRemove(dscp, cnamep);
3432 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3434 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3435 lock_ReleaseWrite(&dscp->rw);
3438 if (cm_CheckDirOpForSingleChange(&dirop) && cnamep != NULL) {
3439 cm_DirDeleteEntry(&dirop, fnamep);
3441 cm_BPlusDirDeleteEntry(&dirop, cnamep);
3445 cm_EndDirOp(&dirop);
3448 cm_ReleaseSCache(scp);
3450 lock_ObtainWrite(&scp->rw);
3451 scp->flags |= CM_SCACHEFLAG_DELETED;
3452 lock_ReleaseWrite(&scp->rw);
3460 /* and return error code */
3464 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
3466 /* grab mutex on contents */
3467 lock_ObtainWrite(&scp->rw);
3469 /* reset the prefetch info */
3470 scp->prefetch.base.LowPart = 0; /* base */
3471 scp->prefetch.base.HighPart = 0;
3472 scp->prefetch.end.LowPart = 0; /* and end */
3473 scp->prefetch.end.HighPart = 0;
3475 /* release mutex on contents */
3476 lock_ReleaseWrite(&scp->rw);
3482 /*! \brief Rename a file or directory
3484 Encapsulates a RXAFS_Rename() call.
3486 \param[in] oldDscp cm_scache_t for the directory containing the old
3489 \param[in] oldNamep The original old name known to the file server.
3490 This is the name that will be passed into the RXAFS_Rename().
3491 If it is not provided, it will be looked up.
3493 \param[in] normalizedOldNamep Normalized old name. This is used for
3494 updating local directory caches.
3496 \param[in] newDscp cm_scache_t for the directory containing the new
3499 \param[in] newNamep New name. Normalized.
3501 \param[in] userp cm_user_t for the request.
3503 \param[in,out] reqp Request tracker.
3506 long cm_Rename(cm_scache_t *oldDscp, fschar_t *oldNamep, clientchar_t *cOldNamep,
3507 cm_scache_t *newDscp, clientchar_t *cNewNamep, cm_user_t *userp,
3512 AFSFid oldDirAFSFid;
3513 AFSFid newDirAFSFid;
3515 AFSFetchStatus updatedOldDirStatus;
3516 AFSFetchStatus updatedNewDirStatus;
3519 struct rx_connection * rxconnp;
3520 cm_dirOp_t oldDirOp;
3523 cm_dirOp_t newDirOp;
3524 fschar_t * newNamep = NULL;
3525 int free_oldNamep = FALSE;
3526 cm_scache_t *oldScp = NULL, *newScp = NULL;
3528 memset(&volSync, 0, sizeof(volSync));
3530 if (cOldNamep == NULL || cNewNamep == NULL ||
3531 cm_ClientStrLen(cOldNamep) == 0 ||
3532 cm_ClientStrLen(cNewNamep) == 0)
3533 return CM_ERROR_INVAL;
3536 * Before we permit the operation, make sure that we do not already have
3537 * an object in the destination directory that has a case-insensitive match
3538 * for this name UNLESS the matching object is the object we are renaming.
3540 code = cm_Lookup(oldDscp, cOldNamep, 0, userp, reqp, &oldScp);
3542 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S old name lookup failed",
3543 oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3547 code = cm_Lookup(newDscp, cNewNamep, CM_FLAG_CASEFOLD, userp, reqp, &newScp);
3549 /* found a matching object with the new name */
3550 if (cm_FidCmp(&oldScp->fid, &newScp->fid)) {
3551 /* and they don't match so return an error */
3552 osi_Log2(afsd_logp, "cm_Rename newDscp 0x%p cNewName %S new name already exists",
3553 newDscp, osi_LogSaveStringW(afsd_logp, cNewNamep));
3554 code = CM_ERROR_EXISTS;
3556 cm_ReleaseSCache(newScp);
3558 } else if (code == CM_ERROR_AMBIGUOUS_FILENAME) {
3559 code = CM_ERROR_EXISTS;
3564 /* Check for RO volume */
3566 (oldDscp->flags & CM_SCACHEFLAG_RO) || (newDscp->flags & CM_SCACHEFLAG_RO)) {
3567 code = CM_ERROR_READONLY;
3573 if (oldNamep == NULL) {
3576 code = cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_READ,
3577 CM_DIROP_FLAG_NONE, &oldDirOp);
3579 code = cm_BPlusDirLookupOriginalName(&oldDirOp, cOldNamep, &oldNamep);
3581 free_oldNamep = TRUE;
3582 cm_EndDirOp(&oldDirOp);
3586 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S Original Name lookup failed",
3587 oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3593 /* before starting the RPC, mark that we're changing the directory data,
3594 * so that someone who does a chmod on the dir will wait until our call
3595 * completes. We do this in vnode order so that we don't deadlock,
3596 * which makes the code a little verbose.
3598 if (oldDscp == newDscp) {
3599 /* check for identical names */
3600 if (cm_ClientStrCmp(cOldNamep, cNewNamep) == 0) {
3601 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_RENAME_IDENTICAL",
3603 code = CM_ERROR_RENAME_IDENTICAL;
3608 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE,
3609 CM_DIROP_FLAG_NONE, &oldDirOp);
3610 lock_ObtainWrite(&oldDscp->rw);
3611 cm_dnlcRemove(oldDscp, cOldNamep);
3612 cm_dnlcRemove(oldDscp, cNewNamep);
3613 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3614 CM_SCACHESYNC_STOREDATA);
3615 lock_ReleaseWrite(&oldDscp->rw);
3617 cm_EndDirOp(&oldDirOp);
3621 /* two distinct dir vnodes */
3623 if (oldDscp->fid.cell != newDscp->fid.cell ||
3624 oldDscp->fid.volume != newDscp->fid.volume) {
3625 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_CROSSDEVLINK",
3627 code = CM_ERROR_CROSSDEVLINK;
3631 /* shouldn't happen that we have distinct vnodes for two
3632 * different files, but could due to deliberate attack, or
3633 * stale info. Avoid deadlocks and quit now.
3635 if (oldDscp->fid.vnode == newDscp->fid.vnode) {
3636 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p vnode collision",
3638 code = CM_ERROR_CROSSDEVLINK;
3642 if (oldDscp->fid.vnode < newDscp->fid.vnode) {
3643 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE,
3644 CM_DIROP_FLAG_NONE, &oldDirOp);
3645 lock_ObtainWrite(&oldDscp->rw);
3646 cm_dnlcRemove(oldDscp, cOldNamep);
3647 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3648 CM_SCACHESYNC_STOREDATA);
3649 lock_ReleaseWrite(&oldDscp->rw);
3651 cm_EndDirOp(&oldDirOp);
3653 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE,
3654 CM_DIROP_FLAG_NONE, &newDirOp);
3655 lock_ObtainWrite(&newDscp->rw);
3656 cm_dnlcRemove(newDscp, cNewNamep);
3657 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3658 CM_SCACHESYNC_STOREDATA);
3659 lock_ReleaseWrite(&newDscp->rw);
3661 cm_EndDirOp(&newDirOp);
3663 /* cleanup first one */
3664 lock_ObtainWrite(&oldDscp->rw);
3665 cm_SyncOpDone(oldDscp, NULL,
3666 CM_SCACHESYNC_STOREDATA);
3667 lock_ReleaseWrite(&oldDscp->rw);
3668 cm_EndDirOp(&oldDirOp);
3673 /* lock the new vnode entry first */
3674 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE,
3675 CM_DIROP_FLAG_NONE, &newDirOp);
3676 lock_ObtainWrite(&newDscp->rw);
3677 cm_dnlcRemove(newDscp, cNewNamep);
3678 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3679 CM_SCACHESYNC_STOREDATA);
3680 lock_ReleaseWrite(&newDscp->rw);
3682 cm_EndDirOp(&newDirOp);
3684 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE,
3685 CM_DIROP_FLAG_NONE, &oldDirOp);
3686 lock_ObtainWrite(&oldDscp->rw);
3687 cm_dnlcRemove(oldDscp, cOldNamep);
3688 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3689 CM_SCACHESYNC_STOREDATA);
3690 lock_ReleaseWrite(&oldDscp->rw);
3692 cm_EndDirOp(&oldDirOp);
3694 /* cleanup first one */
3695 lock_ObtainWrite(&newDscp->rw);
3696 cm_SyncOpDone(newDscp, NULL,
3697 CM_SCACHESYNC_STOREDATA);
3698 lock_ReleaseWrite(&newDscp->rw);
3699 cm_EndDirOp(&newDirOp);
3703 } /* two distinct vnodes */
3710 newNamep = cm_ClientStringToFsStringAlloc(cNewNamep, -1, NULL);
3712 /* try the RPC now */
3713 osi_Log2(afsd_logp, "CALL Rename old scp 0x%p new scp 0x%p",
3716 code = cm_ConnFromFID(&oldDscp->fid, userp, reqp, &connp);
3720 oldDirAFSFid.Volume = oldDscp->fid.volume;
3721 oldDirAFSFid.Vnode = oldDscp->fid.vnode;
3722 oldDirAFSFid.Unique = oldDscp->fid.unique;
3723 newDirAFSFid.Volume = newDscp->fid.volume;
3724 newDirAFSFid.Vnode = newDscp->fid.vnode;
3725 newDirAFSFid.Unique = newDscp->fid.unique;
3727 rxconnp = cm_GetRxConn(connp);
3728 code = RXAFS_Rename(rxconnp, &oldDirAFSFid, oldNamep,
3729 &newDirAFSFid, newNamep,
3730 &updatedOldDirStatus, &updatedNewDirStatus,
3732 rx_PutConnection(rxconnp);
3734 } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
3735 &volSync, NULL, NULL, code));
3736 code = cm_MapRPCError(code, reqp);
3739 osi_Log1(afsd_logp, "CALL Rename FAILURE, code 0x%x", code);
3741 osi_Log0(afsd_logp, "CALL Rename SUCCESS");
3743 /* update the individual stat cache entries for the directories */
3745 lock_ObtainWrite(&oldDirOp.scp->dirlock);
3746 oldDirOp.lockType = CM_DIRLOCK_WRITE;
3748 lock_ObtainWrite(&oldDscp->rw);
3751 cm_MergeStatus(NULL, oldDscp, &updatedOldDirStatus, &volSync,
3752 userp, reqp, CM_MERGEFLAG_DIROP);
3753 cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
3754 lock_ReleaseWrite(&oldDscp->rw);
3756 if (code == 0 && cm_CheckDirOpForSingleChange(&oldDirOp)) {
3758 diropCode = cm_BPlusDirLookup(&oldDirOp, cOldNamep, &fileFid);
3759 if (diropCode == CM_ERROR_INEXACT_MATCH)
3761 else if (diropCode == EINVAL)
3763 diropCode = cm_DirLookup(&oldDirOp, oldNamep, &fileFid);
3765 if (diropCode == 0) {
3767 diropCode = cm_DirCreateEntry(&oldDirOp, newNamep, &fileFid);
3769 cm_BPlusDirCreateEntry(&oldDirOp, cNewNamep, &fileFid);
3773 if (diropCode == 0) {
3774 diropCode = cm_DirDeleteEntry(&oldDirOp, oldNamep);
3776 cm_BPlusDirDeleteEntry(&oldDirOp, cOldNamep);
3781 cm_EndDirOp(&oldDirOp);
3783 /* and update it for the new one, too, if necessary */
3786 lock_ObtainWrite(&newDirOp.scp->dirlock);
3787 newDirOp.lockType = CM_DIRLOCK_WRITE;
3789 lock_ObtainWrite(&newDscp->rw);
3791 cm_MergeStatus(NULL, newDscp, &updatedNewDirStatus, &volSync,
3792 userp, reqp, CM_MERGEFLAG_DIROP);
3793 cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
3794 lock_ReleaseWrite(&newDscp->rw);
3798 * The following optimization does not work.
3799 * When the file server processed a RXAFS_Rename() request the
3800 * FID of the object being moved between directories is not
3801 * preserved. The client does not know the new FID nor the
3802 * version number of the target. Not only can we not create
3803 * the directory entry in the new directory, but we can't
3804 * preserve the cached data for the file. It must be re-read
3805 * from the file server. - jaltman, 2009/02/20
3808 /* we only make the local change if we successfully made
3809 the change in the old directory AND there was only one
3810 change in the new directory */
3811 if (diropCode == 0 && cm_CheckDirOpForSingleChange(&newDirOp)) {
3812 cm_DirCreateEntry(&newDirOp, newNamep, &fileFid);
3814 cm_BPlusDirCreateEntry(&newDirOp, cNewNamep, &fileFid);
3819 cm_EndDirOp(&newDirOp);
3823 * After the rename the file server has invalidated the callbacks
3824 * on the file that was moved nor do we have a directory reference
3827 lock_ObtainWrite(&oldScp->rw);
3828 cm_DiscardSCache(oldScp);
3829 lock_ReleaseWrite(&oldScp->rw);
3833 cm_ReleaseSCache(oldScp);
3840 /* and return error code */
3844 /* Byte range locks:
3846 The OpenAFS Windows client has to fake byte range locks given no
3847 server side support for such locks. This is implemented as keyed
3848 byte range locks on the cache manager.
3850 Keyed byte range locks:
3852 Each cm_scache_t structure keeps track of a list of keyed locks.
3853 The key for a lock identifies an owner of a set of locks (referred
3854 to as a client). Each key is represented by a value. The set of
3855 key values used within a specific cm_scache_t structure form a
3856 namespace that has a scope of just that cm_scache_t structure. The
3857 same key value can be used with another cm_scache_t structure and
3858 correspond to a completely different client. However it is
3859 advantageous for the SMB or IFS layer to make sure that there is a
3860 1-1 mapping between client and keys over all cm_scache_t objects.
3862 Assume a client C has key Key(C) (although, since the scope of the
3863 key is a cm_scache_t, the key can be Key(C,S), where S is the
3864 cm_scache_t. But assume a 1-1 relation between keys and clients).
3865 A byte range (O,+L) denotes byte addresses (O) through (O+L-1)
3866 inclusive (a.k.a. [O,O+L-1]). The function Key(x) is implemented
3867 through cm_generateKey() function for both SMB and IFS.
3869 The list of locks for a cm_scache_t object S is maintained in
3870 S->fileLocks. The cache manager will set a lock on the AFS file
3871 server in order to assert the locks in S->fileLocks. If only
3872 shared locks are in place for S, then the cache manager will obtain
3873 a LockRead lock, while if there are any exclusive locks, it will
3874 obtain a LockWrite lock. If the exclusive locks are all released
3875 while the shared locks remain, then the cache manager will
3876 downgrade the lock from LockWrite to LockRead. Similarly, if an
3877 exclusive lock is obtained when only shared locks exist, then the
3878 cache manager will try to upgrade the lock from LockRead to
3881 Each lock L owned by client C maintains a key L->key such that
3882 L->key == Key(C), the effective range defined by L->LOffset and
3883 L->LLength such that the range of bytes affected by the lock is
3884 (L->LOffset, +L->LLength), a type maintained in L->LockType which
3885 is either exclusive or shared.
3889 A lock exists iff it is in S->fileLocks for some cm_scache_t
3890 S. Existing locks are in one of the following states: ACTIVE,
3891 WAITLOCK, WAITUNLOCK, LOST, DELETED.
3893 The following sections describe each lock and the associated
3896 1. ACTIVE: A lock L is ACTIVE iff the cache manager has asserted
3897 the lock with the AFS file server. This type of lock can be
3898 exercised by a client to read or write to the locked region (as
3901 1.1 ACTIVE->LOST: When the AFS file server fails to extend a
3902 server lock that was required to assert the lock. Before
3903 marking the lock as lost, the cache manager checks if the file
3904 has changed on the server. If the file has not changed, then
3905 the cache manager will attempt to obtain a new server lock
3906 that is sufficient to assert the client side locks for the
3907 file. If any of these fail, the lock is marked as LOST.
3908 Otherwise, it is left as ACTIVE.
3910 1.2 ACTIVE->DELETED: Lock is released.
3912 2. WAITLOCK: A lock is in a WAITLOCK state if the cache manager
3913 grants the lock but the lock is yet to be asserted with the AFS
3914 file server. Once the file server grants the lock, the state
3915 will transition to an ACTIVE lock.
3917 2.1 WAITLOCK->ACTIVE: The server granted the lock.
3919 2.2 WAITLOCK->DELETED: Lock is abandoned, or timed out during
3922 2.3 WAITLOCK->LOST: One or more locks from this client were
3923 marked as LOST. No further locks will be granted to this
3924 client until all lost locks are removed.
3926 3. WAITUNLOCK: A lock is in a WAITUNLOCK state if the cache manager
3927 receives a request for a lock that conflicts with an existing
3928 ACTIVE or WAITLOCK lock. The lock will be placed in the queue
3929 and will be granted at such time the conflicting locks are
3930 removed, at which point the state will transition to either
3933 3.1 WAITUNLOCK->ACTIVE: The conflicting lock was removed. The
3934 current serverLock is sufficient to assert this lock, or a
3935 sufficient serverLock is obtained.
3937 3.2 WAITUNLOCK->WAITLOCK: The conflicting lock was removed,
3938 however the required serverLock is yet to be asserted with the
3941 3.3 WAITUNLOCK->DELETED: The lock is abandoned, timed out or
3944 3.5 WAITUNLOCK->LOST: One or more locks from this client were
3945 marked as LOST. No further locks will be granted to this
3946 client until all lost locks are removed.
3948 4. LOST: A lock L is LOST if the server lock that was required to
3949 assert the lock could not be obtained or if it could not be
3950 extended, or if other locks by the same client were LOST.
3951 Essentially, once a lock is LOST, the contract between the cache
3952 manager and that specific client is no longer valid.
3954 The cache manager rechecks the server lock once every minute and
3955 extends it as appropriate. If this is not done for 5 minutes,
3956 the AFS file server will release the lock (the 5 minute timeout
3957 is based on current file server code and is fairly arbitrary).
3958 Once released, the lock cannot be re-obtained without verifying
3959 that the contents of the file hasn't been modified since the
3960 time the lock was released. Re-obtaining the lock without
3961 verifying this may lead to data corruption. If the lock can not
3962 be obtained safely, then all active locks for the cm_scache_t
3965 4.1 LOST->DELETED: The lock is released.
3967 5. DELETED: The lock is no longer relevant. Eventually, it will
3968 get removed from the cm_scache_t. In the meantime, it will be
3969 treated as if it does not exist.
3971 5.1 DELETED->not exist: The lock is removed from the
3974 The following are classifications of locks based on their state.
3976 6* A lock L is ACCEPTED if it is ACTIVE or WAITLOCK. These locks
3977 have been accepted by the cache manager, but may or may not have
3978 been granted back to the client.
3980 7* A lock L is QUEUED if it is ACTIVE, WAITLOCK or WAITUNLOCK.
3982 8* A lock L is WAITING if it is WAITLOCK or WAITUNLOCK.
3986 A client C can READ range (Offset,+Length) of a file represented by
3987 cm_scache_t S iff (1):
3989 1. for all _a_ in (Offset,+Length), all of the following is true:
3991 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3992 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3995 1.2 For each LOST lock L in S->fileLocks such that _a_ in
3996 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3999 (When locks are lost on an cm_scache_t, all locks are lost. By
4000 4.2 (below), if there is an exclusive LOST lock, then there
4001 can't be any overlapping ACTIVE locks.)
4003 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
4005 2. for all _a_ in (Offset,+Length), one of the following is true:
4007 2.1 Byte _a_ of S is unowned (as specified in 1.1) AND there
4008 does not exist a LOST lock L such that _a_ in
4009 (L->LOffset,+L->LLength).
4011 2.2 Byte _a_ of S is owned by C under lock L (as specified in
4012 1.2) AND L->LockType is exclusive.
4014 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
4016 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
4019 3.1 If L->LockType is exclusive then there does NOT exist a
4020 ACCEPTED lock M in S->fileLocks such that _a_ in
4021 (M->LOffset,+M->LLength).
4023 (If we count all QUEUED locks then we hit cases such as
4024 cascading waiting locks where the locks later on in the queue
4025 can be granted without compromising file integrity. On the
4026 other hand if only ACCEPTED locks are considered, then locks
4027 that were received earlier may end up waiting for locks that
4028 were received later to be unlocked. The choice of ACCEPTED
4029 locks was made to mimic the Windows byte range lock
4032 3.2 If L->LockType is shared then for each ACCEPTED lock M in
4033 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
4034 M->LockType is shared.
4036 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
4038 4.1 M->key != Key(C)
4040 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
4041 and (M->LOffset,+M->LLength) do not intersect.
4043 (Note: If a client loses a lock, it loses all locks.
4044 Subsequently, it will not be allowed to obtain any more locks
4045 until all existing LOST locks that belong to the client are
4046 released. Once all locks are released by a single client,
4047 there exists no further contract between the client and AFS
4048 about the contents of the file, hence the client can then
4049 proceed to obtain new locks and establish a new contract.
4051 This doesn't quite work as you think it should, because most
4052 applications aren't built to deal with losing locks they
4053 thought they once had. For now, we don't have a good
4054 solution to lost locks.
4056 Also, for consistency reasons, we have to hold off on
4057 granting locks that overlap exclusive LOST locks.)
4059 A client C can only unlock locks L in S->fileLocks which have
4062 The representation and invariants are as follows:
4064 - Each cm_scache_t structure keeps:
4066 - A queue of byte-range locks (cm_scache_t::fileLocks) which
4067 are of type cm_file_lock_t.
4069 - A record of the highest server-side lock that has been
4070 obtained for this object (cm_scache_t::serverLock), which is
4071 one of (-1), LockRead, LockWrite.
4073 - A count of ACCEPTED exclusive and shared locks that are in the
4074 queue (cm_scache_t::sharedLocks and
4075 cm_scache_t::exclusiveLocks)
4077 - Each cm_file_lock_t structure keeps:
4079 - The type of lock (cm_file_lock_t::LockType)
4081 - The key associated with the lock (cm_file_lock_t::key)
4083 - The offset and length of the lock (cm_file_lock_t::LOffset
4084 and cm_file_lock_t::LLength)
4086 - The state of the lock.
4088 - Time of issuance or last successful extension
4090 Semantic invariants:
4092 I1. The number of ACCEPTED locks in S->fileLocks are
4093 (S->sharedLocks + S->exclusiveLocks)
4095 External invariants:
4097 I3. S->serverLock is the lock that we have asserted with the
4098 AFS file server for this cm_scache_t.
4100 I4. S->serverLock == LockRead iff there is at least one ACTIVE
4101 shared lock, but no ACTIVE exclusive locks.
4103 I5. S->serverLock == LockWrite iff there is at least one ACTIVE
4106 I6. If L is a LOST lock, then for each lock M in S->fileLocks,
4107 M->key == L->key IMPLIES M is LOST or DELETED.
4112 #define IS_LOCK_ACTIVE(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == 0)
4114 #define IS_LOCK_WAITLOCK(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_WAITLOCK)
4116 #define IS_LOCK_WAITUNLOCK(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_WAITUNLOCK)
4118 #define IS_LOCK_LOST(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_LOST)
4120 #define IS_LOCK_DELETED(lockp) (((lockp)->flags & CM_FILELOCK_FLAG_DELETED) == CM_FILELOCK_FLAG_DELETED)
4123 #define IS_LOCK_ACCEPTED(lockp) (IS_LOCK_ACTIVE(lockp) || IS_LOCK_WAITLOCK(lockp))
4126 #define IS_LOCK_CLIENTONLY(lockp) ((((lockp)->scp->flags & CM_SCACHEFLAG_RO) == CM_SCACHEFLAG_RO) || (((lockp)->flags & CM_FILELOCK_FLAG_CLIENTONLY) == CM_FILELOCK_FLAG_CLIENTONLY))
4129 #define INTERSECT_RANGE(r1,r2) (((r2).offset+(r2).length) > (r1).offset && ((r1).offset +(r1).length) > (r2).offset)
4132 #define CONTAINS_RANGE(r1,r2) (((r2).offset+(r2).length) <= ((r1).offset+(r1).length) && (r1).offset <= (r2).offset)
4134 #if defined(VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS) && !defined(LOCK_TESTING)
4135 #define SCP_SUPPORTS_BRLOCKS(scp) ((scp)->cbServerp && ((scp)->cbServerp->capabilities & VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS))
4137 #define SCP_SUPPORTS_BRLOCKS(scp) (1)
4140 #define SERVERLOCKS_ENABLED(scp) (!((scp)->flags & CM_SCACHEFLAG_RO) && cm_enableServerLocks && SCP_SUPPORTS_BRLOCKS(scp))
4142 #if defined(VICED_CAPABILITY_WRITELOCKACL)
4143 #define SCP_SUPPORTS_WRITELOCKACL(scp) ((scp)->cbServerp && ((scp->cbServerp->capabilities & VICED_CAPABILITY_WRITELOCKACL)))
4145 #define SCP_SUPPORTS_WRITELOCKACL(scp) (0)
4147 /* This should really be defined in any build that this code is being
4149 #error VICED_CAPABILITY_WRITELOCKACL not defined.
4152 static void cm_LockRangeSubtract(cm_range_t * pos, const cm_range_t * neg)
4154 afs_int64 int_begin;
4157 int_begin = MAX(pos->offset, neg->offset);
4158 int_end = MIN(pos->offset+pos->length, neg->offset+neg->length);
4160 if (int_begin < int_end) {
4161 if (int_begin == pos->offset) {
4162 pos->length = pos->offset + pos->length - int_end;
4163 pos->offset = int_end;
4164 } else if (int_end == pos->offset + pos->length) {
4165 pos->length = int_begin - pos->offset;
4168 /* We only subtract ranges if the resulting range is
4169 contiguous. If we try to support non-contigous ranges, we
4170 aren't actually improving performance. */
4174 /* Called with scp->rw held. Returns 0 if all is clear to read the
4175 specified range by the client identified by key.
4177 long cm_LockCheckRead(cm_scache_t *scp,
4178 LARGE_INTEGER LOffset,
4179 LARGE_INTEGER LLength,
4182 #ifndef ADVISORY_LOCKS
4184 cm_file_lock_t *fileLock;
4188 int substract_ranges = FALSE;
4190 range.offset = LOffset.QuadPart;
4191 range.length = LLength.QuadPart;
4195 1. for all _a_ in (Offset,+Length), all of the following is true:
4197 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
4198 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
4201 1.2 For each LOST lock L in S->fileLocks such that _a_ in
4202 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
4207 lock_ObtainRead(&cm_scacheLock);
4209 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
4211 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4213 if (INTERSECT_RANGE(range, fileLock->range)) {
4214 if (IS_LOCK_ACTIVE(fileLock)) {
4215 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4217 /* If there is an active lock for this client, it
4218 is safe to substract ranges.*/
4219 cm_LockRangeSubtract(&range, &fileLock->range);
4220 substract_ranges = TRUE;
4222 if (fileLock->lockType != LockRead) {
4223 code = CM_ERROR_LOCK_CONFLICT;
4227 /* even if the entire range is locked for reading,
4228 we still can't grant the lock at this point
4229 because the client may have lost locks. That
4230 is, unless we have already seen an active lock
4231 belonging to the client, in which case there
4232 can't be any lost locks for this client. */
4233 if (substract_ranges)
4234 cm_LockRangeSubtract(&range, &fileLock->range);
4236 } else if (IS_LOCK_LOST(fileLock) &&
4237 (cm_KeyEquals(&fileLock->key, &key, 0) || fileLock->lockType == LockWrite)) {
4238 code = CM_ERROR_BADFD;
4244 lock_ReleaseRead(&cm_scacheLock);
4246 osi_Log4(afsd_logp, "cm_LockCheckRead scp 0x%x offset %d length %d code 0x%x",
4247 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
4258 /* Called with scp->rw held. Returns 0 if all is clear to write the
4259 specified range by the client identified by key.
4261 long cm_LockCheckWrite(cm_scache_t *scp,
4262 LARGE_INTEGER LOffset,
4263 LARGE_INTEGER LLength,
4266 #ifndef ADVISORY_LOCKS
4268 cm_file_lock_t *fileLock;
4273 range.offset = LOffset.QuadPart;
4274 range.length = LLength.QuadPart;
4277 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
4279 2. for all _a_ in (Offset,+Length), one of the following is true:
4281 2.1 Byte _a_ of S is unowned AND there does not exist a LOST
4282 lock L such that _a_ in (L->LOffset,+L->LLength).
4284 2.2 Byte _a_ of S is owned by C under lock L AND L->LockType is
4288 lock_ObtainRead(&cm_scacheLock);
4290 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
4292 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4294 if (INTERSECT_RANGE(range, fileLock->range)) {
4295 if (IS_LOCK_ACTIVE(fileLock)) {
4296 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4297 if (fileLock->lockType == LockWrite) {
4299 /* if there is an active lock for this client, it
4300 is safe to substract ranges */
4301 cm_LockRangeSubtract(&range, &fileLock->range);
4303 code = CM_ERROR_LOCK_CONFLICT;
4307 code = CM_ERROR_LOCK_CONFLICT;
4310 } else if (IS_LOCK_LOST(fileLock)) {
4311 code = CM_ERROR_BADFD;
4317 lock_ReleaseRead(&cm_scacheLock);
4319 osi_Log4(afsd_logp, "cm_LockCheckWrite scp 0x%x offset %d length %d code 0x%x",
4320 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
4331 /* Called with cm_scacheLock write locked */
4332 static cm_file_lock_t * cm_GetFileLock(void) {
4335 l = (cm_file_lock_t *) cm_freeFileLocks;
4337 osi_QRemove(&cm_freeFileLocks, &l->q);
4339 l = malloc(sizeof(cm_file_lock_t));
4340 osi_assertx(l, "null cm_file_lock_t");
4343 memset(l, 0, sizeof(cm_file_lock_t));
4348 /* Called with cm_scacheLock write locked */
4349 static void cm_PutFileLock(cm_file_lock_t *l) {
4350 osi_QAdd(&cm_freeFileLocks, &l->q);
4353 /* called with scp->rw held. May release it during processing, but
4354 leaves it held on exit. */
4355 long cm_IntSetLock(cm_scache_t * scp, cm_user_t * userp, int lockType,
4361 struct rx_connection * rxconnp;
4363 afs_uint32 reqflags = reqp->flags;
4365 memset(&volSync, 0, sizeof(volSync));
4367 tfid.Volume = scp->fid.volume;
4368 tfid.Vnode = scp->fid.vnode;
4369 tfid.Unique = scp->fid.unique;
4372 osi_Log2(afsd_logp, "CALL SetLock scp 0x%p for lock %d", scp, lockType);
4374 reqp->flags |= CM_REQ_NORETRY;
4375 lock_ReleaseWrite(&scp->rw);
4378 code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4382 rxconnp = cm_GetRxConn(connp);
4383 code = RXAFS_SetLock(rxconnp, &tfid, lockType,
4385 rx_PutConnection(rxconnp);
4387 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
4390 code = cm_MapRPCError(code, reqp);
4392 osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
4394 osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
4397 lock_ObtainWrite(&scp->rw);
4398 reqp->flags = reqflags;
4402 /* called with scp->rw held. Releases it during processing */
4403 long cm_IntReleaseLock(cm_scache_t * scp, cm_user_t * userp,
4409 struct rx_connection * rxconnp;
4412 if (scp->flags & CM_SCACHEFLAG_DELETED) {
4413 osi_Log1(afsd_logp, "CALL ReleaseLock on Deleted Vnode scp 0x%p", scp);
4417 memset(&volSync, 0, sizeof(volSync));
4419 tfid.Volume = scp->fid.volume;
4420 tfid.Vnode = scp->fid.vnode;
4421 tfid.Unique = scp->fid.unique;
4424 lock_ReleaseWrite(&scp->rw);
4426 osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
4429 code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4433 rxconnp = cm_GetRxConn(connp);
4434 code = RXAFS_ReleaseLock(rxconnp, &tfid, &volSync);
4435 rx_PutConnection(rxconnp);
4437 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
4439 code = cm_MapRPCError(code, reqp);
4442 "CALL ReleaseLock FAILURE, code 0x%x", code);
4445 "CALL ReleaseLock SUCCESS");
4447 lock_ObtainWrite(&scp->rw);
4449 return (code != CM_ERROR_BADFD ? code : 0);
4452 /* called with scp->rw held. May release it during processing, but
4453 will exit with lock held.
4457 - 0 if the user has permission to get the specified lock for the scp
4459 - CM_ERROR_NOACCESS if not
4461 Any other error from cm_SyncOp will be sent down untranslated.
4463 If CM_ERROR_NOACCESS is returned and lock_type is LockRead, then
4464 phas_insert (if non-NULL) will receive a boolean value indicating
4465 whether the user has INSERT permission or not.
4467 long cm_LockCheckPerms(cm_scache_t * scp,
4474 long code = 0, code2 = 0;
4476 /* lock permissions are slightly tricky because of the 'i' bit.
4477 If the user has PRSFS_LOCK, she can read-lock the file. If the
4478 user has PRSFS_WRITE, she can write-lock the file. However, if
4479 the user has PRSFS_INSERT, then she can write-lock new files,
4480 but not old ones. Since we don't have information about
4481 whether a file is new or not, we assume that if the user owns
4482 the scp, then she has the permissions that are granted by
4485 osi_Log3(afsd_logp, "cm_LockCheckPerms for scp[0x%p] type[%d] user[0x%p]",
4486 scp, lock_type, userp);
4488 if (lock_type == LockRead)
4489 rights |= PRSFS_LOCK;
4490 else if (lock_type == LockWrite)
4491 rights |= PRSFS_WRITE | PRSFS_LOCK;
4494 osi_assertx(FALSE, "invalid lock type");
4499 *phas_insert = FALSE;
4501 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
4502 CM_SCACHESYNC_GETSTATUS |
4503 CM_SCACHESYNC_NEEDCALLBACK);
4505 if (phas_insert && scp->creator == userp) {
4507 /* If this file was created by the user, then we check for
4508 PRSFS_INSERT. If the file server is recent enough, then
4509 this should be sufficient for her to get a write-lock (but
4510 not necessarily a read-lock). VICED_CAPABILITY_WRITELOCKACL
4511 indicates whether a file server supports getting write
4512 locks when the user only has PRSFS_INSERT.
4514 If the file was not created by the user we skip the check
4515 because the INSERT bit will not apply to this user even
4519 code2 = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
4520 CM_SCACHESYNC_GETSTATUS |
4521 CM_SCACHESYNC_NEEDCALLBACK);
4523 if (code2 == CM_ERROR_NOACCESS) {
4524 osi_Log0(afsd_logp, "cm_LockCheckPerms user has no INSERT bits");
4526 *phas_insert = TRUE;
4527 osi_Log0(afsd_logp, "cm_LockCheckPerms user has INSERT bits");
4531 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
4533 osi_Log1(afsd_logp, "cm_LockCheckPerms returning code %d", code);
4538 /* called with scp->rw held */
4539 long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
4540 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
4542 int allowWait, cm_user_t *userp, cm_req_t *reqp,
4543 cm_file_lock_t **lockpp)
4546 int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
4547 cm_file_lock_t *fileLock;
4550 int wait_unlock = FALSE;
4551 int force_client_lock = FALSE;
4553 osi_Log4(afsd_logp, "cm_Lock scp 0x%x type 0x%x offset %d length %d",
4554 scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
4555 osi_Log4(afsd_logp, "... allowWait %d key <0x%x, 0x%x, 0x%x>", allowWait,
4556 key.process_id, key.session_id, key.file_id);
4559 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
4561 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
4564 3.1 If L->LockType is exclusive then there does NOT exist a
4565 ACCEPTED lock M in S->fileLocks such that _a_ in
4566 (M->LOffset,+M->LLength).
4568 3.2 If L->LockType is shared then for each ACCEPTED lock M in
4569 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
4570 M->LockType is shared.
4572 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
4574 4.1 M->key != Key(C)
4576 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
4577 and (M->LOffset,+M->LLength) do not intersect.
4580 range.offset = LOffset.QuadPart;
4581 range.length = LLength.QuadPart;
4583 lock_ObtainRead(&cm_scacheLock);
4585 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4587 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4589 if (IS_LOCK_LOST(fileLock)) {
4590 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4591 code = CM_ERROR_BADFD;
4593 } else if (fileLock->lockType == LockWrite && INTERSECT_RANGE(range, fileLock->range)) {
4594 code = CM_ERROR_WOULDBLOCK;
4600 /* we don't need to check for deleted locks here since deleted
4601 locks are dequeued from scp->fileLocks */
4602 if (IS_LOCK_ACCEPTED(fileLock) &&
4603 INTERSECT_RANGE(range, fileLock->range)) {
4605 if ((sLockType & LOCKING_ANDX_SHARED_LOCK) == 0 ||
4606 fileLock->lockType != LockRead) {
4608 code = CM_ERROR_WOULDBLOCK;
4614 lock_ReleaseRead(&cm_scacheLock);
4616 if (code == 0 && SERVERLOCKS_ENABLED(scp)) {
4617 if (Which == scp->serverLock ||
4618 (Which == LockRead && scp->serverLock == LockWrite)) {
4622 /* we already have the lock we need */
4623 osi_Log3(afsd_logp, " we already have the correct lock. exclusives[%d], shared[%d], serverLock[%d]",
4624 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4626 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4628 /* special case: if we don't have permission to read-lock
4629 the file, then we force a clientside lock. This is to
4630 compensate for applications that obtain a read-lock for
4631 reading files off of directories that don't grant
4632 read-locks to the user. */
4633 if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4635 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4636 osi_Log0(afsd_logp, " User has no read-lock perms, but has INSERT perms.");
4639 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4640 force_client_lock = TRUE;
4644 } else if ((scp->exclusiveLocks > 0) ||
4645 (scp->sharedLocks > 0 && scp->serverLock != LockRead)) {
4648 /* We are already waiting for some other lock. We should
4649 wait for the daemon to catch up instead of generating a
4650 flood of SetLock calls. */
4651 osi_Log3(afsd_logp, " already waiting for other lock. exclusives[%d], shared[%d], serverLock[%d]",
4652 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4654 /* see if we have permission to create the lock in the
4656 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4658 code = CM_ERROR_WOULDBLOCK;
4659 else if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4661 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4663 " User has no read-lock perms, but has INSERT perms.");
4664 code = CM_ERROR_WOULDBLOCK;
4667 " User has no read-lock perms. Forcing client-side lock");
4668 force_client_lock = TRUE;
4672 /* leave any other codes as-is */
4676 int check_data_version = FALSE;
4679 /* first check if we have permission to elevate or obtain
4681 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4683 if (code == CM_ERROR_NOACCESS && Which == LockRead &&
4684 (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp))) {
4685 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4686 force_client_lock = TRUE;
4691 /* has_insert => (Which == LockRead, code == CM_ERROR_NOACCESS) */
4693 if (scp->serverLock == LockRead && Which == LockWrite) {
4695 /* We want to escalate the lock to a LockWrite.
4696 * Unfortunately that's not really possible without
4697 * letting go of the current lock. But for now we do
4701 " attempting to UPGRADE from LockRead to LockWrite.");
4703 " dataVersion on scp: %I64d", scp->dataVersion);
4705 /* we assume at this point (because scp->serverLock
4706 was valid) that we had a valid server lock. */
4707 scp->lockDataVersion = scp->dataVersion;
4708 check_data_version = TRUE;
4710 code = cm_IntReleaseLock(scp, userp, reqp);
4713 /* We couldn't release the lock */
4716 scp->serverLock = -1;
4720 /* We need to obtain a server lock of type Which in order
4721 * to assert this file lock */
4722 #ifndef AGGRESSIVE_LOCKS
4725 newLock = LockWrite;
4728 code = cm_IntSetLock(scp, userp, newLock, reqp);
4730 #ifdef AGGRESSIVE_LOCKS
4731 if ((code == CM_ERROR_WOULDBLOCK ||
4732 code == CM_ERROR_NOACCESS) && newLock != Which) {
4733 /* we wanted LockRead. We tried LockWrite. Now try
4738 osi_assertx(newLock == LockRead, "lock type not read");
4740 code = cm_IntSetLock(scp, userp, newLock, reqp);
4744 if (code == CM_ERROR_NOACCESS) {
4745 if (Which == LockRead) {
4746 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4748 /* We requested a read-lock, but we have permission to
4749 * get a write-lock. Try that */
4751 tcode = cm_LockCheckPerms(scp, LockWrite, userp, reqp, NULL);
4754 newLock = LockWrite;
4756 osi_Log0(afsd_logp, " User has 'i' perms and the request was for a LockRead. Trying to get a LockWrite instead");
4758 code = cm_IntSetLock(scp, userp, newLock, reqp);
4761 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4762 force_client_lock = TRUE;
4764 } else if (Which == LockWrite &&
4765 scp->creator == userp && !SCP_SUPPORTS_WRITELOCKACL(scp)) {
4768 /* Special case: if the lock request was for a
4769 * LockWrite and the user owns the file and we weren't
4770 * allowed to obtain the serverlock, we either lost a
4771 * race (the permissions changed from under us), or we
4772 * have 'i' bits, but we aren't allowed to lock the
4775 /* check if we lost a race... */
4776 tcode = cm_LockCheckPerms(scp, Which, userp, reqp, NULL);
4779 osi_Log0(afsd_logp, " User has 'i' perms but can't obtain write locks. Using client-side locks.");
4780 force_client_lock = TRUE;
4785 if (code == 0 && check_data_version &&
4786 scp->dataVersion != scp->lockDataVersion) {
4787 /* We lost a race. Although we successfully obtained
4788 * a lock, someone modified the file in between. The
4789 * locks have all been technically lost. */
4792 " Data version mismatch while upgrading lock.");
4794 " Data versions before=%I64d, after=%I64d",
4795 scp->lockDataVersion,
4798 " Releasing stale lock for scp 0x%x", scp);
4800 code = cm_IntReleaseLock(scp, userp, reqp);
4802 scp->serverLock = -1;
4804 code = CM_ERROR_INVAL;
4805 } else if (code == 0) {
4806 scp->serverLock = newLock;
4807 scp->lockDataVersion = scp->dataVersion;
4811 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4812 scp->serverLock == -1) {
4813 /* Oops. We lost the lock. */
4814 cm_LockMarkSCacheLost(scp);
4817 } else if (code == 0) { /* server locks not enabled */
4819 " Skipping server lock for scp");
4824 if (code != 0 && !force_client_lock) {
4825 /* Special case error translations
4827 Applications don't expect certain errors from a
4828 LockFile/UnlockFile call. We need to translate some error
4829 code to codes that apps expect and handle. */
4831 /* We shouldn't actually need to handle this case since we
4832 simulate locks for RO scps anyway. */
4833 if (code == CM_ERROR_READONLY) {
4834 osi_Log0(afsd_logp, " Reinterpreting CM_ERROR_READONLY as CM_ERROR_NOACCESS");
4835 code = CM_ERROR_NOACCESS;
4839 if (code == 0 || (code == CM_ERROR_WOULDBLOCK && allowWait) ||
4840 force_client_lock) {
4842 /* clear the error if we are forcing a client lock, so we
4843 don't get confused later. */
4844 if (force_client_lock && code != CM_ERROR_WOULDBLOCK)
4847 lock_ObtainWrite(&cm_scacheLock);
4848 fileLock = cm_GetFileLock();
4849 lock_ReleaseWrite(&cm_scacheLock);
4851 fileLock->fid = scp->fid;
4853 fileLock->key = key;
4854 fileLock->lockType = Which;
4856 fileLock->userp = userp;
4857 fileLock->range = range;
4858 fileLock->flags = (code == 0 ? 0 :
4860 CM_FILELOCK_FLAG_WAITUNLOCK :
4861 CM_FILELOCK_FLAG_WAITLOCK));
4863 if (force_client_lock || !SERVERLOCKS_ENABLED(scp))
4864 fileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
4866 fileLock->lastUpdate = (code == 0 && !force_client_lock) ? time(NULL) : 0;
4868 lock_ObtainWrite(&cm_scacheLock);
4869 osi_QAddT(&scp->fileLocksH, &scp->fileLocksT, &fileLock->fileq);
4870 cm_HoldSCacheNoLock(scp);
4871 fileLock->scp = scp;
4872 osi_QAdd(&cm_allFileLocks, &fileLock->q);
4873 lock_ReleaseWrite(&cm_scacheLock);
4879 if (IS_LOCK_CLIENTONLY(fileLock)) {
4881 } else if (IS_LOCK_ACCEPTED(fileLock)) {
4882 if (Which == LockRead)
4885 scp->exclusiveLocks++;
4889 "cm_Lock Lock added 0x%p flags 0x%x to scp [0x%p]",
4890 fileLock, fileLock->flags, scp);
4892 " exclusives[%d] shared[%d] client[%d] serverLock[%d]",
4893 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4894 (int)(signed char) scp->serverLock);
4897 "cm_Lock Rejecting lock (code = 0x%x)", code);
4900 /* Convert from would block to lock not granted */
4901 if (code == CM_ERROR_WOULDBLOCK)
4902 code = CM_ERROR_LOCK_NOT_GRANTED;
4908 cm_IntUnlock(cm_scache_t * scp,
4914 osi_assertx(scp->sharedLocks >= 0, "scp->sharedLocks < 0");
4915 osi_assertx(scp->exclusiveLocks >= 0, "scp->exclusiveLocks < 0");
4916 osi_assertx(scp->clientLocks >= 0, "scp->clientLocks < 0");
4918 if (!SERVERLOCKS_ENABLED(scp)) {
4919 osi_Log0(afsd_logp, " Skipping server lock for scp");
4923 /* Ideally we would go through the rest of the locks to determine
4924 * if one or more locks that were formerly in WAITUNLOCK can now
4925 * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
4926 * scp->sharedLocks accordingly. However, the retrying of locks
4927 * in that manner is done cm_RetryLock() manually.
4930 if (scp->serverLock == LockWrite &&
4931 scp->exclusiveLocks == 0 &&
4932 scp->sharedLocks > 0) {
4933 /* The serverLock should be downgraded to LockRead */
4934 osi_Log0(afsd_logp, " DOWNGRADE lock from LockWrite to LockRead");
4936 /* Make sure there are no dirty buffers left. */
4937 code = cm_FSync(scp, userp, reqp, TRUE);
4939 /* since scp->serverLock looked sane, we are going to assume
4940 that we have a valid server lock. */
4941 scp->lockDataVersion = scp->dataVersion;
4942 osi_Log1(afsd_logp, " dataVersion on scp = %I64d", scp->dataVersion);
4944 /* before we downgrade, make sure that we have enough
4945 permissions to get the read lock. */
4946 code = cm_LockCheckPerms(scp, LockRead, userp, reqp, NULL);
4949 osi_Log0(afsd_logp, " SKIPPING downgrade because user doesn't have perms to get downgraded lock");
4955 code = cm_IntReleaseLock(scp, userp, reqp);
4958 /* so we couldn't release it. Just let the lock be for now */
4962 scp->serverLock = -1;
4965 code = cm_IntSetLock(scp, userp, LockRead, reqp);
4967 if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
4968 scp->serverLock = LockRead;
4969 } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
4970 /* We lost a race condition. Although we have a valid
4971 lock on the file, the data has changed and essentially
4972 we have lost the lock we had during the transition. */
4974 osi_Log0(afsd_logp, "Data version mismatch during lock downgrade");
4975 osi_Log2(afsd_logp, " Data versions before=%I64d, after=%I64d",
4976 scp->lockDataVersion,
4979 code = cm_IntReleaseLock(scp, userp, reqp);
4981 code = CM_ERROR_INVAL;
4982 scp->serverLock = -1;
4986 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4987 (scp->serverLock == -1)) {
4989 cm_LockMarkSCacheLost(scp);
4992 /* failure here has no bearing on the return value of cm_Unlock() */
4995 } else if (scp->serverLock != (-1) &&
4996 scp->exclusiveLocks == 0 &&
4997 scp->sharedLocks == 0) {
4998 /* The serverLock should be released entirely */
5000 if (scp->serverLock == LockWrite) {
5001 osi_Log0(afsd_logp, " RELEASE LockWrite -> LockNone");
5003 /* Make sure there are no dirty buffers left. */
5004 code = cm_FSync(scp, userp, reqp, TRUE);
5006 osi_Log0(afsd_logp, " RELEASE LockRead -> LockNone");
5009 code = cm_IntReleaseLock(scp, userp, reqp);
5012 scp->serverLock = (-1);
5018 /* Called with scp->rw held */
5019 long cm_UnlockByKey(cm_scache_t * scp,
5026 cm_file_lock_t *fileLock;
5027 osi_queue_t *q, *qn;
5030 osi_Log4(afsd_logp, "cm_UnlockByKey scp 0x%p key <0x%x,0x%x,0x%x",
5031 scp, key.process_id, key.session_id, key.file_id);
5032 osi_Log1(afsd_logp, " flags=0x%x", flags);
5034 lock_ObtainWrite(&cm_scacheLock);
5036 for (q = scp->fileLocksH; q; q = qn) {
5039 fileLock = (cm_file_lock_t *)
5040 ((char *) q - offsetof(cm_file_lock_t, fileq));
5043 osi_Log4(afsd_logp, " Checking lock[0x%x] range[%d,+%d] type[%d]",
5045 (unsigned long) fileLock->range.offset,
5046 (unsigned long) fileLock->range.length,
5047 fileLock->lockType);
5048 osi_Log4(afsd_logp, " key<0x%x, 0x%x, 0x%x> flags[0x%x]",
5049 fileLock->key.process_id, fileLock->key.session_id, fileLock->key.file_id,
5052 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5053 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5054 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5056 fileLock->fid.volume,
5057 fileLock->fid.vnode,
5058 fileLock->fid.unique);
5059 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5060 fileLock->scp->fid.cell,
5061 fileLock->scp->fid.volume,
5062 fileLock->scp->fid.vnode,
5063 fileLock->scp->fid.unique);
5064 osi_assertx(FALSE, "invalid fid value");
5068 if (!IS_LOCK_DELETED(fileLock) &&
5069 cm_KeyEquals(&fileLock->key, &key, flags)) {
5070 osi_Log3(afsd_logp, "...Unlock range [%d,+%d] type %d",
5071 fileLock->range.offset,
5072 fileLock->range.length,
5073 fileLock->lockType);
5075 if (scp->fileLocksT == q)
5076 scp->fileLocksT = osi_QPrev(q);
5077 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
5079 if (IS_LOCK_CLIENTONLY(fileLock)) {
5081 } else if (IS_LOCK_ACCEPTED(fileLock)) {
5082 if (fileLock->lockType == LockRead)
5085 scp->exclusiveLocks--;
5088 fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5094 lock_ReleaseWrite(&cm_scacheLock);
5096 if (n_unlocks == 0) {
5097 osi_Log0(afsd_logp, "cm_UnlockByKey no locks found");
5098 osi_Log3(afsd_logp, " Leaving scp with exclusives[%d], shared[%d], serverLock[%d]",
5099 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
5104 osi_Log1(afsd_logp, "cm_UnlockByKey done with %d locks", n_unlocks);
5106 osi_Log1(afsd_logp, "cm_UnlockByKey code 0x%x", code);
5107 osi_Log4(afsd_logp, " Leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
5108 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
5109 (int)(signed char) scp->serverLock);
5114 long cm_Unlock(cm_scache_t *scp,
5115 unsigned char sLockType,
5116 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
5123 int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
5124 cm_file_lock_t *fileLock;
5126 int release_userp = FALSE;
5127 int exact_match = !(flags & CM_UNLOCK_FLAG_MATCH_RANGE);
5129 LARGE_INTEGER RangeEnd;
5131 osi_Log4(afsd_logp, "cm_Unlock scp 0x%p type 0x%x offset 0x%x length 0x%x",
5132 scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
5133 osi_Log4(afsd_logp, "... key <0x%x,0x%x,0x%x> flags 0x%x",
5134 key.process_id, key.session_id, key.file_id, flags);
5137 RangeEnd.QuadPart = LOffset.QuadPart + LLength.QuadPart;
5140 lock_ObtainRead(&cm_scacheLock);
5142 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5143 fileLock = (cm_file_lock_t *)
5144 ((char *) q - offsetof(cm_file_lock_t, fileq));
5147 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5148 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5149 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5151 fileLock->fid.volume,
5152 fileLock->fid.vnode,
5153 fileLock->fid.unique);
5154 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5155 fileLock->scp->fid.cell,
5156 fileLock->scp->fid.volume,
5157 fileLock->scp->fid.vnode,
5158 fileLock->scp->fid.unique);
5159 osi_assertx(FALSE, "invalid fid value");
5163 if (!IS_LOCK_DELETED(fileLock) &&
5164 cm_KeyEquals(&fileLock->key, &key, 0) &&
5165 fileLock->range.offset == LOffset.QuadPart &&
5166 fileLock->range.length == LLength.QuadPart) {
5172 if (!IS_LOCK_DELETED(fileLock) &&
5173 cm_KeyEquals(&fileLock->key, &key, 0) &&
5174 fileLock->range.offset >= LOffset.QuadPart &&
5175 fileLock->range.offset < RangeEnd.QuadPart &&
5176 (fileLock->range.offset + fileLock->range.length) <= RangeEnd.QuadPart) {
5184 lock_ReleaseRead(&cm_scacheLock);
5186 if (lock_found && !exact_match) {
5190 osi_Log0(afsd_logp, "cm_Unlock lock not found; failure");
5192 /* The lock didn't exist anyway. *shrug* */
5193 return CM_ERROR_RANGE_NOT_LOCKED;
5197 /* discard lock record */
5198 lock_ConvertRToW(&cm_scacheLock);
5199 if (scp->fileLocksT == q)
5200 scp->fileLocksT = osi_QPrev(q);
5201 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
5204 * Don't delete it here; let the daemon delete it, to simplify
5205 * the daemon's traversal of the list.
5208 if (IS_LOCK_CLIENTONLY(fileLock)) {
5210 } else if (IS_LOCK_ACCEPTED(fileLock)) {
5211 if (fileLock->lockType == LockRead)
5214 scp->exclusiveLocks--;
5217 fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5218 lock_ReleaseWrite(&cm_scacheLock);
5220 if (release_userp) {
5221 cm_ReleaseUser(userp);
5222 release_userp = FALSE;
5226 osi_Log1(afsd_logp, "cm_Unlock not exact match, searching for next lock, code 0x%x", code);
5227 goto try_again; /* might be more than one lock in the range */
5232 osi_Log1(afsd_logp, "cm_Unlock code 0x%x", code);
5233 osi_Log4(afsd_logp, " leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
5234 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
5235 (int)(signed char) scp->serverLock);
5240 /* called with scp->rw held */
5241 void cm_LockMarkSCacheLost(cm_scache_t * scp)
5243 cm_file_lock_t *fileLock;
5246 osi_Log1(afsd_logp, "cm_LockMarkSCacheLost scp 0x%x", scp);
5248 /* cm_scacheLock needed because we are modifying fileLock->flags */
5249 lock_ObtainWrite(&cm_scacheLock);
5251 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5253 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
5255 if (IS_LOCK_ACTIVE(fileLock) &&
5256 !IS_LOCK_CLIENTONLY(fileLock)) {
5257 if (fileLock->lockType == LockRead)
5260 scp->exclusiveLocks--;
5262 fileLock->flags |= CM_FILELOCK_FLAG_LOST;
5266 scp->serverLock = -1;
5267 scp->lockDataVersion = CM_SCACHE_VERSION_BAD;
5268 lock_ReleaseWrite(&cm_scacheLock);
5271 /* Called with no relevant locks held */
5272 void cm_CheckLocks()
5274 osi_queue_t *q, *nq;
5275 cm_file_lock_t *fileLock;
5281 struct rx_connection * rxconnp;
5284 memset(&volSync, 0, sizeof(volSync));
5288 lock_ObtainWrite(&cm_scacheLock);
5290 cm_lockRefreshCycle++;
5292 osi_Log1(afsd_logp, "cm_CheckLocks starting lock check cycle %d", cm_lockRefreshCycle);
5294 for (q = cm_allFileLocks; q; q = nq) {
5295 fileLock = (cm_file_lock_t *) q;
5299 if (IS_LOCK_DELETED(fileLock)) {
5300 cm_user_t *userp = fileLock->userp;
5301 cm_scache_t *scp = fileLock->scp;
5302 fileLock->userp = NULL;
5303 fileLock->scp = NULL;
5305 lock_ReleaseWrite(&cm_scacheLock);
5306 lock_ObtainWrite(&scp->rw);
5307 code = cm_IntUnlock(scp, userp, &req);
5308 lock_ReleaseWrite(&scp->rw);
5310 cm_ReleaseUser(fileLock->userp);
5311 lock_ObtainWrite(&cm_scacheLock);
5312 cm_ReleaseSCacheNoLock(scp);
5314 osi_QRemove(&cm_allFileLocks, q);
5315 cm_PutFileLock(fileLock);
5317 } else if (IS_LOCK_ACTIVE(fileLock) && !IS_LOCK_CLIENTONLY(fileLock)) {
5319 /* Server locks must have been enabled for us to have
5320 received an active non-client-only lock. */
5321 osi_assertx(cm_enableServerLocks, "!cm_enableServerLocks");
5323 scp = fileLock->scp;
5324 osi_assertx(scp != NULL, "null cm_scache_t");
5326 cm_HoldSCacheNoLock(scp);
5329 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5330 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5331 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5333 fileLock->fid.volume,
5334 fileLock->fid.vnode,
5335 fileLock->fid.unique);
5336 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5337 fileLock->scp->fid.cell,
5338 fileLock->scp->fid.volume,
5339 fileLock->scp->fid.vnode,
5340 fileLock->scp->fid.unique);
5341 osi_assertx(FALSE, "invalid fid");
5344 /* Server locks are extended once per scp per refresh
5346 if (scp->lastRefreshCycle != cm_lockRefreshCycle) {
5348 int scp_done = FALSE;
5350 osi_Log1(afsd_logp, "cm_CheckLocks Updating scp 0x%x", scp);
5352 lock_ReleaseWrite(&cm_scacheLock);
5353 lock_ObtainWrite(&scp->rw);
5355 /* did the lock change while we weren't holding the lock? */
5356 if (!IS_LOCK_ACTIVE(fileLock))
5357 goto post_syncopdone;
5359 code = cm_SyncOp(scp, NULL, fileLock->userp, &req, 0,
5360 CM_SCACHESYNC_NEEDCALLBACK
5361 | CM_SCACHESYNC_GETSTATUS
5362 | CM_SCACHESYNC_LOCK);
5366 "cm_CheckLocks SyncOp failure code 0x%x", code);
5367 goto post_syncopdone;
5370 /* cm_SyncOp releases scp->rw during which the lock
5371 may get released. */
5372 if (!IS_LOCK_ACTIVE(fileLock))
5373 goto pre_syncopdone;
5375 if (scp->serverLock != -1 && !(scp->flags & CM_SCACHEFLAG_DELETED)) {
5379 tfid.Volume = scp->fid.volume;
5380 tfid.Vnode = scp->fid.vnode;
5381 tfid.Unique = scp->fid.unique;
5383 userp = fileLock->userp;
5385 osi_Log3(afsd_logp, "CALL ExtendLock lock 0x%p for scp=0x%p with lock %d",
5388 (int) scp->serverLock);
5390 lock_ReleaseWrite(&scp->rw);
5393 code = cm_ConnFromFID(&cfid, userp,
5398 rxconnp = cm_GetRxConn(connp);
5399 code = RXAFS_ExtendLock(rxconnp, &tfid,
5401 rx_PutConnection(rxconnp);
5403 osi_Log1(afsd_logp, " ExtendLock returns %d", code);
5405 } while (cm_Analyze(connp, userp, &req,
5406 &cfid, &volSync, NULL, NULL,
5409 code = cm_MapRPCError(code, &req);
5411 lock_ObtainWrite(&scp->rw);
5414 osi_Log1(afsd_logp, "CALL ExtendLock FAILURE, code 0x%x", code);
5416 osi_Log0(afsd_logp, "CALL ExtendLock SUCCESS");
5417 scp->lockDataVersion = scp->dataVersion;
5420 if ((code == EINVAL || code == CM_ERROR_INVAL) &&
5421 scp->lockDataVersion == scp->dataVersion) {
5425 (scp->exclusiveLocks > 0) ? LockWrite: LockRead;
5427 /* we might still have a chance to obtain a
5430 code = cm_IntSetLock(scp, userp, lockType, &req);
5433 code = CM_ERROR_INVAL;
5434 } else if (scp->lockDataVersion != scp->dataVersion) {
5436 /* now check if we still have the file at
5437 the right data version. */
5439 "Data version mismatch on scp 0x%p",
5442 " Data versions: before=%I64d, after=%I64d",
5443 scp->lockDataVersion,
5446 code = cm_IntReleaseLock(scp, userp, &req);
5448 code = CM_ERROR_INVAL;
5452 if (code == EINVAL || code == CM_ERROR_INVAL ||
5453 code == CM_ERROR_BADFD) {
5454 cm_LockMarkSCacheLost(scp);
5458 /* interestingly, we have found an active lock
5459 belonging to an scache that has no
5461 cm_LockMarkSCacheLost(scp);
5468 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5471 lock_ReleaseWrite(&scp->rw);
5473 lock_ObtainWrite(&cm_scacheLock);
5476 fileLock->lastUpdate = time(NULL);
5480 scp->lastRefreshCycle = cm_lockRefreshCycle;
5483 /* we have already refreshed the locks on this scp */
5484 fileLock->lastUpdate = time(NULL);
5487 cm_ReleaseSCacheNoLock(scp);
5489 } else if (IS_LOCK_ACTIVE(fileLock) && IS_LOCK_CLIENTONLY(fileLock)) {
5490 /* TODO: Check callbacks */
5494 lock_ReleaseWrite(&cm_scacheLock);
5495 osi_Log1(afsd_logp, "cm_CheckLocks completes lock check cycle %d", cm_lockRefreshCycle);
5498 /* NOT called with scp->rw held. */
5499 long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
5502 cm_scache_t *scp = NULL;
5503 cm_file_lock_t *fileLock;
5507 int force_client_lock = FALSE;
5508 int has_insert = FALSE;
5509 int check_data_version = FALSE;
5513 if (client_is_dead) {
5514 code = CM_ERROR_TIMEDOUT;
5518 lock_ObtainRead(&cm_scacheLock);
5520 osi_Log2(afsd_logp, "cm_RetryLock checking lock %p (scp=%p)", oldFileLock, oldFileLock->scp);
5521 osi_Log4(afsd_logp, " offset(%x:%x) length(%x:%x)",
5522 (unsigned)(oldFileLock->range.offset >> 32),
5523 (unsigned)(oldFileLock->range.offset & 0xffffffff),
5524 (unsigned)(oldFileLock->range.length >> 32),
5525 (unsigned)(oldFileLock->range.length & 0xffffffff));
5526 osi_Log4(afsd_logp, " key<0x%x,0x%x,0x%x> flags=%x",
5527 oldFileLock->key.process_id, oldFileLock->key.session_id, oldFileLock->key.file_id,
5528 (unsigned)(oldFileLock->flags));
5530 /* if the lock has already been granted, then we have nothing to do */
5531 if (IS_LOCK_ACTIVE(oldFileLock)) {
5532 lock_ReleaseRead(&cm_scacheLock);
5533 osi_Log0(afsd_logp, "cm_RetryLock lock already granted");
5537 /* we can't do anything with lost or deleted locks at the moment. */
5538 if (IS_LOCK_LOST(oldFileLock) || IS_LOCK_DELETED(oldFileLock)) {
5539 code = CM_ERROR_BADFD;
5540 osi_Log0(afsd_logp, "cm_RetryLock lock is lost or deleted");
5541 lock_ReleaseRead(&cm_scacheLock);
5545 scp = oldFileLock->scp;
5547 osi_assertx(scp != NULL, "null cm_scache_t");
5549 lock_ReleaseRead(&cm_scacheLock);
5550 lock_ObtainWrite(&scp->rw);
5552 code = cm_LockCheckPerms(scp, oldFileLock->lockType,
5556 if (code == CM_ERROR_NOACCESS && oldFileLock->lockType == LockRead) {
5557 if (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp)) {
5558 force_client_lock = TRUE;
5562 lock_ReleaseWrite(&scp->rw);
5566 lock_ObtainWrite(&cm_scacheLock);
5568 /* Check if we already have a sufficient server lock to allow this
5569 lock to go through. */
5570 if (IS_LOCK_WAITLOCK(oldFileLock) &&
5571 (!SERVERLOCKS_ENABLED(scp) ||
5572 scp->serverLock == oldFileLock->lockType ||
5573 scp->serverLock == LockWrite)) {
5575 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5577 if (SERVERLOCKS_ENABLED(scp)) {
5578 osi_Log1(afsd_logp, "cm_RetryLock Server lock (%d) is sufficient for lock. Granting",
5579 (int) scp->serverLock);
5581 osi_Log0(afsd_logp, "cm_RetryLock skipping server lock for scp");
5584 lock_ReleaseWrite(&cm_scacheLock);
5585 lock_ReleaseWrite(&scp->rw);
5590 if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5592 /* check if the conflicting locks have dissappeared already */
5593 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5595 fileLock = (cm_file_lock_t *)
5596 ((char *) q - offsetof(cm_file_lock_t, fileq));
5598 if (IS_LOCK_LOST(fileLock)) {
5599 if (cm_KeyEquals(&fileLock->key, &oldFileLock->key, 0)) {
5600 code = CM_ERROR_BADFD;
5601 oldFileLock->flags |= CM_FILELOCK_FLAG_LOST;
5602 osi_Log1(afsd_logp, " found lost lock %p for same key. Marking lock as lost",
5605 } else if (fileLock->lockType == LockWrite &&
5606 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5607 osi_Log1(afsd_logp, " found conflicting LOST lock %p", fileLock);
5608 code = CM_ERROR_WOULDBLOCK;
5613 if (IS_LOCK_ACCEPTED(fileLock) &&
5614 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5616 if (oldFileLock->lockType != LockRead ||
5617 fileLock->lockType != LockRead) {
5619 osi_Log1(afsd_logp, " found conflicting lock %p", fileLock);
5620 code = CM_ERROR_WOULDBLOCK;
5628 lock_ReleaseWrite(&cm_scacheLock);
5629 lock_ReleaseWrite(&scp->rw);
5634 /* when we get here, the lock is either a WAITUNLOCK or WAITLOCK.
5635 If it is WAITUNLOCK, then we didn't find any conflicting lock
5636 but we haven't verfied whether the serverLock is sufficient to
5637 assert it. If it is WAITLOCK, then the serverLock is
5638 insufficient to assert it. Eitherway, we are ready to accept
5639 the lock as either ACTIVE or WAITLOCK depending on the
5642 /* First, promote the WAITUNLOCK to a WAITLOCK */
5643 if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5644 if (oldFileLock->lockType == LockRead)
5647 scp->exclusiveLocks++;
5649 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITUNLOCK;
5650 oldFileLock->flags |= CM_FILELOCK_FLAG_WAITLOCK;
5653 osi_assertx(IS_LOCK_WAITLOCK(oldFileLock), "!IS_LOCK_WAITLOCK");
5655 if (force_client_lock ||
5656 !SERVERLOCKS_ENABLED(scp) ||
5657 scp->serverLock == oldFileLock->lockType ||
5658 (oldFileLock->lockType == LockRead &&
5659 scp->serverLock == LockWrite)) {
5661 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5663 if ((force_client_lock ||
5664 !SERVERLOCKS_ENABLED(scp)) &&
5665 !IS_LOCK_CLIENTONLY(oldFileLock)) {
5667 oldFileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
5669 if (oldFileLock->lockType == LockRead)
5672 scp->exclusiveLocks--;
5677 lock_ReleaseWrite(&cm_scacheLock);
5678 lock_ReleaseWrite(&scp->rw);
5685 code = cm_SyncOp(scp, NULL, oldFileLock->userp, &req, 0,
5686 CM_SCACHESYNC_NEEDCALLBACK
5687 | CM_SCACHESYNC_GETSTATUS
5688 | CM_SCACHESYNC_LOCK);
5690 osi_Log1(afsd_logp, "cm_RetryLock SyncOp failure code 0x%x", code);
5691 lock_ReleaseWrite(&cm_scacheLock);
5692 goto post_syncopdone;
5695 if (!IS_LOCK_WAITLOCK(oldFileLock))
5696 goto pre_syncopdone;
5698 userp = oldFileLock->userp;
5700 #ifndef AGGRESSIVE_LOCKS
5701 newLock = oldFileLock->lockType;
5703 newLock = LockWrite;
5707 /* if has_insert is non-zero, then:
5708 - the lock a LockRead
5709 - we don't have permission to get a LockRead
5710 - we do have permission to get a LockWrite
5711 - the server supports VICED_CAPABILITY_WRITELOCKACL
5714 newLock = LockWrite;
5717 lock_ReleaseWrite(&cm_scacheLock);
5719 /* when we get here, either we have a read-lock and want a
5720 write-lock or we don't have any locks and we want some
5723 if (scp->serverLock == LockRead) {
5725 osi_assertx(newLock == LockWrite, "!LockWrite");
5727 osi_Log0(afsd_logp, " Attempting to UPGRADE from LockRead to LockWrite");
5729 scp->lockDataVersion = scp->dataVersion;
5730 check_data_version = TRUE;
5732 code = cm_IntReleaseLock(scp, userp, &req);
5735 goto pre_syncopdone;
5737 scp->serverLock = -1;
5740 code = cm_IntSetLock(scp, userp, newLock, &req);
5743 if (scp->dataVersion != scp->lockDataVersion) {
5744 /* we lost a race. too bad */
5747 " Data version mismatch while upgrading lock.");
5749 " Data versions before=%I64d, after=%I64d",
5750 scp->lockDataVersion,
5753 " Releasing stale lock for scp 0x%x", scp);
5755 code = cm_IntReleaseLock(scp, userp, &req);
5757 scp->serverLock = -1;
5759 code = CM_ERROR_INVAL;
5761 cm_LockMarkSCacheLost(scp);
5763 scp->serverLock = newLock;
5768 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5774 if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
5775 lock_ObtainWrite(&cm_scacheLock);
5776 if (scp->fileLocksT == &oldFileLock->fileq)
5777 scp->fileLocksT = osi_QPrev(&oldFileLock->fileq);
5778 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, &oldFileLock->fileq);
5779 lock_ReleaseWrite(&cm_scacheLock);
5781 lock_ReleaseWrite(&scp->rw);
5784 lock_ObtainWrite(&cm_scacheLock);
5786 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5787 } else if (code != CM_ERROR_WOULDBLOCK) {
5788 oldFileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5789 cm_ReleaseUser(oldFileLock->userp);
5790 oldFileLock->userp = NULL;
5791 if (oldFileLock->scp) {
5792 cm_ReleaseSCacheNoLock(oldFileLock->scp);
5793 oldFileLock->scp = NULL;
5796 lock_ReleaseWrite(&cm_scacheLock);
5801 cm_key_t cm_GenerateKey(afs_uint16 session_id, afs_offs_t process_id, afs_uint16 file_id)
5805 key.process_id = process_id;
5806 key.session_id = session_id;
5807 key.file_id = file_id;
5812 int cm_KeyEquals(cm_key_t *k1, cm_key_t *k2, int flags)
5814 return (k1->session_id == k2->session_id) && (k1->file_id == k2->file_id) &&
5815 ((flags & CM_UNLOCK_BY_FID) || (k1->process_id == k2->process_id));
5818 void cm_ReleaseAllLocks(void)
5824 cm_file_lock_t *fileLock;
5827 for (i = 0; i < cm_data.scacheHashTableSize; i++)
5829 for ( scp = cm_data.scacheHashTablep[i]; scp; scp = scp->nextp ) {
5830 while (scp->fileLocksH != NULL) {
5831 lock_ObtainWrite(&scp->rw);
5832 lock_ObtainWrite(&cm_scacheLock);
5833 if (!scp->fileLocksH) {
5834 lock_ReleaseWrite(&cm_scacheLock);
5835 lock_ReleaseWrite(&scp->rw);
5838 fileLock = (cm_file_lock_t *)((char *) scp->fileLocksH - offsetof(cm_file_lock_t, fileq));
5839 userp = fileLock->userp;
5841 key = fileLock->key;
5842 cm_HoldSCacheNoLock(scp);
5843 lock_ReleaseWrite(&cm_scacheLock);
5844 cm_UnlockByKey(scp, key, 0, userp, &req);
5845 cm_ReleaseSCache(scp);
5846 cm_ReleaseUser(userp);
5847 lock_ReleaseWrite(&scp->rw);