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>
15 #include <afs/unified_afs.h>
29 extern void afsi_log(char *pattern, ...);
32 int cm_enableServerLocks = 1;
34 int cm_followBackupPath = 0;
37 * Case-folding array. This was constructed by inspecting of SMBtrace output.
38 * I do not know anything more about it.
40 unsigned char cm_foldUpper[256] = {
41 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
42 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
43 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
44 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
45 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
46 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
47 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
48 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
49 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
50 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
51 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
52 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
53 0x60, 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, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
57 0x80, 0x9a, 0x90, 0x41, 0x8e, 0x41, 0x8f, 0x80,
58 0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x8e, 0x8f,
59 0x90, 0x92, 0x92, 0x4f, 0x99, 0x4f, 0x55, 0x55,
60 0x59, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
61 0x41, 0x49, 0x4f, 0x55, 0xa5, 0xa5, 0x56, 0xa7,
62 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
63 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
64 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
65 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
66 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
67 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
68 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
69 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
70 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
71 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
72 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
76 * Case-insensitive string comparison. We used to use stricmp, but it doesn't
77 * know about 8-bit characters (e.g. 129 is lowercase u-umlaut, 154 is
78 * upper-case u-umlaut).
80 int cm_stricmp(const char *str1, const char *str2)
92 c1 = (char) cm_foldUpper[(unsigned char)(*str1++)];
93 c2 = (char) cm_foldUpper[(unsigned char)(*str2++)];
103 /* return success if we can open this file in this mode */
104 long cm_CheckOpen(cm_scache_t *scp, int openMode, int trunc, cm_user_t *userp,
112 rights |= PRSFS_READ;
113 if (openMode == 1 || openMode == 2 || trunc)
114 rights |= PRSFS_WRITE;
116 lock_ObtainWrite(&scp->rw);
118 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
119 CM_SCACHESYNC_GETSTATUS
120 | CM_SCACHESYNC_NEEDCALLBACK
121 | CM_SCACHESYNC_LOCK);
124 ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
125 scp->fileType == CM_SCACHETYPE_FILE) {
128 unsigned int sLockType;
129 LARGE_INTEGER LOffset, LLength;
131 /* Check if there's some sort of lock on the file at the
134 key = cm_GenerateKey(CM_SESSION_CMINT,0,0);
136 if (rights & PRSFS_WRITE)
139 sLockType = LOCKING_ANDX_SHARED_LOCK;
141 LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
142 LOffset.LowPart = CM_FLSHARE_OFFSET_LOW;
143 LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
144 LLength.LowPart = CM_FLSHARE_LENGTH_LOW;
146 code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
149 cm_Unlock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp);
151 /* In this case, we allow the file open to go through even
152 though we can't enforce mandatory locking on the
154 if (code == CM_ERROR_NOACCESS &&
155 !(rights & PRSFS_WRITE))
158 if (code == CM_ERROR_LOCK_NOT_GRANTED)
159 code = CM_ERROR_SHARING_VIOLATION;
163 } else if (code != 0) {
167 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
171 lock_ReleaseWrite(&scp->rw);
176 /* return success if we can open this file in this mode */
177 long cm_CheckNTOpen(cm_scache_t *scp,
178 unsigned int desiredAccess,
179 unsigned int shareAccess,
180 unsigned int createDisp,
181 afs_offs_t process_id,
182 afs_offs_t handle_id,
183 cm_user_t *userp, cm_req_t *reqp,
184 cm_lock_data_t **ldpp)
188 afs_uint16 session_id;
190 osi_assertx(ldpp != NULL, "null cm_lock_data_t");
193 /* compute the session id */
194 if (reqp->flags & CM_REQ_SOURCE_SMB)
195 session_id = CM_SESSION_SMB;
196 else if (reqp->flags & CM_REQ_SOURCE_REDIR)
197 session_id = CM_SESSION_IFS;
199 session_id = CM_SESSION_CMINT;
201 /* Ignore the SYNCHRONIZE privilege */
202 desiredAccess &= ~SYNCHRONIZE;
204 /* Always allow delete; the RPC will tell us if it's OK */
207 if (desiredAccess == DELETE)
210 /* Always allow reading attributes (Hidden, System, Readonly, ...) */
211 if (desiredAccess == FILE_READ_ATTRIBUTES)
214 if (desiredAccess & (AFS_ACCESS_READ|AFS_ACCESS_EXECUTE))
215 rights |= (scp->fileType == CM_SCACHETYPE_DIRECTORY ? PRSFS_LOOKUP : PRSFS_READ);
217 /* We used to require PRSFS_WRITE if createDisp was 4
218 (OPEN_ALWAYS) even if AFS_ACCESS_WRITE was not requested.
219 However, we don't need to do that since the existence of the
220 scp implies that we don't need to create it. */
221 if (desiredAccess & AFS_ACCESS_WRITE)
222 rights |= PRSFS_WRITE;
224 if (desiredAccess & DELETE)
225 rights |= PRSFS_DELETE;
227 lock_ObtainWrite(&scp->rw);
229 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
230 CM_SCACHESYNC_GETSTATUS
231 | CM_SCACHESYNC_NEEDCALLBACK
232 | CM_SCACHESYNC_LOCK);
235 * If the open will fail because the volume is readonly, then we will
236 * return an access denied error instead. This is to help brain-dead
237 * apps run correctly on replicated volumes.
238 * See defect 10007 for more information.
240 if (code == CM_ERROR_READONLY)
241 code = CM_ERROR_NOACCESS;
244 !(shareAccess & FILE_SHARE_WRITE) &&
245 ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
246 scp->fileType == CM_SCACHETYPE_FILE) {
248 unsigned int sLockType;
249 LARGE_INTEGER LOffset, LLength;
251 /* Check if there's some sort of lock on the file at the
254 if (rights & PRSFS_WRITE)
257 sLockType = LOCKING_ANDX_SHARED_LOCK;
259 key = cm_GenerateKey(session_id, process_id, 0);
261 /* single byte lock at offset 0x0100 0000 0000 0000 */
262 LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
263 LOffset.LowPart = CM_FLSHARE_OFFSET_LOW;
264 LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
265 LLength.LowPart = CM_FLSHARE_LENGTH_LOW;
267 code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
270 (*ldpp) = (cm_lock_data_t *)malloc(sizeof(cm_lock_data_t));
277 (*ldpp)->sLockType = sLockType;
278 (*ldpp)->LOffset.HighPart = LOffset.HighPart;
279 (*ldpp)->LOffset.LowPart = LOffset.LowPart;
280 (*ldpp)->LLength.HighPart = LLength.HighPart;
281 (*ldpp)->LLength.LowPart = LLength.LowPart;
284 * In this case, we allow the file open to go through even
285 * though we can't enforce mandatory locking on the
287 if (code == CM_ERROR_NOACCESS &&
288 !(rights & PRSFS_WRITE))
291 if (code == CM_ERROR_LOCK_NOT_GRANTED)
292 code = CM_ERROR_SHARING_VIOLATION;
295 } else if (code != 0) {
300 lock_ReleaseWrite(&scp->rw);
303 osi_Log3(afsd_logp,"cm_CheckNTOpen scp 0x%p ldp 0x%p code 0x%x", scp, *ldpp, code);
307 extern long cm_CheckNTOpenDone(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp,
308 cm_lock_data_t ** ldpp)
310 osi_Log2(afsd_logp,"cm_CheckNTOpenDone scp 0x%p ldp 0x%p", scp, ldpp ? *ldpp : 0);
311 lock_ObtainWrite(&scp->rw);
313 cm_Unlock(scp, (*ldpp)->sLockType, (*ldpp)->LOffset, (*ldpp)->LLength,
314 (*ldpp)->key, 0, userp, reqp);
318 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
319 lock_ReleaseWrite(&scp->rw);
323 * When CAP_NT_SMBS has been negotiated, deletion (of files or directories) is
324 * done in three steps:
325 * (1) open for deletion (NT_CREATE_AND_X)
326 * (2) set for deletion on close (NT_TRANSACTION2, SET_FILE_INFO)
328 * We must not do the RPC until step 3. But if we are going to return an error
329 * code (e.g. directory not empty), we must return it by step 2, otherwise most
330 * clients will not notice it. So we do a preliminary check. For deleting
331 * files, this is almost free, since we have already done the RPC to get the
332 * parent directory's status bits. But for deleting directories, we must do an
333 * additional RPC to get the directory's data to check if it is empty. Sigh.
335 long cm_CheckNTDelete(cm_scache_t *dscp, cm_scache_t *scp, cm_user_t *userp,
341 cm_dirEntry_t *dep = 0;
342 unsigned short *hashTable;
344 int BeyondPage = 0, HaveDot = 0, HaveDotDot = 0;
347 /* First check permissions */
348 lock_ObtainWrite(&scp->rw);
349 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_DELETE,
350 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
352 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
353 lock_ReleaseWrite(&scp->rw);
357 /* If deleting directory, must be empty */
359 if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
362 thyper.HighPart = 0; thyper.LowPart = 0;
363 code = buf_Get(scp, &thyper, reqp, 0, &bufferp);
367 lock_ObtainMutex(&bufferp->mx);
368 lock_ObtainWrite(&scp->rw);
371 code = cm_SyncOp(scp, bufferp, userp, reqp, 0,
372 CM_SCACHESYNC_NEEDCALLBACK
374 | CM_SCACHESYNC_BUFLOCKED);
378 if (cm_HaveBuffer(scp, bufferp, 1))
381 /* otherwise, load the buffer and try again */
382 lock_ReleaseMutex(&bufferp->mx);
383 code = cm_GetBuffer(scp, bufferp, NULL, userp, reqp);
384 lock_ReleaseWrite(&scp->rw);
385 lock_ObtainMutex(&bufferp->mx);
386 lock_ObtainWrite(&scp->rw);
387 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
392 lock_ReleaseWrite(&scp->rw);
395 /* We try to determine emptiness without looking beyond the first page,
396 * and without assuming "." and ".." are present and are on the first
397 * page (though these assumptions might, after all, be reasonable).
399 hashTable = (unsigned short *)(bufferp->datap + (32 * 5));
400 for (i=0; i<128; i++) {
401 idx = ntohs(hashTable[i]);
407 dep = (cm_dirEntry_t *)(bufferp->datap + (32 * idx));
408 if (strcmp(dep->name, ".") == 0)
410 else if (strcmp(dep->name, "..") == 0)
413 code = CM_ERROR_NOTEMPTY;
416 idx = ntohs(dep->next);
419 if (BeyondPage && HaveDot && HaveDotDot)
420 code = CM_ERROR_NOTEMPTY;
424 lock_ReleaseMutex(&bufferp->mx);
425 buf_Release(bufferp);
427 lock_ReleaseWrite(&scp->rw);
432 * Iterate through all entries in a directory.
433 * When the function funcp is called, the buffer is locked but the
434 * directory vnode is not.
436 * If the retscp parameter is not NULL, the parmp must be a
437 * cm_lookupSearch_t object.
439 long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
440 osi_hyper_t *startOffsetp, cm_user_t *userp, cm_req_t *reqp,
441 cm_scache_t **retscp)
445 cm_dirEntry_t *dep = 0;
448 osi_hyper_t dirLength;
449 osi_hyper_t bufferOffset;
450 osi_hyper_t curOffset;
454 cm_pageHeader_t *pageHeaderp;
456 long nextEntryCookie;
457 int numDirChunks; /* # of 32 byte dir chunks in this entry */
459 /* get the directory size */
460 lock_ObtainWrite(&scp->rw);
461 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_LOOKUP,
462 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
463 lock_ReleaseWrite(&scp->rw);
467 if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
468 return CM_ERROR_NOTDIR;
470 if (retscp) /* if this is a lookup call */
472 cm_lookupSearch_t* sp = parmp;
475 #ifdef AFS_FREELANCE_CLIENT
476 /* Freelance entries never end up in the DNLC because they
477 * do not have an associated cm_server_t
479 !(cm_freelanceEnabled &&
480 sp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
481 sp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
482 #else /* !AFS_FREELANCE_CLIENT */
487 int casefold = sp->caseFold;
488 sp->caseFold = 0; /* we have a strong preference for exact matches */
489 if ( *retscp = cm_dnlcLookup(scp, sp)) /* dnlc hit */
491 sp->caseFold = casefold;
494 sp->caseFold = casefold;
498 * see if we can find it using the directory hash tables.
499 * we can only do exact matches, since the hash is case
502 if (funcp != (cm_DirFuncp_t)cm_BPlusDirFoo)
511 code = cm_BeginDirOp(scp, userp, reqp, CM_DIRLOCK_READ,
512 CM_DIROP_FLAG_NONE, &dirop);
516 code = cm_BPlusDirLookup(&dirop, sp->nsearchNamep, &sp->fid);
521 code = cm_DirLookup(&dirop, sp->searchNamep, &sp->fid);
529 sp->ExactFound = TRUE;
530 *retscp = NULL; /* force caller to call cm_GetSCache() */
535 if (sp->caseFold && code == CM_ERROR_INEXACT_MATCH) {
538 sp->ExactFound = FALSE;
539 *retscp = NULL; /* force caller to call cm_GetSCache() */
543 return CM_ERROR_BPLUS_NOMATCH;
550 * XXX We only get the length once. It might change when we drop the
553 dirLength = scp->length;
556 bufferOffset.LowPart = bufferOffset.HighPart = 0;
558 curOffset = *startOffsetp;
560 curOffset.HighPart = 0;
561 curOffset.LowPart = 0;
565 /* make sure that curOffset.LowPart doesn't point to the first
566 * 32 bytes in the 2nd through last dir page, and that it
567 * doesn't point at the first 13 32-byte chunks in the first
568 * dir page, since those are dir and page headers, and don't
569 * contain useful information.
571 temp = curOffset.LowPart & (2048-1);
572 if (curOffset.HighPart == 0 && curOffset.LowPart < 2048) {
573 /* we're in the first page */
574 if (temp < 13*32) temp = 13*32;
577 /* we're in a later dir page */
578 if (temp < 32) temp = 32;
581 /* make sure the low order 5 bits are zero */
584 /* now put temp bits back ito curOffset.LowPart */
585 curOffset.LowPart &= ~(2048-1);
586 curOffset.LowPart |= temp;
588 /* check if we've passed the dir's EOF */
589 if (LargeIntegerGreaterThanOrEqualTo(curOffset, dirLength))
592 /* see if we can use the bufferp we have now; compute in which
593 * page the current offset would be, and check whether that's
594 * the offset of the buffer we have. If not, get the buffer.
596 thyper.HighPart = curOffset.HighPart;
597 thyper.LowPart = curOffset.LowPart & ~(cm_data.buf_blockSize-1);
598 if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
601 lock_ReleaseMutex(&bufferp->mx);
602 buf_Release(bufferp);
606 code = buf_Get(scp, &thyper, reqp, 0, &bufferp);
608 /* if buf_Get() fails we do not have a buffer object to lock */
613 lock_ObtainMutex(&bufferp->mx);
614 bufferOffset = thyper;
616 /* now get the data in the cache */
618 lock_ObtainWrite(&scp->rw);
619 code = cm_SyncOp(scp, bufferp, userp, reqp,
621 CM_SCACHESYNC_NEEDCALLBACK
623 | CM_SCACHESYNC_BUFLOCKED);
625 lock_ReleaseWrite(&scp->rw);
628 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
630 if (cm_HaveBuffer(scp, bufferp, 1)) {
631 lock_ReleaseWrite(&scp->rw);
635 /* otherwise, load the buffer and try again */
636 lock_ReleaseMutex(&bufferp->mx);
637 code = cm_GetBuffer(scp, bufferp, NULL, userp,
639 lock_ReleaseWrite(&scp->rw);
640 lock_ObtainMutex(&bufferp->mx);
645 lock_ReleaseMutex(&bufferp->mx);
646 buf_Release(bufferp);
650 } /* if (wrong buffer) ... */
652 /* now we have the buffer containing the entry we're interested
653 * in; copy it out if it represents a non-deleted entry.
655 entryInDir = curOffset.LowPart & (2048-1);
656 entryInBuffer = curOffset.LowPart & (cm_data.buf_blockSize - 1);
658 /* page header will help tell us which entries are free. Page
659 * header can change more often than once per buffer, since
660 * AFS 3 dir page size may be less than (but not more than) a
661 * buffer package buffer.
663 /* only look intra-buffer */
664 temp = curOffset.LowPart & (cm_data.buf_blockSize - 1);
665 temp &= ~(2048 - 1); /* turn off intra-page bits */
666 pageHeaderp = (cm_pageHeader_t *) (bufferp->datap + temp);
668 /* now determine which entry we're looking at in the page. If
669 * it is free (there's a free bitmap at the start of the dir),
670 * we should skip these 32 bytes.
672 slotInPage = (entryInDir & 0x7e0) >> 5;
673 if (!(pageHeaderp->freeBitmap[slotInPage>>3]
674 & (1 << (slotInPage & 0x7)))) {
675 /* this entry is free */
676 numDirChunks = 1; /* only skip this guy */
680 tp = bufferp->datap + entryInBuffer;
681 dep = (cm_dirEntry_t *) tp; /* now points to AFS3 dir entry */
684 * here are some consistency checks
686 if (dep->flag != CM_DIR_FFIRST ||
687 strlen(dep->name) > 256) {
688 code = CM_ERROR_INVAL;
690 "cm_ApplyDir invalid directory entry for scp %p bufp %p",
692 osi_Log4(afsd_logp,"... cell %u vol %u vnode %u uniq %u",
693 scp->fid.cell, scp->fid.volume, scp->fid.vnode, scp->fid.unique);
694 bufferp->dataVersion = CM_BUF_VERSION_BAD;
698 /* while we're here, compute the next entry's location, too,
699 * since we'll need it when writing out the cookie into the
700 * dir listing stream.
702 numDirChunks = cm_NameEntries(dep->name, NULL);
704 /* compute the offset of the cookie representing the next entry */
705 nextEntryCookie = curOffset.LowPart
706 + (CM_DIR_CHUNKSIZE * numDirChunks);
708 if (dep->fid.vnode != 0) {
709 /* this is one of the entries to use: it is not deleted */
710 code = (*funcp)(scp, dep, parmp, &curOffset);
713 } /* if we're including this name */
716 /* and adjust curOffset to be where the new cookie is */
718 thyper.LowPart = CM_DIR_CHUNKSIZE * numDirChunks;
719 curOffset = LargeIntegerAdd(thyper, curOffset);
720 } /* while copying data for dir listing */
722 /* release the mutex */
724 lock_ReleaseMutex(&bufferp->mx);
725 buf_Release(bufferp);
730 int cm_NoneUpper(normchar_t *s)
734 if (c >= 'A' && c <= 'Z')
739 int cm_NoneLower(normchar_t *s)
743 if (c >= 'a' && c <= 'z')
748 long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
751 cm_lookupSearch_t *sp;
753 normchar_t matchName[MAX_PATH];
754 int looking_for_short_name = FALSE;
756 sp = (cm_lookupSearch_t *) rockp;
758 if (cm_FsStringToNormString(dep->name, -1, matchName, lengthof(matchName)) == 0) {
759 /* Can't normalize FS string. */
764 match = cm_NormStrCmpI(matchName, sp->nsearchNamep);
766 match = cm_NormStrCmp(matchName, sp->nsearchNamep);
771 && !cm_Is8Dot3(matchName)) {
773 cm_Gen8Dot3NameInt(dep->name, &dep->fid, matchName, NULL);
775 match = cm_NormStrCmpI(matchName, sp->nsearchNamep);
777 match = cm_NormStrCmp(matchName, sp->nsearchNamep);
778 looking_for_short_name = TRUE;
788 if (!sp->caseFold || looking_for_short_name) {
789 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
790 return CM_ERROR_STOPNOW;
794 * If we get here, we are doing a case-insensitive search, and we
795 * have found a match. Now we determine what kind of match it is:
796 * exact, lower-case, upper-case, or none of the above. This is done
797 * in order to choose among matches, if there are more than one.
800 /* Exact matches are the best. */
801 match = cm_NormStrCmp(matchName, sp->nsearchNamep);
804 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
805 return CM_ERROR_STOPNOW;
808 /* Lower-case matches are next. */
811 if (cm_NoneUpper(matchName)) {
816 /* Upper-case matches are next. */
819 if (cm_NoneLower(matchName)) {
824 /* General matches are last. */
830 cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
834 /* read the contents of a mount point into the appropriate string.
835 * called with write locked scp, and returns with locked scp.
837 long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
841 code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_FETCHDATA);
845 if (scp->mountPointStringp[0] &&
846 scp->mpDataVersion == scp->dataVersion) {
851 #ifdef AFS_FREELANCE_CLIENT
852 /* File servers do not have data for freelance entries */
853 if (cm_freelanceEnabled &&
854 scp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
855 scp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
857 code = cm_FreelanceFetchMountPointString(scp);
859 #endif /* AFS_FREELANCE_CLIENT */
861 char temp[MOUNTPOINTLEN];
863 afs_uint32 bytesRead = 0;
865 /* otherwise, we have to read it in */
866 offset.LowPart = offset.HighPart = 0;
867 code = cm_GetData(scp, &offset, temp, MOUNTPOINTLEN, &bytesRead, userp, reqp);
872 * scp->length is the actual length of the mount point string.
873 * It is current because cm_GetData merged the most up to date
874 * status info into scp and has not dropped the rwlock since.
876 if (scp->length.LowPart > MOUNTPOINTLEN - 1) {
877 code = CM_ERROR_TOOBIG;
881 if (scp->length.LowPart == 0) {
882 code = CM_ERROR_INVAL;
886 /* convert the terminating dot to a NUL */
887 temp[scp->length.LowPart - 1] = 0;
888 memcpy(scp->mountPointStringp, temp, scp->length.LowPart);
889 scp->mpDataVersion = scp->dataVersion;
893 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_FETCHDATA);
899 /* called with a locked scp and chases the mount point, yielding outScpp.
900 * scp remains write locked, just for simplicity of describing the interface.
902 long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
903 cm_req_t *reqp, cm_scache_t **outScpp)
905 fschar_t *cellNamep = NULL;
906 fschar_t *volNamep = NULL;
910 cm_volume_t *volp = NULL;
919 if (scp->mountRootFid.cell != 0 && scp->mountRootGen >= cm_data.mountRootGen) {
920 tfid = scp->mountRootFid;
921 lock_ReleaseWrite(&scp->rw);
922 code = cm_GetSCache(&tfid, NULL, outScpp, userp, reqp);
923 lock_ObtainWrite(&scp->rw);
927 /* parse the volume name */
928 mpNamep = scp->mountPointStringp;
930 return CM_ERROR_NOSUCHPATH;
931 mtType = *scp->mountPointStringp;
933 cp = cm_FsStrChr(mpNamep, _FS(':'));
935 /* cellular mount point */
936 cellNamep = (fschar_t *)malloc((cp - mpNamep) * sizeof(fschar_t));
937 cm_FsStrCpyN(cellNamep, cp - mpNamep, mpNamep + 1, cp - mpNamep - 1);
938 volNamep = cm_FsStrDup(cp+1);
940 /* now look up the cell */
941 lock_ReleaseWrite(&scp->rw);
942 cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE|CM_FLAG_NOPROBE);
943 lock_ObtainWrite(&scp->rw);
946 volNamep = cm_FsStrDup(mpNamep + 1);
948 #ifdef AFS_FREELANCE_CLIENT
950 * Mount points in the Freelance cell should default
951 * to the workstation cell.
953 if (cm_freelanceEnabled &&
954 scp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
955 scp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
957 fschar_t rootCellName[256]="";
958 cm_GetRootCellName(rootCellName);
959 cellp = cm_GetCell(rootCellName, 0);
961 #endif /* AFS_FREELANCE_CLIENT */
962 cellp = cm_FindCellByID(scp->fid.cell, 0);
966 code = CM_ERROR_NOSUCHCELL;
970 vnLength = cm_FsStrLen(volNamep);
971 if (vnLength >= 8 && cm_FsStrCmp(volNamep + vnLength - 7, ".backup") == 0)
972 targetType = BACKVOL;
973 else if (vnLength >= 10
974 && cm_FsStrCmp(volNamep + vnLength - 9, ".readonly") == 0)
979 /* check for backups within backups */
980 if (targetType == BACKVOL
981 && (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
982 == CM_SCACHEFLAG_RO) {
983 code = CM_ERROR_NOSUCHVOLUME;
987 /* now we need to get the volume */
988 lock_ReleaseWrite(&scp->rw);
989 if (cm_VolNameIsID(volNamep)) {
990 code = cm_FindVolumeByID(cellp, atoi(volNamep), userp, reqp,
991 CM_GETVOL_FLAG_CREATE, &volp);
993 code = cm_FindVolumeByName(cellp, volNamep, userp, reqp,
994 CM_GETVOL_FLAG_CREATE, &volp);
996 lock_ObtainWrite(&scp->rw);
999 afs_uint32 cell, volume;
1000 cm_vol_state_t *statep;
1002 cell = cellp->cellID;
1004 /* if the mt pt originates in a .backup volume (not a .readonly)
1005 * and FollowBackupPath is active, and if there is a .backup
1006 * volume for the target, then use the .backup of the target
1007 * instead of the read-write.
1009 if (cm_followBackupPath &&
1010 volp->vol[BACKVOL].ID != 0 &&
1011 (dscp->flags & (CM_SCACHEFLAG_RO|CM_SCACHEFLAG_PURERO)) == CM_SCACHEFLAG_RO &&
1012 (targetType == RWVOL || targetType == ROVOL && volp->vol[ROVOL].ID == 0)
1014 targetType = BACKVOL;
1016 /* if the mt pt is in a read-only volume (not just a
1017 * backup), and if there is a read-only volume for the
1018 * target, and if this is a targetType '#' mount point, use
1019 * the read-only, otherwise use the one specified.
1021 else if (mtType == '#' && targetType == RWVOL &&
1022 (scp->flags & CM_SCACHEFLAG_PURERO) &&
1023 volp->vol[ROVOL].ID != 0) {
1027 lock_ObtainWrite(&volp->rw);
1028 statep = cm_VolumeStateByType(volp, targetType);
1029 volume = statep->ID;
1030 statep->dotdotFid = dscp->fid;
1031 lock_ReleaseWrite(&volp->rw);
1033 /* the rest of the fid is a magic number */
1034 cm_SetFid(&scp->mountRootFid, cell, volume, 1, 1);
1035 scp->mountRootGen = cm_data.mountRootGen;
1037 tfid = scp->mountRootFid;
1038 lock_ReleaseWrite(&scp->rw);
1039 code = cm_GetSCache(&tfid, NULL, outScpp, userp, reqp);
1040 lock_ObtainWrite(&scp->rw);
1053 long cm_LookupInternal(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_user_t *userp,
1054 cm_req_t *reqp, cm_scache_t **outScpp)
1057 int dnlcHit = 1; /* did we hit in the dnlc? yes, we did */
1058 cm_scache_t *tscp = NULL;
1059 cm_scache_t *mountedScp;
1060 cm_lookupSearch_t rock;
1062 normchar_t *nnamep = NULL;
1063 fschar_t *fnamep = NULL;
1068 memset(&rock, 0, sizeof(rock));
1070 if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
1071 && cm_ClientStrCmp(cnamep, _C("..")) == 0) {
1072 if (dscp->dotdotFid.volume == 0)
1073 return CM_ERROR_NOSUCHVOLUME;
1074 rock.fid = dscp->dotdotFid;
1076 } else if (cm_ClientStrCmp(cnamep, _C(".")) == 0) {
1077 rock.fid = dscp->fid;
1081 nnamep = cm_ClientStringToNormStringAlloc(cnamep, -1, NULL);
1083 code = CM_ERROR_NOSUCHFILE;
1086 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
1088 code = CM_ERROR_NOSUCHFILE;
1093 if (flags & CM_FLAG_NOMOUNTCHASE) {
1094 /* In this case, we should go and call cm_Dir* functions
1095 directly since the following cm_ApplyDir() function will
1103 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ,
1104 CM_DIROP_FLAG_NONE, &dirop);
1107 code = cm_BPlusDirLookup(&dirop, nnamep, &rock.fid);
1112 code = cm_DirLookup(&dirop, fnamep, &rock.fid);
1114 cm_EndDirOp(&dirop);
1124 if (code == CM_ERROR_INEXACT_MATCH && (flags & CM_FLAG_CASEFOLD)) {
1131 code = CM_ERROR_BPLUS_NOMATCH;
1137 rock.fid.cell = dscp->fid.cell;
1138 rock.fid.volume = dscp->fid.volume;
1139 rock.searchNamep = fnamep;
1140 rock.nsearchNamep = nnamep;
1141 rock.caseFold = (flags & CM_FLAG_CASEFOLD);
1142 rock.hasTilde = ((cm_ClientStrChr(cnamep, '~') != NULL) ? 1 : 0);
1144 /* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
1145 code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
1146 (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
1148 /* code == 0 means we fell off the end of the dir, while stopnow means
1149 * that we stopped early, probably because we found the entry we're
1150 * looking for. Any other non-zero code is an error.
1152 if (code && code != CM_ERROR_STOPNOW) {
1153 /* if the cm_scache_t we are searching in is not a directory
1154 * we must return path not found because the error
1155 * is to describe the final component not an intermediary
1157 if (code == CM_ERROR_NOTDIR) {
1158 if (flags & CM_FLAG_CHECKPATH)
1159 code = CM_ERROR_NOSUCHPATH;
1161 code = CM_ERROR_NOSUCHFILE;
1167 getroot = (dscp==cm_data.rootSCachep) ;
1169 if (!(cm_freelanceEnabled && cm_freelanceDiscovery) || !getroot) {
1170 if (flags & CM_FLAG_CHECKPATH)
1171 code = CM_ERROR_NOSUCHPATH;
1173 code = CM_ERROR_NOSUCHFILE;
1176 else if (!cm_ClientStrChr(cnamep, '#') &&
1177 !cm_ClientStrChr(cnamep, '%') &&
1178 cm_ClientStrCmpI(cnamep, _C("srvsvc")) &&
1179 cm_ClientStrCmpI(cnamep, _C("wkssvc")) &&
1180 cm_ClientStrCmpI(cnamep, _C("ipc$")))
1182 /* nonexistent dir on freelance root, so add it */
1183 fschar_t fullname[CELL_MAXNAMELEN + 1] = "."; /* +1 so that when we skip the . the size is still CELL_MAXNAMELEN */
1187 osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %S",
1188 osi_LogSaveClientString(afsd_logp,cnamep));
1191 * There is an ugly behavior where a share name "foo" will be searched
1192 * for as "fo". If the searched for name differs by an already existing
1193 * symlink or mount point in the Freelance directory, do not add the
1194 * new value automatically.
1198 fnlen = strlen(fnamep);
1199 if ( fnamep[fnlen-1] == '.') {
1200 fnamep[fnlen-1] = '\0';
1205 if (cnamep[0] == '.') {
1206 if (cm_GetCell_Gen(&fnamep[1], &fullname[1], CM_FLAG_CREATE)) {
1208 code = cm_FreelanceAddMount(fullname, &fullname[1], "root.cell", 1, &rock.fid);
1209 if ( cm_FsStrCmpI(&fnamep[1], &fullname[1])) {
1211 * Do not permit symlinks that are one of:
1212 * . the cellname followed by a dot
1213 * . the cellname minus a single character
1214 * . a substring of the cellname that does not consist of full components
1216 if ( cm_strnicmp_utf8(&fnamep[1], fullname, (int)fnlen-1) == 0 &&
1217 (fnlen-1 == strlen(fullname)-1 || fullname[fnlen-1] != '.'))
1219 /* do not add; substitute fullname for the search */
1221 fnamep = malloc(strlen(fullname)+2);
1223 strncpy(&fnamep[1], fullname, strlen(fullname)+1);
1226 code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
1231 if (cm_GetCell_Gen(fnamep, fullname, CM_FLAG_CREATE)) {
1233 code = cm_FreelanceAddMount(fullname, fullname, "root.cell", 0, &rock.fid);
1234 if ( cm_FsStrCmpI(fnamep, fullname)) {
1236 * Do not permit symlinks that are one of:
1237 * . the cellname followed by a dot
1238 * . the cellname minus a single character
1239 * . a substring of the cellname that does not consist of full components
1241 if ( cm_strnicmp_utf8(fnamep, fullname, (int)fnlen-1) == 0 &&
1242 (fnlen == strlen(fullname)-1 || fullname[fnlen] != '.'))
1244 /* do not add; substitute fullname for the search */
1246 fnamep = strdup(fullname);
1250 code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
1259 nnamep = cm_FsStringToNormStringAlloc(fnamep, -1, NULL);
1263 if (!found || code) { /* add mount point failed, so give up */
1264 if (flags & CM_FLAG_CHECKPATH)
1265 code = CM_ERROR_NOSUCHPATH;
1267 code = CM_ERROR_NOSUCHFILE;
1270 tscp = NULL; /* to force call of cm_GetSCache */
1272 if (flags & CM_FLAG_CHECKPATH)
1273 code = CM_ERROR_NOSUCHPATH;
1275 code = CM_ERROR_NOSUCHFILE;
1281 if ( !tscp ) /* we did not find it in the dnlc */
1284 code = cm_GetSCache(&rock.fid, &dscp->fid, &tscp, userp, reqp);
1288 /* tscp is now held */
1290 lock_ObtainWrite(&tscp->rw);
1293 * Do not get status if we do not already have a callback or know the type.
1294 * The process of reading the mount point string will obtain status information
1295 * in a single RPC. No reason to add a second round trip.
1297 * If we do have a callback, use cm_SyncOp to get status in case the
1298 * current cm_user_t is not the same as the one that obtained the
1299 * mount point string contents.
1301 if (cm_HaveCallback(tscp) || tscp->fileType == CM_SCACHETYPE_UNKNOWN) {
1302 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1303 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1305 lock_ReleaseWrite(&tscp->rw);
1306 cm_ReleaseSCache(tscp);
1309 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1311 /* tscp is now locked */
1313 if (!(flags & CM_FLAG_NOMOUNTCHASE)
1314 && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
1315 /* mount points are funny: they have a volume name to mount
1318 code = cm_ReadMountPoint(tscp, userp, reqp);
1320 code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
1322 lock_ReleaseWrite(&tscp->rw);
1323 cm_ReleaseSCache(tscp);
1330 lock_ReleaseWrite(&tscp->rw);
1333 /* copy back pointer */
1336 /* insert scache in dnlc */
1337 if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
1338 /* lock the directory entry to prevent racing callback revokes */
1339 lock_ObtainRead(&dscp->rw);
1340 if ( dscp->cbServerp != NULL && dscp->cbExpires > 0 ) {
1341 /* TODO: reuse nnamep from above */
1344 nnamep = cm_ClientStringToNormStringAlloc(cnamep, -1, NULL);
1346 cm_dnlcEnter(dscp, nnamep, tscp);
1348 lock_ReleaseRead(&dscp->rw);
1365 int cm_ExpandSysName(cm_req_t * reqp, clientchar_t *inp, clientchar_t *outp, long outSizeCch, unsigned int index)
1370 int use_sysname64 = 0;
1372 if (cm_sysName64Count > 0 && reqp && !(reqp->flags & CM_REQ_WOW64) && (reqp->flags & CM_REQ_SOURCE_REDIR))
1376 tp = cm_ClientStrRChr(inp, '@');
1378 return 0; /* no @sys */
1380 if (cm_ClientStrCmp(tp, _C("@sys")) != 0)
1381 return 0; /* no @sys */
1383 /* caller just wants to know if this is a valid @sys type of name */
1388 if (use_sysname64 && index >= cm_sysName64Count)
1392 if (index >= cm_sysNameCount)
1395 /* otherwise generate the properly expanded @sys name */
1396 prefixCount = (int)(tp - inp);
1398 cm_ClientStrCpyN(outp, outSizeCch, inp, prefixCount); /* copy out "a." from "a.@sys" */
1399 outp[prefixCount] = 0; /* null terminate the "a." */
1402 cm_ClientStrCat(outp, outSizeCch, cm_sysName64List[index]);
1405 cm_ClientStrCat(outp, outSizeCch, cm_sysNameList[index]);
1410 long cm_EvaluateVolumeReference(clientchar_t * namep, long flags, cm_user_t * userp,
1411 cm_req_t *reqp, cm_scache_t ** outScpp)
1413 afs_uint32 code = 0;
1414 fschar_t cellName[CELL_MAXNAMELEN];
1415 fschar_t volumeName[VL_MAXNAMELEN];
1419 fschar_t * fnamep = NULL;
1421 cm_cell_t * cellp = NULL;
1422 cm_volume_t * volp = NULL;
1426 int mountType = RWVOL;
1428 osi_Log1(afsd_logp, "cm_EvaluateVolumeReference for string [%S]",
1429 osi_LogSaveClientString(afsd_logp, namep));
1431 if (cm_ClientStrCmpNI(namep, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH) != 0) {
1432 goto _exit_invalid_path;
1435 /* namep is assumed to look like the following:
1437 @vol:<cellname>%<volume>\0
1439 @vol:<cellname>#<volume>\0
1443 fnamep = cm_ClientStringToFsStringAlloc(namep, -1, NULL);
1444 cp = fnamep + CM_PREFIX_VOL_CCH; /* cp points to cell name, hopefully */
1445 tp = cm_FsStrChr(cp, '%');
1447 tp = cm_FsStrChr(cp, '#');
1449 (len = tp - cp) == 0 ||
1450 len > CELL_MAXNAMELEN)
1451 goto _exit_invalid_path;
1452 cm_FsStrCpyN(cellName, lengthof(cellName), cp, len);
1457 cp = tp+1; /* cp now points to volume, supposedly */
1458 cm_FsStrCpy(volumeName, lengthof(volumeName), cp);
1460 /* OK, now we have the cell and the volume */
1461 osi_Log2(afsd_logp, " Found cell [%s] and volume [%s]",
1462 osi_LogSaveFsString(afsd_logp, cellName),
1463 osi_LogSaveFsString(afsd_logp, volumeName));
1465 cellp = cm_GetCell(cellName, CM_FLAG_CREATE);
1466 if (cellp == NULL) {
1467 goto _exit_invalid_path;
1470 len = cm_FsStrLen(volumeName);
1471 if (len >= 8 && cm_FsStrCmp(volumeName + len - 7, ".backup") == 0)
1473 else if (len >= 10 &&
1474 cm_FsStrCmp(volumeName + len - 9, ".readonly") == 0)
1479 if (cm_VolNameIsID(volumeName)) {
1480 code = cm_FindVolumeByID(cellp, atoi(volumeName), userp, reqp,
1481 CM_GETVOL_FLAG_CREATE, &volp);
1483 code = cm_FindVolumeByName(cellp, volumeName, userp, reqp,
1484 CM_GETVOL_FLAG_CREATE, &volp);
1490 if (volType == BACKVOL)
1491 volume = volp->vol[BACKVOL].ID;
1492 else if (volType == ROVOL ||
1493 (volType == RWVOL && mountType == ROVOL && volp->vol[ROVOL].ID != 0))
1494 volume = volp->vol[ROVOL].ID;
1496 volume = volp->vol[RWVOL].ID;
1498 cm_SetFid(&fid, cellp->cellID, volume, 1, 1);
1500 code = cm_GetSCache(&fid, NULL, outScpp, userp, reqp);
1513 if (flags & CM_FLAG_CHECKPATH)
1514 return CM_ERROR_NOSUCHPATH;
1516 return CM_ERROR_NOSUCHFILE;
1519 #ifdef DEBUG_REFCOUNT
1520 long cm_LookupDbg(cm_scache_t *dscp, clientchar_t *namep, long flags, cm_user_t *userp,
1521 cm_req_t *reqp, cm_scache_t **outScpp, char * file, long line)
1523 long cm_Lookup(cm_scache_t *dscp, clientchar_t *namep, long flags, cm_user_t *userp,
1524 cm_req_t *reqp, cm_scache_t **outScpp)
1528 clientchar_t tname[AFSPATHMAX];
1529 int sysNameIndex = 0;
1530 cm_scache_t *scp = NULL;
1532 #ifdef DEBUG_REFCOUNT
1533 afsi_log("%s:%d cm_Lookup dscp 0x%p ref %d", file, line, dscp, dscp->refCount, file, line);
1534 osi_Log2(afsd_logp, "cm_Lookup dscp 0x%p ref %d", dscp, dscp->refCount);
1537 if ( cm_ClientStrCmpI(namep,_C(SMB_IOCTL_FILENAME_NOSLASH)) == 0 ) {
1538 if (flags & CM_FLAG_CHECKPATH)
1539 return CM_ERROR_NOSUCHPATH;
1541 return CM_ERROR_NOSUCHFILE;
1544 if (dscp == cm_data.rootSCachep &&
1545 cm_ClientStrCmpNI(namep, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH) == 0) {
1546 return cm_EvaluateVolumeReference(namep, flags, userp, reqp, outScpp);
1549 if (cm_ExpandSysName(reqp, namep, NULL, 0, 0) > 0) {
1550 for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
1551 code = cm_ExpandSysName(reqp, namep, tname, lengthof(tname), sysNameIndex);
1553 code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
1554 #ifdef DEBUG_REFCOUNT
1555 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);
1556 osi_Log3(afsd_logp, "cm_LookupInternal (1) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1564 cm_ReleaseSCache(scp);
1568 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1569 #ifdef DEBUG_REFCOUNT
1570 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);
1571 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1578 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1579 #ifdef DEBUG_REFCOUNT
1580 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);
1581 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1587 /* None of the possible sysName expansions could be found */
1588 if (flags & CM_FLAG_CHECKPATH)
1589 return CM_ERROR_NOSUCHPATH;
1591 return CM_ERROR_NOSUCHFILE;
1594 /*! \brief Unlink a file name
1596 Encapsulates a call to RXAFS_RemoveFile().
1598 \param[in] dscp cm_scache_t pointing at the directory containing the
1599 name to be unlinked.
1601 \param[in] fnamep Original name to be unlinked. This is the
1602 name that will be passed into the RXAFS_RemoveFile() call.
1603 This parameter is optional. If not provided, the value will
1606 \param[in] came Client name to be unlinked. This name will be used
1607 to update the local directory caches.
1609 \param[in] userp cm_user_t for the request.
1611 \param[in] reqp Request tracker.
1614 long cm_Unlink(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t * cnamep,
1615 cm_user_t *userp, cm_req_t *reqp)
1621 AFSFetchStatus newDirStatus;
1623 struct rx_connection * rxconnp;
1625 cm_scache_t *scp = NULL;
1626 int free_fnamep = FALSE;
1629 memset(&volSync, 0, sizeof(volSync));
1631 if (fnamep == NULL) {
1634 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ,
1635 CM_DIROP_FLAG_NONE, &dirop);
1637 code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
1640 cm_EndDirOp(&dirop);
1647 #ifdef AFS_FREELANCE_CLIENT
1648 if (cm_freelanceEnabled && dscp == cm_data.rootSCachep) {
1649 /* deleting a mount point from the root dir. */
1650 code = cm_FreelanceRemoveMount(fnamep);
1655 code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
1659 /* Check for RO volume */
1660 if (dscp->flags & CM_SCACHEFLAG_RO) {
1661 code = CM_ERROR_READONLY;
1665 /* make sure we don't screw up the dir status during the merge */
1666 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE,
1667 CM_DIROP_FLAG_NONE, &dirop);
1669 lock_ObtainWrite(&dscp->rw);
1670 sflags = CM_SCACHESYNC_STOREDATA;
1671 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1672 lock_ReleaseWrite(&dscp->rw);
1674 cm_EndDirOp(&dirop);
1679 InterlockedIncrement(&dscp->activeRPCs);
1681 afsFid.Volume = dscp->fid.volume;
1682 afsFid.Vnode = dscp->fid.vnode;
1683 afsFid.Unique = dscp->fid.unique;
1685 osi_Log1(afsd_logp, "CALL RemoveFile scp 0x%p", dscp);
1687 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
1691 rxconnp = cm_GetRxConn(connp);
1692 code = RXAFS_RemoveFile(rxconnp, &afsFid, fnamep,
1693 &newDirStatus, &volSync);
1694 rx_PutConnection(rxconnp);
1696 } while (cm_Analyze(connp, userp, reqp, &dscp->fid, NULL, 1, &newDirStatus, &volSync, NULL, NULL, code));
1697 code = cm_MapRPCError(code, reqp);
1700 osi_Log1(afsd_logp, "CALL RemoveFile FAILURE, code 0x%x", code);
1702 osi_Log0(afsd_logp, "CALL RemoveFile SUCCESS");
1705 lock_ObtainWrite(&dirop.scp->dirlock);
1706 dirop.lockType = CM_DIRLOCK_WRITE;
1708 lock_ObtainWrite(&dscp->rw);
1709 cm_dnlcRemove(dscp, cnamep);
1711 code = cm_MergeStatus(NULL, dscp, &newDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
1713 if (cm_CheckDirOpForSingleChange(&dirop) && cnamep) {
1714 lock_ReleaseWrite(&dscp->rw);
1715 cm_DirDeleteEntry(&dirop, fnamep);
1717 cm_BPlusDirDeleteEntry(&dirop, cnamep);
1719 lock_ObtainWrite(&dscp->rw);
1722 InterlockedDecrement(&scp->activeRPCs);
1723 if (code == CM_ERROR_NOSUCHFILE) {
1724 /* windows would not have allowed the request to delete the file
1725 * if it did not believe the file existed. therefore, we must
1726 * have an inconsistent view of the world.
1728 dscp->cbServerp = NULL;
1732 cm_SyncOpDone(dscp, NULL, sflags);
1733 lock_ReleaseWrite(&dscp->rw);
1735 cm_EndDirOp(&dirop);
1737 if (invalidate && RDR_Initialized &&
1738 scp->fileType != CM_SCACHETYPE_FILE && scp->fileType != CM_SCACHETYPE_DIRECTORY)
1739 RDR_InvalidateObject(dscp->fid.cell, dscp->fid.volume, dscp->fid.vnode,
1740 dscp->fid.unique, dscp->fid.hash,
1741 dscp->fileType, AFS_INVALIDATE_DATA_VERSION);
1745 lock_ObtainWrite(&scp->rw);
1746 if (--scp->linkCount == 0) {
1747 _InterlockedOr(&scp->flags, CM_SCACHEFLAG_DELETED);
1748 lock_ObtainWrite(&cm_scacheLock);
1749 cm_AdjustScacheLRU(scp);
1750 cm_RemoveSCacheFromHashTable(scp);
1751 lock_ReleaseWrite(&cm_scacheLock);
1753 cm_DiscardSCache(scp);
1754 lock_ReleaseWrite(&scp->rw);
1755 if (RDR_Initialized && !(reqp->flags & CM_REQ_SOURCE_REDIR))
1756 RDR_InvalidateObject(scp->fid.cell, scp->fid.volume, scp->fid.vnode,
1757 scp->fid.unique, scp->fid.hash,
1758 scp->fileType, AFS_INVALIDATE_DELETED);
1760 cm_ReleaseSCache(scp);
1770 /* called with a write locked vnode, and fills in the link info.
1771 * returns this the vnode still write locked.
1773 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1777 lock_AssertWrite(&linkScp->rw);
1778 if (!linkScp->mountPointStringp[0] ||
1779 linkScp->mpDataVersion != linkScp->dataVersion) {
1781 #ifdef AFS_FREELANCE_CLIENT
1782 /* File servers do not have data for freelance entries */
1783 if (cm_freelanceEnabled &&
1784 linkScp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
1785 linkScp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
1787 code = cm_FreelanceFetchMountPointString(linkScp);
1789 #endif /* AFS_FREELANCE_CLIENT */
1791 char temp[MOUNTPOINTLEN];
1793 afs_uint32 bytesRead = 0;
1795 /* read the link data from the file server */
1796 offset.LowPart = offset.HighPart = 0;
1797 code = cm_GetData(linkScp, &offset, temp, MOUNTPOINTLEN, &bytesRead, userp, reqp);
1802 * linkScp->length is the actual length of the symlink target string.
1803 * It is current because cm_GetData merged the most up to date
1804 * status info into scp and has not dropped the rwlock since.
1806 if (linkScp->length.LowPart > MOUNTPOINTLEN - 1)
1807 return CM_ERROR_TOOBIG;
1808 if (linkScp->length.LowPart == 0)
1809 return CM_ERROR_INVAL;
1811 /* make sure we are NUL terminated */
1812 temp[linkScp->length.LowPart] = 0;
1813 memcpy(linkScp->mountPointStringp, temp, linkScp->length.LowPart + 1);
1814 linkScp->mpDataVersion = linkScp->dataVersion;
1817 if ( !strnicmp(linkScp->mountPointStringp, "msdfs:", strlen("msdfs:")) )
1818 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1820 } /* don't have symlink contents cached */
1825 /* called with a held vnode and a path suffix, with the held vnode being a
1826 * symbolic link. Our goal is to generate a new path to interpret, and return
1827 * this new path in newSpaceBufferp. If the new vnode is relative to a dir
1828 * other than the directory containing the symbolic link, then the new root is
1829 * returned in *newRootScpp, otherwise a null is returned there.
1831 long cm_AssembleLink(cm_scache_t *linkScp, fschar_t *pathSuffixp,
1832 cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1833 cm_user_t *userp, cm_req_t *reqp)
1840 *newRootScpp = NULL;
1841 *newSpaceBufferp = NULL;
1843 lock_ObtainWrite(&linkScp->rw);
1845 * Do not get status if we do not already have a callback.
1846 * The process of reading the symlink string will obtain status information
1847 * in a single RPC. No reason to add a second round trip.
1849 * If we do have a callback, use cm_SyncOp to get status in case the
1850 * current cm_user_t is not the same as the one that obtained the
1851 * symlink string contents.
1853 if (cm_HaveCallback(linkScp)) {
1854 code = cm_SyncOp(linkScp, NULL, userp, reqp, 0,
1855 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1857 lock_ReleaseWrite(&linkScp->rw);
1858 cm_ReleaseSCache(linkScp);
1861 cm_SyncOpDone(linkScp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1863 code = cm_HandleLink(linkScp, userp, reqp);
1867 /* if we may overflow the buffer, bail out; buffer is signficantly
1868 * bigger than max path length, so we don't really have to worry about
1869 * being a little conservative here.
1871 if (cm_FsStrLen(linkScp->mountPointStringp) + cm_FsStrLen(pathSuffixp) + 2
1872 >= CM_UTILS_SPACESIZE) {
1873 code = CM_ERROR_TOOBIG;
1877 tsp = cm_GetSpace();
1878 linkp = linkScp->mountPointStringp;
1879 if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1880 if (strlen(linkp) > cm_mountRootLen)
1881 StringCbCopyA((char *) tsp->data, sizeof(tsp->data), linkp+cm_mountRootLen+1);
1884 *newRootScpp = cm_RootSCachep(userp, reqp);
1885 cm_HoldSCache(*newRootScpp);
1886 } else if (linkp[0] == '\\' && linkp[1] == '\\') {
1887 if (!strnicmp(&linkp[2], cm_NetbiosName, (len = (long)strlen(cm_NetbiosName))))
1889 char * p = &linkp[len + 3];
1890 if (strnicmp(p, "all", 3) == 0)
1893 StringCbCopyA(tsp->data, sizeof(tsp->data), p);
1894 for (p = tsp->data; *p; p++) {
1898 *newRootScpp = cm_RootSCachep(userp, reqp);
1899 cm_HoldSCache(*newRootScpp);
1901 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1902 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1903 code = CM_ERROR_PATH_NOT_COVERED;
1905 } else if ( linkScp->fileType == CM_SCACHETYPE_DFSLINK ||
1906 !strnicmp(linkp, "msdfs:", (len = (long)strlen("msdfs:"))) ) {
1907 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1908 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1909 code = CM_ERROR_PATH_NOT_COVERED;
1910 } else if (*linkp == '\\' || *linkp == '/') {
1912 /* formerly, this was considered to be from the AFS root,
1913 * but this seems to create problems. instead, we will just
1914 * reject the link */
1915 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp+1);
1916 *newRootScpp = cm_RootSCachep(userp, reqp);
1917 cm_HoldSCache(*newRootScpp);
1919 /* we still copy the link data into the response so that
1920 * the user can see what the link points to
1922 linkScp->fileType = CM_SCACHETYPE_INVALID;
1923 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1924 code = CM_ERROR_NOSUCHPATH;
1927 /* a relative link */
1928 StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1930 if (pathSuffixp[0] != 0) { /* if suffix string is non-null */
1931 StringCchCatA(tsp->data,lengthof(tsp->data), "\\");
1932 StringCchCatA(tsp->data,lengthof(tsp->data), pathSuffixp);
1936 clientchar_t * cpath = cm_FsStringToClientStringAlloc(tsp->data, -1, NULL);
1937 if (cpath != NULL) {
1938 cm_ClientStrCpy(tsp->wdata, lengthof(tsp->wdata), cpath);
1940 *newSpaceBufferp = tsp;
1942 code = CM_ERROR_NOSUCHPATH;
1949 if (code == CM_ERROR_PATH_NOT_COVERED && reqp->tidPathp && reqp->relPathp) {
1950 cm_VolStatus_Notify_DFS_Mapping(linkScp, reqp->tidPathp, reqp->relPathp);
1955 lock_ReleaseWrite(&linkScp->rw);
1958 #ifdef DEBUG_REFCOUNT
1959 long cm_NameIDbg(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
1960 cm_user_t *userp, clientchar_t *tidPathp, cm_req_t *reqp,
1961 cm_scache_t **outScpp,
1962 char * file, long line)
1964 long cm_NameI(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
1965 cm_user_t *userp, clientchar_t *tidPathp,
1966 cm_req_t *reqp, cm_scache_t **outScpp)
1970 clientchar_t *tp; /* ptr moving through input buffer */
1971 clientchar_t tc; /* temp char */
1972 int haveComponent; /* has new component started? */
1973 clientchar_t component[AFSPATHMAX]; /* this is the new component */
1974 clientchar_t *cp; /* component name being assembled */
1975 cm_scache_t *tscp; /* current location in the hierarchy */
1976 cm_scache_t *nscp; /* next dude down */
1977 cm_scache_t *dirScp; /* last dir we searched */
1978 cm_scache_t *linkScp; /* new root for the symlink we just
1980 cm_space_t *psp; /* space for current path, if we've hit
1982 cm_space_t *tempsp; /* temp vbl */
1983 clientchar_t *restp; /* rest of the pathname to interpret */
1984 int symlinkCount; /* count of # of symlinks traversed */
1985 int extraFlag; /* avoid chasing mt pts for dir cmd */
1986 int phase = 1; /* 1 = tidPathp, 2 = pathp */
1987 cm_fid_t fids[MAX_FID_COUNT]; /* array of fids processed in this path walk */
1988 int fid_count = 0; /* number of fids processed in this path walk */
1993 #ifdef DEBUG_REFCOUNT
1994 afsi_log("%s:%d cm_NameI rootscp 0x%p ref %d", file, line, rootSCachep, rootSCachep->refCount);
1995 osi_Log4(afsd_logp,"cm_NameI rootscp 0x%p path %S tidpath %S flags 0x%x",
1996 rootSCachep, pathp ? pathp : L"<NULL>", tidPathp ? tidPathp : L"<NULL>",
2011 cm_HoldSCache(tscp);
2019 /* map Unix slashes into DOS ones so we can interpret Unix
2025 if (!haveComponent) {
2028 } else if (tc == 0) {
2042 /* we have a component here */
2043 if (tc == 0 || tc == '\\') {
2044 /* end of the component; we're at the last
2045 * component if tc == 0. However, if the last
2046 * is a symlink, we have more to do.
2048 *cp++ = 0; /* add null termination */
2051 if (tscp == cm_RootSCachep(userp, reqp)) {
2052 code = cm_Lookup(tscp, component, CM_FLAG_CHECKPATH, userp, reqp, &nscp);
2054 if ((code == CM_ERROR_NOSUCHPATH || code == CM_ERROR_NOSUCHFILE ||
2055 code == CM_ERROR_BPLUS_NOMATCH) &&
2056 tscp == cm_data.rootSCachep) {
2058 clientchar_t volref[AFSPATHMAX];
2060 if (wcschr(component, '%') != NULL || wcschr(component, '#') != NULL) {
2062 * A volume reference: <cell>{%,#}<volume> -> @vol:<cell>{%,#}<volume>
2064 cm_ClientStrCpyN(volref, AFSPATHMAX, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH);
2065 cm_ClientStrCat(volref, AFSPATHMAX, component);
2067 code = cm_EvaluateVolumeReference(volref, CM_FLAG_CHECKPATH, userp, reqp, &nscp);
2069 #ifdef AFS_FREELANCE_CLIENT
2070 else if (tscp->fid.cell == AFS_FAKE_ROOT_CELL_ID && tscp->fid.volume == AFS_FAKE_ROOT_VOL_ID &&
2071 tscp->fid.vnode == 1 && tscp->fid.unique == 1) {
2073 * If this is the Freelance volume root directory then treat unrecognized
2074 * names as cell names and attempt to find the appropriate "root.cell".
2076 cm_ClientStrCpyN(volref, AFSPATHMAX, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH);
2077 if (component[0] == L'.') {
2078 cm_ClientStrCat(volref, AFSPATHMAX, &component[1]);
2079 cm_ClientStrCatN(volref, AFSPATHMAX, L"%", sizeof(WCHAR));
2081 cm_ClientStrCat(volref, AFSPATHMAX, component);
2082 cm_ClientStrCatN(volref, AFSPATHMAX, L"#", sizeof(WCHAR));
2084 cm_ClientStrCatN(volref, AFSPATHMAX, L"root.cell", 9 * sizeof(WCHAR));
2086 code = cm_EvaluateVolumeReference(volref, CM_FLAG_CHECKPATH, userp, reqp, &nscp);
2091 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
2092 extraFlag = CM_FLAG_NOMOUNTCHASE;
2093 code = cm_Lookup(tscp, component,
2095 userp, reqp, &nscp);
2098 if (!cm_ClientStrCmp(component,_C("..")) ||
2099 !cm_ClientStrCmp(component,_C("."))) {
2101 * roll back the fid list until we find the
2102 * fid that matches where we are now. Its not
2103 * necessarily one or two fids because they
2104 * might have been symlinks or mount points or
2105 * both that were crossed.
2107 for ( i=fid_count-1; i>=0; i--) {
2108 if (!cm_FidCmp(&nscp->fid, &fids[i]))
2113 /* add the new fid to the list */
2114 if (fid_count == MAX_FID_COUNT) {
2115 code = CM_ERROR_TOO_MANY_SYMLINKS;
2116 cm_ReleaseSCache(nscp);
2120 fids[fid_count++] = nscp->fid;
2125 cm_ReleaseSCache(tscp);
2127 cm_ReleaseSCache(dirScp);
2130 if ((code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH) &&
2131 tscp->fileType == CM_SCACHETYPE_SYMLINK) {
2132 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_NOSUCHPATH");
2133 return CM_ERROR_NOSUCHPATH;
2135 osi_Log1(afsd_logp,"cm_NameI code 0x%x", code);
2140 haveComponent = 0; /* component done */
2142 cm_ReleaseSCache(dirScp);
2143 dirScp = tscp; /* for some symlinks */
2144 tscp = nscp; /* already held */
2146 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
2149 cm_ReleaseSCache(dirScp);
2155 /* now, if tscp is a symlink, we should follow it and
2156 * assemble the path again.
2158 lock_ObtainWrite(&tscp->rw);
2159 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
2160 CM_SCACHESYNC_GETSTATUS
2161 | CM_SCACHESYNC_NEEDCALLBACK);
2163 lock_ReleaseWrite(&tscp->rw);
2164 cm_ReleaseSCache(tscp);
2167 cm_ReleaseSCache(dirScp);
2172 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2174 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
2175 /* this is a symlink; assemble a new buffer */
2176 lock_ReleaseWrite(&tscp->rw);
2177 if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
2178 cm_ReleaseSCache(tscp);
2181 cm_ReleaseSCache(dirScp);
2186 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_TOO_MANY_SYMLINKS");
2187 return CM_ERROR_TOO_MANY_SYMLINKS;
2197 /* TODO: make this better */
2198 frestp = cm_ClientStringToFsStringAlloc(restp, -1, NULL);
2199 code = cm_AssembleLink(tscp, frestp, &linkScp, &tempsp, userp, reqp);
2203 if (code == 0 && linkScp != NULL) {
2204 if (linkScp == cm_data.rootSCachep) {
2208 for ( i=0; i<fid_count; i++) {
2209 if ( !cm_FidCmp(&linkScp->fid, &fids[i]) ) {
2210 code = CM_ERROR_TOO_MANY_SYMLINKS;
2211 cm_ReleaseSCache(linkScp);
2217 if (i == fid_count && fid_count < MAX_FID_COUNT) {
2218 fids[fid_count++] = linkScp->fid;
2223 /* something went wrong */
2224 cm_ReleaseSCache(tscp);
2227 cm_ReleaseSCache(dirScp);
2233 /* otherwise, tempsp has the new path,
2234 * and linkScp is the new root from
2235 * which to interpret that path.
2236 * Continue with the namei processing,
2237 * also doing the bookkeeping for the
2238 * space allocation and tracking the
2239 * vnode reference counts.
2245 cm_ReleaseSCache(tscp);
2250 * now, if linkScp is null, that's
2251 * AssembleLink's way of telling us that
2252 * the sym link is relative to the dir
2253 * containing the link. We have a ref
2254 * to it in dirScp, and we hold it now
2255 * and reuse it as the new spot in the
2263 /* not a symlink, we may be done */
2264 lock_ReleaseWrite(&tscp->rw);
2272 cm_ReleaseSCache(dirScp);
2280 cm_ReleaseSCache(dirScp);
2283 } /* end of a component */
2286 } /* we have a component */
2287 } /* big while loop over all components */
2291 cm_ReleaseSCache(dirScp);
2297 cm_ReleaseSCache(tscp);
2299 #ifdef DEBUG_REFCOUNT
2300 afsi_log("%s:%d cm_NameI code 0x%x outScpp 0x%p ref %d", file, line, code, *outScpp, (*outScpp) ? (*outScpp)->refCount : 0);
2302 osi_Log2(afsd_logp,"cm_NameI code 0x%x outScpp 0x%p", code, *outScpp);
2306 /* called with a dir, and a vnode within the dir that happens to be a symlink.
2307 * We chase the link, and return a held pointer to the target, if it exists,
2308 * in *outScpp. If we succeed, we return 0, otherwise we return an error code
2309 * and do not hold or return a target vnode.
2311 * This is very similar to calling cm_NameI with the last component of a name,
2312 * which happens to be a symlink, except that we've already passed by the name.
2314 * This function is typically called by the directory listing functions, which
2315 * encounter symlinks but need to return the proper file length so programs
2316 * like "more" work properly when they make use of the attributes retrieved from
2319 * The input vnode should not be locked when this function is called.
2321 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
2322 cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
2326 cm_scache_t *newRootScp;
2330 osi_Log1(afsd_logp, "Evaluating symlink scp 0x%p", linkScp);
2332 code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
2336 /* now, if newRootScp is NULL, we're really being told that the symlink
2337 * is relative to the current directory (dscp).
2339 if (newRootScp == NULL) {
2341 cm_HoldSCache(dscp);
2344 code = cm_NameI(newRootScp, spacep->wdata,
2345 CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
2346 userp, NULL, reqp, outScpp);
2348 if (code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH)
2349 code = CM_ERROR_NOSUCHPATH;
2351 /* this stuff is allocated no matter what happened on the namei call,
2353 cm_FreeSpace(spacep);
2354 cm_ReleaseSCache(newRootScp);
2356 if (linkScp == *outScpp) {
2357 cm_ReleaseSCache(*outScpp);
2359 code = CM_ERROR_NOSUCHPATH;
2365 /* for a given entry, make sure that it isn't in the stat cache, and then
2366 * add it to the list of file IDs to be obtained.
2368 * Don't bother adding it if we already have a vnode. Note that the dir
2369 * is locked, so we have to be careful checking the vnode we're thinking of
2370 * processing, to avoid deadlocks.
2372 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
2383 /* Don't overflow bsp. */
2384 if (bsp->counter >= CM_BULKMAX)
2385 return CM_ERROR_STOPNOW;
2387 thyper.LowPart = cm_data.buf_blockSize;
2388 thyper.HighPart = 0;
2389 thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
2391 /* thyper is now the first byte past the end of the record we're
2392 * interested in, and bsp->bufOffset is the first byte of the record
2393 * we're interested in.
2394 * Skip data in the others.
2397 if (LargeIntegerLessThan(*offp, bsp->bufOffset))
2399 if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
2400 return CM_ERROR_STOPNOW;
2401 if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
2404 cm_SetFid(&tfid, scp->fid.cell, scp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
2405 tscp = cm_FindSCache(&tfid);
2407 if (lock_TryWrite(&tscp->rw)) {
2408 /* we have an entry that we can look at */
2409 if (!cm_EAccesFindEntry(bsp->userp, &tscp->fid) && cm_HaveCallback(tscp)) {
2410 /* we have a callback on it. Don't bother
2411 * fetching this stat entry, since we're happy
2412 * with the info we have.
2414 lock_ReleaseWrite(&tscp->rw);
2415 cm_ReleaseSCache(tscp);
2418 lock_ReleaseWrite(&tscp->rw);
2420 cm_ReleaseSCache(tscp);
2423 #ifdef AFS_FREELANCE_CLIENT
2424 // yj: if this is a mountpoint under root.afs then we don't want it
2425 // to be bulkstat-ed, instead, we call getSCache directly and under
2426 // getSCache, it is handled specially.
2427 if ( cm_freelanceEnabled &&
2428 tfid.cell==AFS_FAKE_ROOT_CELL_ID &&
2429 tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
2430 !(tfid.vnode==0x1 && tfid.unique==0x1) )
2432 osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
2433 return cm_GetSCache(&tfid, NULL, &tscp, NULL, NULL);
2435 #endif /* AFS_FREELANCE_CLIENT */
2438 bsp->fids[i].Volume = scp->fid.volume;
2439 bsp->fids[i].Vnode = tfid.vnode;
2440 bsp->fids[i].Unique = tfid.unique;
2445 cm_TryBulkStatRPC(cm_scache_t *dscp, cm_bulkStat_t *bbp, cm_user_t *userp, cm_req_t *reqp)
2448 AFSCBFids fidStruct;
2449 AFSBulkStats statStruct;
2451 AFSCBs callbackStruct;
2454 cm_callbackRequest_t cbReq;
2461 struct rx_connection * rxconnp;
2462 int inlinebulk; /* Did we use InlineBulkStatus RPC or not? */
2464 memset(&volSync, 0, sizeof(volSync));
2466 /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
2467 * make the calls to create the entries. Handle AFSCBMAX files at a
2470 for (filex = 0; filex < bbp->counter; filex += filesThisCall) {
2471 filesThisCall = bbp->counter - filex;
2472 if (filesThisCall > AFSCBMAX)
2473 filesThisCall = AFSCBMAX;
2475 fidStruct.AFSCBFids_len = filesThisCall;
2476 fidStruct.AFSCBFids_val = &bbp->fids[filex];
2477 statStruct.AFSBulkStats_len = filesThisCall;
2478 statStruct.AFSBulkStats_val = &bbp->stats[filex];
2479 callbackStruct.AFSCBs_len = filesThisCall;
2480 callbackStruct.AFSCBs_val = &bbp->callbacks[filex];
2481 cm_StartCallbackGrantingCall(NULL, &cbReq);
2482 osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
2485 * Whenever cm_Analyze is called for a RXAFS_ RPC there must
2486 * be a FID provided. However, the error code from RXAFS_BulkStatus
2487 * or RXAFS_InlinkBulkStatus does not apply to any FID. Therefore,
2488 * we generate an invalid FID to match with the RPC error.
2490 cm_SetFid(&tfid, dscp->fid.cell, dscp->fid.volume, 0, 0);
2495 code = cm_ConnFromFID(&tfid, userp, reqp, &connp);
2499 rxconnp = cm_GetRxConn(connp);
2500 if (SERVERHASINLINEBULK(connp)) {
2501 code = RXAFS_InlineBulkStatus(rxconnp, &fidStruct,
2502 &statStruct, &callbackStruct, &volSync);
2503 if (code == RXGEN_OPCODE) {
2504 SET_SERVERHASNOINLINEBULK(connp);
2511 * It is important to note that RXAFS_BulkStatus is quite braindead.
2512 * The AFS 3.6 file server implementation returns arrays that are
2513 * sized to hold responses for all of the requested FIDs but it only
2514 * populates their contents up to the point where it detects an error.
2515 * Unfortunately, it does inform the caller which entries were filled
2516 * and which were not. The caller has no ability to determine which
2517 * FID the RPC return code applies to or which of the FIDs valid status
2518 * info and callbacks have been issued for. As a result, when an
2519 * error is returned, none of the data received can be trusted.
2521 code = RXAFS_BulkStatus(rxconnp, &fidStruct,
2522 &statStruct, &callbackStruct, &volSync);
2524 rx_PutConnection(rxconnp);
2527 * If InlineBulk RPC was called and it succeeded,
2528 * then pull out the return code from the status info
2529 * and use it for cm_Analyze so that we can failover to other
2530 * .readonly volume instances. But only do it for errors that
2531 * are volume global.
2533 if (inlinebulk && code == 0 && (&bbp->stats[0])->errorCode) {
2534 osi_Log1(afsd_logp, "cm_TryBulkStat inline-bulk stat error: %d",
2535 (&bbp->stats[0])->errorCode);
2536 switch ((&bbp->stats[0])->errorCode) {
2545 code = (&bbp->stats[0])->errorCode;
2548 /* Rx and Rxkad errors are volume global */
2549 if ( (&bbp->stats[0])->errorCode >= -64 && (&bbp->stats[0])->errorCode < 0 ||
2550 (&bbp->stats[0])->errorCode >= ERROR_TABLE_BASE_RXK && (&bbp->stats[0])->errorCode < ERROR_TABLE_BASE_RXK + 256)
2551 code = (&bbp->stats[0])->errorCode;
2554 } while (cm_Analyze(connp, userp, reqp, &tfid, NULL, 0, &bbp->stats[0], &volSync, NULL, &cbReq, code));
2555 code = cm_MapRPCError(code, reqp);
2558 * might as well quit on an error, since we're not going to do
2559 * much better on the next immediate call, either.
2562 osi_Log2(afsd_logp, "CALL %sBulkStatus FAILURE code 0x%x",
2563 inlinebulk ? "Inline" : "", code);
2566 * Since an error occurred and it is impossible to determine
2567 * the context in which the returned error code should be
2568 * interpreted, we return the CM_ERROR_BULKSTAT_FAILURE error
2569 * which indicates that Bulk Stat cannot be used for the
2570 * current request. The caller should fallback to using
2571 * individual RXAFS_FetchStatus calls.
2573 code = CM_ERROR_BULKSTAT_FAILURE;
2575 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2580 * The bulk RPC has succeeded or at least not failed with a
2581 * volume global error result. For items that have inlineBulk
2582 * errors we must call cm_Analyze in order to perform required
2583 * logging of errors.
2585 * If the RPC was not inline bulk or the entry either has no error
2586 * the status must be merged.
2588 osi_Log1(afsd_logp, "CALL %sBulkStatus SUCCESS", inlinebulk ? "Inline" : "");
2590 for (i = 0; i<filesThisCall; i++) {
2592 cm_SetFid(&tfid, dscp->fid.cell, bbp->fids[j].Volume, bbp->fids[j].Vnode, bbp->fids[j].Unique);
2594 if (inlinebulk && (&bbp->stats[j])->errorCode) {
2595 cm_req_t treq = *reqp;
2596 cm_Analyze(NULL, userp, &treq, &tfid, NULL, 0, &bbp->stats[j], &volSync,
2597 NULL, &cbReq, (&bbp->stats[j])->errorCode);
2598 switch ((&bbp->stats[j])->errorCode) {
2603 cm_EAccesAddEntry(userp, &tfid, &dscp->fid);
2606 code = cm_GetSCache(&tfid, &dscp->fid, &scp, userp, reqp);
2611 * otherwise, if this entry has no callback info,
2612 * merge in this. If there is existing callback info
2613 * we skip the merge because the existing data must be
2614 * current (we have a callback) and the response from
2615 * a non-inline bulk rpc might actually be wrong.
2617 * now, we have to be extra paranoid on merging in this
2618 * information, since we didn't use cm_SyncOp before
2619 * starting the fetch to make sure that no bad races
2620 * were occurring. Specifically, we need to make sure
2621 * we don't obliterate any newer information in the
2622 * vnode than have here.
2624 * Right now, be pretty conservative: if there's a
2625 * callback or a pending call, skip it.
2626 * However, if the prior attempt to obtain status
2627 * was refused access or the volume is .readonly,
2628 * take the data in any case since we have nothing
2629 * better for the in flight directory enumeration that
2630 * resulted in this function being called.
2632 lock_ObtainRead(&scp->rw);
2633 if ((scp->cbServerp == NULL &&
2634 !(scp->flags & (CM_SCACHEFLAG_FETCHING | CM_SCACHEFLAG_STORING | CM_SCACHEFLAG_SIZESTORING))) ||
2635 (scp->flags & CM_SCACHEFLAG_PURERO) ||
2636 cm_EAccesFindEntry(userp, &scp->fid))
2638 lock_ConvertRToW(&scp->rw);
2639 lostRace = cm_EndCallbackGrantingCall(scp, &cbReq,
2642 CM_CALLBACK_MAINTAINCOUNT|CM_CALLBACK_BULKSTAT);
2643 InterlockedIncrement(&scp->activeRPCs);
2645 code = cm_MergeStatus(dscp, scp, &bbp->stats[j], &volSync, userp, reqp, CM_MERGEFLAG_BULKSTAT);
2646 lock_ReleaseWrite(&scp->rw);
2648 lock_ReleaseRead(&scp->rw);
2650 cm_ReleaseSCache(scp);
2652 } /* all files in the response */
2653 /* now tell it to drop the count,
2654 * after doing the vnode processing above */
2655 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2656 } /* while there are still more files to process */
2661 /* called with a write locked scp and a pointer to a buffer. Make bulk stat
2662 * calls on all undeleted files in the page of the directory specified.
2665 cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
2671 osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%p", dscp);
2673 /* should be on a buffer boundary */
2674 osi_assertx((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0, "invalid offset");
2676 bbp = malloc(sizeof(cm_bulkStat_t));
2677 memset(bbp, 0, sizeof(cm_bulkStat_t));
2679 bbp->bufOffset = *offsetp;
2681 lock_ReleaseWrite(&dscp->rw);
2682 /* first, assemble the file IDs we need to stat */
2683 code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) bbp, offsetp, userp, reqp, NULL);
2685 /* if we failed, bail out early */
2686 if (code && code != CM_ERROR_STOPNOW) {
2688 lock_ObtainWrite(&dscp->rw);
2692 code = cm_TryBulkStatRPC(dscp, bbp, userp, reqp);
2693 osi_Log1(afsd_logp, "END cm_TryBulkStat code 0x%x", code);
2695 lock_ObtainWrite(&dscp->rw);
2700 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
2704 /* initialize store back mask as inexpensive local variable */
2706 memset(statusp, 0, sizeof(AFSStoreStatus));
2708 /* copy out queued info from scache first, if scp passed in */
2710 if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
2711 statusp->ClientModTime = scp->clientModTime;
2712 mask |= AFS_SETMODTIME;
2713 scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
2718 /* now add in our locally generated request */
2719 if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
2720 statusp->ClientModTime = attrp->clientModTime;
2721 mask |= AFS_SETMODTIME;
2723 if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
2724 statusp->UnixModeBits = attrp->unixModeBits;
2725 mask |= AFS_SETMODE;
2727 if (attrp->mask & CM_ATTRMASK_OWNER) {
2728 statusp->Owner = attrp->owner;
2729 mask |= AFS_SETOWNER;
2731 if (attrp->mask & CM_ATTRMASK_GROUP) {
2732 statusp->Group = attrp->group;
2733 mask |= AFS_SETGROUP;
2736 statusp->Mask = mask;
2740 cm_IsSpaceAvailable(cm_fid_t * fidp, osi_hyper_t *sizep, cm_user_t *userp, cm_req_t *reqp)
2745 struct rx_connection * rxconnp;
2746 AFSFetchVolumeStatus volStat;
2747 cm_volume_t *volp = NULL;
2752 char volName[32]="(unknown)";
2753 char offLineMsg[256]="server temporarily inaccessible";
2754 char motd[256]="server temporarily inaccessible";
2755 osi_hyper_t freespace;
2759 if (fidp->cell==AFS_FAKE_ROOT_CELL_ID &&
2760 fidp->volume==AFS_FAKE_ROOT_VOL_ID)
2765 volp = cm_GetVolumeByFID(fidp);
2770 volType = cm_VolumeType(volp, fidp->volume);
2771 if (volType == ROVOL || volType == BACKVOL) {
2776 cm_SetFid(&vfid, fidp->cell, fidp->volume, 1, 1);
2777 code = cm_GetSCache(&vfid, NULL, &vscp, userp, reqp);
2779 lock_ObtainWrite(&vscp->rw);
2780 code = cm_SyncOp(vscp, NULL, userp, reqp, PRSFS_READ,
2781 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2782 lock_ReleaseWrite(&vscp->rw);
2785 OfflineMsg = offLineMsg;
2789 code = cm_ConnFromFID(&vfid, userp, reqp, &connp);
2792 rxconnp = cm_GetRxConn(connp);
2793 code = RXAFS_GetVolumeStatus(rxconnp, fidp->volume,
2794 &volStat, &Name, &OfflineMsg, &MOTD);
2795 rx_PutConnection(rxconnp);
2797 } while (cm_Analyze(connp, userp, reqp, &vfid, NULL, 0, NULL, NULL, NULL, NULL, code));
2798 code = cm_MapRPCError(code, reqp);
2801 lock_ObtainWrite(&vscp->rw);
2802 cm_SyncOpDone(vscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2803 lock_ReleaseWrite(&vscp->rw);
2804 cm_ReleaseSCache(vscp);
2808 if (volStat.MaxQuota) {
2809 freespace.QuadPart = 1024 * (afs_int64)min(volStat.MaxQuota - volStat.BlocksInUse, volStat.PartBlocksAvail);
2811 freespace.QuadPart = 1024 * (afs_int64)volStat.PartBlocksAvail;
2813 spaceAvail = LargeIntegerGreaterThanOrEqualTo(freespace, *sizep);
2815 /* the rpc failed, assume there is space and we can fail it later. */
2824 /* set the file size, and make sure that all relevant buffers have been
2825 * truncated. Ensure that any partially truncated buffers have been zeroed
2826 * to the end of the buffer.
2828 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
2835 /* start by locking out buffer creation */
2836 lock_ObtainWrite(&scp->bufCreateLock);
2838 /* verify that this is a file, not a dir or a symlink */
2839 lock_ObtainWrite(&scp->rw);
2840 code = cm_SyncOp(scp, NULL, userp, reqp, 0,
2841 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2844 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2846 if (scp->fileType != CM_SCACHETYPE_FILE) {
2847 code = CM_ERROR_ISDIR;
2852 if (LargeIntegerLessThan(*sizep, scp->length))
2857 lock_ReleaseWrite(&scp->rw);
2859 /* can't hold scp->rw lock here, since we may wait for a storeback to
2860 * finish if the buffer package is cleaning a buffer by storing it to
2864 buf_Truncate(scp, userp, reqp, sizep);
2866 /* now ensure that file length is short enough, and update truncPos */
2867 lock_ObtainWrite(&scp->rw);
2869 /* make sure we have a callback (so we have the right value for the
2870 * length), and wait for it to be safe to do a truncate.
2872 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
2873 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2874 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2876 /* If we only have 'i' bits, then we should still be able to set
2877 the size of a file we created. */
2878 if (code == CM_ERROR_NOACCESS && scp->creator == userp) {
2879 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
2880 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2881 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2887 if (LargeIntegerLessThan(*sizep, scp->length)) {
2888 /* a real truncation. If truncPos is not set yet, or is bigger
2889 * than where we're truncating the file, set truncPos to this
2894 if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
2895 || LargeIntegerLessThan(*sizep, scp->length)) {
2897 scp->truncPos = *sizep;
2898 scp->mask |= CM_SCACHEMASK_TRUNCPOS;
2900 /* in either case, the new file size has been changed */
2901 scp->length = *sizep;
2902 scp->mask |= CM_SCACHEMASK_LENGTH;
2904 else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
2906 * Really extending the file so must check to see if we
2907 * have sufficient quota. cm_IsSpaceAvailable() obtains
2908 * the cm_scache.rw lock on the volume root directory.
2909 * vnode 1 < scp->fid.vnode therefore calling cm_IsSpaceAvailable
2910 * while holding scp->rw is a lock order violation.
2911 * Dropping it is ok because we are holding scp->bufCreateLock
2912 * which prevents the size of the file from changing.
2914 afs_uint64 nextChunk = scp->length.QuadPart;
2916 nextChunk -= (nextChunk & 0xFFFFF);
2917 nextChunk += 0x100000;
2919 if (sizep->QuadPart > nextChunk) {
2920 lock_ReleaseWrite(&scp->rw);
2921 available = cm_IsSpaceAvailable(&scp->fid, sizep, userp, reqp);
2922 lock_ObtainWrite(&scp->rw);
2925 * The file server permits 1MB quota overruns so only check
2926 * when the file size increases by at least that much.
2931 scp->length = *sizep;
2932 scp->mask |= CM_SCACHEMASK_LENGTH;
2934 code = CM_ERROR_SPACE;
2939 /* done successfully */
2943 cm_SyncOpDone(scp, NULL,
2944 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2945 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2948 lock_ReleaseWrite(&scp->rw);
2949 lock_ReleaseWrite(&scp->bufCreateLock);
2954 /* set the file size or other attributes (but not both at once) */
2955 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
2959 AFSFetchStatus afsOutStatus;
2963 AFSStoreStatus afsInStatus;
2964 struct rx_connection * rxconnp;
2966 memset(&volSync, 0, sizeof(volSync));
2968 /* handle file length setting */
2969 if (attrp->mask & CM_ATTRMASK_LENGTH)
2970 return cm_SetLength(scp, &attrp->length, userp, reqp);
2972 lock_ObtainWrite(&scp->rw);
2973 /* Check for RO volume */
2974 if (scp->flags & CM_SCACHEFLAG_RO) {
2975 code = CM_ERROR_READONLY;
2976 lock_ReleaseWrite(&scp->rw);
2980 /* otherwise, we have to make an RPC to get the status */
2981 code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
2983 lock_ReleaseWrite(&scp->rw);
2986 lock_ConvertWToR(&scp->rw);
2988 /* make the attr structure */
2989 cm_StatusFromAttr(&afsInStatus, scp, attrp);
2991 tfid.Volume = scp->fid.volume;
2992 tfid.Vnode = scp->fid.vnode;
2993 tfid.Unique = scp->fid.unique;
2994 lock_ReleaseRead(&scp->rw);
2996 /* now make the RPC */
2997 InterlockedIncrement(&scp->activeRPCs);
2999 osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%p", scp);
3001 code = cm_ConnFromFID(&scp->fid, userp, reqp, &connp);
3005 rxconnp = cm_GetRxConn(connp);
3006 code = RXAFS_StoreStatus(rxconnp, &tfid,
3007 &afsInStatus, &afsOutStatus, &volSync);
3008 rx_PutConnection(rxconnp);
3010 } while (cm_Analyze(connp, userp, reqp,
3011 &scp->fid, NULL, 1, &afsOutStatus, &volSync, NULL, NULL, code));
3012 code = cm_MapRPCError(code, reqp);
3015 osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
3017 osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
3019 lock_ObtainWrite(&scp->rw);
3021 code = cm_MergeStatus( NULL, scp, &afsOutStatus, &volSync, userp, reqp,
3022 CM_MERGEFLAG_FORCE|CM_MERGEFLAG_STOREDATA);
3024 InterlockedDecrement(&scp->activeRPCs);
3025 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
3027 /* if we're changing the mode bits, discard the ACL cache,
3028 * since we changed the mode bits.
3030 if (afsInStatus.Mask & AFS_SETMODE)
3031 cm_FreeAllACLEnts(scp);
3032 lock_ReleaseWrite(&scp->rw);
3036 long cm_Create(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
3037 cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
3042 cm_callbackRequest_t cbReq;
3045 cm_scache_t *scp = NULL;
3048 AFSStoreStatus inStatus;
3049 AFSFetchStatus updatedDirStatus;
3050 AFSFetchStatus newFileStatus;
3051 AFSCallBack newFileCallback;
3053 struct rx_connection * rxconnp;
3055 fschar_t * fnamep = NULL;
3057 memset(&volSync, 0, sizeof(volSync));
3059 /* can't create names with @sys in them; must expand it manually first.
3060 * return "invalid request" if they try.
3062 if (cm_ExpandSysName(NULL, cnamep, NULL, 0, 0)) {
3063 return CM_ERROR_ATSYS;
3066 #ifdef AFS_FREELANCE_CLIENT
3067 /* Freelance root volume does not hold files */
3068 if (cm_freelanceEnabled &&
3069 dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
3070 dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
3072 return CM_ERROR_NOACCESS;
3074 #endif /* AFS_FREELANCE_CLIENT */
3076 /* Check for RO volume */
3077 if (dscp->flags & CM_SCACHEFLAG_RO)
3078 return CM_ERROR_READONLY;
3080 /* before starting the RPC, mark that we're changing the file data, so
3081 * that someone who does a chmod will know to wait until our call
3084 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3086 lock_ObtainWrite(&dscp->rw);
3087 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3088 lock_ReleaseWrite(&dscp->rw);
3090 cm_StartCallbackGrantingCall(NULL, &cbReq);
3092 cm_EndDirOp(&dirop);
3099 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3101 cm_StatusFromAttr(&inStatus, NULL, attrp);
3103 /* try the RPC now */
3104 InterlockedIncrement(&dscp->activeRPCs);
3105 osi_Log1(afsd_logp, "CALL CreateFile scp 0x%p", dscp);
3107 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3111 dirAFSFid.Volume = dscp->fid.volume;
3112 dirAFSFid.Vnode = dscp->fid.vnode;
3113 dirAFSFid.Unique = dscp->fid.unique;
3115 rxconnp = cm_GetRxConn(connp);
3116 code = RXAFS_CreateFile(connp->rxconnp, &dirAFSFid, fnamep,
3117 &inStatus, &newAFSFid, &newFileStatus,
3118 &updatedDirStatus, &newFileCallback,
3120 rx_PutConnection(rxconnp);
3122 } while (cm_Analyze(connp, userp, reqp,
3123 &dscp->fid, NULL, 1, &updatedDirStatus, &volSync, NULL, &cbReq, code));
3124 code = cm_MapRPCError(code, reqp);
3127 osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
3129 osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
3132 lock_ObtainWrite(&dirop.scp->dirlock);
3133 dirop.lockType = CM_DIRLOCK_WRITE;
3135 lock_ObtainWrite(&dscp->rw);
3137 code = cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3138 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3139 if (cm_CheckDirOpForSingleChange(&dirop)) {
3140 lock_ReleaseWrite(&dscp->rw);
3141 cm_DirCreateEntry(&dirop, fnamep, &newFid);
3143 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
3145 lock_ObtainWrite(&dscp->rw);
3148 InterlockedDecrement(&dscp->activeRPCs);
3150 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3151 lock_ReleaseWrite(&dscp->rw);
3153 /* now try to create the file's entry, too, but be careful to
3154 * make sure that we don't merge in old info. Since we weren't locking
3155 * out any requests during the file's creation, we may have pretty old
3159 code = cm_GetSCache(&newFid, &dscp->fid, &scp, userp, reqp);
3161 lock_ObtainWrite(&scp->rw);
3162 scp->creator = userp; /* remember who created it */
3163 if (!cm_HaveCallback(scp)) {
3164 lostRace = cm_EndCallbackGrantingCall(scp, &cbReq,
3165 &newFileCallback, &volSync, 0);
3166 InterlockedIncrement(&scp->activeRPCs);
3168 code = cm_MergeStatus( dscp, scp, &newFileStatus, &volSync,
3172 lock_ReleaseWrite(&scp->rw);
3176 /* make sure we end things properly */
3178 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
3180 cm_EndDirOp(&dirop);
3189 cm_ReleaseSCache(scp);
3195 * locked if TRUE means write-locked
3196 * else the cm_scache_t rw must not be held
3198 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp, afs_uint32 locked)
3203 lock_ReleaseWrite(&scp->rw);
3205 osi_Log2(afsd_logp, "cm_FSync scp 0x%p userp 0x%p", scp, userp);
3207 code = buf_CleanVnode(scp, userp, reqp);
3209 lock_ObtainWrite(&scp->rw);
3211 if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
3212 | CM_SCACHEMASK_CLIENTMODTIME
3213 | CM_SCACHEMASK_LENGTH))
3214 code = cm_StoreMini(scp, userp, reqp);
3216 if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) {
3217 code = (scp->flags & CM_SCACHEFLAG_OVERQUOTA) ? CM_ERROR_QUOTA : CM_ERROR_SPACE;
3218 scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE);
3222 lock_ReleaseWrite(&scp->rw);
3223 } else if (locked) {
3224 lock_ObtainWrite(&scp->rw);
3229 long cm_MakeDir(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
3230 cm_user_t *userp, cm_req_t *reqp, cm_scache_t **scpp)
3235 cm_callbackRequest_t cbReq;
3238 cm_scache_t *scp = NULL;
3241 AFSStoreStatus inStatus;
3242 AFSFetchStatus updatedDirStatus;
3243 AFSFetchStatus newDirStatus;
3244 AFSCallBack newDirCallback;
3246 struct rx_connection * rxconnp;
3248 fschar_t * fnamep = NULL;
3250 memset(&volSync, 0, sizeof(volSync));
3252 /* can't create names with @sys in them; must expand it manually first.
3253 * return "invalid request" if they try.
3255 if (cm_ExpandSysName(NULL, cnamep, NULL, 0, 0)) {
3256 return CM_ERROR_ATSYS;
3259 #ifdef AFS_FREELANCE_CLIENT
3260 /* Freelance root volume does not hold subdirectories */
3261 if (cm_freelanceEnabled &&
3262 dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
3263 dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
3265 return CM_ERROR_NOACCESS;
3267 #endif /* AFS_FREELANCE_CLIENT */
3269 /* Check for RO volume */
3270 if (dscp->flags & CM_SCACHEFLAG_RO)
3271 return CM_ERROR_READONLY;
3273 /* before starting the RPC, mark that we're changing the directory
3274 * data, so that someone who does a chmod on the dir will wait until
3275 * our call completes.
3277 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3279 lock_ObtainWrite(&dscp->rw);
3280 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3281 lock_ReleaseWrite(&dscp->rw);
3283 cm_StartCallbackGrantingCall(NULL, &cbReq);
3285 cm_EndDirOp(&dirop);
3292 fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3293 cm_StatusFromAttr(&inStatus, NULL, attrp);
3295 /* try the RPC now */
3296 InterlockedIncrement(&dscp->activeRPCs);
3297 osi_Log1(afsd_logp, "CALL MakeDir scp 0x%p", dscp);
3299 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3303 dirAFSFid.Volume = dscp->fid.volume;
3304 dirAFSFid.Vnode = dscp->fid.vnode;
3305 dirAFSFid.Unique = dscp->fid.unique;
3307 rxconnp = cm_GetRxConn(connp);
3308 code = RXAFS_MakeDir(connp->rxconnp, &dirAFSFid, fnamep,
3309 &inStatus, &newAFSFid, &newDirStatus,
3310 &updatedDirStatus, &newDirCallback,
3312 rx_PutConnection(rxconnp);
3314 } while (cm_Analyze(connp, userp, reqp,
3315 &dscp->fid, NULL, 1, &updatedDirStatus, &volSync, NULL, &cbReq, code));
3316 code = cm_MapRPCError(code, reqp);
3319 osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
3321 osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
3324 lock_ObtainWrite(&dirop.scp->dirlock);
3325 dirop.lockType = CM_DIRLOCK_WRITE;
3327 lock_ObtainWrite(&dscp->rw);
3329 code = cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3330 cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3331 if (cm_CheckDirOpForSingleChange(&dirop)) {
3332 lock_ReleaseWrite(&dscp->rw);
3333 cm_DirCreateEntry(&dirop, fnamep, &newFid);
3335 cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
3337 lock_ObtainWrite(&dscp->rw);
3340 InterlockedDecrement(&dscp->activeRPCs);
3342 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3343 lock_ReleaseWrite(&dscp->rw);
3345 /* now try to create the new dir's entry, too, but be careful to
3346 * make sure that we don't merge in old info. Since we weren't locking
3347 * out any requests during the file's creation, we may have pretty old
3351 code = cm_GetSCache(&newFid, &dscp->fid, &scp, userp, reqp);
3353 lock_ObtainWrite(&scp->rw);
3354 if (!cm_HaveCallback(scp)) {
3355 lostRace = cm_EndCallbackGrantingCall(scp, &cbReq,
3356 &newDirCallback, &volSync, 0);
3357 InterlockedIncrement(&scp->activeRPCs);
3359 code = cm_MergeStatus( dscp, scp, &newDirStatus, &volSync,
3363 lock_ReleaseWrite(&scp->rw);
3367 /* make sure we end things properly */
3369 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
3371 cm_EndDirOp(&dirop);
3379 cm_ReleaseSCache(scp);
3382 /* and return error code */
3386 long cm_Link(cm_scache_t *dscp, clientchar_t *cnamep, cm_scache_t *sscp, long flags,
3387 cm_user_t *userp, cm_req_t *reqp)
3392 AFSFid existingAFSFid;
3393 AFSFetchStatus updatedDirStatus;
3394 AFSFetchStatus newLinkStatus;
3396 struct rx_connection * rxconnp;
3398 fschar_t * fnamep = NULL;
3401 memset(&volSync, 0, sizeof(volSync));
3403 if (dscp->fid.cell != sscp->fid.cell ||
3404 dscp->fid.volume != sscp->fid.volume) {
3405 return CM_ERROR_CROSSDEVLINK;
3408 /* Check for RO volume */
3409 if (dscp->flags & CM_SCACHEFLAG_RO)
3410 return CM_ERROR_READONLY;
3412 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3414 lock_ObtainWrite(&dscp->rw);
3415 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);