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 */
548 lock_ObtainMutex(&bufferp->mx);
549 bufferOffset = thyper;
551 /* now get the data in the cache */
553 lock_ObtainMutex(&scp->mx);
554 code = cm_SyncOp(scp, bufferp, userp, reqp,
556 CM_SCACHESYNC_NEEDCALLBACK
558 | CM_SCACHESYNC_BUFLOCKED);
560 lock_ReleaseMutex(&scp->mx);
564 if (cm_HaveBuffer(scp, bufferp, 1)) {
565 lock_ReleaseMutex(&scp->mx);
569 /* otherwise, load the buffer and try again */
570 lock_ReleaseMutex(&bufferp->mx);
571 code = cm_GetBuffer(scp, bufferp, NULL, userp,
573 lock_ReleaseMutex(&scp->mx);
574 lock_ObtainMutex(&bufferp->mx);
579 lock_ReleaseMutex(&bufferp->mx);
580 buf_Release(bufferp);
584 } /* if (wrong buffer) ... */
586 /* now we have the buffer containing the entry we're interested
587 * in; copy it out if it represents a non-deleted entry.
589 entryInDir = curOffset.LowPart & (2048-1);
590 entryInBuffer = curOffset.LowPart & (cm_data.buf_blockSize - 1);
592 /* page header will help tell us which entries are free. Page
593 * header can change more often than once per buffer, since
594 * AFS 3 dir page size may be less than (but not more than) a
595 * buffer package buffer.
597 /* only look intra-buffer */
598 temp = curOffset.LowPart & (cm_data.buf_blockSize - 1);
599 temp &= ~(2048 - 1); /* turn off intra-page bits */
600 pageHeaderp = (cm_pageHeader_t *) (bufferp->datap + temp);
602 /* now determine which entry we're looking at in the page. If
603 * it is free (there's a free bitmap at the start of the dir),
604 * we should skip these 32 bytes.
606 slotInPage = (entryInDir & 0x7e0) >> 5;
607 if (!(pageHeaderp->freeBitmap[slotInPage>>3]
608 & (1 << (slotInPage & 0x7)))) {
609 /* this entry is free */
610 numDirChunks = 1; /* only skip this guy */
614 tp = bufferp->datap + entryInBuffer;
615 dep = (cm_dirEntry_t *) tp; /* now points to AFS3 dir entry */
617 /* while we're here, compute the next entry's location, too,
618 * since we'll need it when writing out the cookie into the
619 * dir listing stream.
621 numDirChunks = cm_NameEntries(dep->name, NULL);
623 /* compute the offset of the cookie representing the next entry */
624 nextEntryCookie = curOffset.LowPart
625 + (CM_DIR_CHUNKSIZE * numDirChunks);
627 if (dep->fid.vnode != 0) {
628 /* this is one of the entries to use: it is not deleted */
629 code = (*funcp)(scp, dep, parmp, &curOffset);
632 } /* if we're including this name */
635 /* and adjust curOffset to be where the new cookie is */
637 thyper.LowPart = CM_DIR_CHUNKSIZE * numDirChunks;
638 curOffset = LargeIntegerAdd(thyper, curOffset);
639 } /* while copying data for dir listing */
641 /* release the mutex */
643 lock_ReleaseMutex(&bufferp->mx);
644 buf_Release(bufferp);
649 int cm_NoneUpper(char *s)
653 if (c >= 'A' && c <= 'Z')
658 int cm_NoneLower(char *s)
662 if (c >= 'a' && c <= 'z')
667 long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
670 cm_lookupSearch_t *sp;
675 sp = (cm_lookupSearch_t *) rockp;
677 matchName = dep->name;
679 match = cm_stricmp(matchName, sp->searchNamep);
681 match = strcmp(matchName, sp->searchNamep);
685 && !cm_Is8Dot3(dep->name)) {
686 matchName = shortName;
687 cm_Gen8Dot3Name(dep, shortName, NULL);
689 match = cm_stricmp(matchName, sp->searchNamep);
691 match = strcmp(matchName, sp->searchNamep);
701 if (!sp->caseFold || matchName == shortName) {
702 sp->fid.vnode = ntohl(dep->fid.vnode);
703 sp->fid.unique = ntohl(dep->fid.unique);
704 return CM_ERROR_STOPNOW;
708 * If we get here, we are doing a case-insensitive search, and we
709 * have found a match. Now we determine what kind of match it is:
710 * exact, lower-case, upper-case, or none of the above. This is done
711 * in order to choose among matches, if there are more than one.
714 /* Exact matches are the best. */
715 match = strcmp(matchName, sp->searchNamep);
718 sp->fid.vnode = ntohl(dep->fid.vnode);
719 sp->fid.unique = ntohl(dep->fid.unique);
720 return CM_ERROR_STOPNOW;
723 /* Lower-case matches are next. */
726 if (cm_NoneUpper(matchName)) {
731 /* Upper-case matches are next. */
734 if (cm_NoneLower(matchName)) {
739 /* General matches are last. */
745 sp->fid.vnode = ntohl(dep->fid.vnode);
746 sp->fid.unique = ntohl(dep->fid.unique);
750 /* read the contents of a mount point into the appropriate string.
751 * called with locked scp, and returns with locked scp.
753 long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
760 if (scp->mountPointStringp[0])
763 /* otherwise, we have to read it in */
764 lock_ReleaseMutex(&scp->mx);
766 lock_ObtainRead(&scp->bufCreateLock);
767 thyper.LowPart = thyper.HighPart = 0;
768 code = buf_Get(scp, &thyper, &bufp);
769 lock_ReleaseRead(&scp->bufCreateLock);
771 lock_ObtainMutex(&scp->mx);
776 code = cm_SyncOp(scp, bufp, userp, reqp, 0,
777 CM_SCACHESYNC_READ | CM_SCACHESYNC_NEEDCALLBACK);
782 if (cm_HaveBuffer(scp, bufp, 0))
785 /* otherwise load buffer */
786 code = cm_GetBuffer(scp, bufp, NULL, userp, reqp);
791 /* locked, has callback, has valid data in buffer */
792 if ((tlen = scp->length.LowPart) > 1000)
793 return CM_ERROR_TOOBIG;
795 code = CM_ERROR_INVAL;
799 /* someone else did the work while we were out */
800 if (scp->mountPointStringp[0]) {
805 /* otherwise, copy out the link */
806 memcpy(scp->mountPointStringp, bufp->datap, tlen);
808 /* now make it null-terminated. Note that the original contents of a
809 * link that is a mount point is "#volname." where "." is there just to
810 * be turned into a null. That is, we can trash the last char of the
811 * link without damaging the vol name. This is a stupid convention,
812 * but that's the protocol.
814 scp->mountPointStringp[tlen-1] = 0;
823 /* called with a locked scp and chases the mount point, yielding outScpp.
824 * scp remains locked, just for simplicity of describing the interface.
826 long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
827 cm_req_t *reqp, cm_scache_t **outScpp)
842 if (scp->mountRootFid.cell != 0 && scp->mountRootGen >= cm_data.mountRootGen) {
843 tfid = scp->mountRootFid;
844 lock_ReleaseMutex(&scp->mx);
845 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
846 lock_ObtainMutex(&scp->mx);
850 /* parse the volume name */
851 mpNamep = scp->mountPointStringp;
852 osi_assert(mpNamep[0]);
853 tlen = strlen(scp->mountPointStringp);
854 mtType = *scp->mountPointStringp;
855 cellNamep = malloc(tlen);
856 volNamep = malloc(tlen);
858 cp = strrchr(mpNamep, ':');
860 /* cellular mount point */
861 memset(cellNamep, 0, tlen);
862 strncpy(cellNamep, mpNamep+1, cp - mpNamep - 1);
863 strcpy(volNamep, cp+1);
864 /* now look up the cell */
865 cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE);
869 strcpy(volNamep, mpNamep+1);
871 cellp = cm_FindCellByID(scp->fid.cell);
875 code = CM_ERROR_NOSUCHCELL;
879 vnLength = strlen(volNamep);
880 if (vnLength >= 8 && strcmp(volNamep + vnLength - 7, ".backup") == 0)
882 else if (vnLength >= 10
883 && strcmp(volNamep + vnLength - 9, ".readonly") == 0)
888 /* check for backups within backups */
890 && (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
891 == CM_SCACHEFLAG_RO) {
892 code = CM_ERROR_NOSUCHVOLUME;
896 /* now we need to get the volume */
897 lock_ReleaseMutex(&scp->mx);
898 code = cm_GetVolumeByName(cellp, volNamep, userp, reqp, 0, &volp);
899 lock_ObtainMutex(&scp->mx);
902 /* save the parent of the volume root for this is the
903 * place where the volume is mounted and we must remember
904 * this in the volume structure rather than just in the
905 * scache entry lest the scache entry gets recycled
908 lock_ObtainMutex(&volp->mx);
909 volp->dotdotFid = dscp->fid;
910 lock_ReleaseMutex(&volp->mx);
912 scp->mountRootFid.cell = cellp->cellID;
913 /* if the mt pt is in a read-only volume (not just a
914 * backup), and if there is a read-only volume for the
915 * target, and if this is a type '#' mount point, use
916 * the read-only, otherwise use the one specified.
918 if (mtType == '#' && (scp->flags & CM_SCACHEFLAG_PURERO)
919 && volp->roID != 0 && type == RWVOL)
922 scp->mountRootFid.volume = volp->roID;
923 else if (type == BACKVOL)
924 scp->mountRootFid.volume = volp->bkID;
926 scp->mountRootFid.volume = volp->rwID;
928 /* the rest of the fid is a magic number */
929 scp->mountRootFid.vnode = 1;
930 scp->mountRootFid.unique = 1;
931 scp->mountRootGen = cm_data.mountRootGen;
933 tfid = scp->mountRootFid;
934 lock_ReleaseMutex(&scp->mx);
935 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
936 lock_ObtainMutex(&scp->mx);
945 long cm_LookupInternal(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
946 cm_req_t *reqp, cm_scache_t **outpScpp)
949 int dnlcHit = 1; /* did we hit in the dnlc? yes, we did */
950 cm_scache_t *tscp = NULL;
951 cm_scache_t *mountedScp;
952 cm_lookupSearch_t rock;
955 if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
956 && strcmp(namep, "..") == 0) {
957 if (dscp->dotdotFid.volume == 0)
958 return CM_ERROR_NOSUCHVOLUME;
959 rock.fid = dscp->dotdotFid;
963 memset(&rock, 0, sizeof(rock));
964 rock.fid.cell = dscp->fid.cell;
965 rock.fid.volume = dscp->fid.volume;
966 rock.searchNamep = namep;
967 rock.caseFold = (flags & CM_FLAG_CASEFOLD);
968 rock.hasTilde = ((strchr(namep, '~') != NULL) ? 1 : 0);
970 /* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
971 code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
972 (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
974 /* code == 0 means we fell off the end of the dir, while stopnow means
975 * that we stopped early, probably because we found the entry we're
976 * looking for. Any other non-zero code is an error.
978 if (code && code != CM_ERROR_STOPNOW) {
979 /* if the cm_scache_t we are searching in is not a directory
980 * we must return path not found because the error
981 * is to describe the final component not an intermediary
983 if (code == CM_ERROR_NOTDIR) {
984 if (flags & CM_FLAG_CHECKPATH)
985 return CM_ERROR_NOSUCHPATH;
987 return CM_ERROR_NOSUCHFILE;
992 getroot = (dscp==cm_data.rootSCachep) ;
994 if (!cm_freelanceEnabled || !getroot) {
995 if (flags & CM_FLAG_CHECKPATH)
996 return CM_ERROR_NOSUCHPATH;
998 return CM_ERROR_NOSUCHFILE;
1000 else { /* nonexistent dir on freelance root, so add it */
1001 char fullname[200] = ".";
1004 osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %s",
1005 osi_LogSaveString(afsd_logp,namep));
1006 if (namep[0] == '.') {
1007 if (cm_GetCell_Gen(&namep[1], &fullname[1], CM_FLAG_CREATE)) {
1009 if ( stricmp(&namep[1], &fullname[1]) )
1010 code = cm_FreelanceAddSymlink(namep, fullname, &rock.fid);
1012 code = cm_FreelanceAddMount(namep, &fullname[1], "root.cell.", 1, &rock.fid);
1015 if (cm_GetCell_Gen(namep, fullname, CM_FLAG_CREATE)) {
1017 if ( stricmp(namep, fullname) )
1018 code = cm_FreelanceAddSymlink(namep, fullname, &rock.fid);
1020 code = cm_FreelanceAddMount(namep, fullname, "root.cell.", 0, &rock.fid);
1023 if (!found || code < 0) { /* add mount point failed, so give up */
1024 if (flags & CM_FLAG_CHECKPATH)
1025 return CM_ERROR_NOSUCHPATH;
1027 return CM_ERROR_NOSUCHFILE;
1029 tscp = NULL; /* to force call of cm_GetSCache */
1034 if ( !tscp ) /* we did not find it in the dnlc */
1037 code = cm_GetSCache(&rock.fid, &tscp, userp, reqp);
1041 /* tscp is now held */
1043 lock_ObtainMutex(&tscp->mx);
1044 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1045 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1047 lock_ReleaseMutex(&tscp->mx);
1048 cm_ReleaseSCache(tscp);
1051 /* tscp is now locked */
1053 if (!(flags & CM_FLAG_NOMOUNTCHASE)
1054 && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
1055 /* mount points are funny: they have a volume name to mount
1058 code = cm_ReadMountPoint(tscp, userp, reqp);
1060 code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
1062 lock_ReleaseMutex(&tscp->mx);
1063 cm_ReleaseSCache(tscp);
1070 lock_ReleaseMutex(&tscp->mx);
1073 /* copy back pointer */
1076 /* insert scache in dnlc */
1077 if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
1078 /* lock the directory entry to prevent racing callback revokes */
1079 lock_ObtainMutex(&dscp->mx);
1080 if ( dscp->cbServerp && dscp->cbExpires )
1081 cm_dnlcEnter(dscp, namep, tscp);
1082 lock_ReleaseMutex(&dscp->mx);
1089 int cm_ExpandSysName(char *inp, char *outp, long outSize, unsigned int index)
1094 tp = strrchr(inp, '@');
1096 return 0; /* no @sys */
1098 if (strcmp(tp, "@sys") != 0)
1099 return 0; /* no @sys */
1101 /* caller just wants to know if this is a valid @sys type of name */
1105 if (index >= MAXNUMSYSNAMES)
1108 /* otherwise generate the properly expanded @sys name */
1109 prefixCount = tp - inp;
1111 strncpy(outp, inp, prefixCount); /* copy out "a." from "a.@sys" */
1112 outp[prefixCount] = 0; /* null terminate the "a." */
1113 strcat(outp, cm_sysNameList[index]);/* append i386_nt40 */
1117 long cm_Lookup(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
1118 cm_req_t *reqp, cm_scache_t **outpScpp)
1122 int sysNameIndex = 0;
1123 cm_scache_t *scp = 0;
1125 if ( stricmp(namep,SMB_IOCTL_FILENAME_NOSLASH) == 0 ) {
1126 if (flags & CM_FLAG_CHECKPATH)
1127 return CM_ERROR_NOSUCHPATH;
1129 return CM_ERROR_NOSUCHFILE;
1132 for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
1133 code = cm_ExpandSysName(namep, tname, sizeof(tname), sysNameIndex);
1135 code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
1141 cm_ReleaseSCache(scp);
1145 return cm_LookupInternal(dscp, namep, flags, userp, reqp, outpScpp);
1149 /* None of the possible sysName expansions could be found */
1150 if (flags & CM_FLAG_CHECKPATH)
1151 return CM_ERROR_NOSUCHPATH;
1153 return CM_ERROR_NOSUCHFILE;
1156 long cm_Unlink(cm_scache_t *dscp, char *namep, cm_user_t *userp, cm_req_t *reqp)
1162 AFSFetchStatus newDirStatus;
1164 struct rx_connection * callp;
1166 #ifdef AFS_FREELANCE_CLIENT
1167 if (cm_freelanceEnabled && dscp == cm_data.rootSCachep) {
1168 /* deleting a mount point from the root dir. */
1169 code = cm_FreelanceRemoveMount(namep);
1174 /* make sure we don't screw up the dir status during the merge */
1175 lock_ObtainMutex(&dscp->mx);
1176 sflags = CM_SCACHESYNC_STOREDATA;
1177 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1178 lock_ReleaseMutex(&dscp->mx);
1183 afsFid.Volume = dscp->fid.volume;
1184 afsFid.Vnode = dscp->fid.vnode;
1185 afsFid.Unique = dscp->fid.unique;
1187 osi_Log1(afsd_logp, "CALL RemoveFile scp 0x%x", (long) dscp);
1189 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1193 callp = cm_GetRxConn(connp);
1194 code = RXAFS_RemoveFile(callp, &afsFid, namep,
1195 &newDirStatus, &volSync);
1196 rx_PutConnection(callp);
1198 } while (cm_Analyze(connp, userp, reqp, &dscp->fid, &volSync, NULL, NULL, code));
1199 code = cm_MapRPCError(code, reqp);
1202 osi_Log1(afsd_logp, "CALL RemoveFile FAILURE, code 0x%x", code);
1204 osi_Log0(afsd_logp, "CALL RemoveFile SUCCESS");
1206 lock_ObtainMutex(&dscp->mx);
1207 cm_dnlcRemove(dscp, namep);
1208 cm_SyncOpDone(dscp, NULL, sflags);
1210 cm_MergeStatus(dscp, &newDirStatus, &volSync, userp, 0);
1211 lock_ReleaseMutex(&dscp->mx);
1216 /* called with a locked vnode, and fills in the link info.
1217 * returns this the vnode still locked.
1219 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1226 lock_AssertMutex(&linkScp->mx);
1227 if (!linkScp->mountPointStringp[0]) {
1228 /* read the link data */
1229 lock_ReleaseMutex(&linkScp->mx);
1230 thyper.LowPart = thyper.HighPart = 0;
1231 code = buf_Get(linkScp, &thyper, &bufp);
1232 lock_ObtainMutex(&linkScp->mx);
1236 code = cm_SyncOp(linkScp, bufp, userp, reqp, 0,
1237 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1242 if (cm_HaveBuffer(linkScp, bufp, 0))
1245 code = cm_GetBuffer(linkScp, bufp, NULL, userp, reqp);
1250 } /* while loop to get the data */
1252 /* now if we still have no link read in,
1253 * copy the data from the buffer */
1254 if ((temp = linkScp->length.LowPart) >= MOUNTPOINTLEN) {
1256 return CM_ERROR_TOOBIG;
1259 /* otherwise, it fits; make sure it is still null (could have
1260 * lost race with someone else referencing this link above),
1261 * and if so, copy in the data.
1263 if (!linkScp->mountPointStringp[0]) {
1264 strncpy(linkScp->mountPointStringp, bufp->datap, temp);
1265 linkScp->mountPointStringp[temp] = 0; /* null terminate */
1268 } /* don't have sym link contents cached */
1273 /* called with a held vnode and a path suffix, with the held vnode being a
1274 * symbolic link. Our goal is to generate a new path to interpret, and return
1275 * this new path in newSpaceBufferp. If the new vnode is relative to a dir
1276 * other than the directory containing the symbolic link, then the new root is
1277 * returned in *newRootScpp, otherwise a null is returned there.
1279 long cm_AssembleLink(cm_scache_t *linkScp, char *pathSuffixp,
1280 cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1281 cm_user_t *userp, cm_req_t *reqp)
1288 lock_ObtainMutex(&linkScp->mx);
1289 code = cm_HandleLink(linkScp, userp, reqp);
1293 /* if we may overflow the buffer, bail out; buffer is signficantly
1294 * bigger than max path length, so we don't really have to worry about
1295 * being a little conservative here.
1297 if (strlen(linkScp->mountPointStringp) + strlen(pathSuffixp) + 2
1298 >= CM_UTILS_SPACESIZE)
1299 return CM_ERROR_TOOBIG;
1301 tsp = cm_GetSpace();
1302 linkp = linkScp->mountPointStringp;
1303 if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1304 if (strlen(linkp) > cm_mountRootLen)
1305 strcpy(tsp->data, linkp+cm_mountRootLen+1);
1308 *newRootScpp = cm_data.rootSCachep;
1309 cm_HoldSCache(cm_data.rootSCachep);
1310 } else if (linkp[0] == '\\' && linkp[1] == '\\') {
1311 if (!strnicmp(&linkp[2], cm_NetbiosName, (len = strlen(cm_NetbiosName))))
1313 char * p = &linkp[len + 3];
1314 if (strnicmp(p, "all", 3) == 0)
1317 strcpy(tsp->data, p);
1318 for (p = tsp->data; *p; p++) {
1322 *newRootScpp = cm_data.rootSCachep;
1323 cm_HoldSCache(cm_data.rootSCachep);
1325 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1326 strcpy(tsp->data, linkp);
1327 *newRootScpp = NULL;
1328 code = CM_ERROR_PATH_NOT_COVERED;
1330 } else if ( !strnicmp(linkp, "msdfs:", (len = strlen("msdfs:"))) ) {
1331 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1332 strcpy(tsp->data, linkp);
1333 *newRootScpp = NULL;
1334 code = CM_ERROR_PATH_NOT_COVERED;
1335 } else if (*linkp == '\\' || *linkp == '/') {
1337 /* formerly, this was considered to be from the AFS root,
1338 * but this seems to create problems. instead, we will just
1339 * reject the link */
1340 strcpy(tsp->data, linkp+1);
1341 *newRootScpp = cm_data.rootSCachep;
1342 cm_HoldSCache(cm_data.rootSCachep);
1344 /* we still copy the link data into the response so that
1345 * the user can see what the link points to
1347 linkScp->fileType = CM_SCACHETYPE_INVALID;
1348 strcpy(tsp->data, linkp);
1349 *newRootScpp = NULL;
1350 code = CM_ERROR_NOSUCHPATH;
1353 /* a relative link */
1354 strcpy(tsp->data, linkp);
1355 *newRootScpp = NULL;
1357 if (pathSuffixp[0] != 0) { /* if suffix string is non-null */
1358 strcat(tsp->data, "\\");
1359 strcat(tsp->data, pathSuffixp);
1361 *newSpaceBufferp = tsp;
1364 lock_ReleaseMutex(&linkScp->mx);
1368 long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
1369 cm_user_t *userp, char *tidPathp, cm_req_t *reqp, cm_scache_t **outScpp)
1372 char *tp; /* ptr moving through input buffer */
1373 char tc; /* temp char */
1374 int haveComponent; /* has new component started? */
1375 char component[256]; /* this is the new component */
1376 char *cp; /* component name being assembled */
1377 cm_scache_t *tscp; /* current location in the hierarchy */
1378 cm_scache_t *nscp; /* next dude down */
1379 cm_scache_t *dirScp; /* last dir we searched */
1380 cm_scache_t *linkScp; /* new root for the symlink we just
1382 cm_space_t *psp; /* space for current path, if we've hit
1384 cm_space_t *tempsp; /* temp vbl */
1385 char *restp; /* rest of the pathname to interpret */
1386 int symlinkCount; /* count of # of symlinks traversed */
1387 int extraFlag; /* avoid chasing mt pts for dir cmd */
1388 int phase = 1; /* 1 = tidPathp, 2 = pathp */
1401 cm_HoldSCache(tscp);
1408 /* map Unix slashes into DOS ones so we can interpret Unix
1414 if (!haveComponent) {
1417 } else if (tc == 0) {
1431 /* we have a component here */
1432 if (tc == 0 || tc == '\\') {
1433 /* end of the component; we're at the last
1434 * component if tc == 0. However, if the last
1435 * is a symlink, we have more to do.
1437 *cp++ = 0; /* add null termination */
1439 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
1440 extraFlag = CM_FLAG_NOMOUNTCHASE;
1441 code = cm_Lookup(tscp, component,
1443 userp, reqp, &nscp);
1445 cm_ReleaseSCache(tscp);
1447 cm_ReleaseSCache(dirScp);
1450 if (code == CM_ERROR_NOSUCHFILE && tscp->fileType == CM_SCACHETYPE_SYMLINK)
1451 return CM_ERROR_NOSUCHPATH;
1455 haveComponent = 0; /* component done */
1457 cm_ReleaseSCache(dirScp);
1458 dirScp = tscp; /* for some symlinks */
1459 tscp = nscp; /* already held */
1461 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
1464 cm_ReleaseSCache(dirScp);
1470 /* now, if tscp is a symlink, we should follow
1471 * it and assemble the path again.
1473 lock_ObtainMutex(&tscp->mx);
1474 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1475 CM_SCACHESYNC_GETSTATUS
1476 | CM_SCACHESYNC_NEEDCALLBACK);
1478 lock_ReleaseMutex(&tscp->mx);
1479 cm_ReleaseSCache(tscp);
1482 cm_ReleaseSCache(dirScp);
1487 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
1488 /* this is a symlink; assemble a new buffer */
1489 lock_ReleaseMutex(&tscp->mx);
1490 if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
1491 cm_ReleaseSCache(tscp);
1494 cm_ReleaseSCache(dirScp);
1499 return CM_ERROR_TOO_MANY_SYMLINKS;
1505 code = cm_AssembleLink(tscp, restp, &linkScp, &tempsp, userp, reqp);
1507 /* something went wrong */
1508 cm_ReleaseSCache(tscp);
1511 cm_ReleaseSCache(dirScp);
1517 /* otherwise, tempsp has the new path,
1518 * and linkScp is the new root from
1519 * which to interpret that path.
1520 * Continue with the namei processing,
1521 * also doing the bookkeeping for the
1522 * space allocation and tracking the
1523 * vnode reference counts.
1529 cm_ReleaseSCache(tscp);
1534 * now, if linkScp is null, that's
1535 * AssembleLink's way of telling us that
1536 * the sym link is relative to the dir
1537 * containing the link. We have a ref
1538 * to it in dirScp, and we hold it now
1539 * and reuse it as the new spot in the
1547 /* not a symlink, we may be done */
1548 lock_ReleaseMutex(&tscp->mx);
1556 cm_ReleaseSCache(dirScp);
1564 cm_ReleaseSCache(dirScp);
1567 } /* end of a component */
1570 } /* we have a component */
1571 } /* big while loop over all components */
1575 cm_ReleaseSCache(dirScp);
1581 cm_ReleaseSCache(tscp);
1585 /* called with a dir, and a vnode within the dir that happens to be a symlink.
1586 * We chase the link, and return a held pointer to the target, if it exists,
1587 * in *outScpp. If we succeed, we return 0, otherwise we return an error code
1588 * and do not hold or return a target vnode.
1590 * This is very similar to calling cm_NameI with the last component of a name,
1591 * which happens to be a symlink, except that we've already passed by the name.
1593 * This function is typically called by the directory listing functions, which
1594 * encounter symlinks but need to return the proper file length so programs
1595 * like "more" work properly when they make use of the attributes retrieved from
1598 * The input vnode should not be locked when this function is called.
1600 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
1601 cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
1605 cm_scache_t *newRootScp;
1607 osi_Log1(afsd_logp, "Evaluating symlink scp 0x%x", linkScp);
1609 code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
1613 /* now, if newRootScp is NULL, we're really being told that the symlink
1614 * is relative to the current directory (dscp).
1616 if (newRootScp == NULL) {
1618 cm_HoldSCache(dscp);
1621 code = cm_NameI(newRootScp, spacep->data,
1622 CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
1623 userp, NULL, reqp, outScpp);
1625 if (code == CM_ERROR_NOSUCHFILE)
1626 code = CM_ERROR_NOSUCHPATH;
1628 /* this stuff is allocated no matter what happened on the namei call,
1630 cm_FreeSpace(spacep);
1631 cm_ReleaseSCache(newRootScp);
1636 /* make this big enough so that one buffer of dir pages won't overflow. We'll
1637 * check anyway, but we want to minimize the chance that we have to leave stuff
1640 #define CM_BULKMAX 128
1642 /* rock for bulk stat calls */
1643 typedef struct cm_bulkStat {
1644 osi_hyper_t bufOffset; /* only do it for things in this buffer page */
1646 /* info for the actual call */
1647 int counter; /* next free slot */
1648 AFSFid fids[CM_BULKMAX];
1649 AFSFetchStatus stats[CM_BULKMAX];
1650 AFSCallBack callbacks[CM_BULKMAX];
1653 /* for a given entry, make sure that it isn't in the stat cache, and then
1654 * add it to the list of file IDs to be obtained.
1656 * Don't bother adding it if we already have a vnode. Note that the dir
1657 * is locked, so we have to be careful checking the vnode we're thinking of
1658 * processing, to avoid deadlocks.
1660 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
1671 /* Don't overflow bsp. */
1672 if (bsp->counter >= CM_BULKMAX)
1673 return CM_ERROR_STOPNOW;
1675 thyper.LowPart = cm_data.buf_blockSize;
1676 thyper.HighPart = 0;
1677 thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
1679 /* thyper is now the first byte past the end of the record we're
1680 * interested in, and bsp->bufOffset is the first byte of the record
1681 * we're interested in.
1682 * Skip data in the others.
1685 if (LargeIntegerLessThan(*offp, bsp->bufOffset))
1687 if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
1688 return CM_ERROR_STOPNOW;
1689 if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
1692 tfid.cell = scp->fid.cell;
1693 tfid.volume = scp->fid.volume;
1694 tfid.vnode = ntohl(dep->fid.vnode);
1695 tfid.unique = ntohl(dep->fid.unique);
1696 tscp = cm_FindSCache(&tfid);
1698 if (lock_TryMutex(&tscp->mx)) {
1699 /* we have an entry that we can look at */
1700 if (cm_HaveCallback(tscp)) {
1701 /* we have a callback on it. Don't bother
1702 * fetching this stat entry, since we're happy
1703 * with the info we have.
1705 lock_ReleaseMutex(&tscp->mx);
1706 cm_ReleaseSCache(tscp);
1709 lock_ReleaseMutex(&tscp->mx);
1711 cm_ReleaseSCache(tscp);
1714 #ifdef AFS_FREELANCE_CLIENT
1715 // yj: if this is a mountpoint under root.afs then we don't want it
1716 // to be bulkstat-ed, instead, we call getSCache directly and under
1717 // getSCache, it is handled specially.
1718 if ( cm_freelanceEnabled &&
1719 tfid.cell==AFS_FAKE_ROOT_CELL_ID &&
1720 tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
1721 !(tfid.vnode==0x1 && tfid.unique==0x1) )
1723 osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
1724 return cm_GetSCache(&tfid, &tscp, NULL, NULL);
1726 #endif /* AFS_FREELANCE_CLIENT */
1729 bsp->fids[i].Volume = scp->fid.volume;
1730 bsp->fids[i].Vnode = tfid.vnode;
1731 bsp->fids[i].Unique = tfid.unique;
1735 /* called with a locked scp and a pointer to a buffer. Make bulk stat
1736 * calls on all undeleted files in the page of the directory specified.
1738 void cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
1742 cm_bulkStat_t bb; /* this is *BIG*, probably 12K or so;
1743 * watch for stack problems */
1744 AFSCBFids fidStruct;
1745 AFSBulkStats statStruct;
1747 AFSCBs callbackStruct;
1750 cm_callbackRequest_t cbReq;
1756 struct rx_connection * callp;
1758 osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%x", (long) dscp);
1760 /* should be on a buffer boundary */
1761 osi_assert((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0);
1764 bb.bufOffset = *offsetp;
1766 lock_ReleaseMutex(&dscp->mx);
1767 /* first, assemble the file IDs we need to stat */
1768 code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) &bb, offsetp, userp, reqp, NULL);
1770 /* if we failed, bail out early */
1771 if (code && code != CM_ERROR_STOPNOW) {
1772 lock_ObtainMutex(&dscp->mx);
1776 /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
1777 * make the calls to create the entries. Handle AFSCBMAX files at a
1781 while (filex < bb.counter) {
1782 filesThisCall = bb.counter - filex;
1783 if (filesThisCall > AFSCBMAX)
1784 filesThisCall = AFSCBMAX;
1786 fidStruct.AFSCBFids_len = filesThisCall;
1787 fidStruct.AFSCBFids_val = &bb.fids[filex];
1788 statStruct.AFSBulkStats_len = filesThisCall;
1789 statStruct.AFSBulkStats_val = &bb.stats[filex];
1790 callbackStruct.AFSCBs_len = filesThisCall;
1791 callbackStruct.AFSCBs_val = &bb.callbacks[filex];
1792 cm_StartCallbackGrantingCall(NULL, &cbReq);
1793 osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
1795 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1799 callp = cm_GetRxConn(connp);
1800 code = RXAFS_BulkStatus(callp, &fidStruct,
1801 &statStruct, &callbackStruct, &volSync);
1802 rx_PutConnection(callp);
1804 } while (cm_Analyze(connp, userp, reqp, &dscp->fid,
1805 &volSync, NULL, &cbReq, code));
1806 code = cm_MapRPCError(code, reqp);
1809 osi_Log1(afsd_logp, "CALL BulkStatus FAILURE code 0x%x", code);
1811 osi_Log0(afsd_logp, "CALL BulkStatus SUCCESS");
1813 /* may as well quit on an error, since we're not going to do
1814 * much better on the next immediate call, either.
1817 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
1821 /* otherwise, we should do the merges */
1822 for (i = 0; i<filesThisCall; i++) {
1824 tfid.cell = dscp->fid.cell;
1825 tfid.volume = bb.fids[j].Volume;
1826 tfid.vnode = bb.fids[j].Vnode;
1827 tfid.unique = bb.fids[j].Unique;
1828 code = cm_GetSCache(&tfid, &scp, userp, reqp);
1832 /* otherwise, if this entry has no callback info,
1835 lock_ObtainMutex(&scp->mx);
1836 /* now, we have to be extra paranoid on merging in this
1837 * information, since we didn't use cm_SyncOp before
1838 * starting the fetch to make sure that no bad races
1839 * were occurring. Specifically, we need to make sure
1840 * we don't obliterate any newer information in the
1841 * vnode than have here.
1843 * Right now, be pretty conservative: if there's a
1844 * callback or a pending call, skip it.
1846 if (scp->cbServerp == NULL
1848 (CM_SCACHEFLAG_FETCHING
1849 | CM_SCACHEFLAG_STORING
1850 | CM_SCACHEFLAG_SIZESTORING))) {
1851 cm_EndCallbackGrantingCall(scp, &cbReq,
1853 CM_CALLBACK_MAINTAINCOUNT);
1854 cm_MergeStatus(scp, &bb.stats[j], &volSync,
1857 lock_ReleaseMutex(&scp->mx);
1858 cm_ReleaseSCache(scp);
1859 } /* all files in the response */
1860 /* now tell it to drop the count,
1861 * after doing the vnode processing above */
1862 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
1864 filex += filesThisCall;
1865 } /* while there are still more files to process */
1866 lock_ObtainMutex(&dscp->mx);
1867 osi_Log0(afsd_logp, "END cm_TryBulkStat");
1870 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
1874 /* initialize store back mask as inexpensive local variable */
1876 memset(statusp, 0, sizeof(AFSStoreStatus));
1878 /* copy out queued info from scache first, if scp passed in */
1880 if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
1881 statusp->ClientModTime = scp->clientModTime;
1882 mask |= AFS_SETMODTIME;
1883 scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
1888 /* now add in our locally generated request */
1889 if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
1890 statusp->ClientModTime = attrp->clientModTime;
1891 mask |= AFS_SETMODTIME;
1893 if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
1894 statusp->UnixModeBits = attrp->unixModeBits;
1895 mask |= AFS_SETMODE;
1897 if (attrp->mask & CM_ATTRMASK_OWNER) {
1898 statusp->Owner = attrp->owner;
1899 mask |= AFS_SETOWNER;
1901 if (attrp->mask & CM_ATTRMASK_GROUP) {
1902 statusp->Group = attrp->group;
1903 mask |= AFS_SETGROUP;
1906 statusp->Mask = mask;
1909 /* set the file size, and make sure that all relevant buffers have been
1910 * truncated. Ensure that any partially truncated buffers have been zeroed
1911 * to the end of the buffer.
1913 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
1919 /* start by locking out buffer creation */
1920 lock_ObtainWrite(&scp->bufCreateLock);
1922 /* verify that this is a file, not a dir or a symlink */
1923 lock_ObtainMutex(&scp->mx);
1924 code = cm_SyncOp(scp, NULL, userp, reqp, 0,
1925 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1929 if (scp->fileType != CM_SCACHETYPE_FILE) {
1930 code = CM_ERROR_ISDIR;
1935 if (LargeIntegerLessThan(*sizep, scp->length))
1940 lock_ReleaseMutex(&scp->mx);
1942 /* can't hold scp->mx lock here, since we may wait for a storeback to
1943 * finish if the buffer package is cleaning a buffer by storing it to
1947 buf_Truncate(scp, userp, reqp, sizep);
1949 /* now ensure that file length is short enough, and update truncPos */
1950 lock_ObtainMutex(&scp->mx);
1952 /* make sure we have a callback (so we have the right value for the
1953 * length), and wait for it to be safe to do a truncate.
1955 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
1956 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
1957 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
1961 if (LargeIntegerLessThan(*sizep, scp->length)) {
1962 /* a real truncation. If truncPos is not set yet, or is bigger
1963 * than where we're truncating the file, set truncPos to this
1968 if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
1969 || LargeIntegerLessThan(*sizep, scp->length)) {
1971 scp->truncPos = *sizep;
1972 scp->mask |= CM_SCACHEMASK_TRUNCPOS;
1974 /* in either case, the new file size has been changed */
1975 scp->length = *sizep;
1976 scp->mask |= CM_SCACHEMASK_LENGTH;
1978 else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
1979 /* really extending the file */
1980 scp->length = *sizep;
1981 scp->mask |= CM_SCACHEMASK_LENGTH;
1984 /* done successfully */
1988 lock_ReleaseMutex(&scp->mx);
1989 lock_ReleaseWrite(&scp->bufCreateLock);
1994 /* set the file size or other attributes (but not both at once) */
1995 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
2000 AFSFetchStatus afsOutStatus;
2004 AFSStoreStatus afsInStatus;
2005 struct rx_connection * callp;
2007 /* handle file length setting */
2008 if (attrp->mask & CM_ATTRMASK_LENGTH)
2009 return cm_SetLength(scp, &attrp->length, userp, reqp);
2011 flags = CM_SCACHESYNC_STORESTATUS;
2013 lock_ObtainMutex(&scp->mx);
2014 /* otherwise, we have to make an RPC to get the status */
2015 code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
2017 /* make the attr structure */
2018 cm_StatusFromAttr(&afsInStatus, scp, attrp);
2020 tfid.Volume = scp->fid.volume;
2021 tfid.Vnode = scp->fid.vnode;
2022 tfid.Unique = scp->fid.unique;
2024 lock_ReleaseMutex(&scp->mx);
2028 /* now make the RPC */
2029 osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%x", (long) scp);
2031 code = cm_Conn(&scp->fid, userp, reqp, &connp);
2035 callp = cm_GetRxConn(connp);
2036 code = RXAFS_StoreStatus(callp, &tfid,
2037 &afsInStatus, &afsOutStatus, &volSync);
2038 rx_PutConnection(callp);
2040 } while (cm_Analyze(connp, userp, reqp,
2041 &scp->fid, &volSync, NULL, NULL, code));
2042 code = cm_MapRPCError(code, reqp);
2045 osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
2047 osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
2049 lock_ObtainMutex(&scp->mx);
2050 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
2052 cm_MergeStatus(scp, &afsOutStatus, &volSync, userp,
2053 CM_MERGEFLAG_FORCE);
2055 /* if we're changing the mode bits, discard the ACL cache,
2056 * since we changed the mode bits.
2058 if (afsInStatus.Mask & AFS_SETMODE) cm_FreeAllACLEnts(scp);
2059 lock_ReleaseMutex(&scp->mx);
2063 long cm_Create(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2064 cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
2069 cm_callbackRequest_t cbReq;
2074 AFSStoreStatus inStatus;
2075 AFSFetchStatus updatedDirStatus;
2076 AFSFetchStatus newFileStatus;
2077 AFSCallBack newFileCallback;
2079 struct rx_connection * callp;
2081 /* can't create names with @sys in them; must expand it manually first.
2082 * return "invalid request" if they try.
2084 if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2085 return CM_ERROR_ATSYS;
2088 /* before starting the RPC, mark that we're changing the file data, so
2089 * that someone who does a chmod will know to wait until our call
2092 lock_ObtainMutex(&dscp->mx);
2093 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2095 cm_StartCallbackGrantingCall(NULL, &cbReq);
2097 lock_ReleaseMutex(&dscp->mx);
2103 cm_StatusFromAttr(&inStatus, NULL, attrp);
2105 /* try the RPC now */
2106 osi_Log1(afsd_logp, "CALL CreateFile scp 0x%x", (long) dscp);
2108 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2112 dirAFSFid.Volume = dscp->fid.volume;
2113 dirAFSFid.Vnode = dscp->fid.vnode;
2114 dirAFSFid.Unique = dscp->fid.unique;
2116 callp = cm_GetRxConn(connp);
2117 code = RXAFS_CreateFile(connp->callp, &dirAFSFid, namep,
2118 &inStatus, &newAFSFid, &newFileStatus,
2119 &updatedDirStatus, &newFileCallback,
2121 rx_PutConnection(callp);
2123 } while (cm_Analyze(connp, userp, reqp,
2124 &dscp->fid, &volSync, NULL, &cbReq, code));
2125 code = cm_MapRPCError(code, reqp);
2128 osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
2130 osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
2132 lock_ObtainMutex(&dscp->mx);
2133 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2135 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2137 lock_ReleaseMutex(&dscp->mx);
2139 /* now try to create the file's entry, too, but be careful to
2140 * make sure that we don't merge in old info. Since we weren't locking
2141 * out any requests during the file's creation, we may have pretty old
2145 newFid.cell = dscp->fid.cell;
2146 newFid.volume = dscp->fid.volume;
2147 newFid.vnode = newAFSFid.Vnode;
2148 newFid.unique = newAFSFid.Unique;
2149 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2151 lock_ObtainMutex(&scp->mx);
2152 if (!cm_HaveCallback(scp)) {
2153 cm_MergeStatus(scp, &newFileStatus, &volSync,
2155 cm_EndCallbackGrantingCall(scp, &cbReq,
2156 &newFileCallback, 0);
2159 lock_ReleaseMutex(&scp->mx);
2164 /* make sure we end things properly */
2166 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2171 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
2175 lock_ObtainWrite(&scp->bufCreateLock);
2176 code = buf_CleanVnode(scp, userp, reqp);
2177 lock_ReleaseWrite(&scp->bufCreateLock);
2179 lock_ObtainMutex(&scp->mx);
2180 scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA
2181 | CM_SCACHEFLAG_OUTOFSPACE);
2182 if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
2183 | CM_SCACHEMASK_CLIENTMODTIME
2184 | CM_SCACHEMASK_LENGTH))
2185 code = cm_StoreMini(scp, userp, reqp);
2186 lock_ReleaseMutex(&scp->mx);
2191 long cm_MakeDir(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2192 cm_user_t *userp, cm_req_t *reqp)
2197 cm_callbackRequest_t cbReq;
2202 AFSStoreStatus inStatus;
2203 AFSFetchStatus updatedDirStatus;
2204 AFSFetchStatus newDirStatus;
2205 AFSCallBack newDirCallback;
2207 struct rx_connection * callp;
2209 /* can't create names with @sys in them; must expand it manually first.
2210 * return "invalid request" if they try.
2212 if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2213 return CM_ERROR_ATSYS;
2216 /* before starting the RPC, mark that we're changing the directory
2217 * data, so that someone who does a chmod on the dir will wait until
2218 * our call completes.
2220 lock_ObtainMutex(&dscp->mx);
2221 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2223 cm_StartCallbackGrantingCall(NULL, &cbReq);
2225 lock_ReleaseMutex(&dscp->mx);
2231 cm_StatusFromAttr(&inStatus, NULL, attrp);
2233 /* try the RPC now */
2234 osi_Log1(afsd_logp, "CALL MakeDir scp 0x%x", (long) dscp);
2236 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2240 dirAFSFid.Volume = dscp->fid.volume;
2241 dirAFSFid.Vnode = dscp->fid.vnode;
2242 dirAFSFid.Unique = dscp->fid.unique;
2244 callp = cm_GetRxConn(connp);
2245 code = RXAFS_MakeDir(connp->callp, &dirAFSFid, namep,
2246 &inStatus, &newAFSFid, &newDirStatus,
2247 &updatedDirStatus, &newDirCallback,
2249 rx_PutConnection(callp);
2251 } while (cm_Analyze(connp, userp, reqp,
2252 &dscp->fid, &volSync, NULL, &cbReq, code));
2253 code = cm_MapRPCError(code, reqp);
2256 osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
2258 osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
2260 lock_ObtainMutex(&dscp->mx);
2261 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2263 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2265 lock_ReleaseMutex(&dscp->mx);
2267 /* now try to create the new dir's entry, too, but be careful to
2268 * make sure that we don't merge in old info. Since we weren't locking
2269 * out any requests during the file's creation, we may have pretty old
2273 newFid.cell = dscp->fid.cell;
2274 newFid.volume = dscp->fid.volume;
2275 newFid.vnode = newAFSFid.Vnode;
2276 newFid.unique = newAFSFid.Unique;
2277 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2279 lock_ObtainMutex(&scp->mx);
2280 if (!cm_HaveCallback(scp)) {
2281 cm_MergeStatus(scp, &newDirStatus, &volSync,
2283 cm_EndCallbackGrantingCall(scp, &cbReq,
2284 &newDirCallback, 0);
2287 lock_ReleaseMutex(&scp->mx);
2288 cm_ReleaseSCache(scp);
2292 /* make sure we end things properly */
2294 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2296 /* and return error code */
2300 long cm_Link(cm_scache_t *dscp, char *namep, cm_scache_t *sscp, long flags,
2301 cm_user_t *userp, cm_req_t *reqp)
2306 AFSFid existingAFSFid;
2307 AFSFetchStatus updatedDirStatus;
2308 AFSFetchStatus newLinkStatus;
2310 struct rx_connection * callp;
2312 if (dscp->fid.cell != sscp->fid.cell ||
2313 dscp->fid.volume != sscp->fid.volume) {
2314 return CM_ERROR_CROSSDEVLINK;
2317 lock_ObtainMutex(&dscp->mx);
2318 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2319 lock_ReleaseMutex(&dscp->mx);
2324 /* try the RPC now */
2325 osi_Log1(afsd_logp, "CALL Link scp 0x%x", (long) dscp);
2327 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2330 dirAFSFid.Volume = dscp->fid.volume;
2331 dirAFSFid.Vnode = dscp->fid.vnode;
2332 dirAFSFid.Unique = dscp->fid.unique;
2334 existingAFSFid.Volume = sscp->fid.volume;
2335 existingAFSFid.Vnode = sscp->fid.vnode;
2336 existingAFSFid.Unique = sscp->fid.unique;
2338 callp = cm_GetRxConn(connp);
2339 code = RXAFS_Link(callp, &dirAFSFid, namep, &existingAFSFid,
2340 &newLinkStatus, &updatedDirStatus, &volSync);
2341 rx_PutConnection(callp);
2342 osi_Log1(smb_logp," RXAFS_Link returns 0x%x", code);
2344 } while (cm_Analyze(connp, userp, reqp,
2345 &dscp->fid, &volSync, NULL, NULL, code));
2347 code = cm_MapRPCError(code, reqp);
2350 osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
2352 osi_Log0(afsd_logp, "CALL Link SUCCESS");
2354 lock_ObtainMutex(&dscp->mx);
2355 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2357 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2359 lock_ReleaseMutex(&dscp->mx);
2364 long cm_SymLink(cm_scache_t *dscp, char *namep, char *contentsp, long flags,
2365 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
2373 AFSStoreStatus inStatus;
2374 AFSFetchStatus updatedDirStatus;
2375 AFSFetchStatus newLinkStatus;
2377 struct rx_connection * callp;
2379 /* before starting the RPC, mark that we're changing the directory data,
2380 * so that someone who does a chmod on the dir will wait until our
2383 lock_ObtainMutex(&dscp->mx);
2384 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2385 lock_ReleaseMutex(&dscp->mx);
2390 cm_StatusFromAttr(&inStatus, NULL, attrp);
2392 /* try the RPC now */
2393 osi_Log1(afsd_logp, "CALL Symlink scp 0x%x", (long) dscp);
2395 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2399 dirAFSFid.Volume = dscp->fid.volume;
2400 dirAFSFid.Vnode = dscp->fid.vnode;
2401 dirAFSFid.Unique = dscp->fid.unique;
2403 callp = cm_GetRxConn(connp);
2404 code = RXAFS_Symlink(callp, &dirAFSFid, namep, contentsp,
2405 &inStatus, &newAFSFid, &newLinkStatus,
2406 &updatedDirStatus, &volSync);
2407 rx_PutConnection(callp);
2409 } while (cm_Analyze(connp, userp, reqp,
2410 &dscp->fid, &volSync, NULL, NULL, code));
2411 code = cm_MapRPCError(code, reqp);
2414 osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
2416 osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
2418 lock_ObtainMutex(&dscp->mx);
2419 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2421 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2423 lock_ReleaseMutex(&dscp->mx);
2425 /* now try to create the new dir's entry, too, but be careful to
2426 * make sure that we don't merge in old info. Since we weren't locking
2427 * out any requests during the file's creation, we may have pretty old
2431 newFid.cell = dscp->fid.cell;
2432 newFid.volume = dscp->fid.volume;
2433 newFid.vnode = newAFSFid.Vnode;
2434 newFid.unique = newAFSFid.Unique;
2435 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2437 lock_ObtainMutex(&scp->mx);
2438 if (!cm_HaveCallback(scp)) {
2439 cm_MergeStatus(scp, &newLinkStatus, &volSync,
2442 lock_ReleaseMutex(&scp->mx);
2443 cm_ReleaseSCache(scp);
2447 /* and return error code */
2451 long cm_RemoveDir(cm_scache_t *dscp, char *namep, cm_user_t *userp,
2458 AFSFetchStatus updatedDirStatus;
2460 struct rx_connection * callp;
2462 /* before starting the RPC, mark that we're changing the directory data,
2463 * so that someone who does a chmod on the dir will wait until our
2466 lock_ObtainMutex(&dscp->mx);
2467 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2468 lock_ReleaseMutex(&dscp->mx);
2474 /* try the RPC now */
2475 osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%x", (long) dscp);
2477 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2481 dirAFSFid.Volume = dscp->fid.volume;
2482 dirAFSFid.Vnode = dscp->fid.vnode;
2483 dirAFSFid.Unique = dscp->fid.unique;
2485 callp = cm_GetRxConn(connp);
2486 code = RXAFS_RemoveDir(callp, &dirAFSFid, namep,
2487 &updatedDirStatus, &volSync);
2488 rx_PutConnection(callp);
2490 } while (cm_Analyze(connp, userp, reqp,
2491 &dscp->fid, &volSync, NULL, NULL, code));
2492 code = cm_MapRPCErrorRmdir(code, reqp);
2495 osi_Log1(afsd_logp, "CALL RemoveDir FAILURE, code 0x%x", code);
2497 osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
2499 lock_ObtainMutex(&dscp->mx);
2500 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2502 cm_dnlcRemove(dscp, namep);
2503 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2505 lock_ReleaseMutex(&dscp->mx);
2507 /* and return error code */
2511 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
2513 /* grab mutex on contents */
2514 lock_ObtainMutex(&scp->mx);
2516 /* reset the prefetch info */
2517 scp->prefetch.base.LowPart = 0; /* base */
2518 scp->prefetch.base.HighPart = 0;
2519 scp->prefetch.end.LowPart = 0; /* and end */
2520 scp->prefetch.end.HighPart = 0;
2522 /* release mutex on contents */
2523 lock_ReleaseMutex(&scp->mx);
2529 long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
2530 char *newNamep, cm_user_t *userp, cm_req_t *reqp)
2534 AFSFid oldDirAFSFid;
2535 AFSFid newDirAFSFid;
2537 AFSFetchStatus updatedOldDirStatus;
2538 AFSFetchStatus updatedNewDirStatus;
2541 struct rx_connection * callp;
2543 /* before starting the RPC, mark that we're changing the directory data,
2544 * so that someone who does a chmod on the dir will wait until our call
2545 * completes. We do this in vnode order so that we don't deadlock,
2546 * which makes the code a little verbose.
2548 if (oldDscp == newDscp) {
2549 /* check for identical names */
2550 if (strcmp(oldNamep, newNamep) == 0)
2551 return CM_ERROR_RENAME_IDENTICAL;
2554 lock_ObtainMutex(&oldDscp->mx);
2555 cm_dnlcRemove(oldDscp, oldNamep);
2556 cm_dnlcRemove(oldDscp, newNamep);
2557 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2558 CM_SCACHESYNC_STOREDATA);
2559 lock_ReleaseMutex(&oldDscp->mx);
2562 /* two distinct dir vnodes */
2564 if (oldDscp->fid.cell != newDscp->fid.cell ||
2565 oldDscp->fid.volume != newDscp->fid.volume)
2566 return CM_ERROR_CROSSDEVLINK;
2568 /* shouldn't happen that we have distinct vnodes for two
2569 * different files, but could due to deliberate attack, or
2570 * stale info. Avoid deadlocks and quit now.
2572 if (oldDscp->fid.vnode == newDscp->fid.vnode)
2573 return CM_ERROR_CROSSDEVLINK;
2575 if (oldDscp->fid.vnode < newDscp->fid.vnode) {
2576 lock_ObtainMutex(&oldDscp->mx);
2577 cm_dnlcRemove(oldDscp, oldNamep);
2578 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2579 CM_SCACHESYNC_STOREDATA);
2580 lock_ReleaseMutex(&oldDscp->mx);
2582 lock_ObtainMutex(&newDscp->mx);
2583 cm_dnlcRemove(newDscp, newNamep);
2584 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2585 CM_SCACHESYNC_STOREDATA);
2586 lock_ReleaseMutex(&newDscp->mx);
2588 /* cleanup first one */
2589 lock_ObtainMutex(&newDscp->mx);
2590 cm_SyncOpDone(oldDscp, NULL,
2591 CM_SCACHESYNC_STOREDATA);
2592 lock_ReleaseMutex(&oldDscp->mx);
2597 /* lock the new vnode entry first */
2598 lock_ObtainMutex(&newDscp->mx);
2599 cm_dnlcRemove(newDscp, newNamep);
2600 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2601 CM_SCACHESYNC_STOREDATA);
2602 lock_ReleaseMutex(&newDscp->mx);
2604 lock_ObtainMutex(&oldDscp->mx);
2605 cm_dnlcRemove(oldDscp, oldNamep);
2606 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2607 CM_SCACHESYNC_STOREDATA);
2608 lock_ReleaseMutex(&oldDscp->mx);
2610 /* cleanup first one */
2611 lock_ObtainMutex(&newDscp->mx);
2612 cm_SyncOpDone(newDscp, NULL,
2613 CM_SCACHESYNC_STOREDATA);
2614 lock_ReleaseMutex(&newDscp->mx);
2618 } /* two distinct vnodes */
2625 /* try the RPC now */
2626 osi_Log2(afsd_logp, "CALL Rename old scp 0x%x new scp 0x%x",
2627 (long) oldDscp, (long) newDscp);
2629 code = cm_Conn(&oldDscp->fid, userp, reqp, &connp);
2633 oldDirAFSFid.Volume = oldDscp->fid.volume;
2634 oldDirAFSFid.Vnode = oldDscp->fid.vnode;
2635 oldDirAFSFid.Unique = oldDscp->fid.unique;
2636 newDirAFSFid.Volume = newDscp->fid.volume;
2637 newDirAFSFid.Vnode = newDscp->fid.vnode;
2638 newDirAFSFid.Unique = newDscp->fid.unique;
2640 callp = cm_GetRxConn(connp);
2641 code = RXAFS_Rename(callp, &oldDirAFSFid, oldNamep,
2642 &newDirAFSFid, newNamep,
2643 &updatedOldDirStatus, &updatedNewDirStatus,
2645 rx_PutConnection(callp);
2647 } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
2648 &volSync, NULL, NULL, code));
2649 code = cm_MapRPCError(code, reqp);
2652 osi_Log1(afsd_logp, "CALL Rename FAILURE, code 0x%x", code);
2654 osi_Log0(afsd_logp, "CALL Rename SUCCESS");
2656 /* update the individual stat cache entries for the directories */
2657 lock_ObtainMutex(&oldDscp->mx);
2658 cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
2660 cm_MergeStatus(oldDscp, &updatedOldDirStatus, &volSync,
2663 lock_ReleaseMutex(&oldDscp->mx);
2665 /* and update it for the new one, too, if necessary */
2667 lock_ObtainMutex(&newDscp->mx);
2668 cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
2670 cm_MergeStatus(newDscp, &updatedNewDirStatus, &volSync,
2673 lock_ReleaseMutex(&newDscp->mx);
2676 /* and return error code */
2680 long cm_Lock(cm_scache_t *scp, unsigned char LockType,
2681 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
2682 u_long Timeout, cm_user_t *userp, cm_req_t *reqp,
2686 int Which = ((LockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
2690 cm_file_lock_t *fileLock;
2693 struct rx_connection * callp;
2695 /* Look for a conflict. Also, if we are asking for a shared lock,
2696 * look for another shared lock, so we don't have to do an RPC.
2700 fileLock = (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
2701 if ((fileLock->flags & (CM_FILELOCK_FLAG_INVALID | CM_FILELOCK_FLAG_WAITING)) == 0) {
2702 if ((LockType & LOCKING_ANDX_SHARED_LOCK) == 0 ||
2703 (fileLock->LockType & LOCKING_ANDX_SHARED_LOCK) == 0)
2704 return CM_ERROR_WOULDBLOCK;
2713 tfid.Volume = scp->fid.volume;
2714 tfid.Vnode = scp->fid.vnode;
2715 tfid.Unique = scp->fid.unique;
2716 lock_ReleaseMutex(&scp->mx);
2718 code = cm_Conn(&scp->fid, userp, reqp, &connp);
2722 callp = cm_GetRxConn(connp);
2723 code = RXAFS_SetLock(callp, &tfid, Which,
2725 rx_PutConnection(callp);
2727 } while (cm_Analyze(connp, userp, reqp, &scp->fid, &volSync,
2729 lock_ObtainMutex(&scp->mx);
2730 code = cm_MapRPCError(code, reqp);
2733 if (code == 0 || Timeout != 0) {
2734 fileLock = malloc(sizeof(cm_file_lock_t));
2735 fileLock->LockType = LockType;
2737 fileLock->userp = userp;
2738 fileLock->fid = scp->fid;
2739 fileLock->LOffset = LOffset;
2740 fileLock->LLength = LLength;
2741 fileLock->flags = (code == 0 ? 0 : CM_FILELOCK_FLAG_WAITING);
2742 osi_QAdd(&scp->fileLocks, &fileLock->fileq);
2743 lock_ObtainWrite(&cm_scacheLock);
2744 osi_QAdd(&cm_allFileLocks, &fileLock->q);
2745 lock_ReleaseWrite(&cm_scacheLock);
2752 long cm_Unlock(cm_scache_t *scp, unsigned char LockType,
2753 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
2754 cm_user_t *userp, cm_req_t *reqp)
2757 int Which = ((LockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
2761 cm_file_lock_t *fileLock, *ourLock;
2762 osi_queue_t *q, *qq;
2763 int anotherReader = 0;
2766 struct rx_connection * callp;
2768 if (LargeIntegerLessThan(LLength, scp->length))
2771 /* Look for our own lock on the list, so as to remove it.
2772 * Also, determine if we're the last reader; if not, avoid an RPC.
2776 fileLock = (cm_file_lock_t *)
2777 ((char *) q - offsetof(cm_file_lock_t, fileq));
2779 && fileLock->userp == userp
2780 && LargeIntegerEqualTo(fileLock->LOffset, LOffset)
2781 && LargeIntegerEqualTo(fileLock->LLength, LLength)) {
2786 else if (fileLock->LockType & LOCKING_ANDX_SHARED_LOCK)
2791 /* ignore byte ranges */
2792 if (smallLock && !found)
2795 /* don't try to unlock other people's locks */
2797 return CM_ERROR_WOULDBLOCK;
2799 /* discard lock record */
2800 osi_QRemove(&scp->fileLocks, qq);
2802 * Don't delete it here; let the daemon delete it, to simplify
2803 * the daemon's traversal of the list.
2805 lock_ObtainWrite(&cm_scacheLock);
2806 ourLock->flags |= CM_FILELOCK_FLAG_INVALID;
2807 cm_ReleaseUser(ourLock->userp);
2808 lock_ReleaseWrite(&cm_scacheLock);
2810 if (!anotherReader) {
2811 tfid.Volume = scp->fid.volume;
2812 tfid.Vnode = scp->fid.vnode;
2813 tfid.Unique = scp->fid.unique;
2814 lock_ReleaseMutex(&scp->mx);
2815 osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%x", (long) scp);
2817 code = cm_Conn(&scp->fid, userp, reqp, &connp);
2821 callp = cm_GetRxConn(connp);
2822 code = RXAFS_ReleaseLock(callp, &tfid, &volSync);
2823 rx_PutConnection(callp);
2825 } while (cm_Analyze(connp, userp, reqp, &scp->fid, &volSync,
2827 code = cm_MapRPCError(code, reqp);
2830 osi_Log1(afsd_logp, "CALL ReleaseLock FAILURE, code 0x%x", code);
2832 osi_Log0(afsd_logp, "CALL ReleaseLock SUCCESS");
2834 lock_ObtainMutex(&scp->mx);
2840 void cm_CheckLocks()
2842 osi_queue_t *q, *nq;
2843 cm_file_lock_t *fileLock;
2849 struct rx_connection * callp;
2853 lock_ObtainWrite(&cm_scacheLock);
2854 q = cm_allFileLocks;
2856 fileLock = (cm_file_lock_t *) q;
2858 if (fileLock->flags & CM_FILELOCK_FLAG_INVALID) {
2859 osi_QRemove(&cm_allFileLocks, q);
2862 else if (!(fileLock->flags & CM_FILELOCK_FLAG_WAITING)) {
2863 tfid.Volume = fileLock->fid.volume;
2864 tfid.Vnode = fileLock->fid.vnode;
2865 tfid.Unique = fileLock->fid.unique;
2866 lock_ReleaseWrite(&cm_scacheLock);
2867 osi_Log1(afsd_logp, "CALL ExtendLock lock 0x%x", (long) fileLock);
2869 code = cm_Conn(&fileLock->fid, fileLock->userp,
2874 callp = cm_GetRxConn(connp);
2875 code = RXAFS_ExtendLock(callp, &tfid,
2877 rx_PutConnection(callp);
2879 } while (cm_Analyze(connp, fileLock->userp, &req,
2880 &fileLock->fid, &volSync, NULL, NULL,
2882 code = cm_MapRPCError(code, &req);
2884 osi_Log1(afsd_logp, "CALL ExtendLock FAILURE, code 0x%x", code);
2886 osi_Log0(afsd_logp, "CALL ExtendLock SUCCESS");
2888 lock_ObtainWrite(&cm_scacheLock);
2892 lock_ReleaseWrite(&cm_scacheLock);
2895 long cm_RetryLock(cm_file_lock_t *oldFileLock, int vcp_is_dead)
2898 int Which = ((oldFileLock->LockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
2903 cm_file_lock_t *fileLock;
2907 struct rx_connection * callp;
2910 code = CM_ERROR_TIMEDOUT;
2916 /* Look for a conflict. Also, if we are asking for a shared lock,
2917 * look for another shared lock, so we don't have to do an RPC.
2919 code = cm_GetSCache(&oldFileLock->fid, &scp, oldFileLock->userp, &req);
2925 fileLock = (cm_file_lock_t *)
2926 ((char *) q - offsetof(cm_file_lock_t, fileq));
2927 if ((fileLock->flags &
2928 (CM_FILELOCK_FLAG_INVALID | CM_FILELOCK_FLAG_WAITING))
2930 if ((oldFileLock->LockType & LOCKING_ANDX_SHARED_LOCK) == 0
2931 || (fileLock->LockType & LOCKING_ANDX_SHARED_LOCK) == 0) {
2932 cm_ReleaseSCache(scp);
2933 return CM_ERROR_WOULDBLOCK;
2943 tfid.Volume = oldFileLock->fid.volume;
2944 tfid.Vnode = oldFileLock->fid.vnode;
2945 tfid.Unique = oldFileLock->fid.unique;
2946 osi_Log1(afsd_logp, "CALL SetLock lock 0x%x", (long) oldFileLock);
2948 code = cm_Conn(&oldFileLock->fid, oldFileLock->userp,
2953 callp = cm_GetRxConn(connp);
2954 code = RXAFS_SetLock(callp, &tfid, Which,
2956 rx_PutConnection(callp);
2958 } while (cm_Analyze(connp, oldFileLock->userp, &req,
2959 &oldFileLock->fid, &volSync,
2961 code = cm_MapRPCError(code, &req);
2964 osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
2966 osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
2970 if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
2971 lock_ObtainMutex(&scp->mx);
2972 osi_QRemove(&scp->fileLocks, &oldFileLock->fileq);
2973 lock_ReleaseMutex(&scp->mx);
2975 lock_ObtainWrite(&cm_scacheLock);
2977 oldFileLock->flags = 0;
2978 else if (code != CM_ERROR_WOULDBLOCK) {
2979 oldFileLock->flags |= CM_FILELOCK_FLAG_INVALID;
2980 cm_ReleaseUser(oldFileLock->userp);
2981 oldFileLock->userp = NULL;
2983 lock_ReleaseWrite(&cm_scacheLock);