2 * Copyright 2000, International Business Machines Corporation and others.
5 * This software has been released under the terms of the IBM Public
6 * License. For details, see the LICENSE file in the top-level source
7 * directory or online at http://www.openafs.org/dl/license10.html
10 #include <afs/param.h>
26 /* Used by cm_FollowMountPoint */
32 extern void afsi_log(char *pattern, ...);
36 * Case-folding array. This was constructed by inspecting of SMBtrace output.
37 * I do not know anything more about it.
39 unsigned char cm_foldUpper[256] = {
40 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
41 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
42 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
43 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
44 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
45 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
46 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
47 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
48 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
49 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
50 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
51 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
52 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
53 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
54 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
55 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
56 0x80, 0x9a, 0x90, 0x41, 0x8e, 0x41, 0x8f, 0x80,
57 0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x8e, 0x8f,
58 0x90, 0x92, 0x92, 0x4f, 0x99, 0x4f, 0x55, 0x55,
59 0x59, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
60 0x41, 0x49, 0x4f, 0x55, 0xa5, 0xa5, 0x56, 0xa7,
61 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
62 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
63 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
64 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
65 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
66 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
67 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
68 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
69 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
70 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
71 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
75 * Case-insensitive string comparison. We used to use stricmp, but it doesn't
76 * know about 8-bit characters (e.g. 129 is lowercase u-umlaut, 154 is
77 * upper-case u-umlaut).
79 int cm_stricmp(const char *str1, const char *str2)
91 c1 = (char) cm_foldUpper[(unsigned char)(*str1++)];
92 c2 = (char) cm_foldUpper[(unsigned char)(*str2++)];
100 /* characters that are legal in an 8.3 name */
102 * We used to have 1's for all characters from 128 to 254. But
103 * the NT client behaves better if we create an 8.3 name for any
104 * name that has a character with the high bit on, and if we
105 * delete those characters from 8.3 names. In particular, see
106 * Sybase defect 10859.
108 char cm_LegalChars[256] = {
109 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
110 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
111 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0,
112 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
113 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
114 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1,
115 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
116 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1,
117 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
118 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
119 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
120 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
121 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
122 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
123 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
124 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
127 /* return true iff component is a valid 8.3 name */
128 int cm_Is8Dot3(char *namep)
131 int sawUpper = 0, sawLower = 0;
136 * can't have a leading dot;
137 * special case for . and ..
139 if (namep[0] == '.') {
142 if (namep[1] == '.' && namep[2] == 0)
146 while (tc = *namep++) {
148 /* saw another dot */
149 if (sawDot) return 0; /* second dot */
154 if (cm_LegalChars[tc] == 0)
156 if (tc >= 'A' && tc <= 'Z')
158 else if (tc >= 'a' && tc <= 'z')
161 if (!sawDot && charCount > 8)
162 /* more than 8 chars in name */
164 if (sawDot && charCount > 3)
165 /* more than 3 chars in extension */
169 * Used to check that all characters were the same case.
170 * This doesn't help 16-bit apps, and meanwhile it causes the
171 * MS-DOS Command Prompt to misbehave; see Sybase defect 10709.
173 if (sawUpper && sawLower)
180 * Number unparsing map for generating 8.3 names;
181 * The version taken from DFS was on drugs.
182 * You can't include '&' and '@' in a file name.
184 char cm_8Dot3Mapping[42] =
185 {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
186 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K',
187 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U',
188 'V', 'W', 'X', 'Y', 'Z', '_', '-', '$', '#', '!', '+', '='
190 int cm_8Dot3MapSize = sizeof(cm_8Dot3Mapping);
192 void cm_Gen8Dot3Name(cm_dirEntry_t *dep, char *shortName, char **shortNameEndp)
196 int vnode = ntohl(dep->fid.vnode);
198 int validExtension = 0;
199 char tc, *temp, *name;
201 /* Unparse the file's vnode number to get a "uniquifier" */
203 number[nsize] = cm_8Dot3Mapping[vnode % cm_8Dot3MapSize];
205 vnode /= cm_8Dot3MapSize;
209 * Look for valid extension. There has to be a dot, and
210 * at least one of the characters following has to be legal.
212 lastDot = strrchr(dep->name, '.');
214 temp = lastDot; temp++;
216 if (cm_LegalChars[tc])
222 /* Copy name characters */
224 for (i = 0, name = dep->name;
225 i < (7 - nsize) && name != lastDot; ) {
230 if (!cm_LegalChars[tc])
233 *shortName++ = toupper(tc);
239 /* Copy uniquifier characters */
240 memcpy(shortName, number, nsize);
243 if (validExtension) {
244 /* Copy extension characters */
245 *shortName++ = *lastDot++; /* copy dot */
246 for (i = 0, tc = *lastDot++;
249 if (cm_LegalChars[tc]) {
251 *shortName++ = toupper(tc);
260 *shortNameEndp = shortName;
263 /* return success if we can open this file in this mode */
264 long cm_CheckOpen(cm_scache_t *scp, int openMode, int trunc, cm_user_t *userp,
271 if (openMode != 1) rights |= PRSFS_READ;
272 if (openMode == 1 || openMode == 2 || trunc) rights |= PRSFS_WRITE;
274 lock_ObtainMutex(&scp->mx);
276 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
277 CM_SCACHESYNC_GETSTATUS
278 | CM_SCACHESYNC_NEEDCALLBACK);
279 lock_ReleaseMutex(&scp->mx);
284 /* return success if we can open this file in this mode */
285 long cm_CheckNTOpen(cm_scache_t *scp, unsigned int desiredAccess,
286 unsigned int createDisp, cm_user_t *userp, cm_req_t *reqp)
291 /* Always allow delete; the RPC will tell us if it's OK */
292 if (desiredAccess == DELETE)
297 if (desiredAccess & AFS_ACCESS_READ)
298 rights |= PRSFS_READ;
300 if ((desiredAccess & AFS_ACCESS_WRITE)
302 rights |= PRSFS_WRITE;
304 lock_ObtainMutex(&scp->mx);
306 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
307 CM_SCACHESYNC_GETSTATUS
308 | CM_SCACHESYNC_NEEDCALLBACK);
309 lock_ReleaseMutex(&scp->mx);
312 * If the open will fail because the volume is readonly, then we will
313 * return an access denied error instead. This is to help brain-dead
314 * apps run correctly on replicated volumes.
315 * See defect 10007 for more information.
317 if (code == CM_ERROR_READONLY)
318 code = CM_ERROR_NOACCESS;
324 * When CAP_NT_SMBS has been negotiated, deletion (of files or directories) is
325 * done in three steps:
326 * (1) open for deletion (NT_CREATE_AND_X)
327 * (2) set for deletion on close (NTWTRANSACTION2, SET_FILE_INFO)
329 * We must not do the RPC until step 3. But if we are going to return an error
330 * code (e.g. directory not empty), we must return it by step 2, otherwise most
331 * clients will not notice it. So we do a preliminary check. For deleting
332 * files, this is almost free, since we have already done the RPC to get the
333 * parent directory's status bits. But for deleting directories, we must do an
334 * additional RPC to get the directory's data to check if it is empty. Sigh.
336 long cm_CheckNTDelete(cm_scache_t *dscp, cm_scache_t *scp, cm_user_t *userp,
343 unsigned short *hashTable;
345 int BeyondPage = 0, HaveDot = 0, HaveDotDot = 0;
347 /* First check permissions */
348 lock_ObtainMutex(&dscp->mx);
349 code = cm_SyncOp(dscp, NULL, userp, reqp, PRSFS_DELETE,
350 CM_SCACHESYNC_GETSTATUS
351 | CM_SCACHESYNC_NEEDCALLBACK);
352 lock_ReleaseMutex(&dscp->mx);
356 /* If deleting directory, must be empty */
358 if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
361 thyper.HighPart = 0; thyper.LowPart = 0;
362 lock_ObtainRead(&scp->bufCreateLock);
363 code = buf_Get(scp, &thyper, &bufferp);
364 lock_ReleaseRead(&scp->bufCreateLock);
368 lock_ObtainMutex(&bufferp->mx);
369 lock_ObtainMutex(&scp->mx);
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_ReleaseMutex(&scp->mx);
385 lock_ObtainMutex(&bufferp->mx);
386 lock_ObtainMutex(&scp->mx);
391 /* We try to determine emptiness without looking beyond the first page,
392 * and without assuming "." and ".." are present and are on the first
393 * page (though these assumptions might, after all, be reasonable).
395 hashTable = (unsigned short *)(bufferp->datap + (32 * 5));
396 for (i=0; i<128; i++) {
397 idx = ntohs(hashTable[i]);
403 dep = (cm_dirEntry_t *)(bufferp->datap + (32 * idx));
404 if (strcmp(dep->name, ".") == 0)
406 else if (strcmp(dep->name, "..") == 0)
409 code = CM_ERROR_NOTEMPTY;
412 idx = ntohs(dep->next);
415 if (BeyondPage && HaveDot && HaveDotDot)
416 code = CM_ERROR_NOTEMPTY;
420 lock_ReleaseMutex(&bufferp->mx);
421 buf_Release(bufferp);
422 lock_ReleaseMutex(&scp->mx);
427 * Iterate through all entries in a directory.
428 * When the function funcp is called, the buffer is locked but the
429 * directory vnode is not.
431 * If the retscp parameter is not NULL, the parmp must be a
432 * cm_lookupSearch_t object.
434 long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
435 osi_hyper_t *startOffsetp, cm_user_t *userp, cm_req_t *reqp,
436 cm_scache_t **retscp)
443 osi_hyper_t dirLength;
444 osi_hyper_t bufferOffset;
445 osi_hyper_t curOffset;
449 cm_pageHeader_t *pageHeaderp;
451 long nextEntryCookie;
452 int numDirChunks; /* # of 32 byte dir chunks in this entry */
454 /* get the directory size */
455 lock_ObtainMutex(&scp->mx);
456 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_LOOKUP,
457 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
459 lock_ReleaseMutex(&scp->mx);
463 if (scp->fileType != CM_SCACHETYPE_DIRECTORY) {
464 lock_ReleaseMutex(&scp->mx);
465 return CM_ERROR_NOTDIR;
468 if (retscp) /* if this is a lookup call */
470 cm_lookupSearch_t* sp = parmp;
472 #ifdef AFS_FREELANCE_CLIENT
473 /* Freelance entries never end up in the DNLC because they
474 * do not have an associated cm_server_t
476 if ( !(cm_freelanceEnabled &&
477 sp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
478 sp->fid.volume==AFS_FAKE_ROOT_VOL_ID ) )
479 #endif /* AFS_FREELANCE_CLIENT */
481 int casefold = sp->caseFold;
482 sp->caseFold = 0; /* we have a strong preference for exact matches */
483 if ( *retscp = cm_dnlcLookup(scp, sp)) /* dnlc hit */
485 sp->caseFold = casefold;
486 lock_ReleaseMutex(&scp->mx);
489 sp->caseFold = casefold;
494 * XXX We only get the length once. It might change when we drop the
497 dirLength = scp->length;
499 lock_ReleaseMutex(&scp->mx);
502 bufferOffset.LowPart = bufferOffset.HighPart = 0;
504 curOffset = *startOffsetp;
506 curOffset.HighPart = 0;
507 curOffset.LowPart = 0;
511 /* make sure that curOffset.LowPart doesn't point to the first
512 * 32 bytes in the 2nd through last dir page, and that it
513 * doesn't point at the first 13 32-byte chunks in the first
514 * dir page, since those are dir and page headers, and don't
515 * contain useful information.
517 temp = curOffset.LowPart & (2048-1);
518 if (curOffset.HighPart == 0 && curOffset.LowPart < 2048) {
519 /* we're in the first page */
520 if (temp < 13*32) temp = 13*32;
523 /* we're in a later dir page */
524 if (temp < 32) temp = 32;
527 /* make sure the low order 5 bits are zero */
530 /* now put temp bits back ito curOffset.LowPart */
531 curOffset.LowPart &= ~(2048-1);
532 curOffset.LowPart |= temp;
534 /* check if we've passed the dir's EOF */
535 if (LargeIntegerGreaterThanOrEqualTo(curOffset, dirLength))
538 /* see if we can use the bufferp we have now; compute in which
539 * page the current offset would be, and check whether that's
540 * the offset of the buffer we have. If not, get the buffer.
542 thyper.HighPart = curOffset.HighPart;
543 thyper.LowPart = curOffset.LowPart & ~(cm_data.buf_blockSize-1);
544 if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
547 lock_ReleaseMutex(&bufferp->mx);
548 buf_Release(bufferp);
552 lock_ObtainRead(&scp->bufCreateLock);
553 code = buf_Get(scp, &thyper, &bufferp);
554 lock_ReleaseRead(&scp->bufCreateLock);
556 /* if buf_Get() fails we do not have a buffer object to lock */
561 lock_ObtainMutex(&bufferp->mx);
562 bufferOffset = thyper;
564 /* now get the data in the cache */
566 lock_ObtainMutex(&scp->mx);
567 code = cm_SyncOp(scp, bufferp, userp, reqp,
569 CM_SCACHESYNC_NEEDCALLBACK
571 | CM_SCACHESYNC_BUFLOCKED);
573 lock_ReleaseMutex(&scp->mx);
577 if (cm_HaveBuffer(scp, bufferp, 1)) {
578 lock_ReleaseMutex(&scp->mx);
582 /* otherwise, load the buffer and try again */
583 lock_ReleaseMutex(&bufferp->mx);
584 code = cm_GetBuffer(scp, bufferp, NULL, userp,
586 lock_ReleaseMutex(&scp->mx);
587 lock_ObtainMutex(&bufferp->mx);
592 lock_ReleaseMutex(&bufferp->mx);
593 buf_Release(bufferp);
597 } /* if (wrong buffer) ... */
599 /* now we have the buffer containing the entry we're interested
600 * in; copy it out if it represents a non-deleted entry.
602 entryInDir = curOffset.LowPart & (2048-1);
603 entryInBuffer = curOffset.LowPart & (cm_data.buf_blockSize - 1);
605 /* page header will help tell us which entries are free. Page
606 * header can change more often than once per buffer, since
607 * AFS 3 dir page size may be less than (but not more than) a
608 * buffer package buffer.
610 /* only look intra-buffer */
611 temp = curOffset.LowPart & (cm_data.buf_blockSize - 1);
612 temp &= ~(2048 - 1); /* turn off intra-page bits */
613 pageHeaderp = (cm_pageHeader_t *) (bufferp->datap + temp);
615 /* now determine which entry we're looking at in the page. If
616 * it is free (there's a free bitmap at the start of the dir),
617 * we should skip these 32 bytes.
619 slotInPage = (entryInDir & 0x7e0) >> 5;
620 if (!(pageHeaderp->freeBitmap[slotInPage>>3]
621 & (1 << (slotInPage & 0x7)))) {
622 /* this entry is free */
623 numDirChunks = 1; /* only skip this guy */
627 tp = bufferp->datap + entryInBuffer;
628 dep = (cm_dirEntry_t *) tp; /* now points to AFS3 dir entry */
630 /* while we're here, compute the next entry's location, too,
631 * since we'll need it when writing out the cookie into the
632 * dir listing stream.
634 numDirChunks = cm_NameEntries(dep->name, NULL);
636 /* compute the offset of the cookie representing the next entry */
637 nextEntryCookie = curOffset.LowPart
638 + (CM_DIR_CHUNKSIZE * numDirChunks);
640 if (dep->fid.vnode != 0) {
641 /* this is one of the entries to use: it is not deleted */
642 code = (*funcp)(scp, dep, parmp, &curOffset);
645 } /* if we're including this name */
648 /* and adjust curOffset to be where the new cookie is */
650 thyper.LowPart = CM_DIR_CHUNKSIZE * numDirChunks;
651 curOffset = LargeIntegerAdd(thyper, curOffset);
652 } /* while copying data for dir listing */
654 /* release the mutex */
656 lock_ReleaseMutex(&bufferp->mx);
657 buf_Release(bufferp);
662 int cm_NoneUpper(char *s)
666 if (c >= 'A' && c <= 'Z')
671 int cm_NoneLower(char *s)
675 if (c >= 'a' && c <= 'z')
680 long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
683 cm_lookupSearch_t *sp;
688 sp = (cm_lookupSearch_t *) rockp;
690 matchName = dep->name;
692 match = cm_stricmp(matchName, sp->searchNamep);
694 match = strcmp(matchName, sp->searchNamep);
698 && !cm_Is8Dot3(dep->name)) {
699 matchName = shortName;
700 cm_Gen8Dot3Name(dep, shortName, NULL);
702 match = cm_stricmp(matchName, sp->searchNamep);
704 match = strcmp(matchName, sp->searchNamep);
714 if (!sp->caseFold || matchName == shortName) {
715 sp->fid.vnode = ntohl(dep->fid.vnode);
716 sp->fid.unique = ntohl(dep->fid.unique);
717 return CM_ERROR_STOPNOW;
721 * If we get here, we are doing a case-insensitive search, and we
722 * have found a match. Now we determine what kind of match it is:
723 * exact, lower-case, upper-case, or none of the above. This is done
724 * in order to choose among matches, if there are more than one.
727 /* Exact matches are the best. */
728 match = strcmp(matchName, sp->searchNamep);
731 sp->fid.vnode = ntohl(dep->fid.vnode);
732 sp->fid.unique = ntohl(dep->fid.unique);
733 return CM_ERROR_STOPNOW;
736 /* Lower-case matches are next. */
739 if (cm_NoneUpper(matchName)) {
744 /* Upper-case matches are next. */
747 if (cm_NoneLower(matchName)) {
752 /* General matches are last. */
758 sp->fid.vnode = ntohl(dep->fid.vnode);
759 sp->fid.unique = ntohl(dep->fid.unique);
763 /* read the contents of a mount point into the appropriate string.
764 * called with locked scp, and returns with locked scp.
766 long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
773 if (scp->mountPointStringp[0])
776 /* otherwise, we have to read it in */
777 lock_ReleaseMutex(&scp->mx);
779 lock_ObtainRead(&scp->bufCreateLock);
780 thyper.LowPart = thyper.HighPart = 0;
781 code = buf_Get(scp, &thyper, &bufp);
782 lock_ReleaseRead(&scp->bufCreateLock);
784 lock_ObtainMutex(&scp->mx);
789 code = cm_SyncOp(scp, bufp, userp, reqp, 0,
790 CM_SCACHESYNC_READ | CM_SCACHESYNC_NEEDCALLBACK);
795 if (cm_HaveBuffer(scp, bufp, 0))
798 /* otherwise load buffer */
799 code = cm_GetBuffer(scp, bufp, NULL, userp, reqp);
804 /* locked, has callback, has valid data in buffer */
805 if ((tlen = scp->length.LowPart) > 1000)
806 return CM_ERROR_TOOBIG;
808 code = CM_ERROR_INVAL;
812 /* someone else did the work while we were out */
813 if (scp->mountPointStringp[0]) {
818 /* otherwise, copy out the link */
819 memcpy(scp->mountPointStringp, bufp->datap, tlen);
821 /* now make it null-terminated. Note that the original contents of a
822 * link that is a mount point is "#volname." where "." is there just to
823 * be turned into a null. That is, we can trash the last char of the
824 * link without damaging the vol name. This is a stupid convention,
825 * but that's the protocol.
827 scp->mountPointStringp[tlen-1] = 0;
836 /* called with a locked scp and chases the mount point, yielding outScpp.
837 * scp remains locked, just for simplicity of describing the interface.
839 long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
840 cm_req_t *reqp, cm_scache_t **outScpp)
855 if (scp->mountRootFid.cell != 0 && scp->mountRootGen >= cm_data.mountRootGen) {
856 tfid = scp->mountRootFid;
857 lock_ReleaseMutex(&scp->mx);
858 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
859 lock_ObtainMutex(&scp->mx);
863 /* parse the volume name */
864 mpNamep = scp->mountPointStringp;
865 osi_assert(mpNamep[0]);
866 tlen = strlen(scp->mountPointStringp);
867 mtType = *scp->mountPointStringp;
868 cellNamep = malloc(tlen);
869 volNamep = malloc(tlen);
871 cp = strrchr(mpNamep, ':');
873 /* cellular mount point */
874 memset(cellNamep, 0, tlen);
875 strncpy(cellNamep, mpNamep+1, cp - mpNamep - 1);
876 strcpy(volNamep, cp+1);
877 /* now look up the cell */
878 cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE);
882 strcpy(volNamep, mpNamep+1);
884 cellp = cm_FindCellByID(scp->fid.cell);
888 code = CM_ERROR_NOSUCHCELL;
892 vnLength = strlen(volNamep);
893 if (vnLength >= 8 && strcmp(volNamep + vnLength - 7, ".backup") == 0)
895 else if (vnLength >= 10
896 && strcmp(volNamep + vnLength - 9, ".readonly") == 0)
901 /* check for backups within backups */
903 && (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
904 == CM_SCACHEFLAG_RO) {
905 code = CM_ERROR_NOSUCHVOLUME;
909 /* now we need to get the volume */
910 lock_ReleaseMutex(&scp->mx);
911 code = cm_GetVolumeByName(cellp, volNamep, userp, reqp, 0, &volp);
912 lock_ObtainMutex(&scp->mx);
915 /* save the parent of the volume root for this is the
916 * place where the volume is mounted and we must remember
917 * this in the volume structure rather than just in the
918 * scache entry lest the scache entry gets recycled
921 lock_ObtainMutex(&volp->mx);
922 volp->dotdotFid = dscp->fid;
923 lock_ReleaseMutex(&volp->mx);
925 scp->mountRootFid.cell = cellp->cellID;
926 /* if the mt pt is in a read-only volume (not just a
927 * backup), and if there is a read-only volume for the
928 * target, and if this is a type '#' mount point, use
929 * the read-only, otherwise use the one specified.
931 if (mtType == '#' && (scp->flags & CM_SCACHEFLAG_PURERO)
932 && volp->roID != 0 && type == RWVOL)
935 scp->mountRootFid.volume = volp->roID;
936 else if (type == BACKVOL)
937 scp->mountRootFid.volume = volp->bkID;
939 scp->mountRootFid.volume = volp->rwID;
941 /* the rest of the fid is a magic number */
942 scp->mountRootFid.vnode = 1;
943 scp->mountRootFid.unique = 1;
944 scp->mountRootGen = cm_data.mountRootGen;
946 tfid = scp->mountRootFid;
947 lock_ReleaseMutex(&scp->mx);
948 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
949 lock_ObtainMutex(&scp->mx);
958 long cm_LookupInternal(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
959 cm_req_t *reqp, cm_scache_t **outpScpp)
962 int dnlcHit = 1; /* did we hit in the dnlc? yes, we did */
963 cm_scache_t *tscp = NULL;
964 cm_scache_t *mountedScp;
965 cm_lookupSearch_t rock;
968 if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
969 && strcmp(namep, "..") == 0) {
970 if (dscp->dotdotFid.volume == 0)
971 return CM_ERROR_NOSUCHVOLUME;
972 rock.fid = dscp->dotdotFid;
976 memset(&rock, 0, sizeof(rock));
977 rock.fid.cell = dscp->fid.cell;
978 rock.fid.volume = dscp->fid.volume;
979 rock.searchNamep = namep;
980 rock.caseFold = (flags & CM_FLAG_CASEFOLD);
981 rock.hasTilde = ((strchr(namep, '~') != NULL) ? 1 : 0);
983 /* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
984 code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
985 (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
987 /* code == 0 means we fell off the end of the dir, while stopnow means
988 * that we stopped early, probably because we found the entry we're
989 * looking for. Any other non-zero code is an error.
991 if (code && code != CM_ERROR_STOPNOW) {
992 /* if the cm_scache_t we are searching in is not a directory
993 * we must return path not found because the error
994 * is to describe the final component not an intermediary
996 if (code == CM_ERROR_NOTDIR) {
997 if (flags & CM_FLAG_CHECKPATH)
998 return CM_ERROR_NOSUCHPATH;
1000 return CM_ERROR_NOSUCHFILE;
1005 getroot = (dscp==cm_data.rootSCachep) ;
1007 if (!cm_freelanceEnabled || !getroot) {
1008 if (flags & CM_FLAG_CHECKPATH)
1009 return CM_ERROR_NOSUCHPATH;
1011 return CM_ERROR_NOSUCHFILE;
1013 else { /* nonexistent dir on freelance root, so add it */
1014 char fullname[200] = ".";
1017 osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %s",
1018 osi_LogSaveString(afsd_logp,namep));
1019 if (namep[0] == '.') {
1020 if (cm_GetCell_Gen(&namep[1], &fullname[1], CM_FLAG_CREATE)) {
1022 if ( stricmp(&namep[1], &fullname[1]) )
1023 code = cm_FreelanceAddSymlink(namep, fullname, &rock.fid);
1025 code = cm_FreelanceAddMount(namep, &fullname[1], "root.cell.", 1, &rock.fid);
1028 if (cm_GetCell_Gen(namep, fullname, CM_FLAG_CREATE)) {
1030 if ( stricmp(namep, fullname) )
1031 code = cm_FreelanceAddSymlink(namep, fullname, &rock.fid);
1033 code = cm_FreelanceAddMount(namep, fullname, "root.cell.", 0, &rock.fid);
1036 if (!found || code < 0) { /* add mount point failed, so give up */
1037 if (flags & CM_FLAG_CHECKPATH)
1038 return CM_ERROR_NOSUCHPATH;
1040 return CM_ERROR_NOSUCHFILE;
1042 tscp = NULL; /* to force call of cm_GetSCache */
1047 if ( !tscp ) /* we did not find it in the dnlc */
1050 code = cm_GetSCache(&rock.fid, &tscp, userp, reqp);
1054 /* tscp is now held */
1056 lock_ObtainMutex(&tscp->mx);
1057 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1058 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1060 lock_ReleaseMutex(&tscp->mx);
1061 cm_ReleaseSCache(tscp);
1064 /* tscp is now locked */
1066 if (!(flags & CM_FLAG_NOMOUNTCHASE)
1067 && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
1068 /* mount points are funny: they have a volume name to mount
1071 code = cm_ReadMountPoint(tscp, userp, reqp);
1073 code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
1075 lock_ReleaseMutex(&tscp->mx);
1076 cm_ReleaseSCache(tscp);
1083 lock_ReleaseMutex(&tscp->mx);
1086 /* copy back pointer */
1089 /* insert scache in dnlc */
1090 if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
1091 /* lock the directory entry to prevent racing callback revokes */
1092 lock_ObtainMutex(&dscp->mx);
1093 if ( dscp->cbServerp && dscp->cbExpires )
1094 cm_dnlcEnter(dscp, namep, tscp);
1095 lock_ReleaseMutex(&dscp->mx);
1102 int cm_ExpandSysName(char *inp, char *outp, long outSize, unsigned int index)
1107 tp = strrchr(inp, '@');
1109 return 0; /* no @sys */
1111 if (strcmp(tp, "@sys") != 0)
1112 return 0; /* no @sys */
1114 /* caller just wants to know if this is a valid @sys type of name */
1118 if (index >= MAXNUMSYSNAMES)
1121 /* otherwise generate the properly expanded @sys name */
1122 prefixCount = tp - inp;
1124 strncpy(outp, inp, prefixCount); /* copy out "a." from "a.@sys" */
1125 outp[prefixCount] = 0; /* null terminate the "a." */
1126 strcat(outp, cm_sysNameList[index]);/* append i386_nt40 */
1130 long cm_Lookup(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
1131 cm_req_t *reqp, cm_scache_t **outpScpp)
1135 int sysNameIndex = 0;
1136 cm_scache_t *scp = 0;
1138 if ( stricmp(namep,SMB_IOCTL_FILENAME_NOSLASH) == 0 ) {
1139 if (flags & CM_FLAG_CHECKPATH)
1140 return CM_ERROR_NOSUCHPATH;
1142 return CM_ERROR_NOSUCHFILE;
1145 for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
1146 code = cm_ExpandSysName(namep, tname, sizeof(tname), sysNameIndex);
1148 code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
1154 cm_ReleaseSCache(scp);
1158 return cm_LookupInternal(dscp, namep, flags, userp, reqp, outpScpp);
1162 /* None of the possible sysName expansions could be found */
1163 if (flags & CM_FLAG_CHECKPATH)
1164 return CM_ERROR_NOSUCHPATH;
1166 return CM_ERROR_NOSUCHFILE;
1169 long cm_Unlink(cm_scache_t *dscp, char *namep, cm_user_t *userp, cm_req_t *reqp)
1175 AFSFetchStatus newDirStatus;
1177 struct rx_connection * callp;
1179 #ifdef AFS_FREELANCE_CLIENT
1180 if (cm_freelanceEnabled && dscp == cm_data.rootSCachep) {
1181 /* deleting a mount point from the root dir. */
1182 code = cm_FreelanceRemoveMount(namep);
1187 /* make sure we don't screw up the dir status during the merge */
1188 lock_ObtainMutex(&dscp->mx);
1189 sflags = CM_SCACHESYNC_STOREDATA;
1190 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1191 lock_ReleaseMutex(&dscp->mx);
1196 afsFid.Volume = dscp->fid.volume;
1197 afsFid.Vnode = dscp->fid.vnode;
1198 afsFid.Unique = dscp->fid.unique;
1200 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1204 callp = cm_GetRxConn(connp);
1205 code = RXAFS_RemoveFile(callp, &afsFid, namep,
1206 &newDirStatus, &volSync);
1207 rx_PutConnection(callp);
1209 } while (cm_Analyze(connp, userp, reqp, &dscp->fid, &volSync, NULL, NULL, code));
1210 code = cm_MapRPCError(code, reqp);
1212 lock_ObtainMutex(&dscp->mx);
1213 cm_dnlcRemove(dscp, namep);
1214 cm_SyncOpDone(dscp, NULL, sflags);
1216 cm_MergeStatus(dscp, &newDirStatus, &volSync, userp, 0);
1217 lock_ReleaseMutex(&dscp->mx);
1222 /* called with a locked vnode, and fills in the link info.
1223 * returns this the vnode still locked.
1225 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1232 lock_AssertMutex(&linkScp->mx);
1233 if (!linkScp->mountPointStringp[0]) {
1234 /* read the link data */
1235 lock_ReleaseMutex(&linkScp->mx);
1236 thyper.LowPart = thyper.HighPart = 0;
1237 code = buf_Get(linkScp, &thyper, &bufp);
1238 lock_ObtainMutex(&linkScp->mx);
1242 code = cm_SyncOp(linkScp, bufp, userp, reqp, 0,
1243 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1248 if (cm_HaveBuffer(linkScp, bufp, 0))
1251 code = cm_GetBuffer(linkScp, bufp, NULL, userp, reqp);
1256 } /* while loop to get the data */
1258 /* now if we still have no link read in,
1259 * copy the data from the buffer */
1260 if ((temp = linkScp->length.LowPart) >= MOUNTPOINTLEN) {
1262 return CM_ERROR_TOOBIG;
1265 /* otherwise, it fits; make sure it is still null (could have
1266 * lost race with someone else referencing this link above),
1267 * and if so, copy in the data.
1269 if (!linkScp->mountPointStringp[0]) {
1270 strncpy(linkScp->mountPointStringp, bufp->datap, temp);
1271 linkScp->mountPointStringp[temp] = 0; /* null terminate */
1274 } /* don't have sym link contents cached */
1279 /* called with a held vnode and a path suffix, with the held vnode being a
1280 * symbolic link. Our goal is to generate a new path to interpret, and return
1281 * this new path in newSpaceBufferp. If the new vnode is relative to a dir
1282 * other than the directory containing the symbolic link, then the new root is
1283 * returned in *newRootScpp, otherwise a null is returned there.
1285 long cm_AssembleLink(cm_scache_t *linkScp, char *pathSuffixp,
1286 cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1287 cm_user_t *userp, cm_req_t *reqp)
1294 lock_ObtainMutex(&linkScp->mx);
1295 code = cm_HandleLink(linkScp, userp, reqp);
1299 /* if we may overflow the buffer, bail out; buffer is signficantly
1300 * bigger than max path length, so we don't really have to worry about
1301 * being a little conservative here.
1303 if (strlen(linkScp->mountPointStringp) + strlen(pathSuffixp) + 2
1304 >= CM_UTILS_SPACESIZE)
1305 return CM_ERROR_TOOBIG;
1307 tsp = cm_GetSpace();
1308 linkp = linkScp->mountPointStringp;
1309 if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1310 if (strlen(linkp) > cm_mountRootLen)
1311 strcpy(tsp->data, linkp+cm_mountRootLen+1);
1314 *newRootScpp = cm_data.rootSCachep;
1315 cm_HoldSCache(cm_data.rootSCachep);
1316 } else if (linkp[0] == '\\' && linkp[1] == '\\') {
1317 if (!strnicmp(&linkp[2], cm_NetbiosName, (len = strlen(cm_NetbiosName))))
1319 char * p = &linkp[len + 3];
1320 if (strnicmp(p, "all", 3) == 0)
1323 strcpy(tsp->data, p);
1324 for (p = tsp->data; *p; p++) {
1328 *newRootScpp = cm_data.rootSCachep;
1329 cm_HoldSCache(cm_data.rootSCachep);
1331 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1332 strcpy(tsp->data, linkp);
1333 *newRootScpp = NULL;
1334 code = CM_ERROR_PATH_NOT_COVERED;
1336 } else if ( !strnicmp(linkp, "msdfs:", (len = strlen("msdfs:"))) ) {
1337 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1338 strcpy(tsp->data, linkp);
1339 *newRootScpp = NULL;
1340 code = CM_ERROR_PATH_NOT_COVERED;
1341 } else if (*linkp == '\\' || *linkp == '/') {
1343 /* formerly, this was considered to be from the AFS root,
1344 * but this seems to create problems. instead, we will just
1345 * reject the link */
1346 strcpy(tsp->data, linkp+1);
1347 *newRootScpp = cm_data.rootSCachep;
1348 cm_HoldSCache(cm_data.rootSCachep);
1350 /* we still copy the link data into the response so that
1351 * the user can see what the link points to
1353 linkScp->fileType = CM_SCACHETYPE_INVALID;
1354 strcpy(tsp->data, linkp);
1355 *newRootScpp = NULL;
1356 code = CM_ERROR_NOSUCHPATH;
1359 /* a relative link */
1360 strcpy(tsp->data, linkp);
1361 *newRootScpp = NULL;
1363 if (pathSuffixp[0] != 0) { /* if suffix string is non-null */
1364 strcat(tsp->data, "\\");
1365 strcat(tsp->data, pathSuffixp);
1367 *newSpaceBufferp = tsp;
1370 lock_ReleaseMutex(&linkScp->mx);
1374 long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
1375 cm_user_t *userp, char *tidPathp, cm_req_t *reqp, cm_scache_t **outScpp)
1378 char *tp; /* ptr moving through input buffer */
1379 char tc; /* temp char */
1380 int haveComponent; /* has new component started? */
1381 char component[256]; /* this is the new component */
1382 char *cp; /* component name being assembled */
1383 cm_scache_t *tscp; /* current location in the hierarchy */
1384 cm_scache_t *nscp; /* next dude down */
1385 cm_scache_t *dirScp; /* last dir we searched */
1386 cm_scache_t *linkScp; /* new root for the symlink we just
1388 cm_space_t *psp; /* space for current path, if we've hit
1390 cm_space_t *tempsp; /* temp vbl */
1391 char *restp; /* rest of the pathname to interpret */
1392 int symlinkCount; /* count of # of symlinks traversed */
1393 int extraFlag; /* avoid chasing mt pts for dir cmd */
1394 int phase = 1; /* 1 = tidPathp, 2 = pathp */
1407 cm_HoldSCache(tscp);
1414 /* map Unix slashes into DOS ones so we can interpret Unix
1420 if (!haveComponent) {
1423 } else if (tc == 0) {
1437 /* we have a component here */
1438 if (tc == 0 || tc == '\\') {
1439 /* end of the component; we're at the last
1440 * component if tc == 0. However, if the last
1441 * is a symlink, we have more to do.
1443 *cp++ = 0; /* add null termination */
1445 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
1446 extraFlag = CM_FLAG_NOMOUNTCHASE;
1447 code = cm_Lookup(tscp, component,
1449 userp, reqp, &nscp);
1451 cm_ReleaseSCache(tscp);
1453 cm_ReleaseSCache(dirScp);
1456 if (code == CM_ERROR_NOSUCHFILE && tscp->fileType == CM_SCACHETYPE_SYMLINK)
1457 return CM_ERROR_NOSUCHPATH;
1461 haveComponent = 0; /* component done */
1463 cm_ReleaseSCache(dirScp);
1464 dirScp = tscp; /* for some symlinks */
1465 tscp = nscp; /* already held */
1467 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
1470 cm_ReleaseSCache(dirScp);
1476 /* now, if tscp is a symlink, we should follow
1477 * it and assemble the path again.
1479 lock_ObtainMutex(&tscp->mx);
1480 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1481 CM_SCACHESYNC_GETSTATUS
1482 | CM_SCACHESYNC_NEEDCALLBACK);
1484 lock_ReleaseMutex(&tscp->mx);
1485 cm_ReleaseSCache(tscp);
1488 cm_ReleaseSCache(dirScp);
1493 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
1494 /* this is a symlink; assemble a new buffer */
1495 lock_ReleaseMutex(&tscp->mx);
1496 if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
1497 cm_ReleaseSCache(tscp);
1500 cm_ReleaseSCache(dirScp);
1505 return CM_ERROR_TOO_MANY_SYMLINKS;
1511 code = cm_AssembleLink(tscp, restp, &linkScp, &tempsp, userp, reqp);
1513 /* something went wrong */
1514 cm_ReleaseSCache(tscp);
1517 cm_ReleaseSCache(dirScp);
1523 /* otherwise, tempsp has the new path,
1524 * and linkScp is the new root from
1525 * which to interpret that path.
1526 * Continue with the namei processing,
1527 * also doing the bookkeeping for the
1528 * space allocation and tracking the
1529 * vnode reference counts.
1535 cm_ReleaseSCache(tscp);
1540 * now, if linkScp is null, that's
1541 * AssembleLink's way of telling us that
1542 * the sym link is relative to the dir
1543 * containing the link. We have a ref
1544 * to it in dirScp, and we hold it now
1545 * and reuse it as the new spot in the
1553 /* not a symlink, we may be done */
1554 lock_ReleaseMutex(&tscp->mx);
1562 cm_ReleaseSCache(dirScp);
1570 cm_ReleaseSCache(dirScp);
1573 } /* end of a component */
1576 } /* we have a component */
1577 } /* big while loop over all components */
1581 cm_ReleaseSCache(dirScp);
1587 cm_ReleaseSCache(tscp);
1591 /* called with a dir, and a vnode within the dir that happens to be a symlink.
1592 * We chase the link, and return a held pointer to the target, if it exists,
1593 * in *outScpp. If we succeed, we return 0, otherwise we return an error code
1594 * and do not hold or return a target vnode.
1596 * This is very similar to calling cm_NameI with the last component of a name,
1597 * which happens to be a symlink, except that we've already passed by the name.
1599 * This function is typically called by the directory listing functions, which
1600 * encounter symlinks but need to return the proper file length so programs
1601 * like "more" work properly when they make use of the attributes retrieved from
1604 * The input vnode should not be locked when this function is called.
1606 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
1607 cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
1611 cm_scache_t *newRootScp;
1613 osi_Log1(afsd_logp, "Evaluating symlink vp %x", linkScp);
1615 code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
1619 /* now, if newRootScp is NULL, we're really being told that the symlink
1620 * is relative to the current directory (dscp).
1622 if (newRootScp == NULL) {
1624 cm_HoldSCache(dscp);
1627 code = cm_NameI(newRootScp, spacep->data,
1628 CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
1629 userp, NULL, reqp, outScpp);
1631 if (code == CM_ERROR_NOSUCHFILE)
1632 code = CM_ERROR_NOSUCHPATH;
1634 /* this stuff is allocated no matter what happened on the namei call,
1636 cm_FreeSpace(spacep);
1637 cm_ReleaseSCache(newRootScp);
1642 /* make this big enough so that one buffer of dir pages won't overflow. We'll
1643 * check anyway, but we want to minimize the chance that we have to leave stuff
1646 #define CM_BULKMAX 128
1648 /* rock for bulk stat calls */
1649 typedef struct cm_bulkStat {
1650 osi_hyper_t bufOffset; /* only do it for things in this buffer page */
1652 /* info for the actual call */
1653 int counter; /* next free slot */
1654 AFSFid fids[CM_BULKMAX];
1655 AFSFetchStatus stats[CM_BULKMAX];
1656 AFSCallBack callbacks[CM_BULKMAX];
1659 /* for a given entry, make sure that it isn't in the stat cache, and then
1660 * add it to the list of file IDs to be obtained.
1662 * Don't bother adding it if we already have a vnode. Note that the dir
1663 * is locked, so we have to be careful checking the vnode we're thinking of
1664 * processing, to avoid deadlocks.
1666 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
1677 /* Don't overflow bsp. */
1678 if (bsp->counter >= CM_BULKMAX)
1679 return CM_ERROR_STOPNOW;
1681 thyper.LowPart = cm_data.buf_blockSize;
1682 thyper.HighPart = 0;
1683 thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
1685 /* thyper is now the first byte past the end of the record we're
1686 * interested in, and bsp->bufOffset is the first byte of the record
1687 * we're interested in.
1688 * Skip data in the others.
1691 if (LargeIntegerLessThan(*offp, bsp->bufOffset))
1693 if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
1694 return CM_ERROR_STOPNOW;
1695 if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
1698 tfid.cell = scp->fid.cell;
1699 tfid.volume = scp->fid.volume;
1700 tfid.vnode = ntohl(dep->fid.vnode);
1701 tfid.unique = ntohl(dep->fid.unique);
1702 tscp = cm_FindSCache(&tfid);
1704 if (lock_TryMutex(&tscp->mx)) {
1705 /* we have an entry that we can look at */
1706 if (cm_HaveCallback(tscp)) {
1707 /* we have a callback on it. Don't bother
1708 * fetching this stat entry, since we're happy
1709 * with the info we have.
1711 lock_ReleaseMutex(&tscp->mx);
1712 cm_ReleaseSCache(tscp);
1715 lock_ReleaseMutex(&tscp->mx);
1717 cm_ReleaseSCache(tscp);
1720 #ifdef AFS_FREELANCE_CLIENT
1721 // yj: if this is a mountpoint under root.afs then we don't want it
1722 // to be bulkstat-ed, instead, we call getSCache directly and under
1723 // getSCache, it is handled specially.
1724 if ( cm_freelanceEnabled &&
1725 tfid.cell==AFS_FAKE_ROOT_CELL_ID &&
1726 tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
1727 !(tfid.vnode==0x1 && tfid.unique==0x1) )
1729 osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
1730 return cm_GetSCache(&tfid, &tscp, NULL, NULL);
1732 #endif /* AFS_FREELANCE_CLIENT */
1735 bsp->fids[i].Volume = scp->fid.volume;
1736 bsp->fids[i].Vnode = tfid.vnode;
1737 bsp->fids[i].Unique = tfid.unique;
1741 /* called with a locked scp and a pointer to a buffer. Make bulk stat
1742 * calls on all undeleted files in the page of the directory specified.
1744 void cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
1748 cm_bulkStat_t bb; /* this is *BIG*, probably 12K or so;
1749 * watch for stack problems */
1750 AFSCBFids fidStruct;
1751 AFSBulkStats statStruct;
1753 AFSCBs callbackStruct;
1756 cm_callbackRequest_t cbReq;
1762 struct rx_connection * callp;
1764 osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%x", (long) dscp);
1766 /* should be on a buffer boundary */
1767 osi_assert((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0);
1770 bb.bufOffset = *offsetp;
1772 lock_ReleaseMutex(&dscp->mx);
1773 /* first, assemble the file IDs we need to stat */
1774 code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) &bb, offsetp, userp, reqp, NULL);
1775 lock_ObtainMutex(&dscp->mx);
1777 /* if we failed, bail out early */
1778 if (code && code != CM_ERROR_STOPNOW) return;
1780 /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
1781 * make the calls to create the entries. Handle AFSCBMAX files at a
1785 while (filex < bb.counter) {
1786 filesThisCall = bb.counter - filex;
1787 if (filesThisCall > AFSCBMAX) filesThisCall = AFSCBMAX;
1789 fidStruct.AFSCBFids_len = filesThisCall;
1790 fidStruct.AFSCBFids_val = &bb.fids[filex];
1791 statStruct.AFSBulkStats_len = filesThisCall;
1792 statStruct.AFSBulkStats_val = &bb.stats[filex];
1793 callbackStruct.AFSCBs_len = filesThisCall;
1794 callbackStruct.AFSCBs_val = &bb.callbacks[filex];
1795 cm_StartCallbackGrantingCall(NULL, &cbReq);
1796 osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
1798 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1802 callp = cm_GetRxConn(connp);
1803 code = RXAFS_BulkStatus(callp, &fidStruct,
1804 &statStruct, &callbackStruct, &volSync);
1805 rx_PutConnection(callp);
1807 } while (cm_Analyze(connp, userp, reqp, &dscp->fid,
1808 &volSync, NULL, &cbReq, code));
1809 code = cm_MapRPCError(code, reqp);
1811 osi_Log0(afsd_logp, "CALL BulkStatus DONE");
1813 /* may as well quit on an error, since we're not going to do
1814 * much better on the next immediate call, either.
1819 /* otherwise, we should do the merges */
1820 for(i = 0; i<filesThisCall; i++) {
1822 tfid.cell = dscp->fid.cell;
1823 tfid.volume = bb.fids[j].Volume;
1824 tfid.vnode = bb.fids[j].Vnode;
1825 tfid.unique = bb.fids[j].Unique;
1826 code = cm_GetSCache(&tfid, &scp, userp, reqp);
1830 /* otherwise, if this entry has no callback info,
1833 lock_ObtainMutex(&scp->mx);
1834 /* now, we have to be extra paranoid on merging in this
1835 * information, since we didn't use cm_SyncOp before
1836 * starting the fetch to make sure that no bad races
1837 * were occurring. Specifically, we need to make sure
1838 * we don't obliterate any newer information in the
1839 * vnode than have here.
1841 * Right now, be pretty conservative: if there's a
1842 * callback or a pending call, skip it.
1844 if (scp->cbServerp == NULL
1846 (CM_SCACHEFLAG_FETCHING
1847 | CM_SCACHEFLAG_STORING
1848 | CM_SCACHEFLAG_SIZESTORING))) {
1849 cm_EndCallbackGrantingCall(scp, &cbReq,
1851 CM_CALLBACK_MAINTAINCOUNT);
1852 cm_MergeStatus(scp, &bb.stats[j], &volSync,
1855 lock_ReleaseMutex(&scp->mx);
1856 cm_ReleaseSCache(scp);
1857 } /* all files in the response */
1858 /* now tell it to drop the count,
1859 * after doing the vnode processing above */
1860 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
1862 filex += filesThisCall;
1863 } /* while there are still more files to process */
1864 osi_Log0(afsd_logp, "END cm_TryBulkStat");
1867 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
1871 /* initialize store back mask as inexpensive local variable */
1873 memset(statusp, 0, sizeof(AFSStoreStatus));
1875 /* copy out queued info from scache first, if scp passed in */
1877 if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
1878 statusp->ClientModTime = scp->clientModTime;
1879 mask |= AFS_SETMODTIME;
1880 scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
1885 /* now add in our locally generated request */
1886 if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
1887 statusp->ClientModTime = attrp->clientModTime;
1888 mask |= AFS_SETMODTIME;
1890 if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
1891 statusp->UnixModeBits = attrp->unixModeBits;
1892 mask |= AFS_SETMODE;
1894 if (attrp->mask & CM_ATTRMASK_OWNER) {
1895 statusp->Owner = attrp->owner;
1896 mask |= AFS_SETOWNER;
1898 if (attrp->mask & CM_ATTRMASK_GROUP) {
1899 statusp->Group = attrp->group;
1900 mask |= AFS_SETGROUP;
1903 statusp->Mask = mask;
1906 /* set the file size, and make sure that all relevant buffers have been
1907 * truncated. Ensure that any partially truncated buffers have been zeroed
1908 * to the end of the buffer.
1910 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
1916 /* start by locking out buffer creation */
1917 lock_ObtainWrite(&scp->bufCreateLock);
1919 /* verify that this is a file, not a dir or a symlink */
1920 lock_ObtainMutex(&scp->mx);
1921 code = cm_SyncOp(scp, NULL, userp, reqp, 0,
1922 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1926 if (scp->fileType != CM_SCACHETYPE_FILE) {
1927 code = CM_ERROR_ISDIR;
1932 if (LargeIntegerLessThan(*sizep, scp->length))
1937 lock_ReleaseMutex(&scp->mx);
1939 /* can't hold scp->mx lock here, since we may wait for a storeback to
1940 * finish if the buffer package is cleaning a buffer by storing it to
1944 buf_Truncate(scp, userp, reqp, sizep);
1946 /* now ensure that file length is short enough, and update truncPos */
1947 lock_ObtainMutex(&scp->mx);
1949 /* make sure we have a callback (so we have the right value for the
1950 * length), and wait for it to be safe to do a truncate.
1952 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
1953 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
1954 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
1958 if (LargeIntegerLessThan(*sizep, scp->length)) {
1959 /* a real truncation. If truncPos is not set yet, or is bigger
1960 * than where we're truncating the file, set truncPos to this
1965 if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
1966 || LargeIntegerLessThan(*sizep, scp->length)) {
1968 scp->truncPos = *sizep;
1969 scp->mask |= CM_SCACHEMASK_TRUNCPOS;
1971 /* in either case, the new file size has been changed */
1972 scp->length = *sizep;
1973 scp->mask |= CM_SCACHEMASK_LENGTH;
1975 else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
1976 /* really extending the file */
1977 scp->length = *sizep;
1978 scp->mask |= CM_SCACHEMASK_LENGTH;
1981 /* done successfully */
1985 lock_ReleaseMutex(&scp->mx);
1986 lock_ReleaseWrite(&scp->bufCreateLock);
1991 /* set the file size or other attributes (but not both at once) */
1992 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
1997 AFSFetchStatus afsOutStatus;
2001 AFSStoreStatus afsInStatus;
2002 struct rx_connection * callp;
2004 /* handle file length setting */
2005 if (attrp->mask & CM_ATTRMASK_LENGTH)
2006 return cm_SetLength(scp, &attrp->length, userp, reqp);
2008 flags = CM_SCACHESYNC_STORESTATUS;
2010 lock_ObtainMutex(&scp->mx);
2011 /* otherwise, we have to make an RPC to get the status */
2012 code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
2014 /* make the attr structure */
2015 cm_StatusFromAttr(&afsInStatus, scp, attrp);
2017 lock_ReleaseMutex(&scp->mx);
2021 /* now make the RPC */
2022 osi_Log1(afsd_logp, "CALL StoreStatus vp %x", (long) scp);
2023 tfid.Volume = scp->fid.volume;
2024 tfid.Vnode = scp->fid.vnode;
2025 tfid.Unique = scp->fid.unique;
2027 code = cm_Conn(&scp->fid, userp, reqp, &connp);
2031 callp = cm_GetRxConn(connp);
2032 code = RXAFS_StoreStatus(callp, &tfid,
2033 &afsInStatus, &afsOutStatus, &volSync);
2034 rx_PutConnection(callp);
2036 } while (cm_Analyze(connp, userp, reqp,
2037 &scp->fid, &volSync, NULL, NULL, code));
2038 code = cm_MapRPCError(code, reqp);
2040 osi_Log1(afsd_logp, "CALL StoreStatus DONE, code %d", code);
2042 lock_ObtainMutex(&scp->mx);
2043 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
2045 cm_MergeStatus(scp, &afsOutStatus, &volSync, userp,
2046 CM_MERGEFLAG_FORCE);
2048 /* if we're changing the mode bits, discard the ACL cache,
2049 * since we changed the mode bits.
2051 if (afsInStatus.Mask & AFS_SETMODE) cm_FreeAllACLEnts(scp);
2052 lock_ReleaseMutex(&scp->mx);
2056 long cm_Create(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2057 cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
2062 cm_callbackRequest_t cbReq;
2067 AFSStoreStatus inStatus;
2068 AFSFetchStatus updatedDirStatus;
2069 AFSFetchStatus newFileStatus;
2070 AFSCallBack newFileCallback;
2072 struct rx_connection * callp;
2074 /* can't create names with @sys in them; must expand it manually first.
2075 * return "invalid request" if they try.
2077 if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2078 return CM_ERROR_ATSYS;
2081 /* before starting the RPC, mark that we're changing the file data, so
2082 * that someone who does a chmod will know to wait until our call
2085 lock_ObtainMutex(&dscp->mx);
2086 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2088 cm_StartCallbackGrantingCall(NULL, &cbReq);
2090 lock_ReleaseMutex(&dscp->mx);
2096 cm_StatusFromAttr(&inStatus, NULL, attrp);
2098 /* try the RPC now */
2100 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2104 dirAFSFid.Volume = dscp->fid.volume;
2105 dirAFSFid.Vnode = dscp->fid.vnode;
2106 dirAFSFid.Unique = dscp->fid.unique;
2108 callp = cm_GetRxConn(connp);
2109 code = RXAFS_CreateFile(connp->callp, &dirAFSFid, namep,
2110 &inStatus, &newAFSFid, &newFileStatus,
2111 &updatedDirStatus, &newFileCallback,
2113 rx_PutConnection(callp);
2115 } while (cm_Analyze(connp, userp, reqp,
2116 &dscp->fid, &volSync, NULL, &cbReq, code));
2117 code = cm_MapRPCError(code, reqp);
2119 lock_ObtainMutex(&dscp->mx);
2120 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2122 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2124 lock_ReleaseMutex(&dscp->mx);
2126 /* now try to create the file's entry, too, but be careful to
2127 * make sure that we don't merge in old info. Since we weren't locking
2128 * out any requests during the file's creation, we may have pretty old
2132 newFid.cell = dscp->fid.cell;
2133 newFid.volume = dscp->fid.volume;
2134 newFid.vnode = newAFSFid.Vnode;
2135 newFid.unique = newAFSFid.Unique;
2136 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2138 lock_ObtainMutex(&scp->mx);
2139 if (!cm_HaveCallback(scp)) {
2140 cm_MergeStatus(scp, &newFileStatus, &volSync,
2142 cm_EndCallbackGrantingCall(scp, &cbReq,
2143 &newFileCallback, 0);
2146 lock_ReleaseMutex(&scp->mx);
2151 /* make sure we end things properly */
2153 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2158 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
2162 lock_ObtainWrite(&scp->bufCreateLock);
2163 code = buf_CleanVnode(scp, userp, reqp);
2164 lock_ReleaseWrite(&scp->bufCreateLock);
2166 lock_ObtainMutex(&scp->mx);
2167 scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA
2168 | CM_SCACHEFLAG_OUTOFSPACE);
2169 if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
2170 | CM_SCACHEMASK_CLIENTMODTIME
2171 | CM_SCACHEMASK_LENGTH))
2172 code = cm_StoreMini(scp, userp, reqp);
2173 lock_ReleaseMutex(&scp->mx);
2178 long cm_MakeDir(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2179 cm_user_t *userp, cm_req_t *reqp)
2184 cm_callbackRequest_t cbReq;
2189 AFSStoreStatus inStatus;
2190 AFSFetchStatus updatedDirStatus;
2191 AFSFetchStatus newDirStatus;
2192 AFSCallBack newDirCallback;
2194 struct rx_connection * callp;
2196 /* can't create names with @sys in them; must expand it manually first.
2197 * return "invalid request" if they try.
2199 if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2200 return CM_ERROR_ATSYS;
2203 /* before starting the RPC, mark that we're changing the directory
2204 * data, so that someone who does a chmod on the dir will wait until
2205 * our call completes.
2207 lock_ObtainMutex(&dscp->mx);
2208 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2210 cm_StartCallbackGrantingCall(NULL, &cbReq);
2212 lock_ReleaseMutex(&dscp->mx);
2218 cm_StatusFromAttr(&inStatus, NULL, attrp);
2220 /* try the RPC now */
2222 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2226 dirAFSFid.Volume = dscp->fid.volume;
2227 dirAFSFid.Vnode = dscp->fid.vnode;
2228 dirAFSFid.Unique = dscp->fid.unique;
2230 callp = cm_GetRxConn(connp);
2231 code = RXAFS_MakeDir(connp->callp, &dirAFSFid, namep,
2232 &inStatus, &newAFSFid, &newDirStatus,
2233 &updatedDirStatus, &newDirCallback,
2235 rx_PutConnection(callp);
2237 } while (cm_Analyze(connp, userp, reqp,
2238 &dscp->fid, &volSync, NULL, &cbReq, code));
2239 code = cm_MapRPCError(code, reqp);
2241 lock_ObtainMutex(&dscp->mx);
2242 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2244 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2246 lock_ReleaseMutex(&dscp->mx);
2248 /* now try to create the new dir's entry, too, but be careful to
2249 * make sure that we don't merge in old info. Since we weren't locking
2250 * out any requests during the file's creation, we may have pretty old
2254 newFid.cell = dscp->fid.cell;
2255 newFid.volume = dscp->fid.volume;
2256 newFid.vnode = newAFSFid.Vnode;
2257 newFid.unique = newAFSFid.Unique;
2258 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2260 lock_ObtainMutex(&scp->mx);
2261 if (!cm_HaveCallback(scp)) {
2262 cm_MergeStatus(scp, &newDirStatus, &volSync,
2264 cm_EndCallbackGrantingCall(scp, &cbReq,
2265 &newDirCallback, 0);
2268 lock_ReleaseMutex(&scp->mx);
2269 cm_ReleaseSCache(scp);
2273 /* make sure we end things properly */
2275 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2277 /* and return error code */
2281 long cm_Link(cm_scache_t *dscp, char *namep, cm_scache_t *sscp, long flags,
2282 cm_user_t *userp, cm_req_t *reqp)
2287 AFSFid existingAFSFid;
2288 AFSFetchStatus updatedDirStatus;
2289 AFSFetchStatus newLinkStatus;
2291 struct rx_connection * callp;
2293 if (dscp->fid.cell != sscp->fid.cell ||
2294 dscp->fid.volume != sscp->fid.volume) {
2295 return CM_ERROR_CROSSDEVLINK;
2298 lock_ObtainMutex(&dscp->mx);
2299 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2300 lock_ReleaseMutex(&dscp->mx);
2306 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2309 dirAFSFid.Volume = dscp->fid.volume;
2310 dirAFSFid.Vnode = dscp->fid.vnode;
2311 dirAFSFid.Unique = dscp->fid.unique;
2313 existingAFSFid.Volume = sscp->fid.volume;
2314 existingAFSFid.Vnode = sscp->fid.vnode;
2315 existingAFSFid.Unique = sscp->fid.unique;
2317 callp = cm_GetRxConn(connp);
2318 code = RXAFS_Link(callp, &dirAFSFid, namep, &existingAFSFid,
2319 &newLinkStatus, &updatedDirStatus, &volSync);
2320 rx_PutConnection(callp);
2321 osi_Log1(smb_logp," RXAFS_Link returns %d", code);
2323 } while (cm_Analyze(connp, userp, reqp,
2324 &dscp->fid, &volSync, NULL, NULL, code));
2326 code = cm_MapRPCError(code, reqp);
2328 lock_ObtainMutex(&dscp->mx);
2329 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2331 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2333 lock_ReleaseMutex(&dscp->mx);
2338 long cm_SymLink(cm_scache_t *dscp, char *namep, char *contentsp, long flags,
2339 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
2347 AFSStoreStatus inStatus;
2348 AFSFetchStatus updatedDirStatus;
2349 AFSFetchStatus newLinkStatus;
2351 struct rx_connection * callp;
2353 /* before starting the RPC, mark that we're changing the directory data,
2354 * so that someone who does a chmod on the dir will wait until our
2357 lock_ObtainMutex(&dscp->mx);
2358 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2359 lock_ReleaseMutex(&dscp->mx);
2364 cm_StatusFromAttr(&inStatus, NULL, attrp);
2366 /* try the RPC now */
2368 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2372 dirAFSFid.Volume = dscp->fid.volume;
2373 dirAFSFid.Vnode = dscp->fid.vnode;
2374 dirAFSFid.Unique = dscp->fid.unique;
2376 callp = cm_GetRxConn(connp);
2377 code = RXAFS_Symlink(callp, &dirAFSFid, namep, contentsp,
2378 &inStatus, &newAFSFid, &newLinkStatus,
2379 &updatedDirStatus, &volSync);
2380 rx_PutConnection(callp);
2382 } while (cm_Analyze(connp, userp, reqp,
2383 &dscp->fid, &volSync, NULL, NULL, code));
2384 code = cm_MapRPCError(code, reqp);
2386 lock_ObtainMutex(&dscp->mx);
2387 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2389 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2391 lock_ReleaseMutex(&dscp->mx);
2393 /* now try to create the new dir's entry, too, but be careful to
2394 * make sure that we don't merge in old info. Since we weren't locking
2395 * out any requests during the file's creation, we may have pretty old
2399 newFid.cell = dscp->fid.cell;
2400 newFid.volume = dscp->fid.volume;
2401 newFid.vnode = newAFSFid.Vnode;
2402 newFid.unique = newAFSFid.Unique;
2403 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2405 lock_ObtainMutex(&scp->mx);
2406 if (!cm_HaveCallback(scp)) {
2407 cm_MergeStatus(scp, &newLinkStatus, &volSync,
2410 lock_ReleaseMutex(&scp->mx);
2411 cm_ReleaseSCache(scp);
2415 /* and return error code */
2419 long cm_RemoveDir(cm_scache_t *dscp, char *namep, cm_user_t *userp,
2426 AFSFetchStatus updatedDirStatus;
2428 struct rx_connection * callp;
2430 /* before starting the RPC, mark that we're changing the directory data,
2431 * so that someone who does a chmod on the dir will wait until our
2434 lock_ObtainMutex(&dscp->mx);
2435 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2436 lock_ReleaseMutex(&dscp->mx);
2442 /* try the RPC now */
2444 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2448 dirAFSFid.Volume = dscp->fid.volume;
2449 dirAFSFid.Vnode = dscp->fid.vnode;
2450 dirAFSFid.Unique = dscp->fid.unique;
2452 callp = cm_GetRxConn(connp);
2453 code = RXAFS_RemoveDir(callp, &dirAFSFid, namep,
2454 &updatedDirStatus, &volSync);
2455 rx_PutConnection(callp);
2457 } while (cm_Analyze(connp, userp, reqp,
2458 &dscp->fid, &volSync, NULL, NULL, code));
2459 code = cm_MapRPCErrorRmdir(code, reqp);
2461 lock_ObtainMutex(&dscp->mx);
2462 cm_dnlcRemove(dscp, namep);
2463 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2465 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2467 lock_ReleaseMutex(&dscp->mx);
2469 /* and return error code */
2473 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
2475 /* grab mutex on contents */
2476 lock_ObtainMutex(&scp->mx);
2478 /* reset the prefetch info */
2479 scp->prefetch.base.LowPart = 0; /* base */
2480 scp->prefetch.base.HighPart = 0;
2481 scp->prefetch.end.LowPart = 0; /* and end */
2482 scp->prefetch.end.HighPart = 0;
2484 /* release mutex on contents */
2485 lock_ReleaseMutex(&scp->mx);
2491 long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
2492 char *newNamep, cm_user_t *userp, cm_req_t *reqp)
2496 AFSFid oldDirAFSFid;
2497 AFSFid newDirAFSFid;
2499 AFSFetchStatus updatedOldDirStatus;
2500 AFSFetchStatus updatedNewDirStatus;
2503 struct rx_connection * callp;
2505 /* before starting the RPC, mark that we're changing the directory data,
2506 * so that someone who does a chmod on the dir will wait until our call
2507 * completes. We do this in vnode order so that we don't deadlock,
2508 * which makes the code a little verbose.
2510 if (oldDscp == newDscp) {
2511 /* check for identical names */
2512 if (strcmp(oldNamep, newNamep) == 0)
2513 return CM_ERROR_RENAME_IDENTICAL;
2516 lock_ObtainMutex(&oldDscp->mx);
2517 cm_dnlcRemove(oldDscp, oldNamep);
2518 cm_dnlcRemove(oldDscp, newNamep);
2519 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2520 CM_SCACHESYNC_STOREDATA);
2521 lock_ReleaseMutex(&oldDscp->mx);
2524 /* two distinct dir vnodes */
2526 if (oldDscp->fid.cell != newDscp->fid.cell ||
2527 oldDscp->fid.volume != newDscp->fid.volume)
2528 return CM_ERROR_CROSSDEVLINK;
2530 /* shouldn't happen that we have distinct vnodes for two
2531 * different files, but could due to deliberate attack, or
2532 * stale info. Avoid deadlocks and quit now.
2534 if (oldDscp->fid.vnode == newDscp->fid.vnode)
2535 return CM_ERROR_CROSSDEVLINK;
2537 if (oldDscp->fid.vnode < newDscp->fid.vnode) {
2538 lock_ObtainMutex(&oldDscp->mx);
2539 cm_dnlcRemove(oldDscp, oldNamep);
2540 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2541 CM_SCACHESYNC_STOREDATA);
2542 lock_ReleaseMutex(&oldDscp->mx);
2544 lock_ObtainMutex(&newDscp->mx);
2545 cm_dnlcRemove(newDscp, newNamep);
2546 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2547 CM_SCACHESYNC_STOREDATA);
2548 lock_ReleaseMutex(&newDscp->mx);
2550 /* cleanup first one */
2551 cm_SyncOpDone(oldDscp, NULL,
2552 CM_SCACHESYNC_STOREDATA);
2557 /* lock the new vnode entry first */
2558 lock_ObtainMutex(&newDscp->mx);
2559 cm_dnlcRemove(newDscp, newNamep);
2560 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2561 CM_SCACHESYNC_STOREDATA);
2562 lock_ReleaseMutex(&newDscp->mx);
2564 lock_ObtainMutex(&oldDscp->mx);
2565 cm_dnlcRemove(oldDscp, oldNamep);
2566 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2567 CM_SCACHESYNC_STOREDATA);
2568 lock_ReleaseMutex(&oldDscp->mx);
2570 /* cleanup first one */
2571 cm_SyncOpDone(newDscp, NULL,
2572 CM_SCACHESYNC_STOREDATA);
2576 } /* two distinct vnodes */
2583 /* try the RPC now */
2585 code = cm_Conn(&oldDscp->fid, userp, reqp, &connp);
2589 oldDirAFSFid.Volume = oldDscp->fid.volume;
2590 oldDirAFSFid.Vnode = oldDscp->fid.vnode;
2591 oldDirAFSFid.Unique = oldDscp->fid.unique;
2592 newDirAFSFid.Volume = newDscp->fid.volume;
2593 newDirAFSFid.Vnode = newDscp->fid.vnode;
2594 newDirAFSFid.Unique = newDscp->fid.unique;
2596 callp = cm_GetRxConn(connp);
2597 code = RXAFS_Rename(callp, &oldDirAFSFid, oldNamep,
2598 &newDirAFSFid, newNamep,
2599 &updatedOldDirStatus, &updatedNewDirStatus,
2601 rx_PutConnection(callp);
2603 } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
2604 &volSync, NULL, NULL, code));
2605 code = cm_MapRPCError(code, reqp);
2607 /* update the individual stat cache entries for the directories */
2608 lock_ObtainMutex(&oldDscp->mx);
2609 cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
2611 cm_MergeStatus(oldDscp, &updatedOldDirStatus, &volSync,
2614 lock_ReleaseMutex(&oldDscp->mx);
2616 /* and update it for the new one, too, if necessary */
2618 lock_ObtainMutex(&newDscp->mx);
2619 cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
2621 cm_MergeStatus(newDscp, &updatedNewDirStatus, &volSync,
2624 lock_ReleaseMutex(&newDscp->mx);
2627 /* and return error code */
2631 long cm_Lock(cm_scache_t *scp, unsigned char LockType,
2632 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
2633 u_long Timeout, cm_user_t *userp, cm_req_t *reqp,
2637 int Which = ((LockType & 0x1) ? LockRead : LockWrite);
2641 cm_file_lock_t *fileLock;
2644 struct rx_connection * callp;
2646 /* Look for a conflict. Also, if we are asking for a shared lock,
2647 * look for another shared lock, so we don't have to do an RPC.
2651 fileLock = (cm_file_lock_t *)
2652 ((char *) q - offsetof(cm_file_lock_t, fileq));
2653 if ((fileLock->flags &
2654 (CM_FILELOCK_FLAG_INVALID | CM_FILELOCK_FLAG_WAITING))
2656 if ((LockType & 0x1) == 0
2657 || (fileLock->LockType & 0x1) == 0)
2658 return CM_ERROR_WOULDBLOCK;
2667 tfid.Volume = scp->fid.volume;
2668 tfid.Vnode = scp->fid.vnode;
2669 tfid.Unique = scp->fid.unique;
2670 lock_ReleaseMutex(&scp->mx);
2672 code = cm_Conn(&scp->fid, userp, reqp, &connp);
2676 callp = cm_GetRxConn(connp);
2677 code = RXAFS_SetLock(callp, &tfid, Which,
2679 rx_PutConnection(callp);
2681 } while (cm_Analyze(connp, userp, reqp, &scp->fid, &volSync,
2683 lock_ObtainMutex(&scp->mx);
2684 code = cm_MapRPCError(code, reqp);
2687 if (code == 0 || Timeout != 0) {
2688 fileLock = malloc(sizeof(cm_file_lock_t));
2689 fileLock->LockType = LockType;
2691 fileLock->userp = userp;
2692 fileLock->fid = scp->fid;
2693 fileLock->LOffset = LOffset;
2694 fileLock->LLength = LLength;
2695 fileLock->flags = (code == 0 ? 0 : CM_FILELOCK_FLAG_WAITING);
2696 osi_QAdd(&scp->fileLocks, &fileLock->fileq);
2697 lock_ObtainWrite(&cm_scacheLock);
2698 osi_QAdd(&cm_allFileLocks, &fileLock->q);
2699 lock_ReleaseWrite(&cm_scacheLock);
2706 long cm_Unlock(cm_scache_t *scp, unsigned char LockType,
2707 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
2708 cm_user_t *userp, cm_req_t *reqp)
2711 int Which = ((LockType & 0x1) ? LockRead : LockWrite);
2715 cm_file_lock_t *fileLock, *ourLock;
2716 osi_queue_t *q, *qq;
2717 int anotherReader = 0;
2720 struct rx_connection * callp;
2722 if (LargeIntegerLessThan(LLength, scp->length))
2725 /* Look for our own lock on the list, so as to remove it.
2726 * Also, determine if we're the last reader; if not, avoid an RPC.
2730 fileLock = (cm_file_lock_t *)
2731 ((char *) q - offsetof(cm_file_lock_t, fileq));
2733 && fileLock->userp == userp
2734 && LargeIntegerEqualTo(fileLock->LOffset, LOffset)
2735 && LargeIntegerEqualTo(fileLock->LLength, LLength)) {
2740 else if (fileLock->LockType & 0x1)
2745 /* ignore byte ranges */
2746 if (smallLock && !found)
2749 /* don't try to unlock other people's locks */
2751 return CM_ERROR_WOULDBLOCK;
2753 /* discard lock record */
2754 osi_QRemove(&scp->fileLocks, qq);
2756 * Don't delete it here; let the daemon delete it, to simplify
2757 * the daemon's traversal of the list.
2759 lock_ObtainWrite(&cm_scacheLock);
2760 ourLock->flags |= CM_FILELOCK_FLAG_INVALID;
2761 cm_ReleaseUser(ourLock->userp);
2762 lock_ReleaseWrite(&cm_scacheLock);
2764 if (!anotherReader) {
2765 tfid.Volume = scp->fid.volume;
2766 tfid.Vnode = scp->fid.vnode;
2767 tfid.Unique = scp->fid.unique;
2768 lock_ReleaseMutex(&scp->mx);
2770 code = cm_Conn(&scp->fid, userp, reqp, &connp);
2774 callp = cm_GetRxConn(connp);
2775 code = RXAFS_ReleaseLock(callp, &tfid, &volSync);
2776 rx_PutConnection(callp);
2778 } while (cm_Analyze(connp, userp, reqp, &scp->fid, &volSync,
2780 code = cm_MapRPCError(code, reqp);
2781 lock_ObtainMutex(&scp->mx);
2787 void cm_CheckLocks()
2789 osi_queue_t *q, *nq;
2790 cm_file_lock_t *fileLock;
2796 struct rx_connection * callp;
2800 lock_ObtainWrite(&cm_scacheLock);
2801 q = cm_allFileLocks;
2803 fileLock = (cm_file_lock_t *) q;
2805 if (fileLock->flags & CM_FILELOCK_FLAG_INVALID) {
2806 osi_QRemove(&cm_allFileLocks, q);
2809 else if (!(fileLock->flags & CM_FILELOCK_FLAG_WAITING)) {
2810 tfid.Volume = fileLock->fid.volume;
2811 tfid.Vnode = fileLock->fid.vnode;
2812 tfid.Unique = fileLock->fid.unique;
2813 lock_ReleaseWrite(&cm_scacheLock);
2815 code = cm_Conn(&fileLock->fid, fileLock->userp,
2820 callp = cm_GetRxConn(connp);
2821 code = RXAFS_ExtendLock(callp, &tfid,
2823 rx_PutConnection(callp);
2825 } while (cm_Analyze(connp, fileLock->userp, &req,
2826 &fileLock->fid, &volSync, NULL, NULL,
2828 code = cm_MapRPCError(code, &req);
2829 lock_ObtainWrite(&cm_scacheLock);
2833 lock_ReleaseWrite(&cm_scacheLock);
2836 long cm_RetryLock(cm_file_lock_t *oldFileLock, int vcp_is_dead)
2839 int Which = ((oldFileLock->LockType & 0x1) ? LockRead : LockWrite);
2844 cm_file_lock_t *fileLock;
2848 struct rx_connection * callp;
2851 code = CM_ERROR_TIMEDOUT;
2857 /* Look for a conflict. Also, if we are asking for a shared lock,
2858 * look for another shared lock, so we don't have to do an RPC.
2860 code = cm_GetSCache(&oldFileLock->fid, &scp, oldFileLock->userp, &req);
2866 fileLock = (cm_file_lock_t *)
2867 ((char *) q - offsetof(cm_file_lock_t, fileq));
2868 if ((fileLock->flags &
2869 (CM_FILELOCK_FLAG_INVALID | CM_FILELOCK_FLAG_WAITING))
2871 if ((oldFileLock->LockType & 0x1) == 0
2872 || (fileLock->LockType & 0x1) == 0) {
2873 cm_ReleaseSCache(scp);
2874 return CM_ERROR_WOULDBLOCK;
2884 tfid.Volume = oldFileLock->fid.volume;
2885 tfid.Vnode = oldFileLock->fid.vnode;
2886 tfid.Unique = oldFileLock->fid.unique;
2888 code = cm_Conn(&oldFileLock->fid, oldFileLock->userp,
2893 callp = cm_GetRxConn(connp);
2894 code = RXAFS_SetLock(callp, &tfid, Which,
2896 rx_PutConnection(callp);
2898 } while (cm_Analyze(connp, oldFileLock->userp, &req,
2899 &oldFileLock->fid, &volSync,
2901 code = cm_MapRPCError(code, &req);
2905 if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
2906 lock_ObtainMutex(&scp->mx);
2907 osi_QRemove(&scp->fileLocks, &oldFileLock->fileq);
2908 lock_ReleaseMutex(&scp->mx);
2910 lock_ObtainWrite(&cm_scacheLock);
2912 oldFileLock->flags = 0;
2913 else if (code != CM_ERROR_WOULDBLOCK) {
2914 oldFileLock->flags |= CM_FILELOCK_FLAG_INVALID;
2915 cm_ReleaseUser(oldFileLock->userp);
2916 oldFileLock->userp = NULL;
2918 lock_ReleaseWrite(&cm_scacheLock);