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 /* Why was this added for IFS? - jaltman 06/18/2006 */
550 lock_ObtainMutex(&scp->mx);
551 if ((scp->flags & CM_SCACHEFLAG_BULKSTATTING) == 0
552 && (scp->bulkStatProgress.QuadPart <= thyper.QuadPart))
554 scp->flags |= CM_SCACHEFLAG_BULKSTATTING;
555 cm_TryBulkStat(scp, &thyper, userp, reqp);
556 scp->flags &= ~CM_SCACHEFLAG_BULKSTATTING;
557 scp->bulkStatProgress = thyper;
559 lock_ReleaseMutex(&scp->mx);
562 lock_ObtainMutex(&bufferp->mx);
563 bufferOffset = thyper;
565 /* now get the data in the cache */
567 lock_ObtainMutex(&scp->mx);
568 code = cm_SyncOp(scp, bufferp, userp, reqp,
570 CM_SCACHESYNC_NEEDCALLBACK
572 | CM_SCACHESYNC_BUFLOCKED);
574 lock_ReleaseMutex(&scp->mx);
578 if (cm_HaveBuffer(scp, bufferp, 1)) {
579 lock_ReleaseMutex(&scp->mx);
583 /* otherwise, load the buffer and try again */
584 lock_ReleaseMutex(&bufferp->mx);
585 code = cm_GetBuffer(scp, bufferp, NULL, userp,
587 lock_ReleaseMutex(&scp->mx);
588 lock_ObtainMutex(&bufferp->mx);
593 lock_ReleaseMutex(&bufferp->mx);
594 buf_Release(bufferp);
598 } /* if (wrong buffer) ... */
600 /* now we have the buffer containing the entry we're interested
601 * in; copy it out if it represents a non-deleted entry.
603 entryInDir = curOffset.LowPart & (2048-1);
604 entryInBuffer = curOffset.LowPart & (cm_data.buf_blockSize - 1);
606 /* page header will help tell us which entries are free. Page
607 * header can change more often than once per buffer, since
608 * AFS 3 dir page size may be less than (but not more than) a
609 * buffer package buffer.
611 /* only look intra-buffer */
612 temp = curOffset.LowPart & (cm_data.buf_blockSize - 1);
613 temp &= ~(2048 - 1); /* turn off intra-page bits */
614 pageHeaderp = (cm_pageHeader_t *) (bufferp->datap + temp);
616 /* now determine which entry we're looking at in the page. If
617 * it is free (there's a free bitmap at the start of the dir),
618 * we should skip these 32 bytes.
620 slotInPage = (entryInDir & 0x7e0) >> 5;
621 if (!(pageHeaderp->freeBitmap[slotInPage>>3]
622 & (1 << (slotInPage & 0x7)))) {
623 /* this entry is free */
624 numDirChunks = 1; /* only skip this guy */
628 tp = bufferp->datap + entryInBuffer;
629 dep = (cm_dirEntry_t *) tp; /* now points to AFS3 dir entry */
631 /* while we're here, compute the next entry's location, too,
632 * since we'll need it when writing out the cookie into the
633 * dir listing stream.
635 numDirChunks = cm_NameEntries(dep->name, NULL);
637 /* compute the offset of the cookie representing the next entry */
638 nextEntryCookie = curOffset.LowPart
639 + (CM_DIR_CHUNKSIZE * numDirChunks);
641 if (dep->fid.vnode != 0) {
642 /* this is one of the entries to use: it is not deleted */
643 code = (*funcp)(scp, dep, parmp, &curOffset);
646 } /* if we're including this name */
649 /* and adjust curOffset to be where the new cookie is */
651 thyper.LowPart = CM_DIR_CHUNKSIZE * numDirChunks;
652 curOffset = LargeIntegerAdd(thyper, curOffset);
653 } /* while copying data for dir listing */
655 /* release the mutex */
657 lock_ReleaseMutex(&bufferp->mx);
658 buf_Release(bufferp);
663 int cm_NoneUpper(char *s)
667 if (c >= 'A' && c <= 'Z')
672 int cm_NoneLower(char *s)
676 if (c >= 'a' && c <= 'z')
681 long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
684 cm_lookupSearch_t *sp;
689 sp = (cm_lookupSearch_t *) rockp;
691 matchName = dep->name;
693 match = cm_stricmp(matchName, sp->searchNamep);
695 match = strcmp(matchName, sp->searchNamep);
699 && !cm_Is8Dot3(dep->name)) {
700 matchName = shortName;
701 cm_Gen8Dot3Name(dep, shortName, NULL);
703 match = cm_stricmp(matchName, sp->searchNamep);
705 match = strcmp(matchName, sp->searchNamep);
715 if (!sp->caseFold || matchName == shortName) {
716 sp->fid.vnode = ntohl(dep->fid.vnode);
717 sp->fid.unique = ntohl(dep->fid.unique);
718 return CM_ERROR_STOPNOW;
722 * If we get here, we are doing a case-insensitive search, and we
723 * have found a match. Now we determine what kind of match it is:
724 * exact, lower-case, upper-case, or none of the above. This is done
725 * in order to choose among matches, if there are more than one.
728 /* Exact matches are the best. */
729 match = strcmp(matchName, sp->searchNamep);
732 sp->fid.vnode = ntohl(dep->fid.vnode);
733 sp->fid.unique = ntohl(dep->fid.unique);
734 return CM_ERROR_STOPNOW;
737 /* Lower-case matches are next. */
740 if (cm_NoneUpper(matchName)) {
745 /* Upper-case matches are next. */
748 if (cm_NoneLower(matchName)) {
753 /* General matches are last. */
759 sp->fid.vnode = ntohl(dep->fid.vnode);
760 sp->fid.unique = ntohl(dep->fid.unique);
764 /* read the contents of a mount point into the appropriate string.
765 * called with locked scp, and returns with locked scp.
767 long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
774 if (scp->mountPointStringp[0])
777 /* otherwise, we have to read it in */
778 lock_ReleaseMutex(&scp->mx);
780 lock_ObtainRead(&scp->bufCreateLock);
781 thyper.LowPart = thyper.HighPart = 0;
782 code = buf_Get(scp, &thyper, &bufp);
783 lock_ReleaseRead(&scp->bufCreateLock);
785 lock_ObtainMutex(&scp->mx);
790 code = cm_SyncOp(scp, bufp, userp, reqp, 0,
791 CM_SCACHESYNC_READ | CM_SCACHESYNC_NEEDCALLBACK);
796 if (cm_HaveBuffer(scp, bufp, 0))
799 /* otherwise load buffer */
800 code = cm_GetBuffer(scp, bufp, NULL, userp, reqp);
805 /* locked, has callback, has valid data in buffer */
806 if ((tlen = scp->length.LowPart) > 1000)
807 return CM_ERROR_TOOBIG;
809 code = CM_ERROR_INVAL;
813 /* someone else did the work while we were out */
814 if (scp->mountPointStringp[0]) {
819 /* otherwise, copy out the link */
820 memcpy(scp->mountPointStringp, bufp->datap, tlen);
822 /* now make it null-terminated. Note that the original contents of a
823 * link that is a mount point is "#volname." where "." is there just to
824 * be turned into a null. That is, we can trash the last char of the
825 * link without damaging the vol name. This is a stupid convention,
826 * but that's the protocol.
828 scp->mountPointStringp[tlen-1] = 0;
837 /* called with a locked scp and chases the mount point, yielding outScpp.
838 * scp remains locked, just for simplicity of describing the interface.
840 long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
841 cm_req_t *reqp, cm_scache_t **outScpp)
856 if (scp->mountRootFid.cell != 0 && scp->mountRootGen >= cm_data.mountRootGen) {
857 tfid = scp->mountRootFid;
858 lock_ReleaseMutex(&scp->mx);
859 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
860 lock_ObtainMutex(&scp->mx);
864 /* parse the volume name */
865 mpNamep = scp->mountPointStringp;
866 osi_assert(mpNamep[0]);
867 tlen = strlen(scp->mountPointStringp);
868 mtType = *scp->mountPointStringp;
869 cellNamep = malloc(tlen);
870 volNamep = malloc(tlen);
872 cp = strrchr(mpNamep, ':');
874 /* cellular mount point */
875 memset(cellNamep, 0, tlen);
876 strncpy(cellNamep, mpNamep+1, cp - mpNamep - 1);
877 strcpy(volNamep, cp+1);
878 /* now look up the cell */
879 cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE);
883 strcpy(volNamep, mpNamep+1);
885 cellp = cm_FindCellByID(scp->fid.cell);
889 code = CM_ERROR_NOSUCHCELL;
893 vnLength = strlen(volNamep);
894 if (vnLength >= 8 && strcmp(volNamep + vnLength - 7, ".backup") == 0)
896 else if (vnLength >= 10
897 && strcmp(volNamep + vnLength - 9, ".readonly") == 0)
902 /* check for backups within backups */
904 && (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
905 == CM_SCACHEFLAG_RO) {
906 code = CM_ERROR_NOSUCHVOLUME;
910 /* now we need to get the volume */
911 lock_ReleaseMutex(&scp->mx);
912 code = cm_GetVolumeByName(cellp, volNamep, userp, reqp, 0, &volp);
913 lock_ObtainMutex(&scp->mx);
916 /* save the parent of the volume root for this is the
917 * place where the volume is mounted and we must remember
918 * this in the volume structure rather than just in the
919 * scache entry lest the scache entry gets recycled
922 lock_ObtainMutex(&volp->mx);
923 volp->dotdotFid = dscp->fid;
924 lock_ReleaseMutex(&volp->mx);
926 scp->mountRootFid.cell = cellp->cellID;
927 /* if the mt pt is in a read-only volume (not just a
928 * backup), and if there is a read-only volume for the
929 * target, and if this is a type '#' mount point, use
930 * the read-only, otherwise use the one specified.
932 if (mtType == '#' && (scp->flags & CM_SCACHEFLAG_PURERO)
933 && volp->roID != 0 && type == RWVOL)
936 scp->mountRootFid.volume = volp->roID;
937 else if (type == BACKVOL)
938 scp->mountRootFid.volume = volp->bkID;
940 scp->mountRootFid.volume = volp->rwID;
942 /* the rest of the fid is a magic number */
943 scp->mountRootFid.vnode = 1;
944 scp->mountRootFid.unique = 1;
945 scp->mountRootGen = cm_data.mountRootGen;
947 tfid = scp->mountRootFid;
948 lock_ReleaseMutex(&scp->mx);
949 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
950 lock_ObtainMutex(&scp->mx);
959 long cm_LookupInternal(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
960 cm_req_t *reqp, cm_scache_t **outpScpp)
963 int dnlcHit = 1; /* did we hit in the dnlc? yes, we did */
964 cm_scache_t *tscp = NULL;
965 cm_scache_t *mountedScp;
966 cm_lookupSearch_t rock;
969 if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
970 && strcmp(namep, "..") == 0) {
971 if (dscp->dotdotFid.volume == 0)
972 return CM_ERROR_NOSUCHVOLUME;
973 rock.fid = dscp->dotdotFid;
977 memset(&rock, 0, sizeof(rock));
978 rock.fid.cell = dscp->fid.cell;
979 rock.fid.volume = dscp->fid.volume;
980 rock.searchNamep = namep;
981 rock.caseFold = (flags & CM_FLAG_CASEFOLD);
982 rock.hasTilde = ((strchr(namep, '~') != NULL) ? 1 : 0);
984 /* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
985 code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
986 (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
988 /* code == 0 means we fell off the end of the dir, while stopnow means
989 * that we stopped early, probably because we found the entry we're
990 * looking for. Any other non-zero code is an error.
992 if (code && code != CM_ERROR_STOPNOW) {
993 /* if the cm_scache_t we are searching in is not a directory
994 * we must return path not found because the error
995 * is to describe the final component not an intermediary
997 if (code == CM_ERROR_NOTDIR) {
998 if (flags & CM_FLAG_CHECKPATH)
999 return CM_ERROR_NOSUCHPATH;
1001 return CM_ERROR_NOSUCHFILE;
1006 getroot = (dscp==cm_data.rootSCachep) ;
1008 if (!cm_freelanceEnabled || !getroot) {
1009 if (flags & CM_FLAG_CHECKPATH)
1010 return CM_ERROR_NOSUCHPATH;
1012 return CM_ERROR_NOSUCHFILE;
1014 else { /* nonexistent dir on freelance root, so add it */
1015 char fullname[200] = ".";
1018 osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %s",
1019 osi_LogSaveString(afsd_logp,namep));
1020 if (namep[0] == '.') {
1021 if (cm_GetCell_Gen(&namep[1], &fullname[1], CM_FLAG_CREATE)) {
1023 if ( stricmp(&namep[1], &fullname[1]) )
1024 code = cm_FreelanceAddSymlink(namep, fullname, &rock.fid);
1026 code = cm_FreelanceAddMount(namep, &fullname[1], "root.cell.", 1, &rock.fid);
1029 if (cm_GetCell_Gen(namep, fullname, CM_FLAG_CREATE)) {
1031 if ( stricmp(namep, fullname) )
1032 code = cm_FreelanceAddSymlink(namep, fullname, &rock.fid);
1034 code = cm_FreelanceAddMount(namep, fullname, "root.cell.", 0, &rock.fid);
1037 if (!found || code < 0) { /* add mount point failed, so give up */
1038 if (flags & CM_FLAG_CHECKPATH)
1039 return CM_ERROR_NOSUCHPATH;
1041 return CM_ERROR_NOSUCHFILE;
1043 tscp = NULL; /* to force call of cm_GetSCache */
1048 if ( !tscp ) /* we did not find it in the dnlc */
1051 code = cm_GetSCache(&rock.fid, &tscp, userp, reqp);
1055 /* tscp is now held */
1057 lock_ObtainMutex(&tscp->mx);
1058 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1059 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1061 lock_ReleaseMutex(&tscp->mx);
1062 cm_ReleaseSCache(tscp);
1065 /* tscp is now locked */
1067 if (!(flags & CM_FLAG_NOMOUNTCHASE)
1068 && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
1069 /* mount points are funny: they have a volume name to mount
1072 code = cm_ReadMountPoint(tscp, userp, reqp);
1074 code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
1076 lock_ReleaseMutex(&tscp->mx);
1077 cm_ReleaseSCache(tscp);
1084 lock_ReleaseMutex(&tscp->mx);
1087 /* copy back pointer */
1090 /* insert scache in dnlc */
1091 if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
1092 /* lock the directory entry to prevent racing callback revokes */
1093 lock_ObtainMutex(&dscp->mx);
1094 if ( dscp->cbServerp && dscp->cbExpires )
1095 cm_dnlcEnter(dscp, namep, tscp);
1096 lock_ReleaseMutex(&dscp->mx);
1103 int cm_ExpandSysName(char *inp, char *outp, long outSize, unsigned int index)
1108 tp = strrchr(inp, '@');
1110 return 0; /* no @sys */
1112 if (strcmp(tp, "@sys") != 0)
1113 return 0; /* no @sys */
1115 /* caller just wants to know if this is a valid @sys type of name */
1119 if (index >= MAXNUMSYSNAMES)
1122 /* otherwise generate the properly expanded @sys name */
1123 prefixCount = tp - inp;
1125 strncpy(outp, inp, prefixCount); /* copy out "a." from "a.@sys" */
1126 outp[prefixCount] = 0; /* null terminate the "a." */
1127 strcat(outp, cm_sysNameList[index]);/* append i386_nt40 */
1131 long cm_Lookup(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
1132 cm_req_t *reqp, cm_scache_t **outpScpp)
1136 int sysNameIndex = 0;
1137 cm_scache_t *scp = 0;
1139 if ( stricmp(namep,SMB_IOCTL_FILENAME_NOSLASH) == 0 ) {
1140 if (flags & CM_FLAG_CHECKPATH)
1141 return CM_ERROR_NOSUCHPATH;
1143 return CM_ERROR_NOSUCHFILE;
1146 for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
1147 code = cm_ExpandSysName(namep, tname, sizeof(tname), sysNameIndex);
1149 code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
1155 cm_ReleaseSCache(scp);
1159 return cm_LookupInternal(dscp, namep, flags, userp, reqp, outpScpp);
1163 /* None of the possible sysName expansions could be found */
1164 if (flags & CM_FLAG_CHECKPATH)
1165 return CM_ERROR_NOSUCHPATH;
1167 return CM_ERROR_NOSUCHFILE;
1170 long cm_Unlink(cm_scache_t *dscp, char *namep, cm_user_t *userp, cm_req_t *reqp)
1176 AFSFetchStatus newDirStatus;
1178 struct rx_connection * callp;
1180 #ifdef AFS_FREELANCE_CLIENT
1181 if (cm_freelanceEnabled && dscp == cm_data.rootSCachep) {
1182 /* deleting a mount point from the root dir. */
1183 code = cm_FreelanceRemoveMount(namep);
1188 /* make sure we don't screw up the dir status during the merge */
1189 lock_ObtainMutex(&dscp->mx);
1190 sflags = CM_SCACHESYNC_STOREDATA;
1191 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1192 lock_ReleaseMutex(&dscp->mx);
1197 afsFid.Volume = dscp->fid.volume;
1198 afsFid.Vnode = dscp->fid.vnode;
1199 afsFid.Unique = dscp->fid.unique;
1201 osi_Log1(afsd_logp, "CALL RemoveFile scp 0x%x", (long) dscp);
1203 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1207 callp = cm_GetRxConn(connp);
1208 code = RXAFS_RemoveFile(callp, &afsFid, namep,
1209 &newDirStatus, &volSync);
1210 rx_PutConnection(callp);
1212 } while (cm_Analyze(connp, userp, reqp, &dscp->fid, &volSync, NULL, NULL, code));
1213 code = cm_MapRPCError(code, reqp);
1216 osi_Log1(afsd_logp, "CALL RemoveFile FAILURE, code 0x%x", code);
1218 osi_Log0(afsd_logp, "CALL RemoveFile SUCCESS");
1220 lock_ObtainMutex(&dscp->mx);
1221 cm_dnlcRemove(dscp, namep);
1222 cm_SyncOpDone(dscp, NULL, sflags);
1224 cm_MergeStatus(dscp, &newDirStatus, &volSync, userp, 0);
1225 lock_ReleaseMutex(&dscp->mx);
1230 /* called with a locked vnode, and fills in the link info.
1231 * returns this the vnode still locked.
1233 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1240 lock_AssertMutex(&linkScp->mx);
1241 if (!linkScp->mountPointStringp[0]) {
1242 /* read the link data */
1243 lock_ReleaseMutex(&linkScp->mx);
1244 thyper.LowPart = thyper.HighPart = 0;
1245 code = buf_Get(linkScp, &thyper, &bufp);
1246 lock_ObtainMutex(&linkScp->mx);
1250 code = cm_SyncOp(linkScp, bufp, userp, reqp, 0,
1251 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1256 if (cm_HaveBuffer(linkScp, bufp, 0))
1259 code = cm_GetBuffer(linkScp, bufp, NULL, userp, reqp);
1264 } /* while loop to get the data */
1266 /* now if we still have no link read in,
1267 * copy the data from the buffer */
1268 if ((temp = linkScp->length.LowPart) >= MOUNTPOINTLEN) {
1270 return CM_ERROR_TOOBIG;
1273 /* otherwise, it fits; make sure it is still null (could have
1274 * lost race with someone else referencing this link above),
1275 * and if so, copy in the data.
1277 if (!linkScp->mountPointStringp[0]) {
1278 strncpy(linkScp->mountPointStringp, bufp->datap, temp);
1279 linkScp->mountPointStringp[temp] = 0; /* null terminate */
1282 } /* don't have sym link contents cached */
1287 /* called with a held vnode and a path suffix, with the held vnode being a
1288 * symbolic link. Our goal is to generate a new path to interpret, and return
1289 * this new path in newSpaceBufferp. If the new vnode is relative to a dir
1290 * other than the directory containing the symbolic link, then the new root is
1291 * returned in *newRootScpp, otherwise a null is returned there.
1293 long cm_AssembleLink(cm_scache_t *linkScp, char *pathSuffixp,
1294 cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1295 cm_user_t *userp, cm_req_t *reqp)
1302 lock_ObtainMutex(&linkScp->mx);
1303 code = cm_HandleLink(linkScp, userp, reqp);
1307 /* if we may overflow the buffer, bail out; buffer is signficantly
1308 * bigger than max path length, so we don't really have to worry about
1309 * being a little conservative here.
1311 if (strlen(linkScp->mountPointStringp) + strlen(pathSuffixp) + 2
1312 >= CM_UTILS_SPACESIZE)
1313 return CM_ERROR_TOOBIG;
1315 tsp = cm_GetSpace();
1316 linkp = linkScp->mountPointStringp;
1317 if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1318 if (strlen(linkp) > cm_mountRootLen)
1319 strcpy(tsp->data, linkp+cm_mountRootLen+1);
1322 *newRootScpp = cm_data.rootSCachep;
1323 cm_HoldSCache(cm_data.rootSCachep);
1324 } else if (linkp[0] == '\\' && linkp[1] == '\\') {
1325 if (!strnicmp(&linkp[2], cm_NetbiosName, (len = strlen(cm_NetbiosName))))
1327 char * p = &linkp[len + 3];
1328 if (strnicmp(p, "all", 3) == 0)
1331 strcpy(tsp->data, p);
1332 for (p = tsp->data; *p; p++) {
1336 *newRootScpp = cm_data.rootSCachep;
1337 cm_HoldSCache(cm_data.rootSCachep);
1339 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1340 strcpy(tsp->data, linkp);
1341 *newRootScpp = NULL;
1342 code = CM_ERROR_PATH_NOT_COVERED;
1344 } else if ( !strnicmp(linkp, "msdfs:", (len = strlen("msdfs:"))) ) {
1345 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1346 strcpy(tsp->data, linkp);
1347 *newRootScpp = NULL;
1348 code = CM_ERROR_PATH_NOT_COVERED;
1349 } else if (*linkp == '\\' || *linkp == '/') {
1351 /* formerly, this was considered to be from the AFS root,
1352 * but this seems to create problems. instead, we will just
1353 * reject the link */
1354 strcpy(tsp->data, linkp+1);
1355 *newRootScpp = cm_data.rootSCachep;
1356 cm_HoldSCache(cm_data.rootSCachep);
1358 /* we still copy the link data into the response so that
1359 * the user can see what the link points to
1361 linkScp->fileType = CM_SCACHETYPE_INVALID;
1362 strcpy(tsp->data, linkp);
1363 *newRootScpp = NULL;
1364 code = CM_ERROR_NOSUCHPATH;
1367 /* a relative link */
1368 strcpy(tsp->data, linkp);
1369 *newRootScpp = NULL;
1371 if (pathSuffixp[0] != 0) { /* if suffix string is non-null */
1372 strcat(tsp->data, "\\");
1373 strcat(tsp->data, pathSuffixp);
1375 *newSpaceBufferp = tsp;
1378 lock_ReleaseMutex(&linkScp->mx);
1382 long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
1383 cm_user_t *userp, char *tidPathp, cm_req_t *reqp, cm_scache_t **outScpp)
1386 char *tp; /* ptr moving through input buffer */
1387 char tc; /* temp char */
1388 int haveComponent; /* has new component started? */
1389 char component[256]; /* this is the new component */
1390 char *cp; /* component name being assembled */
1391 cm_scache_t *tscp; /* current location in the hierarchy */
1392 cm_scache_t *nscp; /* next dude down */
1393 cm_scache_t *dirScp; /* last dir we searched */
1394 cm_scache_t *linkScp; /* new root for the symlink we just
1396 cm_space_t *psp; /* space for current path, if we've hit
1398 cm_space_t *tempsp; /* temp vbl */
1399 char *restp; /* rest of the pathname to interpret */
1400 int symlinkCount; /* count of # of symlinks traversed */
1401 int extraFlag; /* avoid chasing mt pts for dir cmd */
1402 int phase = 1; /* 1 = tidPathp, 2 = pathp */
1415 cm_HoldSCache(tscp);
1422 /* map Unix slashes into DOS ones so we can interpret Unix
1428 if (!haveComponent) {
1431 } else if (tc == 0) {
1445 /* we have a component here */
1446 if (tc == 0 || tc == '\\') {
1447 /* end of the component; we're at the last
1448 * component if tc == 0. However, if the last
1449 * is a symlink, we have more to do.
1451 *cp++ = 0; /* add null termination */
1453 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
1454 extraFlag = CM_FLAG_NOMOUNTCHASE;
1455 code = cm_Lookup(tscp, component,
1457 userp, reqp, &nscp);
1459 cm_ReleaseSCache(tscp);
1461 cm_ReleaseSCache(dirScp);
1464 if (code == CM_ERROR_NOSUCHFILE && tscp->fileType == CM_SCACHETYPE_SYMLINK)
1465 return CM_ERROR_NOSUCHPATH;
1469 haveComponent = 0; /* component done */
1471 cm_ReleaseSCache(dirScp);
1472 dirScp = tscp; /* for some symlinks */
1473 tscp = nscp; /* already held */
1475 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
1478 cm_ReleaseSCache(dirScp);
1484 /* now, if tscp is a symlink, we should follow
1485 * it and assemble the path again.
1487 lock_ObtainMutex(&tscp->mx);
1488 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1489 CM_SCACHESYNC_GETSTATUS
1490 | CM_SCACHESYNC_NEEDCALLBACK);
1492 lock_ReleaseMutex(&tscp->mx);
1493 cm_ReleaseSCache(tscp);
1496 cm_ReleaseSCache(dirScp);
1501 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
1502 /* this is a symlink; assemble a new buffer */
1503 lock_ReleaseMutex(&tscp->mx);
1504 if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
1505 cm_ReleaseSCache(tscp);
1508 cm_ReleaseSCache(dirScp);
1513 return CM_ERROR_TOO_MANY_SYMLINKS;
1519 code = cm_AssembleLink(tscp, restp, &linkScp, &tempsp, userp, reqp);
1521 /* something went wrong */
1522 cm_ReleaseSCache(tscp);
1525 cm_ReleaseSCache(dirScp);
1531 /* otherwise, tempsp has the new path,
1532 * and linkScp is the new root from
1533 * which to interpret that path.
1534 * Continue with the namei processing,
1535 * also doing the bookkeeping for the
1536 * space allocation and tracking the
1537 * vnode reference counts.
1543 cm_ReleaseSCache(tscp);
1548 * now, if linkScp is null, that's
1549 * AssembleLink's way of telling us that
1550 * the sym link is relative to the dir
1551 * containing the link. We have a ref
1552 * to it in dirScp, and we hold it now
1553 * and reuse it as the new spot in the
1561 /* not a symlink, we may be done */
1562 lock_ReleaseMutex(&tscp->mx);
1570 cm_ReleaseSCache(dirScp);
1578 cm_ReleaseSCache(dirScp);
1581 } /* end of a component */
1584 } /* we have a component */
1585 } /* big while loop over all components */
1589 cm_ReleaseSCache(dirScp);
1595 cm_ReleaseSCache(tscp);
1599 /* called with a dir, and a vnode within the dir that happens to be a symlink.
1600 * We chase the link, and return a held pointer to the target, if it exists,
1601 * in *outScpp. If we succeed, we return 0, otherwise we return an error code
1602 * and do not hold or return a target vnode.
1604 * This is very similar to calling cm_NameI with the last component of a name,
1605 * which happens to be a symlink, except that we've already passed by the name.
1607 * This function is typically called by the directory listing functions, which
1608 * encounter symlinks but need to return the proper file length so programs
1609 * like "more" work properly when they make use of the attributes retrieved from
1612 * The input vnode should not be locked when this function is called.
1614 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
1615 cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
1619 cm_scache_t *newRootScp;
1621 osi_Log1(afsd_logp, "Evaluating symlink scp 0x%x", linkScp);
1623 code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
1627 /* now, if newRootScp is NULL, we're really being told that the symlink
1628 * is relative to the current directory (dscp).
1630 if (newRootScp == NULL) {
1632 cm_HoldSCache(dscp);
1635 code = cm_NameI(newRootScp, spacep->data,
1636 CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
1637 userp, NULL, reqp, outScpp);
1639 if (code == CM_ERROR_NOSUCHFILE)
1640 code = CM_ERROR_NOSUCHPATH;
1642 /* this stuff is allocated no matter what happened on the namei call,
1644 cm_FreeSpace(spacep);
1645 cm_ReleaseSCache(newRootScp);
1650 /* make this big enough so that one buffer of dir pages won't overflow. We'll
1651 * check anyway, but we want to minimize the chance that we have to leave stuff
1654 #define CM_BULKMAX 128
1656 /* rock for bulk stat calls */
1657 typedef struct cm_bulkStat {
1658 osi_hyper_t bufOffset; /* only do it for things in this buffer page */
1660 /* info for the actual call */
1661 int counter; /* next free slot */
1662 AFSFid fids[CM_BULKMAX];
1663 AFSFetchStatus stats[CM_BULKMAX];
1664 AFSCallBack callbacks[CM_BULKMAX];
1667 /* for a given entry, make sure that it isn't in the stat cache, and then
1668 * add it to the list of file IDs to be obtained.
1670 * Don't bother adding it if we already have a vnode. Note that the dir
1671 * is locked, so we have to be careful checking the vnode we're thinking of
1672 * processing, to avoid deadlocks.
1674 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
1685 /* Don't overflow bsp. */
1686 if (bsp->counter >= CM_BULKMAX)
1687 return CM_ERROR_STOPNOW;
1689 thyper.LowPart = cm_data.buf_blockSize;
1690 thyper.HighPart = 0;
1691 thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
1693 /* thyper is now the first byte past the end of the record we're
1694 * interested in, and bsp->bufOffset is the first byte of the record
1695 * we're interested in.
1696 * Skip data in the others.
1699 if (LargeIntegerLessThan(*offp, bsp->bufOffset))
1701 if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
1702 return CM_ERROR_STOPNOW;
1703 if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
1706 tfid.cell = scp->fid.cell;
1707 tfid.volume = scp->fid.volume;
1708 tfid.vnode = ntohl(dep->fid.vnode);
1709 tfid.unique = ntohl(dep->fid.unique);
1710 tscp = cm_FindSCache(&tfid);
1712 if (lock_TryMutex(&tscp->mx)) {
1713 /* we have an entry that we can look at */
1714 if (cm_HaveCallback(tscp)) {
1715 /* we have a callback on it. Don't bother
1716 * fetching this stat entry, since we're happy
1717 * with the info we have.
1719 lock_ReleaseMutex(&tscp->mx);
1720 cm_ReleaseSCache(tscp);
1723 lock_ReleaseMutex(&tscp->mx);
1725 cm_ReleaseSCache(tscp);
1728 #ifdef AFS_FREELANCE_CLIENT
1729 // yj: if this is a mountpoint under root.afs then we don't want it
1730 // to be bulkstat-ed, instead, we call getSCache directly and under
1731 // getSCache, it is handled specially.
1732 if ( cm_freelanceEnabled &&
1733 tfid.cell==AFS_FAKE_ROOT_CELL_ID &&
1734 tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
1735 !(tfid.vnode==0x1 && tfid.unique==0x1) )
1737 osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
1738 return cm_GetSCache(&tfid, &tscp, NULL, NULL);
1740 #endif /* AFS_FREELANCE_CLIENT */
1743 bsp->fids[i].Volume = scp->fid.volume;
1744 bsp->fids[i].Vnode = tfid.vnode;
1745 bsp->fids[i].Unique = tfid.unique;
1749 /* called with a locked scp and a pointer to a buffer. Make bulk stat
1750 * calls on all undeleted files in the page of the directory specified.
1752 void cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
1756 cm_bulkStat_t bb; /* this is *BIG*, probably 12K or so;
1757 * watch for stack problems */
1758 AFSCBFids fidStruct;
1759 AFSBulkStats statStruct;
1761 AFSCBs callbackStruct;
1764 cm_callbackRequest_t cbReq;
1770 struct rx_connection * callp;
1772 osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%x", (long) dscp);
1774 /* should be on a buffer boundary */
1775 osi_assert((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0);
1778 bb.bufOffset = *offsetp;
1780 lock_ReleaseMutex(&dscp->mx);
1781 /* first, assemble the file IDs we need to stat */
1782 code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) &bb, offsetp, userp, reqp, NULL);
1784 /* if we failed, bail out early */
1785 if (code && code != CM_ERROR_STOPNOW) {
1786 lock_ObtainMutex(&dscp->mx);
1790 /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
1791 * make the calls to create the entries. Handle AFSCBMAX files at a
1795 while (filex < bb.counter) {
1796 filesThisCall = bb.counter - filex;
1797 if (filesThisCall > AFSCBMAX)
1798 filesThisCall = AFSCBMAX;
1800 fidStruct.AFSCBFids_len = filesThisCall;
1801 fidStruct.AFSCBFids_val = &bb.fids[filex];
1802 statStruct.AFSBulkStats_len = filesThisCall;
1803 statStruct.AFSBulkStats_val = &bb.stats[filex];
1804 callbackStruct.AFSCBs_len = filesThisCall;
1805 callbackStruct.AFSCBs_val = &bb.callbacks[filex];
1806 cm_StartCallbackGrantingCall(NULL, &cbReq);
1807 osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
1809 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1813 callp = cm_GetRxConn(connp);
1814 code = RXAFS_BulkStatus(callp, &fidStruct,
1815 &statStruct, &callbackStruct, &volSync);
1816 rx_PutConnection(callp);
1818 } while (cm_Analyze(connp, userp, reqp, &dscp->fid,
1819 &volSync, NULL, &cbReq, code));
1820 code = cm_MapRPCError(code, reqp);
1823 osi_Log1(afsd_logp, "CALL BulkStatus FAILURE code 0x%x", code);
1825 osi_Log0(afsd_logp, "CALL BulkStatus SUCCESS");
1827 /* may as well quit on an error, since we're not going to do
1828 * much better on the next immediate call, either.
1831 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
1835 /* otherwise, we should do the merges */
1836 for (i = 0; i<filesThisCall; i++) {
1838 tfid.cell = dscp->fid.cell;
1839 tfid.volume = bb.fids[j].Volume;
1840 tfid.vnode = bb.fids[j].Vnode;
1841 tfid.unique = bb.fids[j].Unique;
1842 code = cm_GetSCache(&tfid, &scp, userp, reqp);
1846 /* otherwise, if this entry has no callback info,
1849 lock_ObtainMutex(&scp->mx);
1850 /* now, we have to be extra paranoid on merging in this
1851 * information, since we didn't use cm_SyncOp before
1852 * starting the fetch to make sure that no bad races
1853 * were occurring. Specifically, we need to make sure
1854 * we don't obliterate any newer information in the
1855 * vnode than have here.
1857 * Right now, be pretty conservative: if there's a
1858 * callback or a pending call, skip it.
1860 if (scp->cbServerp == NULL
1862 (CM_SCACHEFLAG_FETCHING
1863 | CM_SCACHEFLAG_STORING
1864 | CM_SCACHEFLAG_SIZESTORING))) {
1865 cm_EndCallbackGrantingCall(scp, &cbReq,
1867 CM_CALLBACK_MAINTAINCOUNT);
1868 cm_MergeStatus(scp, &bb.stats[j], &volSync,
1871 lock_ReleaseMutex(&scp->mx);
1872 cm_ReleaseSCache(scp);
1873 } /* all files in the response */
1874 /* now tell it to drop the count,
1875 * after doing the vnode processing above */
1876 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
1878 filex += filesThisCall;
1879 } /* while there are still more files to process */
1880 lock_ObtainMutex(&dscp->mx);
1881 osi_Log0(afsd_logp, "END cm_TryBulkStat");
1884 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
1888 /* initialize store back mask as inexpensive local variable */
1890 memset(statusp, 0, sizeof(AFSStoreStatus));
1892 /* copy out queued info from scache first, if scp passed in */
1894 if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
1895 statusp->ClientModTime = scp->clientModTime;
1896 mask |= AFS_SETMODTIME;
1897 scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
1902 /* now add in our locally generated request */
1903 if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
1904 statusp->ClientModTime = attrp->clientModTime;
1905 mask |= AFS_SETMODTIME;
1907 if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
1908 statusp->UnixModeBits = attrp->unixModeBits;
1909 mask |= AFS_SETMODE;
1911 if (attrp->mask & CM_ATTRMASK_OWNER) {
1912 statusp->Owner = attrp->owner;
1913 mask |= AFS_SETOWNER;
1915 if (attrp->mask & CM_ATTRMASK_GROUP) {
1916 statusp->Group = attrp->group;
1917 mask |= AFS_SETGROUP;
1920 statusp->Mask = mask;
1923 /* set the file size, and make sure that all relevant buffers have been
1924 * truncated. Ensure that any partially truncated buffers have been zeroed
1925 * to the end of the buffer.
1927 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
1933 /* start by locking out buffer creation */
1934 lock_ObtainWrite(&scp->bufCreateLock);
1936 /* verify that this is a file, not a dir or a symlink */
1937 lock_ObtainMutex(&scp->mx);
1938 code = cm_SyncOp(scp, NULL, userp, reqp, 0,
1939 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1943 if (scp->fileType != CM_SCACHETYPE_FILE) {
1944 code = CM_ERROR_ISDIR;
1949 if (LargeIntegerLessThan(*sizep, scp->length))
1954 lock_ReleaseMutex(&scp->mx);
1956 /* can't hold scp->mx lock here, since we may wait for a storeback to
1957 * finish if the buffer package is cleaning a buffer by storing it to
1961 buf_Truncate(scp, userp, reqp, sizep);
1963 /* now ensure that file length is short enough, and update truncPos */
1964 lock_ObtainMutex(&scp->mx);
1966 /* make sure we have a callback (so we have the right value for the
1967 * length), and wait for it to be safe to do a truncate.
1969 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
1970 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
1971 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
1975 if (LargeIntegerLessThan(*sizep, scp->length)) {
1976 /* a real truncation. If truncPos is not set yet, or is bigger
1977 * than where we're truncating the file, set truncPos to this
1982 if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
1983 || LargeIntegerLessThan(*sizep, scp->length)) {
1985 scp->truncPos = *sizep;
1986 scp->mask |= CM_SCACHEMASK_TRUNCPOS;
1988 /* in either case, the new file size has been changed */
1989 scp->length = *sizep;
1990 scp->mask |= CM_SCACHEMASK_LENGTH;
1992 else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
1993 /* really extending the file */
1994 scp->length = *sizep;
1995 scp->mask |= CM_SCACHEMASK_LENGTH;
1998 /* done successfully */
2002 lock_ReleaseMutex(&scp->mx);
2003 lock_ReleaseWrite(&scp->bufCreateLock);
2008 /* set the file size or other attributes (but not both at once) */
2009 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
2014 AFSFetchStatus afsOutStatus;
2018 AFSStoreStatus afsInStatus;
2019 struct rx_connection * callp;
2021 /* handle file length setting */
2022 if (attrp->mask & CM_ATTRMASK_LENGTH)
2023 return cm_SetLength(scp, &attrp->length, userp, reqp);
2025 flags = CM_SCACHESYNC_STORESTATUS;
2027 lock_ObtainMutex(&scp->mx);
2028 /* otherwise, we have to make an RPC to get the status */
2029 code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
2031 /* make the attr structure */
2032 cm_StatusFromAttr(&afsInStatus, scp, attrp);
2034 tfid.Volume = scp->fid.volume;
2035 tfid.Vnode = scp->fid.vnode;
2036 tfid.Unique = scp->fid.unique;
2038 lock_ReleaseMutex(&scp->mx);
2042 /* now make the RPC */
2043 osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%x", (long) scp);
2045 code = cm_Conn(&scp->fid, userp, reqp, &connp);
2049 callp = cm_GetRxConn(connp);
2050 code = RXAFS_StoreStatus(callp, &tfid,
2051 &afsInStatus, &afsOutStatus, &volSync);
2052 rx_PutConnection(callp);
2054 } while (cm_Analyze(connp, userp, reqp,
2055 &scp->fid, &volSync, NULL, NULL, code));
2056 code = cm_MapRPCError(code, reqp);
2059 osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
2061 osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
2063 lock_ObtainMutex(&scp->mx);
2064 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
2066 cm_MergeStatus(scp, &afsOutStatus, &volSync, userp,
2067 CM_MERGEFLAG_FORCE);
2069 /* if we're changing the mode bits, discard the ACL cache,
2070 * since we changed the mode bits.
2072 if (afsInStatus.Mask & AFS_SETMODE) cm_FreeAllACLEnts(scp);
2073 lock_ReleaseMutex(&scp->mx);
2077 long cm_Create(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2078 cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
2083 cm_callbackRequest_t cbReq;
2088 AFSStoreStatus inStatus;
2089 AFSFetchStatus updatedDirStatus;
2090 AFSFetchStatus newFileStatus;
2091 AFSCallBack newFileCallback;
2093 struct rx_connection * callp;
2095 /* can't create names with @sys in them; must expand it manually first.
2096 * return "invalid request" if they try.
2098 if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2099 return CM_ERROR_ATSYS;
2102 /* before starting the RPC, mark that we're changing the file data, so
2103 * that someone who does a chmod will know to wait until our call
2106 lock_ObtainMutex(&dscp->mx);
2107 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2109 cm_StartCallbackGrantingCall(NULL, &cbReq);
2111 lock_ReleaseMutex(&dscp->mx);
2117 cm_StatusFromAttr(&inStatus, NULL, attrp);
2119 /* try the RPC now */
2120 osi_Log1(afsd_logp, "CALL CreateFile scp 0x%x", (long) dscp);
2122 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2126 dirAFSFid.Volume = dscp->fid.volume;
2127 dirAFSFid.Vnode = dscp->fid.vnode;
2128 dirAFSFid.Unique = dscp->fid.unique;
2130 callp = cm_GetRxConn(connp);
2131 code = RXAFS_CreateFile(connp->callp, &dirAFSFid, namep,
2132 &inStatus, &newAFSFid, &newFileStatus,
2133 &updatedDirStatus, &newFileCallback,
2135 rx_PutConnection(callp);
2137 } while (cm_Analyze(connp, userp, reqp,
2138 &dscp->fid, &volSync, NULL, &cbReq, code));
2139 code = cm_MapRPCError(code, reqp);
2142 osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
2144 osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
2146 lock_ObtainMutex(&dscp->mx);
2147 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2149 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2151 lock_ReleaseMutex(&dscp->mx);
2153 /* now try to create the file's entry, too, but be careful to
2154 * make sure that we don't merge in old info. Since we weren't locking
2155 * out any requests during the file's creation, we may have pretty old
2159 newFid.cell = dscp->fid.cell;
2160 newFid.volume = dscp->fid.volume;
2161 newFid.vnode = newAFSFid.Vnode;
2162 newFid.unique = newAFSFid.Unique;
2163 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2165 lock_ObtainMutex(&scp->mx);
2166 if (!cm_HaveCallback(scp)) {
2167 cm_MergeStatus(scp, &newFileStatus, &volSync,
2169 cm_EndCallbackGrantingCall(scp, &cbReq,
2170 &newFileCallback, 0);
2173 lock_ReleaseMutex(&scp->mx);
2178 /* make sure we end things properly */
2180 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2185 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
2189 lock_ObtainWrite(&scp->bufCreateLock);
2190 code = buf_CleanVnode(scp, userp, reqp);
2191 lock_ReleaseWrite(&scp->bufCreateLock);
2193 lock_ObtainMutex(&scp->mx);
2194 scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA
2195 | CM_SCACHEFLAG_OUTOFSPACE);
2196 if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
2197 | CM_SCACHEMASK_CLIENTMODTIME
2198 | CM_SCACHEMASK_LENGTH))
2199 code = cm_StoreMini(scp, userp, reqp);
2200 lock_ReleaseMutex(&scp->mx);
2205 long cm_MakeDir(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2206 cm_user_t *userp, cm_req_t *reqp)
2211 cm_callbackRequest_t cbReq;
2216 AFSStoreStatus inStatus;
2217 AFSFetchStatus updatedDirStatus;
2218 AFSFetchStatus newDirStatus;
2219 AFSCallBack newDirCallback;
2221 struct rx_connection * callp;
2223 /* can't create names with @sys in them; must expand it manually first.
2224 * return "invalid request" if they try.
2226 if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2227 return CM_ERROR_ATSYS;
2230 /* before starting the RPC, mark that we're changing the directory
2231 * data, so that someone who does a chmod on the dir will wait until
2232 * our call completes.
2234 lock_ObtainMutex(&dscp->mx);
2235 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2237 cm_StartCallbackGrantingCall(NULL, &cbReq);
2239 lock_ReleaseMutex(&dscp->mx);
2245 cm_StatusFromAttr(&inStatus, NULL, attrp);
2247 /* try the RPC now */
2248 osi_Log1(afsd_logp, "CALL MakeDir scp 0x%x", (long) dscp);
2250 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2254 dirAFSFid.Volume = dscp->fid.volume;
2255 dirAFSFid.Vnode = dscp->fid.vnode;
2256 dirAFSFid.Unique = dscp->fid.unique;
2258 callp = cm_GetRxConn(connp);
2259 code = RXAFS_MakeDir(connp->callp, &dirAFSFid, namep,
2260 &inStatus, &newAFSFid, &newDirStatus,
2261 &updatedDirStatus, &newDirCallback,
2263 rx_PutConnection(callp);
2265 } while (cm_Analyze(connp, userp, reqp,
2266 &dscp->fid, &volSync, NULL, &cbReq, code));
2267 code = cm_MapRPCError(code, reqp);
2270 osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
2272 osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
2274 lock_ObtainMutex(&dscp->mx);
2275 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2277 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2279 lock_ReleaseMutex(&dscp->mx);
2281 /* now try to create the new dir's entry, too, but be careful to
2282 * make sure that we don't merge in old info. Since we weren't locking
2283 * out any requests during the file's creation, we may have pretty old
2287 newFid.cell = dscp->fid.cell;
2288 newFid.volume = dscp->fid.volume;
2289 newFid.vnode = newAFSFid.Vnode;
2290 newFid.unique = newAFSFid.Unique;
2291 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2293 lock_ObtainMutex(&scp->mx);
2294 if (!cm_HaveCallback(scp)) {
2295 cm_MergeStatus(scp, &newDirStatus, &volSync,
2297 cm_EndCallbackGrantingCall(scp, &cbReq,
2298 &newDirCallback, 0);
2301 lock_ReleaseMutex(&scp->mx);
2302 cm_ReleaseSCache(scp);
2306 /* make sure we end things properly */
2308 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2310 /* and return error code */
2314 long cm_Link(cm_scache_t *dscp, char *namep, cm_scache_t *sscp, long flags,
2315 cm_user_t *userp, cm_req_t *reqp)
2320 AFSFid existingAFSFid;
2321 AFSFetchStatus updatedDirStatus;
2322 AFSFetchStatus newLinkStatus;
2324 struct rx_connection * callp;
2326 if (dscp->fid.cell != sscp->fid.cell ||
2327 dscp->fid.volume != sscp->fid.volume) {
2328 return CM_ERROR_CROSSDEVLINK;
2331 lock_ObtainMutex(&dscp->mx);
2332 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2333 lock_ReleaseMutex(&dscp->mx);
2338 /* try the RPC now */
2339 osi_Log1(afsd_logp, "CALL Link scp 0x%x", (long) dscp);
2341 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2344 dirAFSFid.Volume = dscp->fid.volume;
2345 dirAFSFid.Vnode = dscp->fid.vnode;
2346 dirAFSFid.Unique = dscp->fid.unique;
2348 existingAFSFid.Volume = sscp->fid.volume;
2349 existingAFSFid.Vnode = sscp->fid.vnode;
2350 existingAFSFid.Unique = sscp->fid.unique;
2352 callp = cm_GetRxConn(connp);
2353 code = RXAFS_Link(callp, &dirAFSFid, namep, &existingAFSFid,
2354 &newLinkStatus, &updatedDirStatus, &volSync);
2355 rx_PutConnection(callp);
2356 osi_Log1(smb_logp," RXAFS_Link returns 0x%x", code);
2358 } while (cm_Analyze(connp, userp, reqp,
2359 &dscp->fid, &volSync, NULL, NULL, code));
2361 code = cm_MapRPCError(code, reqp);
2364 osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
2366 osi_Log0(afsd_logp, "CALL Link SUCCESS");
2368 lock_ObtainMutex(&dscp->mx);
2369 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2371 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2373 lock_ReleaseMutex(&dscp->mx);
2378 long cm_SymLink(cm_scache_t *dscp, char *namep, char *contentsp, long flags,
2379 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
2387 AFSStoreStatus inStatus;
2388 AFSFetchStatus updatedDirStatus;
2389 AFSFetchStatus newLinkStatus;
2391 struct rx_connection * callp;
2393 /* before starting the RPC, mark that we're changing the directory data,
2394 * so that someone who does a chmod on the dir will wait until our
2397 lock_ObtainMutex(&dscp->mx);
2398 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2399 lock_ReleaseMutex(&dscp->mx);
2404 cm_StatusFromAttr(&inStatus, NULL, attrp);
2406 /* try the RPC now */
2407 osi_Log1(afsd_logp, "CALL Symlink scp 0x%x", (long) dscp);
2409 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2413 dirAFSFid.Volume = dscp->fid.volume;
2414 dirAFSFid.Vnode = dscp->fid.vnode;
2415 dirAFSFid.Unique = dscp->fid.unique;
2417 callp = cm_GetRxConn(connp);
2418 code = RXAFS_Symlink(callp, &dirAFSFid, namep, contentsp,
2419 &inStatus, &newAFSFid, &newLinkStatus,
2420 &updatedDirStatus, &volSync);
2421 rx_PutConnection(callp);
2423 } while (cm_Analyze(connp, userp, reqp,
2424 &dscp->fid, &volSync, NULL, NULL, code));
2425 code = cm_MapRPCError(code, reqp);
2428 osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
2430 osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
2432 lock_ObtainMutex(&dscp->mx);
2433 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2435 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2437 lock_ReleaseMutex(&dscp->mx);
2439 /* now try to create the new dir's entry, too, but be careful to
2440 * make sure that we don't merge in old info. Since we weren't locking
2441 * out any requests during the file's creation, we may have pretty old
2445 newFid.cell = dscp->fid.cell;
2446 newFid.volume = dscp->fid.volume;
2447 newFid.vnode = newAFSFid.Vnode;
2448 newFid.unique = newAFSFid.Unique;
2449 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2451 lock_ObtainMutex(&scp->mx);
2452 if (!cm_HaveCallback(scp)) {
2453 cm_MergeStatus(scp, &newLinkStatus, &volSync,
2456 lock_ReleaseMutex(&scp->mx);
2457 cm_ReleaseSCache(scp);
2461 /* and return error code */
2465 long cm_RemoveDir(cm_scache_t *dscp, char *namep, cm_user_t *userp,
2472 AFSFetchStatus updatedDirStatus;
2474 struct rx_connection * callp;
2476 /* before starting the RPC, mark that we're changing the directory data,
2477 * so that someone who does a chmod on the dir will wait until our
2480 lock_ObtainMutex(&dscp->mx);
2481 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2482 lock_ReleaseMutex(&dscp->mx);
2488 /* try the RPC now */
2489 osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%x", (long) dscp);
2491 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2495 dirAFSFid.Volume = dscp->fid.volume;
2496 dirAFSFid.Vnode = dscp->fid.vnode;
2497 dirAFSFid.Unique = dscp->fid.unique;
2499 callp = cm_GetRxConn(connp);
2500 code = RXAFS_RemoveDir(callp, &dirAFSFid, namep,
2501 &updatedDirStatus, &volSync);
2502 rx_PutConnection(callp);
2504 } while (cm_Analyze(connp, userp, reqp,
2505 &dscp->fid, &volSync, NULL, NULL, code));
2506 code = cm_MapRPCErrorRmdir(code, reqp);
2509 osi_Log1(afsd_logp, "CALL RemoveDir FAILURE, code 0x%x", code);
2511 osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
2513 lock_ObtainMutex(&dscp->mx);
2514 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2516 cm_dnlcRemove(dscp, namep);
2517 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2519 lock_ReleaseMutex(&dscp->mx);
2521 /* and return error code */
2525 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
2527 /* grab mutex on contents */
2528 lock_ObtainMutex(&scp->mx);
2530 /* reset the prefetch info */
2531 scp->prefetch.base.LowPart = 0; /* base */
2532 scp->prefetch.base.HighPart = 0;
2533 scp->prefetch.end.LowPart = 0; /* and end */
2534 scp->prefetch.end.HighPart = 0;
2536 /* release mutex on contents */
2537 lock_ReleaseMutex(&scp->mx);
2543 long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
2544 char *newNamep, cm_user_t *userp, cm_req_t *reqp)
2548 AFSFid oldDirAFSFid;
2549 AFSFid newDirAFSFid;
2551 AFSFetchStatus updatedOldDirStatus;
2552 AFSFetchStatus updatedNewDirStatus;
2555 struct rx_connection * callp;
2557 /* before starting the RPC, mark that we're changing the directory data,
2558 * so that someone who does a chmod on the dir will wait until our call
2559 * completes. We do this in vnode order so that we don't deadlock,
2560 * which makes the code a little verbose.
2562 if (oldDscp == newDscp) {
2563 /* check for identical names */
2564 if (strcmp(oldNamep, newNamep) == 0)
2565 return CM_ERROR_RENAME_IDENTICAL;
2568 lock_ObtainMutex(&oldDscp->mx);
2569 cm_dnlcRemove(oldDscp, oldNamep);
2570 cm_dnlcRemove(oldDscp, newNamep);
2571 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2572 CM_SCACHESYNC_STOREDATA);
2573 lock_ReleaseMutex(&oldDscp->mx);
2576 /* two distinct dir vnodes */
2578 if (oldDscp->fid.cell != newDscp->fid.cell ||
2579 oldDscp->fid.volume != newDscp->fid.volume)
2580 return CM_ERROR_CROSSDEVLINK;
2582 /* shouldn't happen that we have distinct vnodes for two
2583 * different files, but could due to deliberate attack, or
2584 * stale info. Avoid deadlocks and quit now.
2586 if (oldDscp->fid.vnode == newDscp->fid.vnode)
2587 return CM_ERROR_CROSSDEVLINK;
2589 if (oldDscp->fid.vnode < newDscp->fid.vnode) {
2590 lock_ObtainMutex(&oldDscp->mx);
2591 cm_dnlcRemove(oldDscp, oldNamep);
2592 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2593 CM_SCACHESYNC_STOREDATA);
2594 lock_ReleaseMutex(&oldDscp->mx);
2596 lock_ObtainMutex(&newDscp->mx);
2597 cm_dnlcRemove(newDscp, newNamep);
2598 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2599 CM_SCACHESYNC_STOREDATA);
2600 lock_ReleaseMutex(&newDscp->mx);
2602 /* cleanup first one */
2603 lock_ObtainMutex(&newDscp->mx);
2604 cm_SyncOpDone(oldDscp, NULL,
2605 CM_SCACHESYNC_STOREDATA);
2606 lock_ReleaseMutex(&oldDscp->mx);
2611 /* lock the new vnode entry first */
2612 lock_ObtainMutex(&newDscp->mx);
2613 cm_dnlcRemove(newDscp, newNamep);
2614 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2615 CM_SCACHESYNC_STOREDATA);
2616 lock_ReleaseMutex(&newDscp->mx);
2618 lock_ObtainMutex(&oldDscp->mx);
2619 cm_dnlcRemove(oldDscp, oldNamep);
2620 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2621 CM_SCACHESYNC_STOREDATA);
2622 lock_ReleaseMutex(&oldDscp->mx);
2624 /* cleanup first one */
2625 lock_ObtainMutex(&newDscp->mx);
2626 cm_SyncOpDone(newDscp, NULL,
2627 CM_SCACHESYNC_STOREDATA);
2628 lock_ReleaseMutex(&newDscp->mx);
2632 } /* two distinct vnodes */
2639 /* try the RPC now */
2640 osi_Log2(afsd_logp, "CALL Rename old scp 0x%x new scp 0x%x",
2641 (long) oldDscp, (long) newDscp);
2643 code = cm_Conn(&oldDscp->fid, userp, reqp, &connp);
2647 oldDirAFSFid.Volume = oldDscp->fid.volume;
2648 oldDirAFSFid.Vnode = oldDscp->fid.vnode;
2649 oldDirAFSFid.Unique = oldDscp->fid.unique;
2650 newDirAFSFid.Volume = newDscp->fid.volume;
2651 newDirAFSFid.Vnode = newDscp->fid.vnode;
2652 newDirAFSFid.Unique = newDscp->fid.unique;
2654 callp = cm_GetRxConn(connp);
2655 code = RXAFS_Rename(callp, &oldDirAFSFid, oldNamep,
2656 &newDirAFSFid, newNamep,
2657 &updatedOldDirStatus, &updatedNewDirStatus,
2659 rx_PutConnection(callp);
2661 } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
2662 &volSync, NULL, NULL, code));
2663 code = cm_MapRPCError(code, reqp);
2666 osi_Log1(afsd_logp, "CALL Rename FAILURE, code 0x%x", code);
2668 osi_Log0(afsd_logp, "CALL Rename SUCCESS");
2670 /* update the individual stat cache entries for the directories */
2671 lock_ObtainMutex(&oldDscp->mx);
2672 cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
2674 cm_MergeStatus(oldDscp, &updatedOldDirStatus, &volSync,
2677 lock_ReleaseMutex(&oldDscp->mx);
2679 /* and update it for the new one, too, if necessary */
2681 lock_ObtainMutex(&newDscp->mx);
2682 cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
2684 cm_MergeStatus(newDscp, &updatedNewDirStatus, &volSync,
2687 lock_ReleaseMutex(&newDscp->mx);
2690 /* and return error code */
2694 long cm_Lock(cm_scache_t *scp, unsigned char LockType,
2695 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
2696 u_long Timeout, cm_user_t *userp, cm_req_t *reqp,
2700 int Which = ((LockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
2704 cm_file_lock_t *fileLock;
2707 struct rx_connection * callp;
2709 osi_Log1(afsd_logp, "cm_Lock scp 0x%x ...", (long) scp);
2710 osi_Log4(afsd_logp, "cm_Lock type 0x%x offset %d length %d timeout %d",
2711 LockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, Timeout);
2713 /* Look for a conflict. Also, if we are asking for a shared lock,
2714 * look for another shared lock, so we don't have to do an RPC.
2718 fileLock = (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
2719 if ((fileLock->flags & (CM_FILELOCK_FLAG_INVALID | CM_FILELOCK_FLAG_WAITING)) == 0) {
2720 if ((LockType & LOCKING_ANDX_SHARED_LOCK) == 0 ||
2721 (fileLock->LockType & LOCKING_ANDX_SHARED_LOCK) == 0)
2722 return CM_ERROR_WOULDBLOCK;
2728 osi_Log1(afsd_logp, "cm_Lock found = %d", found);
2733 tfid.Volume = scp->fid.volume;
2734 tfid.Vnode = scp->fid.vnode;
2735 tfid.Unique = scp->fid.unique;
2736 lock_ReleaseMutex(&scp->mx);
2738 osi_Log1(afsd_logp, "CALL SetLock scp 0x%x", (long) scp);
2739 code = cm_Conn(&scp->fid, userp, reqp, &connp);
2743 callp = cm_GetRxConn(connp);
2744 code = RXAFS_SetLock(callp, &tfid, Which,
2746 rx_PutConnection(callp);
2748 } while (cm_Analyze(connp, userp, reqp, &scp->fid, &volSync,
2751 osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
2753 osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
2754 lock_ObtainMutex(&scp->mx);
2755 code = cm_MapRPCError(code, reqp);
2758 if (code == 0 || Timeout != 0) {
2759 fileLock = malloc(sizeof(cm_file_lock_t));
2760 fileLock->LockType = LockType;
2762 fileLock->userp = userp;
2763 fileLock->fid = scp->fid;
2764 fileLock->LOffset = LOffset;
2765 fileLock->LLength = LLength;
2766 fileLock->flags = (code == 0 ? 0 : CM_FILELOCK_FLAG_WAITING);
2767 osi_QAdd(&scp->fileLocks, &fileLock->fileq);
2768 lock_ObtainWrite(&cm_scacheLock);
2769 osi_QAdd(&cm_allFileLocks, &fileLock->q);
2770 lock_ReleaseWrite(&cm_scacheLock);
2773 osi_Log1(afsd_logp, "cm_Lock Lock added 0x%x", (long) fileLock);
2778 long cm_Unlock(cm_scache_t *scp, unsigned char LockType,
2779 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
2780 cm_user_t *userp, cm_req_t *reqp)
2783 int Which = ((LockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
2787 cm_file_lock_t *fileLock, *ourLock;
2788 osi_queue_t *q, *qq;
2789 int anotherReader = 0;
2792 struct rx_connection * callp;
2794 osi_Log4(afsd_logp, "cm_Unlock scp 0x%x type 0x%x offset %d length %d",
2795 (long) scp, LockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
2797 if (LargeIntegerLessThan(LLength, scp->length))
2800 /* Look for our own lock on the list, so as to remove it.
2801 * Also, determine if we're the last reader; if not, avoid an RPC.
2805 fileLock = (cm_file_lock_t *)
2806 ((char *) q - offsetof(cm_file_lock_t, fileq));
2808 && fileLock->userp == userp
2809 && LargeIntegerEqualTo(fileLock->LOffset, LOffset)
2810 && LargeIntegerEqualTo(fileLock->LLength, LLength)) {
2815 else if (fileLock->LockType & LOCKING_ANDX_SHARED_LOCK)
2820 /* ignore byte ranges */
2821 if (smallLock && !found) {
2822 osi_Log0(afsd_logp, "cm_Unlock lock not found and ignored");
2826 /* don't try to unlock other people's locks */
2828 osi_Log0(afsd_logp, "cm_Unlock lock not found; failure");
2829 return CM_ERROR_WOULDBLOCK;
2832 /* discard lock record */
2833 osi_QRemove(&scp->fileLocks, qq);
2835 * Don't delete it here; let the daemon delete it, to simplify
2836 * the daemon's traversal of the list.
2838 lock_ObtainWrite(&cm_scacheLock);
2839 ourLock->flags |= CM_FILELOCK_FLAG_INVALID;
2840 cm_ReleaseUser(ourLock->userp);
2841 lock_ReleaseWrite(&cm_scacheLock);
2843 if (!anotherReader) {
2844 tfid.Volume = scp->fid.volume;
2845 tfid.Vnode = scp->fid.vnode;
2846 tfid.Unique = scp->fid.unique;
2847 lock_ReleaseMutex(&scp->mx);
2848 osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%x", (long) scp);
2850 code = cm_Conn(&scp->fid, userp, reqp, &connp);
2854 callp = cm_GetRxConn(connp);
2855 code = RXAFS_ReleaseLock(callp, &tfid, &volSync);
2856 rx_PutConnection(callp);
2858 } while (cm_Analyze(connp, userp, reqp, &scp->fid, &volSync,
2860 code = cm_MapRPCError(code, reqp);
2863 osi_Log1(afsd_logp, "CALL ReleaseLock FAILURE, code 0x%x", code);
2865 osi_Log0(afsd_logp, "CALL ReleaseLock SUCCESS");
2867 lock_ObtainMutex(&scp->mx);
2870 osi_Log1(afsd_logp, "cm_Unlock code 0x%x", code);
2874 void cm_CheckLocks()
2876 osi_queue_t *q, *nq;
2877 cm_file_lock_t *fileLock;
2883 struct rx_connection * callp;
2887 lock_ObtainWrite(&cm_scacheLock);
2888 q = cm_allFileLocks;
2890 fileLock = (cm_file_lock_t *) q;
2892 if (fileLock->flags & CM_FILELOCK_FLAG_INVALID) {
2893 osi_QRemove(&cm_allFileLocks, q);
2896 else if (!(fileLock->flags & CM_FILELOCK_FLAG_WAITING)) {
2897 tfid.Volume = fileLock->fid.volume;
2898 tfid.Vnode = fileLock->fid.vnode;
2899 tfid.Unique = fileLock->fid.unique;
2900 lock_ReleaseWrite(&cm_scacheLock);
2901 osi_Log1(afsd_logp, "CALL ExtendLock lock 0x%x", (long) fileLock);
2903 code = cm_Conn(&fileLock->fid, fileLock->userp,
2908 callp = cm_GetRxConn(connp);
2909 code = RXAFS_ExtendLock(callp, &tfid,
2911 rx_PutConnection(callp);
2913 } while (cm_Analyze(connp, fileLock->userp, &req,
2914 &fileLock->fid, &volSync, NULL, NULL,
2916 code = cm_MapRPCError(code, &req);
2918 osi_Log1(afsd_logp, "CALL ExtendLock FAILURE, code 0x%x", code);
2920 osi_Log0(afsd_logp, "CALL ExtendLock SUCCESS");
2922 lock_ObtainWrite(&cm_scacheLock);
2926 lock_ReleaseWrite(&cm_scacheLock);
2929 long cm_RetryLock(cm_file_lock_t *oldFileLock, int vcp_is_dead)
2932 int Which = ((oldFileLock->LockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
2937 cm_file_lock_t *fileLock;
2941 struct rx_connection * callp;
2944 code = CM_ERROR_TIMEDOUT;
2950 /* Look for a conflict. Also, if we are asking for a shared lock,
2951 * look for another shared lock, so we don't have to do an RPC.
2953 code = cm_GetSCache(&oldFileLock->fid, &scp, oldFileLock->userp, &req);
2959 fileLock = (cm_file_lock_t *)
2960 ((char *) q - offsetof(cm_file_lock_t, fileq));
2961 if ((fileLock->flags &
2962 (CM_FILELOCK_FLAG_INVALID | CM_FILELOCK_FLAG_WAITING))
2964 if ((oldFileLock->LockType & LOCKING_ANDX_SHARED_LOCK) == 0
2965 || (fileLock->LockType & LOCKING_ANDX_SHARED_LOCK) == 0) {
2966 cm_ReleaseSCache(scp);
2967 return CM_ERROR_WOULDBLOCK;
2977 tfid.Volume = oldFileLock->fid.volume;
2978 tfid.Vnode = oldFileLock->fid.vnode;
2979 tfid.Unique = oldFileLock->fid.unique;
2980 osi_Log1(afsd_logp, "CALL SetLock lock 0x%x", (long) oldFileLock);
2982 code = cm_Conn(&oldFileLock->fid, oldFileLock->userp,
2987 callp = cm_GetRxConn(connp);
2988 code = RXAFS_SetLock(callp, &tfid, Which,
2990 rx_PutConnection(callp);
2992 } while (cm_Analyze(connp, oldFileLock->userp, &req,
2993 &oldFileLock->fid, &volSync,
2995 code = cm_MapRPCError(code, &req);
2998 osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
3000 osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
3004 if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
3005 lock_ObtainMutex(&scp->mx);
3006 osi_QRemove(&scp->fileLocks, &oldFileLock->fileq);
3007 lock_ReleaseMutex(&scp->mx);
3009 lock_ObtainWrite(&cm_scacheLock);
3011 oldFileLock->flags = 0;
3012 else if (code != CM_ERROR_WOULDBLOCK) {
3013 oldFileLock->flags |= CM_FILELOCK_FLAG_INVALID;
3014 cm_ReleaseUser(oldFileLock->userp);
3015 oldFileLock->userp = NULL;
3017 lock_ReleaseWrite(&cm_scacheLock);