2 * Copyright 2000, International Business Machines Corporation and others.
5 * This software has been released under the terms of the IBM Public
6 * License. For details, see the LICENSE file in the top-level source
7 * directory or online at http://www.openafs.org/dl/license10.html
10 #include <afsconfig.h>
11 #include <afs/param.h>
33 extern void afsi_log(char *pattern, ...);
36 int cm_enableServerLocks = 1;
38 int cm_followBackupPath = 0;
41 * Case-folding array. This was constructed by inspecting of SMBtrace output.
42 * I do not know anything more about it.
44 unsigned char cm_foldUpper[256] = {
45 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
46 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
47 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
48 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
49 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
50 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
51 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
52 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
53 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
54 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
55 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
56 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
57 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
58 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
59 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
60 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
61 0x80, 0x9a, 0x90, 0x41, 0x8e, 0x41, 0x8f, 0x80,
62 0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x8e, 0x8f,
63 0x90, 0x92, 0x92, 0x4f, 0x99, 0x4f, 0x55, 0x55,
64 0x59, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
65 0x41, 0x49, 0x4f, 0x55, 0xa5, 0xa5, 0x56, 0xa7,
66 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
67 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
68 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
69 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
70 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
71 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
72 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
73 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
74 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
75 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
76 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
80 * Case-insensitive string comparison. We used to use stricmp, but it doesn't
81 * know about 8-bit characters (e.g. 129 is lowercase u-umlaut, 154 is
82 * upper-case u-umlaut).
84 int cm_stricmp(const char *str1, const char *str2)
96 c1 = (char) cm_foldUpper[(unsigned char)(*str1++)];
97 c2 = (char) cm_foldUpper[(unsigned char)(*str2++)];
107 /* return success if we can open this file in this mode */
108 long cm_CheckOpen(cm_scache_t *scp, int openMode, int trunc, cm_user_t *userp,
116 rights |= PRSFS_READ;
117 if (openMode == 1 || openMode == 2 || trunc)
118 rights |= PRSFS_WRITE;
120 lock_ObtainWrite(&scp->rw);
122 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
123 CM_SCACHESYNC_GETSTATUS
124 | CM_SCACHESYNC_NEEDCALLBACK
125 | CM_SCACHESYNC_LOCK);
128 ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
129 scp->fileType == CM_SCACHETYPE_FILE) {
132 unsigned int sLockType;
133 LARGE_INTEGER LOffset, LLength;
135 /* Check if there's some sort of lock on the file at the
138 key = cm_GenerateKey(CM_SESSION_CMINT,0,0);
140 if (rights & PRSFS_WRITE)
143 sLockType = LOCKING_ANDX_SHARED_LOCK;
145 LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
146 LOffset.LowPart = CM_FLSHARE_OFFSET_LOW;
147 LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
148 LLength.LowPart = CM_FLSHARE_LENGTH_LOW;
150 code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
153 cm_Unlock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp);
155 /* In this case, we allow the file open to go through even
156 though we can't enforce mandatory locking on the
158 if (code == CM_ERROR_NOACCESS &&
159 !(rights & PRSFS_WRITE))
162 if (code == CM_ERROR_LOCK_NOT_GRANTED)
163 code = CM_ERROR_SHARING_VIOLATION;
167 } else if (code != 0) {
171 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
175 lock_ReleaseWrite(&scp->rw);
180 /* return success if we can open this file in this mode */
181 long cm_CheckNTOpen(cm_scache_t *scp,
182 unsigned int desiredAccess,
183 unsigned int shareAccess,
184 unsigned int createDisp,
185 afs_offs_t process_id,
186 afs_offs_t handle_id,
187 cm_user_t *userp, cm_req_t *reqp,
188 cm_lock_data_t **ldpp)
192 afs_uint16 session_id;
194 osi_assertx(ldpp != NULL, "null cm_lock_data_t");
197 /* compute the session id */
198 if (reqp->flags & CM_REQ_SOURCE_SMB)
199 session_id = CM_SESSION_SMB;
200 else if (reqp->flags & CM_REQ_SOURCE_REDIR)
201 session_id = CM_SESSION_IFS;
203 session_id = CM_SESSION_CMINT;
205 /* Ignore the SYNCHRONIZE privilege */
206 desiredAccess &= ~SYNCHRONIZE;
208 /* Always allow delete; the RPC will tell us if it's OK */
211 if (desiredAccess == DELETE)
214 /* Always allow reading attributes (Hidden, System, Readonly, ...) */
215 if (desiredAccess == FILE_READ_ATTRIBUTES)
218 if (desiredAccess & (AFS_ACCESS_READ|AFS_ACCESS_EXECUTE))
219 rights |= (scp->fileType == CM_SCACHETYPE_DIRECTORY ? PRSFS_LOOKUP : PRSFS_READ);
221 /* We used to require PRSFS_WRITE if createDisp was 4
222 (OPEN_ALWAYS) even if AFS_ACCESS_WRITE was not requested.
223 However, we don't need to do that since the existence of the
224 scp implies that we don't need to create it. */
225 if (desiredAccess & AFS_ACCESS_WRITE)
226 rights |= PRSFS_WRITE;
228 if (desiredAccess & DELETE)
229 rights |= PRSFS_DELETE;
231 lock_ObtainWrite(&scp->rw);
233 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
234 CM_SCACHESYNC_GETSTATUS
235 | CM_SCACHESYNC_NEEDCALLBACK
236 | CM_SCACHESYNC_LOCK);
239 * If the open will fail because the volume is readonly, then we will
240 * return an access denied error instead. This is to help brain-dead
241 * apps run correctly on replicated volumes.
242 * See defect 10007 for more information.
244 if (code == CM_ERROR_READONLY)
245 code = CM_ERROR_NOACCESS;
248 !(shareAccess & FILE_SHARE_WRITE) &&
249 ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
250 scp->fileType == CM_SCACHETYPE_FILE) {
252 unsigned int sLockType;
253 LARGE_INTEGER LOffset, LLength;
255 /* Check if there's some sort of lock on the file at the
258 if (rights & PRSFS_WRITE)
261 sLockType = LOCKING_ANDX_SHARED_LOCK;
263 key = cm_GenerateKey(session_id, process_id, 0);
265 /* single byte lock at offset 0x0100 0000 0000 0000 */
266 LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
267 LOffset.LowPart = CM_FLSHARE_OFFSET_LOW;
268 LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
269 LLength.LowPart = CM_FLSHARE_LENGTH_LOW;
271 code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
274 (*ldpp) = (cm_lock_data_t *)malloc(sizeof(cm_lock_data_t));
281 (*ldpp)->sLockType = sLockType;
282 (*ldpp)->LOffset.HighPart = LOffset.HighPart;
283 (*ldpp)->LOffset.LowPart = LOffset.LowPart;
284 (*ldpp)->LLength.HighPart = LLength.HighPart;
285 (*ldpp)->LLength.LowPart = LLength.LowPart;
288 * In this case, we allow the file open to go through even
289 * though we can't enforce mandatory locking on the
291 if (code == CM_ERROR_NOACCESS &&
292 !(rights & PRSFS_WRITE))
295 if (code == CM_ERROR_LOCK_NOT_GRANTED)
296 code = CM_ERROR_SHARING_VIOLATION;
299 } else if (code != 0) {
304 lock_ReleaseWrite(&scp->rw);
307 osi_Log3(afsd_logp,"cm_CheckNTOpen scp 0x%p ldp 0x%p code 0x%x", scp, *ldpp, code);
311 extern long cm_CheckNTOpenDone(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp,
312 cm_lock_data_t ** ldpp)
314 osi_Log2(afsd_logp,"cm_CheckNTOpenDone scp 0x%p ldp 0x%p", scp, ldpp ? *ldpp : 0);
315 lock_ObtainWrite(&scp->rw);
317 cm_Unlock(scp, (*ldpp)->sLockType, (*ldpp)->LOffset, (*ldpp)->LLength,
318 (*ldpp)->key, 0, userp, reqp);
322 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
323 lock_ReleaseWrite(&scp->rw);
327 * When CAP_NT_SMBS has been negotiated, deletion (of files or directories) is
328 * done in three steps:
329 * (1) open for deletion (NT_CREATE_AND_X)
330 * (2) set for deletion on close (NT_TRANSACTION2, SET_FILE_INFO)
332 * We must not do the RPC until step 3. But if we are going to return an error
333 * code (e.g. directory not empty), we must return it by step 2, otherwise most
334 * clients will not notice it. So we do a preliminary check. For deleting
335 * files, this is almost free, since we have already done the RPC to get the
336 * parent directory's status bits. But for deleting directories, we must do an
337 * additional RPC to get the directory's data to check if it is empty. Sigh.
339 long cm_CheckNTDelete(cm_scache_t *dscp, cm_scache_t *scp, cm_user_t *userp,
345 cm_dirEntry_t *dep = 0;
346 unsigned short *hashTable;
348 int BeyondPage = 0, HaveDot = 0, HaveDotDot = 0;
351 /* First check permissions */
352 lock_ObtainWrite(&scp->rw);
353 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_DELETE,
354 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
356 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
357 lock_ReleaseWrite(&scp->rw);
361 /* If deleting directory, must be empty */
363 if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
366 thyper.HighPart = 0; thyper.LowPart = 0;
367 code = buf_Get(scp, &thyper, reqp, &bufferp);
371 lock_ObtainMutex(&bufferp->mx);
372 lock_ObtainWrite(&scp->rw);
375 code = cm_SyncOp(scp, bufferp, userp, reqp, 0,
376 CM_SCACHESYNC_NEEDCALLBACK
378 | CM_SCACHESYNC_BUFLOCKED);
382 if (cm_HaveBuffer(scp, bufferp, 1))
385 /* otherwise, load the buffer and try again */
386 lock_ReleaseMutex(&bufferp->mx);
387 code = cm_GetBuffer(scp, bufferp, NULL, userp, reqp);
388 lock_ReleaseWrite(&scp->rw);
389 lock_ObtainMutex(&bufferp->mx);
390 lock_ObtainWrite(&scp->rw);
391 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
396 lock_ReleaseWrite(&scp->rw);
399 /* We try to determine emptiness without looking beyond the first page,
400 * and without assuming "." and ".." are present and are on the first
401 * page (though these assumptions might, after all, be reasonable).
403 hashTable = (unsigned short *)(bufferp->datap + (32 * 5));
404 for (i=0; i<128; i++) {
405 idx = ntohs(hashTable[i]);
411 dep = (cm_dirEntry_t *)(bufferp->datap + (32 * idx));
412 if (strcmp(dep->name, ".") == 0)
414 else if (strcmp(dep->name, "..") == 0)
417 code = CM_ERROR_NOTEMPTY;
420 idx = ntohs(dep->next);
423 if (BeyondPage && HaveDot && HaveDotDot)
424 code = CM_ERROR_NOTEMPTY;
428 lock_ReleaseMutex(&bufferp->mx);
429 buf_Release(bufferp);
431 lock_ReleaseWrite(&scp->rw);
436 * Iterate through all entries in a directory.
437 * When the function funcp is called, the buffer is locked but the
438 * directory vnode is not.
440 * If the retscp parameter is not NULL, the parmp must be a
441 * cm_lookupSearch_t object.
443 long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
444 osi_hyper_t *startOffsetp, cm_user_t *userp, cm_req_t *reqp,
445 cm_scache_t **retscp)
449 cm_dirEntry_t *dep = 0;
452 osi_hyper_t dirLength;
453 osi_hyper_t bufferOffset;
454 osi_hyper_t curOffset;
458 cm_pageHeader_t *pageHeaderp;
460 long nextEntryCookie;
461 int numDirChunks; /* # of 32 byte dir chunks in this entry */
463 /* get the directory size */
464 lock_ObtainWrite(&scp->rw);
465 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_LOOKUP,
466 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
467 lock_ReleaseWrite(&scp->rw);
471 if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
472 return CM_ERROR_NOTDIR;
474 if (retscp) /* if this is a lookup call */
476 cm_lookupSearch_t* sp = parmp;
479 #ifdef AFS_FREELANCE_CLIENT
480 /* Freelance entries never end up in the DNLC because they
481 * do not have an associated cm_server_t
483 !(cm_freelanceEnabled &&
484 sp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
485 sp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
486 #else /* !AFS_FREELANCE_CLIENT */
491 int casefold = sp->caseFold;
492 sp->caseFold = 0; /* we have a strong preference for exact matches */
493 if ( *retscp = cm_dnlcLookup(scp, sp)) /* dnlc hit */
495 sp->caseFold = casefold;
498 sp->caseFold = casefold;
502 * see if we can find it using the directory hash tables.
503 * we can only do exact matches, since the hash is case
506 if (funcp != (cm_DirFuncp_t)cm_BPlusDirFoo)
515 code = cm_BeginDirOp(scp, userp, reqp, CM_DIRLOCK_READ,
516 CM_DIROP_FLAG_NONE, &dirop);
520 code = cm_BPlusDirLookup(&dirop, sp->nsearchNamep, &sp->fid);
525 code = cm_DirLookup(&dirop, sp->searchNamep, &sp->fid);
533 sp->ExactFound = TRUE;
534 *retscp = NULL; /* force caller to call cm_GetSCache() */
539 if (sp->caseFold && code == CM_ERROR_INEXACT_MATCH) {
542 sp->ExactFound = FALSE;
543 *retscp = NULL; /* force caller to call cm_GetSCache() */
547 return CM_ERROR_BPLUS_NOMATCH;
554 * XXX We only get the length once. It might change when we drop the
557 dirLength = scp->length;
560 bufferOffset.LowPart = bufferOffset.HighPart = 0;
562 curOffset = *startOffsetp;
564 curOffset.HighPart = 0;
565 curOffset.LowPart = 0;
569 /* make sure that curOffset.LowPart doesn't point to the first
570 * 32 bytes in the 2nd through last dir page, and that it
571 * doesn't point at the first 13 32-byte chunks in the first
572 * dir page, since those are dir and page headers, and don't
573 * contain useful information.
575 temp = curOffset.LowPart & (2048-1);
576 if (curOffset.HighPart == 0 && curOffset.LowPart < 2048) {
577 /* we're in the first page */
578 if (temp < 13*32) temp = 13*32;
581 /* we're in a later dir page */
582 if (temp < 32) temp = 32;
585 /* make sure the low order 5 bits are zero */
588 /* now put temp bits back ito curOffset.LowPart */
589 curOffset.LowPart &= ~(2048-1);
590 curOffset.LowPart |= temp;
592 /* check if we've passed the dir's EOF */
593 if (LargeIntegerGreaterThanOrEqualTo(curOffset, dirLength))
596 /* see if we can use the bufferp we have now; compute in which
597 * page the current offset would be, and check whether that's
598 * the offset of the buffer we have. If not, get the buffer.
600 thyper.HighPart = curOffset.HighPart;
601 thyper.LowPart = curOffset.LowPart & ~(cm_data.buf_blockSize-1);
602 if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
605 lock_ReleaseMutex(&bufferp->mx);
606 buf_Release(bufferp);
610 code = buf_Get(scp, &thyper, reqp, &bufferp);
612 /* if buf_Get() fails we do not have a buffer object to lock */
617 lock_ObtainMutex(&bufferp->mx);
618 bufferOffset = thyper;
620 /* now get the data in the cache */
622 lock_ObtainWrite(&scp->rw);
623 code = cm_SyncOp(scp, bufferp, userp, reqp,
625 CM_SCACHESYNC_NEEDCALLBACK
627 | CM_SCACHESYNC_BUFLOCKED);
629 lock_ReleaseWrite(&scp->rw);
632 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
634 if (cm_HaveBuffer(scp, bufferp, 1)) {
635 lock_ReleaseWrite(&scp->rw);
639 /* otherwise, load the buffer and try again */
640 lock_ReleaseMutex(&bufferp->mx);
641 code = cm_GetBuffer(scp, bufferp, NULL, userp,
643 lock_ReleaseWrite(&scp->rw);
644 lock_ObtainMutex(&bufferp->mx);
649 lock_ReleaseMutex(&bufferp->mx);
650 buf_Release(bufferp);
654 } /* if (wrong buffer) ... */
656 /* now we have the buffer containing the entry we're interested
657 * in; copy it out if it represents a non-deleted entry.
659 entryInDir = curOffset.LowPart & (2048-1);
660 entryInBuffer = curOffset.LowPart & (cm_data.buf_blockSize - 1);
662 /* page header will help tell us which entries are free. Page
663 * header can change more often than once per buffer, since
664 * AFS 3 dir page size may be less than (but not more than) a
665 * buffer package buffer.
667 /* only look intra-buffer */
668 temp = curOffset.LowPart & (cm_data.buf_blockSize - 1);
669 temp &= ~(2048 - 1); /* turn off intra-page bits */
670 pageHeaderp = (cm_pageHeader_t *) (bufferp->datap + temp);
672 /* now determine which entry we're looking at in the page. If
673 * it is free (there's a free bitmap at the start of the dir),
674 * we should skip these 32 bytes.
676 slotInPage = (entryInDir & 0x7e0) >> 5;
677 if (!(pageHeaderp->freeBitmap[slotInPage>>3]
678 & (1 << (slotInPage & 0x7)))) {
679 /* this entry is free */
680 numDirChunks = 1; /* only skip this guy */
684 tp = bufferp->datap + entryInBuffer;
685 dep = (cm_dirEntry_t *) tp; /* now points to AFS3 dir entry */
688 * here are some consistency checks
690 if (dep->flag != CM_DIR_FFIRST ||
691 strlen(dep->name) > 256) {
692 code = CM_ERROR_INVAL;
694 "cm_ApplyDir invalid directory entry for scp %p bufp %p",
696 osi_Log4(afsd_logp,"... cell %u vol %u vnode %u uniq %u",
697 scp->fid.cell, scp->fid.volume, scp->fid.vnode, scp->fid.unique);
698 bufferp->dataVersion = CM_BUF_VERSION_BAD;
702 /* while we're here, compute the next entry's location, too,
703 * since we'll need it when writing out the cookie into the
704 * dir listing stream.
706 numDirChunks = cm_NameEntries(dep->name, NULL);
708 /* compute the offset of the cookie representing the next entry */
709 nextEntryCookie = curOffset.LowPart
710 + (CM_DIR_CHUNKSIZE * numDirChunks);
712 if (dep->fid.vnode != 0) {
713 /* this is one of the entries to use: it is not deleted */
714 code = (*funcp)(scp, dep, parmp, &curOffset);
717 } /* if we're including this name */
720 /* and adjust curOffset to be where the new cookie is */
722 thyper.LowPart = CM_DIR_CHUNKSIZE * numDirChunks;
723 curOffset = LargeIntegerAdd(thyper, curOffset);
724 } /* while copying data for dir listing */
726 /* release the mutex */
728 lock_ReleaseMutex(&bufferp->mx);
729 buf_Release(bufferp);
734 int cm_NoneUpper(normchar_t *s)
738 if (c >= 'A' && c <= 'Z')
743 int cm_NoneLower(normchar_t *s)
747 if (c >= 'a' && c <= 'z')
752 long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
755 cm_lookupSearch_t *sp;
757 normchar_t matchName[MAX_PATH];
758 int looking_for_short_name = FALSE;
760 sp = (cm_lookupSearch_t *) rockp;
762 if (cm_FsStringToNormString(dep->name, -1, matchName, lengthof(matchName)) == 0) {
763 /* Can't normalize FS string. */
768 match = cm_NormStrCmpI(matchName, sp->nsearchNamep);
770 match = cm_NormStrCmp(matchName, sp->nsearchNamep);
774 && !cm_Is8Dot3(matchName)) {
776 cm_Gen8Dot3NameInt(dep->name, &dep->fid, matchName, NULL);
778 match = cm_NormStrCmpI(matchName, sp->nsearchNamep);
780 match = cm_NormStrCmp(matchName, sp->nsearchNamep);
781 looking_for_short_name = TRUE;
791 if (!sp->caseFold || looking_for_short_name) {
792 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
793 return CM_ERROR_STOPNOW;
797 * If we get here, we are doing a case-insensitive search, and we
798 * have found a match. Now we determine what kind of match it is:
799 * exact, lower-case, upper-case, or none of the above. This is done
800 * in order to choose among matches, if there are more than one.
803 /* Exact matches are the best. */
804 match = cm_NormStrCmp(matchName, sp->nsearchNamep);
807 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
808 return CM_ERROR_STOPNOW;
811 /* Lower-case matches are next. */
814 if (cm_NoneUpper(matchName)) {
819 /* Upper-case matches are next. */
822 if (cm_NoneLower(matchName)) {
827 /* General matches are last. */
833 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
837 /* read the contents of a mount point into the appropriate string.
838 * called with write locked scp, and returns with locked scp.
840 long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
844 if (scp->mountPointStringp[0])
847 #ifdef AFS_FREELANCE_CLIENT
848 /* File servers do not have data for freelance entries */
849 if (cm_freelanceEnabled &&
850 scp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
851 scp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
853 code = cm_FreelanceFetchMountPointString(scp);
855 #endif /* AFS_FREELANCE_CLIENT */
857 char temp[MOUNTPOINTLEN];
860 /* otherwise, we have to read it in */
861 offset.LowPart = offset.HighPart = 0;
862 code = cm_GetData(scp, &offset, temp, MOUNTPOINTLEN, userp, reqp);
867 * scp->length is the actual length of the mount point string.
868 * It is current because cm_GetData merged the most up to date
869 * status info into scp and has not dropped the rwlock since.
871 if (scp->length.LowPart > MOUNTPOINTLEN - 1)
872 return CM_ERROR_TOOBIG;
873 if (scp->length.LowPart == 0)
874 return CM_ERROR_INVAL;
876 /* convert the terminating dot to a NUL */
877 temp[scp->length.LowPart - 1] = 0;
878 memcpy(scp->mountPointStringp, temp, scp->length.LowPart);
885 /* called with a locked scp and chases the mount point, yielding outScpp.
886 * scp remains write locked, just for simplicity of describing the interface.
888 long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
889 cm_req_t *reqp, cm_scache_t **outScpp)
891 fschar_t *cellNamep = NULL;
892 fschar_t *volNamep = NULL;
896 cm_volume_t *volp = NULL;
905 if (scp->mountRootFid.cell != 0 && scp->mountRootGen >= cm_data.mountRootGen) {
906 tfid = scp->mountRootFid;
907 lock_ReleaseWrite(&scp->rw);
908 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
909 lock_ObtainWrite(&scp->rw);
913 /* parse the volume name */
914 mpNamep = scp->mountPointStringp;
916 return CM_ERROR_NOSUCHPATH;
917 mtType = *scp->mountPointStringp;
919 cp = cm_FsStrChr(mpNamep, _FS(':'));
921 /* cellular mount point */
922 cellNamep = (fschar_t *)malloc((cp - mpNamep) * sizeof(fschar_t));
923 cm_FsStrCpyN(cellNamep, cp - mpNamep, mpNamep + 1, cp - mpNamep - 1);
924 volNamep = cm_FsStrDup(cp+1);
926 /* now look up the cell */
927 lock_ReleaseWrite(&scp->rw);
928 cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE);
929 lock_ObtainWrite(&scp->rw);
932 volNamep = cm_FsStrDup(mpNamep + 1);
934 #ifdef AFS_FREELANCE_CLIENT
936 * Mount points in the Freelance cell should default
937 * to the workstation cell.
939 if (cm_freelanceEnabled &&
940 scp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
941 scp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
943 fschar_t rootCellName[256]="";
944 cm_GetRootCellName(rootCellName);
945 cellp = cm_GetCell(rootCellName, 0);
947 #endif /* AFS_FREELANCE_CLIENT */
948 cellp = cm_FindCellByID(scp->fid.cell, 0);
952 code = CM_ERROR_NOSUCHCELL;
956 vnLength = cm_FsStrLen(volNamep);
957 if (vnLength >= 8 && cm_FsStrCmp(volNamep + vnLength - 7, ".backup") == 0)
958 targetType = BACKVOL;
959 else if (vnLength >= 10
960 && cm_FsStrCmp(volNamep + vnLength - 9, ".readonly") == 0)
965 /* check for backups within backups */
966 if (targetType == BACKVOL
967 && (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
968 == CM_SCACHEFLAG_RO) {
969 code = CM_ERROR_NOSUCHVOLUME;
973 /* now we need to get the volume */
974 lock_ReleaseWrite(&scp->rw);
975 if (cm_VolNameIsID(volNamep)) {
976 code = cm_FindVolumeByID(cellp, atoi(volNamep), userp, reqp,
977 CM_GETVOL_FLAG_CREATE, &volp);
979 code = cm_FindVolumeByName(cellp, volNamep, userp, reqp,
980 CM_GETVOL_FLAG_CREATE, &volp);
982 lock_ObtainWrite(&scp->rw);
985 afs_uint32 cell, volume;
986 cm_vol_state_t *statep;
988 cell = cellp->cellID;
990 /* if the mt pt originates in a .backup volume (not a .readonly)
991 * and FollowBackupPath is active, and if there is a .backup
992 * volume for the target, then use the .backup of the target
993 * instead of the read-write.
995 if (cm_followBackupPath &&
996 volp->vol[BACKVOL].ID != 0 &&
997 (dscp->flags & (CM_SCACHEFLAG_RO|CM_SCACHEFLAG_PURERO)) == CM_SCACHEFLAG_RO &&
998 (targetType == RWVOL || targetType == ROVOL && volp->vol[ROVOL].ID == 0)
1000 targetType = BACKVOL;
1002 /* if the mt pt is in a read-only volume (not just a
1003 * backup), and if there is a read-only volume for the
1004 * target, and if this is a targetType '#' mount point, use
1005 * the read-only, otherwise use the one specified.
1007 else if (mtType == '#' && targetType == RWVOL &&
1008 (scp->flags & CM_SCACHEFLAG_PURERO) &&
1009 volp->vol[ROVOL].ID != 0) {
1013 lock_ObtainWrite(&volp->rw);
1014 statep = cm_VolumeStateByType(volp, targetType);
1015 volume = statep->ID;
1016 statep->dotdotFid = dscp->fid;
1017 lock_ReleaseWrite(&volp->rw);
1019 /* the rest of the fid is a magic number */
1020 cm_SetFid(&scp->mountRootFid, cell, volume, 1, 1);
1021 scp->mountRootGen = cm_data.mountRootGen;
1023 tfid = scp->mountRootFid;
1024 lock_ReleaseWrite(&scp->rw);
1025 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
1026 lock_ObtainWrite(&scp->rw);
1039 long cm_LookupInternal(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_user_t *userp,
1040 cm_req_t *reqp, cm_scache_t **outScpp)
1043 int dnlcHit = 1; /* did we hit in the dnlc? yes, we did */
1044 cm_scache_t *tscp = NULL;
1045 cm_scache_t *mountedScp;
1046 cm_lookupSearch_t rock;
1048 normchar_t *nnamep = NULL;
1049 fschar_t *fnamep = NULL;
1054 memset(&rock, 0, sizeof(rock));
1056 if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
1057 && cm_ClientStrCmp(cnamep, _C("..")) == 0) {
1058 if (dscp->dotdotFid.volume == 0)
1059 return CM_ERROR_NOSUCHVOLUME;
1060 rock.fid = dscp->dotdotFid;
1062 } else if (cm_ClientStrCmp(cnamep, _C(".")) == 0) {
1063 rock.fid = dscp->fid;
1067 nnamep = cm_ClientStringToNormStringAlloc(cnamep, -1, NULL);
1069 code = CM_ERROR_NOSUCHFILE;
1072 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
1074 code = CM_ERROR_NOSUCHFILE;
1079 if (flags & CM_FLAG_NOMOUNTCHASE) {
1080 /* In this case, we should go and call cm_Dir* functions
1081 directly since the following cm_ApplyDir() function will
1089 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ,
1090 CM_DIROP_FLAG_NONE, &dirop);
1093 code = cm_BPlusDirLookup(&dirop, nnamep, &rock.fid);
1098 code = cm_DirLookup(&dirop, fnamep, &rock.fid);
1100 cm_EndDirOp(&dirop);
1110 if (code == CM_ERROR_INEXACT_MATCH && (flags & CM_FLAG_CASEFOLD)) {
1117 code = CM_ERROR_BPLUS_NOMATCH;
1123 rock.fid.cell = dscp->fid.cell;
1124 rock.fid.volume = dscp->fid.volume;
1125 rock.searchNamep = fnamep;
1126 rock.nsearchNamep = nnamep;
1127 rock.caseFold = (flags & CM_FLAG_CASEFOLD);
1128 rock.hasTilde = ((cm_ClientStrChr(cnamep, '~') != NULL) ? 1 : 0);
1130 /* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
1131 code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
1132 (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
1134 /* code == 0 means we fell off the end of the dir, while stopnow means
1135 * that we stopped early, probably because we found the entry we're
1136 * looking for. Any other non-zero code is an error.
1138 if (code && code != CM_ERROR_STOPNOW && code != CM_ERROR_BPLUS_NOMATCH) {
1139 /* if the cm_scache_t we are searching in is not a directory
1140 * we must return path not found because the error
1141 * is to describe the final component not an intermediary
1143 if (code == CM_ERROR_NOTDIR) {
1144 if (flags & CM_FLAG_CHECKPATH)
1145 code = CM_ERROR_NOSUCHPATH;
1147 code = CM_ERROR_NOSUCHFILE;
1153 getroot = (dscp==cm_data.rootSCachep) ;
1155 if (!cm_freelanceEnabled || !getroot) {
1156 if (flags & CM_FLAG_CHECKPATH)
1157 code = CM_ERROR_NOSUCHPATH;
1159 code = CM_ERROR_NOSUCHFILE;
1162 else if (!cm_ClientStrChr(cnamep, '#') &&
1163 !cm_ClientStrChr(cnamep, '%') &&
1164 cm_ClientStrCmpI(cnamep, _C("srvsvc")) &&
1165 cm_ClientStrCmpI(cnamep, _C("wkssvc")) &&
1166 cm_ClientStrCmpI(cnamep, _C("ipc$")))
1168 /* nonexistent dir on freelance root, so add it */
1169 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 fnlen = strlen(fnamep);
1185 if ( fnamep[fnlen-1] == '.') {
1186 fnamep[fnlen-1] = '\0';
1191 if (cnamep[0] == '.') {
1192 if (cm_GetCell_Gen(&fnamep[1], &fullname[1], CM_FLAG_CREATE)) {
1194 code = cm_FreelanceAddMount(fullname, &fullname[1], "root.cell", 1, &rock.fid);
1195 if ( cm_FsStrCmpI(&fnamep[1], &fullname[1])) {
1197 * Do not permit symlinks that are one of:
1198 * . the cellname followed by a dot
1199 * . the cellname minus a single character
1200 * . a substring of the cellname that does not consist of full components
1202 if ( cm_strnicmp_utf8(&fnamep[1], fullname, (int)fnlen-1) == 0 &&
1203 (fnlen-1 == strlen(fullname)-1 || fullname[fnlen-1] != '.'))
1205 /* do not add; substitute fullname for the search */
1207 fnamep = malloc(strlen(fullname)+2);
1209 strncpy(&fnamep[1], fullname, strlen(fullname)+1);
1212 code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
1217 if (cm_GetCell_Gen(fnamep, fullname, CM_FLAG_CREATE)) {
1219 code = cm_FreelanceAddMount(fullname, fullname, "root.cell", 0, &rock.fid);
1220 if ( cm_FsStrCmpI(fnamep, fullname)) {
1222 * Do not permit symlinks that are one of:
1223 * . the cellname followed by a dot
1224 * . the cellname minus a single character
1225 * . a substring of the cellname that does not consist of full components
1227 if ( cm_strnicmp_utf8(fnamep, fullname, (int)fnlen-1) == 0 &&
1228 (fnlen == strlen(fullname)-1 || fullname[fnlen] != '.'))
1230 /* do not add; substitute fullname for the search */
1232 fnamep = strdup(fullname);
1236 code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
1245 nnamep = cm_FsStringToNormStringAlloc(fnamep, -1, NULL);
1249 if (!found || code) { /* add mount point failed, so give up */
1250 if (flags & CM_FLAG_CHECKPATH)
1251 code = CM_ERROR_NOSUCHPATH;
1253 code = CM_ERROR_NOSUCHFILE;
1256 tscp = NULL; /* to force call of cm_GetSCache */
1258 if (flags & CM_FLAG_CHECKPATH)
1259 code = CM_ERROR_NOSUCHPATH;
1261 code = CM_ERROR_NOSUCHFILE;
1267 if ( !tscp ) /* we did not find it in the dnlc */
1270 code = cm_GetSCache(&rock.fid, &tscp, userp, reqp);
1274 /* tscp is now held */
1276 lock_ObtainWrite(&tscp->rw);
1279 * Do not get status if we do not already have a callback.
1280 * The process of reading the mount point string will obtain status information
1281 * in a single RPC. No reason to add a second round trip.
1283 * If we do have a callback, use cm_SyncOp to get status in case the
1284 * current cm_user_t is not the same as the one that obtained the
1285 * mount point string contents.
1287 if (cm_HaveCallback(tscp)) {
1288 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1289 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1291 lock_ReleaseWrite(&tscp->rw);
1292 cm_ReleaseSCache(tscp);
1295 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1297 /* tscp is now locked */
1299 if (!(flags & CM_FLAG_NOMOUNTCHASE)
1300 && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
1301 /* mount points are funny: they have a volume name to mount
1304 code = cm_ReadMountPoint(tscp, userp, reqp);
1306 code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
1308 lock_ReleaseWrite(&tscp->rw);
1309 cm_ReleaseSCache(tscp);
1316 lock_ReleaseWrite(&tscp->rw);
1319 /* copy back pointer */
1322 /* insert scache in dnlc */
1323 if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
1324 /* lock the directory entry to prevent racing callback revokes */
1325 lock_ObtainRead(&dscp->rw);
1326 if ( dscp->cbServerp != NULL && dscp->cbExpires > 0 ) {
1327 /* TODO: reuse nnamep from above */
1330 nnamep = cm_ClientStringToNormStringAlloc(cnamep, -1, NULL);
1332 cm_dnlcEnter(dscp, nnamep, tscp);
1334 lock_ReleaseRead(&dscp->rw);
1351 int cm_ExpandSysName(cm_req_t * reqp, clientchar_t *inp, clientchar_t *outp, long outSizeCch, unsigned int index)
1356 int use_sysname64 = 0;
1358 if (cm_sysName64Count > 0 && reqp && (reqp->flags & CM_REQ_WOW64) && (reqp->flags & CM_REQ_SOURCE_REDIR))
1362 tp = cm_ClientStrRChr(inp, '@');
1364 return 0; /* no @sys */
1366 if (cm_ClientStrCmp(tp, _C("@sys")) != 0)
1367 return 0; /* no @sys */
1369 /* caller just wants to know if this is a valid @sys type of name */
1374 if (use_sysname64 && index >= cm_sysName64Count)
1378 if (index >= cm_sysNameCount)
1381 /* otherwise generate the properly expanded @sys name */
1382 prefixCount = (int)(tp - inp);
1384 cm_ClientStrCpyN(outp, outSizeCch, inp, prefixCount); /* copy out "a." from "a.@sys" */
1385 outp[prefixCount] = 0; /* null terminate the "a." */
1388 cm_ClientStrCat(outp, outSizeCch, cm_sysName64List[index]);
1391 cm_ClientStrCat(outp, outSizeCch, cm_sysNameList[index]);
1396 long cm_EvaluateVolumeReference(clientchar_t * namep, long flags, cm_user_t * userp,
1397 cm_req_t *reqp, cm_scache_t ** outScpp)
1399 afs_uint32 code = 0;
1400 fschar_t cellName[CELL_MAXNAMELEN];
1401 fschar_t volumeName[VL_MAXNAMELEN];
1405 fschar_t * fnamep = NULL;
1407 cm_cell_t * cellp = NULL;
1408 cm_volume_t * volp = NULL;
1412 int mountType = RWVOL;
1414 osi_Log1(afsd_logp, "cm_EvaluateVolumeReference for string [%S]",
1415 osi_LogSaveClientString(afsd_logp, namep));
1417 if (cm_ClientStrCmpNI(namep, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH) != 0) {
1418 goto _exit_invalid_path;
1421 /* namep is assumed to look like the following:
1423 @vol:<cellname>%<volume>\0
1425 @vol:<cellname>#<volume>\0
1429 fnamep = cm_ClientStringToFsStringAlloc(namep, -1, NULL);
1430 cp = fnamep + CM_PREFIX_VOL_CCH; /* cp points to cell name, hopefully */
1431 tp = cm_FsStrChr(cp, '%');
1433 tp = cm_FsStrChr(cp, '#');
1435 (len = tp - cp) == 0 ||
1436 len > CELL_MAXNAMELEN)
1437 goto _exit_invalid_path;
1438 cm_FsStrCpyN(cellName, lengthof(cellName), cp, len);
1443 cp = tp+1; /* cp now points to volume, supposedly */
1444 cm_FsStrCpy(volumeName, lengthof(volumeName), cp);
1446 /* OK, now we have the cell and the volume */
1447 osi_Log2(afsd_logp, " Found cell [%s] and volume [%s]",
1448 osi_LogSaveFsString(afsd_logp, cellName),
1449 osi_LogSaveFsString(afsd_logp, volumeName));
1451 cellp = cm_GetCell(cellName, CM_FLAG_CREATE);
1452 if (cellp == NULL) {
1453 goto _exit_invalid_path;
1456 len = cm_FsStrLen(volumeName);
1457 if (len >= 8 && cm_FsStrCmp(volumeName + len - 7, ".backup") == 0)
1459 else if (len >= 10 &&
1460 cm_FsStrCmp(volumeName + len - 9, ".readonly") == 0)
1465 if (cm_VolNameIsID(volumeName)) {
1466 code = cm_FindVolumeByID(cellp, atoi(volumeName), userp, reqp,
1467 CM_GETVOL_FLAG_CREATE, &volp);
1469 code = cm_FindVolumeByName(cellp, volumeName, userp, reqp,
1470 CM_GETVOL_FLAG_CREATE, &volp);
1476 if (volType == BACKVOL)
1477 volume = volp->vol[BACKVOL].ID;
1478 else if (volType == ROVOL ||
1479 (volType == RWVOL && mountType == ROVOL && volp->vol[ROVOL].ID != 0))
1480 volume = volp->vol[ROVOL].ID;
1482 volume = volp->vol[RWVOL].ID;
1484 cm_SetFid(&fid, cellp->cellID, volume, 1, 1);
1486 code = cm_GetSCache(&fid, outScpp, userp, reqp);
1499 if (flags & CM_FLAG_CHECKPATH)
1500 return CM_ERROR_NOSUCHPATH;
1502 return CM_ERROR_NOSUCHFILE;
1505 #ifdef DEBUG_REFCOUNT
1506 long cm_LookupDbg(cm_scache_t *dscp, clientchar_t *namep, long flags, cm_user_t *userp,
1507 cm_req_t *reqp, cm_scache_t **outScpp, char * file, long line)
1509 long cm_Lookup(cm_scache_t *dscp, clientchar_t *namep, long flags, cm_user_t *userp,
1510 cm_req_t *reqp, cm_scache_t **outScpp)
1514 clientchar_t tname[AFSPATHMAX];
1515 int sysNameIndex = 0;
1516 cm_scache_t *scp = NULL;
1518 #ifdef DEBUG_REFCOUNT
1519 afsi_log("%s:%d cm_Lookup dscp 0x%p ref %d", file, line, dscp, dscp->refCount, file, line);
1520 osi_Log2(afsd_logp, "cm_Lookup dscp 0x%p ref %d", dscp, dscp->refCount);
1523 if ( cm_ClientStrCmpI(namep,_C(SMB_IOCTL_FILENAME_NOSLASH)) == 0 ) {
1524 if (flags & CM_FLAG_CHECKPATH)
1525 return CM_ERROR_NOSUCHPATH;
1527 return CM_ERROR_NOSUCHFILE;
1530 if (dscp == cm_data.rootSCachep &&
1531 cm_ClientStrCmpNI(namep, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH) == 0) {
1532 return cm_EvaluateVolumeReference(namep, flags, userp, reqp, outScpp);
1535 if (cm_ExpandSysName(reqp, namep, NULL, 0, 0) > 0) {
1536 for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
1537 code = cm_ExpandSysName(reqp, namep, tname, lengthof(tname), sysNameIndex);
1539 code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
1540 #ifdef DEBUG_REFCOUNT
1541 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);
1542 osi_Log3(afsd_logp, "cm_LookupInternal (1) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1550 cm_ReleaseSCache(scp);
1554 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1555 #ifdef DEBUG_REFCOUNT
1556 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);
1557 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1564 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1565 #ifdef DEBUG_REFCOUNT
1566 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);
1567 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1573 /* None of the possible sysName expansions could be found */
1574 if (flags & CM_FLAG_CHECKPATH)
1575 return CM_ERROR_NOSUCHPATH;
1577 return CM_ERROR_NOSUCHFILE;
1580 /*! \brief Unlink a file name
1582 Encapsulates a call to RXAFS_RemoveFile().
1584 \param[in] dscp cm_scache_t pointing at the directory containing the
1585 name to be unlinked.
1587 \param[in] fnamep Original name to be unlinked. This is the
1588 name that will be passed into the RXAFS_RemoveFile() call.
1589 This parameter is optional. If not provided, the value will
1592 \param[in] came Client name to be unlinked. This name will be used
1593 to update the local directory caches.
1595 \param[in] userp cm_user_t for the request.
1597 \param[in] reqp Request tracker.
1600 long cm_Unlink(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t * cnamep,
1601 cm_user_t *userp, cm_req_t *reqp)
1607 AFSFetchStatus newDirStatus;
1609 struct rx_connection * rxconnp;
1611 cm_scache_t *scp = NULL;
1612 int free_fnamep = FALSE;
1615 memset(&volSync, 0, sizeof(volSync));
1617 if (fnamep == NULL) {
1620 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ,
1621 CM_DIROP_FLAG_NONE, &dirop);
1623 code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
1626 cm_EndDirOp(&dirop);
1633 #ifdef AFS_FREELANCE_CLIENT
1634 if (cm_freelanceEnabled && dscp == cm_data.rootSCachep) {
1635 /* deleting a mount point from the root dir. */
1636 code = cm_FreelanceRemoveMount(fnamep);
1641 code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
1645 /* Check for RO volume */
1646 if (dscp->flags & CM_SCACHEFLAG_RO) {
1647 code = CM_ERROR_READONLY;
1651 /* make sure we don't screw up the dir status during the merge */
1652 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE,
1653 CM_DIROP_FLAG_NONE, &dirop);
1655 lock_ObtainWrite(&dscp->rw);
1656 sflags = CM_SCACHESYNC_STOREDATA;
1657 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1658 lock_ReleaseWrite(&dscp->rw);
1660 cm_EndDirOp(&dirop);
1665 InterlockedIncrement(&dscp->activeRPCs);
1667 afsFid.Volume = dscp->fid.volume;
1668 afsFid.Vnode = dscp->fid.vnode;
1669 afsFid.Unique = dscp->fid.unique;
1671 osi_Log1(afsd_logp, "CALL RemoveFile scp 0x%p", dscp);
1673 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
1677 rxconnp = cm_GetRxConn(connp);
1678 code = RXAFS_RemoveFile(rxconnp, &afsFid, fnamep,
1679 &newDirStatus, &volSync);
1680 rx_PutConnection(rxconnp);
1682 } while (cm_Analyze(connp, userp, reqp, &dscp->fid, &volSync, NULL, NULL, code));
1683 code = cm_MapRPCError(code, reqp);
1686 osi_Log1(afsd_logp, "CALL RemoveFile FAILURE, code 0x%x", code);
1688 osi_Log0(afsd_logp, "CALL RemoveFile SUCCESS");
1691 lock_ObtainWrite(&dirop.scp->dirlock);
1692 dirop.lockType = CM_DIRLOCK_WRITE;
1694 lock_ObtainWrite(&dscp->rw);
1695 cm_dnlcRemove(dscp, cnamep);
1697 cm_MergeStatus(NULL, dscp, &newDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
1699 if (cm_CheckDirOpForSingleChange(&dirop) && cnamep) {
1700 lock_ReleaseWrite(&dscp->rw);
1701 cm_DirDeleteEntry(&dirop, fnamep);
1703 cm_BPlusDirDeleteEntry(&dirop, cnamep);
1705 lock_ObtainWrite(&dscp->rw);
1708 InterlockedDecrement(&scp->activeRPCs);
1709 if (code == CM_ERROR_NOSUCHFILE) {
1710 /* windows would not have allowed the request to delete the file
1711 * if it did not believe the file existed. therefore, we must
1712 * have an inconsistent view of the world.
1714 dscp->cbServerp = NULL;
1718 cm_SyncOpDone(dscp, NULL, sflags);
1719 lock_ReleaseWrite(&dscp->rw);
1721 cm_EndDirOp(&dirop);
1723 if (invalidate && RDR_Initialized &&
1724 scp->fileType != CM_SCACHETYPE_FILE && scp->fileType != CM_SCACHETYPE_DIRECTORY)
1725 RDR_InvalidateObject(dscp->fid.cell, dscp->fid.volume, dscp->fid.vnode,
1726 dscp->fid.unique, dscp->fid.hash,
1727 dscp->fileType, AFS_INVALIDATE_DATA_VERSION);
1730 cm_ReleaseSCache(scp);
1732 lock_ObtainWrite(&scp->rw);
1733 if (--scp->linkCount == 0) {
1734 scp->flags |= CM_SCACHEFLAG_DELETED;
1735 lock_ObtainWrite(&cm_scacheLock);
1736 cm_AdjustScacheLRU(scp);
1737 cm_RemoveSCacheFromHashTable(scp);
1738 lock_ReleaseWrite(&cm_scacheLock);
1740 cm_DiscardSCache(scp);
1741 lock_ReleaseWrite(&scp->rw);
1742 if (RDR_Initialized && !(reqp->flags & CM_REQ_SOURCE_REDIR) &&
1743 !RDR_InvalidateObject(scp->fid.cell, scp->fid.volume, scp->fid.vnode,
1744 scp->fid.unique, scp->fid.hash,
1745 scp->fileType, AFS_INVALIDATE_DELETED))
1746 buf_ClearRDRFlag(scp, "unlink");
1757 /* called with a write locked vnode, and fills in the link info.
1758 * returns this the vnode still write locked.
1760 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1764 lock_AssertWrite(&linkScp->rw);
1765 if (!linkScp->mountPointStringp[0]) {
1767 #ifdef AFS_FREELANCE_CLIENT
1768 /* File servers do not have data for freelance entries */
1769 if (cm_freelanceEnabled &&
1770 linkScp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
1771 linkScp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
1773 code = cm_FreelanceFetchMountPointString(linkScp);
1775 #endif /* AFS_FREELANCE_CLIENT */
1777 char temp[MOUNTPOINTLEN];
1780 /* read the link data from the file server */
1781 offset.LowPart = offset.HighPart = 0;
1782 code = cm_GetData(linkScp, &offset, temp, MOUNTPOINTLEN, userp, reqp);
1787 * linkScp->length is the actual length of the symlink target string.
1788 * It is current because cm_GetData merged the most up to date
1789 * status info into scp and has not dropped the rwlock since.
1791 if (linkScp->length.LowPart > MOUNTPOINTLEN - 1)
1792 return CM_ERROR_TOOBIG;
1793 if (linkScp->length.LowPart == 0)
1794 return CM_ERROR_INVAL;
1796 /* make sure we are NUL terminated */
1797 temp[linkScp->length.LowPart] = 0;
1798 memcpy(linkScp->mountPointStringp, temp, linkScp->length.LowPart + 1);
1801 if ( !strnicmp(linkScp->mountPointStringp, "msdfs:", strlen("msdfs:")) )
1802 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1804 } /* don't have symlink contents cached */
1809 /* called with a held vnode and a path suffix, with the held vnode being a
1810 * symbolic link. Our goal is to generate a new path to interpret, and return
1811 * this new path in newSpaceBufferp. If the new vnode is relative to a dir
1812 * other than the directory containing the symbolic link, then the new root is
1813 * returned in *newRootScpp, otherwise a null is returned there.
1815 long cm_AssembleLink(cm_scache_t *linkScp, fschar_t *pathSuffixp,
1816 cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1817 cm_user_t *userp, cm_req_t *reqp)
1824 *newRootScpp = NULL;
1825 *newSpaceBufferp = NULL;
1827 lock_ObtainWrite(&linkScp->rw);
1829 * Do not get status if we do not already have a callback.
1830 * The process of reading the symlink string will obtain status information
1831 * in a single RPC. No reason to add a second round trip.
1833 * If we do have a callback, use cm_SyncOp to get status in case the
1834 * current cm_user_t is not the same as the one that obtained the
1835 * symlink string contents.
1837 if (cm_HaveCallback(linkScp)) {
1838 code = cm_SyncOp(linkScp, NULL, userp, reqp, 0,
1839 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1841 lock_ReleaseWrite(&linkScp->rw);
1842 cm_ReleaseSCache(linkScp);
1845 cm_SyncOpDone(linkScp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1847 code = cm_HandleLink(linkScp, userp, reqp);
1851 /* if we may overflow the buffer, bail out; buffer is signficantly
1852 * bigger than max path length, so we don't really have to worry about
1853 * being a little conservative here.
1855 if (cm_FsStrLen(linkScp->mountPointStringp) + cm_FsStrLen(pathSuffixp) + 2
1856 >= CM_UTILS_SPACESIZE) {
1857 code = CM_ERROR_TOOBIG;
1861 tsp = cm_GetSpace();
1862 linkp = linkScp->mountPointStringp;
1863 if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1864 if (strlen(linkp) > cm_mountRootLen)
1865 StringCbCopyA((char *) tsp->data, sizeof(tsp->data), linkp+cm_mountRootLen+1);
1868 *newRootScpp = cm_RootSCachep(userp, reqp);
1869 cm_HoldSCache(*newRootScpp);
1870 } else if (linkp[0] == '\\' && linkp[1] == '\\') {
1871 if (!strnicmp(&linkp[2], cm_NetbiosName, (len = (long)strlen(cm_NetbiosName))))
1873 char * p = &linkp[len + 3];
1874 if (strnicmp(p, "all", 3) == 0)
1877 StringCbCopyA(tsp->data, sizeof(tsp->data), p);
1878 for (p = tsp->data; *p; p++) {
1882 *newRootScpp = cm_RootSCachep(userp, reqp);
1883 cm_HoldSCache(*newRootScpp);
1885 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1886 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1887 code = CM_ERROR_PATH_NOT_COVERED;
1889 } else if ( linkScp->fileType == CM_SCACHETYPE_DFSLINK ||
1890 !strnicmp(linkp, "msdfs:", (len = (long)strlen("msdfs:"))) ) {
1891 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1892 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1893 code = CM_ERROR_PATH_NOT_COVERED;
1894 } else if (*linkp == '\\' || *linkp == '/') {
1896 /* formerly, this was considered to be from the AFS root,
1897 * but this seems to create problems. instead, we will just
1898 * reject the link */
1899 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp+1);
1900 *newRootScpp = cm_RootSCachep(userp, reqp);
1901 cm_HoldSCache(*newRootScpp);
1903 /* we still copy the link data into the response so that
1904 * the user can see what the link points to
1906 linkScp->fileType = CM_SCACHETYPE_INVALID;
1907 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1908 code = CM_ERROR_NOSUCHPATH;
1911 /* a relative link */
1912 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1914 if (pathSuffixp[0] != 0) { /* if suffix string is non-null */
1915 StringCchCatA(tsp->data,lengthof(tsp->data), "\\");
1916 StringCchCatA(tsp->data,lengthof(tsp->data), pathSuffixp);
1920 clientchar_t * cpath = cm_FsStringToClientStringAlloc(tsp->data, -1, NULL);
1921 if (cpath != NULL) {
1922 cm_ClientStrCpy(tsp->wdata, lengthof(tsp->wdata), cpath);
1924 *newSpaceBufferp = tsp;
1926 code = CM_ERROR_NOSUCHPATH;
1933 if (code == CM_ERROR_PATH_NOT_COVERED && reqp->tidPathp && reqp->relPathp) {
1934 cm_VolStatus_Notify_DFS_Mapping(linkScp, reqp->tidPathp, reqp->relPathp);
1939 lock_ReleaseWrite(&linkScp->rw);
1942 #ifdef DEBUG_REFCOUNT
1943 long cm_NameIDbg(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
1944 cm_user_t *userp, clientchar_t *tidPathp, cm_req_t *reqp,
1945 cm_scache_t **outScpp,
1946 char * file, long line)
1948 long cm_NameI(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
1949 cm_user_t *userp, clientchar_t *tidPathp,
1950 cm_req_t *reqp, cm_scache_t **outScpp)
1954 clientchar_t *tp; /* ptr moving through input buffer */
1955 clientchar_t tc; /* temp char */
1956 int haveComponent; /* has new component started? */
1957 clientchar_t component[AFSPATHMAX]; /* this is the new component */
1958 clientchar_t *cp; /* component name being assembled */
1959 cm_scache_t *tscp; /* current location in the hierarchy */
1960 cm_scache_t *nscp; /* next dude down */
1961 cm_scache_t *dirScp; /* last dir we searched */
1962 cm_scache_t *linkScp; /* new root for the symlink we just
1964 cm_space_t *psp; /* space for current path, if we've hit
1966 cm_space_t *tempsp; /* temp vbl */
1967 clientchar_t *restp; /* rest of the pathname to interpret */
1968 int symlinkCount; /* count of # of symlinks traversed */
1969 int extraFlag; /* avoid chasing mt pts for dir cmd */
1970 int phase = 1; /* 1 = tidPathp, 2 = pathp */
1971 #define MAX_FID_COUNT 512
1972 cm_fid_t fids[MAX_FID_COUNT]; /* array of fids processed in this path walk */
1973 int fid_count = 0; /* number of fids processed in this path walk */
1978 #ifdef DEBUG_REFCOUNT
1979 afsi_log("%s:%d cm_NameI rootscp 0x%p ref %d", file, line, rootSCachep, rootSCachep->refCount);
1980 osi_Log4(afsd_logp,"cm_NameI rootscp 0x%p path %S tidpath %S flags 0x%x",
1981 rootSCachep, pathp ? pathp : L"<NULL>", tidPathp ? tidPathp : L"<NULL>",
1996 cm_HoldSCache(tscp);
2004 /* map Unix slashes into DOS ones so we can interpret Unix
2010 if (!haveComponent) {
2013 } else if (tc == 0) {
2027 /* we have a component here */
2028 if (tc == 0 || tc == '\\') {
2029 /* end of the component; we're at the last
2030 * component if tc == 0. However, if the last
2031 * is a symlink, we have more to do.
2033 *cp++ = 0; /* add null termination */
2035 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
2036 extraFlag = CM_FLAG_NOMOUNTCHASE;
2037 code = cm_Lookup(tscp, component,
2039 userp, reqp, &nscp);
2042 if (!cm_ClientStrCmp(component,_C("..")) ||
2043 !cm_ClientStrCmp(component,_C("."))) {
2045 * roll back the fid list until we find the
2046 * fid that matches where we are now. Its not
2047 * necessarily one or two fids because they
2048 * might have been symlinks or mount points or
2049 * both that were crossed.
2051 for ( i=fid_count-1; i>=0; i--) {
2052 if (!cm_FidCmp(&nscp->fid, &fids[i]))
2057 /* add the new fid to the list */
2058 if (fid_count == MAX_FID_COUNT) {
2059 code = CM_ERROR_TOO_MANY_SYMLINKS;
2060 cm_ReleaseSCache(nscp);
2064 fids[fid_count++] = nscp->fid;
2069 cm_ReleaseSCache(tscp);
2071 cm_ReleaseSCache(dirScp);
2074 if ((code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH) &&
2075 tscp->fileType == CM_SCACHETYPE_SYMLINK) {
2076 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_NOSUCHPATH");
2077 return CM_ERROR_NOSUCHPATH;
2079 osi_Log1(afsd_logp,"cm_NameI code 0x%x", code);
2084 haveComponent = 0; /* component done */
2086 cm_ReleaseSCache(dirScp);
2087 dirScp = tscp; /* for some symlinks */
2088 tscp = nscp; /* already held */
2090 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
2093 cm_ReleaseSCache(dirScp);
2099 /* now, if tscp is a symlink, we should follow it and
2100 * assemble the path again.
2102 lock_ObtainWrite(&tscp->rw);
2103 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
2104 CM_SCACHESYNC_GETSTATUS
2105 | CM_SCACHESYNC_NEEDCALLBACK);
2107 lock_ReleaseWrite(&tscp->rw);
2108 cm_ReleaseSCache(tscp);
2111 cm_ReleaseSCache(dirScp);
2116 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2118 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
2119 /* this is a symlink; assemble a new buffer */
2120 lock_ReleaseWrite(&tscp->rw);
2121 if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
2122 cm_ReleaseSCache(tscp);
2125 cm_ReleaseSCache(dirScp);
2130 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_TOO_MANY_SYMLINKS");
2131 return CM_ERROR_TOO_MANY_SYMLINKS;
2141 /* TODO: make this better */
2142 frestp = cm_ClientStringToFsStringAlloc(restp, -1, NULL);
2143 code = cm_AssembleLink(tscp, frestp, &linkScp, &tempsp, userp, reqp);
2147 if (code == 0 && linkScp != NULL) {
2148 if (linkScp == cm_data.rootSCachep) {
2152 for ( i=0; i<fid_count; i++) {
2153 if ( !cm_FidCmp(&linkScp->fid, &fids[i]) ) {
2154 code = CM_ERROR_TOO_MANY_SYMLINKS;
2155 cm_ReleaseSCache(linkScp);
2161 if (i == fid_count && fid_count < MAX_FID_COUNT) {
2162 fids[fid_count++] = linkScp->fid;
2167 /* something went wrong */
2168 cm_ReleaseSCache(tscp);
2171 cm_ReleaseSCache(dirScp);
2177 /* otherwise, tempsp has the new path,
2178 * and linkScp is the new root from
2179 * which to interpret that path.
2180 * Continue with the namei processing,
2181 * also doing the bookkeeping for the
2182 * space allocation and tracking the
2183 * vnode reference counts.
2189 cm_ReleaseSCache(tscp);
2194 * now, if linkScp is null, that's
2195 * AssembleLink's way of telling us that
2196 * the sym link is relative to the dir
2197 * containing the link. We have a ref
2198 * to it in dirScp, and we hold it now
2199 * and reuse it as the new spot in the
2207 /* not a symlink, we may be done */
2208 lock_ReleaseWrite(&tscp->rw);
2216 cm_ReleaseSCache(dirScp);
2224 cm_ReleaseSCache(dirScp);
2227 } /* end of a component */
2230 } /* we have a component */
2231 } /* big while loop over all components */
2235 cm_ReleaseSCache(dirScp);
2241 cm_ReleaseSCache(tscp);
2243 #ifdef DEBUG_REFCOUNT
2244 afsi_log("%s:%d cm_NameI code 0x%x outScpp 0x%p ref %d", file, line, code, *outScpp, (*outScpp) ? (*outScpp)->refCount : 0);
2246 osi_Log2(afsd_logp,"cm_NameI code 0x%x outScpp 0x%p", code, *outScpp);
2250 /* called with a dir, and a vnode within the dir that happens to be a symlink.
2251 * We chase the link, and return a held pointer to the target, if it exists,
2252 * in *outScpp. If we succeed, we return 0, otherwise we return an error code
2253 * and do not hold or return a target vnode.
2255 * This is very similar to calling cm_NameI with the last component of a name,
2256 * which happens to be a symlink, except that we've already passed by the name.
2258 * This function is typically called by the directory listing functions, which
2259 * encounter symlinks but need to return the proper file length so programs
2260 * like "more" work properly when they make use of the attributes retrieved from
2263 * The input vnode should not be locked when this function is called.
2265 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
2266 cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
2270 cm_scache_t *newRootScp;
2274 osi_Log1(afsd_logp, "Evaluating symlink scp 0x%p", linkScp);
2276 code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
2280 /* now, if newRootScp is NULL, we're really being told that the symlink
2281 * is relative to the current directory (dscp).
2283 if (newRootScp == NULL) {
2285 cm_HoldSCache(dscp);
2288 code = cm_NameI(newRootScp, spacep->wdata,
2289 CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
2290 userp, NULL, reqp, outScpp);
2292 if (code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH)
2293 code = CM_ERROR_NOSUCHPATH;
2295 /* this stuff is allocated no matter what happened on the namei call,
2297 cm_FreeSpace(spacep);
2298 cm_ReleaseSCache(newRootScp);
2300 if (linkScp == *outScpp) {
2301 cm_ReleaseSCache(*outScpp);
2303 code = CM_ERROR_NOSUCHPATH;
2309 /* for a given entry, make sure that it isn't in the stat cache, and then
2310 * add it to the list of file IDs to be obtained.
2312 * Don't bother adding it if we already have a vnode. Note that the dir
2313 * is locked, so we have to be careful checking the vnode we're thinking of
2314 * processing, to avoid deadlocks.
2316 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
2327 /* Don't overflow bsp. */
2328 if (bsp->counter >= CM_BULKMAX)
2329 return CM_ERROR_STOPNOW;
2331 thyper.LowPart = cm_data.buf_blockSize;
2332 thyper.HighPart = 0;
2333 thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
2335 /* thyper is now the first byte past the end of the record we're
2336 * interested in, and bsp->bufOffset is the first byte of the record
2337 * we're interested in.
2338 * Skip data in the others.
2341 if (LargeIntegerLessThan(*offp, bsp->bufOffset))
2343 if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
2344 return CM_ERROR_STOPNOW;
2345 if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
2348 cm_SetFid(&tfid, scp->fid.cell, scp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
2349 tscp = cm_FindSCache(&tfid);
2351 if (lock_TryWrite(&tscp->rw)) {
2352 /* we have an entry that we can look at */
2353 if (!(tscp->flags & CM_SCACHEFLAG_EACCESS) && cm_HaveCallback(tscp)) {
2354 /* we have a callback on it. Don't bother
2355 * fetching this stat entry, since we're happy
2356 * with the info we have.
2358 lock_ReleaseWrite(&tscp->rw);
2359 cm_ReleaseSCache(tscp);
2362 lock_ReleaseWrite(&tscp->rw);
2364 cm_ReleaseSCache(tscp);
2367 #ifdef AFS_FREELANCE_CLIENT
2368 // yj: if this is a mountpoint under root.afs then we don't want it
2369 // to be bulkstat-ed, instead, we call getSCache directly and under
2370 // getSCache, it is handled specially.
2371 if ( cm_freelanceEnabled &&
2372 tfid.cell==AFS_FAKE_ROOT_CELL_ID &&
2373 tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
2374 !(tfid.vnode==0x1 && tfid.unique==0x1) )
2376 osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
2377 return cm_GetSCache(&tfid, &tscp, NULL, NULL);
2379 #endif /* AFS_FREELANCE_CLIENT */
2382 bsp->fids[i].Volume = scp->fid.volume;
2383 bsp->fids[i].Vnode = tfid.vnode;
2384 bsp->fids[i].Unique = tfid.unique;
2389 cm_TryBulkStatRPC(cm_scache_t *dscp, cm_bulkStat_t *bbp, cm_user_t *userp, cm_req_t *reqp)
2392 AFSCBFids fidStruct;
2393 AFSBulkStats statStruct;
2395 AFSCBs callbackStruct;
2398 cm_callbackRequest_t cbReq;
2404 struct rx_connection * rxconnp;
2405 int inlinebulk; /* Did we use InlineBulkStatus RPC or not? */
2407 memset(&volSync, 0, sizeof(volSync));
2409 /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
2410 * make the calls to create the entries. Handle AFSCBMAX files at a
2413 for (filex = 0; filex < bbp->counter; filex += filesThisCall) {
2414 filesThisCall = bbp->counter - filex;
2415 if (filesThisCall > AFSCBMAX)
2416 filesThisCall = AFSCBMAX;
2418 fidStruct.AFSCBFids_len = filesThisCall;
2419 fidStruct.AFSCBFids_val = &bbp->fids[filex];
2420 statStruct.AFSBulkStats_len = filesThisCall;
2421 statStruct.AFSBulkStats_val = &bbp->stats[filex];
2422 callbackStruct.AFSCBs_len = filesThisCall;
2423 callbackStruct.AFSCBs_val = &bbp->callbacks[filex];
2424 cm_StartCallbackGrantingCall(NULL, &cbReq);
2425 osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
2428 * Whenever cm_Analyze is called for a RXAFS_ RPC there must
2429 * be a FID provided. However, the error code from RXAFS_BulkStatus
2430 * or RXAFS_InlinkBulkStatus does not apply to any FID. Therefore,
2431 * we generate an invalid FID to match with the RPC error.
2433 cm_SetFid(&tfid, dscp->fid.cell, dscp->fid.volume, 0, 0);
2438 code = cm_ConnFromFID(&tfid, userp, reqp, &connp);
2442 rxconnp = cm_GetRxConn(connp);
2443 if (!(connp->serverp->flags & CM_SERVERFLAG_NOINLINEBULK)) {
2444 code = RXAFS_InlineBulkStatus(rxconnp, &fidStruct,
2445 &statStruct, &callbackStruct, &volSync);
2446 if (code == RXGEN_OPCODE) {
2447 cm_SetServerNoInlineBulk(connp->serverp, 0);
2453 code = RXAFS_BulkStatus(rxconnp, &fidStruct,
2454 &statStruct, &callbackStruct, &volSync);
2456 rx_PutConnection(rxconnp);
2459 * If InlineBulk RPC was called and it succeeded,
2460 * then pull out the return code from the status info
2461 * and use it for cm_Analyze so that we can failover to other
2462 * .readonly volume instances. But only do it for errors that
2463 * are volume global.
2465 if (inlinebulk && code == 0 && (&bbp->stats[0])->errorCode) {
2466 osi_Log1(afsd_logp, "cm_TryBulkStat inline-bulk stat error: %d",
2467 (&bbp->stats[0])->errorCode);
2468 switch ((&bbp->stats[0])->errorCode) {
2477 code = (&bbp->stats[0])->errorCode;
2480 /* Rx and Rxkad errors are volume global */
2481 if ( (&bbp->stats[0])->errorCode >= -64 && (&bbp->stats[0])->errorCode < 0 ||
2482 (&bbp->stats[0])->errorCode >= ERROR_TABLE_BASE_RXK && (&bbp->stats[0])->errorCode < ERROR_TABLE_BASE_RXK + 256)
2483 code = (&bbp->stats[0])->errorCode;
2486 } while (cm_Analyze(connp, userp, reqp, &tfid, &volSync, NULL, &cbReq, code));
2487 code = cm_MapRPCError(code, reqp);
2490 * might as well quit on an error, since we're not going to do
2491 * much better on the next immediate call, either.
2494 osi_Log2(afsd_logp, "CALL %sBulkStatus FAILURE code 0x%x",
2495 inlinebulk ? "Inline" : "", code);
2496 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2501 * The bulk RPC has succeeded or at least not failed with a
2502 * volume global error result. For items that have inlineBulk
2503 * errors we must call cm_Analyze in order to perform required
2504 * logging of errors.
2506 * If the RPC was not inline bulk or the entry either has no error
2507 * the status must be merged.
2509 osi_Log1(afsd_logp, "CALL %sBulkStatus SUCCESS", inlinebulk ? "Inline" : "");
2511 for (i = 0; i<filesThisCall; i++) {
2513 cm_SetFid(&tfid, dscp->fid.cell, bbp->fids[j].Volume, bbp->fids[j].Vnode, bbp->fids[j].Unique);
2515 if (inlinebulk && (&bbp->stats[j])->errorCode) {
2516 cm_req_t treq = *reqp;
2517 cm_Analyze(NULL, userp, &treq, &tfid, &volSync, NULL, &cbReq, (&bbp->stats[j])->errorCode);
2519 code = cm_GetSCache(&tfid, &scp, userp, reqp);
2524 * otherwise, if this entry has no callback info,
2525 * merge in this. If there is existing callback info
2526 * we skip the merge because the existing data must be
2527 * current (we have a callback) and the response from
2528 * a non-inline bulk rpc might actually be wrong.
2530 * now, we have to be extra paranoid on merging in this
2531 * information, since we didn't use cm_SyncOp before
2532 * starting the fetch to make sure that no bad races
2533 * were occurring. Specifically, we need to make sure
2534 * we don't obliterate any newer information in the
2535 * vnode than have here.
2537 * Right now, be pretty conservative: if there's a
2538 * callback or a pending call, skip it.
2539 * However, if the prior attempt to obtain status
2540 * was refused access or the volume is .readonly,
2541 * take the data in any case since we have nothing
2542 * better for the in flight directory enumeration that
2543 * resulted in this function being called.
2545 lock_ObtainRead(&scp->rw);
2546 if ((scp->cbServerp == NULL &&
2547 !(scp->flags & (CM_SCACHEFLAG_FETCHING | CM_SCACHEFLAG_STORING | CM_SCACHEFLAG_SIZESTORING))) ||
2548 (scp->flags & CM_SCACHEFLAG_PURERO) ||
2549 (scp->flags & CM_SCACHEFLAG_EACCESS))
2551 lock_ConvertRToW(&scp->rw);
2552 cm_EndCallbackGrantingCall(scp, &cbReq,
2555 CM_CALLBACK_MAINTAINCOUNT);
2556 InterlockedIncrement(&scp->activeRPCs);
2557 cm_MergeStatus(dscp, scp, &bbp->stats[j], &volSync, userp, reqp, 0);
2558 lock_ReleaseWrite(&scp->rw);
2560 lock_ReleaseRead(&scp->rw);
2562 cm_ReleaseSCache(scp);
2564 } /* all files in the response */
2565 /* now tell it to drop the count,
2566 * after doing the vnode processing above */
2567 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2568 } /* while there are still more files to process */
2573 /* called with a write locked scp and a pointer to a buffer. Make bulk stat
2574 * calls on all undeleted files in the page of the directory specified.
2577 cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
2583 osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%p", dscp);
2585 /* should be on a buffer boundary */
2586 osi_assertx((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0, "invalid offset");
2588 bbp = malloc(sizeof(cm_bulkStat_t));
2589 memset(bbp, 0, sizeof(cm_bulkStat_t));
2590 bbp->bufOffset = *offsetp;
2592 lock_ReleaseWrite(&dscp->rw);
2593 /* first, assemble the file IDs we need to stat */
2594 code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) bbp, offsetp, userp, reqp, NULL);
2596 /* if we failed, bail out early */
2597 if (code && code != CM_ERROR_STOPNOW) {
2599 lock_ObtainWrite(&dscp->rw);
2603 code = cm_TryBulkStatRPC(dscp, bbp, userp, reqp);
2604 osi_Log1(afsd_logp, "END cm_TryBulkStat code 0x%x", code);
2606 lock_ObtainWrite(&dscp->rw);
2611 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
2615 /* initialize store back mask as inexpensive local variable */
2617 memset(statusp, 0, sizeof(AFSStoreStatus));
2619 /* copy out queued info from scache first, if scp passed in */
2621 if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
2622 statusp->ClientModTime = scp->clientModTime;
2623 mask |= AFS_SETMODTIME;
2624 scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
2629 /* now add in our locally generated request */
2630 if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
2631 statusp->ClientModTime = attrp->clientModTime;
2632 mask |= AFS_SETMODTIME;
2634 if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
2635 statusp->UnixModeBits = attrp->unixModeBits;
2636 mask |= AFS_SETMODE;
2638 if (attrp->mask & CM_ATTRMASK_OWNER) {
2639 statusp->Owner = attrp->owner;
2640 mask |= AFS_SETOWNER;
2642 if (attrp->mask & CM_ATTRMASK_GROUP) {
2643 statusp->Group = attrp->group;
2644 mask |= AFS_SETGROUP;
2647 statusp->Mask = mask;
2650 /* set the file size, and make sure that all relevant buffers have been
2651 * truncated. Ensure that any partially truncated buffers have been zeroed
2652 * to the end of the buffer.
2654 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
2660 /* start by locking out buffer creation */
2661 lock_ObtainWrite(&scp->bufCreateLock);
2663 /* verify that this is a file, not a dir or a symlink */
2664 lock_ObtainWrite(&scp->rw);
2665 code = cm_SyncOp(scp, NULL, userp, reqp, 0,
2666 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2669 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2671 if (scp->fileType != CM_SCACHETYPE_FILE) {
2672 code = CM_ERROR_ISDIR;
2677 if (LargeIntegerLessThan(*sizep, scp->length))
2682 lock_ReleaseWrite(&scp->rw);
2684 /* can't hold scp->rw lock here, since we may wait for a storeback to
2685 * finish if the buffer package is cleaning a buffer by storing it to
2689 buf_Truncate(scp, userp, reqp, sizep);
2691 /* now ensure that file length is short enough, and update truncPos */
2692 lock_ObtainWrite(&scp->rw);
2694 /* make sure we have a callback (so we have the right value for the
2695 * length), and wait for it to be safe to do a truncate.
2697 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
2698 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2699 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2701 /* If we only have 'i' bits, then we should still be able to set
2702 the size of a file we created. */
2703 if (code == CM_ERROR_NOACCESS && scp->creator == userp) {
2704 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
2705 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2706 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2712 if (LargeIntegerLessThan(*sizep, scp->length)) {
2713 /* a real truncation. If truncPos is not set yet, or is bigger
2714 * than where we're truncating the file, set truncPos to this
2719 if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
2720 || LargeIntegerLessThan(*sizep, scp->length)) {
2722 scp->truncPos = *sizep;
2723 scp->mask |= CM_SCACHEMASK_TRUNCPOS;
2725 /* in either case, the new file size has been changed */
2726 scp->length = *sizep;
2727 scp->mask |= CM_SCACHEMASK_LENGTH;
2729 else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
2730 /* really extending the file */
2731 scp->length = *sizep;
2732 scp->mask |= CM_SCACHEMASK_LENGTH;
2735 /* done successfully */
2738 cm_SyncOpDone(scp, NULL,
2739 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2740 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2743 lock_ReleaseWrite(&scp->rw);
2744 lock_ReleaseWrite(&scp->bufCreateLock);
2749 /* set the file size or other attributes (but not both at once) */
2750 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
2754 AFSFetchStatus afsOutStatus;
2758 AFSStoreStatus afsInStatus;
2759 struct rx_connection * rxconnp;
2761 memset(&volSync, 0, sizeof(volSync));
2763 /* handle file length setting */
2764 if (attrp->mask & CM_ATTRMASK_LENGTH)
2765 return cm_SetLength(scp, &attrp->length, userp, reqp);
2767 lock_ObtainWrite(&scp->rw);
2768 /* Check for RO volume */
2769 if (scp->flags & CM_SCACHEFLAG_RO) {
2770 code = CM_ERROR_READONLY;
2771 lock_ReleaseWrite(&scp->rw);
2775 /* otherwise, we have to make an RPC to get the status */
2776 code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
2778 lock_ReleaseWrite(&scp->rw);
2781 lock_ConvertWToR(&scp->rw);
2783 /* make the attr structure */
2784 cm_StatusFromAttr(&afsInStatus, scp, attrp);
2786 tfid.Volume = scp->fid.volume;
2787 tfid.Vnode = scp->fid.vnode;
2788 tfid.Unique = scp->fid.unique;
2789 lock_ReleaseRead(&scp->rw);
2791 /* now make the RPC */
2792 InterlockedIncrement(&scp->activeRPCs);
2794 osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%p", scp);
2796 code = cm_ConnFromFID(&scp->fid, userp, reqp, &connp);
2800 rxconnp = cm_GetRxConn(connp);
2801 code = RXAFS_StoreStatus(rxconnp, &tfid,
2802 &afsInStatus, &afsOutStatus, &volSync);
2803 rx_PutConnection(rxconnp);
2805 } while (cm_Analyze(connp, userp, reqp,
2806 &scp->fid, &volSync, NULL, NULL, code));
2807 code = cm_MapRPCError(code, reqp);
2810 osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
2812 osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
2814 lock_ObtainWrite(&scp->rw);
2816 cm_MergeStatus(NULL, scp, &afsOutStatus, &volSync, userp, reqp,
2817 CM_MERGEFLAG_FORCE|CM_MERGEFLAG_STOREDATA);
2819 InterlockedDecrement(&scp->activeRPCs);
2820 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
2822 /* if we're changing the mode bits, discard the ACL cache,
2823 * since we changed the mode bits.
2825 if (afsInStatus.Mask & AFS_SETMODE)
2826 cm_FreeAllACLEnts(scp);
2827 lock_ReleaseWrite(&scp->rw);
2831 long cm_Create(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
2832 cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
2837 cm_callbackRequest_t cbReq;
2840 cm_scache_t *scp = NULL;
2842 AFSStoreStatus inStatus;
2843 AFSFetchStatus updatedDirStatus;
2844 AFSFetchStatus newFileStatus;
2845 AFSCallBack newFileCallback;
2847 struct rx_connection * rxconnp;
2849 fschar_t * fnamep = NULL;
2851 memset(&volSync, 0, sizeof(volSync));
2853 /* can't create names with @sys in them; must expand it manually first.
2854 * return "invalid request" if they try.
2856 if (cm_ExpandSysName(NULL, cnamep, NULL, 0, 0)) {
2857 return CM_ERROR_ATSYS;
2860 #ifdef AFS_FREELANCE_CLIENT
2861 /* Freelance root volume does not hold files */
2862 if (cm_freelanceEnabled &&
2863 dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
2864 dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
2866 return CM_ERROR_NOACCESS;
2868 #endif /* AFS_FREELANCE_CLIENT */
2870 /* Check for RO volume */
2871 if (dscp->flags & CM_SCACHEFLAG_RO)
2872 return CM_ERROR_READONLY;
2874 /* before starting the RPC, mark that we're changing the file data, so
2875 * that someone who does a chmod will know to wait until our call
2878 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
2880 lock_ObtainWrite(&dscp->rw);
2881 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2882 lock_ReleaseWrite(&dscp->rw);
2884 cm_StartCallbackGrantingCall(NULL, &cbReq);
2886 cm_EndDirOp(&dirop);
2893 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
2895 cm_StatusFromAttr(&inStatus, NULL, attrp);
2897 /* try the RPC now */
2898 InterlockedIncrement(&dscp->activeRPCs);
2899 osi_Log1(afsd_logp, "CALL CreateFile scp 0x%p", dscp);
2901 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2905 dirAFSFid.Volume = dscp->fid.volume;
2906 dirAFSFid.Vnode = dscp->fid.vnode;
2907 dirAFSFid.Unique = dscp->fid.unique;
2909 rxconnp = cm_GetRxConn(connp);
2910 code = RXAFS_CreateFile(connp->rxconnp, &dirAFSFid, fnamep,
2911 &inStatus, &newAFSFid, &newFileStatus,
2912 &updatedDirStatus, &newFileCallback,
2914 rx_PutConnection(rxconnp);
2916 } while (cm_Analyze(connp, userp, reqp,
2917 &dscp->fid, &volSync, NULL, &cbReq, code));
2918 code = cm_MapRPCError(code, reqp);
2921 osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
2923 osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
2926 lock_ObtainWrite(&dirop.scp->dirlock);
2927 dirop.lockType = CM_DIRLOCK_WRITE;
2929 lock_ObtainWrite(&dscp->rw);
2931 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
2932 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
2933 if (cm_CheckDirOpForSingleChange(&dirop)) {
2934 lock_ReleaseWrite(&dscp->rw);
2935 cm_DirCreateEntry(&dirop, fnamep, &newFid);
2937 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
2939 lock_ObtainWrite(&dscp->rw);
2942 InterlockedDecrement(&dscp->activeRPCs);
2944 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2945 lock_ReleaseWrite(&dscp->rw);
2947 /* now try to create the file's entry, too, but be careful to
2948 * make sure that we don't merge in old info. Since we weren't locking
2949 * out any requests during the file's creation, we may have pretty old
2953 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2955 lock_ObtainWrite(&scp->rw);
2956 scp->creator = userp; /* remember who created it */
2957 if (!cm_HaveCallback(scp)) {
2958 cm_EndCallbackGrantingCall(scp, &cbReq,
2959 &newFileCallback, &volSync, 0);
2960 InterlockedIncrement(&scp->activeRPCs);
2961 cm_MergeStatus(dscp, scp, &newFileStatus, &volSync,
2965 lock_ReleaseWrite(&scp->rw);
2969 /* make sure we end things properly */
2971 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2973 cm_EndDirOp(&dirop);
2982 cm_ReleaseSCache(scp);
2988 * locked if TRUE means write-locked
2989 * else the cm_scache_t rw must not be held
2991 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp, afs_uint32 locked)
2996 lock_ReleaseWrite(&scp->rw);
2998 osi_Log2(afsd_logp, "cm_FSync scp 0x%p userp 0x%p", scp, userp);
3000 code = buf_CleanVnode(scp, userp, reqp);
3002 lock_ObtainWrite(&scp->rw);
3004 if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
3005 | CM_SCACHEMASK_CLIENTMODTIME
3006 | CM_SCACHEMASK_LENGTH))
3007 code = cm_StoreMini(scp, userp, reqp);
3009 if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) {
3010 code = (scp->flags & CM_SCACHEFLAG_OVERQUOTA) ? CM_ERROR_QUOTA : CM_ERROR_SPACE;
3011 scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE);
3015 lock_ReleaseWrite(&scp->rw);
3016 } else if (locked) {
3017 lock_ObtainWrite(&scp->rw);
3022 long cm_MakeDir(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
3023 cm_user_t *userp, cm_req_t *reqp, cm_scache_t **scpp)
3028 cm_callbackRequest_t cbReq;
3031 cm_scache_t *scp = NULL;
3033 AFSStoreStatus inStatus;
3034 AFSFetchStatus updatedDirStatus;
3035 AFSFetchStatus newDirStatus;
3036 AFSCallBack newDirCallback;
3038 struct rx_connection * rxconnp;
3040 fschar_t * fnamep = NULL;
3042 memset(&volSync, 0, sizeof(volSync));
3044 /* can't create names with @sys in them; must expand it manually first.
3045 * return "invalid request" if they try.
3047 if (cm_ExpandSysName(NULL, cnamep, NULL, 0, 0)) {
3048 return CM_ERROR_ATSYS;
3051 #ifdef AFS_FREELANCE_CLIENT
3052 /* Freelance root volume does not hold subdirectories */
3053 if (cm_freelanceEnabled &&
3054 dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
3055 dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
3057 return CM_ERROR_NOACCESS;
3059 #endif /* AFS_FREELANCE_CLIENT */
3061 /* Check for RO volume */
3062 if (dscp->flags & CM_SCACHEFLAG_RO)
3063 return CM_ERROR_READONLY;
3065 /* before starting the RPC, mark that we're changing the directory
3066 * data, so that someone who does a chmod on the dir will wait until
3067 * our call completes.
3069 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3071 lock_ObtainWrite(&dscp->rw);
3072 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3073 lock_ReleaseWrite(&dscp->rw);
3075 cm_StartCallbackGrantingCall(NULL, &cbReq);
3077 cm_EndDirOp(&dirop);
3084 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3085 cm_StatusFromAttr(&inStatus, NULL, attrp);
3087 /* try the RPC now */
3088 InterlockedIncrement(&dscp->activeRPCs);
3089 osi_Log1(afsd_logp, "CALL MakeDir scp 0x%p", dscp);
3091 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3095 dirAFSFid.Volume = dscp->fid.volume;
3096 dirAFSFid.Vnode = dscp->fid.vnode;
3097 dirAFSFid.Unique = dscp->fid.unique;
3099 rxconnp = cm_GetRxConn(connp);
3100 code = RXAFS_MakeDir(connp->rxconnp, &dirAFSFid, fnamep,
3101 &inStatus, &newAFSFid, &newDirStatus,
3102 &updatedDirStatus, &newDirCallback,
3104 rx_PutConnection(rxconnp);
3106 } while (cm_Analyze(connp, userp, reqp,
3107 &dscp->fid, &volSync, NULL, &cbReq, code));
3108 code = cm_MapRPCError(code, reqp);
3111 osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
3113 osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
3116 lock_ObtainWrite(&dirop.scp->dirlock);
3117 dirop.lockType = CM_DIRLOCK_WRITE;
3119 lock_ObtainWrite(&dscp->rw);
3121 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3122 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3123 if (cm_CheckDirOpForSingleChange(&dirop)) {
3124 lock_ReleaseWrite(&dscp->rw);
3125 cm_DirCreateEntry(&dirop, fnamep, &newFid);
3127 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
3129 lock_ObtainWrite(&dscp->rw);
3132 InterlockedDecrement(&dscp->activeRPCs);
3134 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3135 lock_ReleaseWrite(&dscp->rw);
3137 /* now try to create the new dir's entry, too, but be careful to
3138 * make sure that we don't merge in old info. Since we weren't locking
3139 * out any requests during the file's creation, we may have pretty old
3143 code = cm_GetSCache(&newFid, &scp, userp, reqp);
3145 lock_ObtainWrite(&scp->rw);
3146 if (!cm_HaveCallback(scp)) {
3147 cm_EndCallbackGrantingCall(scp, &cbReq,
3148 &newDirCallback, &volSync, 0);
3149 InterlockedIncrement(&scp->activeRPCs);
3150 cm_MergeStatus(dscp, scp, &newDirStatus, &volSync,
3154 lock_ReleaseWrite(&scp->rw);
3158 /* make sure we end things properly */
3160 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
3162 cm_EndDirOp(&dirop);
3170 cm_ReleaseSCache(scp);
3173 /* and return error code */
3177 long cm_Link(cm_scache_t *dscp, clientchar_t *cnamep, cm_scache_t *sscp, long flags,
3178 cm_user_t *userp, cm_req_t *reqp)
3183 AFSFid existingAFSFid;
3184 AFSFetchStatus updatedDirStatus;
3185 AFSFetchStatus newLinkStatus;
3187 struct rx_connection * rxconnp;
3189 fschar_t * fnamep = NULL;
3192 memset(&volSync, 0, sizeof(volSync));
3194 if (dscp->fid.cell != sscp->fid.cell ||
3195 dscp->fid.volume != sscp->fid.volume) {
3196 return CM_ERROR_CROSSDEVLINK;
3199 /* Check for RO volume */
3200 if (dscp->flags & CM_SCACHEFLAG_RO)
3201 return CM_ERROR_READONLY;
3203 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3205 lock_ObtainWrite(&dscp->rw);
3206 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3207 lock_ReleaseWrite(&dscp->rw);
3209 cm_EndDirOp(&dirop);
3214 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3216 /* try the RPC now */
3217 InterlockedIncrement(&dscp->activeRPCs);
3218 osi_Log1(afsd_logp, "CALL Link scp 0x%p", dscp);
3220 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3223 dirAFSFid.Volume = dscp->fid.volume;
3224 dirAFSFid.Vnode = dscp->fid.vnode;
3225 dirAFSFid.Unique = dscp->fid.unique;
3227 existingAFSFid.Volume = sscp->fid.volume;
3228 existingAFSFid.Vnode = sscp->fid.vnode;
3229 existingAFSFid.Unique = sscp->fid.unique;
3231 rxconnp = cm_GetRxConn(connp);
3232 code = RXAFS_Link(rxconnp, &dirAFSFid, fnamep, &existingAFSFid,
3233 &newLinkStatus, &updatedDirStatus, &volSync);
3234 rx_PutConnection(rxconnp);
3235 osi_Log1(afsd_logp," RXAFS_Link returns 0x%x", code);
3237 } while (cm_Analyze(connp, userp, reqp,
3238 &dscp->fid, &volSync, NULL, NULL, code));
3240 code = cm_MapRPCError(code, reqp);
3243 osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
3245 osi_Log0(afsd_logp, "CALL Link SUCCESS");
3248 lock_ObtainWrite(&dirop.scp->dirlock);
3249 dirop.lockType = CM_DIRLOCK_WRITE;
3251 lock_ObtainWrite(&dscp->rw);
3253 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3256 if (cm_CheckDirOpForSingleChange(&dirop)) {
3257 lock_ReleaseWrite(&dscp->rw);
3258 cm_DirCreateEntry(&dirop, fnamep, &sscp->fid);
3260 cm_BPlusDirCreateEntry(&dirop, cnamep, &sscp->fid);
3262 lock_ObtainWrite(&dscp->rw);
3265 InterlockedDecrement(&dscp->activeRPCs);
3267 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3268 lock_ReleaseWrite(&dscp->rw);
3270 cm_EndDirOp(&dirop);
3272 if (invalidate && RDR_Initialized)
3273 RDR_InvalidateObject(dscp->fid.cell, dscp->fid.volume, dscp->fid.vnode,
3274 dscp->fid.unique, dscp->fid.hash,
3275 dscp->fileType, AFS_INVALIDATE_DATA_VERSION);
3277 /* Update the linked object status */
3279 lock_ObtainWrite(&sscp->rw);
3280 InterlockedIncrement(&sscp->activeRPCs);
3281 cm_MergeStatus(NULL, sscp, &newLinkStatus, &volSync, userp, reqp, 0);
3282 lock_ReleaseWrite(&sscp->rw);
3290 long cm_SymLink(cm_scache_t *dscp, clientchar_t *cnamep, fschar_t *contentsp, long flags,
3291 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp, cm_scache_t **scpp)
3299 AFSStoreStatus inStatus;
3300 AFSFetchStatus updatedDirStatus;
3301 AFSFetchStatus newLinkStatus;
3303 struct rx_connection * rxconnp;
3305 fschar_t *fnamep = NULL;
3310 /* Check for RO volume */
3311 if (dscp->flags & CM_SCACHEFLAG_RO)
3312 return CM_ERROR_READONLY;
3314 memset(&volSync, 0, sizeof(volSync));
3316 /* before starting the RPC, mark that we're changing the directory data,
3317 * so that someone who does a chmod on the dir will wait until our
3320 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3322 lock_ObtainWrite(&dscp->rw);
3323 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3324 lock_ReleaseWrite(&dscp->rw);
3326 cm_EndDirOp(&dirop);
3331 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3333 cm_StatusFromAttr(&inStatus, NULL, attrp);
3335 /* try the RPC now */
3336 InterlockedIncrement(&dscp->activeRPCs);
3337 osi_Log1(afsd_logp, "CALL Symlink scp 0x%p", dscp);
3339 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3343 dirAFSFid.Volume = dscp->fid.volume;
3344 dirAFSFid.Vnode = dscp->fid.vnode;
3345 dirAFSFid.Unique = dscp->fid.unique;
3347 rxconnp = cm_GetRxConn(connp);
3348 code = RXAFS_Symlink(rxconnp, &dirAFSFid, fnamep, contentsp,
3349 &inStatus, &newAFSFid, &newLinkStatus,
3350 &updatedDirStatus, &volSync);
3351 rx_PutConnection(rxconnp);
3353 } while (cm_Analyze(connp, userp, reqp,
3354 &dscp->fid, &volSync, NULL, NULL, code));
3355 code = cm_MapRPCError(code, reqp);
3358 osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
3360 osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
3363 lock_ObtainWrite(&dirop.scp->dirlock);
3364 dirop.lockType = CM_DIRLOCK_WRITE;
3366 lock_ObtainWrite(&dscp->rw);
3368 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3369 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3370 if (cm_CheckDirOpForSingleChange(&dirop)) {
3371 lock_ReleaseWrite(&dscp->rw);
3372 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3374 cm_DirCreateEntry(&dirop, fnamep, &newFid);
3376 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
3378 lock_ObtainWrite(&dscp->rw);
3381 InterlockedDecrement(&dscp->activeRPCs);
3383 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3384 lock_ReleaseWrite(&dscp->rw);
3386 cm_EndDirOp(&dirop);
3388 /* now try to create the new dir's entry, too, but be careful to
3389 * make sure that we don't merge in old info. Since we weren't locking
3390 * out any requests during the file's creation, we may have pretty old
3394 code = cm_GetSCache(&newFid, &scp, userp, reqp);
3396 lock_ObtainWrite(&scp->rw);
3397 if (!cm_HaveCallback(scp)) {
3398 InterlockedIncrement(&scp->activeRPCs);
3399 cm_MergeStatus(dscp, scp, &newLinkStatus, &volSync,
3402 lock_ReleaseWrite(&scp->rw);
3407 cm_ReleaseSCache(scp);
3414 /* and return error code */
3418 /*! \brief Remove a directory
3420 Encapsulates a call to RXAFS_RemoveDir().
3422 \param[in] dscp cm_scache_t for the directory containing the
3423 directory to be removed.
3425 \param[in] fnamep This will be the original name of the directory
3426 as known to the file server. It will be passed in to RXAFS_RemoveDir().
3427 This parameter is optional. If it is not provided the value
3430 \param[in] cnamep Normalized name used to update the local
3433 \param[in] userp cm_user_t for the request.
3435 \param[in] reqp Request tracker.
3437 long cm_RemoveDir(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t *cnamep, cm_user_t *userp, cm_req_t *reqp)
3443 AFSFetchStatus updatedDirStatus;
3445 struct rx_connection * rxconnp;
3447 cm_scache_t *scp = NULL;
3448 int free_fnamep = FALSE;
3450 memset(&volSync, 0, sizeof(volSync));
3452 if (fnamep == NULL) {