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 <afs/param.h>
30 extern void afsi_log(char *pattern, ...);
33 int cm_enableServerLocks = 1;
35 int cm_followBackupPath = 0;
38 * Case-folding array. This was constructed by inspecting of SMBtrace output.
39 * I do not know anything more about it.
41 unsigned char cm_foldUpper[256] = {
42 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
43 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
44 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
45 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
46 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
47 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
48 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
49 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
50 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
51 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
52 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
53 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
54 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
55 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
56 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
57 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
58 0x80, 0x9a, 0x90, 0x41, 0x8e, 0x41, 0x8f, 0x80,
59 0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x8e, 0x8f,
60 0x90, 0x92, 0x92, 0x4f, 0x99, 0x4f, 0x55, 0x55,
61 0x59, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
62 0x41, 0x49, 0x4f, 0x55, 0xa5, 0xa5, 0x56, 0xa7,
63 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
64 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
65 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
66 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
67 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
68 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
69 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
70 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
71 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
72 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
73 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
77 * Case-insensitive string comparison. We used to use stricmp, but it doesn't
78 * know about 8-bit characters (e.g. 129 is lowercase u-umlaut, 154 is
79 * upper-case u-umlaut).
81 int cm_stricmp(const char *str1, const char *str2)
93 c1 = (char) cm_foldUpper[(unsigned char)(*str1++)];
94 c2 = (char) cm_foldUpper[(unsigned char)(*str2++)];
104 /* return success if we can open this file in this mode */
105 long cm_CheckOpen(cm_scache_t *scp, int openMode, int trunc, cm_user_t *userp,
113 rights |= PRSFS_READ;
114 if (openMode == 1 || openMode == 2 || trunc)
115 rights |= PRSFS_WRITE;
117 lock_ObtainWrite(&scp->rw);
119 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
120 CM_SCACHESYNC_GETSTATUS
121 | CM_SCACHESYNC_NEEDCALLBACK
122 | CM_SCACHESYNC_LOCK);
125 ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
126 scp->fileType == CM_SCACHETYPE_FILE) {
129 unsigned int sLockType;
130 LARGE_INTEGER LOffset, LLength;
132 /* Check if there's some sort of lock on the file at the
135 key = cm_GenerateKey(CM_SESSION_CMINT,0,0);
137 if (rights & PRSFS_WRITE)
140 sLockType = LOCKING_ANDX_SHARED_LOCK;
142 LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
143 LOffset.LowPart = CM_FLSHARE_OFFSET_LOW;
144 LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
145 LLength.LowPart = CM_FLSHARE_LENGTH_LOW;
147 code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
150 cm_Unlock(scp, sLockType, LOffset, LLength, key, userp, reqp);
152 /* In this case, we allow the file open to go through even
153 though we can't enforce mandatory locking on the
155 if (code == CM_ERROR_NOACCESS &&
156 !(rights & PRSFS_WRITE))
160 case CM_ERROR_ALLOFFLINE:
161 case CM_ERROR_ALLDOWN:
162 case CM_ERROR_ALLBUSY:
163 case CM_ERROR_TIMEDOUT:
165 case CM_ERROR_WOULDBLOCK:
168 code = CM_ERROR_SHARING_VIOLATION;
173 } else if (code != 0) {
177 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
181 lock_ReleaseWrite(&scp->rw);
186 /* return success if we can open this file in this mode */
187 long cm_CheckNTOpen(cm_scache_t *scp, unsigned int desiredAccess,
188 unsigned int createDisp, cm_user_t *userp, cm_req_t *reqp,
189 cm_lock_data_t **ldpp)
194 osi_assertx(ldpp != NULL, "null cm_lock_data_t");
197 /* Always allow delete; the RPC will tell us if it's OK */
198 if (desiredAccess == DELETE)
203 if (desiredAccess & (AFS_ACCESS_READ|AFS_ACCESS_EXECUTE))
204 rights |= (scp->fileType == CM_SCACHETYPE_DIRECTORY ? PRSFS_LOOKUP : PRSFS_READ);
206 /* We used to require PRSFS_WRITE if createDisp was 4
207 (OPEN_ALWAYS) even if AFS_ACCESS_WRITE was not requested.
208 However, we don't need to do that since the existence of the
209 scp implies that we don't need to create it. */
210 if (desiredAccess & AFS_ACCESS_WRITE)
211 rights |= PRSFS_WRITE;
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))
275 case CM_ERROR_ALLOFFLINE:
276 case CM_ERROR_ALLDOWN:
277 case CM_ERROR_ALLBUSY:
278 case CM_ERROR_TIMEDOUT:
280 case CM_ERROR_WOULDBLOCK:
283 code = CM_ERROR_SHARING_VIOLATION;
287 } else if (code != 0) {
292 lock_ReleaseWrite(&scp->rw);
294 osi_Log3(afsd_logp,"cm_CheckNTOpen scp 0x%p ldp 0x%p code 0x%x", scp, *ldpp, code);
298 extern long cm_CheckNTOpenDone(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp,
299 cm_lock_data_t ** ldpp)
301 osi_Log2(afsd_logp,"cm_CheckNTOpenDone scp 0x%p ldp 0x%p", scp, *ldpp);
302 lock_ObtainWrite(&scp->rw);
304 cm_Unlock(scp, (*ldpp)->sLockType, (*ldpp)->LOffset, (*ldpp)->LLength,
305 (*ldpp)->key, userp, reqp);
309 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
310 lock_ReleaseWrite(&scp->rw);
314 * When CAP_NT_SMBS has been negotiated, deletion (of files or directories) is
315 * done in three steps:
316 * (1) open for deletion (NT_CREATE_AND_X)
317 * (2) set for deletion on close (NT_TRANSACTION2, SET_FILE_INFO)
319 * We must not do the RPC until step 3. But if we are going to return an error
320 * code (e.g. directory not empty), we must return it by step 2, otherwise most
321 * clients will not notice it. So we do a preliminary check. For deleting
322 * files, this is almost free, since we have already done the RPC to get the
323 * parent directory's status bits. But for deleting directories, we must do an
324 * additional RPC to get the directory's data to check if it is empty. Sigh.
326 long cm_CheckNTDelete(cm_scache_t *dscp, cm_scache_t *scp, cm_user_t *userp,
332 cm_dirEntry_t *dep = 0;
333 unsigned short *hashTable;
335 int BeyondPage = 0, HaveDot = 0, HaveDotDot = 0;
338 /* First check permissions */
339 lock_ObtainWrite(&scp->rw);
340 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_DELETE,
341 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
343 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
344 lock_ReleaseWrite(&scp->rw);
348 /* If deleting directory, must be empty */
350 if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
353 thyper.HighPart = 0; thyper.LowPart = 0;
354 code = buf_Get(scp, &thyper, &bufferp);
358 lock_ObtainMutex(&bufferp->mx);
359 lock_ObtainWrite(&scp->rw);
362 code = cm_SyncOp(scp, bufferp, userp, reqp, 0,
363 CM_SCACHESYNC_NEEDCALLBACK
365 | CM_SCACHESYNC_BUFLOCKED);
369 if (cm_HaveBuffer(scp, bufferp, 1))
372 /* otherwise, load the buffer and try again */
373 lock_ReleaseMutex(&bufferp->mx);
374 code = cm_GetBuffer(scp, bufferp, NULL, userp, reqp);
375 lock_ReleaseWrite(&scp->rw);
376 lock_ObtainMutex(&bufferp->mx);
377 lock_ObtainWrite(&scp->rw);
378 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
383 lock_ReleaseWrite(&scp->rw);
386 /* We try to determine emptiness without looking beyond the first page,
387 * and without assuming "." and ".." are present and are on the first
388 * page (though these assumptions might, after all, be reasonable).
390 hashTable = (unsigned short *)(bufferp->datap + (32 * 5));
391 for (i=0; i<128; i++) {
392 idx = ntohs(hashTable[i]);
398 dep = (cm_dirEntry_t *)(bufferp->datap + (32 * idx));
399 if (strcmp(dep->name, ".") == 0)
401 else if (strcmp(dep->name, "..") == 0)
404 code = CM_ERROR_NOTEMPTY;
407 idx = ntohs(dep->next);
410 if (BeyondPage && HaveDot && HaveDotDot)
411 code = CM_ERROR_NOTEMPTY;
415 lock_ReleaseMutex(&bufferp->mx);
416 buf_Release(bufferp);
418 lock_ReleaseWrite(&scp->rw);
423 * Iterate through all entries in a directory.
424 * When the function funcp is called, the buffer is locked but the
425 * directory vnode is not.
427 * If the retscp parameter is not NULL, the parmp must be a
428 * cm_lookupSearch_t object.
430 long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
431 osi_hyper_t *startOffsetp, cm_user_t *userp, cm_req_t *reqp,
432 cm_scache_t **retscp)
436 cm_dirEntry_t *dep = 0;
439 osi_hyper_t dirLength;
440 osi_hyper_t bufferOffset;
441 osi_hyper_t curOffset;
445 cm_pageHeader_t *pageHeaderp;
447 long nextEntryCookie;
448 int numDirChunks; /* # of 32 byte dir chunks in this entry */
450 /* get the directory size */
451 lock_ObtainWrite(&scp->rw);
452 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_LOOKUP,
453 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
454 lock_ReleaseWrite(&scp->rw);
458 if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
459 return CM_ERROR_NOTDIR;
461 if (retscp) /* if this is a lookup call */
463 cm_lookupSearch_t* sp = parmp;
466 #ifdef AFS_FREELANCE_CLIENT
467 /* Freelance entries never end up in the DNLC because they
468 * do not have an associated cm_server_t
470 !(cm_freelanceEnabled &&
471 sp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
472 sp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
473 #else /* !AFS_FREELANCE_CLIENT */
478 int casefold = sp->caseFold;
479 sp->caseFold = 0; /* we have a strong preference for exact matches */
480 if ( *retscp = cm_dnlcLookup(scp, sp)) /* dnlc hit */
482 sp->caseFold = casefold;
485 sp->caseFold = casefold;
487 /* see if we can find it using the directory hash tables.
488 we can only do exact matches, since the hash is case
498 code = cm_BeginDirOp(scp, userp, reqp, CM_DIRLOCK_READ, &dirop);
502 code = cm_BPlusDirLookup(&dirop, sp->nsearchNamep, &sp->fid);
507 code = cm_DirLookup(&dirop, sp->searchNamep, &sp->fid);
515 sp->ExactFound = TRUE;
516 *retscp = NULL; /* force caller to call cm_GetSCache() */
521 if (sp->caseFold && code == CM_ERROR_INEXACT_MATCH) {
524 sp->ExactFound = FALSE;
525 *retscp = NULL; /* force caller to call cm_GetSCache() */
529 return CM_ERROR_BPLUS_NOMATCH;
537 * XXX We only get the length once. It might change when we drop the
540 dirLength = scp->length;
543 bufferOffset.LowPart = bufferOffset.HighPart = 0;
545 curOffset = *startOffsetp;
547 curOffset.HighPart = 0;
548 curOffset.LowPart = 0;
552 /* make sure that curOffset.LowPart doesn't point to the first
553 * 32 bytes in the 2nd through last dir page, and that it
554 * doesn't point at the first 13 32-byte chunks in the first
555 * dir page, since those are dir and page headers, and don't
556 * contain useful information.
558 temp = curOffset.LowPart & (2048-1);
559 if (curOffset.HighPart == 0 && curOffset.LowPart < 2048) {
560 /* we're in the first page */
561 if (temp < 13*32) temp = 13*32;
564 /* we're in a later dir page */
565 if (temp < 32) temp = 32;
568 /* make sure the low order 5 bits are zero */
571 /* now put temp bits back ito curOffset.LowPart */
572 curOffset.LowPart &= ~(2048-1);
573 curOffset.LowPart |= temp;
575 /* check if we've passed the dir's EOF */
576 if (LargeIntegerGreaterThanOrEqualTo(curOffset, dirLength))
579 /* see if we can use the bufferp we have now; compute in which
580 * page the current offset would be, and check whether that's
581 * the offset of the buffer we have. If not, get the buffer.
583 thyper.HighPart = curOffset.HighPart;
584 thyper.LowPart = curOffset.LowPart & ~(cm_data.buf_blockSize-1);
585 if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
588 lock_ReleaseMutex(&bufferp->mx);
589 buf_Release(bufferp);
593 code = buf_Get(scp, &thyper, &bufferp);
595 /* if buf_Get() fails we do not have a buffer object to lock */
600 lock_ObtainMutex(&bufferp->mx);
601 bufferOffset = thyper;
603 /* now get the data in the cache */
605 lock_ObtainWrite(&scp->rw);
606 code = cm_SyncOp(scp, bufferp, userp, reqp,
608 CM_SCACHESYNC_NEEDCALLBACK
610 | CM_SCACHESYNC_BUFLOCKED);
612 lock_ReleaseWrite(&scp->rw);
615 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
617 if (cm_HaveBuffer(scp, bufferp, 1)) {
618 lock_ReleaseWrite(&scp->rw);
622 /* otherwise, load the buffer and try again */
623 lock_ReleaseMutex(&bufferp->mx);
624 code = cm_GetBuffer(scp, bufferp, NULL, userp,
626 lock_ReleaseWrite(&scp->rw);
627 lock_ObtainMutex(&bufferp->mx);
632 lock_ReleaseMutex(&bufferp->mx);
633 buf_Release(bufferp);
637 } /* if (wrong buffer) ... */
639 /* now we have the buffer containing the entry we're interested
640 * in; copy it out if it represents a non-deleted entry.
642 entryInDir = curOffset.LowPart & (2048-1);
643 entryInBuffer = curOffset.LowPart & (cm_data.buf_blockSize - 1);
645 /* page header will help tell us which entries are free. Page
646 * header can change more often than once per buffer, since
647 * AFS 3 dir page size may be less than (but not more than) a
648 * buffer package buffer.
650 /* only look intra-buffer */
651 temp = curOffset.LowPart & (cm_data.buf_blockSize - 1);
652 temp &= ~(2048 - 1); /* turn off intra-page bits */
653 pageHeaderp = (cm_pageHeader_t *) (bufferp->datap + temp);
655 /* now determine which entry we're looking at in the page. If
656 * it is free (there's a free bitmap at the start of the dir),
657 * we should skip these 32 bytes.
659 slotInPage = (entryInDir & 0x7e0) >> 5;
660 if (!(pageHeaderp->freeBitmap[slotInPage>>3]
661 & (1 << (slotInPage & 0x7)))) {
662 /* this entry is free */
663 numDirChunks = 1; /* only skip this guy */
667 tp = bufferp->datap + entryInBuffer;
668 dep = (cm_dirEntry_t *) tp; /* now points to AFS3 dir entry */
670 /* while we're here, compute the next entry's location, too,
671 * since we'll need it when writing out the cookie into the
672 * dir listing stream.
674 numDirChunks = cm_NameEntries(dep->name, NULL);
676 /* compute the offset of the cookie representing the next entry */
677 nextEntryCookie = curOffset.LowPart
678 + (CM_DIR_CHUNKSIZE * numDirChunks);
680 if (dep->fid.vnode != 0) {
681 /* this is one of the entries to use: it is not deleted */
682 code = (*funcp)(scp, dep, parmp, &curOffset);
685 } /* if we're including this name */
688 /* and adjust curOffset to be where the new cookie is */
690 thyper.LowPart = CM_DIR_CHUNKSIZE * numDirChunks;
691 curOffset = LargeIntegerAdd(thyper, curOffset);
692 } /* while copying data for dir listing */
694 /* release the mutex */
696 lock_ReleaseMutex(&bufferp->mx);
697 buf_Release(bufferp);
702 int cm_NoneUpper(normchar_t *s)
706 if (c >= 'A' && c <= 'Z')
711 int cm_NoneLower(normchar_t *s)
715 if (c >= 'a' && c <= 'z')
720 long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
723 cm_lookupSearch_t *sp;
725 normchar_t matchName[MAX_PATH];
726 int looking_for_short_name = FALSE;
728 sp = (cm_lookupSearch_t *) rockp;
730 if (cm_FsStringToNormString(dep->name, -1, matchName, lengthof(matchName)) == 0) {
731 /* Can't normalize FS string. */
736 match = cm_NormStrCmpI(matchName, sp->nsearchNamep);
738 match = cm_NormStrCmp(matchName, sp->nsearchNamep);
742 && !cm_Is8Dot3(matchName)) {
744 cm_Gen8Dot3NameInt(dep->name, &dep->fid, matchName, NULL);
746 match = cm_NormStrCmpI(matchName, sp->nsearchNamep);
748 match = cm_NormStrCmp(matchName, sp->nsearchNamep);
749 looking_for_short_name = TRUE;
759 if (!sp->caseFold || looking_for_short_name) {
760 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
761 return CM_ERROR_STOPNOW;
765 * If we get here, we are doing a case-insensitive search, and we
766 * have found a match. Now we determine what kind of match it is:
767 * exact, lower-case, upper-case, or none of the above. This is done
768 * in order to choose among matches, if there are more than one.
771 /* Exact matches are the best. */
772 match = cm_NormStrCmp(matchName, sp->nsearchNamep);
775 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
776 return CM_ERROR_STOPNOW;
779 /* Lower-case matches are next. */
782 if (cm_NoneUpper(matchName)) {
787 /* Upper-case matches are next. */
790 if (cm_NoneLower(matchName)) {
795 /* General matches are last. */
801 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
805 /* read the contents of a mount point into the appropriate string.
806 * called with write locked scp, and returns with locked scp.
808 long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
811 cm_buf_t *bufp = NULL;
815 if (scp->mountPointStringp[0])
818 #ifdef AFS_FREELANCE_CLIENT
819 /* File servers do not have data for freelance entries */
820 if (cm_freelanceEnabled &&
821 scp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
822 scp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
824 code = cm_FreelanceFetchMountPointString(scp);
826 #endif /* AFS_FREELANCE_CLIENT */
828 /* otherwise, we have to read it in */
829 lock_ReleaseWrite(&scp->rw);
831 thyper.LowPart = thyper.HighPart = 0;
832 code = buf_Get(scp, &thyper, &bufp);
834 lock_ObtainWrite(&scp->rw);
839 code = cm_SyncOp(scp, bufp, userp, reqp, 0,
840 CM_SCACHESYNC_READ | CM_SCACHESYNC_NEEDCALLBACK);
844 cm_SyncOpDone(scp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
846 if (cm_HaveBuffer(scp, bufp, 0))
849 /* otherwise load buffer */
850 code = cm_GetBuffer(scp, bufp, NULL, userp, reqp);
854 /* locked, has callback, has valid data in buffer */
855 if ((tlen = scp->length.LowPart) > MOUNTPOINTLEN - 1)
856 return CM_ERROR_TOOBIG;
858 code = CM_ERROR_INVAL;
862 /* someone else did the work while we were out */
863 if (scp->mountPointStringp[0]) {
868 /* otherwise, copy out the link */
869 memcpy(scp->mountPointStringp, bufp->datap, tlen);
871 /* now make it null-terminated. Note that the original contents of a
872 * link that is a mount point is "#volname." where "." is there just to
873 * be turned into a null. That is, we can trash the last char of the
874 * link without damaging the vol name. This is a stupid convention,
875 * but that's the protocol.
877 scp->mountPointStringp[tlen-1] = 0;
888 /* called with a locked scp and chases the mount point, yielding outScpp.
889 * scp remains write locked, just for simplicity of describing the interface.
891 long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
892 cm_req_t *reqp, cm_scache_t **outScpp)
894 fschar_t *cellNamep = NULL;
895 fschar_t *volNamep = NULL;
900 cm_volume_t *volp = NULL;
909 if (scp->mountRootFid.cell != 0 && scp->mountRootGen >= cm_data.mountRootGen) {
910 tfid = scp->mountRootFid;
911 lock_ReleaseWrite(&scp->rw);
912 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
913 lock_ObtainWrite(&scp->rw);
917 /* parse the volume name */
918 mpNamep = scp->mountPointStringp;
920 return CM_ERROR_NOSUCHPATH;
921 tlen = cm_FsStrLen(scp->mountPointStringp);
922 mtType = *scp->mountPointStringp;
924 cp = cm_FsStrChr(mpNamep, _FS(':'));
926 /* cellular mount point */
927 cellNamep = (fschar_t *)malloc((cp - mpNamep) * sizeof(fschar_t));
928 cm_FsStrCpyN(cellNamep, cp - mpNamep, mpNamep + 1, cp - mpNamep - 1);
929 volNamep = cm_FsStrDup(cp+1);
931 /* now look up the cell */
932 lock_ReleaseWrite(&scp->rw);
933 cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE);
934 lock_ObtainWrite(&scp->rw);
937 volNamep = cm_FsStrDup(mpNamep + 1);
939 cellp = cm_FindCellByID(scp->fid.cell, 0);
943 code = CM_ERROR_NOSUCHCELL;
947 vnLength = cm_FsStrLen(volNamep);
948 if (vnLength >= 8 && cm_FsStrCmp(volNamep + vnLength - 7, ".backup") == 0)
949 targetType = BACKVOL;
950 else if (vnLength >= 10
951 && cm_FsStrCmp(volNamep + vnLength - 9, ".readonly") == 0)
956 /* check for backups within backups */
957 if (targetType == BACKVOL
958 && (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
959 == CM_SCACHEFLAG_RO) {
960 code = CM_ERROR_NOSUCHVOLUME;
964 /* now we need to get the volume */
965 lock_ReleaseWrite(&scp->rw);
966 if (cm_VolNameIsID(volNamep)) {
967 code = cm_FindVolumeByID(cellp, atoi(volNamep), userp, reqp,
968 CM_GETVOL_FLAG_CREATE, &volp);
970 code = cm_FindVolumeByName(cellp, volNamep, userp, reqp,
971 CM_GETVOL_FLAG_CREATE, &volp);
973 lock_ObtainWrite(&scp->rw);
976 afs_uint32 cell, volume;
977 cm_vol_state_t *statep;
979 cell = cellp->cellID;
981 /* if the mt pt originates in a .backup volume (not a .readonly)
982 * and FollowBackupPath is active, and if there is a .backup
983 * volume for the target, then use the .backup of the target
984 * instead of the read-write.
986 if (cm_followBackupPath &&
987 volp->vol[BACKVOL].ID != 0 &&
988 (dscp->flags & (CM_SCACHEFLAG_RO|CM_SCACHEFLAG_PURERO)) == CM_SCACHEFLAG_RO &&
989 (targetType == RWVOL || targetType == ROVOL && volp->vol[ROVOL].ID == 0)
991 targetType = BACKVOL;
993 /* if the mt pt is in a read-only volume (not just a
994 * backup), and if there is a read-only volume for the
995 * target, and if this is a targetType '#' mount point, use
996 * the read-only, otherwise use the one specified.
998 else if (mtType == '#' && targetType == RWVOL &&
999 (scp->flags & CM_SCACHEFLAG_PURERO) &&
1000 volp->vol[ROVOL].ID != 0) {
1004 lock_ObtainWrite(&volp->rw);
1005 statep = cm_VolumeStateByType(volp, targetType);
1006 volume = statep->ID;
1007 statep->dotdotFid = dscp->fid;
1008 lock_ReleaseWrite(&volp->rw);
1010 /* the rest of the fid is a magic number */
1011 cm_SetFid(&scp->mountRootFid, cell, volume, 1, 1);
1012 scp->mountRootGen = cm_data.mountRootGen;
1014 tfid = scp->mountRootFid;
1015 lock_ReleaseWrite(&scp->rw);
1016 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
1017 lock_ObtainWrite(&scp->rw);
1030 long cm_LookupInternal(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_user_t *userp,
1031 cm_req_t *reqp, cm_scache_t **outScpp)
1034 int dnlcHit = 1; /* did we hit in the dnlc? yes, we did */
1035 cm_scache_t *tscp = NULL;
1036 cm_scache_t *mountedScp;
1037 cm_lookupSearch_t rock;
1039 normchar_t *nnamep = NULL;
1040 fschar_t *fnamep = NULL;
1044 memset(&rock, 0, sizeof(rock));
1046 if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
1047 && cm_ClientStrCmp(cnamep, _C("..")) == 0) {
1048 if (dscp->dotdotFid.volume == 0)
1049 return CM_ERROR_NOSUCHVOLUME;
1050 rock.fid = dscp->dotdotFid;
1052 } else if (cm_ClientStrCmp(cnamep, _C(".")) == 0) {
1053 rock.fid = dscp->fid;
1057 nnamep = cm_ClientStringToNormStringAlloc(cnamep, -1, NULL);
1059 code = CM_ERROR_NOSUCHFILE;
1062 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
1064 code = CM_ERROR_NOSUCHFILE;
1068 if (flags & CM_FLAG_NOMOUNTCHASE) {
1069 /* In this case, we should go and call cm_Dir* functions
1070 directly since the following cm_ApplyDir() function will
1078 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ, &dirop);
1081 code = cm_BPlusDirLookup(&dirop, nnamep, &rock.fid);
1086 code = cm_DirLookup(&dirop, fnamep, &rock.fid);
1088 cm_EndDirOp(&dirop);
1098 if (code == CM_ERROR_INEXACT_MATCH && (flags & CM_FLAG_CASEFOLD)) {
1105 return CM_ERROR_BPLUS_NOMATCH;
1110 rock.fid.cell = dscp->fid.cell;
1111 rock.fid.volume = dscp->fid.volume;
1112 rock.searchNamep = fnamep;
1113 rock.nsearchNamep = nnamep;
1114 rock.caseFold = (flags & CM_FLAG_CASEFOLD);
1115 rock.hasTilde = ((cm_ClientStrChr(cnamep, '~') != NULL) ? 1 : 0);
1117 /* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
1118 code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
1119 (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
1121 /* code == 0 means we fell off the end of the dir, while stopnow means
1122 * that we stopped early, probably because we found the entry we're
1123 * looking for. Any other non-zero code is an error.
1125 if (code && code != CM_ERROR_STOPNOW) {
1126 /* if the cm_scache_t we are searching in is not a directory
1127 * we must return path not found because the error
1128 * is to describe the final component not an intermediary
1130 if (code == CM_ERROR_NOTDIR) {
1131 if (flags & CM_FLAG_CHECKPATH)
1132 code = CM_ERROR_NOSUCHPATH;
1134 code = CM_ERROR_NOSUCHFILE;
1139 getroot = (dscp==cm_data.rootSCachep) ;
1141 if (!cm_freelanceEnabled || !getroot) {
1142 if (flags & CM_FLAG_CHECKPATH)
1143 code = CM_ERROR_NOSUCHPATH;
1145 code = CM_ERROR_NOSUCHFILE;
1148 else if (!cm_ClientStrChr(cnamep, '#') &&
1149 !cm_ClientStrChr(cnamep, '%') &&
1150 cm_ClientStrCmpI(cnamep, _C("srvsvc")) &&
1151 cm_ClientStrCmpI(cnamep, _C("wkssvc")) &&
1152 cm_ClientStrCmpI(cnamep, _C("ipc$")))
1154 /* nonexistent dir on freelance root, so add it */
1155 fschar_t fullname[CELL_MAXNAMELEN + 1] = "."; /* +1 so that when we skip the . the size is still CELL_MAXNAMELEN */
1158 osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %S",
1159 osi_LogSaveClientString(afsd_logp,cnamep));
1162 * There is an ugly behavior where a share name "foo" will be searched
1163 * for as "fo". If the searched for name differs by an already existing
1164 * symlink or mount point in the Freelance directory, do not add the
1165 * new value automatically.
1169 if (cnamep[0] == '.') {
1170 if (cm_GetCell_Gen(&fnamep[1], &fullname[1], CM_FLAG_CREATE)) {
1172 if (!cm_FreelanceMountPointExists(fullname, 0))
1173 code = cm_FreelanceAddMount(fullname, &fullname[1], "root.cell.",
1175 if ( cm_FsStrCmpI(&fnamep[1], &fullname[1]) &&
1176 !cm_FreelanceMountPointExists(fnamep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0) &&
1177 !cm_FreelanceSymlinkExists(fnamep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0))
1178 code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
1181 if (cm_GetCell_Gen(fnamep, fullname, CM_FLAG_CREATE)) {
1183 if (!cm_FreelanceMountPointExists(fullname, 0))
1184 code = cm_FreelanceAddMount(fullname, fullname, "root.cell.", 0, &rock.fid);
1185 if ( cm_FsStrCmpI(fnamep, fullname) &&
1186 !cm_FreelanceMountPointExists(fnamep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0) &&
1187 !cm_FreelanceSymlinkExists(fnamep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0))
1188 code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
1191 if (!found || code) { /* add mount point failed, so give up */
1192 if (flags & CM_FLAG_CHECKPATH)
1193 code = CM_ERROR_NOSUCHPATH;
1195 code = CM_ERROR_NOSUCHFILE;
1198 tscp = NULL; /* to force call of cm_GetSCache */
1200 if (flags & CM_FLAG_CHECKPATH)
1201 code = CM_ERROR_NOSUCHPATH;
1203 code = CM_ERROR_NOSUCHFILE;
1209 if ( !tscp ) /* we did not find it in the dnlc */
1212 code = cm_GetSCache(&rock.fid, &tscp, userp, reqp);
1216 /* tscp is now held */
1218 lock_ObtainWrite(&tscp->rw);
1219 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1220 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1222 lock_ReleaseWrite(&tscp->rw);
1223 cm_ReleaseSCache(tscp);
1226 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1227 /* tscp is now locked */
1229 if (!(flags & CM_FLAG_NOMOUNTCHASE)
1230 && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
1231 /* mount points are funny: they have a volume name to mount
1234 code = cm_ReadMountPoint(tscp, userp, reqp);
1236 code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
1238 lock_ReleaseWrite(&tscp->rw);
1239 cm_ReleaseSCache(tscp);
1246 lock_ReleaseWrite(&tscp->rw);
1249 /* copy back pointer */
1252 /* insert scache in dnlc */
1253 if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
1254 /* lock the directory entry to prevent racing callback revokes */
1255 lock_ObtainRead(&dscp->rw);
1256 if ( dscp->cbServerp != NULL && dscp->cbExpires > 0 ) {
1257 /* TODO: reuse nnamep from above */
1260 nnamep = cm_ClientStringToNormStringAlloc(cnamep, -1, NULL);
1262 cm_dnlcEnter(dscp, nnamep, tscp);
1264 lock_ReleaseRead(&dscp->rw);
1281 int cm_ExpandSysName(clientchar_t *inp, clientchar_t *outp, long outSizeCch, unsigned int index)
1286 tp = cm_ClientStrRChr(inp, '@');
1288 return 0; /* no @sys */
1290 if (cm_ClientStrCmp(tp, _C("@sys")) != 0)
1291 return 0; /* no @sys */
1293 /* caller just wants to know if this is a valid @sys type of name */
1297 if (index >= cm_sysNameCount)
1300 /* otherwise generate the properly expanded @sys name */
1301 prefixCount = (int)(tp - inp);
1303 cm_ClientStrCpyN(outp, outSizeCch, inp, prefixCount); /* copy out "a." from "a.@sys" */
1304 outp[prefixCount] = 0; /* null terminate the "a." */
1305 cm_ClientStrCat(outp, outSizeCch, cm_sysNameList[index]);/* append i386_nt40 */
1309 long cm_EvaluateVolumeReference(clientchar_t * namep, long flags, cm_user_t * userp,
1310 cm_req_t *reqp, cm_scache_t ** outScpp)
1312 afs_uint32 code = 0;
1313 fschar_t cellName[CELL_MAXNAMELEN];
1314 fschar_t volumeName[VL_MAXNAMELEN];
1318 fschar_t * fnamep = NULL;
1320 cm_cell_t * cellp = NULL;
1321 cm_volume_t * volp = NULL;
1325 int mountType = RWVOL;
1327 osi_Log1(afsd_logp, "cm_EvaluateVolumeReference for string [%S]",
1328 osi_LogSaveClientString(afsd_logp, namep));
1330 if (cm_ClientStrCmpNI(namep, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH) != 0) {
1331 goto _exit_invalid_path;
1334 /* namep is assumed to look like the following:
1336 @vol:<cellname>%<volume>\0
1338 @vol:<cellname>#<volume>\0
1342 fnamep = cm_ClientStringToFsStringAlloc(namep, -1, NULL);
1343 cp = fnamep + CM_PREFIX_VOL_CCH; /* cp points to cell name, hopefully */
1344 tp = cm_FsStrChr(cp, '%');
1346 tp = cm_FsStrChr(cp, '#');
1348 (len = tp - cp) == 0 ||
1349 len > CELL_MAXNAMELEN)
1350 goto _exit_invalid_path;
1351 cm_FsStrCpyN(cellName, lengthof(cellName), cp, len);
1356 cp = tp+1; /* cp now points to volume, supposedly */
1357 cm_FsStrCpy(volumeName, lengthof(volumeName), cp);
1359 /* OK, now we have the cell and the volume */
1360 osi_Log2(afsd_logp, " Found cell [%s] and volume [%s]",
1361 osi_LogSaveFsString(afsd_logp, cellName),
1362 osi_LogSaveFsString(afsd_logp, volumeName));
1364 cellp = cm_GetCell(cellName, CM_FLAG_CREATE);
1365 if (cellp == NULL) {
1366 goto _exit_invalid_path;
1369 len = cm_FsStrLen(volumeName);
1370 if (len >= 8 && cm_FsStrCmp(volumeName + len - 7, ".backup") == 0)
1372 else if (len >= 10 &&
1373 cm_FsStrCmp(volumeName + len - 9, ".readonly") == 0)
1378 if (cm_VolNameIsID(volumeName)) {
1379 code = cm_FindVolumeByID(cellp, atoi(volumeName), userp, reqp,
1380 CM_GETVOL_FLAG_CREATE, &volp);
1382 code = cm_FindVolumeByName(cellp, volumeName, userp, reqp,
1383 CM_GETVOL_FLAG_CREATE, &volp);
1389 if (volType == BACKVOL)
1390 volume = volp->vol[BACKVOL].ID;
1391 else if (volType == ROVOL ||
1392 (volType == RWVOL && mountType == ROVOL && volp->vol[ROVOL].ID != 0))
1393 volume = volp->vol[ROVOL].ID;
1395 volume = volp->vol[RWVOL].ID;
1397 cm_SetFid(&fid, cellp->cellID, volume, 1, 1);
1399 code = cm_GetSCache(&fid, outScpp, userp, reqp);
1412 if (flags & CM_FLAG_CHECKPATH)
1413 return CM_ERROR_NOSUCHPATH;
1415 return CM_ERROR_NOSUCHFILE;
1418 #ifdef DEBUG_REFCOUNT
1419 long cm_LookupDbg(cm_scache_t *dscp, clientchar_t *namep, long flags, cm_user_t *userp,
1420 cm_req_t *reqp, cm_scache_t **outScpp, char * file, long line)
1422 long cm_Lookup(cm_scache_t *dscp, clientchar_t *namep, long flags, cm_user_t *userp,
1423 cm_req_t *reqp, cm_scache_t **outScpp)
1427 clientchar_t tname[AFSPATHMAX];
1428 int sysNameIndex = 0;
1429 cm_scache_t *scp = NULL;
1431 #ifdef DEBUG_REFCOUNT
1432 afsi_log("%s:%d cm_Lookup dscp 0x%p ref %d", file, line, dscp, dscp->refCount, file, line);
1433 osi_Log2(afsd_logp, "cm_Lookup dscp 0x%p ref %d", dscp, dscp->refCount);
1436 if ( cm_ClientStrCmpI(namep,_C(SMB_IOCTL_FILENAME_NOSLASH)) == 0 ) {
1437 if (flags & CM_FLAG_CHECKPATH)
1438 return CM_ERROR_NOSUCHPATH;
1440 return CM_ERROR_NOSUCHFILE;
1443 if (dscp == cm_data.rootSCachep &&
1444 cm_ClientStrCmpNI(namep, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH) == 0) {
1445 return cm_EvaluateVolumeReference(namep, flags, userp, reqp, outScpp);
1448 if (cm_ExpandSysName(namep, NULL, 0, 0) > 0) {
1449 for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
1450 code = cm_ExpandSysName(namep, tname, lengthof(tname), sysNameIndex);
1452 code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
1453 #ifdef DEBUG_REFCOUNT
1454 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);
1455 osi_Log3(afsd_logp, "cm_LookupInternal (1) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1463 cm_ReleaseSCache(scp);
1467 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1468 #ifdef DEBUG_REFCOUNT
1469 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);
1470 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1477 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1478 #ifdef DEBUG_REFCOUNT
1479 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);
1480 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1486 /* None of the possible sysName expansions could be found */
1487 if (flags & CM_FLAG_CHECKPATH)
1488 return CM_ERROR_NOSUCHPATH;
1490 return CM_ERROR_NOSUCHFILE;
1493 /*! \brief Unlink a file name
1495 Encapsulates a call to RXAFS_RemoveFile().
1497 \param[in] dscp cm_scache_t pointing at the directory containing the
1498 name to be unlinked.
1500 \param[in] fnamep Original name to be unlinked. This is the
1501 name that will be passed into the RXAFS_RemoveFile() call.
1502 This parameter is optional. If not provided, the value will
1505 \param[in] came Client name to be unlinked. This name will be used
1506 to update the local directory caches.
1508 \param[in] userp cm_user_t for the request.
1510 \param[in] reqp Request tracker.
1513 long cm_Unlink(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t * cnamep,
1514 cm_user_t *userp, cm_req_t *reqp)
1520 AFSFetchStatus newDirStatus;
1522 struct rx_connection * rxconnp;
1524 cm_scache_t *scp = NULL;
1525 int free_fnamep = FALSE;
1527 if (fnamep == NULL) {
1530 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ, &dirop);
1532 code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
1535 cm_EndDirOp(&dirop);
1542 #ifdef AFS_FREELANCE_CLIENT
1543 if (cm_freelanceEnabled && dscp == cm_data.rootSCachep) {
1544 /* deleting a mount point from the root dir. */
1545 code = cm_FreelanceRemoveMount(fnamep);
1550 code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
1552 /* make sure we don't screw up the dir status during the merge */
1553 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
1555 lock_ObtainWrite(&dscp->rw);
1556 sflags = CM_SCACHESYNC_STOREDATA;
1557 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1558 lock_ReleaseWrite(&dscp->rw);
1560 cm_EndDirOp(&dirop);
1565 afsFid.Volume = dscp->fid.volume;
1566 afsFid.Vnode = dscp->fid.vnode;
1567 afsFid.Unique = dscp->fid.unique;
1569 osi_Log1(afsd_logp, "CALL RemoveFile scp 0x%p", dscp);
1571 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
1575 rxconnp = cm_GetRxConn(connp);
1576 code = RXAFS_RemoveFile(rxconnp, &afsFid, fnamep,
1577 &newDirStatus, &volSync);
1578 rx_PutConnection(rxconnp);
1580 } while (cm_Analyze(connp, userp, reqp, &dscp->fid, &volSync, NULL, NULL, code));
1581 code = cm_MapRPCError(code, reqp);
1584 osi_Log1(afsd_logp, "CALL RemoveFile FAILURE, code 0x%x", code);
1586 osi_Log0(afsd_logp, "CALL RemoveFile SUCCESS");
1589 lock_ObtainWrite(&dirop.scp->dirlock);
1590 dirop.lockType = CM_DIRLOCK_WRITE;
1592 lock_ObtainWrite(&dscp->rw);
1593 cm_dnlcRemove(dscp, cnamep);
1594 cm_SyncOpDone(dscp, NULL, sflags);
1596 cm_MergeStatus(NULL, dscp, &newDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
1597 } else if (code == CM_ERROR_NOSUCHFILE) {
1598 /* windows would not have allowed the request to delete the file
1599 * if it did not believe the file existed. therefore, we must
1600 * have an inconsistent view of the world.
1602 dscp->cbServerp = NULL;
1604 lock_ReleaseWrite(&dscp->rw);
1606 if (code == 0 && cm_CheckDirOpForSingleChange(&dirop) && cnamep) {
1607 cm_DirDeleteEntry(&dirop, fnamep);
1609 cm_BPlusDirDeleteEntry(&dirop, cnamep);
1612 cm_EndDirOp(&dirop);
1615 cm_ReleaseSCache(scp);
1617 lock_ObtainWrite(&scp->rw);
1618 scp->flags |= CM_SCACHEFLAG_DELETED;
1619 lock_ReleaseWrite(&scp->rw);
1630 /* called with a write locked vnode, and fills in the link info.
1631 * returns this the vnode still write locked.
1633 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1640 lock_AssertWrite(&linkScp->rw);
1641 if (!linkScp->mountPointStringp[0]) {
1643 #ifdef AFS_FREELANCE_CLIENT
1644 /* File servers do not have data for freelance entries */
1645 if (cm_freelanceEnabled &&
1646 linkScp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
1647 linkScp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
1649 code = cm_FreelanceFetchMountPointString(linkScp);
1651 #endif /* AFS_FREELANCE_CLIENT */
1653 /* read the link data from the file server*/
1654 lock_ReleaseWrite(&linkScp->rw);
1655 thyper.LowPart = thyper.HighPart = 0;
1656 code = buf_Get(linkScp, &thyper, &bufp);
1657 lock_ObtainWrite(&linkScp->rw);
1661 code = cm_SyncOp(linkScp, bufp, userp, reqp, 0,
1662 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1667 cm_SyncOpDone(linkScp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1669 if (cm_HaveBuffer(linkScp, bufp, 0))
1672 code = cm_GetBuffer(linkScp, bufp, NULL, userp, reqp);
1677 } /* while loop to get the data */
1679 /* now if we still have no link read in,
1680 * copy the data from the buffer */
1681 if ((temp = linkScp->length.LowPart) >= MOUNTPOINTLEN) {
1683 return CM_ERROR_TOOBIG;
1686 /* otherwise, it fits; make sure it is still null (could have
1687 * lost race with someone else referencing this link above),
1688 * and if so, copy in the data.
1690 if (!linkScp->mountPointStringp[0]) {
1691 strncpy(linkScp->mountPointStringp, bufp->datap, temp);
1692 linkScp->mountPointStringp[temp] = 0; /* null terminate */
1697 if ( !strnicmp(linkScp->mountPointStringp, "msdfs:", strlen("msdfs:")) )
1698 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1700 } /* don't have sym link contents cached */
1705 /* called with a held vnode and a path suffix, with the held vnode being a
1706 * symbolic link. Our goal is to generate a new path to interpret, and return
1707 * this new path in newSpaceBufferp. If the new vnode is relative to a dir
1708 * other than the directory containing the symbolic link, then the new root is
1709 * returned in *newRootScpp, otherwise a null is returned there.
1711 long cm_AssembleLink(cm_scache_t *linkScp, fschar_t *pathSuffixp,
1712 cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1713 cm_user_t *userp, cm_req_t *reqp)
1720 *newRootScpp = NULL;
1721 *newSpaceBufferp = NULL;
1723 lock_ObtainWrite(&linkScp->rw);
1724 code = cm_HandleLink(linkScp, userp, reqp);
1728 /* if we may overflow the buffer, bail out; buffer is signficantly
1729 * bigger than max path length, so we don't really have to worry about
1730 * being a little conservative here.
1732 if (cm_FsStrLen(linkScp->mountPointStringp) + cm_FsStrLen(pathSuffixp) + 2
1733 >= CM_UTILS_SPACESIZE) {
1734 code = CM_ERROR_TOOBIG;
1738 tsp = cm_GetSpace();
1739 linkp = linkScp->mountPointStringp;
1740 if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1741 if (strlen(linkp) > cm_mountRootLen)
1742 StringCbCopyA((char *) tsp->data, sizeof(tsp->data), linkp+cm_mountRootLen+1);
1745 *newRootScpp = cm_data.rootSCachep;
1746 cm_HoldSCache(cm_data.rootSCachep);
1747 } else if (linkp[0] == '\\' && linkp[1] == '\\') {
1748 if (!strnicmp(&linkp[2], cm_NetbiosName, (len = (long)strlen(cm_NetbiosName))))
1750 char * p = &linkp[len + 3];
1751 if (strnicmp(p, "all", 3) == 0)
1754 StringCbCopyA(tsp->data, sizeof(tsp->data), p);
1755 for (p = tsp->data; *p; p++) {
1759 *newRootScpp = cm_data.rootSCachep;
1760 cm_HoldSCache(cm_data.rootSCachep);
1762 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1763 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1764 code = CM_ERROR_PATH_NOT_COVERED;
1766 } else if ( linkScp->fileType == CM_SCACHETYPE_DFSLINK ||
1767 !strnicmp(linkp, "msdfs:", (len = (long)strlen("msdfs:"))) ) {
1768 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1769 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1770 code = CM_ERROR_PATH_NOT_COVERED;
1771 } else if (*linkp == '\\' || *linkp == '/') {
1773 /* formerly, this was considered to be from the AFS root,
1774 * but this seems to create problems. instead, we will just
1775 * reject the link */
1776 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp+1);
1777 *newRootScpp = cm_data.rootSCachep;
1778 cm_HoldSCache(cm_data.rootSCachep);
1780 /* we still copy the link data into the response so that
1781 * the user can see what the link points to
1783 linkScp->fileType = CM_SCACHETYPE_INVALID;
1784 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1785 code = CM_ERROR_NOSUCHPATH;
1788 /* a relative link */
1789 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1791 if (pathSuffixp[0] != 0) { /* if suffix string is non-null */
1792 StringCchCatA(tsp->data,lengthof(tsp->data), "\\");
1793 StringCchCatA(tsp->data,lengthof(tsp->data), pathSuffixp);
1797 clientchar_t * cpath = cm_FsStringToClientStringAlloc(tsp->data, -1, NULL);
1798 if (cpath != NULL) {
1799 cm_ClientStrCpy(tsp->wdata, lengthof(tsp->wdata), cpath);
1801 *newSpaceBufferp = tsp;
1803 code = CM_ERROR_NOSUCHPATH;
1810 if (code == CM_ERROR_PATH_NOT_COVERED && reqp->tidPathp && reqp->relPathp) {
1811 cm_VolStatus_Notify_DFS_Mapping(linkScp, reqp->tidPathp, reqp->relPathp);
1816 lock_ReleaseWrite(&linkScp->rw);
1819 #ifdef DEBUG_REFCOUNT
1820 long cm_NameIDbg(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
1821 cm_user_t *userp, clientchar_t *tidPathp, cm_req_t *reqp,
1822 cm_scache_t **outScpp,
1823 char * file, long line)
1825 long cm_NameI(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
1826 cm_user_t *userp, clientchar_t *tidPathp,
1827 cm_req_t *reqp, cm_scache_t **outScpp)
1831 clientchar_t *tp; /* ptr moving through input buffer */
1832 clientchar_t tc; /* temp char */
1833 int haveComponent; /* has new component started? */
1834 clientchar_t component[AFSPATHMAX]; /* this is the new component */
1835 clientchar_t *cp; /* component name being assembled */
1836 cm_scache_t *tscp; /* current location in the hierarchy */
1837 cm_scache_t *nscp; /* next dude down */
1838 cm_scache_t *dirScp; /* last dir we searched */
1839 cm_scache_t *linkScp; /* new root for the symlink we just
1841 cm_space_t *psp; /* space for current path, if we've hit
1843 cm_space_t *tempsp; /* temp vbl */
1844 clientchar_t *restp; /* rest of the pathname to interpret */
1845 int symlinkCount; /* count of # of symlinks traversed */
1846 int extraFlag; /* avoid chasing mt pts for dir cmd */
1847 int phase = 1; /* 1 = tidPathp, 2 = pathp */
1848 #define MAX_FID_COUNT 512
1849 cm_fid_t fids[MAX_FID_COUNT]; /* array of fids processed in this path walk */
1850 int fid_count = 0; /* number of fids processed in this path walk */
1855 #ifdef DEBUG_REFCOUNT
1856 afsi_log("%s:%d cm_NameI rootscp 0x%p ref %d", file, line, rootSCachep, rootSCachep->refCount);
1857 osi_Log4(afsd_logp,"cm_NameI rootscp 0x%p path %S tidpath %S flags 0x%x",
1858 rootSCachep, pathp ? pathp : L"<NULL>", tidPathp ? tidPathp : L"<NULL>",
1873 cm_HoldSCache(tscp);
1881 /* map Unix slashes into DOS ones so we can interpret Unix
1887 if (!haveComponent) {
1890 } else if (tc == 0) {
1904 /* we have a component here */
1905 if (tc == 0 || tc == '\\') {
1906 /* end of the component; we're at the last
1907 * component if tc == 0. However, if the last
1908 * is a symlink, we have more to do.
1910 *cp++ = 0; /* add null termination */
1912 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
1913 extraFlag = CM_FLAG_NOMOUNTCHASE;
1914 code = cm_Lookup(tscp, component,
1916 userp, reqp, &nscp);
1919 if (!cm_ClientStrCmp(component,_C("..")) ||
1920 !cm_ClientStrCmp(component,_C("."))) {
1922 * roll back the fid list until we find the
1923 * fid that matches where we are now. Its not
1924 * necessarily one or two fids because they
1925 * might have been symlinks or mount points or
1926 * both that were crossed.
1928 for ( i=fid_count-1; i>=0; i--) {
1929 if (!cm_FidCmp(&nscp->fid, &fids[i]))
1934 /* add the new fid to the list */
1935 for ( i=0; i<fid_count; i++) {
1936 if ( !cm_FidCmp(&nscp->fid, &fids[i]) ) {
1937 code = CM_ERROR_TOO_MANY_SYMLINKS;
1938 cm_ReleaseSCache(nscp);
1943 if (i == fid_count && fid_count < MAX_FID_COUNT) {
1944 fids[fid_count++] = nscp->fid;
1950 cm_ReleaseSCache(tscp);
1952 cm_ReleaseSCache(dirScp);
1955 if ((code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH) &&
1956 tscp->fileType == CM_SCACHETYPE_SYMLINK) {
1957 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_NOSUCHPATH");
1958 return CM_ERROR_NOSUCHPATH;
1960 osi_Log1(afsd_logp,"cm_NameI code 0x%x", code);
1965 haveComponent = 0; /* component done */
1967 cm_ReleaseSCache(dirScp);
1968 dirScp = tscp; /* for some symlinks */
1969 tscp = nscp; /* already held */
1971 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
1974 cm_ReleaseSCache(dirScp);
1980 /* now, if tscp is a symlink, we should follow it and
1981 * assemble the path again.
1983 lock_ObtainWrite(&tscp->rw);
1984 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1985 CM_SCACHESYNC_GETSTATUS
1986 | CM_SCACHESYNC_NEEDCALLBACK);
1988 lock_ReleaseWrite(&tscp->rw);
1989 cm_ReleaseSCache(tscp);
1992 cm_ReleaseSCache(dirScp);
1997 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1999 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
2000 /* this is a symlink; assemble a new buffer */
2001 lock_ReleaseWrite(&tscp->rw);
2002 if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
2003 cm_ReleaseSCache(tscp);
2006 cm_ReleaseSCache(dirScp);
2011 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_TOO_MANY_SYMLINKS");
2012 return CM_ERROR_TOO_MANY_SYMLINKS;
2022 /* TODO: make this better */
2023 frestp = cm_ClientStringToFsStringAlloc(restp, -1, NULL);
2024 code = cm_AssembleLink(tscp, frestp, &linkScp, &tempsp, userp, reqp);
2028 if (code == 0 && linkScp != NULL) {
2029 if (linkScp == cm_data.rootSCachep)
2032 for ( i=0; i<fid_count; i++) {
2033 if ( !cm_FidCmp(&linkScp->fid, &fids[i]) ) {
2034 code = CM_ERROR_TOO_MANY_SYMLINKS;
2035 cm_ReleaseSCache(linkScp);
2041 if (i == fid_count && fid_count < MAX_FID_COUNT) {
2042 fids[fid_count++] = linkScp->fid;
2047 /* something went wrong */
2048 cm_ReleaseSCache(tscp);
2051 cm_ReleaseSCache(dirScp);
2057 /* otherwise, tempsp has the new path,
2058 * and linkScp is the new root from
2059 * which to interpret that path.
2060 * Continue with the namei processing,
2061 * also doing the bookkeeping for the
2062 * space allocation and tracking the
2063 * vnode reference counts.
2069 cm_ReleaseSCache(tscp);
2074 * now, if linkScp is null, that's
2075 * AssembleLink's way of telling us that
2076 * the sym link is relative to the dir
2077 * containing the link. We have a ref
2078 * to it in dirScp, and we hold it now
2079 * and reuse it as the new spot in the
2087 /* not a symlink, we may be done */
2088 lock_ReleaseWrite(&tscp->rw);
2096 cm_ReleaseSCache(dirScp);
2104 cm_ReleaseSCache(dirScp);
2107 } /* end of a component */
2110 } /* we have a component */
2111 } /* big while loop over all components */
2115 cm_ReleaseSCache(dirScp);
2121 cm_ReleaseSCache(tscp);
2123 #ifdef DEBUG_REFCOUNT
2124 afsi_log("%s:%d cm_NameI code 0x%x outScpp 0x%p ref %d", file, line, code, *outScpp, (*outScpp) ? (*outScpp)->refCount : 0);
2126 osi_Log2(afsd_logp,"cm_NameI code 0x%x outScpp 0x%p", code, *outScpp);
2130 /* called with a dir, and a vnode within the dir that happens to be a symlink.
2131 * We chase the link, and return a held pointer to the target, if it exists,
2132 * in *outScpp. If we succeed, we return 0, otherwise we return an error code
2133 * and do not hold or return a target vnode.
2135 * This is very similar to calling cm_NameI with the last component of a name,
2136 * which happens to be a symlink, except that we've already passed by the name.
2138 * This function is typically called by the directory listing functions, which
2139 * encounter symlinks but need to return the proper file length so programs
2140 * like "more" work properly when they make use of the attributes retrieved from
2143 * The input vnode should not be locked when this function is called.
2145 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
2146 cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
2150 cm_scache_t *newRootScp;
2154 osi_Log1(afsd_logp, "Evaluating symlink scp 0x%p", linkScp);
2156 code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
2160 /* now, if newRootScp is NULL, we're really being told that the symlink
2161 * is relative to the current directory (dscp).
2163 if (newRootScp == NULL) {
2165 cm_HoldSCache(dscp);
2168 code = cm_NameI(newRootScp, spacep->wdata,
2169 CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
2170 userp, NULL, reqp, outScpp);
2172 if (code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH)
2173 code = CM_ERROR_NOSUCHPATH;
2175 /* this stuff is allocated no matter what happened on the namei call,
2177 cm_FreeSpace(spacep);
2178 cm_ReleaseSCache(newRootScp);
2180 if (linkScp == *outScpp) {
2181 cm_ReleaseSCache(*outScpp);
2183 code = CM_ERROR_NOSUCHPATH;
2189 /* for a given entry, make sure that it isn't in the stat cache, and then
2190 * add it to the list of file IDs to be obtained.
2192 * Don't bother adding it if we already have a vnode. Note that the dir
2193 * is locked, so we have to be careful checking the vnode we're thinking of
2194 * processing, to avoid deadlocks.
2196 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
2207 /* Don't overflow bsp. */
2208 if (bsp->counter >= CM_BULKMAX)
2209 return CM_ERROR_STOPNOW;
2211 thyper.LowPart = cm_data.buf_blockSize;
2212 thyper.HighPart = 0;
2213 thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
2215 /* thyper is now the first byte past the end of the record we're
2216 * interested in, and bsp->bufOffset is the first byte of the record
2217 * we're interested in.
2218 * Skip data in the others.
2221 if (LargeIntegerLessThan(*offp, bsp->bufOffset))
2223 if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
2224 return CM_ERROR_STOPNOW;
2225 if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
2228 cm_SetFid(&tfid, scp->fid.cell, scp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
2229 tscp = cm_FindSCache(&tfid);
2231 if (lock_TryWrite(&tscp->rw)) {
2232 /* we have an entry that we can look at */
2233 if (!(tscp->flags & CM_SCACHEFLAG_EACCESS) && cm_HaveCallback(tscp)) {
2234 /* we have a callback on it. Don't bother
2235 * fetching this stat entry, since we're happy
2236 * with the info we have.
2238 lock_ReleaseWrite(&tscp->rw);
2239 cm_ReleaseSCache(tscp);
2242 lock_ReleaseWrite(&tscp->rw);
2244 cm_ReleaseSCache(tscp);
2247 #ifdef AFS_FREELANCE_CLIENT
2248 // yj: if this is a mountpoint under root.afs then we don't want it
2249 // to be bulkstat-ed, instead, we call getSCache directly and under
2250 // getSCache, it is handled specially.
2251 if ( cm_freelanceEnabled &&
2252 tfid.cell==AFS_FAKE_ROOT_CELL_ID &&
2253 tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
2254 !(tfid.vnode==0x1 && tfid.unique==0x1) )
2256 osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
2257 return cm_GetSCache(&tfid, &tscp, NULL, NULL);
2259 #endif /* AFS_FREELANCE_CLIENT */
2262 bsp->fids[i].Volume = scp->fid.volume;
2263 bsp->fids[i].Vnode = tfid.vnode;
2264 bsp->fids[i].Unique = tfid.unique;
2269 cm_TryBulkStatRPC(cm_scache_t *dscp, cm_bulkStat_t *bbp, cm_user_t *userp, cm_req_t *reqp)
2272 AFSCBFids fidStruct;
2273 AFSBulkStats statStruct;
2275 AFSCBs callbackStruct;
2278 cm_callbackRequest_t cbReq;
2284 struct rx_connection * rxconnp;
2285 int inlinebulk = 0; /* Did we use InlineBulkStatus RPC or not? */
2287 /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
2288 * make the calls to create the entries. Handle AFSCBMAX files at a
2291 for (filex = 0; filex < bbp->counter; filex += filesThisCall) {
2292 filesThisCall = bbp->counter - filex;
2293 if (filesThisCall > AFSCBMAX)
2294 filesThisCall = AFSCBMAX;
2296 fidStruct.AFSCBFids_len = filesThisCall;
2297 fidStruct.AFSCBFids_val = &bbp->fids[filex];
2298 statStruct.AFSBulkStats_len = filesThisCall;
2299 statStruct.AFSBulkStats_val = &bbp->stats[filex];
2300 callbackStruct.AFSCBs_len = filesThisCall;
2301 callbackStruct.AFSCBs_val = &bbp->callbacks[filex];
2302 cm_StartCallbackGrantingCall(NULL, &cbReq);
2303 osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
2305 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2309 rxconnp = cm_GetRxConn(connp);
2310 if (!(connp->serverp->flags & CM_SERVERFLAG_NOINLINEBULK)) {
2311 code = RXAFS_InlineBulkStatus(rxconnp, &fidStruct,
2312 &statStruct, &callbackStruct, &volSync);
2313 if (code == RXGEN_OPCODE) {
2314 cm_SetServerNoInlineBulk(connp->serverp, 0);
2320 code = RXAFS_BulkStatus(rxconnp, &fidStruct,
2321 &statStruct, &callbackStruct, &volSync);
2323 rx_PutConnection(rxconnp);
2325 } while (cm_Analyze(connp, userp, reqp, &dscp->fid,
2326 &volSync, NULL, &cbReq, code));
2327 code = cm_MapRPCError(code, reqp);
2329 osi_Log2(afsd_logp, "CALL %sBulkStatus FAILURE code 0x%x",
2330 inlinebulk ? "Inline" : "", code);
2332 osi_Log1(afsd_logp, "CALL %sBulkStatus SUCCESS", inlinebulk ? "Inline" : "");
2334 /* may as well quit on an error, since we're not going to do
2335 * much better on the next immediate call, either.
2338 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2342 /* otherwise, we should do the merges */
2343 for (i = 0; i<filesThisCall; i++) {
2345 cm_SetFid(&tfid, dscp->fid.cell, bbp->fids[j].Volume, bbp->fids[j].Vnode, bbp->fids[j].Unique);
2346 code = cm_GetSCache(&tfid, &scp, userp, reqp);
2350 /* otherwise, if this entry has no callback info,
2353 lock_ObtainWrite(&scp->rw);
2354 /* now, we have to be extra paranoid on merging in this
2355 * information, since we didn't use cm_SyncOp before
2356 * starting the fetch to make sure that no bad races
2357 * were occurring. Specifically, we need to make sure
2358 * we don't obliterate any newer information in the
2359 * vnode than have here.
2361 * Right now, be pretty conservative: if there's a
2362 * callback or a pending call, skip it.
2364 if ((scp->cbServerp == NULL || (scp->flags & CM_SCACHEFLAG_EACCESS))
2366 (CM_SCACHEFLAG_FETCHING
2367 | CM_SCACHEFLAG_STORING
2368 | CM_SCACHEFLAG_SIZESTORING))) {
2369 cm_EndCallbackGrantingCall(scp, &cbReq,
2371 CM_CALLBACK_MAINTAINCOUNT);
2372 cm_MergeStatus(dscp, scp, &bbp->stats[j], &volSync, userp, 0);
2374 lock_ReleaseWrite(&scp->rw);
2375 cm_ReleaseSCache(scp);
2376 } /* all files in the response */
2377 /* now tell it to drop the count,
2378 * after doing the vnode processing above */
2379 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2380 } /* while there are still more files to process */
2382 /* If we did the InlineBulk RPC pull out the return code and log it */
2384 if ((&bbp->stats[0])->errorCode) {
2385 osi_Log1(afsd_logp, "cm_TryBulkStat bulk stat error: %d",
2386 (&bbp->stats[0])->errorCode);
2387 code = (&bbp->stats[0])->errorCode;
2394 /* called with a write locked scp and a pointer to a buffer. Make bulk stat
2395 * calls on all undeleted files in the page of the directory specified.
2398 cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
2404 osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%p", dscp);
2406 /* should be on a buffer boundary */
2407 osi_assertx((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0, "invalid offset");
2409 bbp = malloc(sizeof(cm_bulkStat_t));
2410 memset(bbp, 0, sizeof(cm_bulkStat_t));
2411 bbp->bufOffset = *offsetp;
2413 lock_ReleaseWrite(&dscp->rw);
2414 /* first, assemble the file IDs we need to stat */
2415 code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) bbp, offsetp, userp, reqp, NULL);
2417 /* if we failed, bail out early */
2418 if (code && code != CM_ERROR_STOPNOW) {
2420 lock_ObtainWrite(&dscp->rw);
2424 code = cm_TryBulkStatRPC(dscp, bbp, userp, reqp);
2425 osi_Log1(afsd_logp, "END cm_TryBulkStat code 0x%x", code);
2427 lock_ObtainWrite(&dscp->rw);
2432 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
2436 /* initialize store back mask as inexpensive local variable */
2438 memset(statusp, 0, sizeof(AFSStoreStatus));
2440 /* copy out queued info from scache first, if scp passed in */
2442 if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
2443 statusp->ClientModTime = scp->clientModTime;
2444 mask |= AFS_SETMODTIME;
2445 scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
2450 /* now add in our locally generated request */
2451 if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
2452 statusp->ClientModTime = attrp->clientModTime;
2453 mask |= AFS_SETMODTIME;
2455 if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
2456 statusp->UnixModeBits = attrp->unixModeBits;
2457 mask |= AFS_SETMODE;
2459 if (attrp->mask & CM_ATTRMASK_OWNER) {
2460 statusp->Owner = attrp->owner;
2461 mask |= AFS_SETOWNER;
2463 if (attrp->mask & CM_ATTRMASK_GROUP) {
2464 statusp->Group = attrp->group;
2465 mask |= AFS_SETGROUP;
2468 statusp->Mask = mask;
2471 /* set the file size, and make sure that all relevant buffers have been
2472 * truncated. Ensure that any partially truncated buffers have been zeroed
2473 * to the end of the buffer.
2475 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
2481 /* start by locking out buffer creation */
2482 lock_ObtainWrite(&scp->bufCreateLock);
2484 /* verify that this is a file, not a dir or a symlink */
2485 lock_ObtainWrite(&scp->rw);
2486 code = cm_SyncOp(scp, NULL, userp, reqp, 0,
2487 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2490 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2492 if (scp->fileType != CM_SCACHETYPE_FILE) {
2493 code = CM_ERROR_ISDIR;
2498 if (LargeIntegerLessThan(*sizep, scp->length))
2503 lock_ReleaseWrite(&scp->rw);
2505 /* can't hold scp->rw lock here, since we may wait for a storeback to
2506 * finish if the buffer package is cleaning a buffer by storing it to
2510 buf_Truncate(scp, userp, reqp, sizep);
2512 /* now ensure that file length is short enough, and update truncPos */
2513 lock_ObtainWrite(&scp->rw);
2515 /* make sure we have a callback (so we have the right value for the
2516 * length), and wait for it to be safe to do a truncate.
2518 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
2519 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2520 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2522 /* If we only have 'i' bits, then we should still be able to set
2523 the size of a file we created. */
2524 if (code == CM_ERROR_NOACCESS && scp->creator == userp) {
2525 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
2526 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2527 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2533 if (LargeIntegerLessThan(*sizep, scp->length)) {
2534 /* a real truncation. If truncPos is not set yet, or is bigger
2535 * than where we're truncating the file, set truncPos to this
2540 if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
2541 || LargeIntegerLessThan(*sizep, scp->length)) {
2543 scp->truncPos = *sizep;
2544 scp->mask |= CM_SCACHEMASK_TRUNCPOS;
2546 /* in either case, the new file size has been changed */
2547 scp->length = *sizep;
2548 scp->mask |= CM_SCACHEMASK_LENGTH;
2550 else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
2551 /* really extending the file */
2552 scp->length = *sizep;
2553 scp->mask |= CM_SCACHEMASK_LENGTH;
2556 /* done successfully */
2559 cm_SyncOpDone(scp, NULL,
2560 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2561 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2564 lock_ReleaseWrite(&scp->rw);
2565 lock_ReleaseWrite(&scp->bufCreateLock);
2570 /* set the file size or other attributes (but not both at once) */
2571 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
2575 AFSFetchStatus afsOutStatus;
2579 AFSStoreStatus afsInStatus;
2580 struct rx_connection * rxconnp;
2582 /* handle file length setting */
2583 if (attrp->mask & CM_ATTRMASK_LENGTH)
2584 return cm_SetLength(scp, &attrp->length, userp, reqp);
2586 lock_ObtainWrite(&scp->rw);
2587 /* otherwise, we have to make an RPC to get the status */
2588 code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
2590 lock_ReleaseWrite(&scp->rw);
2593 lock_ConvertWToR(&scp->rw);
2595 /* make the attr structure */
2596 cm_StatusFromAttr(&afsInStatus, scp, attrp);
2598 tfid.Volume = scp->fid.volume;
2599 tfid.Vnode = scp->fid.vnode;
2600 tfid.Unique = scp->fid.unique;
2601 lock_ReleaseRead(&scp->rw);
2603 /* now make the RPC */
2604 osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%p", scp);
2606 code = cm_ConnFromFID(&scp->fid, userp, reqp, &connp);
2610 rxconnp = cm_GetRxConn(connp);
2611 code = RXAFS_StoreStatus(rxconnp, &tfid,
2612 &afsInStatus, &afsOutStatus, &volSync);
2613 rx_PutConnection(rxconnp);
2615 } while (cm_Analyze(connp, userp, reqp,
2616 &scp->fid, &volSync, NULL, NULL, code));
2617 code = cm_MapRPCError(code, reqp);
2620 osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
2622 osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
2624 lock_ObtainWrite(&scp->rw);
2625 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
2627 cm_MergeStatus(NULL, scp, &afsOutStatus, &volSync, userp,
2628 CM_MERGEFLAG_FORCE|CM_MERGEFLAG_STOREDATA);
2630 /* if we're changing the mode bits, discard the ACL cache,
2631 * since we changed the mode bits.
2633 if (afsInStatus.Mask & AFS_SETMODE)
2634 cm_FreeAllACLEnts(scp);
2635 lock_ReleaseWrite(&scp->rw);
2639 long cm_Create(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
2640 cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
2645 cm_callbackRequest_t cbReq;
2648 cm_scache_t *scp = NULL;
2650 AFSStoreStatus inStatus;
2651 AFSFetchStatus updatedDirStatus;
2652 AFSFetchStatus newFileStatus;
2653 AFSCallBack newFileCallback;
2655 struct rx_connection * rxconnp;
2657 fschar_t * fnamep = NULL;
2659 /* can't create names with @sys in them; must expand it manually first.
2660 * return "invalid request" if they try.
2662 if (cm_ExpandSysName(cnamep, NULL, 0, 0)) {
2663 return CM_ERROR_ATSYS;
2666 #ifdef AFS_FREELANCE_CLIENT
2667 /* Freelance root volume does not hold files */
2668 if (cm_freelanceEnabled &&
2669 dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
2670 dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
2672 return CM_ERROR_NOACCESS;
2674 #endif /* AFS_FREELANCE_CLIENT */
2676 /* before starting the RPC, mark that we're changing the file data, so
2677 * that someone who does a chmod will know to wait until our call
2680 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2681 lock_ObtainWrite(&dscp->rw);
2682 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2683 lock_ReleaseWrite(&dscp->rw);
2685 cm_StartCallbackGrantingCall(NULL, &cbReq);
2687 cm_EndDirOp(&dirop);
2694 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
2696 cm_StatusFromAttr(&inStatus, NULL, attrp);
2698 /* try the RPC now */
2699 osi_Log1(afsd_logp, "CALL CreateFile scp 0x%p", dscp);
2701 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2705 dirAFSFid.Volume = dscp->fid.volume;
2706 dirAFSFid.Vnode = dscp->fid.vnode;
2707 dirAFSFid.Unique = dscp->fid.unique;
2709 rxconnp = cm_GetRxConn(connp);
2710 code = RXAFS_CreateFile(connp->rxconnp, &dirAFSFid, fnamep,
2711 &inStatus, &newAFSFid, &newFileStatus,
2712 &updatedDirStatus, &newFileCallback,
2714 rx_PutConnection(rxconnp);
2716 } while (cm_Analyze(connp, userp, reqp,
2717 &dscp->fid, &volSync, NULL, &cbReq, code));
2718 code = cm_MapRPCError(code, reqp);
2721 osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
2723 osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
2726 lock_ObtainWrite(&dirop.scp->dirlock);
2727 dirop.lockType = CM_DIRLOCK_WRITE;
2729 lock_ObtainWrite(&dscp->rw);
2730 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2732 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
2734 lock_ReleaseWrite(&dscp->rw);
2736 /* now try to create the file's entry, too, but be careful to
2737 * make sure that we don't merge in old info. Since we weren't locking
2738 * out any requests during the file's creation, we may have pretty old
2742 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
2743 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2745 lock_ObtainWrite(&scp->rw);
2746 scp->creator = userp; /* remember who created it */
2747 if (!cm_HaveCallback(scp)) {
2748 cm_MergeStatus(dscp, scp, &newFileStatus, &volSync,
2750 cm_EndCallbackGrantingCall(scp, &cbReq,
2751 &newFileCallback, 0);
2754 lock_ReleaseWrite(&scp->rw);
2758 /* make sure we end things properly */
2760 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2762 if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
2763 cm_DirCreateEntry(&dirop, fnamep, &newFid);
2765 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
2768 cm_EndDirOp(&dirop);
2777 cm_ReleaseSCache(scp);
2782 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
2786 code = buf_CleanVnode(scp, userp, reqp);
2788 lock_ObtainWrite(&scp->rw);
2790 if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
2791 | CM_SCACHEMASK_CLIENTMODTIME
2792 | CM_SCACHEMASK_LENGTH))
2793 code = cm_StoreMini(scp, userp, reqp);
2795 if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) {
2796 code = (scp->flags & CM_SCACHEFLAG_OVERQUOTA) ? CM_ERROR_QUOTA : CM_ERROR_SPACE;
2797 scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE);
2800 lock_ReleaseWrite(&scp->rw);
2805 long cm_MakeDir(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
2806 cm_user_t *userp, cm_req_t *reqp, cm_scache_t **scpp)
2811 cm_callbackRequest_t cbReq;
2814 cm_scache_t *scp = NULL;
2816 AFSStoreStatus inStatus;
2817 AFSFetchStatus updatedDirStatus;
2818 AFSFetchStatus newDirStatus;
2819 AFSCallBack newDirCallback;
2821 struct rx_connection * rxconnp;
2823 fschar_t * fnamep = NULL;
2825 /* can't create names with @sys in them; must expand it manually first.
2826 * return "invalid request" if they try.
2828 if (cm_ExpandSysName(cnamep, NULL, 0, 0)) {
2829 return CM_ERROR_ATSYS;
2832 #ifdef AFS_FREELANCE_CLIENT
2833 /* Freelance root volume does not hold subdirectories */
2834 if (cm_freelanceEnabled &&
2835 dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
2836 dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
2838 return CM_ERROR_NOACCESS;
2840 #endif /* AFS_FREELANCE_CLIENT */
2842 /* before starting the RPC, mark that we're changing the directory
2843 * data, so that someone who does a chmod on the dir will wait until
2844 * our call completes.
2846 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2847 lock_ObtainWrite(&dscp->rw);
2848 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2849 lock_ReleaseWrite(&dscp->rw);
2851 cm_StartCallbackGrantingCall(NULL, &cbReq);
2853 cm_EndDirOp(&dirop);
2860 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
2861 cm_StatusFromAttr(&inStatus, NULL, attrp);
2863 /* try the RPC now */
2864 osi_Log1(afsd_logp, "CALL MakeDir scp 0x%p", dscp);
2866 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2870 dirAFSFid.Volume = dscp->fid.volume;
2871 dirAFSFid.Vnode = dscp->fid.vnode;
2872 dirAFSFid.Unique = dscp->fid.unique;
2874 rxconnp = cm_GetRxConn(connp);
2875 code = RXAFS_MakeDir(connp->rxconnp, &dirAFSFid, fnamep,
2876 &inStatus, &newAFSFid, &newDirStatus,
2877 &updatedDirStatus, &newDirCallback,
2879 rx_PutConnection(rxconnp);
2881 } while (cm_Analyze(connp, userp, reqp,
2882 &dscp->fid, &volSync, NULL, &cbReq, code));
2883 code = cm_MapRPCError(code, reqp);
2886 osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
2888 osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
2891 lock_ObtainWrite(&dirop.scp->dirlock);
2892 dirop.lockType = CM_DIRLOCK_WRITE;
2894 lock_ObtainWrite(&dscp->rw);
2895 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2897 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
2899 lock_ReleaseWrite(&dscp->rw);
2901 /* now try to create the new dir's entry, too, but be careful to
2902 * make sure that we don't merge in old info. Since we weren't locking
2903 * out any requests during the file's creation, we may have pretty old
2907 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
2908 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2910 lock_ObtainWrite(&scp->rw);
2911 if (!cm_HaveCallback(scp)) {
2912 cm_MergeStatus(dscp, scp, &newDirStatus, &volSync,
2914 cm_EndCallbackGrantingCall(scp, &cbReq,
2915 &newDirCallback, 0);
2918 lock_ReleaseWrite(&scp->rw);
2922 /* make sure we end things properly */
2924 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2926 if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
2927 cm_DirCreateEntry(&dirop, fnamep, &newFid);
2929 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
2932 cm_EndDirOp(&dirop);
2940 cm_ReleaseSCache(scp);
2943 /* and return error code */
2947 long cm_Link(cm_scache_t *dscp, clientchar_t *cnamep, cm_scache_t *sscp, long flags,
2948 cm_user_t *userp, cm_req_t *reqp)
2953 AFSFid existingAFSFid;
2954 AFSFetchStatus updatedDirStatus;
2955 AFSFetchStatus newLinkStatus;
2957 struct rx_connection * rxconnp;
2959 fschar_t * fnamep = NULL;
2961 if (dscp->fid.cell != sscp->fid.cell ||
2962 dscp->fid.volume != sscp->fid.volume) {
2963 return CM_ERROR_CROSSDEVLINK;
2966 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2967 lock_ObtainWrite(&dscp->rw);
2968 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2969 lock_ReleaseWrite(&dscp->rw);
2971 cm_EndDirOp(&dirop);
2976 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
2978 /* try the RPC now */
2979 osi_Log1(afsd_logp, "CALL Link scp 0x%p", dscp);
2981 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2984 dirAFSFid.Volume = dscp->fid.volume;
2985 dirAFSFid.Vnode = dscp->fid.vnode;
2986 dirAFSFid.Unique = dscp->fid.unique;
2988 existingAFSFid.Volume = sscp->fid.volume;
2989 existingAFSFid.Vnode = sscp->fid.vnode;
2990 existingAFSFid.Unique = sscp->fid.unique;
2992 rxconnp = cm_GetRxConn(connp);
2993 code = RXAFS_Link(rxconnp, &dirAFSFid, fnamep, &existingAFSFid,
2994 &newLinkStatus, &updatedDirStatus, &volSync);
2995 rx_PutConnection(rxconnp);
2996 osi_Log1(afsd_logp," RXAFS_Link returns 0x%x", code);
2998 } while (cm_Analyze(connp, userp, reqp,
2999 &dscp->fid, &volSync, NULL, NULL, code));
3001 code = cm_MapRPCError(code, reqp);
3004 osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
3006 osi_Log0(afsd_logp, "CALL Link SUCCESS");
3009 lock_ObtainWrite(&dirop.scp->dirlock);
3010 dirop.lockType = CM_DIRLOCK_WRITE;
3012 lock_ObtainWrite(&dscp->rw);
3013 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3015 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
3017 lock_ReleaseWrite(&dscp->rw);
3020 if (cm_CheckDirOpForSingleChange(&dirop)) {
3021 cm_DirCreateEntry(&dirop, fnamep, &sscp->fid);
3023 cm_BPlusDirCreateEntry(&dirop, cnamep, &sscp->fid);
3027 cm_EndDirOp(&dirop);
3034 long cm_SymLink(cm_scache_t *dscp, clientchar_t *cnamep, fschar_t *contentsp, long flags,
3035 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
3043 AFSStoreStatus inStatus;
3044 AFSFetchStatus updatedDirStatus;
3045 AFSFetchStatus newLinkStatus;
3047 struct rx_connection * rxconnp;
3049 fschar_t *fnamep = NULL;
3051 /* before starting the RPC, mark that we're changing the directory data,
3052 * so that someone who does a chmod on the dir will wait until our
3055 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
3056 lock_ObtainWrite(&dscp->rw);
3057 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3058 lock_ReleaseWrite(&dscp->rw);
3060 cm_EndDirOp(&dirop);
3065 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3067 cm_StatusFromAttr(&inStatus, NULL, attrp);
3069 /* try the RPC now */
3070 osi_Log1(afsd_logp, "CALL Symlink scp 0x%p", dscp);
3072 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3076 dirAFSFid.Volume = dscp->fid.volume;
3077 dirAFSFid.Vnode = dscp->fid.vnode;
3078 dirAFSFid.Unique = dscp->fid.unique;
3080 rxconnp = cm_GetRxConn(connp);
3081 code = RXAFS_Symlink(rxconnp, &dirAFSFid, fnamep, contentsp,
3082 &inStatus, &newAFSFid, &newLinkStatus,
3083 &updatedDirStatus, &volSync);
3084 rx_PutConnection(rxconnp);
3086 } while (cm_Analyze(connp, userp, reqp,
3087 &dscp->fid, &volSync, NULL, NULL, code));
3088 code = cm_MapRPCError(code, reqp);
3091 osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
3093 osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
3096 lock_ObtainWrite(&dirop.scp->dirlock);
3097 dirop.lockType = CM_DIRLOCK_WRITE;
3099 lock_ObtainWrite(&dscp->rw);
3100 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3102 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
3104 lock_ReleaseWrite(&dscp->rw);
3107 if (cm_CheckDirOpForSingleChange(&dirop)) {
3108 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3110 cm_DirCreateEntry(&dirop, fnamep, &newFid);
3112 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
3116 cm_EndDirOp(&dirop);
3118 /* now try to create the new dir's entry, too, but be careful to
3119 * make sure that we don't merge in old info. Since we weren't locking
3120 * out any requests during the file's creation, we may have pretty old
3124 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3125 code = cm_GetSCache(&newFid, &scp, userp, reqp);
3127 lock_ObtainWrite(&scp->rw);
3128 if (!cm_HaveCallback(scp)) {
3129 cm_MergeStatus(dscp, scp, &newLinkStatus, &volSync,
3132 lock_ReleaseWrite(&scp->rw);
3133 cm_ReleaseSCache(scp);
3139 /* and return error code */
3143 /*! \brief Remove a directory
3145 Encapsulates a call to RXAFS_RemoveDir().
3147 \param[in] dscp cm_scache_t for the directory containing the
3148 directory to be removed.
3150 \param[in] fnamep This will be the original name of the directory
3151 as known to the file server. It will be passed in to RXAFS_RemoveDir().
3152 This parameter is optional. If it is not provided the value
3155 \param[in] cnamep Normalized name used to update the local
3158 \param[in] userp cm_user_t for the request.
3160 \param[in] reqp Request tracker.
3162 long cm_RemoveDir(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t *cnamep, cm_user_t *userp, cm_req_t *reqp)
3168 AFSFetchStatus updatedDirStatus;
3170 struct rx_connection * rxconnp;
3172 cm_scache_t *scp = NULL;
3173 int free_fnamep = FALSE;
3175 if (fnamep == NULL) {
3178 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ, &dirop);
3180 code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
3183 cm_EndDirOp(&dirop);
3190 code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
3194 /* before starting the RPC, mark that we're changing the directory data,
3195 * so that someone who does a chmod on the dir will wait until our
3198 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
3199 lock_ObtainWrite(&dscp->rw);
3200 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3201 lock_ReleaseWrite(&dscp->rw);
3203 cm_EndDirOp(&dirop);
3208 /* try the RPC now */
3209 osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%p", dscp);
3211 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3215 dirAFSFid.Volume = dscp->fid.volume;
3216 dirAFSFid.Vnode = dscp->fid.vnode;
3217 dirAFSFid.Unique = dscp->fid.unique;
3219 rxconnp = cm_GetRxConn(connp);
3220 code = RXAFS_RemoveDir(rxconnp, &dirAFSFid, fnamep,
3221 &updatedDirStatus, &volSync);
3222 rx_PutConnection(rxconnp);
3224 } while (cm_Analyze(connp, userp, reqp,
3225 &dscp->fid, &volSync, NULL, NULL, code));
3226 code = cm_MapRPCErrorRmdir(code, reqp);
3229 osi_Log1(afsd_logp, "CALL RemoveDir FAILURE, code 0x%x", code);
3231 osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
3234 lock_ObtainWrite(&dirop.scp->dirlock);
3235 dirop.lockType = CM_DIRLOCK_WRITE;
3237 lock_ObtainWrite(&dscp->rw);
3238 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3240 cm_dnlcRemove(dscp, cnamep);
3241 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
3243 lock_ReleaseWrite(&dscp->rw);
3246 if (cm_CheckDirOpForSingleChange(&dirop) && cnamep != NULL) {
3247 cm_DirDeleteEntry(&dirop, fnamep);
3249 cm_BPlusDirDeleteEntry(&dirop, cnamep);
3253 cm_EndDirOp(&dirop);
3256 cm_ReleaseSCache(scp);
3258 lock_ObtainWrite(&scp->rw);
3259 scp->flags |= CM_SCACHEFLAG_DELETED;
3260 lock_ReleaseWrite(&scp->rw);
3268 /* and return error code */
3272 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
3274 /* grab mutex on contents */
3275 lock_ObtainWrite(&scp->rw);
3277 /* reset the prefetch info */
3278 scp->prefetch.base.LowPart = 0; /* base */
3279 scp->prefetch.base.HighPart = 0;
3280 scp->prefetch.end.LowPart = 0; /* and end */
3281 scp->prefetch.end.HighPart = 0;
3283 /* release mutex on contents */
3284 lock_ReleaseWrite(&scp->rw);
3290 /*! \brief Rename a file or directory
3292 Encapsulates a RXAFS_Rename() call.
3294 \param[in] oldDscp cm_scache_t for the directory containing the old
3297 \param[in] oldNamep The original old name known to the file server.
3298 This is the name that will be passed into the RXAFS_Rename().
3299 If it is not provided, it will be looked up.
3301 \param[in] normalizedOldNamep Normalized old name. This is used for
3302 updating local directory caches.
3304 \param[in] newDscp cm_scache_t for the directory containing the new
3307 \param[in] newNamep New name. Normalized.
3309 \param[in] userp cm_user_t for the request.
3311 \param[in,out] reqp Request tracker.
3314 long cm_Rename(cm_scache_t *oldDscp, fschar_t *oldNamep, clientchar_t *cOldNamep,
3315 cm_scache_t *newDscp, clientchar_t *cNewNamep, cm_user_t *userp,
3320 AFSFid oldDirAFSFid;
3321 AFSFid newDirAFSFid;
3323 AFSFetchStatus updatedOldDirStatus;
3324 AFSFetchStatus updatedNewDirStatus;
3327 struct rx_connection * rxconnp;
3328 cm_dirOp_t oldDirOp;
3331 cm_dirOp_t newDirOp;
3332 fschar_t * newNamep = NULL;
3333 int free_oldNamep = FALSE;
3334 cm_scache_t *oldScp = NULL, *newScp = NULL;
3336 if (cOldNamep == NULL || cNewNamep == NULL ||
3337 cm_ClientStrLen(cOldNamep) == 0 ||
3338 cm_ClientStrLen(cNewNamep) == 0)
3339 return CM_ERROR_INVAL;
3342 * Before we permit the operation, make sure that we do not already have
3343 * an object in the destination directory that has a case-insensitive match
3344 * for this name UNLESS the matching object is the object we are renaming.
3346 code = cm_Lookup(oldDscp, cOldNamep, 0, userp, reqp, &oldScp);
3348 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S old name lookup failed",
3349 oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3353 code = cm_Lookup(newDscp, cNewNamep, CM_FLAG_CASEFOLD, userp, reqp, &newScp);
3355 /* found a matching object with the new name */
3356 if (cm_FidCmp(&oldScp->fid, &newScp->fid)) {
3357 /* and they don't match so return an error */
3358 osi_Log2(afsd_logp, "cm_Rename newDscp 0x%p cNewName %S new name already exists",
3359 newDscp, osi_LogSaveStringW(afsd_logp, cNewNamep));
3360 code = CM_ERROR_EXISTS;
3362 cm_ReleaseSCache(newScp);
3364 } else if (code == CM_ERROR_AMBIGUOUS_FILENAME) {
3365 code = CM_ERROR_EXISTS;
3372 if (oldNamep == NULL) {
3375 code = cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_READ, &oldDirOp);
3377 code = cm_BPlusDirLookupOriginalName(&oldDirOp, cOldNamep, &oldNamep);
3379 free_oldNamep = TRUE;
3380 cm_EndDirOp(&oldDirOp);
3384 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S Original Name lookup failed",
3385 oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3391 /* before starting the RPC, mark that we're changing the directory data,
3392 * so that someone who does a chmod on the dir will wait until our call
3393 * completes. We do this in vnode order so that we don't deadlock,
3394 * which makes the code a little verbose.
3396 if (oldDscp == newDscp) {
3397 /* check for identical names */
3398 if (cm_ClientStrCmp(cOldNamep, cNewNamep) == 0) {
3399 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_RENAME_IDENTICAL",
3401 code = CM_ERROR_RENAME_IDENTICAL;
3406 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3407 lock_ObtainWrite(&oldDscp->rw);
3408 cm_dnlcRemove(oldDscp, cOldNamep);
3409 cm_dnlcRemove(oldDscp, cNewNamep);
3410 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3411 CM_SCACHESYNC_STOREDATA);
3412 lock_ReleaseWrite(&oldDscp->rw);
3414 cm_EndDirOp(&oldDirOp);
3418 /* two distinct dir vnodes */
3420 if (oldDscp->fid.cell != newDscp->fid.cell ||
3421 oldDscp->fid.volume != newDscp->fid.volume) {
3422 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_CROSSDEVLINK",
3424 code = CM_ERROR_CROSSDEVLINK;
3428 /* shouldn't happen that we have distinct vnodes for two
3429 * different files, but could due to deliberate attack, or
3430 * stale info. Avoid deadlocks and quit now.
3432 if (oldDscp->fid.vnode == newDscp->fid.vnode) {
3433 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p vnode collision",
3435 code = CM_ERROR_CROSSDEVLINK;
3439 if (oldDscp->fid.vnode < newDscp->fid.vnode) {
3440 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3441 lock_ObtainWrite(&oldDscp->rw);
3442 cm_dnlcRemove(oldDscp, cOldNamep);
3443 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3444 CM_SCACHESYNC_STOREDATA);
3445 lock_ReleaseWrite(&oldDscp->rw);
3447 cm_EndDirOp(&oldDirOp);
3449 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE, &newDirOp);
3450 lock_ObtainWrite(&newDscp->rw);
3451 cm_dnlcRemove(newDscp, cNewNamep);
3452 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3453 CM_SCACHESYNC_STOREDATA);
3454 lock_ReleaseWrite(&newDscp->rw);
3456 cm_EndDirOp(&newDirOp);
3458 /* cleanup first one */
3459 lock_ObtainWrite(&oldDscp->rw);
3460 cm_SyncOpDone(oldDscp, NULL,
3461 CM_SCACHESYNC_STOREDATA);
3462 lock_ReleaseWrite(&oldDscp->rw);
3463 cm_EndDirOp(&oldDirOp);
3468 /* lock the new vnode entry first */
3469 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE, &newDirOp);
3470 lock_ObtainWrite(&newDscp->rw);
3471 cm_dnlcRemove(newDscp, cNewNamep);
3472 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3473 CM_SCACHESYNC_STOREDATA);
3474 lock_ReleaseWrite(&newDscp->rw);
3476 cm_EndDirOp(&newDirOp);
3478 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3479 lock_ObtainWrite(&oldDscp->rw);
3480 cm_dnlcRemove(oldDscp, cOldNamep);
3481 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3482 CM_SCACHESYNC_STOREDATA);
3483 lock_ReleaseWrite(&oldDscp->rw);
3485 cm_EndDirOp(&oldDirOp);
3487 /* cleanup first one */
3488 lock_ObtainWrite(&newDscp->rw);
3489 cm_SyncOpDone(newDscp, NULL,
3490 CM_SCACHESYNC_STOREDATA);
3491 lock_ReleaseWrite(&newDscp->rw);
3492 cm_EndDirOp(&newDirOp);
3496 } /* two distinct vnodes */
3503 newNamep = cm_ClientStringToFsStringAlloc(cNewNamep, -1, NULL);
3505 /* try the RPC now */
3506 osi_Log2(afsd_logp, "CALL Rename old scp 0x%p new scp 0x%p",
3509 code = cm_ConnFromFID(&oldDscp->fid, userp, reqp, &connp);
3513 oldDirAFSFid.Volume = oldDscp->fid.volume;
3514 oldDirAFSFid.Vnode = oldDscp->fid.vnode;
3515 oldDirAFSFid.Unique = oldDscp->fid.unique;
3516 newDirAFSFid.Volume = newDscp->fid.volume;
3517 newDirAFSFid.Vnode = newDscp->fid.vnode;
3518 newDirAFSFid.Unique = newDscp->fid.unique;
3520 rxconnp = cm_GetRxConn(connp);
3521 code = RXAFS_Rename(rxconnp, &oldDirAFSFid, oldNamep,
3522 &newDirAFSFid, newNamep,
3523 &updatedOldDirStatus, &updatedNewDirStatus,
3525 rx_PutConnection(rxconnp);
3527 } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
3528 &volSync, NULL, NULL, code));
3529 code = cm_MapRPCError(code, reqp);
3532 osi_Log1(afsd_logp, "CALL Rename FAILURE, code 0x%x", code);
3534 osi_Log0(afsd_logp, "CALL Rename SUCCESS");
3536 /* update the individual stat cache entries for the directories */
3538 lock_ObtainWrite(&oldDirOp.scp->dirlock);
3539 oldDirOp.lockType = CM_DIRLOCK_WRITE;
3541 lock_ObtainWrite(&oldDscp->rw);
3542 cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
3545 cm_MergeStatus(NULL, oldDscp, &updatedOldDirStatus, &volSync,
3546 userp, CM_MERGEFLAG_DIROP);
3547 lock_ReleaseWrite(&oldDscp->rw);
3549 if (code == 0 && cm_CheckDirOpForSingleChange(&oldDirOp)) {
3551 diropCode = cm_BPlusDirLookup(&oldDirOp, cOldNamep, &fileFid);
3552 if (diropCode == CM_ERROR_INEXACT_MATCH)
3554 else if (diropCode == EINVAL)
3556 diropCode = cm_DirLookup(&oldDirOp, oldNamep, &fileFid);
3558 if (diropCode == 0) {
3560 diropCode = cm_DirCreateEntry(&oldDirOp, newNamep, &fileFid);
3562 cm_BPlusDirCreateEntry(&oldDirOp, cNewNamep, &fileFid);
3566 if (diropCode == 0) {
3567 diropCode = cm_DirDeleteEntry(&oldDirOp, oldNamep);
3569 cm_BPlusDirDeleteEntry(&oldDirOp, cOldNamep);
3574 cm_EndDirOp(&oldDirOp);
3576 /* and update it for the new one, too, if necessary */
3579 lock_ObtainWrite(&newDirOp.scp->dirlock);
3580 newDirOp.lockType = CM_DIRLOCK_WRITE;
3582 lock_ObtainWrite(&newDscp->rw);
3583 cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
3585 cm_MergeStatus(NULL, newDscp, &updatedNewDirStatus, &volSync,
3586 userp, CM_MERGEFLAG_DIROP);
3587 lock_ReleaseWrite(&newDscp->rw);
3591 * The following optimization does not work.
3592 * When the file server processed a RXAFS_Rename() request the
3593 * FID of the object being moved between directories is not
3594 * preserved. The client does not know the new FID nor the
3595 * version number of the target. Not only can we not create
3596 * the directory entry in the new directory, but we can't
3597 * preserve the cached data for the file. It must be re-read
3598 * from the file server. - jaltman, 2009/02/20
3601 /* we only make the local change if we successfully made
3602 the change in the old directory AND there was only one
3603 change in the new directory */
3604 if (diropCode == 0 && cm_CheckDirOpForSingleChange(&newDirOp)) {
3605 cm_DirCreateEntry(&newDirOp, newNamep, &fileFid);
3607 cm_BPlusDirCreateEntry(&newDirOp, cNewNamep, &fileFid);
3612 cm_EndDirOp(&newDirOp);
3616 * After the rename the file server has invalidated the callbacks
3617 * on the file that was moved nor do we have a directory reference
3620 lock_ObtainWrite(&oldScp->rw);
3621 cm_DiscardSCache(oldScp);
3622 lock_ReleaseWrite(&oldScp->rw);
3626 cm_ReleaseSCache(oldScp);
3633 /* and return error code */
3637 /* Byte range locks:
3639 The OpenAFS Windows client has to fake byte range locks given no
3640 server side support for such locks. This is implemented as keyed
3641 byte range locks on the cache manager.
3643 Keyed byte range locks:
3645 Each cm_scache_t structure keeps track of a list of keyed locks.
3646 The key for a lock identifies an owner of a set of locks (referred
3647 to as a client). Each key is represented by a value. The set of
3648 key values used within a specific cm_scache_t structure form a
3649 namespace that has a scope of just that cm_scache_t structure. The
3650 same key value can be used with another cm_scache_t structure and
3651 correspond to a completely different client. However it is
3652 advantageous for the SMB or IFS layer to make sure that there is a
3653 1-1 mapping between client and keys over all cm_scache_t objects.
3655 Assume a client C has key Key(C) (although, since the scope of the
3656 key is a cm_scache_t, the key can be Key(C,S), where S is the
3657 cm_scache_t. But assume a 1-1 relation between keys and clients).
3658 A byte range (O,+L) denotes byte addresses (O) through (O+L-1)
3659 inclusive (a.k.a. [O,O+L-1]). The function Key(x) is implemented
3660 through cm_generateKey() function for both SMB and IFS.
3662 The list of locks for a cm_scache_t object S is maintained in
3663 S->fileLocks. The cache manager will set a lock on the AFS file
3664 server in order to assert the locks in S->fileLocks. If only
3665 shared locks are in place for S, then the cache manager will obtain
3666 a LockRead lock, while if there are any exclusive locks, it will
3667 obtain a LockWrite lock. If the exclusive locks are all released
3668 while the shared locks remain, then the cache manager will
3669 downgrade the lock from LockWrite to LockRead. Similarly, if an
3670 exclusive lock is obtained when only shared locks exist, then the
3671 cache manager will try to upgrade the lock from LockRead to
3674 Each lock L owned by client C maintains a key L->key such that
3675 L->key == Key(C), the effective range defined by L->LOffset and
3676 L->LLength such that the range of bytes affected by the lock is
3677 (L->LOffset, +L->LLength), a type maintained in L->LockType which
3678 is either exclusive or shared.
3682 A lock exists iff it is in S->fileLocks for some cm_scache_t
3683 S. Existing locks are in one of the following states: ACTIVE,
3684 WAITLOCK, WAITUNLOCK, LOST, DELETED.
3686 The following sections describe each lock and the associated
3689 1. ACTIVE: A lock L is ACTIVE iff the cache manager has asserted
3690 the lock with the AFS file server. This type of lock can be
3691 exercised by a client to read or write to the locked region (as
3694 1.1 ACTIVE->LOST: When the AFS file server fails to extend a
3695 server lock that was required to assert the lock. Before
3696 marking the lock as lost, the cache manager checks if the file
3697 has changed on the server. If the file has not changed, then
3698 the cache manager will attempt to obtain a new server lock
3699 that is sufficient to assert the client side locks for the
3700 file. If any of these fail, the lock is marked as LOST.
3701 Otherwise, it is left as ACTIVE.
3703 1.2 ACTIVE->DELETED: Lock is released.
3705 2. WAITLOCK: A lock is in a WAITLOCK state if the cache manager
3706 grants the lock but the lock is yet to be asserted with the AFS
3707 file server. Once the file server grants the lock, the state
3708 will transition to an ACTIVE lock.
3710 2.1 WAITLOCK->ACTIVE: The server granted the lock.
3712 2.2 WAITLOCK->DELETED: Lock is abandoned, or timed out during
3715 2.3 WAITLOCK->LOST: One or more locks from this client were
3716 marked as LOST. No further locks will be granted to this
3717 client until all lost locks are removed.
3719 3. WAITUNLOCK: A lock is in a WAITUNLOCK state if the cache manager
3720 receives a request for a lock that conflicts with an existing
3721 ACTIVE or WAITLOCK lock. The lock will be placed in the queue
3722 and will be granted at such time the conflicting locks are
3723 removed, at which point the state will transition to either
3726 3.1 WAITUNLOCK->ACTIVE: The conflicting lock was removed. The
3727 current serverLock is sufficient to assert this lock, or a
3728 sufficient serverLock is obtained.
3730 3.2 WAITUNLOCK->WAITLOCK: The conflicting lock was removed,
3731 however the required serverLock is yet to be asserted with the
3734 3.3 WAITUNLOCK->DELETED: The lock is abandoned, timed out or
3737 3.5 WAITUNLOCK->LOST: One or more locks from this client were
3738 marked as LOST. No further locks will be granted to this
3739 client until all lost locks are removed.
3741 4. LOST: A lock L is LOST if the server lock that was required to
3742 assert the lock could not be obtained or if it could not be
3743 extended, or if other locks by the same client were LOST.
3744 Essentially, once a lock is LOST, the contract between the cache
3745 manager and that specific client is no longer valid.
3747 The cache manager rechecks the server lock once every minute and
3748 extends it as appropriate. If this is not done for 5 minutes,
3749 the AFS file server will release the lock (the 5 minute timeout
3750 is based on current file server code and is fairly arbitrary).
3751 Once released, the lock cannot be re-obtained without verifying
3752 that the contents of the file hasn't been modified since the
3753 time the lock was released. Re-obtaining the lock without
3754 verifying this may lead to data corruption. If the lock can not
3755 be obtained safely, then all active locks for the cm_scache_t
3758 4.1 LOST->DELETED: The lock is released.
3760 5. DELETED: The lock is no longer relevant. Eventually, it will
3761 get removed from the cm_scache_t. In the meantime, it will be
3762 treated as if it does not exist.
3764 5.1 DELETED->not exist: The lock is removed from the
3767 The following are classifications of locks based on their state.
3769 6* A lock L is ACCEPTED if it is ACTIVE or WAITLOCK. These locks
3770 have been accepted by the cache manager, but may or may not have
3771 been granted back to the client.
3773 7* A lock L is QUEUED if it is ACTIVE, WAITLOCK or WAITUNLOCK.
3775 8* A lock L is WAITING if it is WAITLOCK or WAITUNLOCK.
3779 A client C can READ range (Offset,+Length) of a file represented by
3780 cm_scache_t S iff (1):
3782 1. for all _a_ in (Offset,+Length), all of the following is true:
3784 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3785 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3788 1.2 For each LOST lock L in S->fileLocks such that _a_ in
3789 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3792 (When locks are lost on an cm_scache_t, all locks are lost. By
3793 4.2 (below), if there is an exclusive LOST lock, then there
3794 can't be any overlapping ACTIVE locks.)
3796 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
3798 2. for all _a_ in (Offset,+Length), one of the following is true:
3800 2.1 Byte _a_ of S is unowned (as specified in 1.1) AND there
3801 does not exist a LOST lock L such that _a_ in
3802 (L->LOffset,+L->LLength).
3804 2.2 Byte _a_ of S is owned by C under lock L (as specified in
3805 1.2) AND L->LockType is exclusive.
3807 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
3809 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
3812 3.1 If L->LockType is exclusive then there does NOT exist a
3813 ACCEPTED lock M in S->fileLocks such that _a_ in
3814 (M->LOffset,+M->LLength).
3816 (If we count all QUEUED locks then we hit cases such as
3817 cascading waiting locks where the locks later on in the queue
3818 can be granted without compromising file integrity. On the
3819 other hand if only ACCEPTED locks are considered, then locks
3820 that were received earlier may end up waiting for locks that
3821 were received later to be unlocked. The choice of ACCEPTED
3822 locks was made to mimic the Windows byte range lock
3825 3.2 If L->LockType is shared then for each ACCEPTED lock M in
3826 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
3827 M->LockType is shared.
3829 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
3831 4.1 M->key != Key(C)
3833 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
3834 and (M->LOffset,+M->LLength) do not intersect.
3836 (Note: If a client loses a lock, it loses all locks.
3837 Subsequently, it will not be allowed to obtain any more locks
3838 until all existing LOST locks that belong to the client are
3839 released. Once all locks are released by a single client,
3840 there exists no further contract between the client and AFS
3841 about the contents of the file, hence the client can then
3842 proceed to obtain new locks and establish a new contract.
3844 This doesn't quite work as you think it should, because most
3845 applications aren't built to deal with losing locks they
3846 thought they once had. For now, we don't have a good
3847 solution to lost locks.
3849 Also, for consistency reasons, we have to hold off on
3850 granting locks that overlap exclusive LOST locks.)
3852 A client C can only unlock locks L in S->fileLocks which have
3855 The representation and invariants are as follows:
3857 - Each cm_scache_t structure keeps:
3859 - A queue of byte-range locks (cm_scache_t::fileLocks) which
3860 are of type cm_file_lock_t.
3862 - A record of the highest server-side lock that has been
3863 obtained for this object (cm_scache_t::serverLock), which is
3864 one of (-1), LockRead, LockWrite.
3866 - A count of ACCEPTED exclusive and shared locks that are in the
3867 queue (cm_scache_t::sharedLocks and
3868 cm_scache_t::exclusiveLocks)
3870 - Each cm_file_lock_t structure keeps:
3872 - The type of lock (cm_file_lock_t::LockType)
3874 - The key associated with the lock (cm_file_lock_t::key)
3876 - The offset and length of the lock (cm_file_lock_t::LOffset
3877 and cm_file_lock_t::LLength)
3879 - The state of the lock.
3881 - Time of issuance or last successful extension
3883 Semantic invariants:
3885 I1. The number of ACCEPTED locks in S->fileLocks are
3886 (S->sharedLocks + S->exclusiveLocks)
3888 External invariants:
3890 I3. S->serverLock is the lock that we have asserted with the
3891 AFS file server for this cm_scache_t.
3893 I4. S->serverLock == LockRead iff there is at least one ACTIVE
3894 shared lock, but no ACTIVE exclusive locks.
3896 I5. S->serverLock == LockWrite iff there is at least one ACTIVE
3899 I6. If L is a LOST lock, then for each lock M in S->fileLocks,
3900 M->key == L->key IMPLIES M is LOST or DELETED.
3905 #define IS_LOCK_ACTIVE(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == 0)
3907 #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)
3909 #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)
3911 #define IS_LOCK_LOST(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_LOST)
3913 #define IS_LOCK_DELETED(lockp) (((lockp)->flags & CM_FILELOCK_FLAG_DELETED) == CM_FILELOCK_FLAG_DELETED)
3916 #define IS_LOCK_ACCEPTED(lockp) (IS_LOCK_ACTIVE(lockp) || IS_LOCK_WAITLOCK(lockp))
3919 #define IS_LOCK_CLIENTONLY(lockp) ((((lockp)->scp->flags & CM_SCACHEFLAG_RO) == CM_SCACHEFLAG_RO) || (((lockp)->flags & CM_FILELOCK_FLAG_CLIENTONLY) == CM_FILELOCK_FLAG_CLIENTONLY))
3922 #define INTERSECT_RANGE(r1,r2) (((r2).offset+(r2).length) > (r1).offset && ((r1).offset +(r1).length) > (r2).offset)
3925 #define CONTAINS_RANGE(r1,r2) (((r2).offset+(r2).length) <= ((r1).offset+(r1).length) && (r1).offset <= (r2).offset)
3927 #if defined(VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS) && !defined(LOCK_TESTING)
3928 #define SCP_SUPPORTS_BRLOCKS(scp) ((scp)->cbServerp && ((scp)->cbServerp->capabilities & VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS))
3930 #define SCP_SUPPORTS_BRLOCKS(scp) (1)
3933 #define SERVERLOCKS_ENABLED(scp) (!((scp)->flags & CM_SCACHEFLAG_RO) && cm_enableServerLocks && SCP_SUPPORTS_BRLOCKS(scp))
3935 #if defined(VICED_CAPABILITY_WRITELOCKACL)
3936 #define SCP_SUPPORTS_WRITELOCKACL(scp) ((scp)->cbServerp && ((scp->cbServerp->capabilities & VICED_CAPABILITY_WRITELOCKACL)))
3938 #define SCP_SUPPORTS_WRITELOCKACL(scp) (0)
3940 /* This should really be defined in any build that this code is being
3942 #error VICED_CAPABILITY_WRITELOCKACL not defined.
3945 static void cm_LockRangeSubtract(cm_range_t * pos, const cm_range_t * neg)
3947 afs_int64 int_begin;
3950 int_begin = MAX(pos->offset, neg->offset);
3951 int_end = MIN(pos->offset+pos->length, neg->offset+neg->length);
3953 if (int_begin < int_end) {
3954 if (int_begin == pos->offset) {
3955 pos->length = pos->offset + pos->length - int_end;
3956 pos->offset = int_end;
3957 } else if (int_end == pos->offset + pos->length) {
3958 pos->length = int_begin - pos->offset;
3961 /* We only subtract ranges if the resulting range is
3962 contiguous. If we try to support non-contigous ranges, we
3963 aren't actually improving performance. */
3967 /* Called with scp->rw held. Returns 0 if all is clear to read the
3968 specified range by the client identified by key.
3970 long cm_LockCheckRead(cm_scache_t *scp,
3971 LARGE_INTEGER LOffset,
3972 LARGE_INTEGER LLength,
3975 #ifndef ADVISORY_LOCKS
3977 cm_file_lock_t *fileLock;
3981 int substract_ranges = FALSE;
3983 range.offset = LOffset.QuadPart;
3984 range.length = LLength.QuadPart;
3988 1. for all _a_ in (Offset,+Length), all of the following is true:
3990 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3991 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3994 1.2 For each LOST lock L in S->fileLocks such that _a_ in
3995 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
4000 lock_ObtainRead(&cm_scacheLock);
4002 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
4004 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4006 if (INTERSECT_RANGE(range, fileLock->range)) {
4007 if (IS_LOCK_ACTIVE(fileLock)) {
4008 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4010 /* If there is an active lock for this client, it
4011 is safe to substract ranges.*/
4012 cm_LockRangeSubtract(&range, &fileLock->range);
4013 substract_ranges = TRUE;
4015 if (fileLock->lockType != LockRead) {
4016 code = CM_ERROR_LOCK_CONFLICT;
4020 /* even if the entire range is locked for reading,
4021 we still can't grant the lock at this point
4022 because the client may have lost locks. That
4023 is, unless we have already seen an active lock
4024 belonging to the client, in which case there
4025 can't be any lost locks for this client. */
4026 if (substract_ranges)
4027 cm_LockRangeSubtract(&range, &fileLock->range);
4029 } else if (IS_LOCK_LOST(fileLock) &&
4030 (cm_KeyEquals(&fileLock->key, &key, 0) || fileLock->lockType == LockWrite)) {
4031 code = CM_ERROR_BADFD;
4037 lock_ReleaseRead(&cm_scacheLock);
4039 osi_Log4(afsd_logp, "cm_LockCheckRead scp 0x%x offset %d length %d code 0x%x",
4040 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);