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, reqp, &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;
489 * see if we can find it using the directory hash tables.
490 * we can only do exact matches, since the hash is case
493 if (funcp != (cm_DirFuncp_t)cm_BPlusDirFoo)
502 code = cm_BeginDirOp(scp, userp, reqp, CM_DIRLOCK_READ, &dirop);
506 code = cm_BPlusDirLookup(&dirop, sp->nsearchNamep, &sp->fid);
511 code = cm_DirLookup(&dirop, sp->searchNamep, &sp->fid);
519 sp->ExactFound = TRUE;
520 *retscp = NULL; /* force caller to call cm_GetSCache() */
525 if (sp->caseFold && code == CM_ERROR_INEXACT_MATCH) {
528 sp->ExactFound = FALSE;
529 *retscp = NULL; /* force caller to call cm_GetSCache() */
533 return CM_ERROR_BPLUS_NOMATCH;
540 * XXX We only get the length once. It might change when we drop the
543 dirLength = scp->length;
546 bufferOffset.LowPart = bufferOffset.HighPart = 0;
548 curOffset = *startOffsetp;
550 curOffset.HighPart = 0;
551 curOffset.LowPart = 0;
555 /* make sure that curOffset.LowPart doesn't point to the first
556 * 32 bytes in the 2nd through last dir page, and that it
557 * doesn't point at the first 13 32-byte chunks in the first
558 * dir page, since those are dir and page headers, and don't
559 * contain useful information.
561 temp = curOffset.LowPart & (2048-1);
562 if (curOffset.HighPart == 0 && curOffset.LowPart < 2048) {
563 /* we're in the first page */
564 if (temp < 13*32) temp = 13*32;
567 /* we're in a later dir page */
568 if (temp < 32) temp = 32;
571 /* make sure the low order 5 bits are zero */
574 /* now put temp bits back ito curOffset.LowPart */
575 curOffset.LowPart &= ~(2048-1);
576 curOffset.LowPart |= temp;
578 /* check if we've passed the dir's EOF */
579 if (LargeIntegerGreaterThanOrEqualTo(curOffset, dirLength))
582 /* see if we can use the bufferp we have now; compute in which
583 * page the current offset would be, and check whether that's
584 * the offset of the buffer we have. If not, get the buffer.
586 thyper.HighPart = curOffset.HighPart;
587 thyper.LowPart = curOffset.LowPart & ~(cm_data.buf_blockSize-1);
588 if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
591 lock_ReleaseMutex(&bufferp->mx);
592 buf_Release(bufferp);
596 code = buf_Get(scp, &thyper, reqp, &bufferp);
598 /* if buf_Get() fails we do not have a buffer object to lock */
603 lock_ObtainMutex(&bufferp->mx);
604 bufferOffset = thyper;
606 /* now get the data in the cache */
608 lock_ObtainWrite(&scp->rw);
609 code = cm_SyncOp(scp, bufferp, userp, reqp,
611 CM_SCACHESYNC_NEEDCALLBACK
613 | CM_SCACHESYNC_BUFLOCKED);
615 lock_ReleaseWrite(&scp->rw);
618 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
620 if (cm_HaveBuffer(scp, bufferp, 1)) {
621 lock_ReleaseWrite(&scp->rw);
625 /* otherwise, load the buffer and try again */
626 lock_ReleaseMutex(&bufferp->mx);
627 code = cm_GetBuffer(scp, bufferp, NULL, userp,
629 lock_ReleaseWrite(&scp->rw);
630 lock_ObtainMutex(&bufferp->mx);
635 lock_ReleaseMutex(&bufferp->mx);
636 buf_Release(bufferp);
640 } /* if (wrong buffer) ... */
642 /* now we have the buffer containing the entry we're interested
643 * in; copy it out if it represents a non-deleted entry.
645 entryInDir = curOffset.LowPart & (2048-1);
646 entryInBuffer = curOffset.LowPart & (cm_data.buf_blockSize - 1);
648 /* page header will help tell us which entries are free. Page
649 * header can change more often than once per buffer, since
650 * AFS 3 dir page size may be less than (but not more than) a
651 * buffer package buffer.
653 /* only look intra-buffer */
654 temp = curOffset.LowPart & (cm_data.buf_blockSize - 1);
655 temp &= ~(2048 - 1); /* turn off intra-page bits */
656 pageHeaderp = (cm_pageHeader_t *) (bufferp->datap + temp);
658 /* now determine which entry we're looking at in the page. If
659 * it is free (there's a free bitmap at the start of the dir),
660 * we should skip these 32 bytes.
662 slotInPage = (entryInDir & 0x7e0) >> 5;
663 if (!(pageHeaderp->freeBitmap[slotInPage>>3]
664 & (1 << (slotInPage & 0x7)))) {
665 /* this entry is free */
666 numDirChunks = 1; /* only skip this guy */
670 tp = bufferp->datap + entryInBuffer;
671 dep = (cm_dirEntry_t *) tp; /* now points to AFS3 dir entry */
673 /* while we're here, compute the next entry's location, too,
674 * since we'll need it when writing out the cookie into the
675 * dir listing stream.
677 numDirChunks = cm_NameEntries(dep->name, NULL);
679 /* compute the offset of the cookie representing the next entry */
680 nextEntryCookie = curOffset.LowPart
681 + (CM_DIR_CHUNKSIZE * numDirChunks);
683 if (dep->fid.vnode != 0) {
684 /* this is one of the entries to use: it is not deleted */
685 code = (*funcp)(scp, dep, parmp, &curOffset);
688 } /* if we're including this name */
691 /* and adjust curOffset to be where the new cookie is */
693 thyper.LowPart = CM_DIR_CHUNKSIZE * numDirChunks;
694 curOffset = LargeIntegerAdd(thyper, curOffset);
695 } /* while copying data for dir listing */
697 /* release the mutex */
699 lock_ReleaseMutex(&bufferp->mx);
700 buf_Release(bufferp);
705 int cm_NoneUpper(normchar_t *s)
709 if (c >= 'A' && c <= 'Z')
714 int cm_NoneLower(normchar_t *s)
718 if (c >= 'a' && c <= 'z')
723 long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
726 cm_lookupSearch_t *sp;
728 normchar_t matchName[MAX_PATH];
729 int looking_for_short_name = FALSE;
731 sp = (cm_lookupSearch_t *) rockp;
733 if (cm_FsStringToNormString(dep->name, -1, matchName, lengthof(matchName)) == 0) {
734 /* Can't normalize FS string. */
739 match = cm_NormStrCmpI(matchName, sp->nsearchNamep);
741 match = cm_NormStrCmp(matchName, sp->nsearchNamep);
745 && !cm_Is8Dot3(matchName)) {
747 cm_Gen8Dot3NameInt(dep->name, &dep->fid, matchName, NULL);
749 match = cm_NormStrCmpI(matchName, sp->nsearchNamep);
751 match = cm_NormStrCmp(matchName, sp->nsearchNamep);
752 looking_for_short_name = TRUE;
762 if (!sp->caseFold || looking_for_short_name) {
763 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
764 return CM_ERROR_STOPNOW;
768 * If we get here, we are doing a case-insensitive search, and we
769 * have found a match. Now we determine what kind of match it is:
770 * exact, lower-case, upper-case, or none of the above. This is done
771 * in order to choose among matches, if there are more than one.
774 /* Exact matches are the best. */
775 match = cm_NormStrCmp(matchName, sp->nsearchNamep);
778 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
779 return CM_ERROR_STOPNOW;
782 /* Lower-case matches are next. */
785 if (cm_NoneUpper(matchName)) {
790 /* Upper-case matches are next. */
793 if (cm_NoneLower(matchName)) {
798 /* General matches are last. */
804 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
808 /* read the contents of a mount point into the appropriate string.
809 * called with write locked scp, and returns with locked scp.
811 long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
814 cm_buf_t *bufp = NULL;
818 if (scp->mountPointStringp[0])
821 #ifdef AFS_FREELANCE_CLIENT
822 /* File servers do not have data for freelance entries */
823 if (cm_freelanceEnabled &&
824 scp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
825 scp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
827 code = cm_FreelanceFetchMountPointString(scp);
829 #endif /* AFS_FREELANCE_CLIENT */
831 /* otherwise, we have to read it in */
832 lock_ReleaseWrite(&scp->rw);
834 thyper.LowPart = thyper.HighPart = 0;
835 code = buf_Get(scp, &thyper, reqp, &bufp);
837 lock_ObtainWrite(&scp->rw);
842 code = cm_SyncOp(scp, bufp, userp, reqp, 0,
843 CM_SCACHESYNC_READ | CM_SCACHESYNC_NEEDCALLBACK);
847 cm_SyncOpDone(scp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
849 if (cm_HaveBuffer(scp, bufp, 0))
852 /* otherwise load buffer */
853 code = cm_GetBuffer(scp, bufp, NULL, userp, reqp);
857 /* locked, has callback, has valid data in buffer */
858 if ((tlen = scp->length.LowPart) > MOUNTPOINTLEN - 1)
859 return CM_ERROR_TOOBIG;
861 code = CM_ERROR_INVAL;
865 /* someone else did the work while we were out */
866 if (scp->mountPointStringp[0]) {
871 /* otherwise, copy out the link */
872 memcpy(scp->mountPointStringp, bufp->datap, tlen);
874 /* now make it null-terminated. Note that the original contents of a
875 * link that is a mount point is "#volname." where "." is there just to
876 * be turned into a null. That is, we can trash the last char of the
877 * link without damaging the vol name. This is a stupid convention,
878 * but that's the protocol.
880 scp->mountPointStringp[tlen-1] = 0;
891 /* called with a locked scp and chases the mount point, yielding outScpp.
892 * scp remains write locked, just for simplicity of describing the interface.
894 long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
895 cm_req_t *reqp, cm_scache_t **outScpp)
897 fschar_t *cellNamep = NULL;
898 fschar_t *volNamep = NULL;
902 cm_volume_t *volp = NULL;
911 if (scp->mountRootFid.cell != 0 && scp->mountRootGen >= cm_data.mountRootGen) {
912 tfid = scp->mountRootFid;
913 lock_ReleaseWrite(&scp->rw);
914 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
915 lock_ObtainWrite(&scp->rw);
919 /* parse the volume name */
920 mpNamep = scp->mountPointStringp;
922 return CM_ERROR_NOSUCHPATH;
923 mtType = *scp->mountPointStringp;
925 cp = cm_FsStrChr(mpNamep, _FS(':'));
927 /* cellular mount point */
928 cellNamep = (fschar_t *)malloc((cp - mpNamep) * sizeof(fschar_t));
929 cm_FsStrCpyN(cellNamep, cp - mpNamep, mpNamep + 1, cp - mpNamep - 1);
930 volNamep = cm_FsStrDup(cp+1);
932 /* now look up the cell */
933 lock_ReleaseWrite(&scp->rw);
934 cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE);
935 lock_ObtainWrite(&scp->rw);
938 volNamep = cm_FsStrDup(mpNamep + 1);
940 #ifdef AFS_FREELANCE_CLIENT
942 * Mount points in the Freelance cell should default
943 * to the workstation cell.
945 if (cm_freelanceEnabled &&
946 scp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
947 scp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
949 fschar_t rootCellName[256]="";
950 cm_GetRootCellName(rootCellName);
951 cellp = cm_GetCell(rootCellName, 0);
953 #endif /* AFS_FREELANCE_CLIENT */
954 cellp = cm_FindCellByID(scp->fid.cell, 0);
958 code = CM_ERROR_NOSUCHCELL;
962 vnLength = cm_FsStrLen(volNamep);
963 if (vnLength >= 8 && cm_FsStrCmp(volNamep + vnLength - 7, ".backup") == 0)
964 targetType = BACKVOL;
965 else if (vnLength >= 10
966 && cm_FsStrCmp(volNamep + vnLength - 9, ".readonly") == 0)
971 /* check for backups within backups */
972 if (targetType == BACKVOL
973 && (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
974 == CM_SCACHEFLAG_RO) {
975 code = CM_ERROR_NOSUCHVOLUME;
979 /* now we need to get the volume */
980 lock_ReleaseWrite(&scp->rw);
981 if (cm_VolNameIsID(volNamep)) {
982 code = cm_FindVolumeByID(cellp, atoi(volNamep), userp, reqp,
983 CM_GETVOL_FLAG_CREATE, &volp);
985 code = cm_FindVolumeByName(cellp, volNamep, userp, reqp,
986 CM_GETVOL_FLAG_CREATE, &volp);
988 lock_ObtainWrite(&scp->rw);
991 afs_uint32 cell, volume;
992 cm_vol_state_t *statep;
994 cell = cellp->cellID;
996 /* if the mt pt originates in a .backup volume (not a .readonly)
997 * and FollowBackupPath is active, and if there is a .backup
998 * volume for the target, then use the .backup of the target
999 * instead of the read-write.
1001 if (cm_followBackupPath &&
1002 volp->vol[BACKVOL].ID != 0 &&
1003 (dscp->flags & (CM_SCACHEFLAG_RO|CM_SCACHEFLAG_PURERO)) == CM_SCACHEFLAG_RO &&
1004 (targetType == RWVOL || targetType == ROVOL && volp->vol[ROVOL].ID == 0)
1006 targetType = BACKVOL;
1008 /* if the mt pt is in a read-only volume (not just a
1009 * backup), and if there is a read-only volume for the
1010 * target, and if this is a targetType '#' mount point, use
1011 * the read-only, otherwise use the one specified.
1013 else if (mtType == '#' && targetType == RWVOL &&
1014 (scp->flags & CM_SCACHEFLAG_PURERO) &&
1015 volp->vol[ROVOL].ID != 0) {
1019 lock_ObtainWrite(&volp->rw);
1020 statep = cm_VolumeStateByType(volp, targetType);
1021 volume = statep->ID;
1022 statep->dotdotFid = dscp->fid;
1023 lock_ReleaseWrite(&volp->rw);
1025 /* the rest of the fid is a magic number */
1026 cm_SetFid(&scp->mountRootFid, cell, volume, 1, 1);
1027 scp->mountRootGen = cm_data.mountRootGen;
1029 tfid = scp->mountRootFid;
1030 lock_ReleaseWrite(&scp->rw);
1031 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
1032 lock_ObtainWrite(&scp->rw);
1045 long cm_LookupInternal(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_user_t *userp,
1046 cm_req_t *reqp, cm_scache_t **outScpp)
1049 int dnlcHit = 1; /* did we hit in the dnlc? yes, we did */
1050 cm_scache_t *tscp = NULL;
1051 cm_scache_t *mountedScp;
1052 cm_lookupSearch_t rock;
1054 normchar_t *nnamep = NULL;
1055 fschar_t *fnamep = NULL;
1059 memset(&rock, 0, sizeof(rock));
1061 if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
1062 && cm_ClientStrCmp(cnamep, _C("..")) == 0) {
1063 if (dscp->dotdotFid.volume == 0)
1064 return CM_ERROR_NOSUCHVOLUME;
1065 rock.fid = dscp->dotdotFid;
1067 } else if (cm_ClientStrCmp(cnamep, _C(".")) == 0) {
1068 rock.fid = dscp->fid;
1072 nnamep = cm_ClientStringToNormStringAlloc(cnamep, -1, NULL);
1074 code = CM_ERROR_NOSUCHFILE;
1077 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
1079 code = CM_ERROR_NOSUCHFILE;
1083 if (flags & CM_FLAG_NOMOUNTCHASE) {
1084 /* In this case, we should go and call cm_Dir* functions
1085 directly since the following cm_ApplyDir() function will
1093 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ, &dirop);
1096 code = cm_BPlusDirLookup(&dirop, nnamep, &rock.fid);
1101 code = cm_DirLookup(&dirop, fnamep, &rock.fid);
1103 cm_EndDirOp(&dirop);
1113 if (code == CM_ERROR_INEXACT_MATCH && (flags & CM_FLAG_CASEFOLD)) {
1120 return CM_ERROR_BPLUS_NOMATCH;
1125 rock.fid.cell = dscp->fid.cell;
1126 rock.fid.volume = dscp->fid.volume;
1127 rock.searchNamep = fnamep;
1128 rock.nsearchNamep = nnamep;
1129 rock.caseFold = (flags & CM_FLAG_CASEFOLD);
1130 rock.hasTilde = ((cm_ClientStrChr(cnamep, '~') != NULL) ? 1 : 0);
1132 /* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
1133 code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
1134 (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
1136 /* code == 0 means we fell off the end of the dir, while stopnow means
1137 * that we stopped early, probably because we found the entry we're
1138 * looking for. Any other non-zero code is an error.
1140 if (code && code != CM_ERROR_STOPNOW) {
1141 /* if the cm_scache_t we are searching in is not a directory
1142 * we must return path not found because the error
1143 * is to describe the final component not an intermediary
1145 if (code == CM_ERROR_NOTDIR) {
1146 if (flags & CM_FLAG_CHECKPATH)
1147 code = CM_ERROR_NOSUCHPATH;
1149 code = CM_ERROR_NOSUCHFILE;
1154 getroot = (dscp==cm_data.rootSCachep) ;
1156 if (!cm_freelanceEnabled || !getroot) {
1157 if (flags & CM_FLAG_CHECKPATH)
1158 code = CM_ERROR_NOSUCHPATH;
1160 code = CM_ERROR_NOSUCHFILE;
1163 else if (!cm_ClientStrChr(cnamep, '#') &&
1164 !cm_ClientStrChr(cnamep, '%') &&
1165 cm_ClientStrCmpI(cnamep, _C("srvsvc")) &&
1166 cm_ClientStrCmpI(cnamep, _C("wkssvc")) &&
1167 cm_ClientStrCmpI(cnamep, _C("ipc$")))
1169 /* nonexistent dir on freelance root, so add it */
1170 fschar_t fullname[CELL_MAXNAMELEN + 1] = "."; /* +1 so that when we skip the . the size is still CELL_MAXNAMELEN */
1173 osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %S",
1174 osi_LogSaveClientString(afsd_logp,cnamep));
1177 * There is an ugly behavior where a share name "foo" will be searched
1178 * for as "fo". If the searched for name differs by an already existing
1179 * symlink or mount point in the Freelance directory, do not add the
1180 * new value automatically.
1184 if (cnamep[0] == '.') {
1185 if (cm_GetCell_Gen(&fnamep[1], &fullname[1], CM_FLAG_CREATE)) {
1187 if (!cm_FreelanceMountPointExists(fullname, 0))
1188 code = cm_FreelanceAddMount(fullname, &fullname[1], "root.cell.",
1190 if ( cm_FsStrCmpI(&fnamep[1], &fullname[1]) &&
1191 !cm_FreelanceMountPointExists(fnamep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0) &&
1192 !cm_FreelanceSymlinkExists(fnamep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0))
1193 code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
1196 if (cm_GetCell_Gen(fnamep, fullname, CM_FLAG_CREATE)) {
1198 if (!cm_FreelanceMountPointExists(fullname, 0))
1199 code = cm_FreelanceAddMount(fullname, fullname, "root.cell.", 0, &rock.fid);
1200 if ( cm_FsStrCmpI(fnamep, fullname) &&
1201 !cm_FreelanceMountPointExists(fnamep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0) &&
1202 !cm_FreelanceSymlinkExists(fnamep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0))
1203 code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
1206 if (!found || code) { /* add mount point failed, so give up */
1207 if (flags & CM_FLAG_CHECKPATH)
1208 code = CM_ERROR_NOSUCHPATH;
1210 code = CM_ERROR_NOSUCHFILE;
1213 tscp = NULL; /* to force call of cm_GetSCache */
1215 if (flags & CM_FLAG_CHECKPATH)
1216 code = CM_ERROR_NOSUCHPATH;
1218 code = CM_ERROR_NOSUCHFILE;
1224 if ( !tscp ) /* we did not find it in the dnlc */
1227 code = cm_GetSCache(&rock.fid, &tscp, userp, reqp);
1231 /* tscp is now held */
1233 lock_ObtainWrite(&tscp->rw);
1234 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1235 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1237 lock_ReleaseWrite(&tscp->rw);
1238 cm_ReleaseSCache(tscp);
1241 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1242 /* tscp is now locked */
1244 if (!(flags & CM_FLAG_NOMOUNTCHASE)
1245 && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
1246 /* mount points are funny: they have a volume name to mount
1249 code = cm_ReadMountPoint(tscp, userp, reqp);
1251 code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
1253 lock_ReleaseWrite(&tscp->rw);
1254 cm_ReleaseSCache(tscp);
1261 lock_ReleaseWrite(&tscp->rw);
1264 /* copy back pointer */
1267 /* insert scache in dnlc */
1268 if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
1269 /* lock the directory entry to prevent racing callback revokes */
1270 lock_ObtainRead(&dscp->rw);
1271 if ( dscp->cbServerp != NULL && dscp->cbExpires > 0 ) {
1272 /* TODO: reuse nnamep from above */
1275 nnamep = cm_ClientStringToNormStringAlloc(cnamep, -1, NULL);
1277 cm_dnlcEnter(dscp, nnamep, tscp);
1279 lock_ReleaseRead(&dscp->rw);
1296 int cm_ExpandSysName(clientchar_t *inp, clientchar_t *outp, long outSizeCch, unsigned int index)
1301 tp = cm_ClientStrRChr(inp, '@');
1303 return 0; /* no @sys */
1305 if (cm_ClientStrCmp(tp, _C("@sys")) != 0)
1306 return 0; /* no @sys */
1308 /* caller just wants to know if this is a valid @sys type of name */
1312 if (index >= cm_sysNameCount)
1315 /* otherwise generate the properly expanded @sys name */
1316 prefixCount = (int)(tp - inp);
1318 cm_ClientStrCpyN(outp, outSizeCch, inp, prefixCount); /* copy out "a." from "a.@sys" */
1319 outp[prefixCount] = 0; /* null terminate the "a." */
1320 cm_ClientStrCat(outp, outSizeCch, cm_sysNameList[index]);/* append i386_nt40 */
1324 long cm_EvaluateVolumeReference(clientchar_t * namep, long flags, cm_user_t * userp,
1325 cm_req_t *reqp, cm_scache_t ** outScpp)
1327 afs_uint32 code = 0;
1328 fschar_t cellName[CELL_MAXNAMELEN];
1329 fschar_t volumeName[VL_MAXNAMELEN];
1333 fschar_t * fnamep = NULL;
1335 cm_cell_t * cellp = NULL;
1336 cm_volume_t * volp = NULL;
1340 int mountType = RWVOL;
1342 osi_Log1(afsd_logp, "cm_EvaluateVolumeReference for string [%S]",
1343 osi_LogSaveClientString(afsd_logp, namep));
1345 if (cm_ClientStrCmpNI(namep, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH) != 0) {
1346 goto _exit_invalid_path;
1349 /* namep is assumed to look like the following:
1351 @vol:<cellname>%<volume>\0
1353 @vol:<cellname>#<volume>\0
1357 fnamep = cm_ClientStringToFsStringAlloc(namep, -1, NULL);
1358 cp = fnamep + CM_PREFIX_VOL_CCH; /* cp points to cell name, hopefully */
1359 tp = cm_FsStrChr(cp, '%');
1361 tp = cm_FsStrChr(cp, '#');
1363 (len = tp - cp) == 0 ||
1364 len > CELL_MAXNAMELEN)
1365 goto _exit_invalid_path;
1366 cm_FsStrCpyN(cellName, lengthof(cellName), cp, len);
1371 cp = tp+1; /* cp now points to volume, supposedly */
1372 cm_FsStrCpy(volumeName, lengthof(volumeName), cp);
1374 /* OK, now we have the cell and the volume */
1375 osi_Log2(afsd_logp, " Found cell [%s] and volume [%s]",
1376 osi_LogSaveFsString(afsd_logp, cellName),
1377 osi_LogSaveFsString(afsd_logp, volumeName));
1379 cellp = cm_GetCell(cellName, CM_FLAG_CREATE);
1380 if (cellp == NULL) {
1381 goto _exit_invalid_path;
1384 len = cm_FsStrLen(volumeName);
1385 if (len >= 8 && cm_FsStrCmp(volumeName + len - 7, ".backup") == 0)
1387 else if (len >= 10 &&
1388 cm_FsStrCmp(volumeName + len - 9, ".readonly") == 0)
1393 if (cm_VolNameIsID(volumeName)) {
1394 code = cm_FindVolumeByID(cellp, atoi(volumeName), userp, reqp,
1395 CM_GETVOL_FLAG_CREATE, &volp);
1397 code = cm_FindVolumeByName(cellp, volumeName, userp, reqp,
1398 CM_GETVOL_FLAG_CREATE, &volp);
1404 if (volType == BACKVOL)
1405 volume = volp->vol[BACKVOL].ID;
1406 else if (volType == ROVOL ||
1407 (volType == RWVOL && mountType == ROVOL && volp->vol[ROVOL].ID != 0))
1408 volume = volp->vol[ROVOL].ID;
1410 volume = volp->vol[RWVOL].ID;
1412 cm_SetFid(&fid, cellp->cellID, volume, 1, 1);
1414 code = cm_GetSCache(&fid, outScpp, userp, reqp);
1427 if (flags & CM_FLAG_CHECKPATH)
1428 return CM_ERROR_NOSUCHPATH;
1430 return CM_ERROR_NOSUCHFILE;
1433 #ifdef DEBUG_REFCOUNT
1434 long cm_LookupDbg(cm_scache_t *dscp, clientchar_t *namep, long flags, cm_user_t *userp,
1435 cm_req_t *reqp, cm_scache_t **outScpp, char * file, long line)
1437 long cm_Lookup(cm_scache_t *dscp, clientchar_t *namep, long flags, cm_user_t *userp,
1438 cm_req_t *reqp, cm_scache_t **outScpp)
1442 clientchar_t tname[AFSPATHMAX];
1443 int sysNameIndex = 0;
1444 cm_scache_t *scp = NULL;
1446 #ifdef DEBUG_REFCOUNT
1447 afsi_log("%s:%d cm_Lookup dscp 0x%p ref %d", file, line, dscp, dscp->refCount, file, line);
1448 osi_Log2(afsd_logp, "cm_Lookup dscp 0x%p ref %d", dscp, dscp->refCount);
1451 if ( cm_ClientStrCmpI(namep,_C(SMB_IOCTL_FILENAME_NOSLASH)) == 0 ) {
1452 if (flags & CM_FLAG_CHECKPATH)
1453 return CM_ERROR_NOSUCHPATH;
1455 return CM_ERROR_NOSUCHFILE;
1458 if (dscp == cm_data.rootSCachep &&
1459 cm_ClientStrCmpNI(namep, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH) == 0) {
1460 return cm_EvaluateVolumeReference(namep, flags, userp, reqp, outScpp);
1463 if (cm_ExpandSysName(namep, NULL, 0, 0) > 0) {
1464 for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
1465 code = cm_ExpandSysName(namep, tname, lengthof(tname), sysNameIndex);
1467 code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
1468 #ifdef DEBUG_REFCOUNT
1469 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);
1470 osi_Log3(afsd_logp, "cm_LookupInternal (1) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1478 cm_ReleaseSCache(scp);
1482 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1483 #ifdef DEBUG_REFCOUNT
1484 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);
1485 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1492 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1493 #ifdef DEBUG_REFCOUNT
1494 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);
1495 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1501 /* None of the possible sysName expansions could be found */
1502 if (flags & CM_FLAG_CHECKPATH)
1503 return CM_ERROR_NOSUCHPATH;
1505 return CM_ERROR_NOSUCHFILE;
1508 /*! \brief Unlink a file name
1510 Encapsulates a call to RXAFS_RemoveFile().
1512 \param[in] dscp cm_scache_t pointing at the directory containing the
1513 name to be unlinked.
1515 \param[in] fnamep Original name to be unlinked. This is the
1516 name that will be passed into the RXAFS_RemoveFile() call.
1517 This parameter is optional. If not provided, the value will
1520 \param[in] came Client name to be unlinked. This name will be used
1521 to update the local directory caches.
1523 \param[in] userp cm_user_t for the request.
1525 \param[in] reqp Request tracker.
1528 long cm_Unlink(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t * cnamep,
1529 cm_user_t *userp, cm_req_t *reqp)
1535 AFSFetchStatus newDirStatus;
1537 struct rx_connection * rxconnp;
1539 cm_scache_t *scp = NULL;
1540 int free_fnamep = FALSE;
1542 memset(&volSync, 0, sizeof(volSync));
1544 if (fnamep == NULL) {
1547 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ, &dirop);
1549 code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
1552 cm_EndDirOp(&dirop);
1559 #ifdef AFS_FREELANCE_CLIENT
1560 if (cm_freelanceEnabled && dscp == cm_data.rootSCachep) {
1561 /* deleting a mount point from the root dir. */
1562 code = cm_FreelanceRemoveMount(fnamep);
1567 code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
1569 /* make sure we don't screw up the dir status during the merge */
1570 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
1572 lock_ObtainWrite(&dscp->rw);
1573 sflags = CM_SCACHESYNC_STOREDATA;
1574 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1575 lock_ReleaseWrite(&dscp->rw);
1577 cm_EndDirOp(&dirop);
1582 afsFid.Volume = dscp->fid.volume;
1583 afsFid.Vnode = dscp->fid.vnode;
1584 afsFid.Unique = dscp->fid.unique;
1586 osi_Log1(afsd_logp, "CALL RemoveFile scp 0x%p", dscp);
1588 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
1592 rxconnp = cm_GetRxConn(connp);
1593 code = RXAFS_RemoveFile(rxconnp, &afsFid, fnamep,
1594 &newDirStatus, &volSync);
1595 rx_PutConnection(rxconnp);
1597 } while (cm_Analyze(connp, userp, reqp, &dscp->fid, &volSync, NULL, NULL, code));
1598 code = cm_MapRPCError(code, reqp);
1601 osi_Log1(afsd_logp, "CALL RemoveFile FAILURE, code 0x%x", code);
1603 osi_Log0(afsd_logp, "CALL RemoveFile SUCCESS");
1606 lock_ObtainWrite(&dirop.scp->dirlock);
1607 dirop.lockType = CM_DIRLOCK_WRITE;
1609 lock_ObtainWrite(&dscp->rw);
1610 cm_dnlcRemove(dscp, cnamep);
1611 cm_SyncOpDone(dscp, NULL, sflags);
1613 cm_MergeStatus(NULL, dscp, &newDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
1614 } else if (code == CM_ERROR_NOSUCHFILE) {
1615 /* windows would not have allowed the request to delete the file
1616 * if it did not believe the file existed. therefore, we must
1617 * have an inconsistent view of the world.
1619 dscp->cbServerp = NULL;
1621 lock_ReleaseWrite(&dscp->rw);
1623 if (code == 0 && cm_CheckDirOpForSingleChange(&dirop) && cnamep) {
1624 cm_DirDeleteEntry(&dirop, fnamep);
1626 cm_BPlusDirDeleteEntry(&dirop, cnamep);
1629 cm_EndDirOp(&dirop);
1632 cm_ReleaseSCache(scp);
1634 lock_ObtainWrite(&scp->rw);
1635 if (--scp->linkCount == 0)
1636 scp->flags |= CM_SCACHEFLAG_DELETED;
1637 cm_DiscardSCache(scp);
1638 lock_ReleaseWrite(&scp->rw);
1649 /* called with a write locked vnode, and fills in the link info.
1650 * returns this the vnode still write locked.
1652 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1659 lock_AssertWrite(&linkScp->rw);
1660 if (!linkScp->mountPointStringp[0]) {
1662 #ifdef AFS_FREELANCE_CLIENT
1663 /* File servers do not have data for freelance entries */
1664 if (cm_freelanceEnabled &&
1665 linkScp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
1666 linkScp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
1668 code = cm_FreelanceFetchMountPointString(linkScp);
1670 #endif /* AFS_FREELANCE_CLIENT */
1672 /* read the link data from the file server*/
1673 lock_ReleaseWrite(&linkScp->rw);
1674 thyper.LowPart = thyper.HighPart = 0;
1675 code = buf_Get(linkScp, &thyper, reqp, &bufp);
1676 lock_ObtainWrite(&linkScp->rw);
1680 code = cm_SyncOp(linkScp, bufp, userp, reqp, 0,
1681 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1686 cm_SyncOpDone(linkScp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1688 if (cm_HaveBuffer(linkScp, bufp, 0))
1691 code = cm_GetBuffer(linkScp, bufp, NULL, userp, reqp);
1696 } /* while loop to get the data */
1698 /* now if we still have no link read in,
1699 * copy the data from the buffer */
1700 if ((temp = linkScp->length.LowPart) >= MOUNTPOINTLEN) {
1702 return CM_ERROR_TOOBIG;
1705 /* otherwise, it fits; make sure it is still null (could have
1706 * lost race with someone else referencing this link above),
1707 * and if so, copy in the data.
1709 if (!linkScp->mountPointStringp[0]) {
1710 strncpy(linkScp->mountPointStringp, bufp->datap, temp);
1711 linkScp->mountPointStringp[temp] = 0; /* null terminate */
1716 if ( !strnicmp(linkScp->mountPointStringp, "msdfs:", strlen("msdfs:")) )
1717 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1719 } /* don't have sym link contents cached */
1724 /* called with a held vnode and a path suffix, with the held vnode being a
1725 * symbolic link. Our goal is to generate a new path to interpret, and return
1726 * this new path in newSpaceBufferp. If the new vnode is relative to a dir
1727 * other than the directory containing the symbolic link, then the new root is
1728 * returned in *newRootScpp, otherwise a null is returned there.
1730 long cm_AssembleLink(cm_scache_t *linkScp, fschar_t *pathSuffixp,
1731 cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1732 cm_user_t *userp, cm_req_t *reqp)
1739 *newRootScpp = NULL;
1740 *newSpaceBufferp = NULL;
1742 lock_ObtainWrite(&linkScp->rw);
1743 code = cm_HandleLink(linkScp, userp, reqp);
1747 /* if we may overflow the buffer, bail out; buffer is signficantly
1748 * bigger than max path length, so we don't really have to worry about
1749 * being a little conservative here.
1751 if (cm_FsStrLen(linkScp->mountPointStringp) + cm_FsStrLen(pathSuffixp) + 2
1752 >= CM_UTILS_SPACESIZE) {
1753 code = CM_ERROR_TOOBIG;
1757 tsp = cm_GetSpace();
1758 linkp = linkScp->mountPointStringp;
1759 if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1760 if (strlen(linkp) > cm_mountRootLen)
1761 StringCbCopyA((char *) tsp->data, sizeof(tsp->data), linkp+cm_mountRootLen+1);
1764 *newRootScpp = cm_data.rootSCachep;
1765 cm_HoldSCache(cm_data.rootSCachep);
1766 } else if (linkp[0] == '\\' && linkp[1] == '\\') {
1767 if (!strnicmp(&linkp[2], cm_NetbiosName, (len = (long)strlen(cm_NetbiosName))))
1769 char * p = &linkp[len + 3];
1770 if (strnicmp(p, "all", 3) == 0)
1773 StringCbCopyA(tsp->data, sizeof(tsp->data), p);
1774 for (p = tsp->data; *p; p++) {
1778 *newRootScpp = cm_data.rootSCachep;
1779 cm_HoldSCache(cm_data.rootSCachep);
1781 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1782 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1783 code = CM_ERROR_PATH_NOT_COVERED;
1785 } else if ( linkScp->fileType == CM_SCACHETYPE_DFSLINK ||
1786 !strnicmp(linkp, "msdfs:", (len = (long)strlen("msdfs:"))) ) {
1787 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1788 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1789 code = CM_ERROR_PATH_NOT_COVERED;
1790 } else if (*linkp == '\\' || *linkp == '/') {
1792 /* formerly, this was considered to be from the AFS root,
1793 * but this seems to create problems. instead, we will just
1794 * reject the link */
1795 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp+1);
1796 *newRootScpp = cm_data.rootSCachep;
1797 cm_HoldSCache(cm_data.rootSCachep);
1799 /* we still copy the link data into the response so that
1800 * the user can see what the link points to
1802 linkScp->fileType = CM_SCACHETYPE_INVALID;
1803 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1804 code = CM_ERROR_NOSUCHPATH;
1807 /* a relative link */
1808 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1810 if (pathSuffixp[0] != 0) { /* if suffix string is non-null */
1811 StringCchCatA(tsp->data,lengthof(tsp->data), "\\");
1812 StringCchCatA(tsp->data,lengthof(tsp->data), pathSuffixp);
1816 clientchar_t * cpath = cm_FsStringToClientStringAlloc(tsp->data, -1, NULL);
1817 if (cpath != NULL) {
1818 cm_ClientStrCpy(tsp->wdata, lengthof(tsp->wdata), cpath);
1820 *newSpaceBufferp = tsp;
1822 code = CM_ERROR_NOSUCHPATH;
1829 if (code == CM_ERROR_PATH_NOT_COVERED && reqp->tidPathp && reqp->relPathp) {
1830 cm_VolStatus_Notify_DFS_Mapping(linkScp, reqp->tidPathp, reqp->relPathp);
1835 lock_ReleaseWrite(&linkScp->rw);
1838 #ifdef DEBUG_REFCOUNT
1839 long cm_NameIDbg(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
1840 cm_user_t *userp, clientchar_t *tidPathp, cm_req_t *reqp,
1841 cm_scache_t **outScpp,
1842 char * file, long line)
1844 long cm_NameI(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
1845 cm_user_t *userp, clientchar_t *tidPathp,
1846 cm_req_t *reqp, cm_scache_t **outScpp)
1850 clientchar_t *tp; /* ptr moving through input buffer */
1851 clientchar_t tc; /* temp char */
1852 int haveComponent; /* has new component started? */
1853 clientchar_t component[AFSPATHMAX]; /* this is the new component */
1854 clientchar_t *cp; /* component name being assembled */
1855 cm_scache_t *tscp; /* current location in the hierarchy */
1856 cm_scache_t *nscp; /* next dude down */
1857 cm_scache_t *dirScp; /* last dir we searched */
1858 cm_scache_t *linkScp; /* new root for the symlink we just
1860 cm_space_t *psp; /* space for current path, if we've hit
1862 cm_space_t *tempsp; /* temp vbl */
1863 clientchar_t *restp; /* rest of the pathname to interpret */
1864 int symlinkCount; /* count of # of symlinks traversed */
1865 int extraFlag; /* avoid chasing mt pts for dir cmd */
1866 int phase = 1; /* 1 = tidPathp, 2 = pathp */
1867 #define MAX_FID_COUNT 512
1868 cm_fid_t fids[MAX_FID_COUNT]; /* array of fids processed in this path walk */
1869 int fid_count = 0; /* number of fids processed in this path walk */
1874 #ifdef DEBUG_REFCOUNT
1875 afsi_log("%s:%d cm_NameI rootscp 0x%p ref %d", file, line, rootSCachep, rootSCachep->refCount);
1876 osi_Log4(afsd_logp,"cm_NameI rootscp 0x%p path %S tidpath %S flags 0x%x",
1877 rootSCachep, pathp ? pathp : L"<NULL>", tidPathp ? tidPathp : L"<NULL>",
1892 cm_HoldSCache(tscp);
1900 /* map Unix slashes into DOS ones so we can interpret Unix
1906 if (!haveComponent) {
1909 } else if (tc == 0) {
1923 /* we have a component here */
1924 if (tc == 0 || tc == '\\') {
1925 /* end of the component; we're at the last
1926 * component if tc == 0. However, if the last
1927 * is a symlink, we have more to do.
1929 *cp++ = 0; /* add null termination */
1931 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
1932 extraFlag = CM_FLAG_NOMOUNTCHASE;
1933 code = cm_Lookup(tscp, component,
1935 userp, reqp, &nscp);
1938 if (!cm_ClientStrCmp(component,_C("..")) ||
1939 !cm_ClientStrCmp(component,_C("."))) {
1941 * roll back the fid list until we find the
1942 * fid that matches where we are now. Its not
1943 * necessarily one or two fids because they
1944 * might have been symlinks or mount points or
1945 * both that were crossed.
1947 for ( i=fid_count-1; i>=0; i--) {
1948 if (!cm_FidCmp(&nscp->fid, &fids[i]))
1953 /* add the new fid to the list */
1954 if (fid_count == MAX_FID_COUNT) {
1955 code = CM_ERROR_TOO_MANY_SYMLINKS;
1956 cm_ReleaseSCache(nscp);
1960 fids[fid_count++] = nscp->fid;
1965 cm_ReleaseSCache(tscp);
1967 cm_ReleaseSCache(dirScp);
1970 if ((code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH) &&
1971 tscp->fileType == CM_SCACHETYPE_SYMLINK) {
1972 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_NOSUCHPATH");
1973 return CM_ERROR_NOSUCHPATH;
1975 osi_Log1(afsd_logp,"cm_NameI code 0x%x", code);
1980 haveComponent = 0; /* component done */
1982 cm_ReleaseSCache(dirScp);
1983 dirScp = tscp; /* for some symlinks */
1984 tscp = nscp; /* already held */
1986 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
1989 cm_ReleaseSCache(dirScp);
1995 /* now, if tscp is a symlink, we should follow it and
1996 * assemble the path again.
1998 lock_ObtainWrite(&tscp->rw);
1999 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
2000 CM_SCACHESYNC_GETSTATUS
2001 | CM_SCACHESYNC_NEEDCALLBACK);
2003 lock_ReleaseWrite(&tscp->rw);
2004 cm_ReleaseSCache(tscp);
2007 cm_ReleaseSCache(dirScp);
2012 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2014 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
2015 /* this is a symlink; assemble a new buffer */
2016 lock_ReleaseWrite(&tscp->rw);
2017 if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
2018 cm_ReleaseSCache(tscp);
2021 cm_ReleaseSCache(dirScp);
2026 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_TOO_MANY_SYMLINKS");
2027 return CM_ERROR_TOO_MANY_SYMLINKS;
2037 /* TODO: make this better */
2038 frestp = cm_ClientStringToFsStringAlloc(restp, -1, NULL);
2039 code = cm_AssembleLink(tscp, frestp, &linkScp, &tempsp, userp, reqp);
2043 if (code == 0 && linkScp != NULL) {
2044 if (linkScp == cm_data.rootSCachep) {
2048 for ( i=0; i<fid_count; i++) {
2049 if ( !cm_FidCmp(&linkScp->fid, &fids[i]) ) {
2050 code = CM_ERROR_TOO_MANY_SYMLINKS;
2051 cm_ReleaseSCache(linkScp);
2057 if (i == fid_count && fid_count < MAX_FID_COUNT) {
2058 fids[fid_count++] = linkScp->fid;
2063 /* something went wrong */
2064 cm_ReleaseSCache(tscp);
2067 cm_ReleaseSCache(dirScp);
2073 /* otherwise, tempsp has the new path,
2074 * and linkScp is the new root from
2075 * which to interpret that path.
2076 * Continue with the namei processing,
2077 * also doing the bookkeeping for the
2078 * space allocation and tracking the
2079 * vnode reference counts.
2085 cm_ReleaseSCache(tscp);
2090 * now, if linkScp is null, that's
2091 * AssembleLink's way of telling us that
2092 * the sym link is relative to the dir
2093 * containing the link. We have a ref
2094 * to it in dirScp, and we hold it now
2095 * and reuse it as the new spot in the
2103 /* not a symlink, we may be done */
2104 lock_ReleaseWrite(&tscp->rw);
2112 cm_ReleaseSCache(dirScp);
2120 cm_ReleaseSCache(dirScp);
2123 } /* end of a component */
2126 } /* we have a component */
2127 } /* big while loop over all components */
2131 cm_ReleaseSCache(dirScp);
2137 cm_ReleaseSCache(tscp);
2139 #ifdef DEBUG_REFCOUNT
2140 afsi_log("%s:%d cm_NameI code 0x%x outScpp 0x%p ref %d", file, line, code, *outScpp, (*outScpp) ? (*outScpp)->refCount : 0);
2142 osi_Log2(afsd_logp,"cm_NameI code 0x%x outScpp 0x%p", code, *outScpp);
2146 /* called with a dir, and a vnode within the dir that happens to be a symlink.
2147 * We chase the link, and return a held pointer to the target, if it exists,
2148 * in *outScpp. If we succeed, we return 0, otherwise we return an error code
2149 * and do not hold or return a target vnode.
2151 * This is very similar to calling cm_NameI with the last component of a name,
2152 * which happens to be a symlink, except that we've already passed by the name.
2154 * This function is typically called by the directory listing functions, which
2155 * encounter symlinks but need to return the proper file length so programs
2156 * like "more" work properly when they make use of the attributes retrieved from
2159 * The input vnode should not be locked when this function is called.
2161 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
2162 cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
2166 cm_scache_t *newRootScp;
2170 osi_Log1(afsd_logp, "Evaluating symlink scp 0x%p", linkScp);
2172 code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
2176 /* now, if newRootScp is NULL, we're really being told that the symlink
2177 * is relative to the current directory (dscp).
2179 if (newRootScp == NULL) {
2181 cm_HoldSCache(dscp);
2184 code = cm_NameI(newRootScp, spacep->wdata,
2185 CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
2186 userp, NULL, reqp, outScpp);
2188 if (code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH)
2189 code = CM_ERROR_NOSUCHPATH;
2191 /* this stuff is allocated no matter what happened on the namei call,
2193 cm_FreeSpace(spacep);
2194 cm_ReleaseSCache(newRootScp);
2196 if (linkScp == *outScpp) {
2197 cm_ReleaseSCache(*outScpp);
2199 code = CM_ERROR_NOSUCHPATH;
2205 /* for a given entry, make sure that it isn't in the stat cache, and then
2206 * add it to the list of file IDs to be obtained.
2208 * Don't bother adding it if we already have a vnode. Note that the dir
2209 * is locked, so we have to be careful checking the vnode we're thinking of
2210 * processing, to avoid deadlocks.
2212 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
2223 /* Don't overflow bsp. */
2224 if (bsp->counter >= CM_BULKMAX)
2225 return CM_ERROR_STOPNOW;
2227 thyper.LowPart = cm_data.buf_blockSize;
2228 thyper.HighPart = 0;
2229 thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
2231 /* thyper is now the first byte past the end of the record we're
2232 * interested in, and bsp->bufOffset is the first byte of the record
2233 * we're interested in.
2234 * Skip data in the others.
2237 if (LargeIntegerLessThan(*offp, bsp->bufOffset))
2239 if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
2240 return CM_ERROR_STOPNOW;
2241 if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
2244 cm_SetFid(&tfid, scp->fid.cell, scp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
2245 tscp = cm_FindSCache(&tfid);
2247 if (lock_TryWrite(&tscp->rw)) {
2248 /* we have an entry that we can look at */
2249 if (!(tscp->flags & CM_SCACHEFLAG_EACCESS) && cm_HaveCallback(tscp)) {
2250 /* we have a callback on it. Don't bother
2251 * fetching this stat entry, since we're happy
2252 * with the info we have.
2254 lock_ReleaseWrite(&tscp->rw);
2255 cm_ReleaseSCache(tscp);
2258 lock_ReleaseWrite(&tscp->rw);
2260 cm_ReleaseSCache(tscp);
2263 #ifdef AFS_FREELANCE_CLIENT
2264 // yj: if this is a mountpoint under root.afs then we don't want it
2265 // to be bulkstat-ed, instead, we call getSCache directly and under
2266 // getSCache, it is handled specially.
2267 if ( cm_freelanceEnabled &&
2268 tfid.cell==AFS_FAKE_ROOT_CELL_ID &&
2269 tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
2270 !(tfid.vnode==0x1 && tfid.unique==0x1) )
2272 osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
2273 return cm_GetSCache(&tfid, &tscp, NULL, NULL);
2275 #endif /* AFS_FREELANCE_CLIENT */
2278 bsp->fids[i].Volume = scp->fid.volume;
2279 bsp->fids[i].Vnode = tfid.vnode;
2280 bsp->fids[i].Unique = tfid.unique;
2285 cm_TryBulkStatRPC(cm_scache_t *dscp, cm_bulkStat_t *bbp, cm_user_t *userp, cm_req_t *reqp)
2288 AFSCBFids fidStruct;
2289 AFSBulkStats statStruct;
2291 AFSCBs callbackStruct;
2294 cm_callbackRequest_t cbReq;
2300 struct rx_connection * rxconnp;
2301 int inlinebulk = 0; /* Did we use InlineBulkStatus RPC or not? */
2303 memset(&volSync, 0, sizeof(volSync));
2305 /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
2306 * make the calls to create the entries. Handle AFSCBMAX files at a
2309 for (filex = 0; filex < bbp->counter; filex += filesThisCall) {
2310 filesThisCall = bbp->counter - filex;
2311 if (filesThisCall > AFSCBMAX)
2312 filesThisCall = AFSCBMAX;
2314 fidStruct.AFSCBFids_len = filesThisCall;
2315 fidStruct.AFSCBFids_val = &bbp->fids[filex];
2316 statStruct.AFSBulkStats_len = filesThisCall;
2317 statStruct.AFSBulkStats_val = &bbp->stats[filex];
2318 callbackStruct.AFSCBs_len = filesThisCall;
2319 callbackStruct.AFSCBs_val = &bbp->callbacks[filex];
2320 cm_StartCallbackGrantingCall(NULL, &cbReq);
2321 osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
2323 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2327 rxconnp = cm_GetRxConn(connp);
2328 if (!(connp->serverp->flags & CM_SERVERFLAG_NOINLINEBULK)) {
2329 code = RXAFS_InlineBulkStatus(rxconnp, &fidStruct,
2330 &statStruct, &callbackStruct, &volSync);
2331 if (code == RXGEN_OPCODE) {
2332 cm_SetServerNoInlineBulk(connp->serverp, 0);
2338 code = RXAFS_BulkStatus(rxconnp, &fidStruct,
2339 &statStruct, &callbackStruct, &volSync);
2341 rx_PutConnection(rxconnp);
2343 } while (cm_Analyze(connp, userp, reqp, &dscp->fid,
2344 &volSync, NULL, &cbReq, code));
2345 code = cm_MapRPCError(code, reqp);
2347 /* may as well quit on an error, since we're not going to do
2348 * much better on the next immediate call, either.
2351 osi_Log2(afsd_logp, "CALL %sBulkStatus FAILURE code 0x%x",
2352 inlinebulk ? "Inline" : "", code);
2353 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2356 osi_Log1(afsd_logp, "CALL %sBulkStatus SUCCESS", inlinebulk ? "Inline" : "");
2359 /* otherwise, we should do the merges */
2360 for (i = 0; i<filesThisCall; i++) {
2362 cm_SetFid(&tfid, dscp->fid.cell, bbp->fids[j].Volume, bbp->fids[j].Vnode, bbp->fids[j].Unique);
2363 code = cm_GetSCache(&tfid, &scp, userp, reqp);
2367 /* otherwise, if this entry has no callback info,
2370 lock_ObtainWrite(&scp->rw);
2371 /* now, we have to be extra paranoid on merging in this
2372 * information, since we didn't use cm_SyncOp before
2373 * starting the fetch to make sure that no bad races
2374 * were occurring. Specifically, we need to make sure
2375 * we don't obliterate any newer information in the
2376 * vnode than have here.
2378 * Right now, be pretty conservative: if there's a
2379 * callback or a pending call, skip it.
2380 * However, if the prior attempt to obtain status
2381 * was refused access or the volume is .readonly,
2382 * take the data in any case since we have nothing
2383 * better for the in flight directory enumeration that
2384 * resulted in this function being called.
2386 if ((scp->cbServerp == NULL &&
2387 !(scp->flags & (CM_SCACHEFLAG_FETCHING | CM_SCACHEFLAG_STORING | CM_SCACHEFLAG_SIZESTORING))) ||
2388 (scp->flags & CM_SCACHEFLAG_PURERO) ||
2389 (scp->flags & CM_SCACHEFLAG_EACCESS)) {
2390 cm_EndCallbackGrantingCall(scp, &cbReq,
2393 CM_CALLBACK_MAINTAINCOUNT);
2394 cm_MergeStatus(dscp, scp, &bbp->stats[j], &volSync, userp, reqp, 0);
2396 lock_ReleaseWrite(&scp->rw);
2397 cm_ReleaseSCache(scp);
2398 } /* all files in the response */
2399 /* now tell it to drop the count,
2400 * after doing the vnode processing above */
2401 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2402 } /* while there are still more files to process */
2404 /* If we did the InlineBulk RPC pull out the return code and log it */
2406 if ((&bbp->stats[0])->errorCode) {
2407 osi_Log1(afsd_logp, "cm_TryBulkStat bulk stat error: %d",
2408 (&bbp->stats[0])->errorCode);
2409 code = (&bbp->stats[0])->errorCode;
2416 /* called with a write locked scp and a pointer to a buffer. Make bulk stat
2417 * calls on all undeleted files in the page of the directory specified.
2420 cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
2426 osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%p", dscp);
2428 /* should be on a buffer boundary */
2429 osi_assertx((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0, "invalid offset");
2431 bbp = malloc(sizeof(cm_bulkStat_t));
2432 memset(bbp, 0, sizeof(cm_bulkStat_t));
2433 bbp->bufOffset = *offsetp;
2435 lock_ReleaseWrite(&dscp->rw);
2436 /* first, assemble the file IDs we need to stat */
2437 code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) bbp, offsetp, userp, reqp, NULL);
2439 /* if we failed, bail out early */
2440 if (code && code != CM_ERROR_STOPNOW) {
2442 lock_ObtainWrite(&dscp->rw);
2446 code = cm_TryBulkStatRPC(dscp, bbp, userp, reqp);
2447 osi_Log1(afsd_logp, "END cm_TryBulkStat code 0x%x", code);
2449 lock_ObtainWrite(&dscp->rw);
2454 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
2458 /* initialize store back mask as inexpensive local variable */
2460 memset(statusp, 0, sizeof(AFSStoreStatus));
2462 /* copy out queued info from scache first, if scp passed in */
2464 if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
2465 statusp->ClientModTime = scp->clientModTime;
2466 mask |= AFS_SETMODTIME;
2467 scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
2472 /* now add in our locally generated request */
2473 if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
2474 statusp->ClientModTime = attrp->clientModTime;
2475 mask |= AFS_SETMODTIME;
2477 if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
2478 statusp->UnixModeBits = attrp->unixModeBits;
2479 mask |= AFS_SETMODE;
2481 if (attrp->mask & CM_ATTRMASK_OWNER) {
2482 statusp->Owner = attrp->owner;
2483 mask |= AFS_SETOWNER;
2485 if (attrp->mask & CM_ATTRMASK_GROUP) {
2486 statusp->Group = attrp->group;
2487 mask |= AFS_SETGROUP;
2490 statusp->Mask = mask;
2493 /* set the file size, and make sure that all relevant buffers have been
2494 * truncated. Ensure that any partially truncated buffers have been zeroed
2495 * to the end of the buffer.
2497 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
2503 /* start by locking out buffer creation */
2504 lock_ObtainWrite(&scp->bufCreateLock);
2506 /* verify that this is a file, not a dir or a symlink */
2507 lock_ObtainWrite(&scp->rw);
2508 code = cm_SyncOp(scp, NULL, userp, reqp, 0,
2509 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2512 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2514 if (scp->fileType != CM_SCACHETYPE_FILE) {
2515 code = CM_ERROR_ISDIR;
2520 if (LargeIntegerLessThan(*sizep, scp->length))
2525 lock_ReleaseWrite(&scp->rw);
2527 /* can't hold scp->rw lock here, since we may wait for a storeback to
2528 * finish if the buffer package is cleaning a buffer by storing it to
2532 buf_Truncate(scp, userp, reqp, sizep);
2534 /* now ensure that file length is short enough, and update truncPos */
2535 lock_ObtainWrite(&scp->rw);
2537 /* make sure we have a callback (so we have the right value for the
2538 * length), and wait for it to be safe to do a truncate.
2540 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
2541 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2542 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2544 /* If we only have 'i' bits, then we should still be able to set
2545 the size of a file we created. */
2546 if (code == CM_ERROR_NOACCESS && scp->creator == userp) {
2547 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
2548 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2549 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2555 if (LargeIntegerLessThan(*sizep, scp->length)) {
2556 /* a real truncation. If truncPos is not set yet, or is bigger
2557 * than where we're truncating the file, set truncPos to this
2562 if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
2563 || LargeIntegerLessThan(*sizep, scp->length)) {
2565 scp->truncPos = *sizep;
2566 scp->mask |= CM_SCACHEMASK_TRUNCPOS;
2568 /* in either case, the new file size has been changed */
2569 scp->length = *sizep;
2570 scp->mask |= CM_SCACHEMASK_LENGTH;
2572 else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
2573 /* really extending the file */
2574 scp->length = *sizep;
2575 scp->mask |= CM_SCACHEMASK_LENGTH;
2578 /* done successfully */
2581 cm_SyncOpDone(scp, NULL,
2582 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2583 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2586 lock_ReleaseWrite(&scp->rw);
2587 lock_ReleaseWrite(&scp->bufCreateLock);
2592 /* set the file size or other attributes (but not both at once) */
2593 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
2597 AFSFetchStatus afsOutStatus;
2601 AFSStoreStatus afsInStatus;
2602 struct rx_connection * rxconnp;
2604 memset(&volSync, 0, sizeof(volSync));
2606 /* handle file length setting */
2607 if (attrp->mask & CM_ATTRMASK_LENGTH)
2608 return cm_SetLength(scp, &attrp->length, userp, reqp);
2610 lock_ObtainWrite(&scp->rw);
2611 /* otherwise, we have to make an RPC to get the status */
2612 code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
2614 lock_ReleaseWrite(&scp->rw);
2617 lock_ConvertWToR(&scp->rw);
2619 /* make the attr structure */
2620 cm_StatusFromAttr(&afsInStatus, scp, attrp);
2622 tfid.Volume = scp->fid.volume;
2623 tfid.Vnode = scp->fid.vnode;
2624 tfid.Unique = scp->fid.unique;
2625 lock_ReleaseRead(&scp->rw);
2627 /* now make the RPC */
2628 osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%p", scp);
2630 code = cm_ConnFromFID(&scp->fid, userp, reqp, &connp);
2634 rxconnp = cm_GetRxConn(connp);
2635 code = RXAFS_StoreStatus(rxconnp, &tfid,
2636 &afsInStatus, &afsOutStatus, &volSync);
2637 rx_PutConnection(rxconnp);
2639 } while (cm_Analyze(connp, userp, reqp,
2640 &scp->fid, &volSync, NULL, NULL, code));
2641 code = cm_MapRPCError(code, reqp);
2644 osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
2646 osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
2648 lock_ObtainWrite(&scp->rw);
2649 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
2651 cm_MergeStatus(NULL, scp, &afsOutStatus, &volSync, userp, reqp,
2652 CM_MERGEFLAG_FORCE|CM_MERGEFLAG_STOREDATA);
2654 /* if we're changing the mode bits, discard the ACL cache,
2655 * since we changed the mode bits.
2657 if (afsInStatus.Mask & AFS_SETMODE)
2658 cm_FreeAllACLEnts(scp);
2659 lock_ReleaseWrite(&scp->rw);
2663 long cm_Create(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
2664 cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
2669 cm_callbackRequest_t cbReq;
2672 cm_scache_t *scp = NULL;
2674 AFSStoreStatus inStatus;
2675 AFSFetchStatus updatedDirStatus;
2676 AFSFetchStatus newFileStatus;
2677 AFSCallBack newFileCallback;
2679 struct rx_connection * rxconnp;
2681 fschar_t * fnamep = NULL;
2683 memset(&volSync, 0, sizeof(volSync));
2685 /* can't create names with @sys in them; must expand it manually first.
2686 * return "invalid request" if they try.
2688 if (cm_ExpandSysName(cnamep, NULL, 0, 0)) {
2689 return CM_ERROR_ATSYS;
2692 #ifdef AFS_FREELANCE_CLIENT
2693 /* Freelance root volume does not hold files */
2694 if (cm_freelanceEnabled &&
2695 dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
2696 dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
2698 return CM_ERROR_NOACCESS;
2700 #endif /* AFS_FREELANCE_CLIENT */
2702 /* before starting the RPC, mark that we're changing the file data, so
2703 * that someone who does a chmod will know to wait until our call
2706 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2707 lock_ObtainWrite(&dscp->rw);
2708 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2709 lock_ReleaseWrite(&dscp->rw);
2711 cm_StartCallbackGrantingCall(NULL, &cbReq);
2713 cm_EndDirOp(&dirop);
2720 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
2722 cm_StatusFromAttr(&inStatus, NULL, attrp);
2724 /* try the RPC now */
2725 osi_Log1(afsd_logp, "CALL CreateFile scp 0x%p", dscp);
2727 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2731 dirAFSFid.Volume = dscp->fid.volume;
2732 dirAFSFid.Vnode = dscp->fid.vnode;
2733 dirAFSFid.Unique = dscp->fid.unique;
2735 rxconnp = cm_GetRxConn(connp);
2736 code = RXAFS_CreateFile(connp->rxconnp, &dirAFSFid, fnamep,
2737 &inStatus, &newAFSFid, &newFileStatus,
2738 &updatedDirStatus, &newFileCallback,
2740 rx_PutConnection(rxconnp);
2742 } while (cm_Analyze(connp, userp, reqp,
2743 &dscp->fid, &volSync, NULL, &cbReq, code));
2744 code = cm_MapRPCError(code, reqp);
2747 osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
2749 osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
2752 lock_ObtainWrite(&dirop.scp->dirlock);
2753 dirop.lockType = CM_DIRLOCK_WRITE;
2755 lock_ObtainWrite(&dscp->rw);
2756 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2758 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
2760 lock_ReleaseWrite(&dscp->rw);
2762 /* now try to create the file's entry, too, but be careful to
2763 * make sure that we don't merge in old info. Since we weren't locking
2764 * out any requests during the file's creation, we may have pretty old
2768 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
2769 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2771 lock_ObtainWrite(&scp->rw);
2772 scp->creator = userp; /* remember who created it */
2773 if (!cm_HaveCallback(scp)) {
2774 cm_EndCallbackGrantingCall(scp, &cbReq,
2775 &newFileCallback, &volSync, 0);
2776 cm_MergeStatus(dscp, scp, &newFileStatus, &volSync,
2780 lock_ReleaseWrite(&scp->rw);
2784 /* make sure we end things properly */
2786 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2788 if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
2789 cm_DirCreateEntry(&dirop, fnamep, &newFid);
2791 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
2794 cm_EndDirOp(&dirop);
2803 cm_ReleaseSCache(scp);
2809 * locked if TRUE means write-locked
2810 * else the cm_scache_t rw must not be held
2812 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp, afs_uint32 locked)
2817 lock_ReleaseWrite(&scp->rw);
2818 code = buf_CleanVnode(scp, userp, reqp);
2820 lock_ObtainWrite(&scp->rw);
2822 if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
2823 | CM_SCACHEMASK_CLIENTMODTIME
2824 | CM_SCACHEMASK_LENGTH))
2825 code = cm_StoreMini(scp, userp, reqp);
2827 if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) {
2828 code = (scp->flags & CM_SCACHEFLAG_OVERQUOTA) ? CM_ERROR_QUOTA : CM_ERROR_SPACE;
2829 scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE);
2833 lock_ReleaseWrite(&scp->rw);
2834 } else if (locked) {
2835 lock_ObtainWrite(&scp->rw);
2840 long cm_MakeDir(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
2841 cm_user_t *userp, cm_req_t *reqp, cm_scache_t **scpp)
2846 cm_callbackRequest_t cbReq;
2849 cm_scache_t *scp = NULL;
2851 AFSStoreStatus inStatus;
2852 AFSFetchStatus updatedDirStatus;
2853 AFSFetchStatus newDirStatus;
2854 AFSCallBack newDirCallback;
2856 struct rx_connection * rxconnp;
2858 fschar_t * fnamep = NULL;
2860 memset(&volSync, 0, sizeof(volSync));
2862 /* can't create names with @sys in them; must expand it manually first.
2863 * return "invalid request" if they try.
2865 if (cm_ExpandSysName(cnamep, NULL, 0, 0)) {
2866 return CM_ERROR_ATSYS;
2869 #ifdef AFS_FREELANCE_CLIENT
2870 /* Freelance root volume does not hold subdirectories */
2871 if (cm_freelanceEnabled &&
2872 dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
2873 dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
2875 return CM_ERROR_NOACCESS;
2877 #endif /* AFS_FREELANCE_CLIENT */
2879 /* before starting the RPC, mark that we're changing the directory
2880 * data, so that someone who does a chmod on the dir will wait until
2881 * our call completes.
2883 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2884 lock_ObtainWrite(&dscp->rw);
2885 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2886 lock_ReleaseWrite(&dscp->rw);
2888 cm_StartCallbackGrantingCall(NULL, &cbReq);
2890 cm_EndDirOp(&dirop);
2897 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
2898 cm_StatusFromAttr(&inStatus, NULL, attrp);
2900 /* try the RPC now */
2901 osi_Log1(afsd_logp, "CALL MakeDir scp 0x%p", dscp);
2903 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2907 dirAFSFid.Volume = dscp->fid.volume;
2908 dirAFSFid.Vnode = dscp->fid.vnode;
2909 dirAFSFid.Unique = dscp->fid.unique;
2911 rxconnp = cm_GetRxConn(connp);
2912 code = RXAFS_MakeDir(connp->rxconnp, &dirAFSFid, fnamep,
2913 &inStatus, &newAFSFid, &newDirStatus,
2914 &updatedDirStatus, &newDirCallback,
2916 rx_PutConnection(rxconnp);
2918 } while (cm_Analyze(connp, userp, reqp,
2919 &dscp->fid, &volSync, NULL, &cbReq, code));
2920 code = cm_MapRPCError(code, reqp);
2923 osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
2925 osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
2928 lock_ObtainWrite(&dirop.scp->dirlock);
2929 dirop.lockType = CM_DIRLOCK_WRITE;
2931 lock_ObtainWrite(&dscp->rw);
2932 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2934 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
2936 lock_ReleaseWrite(&dscp->rw);
2938 /* now try to create the new dir's entry, too, but be careful to
2939 * make sure that we don't merge in old info. Since we weren't locking
2940 * out any requests during the file's creation, we may have pretty old
2944 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
2945 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2947 lock_ObtainWrite(&scp->rw);
2948 if (!cm_HaveCallback(scp)) {
2949 cm_EndCallbackGrantingCall(scp, &cbReq,
2950 &newDirCallback, &volSync, 0);
2951 cm_MergeStatus(dscp, scp, &newDirStatus, &volSync,
2955 lock_ReleaseWrite(&scp->rw);
2959 /* make sure we end things properly */
2961 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2963 if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
2964 cm_DirCreateEntry(&dirop, fnamep, &newFid);
2966 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
2969 cm_EndDirOp(&dirop);
2977 cm_ReleaseSCache(scp);
2980 /* and return error code */
2984 long cm_Link(cm_scache_t *dscp, clientchar_t *cnamep, cm_scache_t *sscp, long flags,
2985 cm_user_t *userp, cm_req_t *reqp)
2990 AFSFid existingAFSFid;
2991 AFSFetchStatus updatedDirStatus;
2992 AFSFetchStatus newLinkStatus;
2994 struct rx_connection * rxconnp;
2996 fschar_t * fnamep = NULL;
2998 memset(&volSync, 0, sizeof(volSync));
3000 if (dscp->fid.cell != sscp->fid.cell ||
3001 dscp->fid.volume != sscp->fid.volume) {
3002 return CM_ERROR_CROSSDEVLINK;
3005 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
3006 lock_ObtainWrite(&dscp->rw);
3007 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3008 lock_ReleaseWrite(&dscp->rw);
3010 cm_EndDirOp(&dirop);
3015 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3017 /* try the RPC now */
3018 osi_Log1(afsd_logp, "CALL Link scp 0x%p", dscp);
3020 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3023 dirAFSFid.Volume = dscp->fid.volume;
3024 dirAFSFid.Vnode = dscp->fid.vnode;
3025 dirAFSFid.Unique = dscp->fid.unique;
3027 existingAFSFid.Volume = sscp->fid.volume;
3028 existingAFSFid.Vnode = sscp->fid.vnode;
3029 existingAFSFid.Unique = sscp->fid.unique;
3031 rxconnp = cm_GetRxConn(connp);
3032 code = RXAFS_Link(rxconnp, &dirAFSFid, fnamep, &existingAFSFid,
3033 &newLinkStatus, &updatedDirStatus, &volSync);
3034 rx_PutConnection(rxconnp);
3035 osi_Log1(afsd_logp," RXAFS_Link returns 0x%x", code);
3037 } while (cm_Analyze(connp, userp, reqp,
3038 &dscp->fid, &volSync, NULL, NULL, code));
3040 code = cm_MapRPCError(code, reqp);
3043 osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
3045 osi_Log0(afsd_logp, "CALL Link SUCCESS");
3048 lock_ObtainWrite(&dirop.scp->dirlock);
3049 dirop.lockType = CM_DIRLOCK_WRITE;
3051 lock_ObtainWrite(&dscp->rw);
3052 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3054 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3056 lock_ReleaseWrite(&dscp->rw);
3059 if (cm_CheckDirOpForSingleChange(&dirop)) {
3060 cm_DirCreateEntry(&dirop, fnamep, &sscp->fid);
3062 cm_BPlusDirCreateEntry(&dirop, cnamep, &sscp->fid);
3066 cm_EndDirOp(&dirop);
3068 /* Update the linked object status */
3070 lock_ObtainWrite(&sscp->rw);
3071 cm_MergeStatus(NULL, sscp, &newLinkStatus, &volSync, userp, reqp, 0);
3072 lock_ReleaseWrite(&sscp->rw);
3080 long cm_SymLink(cm_scache_t *dscp, clientchar_t *cnamep, fschar_t *contentsp, long flags,
3081 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
3089 AFSStoreStatus inStatus;
3090 AFSFetchStatus updatedDirStatus;
3091 AFSFetchStatus newLinkStatus;
3093 struct rx_connection * rxconnp;
3095 fschar_t *fnamep = NULL;
3097 memset(&volSync, 0, sizeof(volSync));
3099 /* before starting the RPC, mark that we're changing the directory data,
3100 * so that someone who does a chmod on the dir will wait until our
3103 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
3104 lock_ObtainWrite(&dscp->rw);
3105 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3106 lock_ReleaseWrite(&dscp->rw);
3108 cm_EndDirOp(&dirop);
3113 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3115 cm_StatusFromAttr(&inStatus, NULL, attrp);
3117 /* try the RPC now */
3118 osi_Log1(afsd_logp, "CALL Symlink scp 0x%p", dscp);
3120 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3124 dirAFSFid.Volume = dscp->fid.volume;
3125 dirAFSFid.Vnode = dscp->fid.vnode;
3126 dirAFSFid.Unique = dscp->fid.unique;
3128 rxconnp = cm_GetRxConn(connp);
3129 code = RXAFS_Symlink(rxconnp, &dirAFSFid, fnamep, contentsp,
3130 &inStatus, &newAFSFid, &newLinkStatus,
3131 &updatedDirStatus, &volSync);
3132 rx_PutConnection(rxconnp);
3134 } while (cm_Analyze(connp, userp, reqp,
3135 &dscp->fid, &volSync, NULL, NULL, code));
3136 code = cm_MapRPCError(code, reqp);
3139 osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
3141 osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
3144 lock_ObtainWrite(&dirop.scp->dirlock);
3145 dirop.lockType = CM_DIRLOCK_WRITE;
3147 lock_ObtainWrite(&dscp->rw);
3148 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3150 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3152 lock_ReleaseWrite(&dscp->rw);
3155 if (cm_CheckDirOpForSingleChange(&dirop)) {
3156 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3158 cm_DirCreateEntry(&dirop, fnamep, &newFid);
3160 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
3164 cm_EndDirOp(&dirop);
3166 /* now try to create the new dir's entry, too, but be careful to
3167 * make sure that we don't merge in old info. Since we weren't locking
3168 * out any requests during the file's creation, we may have pretty old
3172 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3173 code = cm_GetSCache(&newFid, &scp, userp, reqp);
3175 lock_ObtainWrite(&scp->rw);
3176 if (!cm_HaveCallback(scp)) {
3177 cm_MergeStatus(dscp, scp, &newLinkStatus, &volSync,
3180 lock_ReleaseWrite(&scp->rw);
3181 cm_ReleaseSCache(scp);
3187 /* and return error code */
3191 /*! \brief Remove a directory
3193 Encapsulates a call to RXAFS_RemoveDir().
3195 \param[in] dscp cm_scache_t for the directory containing the
3196 directory to be removed.
3198 \param[in] fnamep This will be the original name of the directory
3199 as known to the file server. It will be passed in to RXAFS_RemoveDir().
3200 This parameter is optional. If it is not provided the value
3203 \param[in] cnamep Normalized name used to update the local
3206 \param[in] userp cm_user_t for the request.
3208 \param[in] reqp Request tracker.
3210 long cm_RemoveDir(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t *cnamep, cm_user_t *userp, cm_req_t *reqp)
3216 AFSFetchStatus updatedDirStatus;
3218 struct rx_connection * rxconnp;
3220 cm_scache_t *scp = NULL;
3221 int free_fnamep = FALSE;
3223 memset(&volSync, 0, sizeof(volSync));
3225 if (fnamep == NULL) {
3228 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ, &dirop);
3230 code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
3233 cm_EndDirOp(&dirop);
3240 code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
3244 /* before starting the RPC, mark that we're changing the directory data,
3245 * so that someone who does a chmod on the dir will wait until our
3248 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
3249 lock_ObtainWrite(&dscp->rw);
3250 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3251 lock_ReleaseWrite(&dscp->rw);
3253 cm_EndDirOp(&dirop);
3258 /* try the RPC now */
3259 osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%p", dscp);
3261 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3265 dirAFSFid.Volume = dscp->fid.volume;
3266 dirAFSFid.Vnode = dscp->fid.vnode;
3267 dirAFSFid.Unique = dscp->fid.unique;
3269 rxconnp = cm_GetRxConn(connp);
3270 code = RXAFS_RemoveDir(rxconnp, &dirAFSFid, fnamep,
3271 &updatedDirStatus, &volSync);
3272 rx_PutConnection(rxconnp);
3274 } while (cm_Analyze(connp, userp, reqp,
3275 &dscp->fid, &volSync, NULL, NULL, code));
3276 code = cm_MapRPCErrorRmdir(code, reqp);
3279 osi_Log1(afsd_logp, "CALL RemoveDir FAILURE, code 0x%x", code);
3281 osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
3284 lock_ObtainWrite(&dirop.scp->dirlock);
3285 dirop.lockType = CM_DIRLOCK_WRITE;
3287 lock_ObtainWrite(&dscp->rw);
3288 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3290 cm_dnlcRemove(dscp, cnamep);
3291 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3293 lock_ReleaseWrite(&dscp->rw);
3296 if (cm_CheckDirOpForSingleChange(&dirop) && cnamep != NULL) {
3297 cm_DirDeleteEntry(&dirop, fnamep);
3299 cm_BPlusDirDeleteEntry(&dirop, cnamep);
3303 cm_EndDirOp(&dirop);
3306 cm_ReleaseSCache(scp);
3308 lock_ObtainWrite(&scp->rw);
3309 scp->flags |= CM_SCACHEFLAG_DELETED;
3310 lock_ReleaseWrite(&scp->rw);
3318 /* and return error code */
3322 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
3324 /* grab mutex on contents */
3325 lock_ObtainWrite(&scp->rw);
3327 /* reset the prefetch info */
3328 scp->prefetch.base.LowPart = 0; /* base */
3329 scp->prefetch.base.HighPart = 0;
3330 scp->prefetch.end.LowPart = 0; /* and end */
3331 scp->prefetch.end.HighPart = 0;
3333 /* release mutex on contents */
3334 lock_ReleaseWrite(&scp->rw);
3340 /*! \brief Rename a file or directory
3342 Encapsulates a RXAFS_Rename() call.
3344 \param[in] oldDscp cm_scache_t for the directory containing the old
3347 \param[in] oldNamep The original old name known to the file server.
3348 This is the name that will be passed into the RXAFS_Rename().
3349 If it is not provided, it will be looked up.
3351 \param[in] normalizedOldNamep Normalized old name. This is used for
3352 updating local directory caches.
3354 \param[in] newDscp cm_scache_t for the directory containing the new
3357 \param[in] newNamep New name. Normalized.
3359 \param[in] userp cm_user_t for the request.
3361 \param[in,out] reqp Request tracker.
3364 long cm_Rename(cm_scache_t *oldDscp, fschar_t *oldNamep, clientchar_t *cOldNamep,
3365 cm_scache_t *newDscp, clientchar_t *cNewNamep, cm_user_t *userp,
3370 AFSFid oldDirAFSFid;
3371 AFSFid newDirAFSFid;
3373 AFSFetchStatus updatedOldDirStatus;
3374 AFSFetchStatus updatedNewDirStatus;
3377 struct rx_connection * rxconnp;
3378 cm_dirOp_t oldDirOp;
3381 cm_dirOp_t newDirOp;
3382 fschar_t * newNamep = NULL;
3383 int free_oldNamep = FALSE;
3384 cm_scache_t *oldScp = NULL, *newScp = NULL;
3386 memset(&volSync, 0, sizeof(volSync));
3388 if (cOldNamep == NULL || cNewNamep == NULL ||
3389 cm_ClientStrLen(cOldNamep) == 0 ||
3390 cm_ClientStrLen(cNewNamep) == 0)
3391 return CM_ERROR_INVAL;
3394 * Before we permit the operation, make sure that we do not already have
3395 * an object in the destination directory that has a case-insensitive match
3396 * for this name UNLESS the matching object is the object we are renaming.
3398 code = cm_Lookup(oldDscp, cOldNamep, 0, userp, reqp, &oldScp);
3400 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S old name lookup failed",
3401 oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3405 code = cm_Lookup(newDscp, cNewNamep, CM_FLAG_CASEFOLD, userp, reqp, &newScp);
3407 /* found a matching object with the new name */
3408 if (cm_FidCmp(&oldScp->fid, &newScp->fid)) {
3409 /* and they don't match so return an error */
3410 osi_Log2(afsd_logp, "cm_Rename newDscp 0x%p cNewName %S new name already exists",
3411 newDscp, osi_LogSaveStringW(afsd_logp, cNewNamep));
3412 code = CM_ERROR_EXISTS;
3414 cm_ReleaseSCache(newScp);
3416 } else if (code == CM_ERROR_AMBIGUOUS_FILENAME) {
3417 code = CM_ERROR_EXISTS;
3424 if (oldNamep == NULL) {
3427 code = cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_READ, &oldDirOp);
3429 code = cm_BPlusDirLookupOriginalName(&oldDirOp, cOldNamep, &oldNamep);
3431 free_oldNamep = TRUE;
3432 cm_EndDirOp(&oldDirOp);
3436 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S Original Name lookup failed",
3437 oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3443 /* before starting the RPC, mark that we're changing the directory data,
3444 * so that someone who does a chmod on the dir will wait until our call
3445 * completes. We do this in vnode order so that we don't deadlock,
3446 * which makes the code a little verbose.
3448 if (oldDscp == newDscp) {
3449 /* check for identical names */
3450 if (cm_ClientStrCmp(cOldNamep, cNewNamep) == 0) {
3451 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_RENAME_IDENTICAL",
3453 code = CM_ERROR_RENAME_IDENTICAL;
3458 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3459 lock_ObtainWrite(&oldDscp->rw);
3460 cm_dnlcRemove(oldDscp, cOldNamep);
3461 cm_dnlcRemove(oldDscp, cNewNamep);
3462 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3463 CM_SCACHESYNC_STOREDATA);
3464 lock_ReleaseWrite(&oldDscp->rw);
3466 cm_EndDirOp(&oldDirOp);
3470 /* two distinct dir vnodes */
3472 if (oldDscp->fid.cell != newDscp->fid.cell ||
3473 oldDscp->fid.volume != newDscp->fid.volume) {
3474 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_CROSSDEVLINK",