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, 0, 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, 0, 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);