2 * Copyright 2000, International Business Machines Corporation and others.
5 * This software has been released under the terms of the IBM Public
6 * License. For details, see the LICENSE file in the top-level source
7 * directory or online at http://www.openafs.org/dl/license10.html
10 #include <afs/param.h>
30 extern void afsi_log(char *pattern, ...);
33 int cm_enableServerLocks = 1;
35 int cm_followBackupPath = 0;
38 * Case-folding array. This was constructed by inspecting of SMBtrace output.
39 * I do not know anything more about it.
41 unsigned char cm_foldUpper[256] = {
42 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
43 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
44 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
45 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
46 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
47 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
48 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
49 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
50 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
51 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
52 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
53 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
54 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
55 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
56 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
57 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
58 0x80, 0x9a, 0x90, 0x41, 0x8e, 0x41, 0x8f, 0x80,
59 0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x8e, 0x8f,
60 0x90, 0x92, 0x92, 0x4f, 0x99, 0x4f, 0x55, 0x55,
61 0x59, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
62 0x41, 0x49, 0x4f, 0x55, 0xa5, 0xa5, 0x56, 0xa7,
63 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
64 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
65 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
66 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
67 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
68 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
69 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
70 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
71 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
72 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
73 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
77 * Case-insensitive string comparison. We used to use stricmp, but it doesn't
78 * know about 8-bit characters (e.g. 129 is lowercase u-umlaut, 154 is
79 * upper-case u-umlaut).
81 int cm_stricmp(const char *str1, const char *str2)
93 c1 = (char) cm_foldUpper[(unsigned char)(*str1++)];
94 c2 = (char) cm_foldUpper[(unsigned char)(*str2++)];
104 /* return success if we can open this file in this mode */
105 long cm_CheckOpen(cm_scache_t *scp, int openMode, int trunc, cm_user_t *userp,
113 rights |= PRSFS_READ;
114 if (openMode == 1 || openMode == 2 || trunc)
115 rights |= PRSFS_WRITE;
117 lock_ObtainWrite(&scp->rw);
119 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
120 CM_SCACHESYNC_GETSTATUS
121 | CM_SCACHESYNC_NEEDCALLBACK
122 | CM_SCACHESYNC_LOCK);
125 ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
126 scp->fileType == CM_SCACHETYPE_FILE) {
129 unsigned int sLockType;
130 LARGE_INTEGER LOffset, LLength;
132 /* Check if there's some sort of lock on the file at the
135 key = cm_GenerateKey(CM_SESSION_CMINT,0,0);
137 if (rights & PRSFS_WRITE)
140 sLockType = LOCKING_ANDX_SHARED_LOCK;
142 LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
143 LOffset.LowPart = CM_FLSHARE_OFFSET_LOW;
144 LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
145 LLength.LowPart = CM_FLSHARE_LENGTH_LOW;
147 code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
150 cm_Unlock(scp, sLockType, LOffset, LLength, key, userp, reqp);
152 /* In this case, we allow the file open to go through even
153 though we can't enforce mandatory locking on the
155 if (code == CM_ERROR_NOACCESS &&
156 !(rights & PRSFS_WRITE))
160 case CM_ERROR_ALLOFFLINE:
161 case CM_ERROR_ALLDOWN:
162 case CM_ERROR_ALLBUSY:
163 case CM_ERROR_TIMEDOUT:
165 case CM_ERROR_WOULDBLOCK:
168 code = CM_ERROR_SHARING_VIOLATION;
173 } else if (code != 0) {
177 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
181 lock_ReleaseWrite(&scp->rw);
186 /* return success if we can open this file in this mode */
187 long cm_CheckNTOpen(cm_scache_t *scp, unsigned int desiredAccess,
188 unsigned int createDisp, cm_user_t *userp, cm_req_t *reqp,
189 cm_lock_data_t **ldpp)
194 osi_assertx(ldpp != NULL, "null cm_lock_data_t");
197 /* Always allow delete; the RPC will tell us if it's OK */
198 if (desiredAccess == DELETE)
203 if (desiredAccess & (AFS_ACCESS_READ|AFS_ACCESS_EXECUTE))
204 rights |= (scp->fileType == CM_SCACHETYPE_DIRECTORY ? PRSFS_LOOKUP : PRSFS_READ);
206 /* We used to require PRSFS_WRITE if createDisp was 4
207 (OPEN_ALWAYS) even if AFS_ACCESS_WRITE was not requested.
208 However, we don't need to do that since the existence of the
209 scp implies that we don't need to create it. */
210 if (desiredAccess & AFS_ACCESS_WRITE)
211 rights |= PRSFS_WRITE;
213 lock_ObtainWrite(&scp->rw);
215 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
216 CM_SCACHESYNC_GETSTATUS
217 | CM_SCACHESYNC_NEEDCALLBACK
218 | CM_SCACHESYNC_LOCK);
221 * If the open will fail because the volume is readonly, then we will
222 * return an access denied error instead. This is to help brain-dead
223 * apps run correctly on replicated volumes.
224 * See defect 10007 for more information.
226 if (code == CM_ERROR_READONLY)
227 code = CM_ERROR_NOACCESS;
230 ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
231 scp->fileType == CM_SCACHETYPE_FILE) {
233 unsigned int sLockType;
234 LARGE_INTEGER LOffset, LLength;
236 /* Check if there's some sort of lock on the file at the
239 key = cm_GenerateKey(CM_SESSION_CMINT,0,0);
240 if (rights & PRSFS_WRITE)
243 sLockType = LOCKING_ANDX_SHARED_LOCK;
245 /* single byte lock at offset 0x0100 0000 0000 0000 */
246 LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
247 LOffset.LowPart = CM_FLSHARE_OFFSET_LOW;
248 LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
249 LLength.LowPart = CM_FLSHARE_LENGTH_LOW;
251 code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
254 (*ldpp) = (cm_lock_data_t *)malloc(sizeof(cm_lock_data_t));
261 (*ldpp)->sLockType = sLockType;
262 (*ldpp)->LOffset.HighPart = LOffset.HighPart;
263 (*ldpp)->LOffset.LowPart = LOffset.LowPart;
264 (*ldpp)->LLength.HighPart = LLength.HighPart;
265 (*ldpp)->LLength.LowPart = LLength.LowPart;
267 /* In this case, we allow the file open to go through even
268 though we can't enforce mandatory locking on the
270 if (code == CM_ERROR_NOACCESS &&
271 !(rights & PRSFS_WRITE))
275 case CM_ERROR_ALLOFFLINE:
276 case CM_ERROR_ALLDOWN:
277 case CM_ERROR_ALLBUSY:
278 case CM_ERROR_TIMEDOUT:
280 case CM_ERROR_WOULDBLOCK:
283 code = CM_ERROR_SHARING_VIOLATION;
287 } else if (code != 0) {
292 lock_ReleaseWrite(&scp->rw);
294 osi_Log3(afsd_logp,"cm_CheckNTOpen scp 0x%p ldp 0x%p code 0x%x", scp, *ldpp, code);
298 extern long cm_CheckNTOpenDone(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp,
299 cm_lock_data_t ** ldpp)
301 osi_Log2(afsd_logp,"cm_CheckNTOpenDone scp 0x%p ldp 0x%p", scp, *ldpp);
302 lock_ObtainWrite(&scp->rw);
304 cm_Unlock(scp, (*ldpp)->sLockType, (*ldpp)->LOffset, (*ldpp)->LLength,
305 (*ldpp)->key, userp, reqp);
309 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
310 lock_ReleaseWrite(&scp->rw);
314 * When CAP_NT_SMBS has been negotiated, deletion (of files or directories) is
315 * done in three steps:
316 * (1) open for deletion (NT_CREATE_AND_X)
317 * (2) set for deletion on close (NT_TRANSACTION2, SET_FILE_INFO)
319 * We must not do the RPC until step 3. But if we are going to return an error
320 * code (e.g. directory not empty), we must return it by step 2, otherwise most
321 * clients will not notice it. So we do a preliminary check. For deleting
322 * files, this is almost free, since we have already done the RPC to get the
323 * parent directory's status bits. But for deleting directories, we must do an
324 * additional RPC to get the directory's data to check if it is empty. Sigh.
326 long cm_CheckNTDelete(cm_scache_t *dscp, cm_scache_t *scp, cm_user_t *userp,
332 cm_dirEntry_t *dep = 0;
333 unsigned short *hashTable;
335 int BeyondPage = 0, HaveDot = 0, HaveDotDot = 0;
338 /* First check permissions */
339 lock_ObtainWrite(&scp->rw);
340 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_DELETE,
341 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
343 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
344 lock_ReleaseWrite(&scp->rw);
348 /* If deleting directory, must be empty */
350 if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
353 thyper.HighPart = 0; thyper.LowPart = 0;
354 code = buf_Get(scp, &thyper, &bufferp);
358 lock_ObtainMutex(&bufferp->mx);
359 lock_ObtainWrite(&scp->rw);
362 code = cm_SyncOp(scp, bufferp, userp, reqp, 0,
363 CM_SCACHESYNC_NEEDCALLBACK
365 | CM_SCACHESYNC_BUFLOCKED);
369 if (cm_HaveBuffer(scp, bufferp, 1))
372 /* otherwise, load the buffer and try again */
373 lock_ReleaseMutex(&bufferp->mx);
374 code = cm_GetBuffer(scp, bufferp, NULL, userp, reqp);
375 lock_ReleaseWrite(&scp->rw);
376 lock_ObtainMutex(&bufferp->mx);
377 lock_ObtainWrite(&scp->rw);
378 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
383 lock_ReleaseWrite(&scp->rw);
386 /* We try to determine emptiness without looking beyond the first page,
387 * and without assuming "." and ".." are present and are on the first
388 * page (though these assumptions might, after all, be reasonable).
390 hashTable = (unsigned short *)(bufferp->datap + (32 * 5));
391 for (i=0; i<128; i++) {
392 idx = ntohs(hashTable[i]);
398 dep = (cm_dirEntry_t *)(bufferp->datap + (32 * idx));
399 if (strcmp(dep->name, ".") == 0)
401 else if (strcmp(dep->name, "..") == 0)
404 code = CM_ERROR_NOTEMPTY;
407 idx = ntohs(dep->next);
410 if (BeyondPage && HaveDot && HaveDotDot)
411 code = CM_ERROR_NOTEMPTY;
415 lock_ReleaseMutex(&bufferp->mx);
416 buf_Release(bufferp);
418 lock_ReleaseWrite(&scp->rw);
423 * Iterate through all entries in a directory.
424 * When the function funcp is called, the buffer is locked but the
425 * directory vnode is not.
427 * If the retscp parameter is not NULL, the parmp must be a
428 * cm_lookupSearch_t object.
430 long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
431 osi_hyper_t *startOffsetp, cm_user_t *userp, cm_req_t *reqp,
432 cm_scache_t **retscp)
436 cm_dirEntry_t *dep = 0;
439 osi_hyper_t dirLength;
440 osi_hyper_t bufferOffset;
441 osi_hyper_t curOffset;
445 cm_pageHeader_t *pageHeaderp;
447 long nextEntryCookie;
448 int numDirChunks; /* # of 32 byte dir chunks in this entry */
450 /* get the directory size */
451 lock_ObtainWrite(&scp->rw);
452 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_LOOKUP,
453 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
454 lock_ReleaseWrite(&scp->rw);
458 if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
459 return CM_ERROR_NOTDIR;
461 if (retscp) /* if this is a lookup call */
463 cm_lookupSearch_t* sp = parmp;
466 #ifdef AFS_FREELANCE_CLIENT
467 /* Freelance entries never end up in the DNLC because they
468 * do not have an associated cm_server_t
470 !(cm_freelanceEnabled &&
471 sp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
472 sp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
473 #else /* !AFS_FREELANCE_CLIENT */
478 int casefold = sp->caseFold;
479 sp->caseFold = 0; /* we have a strong preference for exact matches */
480 if ( *retscp = cm_dnlcLookup(scp, sp)) /* dnlc hit */
482 sp->caseFold = casefold;
485 sp->caseFold = casefold;
487 /* see if we can find it using the directory hash tables.
488 we can only do exact matches, since the hash is case
498 code = cm_BeginDirOp(scp, userp, reqp, CM_DIRLOCK_READ, &dirop);
502 code = cm_BPlusDirLookup(&dirop, sp->nsearchNamep, &sp->fid);
507 code = cm_DirLookup(&dirop, sp->searchNamep, &sp->fid);
515 sp->ExactFound = TRUE;
516 *retscp = NULL; /* force caller to call cm_GetSCache() */
521 if (sp->caseFold && code == CM_ERROR_INEXACT_MATCH) {
524 sp->ExactFound = FALSE;
525 *retscp = NULL; /* force caller to call cm_GetSCache() */
529 return CM_ERROR_BPLUS_NOMATCH;
537 * XXX We only get the length once. It might change when we drop the
540 dirLength = scp->length;
543 bufferOffset.LowPart = bufferOffset.HighPart = 0;
545 curOffset = *startOffsetp;
547 curOffset.HighPart = 0;
548 curOffset.LowPart = 0;
552 /* make sure that curOffset.LowPart doesn't point to the first
553 * 32 bytes in the 2nd through last dir page, and that it
554 * doesn't point at the first 13 32-byte chunks in the first
555 * dir page, since those are dir and page headers, and don't
556 * contain useful information.
558 temp = curOffset.LowPart & (2048-1);
559 if (curOffset.HighPart == 0 && curOffset.LowPart < 2048) {
560 /* we're in the first page */
561 if (temp < 13*32) temp = 13*32;
564 /* we're in a later dir page */
565 if (temp < 32) temp = 32;
568 /* make sure the low order 5 bits are zero */
571 /* now put temp bits back ito curOffset.LowPart */
572 curOffset.LowPart &= ~(2048-1);
573 curOffset.LowPart |= temp;
575 /* check if we've passed the dir's EOF */
576 if (LargeIntegerGreaterThanOrEqualTo(curOffset, dirLength))
579 /* see if we can use the bufferp we have now; compute in which
580 * page the current offset would be, and check whether that's
581 * the offset of the buffer we have. If not, get the buffer.
583 thyper.HighPart = curOffset.HighPart;
584 thyper.LowPart = curOffset.LowPart & ~(cm_data.buf_blockSize-1);
585 if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
588 lock_ReleaseMutex(&bufferp->mx);
589 buf_Release(bufferp);
593 code = buf_Get(scp, &thyper, &bufferp);
595 /* if buf_Get() fails we do not have a buffer object to lock */
600 lock_ObtainMutex(&bufferp->mx);
601 bufferOffset = thyper;
603 /* now get the data in the cache */
605 lock_ObtainWrite(&scp->rw);
606 code = cm_SyncOp(scp, bufferp, userp, reqp,
608 CM_SCACHESYNC_NEEDCALLBACK
610 | CM_SCACHESYNC_BUFLOCKED);
612 lock_ReleaseWrite(&scp->rw);
615 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
617 if (cm_HaveBuffer(scp, bufferp, 1)) {
618 lock_ReleaseWrite(&scp->rw);
622 /* otherwise, load the buffer and try again */
623 lock_ReleaseMutex(&bufferp->mx);
624 code = cm_GetBuffer(scp, bufferp, NULL, userp,
626 lock_ReleaseWrite(&scp->rw);
627 lock_ObtainMutex(&bufferp->mx);
632 lock_ReleaseMutex(&bufferp->mx);
633 buf_Release(bufferp);
637 } /* if (wrong buffer) ... */
639 /* now we have the buffer containing the entry we're interested
640 * in; copy it out if it represents a non-deleted entry.
642 entryInDir = curOffset.LowPart & (2048-1);
643 entryInBuffer = curOffset.LowPart & (cm_data.buf_blockSize - 1);
645 /* page header will help tell us which entries are free. Page
646 * header can change more often than once per buffer, since
647 * AFS 3 dir page size may be less than (but not more than) a
648 * buffer package buffer.
650 /* only look intra-buffer */
651 temp = curOffset.LowPart & (cm_data.buf_blockSize - 1);
652 temp &= ~(2048 - 1); /* turn off intra-page bits */
653 pageHeaderp = (cm_pageHeader_t *) (bufferp->datap + temp);
655 /* now determine which entry we're looking at in the page. If
656 * it is free (there's a free bitmap at the start of the dir),
657 * we should skip these 32 bytes.
659 slotInPage = (entryInDir & 0x7e0) >> 5;
660 if (!(pageHeaderp->freeBitmap[slotInPage>>3]
661 & (1 << (slotInPage & 0x7)))) {
662 /* this entry is free */
663 numDirChunks = 1; /* only skip this guy */
667 tp = bufferp->datap + entryInBuffer;
668 dep = (cm_dirEntry_t *) tp; /* now points to AFS3 dir entry */
670 /* while we're here, compute the next entry's location, too,
671 * since we'll need it when writing out the cookie into the
672 * dir listing stream.
674 numDirChunks = cm_NameEntries(dep->name, NULL);
676 /* compute the offset of the cookie representing the next entry */
677 nextEntryCookie = curOffset.LowPart
678 + (CM_DIR_CHUNKSIZE * numDirChunks);
680 if (dep->fid.vnode != 0) {
681 /* this is one of the entries to use: it is not deleted */
682 code = (*funcp)(scp, dep, parmp, &curOffset);
685 } /* if we're including this name */
688 /* and adjust curOffset to be where the new cookie is */
690 thyper.LowPart = CM_DIR_CHUNKSIZE * numDirChunks;
691 curOffset = LargeIntegerAdd(thyper, curOffset);
692 } /* while copying data for dir listing */
694 /* release the mutex */
696 lock_ReleaseMutex(&bufferp->mx);
697 buf_Release(bufferp);
702 int cm_NoneUpper(normchar_t *s)
706 if (c >= 'A' && c <= 'Z')
711 int cm_NoneLower(normchar_t *s)
715 if (c >= 'a' && c <= 'z')
720 long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
723 cm_lookupSearch_t *sp;
725 normchar_t matchName[MAX_PATH];
726 int looking_for_short_name = FALSE;
728 sp = (cm_lookupSearch_t *) rockp;
730 if (cm_FsStringToNormString(dep->name, -1, matchName, lengthof(matchName)) == 0) {
731 /* Can't normalize FS string. */
736 match = cm_NormStrCmpI(matchName, sp->nsearchNamep);
738 match = cm_NormStrCmp(matchName, sp->nsearchNamep);
742 && !cm_Is8Dot3(matchName)) {
744 cm_Gen8Dot3NameInt(dep->name, &dep->fid, matchName, NULL);
746 match = cm_NormStrCmpI(matchName, sp->nsearchNamep);
748 match = cm_NormStrCmp(matchName, sp->nsearchNamep);
749 looking_for_short_name = TRUE;
759 if (!sp->caseFold || looking_for_short_name) {
760 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
761 return CM_ERROR_STOPNOW;
765 * If we get here, we are doing a case-insensitive search, and we
766 * have found a match. Now we determine what kind of match it is:
767 * exact, lower-case, upper-case, or none of the above. This is done
768 * in order to choose among matches, if there are more than one.
771 /* Exact matches are the best. */
772 match = cm_NormStrCmp(matchName, sp->nsearchNamep);
775 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
776 return CM_ERROR_STOPNOW;
779 /* Lower-case matches are next. */
782 if (cm_NoneUpper(matchName)) {
787 /* Upper-case matches are next. */
790 if (cm_NoneLower(matchName)) {
795 /* General matches are last. */
801 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
805 /* read the contents of a mount point into the appropriate string.
806 * called with write locked scp, and returns with locked scp.
808 long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
815 if (scp->mountPointStringp[0])
818 /* otherwise, we have to read it in */
819 lock_ReleaseWrite(&scp->rw);
821 thyper.LowPart = thyper.HighPart = 0;
822 code = buf_Get(scp, &thyper, &bufp);
824 lock_ObtainWrite(&scp->rw);
829 code = cm_SyncOp(scp, bufp, userp, reqp, 0,
830 CM_SCACHESYNC_READ | CM_SCACHESYNC_NEEDCALLBACK);
834 cm_SyncOpDone(scp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
836 if (cm_HaveBuffer(scp, bufp, 0))
839 /* otherwise load buffer */
840 code = cm_GetBuffer(scp, bufp, NULL, userp, reqp);
844 /* locked, has callback, has valid data in buffer */
845 if ((tlen = scp->length.LowPart) > MOUNTPOINTLEN - 1)
846 return CM_ERROR_TOOBIG;
848 code = CM_ERROR_INVAL;
852 /* someone else did the work while we were out */
853 if (scp->mountPointStringp[0]) {
858 /* otherwise, copy out the link */
859 memcpy(scp->mountPointStringp, bufp->datap, tlen);
861 /* now make it null-terminated. Note that the original contents of a
862 * link that is a mount point is "#volname." where "." is there just to
863 * be turned into a null. That is, we can trash the last char of the
864 * link without damaging the vol name. This is a stupid convention,
865 * but that's the protocol.
867 scp->mountPointStringp[tlen-1] = 0;
877 /* called with a locked scp and chases the mount point, yielding outScpp.
878 * scp remains write locked, just for simplicity of describing the interface.
880 long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
881 cm_req_t *reqp, cm_scache_t **outScpp)
883 fschar_t *cellNamep = NULL;
884 fschar_t *volNamep = NULL;
889 cm_volume_t *volp = NULL;
898 if (scp->mountRootFid.cell != 0 && scp->mountRootGen >= cm_data.mountRootGen) {
899 tfid = scp->mountRootFid;
900 lock_ReleaseWrite(&scp->rw);
901 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
902 lock_ObtainWrite(&scp->rw);
906 /* parse the volume name */
907 mpNamep = scp->mountPointStringp;
909 return CM_ERROR_NOSUCHPATH;
910 tlen = cm_FsStrLen(scp->mountPointStringp);
911 mtType = *scp->mountPointStringp;
913 cp = cm_FsStrChr(mpNamep, _FS(':'));
915 /* cellular mount point */
916 cellNamep = (fschar_t *)malloc((cp - mpNamep) * sizeof(fschar_t));
917 cm_FsStrCpyN(cellNamep, cp - mpNamep, mpNamep + 1, cp - mpNamep - 1);
918 volNamep = cm_FsStrDup(cp+1);
920 /* now look up the cell */
921 lock_ReleaseWrite(&scp->rw);
922 cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE);
923 lock_ObtainWrite(&scp->rw);
926 volNamep = cm_FsStrDup(mpNamep + 1);
928 cellp = cm_FindCellByID(scp->fid.cell, 0);
932 code = CM_ERROR_NOSUCHCELL;
936 vnLength = cm_FsStrLen(volNamep);
937 if (vnLength >= 8 && cm_FsStrCmp(volNamep + vnLength - 7, ".backup") == 0)
938 targetType = BACKVOL;
939 else if (vnLength >= 10
940 && cm_FsStrCmp(volNamep + vnLength - 9, ".readonly") == 0)
945 /* check for backups within backups */
946 if (targetType == BACKVOL
947 && (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
948 == CM_SCACHEFLAG_RO) {
949 code = CM_ERROR_NOSUCHVOLUME;
953 /* now we need to get the volume */
954 lock_ReleaseWrite(&scp->rw);
955 if (cm_VolNameIsID(volNamep)) {
956 code = cm_FindVolumeByID(cellp, atoi(volNamep), userp, reqp,
957 CM_GETVOL_FLAG_CREATE, &volp);
959 code = cm_FindVolumeByName(cellp, volNamep, userp, reqp,
960 CM_GETVOL_FLAG_CREATE, &volp);
962 lock_ObtainWrite(&scp->rw);
965 afs_uint32 cell, volume;
966 cm_vol_state_t *statep;
968 cell = cellp->cellID;
970 /* if the mt pt originates in a .backup volume (not a .readonly)
971 * and FollowBackupPath is active, and if there is a .backup
972 * volume for the target, then use the .backup of the target
973 * instead of the read-write.
975 if (cm_followBackupPath &&
976 volp->vol[BACKVOL].ID != 0 &&
977 (dscp->flags & (CM_SCACHEFLAG_RO|CM_SCACHEFLAG_PURERO)) == CM_SCACHEFLAG_RO &&
978 (targetType == RWVOL || targetType == ROVOL && volp->vol[ROVOL].ID == 0)
980 targetType = BACKVOL;
982 /* if the mt pt is in a read-only volume (not just a
983 * backup), and if there is a read-only volume for the
984 * target, and if this is a targetType '#' mount point, use
985 * the read-only, otherwise use the one specified.
987 else if (mtType == '#' && targetType == RWVOL &&
988 (scp->flags & CM_SCACHEFLAG_PURERO) &&
989 volp->vol[ROVOL].ID != 0) {
993 lock_ObtainWrite(&volp->rw);
994 statep = cm_VolumeStateByType(volp, targetType);
996 statep->dotdotFid = dscp->fid;
997 lock_ReleaseWrite(&volp->rw);
999 /* the rest of the fid is a magic number */
1000 cm_SetFid(&scp->mountRootFid, cell, volume, 1, 1);
1001 scp->mountRootGen = cm_data.mountRootGen;
1003 tfid = scp->mountRootFid;
1004 lock_ReleaseWrite(&scp->rw);
1005 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
1006 lock_ObtainWrite(&scp->rw);
1019 long cm_LookupInternal(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_user_t *userp,
1020 cm_req_t *reqp, cm_scache_t **outScpp)
1023 int dnlcHit = 1; /* did we hit in the dnlc? yes, we did */
1024 cm_scache_t *tscp = NULL;
1025 cm_scache_t *mountedScp;
1026 cm_lookupSearch_t rock;
1028 normchar_t *nnamep = NULL;
1029 fschar_t *fnamep = NULL;
1033 memset(&rock, 0, sizeof(rock));
1035 if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
1036 && cm_ClientStrCmp(cnamep, _C("..")) == 0) {
1037 if (dscp->dotdotFid.volume == 0)
1038 return CM_ERROR_NOSUCHVOLUME;
1039 rock.fid = dscp->dotdotFid;
1041 } else if (cm_ClientStrCmp(cnamep, _C(".")) == 0) {
1042 rock.fid = dscp->fid;
1046 nnamep = cm_ClientStringToNormStringAlloc(cnamep, -1, NULL);
1048 code = CM_ERROR_NOSUCHFILE;
1051 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
1053 code = CM_ERROR_NOSUCHFILE;
1057 if (flags & CM_FLAG_NOMOUNTCHASE) {
1058 /* In this case, we should go and call cm_Dir* functions
1059 directly since the following cm_ApplyDir() function will
1067 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ, &dirop);
1070 code = cm_BPlusDirLookup(&dirop, nnamep, &rock.fid);
1075 code = cm_DirLookup(&dirop, fnamep, &rock.fid);
1077 cm_EndDirOp(&dirop);
1087 if (code == CM_ERROR_INEXACT_MATCH && (flags & CM_FLAG_CASEFOLD)) {
1094 return CM_ERROR_BPLUS_NOMATCH;
1099 rock.fid.cell = dscp->fid.cell;
1100 rock.fid.volume = dscp->fid.volume;
1101 rock.searchNamep = fnamep;
1102 rock.nsearchNamep = nnamep;
1103 rock.caseFold = (flags & CM_FLAG_CASEFOLD);
1104 rock.hasTilde = ((cm_ClientStrChr(cnamep, '~') != NULL) ? 1 : 0);
1106 /* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
1107 code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
1108 (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
1110 /* code == 0 means we fell off the end of the dir, while stopnow means
1111 * that we stopped early, probably because we found the entry we're
1112 * looking for. Any other non-zero code is an error.
1114 if (code && code != CM_ERROR_STOPNOW) {
1115 /* if the cm_scache_t we are searching in is not a directory
1116 * we must return path not found because the error
1117 * is to describe the final component not an intermediary
1119 if (code == CM_ERROR_NOTDIR) {
1120 if (flags & CM_FLAG_CHECKPATH)
1121 code = CM_ERROR_NOSUCHPATH;
1123 code = CM_ERROR_NOSUCHFILE;
1128 getroot = (dscp==cm_data.rootSCachep) ;
1130 if (!cm_freelanceEnabled || !getroot) {
1131 if (flags & CM_FLAG_CHECKPATH)
1132 code = CM_ERROR_NOSUCHPATH;
1134 code = CM_ERROR_NOSUCHFILE;
1137 else if (!cm_ClientStrChr(cnamep, '#') &&
1138 !cm_ClientStrChr(cnamep, '%') &&
1139 cm_ClientStrCmpI(cnamep, _C("srvsvc")) &&
1140 cm_ClientStrCmpI(cnamep, _C("wkssvc")) &&
1141 cm_ClientStrCmpI(cnamep, _C("ipc$")))
1143 /* nonexistent dir on freelance root, so add it */
1144 fschar_t fullname[CELL_MAXNAMELEN + 1] = "."; /* +1 so that when we skip the . the size is still CELL_MAXNAMELEN */
1147 osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %S",
1148 osi_LogSaveClientString(afsd_logp,cnamep));
1151 * There is an ugly behavior where a share name "foo" will be searched
1152 * for as "fo". If the searched for name differs by an already existing
1153 * symlink or mount point in the Freelance directory, do not add the
1154 * new value automatically.
1158 if (cnamep[0] == '.') {
1159 if (cm_GetCell_Gen(&fnamep[1], &fullname[1], CM_FLAG_CREATE)) {
1161 if (!cm_FreelanceMountPointExists(fullname, 0))
1162 code = cm_FreelanceAddMount(fullname, &fullname[1], "root.cell.",
1164 if ( cm_FsStrCmpI(&fnamep[1], &fullname[1]) &&
1165 !cm_FreelanceMountPointExists(fnamep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0) &&
1166 !cm_FreelanceSymlinkExists(fnamep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0))
1167 code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
1170 if (cm_GetCell_Gen(fnamep, fullname, CM_FLAG_CREATE)) {
1172 if (!cm_FreelanceMountPointExists(fullname, 0))
1173 code = cm_FreelanceAddMount(fullname, fullname, "root.cell.", 0, &rock.fid);
1174 if ( cm_FsStrCmpI(fnamep, fullname) &&
1175 !cm_FreelanceMountPointExists(fnamep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0) &&
1176 !cm_FreelanceSymlinkExists(fnamep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0))
1177 code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
1180 if (!found || code < 0) { /* add mount point failed, so give up */
1181 if (flags & CM_FLAG_CHECKPATH)
1182 code = CM_ERROR_NOSUCHPATH;
1184 code = CM_ERROR_NOSUCHFILE;
1187 tscp = NULL; /* to force call of cm_GetSCache */
1189 if (flags & CM_FLAG_CHECKPATH)
1190 code = CM_ERROR_NOSUCHPATH;
1192 code = CM_ERROR_NOSUCHFILE;
1198 if ( !tscp ) /* we did not find it in the dnlc */
1201 code = cm_GetSCache(&rock.fid, &tscp, userp, reqp);
1205 /* tscp is now held */
1207 lock_ObtainWrite(&tscp->rw);
1208 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1209 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1211 lock_ReleaseWrite(&tscp->rw);
1212 cm_ReleaseSCache(tscp);
1215 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1216 /* tscp is now locked */
1218 if (!(flags & CM_FLAG_NOMOUNTCHASE)
1219 && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
1220 /* mount points are funny: they have a volume name to mount
1223 code = cm_ReadMountPoint(tscp, userp, reqp);
1225 code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
1227 lock_ReleaseWrite(&tscp->rw);
1228 cm_ReleaseSCache(tscp);
1235 lock_ReleaseWrite(&tscp->rw);
1238 /* copy back pointer */
1241 /* insert scache in dnlc */
1242 if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
1243 /* lock the directory entry to prevent racing callback revokes */
1244 lock_ObtainRead(&dscp->rw);
1245 if ( dscp->cbServerp != NULL && dscp->cbExpires > 0 ) {
1246 /* TODO: reuse nnamep from above */
1249 nnamep = cm_ClientStringToNormStringAlloc(cnamep, -1, NULL);
1251 cm_dnlcEnter(dscp, nnamep, tscp);
1253 lock_ReleaseRead(&dscp->rw);
1270 int cm_ExpandSysName(clientchar_t *inp, clientchar_t *outp, long outSizeCch, unsigned int index)
1275 tp = cm_ClientStrRChr(inp, '@');
1277 return 0; /* no @sys */
1279 if (cm_ClientStrCmp(tp, _C("@sys")) != 0)
1280 return 0; /* no @sys */
1282 /* caller just wants to know if this is a valid @sys type of name */
1286 if (index >= cm_sysNameCount)
1289 /* otherwise generate the properly expanded @sys name */
1290 prefixCount = (int)(tp - inp);
1292 cm_ClientStrCpyN(outp, outSizeCch, inp, prefixCount); /* copy out "a." from "a.@sys" */
1293 outp[prefixCount] = 0; /* null terminate the "a." */
1294 cm_ClientStrCat(outp, outSizeCch, cm_sysNameList[index]);/* append i386_nt40 */
1298 long cm_EvaluateVolumeReference(clientchar_t * namep, long flags, cm_user_t * userp,
1299 cm_req_t *reqp, cm_scache_t ** outScpp)
1301 afs_uint32 code = 0;
1302 fschar_t cellName[CELL_MAXNAMELEN];
1303 fschar_t volumeName[VL_MAXNAMELEN];
1307 fschar_t * fnamep = NULL;
1309 cm_cell_t * cellp = NULL;
1310 cm_volume_t * volp = NULL;
1314 int mountType = RWVOL;
1316 osi_Log1(afsd_logp, "cm_EvaluateVolumeReference for string [%S]",
1317 osi_LogSaveClientString(afsd_logp, namep));
1319 if (cm_ClientStrCmpNI(namep, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH) != 0) {
1320 goto _exit_invalid_path;
1323 /* namep is assumed to look like the following:
1325 @vol:<cellname>%<volume>\0
1327 @vol:<cellname>#<volume>\0
1331 fnamep = cm_ClientStringToFsStringAlloc(namep, -1, NULL);
1332 cp = fnamep + CM_PREFIX_VOL_CCH; /* cp points to cell name, hopefully */
1333 tp = cm_FsStrChr(cp, '%');
1335 tp = cm_FsStrChr(cp, '#');
1337 (len = tp - cp) == 0 ||
1338 len > CELL_MAXNAMELEN)
1339 goto _exit_invalid_path;
1340 cm_FsStrCpyN(cellName, lengthof(cellName), cp, len);
1345 cp = tp+1; /* cp now points to volume, supposedly */
1346 cm_FsStrCpy(volumeName, lengthof(volumeName), cp);
1348 /* OK, now we have the cell and the volume */
1349 osi_Log2(afsd_logp, " Found cell [%s] and volume [%s]",
1350 osi_LogSaveFsString(afsd_logp, cellName),
1351 osi_LogSaveFsString(afsd_logp, volumeName));
1353 cellp = cm_GetCell(cellName, CM_FLAG_CREATE);
1354 if (cellp == NULL) {
1355 goto _exit_invalid_path;
1358 len = cm_FsStrLen(volumeName);
1359 if (len >= 8 && cm_FsStrCmp(volumeName + len - 7, ".backup") == 0)
1361 else if (len >= 10 &&
1362 cm_FsStrCmp(volumeName + len - 9, ".readonly") == 0)
1367 if (cm_VolNameIsID(volumeName)) {
1368 code = cm_FindVolumeByID(cellp, atoi(volumeName), userp, reqp,
1369 CM_GETVOL_FLAG_CREATE, &volp);
1371 code = cm_FindVolumeByName(cellp, volumeName, userp, reqp,
1372 CM_GETVOL_FLAG_CREATE, &volp);
1378 if (volType == BACKVOL)
1379 volume = volp->vol[BACKVOL].ID;
1380 else if (volType == ROVOL ||
1381 (volType == RWVOL && mountType == ROVOL && volp->vol[ROVOL].ID != 0))
1382 volume = volp->vol[ROVOL].ID;
1384 volume = volp->vol[RWVOL].ID;
1386 cm_SetFid(&fid, cellp->cellID, volume, 1, 1);
1388 code = cm_GetSCache(&fid, outScpp, userp, reqp);
1401 if (flags & CM_FLAG_CHECKPATH)
1402 return CM_ERROR_NOSUCHPATH;
1404 return CM_ERROR_NOSUCHFILE;
1407 #ifdef DEBUG_REFCOUNT
1408 long cm_LookupDbg(cm_scache_t *dscp, clientchar_t *namep, long flags, cm_user_t *userp,
1409 cm_req_t *reqp, cm_scache_t **outScpp, char * file, long line)
1411 long cm_Lookup(cm_scache_t *dscp, clientchar_t *namep, long flags, cm_user_t *userp,
1412 cm_req_t *reqp, cm_scache_t **outScpp)
1416 clientchar_t tname[AFSPATHMAX];
1417 int sysNameIndex = 0;
1418 cm_scache_t *scp = NULL;
1420 #ifdef DEBUG_REFCOUNT
1421 afsi_log("%s:%d cm_Lookup dscp 0x%p ref %d", file, line, dscp, dscp->refCount, file, line);
1422 osi_Log2(afsd_logp, "cm_Lookup dscp 0x%p ref %d", dscp, dscp->refCount);
1425 if ( cm_ClientStrCmpI(namep,_C(SMB_IOCTL_FILENAME_NOSLASH)) == 0 ) {
1426 if (flags & CM_FLAG_CHECKPATH)
1427 return CM_ERROR_NOSUCHPATH;
1429 return CM_ERROR_NOSUCHFILE;
1432 if (dscp == cm_data.rootSCachep &&
1433 cm_ClientStrCmpNI(namep, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH) == 0) {
1434 return cm_EvaluateVolumeReference(namep, flags, userp, reqp, outScpp);
1437 if (cm_ExpandSysName(namep, NULL, 0, 0) > 0) {
1438 for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
1439 code = cm_ExpandSysName(namep, tname, lengthof(tname), sysNameIndex);
1441 code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
1442 #ifdef DEBUG_REFCOUNT
1443 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);
1444 osi_Log3(afsd_logp, "cm_LookupInternal (1) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1452 cm_ReleaseSCache(scp);
1456 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1457 #ifdef DEBUG_REFCOUNT
1458 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);
1459 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1466 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1467 #ifdef DEBUG_REFCOUNT
1468 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);
1469 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1475 /* None of the possible sysName expansions could be found */
1476 if (flags & CM_FLAG_CHECKPATH)
1477 return CM_ERROR_NOSUCHPATH;
1479 return CM_ERROR_NOSUCHFILE;
1482 /*! \brief Unlink a file name
1484 Encapsulates a call to RXAFS_RemoveFile().
1486 \param[in] dscp cm_scache_t pointing at the directory containing the
1487 name to be unlinked.
1489 \param[in] fnamep Original name to be unlinked. This is the
1490 name that will be passed into the RXAFS_RemoveFile() call.
1491 This parameter is optional. If not provided, the value will
1494 \param[in] came Client name to be unlinked. This name will be used
1495 to update the local directory caches.
1497 \param[in] userp cm_user_t for the request.
1499 \param[in] reqp Request tracker.
1502 long cm_Unlink(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t * cnamep,
1503 cm_user_t *userp, cm_req_t *reqp)
1509 AFSFetchStatus newDirStatus;
1511 struct rx_connection * rxconnp;
1513 cm_scache_t *scp = NULL;
1514 int free_fnamep = FALSE;
1516 if (fnamep == NULL) {
1519 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ, &dirop);
1521 code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
1524 cm_EndDirOp(&dirop);
1531 #ifdef AFS_FREELANCE_CLIENT
1532 if (cm_freelanceEnabled && dscp == cm_data.rootSCachep) {
1533 /* deleting a mount point from the root dir. */
1534 code = cm_FreelanceRemoveMount(fnamep);
1539 code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
1541 /* make sure we don't screw up the dir status during the merge */
1542 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
1544 lock_ObtainWrite(&dscp->rw);
1545 sflags = CM_SCACHESYNC_STOREDATA;
1546 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1547 lock_ReleaseWrite(&dscp->rw);
1549 cm_EndDirOp(&dirop);
1554 afsFid.Volume = dscp->fid.volume;
1555 afsFid.Vnode = dscp->fid.vnode;
1556 afsFid.Unique = dscp->fid.unique;
1558 osi_Log1(afsd_logp, "CALL RemoveFile scp 0x%p", dscp);
1560 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
1564 rxconnp = cm_GetRxConn(connp);
1565 code = RXAFS_RemoveFile(rxconnp, &afsFid, fnamep,
1566 &newDirStatus, &volSync);
1567 rx_PutConnection(rxconnp);
1569 } while (cm_Analyze(connp, userp, reqp, &dscp->fid, &volSync, NULL, NULL, code));
1570 code = cm_MapRPCError(code, reqp);
1573 osi_Log1(afsd_logp, "CALL RemoveFile FAILURE, code 0x%x", code);
1575 osi_Log0(afsd_logp, "CALL RemoveFile SUCCESS");
1578 lock_ObtainWrite(&dirop.scp->dirlock);
1579 dirop.lockType = CM_DIRLOCK_WRITE;
1581 lock_ObtainWrite(&dscp->rw);
1582 cm_dnlcRemove(dscp, cnamep);
1583 cm_SyncOpDone(dscp, NULL, sflags);
1585 cm_MergeStatus(NULL, dscp, &newDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
1586 } else if (code == CM_ERROR_NOSUCHFILE) {
1587 /* windows would not have allowed the request to delete the file
1588 * if it did not believe the file existed. therefore, we must
1589 * have an inconsistent view of the world.
1591 dscp->cbServerp = NULL;
1593 lock_ReleaseWrite(&dscp->rw);
1595 if (code == 0 && cm_CheckDirOpForSingleChange(&dirop) && cnamep) {
1596 cm_DirDeleteEntry(&dirop, fnamep);
1598 cm_BPlusDirDeleteEntry(&dirop, cnamep);
1601 cm_EndDirOp(&dirop);
1604 cm_ReleaseSCache(scp);
1606 lock_ObtainWrite(&scp->rw);
1607 scp->flags |= CM_SCACHEFLAG_DELETED;
1608 lock_ReleaseWrite(&scp->rw);
1619 /* called with a write locked vnode, and fills in the link info.
1620 * returns this the vnode still write locked.
1622 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1629 lock_AssertWrite(&linkScp->rw);
1630 if (!linkScp->mountPointStringp[0]) {
1631 /* read the link data */
1632 lock_ReleaseWrite(&linkScp->rw);
1633 thyper.LowPart = thyper.HighPart = 0;
1634 code = buf_Get(linkScp, &thyper, &bufp);
1635 lock_ObtainWrite(&linkScp->rw);
1639 code = cm_SyncOp(linkScp, bufp, userp, reqp, 0,
1640 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1645 cm_SyncOpDone(linkScp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1647 if (cm_HaveBuffer(linkScp, bufp, 0))
1650 code = cm_GetBuffer(linkScp, bufp, NULL, userp, reqp);
1655 } /* while loop to get the data */
1657 /* now if we still have no link read in,
1658 * copy the data from the buffer */
1659 if ((temp = linkScp->length.LowPart) >= MOUNTPOINTLEN) {
1661 return CM_ERROR_TOOBIG;
1664 /* otherwise, it fits; make sure it is still null (could have
1665 * lost race with someone else referencing this link above),
1666 * and if so, copy in the data.
1668 if (!linkScp->mountPointStringp[0]) {
1669 strncpy(linkScp->mountPointStringp, bufp->datap, temp);
1670 linkScp->mountPointStringp[temp] = 0; /* null terminate */
1672 if ( !strnicmp(linkScp->mountPointStringp, "msdfs:", strlen("msdfs:")) )
1673 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1676 } /* don't have sym link contents cached */
1681 /* called with a held vnode and a path suffix, with the held vnode being a
1682 * symbolic link. Our goal is to generate a new path to interpret, and return
1683 * this new path in newSpaceBufferp. If the new vnode is relative to a dir
1684 * other than the directory containing the symbolic link, then the new root is
1685 * returned in *newRootScpp, otherwise a null is returned there.
1687 long cm_AssembleLink(cm_scache_t *linkScp, fschar_t *pathSuffixp,
1688 cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1689 cm_user_t *userp, cm_req_t *reqp)
1696 *newRootScpp = NULL;
1697 *newSpaceBufferp = NULL;
1699 lock_ObtainWrite(&linkScp->rw);
1700 code = cm_HandleLink(linkScp, userp, reqp);
1704 /* if we may overflow the buffer, bail out; buffer is signficantly
1705 * bigger than max path length, so we don't really have to worry about
1706 * being a little conservative here.
1708 if (cm_FsStrLen(linkScp->mountPointStringp) + cm_FsStrLen(pathSuffixp) + 2
1709 >= CM_UTILS_SPACESIZE) {
1710 code = CM_ERROR_TOOBIG;
1714 tsp = cm_GetSpace();
1715 linkp = linkScp->mountPointStringp;
1716 if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1717 if (strlen(linkp) > cm_mountRootLen)
1718 StringCbCopyA((char *) tsp->data, sizeof(tsp->data), linkp+cm_mountRootLen+1);
1721 *newRootScpp = cm_data.rootSCachep;
1722 cm_HoldSCache(cm_data.rootSCachep);
1723 } else if (linkp[0] == '\\' && linkp[1] == '\\') {
1724 if (!strnicmp(&linkp[2], cm_NetbiosName, (len = (long)strlen(cm_NetbiosName))))
1726 char * p = &linkp[len + 3];
1727 if (strnicmp(p, "all", 3) == 0)
1730 StringCbCopyA(tsp->data, sizeof(tsp->data), p);
1731 for (p = tsp->data; *p; p++) {
1735 *newRootScpp = cm_data.rootSCachep;
1736 cm_HoldSCache(cm_data.rootSCachep);
1738 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1739 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1740 code = CM_ERROR_PATH_NOT_COVERED;
1742 } else if ( linkScp->fileType == CM_SCACHETYPE_DFSLINK ||
1743 !strnicmp(linkp, "msdfs:", (len = (long)strlen("msdfs:"))) ) {
1744 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1745 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1746 code = CM_ERROR_PATH_NOT_COVERED;
1747 } else if (*linkp == '\\' || *linkp == '/') {
1749 /* formerly, this was considered to be from the AFS root,
1750 * but this seems to create problems. instead, we will just
1751 * reject the link */
1752 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp+1);
1753 *newRootScpp = cm_data.rootSCachep;
1754 cm_HoldSCache(cm_data.rootSCachep);
1756 /* we still copy the link data into the response so that
1757 * the user can see what the link points to
1759 linkScp->fileType = CM_SCACHETYPE_INVALID;
1760 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1761 code = CM_ERROR_NOSUCHPATH;
1764 /* a relative link */
1765 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1767 if (pathSuffixp[0] != 0) { /* if suffix string is non-null */
1768 StringCchCatA(tsp->data,lengthof(tsp->data), "\\");
1769 StringCchCatA(tsp->data,lengthof(tsp->data), pathSuffixp);
1773 clientchar_t * cpath = cm_FsStringToClientStringAlloc(tsp->data, -1, NULL);
1774 if (cpath != NULL) {
1775 cm_ClientStrCpy(tsp->wdata, lengthof(tsp->wdata), cpath);
1777 *newSpaceBufferp = tsp;
1779 code = CM_ERROR_NOSUCHPATH;
1786 if (code == CM_ERROR_PATH_NOT_COVERED && reqp->tidPathp && reqp->relPathp) {
1787 cm_VolStatus_Notify_DFS_Mapping(linkScp, reqp->tidPathp, reqp->relPathp);
1792 lock_ReleaseWrite(&linkScp->rw);
1795 #ifdef DEBUG_REFCOUNT
1796 long cm_NameIDbg(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
1797 cm_user_t *userp, clientchar_t *tidPathp, cm_req_t *reqp,
1798 cm_scache_t **outScpp,
1799 char * file, long line)
1801 long cm_NameI(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
1802 cm_user_t *userp, clientchar_t *tidPathp,
1803 cm_req_t *reqp, cm_scache_t **outScpp)
1807 clientchar_t *tp; /* ptr moving through input buffer */
1808 clientchar_t tc; /* temp char */
1809 int haveComponent; /* has new component started? */
1810 clientchar_t component[AFSPATHMAX]; /* this is the new component */
1811 clientchar_t *cp; /* component name being assembled */
1812 cm_scache_t *tscp; /* current location in the hierarchy */
1813 cm_scache_t *nscp; /* next dude down */
1814 cm_scache_t *dirScp; /* last dir we searched */
1815 cm_scache_t *linkScp; /* new root for the symlink we just
1817 cm_space_t *psp; /* space for current path, if we've hit
1819 cm_space_t *tempsp; /* temp vbl */
1820 clientchar_t *restp; /* rest of the pathname to interpret */
1821 int symlinkCount; /* count of # of symlinks traversed */
1822 int extraFlag; /* avoid chasing mt pts for dir cmd */
1823 int phase = 1; /* 1 = tidPathp, 2 = pathp */
1824 #define MAX_FID_COUNT 512
1825 cm_fid_t fids[MAX_FID_COUNT]; /* array of fids processed in this path walk */
1826 int fid_count = 0; /* number of fids processed in this path walk */
1831 #ifdef DEBUG_REFCOUNT
1832 afsi_log("%s:%d cm_NameI rootscp 0x%p ref %d", file, line, rootSCachep, rootSCachep->refCount);
1833 osi_Log4(afsd_logp,"cm_NameI rootscp 0x%p path %S tidpath %S flags 0x%x",
1834 rootSCachep, pathp ? pathp : L"<NULL>", tidPathp ? tidPathp : L"<NULL>",
1849 cm_HoldSCache(tscp);
1857 /* map Unix slashes into DOS ones so we can interpret Unix
1863 if (!haveComponent) {
1866 } else if (tc == 0) {
1880 /* we have a component here */
1881 if (tc == 0 || tc == '\\') {
1882 /* end of the component; we're at the last
1883 * component if tc == 0. However, if the last
1884 * is a symlink, we have more to do.
1886 *cp++ = 0; /* add null termination */
1888 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
1889 extraFlag = CM_FLAG_NOMOUNTCHASE;
1890 code = cm_Lookup(tscp, component,
1892 userp, reqp, &nscp);
1895 if (!cm_ClientStrCmp(component,_C("..")) ||
1896 !cm_ClientStrCmp(component,_C("."))) {
1898 * roll back the fid list until we find the
1899 * fid that matches where we are now. Its not
1900 * necessarily one or two fids because they
1901 * might have been symlinks or mount points or
1902 * both that were crossed.
1904 for ( i=fid_count-1; i>=0; i--) {
1905 if (!cm_FidCmp(&nscp->fid, &fids[i]))
1910 /* add the new fid to the list */
1911 for ( i=0; i<fid_count; i++) {
1912 if ( !cm_FidCmp(&nscp->fid, &fids[i]) ) {
1913 code = CM_ERROR_TOO_MANY_SYMLINKS;
1914 cm_ReleaseSCache(nscp);
1919 if (i == fid_count && fid_count < MAX_FID_COUNT) {
1920 fids[fid_count++] = nscp->fid;
1926 cm_ReleaseSCache(tscp);
1928 cm_ReleaseSCache(dirScp);
1931 if ((code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH) &&
1932 tscp->fileType == CM_SCACHETYPE_SYMLINK) {
1933 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_NOSUCHPATH");
1934 return CM_ERROR_NOSUCHPATH;
1936 osi_Log1(afsd_logp,"cm_NameI code 0x%x", code);
1941 haveComponent = 0; /* component done */
1943 cm_ReleaseSCache(dirScp);
1944 dirScp = tscp; /* for some symlinks */
1945 tscp = nscp; /* already held */
1947 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
1950 cm_ReleaseSCache(dirScp);
1956 /* now, if tscp is a symlink, we should follow it and
1957 * assemble the path again.
1959 lock_ObtainWrite(&tscp->rw);
1960 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1961 CM_SCACHESYNC_GETSTATUS
1962 | CM_SCACHESYNC_NEEDCALLBACK);
1964 lock_ReleaseWrite(&tscp->rw);
1965 cm_ReleaseSCache(tscp);
1968 cm_ReleaseSCache(dirScp);
1973 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1975 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
1976 /* this is a symlink; assemble a new buffer */
1977 lock_ReleaseWrite(&tscp->rw);
1978 if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
1979 cm_ReleaseSCache(tscp);
1982 cm_ReleaseSCache(dirScp);
1987 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_TOO_MANY_SYMLINKS");
1988 return CM_ERROR_TOO_MANY_SYMLINKS;
1998 /* TODO: make this better */
1999 frestp = cm_ClientStringToFsStringAlloc(restp, -1, NULL);
2000 code = cm_AssembleLink(tscp, frestp, &linkScp, &tempsp, userp, reqp);
2004 if (code == 0 && linkScp != NULL) {
2005 if (linkScp == cm_data.rootSCachep)
2008 for ( i=0; i<fid_count; i++) {
2009 if ( !cm_FidCmp(&linkScp->fid, &fids[i]) ) {
2010 code = CM_ERROR_TOO_MANY_SYMLINKS;
2011 cm_ReleaseSCache(linkScp);
2017 if (i == fid_count && fid_count < MAX_FID_COUNT) {
2018 fids[fid_count++] = linkScp->fid;
2023 /* something went wrong */
2024 cm_ReleaseSCache(tscp);
2027 cm_ReleaseSCache(dirScp);
2033 /* otherwise, tempsp has the new path,
2034 * and linkScp is the new root from
2035 * which to interpret that path.
2036 * Continue with the namei processing,
2037 * also doing the bookkeeping for the
2038 * space allocation and tracking the
2039 * vnode reference counts.
2045 cm_ReleaseSCache(tscp);
2050 * now, if linkScp is null, that's
2051 * AssembleLink's way of telling us that
2052 * the sym link is relative to the dir
2053 * containing the link. We have a ref
2054 * to it in dirScp, and we hold it now
2055 * and reuse it as the new spot in the
2063 /* not a symlink, we may be done */
2064 lock_ReleaseWrite(&tscp->rw);
2072 cm_ReleaseSCache(dirScp);
2080 cm_ReleaseSCache(dirScp);
2083 } /* end of a component */
2086 } /* we have a component */
2087 } /* big while loop over all components */
2091 cm_ReleaseSCache(dirScp);
2097 cm_ReleaseSCache(tscp);
2099 #ifdef DEBUG_REFCOUNT
2100 afsi_log("%s:%d cm_NameI code 0x%x outScpp 0x%p ref %d", file, line, code, *outScpp, (*outScpp) ? (*outScpp)->refCount : 0);
2102 osi_Log2(afsd_logp,"cm_NameI code 0x%x outScpp 0x%p", code, *outScpp);
2106 /* called with a dir, and a vnode within the dir that happens to be a symlink.
2107 * We chase the link, and return a held pointer to the target, if it exists,
2108 * in *outScpp. If we succeed, we return 0, otherwise we return an error code
2109 * and do not hold or return a target vnode.
2111 * This is very similar to calling cm_NameI with the last component of a name,
2112 * which happens to be a symlink, except that we've already passed by the name.
2114 * This function is typically called by the directory listing functions, which
2115 * encounter symlinks but need to return the proper file length so programs
2116 * like "more" work properly when they make use of the attributes retrieved from
2119 * The input vnode should not be locked when this function is called.
2121 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
2122 cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
2126 cm_scache_t *newRootScp;
2130 osi_Log1(afsd_logp, "Evaluating symlink scp 0x%p", linkScp);
2132 code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
2136 /* now, if newRootScp is NULL, we're really being told that the symlink
2137 * is relative to the current directory (dscp).
2139 if (newRootScp == NULL) {
2141 cm_HoldSCache(dscp);
2144 code = cm_NameI(newRootScp, spacep->wdata,
2145 CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
2146 userp, NULL, reqp, outScpp);
2148 if (code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH)
2149 code = CM_ERROR_NOSUCHPATH;
2151 /* this stuff is allocated no matter what happened on the namei call,
2153 cm_FreeSpace(spacep);
2154 cm_ReleaseSCache(newRootScp);
2156 if (linkScp == *outScpp) {
2157 cm_ReleaseSCache(*outScpp);
2159 code = CM_ERROR_NOSUCHPATH;
2165 /* for a given entry, make sure that it isn't in the stat cache, and then
2166 * add it to the list of file IDs to be obtained.
2168 * Don't bother adding it if we already have a vnode. Note that the dir
2169 * is locked, so we have to be careful checking the vnode we're thinking of
2170 * processing, to avoid deadlocks.
2172 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
2183 /* Don't overflow bsp. */
2184 if (bsp->counter >= CM_BULKMAX)
2185 return CM_ERROR_STOPNOW;
2187 thyper.LowPart = cm_data.buf_blockSize;
2188 thyper.HighPart = 0;
2189 thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
2191 /* thyper is now the first byte past the end of the record we're
2192 * interested in, and bsp->bufOffset is the first byte of the record
2193 * we're interested in.
2194 * Skip data in the others.
2197 if (LargeIntegerLessThan(*offp, bsp->bufOffset))
2199 if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
2200 return CM_ERROR_STOPNOW;
2201 if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
2204 cm_SetFid(&tfid, scp->fid.cell, scp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
2205 tscp = cm_FindSCache(&tfid);
2207 if (lock_TryWrite(&tscp->rw)) {
2208 /* we have an entry that we can look at */
2209 if (!(tscp->flags & CM_SCACHEFLAG_EACCESS) && cm_HaveCallback(tscp)) {
2210 /* we have a callback on it. Don't bother
2211 * fetching this stat entry, since we're happy
2212 * with the info we have.
2214 lock_ReleaseWrite(&tscp->rw);
2215 cm_ReleaseSCache(tscp);
2218 lock_ReleaseWrite(&tscp->rw);
2220 cm_ReleaseSCache(tscp);
2223 #ifdef AFS_FREELANCE_CLIENT
2224 // yj: if this is a mountpoint under root.afs then we don't want it
2225 // to be bulkstat-ed, instead, we call getSCache directly and under
2226 // getSCache, it is handled specially.
2227 if ( cm_freelanceEnabled &&
2228 tfid.cell==AFS_FAKE_ROOT_CELL_ID &&
2229 tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
2230 !(tfid.vnode==0x1 && tfid.unique==0x1) )
2232 osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
2233 return cm_GetSCache(&tfid, &tscp, NULL, NULL);
2235 #endif /* AFS_FREELANCE_CLIENT */
2238 bsp->fids[i].Volume = scp->fid.volume;
2239 bsp->fids[i].Vnode = tfid.vnode;
2240 bsp->fids[i].Unique = tfid.unique;
2245 cm_TryBulkStatRPC(cm_scache_t *dscp, cm_bulkStat_t *bbp, cm_user_t *userp, cm_req_t *reqp)
2248 AFSCBFids fidStruct;
2249 AFSBulkStats statStruct;
2251 AFSCBs callbackStruct;
2254 cm_callbackRequest_t cbReq;
2260 struct rx_connection * rxconnp;
2261 int inlinebulk = 0; /* Did we use InlineBulkStatus RPC or not? */
2263 /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
2264 * make the calls to create the entries. Handle AFSCBMAX files at a
2267 for (filex = 0; filex < bbp->counter; filex += filesThisCall) {
2268 filesThisCall = bbp->counter - filex;
2269 if (filesThisCall > AFSCBMAX)
2270 filesThisCall = AFSCBMAX;
2272 fidStruct.AFSCBFids_len = filesThisCall;
2273 fidStruct.AFSCBFids_val = &bbp->fids[filex];
2274 statStruct.AFSBulkStats_len = filesThisCall;
2275 statStruct.AFSBulkStats_val = &bbp->stats[filex];
2276 callbackStruct.AFSCBs_len = filesThisCall;
2277 callbackStruct.AFSCBs_val = &bbp->callbacks[filex];
2278 cm_StartCallbackGrantingCall(NULL, &cbReq);
2279 osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
2281 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2285 rxconnp = cm_GetRxConn(connp);
2286 if (!(connp->serverp->flags & CM_SERVERFLAG_NOINLINEBULK)) {
2287 code = RXAFS_InlineBulkStatus(rxconnp, &fidStruct,
2288 &statStruct, &callbackStruct, &volSync);
2289 if (code == RXGEN_OPCODE) {
2290 cm_SetServerNoInlineBulk(connp->serverp, 0);
2296 code = RXAFS_BulkStatus(rxconnp, &fidStruct,
2297 &statStruct, &callbackStruct, &volSync);
2299 rx_PutConnection(rxconnp);
2301 } while (cm_Analyze(connp, userp, reqp, &dscp->fid,
2302 &volSync, NULL, &cbReq, code));
2303 code = cm_MapRPCError(code, reqp);
2305 osi_Log2(afsd_logp, "CALL %sBulkStatus FAILURE code 0x%x",
2306 inlinebulk ? "Inline" : "", code);
2308 osi_Log1(afsd_logp, "CALL %sBulkStatus SUCCESS", inlinebulk ? "Inline" : "");
2310 /* may as well quit on an error, since we're not going to do
2311 * much better on the next immediate call, either.
2314 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2318 /* otherwise, we should do the merges */
2319 for (i = 0; i<filesThisCall; i++) {
2321 cm_SetFid(&tfid, dscp->fid.cell, bbp->fids[j].Volume, bbp->fids[j].Vnode, bbp->fids[j].Unique);
2322 code = cm_GetSCache(&tfid, &scp, userp, reqp);
2326 /* otherwise, if this entry has no callback info,
2329 lock_ObtainWrite(&scp->rw);
2330 /* now, we have to be extra paranoid on merging in this
2331 * information, since we didn't use cm_SyncOp before
2332 * starting the fetch to make sure that no bad races
2333 * were occurring. Specifically, we need to make sure
2334 * we don't obliterate any newer information in the
2335 * vnode than have here.
2337 * Right now, be pretty conservative: if there's a
2338 * callback or a pending call, skip it.
2340 if ((scp->cbServerp == NULL || (scp->flags & CM_SCACHEFLAG_EACCESS))
2342 (CM_SCACHEFLAG_FETCHING
2343 | CM_SCACHEFLAG_STORING
2344 | CM_SCACHEFLAG_SIZESTORING))) {
2345 cm_EndCallbackGrantingCall(scp, &cbReq,
2347 CM_CALLBACK_MAINTAINCOUNT);
2348 cm_MergeStatus(dscp, scp, &bbp->stats[j], &volSync, userp, 0);
2350 lock_ReleaseWrite(&scp->rw);
2351 cm_ReleaseSCache(scp);
2352 } /* all files in the response */
2353 /* now tell it to drop the count,
2354 * after doing the vnode processing above */
2355 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2356 } /* while there are still more files to process */
2358 /* If we did the InlineBulk RPC pull out the return code and log it */
2360 if ((&bbp->stats[0])->errorCode) {
2361 osi_Log1(afsd_logp, "cm_TryBulkStat bulk stat error: %d",
2362 (&bbp->stats[0])->errorCode);
2363 code = (&bbp->stats[0])->errorCode;
2370 /* called with a write locked scp and a pointer to a buffer. Make bulk stat
2371 * calls on all undeleted files in the page of the directory specified.
2374 cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
2380 osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%p", dscp);
2382 /* should be on a buffer boundary */
2383 osi_assertx((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0, "invalid offset");
2385 bbp = malloc(sizeof(cm_bulkStat_t));
2386 memset(bbp, 0, sizeof(cm_bulkStat_t));
2387 bbp->bufOffset = *offsetp;
2389 lock_ReleaseWrite(&dscp->rw);
2390 /* first, assemble the file IDs we need to stat */
2391 code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) bbp, offsetp, userp, reqp, NULL);
2393 /* if we failed, bail out early */
2394 if (code && code != CM_ERROR_STOPNOW) {
2396 lock_ObtainWrite(&dscp->rw);
2400 code = cm_TryBulkStatRPC(dscp, bbp, userp, reqp);
2401 osi_Log1(afsd_logp, "END cm_TryBulkStat code 0x%x", code);
2403 lock_ObtainWrite(&dscp->rw);
2408 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
2412 /* initialize store back mask as inexpensive local variable */
2414 memset(statusp, 0, sizeof(AFSStoreStatus));
2416 /* copy out queued info from scache first, if scp passed in */
2418 if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
2419 statusp->ClientModTime = scp->clientModTime;
2420 mask |= AFS_SETMODTIME;
2421 scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
2426 /* now add in our locally generated request */
2427 if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
2428 statusp->ClientModTime = attrp->clientModTime;
2429 mask |= AFS_SETMODTIME;
2431 if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
2432 statusp->UnixModeBits = attrp->unixModeBits;
2433 mask |= AFS_SETMODE;
2435 if (attrp->mask & CM_ATTRMASK_OWNER) {
2436 statusp->Owner = attrp->owner;
2437 mask |= AFS_SETOWNER;
2439 if (attrp->mask & CM_ATTRMASK_GROUP) {
2440 statusp->Group = attrp->group;
2441 mask |= AFS_SETGROUP;
2444 statusp->Mask = mask;
2447 /* set the file size, and make sure that all relevant buffers have been
2448 * truncated. Ensure that any partially truncated buffers have been zeroed
2449 * to the end of the buffer.
2451 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
2457 /* start by locking out buffer creation */
2458 lock_ObtainWrite(&scp->bufCreateLock);
2460 /* verify that this is a file, not a dir or a symlink */
2461 lock_ObtainWrite(&scp->rw);
2462 code = cm_SyncOp(scp, NULL, userp, reqp, 0,
2463 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2466 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2468 if (scp->fileType != CM_SCACHETYPE_FILE) {
2469 code = CM_ERROR_ISDIR;
2474 if (LargeIntegerLessThan(*sizep, scp->length))
2479 lock_ReleaseWrite(&scp->rw);
2481 /* can't hold scp->rw lock here, since we may wait for a storeback to
2482 * finish if the buffer package is cleaning a buffer by storing it to
2486 buf_Truncate(scp, userp, reqp, sizep);
2488 /* now ensure that file length is short enough, and update truncPos */
2489 lock_ObtainWrite(&scp->rw);
2491 /* make sure we have a callback (so we have the right value for the
2492 * length), and wait for it to be safe to do a truncate.
2494 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
2495 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2496 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2498 /* If we only have 'i' bits, then we should still be able to set
2499 the size of a file we created. */
2500 if (code == CM_ERROR_NOACCESS && scp->creator == userp) {
2501 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
2502 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2503 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2509 if (LargeIntegerLessThan(*sizep, scp->length)) {
2510 /* a real truncation. If truncPos is not set yet, or is bigger
2511 * than where we're truncating the file, set truncPos to this
2516 if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
2517 || LargeIntegerLessThan(*sizep, scp->length)) {
2519 scp->truncPos = *sizep;
2520 scp->mask |= CM_SCACHEMASK_TRUNCPOS;
2522 /* in either case, the new file size has been changed */
2523 scp->length = *sizep;
2524 scp->mask |= CM_SCACHEMASK_LENGTH;
2526 else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
2527 /* really extending the file */
2528 scp->length = *sizep;
2529 scp->mask |= CM_SCACHEMASK_LENGTH;
2532 /* done successfully */
2535 cm_SyncOpDone(scp, NULL,
2536 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2537 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2540 lock_ReleaseWrite(&scp->rw);
2541 lock_ReleaseWrite(&scp->bufCreateLock);
2546 /* set the file size or other attributes (but not both at once) */
2547 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
2551 AFSFetchStatus afsOutStatus;
2555 AFSStoreStatus afsInStatus;
2556 struct rx_connection * rxconnp;
2558 /* handle file length setting */
2559 if (attrp->mask & CM_ATTRMASK_LENGTH)
2560 return cm_SetLength(scp, &attrp->length, userp, reqp);
2562 lock_ObtainWrite(&scp->rw);
2563 /* otherwise, we have to make an RPC to get the status */
2564 code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
2566 lock_ReleaseWrite(&scp->rw);
2569 lock_ConvertWToR(&scp->rw);
2571 /* make the attr structure */
2572 cm_StatusFromAttr(&afsInStatus, scp, attrp);
2574 tfid.Volume = scp->fid.volume;
2575 tfid.Vnode = scp->fid.vnode;
2576 tfid.Unique = scp->fid.unique;
2577 lock_ReleaseRead(&scp->rw);
2579 /* now make the RPC */
2580 osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%p", scp);
2582 code = cm_ConnFromFID(&scp->fid, userp, reqp, &connp);
2586 rxconnp = cm_GetRxConn(connp);
2587 code = RXAFS_StoreStatus(rxconnp, &tfid,
2588 &afsInStatus, &afsOutStatus, &volSync);
2589 rx_PutConnection(rxconnp);
2591 } while (cm_Analyze(connp, userp, reqp,
2592 &scp->fid, &volSync, NULL, NULL, code));
2593 code = cm_MapRPCError(code, reqp);
2596 osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
2598 osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
2600 lock_ObtainWrite(&scp->rw);
2601 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
2603 cm_MergeStatus(NULL, scp, &afsOutStatus, &volSync, userp,
2604 CM_MERGEFLAG_FORCE|CM_MERGEFLAG_STOREDATA);
2606 /* if we're changing the mode bits, discard the ACL cache,
2607 * since we changed the mode bits.
2609 if (afsInStatus.Mask & AFS_SETMODE)
2610 cm_FreeAllACLEnts(scp);
2611 lock_ReleaseWrite(&scp->rw);
2615 long cm_Create(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
2616 cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
2621 cm_callbackRequest_t cbReq;
2624 cm_scache_t *scp = NULL;
2626 AFSStoreStatus inStatus;
2627 AFSFetchStatus updatedDirStatus;
2628 AFSFetchStatus newFileStatus;
2629 AFSCallBack newFileCallback;
2631 struct rx_connection * rxconnp;
2633 fschar_t * fnamep = NULL;
2635 /* can't create names with @sys in them; must expand it manually first.
2636 * return "invalid request" if they try.
2638 if (cm_ExpandSysName(cnamep, NULL, 0, 0)) {
2639 return CM_ERROR_ATSYS;
2642 #ifdef AFS_FREELANCE_CLIENT
2643 /* Freelance root volume does not hold files */
2644 if (cm_freelanceEnabled &&
2645 dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
2646 dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
2648 return CM_ERROR_NOACCESS;
2650 #endif /* AFS_FREELANCE_CLIENT */
2652 /* before starting the RPC, mark that we're changing the file data, so
2653 * that someone who does a chmod will know to wait until our call
2656 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2657 lock_ObtainWrite(&dscp->rw);
2658 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2659 lock_ReleaseWrite(&dscp->rw);
2661 cm_StartCallbackGrantingCall(NULL, &cbReq);
2663 cm_EndDirOp(&dirop);
2670 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
2672 cm_StatusFromAttr(&inStatus, NULL, attrp);
2674 /* try the RPC now */
2675 osi_Log1(afsd_logp, "CALL CreateFile scp 0x%p", dscp);
2677 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2681 dirAFSFid.Volume = dscp->fid.volume;
2682 dirAFSFid.Vnode = dscp->fid.vnode;
2683 dirAFSFid.Unique = dscp->fid.unique;
2685 rxconnp = cm_GetRxConn(connp);
2686 code = RXAFS_CreateFile(connp->rxconnp, &dirAFSFid, fnamep,
2687 &inStatus, &newAFSFid, &newFileStatus,
2688 &updatedDirStatus, &newFileCallback,
2690 rx_PutConnection(rxconnp);
2692 } while (cm_Analyze(connp, userp, reqp,
2693 &dscp->fid, &volSync, NULL, &cbReq, code));
2694 code = cm_MapRPCError(code, reqp);
2697 osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
2699 osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
2702 lock_ObtainWrite(&dirop.scp->dirlock);
2703 dirop.lockType = CM_DIRLOCK_WRITE;
2705 lock_ObtainWrite(&dscp->rw);
2706 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2708 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
2710 lock_ReleaseWrite(&dscp->rw);
2712 /* now try to create the file's entry, too, but be careful to
2713 * make sure that we don't merge in old info. Since we weren't locking
2714 * out any requests during the file's creation, we may have pretty old
2718 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
2719 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2721 lock_ObtainWrite(&scp->rw);
2722 scp->creator = userp; /* remember who created it */
2723 if (!cm_HaveCallback(scp)) {
2724 cm_MergeStatus(dscp, scp, &newFileStatus, &volSync,
2726 cm_EndCallbackGrantingCall(scp, &cbReq,
2727 &newFileCallback, 0);
2730 lock_ReleaseWrite(&scp->rw);
2734 /* make sure we end things properly */
2736 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2738 if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
2739 cm_DirCreateEntry(&dirop, fnamep, &newFid);
2741 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
2744 cm_EndDirOp(&dirop);
2753 cm_ReleaseSCache(scp);
2758 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
2762 code = buf_CleanVnode(scp, userp, reqp);
2764 lock_ObtainWrite(&scp->rw);
2766 if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
2767 | CM_SCACHEMASK_CLIENTMODTIME
2768 | CM_SCACHEMASK_LENGTH))
2769 code = cm_StoreMini(scp, userp, reqp);
2771 if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) {
2772 code = (scp->flags & CM_SCACHEFLAG_OVERQUOTA) ? CM_ERROR_QUOTA : CM_ERROR_SPACE;
2773 scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE);
2776 lock_ReleaseWrite(&scp->rw);
2781 long cm_MakeDir(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
2782 cm_user_t *userp, cm_req_t *reqp, cm_scache_t **scpp)
2787 cm_callbackRequest_t cbReq;
2790 cm_scache_t *scp = NULL;
2792 AFSStoreStatus inStatus;
2793 AFSFetchStatus updatedDirStatus;
2794 AFSFetchStatus newDirStatus;
2795 AFSCallBack newDirCallback;
2797 struct rx_connection * rxconnp;
2799 fschar_t * fnamep = NULL;
2801 /* can't create names with @sys in them; must expand it manually first.
2802 * return "invalid request" if they try.
2804 if (cm_ExpandSysName(cnamep, NULL, 0, 0)) {
2805 return CM_ERROR_ATSYS;
2808 #ifdef AFS_FREELANCE_CLIENT
2809 /* Freelance root volume does not hold subdirectories */
2810 if (cm_freelanceEnabled &&
2811 dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
2812 dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
2814 return CM_ERROR_NOACCESS;
2816 #endif /* AFS_FREELANCE_CLIENT */
2818 /* before starting the RPC, mark that we're changing the directory
2819 * data, so that someone who does a chmod on the dir will wait until
2820 * our call completes.
2822 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2823 lock_ObtainWrite(&dscp->rw);
2824 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2825 lock_ReleaseWrite(&dscp->rw);
2827 cm_StartCallbackGrantingCall(NULL, &cbReq);
2829 cm_EndDirOp(&dirop);
2836 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
2837 cm_StatusFromAttr(&inStatus, NULL, attrp);
2839 /* try the RPC now */
2840 osi_Log1(afsd_logp, "CALL MakeDir scp 0x%p", dscp);
2842 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2846 dirAFSFid.Volume = dscp->fid.volume;
2847 dirAFSFid.Vnode = dscp->fid.vnode;
2848 dirAFSFid.Unique = dscp->fid.unique;
2850 rxconnp = cm_GetRxConn(connp);
2851 code = RXAFS_MakeDir(connp->rxconnp, &dirAFSFid, fnamep,
2852 &inStatus, &newAFSFid, &newDirStatus,
2853 &updatedDirStatus, &newDirCallback,
2855 rx_PutConnection(rxconnp);
2857 } while (cm_Analyze(connp, userp, reqp,
2858 &dscp->fid, &volSync, NULL, &cbReq, code));
2859 code = cm_MapRPCError(code, reqp);
2862 osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
2864 osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
2867 lock_ObtainWrite(&dirop.scp->dirlock);
2868 dirop.lockType = CM_DIRLOCK_WRITE;
2870 lock_ObtainWrite(&dscp->rw);
2871 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2873 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
2875 lock_ReleaseWrite(&dscp->rw);
2877 /* now try to create the new dir's entry, too, but be careful to
2878 * make sure that we don't merge in old info. Since we weren't locking
2879 * out any requests during the file's creation, we may have pretty old
2883 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
2884 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2886 lock_ObtainWrite(&scp->rw);
2887 if (!cm_HaveCallback(scp)) {
2888 cm_MergeStatus(dscp, scp, &newDirStatus, &volSync,
2890 cm_EndCallbackGrantingCall(scp, &cbReq,
2891 &newDirCallback, 0);
2894 lock_ReleaseWrite(&scp->rw);
2898 /* make sure we end things properly */
2900 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2902 if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
2903 cm_DirCreateEntry(&dirop, fnamep, &newFid);
2905 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
2908 cm_EndDirOp(&dirop);
2916 cm_ReleaseSCache(scp);
2919 /* and return error code */
2923 long cm_Link(cm_scache_t *dscp, clientchar_t *cnamep, cm_scache_t *sscp, long flags,
2924 cm_user_t *userp, cm_req_t *reqp)
2929 AFSFid existingAFSFid;
2930 AFSFetchStatus updatedDirStatus;
2931 AFSFetchStatus newLinkStatus;
2933 struct rx_connection * rxconnp;
2935 fschar_t * fnamep = NULL;
2937 if (dscp->fid.cell != sscp->fid.cell ||
2938 dscp->fid.volume != sscp->fid.volume) {
2939 return CM_ERROR_CROSSDEVLINK;
2942 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2943 lock_ObtainWrite(&dscp->rw);
2944 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2945 lock_ReleaseWrite(&dscp->rw);
2947 cm_EndDirOp(&dirop);
2952 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
2954 /* try the RPC now */
2955 osi_Log1(afsd_logp, "CALL Link scp 0x%p", dscp);
2957 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2960 dirAFSFid.Volume = dscp->fid.volume;
2961 dirAFSFid.Vnode = dscp->fid.vnode;
2962 dirAFSFid.Unique = dscp->fid.unique;
2964 existingAFSFid.Volume = sscp->fid.volume;
2965 existingAFSFid.Vnode = sscp->fid.vnode;
2966 existingAFSFid.Unique = sscp->fid.unique;
2968 rxconnp = cm_GetRxConn(connp);
2969 code = RXAFS_Link(rxconnp, &dirAFSFid, fnamep, &existingAFSFid,
2970 &newLinkStatus, &updatedDirStatus, &volSync);
2971 rx_PutConnection(rxconnp);
2972 osi_Log1(afsd_logp," RXAFS_Link returns 0x%x", code);
2974 } while (cm_Analyze(connp, userp, reqp,
2975 &dscp->fid, &volSync, NULL, NULL, code));
2977 code = cm_MapRPCError(code, reqp);
2980 osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
2982 osi_Log0(afsd_logp, "CALL Link SUCCESS");
2985 lock_ObtainWrite(&dirop.scp->dirlock);
2986 dirop.lockType = CM_DIRLOCK_WRITE;
2988 lock_ObtainWrite(&dscp->rw);
2989 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2991 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
2993 lock_ReleaseWrite(&dscp->rw);
2996 if (cm_CheckDirOpForSingleChange(&dirop)) {
2997 cm_DirCreateEntry(&dirop, fnamep, &sscp->fid);
2999 cm_BPlusDirCreateEntry(&dirop, cnamep, &sscp->fid);
3003 cm_EndDirOp(&dirop);
3010 long cm_SymLink(cm_scache_t *dscp, clientchar_t *cnamep, fschar_t *contentsp, long flags,
3011 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
3019 AFSStoreStatus inStatus;
3020 AFSFetchStatus updatedDirStatus;
3021 AFSFetchStatus newLinkStatus;
3023 struct rx_connection * rxconnp;
3025 fschar_t *fnamep = NULL;
3027 /* before starting the RPC, mark that we're changing the directory data,
3028 * so that someone who does a chmod on the dir will wait until our
3031 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
3032 lock_ObtainWrite(&dscp->rw);
3033 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3034 lock_ReleaseWrite(&dscp->rw);
3036 cm_EndDirOp(&dirop);
3041 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3043 cm_StatusFromAttr(&inStatus, NULL, attrp);
3045 /* try the RPC now */
3046 osi_Log1(afsd_logp, "CALL Symlink scp 0x%p", dscp);
3048 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3052 dirAFSFid.Volume = dscp->fid.volume;
3053 dirAFSFid.Vnode = dscp->fid.vnode;
3054 dirAFSFid.Unique = dscp->fid.unique;
3056 rxconnp = cm_GetRxConn(connp);
3057 code = RXAFS_Symlink(rxconnp, &dirAFSFid, fnamep, contentsp,
3058 &inStatus, &newAFSFid, &newLinkStatus,
3059 &updatedDirStatus, &volSync);
3060 rx_PutConnection(rxconnp);
3062 } while (cm_Analyze(connp, userp, reqp,
3063 &dscp->fid, &volSync, NULL, NULL, code));
3064 code = cm_MapRPCError(code, reqp);
3067 osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
3069 osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
3072 lock_ObtainWrite(&dirop.scp->dirlock);
3073 dirop.lockType = CM_DIRLOCK_WRITE;
3075 lock_ObtainWrite(&dscp->rw);
3076 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3078 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
3080 lock_ReleaseWrite(&dscp->rw);
3083 if (cm_CheckDirOpForSingleChange(&dirop)) {
3084 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3086 cm_DirCreateEntry(&dirop, fnamep, &newFid);
3088 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
3092 cm_EndDirOp(&dirop);
3094 /* now try to create the new dir's entry, too, but be careful to
3095 * make sure that we don't merge in old info. Since we weren't locking
3096 * out any requests during the file's creation, we may have pretty old
3100 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3101 code = cm_GetSCache(&newFid, &scp, userp, reqp);
3103 lock_ObtainWrite(&scp->rw);
3104 if (!cm_HaveCallback(scp)) {
3105 cm_MergeStatus(dscp, scp, &newLinkStatus, &volSync,
3108 lock_ReleaseWrite(&scp->rw);
3109 cm_ReleaseSCache(scp);
3115 /* and return error code */
3119 /*! \brief Remove a directory
3121 Encapsulates a call to RXAFS_RemoveDir().
3123 \param[in] dscp cm_scache_t for the directory containing the
3124 directory to be removed.
3126 \param[in] fnamep This will be the original name of the directory
3127 as known to the file server. It will be passed in to RXAFS_RemoveDir().
3128 This parameter is optional. If it is not provided the value
3131 \param[in] cnamep Normalized name used to update the local
3134 \param[in] userp cm_user_t for the request.
3136 \param[in] reqp Request tracker.
3138 long cm_RemoveDir(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t *cnamep, cm_user_t *userp, cm_req_t *reqp)
3144 AFSFetchStatus updatedDirStatus;
3146 struct rx_connection * rxconnp;
3148 cm_scache_t *scp = NULL;
3149 int free_fnamep = FALSE;
3151 if (fnamep == NULL) {
3154 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ, &dirop);
3156 code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
3159 cm_EndDirOp(&dirop);
3166 code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
3170 /* before starting the RPC, mark that we're changing the directory data,
3171 * so that someone who does a chmod on the dir will wait until our
3174 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
3175 lock_ObtainWrite(&dscp->rw);
3176 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3177 lock_ReleaseWrite(&dscp->rw);
3179 cm_EndDirOp(&dirop);
3184 /* try the RPC now */
3185 osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%p", dscp);
3187 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3191 dirAFSFid.Volume = dscp->fid.volume;
3192 dirAFSFid.Vnode = dscp->fid.vnode;
3193 dirAFSFid.Unique = dscp->fid.unique;
3195 rxconnp = cm_GetRxConn(connp);
3196 code = RXAFS_RemoveDir(rxconnp, &dirAFSFid, fnamep,
3197 &updatedDirStatus, &volSync);
3198 rx_PutConnection(rxconnp);
3200 } while (cm_Analyze(connp, userp, reqp,
3201 &dscp->fid, &volSync, NULL, NULL, code));
3202 code = cm_MapRPCErrorRmdir(code, reqp);
3205 osi_Log1(afsd_logp, "CALL RemoveDir FAILURE, code 0x%x", code);
3207 osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
3210 lock_ObtainWrite(&dirop.scp->dirlock);
3211 dirop.lockType = CM_DIRLOCK_WRITE;
3213 lock_ObtainWrite(&dscp->rw);
3214 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3216 cm_dnlcRemove(dscp, cnamep);
3217 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
3219 lock_ReleaseWrite(&dscp->rw);
3222 if (cm_CheckDirOpForSingleChange(&dirop) && cnamep != NULL) {
3223 cm_DirDeleteEntry(&dirop, fnamep);
3225 cm_BPlusDirDeleteEntry(&dirop, cnamep);
3229 cm_EndDirOp(&dirop);
3232 cm_ReleaseSCache(scp);
3234 lock_ObtainWrite(&scp->rw);
3235 scp->flags |= CM_SCACHEFLAG_DELETED;
3236 lock_ReleaseWrite(&scp->rw);
3244 /* and return error code */
3248 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
3250 /* grab mutex on contents */
3251 lock_ObtainWrite(&scp->rw);
3253 /* reset the prefetch info */
3254 scp->prefetch.base.LowPart = 0; /* base */
3255 scp->prefetch.base.HighPart = 0;
3256 scp->prefetch.end.LowPart = 0; /* and end */
3257 scp->prefetch.end.HighPart = 0;
3259 /* release mutex on contents */
3260 lock_ReleaseWrite(&scp->rw);
3266 /*! \brief Rename a file or directory
3268 Encapsulates a RXAFS_Rename() call.
3270 \param[in] oldDscp cm_scache_t for the directory containing the old
3273 \param[in] oldNamep The original old name known to the file server.
3274 This is the name that will be passed into the RXAFS_Rename().
3275 If it is not provided, it will be looked up.
3277 \param[in] normalizedOldNamep Normalized old name. This is used for
3278 updating local directory caches.
3280 \param[in] newDscp cm_scache_t for the directory containing the new
3283 \param[in] newNamep New name. Normalized.
3285 \param[in] userp cm_user_t for the request.
3287 \param[in,out] reqp Request tracker.
3290 long cm_Rename(cm_scache_t *oldDscp, fschar_t *oldNamep, clientchar_t *cOldNamep,
3291 cm_scache_t *newDscp, clientchar_t *cNewNamep, cm_user_t *userp,
3296 AFSFid oldDirAFSFid;
3297 AFSFid newDirAFSFid;
3299 AFSFetchStatus updatedOldDirStatus;
3300 AFSFetchStatus updatedNewDirStatus;
3303 struct rx_connection * rxconnp;
3304 cm_dirOp_t oldDirOp;
3307 cm_dirOp_t newDirOp;
3308 fschar_t * newNamep = NULL;
3309 int free_oldNamep = FALSE;
3310 cm_scache_t *oldScp = NULL, *newScp = NULL;
3312 if (cOldNamep == NULL || cNewNamep == NULL ||
3313 cm_ClientStrLen(cOldNamep) == 0 ||
3314 cm_ClientStrLen(cNewNamep) == 0)
3315 return CM_ERROR_INVAL;
3318 * Before we permit the operation, make sure that we do not already have
3319 * an object in the destination directory that has a case-insensitive match
3320 * for this name UNLESS the matching object is the object we are renaming.
3322 code = cm_Lookup(oldDscp, cOldNamep, 0, userp, reqp, &oldScp);
3324 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S old name lookup failed",
3325 oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3329 code = cm_Lookup(newDscp, cNewNamep, CM_FLAG_CASEFOLD, userp, reqp, &newScp);
3331 /* found a matching object with the new name */
3332 if (cm_FidCmp(&oldScp->fid, &newScp->fid)) {
3333 /* and they don't match so return an error */
3334 osi_Log2(afsd_logp, "cm_Rename newDscp 0x%p cNewName %S new name already exists",
3335 newDscp, osi_LogSaveStringW(afsd_logp, cNewNamep));
3336 code = CM_ERROR_EXISTS;
3338 cm_ReleaseSCache(newScp);
3340 } else if (code == CM_ERROR_AMBIGUOUS_FILENAME) {
3341 code = CM_ERROR_EXISTS;
3345 cm_ReleaseSCache(oldScp);
3351 if (oldNamep == NULL) {
3354 code = cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_READ, &oldDirOp);
3356 code = cm_BPlusDirLookupOriginalName(&oldDirOp, cOldNamep, &oldNamep);
3358 free_oldNamep = TRUE;
3359 cm_EndDirOp(&oldDirOp);
3363 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S Original Name lookup failed",
3364 oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3370 /* before starting the RPC, mark that we're changing the directory data,
3371 * so that someone who does a chmod on the dir will wait until our call
3372 * completes. We do this in vnode order so that we don't deadlock,
3373 * which makes the code a little verbose.
3375 if (oldDscp == newDscp) {
3376 /* check for identical names */
3377 if (cm_ClientStrCmp(cOldNamep, cNewNamep) == 0) {
3378 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_RENAME_IDENTICAL",
3380 code = CM_ERROR_RENAME_IDENTICAL;
3385 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3386 lock_ObtainWrite(&oldDscp->rw);
3387 cm_dnlcRemove(oldDscp, cOldNamep);
3388 cm_dnlcRemove(oldDscp, cNewNamep);
3389 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3390 CM_SCACHESYNC_STOREDATA);
3391 lock_ReleaseWrite(&oldDscp->rw);
3393 cm_EndDirOp(&oldDirOp);
3397 /* two distinct dir vnodes */
3399 if (oldDscp->fid.cell != newDscp->fid.cell ||
3400 oldDscp->fid.volume != newDscp->fid.volume) {
3401 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_CROSSDEVLINK",
3403 code = CM_ERROR_CROSSDEVLINK;
3407 /* shouldn't happen that we have distinct vnodes for two
3408 * different files, but could due to deliberate attack, or
3409 * stale info. Avoid deadlocks and quit now.
3411 if (oldDscp->fid.vnode == newDscp->fid.vnode) {
3412 osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p vnode collision",
3414 code = CM_ERROR_CROSSDEVLINK;
3418 if (oldDscp->fid.vnode < newDscp->fid.vnode) {
3419 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3420 lock_ObtainWrite(&oldDscp->rw);
3421 cm_dnlcRemove(oldDscp, cOldNamep);
3422 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3423 CM_SCACHESYNC_STOREDATA);
3424 lock_ReleaseWrite(&oldDscp->rw);
3426 cm_EndDirOp(&oldDirOp);
3428 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE, &newDirOp);
3429 lock_ObtainWrite(&newDscp->rw);
3430 cm_dnlcRemove(newDscp, cNewNamep);
3431 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3432 CM_SCACHESYNC_STOREDATA);
3433 lock_ReleaseWrite(&newDscp->rw);
3435 cm_EndDirOp(&newDirOp);
3437 /* cleanup first one */
3438 lock_ObtainWrite(&oldDscp->rw);
3439 cm_SyncOpDone(oldDscp, NULL,
3440 CM_SCACHESYNC_STOREDATA);
3441 lock_ReleaseWrite(&oldDscp->rw);
3442 cm_EndDirOp(&oldDirOp);
3447 /* lock the new vnode entry first */
3448 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE, &newDirOp);
3449 lock_ObtainWrite(&newDscp->rw);
3450 cm_dnlcRemove(newDscp, cNewNamep);
3451 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3452 CM_SCACHESYNC_STOREDATA);
3453 lock_ReleaseWrite(&newDscp->rw);
3455 cm_EndDirOp(&newDirOp);
3457 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3458 lock_ObtainWrite(&oldDscp->rw);
3459 cm_dnlcRemove(oldDscp, cOldNamep);
3460 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3461 CM_SCACHESYNC_STOREDATA);
3462 lock_ReleaseWrite(&oldDscp->rw);