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)
135 * can't have a leading dot;
136 * special case for . and ..
138 if (namep[0] == '.') {
141 if (namep[1] == '.' && namep[2] == 0)
145 while (tc = *namep++) {
147 /* saw another dot */
148 if (sawDot) return 0; /* second dot */
153 if (cm_LegalChars[tc] == 0)
156 if (!sawDot && charCount > 8)
157 /* more than 8 chars in name */
159 if (sawDot && charCount > 3)
160 /* more than 3 chars in extension */
167 * Number unparsing map for generating 8.3 names;
168 * The version taken from DFS was on drugs.
169 * You can't include '&' and '@' in a file name.
171 char cm_8Dot3Mapping[42] =
172 {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
173 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K',
174 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U',
175 'V', 'W', 'X', 'Y', 'Z', '_', '-', '$', '#', '!', '+', '='
177 int cm_8Dot3MapSize = sizeof(cm_8Dot3Mapping);
179 void cm_Gen8Dot3Name(cm_dirEntry_t *dep, char *shortName, char **shortNameEndp)
183 int vnode = ntohl(dep->fid.vnode);
185 int validExtension = 0;
186 char tc, *temp, *name;
188 /* Unparse the file's vnode number to get a "uniquifier" */
190 number[nsize] = cm_8Dot3Mapping[vnode % cm_8Dot3MapSize];
192 vnode /= cm_8Dot3MapSize;
196 * Look for valid extension. There has to be a dot, and
197 * at least one of the characters following has to be legal.
199 lastDot = strrchr(dep->name, '.');
201 temp = lastDot; temp++;
203 if (cm_LegalChars[tc])
209 /* Copy name characters */
211 for (i = 0, name = dep->name;
212 i < (7 - nsize) && name != lastDot; ) {
217 if (!cm_LegalChars[tc])
220 *shortName++ = toupper(tc);
226 /* Copy uniquifier characters */
227 memcpy(shortName, number, nsize);
230 if (validExtension) {
231 /* Copy extension characters */
232 *shortName++ = *lastDot++; /* copy dot */
233 for (i = 0, tc = *lastDot++;
236 if (cm_LegalChars[tc]) {
238 *shortName++ = toupper(tc);
247 *shortNameEndp = shortName;
250 /* return success if we can open this file in this mode */
251 long cm_CheckOpen(cm_scache_t *scp, int openMode, int trunc, cm_user_t *userp,
258 if (openMode != 1) rights |= PRSFS_READ;
259 if (openMode == 1 || openMode == 2 || trunc) rights |= PRSFS_WRITE;
261 lock_ObtainMutex(&scp->mx);
263 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
264 CM_SCACHESYNC_GETSTATUS
265 | CM_SCACHESYNC_NEEDCALLBACK);
266 lock_ReleaseMutex(&scp->mx);
271 /* return success if we can open this file in this mode */
272 long cm_CheckNTOpen(cm_scache_t *scp, unsigned int desiredAccess,
273 unsigned int createDisp, cm_user_t *userp, cm_req_t *reqp)
278 /* Always allow delete; the RPC will tell us if it's OK */
279 if (desiredAccess == DELETE)
284 if (desiredAccess & AFS_ACCESS_READ)
285 rights |= PRSFS_READ;
287 if ((desiredAccess & AFS_ACCESS_WRITE)
289 rights |= PRSFS_WRITE;
291 lock_ObtainMutex(&scp->mx);
293 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
294 CM_SCACHESYNC_GETSTATUS
295 | CM_SCACHESYNC_NEEDCALLBACK);
296 lock_ReleaseMutex(&scp->mx);
299 * If the open will fail because the volume is readonly, then we will
300 * return an access denied error instead. This is to help brain-dead
301 * apps run correctly on replicated volumes.
302 * See defect 10007 for more information.
304 if (code == CM_ERROR_READONLY)
305 code = CM_ERROR_NOACCESS;
311 * When CAP_NT_SMBS has been negotiated, deletion (of files or directories) is
312 * done in three steps:
313 * (1) open for deletion (NT_CREATE_AND_X)
314 * (2) set for deletion on close (NT_TRANSACTION2, SET_FILE_INFO)
316 * We must not do the RPC until step 3. But if we are going to return an error
317 * code (e.g. directory not empty), we must return it by step 2, otherwise most
318 * clients will not notice it. So we do a preliminary check. For deleting
319 * files, this is almost free, since we have already done the RPC to get the
320 * parent directory's status bits. But for deleting directories, we must do an
321 * additional RPC to get the directory's data to check if it is empty. Sigh.
323 long cm_CheckNTDelete(cm_scache_t *dscp, cm_scache_t *scp, cm_user_t *userp,
330 unsigned short *hashTable;
332 int BeyondPage = 0, HaveDot = 0, HaveDotDot = 0;
334 /* First check permissions */
335 lock_ObtainMutex(&dscp->mx);
336 code = cm_SyncOp(dscp, NULL, userp, reqp, PRSFS_DELETE,
337 CM_SCACHESYNC_GETSTATUS
338 | CM_SCACHESYNC_NEEDCALLBACK);
339 lock_ReleaseMutex(&dscp->mx);
343 /* If deleting directory, must be empty */
345 if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
348 thyper.HighPart = 0; thyper.LowPart = 0;
349 lock_ObtainRead(&scp->bufCreateLock);
350 code = buf_Get(scp, &thyper, &bufferp);
351 lock_ReleaseRead(&scp->bufCreateLock);
355 lock_ObtainMutex(&bufferp->mx);
356 lock_ObtainMutex(&scp->mx);
358 code = cm_SyncOp(scp, bufferp, userp, reqp, 0,
359 CM_SCACHESYNC_NEEDCALLBACK
361 | CM_SCACHESYNC_BUFLOCKED);
365 if (cm_HaveBuffer(scp, bufferp, 1))
368 /* otherwise, load the buffer and try again */
369 lock_ReleaseMutex(&bufferp->mx);
370 code = cm_GetBuffer(scp, bufferp, NULL, userp, reqp);
371 lock_ReleaseMutex(&scp->mx);
372 lock_ObtainMutex(&bufferp->mx);
373 lock_ObtainMutex(&scp->mx);
378 /* We try to determine emptiness without looking beyond the first page,
379 * and without assuming "." and ".." are present and are on the first
380 * page (though these assumptions might, after all, be reasonable).
382 hashTable = (unsigned short *)(bufferp->datap + (32 * 5));
383 for (i=0; i<128; i++) {
384 idx = ntohs(hashTable[i]);
390 dep = (cm_dirEntry_t *)(bufferp->datap + (32 * idx));
391 if (strcmp(dep->name, ".") == 0)
393 else if (strcmp(dep->name, "..") == 0)
396 code = CM_ERROR_NOTEMPTY;
399 idx = ntohs(dep->next);
402 if (BeyondPage && HaveDot && HaveDotDot)
403 code = CM_ERROR_NOTEMPTY;
407 lock_ReleaseMutex(&bufferp->mx);
408 buf_Release(bufferp);
409 lock_ReleaseMutex(&scp->mx);
414 * Iterate through all entries in a directory.
415 * When the function funcp is called, the buffer is locked but the
416 * directory vnode is not.
418 * If the retscp parameter is not NULL, the parmp must be a
419 * cm_lookupSearch_t object.
421 long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
422 osi_hyper_t *startOffsetp, cm_user_t *userp, cm_req_t *reqp,
423 cm_scache_t **retscp)
430 osi_hyper_t dirLength;
431 osi_hyper_t bufferOffset;
432 osi_hyper_t curOffset;
436 cm_pageHeader_t *pageHeaderp;
438 long nextEntryCookie;
439 int numDirChunks; /* # of 32 byte dir chunks in this entry */
441 /* get the directory size */
442 lock_ObtainMutex(&scp->mx);
443 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_LOOKUP,
444 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
446 lock_ReleaseMutex(&scp->mx);
450 if (scp->fileType != CM_SCACHETYPE_DIRECTORY) {
451 lock_ReleaseMutex(&scp->mx);
452 return CM_ERROR_NOTDIR;
455 if (retscp) /* if this is a lookup call */
457 cm_lookupSearch_t* sp = parmp;
459 #ifdef AFS_FREELANCE_CLIENT
460 /* Freelance entries never end up in the DNLC because they
461 * do not have an associated cm_server_t
463 if ( !(cm_freelanceEnabled &&
464 sp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
465 sp->fid.volume==AFS_FAKE_ROOT_VOL_ID ) )
466 #endif /* AFS_FREELANCE_CLIENT */
468 int casefold = sp->caseFold;
469 sp->caseFold = 0; /* we have a strong preference for exact matches */
470 if ( *retscp = cm_dnlcLookup(scp, sp)) /* dnlc hit */
472 sp->caseFold = casefold;
473 lock_ReleaseMutex(&scp->mx);
476 sp->caseFold = casefold;
481 * XXX We only get the length once. It might change when we drop the
484 dirLength = scp->length;
486 lock_ReleaseMutex(&scp->mx);
489 bufferOffset.LowPart = bufferOffset.HighPart = 0;
491 curOffset = *startOffsetp;
493 curOffset.HighPart = 0;
494 curOffset.LowPart = 0;
498 /* make sure that curOffset.LowPart doesn't point to the first
499 * 32 bytes in the 2nd through last dir page, and that it
500 * doesn't point at the first 13 32-byte chunks in the first
501 * dir page, since those are dir and page headers, and don't
502 * contain useful information.
504 temp = curOffset.LowPart & (2048-1);
505 if (curOffset.HighPart == 0 && curOffset.LowPart < 2048) {
506 /* we're in the first page */
507 if (temp < 13*32) temp = 13*32;
510 /* we're in a later dir page */
511 if (temp < 32) temp = 32;
514 /* make sure the low order 5 bits are zero */
517 /* now put temp bits back ito curOffset.LowPart */
518 curOffset.LowPart &= ~(2048-1);
519 curOffset.LowPart |= temp;
521 /* check if we've passed the dir's EOF */
522 if (LargeIntegerGreaterThanOrEqualTo(curOffset, dirLength))
525 /* see if we can use the bufferp we have now; compute in which
526 * page the current offset would be, and check whether that's
527 * the offset of the buffer we have. If not, get the buffer.
529 thyper.HighPart = curOffset.HighPart;
530 thyper.LowPart = curOffset.LowPart & ~(cm_data.buf_blockSize-1);
531 if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
534 lock_ReleaseMutex(&bufferp->mx);
535 buf_Release(bufferp);
539 lock_ObtainRead(&scp->bufCreateLock);
540 code = buf_Get(scp, &thyper, &bufferp);
541 lock_ReleaseRead(&scp->bufCreateLock);
543 /* if buf_Get() fails we do not have a buffer object to lock */
549 lock_ObtainMutex(&scp->mx);
550 if ((scp->flags & CM_SCACHEFLAG_BULKSTATTING) == 0
551 && (scp->bulkStatProgress.QuadPart <= thyper.QuadPart))
553 scp->flags |= CM_SCACHEFLAG_BULKSTATTING;
554 cm_TryBulkStat(scp, &thyper, userp, reqp);
555 scp->flags &= ~CM_SCACHEFLAG_BULKSTATTING;
556 scp->bulkStatProgress = thyper;
558 lock_ReleaseMutex(&scp->mx);
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 lock_ObtainMutex(&newDscp->mx);
2603 cm_SyncOpDone(oldDscp, NULL,
2604 CM_SCACHESYNC_STOREDATA);
2605 lock_ReleaseMutex(&oldDscp->mx);
2610 /* lock the new vnode entry first */
2611 lock_ObtainMutex(&newDscp->mx);
2612 cm_dnlcRemove(newDscp, newNamep);
2613 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2614 CM_SCACHESYNC_STOREDATA);
2615 lock_ReleaseMutex(&newDscp->mx);
2617 lock_ObtainMutex(&oldDscp->mx);
2618 cm_dnlcRemove(oldDscp, oldNamep);
2619 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2620 CM_SCACHESYNC_STOREDATA);
2621 lock_ReleaseMutex(&oldDscp->mx);
2623 /* cleanup first one */
2624 lock_ObtainMutex(&newDscp->mx);
2625 cm_SyncOpDone(newDscp, NULL,
2626 CM_SCACHESYNC_STOREDATA);
2627 lock_ReleaseMutex(&newDscp->mx);
2631 } /* two distinct vnodes */
2638 /* try the RPC now */
2639 osi_Log2(afsd_logp, "CALL Rename old scp 0x%x new scp 0x%x",
2640 (long) oldDscp, (long) newDscp);
2642 code = cm_Conn(&oldDscp->fid, userp, reqp, &connp);
2646 oldDirAFSFid.Volume = oldDscp->fid.volume;
2647 oldDirAFSFid.Vnode = oldDscp->fid.vnode;
2648 oldDirAFSFid.Unique = oldDscp->fid.unique;
2649 newDirAFSFid.Volume = newDscp->fid.volume;
2650 newDirAFSFid.Vnode = newDscp->fid.vnode;
2651 newDirAFSFid.Unique = newDscp->fid.unique;
2653 callp = cm_GetRxConn(connp);
2654 code = RXAFS_Rename(callp, &oldDirAFSFid, oldNamep,
2655 &newDirAFSFid, newNamep,
2656 &updatedOldDirStatus, &updatedNewDirStatus,
2658 rx_PutConnection(callp);
2660 } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
2661 &volSync, NULL, NULL, code));
2662 code = cm_MapRPCError(code, reqp);
2665 osi_Log1(afsd_logp, "CALL Rename FAILURE, code 0x%x", code);
2667 osi_Log0(afsd_logp, "CALL Rename SUCCESS");
2669 /* update the individual stat cache entries for the directories */
2670 lock_ObtainMutex(&oldDscp->mx);
2671 cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
2673 cm_MergeStatus(oldDscp, &updatedOldDirStatus, &volSync,
2676 lock_ReleaseMutex(&oldDscp->mx);
2678 /* and update it for the new one, too, if necessary */
2680 lock_ObtainMutex(&newDscp->mx);
2681 cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
2683 cm_MergeStatus(newDscp, &updatedNewDirStatus, &volSync,
2686 lock_ReleaseMutex(&newDscp->mx);
2689 /* and return error code */
2693 long cm_Lock(cm_scache_t *scp, unsigned char LockType,
2694 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
2695 u_long Timeout, cm_user_t *userp, cm_req_t *reqp,
2699 int Which = ((LockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
2703 cm_file_lock_t *fileLock;
2706 struct rx_connection * callp;
2708 /* Look for a conflict. Also, if we are asking for a shared lock,
2709 * look for another shared lock, so we don't have to do an RPC.
2713 fileLock = (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
2714 if ((fileLock->flags & (CM_FILELOCK_FLAG_INVALID | CM_FILELOCK_FLAG_WAITING)) == 0) {
2715 if ((LockType & LOCKING_ANDX_SHARED_LOCK) == 0 ||
2716 (fileLock->LockType & LOCKING_ANDX_SHARED_LOCK) == 0)
2717 return CM_ERROR_WOULDBLOCK;
2726 tfid.Volume = scp->fid.volume;
2727 tfid.Vnode = scp->fid.vnode;
2728 tfid.Unique = scp->fid.unique;
2729 lock_ReleaseMutex(&scp->mx);
2731 code = cm_Conn(&scp->fid, userp, reqp, &connp);
2735 callp = cm_GetRxConn(connp);
2736 code = RXAFS_SetLock(callp, &tfid, Which,
2738 rx_PutConnection(callp);
2740 } while (cm_Analyze(connp, userp, reqp, &scp->fid, &volSync,
2742 lock_ObtainMutex(&scp->mx);
2743 code = cm_MapRPCError(code, reqp);
2746 if (code == 0 || Timeout != 0) {
2747 fileLock = malloc(sizeof(cm_file_lock_t));
2748 fileLock->LockType = LockType;
2750 fileLock->userp = userp;
2751 fileLock->fid = scp->fid;
2752 fileLock->LOffset = LOffset;
2753 fileLock->LLength = LLength;
2754 fileLock->flags = (code == 0 ? 0 : CM_FILELOCK_FLAG_WAITING);
2755 osi_QAdd(&scp->fileLocks, &fileLock->fileq);
2756 lock_ObtainWrite(&cm_scacheLock);
2757 osi_QAdd(&cm_allFileLocks, &fileLock->q);
2758 lock_ReleaseWrite(&cm_scacheLock);
2765 long cm_Unlock(cm_scache_t *scp, unsigned char LockType,
2766 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
2767 cm_user_t *userp, cm_req_t *reqp)
2770 int Which = ((LockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
2774 cm_file_lock_t *fileLock, *ourLock;
2775 osi_queue_t *q, *qq;
2776 int anotherReader = 0;
2779 struct rx_connection * callp;
2781 if (LargeIntegerLessThan(LLength, scp->length))
2784 /* Look for our own lock on the list, so as to remove it.
2785 * Also, determine if we're the last reader; if not, avoid an RPC.
2789 fileLock = (cm_file_lock_t *)
2790 ((char *) q - offsetof(cm_file_lock_t, fileq));
2792 && fileLock->userp == userp
2793 && LargeIntegerEqualTo(fileLock->LOffset, LOffset)
2794 && LargeIntegerEqualTo(fileLock->LLength, LLength)) {
2799 else if (fileLock->LockType & LOCKING_ANDX_SHARED_LOCK)
2804 /* ignore byte ranges */
2805 if (smallLock && !found)
2808 /* don't try to unlock other people's locks */
2810 return CM_ERROR_WOULDBLOCK;
2812 /* discard lock record */
2813 osi_QRemove(&scp->fileLocks, qq);
2815 * Don't delete it here; let the daemon delete it, to simplify
2816 * the daemon's traversal of the list.
2818 lock_ObtainWrite(&cm_scacheLock);
2819 ourLock->flags |= CM_FILELOCK_FLAG_INVALID;
2820 cm_ReleaseUser(ourLock->userp);
2821 lock_ReleaseWrite(&cm_scacheLock);
2823 if (!anotherReader) {
2824 tfid.Volume = scp->fid.volume;
2825 tfid.Vnode = scp->fid.vnode;
2826 tfid.Unique = scp->fid.unique;
2827 lock_ReleaseMutex(&scp->mx);
2828 osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%x", (long) scp);
2830 code = cm_Conn(&scp->fid, userp, reqp, &connp);
2834 callp = cm_GetRxConn(connp);
2835 code = RXAFS_ReleaseLock(callp, &tfid, &volSync);
2836 rx_PutConnection(callp);
2838 } while (cm_Analyze(connp, userp, reqp, &scp->fid, &volSync,
2840 code = cm_MapRPCError(code, reqp);
2843 osi_Log1(afsd_logp, "CALL ReleaseLock FAILURE, code 0x%x", code);
2845 osi_Log0(afsd_logp, "CALL ReleaseLock SUCCESS");
2847 lock_ObtainMutex(&scp->mx);
2853 void cm_CheckLocks()
2855 osi_queue_t *q, *nq;
2856 cm_file_lock_t *fileLock;
2862 struct rx_connection * callp;
2866 lock_ObtainWrite(&cm_scacheLock);
2867 q = cm_allFileLocks;
2869 fileLock = (cm_file_lock_t *) q;
2871 if (fileLock->flags & CM_FILELOCK_FLAG_INVALID) {
2872 osi_QRemove(&cm_allFileLocks, q);
2875 else if (!(fileLock->flags & CM_FILELOCK_FLAG_WAITING)) {
2876 tfid.Volume = fileLock->fid.volume;
2877 tfid.Vnode = fileLock->fid.vnode;
2878 tfid.Unique = fileLock->fid.unique;
2879 lock_ReleaseWrite(&cm_scacheLock);
2880 osi_Log1(afsd_logp, "CALL ExtendLock lock 0x%x", (long) fileLock);
2882 code = cm_Conn(&fileLock->fid, fileLock->userp,
2887 callp = cm_GetRxConn(connp);
2888 code = RXAFS_ExtendLock(callp, &tfid,
2890 rx_PutConnection(callp);
2892 } while (cm_Analyze(connp, fileLock->userp, &req,
2893 &fileLock->fid, &volSync, NULL, NULL,
2895 code = cm_MapRPCError(code, &req);
2897 osi_Log1(afsd_logp, "CALL ExtendLock FAILURE, code 0x%x", code);
2899 osi_Log0(afsd_logp, "CALL ExtendLock SUCCESS");
2901 lock_ObtainWrite(&cm_scacheLock);
2905 lock_ReleaseWrite(&cm_scacheLock);
2908 long cm_RetryLock(cm_file_lock_t *oldFileLock, int vcp_is_dead)
2911 int Which = ((oldFileLock->LockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
2916 cm_file_lock_t *fileLock;
2920 struct rx_connection * callp;
2923 code = CM_ERROR_TIMEDOUT;
2929 /* Look for a conflict. Also, if we are asking for a shared lock,
2930 * look for another shared lock, so we don't have to do an RPC.
2932 code = cm_GetSCache(&oldFileLock->fid, &scp, oldFileLock->userp, &req);
2938 fileLock = (cm_file_lock_t *)
2939 ((char *) q - offsetof(cm_file_lock_t, fileq));
2940 if ((fileLock->flags &
2941 (CM_FILELOCK_FLAG_INVALID | CM_FILELOCK_FLAG_WAITING))
2943 if ((oldFileLock->LockType & LOCKING_ANDX_SHARED_LOCK) == 0
2944 || (fileLock->LockType & LOCKING_ANDX_SHARED_LOCK) == 0) {
2945 cm_ReleaseSCache(scp);
2946 return CM_ERROR_WOULDBLOCK;
2956 tfid.Volume = oldFileLock->fid.volume;
2957 tfid.Vnode = oldFileLock->fid.vnode;
2958 tfid.Unique = oldFileLock->fid.unique;
2959 osi_Log1(afsd_logp, "CALL SetLock lock 0x%x", (long) oldFileLock);
2961 code = cm_Conn(&oldFileLock->fid, oldFileLock->userp,
2966 callp = cm_GetRxConn(connp);
2967 code = RXAFS_SetLock(callp, &tfid, Which,
2969 rx_PutConnection(callp);
2971 } while (cm_Analyze(connp, oldFileLock->userp, &req,
2972 &oldFileLock->fid, &volSync,
2974 code = cm_MapRPCError(code, &req);
2977 osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
2979 osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
2983 if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
2984 lock_ObtainMutex(&scp->mx);
2985 osi_QRemove(&scp->fileLocks, &oldFileLock->fileq);
2986 lock_ReleaseMutex(&scp->mx);
2988 lock_ObtainWrite(&cm_scacheLock);
2990 oldFileLock->flags = 0;
2991 else if (code != CM_ERROR_WOULDBLOCK) {
2992 oldFileLock->flags |= CM_FILELOCK_FLAG_INVALID;
2993 cm_ReleaseUser(oldFileLock->userp);
2994 oldFileLock->userp = NULL;
2996 lock_ReleaseWrite(&cm_scacheLock);