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, ...);
35 unsigned int cm_mountRootGen = 0;
40 * Case-folding array. This was constructed by inspecting of SMBtrace output.
41 * I do not know anything more about it.
43 unsigned char cm_foldUpper[256] = {
44 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
45 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
46 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
47 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
48 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
49 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
50 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
51 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
52 0x40, 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, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
56 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
57 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
58 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
59 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
60 0x80, 0x9a, 0x90, 0x41, 0x8e, 0x41, 0x8f, 0x80,
61 0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x8e, 0x8f,
62 0x90, 0x92, 0x92, 0x4f, 0x99, 0x4f, 0x55, 0x55,
63 0x59, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
64 0x41, 0x49, 0x4f, 0x55, 0xa5, 0xa5, 0x56, 0xa7,
65 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
66 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
67 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
68 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
69 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
70 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
71 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
72 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
73 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
74 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
75 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
79 * Case-insensitive string comparison. We used to use stricmp, but it doesn't
80 * know about 8-bit characters (e.g. 129 is lowercase u-umlaut, 154 is
81 * upper-case u-umlaut).
83 int cm_stricmp(const char *str1, const char *str2)
95 c1 = (char) cm_foldUpper[(unsigned char)(*str1++)];
96 c2 = (char) cm_foldUpper[(unsigned char)(*str2++)];
104 /* characters that are legal in an 8.3 name */
106 * We used to have 1's for all characters from 128 to 254. But
107 * the NT client behaves better if we create an 8.3 name for any
108 * name that has a character with the high bit on, and if we
109 * delete those characters from 8.3 names. In particular, see
110 * Sybase defect 10859.
112 char cm_LegalChars[256] = {
113 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
114 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
115 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0,
116 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
117 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
118 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1,
119 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
120 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1,
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,
125 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
126 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
127 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
128 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
131 /* return true iff component is a valid 8.3 name */
132 int cm_Is8Dot3(char *namep)
135 int sawUpper = 0, sawLower = 0;
140 * can't have a leading dot;
141 * special case for . and ..
143 if (namep[0] == '.') {
146 if (namep[1] == '.' && namep[2] == 0)
150 while (tc = *namep++) {
152 /* saw another dot */
153 if (sawDot) return 0; /* second dot */
158 if (cm_LegalChars[tc] == 0)
160 if (tc >= 'A' && tc <= 'Z')
162 else if (tc >= 'a' && tc <= 'z')
165 if (!sawDot && charCount > 8)
166 /* more than 8 chars in name */
168 if (sawDot && charCount > 3)
169 /* more than 3 chars in extension */
173 * Used to check that all characters were the same case.
174 * This doesn't help 16-bit apps, and meanwhile it causes the
175 * MS-DOS Command Prompt to misbehave; see Sybase defect 10709.
177 if (sawUpper && sawLower)
184 * Number unparsing map for generating 8.3 names;
187 char cm_8Dot3Mapping[41] =
188 {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
189 'B', 'C', 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S',
190 'T', 'V', 'W', 'X', 'Y', 'Z', '_', '-', '$', '#', '@', '%', '!', '&', 'E', 'O'
192 int cm_8Dot3MapSize = sizeof(cm_8Dot3Mapping);
194 void cm_Gen8Dot3Name(cm_dirEntry_t *dep, char *shortName, char **shortNameEndp)
198 int vnode = ntohl(dep->fid.vnode);
200 int validExtension = 0;
201 char tc, *temp, *name;
203 /* Unparse the file's vnode number to get a "uniquifier" */
205 number[nsize] = cm_8Dot3Mapping[vnode % cm_8Dot3MapSize];
207 vnode /= cm_8Dot3MapSize;
211 * Look for valid extension. There has to be a dot, and
212 * at least one of the characters following has to be legal.
214 lastDot = strrchr(dep->name, '.');
216 temp = lastDot; temp++;
218 if (cm_LegalChars[tc])
224 /* Copy name characters */
226 for (i = 0, name = dep->name;
227 i < (7 - nsize) && name != lastDot; ) {
232 if (!cm_LegalChars[tc])
235 *shortName++ = toupper(tc);
241 /* Copy uniquifier characters */
242 memcpy(shortName, number, nsize);
245 if (validExtension) {
246 /* Copy extension characters */
247 *shortName++ = *lastDot++; /* copy dot */
248 for (i = 0, tc = *lastDot++;
251 if (cm_LegalChars[tc]) {
253 *shortName++ = toupper(tc);
262 *shortNameEndp = shortName;
265 /* return success if we can open this file in this mode */
266 long cm_CheckOpen(cm_scache_t *scp, int openMode, int trunc, cm_user_t *userp,
273 if (openMode != 1) rights |= PRSFS_READ;
274 if (openMode == 1 || openMode == 2 || trunc) rights |= PRSFS_WRITE;
276 lock_ObtainMutex(&scp->mx);
278 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
279 CM_SCACHESYNC_GETSTATUS
280 | CM_SCACHESYNC_NEEDCALLBACK);
281 lock_ReleaseMutex(&scp->mx);
286 /* return success if we can open this file in this mode */
287 long cm_CheckNTOpen(cm_scache_t *scp, unsigned int desiredAccess,
288 unsigned int createDisp, cm_user_t *userp, cm_req_t *reqp)
293 /* Always allow delete; the RPC will tell us if it's OK */
294 if (desiredAccess == DELETE)
299 if (desiredAccess & AFS_ACCESS_READ)
300 rights |= PRSFS_READ;
302 if ((desiredAccess & AFS_ACCESS_WRITE)
304 rights |= PRSFS_WRITE;
306 lock_ObtainMutex(&scp->mx);
308 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
309 CM_SCACHESYNC_GETSTATUS
310 | CM_SCACHESYNC_NEEDCALLBACK);
311 lock_ReleaseMutex(&scp->mx);
314 * If the open will fail because the volume is readonly, then we will
315 * return an access denied error instead. This is to help brain-dead
316 * apps run correctly on replicated volumes.
317 * See defect 10007 for more information.
319 if (code == CM_ERROR_READONLY)
320 code = CM_ERROR_NOACCESS;
326 * When CAP_NT_SMBS has been negotiated, deletion (of files or directories) is
327 * done in three steps:
328 * (1) open for deletion (NT_CREATE_AND_X)
329 * (2) set for deletion on close (NTWTRANSACTION2, SET_FILE_INFO)
331 * We must not do the RPC until step 3. But if we are going to return an error
332 * code (e.g. directory not empty), we must return it by step 2, otherwise most
333 * clients will not notice it. So we do a preliminary check. For deleting
334 * files, this is almost free, since we have already done the RPC to get the
335 * parent directory's status bits. But for deleting directories, we must do an
336 * additional RPC to get the directory's data to check if it is empty. Sigh.
338 long cm_CheckNTDelete(cm_scache_t *dscp, cm_scache_t *scp, cm_user_t *userp,
345 unsigned short *hashTable;
347 int BeyondPage = 0, HaveDot = 0, HaveDotDot = 0;
349 /* First check permissions */
350 lock_ObtainMutex(&dscp->mx);
351 code = cm_SyncOp(dscp, NULL, userp, reqp, PRSFS_DELETE,
352 CM_SCACHESYNC_GETSTATUS
353 | CM_SCACHESYNC_NEEDCALLBACK);
354 lock_ReleaseMutex(&dscp->mx);
358 /* If deleting directory, must be empty */
360 if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
363 thyper.HighPart = 0; thyper.LowPart = 0;
364 lock_ObtainRead(&scp->bufCreateLock);
365 code = buf_Get(scp, &thyper, &bufferp);
366 lock_ReleaseRead(&scp->bufCreateLock);
370 lock_ObtainMutex(&bufferp->mx);
371 lock_ObtainMutex(&scp->mx);
373 code = cm_SyncOp(scp, bufferp, userp, reqp, 0,
374 CM_SCACHESYNC_NEEDCALLBACK
376 | CM_SCACHESYNC_BUFLOCKED);
380 if (cm_HaveBuffer(scp, bufferp, 1))
383 /* otherwise, load the buffer and try again */
384 lock_ReleaseMutex(&bufferp->mx);
385 code = cm_GetBuffer(scp, bufferp, NULL, userp, reqp);
386 lock_ReleaseMutex(&scp->mx);
387 lock_ObtainMutex(&bufferp->mx);
388 lock_ObtainMutex(&scp->mx);
393 /* We try to determine emptiness without looking beyond the first page,
394 * and without assuming "." and ".." are present and are on the first
395 * page (though these assumptions might, after all, be reasonable).
397 hashTable = (unsigned short *)(bufferp->datap + (32 * 5));
398 for (i=0; i<128; i++) {
399 idx = ntohs(hashTable[i]);
405 dep = (cm_dirEntry_t *)(bufferp->datap + (32 * idx));
406 if (strcmp(dep->name, ".") == 0)
408 else if (strcmp(dep->name, "..") == 0)
411 code = CM_ERROR_NOTEMPTY;
414 idx = ntohs(dep->next);
417 if (BeyondPage && HaveDot && HaveDotDot)
418 code = CM_ERROR_NOTEMPTY;
422 lock_ReleaseMutex(&bufferp->mx);
423 buf_Release(bufferp);
424 lock_ReleaseMutex(&scp->mx);
429 * Iterate through all entries in a directory.
430 * When the function funcp is called, the buffer is locked but the
431 * directory vnode is not.
433 long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
434 osi_hyper_t *startOffsetp, cm_user_t *userp, cm_req_t *reqp,
435 cm_scache_t **retscp)
442 osi_hyper_t dirLength;
443 osi_hyper_t bufferOffset;
444 osi_hyper_t curOffset;
448 cm_pageHeader_t *pageHeaderp;
450 long nextEntryCookie;
451 int numDirChunks; /* # of 32 byte dir chunks in this entry */
453 /* get the directory size */
454 lock_ObtainMutex(&scp->mx);
455 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_LOOKUP,
456 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
458 lock_ReleaseMutex(&scp->mx);
462 if (scp->fileType != CM_SCACHETYPE_DIRECTORY) {
463 lock_ReleaseMutex(&scp->mx);
464 return CM_ERROR_NOTDIR;
467 if (retscp) /* if this is a lookup call */
469 cm_lookupSearch_t* sp = parmp;
470 int casefold = sp->caseFold;
472 sp->caseFold = 0; /* we have a strong preference for exact matches */
473 if ( *retscp = cm_dnlcLookup(scp, sp)) /* dnlc hit */
475 sp->caseFold = casefold;
476 lock_ReleaseMutex(&scp->mx);
480 sp->caseFold = casefold;
484 * XXX We only get the length once. It might change when we drop the
487 dirLength = scp->length;
489 lock_ReleaseMutex(&scp->mx);
492 bufferOffset.LowPart = bufferOffset.HighPart = 0;
494 curOffset = *startOffsetp;
496 curOffset.HighPart = 0;
497 curOffset.LowPart = 0;
501 /* make sure that curOffset.LowPart doesn't point to the first
502 * 32 bytes in the 2nd through last dir page, and that it
503 * doesn't point at the first 13 32-byte chunks in the first
504 * dir page, since those are dir and page headers, and don't
505 * contain useful information.
507 temp = curOffset.LowPart & (2048-1);
508 if (curOffset.HighPart == 0 && curOffset.LowPart < 2048) {
509 /* we're in the first page */
510 if (temp < 13*32) temp = 13*32;
513 /* we're in a later dir page */
514 if (temp < 32) temp = 32;
517 /* make sure the low order 5 bits are zero */
520 /* now put temp bits back ito curOffset.LowPart */
521 curOffset.LowPart &= ~(2048-1);
522 curOffset.LowPart |= temp;
524 /* check if we've passed the dir's EOF */
525 if (LargeIntegerGreaterThanOrEqualTo(curOffset, dirLength))
528 /* see if we can use the bufferp we have now; compute in which
529 * page the current offset would be, and check whether that's
530 * the offset of the buffer we have. If not, get the buffer.
532 thyper.HighPart = curOffset.HighPart;
533 thyper.LowPart = curOffset.LowPart & ~(buf_bufferSize-1);
534 if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
537 lock_ReleaseMutex(&bufferp->mx);
538 buf_Release(bufferp);
542 lock_ObtainRead(&scp->bufCreateLock);
543 code = buf_Get(scp, &thyper, &bufferp);
544 lock_ReleaseRead(&scp->bufCreateLock);
546 lock_ObtainMutex(&bufferp->mx);
548 bufferOffset = thyper;
550 /* now get the data in the cache */
552 lock_ObtainMutex(&scp->mx);
553 code = cm_SyncOp(scp, bufferp, userp, reqp,
555 CM_SCACHESYNC_NEEDCALLBACK
557 | CM_SCACHESYNC_BUFLOCKED);
559 lock_ReleaseMutex(&scp->mx);
563 if (cm_HaveBuffer(scp, bufferp, 1)) {
564 lock_ReleaseMutex(&scp->mx);
568 /* otherwise, load the buffer and try again */
569 lock_ReleaseMutex(&bufferp->mx);
570 code = cm_GetBuffer(scp, bufferp, NULL, userp,
572 lock_ReleaseMutex(&scp->mx);
573 lock_ObtainMutex(&bufferp->mx);
577 lock_ReleaseMutex(&bufferp->mx);
578 buf_Release(bufferp);
582 } /* if (wrong buffer) ... */
584 /* now we have the buffer containing the entry we're interested
585 * in; copy it out if it represents a non-deleted entry.
587 entryInDir = curOffset.LowPart & (2048-1);
588 entryInBuffer = curOffset.LowPart & (buf_bufferSize - 1);
590 /* page header will help tell us which entries are free. Page
591 * header can change more often than once per buffer, since
592 * AFS 3 dir page size may be less than (but not more than) a
593 * buffer package buffer.
595 /* only look intra-buffer */
596 temp = curOffset.LowPart & (buf_bufferSize - 1);
597 temp &= ~(2048 - 1); /* turn off intra-page bits */
598 pageHeaderp = (cm_pageHeader_t *) (bufferp->datap + temp);
600 /* now determine which entry we're looking at in the page. If
601 * it is free (there's a free bitmap at the start of the dir),
602 * we should skip these 32 bytes.
604 slotInPage = (entryInDir & 0x7e0) >> 5;
605 if (!(pageHeaderp->freeBitmap[slotInPage>>3]
606 & (1 << (slotInPage & 0x7)))) {
607 /* this entry is free */
608 numDirChunks = 1; /* only skip this guy */
612 tp = bufferp->datap + entryInBuffer;
613 dep = (cm_dirEntry_t *) tp; /* now points to AFS3 dir entry */
615 /* while we're here, compute the next entry's location, too,
616 * since we'll need it when writing out the cookie into the
617 * dir listing stream.
619 numDirChunks = cm_NameEntries(dep->name, NULL);
621 /* compute the offset of the cookie representing the next entry */
622 nextEntryCookie = curOffset.LowPart
623 + (CM_DIR_CHUNKSIZE * numDirChunks);
625 if (dep->fid.vnode != 0) {
626 /* this is one of the entries to use: it is not deleted */
627 code = (*funcp)(scp, dep, parmp, &curOffset);
629 } /* if we're including this name */
632 /* and adjust curOffset to be where the new cookie is */
634 thyper.LowPart = CM_DIR_CHUNKSIZE * numDirChunks;
635 curOffset = LargeIntegerAdd(thyper, curOffset);
636 } /* while copying data for dir listing */
638 /* release the mutex */
640 lock_ReleaseMutex(&bufferp->mx);
641 buf_Release(bufferp);
646 int cm_NoneUpper(char *s)
650 if (c >= 'A' && c <= 'Z')
655 int cm_NoneLower(char *s)
659 if (c >= 'a' && c <= 'z')
664 long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
667 cm_lookupSearch_t *sp;
672 sp = (cm_lookupSearch_t *) rockp;
674 matchName = dep->name;
676 match = cm_stricmp(matchName, sp->searchNamep);
678 match = strcmp(matchName, sp->searchNamep);
682 && !cm_Is8Dot3(dep->name)) {
683 matchName = shortName;
684 cm_Gen8Dot3Name(dep, shortName, NULL);
686 match = cm_stricmp(matchName, sp->searchNamep);
688 match = strcmp(matchName, sp->searchNamep);
695 if(!sp->caseFold) sp->ExactFound = 1;
697 if (!sp->caseFold || matchName == shortName) {
698 sp->fid.vnode = ntohl(dep->fid.vnode);
699 sp->fid.unique = ntohl(dep->fid.unique);
700 return CM_ERROR_STOPNOW;
704 * If we get here, we are doing a case-insensitive search, and we
705 * have found a match. Now we determine what kind of match it is:
706 * exact, lower-case, upper-case, or none of the above. This is done
707 * in order to choose among matches, if there are more than one.
710 /* Exact matches are the best. */
711 match = strcmp(matchName, sp->searchNamep);
714 sp->fid.vnode = ntohl(dep->fid.vnode);
715 sp->fid.unique = ntohl(dep->fid.unique);
716 return CM_ERROR_STOPNOW;
719 /* Lower-case matches are next. */
722 if (cm_NoneUpper(matchName)) {
727 /* Upper-case matches are next. */
730 if (cm_NoneLower(matchName)) {
735 /* General matches are last. */
741 sp->fid.vnode = ntohl(dep->fid.vnode);
742 sp->fid.unique = ntohl(dep->fid.unique);
746 /* read the contents of a mount point into the appropriate string.
747 * called with locked scp, and returns with locked scp.
749 long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
756 if (scp->mountPointStringp) return 0;
758 /* otherwise, we have to read it in */
759 lock_ReleaseMutex(&scp->mx);
761 lock_ObtainRead(&scp->bufCreateLock);
762 thyper.LowPart = thyper.HighPart = 0;
763 code = buf_Get(scp, &thyper, &bufp);
764 lock_ReleaseRead(&scp->bufCreateLock);
766 lock_ObtainMutex(&scp->mx);
771 code = cm_SyncOp(scp, bufp, userp, reqp, 0,
772 CM_SCACHESYNC_READ | CM_SCACHESYNC_NEEDCALLBACK);
777 if (cm_HaveBuffer(scp, bufp, 0)) break;
779 /* otherwise load buffer */
780 code = cm_GetBuffer(scp, bufp, NULL, userp, reqp);
785 /* locked, has callback, has valid data in buffer */
786 if ((tlen = scp->length.LowPart) > 1000) return CM_ERROR_TOOBIG;
788 code = CM_ERROR_INVAL;
792 /* someone else did the work while we were out */
793 if (scp->mountPointStringp) {
798 /* otherwise, copy out the link */
799 scp->mountPointStringp = malloc(tlen);
800 memcpy(scp->mountPointStringp, bufp->datap, tlen);
802 /* now make it null-terminated. Note that the original contents of a
803 * link that is a mount point is "#volname." where "." is there just to
804 * be turned into a null. That is, we can trash the last char of the
805 * link without damaging the vol name. This is a stupid convention,
806 * but that's the protocol.
808 scp->mountPointStringp[tlen-1] = 0;
812 if (bufp) buf_Release(bufp);
816 /* called with a locked scp and chases the mount point, yielding outScpp.
817 * scp remains locked, just for simplicity of describing the interface.
819 long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
820 cm_req_t *reqp, cm_scache_t **outScpp)
835 if (scp->mountRootFidp && scp->mountRootGen >= cm_mountRootGen) {
836 tfid = *scp->mountRootFidp;
837 lock_ReleaseMutex(&scp->mx);
838 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
839 lock_ObtainMutex(&scp->mx);
843 /* parse the volume name */
844 mpNamep = scp->mountPointStringp;
846 tlen = strlen(scp->mountPointStringp);
847 mtType = *scp->mountPointStringp;
848 cellNamep = malloc(tlen);
849 volNamep = malloc(tlen);
851 cp = strrchr(mpNamep, ':');
853 /* cellular mount point */
854 memset(cellNamep, 0, tlen);
855 strncpy(cellNamep, mpNamep+1, cp - mpNamep - 1);
856 strcpy(volNamep, cp+1);
857 /* now look up the cell */
858 cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE);
862 strcpy(volNamep, mpNamep+1);
864 cellp = cm_FindCellByID(scp->fid.cell);
868 code = CM_ERROR_NOSUCHCELL;
872 vnLength = strlen(volNamep);
873 if (vnLength >= 8 && strcmp(volNamep + vnLength - 7, ".backup") == 0)
875 else if (vnLength >= 10
876 && strcmp(volNamep + vnLength - 9, ".readonly") == 0)
881 /* check for backups within backups */
883 && (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
884 == CM_SCACHEFLAG_RO) {
885 code = CM_ERROR_NOSUCHVOLUME;
889 /* now we need to get the volume */
890 lock_ReleaseMutex(&scp->mx);
891 code = cm_GetVolumeByName(cellp, volNamep, userp, reqp, 0, &volp);
892 lock_ObtainMutex(&scp->mx);
895 /* save the parent of the volume root for this is the
896 * place where the volume is mounted and we must remember
897 * this in the volume structure rather than just in the
898 * scache entry lest the scache entry gets recycled
901 lock_ObtainMutex(&volp->mx);
902 if(volp->dotdotFidp == (cm_fid_t *) NULL)
903 volp->dotdotFidp = (cm_fid_t *) malloc(sizeof(cm_fid_t));
904 *(volp->dotdotFidp) = dscp->fid;
905 lock_ReleaseMutex(&volp->mx);
907 if (scp->mountRootFidp == 0) {
908 scp->mountRootFidp = malloc(sizeof(cm_fid_t));
910 scp->mountRootFidp->cell = cellp->cellID;
911 /* if the mt pt is in a read-only volume (not just a
912 * backup), and if there is a read-only volume for the
913 * target, and if this is a type '#' mount point, use
914 * the read-only, otherwise use the one specified.
916 if (mtType == '#' && (scp->flags & CM_SCACHEFLAG_PURERO)
917 && volp->roID != 0 && type == RWVOL)
920 scp->mountRootFidp->volume = volp->roID;
921 else if (type == BACKVOL)
922 scp->mountRootFidp->volume = volp->bkID;
924 scp->mountRootFidp->volume = volp->rwID;
926 /* the rest of the fid is a magic number */
927 scp->mountRootFidp->vnode = 1;
928 scp->mountRootFidp->unique = 1;
929 scp->mountRootGen = cm_mountRootGen;
931 tfid = *scp->mountRootFidp;
932 lock_ReleaseMutex(&scp->mx);
933 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
934 lock_ObtainMutex(&scp->mx);
943 int cm_ExpandSysName(char *inp, char *outp, long outSize)
948 tp = strrchr(inp, '@');
949 if (tp == NULL) return 0; /* no @sys */
951 if (strcmp(tp, "@sys") != 0) return 0; /* no @sys */
953 /* caller just wants to know if this is a valid @sys type of name */
954 if (outp == NULL) return 1;
956 /* otherwise generate the properly expanded @sys name */
957 prefixCount = tp - inp;
959 strncpy(outp, inp, prefixCount); /* copy out "a." from "a.@sys" */
960 outp[prefixCount] = 0; /* null terminate the "a." */
961 strcat(outp, cm_sysName); /* append i386_nt40 */
965 long cm_Lookup(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
966 cm_req_t *reqp, cm_scache_t **outpScpp)
969 int dnlcHit = 1; /* did we hit in the dnlc? yes, we did */
970 cm_scache_t *tscp = NULL;
971 cm_scache_t *mountedScp;
972 cm_lookupSearch_t rock;
976 if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
977 && strcmp(namep, "..") == 0) {
978 if (dscp->dotdotFidp == (cm_fid_t *)NULL
979 || dscp->dotdotFidp->volume == 0)
980 return CM_ERROR_NOSUCHVOLUME;
981 rock.fid = *dscp->dotdotFidp;
985 if (cm_ExpandSysName(namep, tname, sizeof(tname))) {
988 memset(&rock, 0, sizeof(rock));
989 rock.fid.cell = dscp->fid.cell;
990 rock.fid.volume = dscp->fid.volume;
991 rock.searchNamep = namep;
992 rock.caseFold = (flags & CM_FLAG_CASEFOLD);
993 rock.hasTilde = ((strchr(namep, '~') != NULL) ? 1 : 0);
995 /* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
996 code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
997 (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
999 /* code == 0 means we fell off the end of the dir, while stopnow means
1000 * that we stopped early, probably because we found the entry we're
1001 * looking for. Any other non-zero code is an error.
1003 if (code && code != CM_ERROR_STOPNOW)
1006 getroot = (dscp==cm_rootSCachep) ;
1008 if (!cm_freelanceEnabled || !getroot) {
1009 if (flags & CM_FLAG_CHECKPATH)
1010 return CM_ERROR_NOSUCHPATH;
1012 return CM_ERROR_NOSUCHFILE;
1014 else { /* nonexistent dir on freelance root, so add it */
1015 osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %s", namep);
1016 code = cm_FreelanceAddMount(namep, namep, "root.cell.",
1018 if (code < 0) { /* add mount point failed, so give up */
1019 if (flags & CM_FLAG_CHECKPATH)
1020 return CM_ERROR_NOSUCHPATH;
1022 return CM_ERROR_NOSUCHFILE;
1024 tscp = NULL; /* to force call of cm_GetSCache */
1029 if ( !tscp ) /* we did not find it in the dnlc */
1032 code = cm_GetSCache(&rock.fid, &tscp, userp, reqp);
1036 /* tscp is now held */
1038 lock_ObtainMutex(&tscp->mx);
1039 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1040 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1042 lock_ReleaseMutex(&tscp->mx);
1043 cm_ReleaseSCache(tscp);
1046 /* tscp is now locked */
1048 if (!(flags & CM_FLAG_NOMOUNTCHASE)
1049 && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
1050 /* mount points are funny: they have a volume name to mount
1053 code = cm_ReadMountPoint(tscp, userp, reqp);
1055 code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
1057 lock_ReleaseMutex(&tscp->mx);
1058 cm_ReleaseSCache(tscp);
1065 lock_ReleaseMutex(&tscp->mx);
1068 /* copy back pointer */
1071 /* insert scache in dnlc */
1072 if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
1073 /* lock the directory entry to prevent racing callback revokes */
1074 lock_ObtainMutex(&dscp->mx);
1075 if ( dscp->cbServerp && dscp->cbExpires )
1076 cm_dnlcEnter(dscp, namep, tscp);
1077 lock_ReleaseMutex(&dscp->mx);
1084 long cm_Unlink(cm_scache_t *dscp, char *namep, cm_user_t *userp, cm_req_t *reqp)
1090 AFSFetchStatus newDirStatus;
1093 #ifdef AFS_FREELANCE_CLIENT
1094 if (cm_freelanceEnabled && dscp == cm_rootSCachep) {
1095 /* deleting a mount point from the root dir. */
1096 code = cm_FreelanceRemoveMount(namep);
1101 /* make sure we don't screw up the dir status during the merge */
1102 lock_ObtainMutex(&dscp->mx);
1103 sflags = CM_SCACHESYNC_STOREDATA;
1104 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1105 lock_ReleaseMutex(&dscp->mx);
1106 if (code) return code;
1109 afsFid.Volume = dscp->fid.volume;
1110 afsFid.Vnode = dscp->fid.vnode;
1111 afsFid.Unique = dscp->fid.unique;
1113 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1116 code = RXAFS_RemoveFile(connp->callp, &afsFid, namep,
1117 &newDirStatus, &volSync);
1119 } while (cm_Analyze(connp, userp, reqp,
1120 &dscp->fid, &volSync, NULL, code));
1121 code = cm_MapRPCError(code, reqp);
1123 lock_ObtainMutex(&dscp->mx);
1124 cm_dnlcRemove(dscp, namep);
1125 cm_SyncOpDone(dscp, NULL, sflags);
1126 if (code == 0) cm_MergeStatus(dscp, &newDirStatus, &volSync, userp, 0);
1127 lock_ReleaseMutex(&dscp->mx);
1132 /* called with a locked vnode, and fills in the link info.
1133 * returns this the vnode still locked.
1135 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1142 lock_AssertMutex(&linkScp->mx);
1143 if (!linkScp->mountPointStringp) {
1144 /* read the link data */
1145 lock_ReleaseMutex(&linkScp->mx);
1146 thyper.LowPart = thyper.HighPart = 0;
1147 code = buf_Get(linkScp, &thyper, &bufp);
1148 lock_ObtainMutex(&linkScp->mx);
1149 if (code) return code;
1151 code = cm_SyncOp(linkScp, bufp, userp, reqp, 0,
1152 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1157 if (cm_HaveBuffer(linkScp, bufp, 0)) break;
1159 code = cm_GetBuffer(linkScp, bufp, NULL, userp, reqp);
1164 } /* while loop to get the data */
1166 /* now if we still have no link read in,
1167 * copy the data from the buffer */
1168 if ((temp = linkScp->length.LowPart) >= 1024) {
1170 return CM_ERROR_TOOBIG;
1173 /* otherwise, it fits; make sure it is still null (could have
1174 * lost race with someone else referencing this link above),
1175 * and if so, copy in the data.
1177 if (linkScp->mountPointStringp == NULL) {
1178 linkScp->mountPointStringp = malloc(temp+1);
1179 strncpy(linkScp->mountPointStringp, bufp->datap, temp);
1180 linkScp->mountPointStringp[temp] = 0; /* null terminate */
1183 } /* don't have sym link contents cached */
1188 /* called with a held vnode and a path suffix, with the held vnode being a
1189 * symbolic link. Our goal is to generate a new path to interpret, and return
1190 * this new path in newSpaceBufferp. If the new vnode is relative to a dir
1191 * other than the directory containing the symbolic link, then the new root is
1192 * returned in *newRootScpp, otherwise a null is returned there.
1194 long cm_AssembleLink(cm_scache_t *linkScp, char *pathSuffixp,
1195 cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1196 cm_user_t *userp, cm_req_t *reqp)
1202 lock_ObtainMutex(&linkScp->mx);
1203 code = cm_HandleLink(linkScp, userp, reqp);
1204 if (code) goto done;
1206 /* if we may overflow the buffer, bail out; buffer is signficantly
1207 * bigger than max path length, so we don't really have to worry about
1208 * being a little conservative here.
1210 if (strlen(linkScp->mountPointStringp) + strlen(pathSuffixp) + 2
1211 >= CM_UTILS_SPACESIZE)
1212 return CM_ERROR_TOOBIG;
1214 tsp = cm_GetSpace();
1215 linkp = linkScp->mountPointStringp;
1216 if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1217 if (strlen(linkp) > cm_mountRootLen)
1218 strcpy(tsp->data, linkp+cm_mountRootLen+1);
1221 *newRootScpp = cm_rootSCachep;
1222 cm_HoldSCache(cm_rootSCachep);
1223 } else if (*linkp == '\\' || *linkp == '/') {
1224 /* formerly, this was considered to be from the AFS root,
1225 but this seems to create problems. instead, we will just
1228 strcpy(tsp->data, linkp+1);
1229 *newRootScpp = cm_rootSCachep;
1230 cm_HoldSCache(cm_rootSCachep);
1232 code = CM_ERROR_NOSUCHPATH;
1237 /* a relative link */
1238 strcpy(tsp->data, linkp);
1239 *newRootScpp = NULL;
1241 if (pathSuffixp[0] != 0) { /* if suffix string is non-null */
1242 strcat(tsp->data, "\\");
1243 strcat(tsp->data, pathSuffixp);
1245 *newSpaceBufferp = tsp;
1249 lock_ReleaseMutex(&linkScp->mx);
1253 long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
1254 cm_user_t *userp, char *tidPathp, cm_req_t *reqp, cm_scache_t **outScpp)
1257 char *tp; /* ptr moving through input buffer */
1258 char tc; /* temp char */
1259 int haveComponent; /* has new component started? */
1260 char component[256]; /* this is the new component */
1261 char *cp; /* component name being assembled */
1262 cm_scache_t *tscp; /* current location in the hierarchy */
1263 cm_scache_t *nscp; /* next dude down */
1264 cm_scache_t *dirScp; /* last dir we searched */
1265 cm_scache_t *linkScp; /* new root for the symlink we just
1267 cm_space_t *psp; /* space for current path, if we've hit
1269 cm_space_t *tempsp; /* temp vbl */
1270 char *restp; /* rest of the pathname to interpret */
1271 int symlinkCount; /* count of # of symlinks traversed */
1272 int extraFlag; /* avoid chasing mt pts for dir cmd */
1273 int phase = 1; /* 1 = tidPathp, 2 = pathp */
1283 cm_HoldSCache(tscp);
1288 /* map Unix slashes into DOS ones so we can interpret Unix
1291 if (tc == '/') tc = '\\';
1293 if (!haveComponent) {
1294 if (tc == '\\') continue;
1311 /* we have a component here */
1312 if (tc == 0 || tc == '\\') {
1313 /* end of the component; we're at the last
1314 * component if tc == 0. However, if the last
1315 * is a symlink, we have more to do.
1317 *cp++ = 0; /* add null termination */
1319 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
1320 extraFlag = CM_FLAG_NOMOUNTCHASE;
1321 code = cm_Lookup(tscp, component,
1323 userp, reqp, &nscp);
1326 cm_ReleaseSCache(tscp);
1327 if (psp) cm_FreeSpace(psp);
1330 haveComponent = 0; /* component done */
1331 dirScp = tscp; /* for some symlinks */
1332 tscp = nscp; /* already held */
1333 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
1335 cm_ReleaseSCache(dirScp);
1339 /* now, if tscp is a symlink, we should follow
1340 * it and assemble the path again.
1342 lock_ObtainMutex(&tscp->mx);
1343 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1344 CM_SCACHESYNC_GETSTATUS
1345 | CM_SCACHESYNC_NEEDCALLBACK);
1347 lock_ReleaseMutex(&tscp->mx);
1348 cm_ReleaseSCache(tscp);
1349 cm_ReleaseSCache(dirScp);
1352 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
1353 /* this is a symlink; assemble a new buffer */
1354 lock_ReleaseMutex(&tscp->mx);
1355 if (symlinkCount++ >= 16) {
1356 cm_ReleaseSCache(tscp);
1357 cm_ReleaseSCache(dirScp);
1358 if (psp) cm_FreeSpace(psp);
1359 return CM_ERROR_TOOBIG;
1361 if (tc == 0) restp = "";
1363 code = cm_AssembleLink(tscp, restp, &linkScp, &tempsp, userp, reqp);
1365 /* something went wrong */
1366 cm_ReleaseSCache(tscp);
1367 cm_ReleaseSCache(dirScp);
1371 /* otherwise, tempsp has the new path,
1372 * and linkScp is the new root from
1373 * which to interpret that path.
1374 * Continue with the namei processing,
1375 * also doing the bookkeeping for the
1376 * space allocation and tracking the
1377 * vnode reference counts.
1379 if (psp) cm_FreeSpace(psp);
1382 cm_ReleaseSCache(tscp);
1383 tscp = linkScp; /* already held
1384 * by AssembleLink */
1385 /* now, if linkScp is null, that's
1386 * AssembleLink's way of telling us that
1387 * the sym link is relative to the dir
1388 * containing the link. We have a ref
1389 * to it in dirScp, and we hold it now
1390 * and reuse it as the new spot in the
1394 cm_HoldSCache(dirScp);
1397 } /* if we have a sym link */
1399 /* not a symlink, we may be done */
1400 lock_ReleaseMutex(&tscp->mx);
1407 cm_ReleaseSCache(dirScp);
1412 cm_ReleaseSCache(dirScp);
1413 } /* end of a component */
1415 } /* we have a component */
1416 } /* big while loop over all components */
1419 if (psp) cm_FreeSpace(psp);
1420 if (code == 0) *outScpp = tscp;
1424 /* called with a dir, and a vnode within the dir that happens to be a symlink.
1425 * We chase the link, and return a held pointer to the target, if it exists,
1426 * in *outScpp. If we succeed, we return 0, otherwise we return an error code
1427 * and do not hold or return a target vnode.
1429 * This is very similar to calling cm_NameI with the last component of a name,
1430 * which happens to be a symlink, except that we've already passed by the name.
1432 * This function is typically called by the directory listing functions, which
1433 * encounter symlinks but need to return the proper file length so programs
1434 * like "more" work properly when they make use of the attributes retrieved from
1437 * The input vnode should not be locked when this function is called.
1439 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
1440 cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
1444 cm_scache_t *newRootScp;
1446 osi_Log1(afsd_logp, "Evaluating symlink vp %x", linkScp);
1448 code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
1449 if (code) return code;
1451 /* now, if newRootScp is NULL, we're really being told that the symlink
1452 * is relative to the current directory (dscp).
1454 if (newRootScp == NULL) {
1456 cm_HoldSCache(dscp);
1459 code = cm_NameI(newRootScp, spacep->data,
1460 CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
1461 userp, NULL, reqp, outScpp);
1463 /* this stuff is allocated no matter what happened on the namei call,
1465 cm_FreeSpace(spacep);
1466 cm_ReleaseSCache(newRootScp);
1471 /* make this big enough so that one buffer of dir pages won't overflow. We'll
1472 * check anyway, but we want to minimize the chance that we have to leave stuff
1475 #define CM_BULKMAX 128
1477 /* rock for bulk stat calls */
1478 typedef struct cm_bulkStat {
1479 osi_hyper_t bufOffset; /* only do it for things in this buffer page */
1481 /* info for the actual call */
1482 int counter; /* next free slot */
1483 AFSFid fids[CM_BULKMAX];
1484 AFSFetchStatus stats[CM_BULKMAX];
1485 AFSCallBack callbacks[CM_BULKMAX];
1488 /* for a given entry, make sure that it isn't in the stat cache, and then
1489 * add it to the list of file IDs to be obtained.
1491 * Don't bother adding it if we already have a vnode. Note that the dir
1492 * is locked, so we have to be careful checking the vnode we're thinking of
1493 * processing, to avoid deadlocks.
1495 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
1506 /* Don't overflow bsp. */
1507 if (bsp->counter >= CM_BULKMAX)
1508 return CM_ERROR_STOPNOW;
1510 thyper.LowPart = buf_bufferSize;
1511 thyper.HighPart = 0;
1512 thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
1514 /* thyper is now the first byte past the end of the record we're
1515 * interested in, and bsp->bufOffset is the first byte of the record
1516 * we're interested in.
1517 * Skip data in the others.
1520 if (LargeIntegerLessThan(*offp, bsp->bufOffset))
1522 if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
1523 return CM_ERROR_STOPNOW;
1524 if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
1527 tfid.cell = scp->fid.cell;
1528 tfid.volume = scp->fid.volume;
1529 tfid.vnode = ntohl(dep->fid.vnode);
1530 tfid.unique = ntohl(dep->fid.unique);
1531 tscp = cm_FindSCache(&tfid);
1533 if (lock_TryMutex(&tscp->mx)) {
1534 /* we have an entry that we can look at */
1535 if (cm_HaveCallback(tscp)) {
1536 /* we have a callback on it. Don't bother
1537 * fetching this stat entry, since we're happy
1538 * with the info we have.
1540 lock_ReleaseMutex(&tscp->mx);
1541 cm_ReleaseSCache(tscp);
1544 lock_ReleaseMutex(&tscp->mx);
1546 cm_ReleaseSCache(tscp);
1549 #ifdef AFS_FREELANCE_CLIENT
1550 // yj: if this is a mountpoint under root.afs then we don't want it
1551 // to be bulkstat-ed, instead, we call getSCache directly and under
1552 // getSCache, it is handled specially.
1553 if (cm_freelanceEnabled &&
1554 tfid.cell==0x1 && tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
1555 !(tfid.vnode==0x1 && tfid.unique==0x1) )
1557 osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
1558 return cm_GetSCache(&tfid, &tscp, NULL, NULL);
1560 #endif /* AFS_FREELANCE_CLIENT */
1563 bsp->fids[i].Volume = scp->fid.volume;
1564 bsp->fids[i].Vnode = tfid.vnode;
1565 bsp->fids[i].Unique = tfid.unique;
1569 /* called with a locked scp and a pointer to a buffer. Make bulk stat
1570 * calls on all undeleted files in the page of the directory specified.
1572 void cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
1576 cm_bulkStat_t bb; /* this is *BIG*, probably 12K or so;
1577 * watch for stack problems */
1578 AFSCBFids fidStruct;
1579 AFSBulkStats statStruct;
1581 AFSCBs callbackStruct;
1584 cm_callbackRequest_t cbReq;
1591 osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%x", (long) dscp);
1593 /* should be on a buffer boundary */
1594 osi_assert((offsetp->LowPart & (buf_bufferSize - 1)) == 0);
1597 bb.bufOffset = *offsetp;
1599 /* first, assemble the file IDs we need to stat */
1600 code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) &bb, offsetp, userp,
1603 /* if we failed, bail out early */
1604 if (code && code != CM_ERROR_STOPNOW) return;
1606 /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
1607 * make the calls to create the entries. Handle AFSCBMAX files at a
1611 while(filex < bb.counter) {
1612 filesThisCall = bb.counter - filex;
1613 if (filesThisCall > AFSCBMAX) filesThisCall = AFSCBMAX;
1615 fidStruct.AFSCBFids_len = filesThisCall;
1616 fidStruct.AFSCBFids_val = &bb.fids[filex];
1617 statStruct.AFSBulkStats_len = filesThisCall;
1618 statStruct.AFSBulkStats_val = &bb.stats[filex];
1619 callbackStruct.AFSCBs_len = filesThisCall;
1620 callbackStruct.AFSCBs_val = &bb.callbacks[filex];
1621 cm_StartCallbackGrantingCall(NULL, &cbReq);
1622 osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
1624 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1627 code = RXAFS_BulkStatus(connp->callp, &fidStruct,
1628 &statStruct, &callbackStruct, &volSync);
1630 } while (cm_Analyze(connp, userp, reqp, &dscp->fid,
1631 &volSync, &cbReq, code));
1632 code = cm_MapRPCError(code, reqp);
1634 osi_Log0(afsd_logp, "CALL BulkStatus DONE");
1636 /* may as well quit on an error, since we're not going to do
1637 * much better on the next immediate call, either.
1641 /* otherwise, we should do the merges */
1642 for(i = 0; i<filesThisCall; i++) {
1644 tfid.cell = dscp->fid.cell;
1645 tfid.volume = bb.fids[j].Volume;
1646 tfid.vnode = bb.fids[j].Vnode;
1647 tfid.unique = bb.fids[j].Unique;
1648 code = cm_GetSCache(&tfid, &scp, userp, reqp);
1649 if (code != 0) continue;
1651 /* otherwise, if this entry has no callback info,
1654 lock_ObtainMutex(&scp->mx);
1655 /* now, we have to be extra paranoid on merging in this
1656 * information, since we didn't use cm_SyncOp before
1657 * starting the fetch to make sure that no bad races
1658 * were occurring. Specifically, we need to make sure
1659 * we don't obliterate any newer information in the
1660 * vnode than have here.
1662 * Right now, be pretty conservative: if there's a
1663 * callback or a pending call, skip it.
1665 if (scp->cbServerp == NULL
1667 (CM_SCACHEFLAG_FETCHING
1668 | CM_SCACHEFLAG_STORING
1669 | CM_SCACHEFLAG_SIZESTORING))) {
1670 cm_EndCallbackGrantingCall(scp, &cbReq,
1672 CM_CALLBACK_MAINTAINCOUNT);
1673 cm_MergeStatus(scp, &bb.stats[j], &volSync,
1676 lock_ReleaseMutex(&scp->mx);
1677 cm_ReleaseSCache(scp);
1678 } /* all files in the response */
1679 /* now tell it to drop the count,
1680 * after doing the vnode processing above */
1681 cm_EndCallbackGrantingCall(NULL, NULL, NULL, 0);
1683 filex += filesThisCall;
1684 } /* while there are still more files to process */
1685 osi_Log0(afsd_logp, "END cm_TryBulkStat");
1688 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
1692 /* initialize store back mask as inexpensive local variable */
1694 memset(statusp, 0, sizeof(AFSStoreStatus));
1696 /* copy out queued info from scache first, if scp passed in */
1698 if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
1699 statusp->ClientModTime = scp->clientModTime;
1700 mask |= AFS_SETMODTIME;
1701 scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
1706 /* now add in our locally generated request */
1707 if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
1708 statusp->ClientModTime = attrp->clientModTime;
1709 mask |= AFS_SETMODTIME;
1711 if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
1712 statusp->UnixModeBits = attrp->unixModeBits;
1713 mask |= AFS_SETMODE;
1715 if (attrp->mask & CM_ATTRMASK_OWNER) {
1716 statusp->Owner = attrp->owner;
1717 mask |= AFS_SETOWNER;
1719 if (attrp->mask & CM_ATTRMASK_GROUP) {
1720 statusp->Group = attrp->group;
1721 mask |= AFS_SETGROUP;
1724 statusp->Mask = mask;
1727 /* set the file size, and make sure that all relevant buffers have been
1728 * truncated. Ensure that any partially truncated buffers have been zeroed
1729 * to the end of the buffer.
1731 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
1737 /* start by locking out buffer creation */
1738 lock_ObtainWrite(&scp->bufCreateLock);
1740 /* verify that this is a file, not a dir or a symlink */
1741 lock_ObtainMutex(&scp->mx);
1742 code = cm_SyncOp(scp, NULL, userp, reqp, 0,
1743 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1744 if (code) goto done;
1746 if (scp->fileType != CM_SCACHETYPE_FILE) {
1747 code = CM_ERROR_ISDIR;
1752 if (LargeIntegerLessThan(*sizep, scp->length))
1757 lock_ReleaseMutex(&scp->mx);
1759 /* can't hold scp->mx lock here, since we may wait for a storeback to
1760 * finish if the buffer package is cleaning a buffer by storing it to
1764 buf_Truncate(scp, userp, reqp, sizep);
1766 /* now ensure that file length is short enough, and update truncPos */
1767 lock_ObtainMutex(&scp->mx);
1769 /* make sure we have a callback (so we have the right value for the
1770 * length), and wait for it to be safe to do a truncate.
1772 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
1773 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
1774 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
1775 if (code) goto done;
1777 if (LargeIntegerLessThan(*sizep, scp->length)) {
1778 /* a real truncation. If truncPos is not set yet, or is bigger
1779 * than where we're truncating the file, set truncPos to this
1784 if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
1785 || LargeIntegerLessThan(*sizep, scp->length)) {
1787 scp->truncPos = *sizep;
1788 scp->mask |= CM_SCACHEMASK_TRUNCPOS;
1790 /* in either case, the new file size has been changed */
1791 scp->length = *sizep;
1792 scp->mask |= CM_SCACHEMASK_LENGTH;
1794 else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
1795 /* really extending the file */
1796 scp->length = *sizep;
1797 scp->mask |= CM_SCACHEMASK_LENGTH;
1800 /* done successfully */
1804 lock_ReleaseMutex(&scp->mx);
1805 lock_ReleaseWrite(&scp->bufCreateLock);
1810 /* set the file size or other attributes (but not both at once) */
1811 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
1816 AFSFetchStatus afsOutStatus;
1820 AFSStoreStatus afsInStatus;
1822 /* handle file length setting */
1823 if (attrp->mask & CM_ATTRMASK_LENGTH)
1824 return cm_SetLength(scp, &attrp->length, userp, reqp);
1826 flags = CM_SCACHESYNC_STORESTATUS;
1828 lock_ObtainMutex(&scp->mx);
1829 /* otherwise, we have to make an RPC to get the status */
1830 code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
1832 /* make the attr structure */
1833 cm_StatusFromAttr(&afsInStatus, scp, attrp);
1835 lock_ReleaseMutex(&scp->mx);
1836 if (code) return code;
1838 /* now make the RPC */
1839 osi_Log1(afsd_logp, "CALL StoreStatus vp %x", (long) scp);
1840 tfid.Volume = scp->fid.volume;
1841 tfid.Vnode = scp->fid.vnode;
1842 tfid.Unique = scp->fid.unique;
1844 code = cm_Conn(&scp->fid, userp, reqp, &connp);
1847 code = RXAFS_StoreStatus(connp->callp, &tfid,
1848 &afsInStatus, &afsOutStatus, &volSync);
1850 } while (cm_Analyze(connp, userp, reqp,
1851 &scp->fid, &volSync, NULL, code));
1852 code = cm_MapRPCError(code, reqp);
1854 osi_Log1(afsd_logp, "CALL StoreStatus DONE, code %d", code);
1856 lock_ObtainMutex(&scp->mx);
1857 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
1859 cm_MergeStatus(scp, &afsOutStatus, &volSync, userp,
1860 CM_MERGEFLAG_FORCE);
1862 /* if we're changing the mode bits, discard the ACL cache,
1863 * since we changed the mode bits.
1865 if (afsInStatus.Mask & AFS_SETMODE) cm_FreeAllACLEnts(scp);
1866 lock_ReleaseMutex(&scp->mx);
1870 long cm_Create(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
1871 cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
1876 cm_callbackRequest_t cbReq;
1881 AFSStoreStatus inStatus;
1882 AFSFetchStatus updatedDirStatus;
1883 AFSFetchStatus newFileStatus;
1884 AFSCallBack newFileCallback;
1887 /* can't create names with @sys in them; must expand it manually first.
1888 * return "invalid request" if they try.
1890 if (cm_ExpandSysName(namep, NULL, 0)) {
1891 return CM_ERROR_ATSYS;
1894 /* before starting the RPC, mark that we're changing the file data, so
1895 * that someone who does a chmod will know to wait until our call
1898 lock_ObtainMutex(&dscp->mx);
1899 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
1901 cm_StartCallbackGrantingCall(NULL, &cbReq);
1903 lock_ReleaseMutex(&dscp->mx);
1909 cm_StatusFromAttr(&inStatus, NULL, attrp);
1911 /* try the RPC now */
1913 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1916 dirAFSFid.Volume = dscp->fid.volume;
1917 dirAFSFid.Vnode = dscp->fid.vnode;
1918 dirAFSFid.Unique = dscp->fid.unique;
1919 code = RXAFS_CreateFile(connp->callp, &dirAFSFid, namep,
1920 &inStatus, &newAFSFid, &newFileStatus,
1921 &updatedDirStatus, &newFileCallback,
1923 } while (cm_Analyze(connp, userp, reqp,
1924 &dscp->fid, &volSync, &cbReq, code));
1925 code = cm_MapRPCError(code, reqp);
1927 lock_ObtainMutex(&dscp->mx);
1928 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
1930 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
1932 lock_ReleaseMutex(&dscp->mx);
1934 /* now try to create the file's entry, too, but be careful to
1935 * make sure that we don't merge in old info. Since we weren't locking
1936 * out any requests during the file's creation, we may have pretty old
1940 newFid.cell = dscp->fid.cell;
1941 newFid.volume = dscp->fid.volume;
1942 newFid.vnode = newAFSFid.Vnode;
1943 newFid.unique = newAFSFid.Unique;
1944 code = cm_GetSCache(&newFid, &scp, userp, reqp);
1946 lock_ObtainMutex(&scp->mx);
1947 if (!cm_HaveCallback(scp)) {
1948 cm_MergeStatus(scp, &newFileStatus, &volSync,
1950 cm_EndCallbackGrantingCall(scp, &cbReq,
1951 &newFileCallback, 0);
1954 lock_ReleaseMutex(&scp->mx);
1959 /* make sure we end things properly */
1961 cm_EndCallbackGrantingCall(NULL, NULL, NULL, 0);
1966 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
1970 lock_ObtainWrite(&scp->bufCreateLock);
1971 code = buf_CleanVnode(scp, userp, reqp);
1972 lock_ReleaseWrite(&scp->bufCreateLock);
1974 lock_ObtainMutex(&scp->mx);
1975 scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA
1976 | CM_SCACHEFLAG_OUTOFSPACE);
1977 if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
1978 | CM_SCACHEMASK_CLIENTMODTIME
1979 | CM_SCACHEMASK_LENGTH))
1980 code = cm_StoreMini(scp, userp, reqp);
1981 lock_ReleaseMutex(&scp->mx);
1986 long cm_MakeDir(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
1987 cm_user_t *userp, cm_req_t *reqp)
1992 cm_callbackRequest_t cbReq;
1997 AFSStoreStatus inStatus;
1998 AFSFetchStatus updatedDirStatus;
1999 AFSFetchStatus newDirStatus;
2000 AFSCallBack newDirCallback;
2003 /* can't create names with @sys in them; must expand it manually first.
2004 * return "invalid request" if they try.
2006 if (cm_ExpandSysName(namep, NULL, 0)) {
2007 return CM_ERROR_ATSYS;
2010 /* before starting the RPC, mark that we're changing the directory
2011 * data, so that someone who does a chmod on the dir will wait until
2012 * our call completes.
2014 lock_ObtainMutex(&dscp->mx);
2015 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2017 cm_StartCallbackGrantingCall(NULL, &cbReq);
2019 lock_ReleaseMutex(&dscp->mx);
2025 cm_StatusFromAttr(&inStatus, NULL, attrp);
2027 /* try the RPC now */
2029 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2032 dirAFSFid.Volume = dscp->fid.volume;
2033 dirAFSFid.Vnode = dscp->fid.vnode;
2034 dirAFSFid.Unique = dscp->fid.unique;
2035 code = RXAFS_MakeDir(connp->callp, &dirAFSFid, namep,
2036 &inStatus, &newAFSFid, &newDirStatus,
2037 &updatedDirStatus, &newDirCallback,
2039 } while (cm_Analyze(connp, userp, reqp,
2040 &dscp->fid, &volSync, &cbReq, code));
2041 code = cm_MapRPCError(code, reqp);
2043 lock_ObtainMutex(&dscp->mx);
2044 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2046 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2048 lock_ReleaseMutex(&dscp->mx);
2050 /* now try to create the new dir's entry, too, but be careful to
2051 * make sure that we don't merge in old info. Since we weren't locking
2052 * out any requests during the file's creation, we may have pretty old
2056 newFid.cell = dscp->fid.cell;
2057 newFid.volume = dscp->fid.volume;
2058 newFid.vnode = newAFSFid.Vnode;
2059 newFid.unique = newAFSFid.Unique;
2060 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2062 lock_ObtainMutex(&scp->mx);
2063 if (!cm_HaveCallback(scp)) {
2064 cm_MergeStatus(scp, &newDirStatus, &volSync,
2066 cm_EndCallbackGrantingCall(scp, &cbReq,
2067 &newDirCallback, 0);
2070 lock_ReleaseMutex(&scp->mx);
2071 cm_ReleaseSCache(scp);
2075 /* make sure we end things properly */
2077 cm_EndCallbackGrantingCall(NULL, NULL, NULL, 0);
2079 /* and return error code */
2083 long cm_SymLink(cm_scache_t *dscp, char *namep, char *contentsp, long flags,
2084 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
2092 AFSStoreStatus inStatus;
2093 AFSFetchStatus updatedDirStatus;
2094 AFSFetchStatus newLinkStatus;
2097 /* before starting the RPC, mark that we're changing the directory data,
2098 * so that someone who does a chmod on the dir will wait until our
2101 lock_ObtainMutex(&dscp->mx);
2102 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2103 lock_ReleaseMutex(&dscp->mx);
2108 cm_StatusFromAttr(&inStatus, NULL, attrp);
2110 /* try the RPC now */
2112 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2115 dirAFSFid.Volume = dscp->fid.volume;
2116 dirAFSFid.Vnode = dscp->fid.vnode;
2117 dirAFSFid.Unique = dscp->fid.unique;
2118 code = RXAFS_Symlink(connp->callp, &dirAFSFid, namep, contentsp,
2119 &inStatus, &newAFSFid, &newLinkStatus,
2120 &updatedDirStatus, &volSync);
2121 } while (cm_Analyze(connp, userp, reqp,
2122 &dscp->fid, &volSync, NULL, code));
2123 code = cm_MapRPCError(code, reqp);
2125 lock_ObtainMutex(&dscp->mx);
2126 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2128 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2130 lock_ReleaseMutex(&dscp->mx);
2132 /* now try to create the new dir's entry, too, but be careful to
2133 * make sure that we don't merge in old info. Since we weren't locking
2134 * out any requests during the file's creation, we may have pretty old
2138 newFid.cell = dscp->fid.cell;
2139 newFid.volume = dscp->fid.volume;
2140 newFid.vnode = newAFSFid.Vnode;
2141 newFid.unique = newAFSFid.Unique;
2142 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2144 lock_ObtainMutex(&scp->mx);
2145 if (!cm_HaveCallback(scp)) {
2146 cm_MergeStatus(scp, &newLinkStatus, &volSync,
2149 lock_ReleaseMutex(&scp->mx);
2150 cm_ReleaseSCache(scp);
2154 /* and return error code */
2158 long cm_RemoveDir(cm_scache_t *dscp, char *namep, cm_user_t *userp,
2165 AFSFetchStatus updatedDirStatus;
2168 /* before starting the RPC, mark that we're changing the directory data,
2169 * so that someone who does a chmod on the dir will wait until our
2172 lock_ObtainMutex(&dscp->mx);
2173 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2174 lock_ReleaseMutex(&dscp->mx);
2180 /* try the RPC now */
2182 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2185 dirAFSFid.Volume = dscp->fid.volume;
2186 dirAFSFid.Vnode = dscp->fid.vnode;
2187 dirAFSFid.Unique = dscp->fid.unique;
2188 code = RXAFS_RemoveDir(connp->callp, &dirAFSFid, namep,
2189 &updatedDirStatus, &volSync);
2190 } while (cm_Analyze(connp, userp, reqp,
2191 &dscp->fid, &volSync, NULL, code));
2192 code = cm_MapRPCErrorRmdir(code, reqp);
2194 lock_ObtainMutex(&dscp->mx);
2195 cm_dnlcRemove(dscp, namep);
2196 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2198 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2200 lock_ReleaseMutex(&dscp->mx);
2202 /* and return error code */
2206 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
2208 /* grab mutex on contents */
2209 lock_ObtainMutex(&scp->mx);
2211 /* reset the prefetch info */
2212 scp->prefetch.base.LowPart = 0; /* base */
2213 scp->prefetch.base.HighPart = 0;
2214 scp->prefetch.end.LowPart = 0; /* and end */
2215 scp->prefetch.end.HighPart = 0;
2217 /* release mutex on contents */
2218 lock_ReleaseMutex(&scp->mx);
2224 long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
2225 char *newNamep, cm_user_t *userp, cm_req_t *reqp)
2229 AFSFid oldDirAFSFid;
2230 AFSFid newDirAFSFid;
2232 AFSFetchStatus updatedOldDirStatus;
2233 AFSFetchStatus updatedNewDirStatus;
2237 /* before starting the RPC, mark that we're changing the directory data,
2238 * so that someone who does a chmod on the dir will wait until our call
2239 * completes. We do this in vnode order so that we don't deadlock,
2240 * which makes the code a little verbose.
2242 if (oldDscp == newDscp) {
2243 /* check for identical names */
2244 if (strcmp(oldNamep, newNamep) == 0)
2245 return CM_ERROR_RENAME_IDENTICAL;
2248 lock_ObtainMutex(&oldDscp->mx);
2249 cm_dnlcRemove(oldDscp, oldNamep);
2250 cm_dnlcRemove(oldDscp, newNamep);
2251 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2252 CM_SCACHESYNC_STOREDATA);
2253 lock_ReleaseMutex(&oldDscp->mx);
2256 /* two distinct dir vnodes */
2258 if (oldDscp->fid.cell != newDscp->fid.cell ||
2259 oldDscp->fid.volume != newDscp->fid.volume)
2260 return CM_ERROR_CROSSDEVLINK;
2262 /* shouldn't happen that we have distinct vnodes for two
2263 * different files, but could due to deliberate attack, or
2264 * stale info. Avoid deadlocks and quit now.
2266 if (oldDscp->fid.vnode == newDscp->fid.vnode)
2267 return CM_ERROR_CROSSDEVLINK;
2269 if (oldDscp->fid.vnode < newDscp->fid.vnode) {
2270 lock_ObtainMutex(&oldDscp->mx);
2271 cm_dnlcRemove(oldDscp, oldNamep);
2272 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2273 CM_SCACHESYNC_STOREDATA);
2274 lock_ReleaseMutex(&oldDscp->mx);
2276 lock_ObtainMutex(&newDscp->mx);
2277 cm_dnlcRemove(newDscp, newNamep);
2278 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2279 CM_SCACHESYNC_STOREDATA);
2280 lock_ReleaseMutex(&newDscp->mx);
2282 /* cleanup first one */
2283 cm_SyncOpDone(oldDscp, NULL,
2284 CM_SCACHESYNC_STOREDATA);
2289 /* lock the new vnode entry first */
2290 lock_ObtainMutex(&newDscp->mx);
2291 cm_dnlcRemove(newDscp, newNamep);
2292 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2293 CM_SCACHESYNC_STOREDATA);
2294 lock_ReleaseMutex(&newDscp->mx);
2296 lock_ObtainMutex(&oldDscp->mx);
2297 cm_dnlcRemove(oldDscp, oldNamep);
2298 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2299 CM_SCACHESYNC_STOREDATA);
2300 lock_ReleaseMutex(&oldDscp->mx);
2302 /* cleanup first one */
2303 cm_SyncOpDone(newDscp, NULL,
2304 CM_SCACHESYNC_STOREDATA);
2308 } /* two distinct vnodes */
2315 /* try the RPC now */
2317 code = cm_Conn(&oldDscp->fid, userp, reqp, &connp);
2320 oldDirAFSFid.Volume = oldDscp->fid.volume;
2321 oldDirAFSFid.Vnode = oldDscp->fid.vnode;
2322 oldDirAFSFid.Unique = oldDscp->fid.unique;
2323 newDirAFSFid.Volume = newDscp->fid.volume;
2324 newDirAFSFid.Vnode = newDscp->fid.vnode;
2325 newDirAFSFid.Unique = newDscp->fid.unique;
2326 code = RXAFS_Rename(connp->callp, &oldDirAFSFid, oldNamep,
2327 &newDirAFSFid, newNamep,
2328 &updatedOldDirStatus, &updatedNewDirStatus,
2330 } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
2331 &volSync, NULL, code));
2332 code = cm_MapRPCError(code, reqp);
2334 /* update the individual stat cache entries for the directories */
2335 lock_ObtainMutex(&oldDscp->mx);
2336 cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
2338 cm_MergeStatus(oldDscp, &updatedOldDirStatus, &volSync,
2341 lock_ReleaseMutex(&oldDscp->mx);
2343 /* and update it for the new one, too, if necessary */
2345 lock_ObtainMutex(&newDscp->mx);
2346 cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
2348 cm_MergeStatus(newDscp, &updatedNewDirStatus, &volSync,
2351 lock_ReleaseMutex(&newDscp->mx);
2354 /* and return error code */
2358 long cm_Lock(cm_scache_t *scp, unsigned char LockType,
2359 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
2360 u_long Timeout, cm_user_t *userp, cm_req_t *reqp,
2364 int Which = ((LockType & 0x1) ? LockRead : LockWrite);
2368 cm_file_lock_t *fileLock;
2372 /* Look for a conflict. Also, if we are asking for a shared lock,
2373 * look for another shared lock, so we don't have to do an RPC.
2377 fileLock = (cm_file_lock_t *)
2378 ((char *) q - offsetof(cm_file_lock_t, fileq));
2379 if ((fileLock->flags &
2380 (CM_FILELOCK_FLAG_INVALID | CM_FILELOCK_FLAG_WAITING))
2382 if ((LockType & 0x1) == 0
2383 || (fileLock->LockType & 0x1) == 0)
2384 return CM_ERROR_WOULDBLOCK;
2393 tfid.Volume = scp->fid.volume;
2394 tfid.Vnode = scp->fid.vnode;
2395 tfid.Unique = scp->fid.unique;
2396 lock_ReleaseMutex(&scp->mx);
2398 code = cm_Conn(&scp->fid, userp, reqp, &connp);
2400 code = RXAFS_SetLock(connp->callp, &tfid, Which,
2402 } while (cm_Analyze(connp, userp, reqp, &scp->fid, &volSync,
2404 lock_ObtainMutex(&scp->mx);
2405 code = cm_MapRPCError(code, reqp);
2408 if (code == 0 || Timeout != 0) {
2409 fileLock = malloc(sizeof(cm_file_lock_t));
2410 fileLock->LockType = LockType;
2412 fileLock->userp = userp;
2413 fileLock->fid = scp->fid;
2414 fileLock->LOffset = LOffset;
2415 fileLock->LLength = LLength;
2416 fileLock->flags = (code == 0 ? 0 : CM_FILELOCK_FLAG_WAITING);
2417 osi_QAdd(&scp->fileLocks, &fileLock->fileq);
2418 lock_ObtainWrite(&cm_scacheLock);
2419 osi_QAdd(&cm_allFileLocks, &fileLock->q);
2420 lock_ReleaseWrite(&cm_scacheLock);
2421 if (code != 0) *lockpp = fileLock;
2426 long cm_Unlock(cm_scache_t *scp, unsigned char LockType,
2427 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
2428 cm_user_t *userp, cm_req_t *reqp)
2431 int Which = ((LockType & 0x1) ? LockRead : LockWrite);
2435 cm_file_lock_t *fileLock, *ourLock;
2436 osi_queue_t *q, *qq;
2437 int anotherReader = 0;
2441 if (LargeIntegerLessThan(LLength, scp->length))
2444 /* Look for our own lock on the list, so as to remove it.
2445 * Also, determine if we're the last reader; if not, avoid an RPC.
2449 fileLock = (cm_file_lock_t *)
2450 ((char *) q - offsetof(cm_file_lock_t, fileq));
2452 && fileLock->userp == userp
2453 && LargeIntegerEqualTo(fileLock->LOffset, LOffset)
2454 && LargeIntegerEqualTo(fileLock->LLength, LLength)) {
2459 else if (fileLock->LockType & 0x1)
2464 /* ignore byte ranges */
2465 if (smallLock && !found)
2468 /* don't try to unlock other people's locks */
2470 return CM_ERROR_WOULDBLOCK;
2472 /* discard lock record */
2473 osi_QRemove(&scp->fileLocks, qq);
2475 * Don't delete it here; let the daemon delete it, to simplify
2476 * the daemon's traversal of the list.
2478 lock_ObtainWrite(&cm_scacheLock);
2479 ourLock->flags |= CM_FILELOCK_FLAG_INVALID;
2480 cm_ReleaseUser(ourLock->userp);
2481 lock_ReleaseWrite(&cm_scacheLock);
2483 if (!anotherReader) {
2484 tfid.Volume = scp->fid.volume;
2485 tfid.Vnode = scp->fid.vnode;
2486 tfid.Unique = scp->fid.unique;
2487 lock_ReleaseMutex(&scp->mx);
2489 code = cm_Conn(&scp->fid, userp, reqp, &connp);
2492 code = RXAFS_ReleaseLock(connp->callp, &tfid, &volSync);
2493 } while (cm_Analyze(connp, userp, reqp, &scp->fid, &volSync,
2495 code = cm_MapRPCError(code, reqp);
2496 lock_ObtainMutex(&scp->mx);
2502 void cm_CheckLocks()
2504 osi_queue_t *q, *nq;
2505 cm_file_lock_t *fileLock;
2514 lock_ObtainWrite(&cm_scacheLock);
2515 q = cm_allFileLocks;
2517 fileLock = (cm_file_lock_t *) q;
2519 if (fileLock->flags & CM_FILELOCK_FLAG_INVALID) {
2520 osi_QRemove(&cm_allFileLocks, q);
2523 else if (!(fileLock->flags & CM_FILELOCK_FLAG_WAITING)) {
2524 tfid.Volume = fileLock->fid.volume;
2525 tfid.Vnode = fileLock->fid.vnode;
2526 tfid.Unique = fileLock->fid.unique;
2527 lock_ReleaseWrite(&cm_scacheLock);
2529 code = cm_Conn(&fileLock->fid, fileLock->userp,
2532 code = RXAFS_ExtendLock(connp->callp, &tfid,
2534 } while (cm_Analyze(connp, fileLock->userp, &req,
2535 &fileLock->fid, &volSync, NULL,
2537 code = cm_MapRPCError(code, &req);
2538 lock_ObtainWrite(&cm_scacheLock);
2542 lock_ReleaseWrite(&cm_scacheLock);
2545 long cm_RetryLock(cm_file_lock_t *oldFileLock, int vcp_is_dead)
2548 int Which = ((oldFileLock->LockType & 0x1) ? LockRead : LockWrite);
2553 cm_file_lock_t *fileLock;
2559 code = CM_ERROR_TIMEDOUT;
2565 /* Look for a conflict. Also, if we are asking for a shared lock,
2566 * look for another shared lock, so we don't have to do an RPC.
2568 code = cm_GetSCache(&oldFileLock->fid, &scp, oldFileLock->userp, &req);
2574 fileLock = (cm_file_lock_t *)
2575 ((char *) q - offsetof(cm_file_lock_t, fileq));
2576 if ((fileLock->flags &
2577 (CM_FILELOCK_FLAG_INVALID | CM_FILELOCK_FLAG_WAITING))
2579 if ((oldFileLock->LockType & 0x1) == 0
2580 || (fileLock->LockType & 0x1) == 0) {
2581 cm_ReleaseSCache(scp);
2582 return CM_ERROR_WOULDBLOCK;
2592 tfid.Volume = oldFileLock->fid.volume;
2593 tfid.Vnode = oldFileLock->fid.vnode;
2594 tfid.Unique = oldFileLock->fid.unique;
2596 code = cm_Conn(&oldFileLock->fid, oldFileLock->userp,
2599 code = RXAFS_SetLock(connp->callp, &tfid, Which,
2601 } while (cm_Analyze(connp, oldFileLock->userp, &req,
2602 &oldFileLock->fid, &volSync,
2604 code = cm_MapRPCError(code, &req);
2608 if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
2609 lock_ObtainMutex(&scp->mx);
2610 osi_QRemove(&scp->fileLocks, &oldFileLock->fileq);
2611 lock_ReleaseMutex(&scp->mx);
2613 lock_ObtainWrite(&cm_scacheLock);
2615 oldFileLock->flags = 0;
2616 else if (code != CM_ERROR_WOULDBLOCK) {
2617 oldFileLock->flags |= CM_FILELOCK_FLAG_INVALID;
2618 cm_ReleaseUser(oldFileLock->userp);
2619 oldFileLock->userp = NULL;
2621 lock_ReleaseWrite(&cm_scacheLock);