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);
1783 /* if we failed, bail out early */
1784 if (code && code != CM_ERROR_STOPNOW) {
1785 lock_ObtainMutex(&dscp->mx);
1789 /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
1790 * make the calls to create the entries. Handle AFSCBMAX files at a
1794 while (filex < bb.counter) {
1795 filesThisCall = bb.counter - filex;
1796 if (filesThisCall > AFSCBMAX)
1797 filesThisCall = AFSCBMAX;
1799 fidStruct.AFSCBFids_len = filesThisCall;
1800 fidStruct.AFSCBFids_val = &bb.fids[filex];
1801 statStruct.AFSBulkStats_len = filesThisCall;
1802 statStruct.AFSBulkStats_val = &bb.stats[filex];
1803 callbackStruct.AFSCBs_len = filesThisCall;
1804 callbackStruct.AFSCBs_val = &bb.callbacks[filex];
1805 cm_StartCallbackGrantingCall(NULL, &cbReq);
1806 osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
1808 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1812 callp = cm_GetRxConn(connp);
1813 code = RXAFS_BulkStatus(callp, &fidStruct,
1814 &statStruct, &callbackStruct, &volSync);
1815 rx_PutConnection(callp);
1817 } while (cm_Analyze(connp, userp, reqp, &dscp->fid,
1818 &volSync, NULL, &cbReq, code));
1819 code = cm_MapRPCError(code, reqp);
1822 osi_Log1(afsd_logp, "CALL BulkStatus FAILURE code 0x%x", code);
1824 osi_Log0(afsd_logp, "CALL BulkStatus SUCCESS");
1826 /* may as well quit on an error, since we're not going to do
1827 * much better on the next immediate call, either.
1830 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
1834 /* otherwise, we should do the merges */
1835 for (i = 0; i<filesThisCall; i++) {
1837 tfid.cell = dscp->fid.cell;
1838 tfid.volume = bb.fids[j].Volume;
1839 tfid.vnode = bb.fids[j].Vnode;
1840 tfid.unique = bb.fids[j].Unique;
1841 code = cm_GetSCache(&tfid, &scp, userp, reqp);
1845 /* otherwise, if this entry has no callback info,
1848 lock_ObtainMutex(&scp->mx);
1849 /* now, we have to be extra paranoid on merging in this
1850 * information, since we didn't use cm_SyncOp before
1851 * starting the fetch to make sure that no bad races
1852 * were occurring. Specifically, we need to make sure
1853 * we don't obliterate any newer information in the
1854 * vnode than have here.
1856 * Right now, be pretty conservative: if there's a
1857 * callback or a pending call, skip it.
1859 if (scp->cbServerp == NULL
1861 (CM_SCACHEFLAG_FETCHING
1862 | CM_SCACHEFLAG_STORING
1863 | CM_SCACHEFLAG_SIZESTORING))) {
1864 cm_EndCallbackGrantingCall(scp, &cbReq,
1866 CM_CALLBACK_MAINTAINCOUNT);
1867 cm_MergeStatus(scp, &bb.stats[j], &volSync,
1870 lock_ReleaseMutex(&scp->mx);
1871 cm_ReleaseSCache(scp);
1872 } /* all files in the response */
1873 /* now tell it to drop the count,
1874 * after doing the vnode processing above */
1875 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
1877 filex += filesThisCall;
1878 } /* while there are still more files to process */
1879 lock_ObtainMutex(&dscp->mx);
1880 osi_Log0(afsd_logp, "END cm_TryBulkStat");
1883 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
1887 /* initialize store back mask as inexpensive local variable */
1889 memset(statusp, 0, sizeof(AFSStoreStatus));
1891 /* copy out queued info from scache first, if scp passed in */
1893 if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
1894 statusp->ClientModTime = scp->clientModTime;
1895 mask |= AFS_SETMODTIME;
1896 scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
1901 /* now add in our locally generated request */
1902 if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
1903 statusp->ClientModTime = attrp->clientModTime;
1904 mask |= AFS_SETMODTIME;
1906 if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
1907 statusp->UnixModeBits = attrp->unixModeBits;
1908 mask |= AFS_SETMODE;
1910 if (attrp->mask & CM_ATTRMASK_OWNER) {
1911 statusp->Owner = attrp->owner;
1912 mask |= AFS_SETOWNER;
1914 if (attrp->mask & CM_ATTRMASK_GROUP) {
1915 statusp->Group = attrp->group;
1916 mask |= AFS_SETGROUP;
1919 statusp->Mask = mask;
1922 /* set the file size, and make sure that all relevant buffers have been
1923 * truncated. Ensure that any partially truncated buffers have been zeroed
1924 * to the end of the buffer.
1926 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
1932 /* start by locking out buffer creation */
1933 lock_ObtainWrite(&scp->bufCreateLock);
1935 /* verify that this is a file, not a dir or a symlink */
1936 lock_ObtainMutex(&scp->mx);
1937 code = cm_SyncOp(scp, NULL, userp, reqp, 0,
1938 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1942 if (scp->fileType != CM_SCACHETYPE_FILE) {
1943 code = CM_ERROR_ISDIR;
1948 if (LargeIntegerLessThan(*sizep, scp->length))
1953 lock_ReleaseMutex(&scp->mx);
1955 /* can't hold scp->mx lock here, since we may wait for a storeback to
1956 * finish if the buffer package is cleaning a buffer by storing it to
1960 buf_Truncate(scp, userp, reqp, sizep);
1962 /* now ensure that file length is short enough, and update truncPos */
1963 lock_ObtainMutex(&scp->mx);
1965 /* make sure we have a callback (so we have the right value for the
1966 * length), and wait for it to be safe to do a truncate.
1968 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
1969 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
1970 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
1974 if (LargeIntegerLessThan(*sizep, scp->length)) {
1975 /* a real truncation. If truncPos is not set yet, or is bigger
1976 * than where we're truncating the file, set truncPos to this
1981 if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
1982 || LargeIntegerLessThan(*sizep, scp->length)) {
1984 scp->truncPos = *sizep;
1985 scp->mask |= CM_SCACHEMASK_TRUNCPOS;
1987 /* in either case, the new file size has been changed */
1988 scp->length = *sizep;
1989 scp->mask |= CM_SCACHEMASK_LENGTH;
1991 else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
1992 /* really extending the file */
1993 scp->length = *sizep;
1994 scp->mask |= CM_SCACHEMASK_LENGTH;
1997 /* done successfully */
2001 lock_ReleaseMutex(&scp->mx);
2002 lock_ReleaseWrite(&scp->bufCreateLock);
2007 /* set the file size or other attributes (but not both at once) */
2008 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
2013 AFSFetchStatus afsOutStatus;
2017 AFSStoreStatus afsInStatus;
2018 struct rx_connection * callp;
2020 /* handle file length setting */
2021 if (attrp->mask & CM_ATTRMASK_LENGTH)
2022 return cm_SetLength(scp, &attrp->length, userp, reqp);
2024 flags = CM_SCACHESYNC_STORESTATUS;
2026 lock_ObtainMutex(&scp->mx);
2027 /* otherwise, we have to make an RPC to get the status */
2028 code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
2030 /* make the attr structure */
2031 cm_StatusFromAttr(&afsInStatus, scp, attrp);
2033 tfid.Volume = scp->fid.volume;
2034 tfid.Vnode = scp->fid.vnode;
2035 tfid.Unique = scp->fid.unique;
2037 lock_ReleaseMutex(&scp->mx);
2041 /* now make the RPC */
2042 osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%x", (long) scp);
2044 code = cm_Conn(&scp->fid, userp, reqp, &connp);
2048 callp = cm_GetRxConn(connp);
2049 code = RXAFS_StoreStatus(callp, &tfid,
2050 &afsInStatus, &afsOutStatus, &volSync);
2051 rx_PutConnection(callp);
2053 } while (cm_Analyze(connp, userp, reqp,
2054 &scp->fid, &volSync, NULL, NULL, code));
2055 code = cm_MapRPCError(code, reqp);
2058 osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
2060 osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
2062 lock_ObtainMutex(&scp->mx);
2063 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
2065 cm_MergeStatus(scp, &afsOutStatus, &volSync, userp,
2066 CM_MERGEFLAG_FORCE);
2068 /* if we're changing the mode bits, discard the ACL cache,
2069 * since we changed the mode bits.
2071 if (afsInStatus.Mask & AFS_SETMODE) cm_FreeAllACLEnts(scp);
2072 lock_ReleaseMutex(&scp->mx);
2076 long cm_Create(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2077 cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
2082 cm_callbackRequest_t cbReq;
2087 AFSStoreStatus inStatus;
2088 AFSFetchStatus updatedDirStatus;
2089 AFSFetchStatus newFileStatus;
2090 AFSCallBack newFileCallback;
2092 struct rx_connection * callp;
2094 /* can't create names with @sys in them; must expand it manually first.
2095 * return "invalid request" if they try.
2097 if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2098 return CM_ERROR_ATSYS;
2101 /* before starting the RPC, mark that we're changing the file data, so
2102 * that someone who does a chmod will know to wait until our call
2105 lock_ObtainMutex(&dscp->mx);
2106 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2108 cm_StartCallbackGrantingCall(NULL, &cbReq);
2110 lock_ReleaseMutex(&dscp->mx);
2116 cm_StatusFromAttr(&inStatus, NULL, attrp);
2118 /* try the RPC now */
2119 osi_Log1(afsd_logp, "CALL CreateFile scp 0x%x", (long) dscp);
2121 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2125 dirAFSFid.Volume = dscp->fid.volume;
2126 dirAFSFid.Vnode = dscp->fid.vnode;
2127 dirAFSFid.Unique = dscp->fid.unique;
2129 callp = cm_GetRxConn(connp);
2130 code = RXAFS_CreateFile(connp->callp, &dirAFSFid, namep,
2131 &inStatus, &newAFSFid, &newFileStatus,
2132 &updatedDirStatus, &newFileCallback,
2134 rx_PutConnection(callp);
2136 } while (cm_Analyze(connp, userp, reqp,
2137 &dscp->fid, &volSync, NULL, &cbReq, code));
2138 code = cm_MapRPCError(code, reqp);
2141 osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
2143 osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
2145 lock_ObtainMutex(&dscp->mx);
2146 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2148 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2150 lock_ReleaseMutex(&dscp->mx);
2152 /* now try to create the file's entry, too, but be careful to
2153 * make sure that we don't merge in old info. Since we weren't locking
2154 * out any requests during the file's creation, we may have pretty old
2158 newFid.cell = dscp->fid.cell;
2159 newFid.volume = dscp->fid.volume;
2160 newFid.vnode = newAFSFid.Vnode;
2161 newFid.unique = newAFSFid.Unique;
2162 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2164 lock_ObtainMutex(&scp->mx);
2165 if (!cm_HaveCallback(scp)) {
2166 cm_MergeStatus(scp, &newFileStatus, &volSync,
2168 cm_EndCallbackGrantingCall(scp, &cbReq,
2169 &newFileCallback, 0);
2172 lock_ReleaseMutex(&scp->mx);
2177 /* make sure we end things properly */
2179 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2184 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
2188 lock_ObtainWrite(&scp->bufCreateLock);
2189 code = buf_CleanVnode(scp, userp, reqp);
2190 lock_ReleaseWrite(&scp->bufCreateLock);
2192 lock_ObtainMutex(&scp->mx);
2193 scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA
2194 | CM_SCACHEFLAG_OUTOFSPACE);
2195 if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
2196 | CM_SCACHEMASK_CLIENTMODTIME
2197 | CM_SCACHEMASK_LENGTH))
2198 code = cm_StoreMini(scp, userp, reqp);
2199 lock_ReleaseMutex(&scp->mx);
2204 long cm_MakeDir(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2205 cm_user_t *userp, cm_req_t *reqp)
2210 cm_callbackRequest_t cbReq;
2215 AFSStoreStatus inStatus;
2216 AFSFetchStatus updatedDirStatus;
2217 AFSFetchStatus newDirStatus;
2218 AFSCallBack newDirCallback;
2220 struct rx_connection * callp;
2222 /* can't create names with @sys in them; must expand it manually first.
2223 * return "invalid request" if they try.
2225 if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2226 return CM_ERROR_ATSYS;
2229 /* before starting the RPC, mark that we're changing the directory
2230 * data, so that someone who does a chmod on the dir will wait until
2231 * our call completes.
2233 lock_ObtainMutex(&dscp->mx);
2234 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2236 cm_StartCallbackGrantingCall(NULL, &cbReq);
2238 lock_ReleaseMutex(&dscp->mx);
2244 cm_StatusFromAttr(&inStatus, NULL, attrp);
2246 /* try the RPC now */
2247 osi_Log1(afsd_logp, "CALL MakeDir scp 0x%x", (long) dscp);
2249 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2253 dirAFSFid.Volume = dscp->fid.volume;
2254 dirAFSFid.Vnode = dscp->fid.vnode;
2255 dirAFSFid.Unique = dscp->fid.unique;
2257 callp = cm_GetRxConn(connp);
2258 code = RXAFS_MakeDir(connp->callp, &dirAFSFid, namep,
2259 &inStatus, &newAFSFid, &newDirStatus,
2260 &updatedDirStatus, &newDirCallback,
2262 rx_PutConnection(callp);
2264 } while (cm_Analyze(connp, userp, reqp,
2265 &dscp->fid, &volSync, NULL, &cbReq, code));
2266 code = cm_MapRPCError(code, reqp);
2269 osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
2271 osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
2273 lock_ObtainMutex(&dscp->mx);
2274 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2276 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2278 lock_ReleaseMutex(&dscp->mx);
2280 /* now try to create the new dir's entry, too, but be careful to
2281 * make sure that we don't merge in old info. Since we weren't locking
2282 * out any requests during the file's creation, we may have pretty old
2286 newFid.cell = dscp->fid.cell;
2287 newFid.volume = dscp->fid.volume;
2288 newFid.vnode = newAFSFid.Vnode;
2289 newFid.unique = newAFSFid.Unique;
2290 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2292 lock_ObtainMutex(&scp->mx);
2293 if (!cm_HaveCallback(scp)) {
2294 cm_MergeStatus(scp, &newDirStatus, &volSync,
2296 cm_EndCallbackGrantingCall(scp, &cbReq,
2297 &newDirCallback, 0);
2300 lock_ReleaseMutex(&scp->mx);
2301 cm_ReleaseSCache(scp);
2305 /* make sure we end things properly */
2307 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2309 /* and return error code */
2313 long cm_Link(cm_scache_t *dscp, char *namep, cm_scache_t *sscp, long flags,
2314 cm_user_t *userp, cm_req_t *reqp)
2319 AFSFid existingAFSFid;
2320 AFSFetchStatus updatedDirStatus;
2321 AFSFetchStatus newLinkStatus;
2323 struct rx_connection * callp;
2325 if (dscp->fid.cell != sscp->fid.cell ||
2326 dscp->fid.volume != sscp->fid.volume) {
2327 return CM_ERROR_CROSSDEVLINK;
2330 lock_ObtainMutex(&dscp->mx);
2331 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2332 lock_ReleaseMutex(&dscp->mx);
2337 /* try the RPC now */
2338 osi_Log1(afsd_logp, "CALL Link scp 0x%x", (long) dscp);
2340 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2343 dirAFSFid.Volume = dscp->fid.volume;
2344 dirAFSFid.Vnode = dscp->fid.vnode;
2345 dirAFSFid.Unique = dscp->fid.unique;
2347 existingAFSFid.Volume = sscp->fid.volume;
2348 existingAFSFid.Vnode = sscp->fid.vnode;
2349 existingAFSFid.Unique = sscp->fid.unique;
2351 callp = cm_GetRxConn(connp);
2352 code = RXAFS_Link(callp, &dirAFSFid, namep, &existingAFSFid,
2353 &newLinkStatus, &updatedDirStatus, &volSync);
2354 rx_PutConnection(callp);
2355 osi_Log1(smb_logp," RXAFS_Link returns 0x%x", code);
2357 } while (cm_Analyze(connp, userp, reqp,
2358 &dscp->fid, &volSync, NULL, NULL, code));
2360 code = cm_MapRPCError(code, reqp);
2363 osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
2365 osi_Log0(afsd_logp, "CALL Link SUCCESS");
2367 lock_ObtainMutex(&dscp->mx);
2368 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2370 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2372 lock_ReleaseMutex(&dscp->mx);
2377 long cm_SymLink(cm_scache_t *dscp, char *namep, char *contentsp, long flags,
2378 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
2386 AFSStoreStatus inStatus;
2387 AFSFetchStatus updatedDirStatus;
2388 AFSFetchStatus newLinkStatus;
2390 struct rx_connection * callp;
2392 /* before starting the RPC, mark that we're changing the directory data,
2393 * so that someone who does a chmod on the dir will wait until our
2396 lock_ObtainMutex(&dscp->mx);
2397 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2398 lock_ReleaseMutex(&dscp->mx);
2403 cm_StatusFromAttr(&inStatus, NULL, attrp);
2405 /* try the RPC now */
2406 osi_Log1(afsd_logp, "CALL Symlink scp 0x%x", (long) dscp);
2408 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2412 dirAFSFid.Volume = dscp->fid.volume;
2413 dirAFSFid.Vnode = dscp->fid.vnode;
2414 dirAFSFid.Unique = dscp->fid.unique;
2416 callp = cm_GetRxConn(connp);
2417 code = RXAFS_Symlink(callp, &dirAFSFid, namep, contentsp,
2418 &inStatus, &newAFSFid, &newLinkStatus,
2419 &updatedDirStatus, &volSync);
2420 rx_PutConnection(callp);
2422 } while (cm_Analyze(connp, userp, reqp,
2423 &dscp->fid, &volSync, NULL, NULL, code));
2424 code = cm_MapRPCError(code, reqp);
2427 osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
2429 osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
2431 lock_ObtainMutex(&dscp->mx);
2432 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2434 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2436 lock_ReleaseMutex(&dscp->mx);
2438 /* now try to create the new dir's entry, too, but be careful to
2439 * make sure that we don't merge in old info. Since we weren't locking
2440 * out any requests during the file's creation, we may have pretty old
2444 newFid.cell = dscp->fid.cell;
2445 newFid.volume = dscp->fid.volume;
2446 newFid.vnode = newAFSFid.Vnode;
2447 newFid.unique = newAFSFid.Unique;
2448 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2450 lock_ObtainMutex(&scp->mx);
2451 if (!cm_HaveCallback(scp)) {
2452 cm_MergeStatus(scp, &newLinkStatus, &volSync,
2455 lock_ReleaseMutex(&scp->mx);
2456 cm_ReleaseSCache(scp);
2460 /* and return error code */
2464 long cm_RemoveDir(cm_scache_t *dscp, char *namep, cm_user_t *userp,
2471 AFSFetchStatus updatedDirStatus;
2473 struct rx_connection * callp;
2475 /* before starting the RPC, mark that we're changing the directory data,
2476 * so that someone who does a chmod on the dir will wait until our
2479 lock_ObtainMutex(&dscp->mx);
2480 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2481 lock_ReleaseMutex(&dscp->mx);
2487 /* try the RPC now */
2488 osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%x", (long) dscp);
2490 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2494 dirAFSFid.Volume = dscp->fid.volume;
2495 dirAFSFid.Vnode = dscp->fid.vnode;
2496 dirAFSFid.Unique = dscp->fid.unique;
2498 callp = cm_GetRxConn(connp);
2499 code = RXAFS_RemoveDir(callp, &dirAFSFid, namep,
2500 &updatedDirStatus, &volSync);
2501 rx_PutConnection(callp);
2503 } while (cm_Analyze(connp, userp, reqp,
2504 &dscp->fid, &volSync, NULL, NULL, code));
2505 code = cm_MapRPCErrorRmdir(code, reqp);
2508 osi_Log1(afsd_logp, "CALL RemoveDir FAILURE, code 0x%x", code);
2510 osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
2512 lock_ObtainMutex(&dscp->mx);
2513 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2515 cm_dnlcRemove(dscp, namep);
2516 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2518 lock_ReleaseMutex(&dscp->mx);
2520 /* and return error code */
2524 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
2526 /* grab mutex on contents */
2527 lock_ObtainMutex(&scp->mx);
2529 /* reset the prefetch info */
2530 scp->prefetch.base.LowPart = 0; /* base */
2531 scp->prefetch.base.HighPart = 0;
2532 scp->prefetch.end.LowPart = 0; /* and end */
2533 scp->prefetch.end.HighPart = 0;
2535 /* release mutex on contents */
2536 lock_ReleaseMutex(&scp->mx);
2542 long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
2543 char *newNamep, cm_user_t *userp, cm_req_t *reqp)
2547 AFSFid oldDirAFSFid;
2548 AFSFid newDirAFSFid;
2550 AFSFetchStatus updatedOldDirStatus;
2551 AFSFetchStatus updatedNewDirStatus;
2554 struct rx_connection * callp;
2556 /* before starting the RPC, mark that we're changing the directory data,
2557 * so that someone who does a chmod on the dir will wait until our call
2558 * completes. We do this in vnode order so that we don't deadlock,
2559 * which makes the code a little verbose.
2561 if (oldDscp == newDscp) {
2562 /* check for identical names */
2563 if (strcmp(oldNamep, newNamep) == 0)
2564 return CM_ERROR_RENAME_IDENTICAL;
2567 lock_ObtainMutex(&oldDscp->mx);
2568 cm_dnlcRemove(oldDscp, oldNamep);
2569 cm_dnlcRemove(oldDscp, newNamep);
2570 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2571 CM_SCACHESYNC_STOREDATA);
2572 lock_ReleaseMutex(&oldDscp->mx);
2575 /* two distinct dir vnodes */
2577 if (oldDscp->fid.cell != newDscp->fid.cell ||
2578 oldDscp->fid.volume != newDscp->fid.volume)
2579 return CM_ERROR_CROSSDEVLINK;
2581 /* shouldn't happen that we have distinct vnodes for two
2582 * different files, but could due to deliberate attack, or
2583 * stale info. Avoid deadlocks and quit now.
2585 if (oldDscp->fid.vnode == newDscp->fid.vnode)
2586 return CM_ERROR_CROSSDEVLINK;
2588 if (oldDscp->fid.vnode < newDscp->fid.vnode) {
2589 lock_ObtainMutex(&oldDscp->mx);
2590 cm_dnlcRemove(oldDscp, oldNamep);
2591 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2592 CM_SCACHESYNC_STOREDATA);
2593 lock_ReleaseMutex(&oldDscp->mx);
2595 lock_ObtainMutex(&newDscp->mx);
2596 cm_dnlcRemove(newDscp, newNamep);
2597 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2598 CM_SCACHESYNC_STOREDATA);
2599 lock_ReleaseMutex(&newDscp->mx);
2601 /* cleanup first one */
2602 cm_SyncOpDone(oldDscp, NULL,
2603 CM_SCACHESYNC_STOREDATA);
2608 /* lock the new vnode entry first */
2609 lock_ObtainMutex(&newDscp->mx);
2610 cm_dnlcRemove(newDscp, newNamep);
2611 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2612 CM_SCACHESYNC_STOREDATA);
2613 lock_ReleaseMutex(&newDscp->mx);
2615 lock_ObtainMutex(&oldDscp->mx);
2616 cm_dnlcRemove(oldDscp, oldNamep);
2617 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2618 CM_SCACHESYNC_STOREDATA);
2619 lock_ReleaseMutex(&oldDscp->mx);
2621 /* cleanup first one */
2622 cm_SyncOpDone(newDscp, NULL,
2623 CM_SCACHESYNC_STOREDATA);
2627 } /* two distinct vnodes */
2634 /* try the RPC now */
2635 osi_Log2(afsd_logp, "CALL Rename old scp 0x%x new scp 0x%x",
2636 (long) oldDscp, (long) newDscp);
2638 code = cm_Conn(&oldDscp->fid, userp, reqp, &connp);
2642 oldDirAFSFid.Volume = oldDscp->fid.volume;
2643 oldDirAFSFid.Vnode = oldDscp->fid.vnode;
2644 oldDirAFSFid.Unique = oldDscp->fid.unique;
2645 newDirAFSFid.Volume = newDscp->fid.volume;
2646 newDirAFSFid.Vnode = newDscp->fid.vnode;
2647 newDirAFSFid.Unique = newDscp->fid.unique;
2649 callp = cm_GetRxConn(connp);
2650 code = RXAFS_Rename(callp, &oldDirAFSFid, oldNamep,
2651 &newDirAFSFid, newNamep,
2652 &updatedOldDirStatus, &updatedNewDirStatus,
2654 rx_PutConnection(callp);
2656 } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
2657 &volSync, NULL, NULL, code));
2658 code = cm_MapRPCError(code, reqp);
2661 osi_Log1(afsd_logp, "CALL Rename FAILURE, code 0x%x", code);
2663 osi_Log0(afsd_logp, "CALL Rename SUCCESS");
2665 /* update the individual stat cache entries for the directories */
2666 lock_ObtainMutex(&oldDscp->mx);
2667 cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
2669 cm_MergeStatus(oldDscp, &updatedOldDirStatus, &volSync,
2672 lock_ReleaseMutex(&oldDscp->mx);
2674 /* and update it for the new one, too, if necessary */
2676 lock_ObtainMutex(&newDscp->mx);
2677 cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
2679 cm_MergeStatus(newDscp, &updatedNewDirStatus, &volSync,
2682 lock_ReleaseMutex(&newDscp->mx);
2685 /* and return error code */
2689 long cm_Lock(cm_scache_t *scp, unsigned char LockType,
2690 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
2691 u_long Timeout, cm_user_t *userp, cm_req_t *reqp,
2695 int Which = ((LockType & 0x1) ? LockRead : LockWrite);
2699 cm_file_lock_t *fileLock;
2702 struct rx_connection * callp;
2704 /* Look for a conflict. Also, if we are asking for a shared lock,
2705 * look for another shared lock, so we don't have to do an RPC.
2709 fileLock = (cm_file_lock_t *)
2710 ((char *) q - offsetof(cm_file_lock_t, fileq));
2711 if ((fileLock->flags &
2712 (CM_FILELOCK_FLAG_INVALID | CM_FILELOCK_FLAG_WAITING))
2714 if ((LockType & 0x1) == 0
2715 || (fileLock->LockType & 0x1) == 0)
2716 return CM_ERROR_WOULDBLOCK;
2725 tfid.Volume = scp->fid.volume;
2726 tfid.Vnode = scp->fid.vnode;
2727 tfid.Unique = scp->fid.unique;
2728 lock_ReleaseMutex(&scp->mx);
2730 code = cm_Conn(&scp->fid, userp, reqp, &connp);
2734 callp = cm_GetRxConn(connp);
2735 code = RXAFS_SetLock(callp, &tfid, Which,
2737 rx_PutConnection(callp);
2739 } while (cm_Analyze(connp, userp, reqp, &scp->fid, &volSync,
2741 lock_ObtainMutex(&scp->mx);
2742 code = cm_MapRPCError(code, reqp);
2745 if (code == 0 || Timeout != 0) {
2746 fileLock = malloc(sizeof(cm_file_lock_t));
2747 fileLock->LockType = LockType;
2749 fileLock->userp = userp;
2750 fileLock->fid = scp->fid;
2751 fileLock->LOffset = LOffset;
2752 fileLock->LLength = LLength;
2753 fileLock->flags = (code == 0 ? 0 : CM_FILELOCK_FLAG_WAITING);
2754 osi_QAdd(&scp->fileLocks, &fileLock->fileq);
2755 lock_ObtainWrite(&cm_scacheLock);
2756 osi_QAdd(&cm_allFileLocks, &fileLock->q);
2757 lock_ReleaseWrite(&cm_scacheLock);
2764 long cm_Unlock(cm_scache_t *scp, unsigned char LockType,
2765 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
2766 cm_user_t *userp, cm_req_t *reqp)
2769 int Which = ((LockType & 0x1) ? LockRead : LockWrite);
2773 cm_file_lock_t *fileLock, *ourLock;
2774 osi_queue_t *q, *qq;
2775 int anotherReader = 0;
2778 struct rx_connection * callp;
2780 if (LargeIntegerLessThan(LLength, scp->length))
2783 /* Look for our own lock on the list, so as to remove it.
2784 * Also, determine if we're the last reader; if not, avoid an RPC.
2788 fileLock = (cm_file_lock_t *)
2789 ((char *) q - offsetof(cm_file_lock_t, fileq));
2791 && fileLock->userp == userp
2792 && LargeIntegerEqualTo(fileLock->LOffset, LOffset)
2793 && LargeIntegerEqualTo(fileLock->LLength, LLength)) {
2798 else if (fileLock->LockType & 0x1)
2803 /* ignore byte ranges */
2804 if (smallLock && !found)
2807 /* don't try to unlock other people's locks */
2809 return CM_ERROR_WOULDBLOCK;
2811 /* discard lock record */
2812 osi_QRemove(&scp->fileLocks, qq);
2814 * Don't delete it here; let the daemon delete it, to simplify
2815 * the daemon's traversal of the list.
2817 lock_ObtainWrite(&cm_scacheLock);
2818 ourLock->flags |= CM_FILELOCK_FLAG_INVALID;
2819 cm_ReleaseUser(ourLock->userp);
2820 lock_ReleaseWrite(&cm_scacheLock);
2822 if (!anotherReader) {
2823 tfid.Volume = scp->fid.volume;
2824 tfid.Vnode = scp->fid.vnode;
2825 tfid.Unique = scp->fid.unique;
2826 lock_ReleaseMutex(&scp->mx);
2827 osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%x", (long) scp);
2829 code = cm_Conn(&scp->fid, userp, reqp, &connp);
2833 callp = cm_GetRxConn(connp);
2834 code = RXAFS_ReleaseLock(callp, &tfid, &volSync);
2835 rx_PutConnection(callp);
2837 } while (cm_Analyze(connp, userp, reqp, &scp->fid, &volSync,
2839 code = cm_MapRPCError(code, reqp);
2842 osi_Log1(afsd_logp, "CALL ReleaseLock FAILURE, code 0x%x", code);
2844 osi_Log0(afsd_logp, "CALL ReleaseLock SUCCESS");
2846 lock_ObtainMutex(&scp->mx);
2852 void cm_CheckLocks()
2854 osi_queue_t *q, *nq;
2855 cm_file_lock_t *fileLock;
2861 struct rx_connection * callp;
2865 lock_ObtainWrite(&cm_scacheLock);
2866 q = cm_allFileLocks;
2868 fileLock = (cm_file_lock_t *) q;
2870 if (fileLock->flags & CM_FILELOCK_FLAG_INVALID) {
2871 osi_QRemove(&cm_allFileLocks, q);
2874 else if (!(fileLock->flags & CM_FILELOCK_FLAG_WAITING)) {
2875 tfid.Volume = fileLock->fid.volume;
2876 tfid.Vnode = fileLock->fid.vnode;
2877 tfid.Unique = fileLock->fid.unique;
2878 lock_ReleaseWrite(&cm_scacheLock);
2879 osi_Log1(afsd_logp, "CALL ExtendLock lock 0x%x", (long) fileLock);
2881 code = cm_Conn(&fileLock->fid, fileLock->userp,
2886 callp = cm_GetRxConn(connp);
2887 code = RXAFS_ExtendLock(callp, &tfid,
2889 rx_PutConnection(callp);
2891 } while (cm_Analyze(connp, fileLock->userp, &req,
2892 &fileLock->fid, &volSync, NULL, NULL,
2894 code = cm_MapRPCError(code, &req);
2896 osi_Log1(afsd_logp, "CALL ExtendLock FAILURE, code 0x%x", code);
2898 osi_Log0(afsd_logp, "CALL ExtendLock SUCCESS");
2900 lock_ObtainWrite(&cm_scacheLock);
2904 lock_ReleaseWrite(&cm_scacheLock);
2907 long cm_RetryLock(cm_file_lock_t *oldFileLock, int vcp_is_dead)
2910 int Which = ((oldFileLock->LockType & 0x1) ? LockRead : LockWrite);
2915 cm_file_lock_t *fileLock;
2919 struct rx_connection * callp;
2922 code = CM_ERROR_TIMEDOUT;
2928 /* Look for a conflict. Also, if we are asking for a shared lock,
2929 * look for another shared lock, so we don't have to do an RPC.
2931 code = cm_GetSCache(&oldFileLock->fid, &scp, oldFileLock->userp, &req);
2937 fileLock = (cm_file_lock_t *)
2938 ((char *) q - offsetof(cm_file_lock_t, fileq));
2939 if ((fileLock->flags &
2940 (CM_FILELOCK_FLAG_INVALID | CM_FILELOCK_FLAG_WAITING))
2942 if ((oldFileLock->LockType & 0x1) == 0
2943 || (fileLock->LockType & 0x1) == 0) {
2944 cm_ReleaseSCache(scp);
2945 return CM_ERROR_WOULDBLOCK;
2955 tfid.Volume = oldFileLock->fid.volume;
2956 tfid.Vnode = oldFileLock->fid.vnode;
2957 tfid.Unique = oldFileLock->fid.unique;
2958 osi_Log1(afsd_logp, "CALL SetLock lock 0x%x", (long) oldFileLock);
2960 code = cm_Conn(&oldFileLock->fid, oldFileLock->userp,
2965 callp = cm_GetRxConn(connp);
2966 code = RXAFS_SetLock(callp, &tfid, Which,
2968 rx_PutConnection(callp);
2970 } while (cm_Analyze(connp, oldFileLock->userp, &req,
2971 &oldFileLock->fid, &volSync,
2973 code = cm_MapRPCError(code, &req);
2976 osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
2978 osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
2982 if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
2983 lock_ObtainMutex(&scp->mx);
2984 osi_QRemove(&scp->fileLocks, &oldFileLock->fileq);
2985 lock_ReleaseMutex(&scp->mx);
2987 lock_ObtainWrite(&cm_scacheLock);
2989 oldFileLock->flags = 0;
2990 else if (code != CM_ERROR_WOULDBLOCK) {
2991 oldFileLock->flags |= CM_FILELOCK_FLAG_INVALID;
2992 cm_ReleaseUser(oldFileLock->userp);
2993 oldFileLock->userp = NULL;
2995 lock_ReleaseWrite(&cm_scacheLock);