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 if ( *retscp = cm_dnlcLookup(scp, sp)) /* dnlc hit */
472 lock_ReleaseMutex(&scp->mx);
478 * XXX We only get the length once. It might change when we drop the
481 dirLength = scp->length;
483 lock_ReleaseMutex(&scp->mx);
486 bufferOffset.LowPart = bufferOffset.HighPart = 0;
488 curOffset = *startOffsetp;
490 curOffset.HighPart = 0;
491 curOffset.LowPart = 0;
495 /* make sure that curOffset.LowPart doesn't point to the first
496 * 32 bytes in the 2nd through last dir page, and that it
497 * doesn't point at the first 13 32-byte chunks in the first
498 * dir page, since those are dir and page headers, and don't
499 * contain useful information.
501 temp = curOffset.LowPart & (2048-1);
502 if (curOffset.HighPart == 0 && curOffset.LowPart < 2048) {
503 /* we're in the first page */
504 if (temp < 13*32) temp = 13*32;
507 /* we're in a later dir page */
508 if (temp < 32) temp = 32;
511 /* make sure the low order 5 bits are zero */
514 /* now put temp bits back ito curOffset.LowPart */
515 curOffset.LowPart &= ~(2048-1);
516 curOffset.LowPart |= temp;
518 /* check if we've passed the dir's EOF */
519 if (LargeIntegerGreaterThanOrEqualTo(curOffset, dirLength))
522 /* see if we can use the bufferp we have now; compute in which
523 * page the current offset would be, and check whether that's
524 * the offset of the buffer we have. If not, get the buffer.
526 thyper.HighPart = curOffset.HighPart;
527 thyper.LowPart = curOffset.LowPart & ~(buf_bufferSize-1);
528 if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
531 lock_ReleaseMutex(&bufferp->mx);
532 buf_Release(bufferp);
536 lock_ObtainRead(&scp->bufCreateLock);
537 code = buf_Get(scp, &thyper, &bufferp);
538 lock_ReleaseRead(&scp->bufCreateLock);
540 lock_ObtainMutex(&bufferp->mx);
542 bufferOffset = thyper;
544 /* now get the data in the cache */
546 lock_ObtainMutex(&scp->mx);
547 code = cm_SyncOp(scp, bufferp, userp, reqp,
549 CM_SCACHESYNC_NEEDCALLBACK
551 | CM_SCACHESYNC_BUFLOCKED);
553 lock_ReleaseMutex(&scp->mx);
557 if (cm_HaveBuffer(scp, bufferp, 1)) {
558 lock_ReleaseMutex(&scp->mx);
562 /* otherwise, load the buffer and try again */
563 lock_ReleaseMutex(&bufferp->mx);
564 code = cm_GetBuffer(scp, bufferp, NULL, userp,
566 lock_ReleaseMutex(&scp->mx);
567 lock_ObtainMutex(&bufferp->mx);
571 lock_ReleaseMutex(&bufferp->mx);
572 buf_Release(bufferp);
576 } /* if (wrong buffer) ... */
578 /* now we have the buffer containing the entry we're interested
579 * in; copy it out if it represents a non-deleted entry.
581 entryInDir = curOffset.LowPart & (2048-1);
582 entryInBuffer = curOffset.LowPart & (buf_bufferSize - 1);
584 /* page header will help tell us which entries are free. Page
585 * header can change more often than once per buffer, since
586 * AFS 3 dir page size may be less than (but not more than) a
587 * buffer package buffer.
589 /* only look intra-buffer */
590 temp = curOffset.LowPart & (buf_bufferSize - 1);
591 temp &= ~(2048 - 1); /* turn off intra-page bits */
592 pageHeaderp = (cm_pageHeader_t *) (bufferp->datap + temp);
594 /* now determine which entry we're looking at in the page. If
595 * it is free (there's a free bitmap at the start of the dir),
596 * we should skip these 32 bytes.
598 slotInPage = (entryInDir & 0x7e0) >> 5;
599 if (!(pageHeaderp->freeBitmap[slotInPage>>3]
600 & (1 << (slotInPage & 0x7)))) {
601 /* this entry is free */
602 numDirChunks = 1; /* only skip this guy */
606 tp = bufferp->datap + entryInBuffer;
607 dep = (cm_dirEntry_t *) tp; /* now points to AFS3 dir entry */
609 /* while we're here, compute the next entry's location, too,
610 * since we'll need it when writing out the cookie into the
611 * dir listing stream.
613 numDirChunks = cm_NameEntries(dep->name, NULL);
615 /* compute the offset of the cookie representing the next entry */
616 nextEntryCookie = curOffset.LowPart
617 + (CM_DIR_CHUNKSIZE * numDirChunks);
619 if (dep->fid.vnode != 0) {
620 /* this is one of the entries to use: it is not deleted */
621 code = (*funcp)(scp, dep, parmp, &curOffset);
623 } /* if we're including this name */
626 /* and adjust curOffset to be where the new cookie is */
628 thyper.LowPart = CM_DIR_CHUNKSIZE * numDirChunks;
629 curOffset = LargeIntegerAdd(thyper, curOffset);
630 } /* while copying data for dir listing */
632 /* release the mutex */
634 lock_ReleaseMutex(&bufferp->mx);
635 buf_Release(bufferp);
640 int cm_NoneUpper(char *s)
644 if (c >= 'A' && c <= 'Z')
649 int cm_NoneLower(char *s)
653 if (c >= 'a' && c <= 'z')
658 long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
661 cm_lookupSearch_t *sp;
668 matchName = dep->name;
670 match = cm_stricmp(matchName, sp->searchNamep);
672 match = strcmp(matchName, sp->searchNamep);
676 && !cm_Is8Dot3(dep->name)) {
677 matchName = shortName;
678 cm_Gen8Dot3Name(dep, shortName, NULL);
680 match = cm_stricmp(matchName, sp->searchNamep);
682 match = strcmp(matchName, sp->searchNamep);
690 if (!sp->caseFold || matchName == shortName) {
691 sp->fid.vnode = ntohl(dep->fid.vnode);
692 sp->fid.unique = ntohl(dep->fid.unique);
693 return CM_ERROR_STOPNOW;
697 * If we get here, we are doing a case-insensitive search, and we
698 * have found a match. Now we determine what kind of match it is:
699 * exact, lower-case, upper-case, or none of the above. This is done
700 * in order to choose among matches, if there are more than one.
703 /* Exact matches are the best. */
704 match = strcmp(matchName, sp->searchNamep);
706 sp->fid.vnode = ntohl(dep->fid.vnode);
707 sp->fid.unique = ntohl(dep->fid.unique);
708 return CM_ERROR_STOPNOW;
711 /* Lower-case matches are next. */
714 if (cm_NoneUpper(matchName)) {
719 /* Upper-case matches are next. */
722 if (cm_NoneLower(matchName)) {
727 /* General matches are last. */
733 sp->fid.vnode = ntohl(dep->fid.vnode);
734 sp->fid.unique = ntohl(dep->fid.unique);
738 /* read the contents of a mount point into the appropriate string.
739 * called with locked scp, and returns with locked scp.
741 long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
748 if (scp->mountPointStringp) return 0;
750 /* otherwise, we have to read it in */
751 lock_ReleaseMutex(&scp->mx);
753 lock_ObtainRead(&scp->bufCreateLock);
754 thyper.LowPart = thyper.HighPart = 0;
755 code = buf_Get(scp, &thyper, &bufp);
756 lock_ReleaseRead(&scp->bufCreateLock);
758 lock_ObtainMutex(&scp->mx);
763 code = cm_SyncOp(scp, bufp, userp, reqp, 0,
764 CM_SCACHESYNC_READ | CM_SCACHESYNC_NEEDCALLBACK);
769 if (cm_HaveBuffer(scp, bufp, 0)) break;
771 /* otherwise load buffer */
772 code = cm_GetBuffer(scp, bufp, NULL, userp, reqp);
777 /* locked, has callback, has valid data in buffer */
778 if ((tlen = scp->length.LowPart) > 1000) return CM_ERROR_TOOBIG;
780 code = CM_ERROR_INVAL;
784 /* someone else did the work while we were out */
785 if (scp->mountPointStringp) {
790 /* otherwise, copy out the link */
791 scp->mountPointStringp = malloc(tlen);
792 memcpy(scp->mountPointStringp, bufp->datap, tlen);
794 /* now make it null-terminated. Note that the original contents of a
795 * link that is a mount point is "#volname." where "." is there just to
796 * be turned into a null. That is, we can trash the last char of the
797 * link without damaging the vol name. This is a stupid convention,
798 * but that's the protocol.
800 scp->mountPointStringp[tlen-1] = 0;
804 if (bufp) buf_Release(bufp);
808 /* called with a locked scp and chases the mount point, yielding outScpp.
809 * scp remains locked, just for simplicity of describing the interface.
811 long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
812 cm_req_t *reqp, cm_scache_t **outScpp)
827 if (scp->mountRootFidp && scp->mountRootGen >= cm_mountRootGen) {
828 tfid = *scp->mountRootFidp;
829 lock_ReleaseMutex(&scp->mx);
830 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
831 lock_ObtainMutex(&scp->mx);
835 /* parse the volume name */
836 mpNamep = scp->mountPointStringp;
838 tlen = strlen(scp->mountPointStringp);
839 mtType = *scp->mountPointStringp;
840 cellNamep = malloc(tlen);
841 volNamep = malloc(tlen);
843 cp = strrchr(mpNamep, ':');
845 /* cellular mount point */
846 memset(cellNamep, 0, tlen);
847 strncpy(cellNamep, mpNamep+1, cp - mpNamep - 1);
848 strcpy(volNamep, cp+1);
849 /* now look up the cell */
850 cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE);
854 strcpy(volNamep, mpNamep+1);
856 cellp = cm_FindCellByID(scp->fid.cell);
860 code = CM_ERROR_NOSUCHCELL;
864 vnLength = strlen(volNamep);
865 if (vnLength >= 8 && strcmp(volNamep + vnLength - 7, ".backup") == 0)
867 else if (vnLength >= 10
868 && strcmp(volNamep + vnLength - 9, ".readonly") == 0)
873 /* check for backups within backups */
875 && (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
876 == CM_SCACHEFLAG_RO) {
877 code = CM_ERROR_NOSUCHVOLUME;
881 /* now we need to get the volume */
882 lock_ReleaseMutex(&scp->mx);
883 code = cm_GetVolumeByName(cellp, volNamep, userp, reqp, 0, &volp);
884 lock_ObtainMutex(&scp->mx);
887 /* save the parent of the volume root for this is the
888 * place where the volume is mounted and we must remember
889 * this in the volume structure rather than just in the
890 * scache entry lest the scache entry gets recycled
893 lock_ObtainMutex(&volp->mx);
894 if(volp->dotdotFidp == (cm_fid_t *) NULL)
895 volp->dotdotFidp = (cm_fid_t *) malloc(sizeof(cm_fid_t));
896 *(volp->dotdotFidp) = dscp->fid;
897 lock_ReleaseMutex(&volp->mx);
899 if (scp->mountRootFidp == 0) {
900 scp->mountRootFidp = malloc(sizeof(cm_fid_t));
902 scp->mountRootFidp->cell = cellp->cellID;
903 /* if the mt pt is in a read-only volume (not just a
904 * backup), and if there is a read-only volume for the
905 * target, and if this is a type '#' mount point, use
906 * the read-only, otherwise use the one specified.
908 if (mtType == '#' && (scp->flags & CM_SCACHEFLAG_PURERO)
909 && volp->roID != 0 && type == RWVOL)
912 scp->mountRootFidp->volume = volp->roID;
913 else if (type == BACKVOL)
914 scp->mountRootFidp->volume = volp->bkID;
916 scp->mountRootFidp->volume = volp->rwID;
918 /* the rest of the fid is a magic number */
919 scp->mountRootFidp->vnode = 1;
920 scp->mountRootFidp->unique = 1;
921 scp->mountRootGen = cm_mountRootGen;
923 tfid = *scp->mountRootFidp;
924 lock_ReleaseMutex(&scp->mx);
925 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
926 lock_ObtainMutex(&scp->mx);
935 int cm_ExpandSysName(char *inp, char *outp, long outSize)
940 tp = strrchr(inp, '@');
941 if (tp == NULL) return 0; /* no @sys */
943 if (strcmp(tp, "@sys") != 0) return 0; /* no @sys */
945 /* caller just wants to know if this is a valid @sys type of name */
946 if (outp == NULL) return 1;
948 /* otherwise generate the properly expanded @sys name */
949 prefixCount = tp - inp;
951 strncpy(outp, inp, prefixCount); /* copy out "a." from "a.@sys" */
952 outp[prefixCount] = 0; /* null terminate the "a." */
953 strcat(outp, cm_sysName); /* append i386_nt40 */
957 long cm_Lookup(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
958 cm_req_t *reqp, cm_scache_t **outpScpp)
961 int dnlcHit = 1; /* did we hit in the dnlc? yes, we did */
962 cm_scache_t *tscp = NULL;
963 cm_scache_t *mountedScp;
964 cm_lookupSearch_t rock;
968 if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
969 && strcmp(namep, "..") == 0) {
970 if (dscp->dotdotFidp == (cm_fid_t *)NULL
971 || dscp->dotdotFidp->volume == 0)
972 return CM_ERROR_NOSUCHVOLUME;
973 rock.fid = *dscp->dotdotFidp;
977 if (cm_ExpandSysName(namep, tname, sizeof(tname))) {
980 memset(&rock, 0, sizeof(rock));
981 rock.fid.cell = dscp->fid.cell;
982 rock.fid.volume = dscp->fid.volume;
983 rock.searchNamep = namep;
984 rock.caseFold = (flags & CM_FLAG_CASEFOLD);
985 rock.hasTilde = ((strchr(namep, '~') != NULL) ? 1 : 0);
987 /* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
988 code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
989 (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
991 /* code == 0 means we fell off the end of the dir, while stopnow means
992 * that we stopped early, probably because we found the entry we're
993 * looking for. Any other non-zero code is an error.
995 if (code && code != CM_ERROR_STOPNOW) return code;
997 getroot = (dscp==cm_rootSCachep) ;
999 if (!cm_freelanceEnabled || !getroot) {
1000 if (flags & CM_FLAG_CHECKPATH)
1001 return CM_ERROR_NOSUCHPATH;
1003 return CM_ERROR_NOSUCHFILE;
1005 else { /* nonexistent dir on freelance root, so add it */
1006 code = cm_FreelanceAddMount(namep, namep, "root.cell.",
1008 if (code < 0) { /* add mount point failed, so give up */
1009 if (flags & CM_FLAG_CHECKPATH)
1010 return CM_ERROR_NOSUCHPATH;
1012 return CM_ERROR_NOSUCHFILE;
1014 tscp = NULL; /* to force call of cm_GetSCache */
1019 if ( !tscp ) /* we did not find it in the dnlc */
1022 code = cm_GetSCache(&rock.fid, &tscp, userp, reqp);
1023 if (code) return code;
1025 /* tscp is now held */
1027 lock_ObtainMutex(&tscp->mx);
1028 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1029 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1031 lock_ReleaseMutex(&tscp->mx);
1032 cm_ReleaseSCache(tscp);
1035 /* tscp is now locked */
1037 if (!(flags & CM_FLAG_NOMOUNTCHASE)
1038 && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
1039 /* mount points are funny: they have a volume name to mount
1042 code = cm_ReadMountPoint(tscp, userp, reqp);
1044 code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
1046 lock_ReleaseMutex(&tscp->mx);
1047 cm_ReleaseSCache(tscp);
1054 lock_ReleaseMutex(&tscp->mx);
1057 /* copy back pointer */
1060 /* insert scache in dnlc */
1061 if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) ) {
1062 /* lock the directory entry to prevent racing callback revokes */
1063 lock_ObtainMutex(&dscp->mx);
1064 if ( dscp->cbServerp && dscp->cbExpires )
1065 cm_dnlcEnter(dscp, namep, tscp);
1066 lock_ReleaseMutex(&dscp->mx);
1073 long cm_Unlink(cm_scache_t *dscp, char *namep, cm_user_t *userp, cm_req_t *reqp)
1079 AFSFetchStatus newDirStatus;
1082 #ifdef AFS_FREELANCE_CLIENT
1083 if (cm_freelanceEnabled && dscp == cm_rootSCachep) {
1084 /* deleting a mount point from the root dir. */
1085 code = cm_FreelanceRemoveMount(namep);
1090 /* make sure we don't screw up the dir status during the merge */
1091 lock_ObtainMutex(&dscp->mx);
1092 sflags = CM_SCACHESYNC_STOREDATA;
1093 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1094 lock_ReleaseMutex(&dscp->mx);
1095 if (code) return code;
1098 afsFid.Volume = dscp->fid.volume;
1099 afsFid.Vnode = dscp->fid.vnode;
1100 afsFid.Unique = dscp->fid.unique;
1102 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1105 code = RXAFS_RemoveFile(connp->callp, &afsFid, namep,
1106 &newDirStatus, &volSync);
1108 } while (cm_Analyze(connp, userp, reqp,
1109 &dscp->fid, &volSync, NULL, code));
1110 code = cm_MapRPCError(code, reqp);
1112 lock_ObtainMutex(&dscp->mx);
1113 cm_dnlcRemove(dscp, namep);
1114 cm_SyncOpDone(dscp, NULL, sflags);
1115 if (code == 0) cm_MergeStatus(dscp, &newDirStatus, &volSync, userp, 0);
1116 lock_ReleaseMutex(&dscp->mx);
1121 /* called with a locked vnode, and fills in the link info.
1122 * returns this the vnode still locked.
1124 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1131 lock_AssertMutex(&linkScp->mx);
1132 if (!linkScp->mountPointStringp) {
1133 /* read the link data */
1134 lock_ReleaseMutex(&linkScp->mx);
1135 thyper.LowPart = thyper.HighPart = 0;
1136 code = buf_Get(linkScp, &thyper, &bufp);
1137 lock_ObtainMutex(&linkScp->mx);
1138 if (code) return code;
1140 code = cm_SyncOp(linkScp, bufp, userp, reqp, 0,
1141 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1146 if (cm_HaveBuffer(linkScp, bufp, 0)) break;
1148 code = cm_GetBuffer(linkScp, bufp, NULL, userp, reqp);
1153 } /* while loop to get the data */
1155 /* now if we still have no link read in,
1156 * copy the data from the buffer */
1157 if ((temp = linkScp->length.LowPart) >= 1024) {
1159 return CM_ERROR_TOOBIG;
1162 /* otherwise, it fits; make sure it is still null (could have
1163 * lost race with someone else referencing this link above),
1164 * and if so, copy in the data.
1166 if (linkScp->mountPointStringp == NULL) {
1167 linkScp->mountPointStringp = malloc(temp+1);
1168 strncpy(linkScp->mountPointStringp, bufp->datap, temp);
1169 linkScp->mountPointStringp[temp] = 0; /* null terminate */
1172 } /* don't have sym link contents cached */
1177 /* called with a held vnode and a path suffix, with the held vnode being a
1178 * symbolic link. Our goal is to generate a new path to interpret, and return
1179 * this new path in newSpaceBufferp. If the new vnode is relative to a dir
1180 * other than the directory containing the symbolic link, then the new root is
1181 * returned in *newRootScpp, otherwise a null is returned there.
1183 long cm_AssembleLink(cm_scache_t *linkScp, char *pathSuffixp,
1184 cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1185 cm_user_t *userp, cm_req_t *reqp)
1191 lock_ObtainMutex(&linkScp->mx);
1192 code = cm_HandleLink(linkScp, userp, reqp);
1193 if (code) goto done;
1195 /* if we may overflow the buffer, bail out; buffer is signficantly
1196 * bigger than max path length, so we don't really have to worry about
1197 * being a little conservative here.
1199 if (strlen(linkScp->mountPointStringp) + strlen(pathSuffixp) + 2
1200 >= CM_UTILS_SPACESIZE)
1201 return CM_ERROR_TOOBIG;
1203 tsp = cm_GetSpace();
1204 linkp = linkScp->mountPointStringp;
1205 if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1206 if (strlen(linkp) > cm_mountRootLen)
1207 strcpy(tsp->data, linkp+cm_mountRootLen+1);
1210 *newRootScpp = cm_rootSCachep;
1211 cm_HoldSCache(cm_rootSCachep);
1212 } else if (*linkp == '\\' || *linkp == '/') {
1213 /* formerly, this was considered to be from the AFS root,
1214 but this seems to create problems. instead, we will just
1217 strcpy(tsp->data, linkp+1);
1218 *newRootScpp = cm_rootSCachep;
1219 cm_HoldSCache(cm_rootSCachep);
1221 code = CM_ERROR_NOSUCHPATH;
1226 /* a relative link */
1227 strcpy(tsp->data, linkp);
1228 *newRootScpp = NULL;
1230 if (pathSuffixp[0] != 0) { /* if suffix string is non-null */
1231 strcat(tsp->data, "\\");
1232 strcat(tsp->data, pathSuffixp);
1234 *newSpaceBufferp = tsp;
1238 lock_ReleaseMutex(&linkScp->mx);
1242 long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
1243 cm_user_t *userp, char *tidPathp, cm_req_t *reqp, cm_scache_t **outScpp)
1246 char *tp; /* ptr moving through input buffer */
1247 char tc; /* temp char */
1248 int haveComponent; /* has new component started? */
1249 char component[256]; /* this is the new component */
1250 char *cp; /* component name being assembled */
1251 cm_scache_t *tscp; /* current location in the hierarchy */
1252 cm_scache_t *nscp; /* next dude down */
1253 cm_scache_t *dirScp; /* last dir we searched */
1254 cm_scache_t *linkScp; /* new root for the symlink we just
1256 cm_space_t *psp; /* space for current path, if we've hit
1258 cm_space_t *tempsp; /* temp vbl */
1259 char *restp; /* rest of the pathname to interpret */
1260 int symlinkCount; /* count of # of symlinks traversed */
1261 int extraFlag; /* avoid chasing mt pts for dir cmd */
1262 int phase = 1; /* 1 = tidPathp, 2 = pathp */
1272 cm_HoldSCache(tscp);
1277 /* map Unix slashes into DOS ones so we can interpret Unix
1280 if (tc == '/') tc = '\\';
1282 if (!haveComponent) {
1283 if (tc == '\\') continue;
1300 /* we have a component here */
1301 if (tc == 0 || tc == '\\') {
1302 /* end of the component; we're at the last
1303 * component if tc == 0. However, if the last
1304 * is a symlink, we have more to do.
1306 *cp++ = 0; /* add null termination */
1308 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
1309 extraFlag = CM_FLAG_NOMOUNTCHASE;
1310 code = cm_Lookup(tscp, component,
1312 userp, reqp, &nscp);
1314 cm_ReleaseSCache(tscp);
1315 if (psp) cm_FreeSpace(psp);
1318 haveComponent = 0; /* component done */
1319 dirScp = tscp; /* for some symlinks */
1320 tscp = nscp; /* already held */
1321 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
1323 cm_ReleaseSCache(dirScp);
1327 /* now, if tscp is a symlink, we should follow
1328 * it and assemble the path again.
1330 lock_ObtainMutex(&tscp->mx);
1331 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1332 CM_SCACHESYNC_GETSTATUS
1333 | CM_SCACHESYNC_NEEDCALLBACK);
1335 lock_ReleaseMutex(&tscp->mx);
1336 cm_ReleaseSCache(tscp);
1337 cm_ReleaseSCache(dirScp);
1340 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
1341 /* this is a symlink; assemble a new buffer */
1342 lock_ReleaseMutex(&tscp->mx);
1343 if (symlinkCount++ >= 16) {
1344 cm_ReleaseSCache(tscp);
1345 cm_ReleaseSCache(dirScp);
1346 if (psp) cm_FreeSpace(psp);
1347 return CM_ERROR_TOOBIG;
1349 if (tc == 0) restp = "";
1351 code = cm_AssembleLink(tscp, restp,
1352 &linkScp, &tempsp, userp, reqp);
1354 /* something went wrong */
1355 cm_ReleaseSCache(tscp);
1356 cm_ReleaseSCache(dirScp);
1360 /* otherwise, tempsp has the new path,
1361 * and linkScp is the new root from
1362 * which to interpret that path.
1363 * Continue with the namei processing,
1364 * also doing the bookkeeping for the
1365 * space allocation and tracking the
1366 * vnode reference counts.
1368 if (psp) cm_FreeSpace(psp);
1371 cm_ReleaseSCache(tscp);
1372 tscp = linkScp; /* already held
1373 * by AssembleLink */
1374 /* now, if linkScp is null, that's
1375 * AssembleLink's way of telling us that
1376 * the sym link is relative to the dir
1377 * containing the link. We have a ref
1378 * to it in dirScp, and we hold it now
1379 * and reuse it as the new spot in the
1383 cm_HoldSCache(dirScp);
1386 } /* if we have a sym link */
1388 /* not a symlink, we may be done */
1389 lock_ReleaseMutex(&tscp->mx);
1396 cm_ReleaseSCache(dirScp);
1401 cm_ReleaseSCache(dirScp);
1402 } /* end of a component */
1404 } /* we have a component */
1405 } /* big while loop over all components */
1408 if (psp) cm_FreeSpace(psp);
1409 if (code == 0) *outScpp = tscp;
1413 /* called with a dir, and a vnode within the dir that happens to be a symlink.
1414 * We chase the link, and return a held pointer to the target, if it exists,
1415 * in *outScpp. If we succeed, we return 0, otherwise we return an error code
1416 * and do not hold or return a target vnode.
1418 * This is very similar to calling cm_NameI with the last component of a name,
1419 * which happens to be a symlink, except that we've already passed by the name.
1421 * This function is typically called by the directory listing functions, which
1422 * encounter symlinks but need to return the proper file length so programs
1423 * like "more" work properly when they make use of the attributes retrieved from
1426 * The input vnode should not be locked when this function is called.
1428 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
1429 cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
1433 cm_scache_t *newRootScp;
1435 osi_Log1(afsd_logp, "Evaluating symlink vp %x", linkScp);
1437 code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
1438 if (code) return code;
1440 /* now, if newRootScp is NULL, we're really being told that the symlink
1441 * is relative to the current directory (dscp).
1443 if (newRootScp == NULL) {
1445 cm_HoldSCache(dscp);
1448 code = cm_NameI(newRootScp, spacep->data,
1449 CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
1450 userp, NULL, reqp, outScpp);
1452 /* this stuff is allocated no matter what happened on the namei call,
1454 cm_FreeSpace(spacep);
1455 cm_ReleaseSCache(newRootScp);
1460 /* make this big enough so that one buffer of dir pages won't overflow. We'll
1461 * check anyway, but we want to minimize the chance that we have to leave stuff
1464 #define CM_BULKMAX 128
1466 /* rock for bulk stat calls */
1467 typedef struct cm_bulkStat {
1468 osi_hyper_t bufOffset; /* only do it for things in this buffer page */
1470 /* info for the actual call */
1471 int counter; /* next free slot */
1472 AFSFid fids[CM_BULKMAX];
1473 AFSFetchStatus stats[CM_BULKMAX];
1474 AFSCallBack callbacks[CM_BULKMAX];
1477 /* for a given entry, make sure that it isn't in the stat cache, and then
1478 * add it to the list of file IDs to be obtained.
1480 * Don't bother adding it if we already have a vnode. Note that the dir
1481 * is locked, so we have to be careful checking the vnode we're thinking of
1482 * processing, to avoid deadlocks.
1484 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
1495 /* Don't overflow bsp. */
1496 if (bsp->counter >= CM_BULKMAX)
1497 return CM_ERROR_STOPNOW;
1499 thyper.LowPart = buf_bufferSize;
1500 thyper.HighPart = 0;
1501 thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
1503 /* thyper is now the first byte past the end of the record we're
1504 * interested in, and bsp->bufOffset is the first byte of the record
1505 * we're interested in.
1506 * Skip data in the others.
1509 if (LargeIntegerLessThan(*offp, bsp->bufOffset))
1511 if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
1512 return CM_ERROR_STOPNOW;
1513 if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
1516 tfid.cell = scp->fid.cell;
1517 tfid.volume = scp->fid.volume;
1518 tfid.vnode = ntohl(dep->fid.vnode);
1519 tfid.unique = ntohl(dep->fid.unique);
1520 tscp = cm_FindSCache(&tfid);
1522 if (lock_TryMutex(&tscp->mx)) {
1523 /* we have an entry that we can look at */
1524 if (cm_HaveCallback(tscp)) {
1525 /* we have a callback on it. Don't bother
1526 * fetching this stat entry, since we're happy
1527 * with the info we have.
1529 lock_ReleaseMutex(&tscp->mx);
1530 cm_ReleaseSCache(tscp);
1533 lock_ReleaseMutex(&tscp->mx);
1535 cm_ReleaseSCache(tscp);
1538 #ifdef AFS_FREELANCE_CLIENT
1539 // yj: if this is a mountpoint under root.afs then we don't want it
1540 // to be bulkstat-ed, instead, we call getSCache directly and under
1541 // getSCache, it is handled specially.
1542 if (cm_freelanceEnabled &&
1543 tfid.cell==0x1 && tfid.volume==0x20000001 &&
1544 !(tfid.vnode==0x1 && tfid.unique==0x1) )
1547 afsi_log(" cm_trybulkproc going to call getscache");
1549 return cm_GetSCache(&tfid, &tscp, NULL, NULL);
1551 #endif /* AFS_FREELANCE_CLIENT */
1554 bsp->fids[i].Volume = scp->fid.volume;
1555 bsp->fids[i].Vnode = tfid.vnode;
1556 bsp->fids[i].Unique = tfid.unique;
1560 /* called with a locked scp and a pointer to a buffer. Make bulk stat
1561 * calls on all undeleted files in the page of the directory specified.
1563 void cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
1567 cm_bulkStat_t bb; /* this is *BIG*, probably 12K or so;
1568 * watch for stack problems */
1569 AFSCBFids fidStruct;
1570 AFSBulkStats statStruct;
1572 AFSCBs callbackStruct;
1575 cm_callbackRequest_t cbReq;
1582 osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%x", (long) dscp);
1584 /* should be on a buffer boundary */
1585 osi_assert((offsetp->LowPart & (buf_bufferSize - 1)) == 0);
1588 bb.bufOffset = *offsetp;
1590 /* first, assemble the file IDs we need to stat */
1591 code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) &bb, offsetp, userp,
1594 /* if we failed, bail out early */
1595 if (code && code != CM_ERROR_STOPNOW) return;
1597 /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
1598 * make the calls to create the entries. Handle AFSCBMAX files at a
1602 while(filex < bb.counter) {
1603 filesThisCall = bb.counter - filex;
1604 if (filesThisCall > AFSCBMAX) filesThisCall = AFSCBMAX;
1606 fidStruct.AFSCBFids_len = filesThisCall;
1607 fidStruct.AFSCBFids_val = &bb.fids[filex];
1608 statStruct.AFSBulkStats_len = filesThisCall;
1609 statStruct.AFSBulkStats_val = &bb.stats[filex];
1610 callbackStruct.AFSCBs_len = filesThisCall;
1611 callbackStruct.AFSCBs_val = &bb.callbacks[filex];
1612 cm_StartCallbackGrantingCall(NULL, &cbReq);
1613 osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
1615 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1618 code = RXAFS_BulkStatus(connp->callp, &fidStruct,
1619 &statStruct, &callbackStruct, &volSync);
1621 } while (cm_Analyze(connp, userp, reqp, &dscp->fid,
1622 &volSync, &cbReq, code));
1623 code = cm_MapRPCError(code, reqp);
1625 osi_Log0(afsd_logp, "CALL BulkStatus DONE");
1627 /* may as well quit on an error, since we're not going to do
1628 * much better on the next immediate call, either.
1632 /* otherwise, we should do the merges */
1633 for(i = 0; i<filesThisCall; i++) {
1635 tfid.cell = dscp->fid.cell;
1636 tfid.volume = bb.fids[j].Volume;
1637 tfid.vnode = bb.fids[j].Vnode;
1638 tfid.unique = bb.fids[j].Unique;
1639 code = cm_GetSCache(&tfid, &scp, userp, reqp);
1640 if (code != 0) continue;
1642 /* otherwise, if this entry has no callback info,
1645 lock_ObtainMutex(&scp->mx);
1646 /* now, we have to be extra paranoid on merging in this
1647 * information, since we didn't use cm_SyncOp before
1648 * starting the fetch to make sure that no bad races
1649 * were occurring. Specifically, we need to make sure
1650 * we don't obliterate any newer information in the
1651 * vnode than have here.
1653 * Right now, be pretty conservative: if there's a
1654 * callback or a pending call, skip it.
1656 if (scp->cbServerp == NULL
1658 (CM_SCACHEFLAG_FETCHING
1659 | CM_SCACHEFLAG_STORING
1660 | CM_SCACHEFLAG_SIZESTORING))) {
1661 cm_EndCallbackGrantingCall(scp, &cbReq,
1663 CM_CALLBACK_MAINTAINCOUNT);
1664 cm_MergeStatus(scp, &bb.stats[j], &volSync,
1667 lock_ReleaseMutex(&scp->mx);
1668 cm_ReleaseSCache(scp);
1669 } /* all files in the response */
1670 /* now tell it to drop the count,
1671 * after doing the vnode processing above */
1672 cm_EndCallbackGrantingCall(NULL, NULL, NULL, 0);
1674 filex += filesThisCall;
1675 } /* while there are still more files to process */
1676 osi_Log0(afsd_logp, "END cm_TryBulkStat");
1679 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
1683 /* initialize store back mask as inexpensive local variable */
1685 memset(statusp, 0, sizeof(AFSStoreStatus));
1687 /* copy out queued info from scache first, if scp passed in */
1689 if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
1690 statusp->ClientModTime = scp->clientModTime;
1691 mask |= AFS_SETMODTIME;
1692 scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
1697 /* now add in our locally generated request */
1698 if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
1699 statusp->ClientModTime = attrp->clientModTime;
1700 mask |= AFS_SETMODTIME;
1702 if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
1703 statusp->UnixModeBits = attrp->unixModeBits;
1704 mask |= AFS_SETMODE;
1706 if (attrp->mask & CM_ATTRMASK_OWNER) {
1707 statusp->Owner = attrp->owner;
1708 mask |= AFS_SETOWNER;
1710 if (attrp->mask & CM_ATTRMASK_GROUP) {
1711 statusp->Group = attrp->group;
1712 mask |= AFS_SETGROUP;
1715 statusp->Mask = mask;
1718 /* set the file size, and make sure that all relevant buffers have been
1719 * truncated. Ensure that any partially truncated buffers have been zeroed
1720 * to the end of the buffer.
1722 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
1728 /* start by locking out buffer creation */
1729 lock_ObtainWrite(&scp->bufCreateLock);
1731 /* verify that this is a file, not a dir or a symlink */
1732 lock_ObtainMutex(&scp->mx);
1733 code = cm_SyncOp(scp, NULL, userp, reqp, 0,
1734 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1735 if (code) goto done;
1737 if (scp->fileType != CM_SCACHETYPE_FILE) {
1738 code = CM_ERROR_ISDIR;
1743 if (LargeIntegerLessThan(*sizep, scp->length))
1748 lock_ReleaseMutex(&scp->mx);
1750 /* can't hold scp->mx lock here, since we may wait for a storeback to
1751 * finish if the buffer package is cleaning a buffer by storing it to
1755 buf_Truncate(scp, userp, reqp, sizep);
1757 /* now ensure that file length is short enough, and update truncPos */
1758 lock_ObtainMutex(&scp->mx);
1760 /* make sure we have a callback (so we have the right value for the
1761 * length), and wait for it to be safe to do a truncate.
1763 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
1764 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
1765 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
1766 if (code) goto done;
1768 if (LargeIntegerLessThan(*sizep, scp->length)) {
1769 /* a real truncation. If truncPos is not set yet, or is bigger
1770 * than where we're truncating the file, set truncPos to this
1775 if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
1776 || LargeIntegerLessThan(*sizep, scp->length)) {
1778 scp->truncPos = *sizep;
1779 scp->mask |= CM_SCACHEMASK_TRUNCPOS;
1781 /* in either case, the new file size has been changed */
1782 scp->length = *sizep;
1783 scp->mask |= CM_SCACHEMASK_LENGTH;
1785 else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
1786 /* really extending the file */
1787 scp->length = *sizep;
1788 scp->mask |= CM_SCACHEMASK_LENGTH;
1791 /* done successfully */
1795 lock_ReleaseMutex(&scp->mx);
1796 lock_ReleaseWrite(&scp->bufCreateLock);
1801 /* set the file size or other attributes (but not both at once) */
1802 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
1807 AFSFetchStatus afsOutStatus;
1811 AFSStoreStatus afsInStatus;
1813 /* handle file length setting */
1814 if (attrp->mask & CM_ATTRMASK_LENGTH)
1815 return cm_SetLength(scp, &attrp->length, userp, reqp);
1817 flags = CM_SCACHESYNC_STORESTATUS;
1819 lock_ObtainMutex(&scp->mx);
1820 /* otherwise, we have to make an RPC to get the status */
1821 code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
1823 /* make the attr structure */
1824 cm_StatusFromAttr(&afsInStatus, scp, attrp);
1826 lock_ReleaseMutex(&scp->mx);
1827 if (code) return code;
1829 /* now make the RPC */
1830 osi_Log1(afsd_logp, "CALL StoreStatus vp %x", (long) scp);
1831 tfid.Volume = scp->fid.volume;
1832 tfid.Vnode = scp->fid.vnode;
1833 tfid.Unique = scp->fid.unique;
1835 code = cm_Conn(&scp->fid, userp, reqp, &connp);
1838 code = RXAFS_StoreStatus(connp->callp, &tfid,
1839 &afsInStatus, &afsOutStatus, &volSync);
1841 } while (cm_Analyze(connp, userp, reqp,
1842 &scp->fid, &volSync, NULL, code));
1843 code = cm_MapRPCError(code, reqp);
1845 osi_Log1(afsd_logp, "CALL StoreStatus DONE, code %d", code);
1847 lock_ObtainMutex(&scp->mx);
1848 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
1850 cm_MergeStatus(scp, &afsOutStatus, &volSync, userp,
1851 CM_MERGEFLAG_FORCE);
1853 /* if we're changing the mode bits, discard the ACL cache,
1854 * since we changed the mode bits.
1856 if (afsInStatus.Mask & AFS_SETMODE) cm_FreeAllACLEnts(scp);
1857 lock_ReleaseMutex(&scp->mx);
1861 long cm_Create(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
1862 cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
1867 cm_callbackRequest_t cbReq;
1872 AFSStoreStatus inStatus;
1873 AFSFetchStatus updatedDirStatus;
1874 AFSFetchStatus newFileStatus;
1875 AFSCallBack newFileCallback;
1878 /* can't create names with @sys in them; must expand it manually first.
1879 * return "invalid request" if they try.
1881 if (cm_ExpandSysName(namep, NULL, 0)) {
1882 return CM_ERROR_ATSYS;
1885 /* before starting the RPC, mark that we're changing the file data, so
1886 * that someone who does a chmod will know to wait until our call
1889 lock_ObtainMutex(&dscp->mx);
1890 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
1892 cm_StartCallbackGrantingCall(NULL, &cbReq);
1894 lock_ReleaseMutex(&dscp->mx);
1900 cm_StatusFromAttr(&inStatus, NULL, attrp);
1902 /* try the RPC now */
1904 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1907 dirAFSFid.Volume = dscp->fid.volume;
1908 dirAFSFid.Vnode = dscp->fid.vnode;
1909 dirAFSFid.Unique = dscp->fid.unique;
1910 code = RXAFS_CreateFile(connp->callp, &dirAFSFid, namep,
1911 &inStatus, &newAFSFid, &newFileStatus,
1912 &updatedDirStatus, &newFileCallback,
1914 } while (cm_Analyze(connp, userp, reqp,
1915 &dscp->fid, &volSync, &cbReq, code));
1916 code = cm_MapRPCError(code, reqp);
1918 lock_ObtainMutex(&dscp->mx);
1919 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
1921 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
1923 lock_ReleaseMutex(&dscp->mx);
1925 /* now try to create the file's entry, too, but be careful to
1926 * make sure that we don't merge in old info. Since we weren't locking
1927 * out any requests during the file's creation, we may have pretty old
1931 newFid.cell = dscp->fid.cell;
1932 newFid.volume = dscp->fid.volume;
1933 newFid.vnode = newAFSFid.Vnode;
1934 newFid.unique = newAFSFid.Unique;
1935 code = cm_GetSCache(&newFid, &scp, userp, reqp);
1937 lock_ObtainMutex(&scp->mx);
1938 if (!cm_HaveCallback(scp)) {
1939 cm_MergeStatus(scp, &newFileStatus, &volSync,
1941 cm_EndCallbackGrantingCall(scp, &cbReq,
1942 &newFileCallback, 0);
1945 lock_ReleaseMutex(&scp->mx);
1950 /* make sure we end things properly */
1952 cm_EndCallbackGrantingCall(NULL, NULL, NULL, 0);
1957 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
1961 lock_ObtainWrite(&scp->bufCreateLock);
1962 code = buf_CleanVnode(scp, userp, reqp);
1963 lock_ReleaseWrite(&scp->bufCreateLock);
1965 lock_ObtainMutex(&scp->mx);
1966 scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA
1967 | CM_SCACHEFLAG_OUTOFSPACE);
1968 if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
1969 | CM_SCACHEMASK_CLIENTMODTIME
1970 | CM_SCACHEMASK_LENGTH))
1971 code = cm_StoreMini(scp, userp, reqp);
1972 lock_ReleaseMutex(&scp->mx);
1977 long cm_MakeDir(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
1978 cm_user_t *userp, cm_req_t *reqp)
1983 cm_callbackRequest_t cbReq;
1988 AFSStoreStatus inStatus;
1989 AFSFetchStatus updatedDirStatus;
1990 AFSFetchStatus newDirStatus;
1991 AFSCallBack newDirCallback;
1994 /* can't create names with @sys in them; must expand it manually first.
1995 * return "invalid request" if they try.
1997 if (cm_ExpandSysName(namep, NULL, 0)) {
1998 return CM_ERROR_ATSYS;
2001 /* before starting the RPC, mark that we're changing the directory
2002 * data, so that someone who does a chmod on the dir will wait until
2003 * our call completes.
2005 lock_ObtainMutex(&dscp->mx);
2006 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2008 cm_StartCallbackGrantingCall(NULL, &cbReq);
2010 lock_ReleaseMutex(&dscp->mx);
2016 cm_StatusFromAttr(&inStatus, NULL, attrp);
2018 /* try the RPC now */
2020 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2023 dirAFSFid.Volume = dscp->fid.volume;
2024 dirAFSFid.Vnode = dscp->fid.vnode;
2025 dirAFSFid.Unique = dscp->fid.unique;
2026 code = RXAFS_MakeDir(connp->callp, &dirAFSFid, namep,
2027 &inStatus, &newAFSFid, &newDirStatus,
2028 &updatedDirStatus, &newDirCallback,
2030 } while (cm_Analyze(connp, userp, reqp,
2031 &dscp->fid, &volSync, &cbReq, code));
2032 code = cm_MapRPCError(code, reqp);
2034 lock_ObtainMutex(&dscp->mx);
2035 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2037 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2039 lock_ReleaseMutex(&dscp->mx);
2041 /* now try to create the new dir's entry, too, but be careful to
2042 * make sure that we don't merge in old info. Since we weren't locking
2043 * out any requests during the file's creation, we may have pretty old
2047 newFid.cell = dscp->fid.cell;
2048 newFid.volume = dscp->fid.volume;
2049 newFid.vnode = newAFSFid.Vnode;
2050 newFid.unique = newAFSFid.Unique;
2051 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2053 lock_ObtainMutex(&scp->mx);
2054 if (!cm_HaveCallback(scp)) {
2055 cm_MergeStatus(scp, &newDirStatus, &volSync,
2057 cm_EndCallbackGrantingCall(scp, &cbReq,
2058 &newDirCallback, 0);
2061 lock_ReleaseMutex(&scp->mx);
2062 cm_ReleaseSCache(scp);
2066 /* make sure we end things properly */
2068 cm_EndCallbackGrantingCall(NULL, NULL, NULL, 0);
2070 /* and return error code */
2074 long cm_SymLink(cm_scache_t *dscp, char *namep, char *contentsp, long flags,
2075 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
2083 AFSStoreStatus inStatus;
2084 AFSFetchStatus updatedDirStatus;
2085 AFSFetchStatus newLinkStatus;
2088 /* before starting the RPC, mark that we're changing the directory data,
2089 * so that someone who does a chmod on the dir will wait until our
2092 lock_ObtainMutex(&dscp->mx);
2093 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2094 lock_ReleaseMutex(&dscp->mx);
2099 cm_StatusFromAttr(&inStatus, NULL, attrp);
2101 /* try the RPC now */
2103 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2106 dirAFSFid.Volume = dscp->fid.volume;
2107 dirAFSFid.Vnode = dscp->fid.vnode;
2108 dirAFSFid.Unique = dscp->fid.unique;
2109 code = RXAFS_Symlink(connp->callp, &dirAFSFid, namep, contentsp,
2110 &inStatus, &newAFSFid, &newLinkStatus,
2111 &updatedDirStatus, &volSync);
2112 } while (cm_Analyze(connp, userp, reqp,
2113 &dscp->fid, &volSync, NULL, code));
2114 code = cm_MapRPCError(code, reqp);
2116 lock_ObtainMutex(&dscp->mx);
2117 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2119 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2121 lock_ReleaseMutex(&dscp->mx);
2123 /* now try to create the new dir's entry, too, but be careful to
2124 * make sure that we don't merge in old info. Since we weren't locking
2125 * out any requests during the file's creation, we may have pretty old
2129 newFid.cell = dscp->fid.cell;
2130 newFid.volume = dscp->fid.volume;
2131 newFid.vnode = newAFSFid.Vnode;
2132 newFid.unique = newAFSFid.Unique;
2133 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2135 lock_ObtainMutex(&scp->mx);
2136 if (!cm_HaveCallback(scp)) {
2137 cm_MergeStatus(scp, &newLinkStatus, &volSync,
2140 lock_ReleaseMutex(&scp->mx);
2141 cm_ReleaseSCache(scp);
2145 /* and return error code */
2149 long cm_RemoveDir(cm_scache_t *dscp, char *namep, cm_user_t *userp,
2156 AFSFetchStatus updatedDirStatus;
2159 /* before starting the RPC, mark that we're changing the directory data,
2160 * so that someone who does a chmod on the dir will wait until our
2163 lock_ObtainMutex(&dscp->mx);
2164 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2165 lock_ReleaseMutex(&dscp->mx);
2171 /* try the RPC now */
2173 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2176 dirAFSFid.Volume = dscp->fid.volume;
2177 dirAFSFid.Vnode = dscp->fid.vnode;
2178 dirAFSFid.Unique = dscp->fid.unique;
2179 code = RXAFS_RemoveDir(connp->callp, &dirAFSFid, namep,
2180 &updatedDirStatus, &volSync);
2181 } while (cm_Analyze(connp, userp, reqp,
2182 &dscp->fid, &volSync, NULL, code));
2183 code = cm_MapRPCErrorRmdir(code, reqp);
2185 lock_ObtainMutex(&dscp->mx);
2186 cm_dnlcRemove(dscp, namep);
2187 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2189 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2191 lock_ReleaseMutex(&dscp->mx);
2193 /* and return error code */
2197 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
2199 /* grab mutex on contents */
2200 lock_ObtainMutex(&scp->mx);
2202 /* reset the prefetch info */
2203 scp->prefetch.base.LowPart = 0; /* base */
2204 scp->prefetch.base.HighPart = 0;
2205 scp->prefetch.end.LowPart = 0; /* and end */
2206 scp->prefetch.end.HighPart = 0;
2208 /* release mutex on contents */
2209 lock_ReleaseMutex(&scp->mx);
2215 long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
2216 char *newNamep, cm_user_t *userp, cm_req_t *reqp)
2220 AFSFid oldDirAFSFid;
2221 AFSFid newDirAFSFid;
2223 AFSFetchStatus updatedOldDirStatus;
2224 AFSFetchStatus updatedNewDirStatus;
2228 /* before starting the RPC, mark that we're changing the directory data,
2229 * so that someone who does a chmod on the dir will wait until our call
2230 * completes. We do this in vnode order so that we don't deadlock,
2231 * which makes the code a little verbose.
2233 if (oldDscp == newDscp) {
2234 /* check for identical names */
2235 if (strcmp(oldNamep, newNamep) == 0)
2236 return CM_ERROR_RENAME_IDENTICAL;
2239 lock_ObtainMutex(&oldDscp->mx);
2240 cm_dnlcRemove(oldDscp, oldNamep);
2241 cm_dnlcRemove(oldDscp, newNamep);
2242 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2243 CM_SCACHESYNC_STOREDATA);
2244 lock_ReleaseMutex(&oldDscp->mx);
2247 /* two distinct dir vnodes */
2249 if (oldDscp->fid.cell != newDscp->fid.cell ||
2250 oldDscp->fid.volume != newDscp->fid.volume)
2251 return CM_ERROR_CROSSDEVLINK;
2253 /* shouldn't happen that we have distinct vnodes for two
2254 * different files, but could due to deliberate attack, or
2255 * stale info. Avoid deadlocks and quit now.
2257 if (oldDscp->fid.vnode == newDscp->fid.vnode)
2258 return CM_ERROR_CROSSDEVLINK;
2260 if (oldDscp->fid.vnode < newDscp->fid.vnode) {
2261 lock_ObtainMutex(&oldDscp->mx);
2262 cm_dnlcRemove(oldDscp, oldNamep);
2263 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2264 CM_SCACHESYNC_STOREDATA);
2265 lock_ReleaseMutex(&oldDscp->mx);
2267 lock_ObtainMutex(&newDscp->mx);
2268 cm_dnlcRemove(newDscp, newNamep);
2269 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2270 CM_SCACHESYNC_STOREDATA);
2271 lock_ReleaseMutex(&newDscp->mx);
2273 /* cleanup first one */
2274 cm_SyncOpDone(oldDscp, NULL,
2275 CM_SCACHESYNC_STOREDATA);
2280 /* lock the new vnode entry first */
2281 lock_ObtainMutex(&newDscp->mx);
2282 cm_dnlcRemove(newDscp, newNamep);
2283 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2284 CM_SCACHESYNC_STOREDATA);
2285 lock_ReleaseMutex(&newDscp->mx);
2287 lock_ObtainMutex(&oldDscp->mx);
2288 cm_dnlcRemove(oldDscp, oldNamep);
2289 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2290 CM_SCACHESYNC_STOREDATA);
2291 lock_ReleaseMutex(&oldDscp->mx);
2293 /* cleanup first one */
2294 cm_SyncOpDone(newDscp, NULL,
2295 CM_SCACHESYNC_STOREDATA);
2299 } /* two distinct vnodes */
2306 /* try the RPC now */
2308 code = cm_Conn(&oldDscp->fid, userp, reqp, &connp);
2311 oldDirAFSFid.Volume = oldDscp->fid.volume;
2312 oldDirAFSFid.Vnode = oldDscp->fid.vnode;
2313 oldDirAFSFid.Unique = oldDscp->fid.unique;
2314 newDirAFSFid.Volume = newDscp->fid.volume;
2315 newDirAFSFid.Vnode = newDscp->fid.vnode;
2316 newDirAFSFid.Unique = newDscp->fid.unique;
2317 code = RXAFS_Rename(connp->callp, &oldDirAFSFid, oldNamep,
2318 &newDirAFSFid, newNamep,
2319 &updatedOldDirStatus, &updatedNewDirStatus,
2321 } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
2322 &volSync, NULL, code));
2323 code = cm_MapRPCError(code, reqp);
2325 /* update the individual stat cache entries for the directories */
2326 lock_ObtainMutex(&oldDscp->mx);
2327 cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
2329 cm_MergeStatus(oldDscp, &updatedOldDirStatus, &volSync,
2332 lock_ReleaseMutex(&oldDscp->mx);
2334 /* and update it for the new one, too, if necessary */
2336 lock_ObtainMutex(&newDscp->mx);
2337 cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
2339 cm_MergeStatus(newDscp, &updatedNewDirStatus, &volSync,
2342 lock_ReleaseMutex(&newDscp->mx);
2345 /* and return error code */
2349 long cm_Lock(cm_scache_t *scp, unsigned char LockType,
2350 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
2351 u_long Timeout, cm_user_t *userp, cm_req_t *reqp,
2355 int Which = ((LockType & 0x1) ? LockRead : LockWrite);
2359 cm_file_lock_t *fileLock;
2363 /* Look for a conflict. Also, if we are asking for a shared lock,
2364 * look for another shared lock, so we don't have to do an RPC.
2368 fileLock = (cm_file_lock_t *)
2369 ((char *) q - offsetof(cm_file_lock_t, fileq));
2370 if ((fileLock->flags &
2371 (CM_FILELOCK_FLAG_INVALID | CM_FILELOCK_FLAG_WAITING))
2373 if ((LockType & 0x1) == 0
2374 || (fileLock->LockType & 0x1) == 0)
2375 return CM_ERROR_WOULDBLOCK;
2384 tfid.Volume = scp->fid.volume;
2385 tfid.Vnode = scp->fid.vnode;
2386 tfid.Unique = scp->fid.unique;
2387 lock_ReleaseMutex(&scp->mx);
2389 code = cm_Conn(&scp->fid, userp, reqp, &connp);
2391 code = RXAFS_SetLock(connp->callp, &tfid, Which,
2393 } while (cm_Analyze(connp, userp, reqp, &scp->fid, &volSync,
2395 lock_ObtainMutex(&scp->mx);
2396 code = cm_MapRPCError(code, reqp);
2399 if (code == 0 || Timeout != 0) {
2400 fileLock = malloc(sizeof(cm_file_lock_t));
2401 fileLock->LockType = LockType;
2402 fileLock->userp = userp;
2404 fileLock->fid = scp->fid;
2405 fileLock->LOffset = LOffset;
2406 fileLock->LLength = LLength;
2407 fileLock->flags = (code == 0 ? 0 : CM_FILELOCK_FLAG_WAITING);
2408 osi_QAdd(&scp->fileLocks, &fileLock->fileq);
2409 lock_ObtainWrite(&cm_scacheLock);
2410 osi_QAdd(&cm_allFileLocks, &fileLock->q);
2411 lock_ReleaseWrite(&cm_scacheLock);
2412 if (code != 0) *lockpp = fileLock;
2417 long cm_Unlock(cm_scache_t *scp, unsigned char LockType,
2418 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
2419 cm_user_t *userp, cm_req_t *reqp)
2422 int Which = ((LockType & 0x1) ? LockRead : LockWrite);
2426 cm_file_lock_t *fileLock, *ourLock;
2427 osi_queue_t *q, *qq;
2428 int anotherReader = 0;
2432 if (LargeIntegerLessThan(LLength, scp->length))
2435 /* Look for our own lock on the list, so as to remove it.
2436 * Also, determine if we're the last reader; if not, avoid an RPC.
2440 fileLock = (cm_file_lock_t *)
2441 ((char *) q - offsetof(cm_file_lock_t, fileq));
2443 && fileLock->userp == userp
2444 && LargeIntegerEqualTo(fileLock->LOffset, LOffset)
2445 && LargeIntegerEqualTo(fileLock->LLength, LLength)) {
2450 else if (fileLock->LockType & 0x1)
2455 /* ignore byte ranges */
2456 if (smallLock && !found)
2459 /* don't try to unlock other people's locks */
2461 return CM_ERROR_WOULDBLOCK;
2463 /* discard lock record */
2464 osi_QRemove(&scp->fileLocks, qq);
2466 * Don't delete it here; let the daemon delete it, to simplify
2467 * the daemon's traversal of the list.
2469 lock_ObtainWrite(&cm_scacheLock);
2470 ourLock->flags |= CM_FILELOCK_FLAG_INVALID;
2471 cm_ReleaseUser(ourLock->userp);
2472 lock_ReleaseWrite(&cm_scacheLock);
2474 if (!anotherReader) {
2475 tfid.Volume = scp->fid.volume;
2476 tfid.Vnode = scp->fid.vnode;
2477 tfid.Unique = scp->fid.unique;
2478 lock_ReleaseMutex(&scp->mx);
2480 code = cm_Conn(&scp->fid, userp, reqp, &connp);
2482 code = RXAFS_ReleaseLock(connp->callp, &tfid, &volSync);
2483 } while (cm_Analyze(connp, userp, reqp, &scp->fid, &volSync,
2485 code = cm_MapRPCError(code, reqp);
2486 lock_ObtainMutex(&scp->mx);
2492 void cm_CheckLocks()
2494 osi_queue_t *q, *nq;
2495 cm_file_lock_t *fileLock;
2504 lock_ObtainWrite(&cm_scacheLock);
2505 q = cm_allFileLocks;
2507 fileLock = (cm_file_lock_t *) q;
2509 if (fileLock->flags & CM_FILELOCK_FLAG_INVALID) {
2510 osi_QRemove(&cm_allFileLocks, q);
2513 else if (!(fileLock->flags & CM_FILELOCK_FLAG_WAITING)) {
2514 tfid.Volume = fileLock->fid.volume;
2515 tfid.Vnode = fileLock->fid.vnode;
2516 tfid.Unique = fileLock->fid.unique;
2517 lock_ReleaseWrite(&cm_scacheLock);
2519 code = cm_Conn(&fileLock->fid, fileLock->userp,
2522 code = RXAFS_ExtendLock(connp->callp, &tfid,
2524 } while (cm_Analyze(connp, fileLock->userp, &req,
2525 &fileLock->fid, &volSync, NULL,
2527 code = cm_MapRPCError(code, &req);
2528 lock_ObtainWrite(&cm_scacheLock);
2532 lock_ReleaseWrite(&cm_scacheLock);
2535 long cm_RetryLock(cm_file_lock_t *oldFileLock, int vcp_is_dead)
2538 int Which = ((oldFileLock->LockType & 0x1) ? LockRead : LockWrite);
2543 cm_file_lock_t *fileLock;
2549 code = CM_ERROR_TIMEDOUT;
2555 /* Look for a conflict. Also, if we are asking for a shared lock,
2556 * look for another shared lock, so we don't have to do an RPC.
2558 code = cm_GetSCache(&oldFileLock->fid, &scp, oldFileLock->userp, &req);
2564 fileLock = (cm_file_lock_t *)
2565 ((char *) q - offsetof(cm_file_lock_t, fileq));
2566 if ((fileLock->flags &
2567 (CM_FILELOCK_FLAG_INVALID | CM_FILELOCK_FLAG_WAITING))
2569 if ((oldFileLock->LockType & 0x1) == 0
2570 || (fileLock->LockType & 0x1) == 0) {
2571 cm_ReleaseSCache(scp);
2572 return CM_ERROR_WOULDBLOCK;
2582 tfid.Volume = oldFileLock->fid.volume;
2583 tfid.Vnode = oldFileLock->fid.vnode;
2584 tfid.Unique = oldFileLock->fid.unique;
2586 code = cm_Conn(&oldFileLock->fid, oldFileLock->userp,
2589 code = RXAFS_SetLock(connp->callp, &tfid, Which,
2591 } while (cm_Analyze(connp, oldFileLock->userp, &req,
2592 &oldFileLock->fid, &volSync,
2594 code = cm_MapRPCError(code, &req);
2598 if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
2599 lock_ObtainMutex(&scp->mx);
2600 osi_QRemove(&scp->fileLocks, &oldFileLock->fileq);
2601 lock_ReleaseMutex(&scp->mx);
2603 lock_ObtainWrite(&cm_scacheLock);
2605 oldFileLock->flags = 0;
2606 else if (code != CM_ERROR_WOULDBLOCK) {
2607 oldFileLock->flags |= CM_FILELOCK_FLAG_INVALID;
2608 cm_ReleaseUser(oldFileLock->userp);
2610 lock_ReleaseWrite(&cm_scacheLock);