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 (NT_TRANSACTION2, 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 osi_Log1(afsd_logp, "CALL RemoveFile scp 0x%x", (long) dscp);
1202 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1206 callp = cm_GetRxConn(connp);
1207 code = RXAFS_RemoveFile(callp, &afsFid, namep,
1208 &newDirStatus, &volSync);
1209 rx_PutConnection(callp);
1211 } while (cm_Analyze(connp, userp, reqp, &dscp->fid, &volSync, NULL, NULL, code));
1212 code = cm_MapRPCError(code, reqp);
1215 osi_Log1(afsd_logp, "CALL RemoveFile FAILURE, code 0x%x", code);
1217 osi_Log0(afsd_logp, "CALL RemoveFile SUCCESS");
1219 lock_ObtainMutex(&dscp->mx);
1220 cm_dnlcRemove(dscp, namep);
1221 cm_SyncOpDone(dscp, NULL, sflags);
1223 cm_MergeStatus(dscp, &newDirStatus, &volSync, userp, 0);
1224 lock_ReleaseMutex(&dscp->mx);
1229 /* called with a locked vnode, and fills in the link info.
1230 * returns this the vnode still locked.
1232 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1239 lock_AssertMutex(&linkScp->mx);
1240 if (!linkScp->mountPointStringp[0]) {
1241 /* read the link data */
1242 lock_ReleaseMutex(&linkScp->mx);
1243 thyper.LowPart = thyper.HighPart = 0;
1244 code = buf_Get(linkScp, &thyper, &bufp);
1245 lock_ObtainMutex(&linkScp->mx);
1249 code = cm_SyncOp(linkScp, bufp, userp, reqp, 0,
1250 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1255 if (cm_HaveBuffer(linkScp, bufp, 0))
1258 code = cm_GetBuffer(linkScp, bufp, NULL, userp, reqp);
1263 } /* while loop to get the data */
1265 /* now if we still have no link read in,
1266 * copy the data from the buffer */
1267 if ((temp = linkScp->length.LowPart) >= MOUNTPOINTLEN) {
1269 return CM_ERROR_TOOBIG;
1272 /* otherwise, it fits; make sure it is still null (could have
1273 * lost race with someone else referencing this link above),
1274 * and if so, copy in the data.
1276 if (!linkScp->mountPointStringp[0]) {
1277 strncpy(linkScp->mountPointStringp, bufp->datap, temp);
1278 linkScp->mountPointStringp[temp] = 0; /* null terminate */
1281 } /* don't have sym link contents cached */
1286 /* called with a held vnode and a path suffix, with the held vnode being a
1287 * symbolic link. Our goal is to generate a new path to interpret, and return
1288 * this new path in newSpaceBufferp. If the new vnode is relative to a dir
1289 * other than the directory containing the symbolic link, then the new root is
1290 * returned in *newRootScpp, otherwise a null is returned there.
1292 long cm_AssembleLink(cm_scache_t *linkScp, char *pathSuffixp,
1293 cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1294 cm_user_t *userp, cm_req_t *reqp)
1301 lock_ObtainMutex(&linkScp->mx);
1302 code = cm_HandleLink(linkScp, userp, reqp);
1306 /* if we may overflow the buffer, bail out; buffer is signficantly
1307 * bigger than max path length, so we don't really have to worry about
1308 * being a little conservative here.
1310 if (strlen(linkScp->mountPointStringp) + strlen(pathSuffixp) + 2
1311 >= CM_UTILS_SPACESIZE)
1312 return CM_ERROR_TOOBIG;
1314 tsp = cm_GetSpace();
1315 linkp = linkScp->mountPointStringp;
1316 if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1317 if (strlen(linkp) > cm_mountRootLen)
1318 strcpy(tsp->data, linkp+cm_mountRootLen+1);
1321 *newRootScpp = cm_data.rootSCachep;
1322 cm_HoldSCache(cm_data.rootSCachep);
1323 } else if (linkp[0] == '\\' && linkp[1] == '\\') {
1324 if (!strnicmp(&linkp[2], cm_NetbiosName, (len = strlen(cm_NetbiosName))))
1326 char * p = &linkp[len + 3];
1327 if (strnicmp(p, "all", 3) == 0)
1330 strcpy(tsp->data, p);
1331 for (p = tsp->data; *p; p++) {
1335 *newRootScpp = cm_data.rootSCachep;
1336 cm_HoldSCache(cm_data.rootSCachep);
1338 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1339 strcpy(tsp->data, linkp);
1340 *newRootScpp = NULL;
1341 code = CM_ERROR_PATH_NOT_COVERED;
1343 } else if ( !strnicmp(linkp, "msdfs:", (len = strlen("msdfs:"))) ) {
1344 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1345 strcpy(tsp->data, linkp);
1346 *newRootScpp = NULL;
1347 code = CM_ERROR_PATH_NOT_COVERED;
1348 } else if (*linkp == '\\' || *linkp == '/') {
1350 /* formerly, this was considered to be from the AFS root,
1351 * but this seems to create problems. instead, we will just
1352 * reject the link */
1353 strcpy(tsp->data, linkp+1);
1354 *newRootScpp = cm_data.rootSCachep;
1355 cm_HoldSCache(cm_data.rootSCachep);
1357 /* we still copy the link data into the response so that
1358 * the user can see what the link points to
1360 linkScp->fileType = CM_SCACHETYPE_INVALID;
1361 strcpy(tsp->data, linkp);
1362 *newRootScpp = NULL;
1363 code = CM_ERROR_NOSUCHPATH;
1366 /* a relative link */
1367 strcpy(tsp->data, linkp);
1368 *newRootScpp = NULL;
1370 if (pathSuffixp[0] != 0) { /* if suffix string is non-null */
1371 strcat(tsp->data, "\\");
1372 strcat(tsp->data, pathSuffixp);
1374 *newSpaceBufferp = tsp;
1377 lock_ReleaseMutex(&linkScp->mx);
1381 long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
1382 cm_user_t *userp, char *tidPathp, cm_req_t *reqp, cm_scache_t **outScpp)
1385 char *tp; /* ptr moving through input buffer */
1386 char tc; /* temp char */
1387 int haveComponent; /* has new component started? */
1388 char component[256]; /* this is the new component */
1389 char *cp; /* component name being assembled */
1390 cm_scache_t *tscp; /* current location in the hierarchy */
1391 cm_scache_t *nscp; /* next dude down */
1392 cm_scache_t *dirScp; /* last dir we searched */
1393 cm_scache_t *linkScp; /* new root for the symlink we just
1395 cm_space_t *psp; /* space for current path, if we've hit
1397 cm_space_t *tempsp; /* temp vbl */
1398 char *restp; /* rest of the pathname to interpret */
1399 int symlinkCount; /* count of # of symlinks traversed */
1400 int extraFlag; /* avoid chasing mt pts for dir cmd */
1401 int phase = 1; /* 1 = tidPathp, 2 = pathp */
1414 cm_HoldSCache(tscp);
1421 /* map Unix slashes into DOS ones so we can interpret Unix
1427 if (!haveComponent) {
1430 } else if (tc == 0) {
1444 /* we have a component here */
1445 if (tc == 0 || tc == '\\') {
1446 /* end of the component; we're at the last
1447 * component if tc == 0. However, if the last
1448 * is a symlink, we have more to do.
1450 *cp++ = 0; /* add null termination */
1452 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
1453 extraFlag = CM_FLAG_NOMOUNTCHASE;
1454 code = cm_Lookup(tscp, component,
1456 userp, reqp, &nscp);
1458 cm_ReleaseSCache(tscp);
1460 cm_ReleaseSCache(dirScp);
1463 if (code == CM_ERROR_NOSUCHFILE && tscp->fileType == CM_SCACHETYPE_SYMLINK)
1464 return CM_ERROR_NOSUCHPATH;
1468 haveComponent = 0; /* component done */
1470 cm_ReleaseSCache(dirScp);
1471 dirScp = tscp; /* for some symlinks */
1472 tscp = nscp; /* already held */
1474 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
1477 cm_ReleaseSCache(dirScp);
1483 /* now, if tscp is a symlink, we should follow
1484 * it and assemble the path again.
1486 lock_ObtainMutex(&tscp->mx);
1487 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1488 CM_SCACHESYNC_GETSTATUS
1489 | CM_SCACHESYNC_NEEDCALLBACK);
1491 lock_ReleaseMutex(&tscp->mx);
1492 cm_ReleaseSCache(tscp);
1495 cm_ReleaseSCache(dirScp);
1500 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
1501 /* this is a symlink; assemble a new buffer */
1502 lock_ReleaseMutex(&tscp->mx);
1503 if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
1504 cm_ReleaseSCache(tscp);
1507 cm_ReleaseSCache(dirScp);
1512 return CM_ERROR_TOO_MANY_SYMLINKS;
1518 code = cm_AssembleLink(tscp, restp, &linkScp, &tempsp, userp, reqp);
1520 /* something went wrong */
1521 cm_ReleaseSCache(tscp);
1524 cm_ReleaseSCache(dirScp);
1530 /* otherwise, tempsp has the new path,
1531 * and linkScp is the new root from
1532 * which to interpret that path.
1533 * Continue with the namei processing,
1534 * also doing the bookkeeping for the
1535 * space allocation and tracking the
1536 * vnode reference counts.
1542 cm_ReleaseSCache(tscp);
1547 * now, if linkScp is null, that's
1548 * AssembleLink's way of telling us that
1549 * the sym link is relative to the dir
1550 * containing the link. We have a ref
1551 * to it in dirScp, and we hold it now
1552 * and reuse it as the new spot in the
1560 /* not a symlink, we may be done */
1561 lock_ReleaseMutex(&tscp->mx);
1569 cm_ReleaseSCache(dirScp);
1577 cm_ReleaseSCache(dirScp);
1580 } /* end of a component */
1583 } /* we have a component */
1584 } /* big while loop over all components */
1588 cm_ReleaseSCache(dirScp);
1594 cm_ReleaseSCache(tscp);
1598 /* called with a dir, and a vnode within the dir that happens to be a symlink.
1599 * We chase the link, and return a held pointer to the target, if it exists,
1600 * in *outScpp. If we succeed, we return 0, otherwise we return an error code
1601 * and do not hold or return a target vnode.
1603 * This is very similar to calling cm_NameI with the last component of a name,
1604 * which happens to be a symlink, except that we've already passed by the name.
1606 * This function is typically called by the directory listing functions, which
1607 * encounter symlinks but need to return the proper file length so programs
1608 * like "more" work properly when they make use of the attributes retrieved from
1611 * The input vnode should not be locked when this function is called.
1613 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
1614 cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
1618 cm_scache_t *newRootScp;
1620 osi_Log1(afsd_logp, "Evaluating symlink scp 0x%x", linkScp);
1622 code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
1626 /* now, if newRootScp is NULL, we're really being told that the symlink
1627 * is relative to the current directory (dscp).
1629 if (newRootScp == NULL) {
1631 cm_HoldSCache(dscp);
1634 code = cm_NameI(newRootScp, spacep->data,
1635 CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
1636 userp, NULL, reqp, outScpp);
1638 if (code == CM_ERROR_NOSUCHFILE)
1639 code = CM_ERROR_NOSUCHPATH;
1641 /* this stuff is allocated no matter what happened on the namei call,
1643 cm_FreeSpace(spacep);
1644 cm_ReleaseSCache(newRootScp);
1649 /* make this big enough so that one buffer of dir pages won't overflow. We'll
1650 * check anyway, but we want to minimize the chance that we have to leave stuff
1653 #define CM_BULKMAX 128
1655 /* rock for bulk stat calls */
1656 typedef struct cm_bulkStat {
1657 osi_hyper_t bufOffset; /* only do it for things in this buffer page */
1659 /* info for the actual call */
1660 int counter; /* next free slot */
1661 AFSFid fids[CM_BULKMAX];
1662 AFSFetchStatus stats[CM_BULKMAX];
1663 AFSCallBack callbacks[CM_BULKMAX];
1666 /* for a given entry, make sure that it isn't in the stat cache, and then
1667 * add it to the list of file IDs to be obtained.
1669 * Don't bother adding it if we already have a vnode. Note that the dir
1670 * is locked, so we have to be careful checking the vnode we're thinking of
1671 * processing, to avoid deadlocks.
1673 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
1684 /* Don't overflow bsp. */
1685 if (bsp->counter >= CM_BULKMAX)
1686 return CM_ERROR_STOPNOW;
1688 thyper.LowPart = cm_data.buf_blockSize;
1689 thyper.HighPart = 0;
1690 thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
1692 /* thyper is now the first byte past the end of the record we're
1693 * interested in, and bsp->bufOffset is the first byte of the record
1694 * we're interested in.
1695 * Skip data in the others.
1698 if (LargeIntegerLessThan(*offp, bsp->bufOffset))
1700 if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
1701 return CM_ERROR_STOPNOW;
1702 if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
1705 tfid.cell = scp->fid.cell;
1706 tfid.volume = scp->fid.volume;
1707 tfid.vnode = ntohl(dep->fid.vnode);
1708 tfid.unique = ntohl(dep->fid.unique);
1709 tscp = cm_FindSCache(&tfid);
1711 if (lock_TryMutex(&tscp->mx)) {
1712 /* we have an entry that we can look at */
1713 if (cm_HaveCallback(tscp)) {
1714 /* we have a callback on it. Don't bother
1715 * fetching this stat entry, since we're happy
1716 * with the info we have.
1718 lock_ReleaseMutex(&tscp->mx);
1719 cm_ReleaseSCache(tscp);
1722 lock_ReleaseMutex(&tscp->mx);
1724 cm_ReleaseSCache(tscp);
1727 #ifdef AFS_FREELANCE_CLIENT
1728 // yj: if this is a mountpoint under root.afs then we don't want it
1729 // to be bulkstat-ed, instead, we call getSCache directly and under
1730 // getSCache, it is handled specially.
1731 if ( cm_freelanceEnabled &&
1732 tfid.cell==AFS_FAKE_ROOT_CELL_ID &&
1733 tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
1734 !(tfid.vnode==0x1 && tfid.unique==0x1) )
1736 osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
1737 return cm_GetSCache(&tfid, &tscp, NULL, NULL);
1739 #endif /* AFS_FREELANCE_CLIENT */
1742 bsp->fids[i].Volume = scp->fid.volume;
1743 bsp->fids[i].Vnode = tfid.vnode;
1744 bsp->fids[i].Unique = tfid.unique;
1748 /* called with a locked scp and a pointer to a buffer. Make bulk stat
1749 * calls on all undeleted files in the page of the directory specified.
1751 void cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
1755 cm_bulkStat_t bb; /* this is *BIG*, probably 12K or so;
1756 * watch for stack problems */
1757 AFSCBFids fidStruct;
1758 AFSBulkStats statStruct;
1760 AFSCBs callbackStruct;
1763 cm_callbackRequest_t cbReq;
1769 struct rx_connection * callp;
1771 osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%x", (long) dscp);
1773 /* should be on a buffer boundary */
1774 osi_assert((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0);
1777 bb.bufOffset = *offsetp;
1779 lock_ReleaseMutex(&dscp->mx);
1780 /* first, assemble the file IDs we need to stat */
1781 code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) &bb, offsetp, userp, reqp, NULL);
1782 lock_ObtainMutex(&dscp->mx);
1784 /* if we failed, bail out early */
1785 if (code && code != CM_ERROR_STOPNOW)
1788 /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
1789 * make the calls to create the entries. Handle AFSCBMAX files at a
1793 while (filex < bb.counter) {
1794 filesThisCall = bb.counter - filex;
1795 if (filesThisCall > AFSCBMAX)
1796 filesThisCall = AFSCBMAX;
1798 fidStruct.AFSCBFids_len = filesThisCall;
1799 fidStruct.AFSCBFids_val = &bb.fids[filex];
1800 statStruct.AFSBulkStats_len = filesThisCall;
1801 statStruct.AFSBulkStats_val = &bb.stats[filex];
1802 callbackStruct.AFSCBs_len = filesThisCall;
1803 callbackStruct.AFSCBs_val = &bb.callbacks[filex];
1804 cm_StartCallbackGrantingCall(NULL, &cbReq);
1805 osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
1807 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1811 callp = cm_GetRxConn(connp);
1812 code = RXAFS_BulkStatus(callp, &fidStruct,
1813 &statStruct, &callbackStruct, &volSync);
1814 rx_PutConnection(callp);
1816 } while (cm_Analyze(connp, userp, reqp, &dscp->fid,
1817 &volSync, NULL, &cbReq, code));
1818 code = cm_MapRPCError(code, reqp);
1821 osi_Log1(afsd_logp, "CALL BulkStatus FAILURE code 0x%x", code);
1823 osi_Log0(afsd_logp, "CALL BulkStatus SUCCESS");
1825 /* may as well quit on an error, since we're not going to do
1826 * much better on the next immediate call, either.
1829 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
1833 /* otherwise, we should do the merges */
1834 for (i = 0; i<filesThisCall; i++) {
1836 tfid.cell = dscp->fid.cell;
1837 tfid.volume = bb.fids[j].Volume;
1838 tfid.vnode = bb.fids[j].Vnode;
1839 tfid.unique = bb.fids[j].Unique;
1840 code = cm_GetSCache(&tfid, &scp, userp, reqp);
1844 /* otherwise, if this entry has no callback info,
1847 lock_ObtainMutex(&scp->mx);
1848 /* now, we have to be extra paranoid on merging in this
1849 * information, since we didn't use cm_SyncOp before
1850 * starting the fetch to make sure that no bad races
1851 * were occurring. Specifically, we need to make sure
1852 * we don't obliterate any newer information in the
1853 * vnode than have here.
1855 * Right now, be pretty conservative: if there's a
1856 * callback or a pending call, skip it.
1858 if (scp->cbServerp == NULL
1860 (CM_SCACHEFLAG_FETCHING
1861 | CM_SCACHEFLAG_STORING
1862 | CM_SCACHEFLAG_SIZESTORING))) {
1863 cm_EndCallbackGrantingCall(scp, &cbReq,
1865 CM_CALLBACK_MAINTAINCOUNT);
1866 cm_MergeStatus(scp, &bb.stats[j], &volSync,
1869 lock_ReleaseMutex(&scp->mx);
1870 cm_ReleaseSCache(scp);
1871 } /* all files in the response */
1872 /* now tell it to drop the count,
1873 * after doing the vnode processing above */
1874 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
1876 filex += filesThisCall;
1877 } /* while there are still more files to process */
1878 osi_Log0(afsd_logp, "END cm_TryBulkStat");
1881 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
1885 /* initialize store back mask as inexpensive local variable */
1887 memset(statusp, 0, sizeof(AFSStoreStatus));
1889 /* copy out queued info from scache first, if scp passed in */
1891 if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
1892 statusp->ClientModTime = scp->clientModTime;
1893 mask |= AFS_SETMODTIME;
1894 scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
1899 /* now add in our locally generated request */
1900 if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
1901 statusp->ClientModTime = attrp->clientModTime;
1902 mask |= AFS_SETMODTIME;
1904 if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
1905 statusp->UnixModeBits = attrp->unixModeBits;
1906 mask |= AFS_SETMODE;
1908 if (attrp->mask & CM_ATTRMASK_OWNER) {
1909 statusp->Owner = attrp->owner;
1910 mask |= AFS_SETOWNER;
1912 if (attrp->mask & CM_ATTRMASK_GROUP) {
1913 statusp->Group = attrp->group;
1914 mask |= AFS_SETGROUP;
1917 statusp->Mask = mask;
1920 /* set the file size, and make sure that all relevant buffers have been
1921 * truncated. Ensure that any partially truncated buffers have been zeroed
1922 * to the end of the buffer.
1924 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
1930 /* start by locking out buffer creation */
1931 lock_ObtainWrite(&scp->bufCreateLock);
1933 /* verify that this is a file, not a dir or a symlink */
1934 lock_ObtainMutex(&scp->mx);
1935 code = cm_SyncOp(scp, NULL, userp, reqp, 0,
1936 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1940 if (scp->fileType != CM_SCACHETYPE_FILE) {
1941 code = CM_ERROR_ISDIR;
1946 if (LargeIntegerLessThan(*sizep, scp->length))
1951 lock_ReleaseMutex(&scp->mx);
1953 /* can't hold scp->mx lock here, since we may wait for a storeback to
1954 * finish if the buffer package is cleaning a buffer by storing it to
1958 buf_Truncate(scp, userp, reqp, sizep);
1960 /* now ensure that file length is short enough, and update truncPos */
1961 lock_ObtainMutex(&scp->mx);
1963 /* make sure we have a callback (so we have the right value for the
1964 * length), and wait for it to be safe to do a truncate.
1966 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
1967 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
1968 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
1972 if (LargeIntegerLessThan(*sizep, scp->length)) {
1973 /* a real truncation. If truncPos is not set yet, or is bigger
1974 * than where we're truncating the file, set truncPos to this
1979 if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
1980 || LargeIntegerLessThan(*sizep, scp->length)) {
1982 scp->truncPos = *sizep;
1983 scp->mask |= CM_SCACHEMASK_TRUNCPOS;
1985 /* in either case, the new file size has been changed */
1986 scp->length = *sizep;
1987 scp->mask |= CM_SCACHEMASK_LENGTH;
1989 else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
1990 /* really extending the file */
1991 scp->length = *sizep;
1992 scp->mask |= CM_SCACHEMASK_LENGTH;
1995 /* done successfully */
1999 lock_ReleaseMutex(&scp->mx);
2000 lock_ReleaseWrite(&scp->bufCreateLock);
2005 /* set the file size or other attributes (but not both at once) */
2006 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
2011 AFSFetchStatus afsOutStatus;
2015 AFSStoreStatus afsInStatus;
2016 struct rx_connection * callp;
2018 /* handle file length setting */
2019 if (attrp->mask & CM_ATTRMASK_LENGTH)
2020 return cm_SetLength(scp, &attrp->length, userp, reqp);
2022 flags = CM_SCACHESYNC_STORESTATUS;
2024 lock_ObtainMutex(&scp->mx);
2025 /* otherwise, we have to make an RPC to get the status */
2026 code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
2028 /* make the attr structure */
2029 cm_StatusFromAttr(&afsInStatus, scp, attrp);
2031 lock_ReleaseMutex(&scp->mx);
2035 /* now make the RPC */
2036 osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%x", (long) scp);
2037 tfid.Volume = scp->fid.volume;
2038 tfid.Vnode = scp->fid.vnode;
2039 tfid.Unique = scp->fid.unique;
2041 code = cm_Conn(&scp->fid, userp, reqp, &connp);
2045 callp = cm_GetRxConn(connp);
2046 code = RXAFS_StoreStatus(callp, &tfid,
2047 &afsInStatus, &afsOutStatus, &volSync);
2048 rx_PutConnection(callp);
2050 } while (cm_Analyze(connp, userp, reqp,
2051 &scp->fid, &volSync, NULL, NULL, code));
2052 code = cm_MapRPCError(code, reqp);
2055 osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
2057 osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
2059 lock_ObtainMutex(&scp->mx);
2060 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
2062 cm_MergeStatus(scp, &afsOutStatus, &volSync, userp,
2063 CM_MERGEFLAG_FORCE);
2065 /* if we're changing the mode bits, discard the ACL cache,
2066 * since we changed the mode bits.
2068 if (afsInStatus.Mask & AFS_SETMODE) cm_FreeAllACLEnts(scp);
2069 lock_ReleaseMutex(&scp->mx);
2073 long cm_Create(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2074 cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
2079 cm_callbackRequest_t cbReq;
2084 AFSStoreStatus inStatus;
2085 AFSFetchStatus updatedDirStatus;
2086 AFSFetchStatus newFileStatus;
2087 AFSCallBack newFileCallback;
2089 struct rx_connection * callp;
2091 /* can't create names with @sys in them; must expand it manually first.
2092 * return "invalid request" if they try.
2094 if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2095 return CM_ERROR_ATSYS;
2098 /* before starting the RPC, mark that we're changing the file data, so
2099 * that someone who does a chmod will know to wait until our call
2102 lock_ObtainMutex(&dscp->mx);
2103 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2105 cm_StartCallbackGrantingCall(NULL, &cbReq);
2107 lock_ReleaseMutex(&dscp->mx);
2113 cm_StatusFromAttr(&inStatus, NULL, attrp);
2115 /* try the RPC now */
2116 osi_Log1(afsd_logp, "CALL CreateFile scp 0x%x", (long) dscp);
2118 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2122 dirAFSFid.Volume = dscp->fid.volume;
2123 dirAFSFid.Vnode = dscp->fid.vnode;
2124 dirAFSFid.Unique = dscp->fid.unique;
2126 callp = cm_GetRxConn(connp);
2127 code = RXAFS_CreateFile(connp->callp, &dirAFSFid, namep,
2128 &inStatus, &newAFSFid, &newFileStatus,
2129 &updatedDirStatus, &newFileCallback,
2131 rx_PutConnection(callp);
2133 } while (cm_Analyze(connp, userp, reqp,
2134 &dscp->fid, &volSync, NULL, &cbReq, code));
2135 code = cm_MapRPCError(code, reqp);
2138 osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
2140 osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
2142 lock_ObtainMutex(&dscp->mx);
2143 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2145 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2147 lock_ReleaseMutex(&dscp->mx);
2149 /* now try to create the file's entry, too, but be careful to
2150 * make sure that we don't merge in old info. Since we weren't locking
2151 * out any requests during the file's creation, we may have pretty old
2155 newFid.cell = dscp->fid.cell;
2156 newFid.volume = dscp->fid.volume;
2157 newFid.vnode = newAFSFid.Vnode;
2158 newFid.unique = newAFSFid.Unique;
2159 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2161 lock_ObtainMutex(&scp->mx);
2162 if (!cm_HaveCallback(scp)) {
2163 cm_MergeStatus(scp, &newFileStatus, &volSync,
2165 cm_EndCallbackGrantingCall(scp, &cbReq,
2166 &newFileCallback, 0);
2169 lock_ReleaseMutex(&scp->mx);
2174 /* make sure we end things properly */
2176 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2181 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
2185 lock_ObtainWrite(&scp->bufCreateLock);
2186 code = buf_CleanVnode(scp, userp, reqp);
2187 lock_ReleaseWrite(&scp->bufCreateLock);
2189 lock_ObtainMutex(&scp->mx);
2190 scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA
2191 | CM_SCACHEFLAG_OUTOFSPACE);
2192 if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
2193 | CM_SCACHEMASK_CLIENTMODTIME
2194 | CM_SCACHEMASK_LENGTH))
2195 code = cm_StoreMini(scp, userp, reqp);
2196 lock_ReleaseMutex(&scp->mx);
2201 long cm_MakeDir(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2202 cm_user_t *userp, cm_req_t *reqp)
2207 cm_callbackRequest_t cbReq;
2212 AFSStoreStatus inStatus;
2213 AFSFetchStatus updatedDirStatus;
2214 AFSFetchStatus newDirStatus;
2215 AFSCallBack newDirCallback;
2217 struct rx_connection * callp;
2219 /* can't create names with @sys in them; must expand it manually first.
2220 * return "invalid request" if they try.
2222 if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2223 return CM_ERROR_ATSYS;
2226 /* before starting the RPC, mark that we're changing the directory
2227 * data, so that someone who does a chmod on the dir will wait until
2228 * our call completes.
2230 lock_ObtainMutex(&dscp->mx);
2231 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2233 cm_StartCallbackGrantingCall(NULL, &cbReq);
2235 lock_ReleaseMutex(&dscp->mx);
2241 cm_StatusFromAttr(&inStatus, NULL, attrp);
2243 /* try the RPC now */
2244 osi_Log1(afsd_logp, "CALL MakeDir scp 0x%x", (long) dscp);
2246 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2250 dirAFSFid.Volume = dscp->fid.volume;
2251 dirAFSFid.Vnode = dscp->fid.vnode;
2252 dirAFSFid.Unique = dscp->fid.unique;
2254 callp = cm_GetRxConn(connp);
2255 code = RXAFS_MakeDir(connp->callp, &dirAFSFid, namep,
2256 &inStatus, &newAFSFid, &newDirStatus,
2257 &updatedDirStatus, &newDirCallback,
2259 rx_PutConnection(callp);
2261 } while (cm_Analyze(connp, userp, reqp,
2262 &dscp->fid, &volSync, NULL, &cbReq, code));
2263 code = cm_MapRPCError(code, reqp);
2266 osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
2268 osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
2270 lock_ObtainMutex(&dscp->mx);
2271 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2273 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2275 lock_ReleaseMutex(&dscp->mx);
2277 /* now try to create the new dir's entry, too, but be careful to
2278 * make sure that we don't merge in old info. Since we weren't locking
2279 * out any requests during the file's creation, we may have pretty old
2283 newFid.cell = dscp->fid.cell;
2284 newFid.volume = dscp->fid.volume;
2285 newFid.vnode = newAFSFid.Vnode;
2286 newFid.unique = newAFSFid.Unique;
2287 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2289 lock_ObtainMutex(&scp->mx);
2290 if (!cm_HaveCallback(scp)) {
2291 cm_MergeStatus(scp, &newDirStatus, &volSync,
2293 cm_EndCallbackGrantingCall(scp, &cbReq,
2294 &newDirCallback, 0);
2297 lock_ReleaseMutex(&scp->mx);
2298 cm_ReleaseSCache(scp);
2302 /* make sure we end things properly */
2304 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2306 /* and return error code */
2310 long cm_Link(cm_scache_t *dscp, char *namep, cm_scache_t *sscp, long flags,
2311 cm_user_t *userp, cm_req_t *reqp)
2316 AFSFid existingAFSFid;
2317 AFSFetchStatus updatedDirStatus;
2318 AFSFetchStatus newLinkStatus;
2320 struct rx_connection * callp;
2322 if (dscp->fid.cell != sscp->fid.cell ||
2323 dscp->fid.volume != sscp->fid.volume) {
2324 return CM_ERROR_CROSSDEVLINK;
2327 lock_ObtainMutex(&dscp->mx);
2328 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2329 lock_ReleaseMutex(&dscp->mx);
2334 /* try the RPC now */
2335 osi_Log1(afsd_logp, "CALL Link scp 0x%x", (long) dscp);
2337 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2340 dirAFSFid.Volume = dscp->fid.volume;
2341 dirAFSFid.Vnode = dscp->fid.vnode;
2342 dirAFSFid.Unique = dscp->fid.unique;
2344 existingAFSFid.Volume = sscp->fid.volume;
2345 existingAFSFid.Vnode = sscp->fid.vnode;
2346 existingAFSFid.Unique = sscp->fid.unique;
2348 callp = cm_GetRxConn(connp);
2349 code = RXAFS_Link(callp, &dirAFSFid, namep, &existingAFSFid,
2350 &newLinkStatus, &updatedDirStatus, &volSync);
2351 rx_PutConnection(callp);
2352 osi_Log1(smb_logp," RXAFS_Link returns 0x%x", code);
2354 } while (cm_Analyze(connp, userp, reqp,
2355 &dscp->fid, &volSync, NULL, NULL, code));
2357 code = cm_MapRPCError(code, reqp);
2360 osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
2362 osi_Log0(afsd_logp, "CALL Link SUCCESS");
2364 lock_ObtainMutex(&dscp->mx);
2365 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2367 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2369 lock_ReleaseMutex(&dscp->mx);
2374 long cm_SymLink(cm_scache_t *dscp, char *namep, char *contentsp, long flags,
2375 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
2383 AFSStoreStatus inStatus;
2384 AFSFetchStatus updatedDirStatus;
2385 AFSFetchStatus newLinkStatus;
2387 struct rx_connection * callp;
2389 /* before starting the RPC, mark that we're changing the directory data,
2390 * so that someone who does a chmod on the dir will wait until our
2393 lock_ObtainMutex(&dscp->mx);
2394 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2395 lock_ReleaseMutex(&dscp->mx);
2400 cm_StatusFromAttr(&inStatus, NULL, attrp);
2402 /* try the RPC now */
2403 osi_Log1(afsd_logp, "CALL Symlink scp 0x%x", (long) dscp);
2405 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2409 dirAFSFid.Volume = dscp->fid.volume;
2410 dirAFSFid.Vnode = dscp->fid.vnode;
2411 dirAFSFid.Unique = dscp->fid.unique;
2413 callp = cm_GetRxConn(connp);
2414 code = RXAFS_Symlink(callp, &dirAFSFid, namep, contentsp,
2415 &inStatus, &newAFSFid, &newLinkStatus,
2416 &updatedDirStatus, &volSync);
2417 rx_PutConnection(callp);
2419 } while (cm_Analyze(connp, userp, reqp,
2420 &dscp->fid, &volSync, NULL, NULL, code));
2421 code = cm_MapRPCError(code, reqp);
2424 osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
2426 osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
2428 lock_ObtainMutex(&dscp->mx);
2429 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2431 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2433 lock_ReleaseMutex(&dscp->mx);
2435 /* now try to create the new dir's entry, too, but be careful to
2436 * make sure that we don't merge in old info. Since we weren't locking
2437 * out any requests during the file's creation, we may have pretty old
2441 newFid.cell = dscp->fid.cell;
2442 newFid.volume = dscp->fid.volume;
2443 newFid.vnode = newAFSFid.Vnode;
2444 newFid.unique = newAFSFid.Unique;
2445 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2447 lock_ObtainMutex(&scp->mx);
2448 if (!cm_HaveCallback(scp)) {
2449 cm_MergeStatus(scp, &newLinkStatus, &volSync,
2452 lock_ReleaseMutex(&scp->mx);
2453 cm_ReleaseSCache(scp);
2457 /* and return error code */
2461 long cm_RemoveDir(cm_scache_t *dscp, char *namep, cm_user_t *userp,
2468 AFSFetchStatus updatedDirStatus;
2470 struct rx_connection * callp;
2472 /* before starting the RPC, mark that we're changing the directory data,
2473 * so that someone who does a chmod on the dir will wait until our
2476 lock_ObtainMutex(&dscp->mx);
2477 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2478 lock_ReleaseMutex(&dscp->mx);
2484 /* try the RPC now */
2485 osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%x", (long) dscp);
2487 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2491 dirAFSFid.Volume = dscp->fid.volume;
2492 dirAFSFid.Vnode = dscp->fid.vnode;
2493 dirAFSFid.Unique = dscp->fid.unique;
2495 callp = cm_GetRxConn(connp);
2496 code = RXAFS_RemoveDir(callp, &dirAFSFid, namep,
2497 &updatedDirStatus, &volSync);
2498 rx_PutConnection(callp);
2500 } while (cm_Analyze(connp, userp, reqp,
2501 &dscp->fid, &volSync, NULL, NULL, code));
2502 code = cm_MapRPCErrorRmdir(code, reqp);
2505 osi_Log1(afsd_logp, "CALL RemoveDir FAILURE, code 0x%x", code);
2507 osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
2509 lock_ObtainMutex(&dscp->mx);
2510 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2512 cm_dnlcRemove(dscp, namep);
2513 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2515 lock_ReleaseMutex(&dscp->mx);
2517 /* and return error code */
2521 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
2523 /* grab mutex on contents */
2524 lock_ObtainMutex(&scp->mx);
2526 /* reset the prefetch info */
2527 scp->prefetch.base.LowPart = 0; /* base */
2528 scp->prefetch.base.HighPart = 0;
2529 scp->prefetch.end.LowPart = 0; /* and end */
2530 scp->prefetch.end.HighPart = 0;
2532 /* release mutex on contents */
2533 lock_ReleaseMutex(&scp->mx);
2539 long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
2540 char *newNamep, cm_user_t *userp, cm_req_t *reqp)
2544 AFSFid oldDirAFSFid;
2545 AFSFid newDirAFSFid;
2547 AFSFetchStatus updatedOldDirStatus;
2548 AFSFetchStatus updatedNewDirStatus;
2551 struct rx_connection * callp;
2553 /* before starting the RPC, mark that we're changing the directory data,
2554 * so that someone who does a chmod on the dir will wait until our call
2555 * completes. We do this in vnode order so that we don't deadlock,
2556 * which makes the code a little verbose.
2558 if (oldDscp == newDscp) {
2559 /* check for identical names */
2560 if (strcmp(oldNamep, newNamep) == 0)
2561 return CM_ERROR_RENAME_IDENTICAL;
2564 lock_ObtainMutex(&oldDscp->mx);
2565 cm_dnlcRemove(oldDscp, oldNamep);
2566 cm_dnlcRemove(oldDscp, newNamep);
2567 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2568 CM_SCACHESYNC_STOREDATA);
2569 lock_ReleaseMutex(&oldDscp->mx);
2572 /* two distinct dir vnodes */
2574 if (oldDscp->fid.cell != newDscp->fid.cell ||
2575 oldDscp->fid.volume != newDscp->fid.volume)
2576 return CM_ERROR_CROSSDEVLINK;
2578 /* shouldn't happen that we have distinct vnodes for two
2579 * different files, but could due to deliberate attack, or
2580 * stale info. Avoid deadlocks and quit now.
2582 if (oldDscp->fid.vnode == newDscp->fid.vnode)
2583 return CM_ERROR_CROSSDEVLINK;
2585 if (oldDscp->fid.vnode < newDscp->fid.vnode) {
2586 lock_ObtainMutex(&oldDscp->mx);
2587 cm_dnlcRemove(oldDscp, oldNamep);
2588 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2589 CM_SCACHESYNC_STOREDATA);
2590 lock_ReleaseMutex(&oldDscp->mx);
2592 lock_ObtainMutex(&newDscp->mx);
2593 cm_dnlcRemove(newDscp, newNamep);
2594 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2595 CM_SCACHESYNC_STOREDATA);
2596 lock_ReleaseMutex(&newDscp->mx);
2598 /* cleanup first one */
2599 cm_SyncOpDone(oldDscp, NULL,
2600 CM_SCACHESYNC_STOREDATA);
2605 /* lock the new vnode entry first */
2606 lock_ObtainMutex(&newDscp->mx);
2607 cm_dnlcRemove(newDscp, newNamep);
2608 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2609 CM_SCACHESYNC_STOREDATA);
2610 lock_ReleaseMutex(&newDscp->mx);
2612 lock_ObtainMutex(&oldDscp->mx);
2613 cm_dnlcRemove(oldDscp, oldNamep);
2614 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2615 CM_SCACHESYNC_STOREDATA);
2616 lock_ReleaseMutex(&oldDscp->mx);
2618 /* cleanup first one */
2619 cm_SyncOpDone(newDscp, NULL,
2620 CM_SCACHESYNC_STOREDATA);
2624 } /* two distinct vnodes */
2631 /* try the RPC now */
2632 osi_Log2(afsd_logp, "CALL Rename old scp 0x%x new scp 0x%x",
2633 (long) oldDscp, (long) newDscp);
2635 code = cm_Conn(&oldDscp->fid, userp, reqp, &connp);
2639 oldDirAFSFid.Volume = oldDscp->fid.volume;
2640 oldDirAFSFid.Vnode = oldDscp->fid.vnode;
2641 oldDirAFSFid.Unique = oldDscp->fid.unique;
2642 newDirAFSFid.Volume = newDscp->fid.volume;
2643 newDirAFSFid.Vnode = newDscp->fid.vnode;
2644 newDirAFSFid.Unique = newDscp->fid.unique;
2646 callp = cm_GetRxConn(connp);
2647 code = RXAFS_Rename(callp, &oldDirAFSFid, oldNamep,
2648 &newDirAFSFid, newNamep,
2649 &updatedOldDirStatus, &updatedNewDirStatus,
2651 rx_PutConnection(callp);
2653 } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
2654 &volSync, NULL, NULL, code));
2655 code = cm_MapRPCError(code, reqp);
2658 osi_Log1(afsd_logp, "CALL Rename FAILURE, code 0x%x", code);
2660 osi_Log0(afsd_logp, "CALL Rename SUCCESS");
2662 /* update the individual stat cache entries for the directories */
2663 lock_ObtainMutex(&oldDscp->mx);
2664 cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
2666 cm_MergeStatus(oldDscp, &updatedOldDirStatus, &volSync,
2669 lock_ReleaseMutex(&oldDscp->mx);
2671 /* and update it for the new one, too, if necessary */
2673 lock_ObtainMutex(&newDscp->mx);
2674 cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
2676 cm_MergeStatus(newDscp, &updatedNewDirStatus, &volSync,
2679 lock_ReleaseMutex(&newDscp->mx);
2682 /* and return error code */
2686 long cm_Lock(cm_scache_t *scp, unsigned char LockType,
2687 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
2688 u_long Timeout, cm_user_t *userp, cm_req_t *reqp,
2692 int Which = ((LockType & 0x1) ? LockRead : LockWrite);
2696 cm_file_lock_t *fileLock;
2699 struct rx_connection * callp;
2701 /* Look for a conflict. Also, if we are asking for a shared lock,
2702 * look for another shared lock, so we don't have to do an RPC.
2706 fileLock = (cm_file_lock_t *)
2707 ((char *) q - offsetof(cm_file_lock_t, fileq));
2708 if ((fileLock->flags &
2709 (CM_FILELOCK_FLAG_INVALID | CM_FILELOCK_FLAG_WAITING))
2711 if ((LockType & 0x1) == 0
2712 || (fileLock->LockType & 0x1) == 0)
2713 return CM_ERROR_WOULDBLOCK;
2722 tfid.Volume = scp->fid.volume;
2723 tfid.Vnode = scp->fid.vnode;
2724 tfid.Unique = scp->fid.unique;
2725 lock_ReleaseMutex(&scp->mx);
2727 code = cm_Conn(&scp->fid, userp, reqp, &connp);
2731 callp = cm_GetRxConn(connp);
2732 code = RXAFS_SetLock(callp, &tfid, Which,
2734 rx_PutConnection(callp);
2736 } while (cm_Analyze(connp, userp, reqp, &scp->fid, &volSync,
2738 lock_ObtainMutex(&scp->mx);
2739 code = cm_MapRPCError(code, reqp);
2742 if (code == 0 || Timeout != 0) {
2743 fileLock = malloc(sizeof(cm_file_lock_t));
2744 fileLock->LockType = LockType;
2746 fileLock->userp = userp;
2747 fileLock->fid = scp->fid;
2748 fileLock->LOffset = LOffset;
2749 fileLock->LLength = LLength;
2750 fileLock->flags = (code == 0 ? 0 : CM_FILELOCK_FLAG_WAITING);
2751 osi_QAdd(&scp->fileLocks, &fileLock->fileq);
2752 lock_ObtainWrite(&cm_scacheLock);
2753 osi_QAdd(&cm_allFileLocks, &fileLock->q);
2754 lock_ReleaseWrite(&cm_scacheLock);
2761 long cm_Unlock(cm_scache_t *scp, unsigned char LockType,
2762 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
2763 cm_user_t *userp, cm_req_t *reqp)
2766 int Which = ((LockType & 0x1) ? LockRead : LockWrite);
2770 cm_file_lock_t *fileLock, *ourLock;
2771 osi_queue_t *q, *qq;
2772 int anotherReader = 0;
2775 struct rx_connection * callp;
2777 if (LargeIntegerLessThan(LLength, scp->length))
2780 /* Look for our own lock on the list, so as to remove it.
2781 * Also, determine if we're the last reader; if not, avoid an RPC.
2785 fileLock = (cm_file_lock_t *)
2786 ((char *) q - offsetof(cm_file_lock_t, fileq));
2788 && fileLock->userp == userp
2789 && LargeIntegerEqualTo(fileLock->LOffset, LOffset)
2790 && LargeIntegerEqualTo(fileLock->LLength, LLength)) {
2795 else if (fileLock->LockType & 0x1)
2800 /* ignore byte ranges */
2801 if (smallLock && !found)
2804 /* don't try to unlock other people's locks */
2806 return CM_ERROR_WOULDBLOCK;
2808 /* discard lock record */
2809 osi_QRemove(&scp->fileLocks, qq);
2811 * Don't delete it here; let the daemon delete it, to simplify
2812 * the daemon's traversal of the list.
2814 lock_ObtainWrite(&cm_scacheLock);
2815 ourLock->flags |= CM_FILELOCK_FLAG_INVALID;
2816 cm_ReleaseUser(ourLock->userp);
2817 lock_ReleaseWrite(&cm_scacheLock);
2819 if (!anotherReader) {
2820 tfid.Volume = scp->fid.volume;
2821 tfid.Vnode = scp->fid.vnode;
2822 tfid.Unique = scp->fid.unique;
2823 lock_ReleaseMutex(&scp->mx);
2824 osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%x", (long) scp);
2826 code = cm_Conn(&scp->fid, userp, reqp, &connp);
2830 callp = cm_GetRxConn(connp);
2831 code = RXAFS_ReleaseLock(callp, &tfid, &volSync);
2832 rx_PutConnection(callp);
2834 } while (cm_Analyze(connp, userp, reqp, &scp->fid, &volSync,
2836 code = cm_MapRPCError(code, reqp);
2839 osi_Log1(afsd_logp, "CALL ReleaseLock FAILURE, code 0x%x", code);
2841 osi_Log0(afsd_logp, "CALL ReleaseLock SUCCESS");
2843 lock_ObtainMutex(&scp->mx);
2849 void cm_CheckLocks()
2851 osi_queue_t *q, *nq;
2852 cm_file_lock_t *fileLock;
2858 struct rx_connection * callp;
2862 lock_ObtainWrite(&cm_scacheLock);
2863 q = cm_allFileLocks;
2865 fileLock = (cm_file_lock_t *) q;
2867 if (fileLock->flags & CM_FILELOCK_FLAG_INVALID) {
2868 osi_QRemove(&cm_allFileLocks, q);
2871 else if (!(fileLock->flags & CM_FILELOCK_FLAG_WAITING)) {
2872 tfid.Volume = fileLock->fid.volume;
2873 tfid.Vnode = fileLock->fid.vnode;
2874 tfid.Unique = fileLock->fid.unique;
2875 lock_ReleaseWrite(&cm_scacheLock);
2876 osi_Log1(afsd_logp, "CALL ExtendLock lock 0x%x", (long) fileLock);
2878 code = cm_Conn(&fileLock->fid, fileLock->userp,
2883 callp = cm_GetRxConn(connp);
2884 code = RXAFS_ExtendLock(callp, &tfid,
2886 rx_PutConnection(callp);
2888 } while (cm_Analyze(connp, fileLock->userp, &req,
2889 &fileLock->fid, &volSync, NULL, NULL,
2891 code = cm_MapRPCError(code, &req);
2893 osi_Log1(afsd_logp, "CALL ExtendLock FAILURE, code 0x%x", code);
2895 osi_Log0(afsd_logp, "CALL ExtendLock SUCCESS");
2897 lock_ObtainWrite(&cm_scacheLock);
2901 lock_ReleaseWrite(&cm_scacheLock);
2904 long cm_RetryLock(cm_file_lock_t *oldFileLock, int vcp_is_dead)
2907 int Which = ((oldFileLock->LockType & 0x1) ? LockRead : LockWrite);
2912 cm_file_lock_t *fileLock;
2916 struct rx_connection * callp;
2919 code = CM_ERROR_TIMEDOUT;
2925 /* Look for a conflict. Also, if we are asking for a shared lock,
2926 * look for another shared lock, so we don't have to do an RPC.
2928 code = cm_GetSCache(&oldFileLock->fid, &scp, oldFileLock->userp, &req);
2934 fileLock = (cm_file_lock_t *)
2935 ((char *) q - offsetof(cm_file_lock_t, fileq));
2936 if ((fileLock->flags &
2937 (CM_FILELOCK_FLAG_INVALID | CM_FILELOCK_FLAG_WAITING))
2939 if ((oldFileLock->LockType & 0x1) == 0
2940 || (fileLock->LockType & 0x1) == 0) {
2941 cm_ReleaseSCache(scp);
2942 return CM_ERROR_WOULDBLOCK;
2952 tfid.Volume = oldFileLock->fid.volume;
2953 tfid.Vnode = oldFileLock->fid.vnode;
2954 tfid.Unique = oldFileLock->fid.unique;
2955 osi_Log1(afsd_logp, "CALL SetLock lock 0x%x", (long) oldFileLock);
2957 code = cm_Conn(&oldFileLock->fid, oldFileLock->userp,
2962 callp = cm_GetRxConn(connp);
2963 code = RXAFS_SetLock(callp, &tfid, Which,
2965 rx_PutConnection(callp);
2967 } while (cm_Analyze(connp, oldFileLock->userp, &req,
2968 &oldFileLock->fid, &volSync,
2970 code = cm_MapRPCError(code, &req);
2973 osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
2975 osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
2979 if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
2980 lock_ObtainMutex(&scp->mx);
2981 osi_QRemove(&scp->fileLocks, &oldFileLock->fileq);
2982 lock_ReleaseMutex(&scp->mx);
2984 lock_ObtainWrite(&cm_scacheLock);
2986 oldFileLock->flags = 0;
2987 else if (code != CM_ERROR_WOULDBLOCK) {
2988 oldFileLock->flags |= CM_FILELOCK_FLAG_INVALID;
2989 cm_ReleaseUser(oldFileLock->userp);
2990 oldFileLock->userp = NULL;
2992 lock_ReleaseWrite(&cm_scacheLock);