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 */
31 unsigned int cm_mountRootGen = 0;
36 * Case-folding array. This was constructed by inspecting of SMBtrace output.
37 * I do not know anything more about it.
39 unsigned char cm_foldUpper[256] = {
40 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
41 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
42 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
43 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
44 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
45 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
46 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
47 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
48 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
49 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
50 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
51 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
52 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
53 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
54 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
55 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
56 0x80, 0x9a, 0x90, 0x41, 0x8e, 0x41, 0x8f, 0x80,
57 0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x8e, 0x8f,
58 0x90, 0x92, 0x92, 0x4f, 0x99, 0x4f, 0x55, 0x55,
59 0x59, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
60 0x41, 0x49, 0x4f, 0x55, 0xa5, 0xa5, 0x56, 0xa7,
61 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
62 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
63 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
64 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
65 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
66 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
67 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
68 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
69 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
70 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
71 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
75 * Case-insensitive string comparison. We used to use stricmp, but it doesn't
76 * know about 8-bit characters (e.g. 129 is lowercase u-umlaut, 154 is
77 * upper-case u-umlaut).
79 int cm_stricmp(const char *str1, const char *str2)
91 c1 = (char) cm_foldUpper[(unsigned char)(*str1++)];
92 c2 = (char) cm_foldUpper[(unsigned char)(*str2++)];
100 /* characters that are legal in an 8.3 name */
102 * We used to have 1's for all characters from 128 to 254. But
103 * the NT client behaves better if we create an 8.3 name for any
104 * name that has a character with the high bit on, and if we
105 * delete those characters from 8.3 names. In particular, see
106 * Sybase defect 10859.
108 char cm_LegalChars[256] = {
109 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
110 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
111 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0,
112 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
113 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
114 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1,
115 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
116 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1,
117 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
118 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
119 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
120 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
121 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
122 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
123 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
124 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
127 /* return true iff component is a valid 8.3 name */
128 int cm_Is8Dot3(char *namep)
131 int sawUpper = 0, sawLower = 0;
136 * can't have a leading dot;
137 * special case for . and ..
139 if (namep[0] == '.') {
142 if (namep[1] == '.' && namep[2] == 0)
146 while (tc = *namep++) {
148 /* saw another dot */
149 if (sawDot) return 0; /* second dot */
154 if (cm_LegalChars[tc] == 0)
156 if (tc >= 'A' && tc <= 'Z')
158 else if (tc >= 'a' && tc <= 'z')
161 if (!sawDot && charCount > 8)
162 /* more than 8 chars in name */
164 if (sawDot && charCount > 3)
165 /* more than 3 chars in extension */
169 * Used to check that all characters were the same case.
170 * This doesn't help 16-bit apps, and meanwhile it causes the
171 * MS-DOS Command Prompt to misbehave; see Sybase defect 10709.
173 if (sawUpper && sawLower)
180 * Number unparsing map for generating 8.3 names;
183 char cm_8Dot3Mapping[41] =
184 {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
185 'B', 'C', 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S',
186 'T', 'V', 'W', 'X', 'Y', 'Z', '_', '-', '$', '#', '@', '%', '!', '&', 'E', 'O'
188 int cm_8Dot3MapSize = sizeof(cm_8Dot3Mapping);
190 void cm_Gen8Dot3Name(cm_dirEntry_t *dep, char *shortName, char **shortNameEndp)
194 int vnode = ntohl(dep->fid.vnode);
196 int validExtension = 0;
197 char tc, *temp, *name;
199 /* Unparse the file's vnode number to get a "uniquifier" */
201 number[nsize] = cm_8Dot3Mapping[vnode % cm_8Dot3MapSize];
203 vnode /= cm_8Dot3MapSize;
207 * Look for valid extension. There has to be a dot, and
208 * at least one of the characters following has to be legal.
210 lastDot = strrchr(dep->name, '.');
212 temp = lastDot; temp++;
214 if (cm_LegalChars[tc])
220 /* Copy name characters */
222 for (i = 0, name = dep->name;
223 i < (7 - nsize) && name != lastDot; ) {
228 if (!cm_LegalChars[tc])
231 *shortName++ = toupper(tc);
237 /* Copy uniquifier characters */
238 memcpy(shortName, number, nsize);
241 if (validExtension) {
242 /* Copy extension characters */
243 *shortName++ = *lastDot++; /* copy dot */
244 for (i = 0, tc = *lastDot++;
247 if (cm_LegalChars[tc]) {
249 *shortName++ = toupper(tc);
258 *shortNameEndp = shortName;
261 /* return success if we can open this file in this mode */
262 long cm_CheckOpen(cm_scache_t *scp, int openMode, int trunc, cm_user_t *userp,
269 if (openMode != 1) rights |= PRSFS_READ;
270 if (openMode == 1 || openMode == 2 || trunc) rights |= PRSFS_WRITE;
272 lock_ObtainMutex(&scp->mx);
274 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
275 CM_SCACHESYNC_GETSTATUS
276 | CM_SCACHESYNC_NEEDCALLBACK);
277 lock_ReleaseMutex(&scp->mx);
282 /* return success if we can open this file in this mode */
283 long cm_CheckNTOpen(cm_scache_t *scp, unsigned int desiredAccess,
284 unsigned int createDisp, cm_user_t *userp, cm_req_t *reqp)
289 /* Always allow delete; the RPC will tell us if it's OK */
290 if (desiredAccess == DELETE)
295 if (desiredAccess & AFS_ACCESS_READ)
296 rights |= PRSFS_READ;
298 if ((desiredAccess & AFS_ACCESS_WRITE)
300 rights |= PRSFS_WRITE;
302 lock_ObtainMutex(&scp->mx);
304 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
305 CM_SCACHESYNC_GETSTATUS
306 | CM_SCACHESYNC_NEEDCALLBACK);
307 lock_ReleaseMutex(&scp->mx);
310 * If the open will fail because the volume is readonly, then we will
311 * return an access denied error instead. This is to help brain-dead
312 * apps run correctly on replicated volumes.
313 * See defect 10007 for more information.
315 if (code == CM_ERROR_READONLY)
316 code = CM_ERROR_NOACCESS;
322 * When CAP_NT_SMBS has been negotiated, deletion (of files or directories) is
323 * done in three steps:
324 * (1) open for deletion (NT_CREATE_AND_X)
325 * (2) set for deletion on close (NTWTRANSACTION2, SET_FILE_INFO)
327 * We must not do the RPC until step 3. But if we are going to return an error
328 * code (e.g. directory not empty), we must return it by step 2, otherwise most
329 * clients will not notice it. So we do a preliminary check. For deleting
330 * files, this is almost free, since we have already done the RPC to get the
331 * parent directory's status bits. But for deleting directories, we must do an
332 * additional RPC to get the directory's data to check if it is empty. Sigh.
334 long cm_CheckNTDelete(cm_scache_t *dscp, cm_scache_t *scp, cm_user_t *userp,
341 unsigned short *hashTable;
343 int BeyondPage = 0, HaveDot = 0, HaveDotDot = 0;
345 /* First check permissions */
346 lock_ObtainMutex(&dscp->mx);
347 code = cm_SyncOp(dscp, NULL, userp, reqp, PRSFS_DELETE,
348 CM_SCACHESYNC_GETSTATUS
349 | CM_SCACHESYNC_NEEDCALLBACK);
350 lock_ReleaseMutex(&dscp->mx);
354 /* If deleting directory, must be empty */
356 if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
359 thyper.HighPart = 0; thyper.LowPart = 0;
360 lock_ObtainRead(&scp->bufCreateLock);
361 code = buf_Get(scp, &thyper, &bufferp);
362 lock_ReleaseRead(&scp->bufCreateLock);
366 lock_ObtainMutex(&bufferp->mx);
367 lock_ObtainMutex(&scp->mx);
369 code = cm_SyncOp(scp, bufferp, userp, reqp, 0,
370 CM_SCACHESYNC_NEEDCALLBACK
372 | CM_SCACHESYNC_BUFLOCKED);
376 if (cm_HaveBuffer(scp, bufferp, 1))
379 /* otherwise, load the buffer and try again */
380 lock_ReleaseMutex(&bufferp->mx);
381 code = cm_GetBuffer(scp, bufferp, NULL, userp, reqp);
382 lock_ReleaseMutex(&scp->mx);
383 lock_ObtainMutex(&bufferp->mx);
384 lock_ObtainMutex(&scp->mx);
389 /* We try to determine emptiness without looking beyond the first page,
390 * and without assuming "." and ".." are present and are on the first
391 * page (though these assumptions might, after all, be reasonable).
393 hashTable = (unsigned short *)(bufferp->datap + (32 * 5));
394 for (i=0; i<128; i++) {
395 idx = ntohs(hashTable[i]);
401 dep = (cm_dirEntry_t *)(bufferp->datap + (32 * idx));
402 if (strcmp(dep->name, ".") == 0)
404 else if (strcmp(dep->name, "..") == 0)
407 code = CM_ERROR_NOTEMPTY;
410 idx = ntohs(dep->next);
413 if (BeyondPage && HaveDot && HaveDotDot)
414 code = CM_ERROR_NOTEMPTY;
418 lock_ReleaseMutex(&bufferp->mx);
419 buf_Release(bufferp);
420 lock_ReleaseMutex(&scp->mx);
425 * Iterate through all entries in a directory.
426 * When the function funcp is called, the buffer is locked but the
427 * directory vnode is not.
429 long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
430 osi_hyper_t *startOffsetp, cm_user_t *userp, cm_req_t *reqp,
431 cm_scache_t **retscp)
438 osi_hyper_t dirLength;
439 osi_hyper_t bufferOffset;
440 osi_hyper_t curOffset;
444 cm_pageHeader_t *pageHeaderp;
446 long nextEntryCookie;
447 int numDirChunks; /* # of 32 byte dir chunks in this entry */
449 /* get the directory size */
450 lock_ObtainMutex(&scp->mx);
451 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_LOOKUP,
452 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
454 lock_ReleaseMutex(&scp->mx);
458 if (scp->fileType != CM_SCACHETYPE_DIRECTORY) {
459 lock_ReleaseMutex(&scp->mx);
460 return CM_ERROR_NOTDIR;
463 if ( retscp ) /* if this is a lookup call */
465 cm_lookupSearch_t* sp = parmp;
466 if ( *retscp = cm_dnlcLookup(scp, sp)) /* dnlc hit */
468 lock_ReleaseMutex(&scp->mx);
474 * XXX We only get the length once. It might change when we drop the
477 dirLength = scp->length;
479 lock_ReleaseMutex(&scp->mx);
482 bufferOffset.LowPart = bufferOffset.HighPart = 0;
484 curOffset = *startOffsetp;
486 curOffset.HighPart = 0;
487 curOffset.LowPart = 0;
491 /* make sure that curOffset.LowPart doesn't point to the first
492 * 32 bytes in the 2nd through last dir page, and that it
493 * doesn't point at the first 13 32-byte chunks in the first
494 * dir page, since those are dir and page headers, and don't
495 * contain useful information.
497 temp = curOffset.LowPart & (2048-1);
498 if (curOffset.HighPart == 0 && curOffset.LowPart < 2048) {
499 /* we're in the first page */
500 if (temp < 13*32) temp = 13*32;
503 /* we're in a later dir page */
504 if (temp < 32) temp = 32;
507 /* make sure the low order 5 bits are zero */
510 /* now put temp bits back ito curOffset.LowPart */
511 curOffset.LowPart &= ~(2048-1);
512 curOffset.LowPart |= temp;
514 /* check if we've passed the dir's EOF */
515 if (LargeIntegerGreaterThanOrEqualTo(curOffset, dirLength))
518 /* see if we can use the bufferp we have now; compute in which
519 * page the current offset would be, and check whether that's
520 * the offset of the buffer we have. If not, get the buffer.
522 thyper.HighPart = curOffset.HighPart;
523 thyper.LowPart = curOffset.LowPart & ~(buf_bufferSize-1);
524 if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
527 lock_ReleaseMutex(&bufferp->mx);
528 buf_Release(bufferp);
532 lock_ObtainRead(&scp->bufCreateLock);
533 code = buf_Get(scp, &thyper, &bufferp);
534 lock_ReleaseRead(&scp->bufCreateLock);
536 lock_ObtainMutex(&bufferp->mx);
538 bufferOffset = thyper;
540 /* now get the data in the cache */
542 lock_ObtainMutex(&scp->mx);
543 code = cm_SyncOp(scp, bufferp, userp, reqp,
545 CM_SCACHESYNC_NEEDCALLBACK
547 | CM_SCACHESYNC_BUFLOCKED);
549 lock_ReleaseMutex(&scp->mx);
553 if (cm_HaveBuffer(scp, bufferp, 1)) {
554 lock_ReleaseMutex(&scp->mx);
558 /* otherwise, load the buffer and try again */
559 lock_ReleaseMutex(&bufferp->mx);
560 code = cm_GetBuffer(scp, bufferp, NULL, userp,
562 lock_ReleaseMutex(&scp->mx);
563 lock_ObtainMutex(&bufferp->mx);
567 lock_ReleaseMutex(&bufferp->mx);
568 buf_Release(bufferp);
572 } /* if (wrong buffer) ... */
574 /* now we have the buffer containing the entry we're interested
575 * in; copy it out if it represents a non-deleted entry.
577 entryInDir = curOffset.LowPart & (2048-1);
578 entryInBuffer = curOffset.LowPart & (buf_bufferSize - 1);
580 /* page header will help tell us which entries are free. Page
581 * header can change more often than once per buffer, since
582 * AFS 3 dir page size may be less than (but not more than) a
583 * buffer package buffer.
585 /* only look intra-buffer */
586 temp = curOffset.LowPart & (buf_bufferSize - 1);
587 temp &= ~(2048 - 1); /* turn off intra-page bits */
588 pageHeaderp = (cm_pageHeader_t *) (bufferp->datap + temp);
590 /* now determine which entry we're looking at in the page. If
591 * it is free (there's a free bitmap at the start of the dir),
592 * we should skip these 32 bytes.
594 slotInPage = (entryInDir & 0x7e0) >> 5;
595 if (!(pageHeaderp->freeBitmap[slotInPage>>3]
596 & (1 << (slotInPage & 0x7)))) {
597 /* this entry is free */
598 numDirChunks = 1; /* only skip this guy */
602 tp = bufferp->datap + entryInBuffer;
603 dep = (cm_dirEntry_t *) tp; /* now points to AFS3 dir entry */
605 /* while we're here, compute the next entry's location, too,
606 * since we'll need it when writing out the cookie into the
607 * dir listing stream.
609 numDirChunks = cm_NameEntries(dep->name, NULL);
611 /* compute the offset of the cookie representing the next entry */
612 nextEntryCookie = curOffset.LowPart
613 + (CM_DIR_CHUNKSIZE * numDirChunks);
615 if (dep->fid.vnode != 0) {
616 /* this is one of the entries to use: it is not deleted */
617 code = (*funcp)(scp, dep, parmp, &curOffset);
619 } /* if we're including this name */
622 /* and adjust curOffset to be where the new cookie is */
624 thyper.LowPart = CM_DIR_CHUNKSIZE * numDirChunks;
625 curOffset = LargeIntegerAdd(thyper, curOffset);
626 } /* while copying data for dir listing */
628 /* release the mutex */
630 lock_ReleaseMutex(&bufferp->mx);
631 buf_Release(bufferp);
636 int cm_NoneUpper(char *s)
640 if (c >= 'A' && c <= 'Z')
645 int cm_NoneLower(char *s)
649 if (c >= 'a' && c <= 'z')
654 long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
657 cm_lookupSearch_t *sp;
664 matchName = dep->name;
666 match = cm_stricmp(matchName, sp->searchNamep);
668 match = strcmp(matchName, sp->searchNamep);
672 && !cm_Is8Dot3(dep->name)) {
673 matchName = shortName;
674 cm_Gen8Dot3Name(dep, shortName, NULL);
676 match = cm_stricmp(matchName, sp->searchNamep);
678 match = strcmp(matchName, sp->searchNamep);
686 if (!sp->caseFold || matchName == shortName) {
687 sp->fid.vnode = ntohl(dep->fid.vnode);
688 sp->fid.unique = ntohl(dep->fid.unique);
689 return CM_ERROR_STOPNOW;
693 * If we get here, we are doing a case-insensitive search, and we
694 * have found a match. Now we determine what kind of match it is:
695 * exact, lower-case, upper-case, or none of the above. This is done
696 * in order to choose among matches, if there are more than one.
699 /* Exact matches are the best. */
700 match = strcmp(matchName, sp->searchNamep);
702 sp->fid.vnode = ntohl(dep->fid.vnode);
703 sp->fid.unique = ntohl(dep->fid.unique);
704 return CM_ERROR_STOPNOW;
707 /* Lower-case matches are next. */
710 if (cm_NoneUpper(matchName)) {
715 /* Upper-case matches are next. */
718 if (cm_NoneLower(matchName)) {
723 /* General matches are last. */
729 sp->fid.vnode = ntohl(dep->fid.vnode);
730 sp->fid.unique = ntohl(dep->fid.unique);
734 /* read the contents of a mount point into the appropriate string.
735 * called with locked scp, and returns with locked scp.
737 long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
744 if (scp->mountPointStringp) return 0;
746 /* otherwise, we have to read it in */
747 lock_ReleaseMutex(&scp->mx);
749 lock_ObtainRead(&scp->bufCreateLock);
750 thyper.LowPart = thyper.HighPart = 0;
751 code = buf_Get(scp, &thyper, &bufp);
752 lock_ReleaseRead(&scp->bufCreateLock);
754 lock_ObtainMutex(&scp->mx);
759 code = cm_SyncOp(scp, bufp, userp, reqp, 0,
760 CM_SCACHESYNC_READ | CM_SCACHESYNC_NEEDCALLBACK);
765 if (cm_HaveBuffer(scp, bufp, 0)) break;
767 /* otherwise load buffer */
768 code = cm_GetBuffer(scp, bufp, NULL, userp, reqp);
773 /* locked, has callback, has valid data in buffer */
774 if ((tlen = scp->length.LowPart) > 1000) return CM_ERROR_TOOBIG;
776 code = CM_ERROR_INVAL;
780 /* someone else did the work while we were out */
781 if (scp->mountPointStringp) {
786 /* otherwise, copy out the link */
787 scp->mountPointStringp = malloc(tlen);
788 memcpy(scp->mountPointStringp, bufp->datap, tlen);
790 /* now make it null-terminated. Note that the original contents of a
791 * link that is a mount point is "#volname." where "." is there just to
792 * be turned into a null. That is, we can trash the last char of the
793 * link without damaging the vol name. This is a stupid convention,
794 * but that's the protocol.
796 scp->mountPointStringp[tlen-1] = 0;
800 if (bufp) buf_Release(bufp);
804 /* called with a locked scp and chases the mount point, yielding outScpp.
805 * scp remains locked, just for simplicity of describing the interface.
807 long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
808 cm_req_t *reqp, cm_scache_t **outScpp)
823 if (scp->mountRootFidp && scp->mountRootGen >= cm_mountRootGen) {
824 tfid = *scp->mountRootFidp;
825 lock_ReleaseMutex(&scp->mx);
826 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
827 lock_ObtainMutex(&scp->mx);
831 /* parse the volume name */
832 mpNamep = scp->mountPointStringp;
834 tlen = strlen(scp->mountPointStringp);
835 mtType = *scp->mountPointStringp;
836 cellNamep = malloc(tlen);
837 volNamep = malloc(tlen);
839 cp = strrchr(mpNamep, ':');
841 /* cellular mount point */
842 memset(cellNamep, 0, tlen);
843 strncpy(cellNamep, mpNamep+1, cp - mpNamep - 1);
844 strcpy(volNamep, cp+1);
845 /* now look up the cell */
846 cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE);
850 strcpy(volNamep, mpNamep+1);
852 cellp = cm_FindCellByID(scp->fid.cell);
856 code = CM_ERROR_NOSUCHCELL;
860 vnLength = strlen(volNamep);
861 if (vnLength >= 8 && strcmp(volNamep + vnLength - 7, ".backup") == 0)
863 else if (vnLength >= 10
864 && strcmp(volNamep + vnLength - 9, ".readonly") == 0)
869 /* check for backups within backups */
871 && (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
872 == CM_SCACHEFLAG_RO) {
873 code = CM_ERROR_NOSUCHVOLUME;
877 /* now we need to get the volume */
878 lock_ReleaseMutex(&scp->mx);
879 code = cm_GetVolumeByName(cellp, volNamep, userp, reqp, 0, &volp);
880 lock_ObtainMutex(&scp->mx);
883 /* save the parent of the volume root for this is the
884 * place where the volume is mounted and we must remember
885 * this in the volume structure rather than just in the
886 * scache entry lest the scache entry gets recycled
889 lock_ObtainMutex(&volp->mx);
890 if(volp->dotdotFidp == (cm_fid_t *) NULL)
891 volp->dotdotFidp = (cm_fid_t *) malloc(sizeof(cm_fid_t));
892 *(volp->dotdotFidp) = dscp->fid;
893 lock_ReleaseMutex(&volp->mx);
895 if (scp->mountRootFidp == 0) {
896 scp->mountRootFidp = malloc(sizeof(cm_fid_t));
898 scp->mountRootFidp->cell = cellp->cellID;
899 /* if the mt pt is in a read-only volume (not just a
900 * backup), and if there is a read-only volume for the
901 * target, and if this is a type '#' mount point, use
902 * the read-only, otherwise use the one specified.
904 if (mtType == '#' && (scp->flags & CM_SCACHEFLAG_PURERO)
905 && volp->roID != 0 && type == RWVOL)
908 scp->mountRootFidp->volume = volp->roID;
909 else if (type == BACKVOL)
910 scp->mountRootFidp->volume = volp->bkID;
912 scp->mountRootFidp->volume = volp->rwID;
914 /* the rest of the fid is a magic number */
915 scp->mountRootFidp->vnode = 1;
916 scp->mountRootFidp->unique = 1;
917 scp->mountRootGen = cm_mountRootGen;
919 tfid = *scp->mountRootFidp;
920 lock_ReleaseMutex(&scp->mx);
921 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
922 lock_ObtainMutex(&scp->mx);
931 int cm_ExpandSysName(char *inp, char *outp, long outSize)
936 tp = strrchr(inp, '@');
937 if (tp == NULL) return 0; /* no @sys */
939 if (strcmp(tp, "@sys") != 0) return 0; /* no @sys */
941 /* caller just wants to know if this is a valid @sys type of name */
942 if (outp == NULL) return 1;
944 /* otherwise generate the properly expanded @sys name */
945 prefixCount = tp - inp;
947 strncpy(outp, inp, prefixCount); /* copy out "a." from "a.@sys" */
948 outp[prefixCount] = 0; /* null terminate the "a." */
949 strcat(outp, cm_sysName); /* append i386_nt40 */
953 long cm_Lookup(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
954 cm_req_t *reqp, cm_scache_t **outpScpp)
957 int dnlcHit = 1; /* did we hit in the dnlc? yes, we did */
958 cm_scache_t *tscp = NULL;
959 cm_scache_t *mountedScp;
960 cm_lookupSearch_t rock;
964 if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
965 && strcmp(namep, "..") == 0) {
966 if (dscp->dotdotFidp == (cm_fid_t *)NULL
967 || dscp->dotdotFidp->volume == 0)
968 return CM_ERROR_NOSUCHVOLUME;
969 rock.fid = *dscp->dotdotFidp;
973 if (cm_ExpandSysName(namep, tname, sizeof(tname))) {
976 memset(&rock, 0, sizeof(rock));
977 rock.fid.cell = dscp->fid.cell;
978 rock.fid.volume = dscp->fid.volume;
979 rock.searchNamep = namep;
980 rock.caseFold = (flags & CM_FLAG_CASEFOLD);
981 rock.hasTilde = ((strchr(namep, '~') != NULL) ? 1 : 0);
983 /* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
984 code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
985 (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
987 /* code == 0 means we fell off the end of the dir, while stopnow means
988 * that we stopped early, probably because we found the entry we're
989 * looking for. Any other non-zero code is an error.
991 if (code && code != CM_ERROR_STOPNOW) return code;
993 getroot = (dscp==cm_rootSCachep) ;
995 if (!cm_freelanceEnabled || !getroot) {
996 if (flags & CM_FLAG_CHECKPATH)
997 return CM_ERROR_NOSUCHPATH;
999 return CM_ERROR_NOSUCHFILE;
1001 else { /* nonexistent dir on freelance root, so add it */
1002 code = cm_FreelanceAddMount(namep, namep, "root.cell.",
1004 if (code < 0) { /* add mount point failed, so give up */
1005 if (flags & CM_FLAG_CHECKPATH)
1006 return CM_ERROR_NOSUCHPATH;
1008 return CM_ERROR_NOSUCHFILE;
1010 tscp = NULL; /* to force call of cm_GetSCache */
1015 if ( !tscp ) /* we did not find it in the dnlc */
1018 code = cm_GetSCache(&rock.fid, &tscp, userp, reqp);
1019 if (code) return code;
1021 /* tscp is now held */
1023 lock_ObtainMutex(&tscp->mx);
1024 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1025 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1027 lock_ReleaseMutex(&tscp->mx);
1028 cm_ReleaseSCache(tscp);
1031 /* tscp is now locked */
1033 if (!(flags & CM_FLAG_NOMOUNTCHASE)
1034 && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
1035 /* mount points are funny: they have a volume name to mount
1038 code = cm_ReadMountPoint(tscp, userp, reqp);
1040 code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
1042 lock_ReleaseMutex(&tscp->mx);
1043 cm_ReleaseSCache(tscp);
1050 lock_ReleaseMutex(&tscp->mx);
1053 /* copy back pointer */
1056 /* insert scache in dnlc */
1057 if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) ) {
1058 /* lock the directory entry to prevent racing callback revokes */
1059 lock_ObtainMutex(&dscp->mx);
1060 if ( dscp->cbServerp && dscp->cbExpires )
1061 cm_dnlcEnter(dscp, namep, tscp);
1062 lock_ReleaseMutex(&dscp->mx);
1069 long cm_Unlink(cm_scache_t *dscp, char *namep, cm_user_t *userp, cm_req_t *reqp)
1075 AFSFetchStatus newDirStatus;
1078 #ifdef AFS_FREELANCE_CLIENT
1079 if (cm_freelanceEnabled && dscp == cm_rootSCachep) {
1080 /* deleting a mount point from the root dir. */
1081 code = cm_FreelanceRemoveMount(namep);
1086 /* make sure we don't screw up the dir status during the merge */
1087 lock_ObtainMutex(&dscp->mx);
1088 sflags = CM_SCACHESYNC_STOREDATA;
1089 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1090 lock_ReleaseMutex(&dscp->mx);
1091 if (code) return code;
1094 afsFid.Volume = dscp->fid.volume;
1095 afsFid.Vnode = dscp->fid.vnode;
1096 afsFid.Unique = dscp->fid.unique;
1098 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1101 code = RXAFS_RemoveFile(connp->callp, &afsFid, namep,
1102 &newDirStatus, &volSync);
1104 } while (cm_Analyze(connp, userp, reqp,
1105 &dscp->fid, &volSync, NULL, code));
1106 code = cm_MapRPCError(code, reqp);
1108 lock_ObtainMutex(&dscp->mx);
1109 cm_dnlcRemove(dscp, namep);
1110 cm_SyncOpDone(dscp, NULL, sflags);
1111 if (code == 0) cm_MergeStatus(dscp, &newDirStatus, &volSync, userp, 0);
1112 lock_ReleaseMutex(&dscp->mx);
1117 /* called with a locked vnode, and fills in the link info.
1118 * returns this the vnode still locked.
1120 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1127 lock_AssertMutex(&linkScp->mx);
1128 if (!linkScp->mountPointStringp) {
1129 /* read the link data */
1130 lock_ReleaseMutex(&linkScp->mx);
1131 thyper.LowPart = thyper.HighPart = 0;
1132 code = buf_Get(linkScp, &thyper, &bufp);
1133 lock_ObtainMutex(&linkScp->mx);
1134 if (code) return code;
1136 code = cm_SyncOp(linkScp, bufp, userp, reqp, 0,
1137 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1142 if (cm_HaveBuffer(linkScp, bufp, 0)) break;
1144 code = cm_GetBuffer(linkScp, bufp, NULL, userp, reqp);
1149 } /* while loop to get the data */
1151 /* now if we still have no link read in,
1152 * copy the data from the buffer */
1153 if ((temp = linkScp->length.LowPart) >= 1024) {
1155 return CM_ERROR_TOOBIG;
1158 /* otherwise, it fits; make sure it is still null (could have
1159 * lost race with someone else referencing this link above),
1160 * and if so, copy in the data.
1162 if (linkScp->mountPointStringp == NULL) {
1163 linkScp->mountPointStringp = malloc(temp+1);
1164 strncpy(linkScp->mountPointStringp, bufp->datap, temp);
1165 linkScp->mountPointStringp[temp] = 0; /* null terminate */
1168 } /* don't have sym link contents cached */
1173 /* called with a held vnode and a path suffix, with the held vnode being a
1174 * symbolic link. Our goal is to generate a new path to interpret, and return
1175 * this new path in newSpaceBufferp. If the new vnode is relative to a dir
1176 * other than the directory containing the symbolic link, then the new root is
1177 * returned in *newRootScpp, otherwise a null is returned there.
1179 long cm_AssembleLink(cm_scache_t *linkScp, char *pathSuffixp,
1180 cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1181 cm_user_t *userp, cm_req_t *reqp)
1187 lock_ObtainMutex(&linkScp->mx);
1188 code = cm_HandleLink(linkScp, userp, reqp);
1189 if (code) goto done;
1191 /* if we may overflow the buffer, bail out; buffer is signficantly
1192 * bigger than max path length, so we don't really have to worry about
1193 * being a little conservative here.
1195 if (strlen(linkScp->mountPointStringp) + strlen(pathSuffixp) + 2
1196 >= CM_UTILS_SPACESIZE)
1197 return CM_ERROR_TOOBIG;
1199 tsp = cm_GetSpace();
1200 linkp = linkScp->mountPointStringp;
1201 if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1202 if (strlen(linkp) > cm_mountRootLen)
1203 strcpy(tsp->data, linkp+cm_mountRootLen+1);
1206 *newRootScpp = cm_rootSCachep;
1207 cm_HoldSCache(cm_rootSCachep);
1208 } else if (*linkp == '\\' || *linkp == '/') {
1209 /* formerly, this was considered to be from the AFS root,
1210 but this seems to create problems. instead, we will just
1213 strcpy(tsp->data, linkp+1);
1214 *newRootScpp = cm_rootSCachep;
1215 cm_HoldSCache(cm_rootSCachep);
1217 code = CM_ERROR_NOSUCHPATH;
1222 /* a relative link */
1223 strcpy(tsp->data, linkp);
1224 *newRootScpp = NULL;
1226 if (pathSuffixp[0] != 0) { /* if suffix string is non-null */
1227 strcat(tsp->data, "\\");
1228 strcat(tsp->data, pathSuffixp);
1230 *newSpaceBufferp = tsp;
1234 lock_ReleaseMutex(&linkScp->mx);
1238 long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
1239 cm_user_t *userp, char *tidPathp, cm_req_t *reqp, cm_scache_t **outScpp)
1242 char *tp; /* ptr moving through input buffer */
1243 char tc; /* temp char */
1244 int haveComponent; /* has new component started? */
1245 char component[256]; /* this is the new component */
1246 char *cp; /* component name being assembled */
1247 cm_scache_t *tscp; /* current location in the hierarchy */
1248 cm_scache_t *nscp; /* next dude down */
1249 cm_scache_t *dirScp; /* last dir we searched */
1250 cm_scache_t *linkScp; /* new root for the symlink we just
1252 cm_space_t *psp; /* space for current path, if we've hit
1254 cm_space_t *tempsp; /* temp vbl */
1255 char *restp; /* rest of the pathname to interpret */
1256 int symlinkCount; /* count of # of symlinks traversed */
1257 int extraFlag; /* avoid chasing mt pts for dir cmd */
1258 int phase = 1; /* 1 = tidPathp, 2 = pathp */
1268 cm_HoldSCache(tscp);
1273 /* map Unix slashes into DOS ones so we can interpret Unix
1276 if (tc == '/') tc = '\\';
1278 if (!haveComponent) {
1279 if (tc == '\\') continue;
1296 /* we have a component here */
1297 if (tc == 0 || tc == '\\') {
1298 /* end of the component; we're at the last
1299 * component if tc == 0. However, if the last
1300 * is a symlink, we have more to do.
1302 *cp++ = 0; /* add null termination */
1304 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
1305 extraFlag = CM_FLAG_NOMOUNTCHASE;
1306 code = cm_Lookup(tscp, component,
1308 userp, reqp, &nscp);
1310 cm_ReleaseSCache(tscp);
1311 if (psp) cm_FreeSpace(psp);
1314 haveComponent = 0; /* component done */
1315 dirScp = tscp; /* for some symlinks */
1316 tscp = nscp; /* already held */
1317 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
1319 cm_ReleaseSCache(dirScp);
1323 /* now, if tscp is a symlink, we should follow
1324 * it and assemble the path again.
1326 lock_ObtainMutex(&tscp->mx);
1327 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1328 CM_SCACHESYNC_GETSTATUS
1329 | CM_SCACHESYNC_NEEDCALLBACK);
1331 lock_ReleaseMutex(&tscp->mx);
1332 cm_ReleaseSCache(tscp);
1333 cm_ReleaseSCache(dirScp);
1336 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
1337 /* this is a symlink; assemble a new buffer */
1338 lock_ReleaseMutex(&tscp->mx);
1339 if (symlinkCount++ >= 16) {
1340 cm_ReleaseSCache(tscp);
1341 cm_ReleaseSCache(dirScp);
1342 if (psp) cm_FreeSpace(psp);
1343 return CM_ERROR_TOOBIG;
1345 if (tc == 0) restp = "";
1347 code = cm_AssembleLink(tscp, restp,
1348 &linkScp, &tempsp, userp, reqp);
1350 /* something went wrong */
1351 cm_ReleaseSCache(tscp);
1352 cm_ReleaseSCache(dirScp);
1356 /* otherwise, tempsp has the new path,
1357 * and linkScp is the new root from
1358 * which to interpret that path.
1359 * Continue with the namei processing,
1360 * also doing the bookkeeping for the
1361 * space allocation and tracking the
1362 * vnode reference counts.
1364 if (psp) cm_FreeSpace(psp);
1367 cm_ReleaseSCache(tscp);
1368 tscp = linkScp; /* already held
1369 * by AssembleLink */
1370 /* now, if linkScp is null, that's
1371 * AssembleLink's way of telling us that
1372 * the sym link is relative to the dir
1373 * containing the link. We have a ref
1374 * to it in dirScp, and we hold it now
1375 * and reuse it as the new spot in the
1379 cm_HoldSCache(dirScp);
1382 } /* if we have a sym link */
1384 /* not a symlink, we may be done */
1385 lock_ReleaseMutex(&tscp->mx);
1392 cm_ReleaseSCache(dirScp);
1397 cm_ReleaseSCache(dirScp);
1398 } /* end of a component */
1400 } /* we have a component */
1401 } /* big while loop over all components */
1404 if (psp) cm_FreeSpace(psp);
1405 if (code == 0) *outScpp = tscp;
1409 /* called with a dir, and a vnode within the dir that happens to be a symlink.
1410 * We chase the link, and return a held pointer to the target, if it exists,
1411 * in *outScpp. If we succeed, we return 0, otherwise we return an error code
1412 * and do not hold or return a target vnode.
1414 * This is very similar to calling cm_NameI with the last component of a name,
1415 * which happens to be a symlink, except that we've already passed by the name.
1417 * This function is typically called by the directory listing functions, which
1418 * encounter symlinks but need to return the proper file length so programs
1419 * like "more" work properly when they make use of the attributes retrieved from
1422 * The input vnode should not be locked when this function is called.
1424 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
1425 cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
1429 cm_scache_t *newRootScp;
1431 osi_Log1(afsd_logp, "Evaluating symlink vp %x", linkScp);
1433 code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
1434 if (code) return code;
1436 /* now, if newRootScp is NULL, we're really being told that the symlink
1437 * is relative to the current directory (dscp).
1439 if (newRootScp == NULL) {
1441 cm_HoldSCache(dscp);
1444 code = cm_NameI(newRootScp, spacep->data,
1445 CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
1446 userp, NULL, reqp, outScpp);
1448 /* this stuff is allocated no matter what happened on the namei call,
1450 cm_FreeSpace(spacep);
1451 cm_ReleaseSCache(newRootScp);
1456 /* make this big enough so that one buffer of dir pages won't overflow. We'll
1457 * check anyway, but we want to minimize the chance that we have to leave stuff
1460 #define CM_BULKMAX 128
1462 /* rock for bulk stat calls */
1463 typedef struct cm_bulkStat {
1464 osi_hyper_t bufOffset; /* only do it for things in this buffer page */
1466 /* info for the actual call */
1467 int counter; /* next free slot */
1468 AFSFid fids[CM_BULKMAX];
1469 AFSFetchStatus stats[CM_BULKMAX];
1470 AFSCallBack callbacks[CM_BULKMAX];
1473 /* for a given entry, make sure that it isn't in the stat cache, and then
1474 * add it to the list of file IDs to be obtained.
1476 * Don't bother adding it if we already have a vnode. Note that the dir
1477 * is locked, so we have to be careful checking the vnode we're thinking of
1478 * processing, to avoid deadlocks.
1480 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
1491 /* Don't overflow bsp. */
1492 if (bsp->counter >= CM_BULKMAX)
1493 return CM_ERROR_STOPNOW;
1495 thyper.LowPart = buf_bufferSize;
1496 thyper.HighPart = 0;
1497 thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
1499 /* thyper is now the first byte past the end of the record we're
1500 * interested in, and bsp->bufOffset is the first byte of the record
1501 * we're interested in.
1502 * Skip data in the others.
1505 if (LargeIntegerLessThan(*offp, bsp->bufOffset))
1507 if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
1508 return CM_ERROR_STOPNOW;
1509 if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
1512 tfid.cell = scp->fid.cell;
1513 tfid.volume = scp->fid.volume;
1514 tfid.vnode = ntohl(dep->fid.vnode);
1515 tfid.unique = ntohl(dep->fid.unique);
1516 tscp = cm_FindSCache(&tfid);
1518 if (lock_TryMutex(&tscp->mx)) {
1519 /* we have an entry that we can look at */
1520 if (cm_HaveCallback(tscp)) {
1521 /* we have a callback on it. Don't bother
1522 * fetching this stat entry, since we're happy
1523 * with the info we have.
1525 lock_ReleaseMutex(&tscp->mx);
1526 cm_ReleaseSCache(tscp);
1529 lock_ReleaseMutex(&tscp->mx);
1531 cm_ReleaseSCache(tscp);
1534 #ifdef AFS_FREELANCE_CLIENT
1535 // yj: if this is a mountpoint under root.afs then we don't want it
1536 // to be bulkstat-ed, instead, we call getSCache directly and under
1537 // getSCache, it is handled specially.
1538 if (cm_freelanceEnabled &&
1539 tfid.cell==0x1 && tfid.volume==0x20000001 &&
1540 !(tfid.vnode==0x1 && tfid.unique==0x1) )
1543 afsi_log(" cm_trybulkproc going to call getscache");
1545 return cm_GetSCache(&tfid, &tscp, NULL, NULL);
1547 #endif /* AFS_FREELANCE_CLIENT */
1550 bsp->fids[i].Volume = scp->fid.volume;
1551 bsp->fids[i].Vnode = tfid.vnode;
1552 bsp->fids[i].Unique = tfid.unique;
1556 /* called with a locked scp and a pointer to a buffer. Make bulk stat
1557 * calls on all undeleted files in the page of the directory specified.
1559 void cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
1563 cm_bulkStat_t bb; /* this is *BIG*, probably 12K or so;
1564 * watch for stack problems */
1565 AFSCBFids fidStruct;
1566 AFSBulkStats statStruct;
1568 AFSCBs callbackStruct;
1571 cm_callbackRequest_t cbReq;
1578 osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%x", (long) dscp);
1580 /* should be on a buffer boundary */
1581 osi_assert((offsetp->LowPart & (buf_bufferSize - 1)) == 0);
1584 bb.bufOffset = *offsetp;
1586 /* first, assemble the file IDs we need to stat */
1587 code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) &bb, offsetp, userp,
1590 /* if we failed, bail out early */
1591 if (code && code != CM_ERROR_STOPNOW) return;
1593 /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
1594 * make the calls to create the entries. Handle AFSCBMAX files at a
1598 while(filex < bb.counter) {
1599 filesThisCall = bb.counter - filex;
1600 if (filesThisCall > AFSCBMAX) filesThisCall = AFSCBMAX;
1602 fidStruct.AFSCBFids_len = filesThisCall;
1603 fidStruct.AFSCBFids_val = &bb.fids[filex];
1604 statStruct.AFSBulkStats_len = filesThisCall;
1605 statStruct.AFSBulkStats_val = &bb.stats[filex];
1606 callbackStruct.AFSCBs_len = filesThisCall;
1607 callbackStruct.AFSCBs_val = &bb.callbacks[filex];
1608 cm_StartCallbackGrantingCall(NULL, &cbReq);
1609 osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
1611 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1614 code = RXAFS_BulkStatus(connp->callp, &fidStruct,
1615 &statStruct, &callbackStruct, &volSync);
1617 } while (cm_Analyze(connp, userp, reqp, &dscp->fid,
1618 &volSync, &cbReq, code));
1619 code = cm_MapRPCError(code, reqp);
1621 osi_Log0(afsd_logp, "CALL BulkStatus DONE");
1623 /* may as well quit on an error, since we're not going to do
1624 * much better on the next immediate call, either.
1628 /* otherwise, we should do the merges */
1629 for(i = 0; i<filesThisCall; i++) {
1631 tfid.cell = dscp->fid.cell;
1632 tfid.volume = bb.fids[j].Volume;
1633 tfid.vnode = bb.fids[j].Vnode;
1634 tfid.unique = bb.fids[j].Unique;
1635 code = cm_GetSCache(&tfid, &scp, userp, reqp);
1636 if (code != 0) continue;
1638 /* otherwise, if this entry has no callback info,
1641 lock_ObtainMutex(&scp->mx);
1642 /* now, we have to be extra paranoid on merging in this
1643 * information, since we didn't use cm_SyncOp before
1644 * starting the fetch to make sure that no bad races
1645 * were occurring. Specifically, we need to make sure
1646 * we don't obliterate any newer information in the
1647 * vnode than have here.
1649 * Right now, be pretty conservative: if there's a
1650 * callback or a pending call, skip it.
1652 if (scp->cbServerp == NULL
1654 (CM_SCACHEFLAG_FETCHING
1655 | CM_SCACHEFLAG_STORING
1656 | CM_SCACHEFLAG_SIZESTORING))) {
1657 cm_EndCallbackGrantingCall(scp, &cbReq,
1659 CM_CALLBACK_MAINTAINCOUNT);
1660 cm_MergeStatus(scp, &bb.stats[j], &volSync,
1663 lock_ReleaseMutex(&scp->mx);
1664 cm_ReleaseSCache(scp);
1665 } /* all files in the response */
1666 /* now tell it to drop the count,
1667 * after doing the vnode processing above */
1668 cm_EndCallbackGrantingCall(NULL, NULL, NULL, 0);
1670 filex += filesThisCall;
1671 } /* while there are still more files to process */
1672 osi_Log0(afsd_logp, "END cm_TryBulkStat");
1675 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
1679 /* initialize store back mask as inexpensive local variable */
1681 memset(statusp, 0, sizeof(AFSStoreStatus));
1683 /* copy out queued info from scache first, if scp passed in */
1685 if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
1686 statusp->ClientModTime = scp->clientModTime;
1687 mask |= AFS_SETMODTIME;
1688 scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
1693 /* now add in our locally generated request */
1694 if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
1695 statusp->ClientModTime = attrp->clientModTime;
1696 mask |= AFS_SETMODTIME;
1698 if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
1699 statusp->UnixModeBits = attrp->unixModeBits;
1700 mask |= AFS_SETMODE;
1702 if (attrp->mask & CM_ATTRMASK_OWNER) {
1703 statusp->Owner = attrp->owner;
1704 mask |= AFS_SETOWNER;
1706 if (attrp->mask & CM_ATTRMASK_GROUP) {
1707 statusp->Group = attrp->group;
1708 mask |= AFS_SETGROUP;
1711 statusp->Mask = mask;
1714 /* set the file size, and make sure that all relevant buffers have been
1715 * truncated. Ensure that any partially truncated buffers have been zeroed
1716 * to the end of the buffer.
1718 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
1724 /* start by locking out buffer creation */
1725 lock_ObtainWrite(&scp->bufCreateLock);
1727 /* verify that this is a file, not a dir or a symlink */
1728 lock_ObtainMutex(&scp->mx);
1729 code = cm_SyncOp(scp, NULL, userp, reqp, 0,
1730 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1731 if (code) goto done;
1733 if (scp->fileType != CM_SCACHETYPE_FILE) {
1734 code = CM_ERROR_ISDIR;
1739 if (LargeIntegerLessThan(*sizep, scp->length))
1744 lock_ReleaseMutex(&scp->mx);
1746 /* can't hold scp->mx lock here, since we may wait for a storeback to
1747 * finish if the buffer package is cleaning a buffer by storing it to
1751 buf_Truncate(scp, userp, reqp, sizep);
1753 /* now ensure that file length is short enough, and update truncPos */
1754 lock_ObtainMutex(&scp->mx);
1756 /* make sure we have a callback (so we have the right value for the
1757 * length), and wait for it to be safe to do a truncate.
1759 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
1760 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
1761 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
1762 if (code) goto done;
1764 if (LargeIntegerLessThan(*sizep, scp->length)) {
1765 /* a real truncation. If truncPos is not set yet, or is bigger
1766 * than where we're truncating the file, set truncPos to this
1771 if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
1772 || LargeIntegerLessThan(*sizep, scp->length)) {
1774 scp->truncPos = *sizep;
1775 scp->mask |= CM_SCACHEMASK_TRUNCPOS;
1777 /* in either case, the new file size has been changed */
1778 scp->length = *sizep;
1779 scp->mask |= CM_SCACHEMASK_LENGTH;
1781 else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
1782 /* really extending the file */
1783 scp->length = *sizep;
1784 scp->mask |= CM_SCACHEMASK_LENGTH;
1787 /* done successfully */
1791 lock_ReleaseMutex(&scp->mx);
1792 lock_ReleaseWrite(&scp->bufCreateLock);
1797 /* set the file size or other attributes (but not both at once) */
1798 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
1803 AFSFetchStatus afsOutStatus;
1807 AFSStoreStatus afsInStatus;
1809 /* handle file length setting */
1810 if (attrp->mask & CM_ATTRMASK_LENGTH)
1811 return cm_SetLength(scp, &attrp->length, userp, reqp);
1813 flags = CM_SCACHESYNC_STORESTATUS;
1815 lock_ObtainMutex(&scp->mx);
1816 /* otherwise, we have to make an RPC to get the status */
1817 code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
1819 /* make the attr structure */
1820 cm_StatusFromAttr(&afsInStatus, scp, attrp);
1822 lock_ReleaseMutex(&scp->mx);
1823 if (code) return code;
1825 /* now make the RPC */
1826 osi_Log1(afsd_logp, "CALL StoreStatus vp %x", (long) scp);
1827 tfid.Volume = scp->fid.volume;
1828 tfid.Vnode = scp->fid.vnode;
1829 tfid.Unique = scp->fid.unique;
1831 code = cm_Conn(&scp->fid, userp, reqp, &connp);
1834 code = RXAFS_StoreStatus(connp->callp, &tfid,
1835 &afsInStatus, &afsOutStatus, &volSync);
1837 } while (cm_Analyze(connp, userp, reqp,
1838 &scp->fid, &volSync, NULL, code));
1839 code = cm_MapRPCError(code, reqp);
1841 osi_Log1(afsd_logp, "CALL StoreStatus DONE, code %d", code);
1843 lock_ObtainMutex(&scp->mx);
1844 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
1846 cm_MergeStatus(scp, &afsOutStatus, &volSync, userp,
1847 CM_MERGEFLAG_FORCE);
1849 /* if we're changing the mode bits, discard the ACL cache,
1850 * since we changed the mode bits.
1852 if (afsInStatus.Mask & AFS_SETMODE) cm_FreeAllACLEnts(scp);
1853 lock_ReleaseMutex(&scp->mx);
1857 long cm_Create(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
1858 cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
1863 cm_callbackRequest_t cbReq;
1868 AFSStoreStatus inStatus;
1869 AFSFetchStatus updatedDirStatus;
1870 AFSFetchStatus newFileStatus;
1871 AFSCallBack newFileCallback;
1874 /* can't create names with @sys in them; must expand it manually first.
1875 * return "invalid request" if they try.
1877 if (cm_ExpandSysName(namep, NULL, 0)) {
1878 return CM_ERROR_ATSYS;
1881 /* before starting the RPC, mark that we're changing the file data, so
1882 * that someone who does a chmod will know to wait until our call
1885 lock_ObtainMutex(&dscp->mx);
1886 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
1888 cm_StartCallbackGrantingCall(NULL, &cbReq);
1890 lock_ReleaseMutex(&dscp->mx);
1896 cm_StatusFromAttr(&inStatus, NULL, attrp);
1898 /* try the RPC now */
1900 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1903 dirAFSFid.Volume = dscp->fid.volume;
1904 dirAFSFid.Vnode = dscp->fid.vnode;
1905 dirAFSFid.Unique = dscp->fid.unique;
1906 code = RXAFS_CreateFile(connp->callp, &dirAFSFid, namep,
1907 &inStatus, &newAFSFid, &newFileStatus,
1908 &updatedDirStatus, &newFileCallback,
1910 } while (cm_Analyze(connp, userp, reqp,
1911 &dscp->fid, &volSync, &cbReq, code));
1912 code = cm_MapRPCError(code, reqp);
1914 lock_ObtainMutex(&dscp->mx);
1915 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
1917 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
1919 lock_ReleaseMutex(&dscp->mx);
1921 /* now try to create the file's entry, too, but be careful to
1922 * make sure that we don't merge in old info. Since we weren't locking
1923 * out any requests during the file's creation, we may have pretty old
1927 newFid.cell = dscp->fid.cell;
1928 newFid.volume = dscp->fid.volume;
1929 newFid.vnode = newAFSFid.Vnode;
1930 newFid.unique = newAFSFid.Unique;
1931 code = cm_GetSCache(&newFid, &scp, userp, reqp);
1933 lock_ObtainMutex(&scp->mx);
1934 if (!cm_HaveCallback(scp)) {
1935 cm_MergeStatus(scp, &newFileStatus, &volSync,
1937 cm_EndCallbackGrantingCall(scp, &cbReq,
1938 &newFileCallback, 0);
1941 lock_ReleaseMutex(&scp->mx);
1946 /* make sure we end things properly */
1948 cm_EndCallbackGrantingCall(NULL, NULL, NULL, 0);
1953 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
1957 lock_ObtainWrite(&scp->bufCreateLock);
1958 code = buf_CleanVnode(scp, userp, reqp);
1959 lock_ReleaseWrite(&scp->bufCreateLock);
1961 lock_ObtainMutex(&scp->mx);
1962 scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA
1963 | CM_SCACHEFLAG_OUTOFSPACE);
1964 if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
1965 | CM_SCACHEMASK_CLIENTMODTIME
1966 | CM_SCACHEMASK_LENGTH))
1967 code = cm_StoreMini(scp, userp, reqp);
1968 lock_ReleaseMutex(&scp->mx);
1973 long cm_MakeDir(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
1974 cm_user_t *userp, cm_req_t *reqp)
1979 cm_callbackRequest_t cbReq;
1984 AFSStoreStatus inStatus;
1985 AFSFetchStatus updatedDirStatus;
1986 AFSFetchStatus newDirStatus;
1987 AFSCallBack newDirCallback;
1990 /* can't create names with @sys in them; must expand it manually first.
1991 * return "invalid request" if they try.
1993 if (cm_ExpandSysName(namep, NULL, 0)) {
1994 return CM_ERROR_ATSYS;
1997 /* before starting the RPC, mark that we're changing the directory
1998 * data, so that someone who does a chmod on the dir will wait until
1999 * our call completes.
2001 lock_ObtainMutex(&dscp->mx);
2002 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2004 cm_StartCallbackGrantingCall(NULL, &cbReq);
2006 lock_ReleaseMutex(&dscp->mx);
2012 cm_StatusFromAttr(&inStatus, NULL, attrp);
2014 /* try the RPC now */
2016 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2019 dirAFSFid.Volume = dscp->fid.volume;
2020 dirAFSFid.Vnode = dscp->fid.vnode;
2021 dirAFSFid.Unique = dscp->fid.unique;
2022 code = RXAFS_MakeDir(connp->callp, &dirAFSFid, namep,
2023 &inStatus, &newAFSFid, &newDirStatus,
2024 &updatedDirStatus, &newDirCallback,
2026 } while (cm_Analyze(connp, userp, reqp,
2027 &dscp->fid, &volSync, &cbReq, code));
2028 code = cm_MapRPCError(code, reqp);
2030 lock_ObtainMutex(&dscp->mx);
2031 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2033 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2035 lock_ReleaseMutex(&dscp->mx);
2037 /* now try to create the new dir's entry, too, but be careful to
2038 * make sure that we don't merge in old info. Since we weren't locking
2039 * out any requests during the file's creation, we may have pretty old
2043 newFid.cell = dscp->fid.cell;
2044 newFid.volume = dscp->fid.volume;
2045 newFid.vnode = newAFSFid.Vnode;
2046 newFid.unique = newAFSFid.Unique;
2047 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2049 lock_ObtainMutex(&scp->mx);
2050 if (!cm_HaveCallback(scp)) {
2051 cm_MergeStatus(scp, &newDirStatus, &volSync,
2053 cm_EndCallbackGrantingCall(scp, &cbReq,
2054 &newDirCallback, 0);
2057 lock_ReleaseMutex(&scp->mx);
2058 cm_ReleaseSCache(scp);
2062 /* make sure we end things properly */
2064 cm_EndCallbackGrantingCall(NULL, NULL, NULL, 0);
2066 /* and return error code */
2070 long cm_SymLink(cm_scache_t *dscp, char *namep, char *contentsp, long flags,
2071 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
2079 AFSStoreStatus inStatus;
2080 AFSFetchStatus updatedDirStatus;
2081 AFSFetchStatus newLinkStatus;
2084 /* before starting the RPC, mark that we're changing the directory data,
2085 * so that someone who does a chmod on the dir will wait until our
2088 lock_ObtainMutex(&dscp->mx);
2089 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2090 lock_ReleaseMutex(&dscp->mx);
2095 cm_StatusFromAttr(&inStatus, NULL, attrp);
2097 /* try the RPC now */
2099 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2102 dirAFSFid.Volume = dscp->fid.volume;
2103 dirAFSFid.Vnode = dscp->fid.vnode;
2104 dirAFSFid.Unique = dscp->fid.unique;
2105 code = RXAFS_Symlink(connp->callp, &dirAFSFid, namep, contentsp,
2106 &inStatus, &newAFSFid, &newLinkStatus,
2107 &updatedDirStatus, &volSync);
2108 } while (cm_Analyze(connp, userp, reqp,
2109 &dscp->fid, &volSync, NULL, code));
2110 code = cm_MapRPCError(code, reqp);
2112 lock_ObtainMutex(&dscp->mx);
2113 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2115 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2117 lock_ReleaseMutex(&dscp->mx);
2119 /* now try to create the new dir's entry, too, but be careful to
2120 * make sure that we don't merge in old info. Since we weren't locking
2121 * out any requests during the file's creation, we may have pretty old
2125 newFid.cell = dscp->fid.cell;
2126 newFid.volume = dscp->fid.volume;
2127 newFid.vnode = newAFSFid.Vnode;
2128 newFid.unique = newAFSFid.Unique;
2129 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2131 lock_ObtainMutex(&scp->mx);
2132 if (!cm_HaveCallback(scp)) {
2133 cm_MergeStatus(scp, &newLinkStatus, &volSync,
2136 lock_ReleaseMutex(&scp->mx);
2137 cm_ReleaseSCache(scp);
2141 /* and return error code */
2145 long cm_RemoveDir(cm_scache_t *dscp, char *namep, cm_user_t *userp,
2152 AFSFetchStatus updatedDirStatus;
2155 /* before starting the RPC, mark that we're changing the directory data,
2156 * so that someone who does a chmod on the dir will wait until our
2159 lock_ObtainMutex(&dscp->mx);
2160 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2161 lock_ReleaseMutex(&dscp->mx);
2167 /* try the RPC now */
2169 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2172 dirAFSFid.Volume = dscp->fid.volume;
2173 dirAFSFid.Vnode = dscp->fid.vnode;
2174 dirAFSFid.Unique = dscp->fid.unique;
2175 code = RXAFS_RemoveDir(connp->callp, &dirAFSFid, namep,
2176 &updatedDirStatus, &volSync);
2177 } while (cm_Analyze(connp, userp, reqp,
2178 &dscp->fid, &volSync, NULL, code));
2179 code = cm_MapRPCErrorRmdir(code, reqp);
2181 lock_ObtainMutex(&dscp->mx);
2182 cm_dnlcRemove(dscp, namep);
2183 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2185 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2187 lock_ReleaseMutex(&dscp->mx);
2189 /* and return error code */
2193 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
2195 /* grab mutex on contents */
2196 lock_ObtainMutex(&scp->mx);
2198 /* reset the prefetch info */
2199 scp->prefetch.base.LowPart = 0; /* base */
2200 scp->prefetch.base.HighPart = 0;
2201 scp->prefetch.end.LowPart = 0; /* and end */
2202 scp->prefetch.end.HighPart = 0;
2204 /* release mutex on contents */
2205 lock_ReleaseMutex(&scp->mx);
2211 long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
2212 char *newNamep, cm_user_t *userp, cm_req_t *reqp)
2216 AFSFid oldDirAFSFid;
2217 AFSFid newDirAFSFid;
2219 AFSFetchStatus updatedOldDirStatus;
2220 AFSFetchStatus updatedNewDirStatus;
2224 /* before starting the RPC, mark that we're changing the directory data,
2225 * so that someone who does a chmod on the dir will wait until our call
2226 * completes. We do this in vnode order so that we don't deadlock,
2227 * which makes the code a little verbose.
2229 if (oldDscp == newDscp) {
2230 /* check for identical names */
2231 if (strcmp(oldNamep, newNamep) == 0)
2232 return CM_ERROR_RENAME_IDENTICAL;
2235 lock_ObtainMutex(&oldDscp->mx);
2236 cm_dnlcRemove(oldDscp, oldNamep);
2237 cm_dnlcRemove(oldDscp, newNamep);
2238 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2239 CM_SCACHESYNC_STOREDATA);
2240 lock_ReleaseMutex(&oldDscp->mx);
2243 /* two distinct dir vnodes */
2245 if (oldDscp->fid.cell != newDscp->fid.cell ||
2246 oldDscp->fid.volume != newDscp->fid.volume)
2247 return CM_ERROR_CROSSDEVLINK;
2249 /* shouldn't happen that we have distinct vnodes for two
2250 * different files, but could due to deliberate attack, or
2251 * stale info. Avoid deadlocks and quit now.
2253 if (oldDscp->fid.vnode == newDscp->fid.vnode)
2254 return CM_ERROR_CROSSDEVLINK;
2256 if (oldDscp->fid.vnode < newDscp->fid.vnode) {
2257 lock_ObtainMutex(&oldDscp->mx);
2258 cm_dnlcRemove(oldDscp, oldNamep);
2259 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2260 CM_SCACHESYNC_STOREDATA);
2261 lock_ReleaseMutex(&oldDscp->mx);
2263 lock_ObtainMutex(&newDscp->mx);
2264 cm_dnlcRemove(newDscp, newNamep);
2265 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2266 CM_SCACHESYNC_STOREDATA);
2267 lock_ReleaseMutex(&newDscp->mx);
2269 /* cleanup first one */
2270 cm_SyncOpDone(oldDscp, NULL,
2271 CM_SCACHESYNC_STOREDATA);
2276 /* lock the new vnode entry first */
2277 lock_ObtainMutex(&newDscp->mx);
2278 cm_dnlcRemove(newDscp, newNamep);
2279 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2280 CM_SCACHESYNC_STOREDATA);
2281 lock_ReleaseMutex(&newDscp->mx);
2283 lock_ObtainMutex(&oldDscp->mx);
2284 cm_dnlcRemove(oldDscp, oldNamep);
2285 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2286 CM_SCACHESYNC_STOREDATA);
2287 lock_ReleaseMutex(&oldDscp->mx);
2289 /* cleanup first one */
2290 cm_SyncOpDone(newDscp, NULL,
2291 CM_SCACHESYNC_STOREDATA);
2295 } /* two distinct vnodes */
2302 /* try the RPC now */
2304 code = cm_Conn(&oldDscp->fid, userp, reqp, &connp);
2307 oldDirAFSFid.Volume = oldDscp->fid.volume;
2308 oldDirAFSFid.Vnode = oldDscp->fid.vnode;
2309 oldDirAFSFid.Unique = oldDscp->fid.unique;
2310 newDirAFSFid.Volume = newDscp->fid.volume;
2311 newDirAFSFid.Vnode = newDscp->fid.vnode;
2312 newDirAFSFid.Unique = newDscp->fid.unique;
2313 code = RXAFS_Rename(connp->callp, &oldDirAFSFid, oldNamep,
2314 &newDirAFSFid, newNamep,
2315 &updatedOldDirStatus, &updatedNewDirStatus,
2317 } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
2318 &volSync, NULL, code));
2319 code = cm_MapRPCError(code, reqp);
2321 /* update the individual stat cache entries for the directories */
2322 lock_ObtainMutex(&oldDscp->mx);
2323 cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
2325 cm_MergeStatus(oldDscp, &updatedOldDirStatus, &volSync,
2328 lock_ReleaseMutex(&oldDscp->mx);
2330 /* and update it for the new one, too, if necessary */
2332 lock_ObtainMutex(&newDscp->mx);
2333 cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
2335 cm_MergeStatus(newDscp, &updatedNewDirStatus, &volSync,
2338 lock_ReleaseMutex(&newDscp->mx);
2341 /* and return error code */
2345 long cm_Lock(cm_scache_t *scp, unsigned char LockType,
2346 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
2347 u_long Timeout, cm_user_t *userp, cm_req_t *reqp,
2351 int Which = ((LockType & 0x1) ? LockRead : LockWrite);
2355 cm_file_lock_t *fileLock;
2359 /* Look for a conflict. Also, if we are asking for a shared lock,
2360 * look for another shared lock, so we don't have to do an RPC.
2364 fileLock = (cm_file_lock_t *)
2365 ((char *) q - offsetof(cm_file_lock_t, fileq));
2366 if ((fileLock->flags &
2367 (CM_FILELOCK_FLAG_INVALID | CM_FILELOCK_FLAG_WAITING))
2369 if ((LockType & 0x1) == 0
2370 || (fileLock->LockType & 0x1) == 0)
2371 return CM_ERROR_WOULDBLOCK;
2380 tfid.Volume = scp->fid.volume;
2381 tfid.Vnode = scp->fid.vnode;
2382 tfid.Unique = scp->fid.unique;
2383 lock_ReleaseMutex(&scp->mx);
2385 code = cm_Conn(&scp->fid, userp, reqp, &connp);
2387 code = RXAFS_SetLock(connp->callp, &tfid, Which,
2389 } while (cm_Analyze(connp, userp, reqp, &scp->fid, &volSync,
2391 lock_ObtainMutex(&scp->mx);
2392 code = cm_MapRPCError(code, reqp);
2395 if (code == 0 || Timeout != 0) {
2396 fileLock = malloc(sizeof(cm_file_lock_t));
2397 fileLock->LockType = LockType;
2398 fileLock->userp = userp;
2400 fileLock->fid = scp->fid;
2401 fileLock->LOffset = LOffset;
2402 fileLock->LLength = LLength;
2403 fileLock->flags = (code == 0 ? 0 : CM_FILELOCK_FLAG_WAITING);
2404 osi_QAdd(&scp->fileLocks, &fileLock->fileq);
2405 lock_ObtainWrite(&cm_scacheLock);
2406 osi_QAdd(&cm_allFileLocks, &fileLock->q);
2407 lock_ReleaseWrite(&cm_scacheLock);
2408 if (code != 0) *lockpp = fileLock;
2413 long cm_Unlock(cm_scache_t *scp, unsigned char LockType,
2414 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
2415 cm_user_t *userp, cm_req_t *reqp)
2418 int Which = ((LockType & 0x1) ? LockRead : LockWrite);
2422 cm_file_lock_t *fileLock, *ourLock;
2423 osi_queue_t *q, *qq;
2424 int anotherReader = 0;
2428 if (LargeIntegerLessThan(LLength, scp->length))
2431 /* Look for our own lock on the list, so as to remove it.
2432 * Also, determine if we're the last reader; if not, avoid an RPC.
2436 fileLock = (cm_file_lock_t *)
2437 ((char *) q - offsetof(cm_file_lock_t, fileq));
2439 && fileLock->userp == userp
2440 && LargeIntegerEqualTo(fileLock->LOffset, LOffset)
2441 && LargeIntegerEqualTo(fileLock->LLength, LLength)) {
2446 else if (fileLock->LockType & 0x1)
2451 /* ignore byte ranges */
2452 if (smallLock && !found)
2455 /* don't try to unlock other people's locks */
2457 return CM_ERROR_WOULDBLOCK;
2459 /* discard lock record */
2460 osi_QRemove(&scp->fileLocks, qq);
2462 * Don't delete it here; let the daemon delete it, to simplify
2463 * the daemon's traversal of the list.
2465 lock_ObtainWrite(&cm_scacheLock);
2466 ourLock->flags |= CM_FILELOCK_FLAG_INVALID;
2467 cm_ReleaseUser(ourLock->userp);
2468 lock_ReleaseWrite(&cm_scacheLock);
2470 if (!anotherReader) {
2471 tfid.Volume = scp->fid.volume;
2472 tfid.Vnode = scp->fid.vnode;
2473 tfid.Unique = scp->fid.unique;
2474 lock_ReleaseMutex(&scp->mx);
2476 code = cm_Conn(&scp->fid, userp, reqp, &connp);
2478 code = RXAFS_ReleaseLock(connp->callp, &tfid, &volSync);
2479 } while (cm_Analyze(connp, userp, reqp, &scp->fid, &volSync,
2481 code = cm_MapRPCError(code, reqp);
2482 lock_ObtainMutex(&scp->mx);
2488 void cm_CheckLocks()
2490 osi_queue_t *q, *nq;
2491 cm_file_lock_t *fileLock;
2500 lock_ObtainWrite(&cm_scacheLock);
2501 q = cm_allFileLocks;
2503 fileLock = (cm_file_lock_t *) q;
2505 if (fileLock->flags & CM_FILELOCK_FLAG_INVALID) {
2506 osi_QRemove(&cm_allFileLocks, q);
2509 else if (!(fileLock->flags & CM_FILELOCK_FLAG_WAITING)) {
2510 tfid.Volume = fileLock->fid.volume;
2511 tfid.Vnode = fileLock->fid.vnode;
2512 tfid.Unique = fileLock->fid.unique;
2513 lock_ReleaseWrite(&cm_scacheLock);
2515 code = cm_Conn(&fileLock->fid, fileLock->userp,
2518 code = RXAFS_ExtendLock(connp->callp, &tfid,
2520 } while (cm_Analyze(connp, fileLock->userp, &req,
2521 &fileLock->fid, &volSync, NULL,
2523 code = cm_MapRPCError(code, &req);
2524 lock_ObtainWrite(&cm_scacheLock);
2528 lock_ReleaseWrite(&cm_scacheLock);
2531 long cm_RetryLock(cm_file_lock_t *oldFileLock, int vcp_is_dead)
2534 int Which = ((oldFileLock->LockType & 0x1) ? LockRead : LockWrite);
2539 cm_file_lock_t *fileLock;
2545 code = CM_ERROR_TIMEDOUT;
2551 /* Look for a conflict. Also, if we are asking for a shared lock,
2552 * look for another shared lock, so we don't have to do an RPC.
2554 code = cm_GetSCache(&oldFileLock->fid, &scp, oldFileLock->userp, &req);
2560 fileLock = (cm_file_lock_t *)
2561 ((char *) q - offsetof(cm_file_lock_t, fileq));
2562 if ((fileLock->flags &
2563 (CM_FILELOCK_FLAG_INVALID | CM_FILELOCK_FLAG_WAITING))
2565 if ((oldFileLock->LockType & 0x1) == 0
2566 || (fileLock->LockType & 0x1) == 0) {
2567 cm_ReleaseSCache(scp);
2568 return CM_ERROR_WOULDBLOCK;
2578 tfid.Volume = oldFileLock->fid.volume;
2579 tfid.Vnode = oldFileLock->fid.vnode;
2580 tfid.Unique = oldFileLock->fid.unique;
2582 code = cm_Conn(&oldFileLock->fid, oldFileLock->userp,
2585 code = RXAFS_SetLock(connp->callp, &tfid, Which,
2587 } while (cm_Analyze(connp, oldFileLock->userp, &req,
2588 &oldFileLock->fid, &volSync,
2590 code = cm_MapRPCError(code, &req);
2594 if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
2595 lock_ObtainMutex(&scp->mx);
2596 osi_QRemove(&scp->fileLocks, &oldFileLock->fileq);
2597 lock_ReleaseMutex(&scp->mx);
2599 lock_ObtainWrite(&cm_scacheLock);
2601 oldFileLock->flags = 0;
2602 else if (code != CM_ERROR_WOULDBLOCK) {
2603 oldFileLock->flags |= CM_FILELOCK_FLAG_INVALID;
2604 cm_ReleaseUser(oldFileLock->userp);
2606 lock_ReleaseWrite(&cm_scacheLock);