2 * Copyright 2000, International Business Machines Corporation and others.
5 * This software has been released under the terms of the IBM Public
6 * License. For details, see the LICENSE file in the top-level source
7 * directory or online at http://www.openafs.org/dl/license10.html
10 #include <afsconfig.h>
11 #include <afs/param.h>
33 extern void afsi_log(char *pattern, ...);
36 int cm_enableServerLocks = 1;
38 int cm_followBackupPath = 0;
41 * Case-folding array. This was constructed by inspecting of SMBtrace output.
42 * I do not know anything more about it.
44 unsigned char cm_foldUpper[256] = {
45 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
46 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
47 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
48 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
49 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
50 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
51 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
52 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
53 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
54 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
55 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
56 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
57 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
58 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
59 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
60 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
61 0x80, 0x9a, 0x90, 0x41, 0x8e, 0x41, 0x8f, 0x80,
62 0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x8e, 0x8f,
63 0x90, 0x92, 0x92, 0x4f, 0x99, 0x4f, 0x55, 0x55,
64 0x59, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
65 0x41, 0x49, 0x4f, 0x55, 0xa5, 0xa5, 0x56, 0xa7,
66 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
67 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
68 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
69 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
70 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
71 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
72 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
73 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
74 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
75 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
76 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
80 * Case-insensitive string comparison. We used to use stricmp, but it doesn't
81 * know about 8-bit characters (e.g. 129 is lowercase u-umlaut, 154 is
82 * upper-case u-umlaut).
84 int cm_stricmp(const char *str1, const char *str2)
96 c1 = (char) cm_foldUpper[(unsigned char)(*str1++)];
97 c2 = (char) cm_foldUpper[(unsigned char)(*str2++)];
107 /* return success if we can open this file in this mode */
108 long cm_CheckOpen(cm_scache_t *scp, int openMode, int trunc, cm_user_t *userp,
116 rights |= PRSFS_READ;
117 if (openMode == 1 || openMode == 2 || trunc)
118 rights |= PRSFS_WRITE;
120 lock_ObtainWrite(&scp->rw);
122 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
123 CM_SCACHESYNC_GETSTATUS
124 | CM_SCACHESYNC_NEEDCALLBACK
125 | CM_SCACHESYNC_LOCK);
128 ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
129 scp->fileType == CM_SCACHETYPE_FILE) {
132 unsigned int sLockType;
133 LARGE_INTEGER LOffset, LLength;
135 /* Check if there's some sort of lock on the file at the
138 key = cm_GenerateKey(CM_SESSION_CMINT,0,0);
140 if (rights & PRSFS_WRITE)
143 sLockType = LOCKING_ANDX_SHARED_LOCK;
145 LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
146 LOffset.LowPart = CM_FLSHARE_OFFSET_LOW;
147 LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
148 LLength.LowPart = CM_FLSHARE_LENGTH_LOW;
150 code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
153 cm_Unlock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp);
155 /* In this case, we allow the file open to go through even
156 though we can't enforce mandatory locking on the
158 if (code == CM_ERROR_NOACCESS &&
159 !(rights & PRSFS_WRITE))
162 if (code == CM_ERROR_LOCK_NOT_GRANTED)
163 code = CM_ERROR_SHARING_VIOLATION;
167 } else if (code != 0) {
171 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
175 lock_ReleaseWrite(&scp->rw);
180 /* return success if we can open this file in this mode */
181 long cm_CheckNTOpen(cm_scache_t *scp, unsigned int desiredAccess,
182 unsigned int createDisp, cm_user_t *userp, cm_req_t *reqp,
183 cm_lock_data_t **ldpp)
188 osi_assertx(ldpp != NULL, "null cm_lock_data_t");
191 /* Ignore the SYNCHRONIZE privilege */
192 desiredAccess &= ~SYNCHRONIZE;
194 /* Always allow delete; the RPC will tell us if it's OK */
197 if (desiredAccess == DELETE)
200 if (desiredAccess & (AFS_ACCESS_READ|AFS_ACCESS_EXECUTE))
201 rights |= (scp->fileType == CM_SCACHETYPE_DIRECTORY ? PRSFS_LOOKUP : PRSFS_READ);
203 /* We used to require PRSFS_WRITE if createDisp was 4
204 (OPEN_ALWAYS) even if AFS_ACCESS_WRITE was not requested.
205 However, we don't need to do that since the existence of the
206 scp implies that we don't need to create it. */
207 if (desiredAccess & AFS_ACCESS_WRITE)
208 rights |= PRSFS_WRITE;
210 if (desiredAccess & DELETE)
211 rights |= PRSFS_DELETE;
213 lock_ObtainWrite(&scp->rw);
215 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
216 CM_SCACHESYNC_GETSTATUS
217 | CM_SCACHESYNC_NEEDCALLBACK
218 | CM_SCACHESYNC_LOCK);
221 * If the open will fail because the volume is readonly, then we will
222 * return an access denied error instead. This is to help brain-dead
223 * apps run correctly on replicated volumes.
224 * See defect 10007 for more information.
226 if (code == CM_ERROR_READONLY)
227 code = CM_ERROR_NOACCESS;
230 ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
231 scp->fileType == CM_SCACHETYPE_FILE) {
233 unsigned int sLockType;
234 LARGE_INTEGER LOffset, LLength;
236 /* Check if there's some sort of lock on the file at the
239 key = cm_GenerateKey(CM_SESSION_CMINT,0,0);
240 if (rights & PRSFS_WRITE)
243 sLockType = LOCKING_ANDX_SHARED_LOCK;
245 /* single byte lock at offset 0x0100 0000 0000 0000 */
246 LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
247 LOffset.LowPart = CM_FLSHARE_OFFSET_LOW;
248 LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
249 LLength.LowPart = CM_FLSHARE_LENGTH_LOW;
251 code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
254 (*ldpp) = (cm_lock_data_t *)malloc(sizeof(cm_lock_data_t));
261 (*ldpp)->sLockType = sLockType;
262 (*ldpp)->LOffset.HighPart = LOffset.HighPart;
263 (*ldpp)->LOffset.LowPart = LOffset.LowPart;
264 (*ldpp)->LLength.HighPart = LLength.HighPart;
265 (*ldpp)->LLength.LowPart = LLength.LowPart;
267 /* In this case, we allow the file open to go through even
268 though we can't enforce mandatory locking on the
270 if (code == CM_ERROR_NOACCESS &&
271 !(rights & PRSFS_WRITE))
274 if (code == CM_ERROR_LOCK_NOT_GRANTED)
275 code = CM_ERROR_SHARING_VIOLATION;
278 } else if (code != 0) {
283 lock_ReleaseWrite(&scp->rw);
286 osi_Log3(afsd_logp,"cm_CheckNTOpen scp 0x%p ldp 0x%p code 0x%x", scp, *ldpp, code);
290 extern long cm_CheckNTOpenDone(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp,
291 cm_lock_data_t ** ldpp)
293 osi_Log2(afsd_logp,"cm_CheckNTOpenDone scp 0x%p ldp 0x%p", scp, *ldpp);
294 lock_ObtainWrite(&scp->rw);
296 cm_Unlock(scp, (*ldpp)->sLockType, (*ldpp)->LOffset, (*ldpp)->LLength,
297 (*ldpp)->key, 0, userp, reqp);
301 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
302 lock_ReleaseWrite(&scp->rw);
306 * When CAP_NT_SMBS has been negotiated, deletion (of files or directories) is
307 * done in three steps:
308 * (1) open for deletion (NT_CREATE_AND_X)
309 * (2) set for deletion on close (NT_TRANSACTION2, SET_FILE_INFO)
311 * We must not do the RPC until step 3. But if we are going to return an error
312 * code (e.g. directory not empty), we must return it by step 2, otherwise most
313 * clients will not notice it. So we do a preliminary check. For deleting
314 * files, this is almost free, since we have already done the RPC to get the
315 * parent directory's status bits. But for deleting directories, we must do an
316 * additional RPC to get the directory's data to check if it is empty. Sigh.
318 long cm_CheckNTDelete(cm_scache_t *dscp, cm_scache_t *scp, cm_user_t *userp,
324 cm_dirEntry_t *dep = 0;
325 unsigned short *hashTable;
327 int BeyondPage = 0, HaveDot = 0, HaveDotDot = 0;
330 /* First check permissions */
331 lock_ObtainWrite(&scp->rw);
332 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_DELETE,
333 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
335 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
336 lock_ReleaseWrite(&scp->rw);
340 /* If deleting directory, must be empty */
342 if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
345 thyper.HighPart = 0; thyper.LowPart = 0;
346 code = buf_Get(scp, &thyper, reqp, &bufferp);
350 lock_ObtainMutex(&bufferp->mx);
351 lock_ObtainWrite(&scp->rw);
354 code = cm_SyncOp(scp, bufferp, userp, reqp, 0,
355 CM_SCACHESYNC_NEEDCALLBACK
357 | CM_SCACHESYNC_BUFLOCKED);
361 if (cm_HaveBuffer(scp, bufferp, 1))
364 /* otherwise, load the buffer and try again */
365 lock_ReleaseMutex(&bufferp->mx);
366 code = cm_GetBuffer(scp, bufferp, NULL, userp, reqp);
367 lock_ReleaseWrite(&scp->rw);
368 lock_ObtainMutex(&bufferp->mx);
369 lock_ObtainWrite(&scp->rw);
370 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
375 lock_ReleaseWrite(&scp->rw);
378 /* We try to determine emptiness without looking beyond the first page,
379 * and without assuming "." and ".." are present and are on the first
380 * page (though these assumptions might, after all, be reasonable).
382 hashTable = (unsigned short *)(bufferp->datap + (32 * 5));
383 for (i=0; i<128; i++) {
384 idx = ntohs(hashTable[i]);
390 dep = (cm_dirEntry_t *)(bufferp->datap + (32 * idx));
391 if (strcmp(dep->name, ".") == 0)
393 else if (strcmp(dep->name, "..") == 0)
396 code = CM_ERROR_NOTEMPTY;
399 idx = ntohs(dep->next);
402 if (BeyondPage && HaveDot && HaveDotDot)
403 code = CM_ERROR_NOTEMPTY;
407 lock_ReleaseMutex(&bufferp->mx);
408 buf_Release(bufferp);
410 lock_ReleaseWrite(&scp->rw);
415 * Iterate through all entries in a directory.
416 * When the function funcp is called, the buffer is locked but the
417 * directory vnode is not.
419 * If the retscp parameter is not NULL, the parmp must be a
420 * cm_lookupSearch_t object.
422 long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
423 osi_hyper_t *startOffsetp, cm_user_t *userp, cm_req_t *reqp,
424 cm_scache_t **retscp)
428 cm_dirEntry_t *dep = 0;
431 osi_hyper_t dirLength;
432 osi_hyper_t bufferOffset;
433 osi_hyper_t curOffset;
437 cm_pageHeader_t *pageHeaderp;
439 long nextEntryCookie;
440 int numDirChunks; /* # of 32 byte dir chunks in this entry */
442 /* get the directory size */
443 lock_ObtainWrite(&scp->rw);
444 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_LOOKUP,
445 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
446 lock_ReleaseWrite(&scp->rw);
450 if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
451 return CM_ERROR_NOTDIR;
453 if (retscp) /* if this is a lookup call */
455 cm_lookupSearch_t* sp = parmp;
458 #ifdef AFS_FREELANCE_CLIENT
459 /* Freelance entries never end up in the DNLC because they
460 * do not have an associated cm_server_t
462 !(cm_freelanceEnabled &&
463 sp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
464 sp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
465 #else /* !AFS_FREELANCE_CLIENT */
470 int casefold = sp->caseFold;
471 sp->caseFold = 0; /* we have a strong preference for exact matches */
472 if ( *retscp = cm_dnlcLookup(scp, sp)) /* dnlc hit */
474 sp->caseFold = casefold;
477 sp->caseFold = casefold;
481 * see if we can find it using the directory hash tables.
482 * we can only do exact matches, since the hash is case
485 if (funcp != (cm_DirFuncp_t)cm_BPlusDirFoo)
494 code = cm_BeginDirOp(scp, userp, reqp, CM_DIRLOCK_READ,
495 CM_DIROP_FLAG_NONE, &dirop);
499 code = cm_BPlusDirLookup(&dirop, sp->nsearchNamep, &sp->fid);
504 code = cm_DirLookup(&dirop, sp->searchNamep, &sp->fid);
512 sp->ExactFound = TRUE;
513 *retscp = NULL; /* force caller to call cm_GetSCache() */
518 if (sp->caseFold && code == CM_ERROR_INEXACT_MATCH) {
521 sp->ExactFound = FALSE;
522 *retscp = NULL; /* force caller to call cm_GetSCache() */
526 return CM_ERROR_BPLUS_NOMATCH;
533 * XXX We only get the length once. It might change when we drop the
536 dirLength = scp->length;
539 bufferOffset.LowPart = bufferOffset.HighPart = 0;
541 curOffset = *startOffsetp;
543 curOffset.HighPart = 0;
544 curOffset.LowPart = 0;
548 /* make sure that curOffset.LowPart doesn't point to the first
549 * 32 bytes in the 2nd through last dir page, and that it
550 * doesn't point at the first 13 32-byte chunks in the first
551 * dir page, since those are dir and page headers, and don't
552 * contain useful information.
554 temp = curOffset.LowPart & (2048-1);
555 if (curOffset.HighPart == 0 && curOffset.LowPart < 2048) {
556 /* we're in the first page */
557 if (temp < 13*32) temp = 13*32;
560 /* we're in a later dir page */
561 if (temp < 32) temp = 32;
564 /* make sure the low order 5 bits are zero */
567 /* now put temp bits back ito curOffset.LowPart */
568 curOffset.LowPart &= ~(2048-1);
569 curOffset.LowPart |= temp;
571 /* check if we've passed the dir's EOF */
572 if (LargeIntegerGreaterThanOrEqualTo(curOffset, dirLength))
575 /* see if we can use the bufferp we have now; compute in which
576 * page the current offset would be, and check whether that's
577 * the offset of the buffer we have. If not, get the buffer.
579 thyper.HighPart = curOffset.HighPart;
580 thyper.LowPart = curOffset.LowPart & ~(cm_data.buf_blockSize-1);
581 if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
584 lock_ReleaseMutex(&bufferp->mx);
585 buf_Release(bufferp);
589 code = buf_Get(scp, &thyper, reqp, &bufferp);
591 /* if buf_Get() fails we do not have a buffer object to lock */
596 lock_ObtainMutex(&bufferp->mx);
597 bufferOffset = thyper;
599 /* now get the data in the cache */
601 lock_ObtainWrite(&scp->rw);
602 code = cm_SyncOp(scp, bufferp, userp, reqp,
604 CM_SCACHESYNC_NEEDCALLBACK
606 | CM_SCACHESYNC_BUFLOCKED);
608 lock_ReleaseWrite(&scp->rw);
611 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
613 if (cm_HaveBuffer(scp, bufferp, 1)) {
614 lock_ReleaseWrite(&scp->rw);
618 /* otherwise, load the buffer and try again */
619 lock_ReleaseMutex(&bufferp->mx);
620 code = cm_GetBuffer(scp, bufferp, NULL, userp,
622 lock_ReleaseWrite(&scp->rw);
623 lock_ObtainMutex(&bufferp->mx);
628 lock_ReleaseMutex(&bufferp->mx);
629 buf_Release(bufferp);
633 } /* if (wrong buffer) ... */
635 /* now we have the buffer containing the entry we're interested
636 * in; copy it out if it represents a non-deleted entry.
638 entryInDir = curOffset.LowPart & (2048-1);
639 entryInBuffer = curOffset.LowPart & (cm_data.buf_blockSize - 1);
641 /* page header will help tell us which entries are free. Page
642 * header can change more often than once per buffer, since
643 * AFS 3 dir page size may be less than (but not more than) a
644 * buffer package buffer.
646 /* only look intra-buffer */
647 temp = curOffset.LowPart & (cm_data.buf_blockSize - 1);
648 temp &= ~(2048 - 1); /* turn off intra-page bits */
649 pageHeaderp = (cm_pageHeader_t *) (bufferp->datap + temp);
651 /* now determine which entry we're looking at in the page. If
652 * it is free (there's a free bitmap at the start of the dir),
653 * we should skip these 32 bytes.
655 slotInPage = (entryInDir & 0x7e0) >> 5;
656 if (!(pageHeaderp->freeBitmap[slotInPage>>3]
657 & (1 << (slotInPage & 0x7)))) {
658 /* this entry is free */
659 numDirChunks = 1; /* only skip this guy */
663 tp = bufferp->datap + entryInBuffer;
664 dep = (cm_dirEntry_t *) tp; /* now points to AFS3 dir entry */
667 * here are some consistency checks
669 if (dep->flag != CM_DIR_FFIRST ||
670 strlen(dep->name) > 256) {
671 code = CM_ERROR_INVAL;
673 "cm_ApplyDir invalid directory entry for scp %p bufp %p",
675 osi_Log4(afsd_logp,"... cell %u vol %u vnode %u uniq %u",
676 scp->fid.cell, scp->fid.volume, scp->fid.vnode, scp->fid.unique);
677 bufferp->dataVersion = CM_BUF_VERSION_BAD;
681 /* while we're here, compute the next entry's location, too,
682 * since we'll need it when writing out the cookie into the
683 * dir listing stream.
685 numDirChunks = cm_NameEntries(dep->name, NULL);
687 /* compute the offset of the cookie representing the next entry */
688 nextEntryCookie = curOffset.LowPart
689 + (CM_DIR_CHUNKSIZE * numDirChunks);
691 if (dep->fid.vnode != 0) {
692 /* this is one of the entries to use: it is not deleted */
693 code = (*funcp)(scp, dep, parmp, &curOffset);
696 } /* if we're including this name */
699 /* and adjust curOffset to be where the new cookie is */
701 thyper.LowPart = CM_DIR_CHUNKSIZE * numDirChunks;
702 curOffset = LargeIntegerAdd(thyper, curOffset);
703 } /* while copying data for dir listing */
705 /* release the mutex */
707 lock_ReleaseMutex(&bufferp->mx);
708 buf_Release(bufferp);
713 int cm_NoneUpper(normchar_t *s)
717 if (c >= 'A' && c <= 'Z')
722 int cm_NoneLower(normchar_t *s)
726 if (c >= 'a' && c <= 'z')
731 long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
734 cm_lookupSearch_t *sp;
736 normchar_t matchName[MAX_PATH];
737 int looking_for_short_name = FALSE;
739 sp = (cm_lookupSearch_t *) rockp;
741 if (cm_FsStringToNormString(dep->name, -1, matchName, lengthof(matchName)) == 0) {
742 /* Can't normalize FS string. */
747 match = cm_NormStrCmpI(matchName, sp->nsearchNamep);
749 match = cm_NormStrCmp(matchName, sp->nsearchNamep);
753 && !cm_Is8Dot3(matchName)) {
755 cm_Gen8Dot3NameInt(dep->name, &dep->fid, matchName, NULL);
757 match = cm_NormStrCmpI(matchName, sp->nsearchNamep);
759 match = cm_NormStrCmp(matchName, sp->nsearchNamep);
760 looking_for_short_name = TRUE;
770 if (!sp->caseFold || looking_for_short_name) {
771 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
772 return CM_ERROR_STOPNOW;
776 * If we get here, we are doing a case-insensitive search, and we
777 * have found a match. Now we determine what kind of match it is:
778 * exact, lower-case, upper-case, or none of the above. This is done
779 * in order to choose among matches, if there are more than one.
782 /* Exact matches are the best. */
783 match = cm_NormStrCmp(matchName, sp->nsearchNamep);
786 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
787 return CM_ERROR_STOPNOW;
790 /* Lower-case matches are next. */
793 if (cm_NoneUpper(matchName)) {
798 /* Upper-case matches are next. */
801 if (cm_NoneLower(matchName)) {
806 /* General matches are last. */
812 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
816 /* read the contents of a mount point into the appropriate string.
817 * called with write locked scp, and returns with locked scp.
819 long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
823 if (scp->mountPointStringp[0])
826 #ifdef AFS_FREELANCE_CLIENT
827 /* File servers do not have data for freelance entries */
828 if (cm_freelanceEnabled &&
829 scp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
830 scp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
832 code = cm_FreelanceFetchMountPointString(scp);
834 #endif /* AFS_FREELANCE_CLIENT */
836 char temp[MOUNTPOINTLEN];
839 /* otherwise, we have to read it in */
840 offset.LowPart = offset.HighPart = 0;
841 code = cm_GetData(scp, &offset, temp, MOUNTPOINTLEN, userp, reqp);
846 * scp->length is the actual length of the mount point string.
847 * It is current because cm_GetData merged the most up to date
848 * status info into scp and has not dropped the rwlock since.
850 if (scp->length.LowPart > MOUNTPOINTLEN - 1)
851 return CM_ERROR_TOOBIG;
852 if (scp->length.LowPart == 0)
853 return CM_ERROR_INVAL;
855 /* convert the terminating dot to a NUL */
856 temp[scp->length.LowPart - 1] = 0;
857 memcpy(scp->mountPointStringp, temp, scp->length.LowPart);
864 /* called with a locked scp and chases the mount point, yielding outScpp.
865 * scp remains write locked, just for simplicity of describing the interface.
867 long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
868 cm_req_t *reqp, cm_scache_t **outScpp)
870 fschar_t *cellNamep = NULL;
871 fschar_t *volNamep = NULL;
875 cm_volume_t *volp = NULL;
884 if (scp->mountRootFid.cell != 0 && scp->mountRootGen >= cm_data.mountRootGen) {
885 tfid = scp->mountRootFid;
886 lock_ReleaseWrite(&scp->rw);
887 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
888 lock_ObtainWrite(&scp->rw);
892 /* parse the volume name */
893 mpNamep = scp->mountPointStringp;
895 return CM_ERROR_NOSUCHPATH;
896 mtType = *scp->mountPointStringp;
898 cp = cm_FsStrChr(mpNamep, _FS(':'));
900 /* cellular mount point */
901 cellNamep = (fschar_t *)malloc((cp - mpNamep) * sizeof(fschar_t));
902 cm_FsStrCpyN(cellNamep, cp - mpNamep, mpNamep + 1, cp - mpNamep - 1);
903 volNamep = cm_FsStrDup(cp+1);
905 /* now look up the cell */
906 lock_ReleaseWrite(&scp->rw);
907 cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE);
908 lock_ObtainWrite(&scp->rw);
911 volNamep = cm_FsStrDup(mpNamep + 1);
913 #ifdef AFS_FREELANCE_CLIENT
915 * Mount points in the Freelance cell should default
916 * to the workstation cell.
918 if (cm_freelanceEnabled &&
919 scp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
920 scp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
922 fschar_t rootCellName[256]="";
923 cm_GetRootCellName(rootCellName);
924 cellp = cm_GetCell(rootCellName, 0);
926 #endif /* AFS_FREELANCE_CLIENT */
927 cellp = cm_FindCellByID(scp->fid.cell, 0);
931 code = CM_ERROR_NOSUCHCELL;
935 vnLength = cm_FsStrLen(volNamep);
936 if (vnLength >= 8 && cm_FsStrCmp(volNamep + vnLength - 7, ".backup") == 0)
937 targetType = BACKVOL;
938 else if (vnLength >= 10
939 && cm_FsStrCmp(volNamep + vnLength - 9, ".readonly") == 0)
944 /* check for backups within backups */
945 if (targetType == BACKVOL
946 && (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
947 == CM_SCACHEFLAG_RO) {
948 code = CM_ERROR_NOSUCHVOLUME;
952 /* now we need to get the volume */
953 lock_ReleaseWrite(&scp->rw);
954 if (cm_VolNameIsID(volNamep)) {
955 code = cm_FindVolumeByID(cellp, atoi(volNamep), userp, reqp,
956 CM_GETVOL_FLAG_CREATE, &volp);
958 code = cm_FindVolumeByName(cellp, volNamep, userp, reqp,
959 CM_GETVOL_FLAG_CREATE, &volp);
961 lock_ObtainWrite(&scp->rw);
964 afs_uint32 cell, volume;
965 cm_vol_state_t *statep;
967 cell = cellp->cellID;
969 /* if the mt pt originates in a .backup volume (not a .readonly)
970 * and FollowBackupPath is active, and if there is a .backup
971 * volume for the target, then use the .backup of the target
972 * instead of the read-write.
974 if (cm_followBackupPath &&
975 volp->vol[BACKVOL].ID != 0 &&
976 (dscp->flags & (CM_SCACHEFLAG_RO|CM_SCACHEFLAG_PURERO)) == CM_SCACHEFLAG_RO &&
977 (targetType == RWVOL || targetType == ROVOL && volp->vol[ROVOL].ID == 0)
979 targetType = BACKVOL;
981 /* if the mt pt is in a read-only volume (not just a
982 * backup), and if there is a read-only volume for the
983 * target, and if this is a targetType '#' mount point, use
984 * the read-only, otherwise use the one specified.
986 else if (mtType == '#' && targetType == RWVOL &&
987 (scp->flags & CM_SCACHEFLAG_PURERO) &&
988 volp->vol[ROVOL].ID != 0) {
992 lock_ObtainWrite(&volp->rw);
993 statep = cm_VolumeStateByType(volp, targetType);
995 statep->dotdotFid = dscp->fid;
996 lock_ReleaseWrite(&volp->rw);
998 /* the rest of the fid is a magic number */
999 cm_SetFid(&scp->mountRootFid, cell, volume, 1, 1);
1000 scp->mountRootGen = cm_data.mountRootGen;
1002 tfid = scp->mountRootFid;
1003 lock_ReleaseWrite(&scp->rw);
1004 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
1005 lock_ObtainWrite(&scp->rw);
1018 long cm_LookupInternal(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_user_t *userp,
1019 cm_req_t *reqp, cm_scache_t **outScpp)
1022 int dnlcHit = 1; /* did we hit in the dnlc? yes, we did */
1023 cm_scache_t *tscp = NULL;
1024 cm_scache_t *mountedScp;
1025 cm_lookupSearch_t rock;
1027 normchar_t *nnamep = NULL;
1028 fschar_t *fnamep = NULL;
1033 memset(&rock, 0, sizeof(rock));
1035 if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
1036 && cm_ClientStrCmp(cnamep, _C("..")) == 0) {
1037 if (dscp->dotdotFid.volume == 0)
1038 return CM_ERROR_NOSUCHVOLUME;
1039 rock.fid = dscp->dotdotFid;
1041 } else if (cm_ClientStrCmp(cnamep, _C(".")) == 0) {
1042 rock.fid = dscp->fid;
1046 nnamep = cm_ClientStringToNormStringAlloc(cnamep, -1, NULL);
1048 code = CM_ERROR_NOSUCHFILE;
1051 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
1053 code = CM_ERROR_NOSUCHFILE;
1058 if (flags & CM_FLAG_NOMOUNTCHASE) {
1059 /* In this case, we should go and call cm_Dir* functions
1060 directly since the following cm_ApplyDir() function will
1068 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ,
1069 CM_DIROP_FLAG_NONE, &dirop);
1072 code = cm_BPlusDirLookup(&dirop, nnamep, &rock.fid);
1077 code = cm_DirLookup(&dirop, fnamep, &rock.fid);
1079 cm_EndDirOp(&dirop);
1089 if (code == CM_ERROR_INEXACT_MATCH && (flags & CM_FLAG_CASEFOLD)) {
1096 code = CM_ERROR_BPLUS_NOMATCH;
1102 rock.fid.cell = dscp->fid.cell;
1103 rock.fid.volume = dscp->fid.volume;
1104 rock.searchNamep = fnamep;
1105 rock.nsearchNamep = nnamep;
1106 rock.caseFold = (flags & CM_FLAG_CASEFOLD);
1107 rock.hasTilde = ((cm_ClientStrChr(cnamep, '~') != NULL) ? 1 : 0);
1109 /* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
1110 code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
1111 (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
1113 /* code == 0 means we fell off the end of the dir, while stopnow means
1114 * that we stopped early, probably because we found the entry we're
1115 * looking for. Any other non-zero code is an error.
1117 if (code && code != CM_ERROR_STOPNOW && code != CM_ERROR_BPLUS_NOMATCH) {
1118 /* if the cm_scache_t we are searching in is not a directory
1119 * we must return path not found because the error
1120 * is to describe the final component not an intermediary
1122 if (code == CM_ERROR_NOTDIR) {
1123 if (flags & CM_FLAG_CHECKPATH)
1124 code = CM_ERROR_NOSUCHPATH;
1126 code = CM_ERROR_NOSUCHFILE;
1132 getroot = (dscp==cm_data.rootSCachep) ;
1134 if (!cm_freelanceEnabled || !getroot) {
1135 if (flags & CM_FLAG_CHECKPATH)
1136 code = CM_ERROR_NOSUCHPATH;
1138 code = CM_ERROR_NOSUCHFILE;
1141 else if (!cm_ClientStrChr(cnamep, '#') &&
1142 !cm_ClientStrChr(cnamep, '%') &&
1143 cm_ClientStrCmpI(cnamep, _C("srvsvc")) &&
1144 cm_ClientStrCmpI(cnamep, _C("wkssvc")) &&
1145 cm_ClientStrCmpI(cnamep, _C("ipc$")))
1147 /* nonexistent dir on freelance root, so add it */
1148 fschar_t fullname[CELL_MAXNAMELEN + 1] = "."; /* +1 so that when we skip the . the size is still CELL_MAXNAMELEN */
1152 osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %S",
1153 osi_LogSaveClientString(afsd_logp,cnamep));
1156 * There is an ugly behavior where a share name "foo" will be searched
1157 * for as "fo". If the searched for name differs by an already existing
1158 * symlink or mount point in the Freelance directory, do not add the
1159 * new value automatically.
1163 fnlen = strlen(fnamep);
1164 if ( fnamep[fnlen-1] == '.') {
1165 fnamep[fnlen-1] = '\0';
1170 if (cnamep[0] == '.') {
1171 if (cm_GetCell_Gen(&fnamep[1], &fullname[1], CM_FLAG_CREATE)) {
1173 code = cm_FreelanceAddMount(fullname, &fullname[1], "root.cell", 1, &rock.fid);
1174 if ( cm_FsStrCmpI(&fnamep[1], &fullname[1])) {
1176 * Do not permit symlinks that are one of:
1177 * . the cellname followed by a dot
1178 * . the cellname minus a single character
1179 * . a substring of the cellname that does not consist of full components
1181 if ( cm_strnicmp_utf8(&fnamep[1], fullname, (int)fnlen-1) == 0 &&
1182 (fnlen-1 == strlen(fullname)-1 || fullname[fnlen-1] != '.'))
1184 /* do not add; substitute fullname for the search */
1186 fnamep = malloc(strlen(fullname)+2);
1188 strncpy(&fnamep[1], fullname, strlen(fullname)+1);
1191 code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
1196 if (cm_GetCell_Gen(fnamep, fullname, CM_FLAG_CREATE)) {
1198 code = cm_FreelanceAddMount(fullname, fullname, "root.cell", 0, &rock.fid);
1199 if ( cm_FsStrCmpI(fnamep, fullname)) {
1201 * Do not permit symlinks that are one of:
1202 * . the cellname followed by a dot
1203 * . the cellname minus a single character
1204 * . a substring of the cellname that does not consist of full components
1206 if ( cm_strnicmp_utf8(fnamep, fullname, (int)fnlen-1) == 0 &&
1207 (fnlen == strlen(fullname)-1 || fullname[fnlen] != '.'))
1209 /* do not add; substitute fullname for the search */
1211 fnamep = strdup(fullname);
1215 code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
1224 nnamep = cm_FsStringToNormStringAlloc(fnamep, -1, NULL);
1228 if (!found || code) { /* add mount point failed, so give up */
1229 if (flags & CM_FLAG_CHECKPATH)
1230 code = CM_ERROR_NOSUCHPATH;
1232 code = CM_ERROR_NOSUCHFILE;
1235 tscp = NULL; /* to force call of cm_GetSCache */
1237 if (flags & CM_FLAG_CHECKPATH)
1238 code = CM_ERROR_NOSUCHPATH;
1240 code = CM_ERROR_NOSUCHFILE;
1246 if ( !tscp ) /* we did not find it in the dnlc */
1249 code = cm_GetSCache(&rock.fid, &tscp, userp, reqp);
1253 /* tscp is now held */
1255 lock_ObtainWrite(&tscp->rw);
1258 * Do not get status if we do not already have a callback.
1259 * The process of reading the mount point string will obtain status information
1260 * in a single RPC. No reason to add a second round trip.
1262 * If we do have a callback, use cm_SyncOp to get status in case the
1263 * current cm_user_t is not the same as the one that obtained the
1264 * mount point string contents.
1266 if (cm_HaveCallback(tscp)) {
1267 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1268 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1270 lock_ReleaseWrite(&tscp->rw);
1271 cm_ReleaseSCache(tscp);
1274 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1276 /* tscp is now locked */
1278 if (!(flags & CM_FLAG_NOMOUNTCHASE)
1279 && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
1280 /* mount points are funny: they have a volume name to mount
1283 code = cm_ReadMountPoint(tscp, userp, reqp);
1285 code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
1287 lock_ReleaseWrite(&tscp->rw);
1288 cm_ReleaseSCache(tscp);
1295 lock_ReleaseWrite(&tscp->rw);
1298 /* copy back pointer */
1301 /* insert scache in dnlc */
1302 if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
1303 /* lock the directory entry to prevent racing callback revokes */
1304 lock_ObtainRead(&dscp->rw);
1305 if ( dscp->cbServerp != NULL && dscp->cbExpires > 0 ) {
1306 /* TODO: reuse nnamep from above */
1309 nnamep = cm_ClientStringToNormStringAlloc(cnamep, -1, NULL);
1311 cm_dnlcEnter(dscp, nnamep, tscp);
1313 lock_ReleaseRead(&dscp->rw);
1330 int cm_ExpandSysName(clientchar_t *inp, clientchar_t *outp, long outSizeCch, unsigned int index)
1335 tp = cm_ClientStrRChr(inp, '@');
1337 return 0; /* no @sys */
1339 if (cm_ClientStrCmp(tp, _C("@sys")) != 0)
1340 return 0; /* no @sys */
1342 /* caller just wants to know if this is a valid @sys type of name */
1346 if (index >= cm_sysNameCount)
1349 /* otherwise generate the properly expanded @sys name */
1350 prefixCount = (int)(tp - inp);
1352 cm_ClientStrCpyN(outp, outSizeCch, inp, prefixCount); /* copy out "a." from "a.@sys" */
1353 outp[prefixCount] = 0; /* null terminate the "a." */
1354 cm_ClientStrCat(outp, outSizeCch, cm_sysNameList[index]);/* append i386_nt40 */
1358 long cm_EvaluateVolumeReference(clientchar_t * namep, long flags, cm_user_t * userp,
1359 cm_req_t *reqp, cm_scache_t ** outScpp)
1361 afs_uint32 code = 0;
1362 fschar_t cellName[CELL_MAXNAMELEN];
1363 fschar_t volumeName[VL_MAXNAMELEN];
1367 fschar_t * fnamep = NULL;
1369 cm_cell_t * cellp = NULL;
1370 cm_volume_t * volp = NULL;
1374 int mountType = RWVOL;
1376 osi_Log1(afsd_logp, "cm_EvaluateVolumeReference for string [%S]",
1377 osi_LogSaveClientString(afsd_logp, namep));
1379 if (cm_ClientStrCmpNI(namep, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH) != 0) {
1380 goto _exit_invalid_path;
1383 /* namep is assumed to look like the following:
1385 @vol:<cellname>%<volume>\0
1387 @vol:<cellname>#<volume>\0
1391 fnamep = cm_ClientStringToFsStringAlloc(namep, -1, NULL);
1392 cp = fnamep + CM_PREFIX_VOL_CCH; /* cp points to cell name, hopefully */
1393 tp = cm_FsStrChr(cp, '%');
1395 tp = cm_FsStrChr(cp, '#');
1397 (len = tp - cp) == 0 ||
1398 len > CELL_MAXNAMELEN)
1399 goto _exit_invalid_path;
1400 cm_FsStrCpyN(cellName, lengthof(cellName), cp, len);
1405 cp = tp+1; /* cp now points to volume, supposedly */
1406 cm_FsStrCpy(volumeName, lengthof(volumeName), cp);
1408 /* OK, now we have the cell and the volume */
1409 osi_Log2(afsd_logp, " Found cell [%s] and volume [%s]",
1410 osi_LogSaveFsString(afsd_logp, cellName),
1411 osi_LogSaveFsString(afsd_logp, volumeName));
1413 cellp = cm_GetCell(cellName, CM_FLAG_CREATE);
1414 if (cellp == NULL) {
1415 goto _exit_invalid_path;
1418 len = cm_FsStrLen(volumeName);
1419 if (len >= 8 && cm_FsStrCmp(volumeName + len - 7, ".backup") == 0)
1421 else if (len >= 10 &&
1422 cm_FsStrCmp(volumeName + len - 9, ".readonly") == 0)
1427 if (cm_VolNameIsID(volumeName)) {
1428 code = cm_FindVolumeByID(cellp, atoi(volumeName), userp, reqp,
1429 CM_GETVOL_FLAG_CREATE, &volp);
1431 code = cm_FindVolumeByName(cellp, volumeName, userp, reqp,
1432 CM_GETVOL_FLAG_CREATE, &volp);
1438 if (volType == BACKVOL)
1439 volume = volp->vol[BACKVOL].ID;
1440 else if (volType == ROVOL ||
1441 (volType == RWVOL && mountType == ROVOL && volp->vol[ROVOL].ID != 0))
1442 volume = volp->vol[ROVOL].ID;
1444 volume = volp->vol[RWVOL].ID;
1446 cm_SetFid(&fid, cellp->cellID, volume, 1, 1);
1448 code = cm_GetSCache(&fid, outScpp, userp, reqp);
1461 if (flags & CM_FLAG_CHECKPATH)
1462 return CM_ERROR_NOSUCHPATH;
1464 return CM_ERROR_NOSUCHFILE;
1467 #ifdef DEBUG_REFCOUNT
1468 long cm_LookupDbg(cm_scache_t *dscp, clientchar_t *namep, long flags, cm_user_t *userp,
1469 cm_req_t *reqp, cm_scache_t **outScpp, char * file, long line)
1471 long cm_Lookup(cm_scache_t *dscp, clientchar_t *namep, long flags, cm_user_t *userp,
1472 cm_req_t *reqp, cm_scache_t **outScpp)
1476 clientchar_t tname[AFSPATHMAX];
1477 int sysNameIndex = 0;
1478 cm_scache_t *scp = NULL;
1480 #ifdef DEBUG_REFCOUNT
1481 afsi_log("%s:%d cm_Lookup dscp 0x%p ref %d", file, line, dscp, dscp->refCount, file, line);
1482 osi_Log2(afsd_logp, "cm_Lookup dscp 0x%p ref %d", dscp, dscp->refCount);
1485 if ( cm_ClientStrCmpI(namep,_C(SMB_IOCTL_FILENAME_NOSLASH)) == 0 ) {
1486 if (flags & CM_FLAG_CHECKPATH)
1487 return CM_ERROR_NOSUCHPATH;
1489 return CM_ERROR_NOSUCHFILE;
1492 if (dscp == cm_data.rootSCachep &&
1493 cm_ClientStrCmpNI(namep, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH) == 0) {
1494 return cm_EvaluateVolumeReference(namep, flags, userp, reqp, outScpp);
1497 if (cm_ExpandSysName(namep, NULL, 0, 0) > 0) {
1498 for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
1499 code = cm_ExpandSysName(namep, tname, lengthof(tname), sysNameIndex);
1501 code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
1502 #ifdef DEBUG_REFCOUNT
1503 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);
1504 osi_Log3(afsd_logp, "cm_LookupInternal (1) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1512 cm_ReleaseSCache(scp);
1516 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1517 #ifdef DEBUG_REFCOUNT
1518 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);
1519 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1526 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1527 #ifdef DEBUG_REFCOUNT
1528 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);
1529 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1535 /* None of the possible sysName expansions could be found */
1536 if (flags & CM_FLAG_CHECKPATH)
1537 return CM_ERROR_NOSUCHPATH;
1539 return CM_ERROR_NOSUCHFILE;
1542 /*! \brief Unlink a file name
1544 Encapsulates a call to RXAFS_RemoveFile().
1546 \param[in] dscp cm_scache_t pointing at the directory containing the
1547 name to be unlinked.
1549 \param[in] fnamep Original name to be unlinked. This is the
1550 name that will be passed into the RXAFS_RemoveFile() call.
1551 This parameter is optional. If not provided, the value will
1554 \param[in] came Client name to be unlinked. This name will be used
1555 to update the local directory caches.
1557 \param[in] userp cm_user_t for the request.
1559 \param[in] reqp Request tracker.
1562 long cm_Unlink(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t * cnamep,
1563 cm_user_t *userp, cm_req_t *reqp)
1569 AFSFetchStatus newDirStatus;
1571 struct rx_connection * rxconnp;
1573 cm_scache_t *scp = NULL;
1574 int free_fnamep = FALSE;
1576 memset(&volSync, 0, sizeof(volSync));
1578 if (fnamep == NULL) {
1581 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ,
1582 CM_DIROP_FLAG_NONE, &dirop);
1584 code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
1587 cm_EndDirOp(&dirop);
1594 #ifdef AFS_FREELANCE_CLIENT
1595 if (cm_freelanceEnabled && dscp == cm_data.rootSCachep) {
1596 /* deleting a mount point from the root dir. */
1597 code = cm_FreelanceRemoveMount(fnamep);
1602 code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
1606 /* Check for RO volume */
1607 if (dscp->flags & CM_SCACHEFLAG_RO) {
1608 code = CM_ERROR_READONLY;
1612 /* make sure we don't screw up the dir status during the merge */
1613 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE,
1614 CM_DIROP_FLAG_NONE, &dirop);
1616 lock_ObtainWrite(&dscp->rw);
1617 sflags = CM_SCACHESYNC_STOREDATA;
1618 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1619 lock_ReleaseWrite(&dscp->rw);
1621 cm_EndDirOp(&dirop);
1626 afsFid.Volume = dscp->fid.volume;
1627 afsFid.Vnode = dscp->fid.vnode;
1628 afsFid.Unique = dscp->fid.unique;
1630 osi_Log1(afsd_logp, "CALL RemoveFile scp 0x%p", dscp);
1632 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
1636 rxconnp = cm_GetRxConn(connp);
1637 code = RXAFS_RemoveFile(rxconnp, &afsFid, fnamep,
1638 &newDirStatus, &volSync);
1639 rx_PutConnection(rxconnp);
1641 } while (cm_Analyze(connp, userp, reqp, &dscp->fid, &volSync, NULL, NULL, code));
1642 code = cm_MapRPCError(code, reqp);
1645 osi_Log1(afsd_logp, "CALL RemoveFile FAILURE, code 0x%x", code);
1647 osi_Log0(afsd_logp, "CALL RemoveFile SUCCESS");
1650 lock_ObtainWrite(&dirop.scp->dirlock);
1651 dirop.lockType = CM_DIRLOCK_WRITE;
1653 lock_ObtainWrite(&dscp->rw);
1654 cm_dnlcRemove(dscp, cnamep);
1656 cm_MergeStatus(NULL, dscp, &newDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
1657 } else if (code == CM_ERROR_NOSUCHFILE) {
1658 /* windows would not have allowed the request to delete the file
1659 * if it did not believe the file existed. therefore, we must
1660 * have an inconsistent view of the world.
1662 dscp->cbServerp = NULL;
1664 cm_SyncOpDone(dscp, NULL, sflags);
1665 lock_ReleaseWrite(&dscp->rw);
1667 if (code == 0 && cm_CheckDirOpForSingleChange(&dirop) && cnamep) {
1668 cm_DirDeleteEntry(&dirop, fnamep);
1670 cm_BPlusDirDeleteEntry(&dirop, cnamep);
1673 cm_EndDirOp(&dirop);
1676 cm_ReleaseSCache(scp);
1678 lock_ObtainWrite(&scp->rw);
1679 if (--scp->linkCount == 0) {
1680 scp->flags |= CM_SCACHEFLAG_DELETED;
1681 lock_ObtainWrite(&cm_scacheLock);
1682 cm_AdjustScacheLRU(scp);
1683 cm_RemoveSCacheFromHashTable(scp);
1684 lock_ReleaseWrite(&cm_scacheLock);
1686 cm_DiscardSCache(scp);
1687 lock_ReleaseWrite(&scp->rw);
1698 /* called with a write locked vnode, and fills in the link info.
1699 * returns this the vnode still write locked.
1701 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1705 lock_AssertWrite(&linkScp->rw);
1706 if (!linkScp->mountPointStringp[0]) {
1708 #ifdef AFS_FREELANCE_CLIENT
1709 /* File servers do not have data for freelance entries */
1710 if (cm_freelanceEnabled &&
1711 linkScp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
1712 linkScp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
1714 code = cm_FreelanceFetchMountPointString(linkScp);
1716 #endif /* AFS_FREELANCE_CLIENT */
1718 char temp[MOUNTPOINTLEN];
1721 /* read the link data from the file server */
1722 offset.LowPart = offset.HighPart = 0;
1723 code = cm_GetData(linkScp, &offset, temp, MOUNTPOINTLEN, userp, reqp);
1728 * linkScp->length is the actual length of the symlink target string.
1729 * It is current because cm_GetData merged the most up to date
1730 * status info into scp and has not dropped the rwlock since.
1732 if (linkScp->length.LowPart > MOUNTPOINTLEN - 1)
1733 return CM_ERROR_TOOBIG;
1734 if (linkScp->length.LowPart == 0)
1735 return CM_ERROR_INVAL;
1737 /* make sure we are NUL terminated */
1738 temp[linkScp->length.LowPart] = 0;
1739 memcpy(linkScp->mountPointStringp, temp, linkScp->length.LowPart + 1);
1742 if ( !strnicmp(linkScp->mountPointStringp, "msdfs:", strlen("msdfs:")) )
1743 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1745 } /* don't have symlink contents cached */
1750 /* called with a held vnode and a path suffix, with the held vnode being a
1751 * symbolic link. Our goal is to generate a new path to interpret, and return
1752 * this new path in newSpaceBufferp. If the new vnode is relative to a dir
1753 * other than the directory containing the symbolic link, then the new root is
1754 * returned in *newRootScpp, otherwise a null is returned there.
1756 long cm_AssembleLink(cm_scache_t *linkScp, fschar_t *pathSuffixp,
1757 cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1758 cm_user_t *userp, cm_req_t *reqp)
1765 *newRootScpp = NULL;
1766 *newSpaceBufferp = NULL;
1768 lock_ObtainWrite(&linkScp->rw);
1770 * Do not get status if we do not already have a callback.
1771 * The process of reading the symlink string will obtain status information
1772 * in a single RPC. No reason to add a second round trip.
1774 * If we do have a callback, use cm_SyncOp to get status in case the
1775 * current cm_user_t is not the same as the one that obtained the
1776 * symlink string contents.
1778 if (cm_HaveCallback(linkScp)) {
1779 code = cm_SyncOp(linkScp, NULL, userp, reqp, 0,
1780 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1782 lock_ReleaseWrite(&linkScp->rw);
1783 cm_ReleaseSCache(linkScp);
1786 cm_SyncOpDone(linkScp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1788 code = cm_HandleLink(linkScp, userp, reqp);
1792 /* if we may overflow the buffer, bail out; buffer is signficantly
1793 * bigger than max path length, so we don't really have to worry about
1794 * being a little conservative here.
1796 if (cm_FsStrLen(linkScp->mountPointStringp) + cm_FsStrLen(pathSuffixp) + 2
1797 >= CM_UTILS_SPACESIZE) {
1798 code = CM_ERROR_TOOBIG;
1802 tsp = cm_GetSpace();
1803 linkp = linkScp->mountPointStringp;
1804 if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1805 if (strlen(linkp) > cm_mountRootLen)
1806 StringCbCopyA((char *) tsp->data, sizeof(tsp->data), linkp+cm_mountRootLen+1);
1809 *newRootScpp = cm_RootSCachep(userp, reqp);
1810 cm_HoldSCache(*newRootScpp);
1811 } else if (linkp[0] == '\\' && linkp[1] == '\\') {
1812 if (!strnicmp(&linkp[2], cm_NetbiosName, (len = (long)strlen(cm_NetbiosName))))
1814 char * p = &linkp[len + 3];
1815 if (strnicmp(p, "all", 3) == 0)
1818 StringCbCopyA(tsp->data, sizeof(tsp->data), p);
1819 for (p = tsp->data; *p; p++) {
1823 *newRootScpp = cm_RootSCachep(userp, reqp);
1824 cm_HoldSCache(*newRootScpp);
1826 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1827 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1828 code = CM_ERROR_PATH_NOT_COVERED;
1830 } else if ( linkScp->fileType == CM_SCACHETYPE_DFSLINK ||
1831 !strnicmp(linkp, "msdfs:", (len = (long)strlen("msdfs:"))) ) {
1832 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1833 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1834 code = CM_ERROR_PATH_NOT_COVERED;
1835 } else if (*linkp == '\\' || *linkp == '/') {
1837 /* formerly, this was considered to be from the AFS root,
1838 * but this seems to create problems. instead, we will just
1839 * reject the link */
1840 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp+1);
1841 *newRootScpp = cm_RootSCachep(userp, reqp);
1842 cm_HoldSCache(*newRootScpp);
1844 /* we still copy the link data into the response so that
1845 * the user can see what the link points to
1847 linkScp->fileType = CM_SCACHETYPE_INVALID;
1848 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1849 code = CM_ERROR_NOSUCHPATH;
1852 /* a relative link */
1853 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1855 if (pathSuffixp[0] != 0) { /* if suffix string is non-null */
1856 StringCchCatA(tsp->data,lengthof(tsp->data), "\\");
1857 StringCchCatA(tsp->data,lengthof(tsp->data), pathSuffixp);
1861 clientchar_t * cpath = cm_FsStringToClientStringAlloc(tsp->data, -1, NULL);
1862 if (cpath != NULL) {
1863 cm_ClientStrCpy(tsp->wdata, lengthof(tsp->wdata), cpath);
1865 *newSpaceBufferp = tsp;
1867 code = CM_ERROR_NOSUCHPATH;
1874 if (code == CM_ERROR_PATH_NOT_COVERED && reqp->tidPathp && reqp->relPathp) {
1875 cm_VolStatus_Notify_DFS_Mapping(linkScp, reqp->tidPathp, reqp->relPathp);
1880 lock_ReleaseWrite(&linkScp->rw);
1883 #ifdef DEBUG_REFCOUNT
1884 long cm_NameIDbg(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
1885 cm_user_t *userp, clientchar_t *tidPathp, cm_req_t *reqp,
1886 cm_scache_t **outScpp,
1887 char * file, long line)
1889 long cm_NameI(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
1890 cm_user_t *userp, clientchar_t *tidPathp,
1891 cm_req_t *reqp, cm_scache_t **outScpp)
1895 clientchar_t *tp; /* ptr moving through input buffer */
1896 clientchar_t tc; /* temp char */
1897 int haveComponent; /* has new component started? */
1898 clientchar_t component[AFSPATHMAX]; /* this is the new component */
1899 clientchar_t *cp; /* component name being assembled */
1900 cm_scache_t *tscp; /* current location in the hierarchy */
1901 cm_scache_t *nscp; /* next dude down */
1902 cm_scache_t *dirScp; /* last dir we searched */
1903 cm_scache_t *linkScp; /* new root for the symlink we just
1905 cm_space_t *psp; /* space for current path, if we've hit
1907 cm_space_t *tempsp; /* temp vbl */
1908 clientchar_t *restp; /* rest of the pathname to interpret */
1909 int symlinkCount; /* count of # of symlinks traversed */
1910 int extraFlag; /* avoid chasing mt pts for dir cmd */
1911 int phase = 1; /* 1 = tidPathp, 2 = pathp */
1912 #define MAX_FID_COUNT 512
1913 cm_fid_t fids[MAX_FID_COUNT]; /* array of fids processed in this path walk */
1914 int fid_count = 0; /* number of fids processed in this path walk */
1919 #ifdef DEBUG_REFCOUNT
1920 afsi_log("%s:%d cm_NameI rootscp 0x%p ref %d", file, line, rootSCachep, rootSCachep->refCount);
1921 osi_Log4(afsd_logp,"cm_NameI rootscp 0x%p path %S tidpath %S flags 0x%x",
1922 rootSCachep, pathp ? pathp : L"<NULL>", tidPathp ? tidPathp : L"<NULL>",
1937 cm_HoldSCache(tscp);
1945 /* map Unix slashes into DOS ones so we can interpret Unix
1951 if (!haveComponent) {
1954 } else if (tc == 0) {
1968 /* we have a component here */
1969 if (tc == 0 || tc == '\\') {
1970 /* end of the component; we're at the last
1971 * component if tc == 0. However, if the last
1972 * is a symlink, we have more to do.
1974 *cp++ = 0; /* add null termination */
1976 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
1977 extraFlag = CM_FLAG_NOMOUNTCHASE;
1978 code = cm_Lookup(tscp, component,
1980 userp, reqp, &nscp);
1983 if (!cm_ClientStrCmp(component,_C("..")) ||
1984 !cm_ClientStrCmp(component,_C("."))) {
1986 * roll back the fid list until we find the
1987 * fid that matches where we are now. Its not
1988 * necessarily one or two fids because they
1989 * might have been symlinks or mount points or
1990 * both that were crossed.
1992 for ( i=fid_count-1; i>=0; i--) {
1993 if (!cm_FidCmp(&nscp->fid, &fids[i]))
1998 /* add the new fid to the list */
1999 if (fid_count == MAX_FID_COUNT) {
2000 code = CM_ERROR_TOO_MANY_SYMLINKS;
2001 cm_ReleaseSCache(nscp);
2005 fids[fid_count++] = nscp->fid;
2010 cm_ReleaseSCache(tscp);
2012 cm_ReleaseSCache(dirScp);
2015 if ((code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH) &&
2016 tscp->fileType == CM_SCACHETYPE_SYMLINK) {
2017 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_NOSUCHPATH");
2018 return CM_ERROR_NOSUCHPATH;
2020 osi_Log1(afsd_logp,"cm_NameI code 0x%x", code);
2025 haveComponent = 0; /* component done */
2027 cm_ReleaseSCache(dirScp);
2028 dirScp = tscp; /* for some symlinks */
2029 tscp = nscp; /* already held */
2031 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
2034 cm_ReleaseSCache(dirScp);
2040 /* now, if tscp is a symlink, we should follow it and
2041 * assemble the path again.
2043 lock_ObtainWrite(&tscp->rw);
2044 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
2045 CM_SCACHESYNC_GETSTATUS
2046 | CM_SCACHESYNC_NEEDCALLBACK);
2048 lock_ReleaseWrite(&tscp->rw);
2049 cm_ReleaseSCache(tscp);
2052 cm_ReleaseSCache(dirScp);
2057 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2059 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
2060 /* this is a symlink; assemble a new buffer */
2061 lock_ReleaseWrite(&tscp->rw);
2062 if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
2063 cm_ReleaseSCache(tscp);
2066 cm_ReleaseSCache(dirScp);
2071 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_TOO_MANY_SYMLINKS");
2072 return CM_ERROR_TOO_MANY_SYMLINKS;
2082 /* TODO: make this better */
2083 frestp = cm_ClientStringToFsStringAlloc(restp, -1, NULL);
2084 code = cm_AssembleLink(tscp, frestp, &linkScp, &tempsp, userp, reqp);
2088 if (code == 0 && linkScp != NULL) {
2089 if (linkScp == cm_data.rootSCachep) {
2093 for ( i=0; i<fid_count; i++) {
2094 if ( !cm_FidCmp(&linkScp->fid, &fids[i]) ) {
2095 code = CM_ERROR_TOO_MANY_SYMLINKS;
2096 cm_ReleaseSCache(linkScp);
2102 if (i == fid_count && fid_count < MAX_FID_COUNT) {
2103 fids[fid_count++] = linkScp->fid;
2108 /* something went wrong */
2109 cm_ReleaseSCache(tscp);
2112 cm_ReleaseSCache(dirScp);
2118 /* otherwise, tempsp has the new path,
2119 * and linkScp is the new root from
2120 * which to interpret that path.
2121 * Continue with the namei processing,
2122 * also doing the bookkeeping for the
2123 * space allocation and tracking the
2124 * vnode reference counts.
2130 cm_ReleaseSCache(tscp);
2135 * now, if linkScp is null, that's
2136 * AssembleLink's way of telling us that
2137 * the sym link is relative to the dir
2138 * containing the link. We have a ref
2139 * to it in dirScp, and we hold it now
2140 * and reuse it as the new spot in the
2148 /* not a symlink, we may be done */
2149 lock_ReleaseWrite(&tscp->rw);
2157 cm_ReleaseSCache(dirScp);
2165 cm_ReleaseSCache(dirScp);
2168 } /* end of a component */
2171 } /* we have a component */
2172 } /* big while loop over all components */
2176 cm_ReleaseSCache(dirScp);
2182 cm_ReleaseSCache(tscp);
2184 #ifdef DEBUG_REFCOUNT
2185 afsi_log("%s:%d cm_NameI code 0x%x outScpp 0x%p ref %d", file, line, code, *outScpp, (*outScpp) ? (*outScpp)->refCount : 0);
2187 osi_Log2(afsd_logp,"cm_NameI code 0x%x outScpp 0x%p", code, *outScpp);
2191 /* called with a dir, and a vnode within the dir that happens to be a symlink.
2192 * We chase the link, and return a held pointer to the target, if it exists,
2193 * in *outScpp. If we succeed, we return 0, otherwise we return an error code
2194 * and do not hold or return a target vnode.
2196 * This is very similar to calling cm_NameI with the last component of a name,
2197 * which happens to be a symlink, except that we've already passed by the name.
2199 * This function is typically called by the directory listing functions, which
2200 * encounter symlinks but need to return the proper file length so programs
2201 * like "more" work properly when they make use of the attributes retrieved from
2204 * The input vnode should not be locked when this function is called.
2206 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
2207 cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
2211 cm_scache_t *newRootScp;
2215 osi_Log1(afsd_logp, "Evaluating symlink scp 0x%p", linkScp);
2217 code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
2221 /* now, if newRootScp is NULL, we're really being told that the symlink
2222 * is relative to the current directory (dscp).
2224 if (newRootScp == NULL) {
2226 cm_HoldSCache(dscp);
2229 code = cm_NameI(newRootScp, spacep->wdata,
2230 CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
2231 userp, NULL, reqp, outScpp);
2233 if (code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH)
2234 code = CM_ERROR_NOSUCHPATH;
2236 /* this stuff is allocated no matter what happened on the namei call,
2238 cm_FreeSpace(spacep);
2239 cm_ReleaseSCache(newRootScp);
2241 if (linkScp == *outScpp) {
2242 cm_ReleaseSCache(*outScpp);
2244 code = CM_ERROR_NOSUCHPATH;
2250 /* for a given entry, make sure that it isn't in the stat cache, and then
2251 * add it to the list of file IDs to be obtained.
2253 * Don't bother adding it if we already have a vnode. Note that the dir
2254 * is locked, so we have to be careful checking the vnode we're thinking of
2255 * processing, to avoid deadlocks.
2257 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
2268 /* Don't overflow bsp. */
2269 if (bsp->counter >= CM_BULKMAX)
2270 return CM_ERROR_STOPNOW;
2272 thyper.LowPart = cm_data.buf_blockSize;
2273 thyper.HighPart = 0;
2274 thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
2276 /* thyper is now the first byte past the end of the record we're
2277 * interested in, and bsp->bufOffset is the first byte of the record
2278 * we're interested in.
2279 * Skip data in the others.
2282 if (LargeIntegerLessThan(*offp, bsp->bufOffset))
2284 if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
2285 return CM_ERROR_STOPNOW;
2286 if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
2289 cm_SetFid(&tfid, scp->fid.cell, scp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
2290 tscp = cm_FindSCache(&tfid);
2292 if (lock_TryWrite(&tscp->rw)) {
2293 /* we have an entry that we can look at */
2294 if (!(tscp->flags & CM_SCACHEFLAG_EACCESS) && cm_HaveCallback(tscp)) {
2295 /* we have a callback on it. Don't bother
2296 * fetching this stat entry, since we're happy
2297 * with the info we have.
2299 lock_ReleaseWrite(&tscp->rw);
2300 cm_ReleaseSCache(tscp);
2303 lock_ReleaseWrite(&tscp->rw);
2305 cm_ReleaseSCache(tscp);
2308 #ifdef AFS_FREELANCE_CLIENT
2309 // yj: if this is a mountpoint under root.afs then we don't want it
2310 // to be bulkstat-ed, instead, we call getSCache directly and under
2311 // getSCache, it is handled specially.
2312 if ( cm_freelanceEnabled &&
2313 tfid.cell==AFS_FAKE_ROOT_CELL_ID &&
2314 tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
2315 !(tfid.vnode==0x1 && tfid.unique==0x1) )
2317 osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
2318 return cm_GetSCache(&tfid, &tscp, NULL, NULL);
2320 #endif /* AFS_FREELANCE_CLIENT */
2323 bsp->fids[i].Volume = scp->fid.volume;
2324 bsp->fids[i].Vnode = tfid.vnode;
2325 bsp->fids[i].Unique = tfid.unique;
2330 cm_TryBulkStatRPC(cm_scache_t *dscp, cm_bulkStat_t *bbp, cm_user_t *userp, cm_req_t *reqp)
2333 AFSCBFids fidStruct;
2334 AFSBulkStats statStruct;
2336 AFSCBs callbackStruct;
2339 cm_callbackRequest_t cbReq;
2345 struct rx_connection * rxconnp;
2346 int inlinebulk; /* Did we use InlineBulkStatus RPC or not? */
2348 memset(&volSync, 0, sizeof(volSync));
2350 /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
2351 * make the calls to create the entries. Handle AFSCBMAX files at a
2354 for (filex = 0; filex < bbp->counter; filex += filesThisCall) {
2355 filesThisCall = bbp->counter - filex;
2356 if (filesThisCall > AFSCBMAX)
2357 filesThisCall = AFSCBMAX;
2359 fidStruct.AFSCBFids_len = filesThisCall;
2360 fidStruct.AFSCBFids_val = &bbp->fids[filex];
2361 statStruct.AFSBulkStats_len = filesThisCall;
2362 statStruct.AFSBulkStats_val = &bbp->stats[filex];
2363 callbackStruct.AFSCBs_len = filesThisCall;
2364 callbackStruct.AFSCBs_val = &bbp->callbacks[filex];
2365 cm_StartCallbackGrantingCall(NULL, &cbReq);
2366 osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
2369 * Whenever cm_Analyze is called for a RXAFS_ RPC there must
2370 * be a FID provided. However, the error code from RXAFS_BulkStatus
2371 * or RXAFS_InlinkBulkStatus does not apply to any FID. Therefore,
2372 * we generate an invalid FID to match with the RPC error.
2374 cm_SetFid(&tfid, dscp->fid.cell, dscp->fid.volume, 0, 0);
2379 code = cm_ConnFromFID(&tfid, userp, reqp, &connp);
2383 rxconnp = cm_GetRxConn(connp);
2384 if (!(connp->serverp->flags & CM_SERVERFLAG_NOINLINEBULK)) {
2385 code = RXAFS_InlineBulkStatus(rxconnp, &fidStruct,
2386 &statStruct, &callbackStruct, &volSync);
2387 if (code == RXGEN_OPCODE) {
2388 cm_SetServerNoInlineBulk(connp->serverp, 0);
2394 code = RXAFS_BulkStatus(rxconnp, &fidStruct,
2395 &statStruct, &callbackStruct, &volSync);
2397 rx_PutConnection(rxconnp);
2400 * If InlineBulk RPC was called and it succeeded,
2401 * then pull out the return code from the status info
2402 * and use it for cm_Analyze so that we can failover to other
2403 * .readonly volume instances. But only do it for errors that
2404 * are volume global.
2406 if (inlinebulk && code == 0 && (&bbp->stats[0])->errorCode) {
2407 osi_Log1(afsd_logp, "cm_TryBulkStat inline-bulk stat error: %d",
2408 (&bbp->stats[0])->errorCode);
2409 switch ((&bbp->stats[0])->errorCode) {
2418 code = (&bbp->stats[0])->errorCode;
2421 /* Rx and Rxkad errors are volume global */
2422 if ( (&bbp->stats[0])->errorCode >= -64 && (&bbp->stats[0])->errorCode < 0 ||
2423 (&bbp->stats[0])->errorCode >= ERROR_TABLE_BASE_RXK && (&bbp->stats[0])->errorCode < ERROR_TABLE_BASE_RXK + 256)
2424 code = (&bbp->stats[0])->errorCode;
2427 } while (cm_Analyze(connp, userp, reqp, &tfid, &volSync, NULL, &cbReq, code));
2428 code = cm_MapRPCError(code, reqp);
2431 * might as well quit on an error, since we're not going to do
2432 * much better on the next immediate call, either.
2435 osi_Log2(afsd_logp, "CALL %sBulkStatus FAILURE code 0x%x",
2436 inlinebulk ? "Inline" : "", code);
2437 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2442 * The bulk RPC has succeeded or at least not failed with a
2443 * volume global error result. For items that have inlineBulk
2444 * errors we must call cm_Analyze in order to perform required
2445 * logging of errors.
2447 * If the RPC was not inline bulk or the entry either has no error
2448 * the status must be merged.
2450 osi_Log1(afsd_logp, "CALL %sBulkStatus SUCCESS", inlinebulk ? "Inline" : "");
2452 for (i = 0; i<filesThisCall; i++) {
2454 cm_SetFid(&tfid, dscp->fid.cell, bbp->fids[j].Volume, bbp->fids[j].Vnode, bbp->fids[j].Unique);
2456 if (inlinebulk && (&bbp->stats[j])->errorCode) {
2457 cm_req_t treq = *reqp;
2458 cm_Analyze(NULL, userp, &treq, &tfid, &volSync, NULL, &cbReq, (&bbp->stats[j])->errorCode);
2460 code = cm_GetSCache(&tfid, &scp, userp, reqp);
2465 * otherwise, if this entry has no callback info,
2466 * merge in this. If there is existing callback info
2467 * we skip the merge because the existing data must be
2468 * current (we have a callback) and the response from
2469 * a non-inline bulk rpc might actually be wrong.
2471 * now, we have to be extra paranoid on merging in this
2472 * information, since we didn't use cm_SyncOp before
2473 * starting the fetch to make sure that no bad races
2474 * were occurring. Specifically, we need to make sure
2475 * we don't obliterate any newer information in the
2476 * vnode than have here.
2478 * Right now, be pretty conservative: if there's a
2479 * callback or a pending call, skip it.
2480 * However, if the prior attempt to obtain status
2481 * was refused access or the volume is .readonly,
2482 * take the data in any case since we have nothing
2483 * better for the in flight directory enumeration that
2484 * resulted in this function being called.
2486 lock_ObtainRead(&scp->rw);
2487 if ((scp->cbServerp == NULL &&
2488 !(scp->flags & (CM_SCACHEFLAG_FETCHING | CM_SCACHEFLAG_STORING | CM_SCACHEFLAG_SIZESTORING))) ||
2489 (scp->flags & CM_SCACHEFLAG_PURERO) ||
2490 (scp->flags & CM_SCACHEFLAG_EACCESS))
2492 lock_ConvertRToW(&scp->rw);
2493 cm_EndCallbackGrantingCall(scp, &cbReq,
2496 CM_CALLBACK_MAINTAINCOUNT);
2497 cm_MergeStatus(dscp, scp, &bbp->stats[j], &volSync, userp, reqp, 0);
2498 lock_ReleaseWrite(&scp->rw);
2500 lock_ReleaseRead(&scp->rw);
2502 cm_ReleaseSCache(scp);
2504 } /* all files in the response */
2505 /* now tell it to drop the count,
2506 * after doing the vnode processing above */
2507 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2508 } /* while there are still more files to process */
2513 /* called with a write locked scp and a pointer to a buffer. Make bulk stat
2514 * calls on all undeleted files in the page of the directory specified.
2517 cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
2523 osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%p", dscp);
2525 /* should be on a buffer boundary */
2526 osi_assertx((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0, "invalid offset");
2528 bbp = malloc(sizeof(cm_bulkStat_t));
2529 memset(bbp, 0, sizeof(cm_bulkStat_t));
2530 bbp->bufOffset = *offsetp;
2532 lock_ReleaseWrite(&dscp->rw);
2533 /* first, assemble the file IDs we need to stat */
2534 code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) bbp, offsetp, userp, reqp, NULL);
2536 /* if we failed, bail out early */
2537 if (code && code != CM_ERROR_STOPNOW) {
2539 lock_ObtainWrite(&dscp->rw);
2543 code = cm_TryBulkStatRPC(dscp, bbp, userp, reqp);
2544 osi_Log1(afsd_logp, "END cm_TryBulkStat code 0x%x", code);
2546 lock_ObtainWrite(&dscp->rw);
2551 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
2555 /* initialize store back mask as inexpensive local variable */
2557 memset(statusp, 0, sizeof(AFSStoreStatus));
2559 /* copy out queued info from scache first, if scp passed in */
2561 if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
2562 statusp->ClientModTime = scp->clientModTime;
2563 mask |= AFS_SETMODTIME;
2564 scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
2569 /* now add in our locally generated request */
2570 if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
2571 statusp->ClientModTime = attrp->clientModTime;
2572 mask |= AFS_SETMODTIME;
2574 if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
2575 statusp->UnixModeBits = attrp->unixModeBits;
2576 mask |= AFS_SETMODE;
2578 if (attrp->mask & CM_ATTRMASK_OWNER) {
2579 statusp->Owner = attrp->owner;
2580 mask |= AFS_SETOWNER;
2582 if (attrp->mask & CM_ATTRMASK_GROUP) {
2583 statusp->Group = attrp->group;
2584 mask |= AFS_SETGROUP;
2587 statusp->Mask = mask;
2590 /* set the file size, and make sure that all relevant buffers have been
2591 * truncated. Ensure that any partially truncated buffers have been zeroed
2592 * to the end of the buffer.
2594 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
2600 /* start by locking out buffer creation */
2601 lock_ObtainWrite(&scp->bufCreateLock);
2603 /* verify that this is a file, not a dir or a symlink */
2604 lock_ObtainWrite(&scp->rw);
2605 code = cm_SyncOp(scp, NULL, userp, reqp, 0,
2606 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2609 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2611 if (scp->fileType != CM_SCACHETYPE_FILE) {
2612 code = CM_ERROR_ISDIR;
2617 if (LargeIntegerLessThan(*sizep, scp->length))
2622 lock_ReleaseWrite(&scp->rw);
2624 /* can't hold scp->rw lock here, since we may wait for a storeback to
2625 * finish if the buffer package is cleaning a buffer by storing it to
2629 buf_Truncate(scp, userp, reqp, sizep);
2631 /* now ensure that file length is short enough, and update truncPos */
2632 lock_ObtainWrite(&scp->rw);
2634 /* make sure we have a callback (so we have the right value for the
2635 * length), and wait for it to be safe to do a truncate.
2637 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
2638 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2639 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2641 /* If we only have 'i' bits, then we should still be able to set
2642 the size of a file we created. */
2643 if (code == CM_ERROR_NOACCESS && scp->creator == userp) {
2644 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
2645 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2646 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2652 if (LargeIntegerLessThan(*sizep, scp->length)) {
2653 /* a real truncation. If truncPos is not set yet, or is bigger
2654 * than where we're truncating the file, set truncPos to this
2659 if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
2660 || LargeIntegerLessThan(*sizep, scp->length)) {
2662 scp->truncPos = *sizep;
2663 scp->mask |= CM_SCACHEMASK_TRUNCPOS;
2665 /* in either case, the new file size has been changed */
2666 scp->length = *sizep;
2667 scp->mask |= CM_SCACHEMASK_LENGTH;
2669 else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
2670 /* really extending the file */
2671 scp->length = *sizep;
2672 scp->mask |= CM_SCACHEMASK_LENGTH;
2675 /* done successfully */
2678 cm_SyncOpDone(scp, NULL,
2679 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2680 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2683 lock_ReleaseWrite(&scp->rw);
2684 lock_ReleaseWrite(&scp->bufCreateLock);
2689 /* set the file size or other attributes (but not both at once) */
2690 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
2694 AFSFetchStatus afsOutStatus;
2698 AFSStoreStatus afsInStatus;
2699 struct rx_connection * rxconnp;
2701 memset(&volSync, 0, sizeof(volSync));
2703 /* handle file length setting */
2704 if (attrp->mask & CM_ATTRMASK_LENGTH)
2705 return cm_SetLength(scp, &attrp->length, userp, reqp);
2707 lock_ObtainWrite(&scp->rw);
2708 /* Check for RO volume */
2709 if (scp->flags & CM_SCACHEFLAG_RO) {
2710 code = CM_ERROR_READONLY;
2711 lock_ReleaseWrite(&scp->rw);
2715 /* otherwise, we have to make an RPC to get the status */
2716 code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
2718 lock_ReleaseWrite(&scp->rw);
2721 lock_ConvertWToR(&scp->rw);
2723 /* make the attr structure */
2724 cm_StatusFromAttr(&afsInStatus, scp, attrp);
2726 tfid.Volume = scp->fid.volume;
2727 tfid.Vnode = scp->fid.vnode;
2728 tfid.Unique = scp->fid.unique;
2729 lock_ReleaseRead(&scp->rw);
2731 /* now make the RPC */
2732 osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%p", scp);
2734 code = cm_ConnFromFID(&scp->fid, userp, reqp, &connp);
2738 rxconnp = cm_GetRxConn(connp);
2739 code = RXAFS_StoreStatus(rxconnp, &tfid,
2740 &afsInStatus, &afsOutStatus, &volSync);
2741 rx_PutConnection(rxconnp);
2743 } while (cm_Analyze(connp, userp, reqp,
2744 &scp->fid, &volSync, NULL, NULL, code));
2745 code = cm_MapRPCError(code, reqp);
2748 osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
2750 osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
2752 lock_ObtainWrite(&scp->rw);
2754 cm_MergeStatus(NULL, scp, &afsOutStatus, &volSync, userp, reqp,
2755 CM_MERGEFLAG_FORCE|CM_MERGEFLAG_STOREDATA);
2756 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
2758 /* if we're changing the mode bits, discard the ACL cache,
2759 * since we changed the mode bits.
2761 if (afsInStatus.Mask & AFS_SETMODE)
2762 cm_FreeAllACLEnts(scp);
2763 lock_ReleaseWrite(&scp->rw);
2767 long cm_Create(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
2768 cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
2773 cm_callbackRequest_t cbReq;
2776 cm_scache_t *scp = NULL;
2778 AFSStoreStatus inStatus;
2779 AFSFetchStatus updatedDirStatus;
2780 AFSFetchStatus newFileStatus;
2781 AFSCallBack newFileCallback;
2783 struct rx_connection * rxconnp;
2785 fschar_t * fnamep = NULL;
2787 memset(&volSync, 0, sizeof(volSync));
2789 /* can't create names with @sys in them; must expand it manually first.
2790 * return "invalid request" if they try.
2792 if (cm_ExpandSysName(cnamep, NULL, 0, 0)) {
2793 return CM_ERROR_ATSYS;
2796 #ifdef AFS_FREELANCE_CLIENT
2797 /* Freelance root volume does not hold files */
2798 if (cm_freelanceEnabled &&
2799 dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
2800 dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
2802 return CM_ERROR_NOACCESS;
2804 #endif /* AFS_FREELANCE_CLIENT */
2806 /* Check for RO volume */
2807 if (dscp->flags & CM_SCACHEFLAG_RO)
2808 return CM_ERROR_READONLY;
2810 /* before starting the RPC, mark that we're changing the file data, so
2811 * that someone who does a chmod will know to wait until our call
2814 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
2816 lock_ObtainWrite(&dscp->rw);
2817 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2818 lock_ReleaseWrite(&dscp->rw);
2820 cm_StartCallbackGrantingCall(NULL, &cbReq);
2822 cm_EndDirOp(&dirop);
2829 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
2831 cm_StatusFromAttr(&inStatus, NULL, attrp);
2833 /* try the RPC now */
2834 osi_Log1(afsd_logp, "CALL CreateFile scp 0x%p", dscp);
2836 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2840 dirAFSFid.Volume = dscp->fid.volume;
2841 dirAFSFid.Vnode = dscp->fid.vnode;
2842 dirAFSFid.Unique = dscp->fid.unique;
2844 rxconnp = cm_GetRxConn(connp);
2845 code = RXAFS_CreateFile(connp->rxconnp, &dirAFSFid, fnamep,
2846 &inStatus, &newAFSFid, &newFileStatus,
2847 &updatedDirStatus, &newFileCallback,
2849 rx_PutConnection(rxconnp);
2851 } while (cm_Analyze(connp, userp, reqp,
2852 &dscp->fid, &volSync, NULL, &cbReq, code));
2853 code = cm_MapRPCError(code, reqp);
2856 osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
2858 osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
2861 lock_ObtainWrite(&dirop.scp->dirlock);
2862 dirop.lockType = CM_DIRLOCK_WRITE;
2864 lock_ObtainWrite(&dscp->rw);
2866 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
2867 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2868 lock_ReleaseWrite(&dscp->rw);
2870 /* now try to create the file's entry, too, but be careful to
2871 * make sure that we don't merge in old info. Since we weren't locking
2872 * out any requests during the file's creation, we may have pretty old
2876 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
2877 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2879 lock_ObtainWrite(&scp->rw);
2880 scp->creator = userp; /* remember who created it */
2881 if (!cm_HaveCallback(scp)) {
2882 cm_EndCallbackGrantingCall(scp, &cbReq,
2883 &newFileCallback, &volSync, 0);
2884 cm_MergeStatus(dscp, scp, &newFileStatus, &volSync,
2888 lock_ReleaseWrite(&scp->rw);
2892 /* make sure we end things properly */
2894 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2896 if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
2897 cm_DirCreateEntry(&dirop, fnamep, &newFid);
2899 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
2902 cm_EndDirOp(&dirop);
2911 cm_ReleaseSCache(scp);
2917 * locked if TRUE means write-locked
2918 * else the cm_scache_t rw must not be held
2920 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp, afs_uint32 locked)
2925 lock_ReleaseWrite(&scp->rw);
2927 osi_Log2(afsd_logp, "cm_FSync scp 0x%p userp 0x%p", scp, userp);
2929 code = buf_CleanVnode(scp, userp, reqp);
2931 lock_ObtainWrite(&scp->rw);
2933 if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
2934 | CM_SCACHEMASK_CLIENTMODTIME
2935 | CM_SCACHEMASK_LENGTH))
2936 code = cm_StoreMini(scp, userp, reqp);
2938 if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) {
2939 code = (scp->flags & CM_SCACHEFLAG_OVERQUOTA) ? CM_ERROR_QUOTA : CM_ERROR_SPACE;
2940 scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE);
2944 lock_ReleaseWrite(&scp->rw);
2945 } else if (locked) {
2946 lock_ObtainWrite(&scp->rw);
2951 long cm_MakeDir(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
2952 cm_user_t *userp, cm_req_t *reqp, cm_scache_t **scpp)
2957 cm_callbackRequest_t cbReq;
2960 cm_scache_t *scp = NULL;
2962 AFSStoreStatus inStatus;
2963 AFSFetchStatus updatedDirStatus;
2964 AFSFetchStatus newDirStatus;
2965 AFSCallBack newDirCallback;
2967 struct rx_connection * rxconnp;
2969 fschar_t * fnamep = NULL;
2971 memset(&volSync, 0, sizeof(volSync));
2973 /* can't create names with @sys in them; must expand it manually first.
2974 * return "invalid request" if they try.
2976 if (cm_ExpandSysName(cnamep, NULL, 0, 0)) {
2977 return CM_ERROR_ATSYS;
2980 #ifdef AFS_FREELANCE_CLIENT
2981 /* Freelance root volume does not hold subdirectories */
2982 if (cm_freelanceEnabled &&
2983 dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
2984 dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
2986 return CM_ERROR_NOACCESS;
2988 #endif /* AFS_FREELANCE_CLIENT */
2990 /* Check for RO volume */
2991 if (dscp->flags & CM_SCACHEFLAG_RO)
2992 return CM_ERROR_READONLY;
2994 /* before starting the RPC, mark that we're changing the directory
2995 * data, so that someone who does a chmod on the dir will wait until
2996 * our call completes.
2998 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3000 lock_ObtainWrite(&dscp->rw);
3001 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3002 lock_ReleaseWrite(&dscp->rw);
3004 cm_StartCallbackGrantingCall(NULL, &cbReq);
3006 cm_EndDirOp(&dirop);
3013 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3014 cm_StatusFromAttr(&inStatus, NULL, attrp);
3016 /* try the RPC now */
3017 osi_Log1(afsd_logp, "CALL MakeDir scp 0x%p", dscp);
3019 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3023 dirAFSFid.Volume = dscp->fid.volume;
3024 dirAFSFid.Vnode = dscp->fid.vnode;
3025 dirAFSFid.Unique = dscp->fid.unique;
3027 rxconnp = cm_GetRxConn(connp);
3028 code = RXAFS_MakeDir(connp->rxconnp, &dirAFSFid, fnamep,
3029 &inStatus, &newAFSFid, &newDirStatus,
3030 &updatedDirStatus, &newDirCallback,
3032 rx_PutConnection(rxconnp);
3034 } while (cm_Analyze(connp, userp, reqp,
3035 &dscp->fid, &volSync, NULL, &cbReq, code));
3036 code = cm_MapRPCError(code, reqp);
3039 osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
3041 osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
3044 lock_ObtainWrite(&dirop.scp->dirlock);
3045 dirop.lockType = CM_DIRLOCK_WRITE;
3047 lock_ObtainWrite(&dscp->rw);
3049 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3050 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3051 lock_ReleaseWrite(&dscp->rw);
3053 /* now try to create the new dir's entry, too, but be careful to
3054 * make sure that we don't merge in old info. Since we weren't locking
3055 * out any requests during the file's creation, we may have pretty old
3059 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3060 code = cm_GetSCache(&newFid, &scp, userp, reqp);
3062 lock_ObtainWrite(&scp->rw);
3063 if (!cm_HaveCallback(scp)) {
3064 cm_EndCallbackGrantingCall(scp, &cbReq,
3065 &newDirCallback, &volSync, 0);
3066 cm_MergeStatus(dscp, scp, &newDirStatus, &volSync,
3070 lock_ReleaseWrite(&scp->rw);
3074 /* make sure we end things properly */
3076 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
3078 if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
3079 cm_DirCreateEntry(&dirop, fnamep, &newFid);
3081 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
3084 cm_EndDirOp(&dirop);
3092 cm_ReleaseSCache(scp);
3095 /* and return error code */
3099 long cm_Link(cm_scache_t *dscp, clientchar_t *cnamep, cm_scache_t *sscp, long flags,
3100 cm_user_t *userp, cm_req_t *reqp)
3105 AFSFid existingAFSFid;
3106 AFSFetchStatus updatedDirStatus;
3107 AFSFetchStatus newLinkStatus;
3109 struct rx_connection * rxconnp;
3111 fschar_t * fnamep = NULL;
3113 memset(&volSync, 0, sizeof(volSync));
3115 if (dscp->fid.cell != sscp->fid.cell ||
3116 dscp->fid.volume != sscp->fid.volume) {
3117 return CM_ERROR_CROSSDEVLINK;
3120 /* Check for RO volume */
3121 if (dscp->flags & CM_SCACHEFLAG_RO)
3122 return CM_ERROR_READONLY;
3124 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3126 lock_ObtainWrite(&dscp->rw);
3127 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3128 lock_ReleaseWrite(&dscp->rw);
3130 cm_EndDirOp(&dirop);
3135 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3137 /* try the RPC now */
3138 osi_Log1(afsd_logp, "CALL Link scp 0x%p", dscp);
3140 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3143 dirAFSFid.Volume = dscp->fid.volume;
3144 dirAFSFid.Vnode = dscp->fid.vnode;
3145 dirAFSFid.Unique = dscp->fid.unique;
3147 existingAFSFid.Volume = sscp->fid.volume;
3148 existingAFSFid.Vnode = sscp->fid.vnode;
3149 existingAFSFid.Unique = sscp->fid.unique;
3151 rxconnp = cm_GetRxConn(connp);
3152 code = RXAFS_Link(rxconnp, &dirAFSFid, fnamep, &existingAFSFid,
3153 &newLinkStatus, &updatedDirStatus, &volSync);
3154 rx_PutConnection(rxconnp);
3155 osi_Log1(afsd_logp," RXAFS_Link returns 0x%x", code);
3157 } while (cm_Analyze(connp, userp, reqp,
3158 &dscp->fid, &volSync, NULL, NULL, code));
3160 code = cm_MapRPCError(code, reqp);
3163 osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
3165 osi_Log0(afsd_logp, "CALL Link SUCCESS");
3168 lock_ObtainWrite(&dirop.scp->dirlock);
3169 dirop.lockType = CM_DIRLOCK_WRITE;
3171 lock_ObtainWrite(&dscp->rw);
3173 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3175 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3176 lock_ReleaseWrite(&dscp->rw);
3179 if (cm_CheckDirOpForSingleChange(&dirop)) {
3180 cm_DirCreateEntry(&dirop, fnamep, &sscp->fid);
3182 cm_BPlusDirCreateEntry(&dirop, cnamep, &sscp->fid);
3186 cm_EndDirOp(&dirop);
3188 /* Update the linked object status */
3190 lock_ObtainWrite(&sscp->rw);
3191 cm_MergeStatus(NULL, sscp, &newLinkStatus, &volSync, userp, reqp, 0);
3192 lock_ReleaseWrite(&sscp->rw);
3200 long cm_SymLink(cm_scache_t *dscp, clientchar_t *cnamep, fschar_t *contentsp, long flags,
3201 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp, cm_scache_t **scpp)
3209 AFSStoreStatus inStatus;
3210 AFSFetchStatus updatedDirStatus;
3211 AFSFetchStatus newLinkStatus;
3213 struct rx_connection * rxconnp;
3215 fschar_t *fnamep = NULL;
3220 /* Check for RO volume */
3221 if (dscp->flags & CM_SCACHEFLAG_RO)
3222 return CM_ERROR_READONLY;
3224 memset(&volSync, 0, sizeof(volSync));
3226 /* before starting the RPC, mark that we're changing the directory data,
3227 * so that someone who does a chmod on the dir will wait until our
3230 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3232 lock_ObtainWrite(&dscp->rw);
3233 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3234 lock_ReleaseWrite(&dscp->rw);
3236 cm_EndDirOp(&dirop);
3241 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3243 cm_StatusFromAttr(&inStatus, NULL, attrp);
3245 /* try the RPC now */
3246 osi_Log1(afsd_logp, "CALL Symlink scp 0x%p", dscp);
3248 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3252 dirAFSFid.Volume = dscp->fid.volume;
3253 dirAFSFid.Vnode = dscp->fid.vnode;
3254 dirAFSFid.Unique = dscp->fid.unique;
3256 rxconnp = cm_GetRxConn(connp);
3257 code = RXAFS_Symlink(rxconnp, &dirAFSFid, fnamep, contentsp,
3258 &inStatus, &newAFSFid, &newLinkStatus,
3259 &updatedDirStatus, &volSync);
3260 rx_PutConnection(rxconnp);
3262 } while (cm_Analyze(connp, userp, reqp,
3263 &dscp->fid, &volSync, NULL, NULL, code));
3264 code = cm_MapRPCError(code, reqp);
3267 osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
3269 osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
3272 lock_ObtainWrite(&dirop.scp->dirlock);
3273 dirop.lockType = CM_DIRLOCK_WRITE;
3275 lock_ObtainWrite(&dscp->rw);
3277 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3278 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3279 lock_ReleaseWrite(&dscp->rw);
3282 if (cm_CheckDirOpForSingleChange(&dirop)) {
3283 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3285 cm_DirCreateEntry(&dirop, fnamep, &newFid);
3287 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
3291 cm_EndDirOp(&dirop);
3293 /* now try to create the new dir's entry, too, but be careful to
3294 * make sure that we don't merge in old info. Since we weren't locking
3295 * out any requests during the file's creation, we may have pretty old
3299 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3300 code = cm_GetSCache(&newFid, &scp, userp, reqp);
3302 lock_ObtainWrite(&scp->rw);
3303 if (!cm_HaveCallback(scp)) {
3304 cm_MergeStatus(dscp, scp, &newLinkStatus, &volSync,
3307 lock_ReleaseWrite(&scp->rw);
3312 cm_ReleaseSCache(scp);
3319 /* and return error code */
3323 /*! \brief Remove a directory
3325 Encapsulates a call to RXAFS_RemoveDir().
3327 \param[in] dscp cm_scache_t for the directory containing the
3328 directory to be removed.
3330 \param[in] fnamep This will be the original name of the directory
3331 as known to the file server. It will be passed in to RXAFS_RemoveDir().
3332 This parameter is optional. If it is not provided the value
3335 \param[in] cnamep Normalized name used to update the local
3338 \param[in] userp cm_user_t for the request.
3340 \param[in] reqp Request tracker.
3342 long cm_RemoveDir(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t *cnamep, cm_user_t *userp, cm_req_t *reqp)
3348 AFSFetchStatus updatedDirStatus;
3350 struct rx_connection * rxconnp;
3352 cm_scache_t *scp = NULL;
3353 int free_fnamep = FALSE;
3355 memset(&volSync, 0, sizeof(volSync));
3357 if (fnamep == NULL) {
3360 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ,
3361 CM_DIROP_FLAG_NONE, &dirop);
3363 code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
3366 cm_EndDirOp(&dirop);
3373 code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
3377 /* Check for RO volume */
3378 if (dscp->flags & CM_SCACHEFLAG_RO) {
3379 code = CM_ERROR_READONLY;
3383 /* before starting the RPC, mark that we're changing the directory data,
3384 * so that someone who does a chmod on the dir will wait until our
3387 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3389 lock_ObtainWrite(&dscp->rw);
3390 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3391 lock_ReleaseWrite(&dscp->rw);
3393 cm_EndDirOp(&dirop);
3398 /* try the RPC now */
3399 osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%p", dscp);
3401 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3405 dirAFSFid.Volume = dscp->fid.volume;
3406 dirAFSFid.Vnode = dscp->fid.vnode;
3407 dirAFSFid.Unique = dscp->fid.unique;
3409 rxconnp = cm_GetRxConn(connp);
3410 code = RXAFS_RemoveDir(rxconnp, &dirAFSFid, fnamep,
3411 &updatedDirStatus, &volSync);
3412 rx_PutConnection(rxconnp);
3414 } while (cm_Analyze(connp, userp, reqp,
3415 &dscp->fid, &volSync, NULL, NULL, code));
3416 code = cm_MapRPCErrorRmdir(code, reqp);
3419 osi_Log1(afsd_logp, "CALL RemoveDir FAILURE, code 0x%x", code);
3421 osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
3424 lock_ObtainWrite(&dirop.scp->dirlock);
3425 dirop.lockType = CM_DIRLOCK_WRITE;
3427 lock_ObtainWrite(&dscp->rw);
3429 cm_dnlcRemove(dscp, cnamep);
3430 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3432 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3433 lock_ReleaseWrite(&dscp->rw);
3436 if (cm_CheckDirOpForSingleChange(&dirop) && cnamep != NULL) {
3437 cm_DirDeleteEntry(&dirop, fnamep);
3439 cm_BPlusDirDeleteEntry(&dirop, cnamep);
3443 cm_EndDirOp(&dirop);
3446 cm_ReleaseSCache(scp);
3448 lock_ObtainWrite(&scp->rw);
3449 scp->flags |= CM_SCACHEFLAG_DELETED;
3450 lock_ObtainWrite(&cm_scacheLock);
3451 cm_AdjustScacheLRU(scp);
3452 cm_RemoveSCacheFromHashTable(scp);
3453 lock_ReleaseWrite(&cm_scacheLock);
3454 lock_ReleaseWrite(&scp->rw);
3462 /* and return error code */
3466 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
3468 /* grab mutex on contents */
3469 lock_ObtainWrite(&scp->rw);
3471 /* reset the prefetch info */
3472 scp->prefetch.base.LowPart = 0; /* base */
3473 scp->prefetch.base.HighPart = 0;
3474 scp->prefetch.end.LowPart = 0; /* and end */
3475 scp->prefetch.end.HighPart = 0;
3477 /* release mutex on contents */
3478 lock_ReleaseWrite(&scp->rw);
3484 /*! \brief Rename a file or directory
3486 Encapsulates a RXAFS_Rename() call.
3488 \param[in] oldDscp cm_scache_t for the directory containing the old
3491 \param[in] oldNamep The original old name known to the file server.
3492 This is the name that will be passed into the RXAFS_Rename().
3493 If it is not provided, it will be looked up.
3495 \param[in] normalizedOldNamep Normalized old name. This is used for
3496 updating local directory caches.
3498 \param[in] newDscp cm_scache_t for the directory containing the new
3501 \param[in] newNamep New name. Normalized.
3503 \param[in] userp cm_user_t for the request.
3505 \param[in,out] reqp Request tracker.
3508 long cm_Rename(cm_scache_t *oldDscp, fschar_t *oldNamep, clientchar_t *cOldNamep,
3509 cm_scache_t *newDscp, clientchar_t *cNewNamep, cm_user_t *userp,
3514 AFSFid oldDirAFSFid;
3515 AFSFid newDirAFSFid;
3517 AFSFetchStatus updatedOldDirStatus;
3518 AFSFetchStatus updatedNewDirStatus;
3521 struct rx_connection * rxconnp;
3522 cm_dirOp_t oldDirOp;
3525 cm_dirOp_t newDirOp;
3526 fschar_t * newNamep = NULL;
3527 int free_oldNamep = FALSE;
3528 cm_scache_t *oldScp = NULL, *newScp = NULL;
3530 memset(&volSync, 0, sizeof(volSync));
3532 if (cOldNamep == NULL || cNewNamep == NULL ||
3533 cm_ClientStrLen(cOldNamep) == 0 ||
3534 cm_ClientStrLen(cNewNamep) == 0)
3535 return CM_ERROR_INVAL;
3538 * Before we permit the operation, make sure that we do not already have
3539 * an object in the destination directory that has a case-insensitive match
3540 * for this name UNLESS the matching object is the object we are renaming.
3542 code = cm_Lookup(oldDscp, cOldNamep, 0, userp, reqp, &oldScp);
3544 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S old name lookup failed",
3545 oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3549 code = cm_Lookup(newDscp, cNewNamep, CM_FLAG_CASEFOLD, userp, reqp, &newScp);
3551 /* found a matching object with the new name */
3552 if (cm_FidCmp(&oldScp->fid, &newScp->fid)) {
3553 /* and they don't match so return an error */
3554 osi_Log2(afsd_logp, "cm_Rename newDscp 0x%p cNewName %S new name already exists",
3555 newDscp, osi_LogSaveStringW(afsd_logp, cNewNamep));
3556 code = CM_ERROR_EXISTS;
3558 cm_ReleaseSCache(newScp);
3560 } else if (code == CM_ERROR_AMBIGUOUS_FILENAME) {
3561 code = CM_ERROR_EXISTS;
3566 /* Check for RO volume */
3568 (oldDscp->flags & CM_SCACHEFLAG_RO) || (newDscp->flags & CM_SCACHEFLAG_RO)) {
3569 code = CM_ERROR_READONLY;
3575 if (oldNamep == NULL) {
3578 code = cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_READ,
3579 CM_DIROP_FLAG_NONE, &oldDirOp);
3581 code = cm_BPlusDirLookupOriginalName(&oldDirOp, cOldNamep, &oldNamep);
3583 free_oldNamep = TRUE;
3584 cm_EndDirOp(&oldDirOp);
3588 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S Original Name lookup failed",
3589 oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3595 /* before starting the RPC, mark that we're changing the directory data,
3596 * so that someone who does a chmod on the dir will wait until our call
3597 * completes. We do this in vnode order so that we don't deadlock,
3598 * which makes the code a little verbose.
3600 if (oldDscp == newDscp) {
3601 /* check for identical names */
3602 if (cm_ClientStrCmp(cOldNamep, cNewNamep) == 0) {
3603 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_RENAME_IDENTICAL",
3605 code = CM_ERROR_RENAME_IDENTICAL;
3610 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE,
3611 CM_DIROP_FLAG_NONE, &oldDirOp);
3612 lock_ObtainWrite(&oldDscp->rw);
3613 cm_dnlcRemove(oldDscp, cOldNamep);
3614 cm_dnlcRemove(oldDscp, cNewNamep);
3615 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3616 CM_SCACHESYNC_STOREDATA);
3617 lock_ReleaseWrite(&oldDscp->rw);
3619 cm_EndDirOp(&oldDirOp);
3623 /* two distinct dir vnodes */
3625 if (oldDscp->fid.cell != newDscp->fid.cell ||
3626 oldDscp->fid.volume != newDscp->fid.volume) {
3627 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_CROSSDEVLINK",
3629 code = CM_ERROR_CROSSDEVLINK;
3633 /* shouldn't happen that we have distinct vnodes for two
3634 * different files, but could due to deliberate attack, or
3635 * stale info. Avoid deadlocks and quit now.
3637 if (oldDscp->fid.vnode == newDscp->fid.vnode) {
3638 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p vnode collision",
3640 code = CM_ERROR_CROSSDEVLINK;
3644 if (oldDscp->fid.vnode < newDscp->fid.vnode) {
3645 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE,
3646 CM_DIROP_FLAG_NONE, &oldDirOp);
3647 lock_ObtainWrite(&oldDscp->rw);
3648 cm_dnlcRemove(oldDscp, cOldNamep);
3649 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3650 CM_SCACHESYNC_STOREDATA);
3651 lock_ReleaseWrite(&oldDscp->rw);
3653 cm_EndDirOp(&oldDirOp);
3655 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE,
3656 CM_DIROP_FLAG_NONE, &newDirOp);
3657 lock_ObtainWrite(&newDscp->rw);
3658 cm_dnlcRemove(newDscp, cNewNamep);
3659 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3660 CM_SCACHESYNC_STOREDATA);
3661 lock_ReleaseWrite(&newDscp->rw);
3663 cm_EndDirOp(&newDirOp);
3665 /* cleanup first one */
3666 lock_ObtainWrite(&oldDscp->rw);
3667 cm_SyncOpDone(oldDscp, NULL,
3668 CM_SCACHESYNC_STOREDATA);
3669 lock_ReleaseWrite(&oldDscp->rw);
3670 cm_EndDirOp(&oldDirOp);
3675 /* lock the new vnode entry first */
3676 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE,
3677 CM_DIROP_FLAG_NONE, &newDirOp);
3678 lock_ObtainWrite(&newDscp->rw);
3679 cm_dnlcRemove(newDscp, cNewNamep);
3680 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3681 CM_SCACHESYNC_STOREDATA);
3682 lock_ReleaseWrite(&newDscp->rw);
3684 cm_EndDirOp(&newDirOp);
3686 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE,
3687 CM_DIROP_FLAG_NONE, &oldDirOp);
3688 lock_ObtainWrite(&oldDscp->rw);
3689 cm_dnlcRemove(oldDscp, cOldNamep);
3690 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3691 CM_SCACHESYNC_STOREDATA);
3692 lock_ReleaseWrite(&oldDscp->rw);
3694 cm_EndDirOp(&oldDirOp);
3696 /* cleanup first one */
3697 lock_ObtainWrite(&newDscp->rw);
3698 cm_SyncOpDone(newDscp, NULL,
3699 CM_SCACHESYNC_STOREDATA);
3700 lock_ReleaseWrite(&newDscp->rw);
3701 cm_EndDirOp(&newDirOp);
3705 } /* two distinct vnodes */
3712 newNamep = cm_ClientStringToFsStringAlloc(cNewNamep, -1, NULL);
3714 /* try the RPC now */
3715 osi_Log2(afsd_logp, "CALL Rename old scp 0x%p new scp 0x%p",
3718 code = cm_ConnFromFID(&oldDscp->fid, userp, reqp, &connp);
3722 oldDirAFSFid.Volume = oldDscp->fid.volume;
3723 oldDirAFSFid.Vnode = oldDscp->fid.vnode;
3724 oldDirAFSFid.Unique = oldDscp->fid.unique;
3725 newDirAFSFid.Volume = newDscp->fid.volume;
3726 newDirAFSFid.Vnode = newDscp->fid.vnode;
3727 newDirAFSFid.Unique = newDscp->fid.unique;
3729 rxconnp = cm_GetRxConn(connp);
3730 code = RXAFS_Rename(rxconnp, &oldDirAFSFid, oldNamep,
3731 &newDirAFSFid, newNamep,
3732 &updatedOldDirStatus, &updatedNewDirStatus,
3734 rx_PutConnection(rxconnp);
3736 } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
3737 &volSync, NULL, NULL, code));
3738 code = cm_MapRPCError(code, reqp);
3741 osi_Log1(afsd_logp, "CALL Rename FAILURE, code 0x%x", code);
3743 osi_Log0(afsd_logp, "CALL Rename SUCCESS");
3745 /* update the individual stat cache entries for the directories */
3747 lock_ObtainWrite(&oldDirOp.scp->dirlock);
3748 oldDirOp.lockType = CM_DIRLOCK_WRITE;
3750 lock_ObtainWrite(&oldDscp->rw);
3753 cm_MergeStatus(NULL, oldDscp, &updatedOldDirStatus, &volSync,
3754 userp, reqp, CM_MERGEFLAG_DIROP);
3755 cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
3756 lock_ReleaseWrite(&oldDscp->rw);
3758 if (code == 0 && cm_CheckDirOpForSingleChange(&oldDirOp)) {
3760 diropCode = cm_BPlusDirLookup(&oldDirOp, cOldNamep, &fileFid);
3761 if (diropCode == CM_ERROR_INEXACT_MATCH)
3763 else if (diropCode == EINVAL)
3765 diropCode = cm_DirLookup(&oldDirOp, oldNamep, &fileFid);
3767 if (diropCode == 0) {
3769 diropCode = cm_DirCreateEntry(&oldDirOp, newNamep, &fileFid);
3771 cm_BPlusDirCreateEntry(&oldDirOp, cNewNamep, &fileFid);
3775 if (diropCode == 0) {
3776 diropCode = cm_DirDeleteEntry(&oldDirOp, oldNamep);
3778 cm_BPlusDirDeleteEntry(&oldDirOp, cOldNamep);
3783 cm_EndDirOp(&oldDirOp);
3785 /* and update it for the new one, too, if necessary */
3788 lock_ObtainWrite(&newDirOp.scp->dirlock);
3789 newDirOp.lockType = CM_DIRLOCK_WRITE;
3791 lock_ObtainWrite(&newDscp->rw);
3793 cm_MergeStatus(NULL, newDscp, &updatedNewDirStatus, &volSync,
3794 userp, reqp, CM_MERGEFLAG_DIROP);
3795 cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
3796 lock_ReleaseWrite(&newDscp->rw);
3800 * The following optimization does not work.
3801 * When the file server processed a RXAFS_Rename() request the
3802 * FID of the object being moved between directories is not
3803 * preserved. The client does not know the new FID nor the
3804 * version number of the target. Not only can we not create
3805 * the directory entry in the new directory, but we can't
3806 * preserve the cached data for the file. It must be re-read
3807 * from the file server. - jaltman, 2009/02/20
3810 /* we only make the local change if we successfully made
3811 the change in the old directory AND there was only one
3812 change in the new directory */
3813 if (diropCode == 0 && cm_CheckDirOpForSingleChange(&newDirOp)) {
3814 cm_DirCreateEntry(&newDirOp, newNamep, &fileFid);
3816 cm_BPlusDirCreateEntry(&newDirOp, cNewNamep, &fileFid);
3821 cm_EndDirOp(&newDirOp);
3825 * After the rename the file server has invalidated the callbacks
3826 * on the file that was moved nor do we have a directory reference
3829 lock_ObtainWrite(&oldScp->rw);
3830 cm_DiscardSCache(oldScp);
3831 lock_ReleaseWrite(&oldScp->rw);
3835 cm_ReleaseSCache(oldScp);
3842 /* and return error code */
3846 /* Byte range locks:
3848 The OpenAFS Windows client has to fake byte range locks given no
3849 server side support for such locks. This is implemented as keyed
3850 byte range locks on the cache manager.
3852 Keyed byte range locks:
3854 Each cm_scache_t structure keeps track of a list of keyed locks.
3855 The key for a lock identifies an owner of a set of locks (referred
3856 to as a client). Each key is represented by a value. The set of
3857 key values used within a specific cm_scache_t structure form a
3858 namespace that has a scope of just that cm_scache_t structure. The
3859 same key value can be used with another cm_scache_t structure and
3860 correspond to a completely different client. However it is
3861 advantageous for the SMB or IFS layer to make sure that there is a
3862 1-1 mapping between client and keys over all cm_scache_t objects.
3864 Assume a client C has key Key(C) (although, since the scope of the
3865 key is a cm_scache_t, the key can be Key(C,S), where S is the
3866 cm_scache_t. But assume a 1-1 relation between keys and clients).
3867 A byte range (O,+L) denotes byte addresses (O) through (O+L-1)
3868 inclusive (a.k.a. [O,O+L-1]). The function Key(x) is implemented
3869 through cm_generateKey() function for both SMB and IFS.
3871 The list of locks for a cm_scache_t object S is maintained in
3872 S->fileLocks. The cache manager will set a lock on the AFS file
3873 server in order to assert the locks in S->fileLocks. If only
3874 shared locks are in place for S, then the cache manager will obtain
3875 a LockRead lock, while if there are any exclusive locks, it will
3876 obtain a LockWrite lock. If the exclusive locks are all released
3877 while the shared locks remain, then the cache manager will
3878 downgrade the lock from LockWrite to LockRead. Similarly, if an
3879 exclusive lock is obtained when only shared locks exist, then the
3880 cache manager will try to upgrade the lock from LockRead to
3883 Each lock L owned by client C maintains a key L->key such that
3884 L->key == Key(C), the effective range defined by L->LOffset and
3885 L->LLength such that the range of bytes affected by the lock is
3886 (L->LOffset, +L->LLength), a type maintained in L->LockType which
3887 is either exclusive or shared.
3891 A lock exists iff it is in S->fileLocks for some cm_scache_t
3892 S. Existing locks are in one of the following states: ACTIVE,
3893 WAITLOCK, WAITUNLOCK, LOST, DELETED.
3895 The following sections describe each lock and the associated
3898 1. ACTIVE: A lock L is ACTIVE iff the cache manager has asserted
3899 the lock with the AFS file server. This type of lock can be
3900 exercised by a client to read or write to the locked region (as
3903 1.1 ACTIVE->LOST: When the AFS file server fails to extend a
3904 server lock that was required to assert the lock. Before
3905 marking the lock as lost, the cache manager checks if the file
3906 has changed on the server. If the file has not changed, then
3907 the cache manager will attempt to obtain a new server lock
3908 that is sufficient to assert the client side locks for the
3909 file. If any of these fail, the lock is marked as LOST.
3910 Otherwise, it is left as ACTIVE.
3912 1.2 ACTIVE->DELETED: Lock is released.
3914 2. WAITLOCK: A lock is in a WAITLOCK state if the cache manager
3915 grants the lock but the lock is yet to be asserted with the AFS
3916 file server. Once the file server grants the lock, the state
3917 will transition to an ACTIVE lock.
3919 2.1 WAITLOCK->ACTIVE: The server granted the lock.
3921 2.2 WAITLOCK->DELETED: Lock is abandoned, or timed out during
3924 2.3 WAITLOCK->LOST: One or more locks from this client were
3925 marked as LOST. No further locks will be granted to this
3926 client until all lost locks are removed.
3928 3. WAITUNLOCK: A lock is in a WAITUNLOCK state if the cache manager
3929 receives a request for a lock that conflicts with an existing
3930 ACTIVE or WAITLOCK lock. The lock will be placed in the queue
3931 and will be granted at such time the conflicting locks are
3932 removed, at which point the state will transition to either
3935 3.1 WAITUNLOCK->ACTIVE: The conflicting lock was removed. The
3936 current serverLock is sufficient to assert this lock, or a
3937 sufficient serverLock is obtained.
3939 3.2 WAITUNLOCK->WAITLOCK: The conflicting lock was removed,
3940 however the required serverLock is yet to be asserted with the
3943 3.3 WAITUNLOCK->DELETED: The lock is abandoned, timed out or
3946 3.5 WAITUNLOCK->LOST: One or more locks from this client were
3947 marked as LOST. No further locks will be granted to this
3948 client until all lost locks are removed.
3950 4. LOST: A lock L is LOST if the server lock that was required to
3951 assert the lock could not be obtained or if it could not be
3952 extended, or if other locks by the same client were LOST.
3953 Essentially, once a lock is LOST, the contract between the cache
3954 manager and that specific client is no longer valid.
3956 The cache manager rechecks the server lock once every minute and
3957 extends it as appropriate. If this is not done for 5 minutes,
3958 the AFS file server will release the lock (the 5 minute timeout
3959 is based on current file server code and is fairly arbitrary).
3960 Once released, the lock cannot be re-obtained without verifying
3961 that the contents of the file hasn't been modified since the
3962 time the lock was released. Re-obtaining the lock without
3963 verifying this may lead to data corruption. If the lock can not
3964 be obtained safely, then all active locks for the cm_scache_t
3967 4.1 LOST->DELETED: The lock is released.
3969 5. DELETED: The lock is no longer relevant. Eventually, it will
3970 get removed from the cm_scache_t. In the meantime, it will be
3971 treated as if it does not exist.
3973 5.1 DELETED->not exist: The lock is removed from the
3976 The following are classifications of locks based on their state.
3978 6* A lock L is ACCEPTED if it is ACTIVE or WAITLOCK. These locks
3979 have been accepted by the cache manager, but may or may not have
3980 been granted back to the client.
3982 7* A lock L is QUEUED if it is ACTIVE, WAITLOCK or WAITUNLOCK.
3984 8* A lock L is WAITING if it is WAITLOCK or WAITUNLOCK.
3988 A client C can READ range (Offset,+Length) of a file represented by
3989 cm_scache_t S iff (1):
3991 1. for all _a_ in (Offset,+Length), all of the following is true:
3993 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3994 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3997 1.2 For each LOST lock L in S->fileLocks such that _a_ in
3998 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
4001 (When locks are lost on an cm_scache_t, all locks are lost. By
4002 4.2 (below), if there is an exclusive LOST lock, then there
4003 can't be any overlapping ACTIVE locks.)
4005 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
4007 2. for all _a_ in (Offset,+Length), one of the following is true:
4009 2.1 Byte _a_ of S is unowned (as specified in 1.1) AND there
4010 does not exist a LOST lock L such that _a_ in
4011 (L->LOffset,+L->LLength).
4013 2.2 Byte _a_ of S is owned by C under lock L (as specified in
4014 1.2) AND L->LockType is exclusive.
4016 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
4018 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
4021 3.1 If L->LockType is exclusive then there does NOT exist a
4022 ACCEPTED lock M in S->fileLocks such that _a_ in
4023 (M->LOffset,+M->LLength).
4025 (If we count all QUEUED locks then we hit cases such as
4026 cascading waiting locks where the locks later on in the queue
4027 can be granted without compromising file integrity. On the
4028 other hand if only ACCEPTED locks are considered, then locks
4029 that were received earlier may end up waiting for locks that
4030 were received later to be unlocked. The choice of ACCEPTED
4031 locks was made to mimic the Windows byte range lock
4034 3.2 If L->LockType is shared then for each ACCEPTED lock M in
4035 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
4036 M->LockType is shared.
4038 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
4040 4.1 M->key != Key(C)
4042 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
4043 and (M->LOffset,+M->LLength) do not intersect.
4045 (Note: If a client loses a lock, it loses all locks.
4046 Subsequently, it will not be allowed to obtain any more locks
4047 until all existing LOST locks that belong to the client are
4048 released. Once all locks are released by a single client,
4049 there exists no further contract between the client and AFS
4050 about the contents of the file, hence the client can then
4051 proceed to obtain new locks and establish a new contract.
4053 This doesn't quite work as you think it should, because most
4054 applications aren't built to deal with losing locks they
4055 thought they once had. For now, we don't have a good
4056 solution to lost locks.
4058 Also, for consistency reasons, we have to hold off on
4059 granting locks that overlap exclusive LOST locks.)
4061 A client C can only unlock locks L in S->fileLocks which have
4064 The representation and invariants are as follows:
4066 - Each cm_scache_t structure keeps:
4068 - A queue of byte-range locks (cm_scache_t::fileLocks) which
4069 are of type cm_file_lock_t.
4071 - A record of the highest server-side lock that has been
4072 obtained for this object (cm_scache_t::serverLock), which is
4073 one of (-1), LockRead, LockWrite.
4075 - A count of ACCEPTED exclusive and shared locks that are in the
4076 queue (cm_scache_t::sharedLocks and
4077 cm_scache_t::exclusiveLocks)
4079 - Each cm_file_lock_t structure keeps:
4081 - The type of lock (cm_file_lock_t::LockType)
4083 - The key associated with the lock (cm_file_lock_t::key)
4085 - The offset and length of the lock (cm_file_lock_t::LOffset
4086 and cm_file_lock_t::LLength)
4088 - The state of the lock.
4090 - Time of issuance or last successful extension
4092 Semantic invariants:
4094 I1. The number of ACCEPTED locks in S->fileLocks are
4095 (S->sharedLocks + S->exclusiveLocks)
4097 External invariants:
4099 I3. S->serverLock is the lock that we have asserted with the
4100 AFS file server for this cm_scache_t.
4102 I4. S->serverLock == LockRead iff there is at least one ACTIVE
4103 shared lock, but no ACTIVE exclusive locks.
4105 I5. S->serverLock == LockWrite iff there is at least one ACTIVE
4108 I6. If L is a LOST lock, then for each lock M in S->fileLocks,
4109 M->key == L->key IMPLIES M is LOST or DELETED.
4114 #define IS_LOCK_ACTIVE(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == 0)
4116 #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)
4118 #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)
4120 #define IS_LOCK_LOST(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_LOST)
4122 #define IS_LOCK_DELETED(lockp) (((lockp)->flags & CM_FILELOCK_FLAG_DELETED) == CM_FILELOCK_FLAG_DELETED)
4125 #define IS_LOCK_ACCEPTED(lockp) (IS_LOCK_ACTIVE(lockp) || IS_LOCK_WAITLOCK(lockp))
4128 #define IS_LOCK_CLIENTONLY(lockp) ((((lockp)->scp->flags & CM_SCACHEFLAG_RO) == CM_SCACHEFLAG_RO) || (((lockp)->flags & CM_FILELOCK_FLAG_CLIENTONLY) == CM_FILELOCK_FLAG_CLIENTONLY))
4131 #define INTERSECT_RANGE(r1,r2) (((r2).offset+(r2).length) > (r1).offset && ((r1).offset +(r1).length) > (r2).offset)
4134 #define CONTAINS_RANGE(r1,r2) (((r2).offset+(r2).length) <= ((r1).offset+(r1).length) && (r1).offset <= (r2).offset)
4136 #if defined(VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS) && !defined(LOCK_TESTING)
4137 #define SCP_SUPPORTS_BRLOCKS(scp) ((scp)->cbServerp && ((scp)->cbServerp->capabilities & VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS))
4139 #define SCP_SUPPORTS_BRLOCKS(scp) (1)
4142 #define SERVERLOCKS_ENABLED(scp) (!((scp)->flags & CM_SCACHEFLAG_RO) && cm_enableServerLocks && SCP_SUPPORTS_BRLOCKS(scp))
4144 #if defined(VICED_CAPABILITY_WRITELOCKACL)
4145 #define SCP_SUPPORTS_WRITELOCKACL(scp) ((scp)->cbServerp && ((scp->cbServerp->capabilities & VICED_CAPABILITY_WRITELOCKACL)))
4147 #define SCP_SUPPORTS_WRITELOCKACL(scp) (0)
4149 /* This should really be defined in any build that this code is being
4151 #error VICED_CAPABILITY_WRITELOCKACL not defined.
4154 static void cm_LockRangeSubtract(cm_range_t * pos, const cm_range_t * neg)
4156 afs_int64 int_begin;
4159 int_begin = MAX(pos->offset, neg->offset);
4160 int_end = MIN(pos->offset+pos->length, neg->offset+neg->length);
4162 if (int_begin < int_end) {
4163 if (int_begin == pos->offset) {
4164 pos->length = pos->offset + pos->length - int_end;
4165 pos->offset = int_end;
4166 } else if (int_end == pos->offset + pos->length) {
4167 pos->length = int_begin - pos->offset;
4170 /* We only subtract ranges if the resulting range is
4171 contiguous. If we try to support non-contigous ranges, we
4172 aren't actually improving performance. */
4176 /* Called with scp->rw held. Returns 0 if all is clear to read the
4177 specified range by the client identified by key.
4179 long cm_LockCheckRead(cm_scache_t *scp,
4180 LARGE_INTEGER LOffset,
4181 LARGE_INTEGER LLength,
4184 #ifndef ADVISORY_LOCKS
4186 cm_file_lock_t *fileLock;
4190 int substract_ranges = FALSE;
4192 range.offset = LOffset.QuadPart;
4193 range.length = LLength.QuadPart;
4197 1. for all _a_ in (Offset,+Length), all of the following is true:
4199 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
4200 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
4203 1.2 For each LOST lock L in S->fileLocks such that _a_ in
4204 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
4209 lock_ObtainRead(&cm_scacheLock);
4211 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
4213 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4215 if (INTERSECT_RANGE(range, fileLock->range)) {
4216 if (IS_LOCK_ACTIVE(fileLock)) {
4217 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4219 /* If there is an active lock for this client, it
4220 is safe to substract ranges.*/
4221 cm_LockRangeSubtract(&range, &fileLock->range);
4222 substract_ranges = TRUE;
4224 if (fileLock->lockType != LockRead) {
4225 code = CM_ERROR_LOCK_CONFLICT;
4229 /* even if the entire range is locked for reading,
4230 we still can't grant the lock at this point
4231 because the client may have lost locks. That
4232 is, unless we have already seen an active lock
4233 belonging to the client, in which case there
4234 can't be any lost locks for this client. */
4235 if (substract_ranges)
4236 cm_LockRangeSubtract(&range, &fileLock->range);
4238 } else if (IS_LOCK_LOST(fileLock) &&
4239 (cm_KeyEquals(&fileLock->key, &key, 0) || fileLock->lockType == LockWrite)) {
4240 code = CM_ERROR_BADFD;
4246 lock_ReleaseRead(&cm_scacheLock);
4248 osi_Log4(afsd_logp, "cm_LockCheckRead scp 0x%x offset %d length %d code 0x%x",
4249 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
4260 /* Called with scp->rw held. Returns 0 if all is clear to write the
4261 specified range by the client identified by key.
4263 long cm_LockCheckWrite(cm_scache_t *scp,
4264 LARGE_INTEGER LOffset,
4265 LARGE_INTEGER LLength,
4268 #ifndef ADVISORY_LOCKS
4270 cm_file_lock_t *fileLock;
4275 range.offset = LOffset.QuadPart;
4276 range.length = LLength.QuadPart;
4279 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
4281 2. for all _a_ in (Offset,+Length), one of the following is true:
4283 2.1 Byte _a_ of S is unowned AND there does not exist a LOST
4284 lock L such that _a_ in (L->LOffset,+L->LLength).
4286 2.2 Byte _a_ of S is owned by C under lock L AND L->LockType is
4290 lock_ObtainRead(&cm_scacheLock);
4292 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
4294 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4296 if (INTERSECT_RANGE(range, fileLock->range)) {
4297 if (IS_LOCK_ACTIVE(fileLock)) {
4298 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4299 if (fileLock->lockType == LockWrite) {
4301 /* if there is an active lock for this client, it
4302 is safe to substract ranges */
4303 cm_LockRangeSubtract(&range, &fileLock->range);
4305 code = CM_ERROR_LOCK_CONFLICT;
4309 code = CM_ERROR_LOCK_CONFLICT;
4312 } else if (IS_LOCK_LOST(fileLock)) {
4313 code = CM_ERROR_BADFD;
4319 lock_ReleaseRead(&cm_scacheLock);
4321 osi_Log4(afsd_logp, "cm_LockCheckWrite scp 0x%x offset %d length %d code 0x%x",
4322 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
4333 /* Called with cm_scacheLock write locked */
4334 static cm_file_lock_t * cm_GetFileLock(void) {
4337 l = (cm_file_lock_t *) cm_freeFileLocks;
4339 osi_QRemove(&cm_freeFileLocks, &l->q);
4341 l = malloc(sizeof(cm_file_lock_t));
4342 osi_assertx(l, "null cm_file_lock_t");
4345 memset(l, 0, sizeof(cm_file_lock_t));
4350 /* Called with cm_scacheLock write locked */
4351 static void cm_PutFileLock(cm_file_lock_t *l) {
4352 osi_QAdd(&cm_freeFileLocks, &l->q);
4355 /* called with scp->rw held. May release it during processing, but
4356 leaves it held on exit. */
4357 long cm_IntSetLock(cm_scache_t * scp, cm_user_t * userp, int lockType,
4363 struct rx_connection * rxconnp;
4365 afs_uint32 reqflags = reqp->flags;
4367 memset(&volSync, 0, sizeof(volSync));
4369 tfid.Volume = scp->fid.volume;
4370 tfid.Vnode = scp->fid.vnode;
4371 tfid.Unique = scp->fid.unique;
4374 osi_Log2(afsd_logp, "CALL SetLock scp 0x%p for lock %d", scp, lockType);
4376 reqp->flags |= CM_REQ_NORETRY;
4377 lock_ReleaseWrite(&scp->rw);
4380 code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4384 rxconnp = cm_GetRxConn(connp);
4385 code = RXAFS_SetLock(rxconnp, &tfid, lockType,
4387 rx_PutConnection(rxconnp);
4389 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
4392 code = cm_MapRPCError(code, reqp);
4394 osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
4396 osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
4399 reqp->flags = reqflags;
4401 lock_ObtainWrite(&scp->rw);
4404 * The file server does not return a status structure so we must
4405 * locally track the file server lock count to the best of our
4408 if (lockType == LockWrite)
4409 scp->fsLockCount = -1;
4416 /* called with scp->rw held. Releases it during processing */
4417 long cm_IntReleaseLock(cm_scache_t * scp, cm_user_t * userp,
4423 struct rx_connection * rxconnp;
4426 if (scp->flags & CM_SCACHEFLAG_DELETED) {
4427 osi_Log1(afsd_logp, "CALL ReleaseLock on Deleted Vnode scp 0x%p", scp);
4431 memset(&volSync, 0, sizeof(volSync));
4433 tfid.Volume = scp->fid.volume;
4434 tfid.Vnode = scp->fid.vnode;
4435 tfid.Unique = scp->fid.unique;
4438 lock_ReleaseWrite(&scp->rw);
4440 osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
4443 code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4447 rxconnp = cm_GetRxConn(connp);
4448 code = RXAFS_ReleaseLock(rxconnp, &tfid, &volSync);
4449 rx_PutConnection(rxconnp);
4451 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
4453 code = cm_MapRPCError(code, reqp);
4456 "CALL ReleaseLock FAILURE, code 0x%x", code);
4459 "CALL ReleaseLock SUCCESS");
4461 lock_ObtainWrite(&scp->rw);
4464 * The file server does not return a status structure so we must
4465 * locally track the file server lock count to the best of our
4469 if (scp->fsLockCount < 0)
4470 scp->fsLockCount = 0;
4473 return (code != CM_ERROR_BADFD ? code : 0);
4476 /* called with scp->rw held. May release it during processing, but
4477 will exit with lock held.
4481 - 0 if the user has permission to get the specified lock for the scp
4483 - CM_ERROR_NOACCESS if not
4485 Any other error from cm_SyncOp will be sent down untranslated.
4487 If CM_ERROR_NOACCESS is returned and lock_type is LockRead, then
4488 phas_insert (if non-NULL) will receive a boolean value indicating
4489 whether the user has INSERT permission or not.
4491 long cm_LockCheckPerms(cm_scache_t * scp,
4498 long code = 0, code2 = 0;
4500 /* lock permissions are slightly tricky because of the 'i' bit.
4501 If the user has PRSFS_LOCK, she can read-lock the file. If the
4502 user has PRSFS_WRITE, she can write-lock the file. However, if
4503 the user has PRSFS_INSERT, then she can write-lock new files,
4504 but not old ones. Since we don't have information about
4505 whether a file is new or not, we assume that if the user owns
4506 the scp, then she has the permissions that are granted by
4509 osi_Log3(afsd_logp, "cm_LockCheckPerms for scp[0x%p] type[%d] user[0x%p]",
4510 scp, lock_type, userp);
4512 if (lock_type == LockRead)
4513 rights |= PRSFS_LOCK;
4514 else if (lock_type == LockWrite)
4515 rights |= PRSFS_WRITE | PRSFS_LOCK;
4518 osi_assertx(FALSE, "invalid lock type");
4523 *phas_insert = FALSE;
4525 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
4526 CM_SCACHESYNC_GETSTATUS |
4527 CM_SCACHESYNC_NEEDCALLBACK);
4529 if (phas_insert && scp->creator == userp) {
4531 /* If this file was created by the user, then we check for
4532 PRSFS_INSERT. If the file server is recent enough, then
4533 this should be sufficient for her to get a write-lock (but
4534 not necessarily a read-lock). VICED_CAPABILITY_WRITELOCKACL
4535 indicates whether a file server supports getting write
4536 locks when the user only has PRSFS_INSERT.
4538 If the file was not created by the user we skip the check
4539 because the INSERT bit will not apply to this user even
4543 code2 = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
4544 CM_SCACHESYNC_GETSTATUS |
4545 CM_SCACHESYNC_NEEDCALLBACK);
4547 if (code2 == CM_ERROR_NOACCESS) {
4548 osi_Log0(afsd_logp, "cm_LockCheckPerms user has no INSERT bits");
4550 *phas_insert = TRUE;
4551 osi_Log0(afsd_logp, "cm_LockCheckPerms user has INSERT bits");
4555 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
4557 osi_Log1(afsd_logp, "cm_LockCheckPerms returning code %d", code);
4562 /* called with scp->rw held */
4563 long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
4564 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
4566 int allowWait, cm_user_t *userp, cm_req_t *reqp,
4567 cm_file_lock_t **lockpp)
4570 int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
4571 cm_file_lock_t *fileLock;
4574 int wait_unlock = FALSE;
4575 int force_client_lock = FALSE;
4577 osi_Log4(afsd_logp, "cm_Lock scp 0x%x type 0x%x offset %d length %d",
4578 scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
4579 osi_Log4(afsd_logp, "... allowWait %d key <0x%x, 0x%x, 0x%x>", allowWait,
4580 key.process_id, key.session_id, key.file_id);
4583 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
4585 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
4588 3.1 If L->LockType is exclusive then there does NOT exist a
4589 ACCEPTED lock M in S->fileLocks such that _a_ in
4590 (M->LOffset,+M->LLength).
4592 3.2 If L->LockType is shared then for each ACCEPTED lock M in
4593 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
4594 M->LockType is shared.
4596 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
4598 4.1 M->key != Key(C)
4600 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
4601 and (M->LOffset,+M->LLength) do not intersect.
4604 range.offset = LOffset.QuadPart;
4605 range.length = LLength.QuadPart;
4607 lock_ObtainRead(&cm_scacheLock);
4609 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4611 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4613 if (IS_LOCK_LOST(fileLock)) {
4614 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4615 code = CM_ERROR_BADFD;
4617 } else if (fileLock->lockType == LockWrite && INTERSECT_RANGE(range, fileLock->range)) {
4618 code = CM_ERROR_WOULDBLOCK;
4624 /* we don't need to check for deleted locks here since deleted
4625 locks are dequeued from scp->fileLocks */
4626 if (IS_LOCK_ACCEPTED(fileLock) &&
4627 INTERSECT_RANGE(range, fileLock->range)) {
4629 if ((sLockType & LOCKING_ANDX_SHARED_LOCK) == 0 ||
4630 fileLock->lockType != LockRead) {
4632 code = CM_ERROR_WOULDBLOCK;
4638 lock_ReleaseRead(&cm_scacheLock);
4640 if (code == 0 && SERVERLOCKS_ENABLED(scp)) {
4641 if (Which == scp->serverLock ||
4642 (Which == LockRead && scp->serverLock == LockWrite)) {
4646 /* we already have the lock we need */
4647 osi_Log3(afsd_logp, " we already have the correct lock. exclusives[%d], shared[%d], serverLock[%d]",
4648 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4650 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4652 /* special case: if we don't have permission to read-lock
4653 the file, then we force a clientside lock. This is to
4654 compensate for applications that obtain a read-lock for
4655 reading files off of directories that don't grant
4656 read-locks to the user. */
4657 if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4659 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4660 osi_Log0(afsd_logp, " User has no read-lock perms, but has INSERT perms.");
4663 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4664 force_client_lock = TRUE;
4668 } else if ((scp->exclusiveLocks > 0) ||
4669 (scp->sharedLocks > 0 && scp->serverLock != LockRead)) {
4672 /* We are already waiting for some other lock. We should
4673 wait for the daemon to catch up instead of generating a
4674 flood of SetLock calls. */
4675 osi_Log3(afsd_logp, " already waiting for other lock. exclusives[%d], shared[%d], serverLock[%d]",
4676 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4678 /* see if we have permission to create the lock in the
4680 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4682 code = CM_ERROR_WOULDBLOCK;
4683 else if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4685 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4687 " User has no read-lock perms, but has INSERT perms.");
4688 code = CM_ERROR_WOULDBLOCK;
4691 " User has no read-lock perms. Forcing client-side lock");
4692 force_client_lock = TRUE;
4696 /* leave any other codes as-is */
4700 int check_data_version = FALSE;
4703 /* first check if we have permission to elevate or obtain
4705 code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4707 if (code == CM_ERROR_NOACCESS && Which == LockRead &&
4708 (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp))) {
4709 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4710 force_client_lock = TRUE;
4715 /* has_insert => (Which == LockRead, code == CM_ERROR_NOACCESS) */
4717 if (scp->serverLock == LockRead && Which == LockWrite) {
4719 /* We want to escalate the lock to a LockWrite.
4720 * Unfortunately that's not really possible without
4721 * letting go of the current lock. But for now we do
4725 " attempting to UPGRADE from LockRead to LockWrite.");
4727 " dataVersion on scp: %I64d", scp->dataVersion);
4729 /* we assume at this point (because scp->serverLock
4730 was valid) that we had a valid server lock. */
4731 scp->lockDataVersion = scp->dataVersion;
4732 check_data_version = TRUE;
4734 code = cm_IntReleaseLock(scp, userp, reqp);
4737 /* We couldn't release the lock */
4740 scp->serverLock = -1;
4744 /* We need to obtain a server lock of type Which in order
4745 * to assert this file lock */
4746 #ifndef AGGRESSIVE_LOCKS
4749 newLock = LockWrite;
4752 code = cm_IntSetLock(scp, userp, newLock, reqp);
4754 #ifdef AGGRESSIVE_LOCKS
4755 if ((code == CM_ERROR_WOULDBLOCK ||
4756 code == CM_ERROR_NOACCESS) && newLock != Which) {
4757 /* we wanted LockRead. We tried LockWrite. Now try
4762 osi_assertx(newLock == LockRead, "lock type not read");
4764 code = cm_IntSetLock(scp, userp, newLock, reqp);
4768 if (code == CM_ERROR_NOACCESS) {
4769 if (Which == LockRead) {
4770 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4772 /* We requested a read-lock, but we have permission to
4773 * get a write-lock. Try that */
4775 tcode = cm_LockCheckPerms(scp, LockWrite, userp, reqp, NULL);
4778 newLock = LockWrite;
4780 osi_Log0(afsd_logp, " User has 'i' perms and the request was for a LockRead. Trying to get a LockWrite instead");
4782 code = cm_IntSetLock(scp, userp, newLock, reqp);
4785 osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
4786 force_client_lock = TRUE;
4788 } else if (Which == LockWrite &&
4789 scp->creator == userp && !SCP_SUPPORTS_WRITELOCKACL(scp)) {
4792 /* Special case: if the lock request was for a
4793 * LockWrite and the user owns the file and we weren't
4794 * allowed to obtain the serverlock, we either lost a
4795 * race (the permissions changed from under us), or we
4796 * have 'i' bits, but we aren't allowed to lock the
4799 /* check if we lost a race... */
4800 tcode = cm_LockCheckPerms(scp, Which, userp, reqp, NULL);
4803 osi_Log0(afsd_logp, " User has 'i' perms but can't obtain write locks. Using client-side locks.");
4804 force_client_lock = TRUE;
4809 if (code == 0 && check_data_version &&
4810 scp->dataVersion != scp->lockDataVersion) {
4811 /* We lost a race. Although we successfully obtained
4812 * a lock, someone modified the file in between. The
4813 * locks have all been technically lost. */
4816 " Data version mismatch while upgrading lock.");
4818 " Data versions before=%I64d, after=%I64d",
4819 scp->lockDataVersion,
4822 " Releasing stale lock for scp 0x%x", scp);
4824 code = cm_IntReleaseLock(scp, userp, reqp);
4826 scp->serverLock = -1;
4828 code = CM_ERROR_INVAL;
4829 } else if (code == 0) {
4830 scp->serverLock = newLock;
4831 scp->lockDataVersion = scp->dataVersion;
4835 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4836 scp->serverLock == -1) {
4837 /* Oops. We lost the lock. */
4838 cm_LockMarkSCacheLost(scp);
4841 } else if (code == 0) { /* server locks not enabled */
4843 " Skipping server lock for scp");
4848 if (code != 0 && !force_client_lock) {
4849 /* Special case error translations
4851 Applications don't expect certain errors from a
4852 LockFile/UnlockFile call. We need to translate some error
4853 code to codes that apps expect and handle. */
4855 /* We shouldn't actually need to handle this case since we
4856 simulate locks for RO scps anyway. */
4857 if (code == CM_ERROR_READONLY) {
4858 osi_Log0(afsd_logp, " Reinterpreting CM_ERROR_READONLY as CM_ERROR_NOACCESS");
4859 code = CM_ERROR_NOACCESS;
4863 if (code == 0 || (code == CM_ERROR_WOULDBLOCK && allowWait) ||
4864 force_client_lock) {
4866 /* clear the error if we are forcing a client lock, so we
4867 don't get confused later. */
4868 if (force_client_lock && code != CM_ERROR_WOULDBLOCK)
4873 lock_ObtainWrite(&cm_scacheLock);
4874 fileLock = cm_GetFileLock();
4876 fileLock->fid = scp->fid;
4878 fileLock->key = key;
4879 fileLock->lockType = Which;
4880 fileLock->userp = userp;
4881 fileLock->range = range;
4882 fileLock->flags = (code == 0 ? 0 :
4884 CM_FILELOCK_FLAG_WAITUNLOCK :
4885 CM_FILELOCK_FLAG_WAITLOCK));
4887 if (force_client_lock || !SERVERLOCKS_ENABLED(scp))
4888 fileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
4890 fileLock->lastUpdate = (code == 0 && !force_client_lock) ? time(NULL) : 0;
4892 osi_QAddT(&scp->fileLocksH, &scp->fileLocksT, &fileLock->fileq);
4893 cm_HoldSCacheNoLock(scp);
4894 fileLock->scp = scp;
4895 osi_QAdd(&cm_allFileLocks, &fileLock->q);
4896 lock_ReleaseWrite(&cm_scacheLock);
4902 if (IS_LOCK_CLIENTONLY(fileLock)) {
4904 } else if (IS_LOCK_ACCEPTED(fileLock)) {
4905 if (Which == LockRead)
4908 scp->exclusiveLocks++;
4912 "cm_Lock Lock added 0x%p flags 0x%x to scp [0x%p]",
4913 fileLock, fileLock->flags, scp);
4915 " exclusives[%d] shared[%d] client[%d] serverLock[%d]",
4916 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4917 (int)(signed char) scp->serverLock);
4920 "cm_Lock Rejecting lock (code = 0x%x)", code);
4923 /* Convert from would block to lock not granted */
4924 if (code == CM_ERROR_WOULDBLOCK)
4925 code = CM_ERROR_LOCK_NOT_GRANTED;
4931 cm_IntUnlock(cm_scache_t * scp,
4937 osi_assertx(scp->sharedLocks >= 0, "scp->sharedLocks < 0");
4938 osi_assertx(scp->exclusiveLocks >= 0, "scp->exclusiveLocks < 0");
4939 osi_assertx(scp->clientLocks >= 0, "scp->clientLocks < 0");
4941 if (!SERVERLOCKS_ENABLED(scp)) {
4942 osi_Log0(afsd_logp, " Skipping server lock for scp");
4946 /* Ideally we would go through the rest of the locks to determine
4947 * if one or more locks that were formerly in WAITUNLOCK can now
4948 * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
4949 * scp->sharedLocks accordingly. However, the retrying of locks
4950 * in that manner is done cm_RetryLock() manually.
4953 if (scp->serverLock == LockWrite &&
4954 scp->exclusiveLocks == 0 &&
4955 scp->sharedLocks > 0) {
4956 /* The serverLock should be downgraded to LockRead */
4957 osi_Log0(afsd_logp, " DOWNGRADE lock from LockWrite to LockRead");
4959 /* Make sure there are no dirty buffers left. */
4960 code = cm_FSync(scp, userp, reqp, TRUE);
4962 /* since scp->serverLock looked sane, we are going to assume
4963 that we have a valid server lock. */
4964 scp->lockDataVersion = scp->dataVersion;
4965 osi_Log1(afsd_logp, " dataVersion on scp = %I64d", scp->dataVersion);
4967 /* before we downgrade, make sure that we have enough
4968 permissions to get the read lock. */
4969 code = cm_LockCheckPerms(scp, LockRead, userp, reqp, NULL);
4972 osi_Log0(afsd_logp, " SKIPPING downgrade because user doesn't have perms to get downgraded lock");
4978 code = cm_IntReleaseLock(scp, userp, reqp);
4981 /* so we couldn't release it. Just let the lock be for now */
4985 scp->serverLock = -1;
4988 code = cm_IntSetLock(scp, userp, LockRead, reqp);
4990 if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
4991 scp->serverLock = LockRead;
4992 } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
4993 /* We lost a race condition. Although we have a valid
4994 lock on the file, the data has changed and essentially
4995 we have lost the lock we had during the transition. */
4997 osi_Log0(afsd_logp, "Data version mismatch during lock downgrade");
4998 osi_Log2(afsd_logp, " Data versions before=%I64d, after=%I64d",
4999 scp->lockDataVersion,
5002 code = cm_IntReleaseLock(scp, userp, reqp);
5004 code = CM_ERROR_INVAL;
5005 scp->serverLock = -1;
5009 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
5010 (scp->serverLock == -1)) {
5012 cm_LockMarkSCacheLost(scp);
5015 /* failure here has no bearing on the return value of cm_Unlock() */
5018 } else if (scp->serverLock != (-1) &&
5019 scp->exclusiveLocks == 0 &&
5020 scp->sharedLocks == 0) {
5021 /* The serverLock should be released entirely */
5023 if (scp->serverLock == LockWrite) {
5024 osi_Log0(afsd_logp, " RELEASE LockWrite -> LockNone");
5026 /* Make sure there are no dirty buffers left. */
5027 code = cm_FSync(scp, userp, reqp, TRUE);
5029 osi_Log0(afsd_logp, " RELEASE LockRead -> LockNone");
5032 code = cm_IntReleaseLock(scp, userp, reqp);
5035 scp->serverLock = (-1);
5041 /* Called with scp->rw held */
5042 long cm_UnlockByKey(cm_scache_t * scp,
5049 cm_file_lock_t *fileLock;
5050 osi_queue_t *q, *qn;
5053 osi_Log4(afsd_logp, "cm_UnlockByKey scp 0x%p key <0x%x,0x%x,0x%x",
5054 scp, key.process_id, key.session_id, key.file_id);
5055 osi_Log1(afsd_logp, " flags=0x%x", flags);
5057 lock_ObtainWrite(&cm_scacheLock);
5059 for (q = scp->fileLocksH; q; q = qn) {
5062 fileLock = (cm_file_lock_t *)
5063 ((char *) q - offsetof(cm_file_lock_t, fileq));
5066 osi_Log4(afsd_logp, " Checking lock[0x%x] range[%d,+%d] type[%d]",
5068 (unsigned long) fileLock->range.offset,
5069 (unsigned long) fileLock->range.length,
5070 fileLock->lockType);
5071 osi_Log4(afsd_logp, " key<0x%x, 0x%x, 0x%x> flags[0x%x]",
5072 fileLock->key.process_id, fileLock->key.session_id, fileLock->key.file_id,
5075 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5076 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5077 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5079 fileLock->fid.volume,
5080 fileLock->fid.vnode,
5081 fileLock->fid.unique);
5082 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5083 fileLock->scp->fid.cell,
5084 fileLock->scp->fid.volume,
5085 fileLock->scp->fid.vnode,
5086 fileLock->scp->fid.unique);
5087 osi_assertx(FALSE, "invalid fid value");
5091 if (!IS_LOCK_DELETED(fileLock) &&
5092 cm_KeyEquals(&fileLock->key, &key, flags)) {
5093 osi_Log3(afsd_logp, "...Unlock range [%d,+%d] type %d",
5094 fileLock->range.offset,
5095 fileLock->range.length,
5096 fileLock->lockType);
5098 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
5100 if (IS_LOCK_CLIENTONLY(fileLock)) {
5102 } else if (IS_LOCK_ACCEPTED(fileLock)) {
5103 if (fileLock->lockType == LockRead)
5106 scp->exclusiveLocks--;
5109 fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5115 lock_ReleaseWrite(&cm_scacheLock);
5117 if (n_unlocks == 0) {
5118 osi_Log0(afsd_logp, "cm_UnlockByKey no locks found");
5119 osi_Log3(afsd_logp, " Leaving scp with exclusives[%d], shared[%d], serverLock[%d]",
5120 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
5125 osi_Log1(afsd_logp, "cm_UnlockByKey done with %d locks", n_unlocks);
5127 osi_Log1(afsd_logp, "cm_UnlockByKey code 0x%x", code);
5128 osi_Log4(afsd_logp, " Leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
5129 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
5130 (int)(signed char) scp->serverLock);
5135 long cm_Unlock(cm_scache_t *scp,
5136 unsigned char sLockType,
5137 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
5144 int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
5145 cm_file_lock_t *fileLock;
5147 int release_userp = FALSE;
5148 int exact_match = !(flags & CM_UNLOCK_FLAG_MATCH_RANGE);
5150 LARGE_INTEGER RangeEnd;
5152 osi_Log4(afsd_logp, "cm_Unlock scp 0x%p type 0x%x offset 0x%x length 0x%x",
5153 scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
5154 osi_Log4(afsd_logp, "... key <0x%x,0x%x,0x%x> flags 0x%x",
5155 key.process_id, key.session_id, key.file_id, flags);
5158 RangeEnd.QuadPart = LOffset.QuadPart + LLength.QuadPart;
5161 lock_ObtainRead(&cm_scacheLock);
5163 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5164 fileLock = (cm_file_lock_t *)
5165 ((char *) q - offsetof(cm_file_lock_t, fileq));
5168 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5169 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5170 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5172 fileLock->fid.volume,
5173 fileLock->fid.vnode,
5174 fileLock->fid.unique);
5175 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5176 fileLock->scp->fid.cell,
5177 fileLock->scp->fid.volume,
5178 fileLock->scp->fid.vnode,
5179 fileLock->scp->fid.unique);
5180 osi_assertx(FALSE, "invalid fid value");
5184 if (!IS_LOCK_DELETED(fileLock) &&
5185 cm_KeyEquals(&fileLock->key, &key, 0) &&
5186 fileLock->range.offset == LOffset.QuadPart &&
5187 fileLock->range.length == LLength.QuadPart) {
5193 if (!IS_LOCK_DELETED(fileLock) &&
5194 cm_KeyEquals(&fileLock->key, &key, 0) &&
5195 fileLock->range.offset >= LOffset.QuadPart &&
5196 fileLock->range.offset < RangeEnd.QuadPart &&
5197 (fileLock->range.offset + fileLock->range.length) <= RangeEnd.QuadPart) {
5205 lock_ReleaseRead(&cm_scacheLock);
5207 if (lock_found && !exact_match) {
5211 osi_Log0(afsd_logp, "cm_Unlock lock not found; failure");
5213 /* The lock didn't exist anyway. *shrug* */
5214 return CM_ERROR_RANGE_NOT_LOCKED;
5218 /* discard lock record */
5219 lock_ConvertRToW(&cm_scacheLock);
5220 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
5223 * Don't delete it here; let the daemon delete it, to simplify
5224 * the daemon's traversal of the list.
5227 if (IS_LOCK_CLIENTONLY(fileLock)) {
5229 } else if (IS_LOCK_ACCEPTED(fileLock)) {
5230 if (fileLock->lockType == LockRead)
5233 scp->exclusiveLocks--;
5236 fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5237 lock_ReleaseWrite(&cm_scacheLock);
5239 if (release_userp) {
5240 cm_ReleaseUser(userp);
5241 release_userp = FALSE;
5245 osi_Log1(afsd_logp, "cm_Unlock not exact match, searching for next lock, code 0x%x", code);
5246 goto try_again; /* might be more than one lock in the range */
5251 osi_Log1(afsd_logp, "cm_Unlock code 0x%x", code);
5252 osi_Log4(afsd_logp, " leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
5253 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
5254 (int)(signed char) scp->serverLock);
5259 /* called with scp->rw held */
5260 void cm_LockMarkSCacheLost(cm_scache_t * scp)
5262 cm_file_lock_t *fileLock;
5265 osi_Log1(afsd_logp, "cm_LockMarkSCacheLost scp 0x%x", scp);
5267 /* cm_scacheLock needed because we are modifying fileLock->flags */
5268 lock_ObtainWrite(&cm_scacheLock);
5270 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5272 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
5274 if (IS_LOCK_ACTIVE(fileLock) &&
5275 !IS_LOCK_CLIENTONLY(fileLock)) {
5276 if (fileLock->lockType == LockRead)
5279 scp->exclusiveLocks--;
5281 fileLock->flags |= CM_FILELOCK_FLAG_LOST;
5285 scp->serverLock = -1;
5286 scp->lockDataVersion = CM_SCACHE_VERSION_BAD;
5287 lock_ReleaseWrite(&cm_scacheLock);
5290 /* Called with no relevant locks held */
5291 void cm_CheckLocks()
5293 osi_queue_t *q, *nq;
5294 cm_file_lock_t *fileLock;
5300 struct rx_connection * rxconnp;
5303 memset(&volSync, 0, sizeof(volSync));
5307 lock_ObtainWrite(&cm_scacheLock);
5309 cm_lockRefreshCycle++;
5311 osi_Log1(afsd_logp, "cm_CheckLocks starting lock check cycle %d", cm_lockRefreshCycle);
5313 for (q = cm_allFileLocks; q; q = nq) {
5314 fileLock = (cm_file_lock_t *) q;
5318 if (IS_LOCK_DELETED(fileLock)) {
5319 cm_user_t *userp = fileLock->userp;
5320 cm_scache_t *scp = fileLock->scp;
5321 fileLock->userp = NULL;
5322 fileLock->scp = NULL;
5324 lock_ReleaseWrite(&cm_scacheLock);
5325 lock_ObtainWrite(&scp->rw);
5326 code = cm_IntUnlock(scp, userp, &req);
5327 lock_ReleaseWrite(&scp->rw);
5329 cm_ReleaseUser(fileLock->userp);
5330 lock_ObtainWrite(&cm_scacheLock);
5331 cm_ReleaseSCacheNoLock(scp);
5333 osi_QRemove(&cm_allFileLocks, q);
5334 cm_PutFileLock(fileLock);
5336 } else if (IS_LOCK_ACTIVE(fileLock) && !IS_LOCK_CLIENTONLY(fileLock)) {
5338 /* Server locks must have been enabled for us to have
5339 received an active non-client-only lock. */
5340 osi_assertx(cm_enableServerLocks, "!cm_enableServerLocks");
5342 scp = fileLock->scp;
5343 osi_assertx(scp != NULL, "null cm_scache_t");
5345 cm_HoldSCacheNoLock(scp);
5348 if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5349 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5350 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5352 fileLock->fid.volume,
5353 fileLock->fid.vnode,
5354 fileLock->fid.unique);
5355 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5356 fileLock->scp->fid.cell,
5357 fileLock->scp->fid.volume,
5358 fileLock->scp->fid.vnode,
5359 fileLock->scp->fid.unique);
5360 osi_assertx(FALSE, "invalid fid");
5363 /* Server locks are extended once per scp per refresh
5365 if (scp->lastRefreshCycle != cm_lockRefreshCycle) {
5367 int scp_done = FALSE;
5369 osi_Log1(afsd_logp, "cm_CheckLocks Updating scp 0x%x", scp);
5371 lock_ReleaseWrite(&cm_scacheLock);
5372 lock_ObtainWrite(&scp->rw);
5374 /* did the lock change while we weren't holding the lock? */
5375 if (!IS_LOCK_ACTIVE(fileLock))
5376 goto post_syncopdone;
5378 code = cm_SyncOp(scp, NULL, fileLock->userp, &req, 0,
5379 CM_SCACHESYNC_NEEDCALLBACK
5380 | CM_SCACHESYNC_GETSTATUS
5381 | CM_SCACHESYNC_LOCK);
5385 "cm_CheckLocks SyncOp failure code 0x%x", code);
5386 goto post_syncopdone;
5389 /* cm_SyncOp releases scp->rw during which the lock
5390 may get released. */
5391 if (!IS_LOCK_ACTIVE(fileLock))
5392 goto pre_syncopdone;
5394 if (scp->serverLock != -1 && !(scp->flags & CM_SCACHEFLAG_DELETED)) {
5398 tfid.Volume = scp->fid.volume;
5399 tfid.Vnode = scp->fid.vnode;
5400 tfid.Unique = scp->fid.unique;
5402 userp = fileLock->userp;
5404 osi_Log3(afsd_logp, "CALL ExtendLock lock 0x%p for scp=0x%p with lock %d",
5407 (int) scp->serverLock);
5409 lock_ReleaseWrite(&scp->rw);
5412 code = cm_ConnFromFID(&cfid, userp,
5417 rxconnp = cm_GetRxConn(connp);
5418 code = RXAFS_ExtendLock(rxconnp, &tfid,
5420 rx_PutConnection(rxconnp);
5422 osi_Log1(afsd_logp, " ExtendLock returns %d", code);
5424 } while (cm_Analyze(connp, userp, &req,
5425 &cfid, &volSync, NULL, NULL,
5428 code = cm_MapRPCError(code, &req);
5430 lock_ObtainWrite(&scp->rw);
5433 osi_Log1(afsd_logp, "CALL ExtendLock FAILURE, code 0x%x", code);
5434 scp->fsLockCount = 0;
5436 osi_Log0(afsd_logp, "CALL ExtendLock SUCCESS");
5437 scp->lockDataVersion = scp->dataVersion;
5440 if ((code == EINVAL || code == CM_ERROR_INVAL) &&
5441 scp->lockDataVersion == scp->dataVersion) {
5445 (scp->exclusiveLocks > 0) ? LockWrite: LockRead;
5447 /* we might still have a chance to obtain a
5450 code = cm_IntSetLock(scp, userp, lockType, &req);
5453 code = CM_ERROR_INVAL;
5454 } else if (scp->lockDataVersion != scp->dataVersion) {
5456 /* now check if we still have the file at
5457 the right data version. */
5459 "Data version mismatch on scp 0x%p",
5462 " Data versions: before=%I64d, after=%I64d",
5463 scp->lockDataVersion,
5466 code = cm_IntReleaseLock(scp, userp, &req);
5468 code = CM_ERROR_INVAL;
5472 if (code == EINVAL || code == CM_ERROR_INVAL ||
5473 code == CM_ERROR_BADFD) {
5474 cm_LockMarkSCacheLost(scp);
5478 /* interestingly, we have found an active lock
5479 belonging to an scache that has no
5481 cm_LockMarkSCacheLost(scp);
5488 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5491 lock_ReleaseWrite(&scp->rw);
5493 lock_ObtainWrite(&cm_scacheLock);
5496 fileLock->lastUpdate = time(NULL);
5500 scp->lastRefreshCycle = cm_lockRefreshCycle;
5503 /* we have already refreshed the locks on this scp */
5504 fileLock->lastUpdate = time(NULL);
5507 cm_ReleaseSCacheNoLock(scp);
5509 } else if (IS_LOCK_ACTIVE(fileLock) && IS_LOCK_CLIENTONLY(fileLock)) {
5510 /* TODO: Check callbacks */
5514 lock_ReleaseWrite(&cm_scacheLock);
5515 osi_Log1(afsd_logp, "cm_CheckLocks completes lock check cycle %d", cm_lockRefreshCycle);
5518 /* NOT called with scp->rw held. */
5519 long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
5522 cm_scache_t *scp = NULL;
5523 cm_file_lock_t *fileLock;
5527 int force_client_lock = FALSE;
5528 int has_insert = FALSE;
5529 int check_data_version = FALSE;
5533 if (client_is_dead) {
5534 code = CM_ERROR_TIMEDOUT;
5538 lock_ObtainRead(&cm_scacheLock);
5540 osi_Log2(afsd_logp, "cm_RetryLock checking lock %p (scp=%p)", oldFileLock, oldFileLock->scp);
5541 osi_Log4(afsd_logp, " offset(%x:%x) length(%x:%x)",
5542 (unsigned)(oldFileLock->range.offset >> 32),
5543 (unsigned)(oldFileLock->range.offset & 0xffffffff),
5544 (unsigned)(oldFileLock->range.length >> 32),
5545 (unsigned)(oldFileLock->range.length & 0xffffffff));
5546 osi_Log4(afsd_logp, " key<0x%x,0x%x,0x%x> flags=%x",
5547 oldFileLock->key.process_id, oldFileLock->key.session_id, oldFileLock->key.file_id,
5548 (unsigned)(oldFileLock->flags));
5550 /* if the lock has already been granted, then we have nothing to do */
5551 if (IS_LOCK_ACTIVE(oldFileLock)) {
5552 lock_ReleaseRead(&cm_scacheLock);
5553 osi_Log0(afsd_logp, "cm_RetryLock lock already granted");
5557 /* we can't do anything with lost or deleted locks at the moment. */
5558 if (IS_LOCK_LOST(oldFileLock) || IS_LOCK_DELETED(oldFileLock)) {
5559 code = CM_ERROR_BADFD;
5560 osi_Log0(afsd_logp, "cm_RetryLock lock is lost or deleted");
5561 lock_ReleaseRead(&cm_scacheLock);
5565 scp = oldFileLock->scp;
5567 osi_assertx(scp != NULL, "null cm_scache_t");
5569 lock_ReleaseRead(&cm_scacheLock);
5570 lock_ObtainWrite(&scp->rw);
5572 code = cm_LockCheckPerms(scp, oldFileLock->lockType,
5576 if (code == CM_ERROR_NOACCESS && oldFileLock->lockType == LockRead) {
5577 if (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp)) {
5578 force_client_lock = TRUE;
5582 lock_ReleaseWrite(&scp->rw);
5586 lock_ObtainWrite(&cm_scacheLock);
5588 /* Check if we already have a sufficient server lock to allow this
5589 lock to go through. */
5590 if (IS_LOCK_WAITLOCK(oldFileLock) &&
5591 (!SERVERLOCKS_ENABLED(scp) ||
5592 scp->serverLock == oldFileLock->lockType ||
5593 scp->serverLock == LockWrite)) {
5595 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5597 if (SERVERLOCKS_ENABLED(scp)) {
5598 osi_Log1(afsd_logp, "cm_RetryLock Server lock (%d) is sufficient for lock. Granting",
5599 (int) scp->serverLock);
5601 osi_Log0(afsd_logp, "cm_RetryLock skipping server lock for scp");
5604 lock_ReleaseWrite(&cm_scacheLock);
5605 lock_ReleaseWrite(&scp->rw);
5610 if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5612 /* check if the conflicting locks have dissappeared already */
5613 for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5615 fileLock = (cm_file_lock_t *)
5616 ((char *) q - offsetof(cm_file_lock_t, fileq));
5618 if (IS_LOCK_LOST(fileLock)) {
5619 if (cm_KeyEquals(&fileLock->key, &oldFileLock->key, 0)) {
5620 code = CM_ERROR_BADFD;
5621 oldFileLock->flags |= CM_FILELOCK_FLAG_LOST;
5622 osi_Log1(afsd_logp, " found lost lock %p for same key. Marking lock as lost",
5625 } else if (fileLock->lockType == LockWrite &&
5626 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5627 osi_Log1(afsd_logp, " found conflicting LOST lock %p", fileLock);
5628 code = CM_ERROR_WOULDBLOCK;
5633 if (IS_LOCK_ACCEPTED(fileLock) &&
5634 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5636 if (oldFileLock->lockType != LockRead ||
5637 fileLock->lockType != LockRead) {
5639 osi_Log1(afsd_logp, " found conflicting lock %p", fileLock);
5640 code = CM_ERROR_WOULDBLOCK;
5648 lock_ReleaseWrite(&cm_scacheLock);
5649 lock_ReleaseWrite(&scp->rw);
5654 /* when we get here, the lock is either a WAITUNLOCK or WAITLOCK.
5655 If it is WAITUNLOCK, then we didn't find any conflicting lock
5656 but we haven't verfied whether the serverLock is sufficient to
5657 assert it. If it is WAITLOCK, then the serverLock is
5658 insufficient to assert it. Eitherway, we are ready to accept
5659 the lock as either ACTIVE or WAITLOCK depending on the
5662 /* First, promote the WAITUNLOCK to a WAITLOCK */
5663 if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5664 if (oldFileLock->lockType == LockRead)
5667 scp->exclusiveLocks++;
5669 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITUNLOCK;
5670 oldFileLock->flags |= CM_FILELOCK_FLAG_WAITLOCK;
5673 osi_assertx(IS_LOCK_WAITLOCK(oldFileLock), "!IS_LOCK_WAITLOCK");
5675 if (force_client_lock ||
5676 !SERVERLOCKS_ENABLED(scp) ||
5677 scp->serverLock == oldFileLock->lockType ||
5678 (oldFileLock->lockType == LockRead &&
5679 scp->serverLock == LockWrite)) {
5681 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5683 if ((force_client_lock ||
5684 !SERVERLOCKS_ENABLED(scp)) &&
5685 !IS_LOCK_CLIENTONLY(oldFileLock)) {
5687 oldFileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
5689 if (oldFileLock->lockType == LockRead)
5692 scp->exclusiveLocks--;
5697 lock_ReleaseWrite(&cm_scacheLock);
5698 lock_ReleaseWrite(&scp->rw);
5705 code = cm_SyncOp(scp, NULL, oldFileLock->userp, &req, 0,
5706 CM_SCACHESYNC_NEEDCALLBACK
5707 | CM_SCACHESYNC_GETSTATUS
5708 | CM_SCACHESYNC_LOCK);
5710 osi_Log1(afsd_logp, "cm_RetryLock SyncOp failure code 0x%x", code);
5711 lock_ReleaseWrite(&cm_scacheLock);
5712 goto post_syncopdone;
5715 if (!IS_LOCK_WAITLOCK(oldFileLock))
5716 goto pre_syncopdone;
5718 userp = oldFileLock->userp;
5720 #ifndef AGGRESSIVE_LOCKS
5721 newLock = oldFileLock->lockType;
5723 newLock = LockWrite;
5727 /* if has_insert is non-zero, then:
5728 - the lock a LockRead
5729 - we don't have permission to get a LockRead
5730 - we do have permission to get a LockWrite
5731 - the server supports VICED_CAPABILITY_WRITELOCKACL
5734 newLock = LockWrite;
5737 lock_ReleaseWrite(&cm_scacheLock);
5739 /* when we get here, either we have a read-lock and want a
5740 write-lock or we don't have any locks and we want some
5743 if (scp->serverLock == LockRead) {
5745 osi_assertx(newLock == LockWrite, "!LockWrite");
5747 osi_Log0(afsd_logp, " Attempting to UPGRADE from LockRead to LockWrite");
5749 scp->lockDataVersion = scp->dataVersion;
5750 check_data_version = TRUE;
5752 code = cm_IntReleaseLock(scp, userp, &req);
5755 goto pre_syncopdone;
5757 scp->serverLock = -1;
5760 code = cm_IntSetLock(scp, userp, newLock, &req);
5763 if (scp->dataVersion != scp->lockDataVersion) {
5764 /* we lost a race. too bad */
5767 " Data version mismatch while upgrading lock.");
5769 " Data versions before=%I64d, after=%I64d",
5770 scp->lockDataVersion,
5773 " Releasing stale lock for scp 0x%x", scp);
5775 code = cm_IntReleaseLock(scp, userp, &req);
5777 scp->serverLock = -1;
5779 code = CM_ERROR_INVAL;
5781 cm_LockMarkSCacheLost(scp);
5783 scp->serverLock = newLock;
5788 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5794 if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
5795 lock_ObtainWrite(&cm_scacheLock);
5796 osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, &oldFileLock->fileq);
5797 lock_ReleaseWrite(&cm_scacheLock);
5799 lock_ReleaseWrite(&scp->rw);
5802 lock_ObtainWrite(&cm_scacheLock);
5804 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5805 } else if (code != CM_ERROR_WOULDBLOCK) {
5806 oldFileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5807 cm_ReleaseUser(oldFileLock->userp);
5808 oldFileLock->userp = NULL;
5809 if (oldFileLock->scp) {
5810 cm_ReleaseSCacheNoLock(oldFileLock->scp);
5811 oldFileLock->scp = NULL;
5814 lock_ReleaseWrite(&cm_scacheLock);
5819 cm_key_t cm_GenerateKey(afs_uint16 session_id, afs_offs_t process_id, afs_uint16 file_id)
5823 key.process_id = process_id;
5824 key.session_id = session_id;
5825 key.file_id = file_id;
5830 int cm_KeyEquals(cm_key_t *k1, cm_key_t *k2, int flags)
5832 return (k1->session_id == k2->session_id) && (k1->file_id == k2->file_id) &&
5833 ((flags & CM_UNLOCK_BY_FID) || (k1->process_id == k2->process_id));
5836 void cm_ReleaseAllLocks(void)
5842 cm_file_lock_t *fileLock;
5845 for (i = 0; i < cm_data.scacheHashTableSize; i++)
5847 for ( scp = cm_data.scacheHashTablep[i]; scp; scp = scp->nextp ) {
5848 while (scp->fileLocksH != NULL) {
5849 lock_ObtainWrite(&scp->rw);
5850 lock_ObtainWrite(&cm_scacheLock);
5851 if (!scp->fileLocksH) {
5852 lock_ReleaseWrite(&cm_scacheLock);
5853 lock_ReleaseWrite(&scp->rw);
5856 fileLock = (cm_file_lock_t *)((char *) scp->fileLocksH - offsetof(cm_file_lock_t, fileq));
5857 userp = fileLock->userp;
5859 key = fileLock->key;
5860 cm_HoldSCacheNoLock(scp);
5861 lock_ReleaseWrite(&cm_scacheLock);
5862 cm_UnlockByKey(scp, key, 0, userp, &req);
5863 cm_ReleaseSCache(scp);
5864 cm_ReleaseUser(userp);
5865 lock_ReleaseWrite(&scp->rw);