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>
24 /* Used by cm_FollowMountPoint */
29 unsigned int cm_mountRootGen = 0;
34 * Case-folding array. This was constructed by inspecting of SMBtrace output.
35 * I do not know anything more about it.
37 unsigned char cm_foldUpper[256] = {
38 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
39 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
40 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
41 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
42 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
43 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
44 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
45 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
46 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
47 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
48 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
49 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
50 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
51 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
52 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
53 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
54 0x80, 0x9a, 0x90, 0x41, 0x8e, 0x41, 0x8f, 0x80,
55 0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x8e, 0x8f,
56 0x90, 0x92, 0x92, 0x4f, 0x99, 0x4f, 0x55, 0x55,
57 0x59, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
58 0x41, 0x49, 0x4f, 0x55, 0xa5, 0xa5, 0x56, 0xa7,
59 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
60 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
61 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
62 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
63 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
64 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
65 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
66 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
67 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
68 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
69 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
73 * Case-insensitive string comparison. We used to use stricmp, but it doesn't
74 * know about 8-bit characters (e.g. 129 is lowercase u-umlaut, 154 is
75 * upper-case u-umlaut).
77 int cm_stricmp(const char *str1, const char *str2)
89 c1 = (char) cm_foldUpper[(unsigned char)(*str1++)];
90 c2 = (char) cm_foldUpper[(unsigned char)(*str2++)];
98 /* characters that are legal in an 8.3 name */
100 * We used to have 1's for all characters from 128 to 254. But
101 * the NT client behaves better if we create an 8.3 name for any
102 * name that has a character with the high bit on, and if we
103 * delete those characters from 8.3 names. In particular, see
104 * Sybase defect 10859.
106 char cm_LegalChars[256] = {
107 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
108 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
109 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0,
110 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
111 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
112 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1,
113 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
114 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1,
115 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
116 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
117 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
118 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
119 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
120 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
121 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
122 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
125 /* return true iff component is a valid 8.3 name */
126 int cm_Is8Dot3(char *namep)
129 int sawUpper = 0, sawLower = 0;
134 * can't have a leading dot;
135 * special case for . and ..
137 if (namep[0] == '.') {
140 if (namep[1] == '.' && namep[2] == 0)
144 while (tc = *namep++) {
146 /* saw another dot */
147 if (sawDot) return 0; /* second dot */
152 if (cm_LegalChars[tc] == 0)
154 if (tc >= 'A' && tc <= 'Z')
156 else if (tc >= 'a' && tc <= 'z')
159 if (!sawDot && charCount > 8)
160 /* more than 8 chars in name */
162 if (sawDot && charCount > 3)
163 /* more than 3 chars in extension */
167 * Used to check that all characters were the same case.
168 * This doesn't help 16-bit apps, and meanwhile it causes the
169 * MS-DOS Command Prompt to misbehave; see Sybase defect 10709.
171 if (sawUpper && sawLower)
178 * Number unparsing map for generating 8.3 names;
181 char cm_8Dot3Mapping[41] =
182 {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
183 'B', 'C', 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S',
184 'T', 'V', 'W', 'X', 'Y', 'Z', '_', '-', '$', '#', '@', '%', '!', '&', 'E', 'O'
186 int cm_8Dot3MapSize = sizeof(cm_8Dot3Mapping);
188 void cm_Gen8Dot3Name(cm_dirEntry_t *dep, char *shortName, char **shortNameEndp)
192 int vnode = ntohl(dep->fid.vnode);
194 int validExtension = 0;
195 char tc, *temp, *name;
197 /* Unparse the file's vnode number to get a "uniquifier" */
199 number[nsize] = cm_8Dot3Mapping[vnode % cm_8Dot3MapSize];
201 vnode /= cm_8Dot3MapSize;
205 * Look for valid extension. There has to be a dot, and
206 * at least one of the characters following has to be legal.
208 lastDot = strrchr(dep->name, '.');
210 temp = lastDot; temp++;
212 if (cm_LegalChars[tc])
218 /* Copy name characters */
220 for (i = 0, name = dep->name;
221 i < (7 - nsize) && name != lastDot; ) {
226 if (!cm_LegalChars[tc])
229 *shortName++ = toupper(tc);
235 /* Copy uniquifier characters */
236 memcpy(shortName, number, nsize);
239 if (validExtension) {
240 /* Copy extension characters */
241 *shortName++ = *lastDot++; /* copy dot */
242 for (i = 0, tc = *lastDot++;
245 if (cm_LegalChars[tc]) {
247 *shortName++ = toupper(tc);
256 *shortNameEndp = shortName;
259 /* return success if we can open this file in this mode */
260 long cm_CheckOpen(cm_scache_t *scp, int openMode, int trunc, cm_user_t *userp,
267 if (openMode != 1) rights |= PRSFS_READ;
268 if (openMode == 1 || openMode == 2 || trunc) rights |= PRSFS_WRITE;
270 lock_ObtainMutex(&scp->mx);
272 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
273 CM_SCACHESYNC_GETSTATUS
274 | CM_SCACHESYNC_NEEDCALLBACK);
275 lock_ReleaseMutex(&scp->mx);
280 /* return success if we can open this file in this mode */
281 long cm_CheckNTOpen(cm_scache_t *scp, unsigned int desiredAccess,
282 unsigned int createDisp, cm_user_t *userp, cm_req_t *reqp)
287 /* Always allow delete; the RPC will tell us if it's OK */
288 if (desiredAccess == DELETE)
293 if (desiredAccess & AFS_ACCESS_READ)
294 rights |= PRSFS_READ;
296 if ((desiredAccess & AFS_ACCESS_WRITE)
298 rights |= PRSFS_WRITE;
300 lock_ObtainMutex(&scp->mx);
302 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
303 CM_SCACHESYNC_GETSTATUS
304 | CM_SCACHESYNC_NEEDCALLBACK);
305 lock_ReleaseMutex(&scp->mx);
308 * If the open will fail because the volume is readonly, then we will
309 * return an access denied error instead. This is to help brain-dead
310 * apps run correctly on replicated volumes.
311 * See defect 10007 for more information.
313 if (code == CM_ERROR_READONLY)
314 code = CM_ERROR_NOACCESS;
320 * When CAP_NT_SMBS has been negotiated, deletion (of files or directories) is
321 * done in three steps:
322 * (1) open for deletion (NT_CREATE_AND_X)
323 * (2) set for deletion on close (NTWTRANSACTION2, SET_FILE_INFO)
325 * We must not do the RPC until step 3. But if we are going to return an error
326 * code (e.g. directory not empty), we must return it by step 2, otherwise most
327 * clients will not notice it. So we do a preliminary check. For deleting
328 * files, this is almost free, since we have already done the RPC to get the
329 * parent directory's status bits. But for deleting directories, we must do an
330 * additional RPC to get the directory's data to check if it is empty. Sigh.
332 long cm_CheckNTDelete(cm_scache_t *dscp, cm_scache_t *scp, cm_user_t *userp,
339 unsigned short *hashTable;
341 int BeyondPage = 0, HaveDot = 0, HaveDotDot = 0;
343 /* First check permissions */
344 lock_ObtainMutex(&dscp->mx);
345 code = cm_SyncOp(dscp, NULL, userp, reqp, PRSFS_DELETE,
346 CM_SCACHESYNC_GETSTATUS
347 | CM_SCACHESYNC_NEEDCALLBACK);
348 lock_ReleaseMutex(&dscp->mx);
352 /* If deleting directory, must be empty */
354 if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
357 thyper.HighPart = 0; thyper.LowPart = 0;
358 lock_ObtainRead(&scp->bufCreateLock);
359 code = buf_Get(scp, &thyper, &bufferp);
360 lock_ReleaseRead(&scp->bufCreateLock);
364 lock_ObtainMutex(&bufferp->mx);
365 lock_ObtainMutex(&scp->mx);
367 code = cm_SyncOp(scp, bufferp, userp, reqp, 0,
368 CM_SCACHESYNC_NEEDCALLBACK
370 | CM_SCACHESYNC_BUFLOCKED);
374 if (cm_HaveBuffer(scp, bufferp, 1))
377 /* otherwise, load the buffer and try again */
378 lock_ReleaseMutex(&bufferp->mx);
379 code = cm_GetBuffer(scp, bufferp, NULL, userp, reqp);
380 lock_ReleaseMutex(&scp->mx);
381 lock_ObtainMutex(&bufferp->mx);
382 lock_ObtainMutex(&scp->mx);
387 /* We try to determine emptiness without looking beyond the first page,
388 * and without assuming "." and ".." are present and are on the first
389 * page (though these assumptions might, after all, be reasonable).
391 hashTable = (unsigned short *)(bufferp->datap + (32 * 5));
392 for (i=0; i<128; i++) {
393 idx = ntohs(hashTable[i]);
399 dep = (cm_dirEntry_t *)(bufferp->datap + (32 * idx));
400 if (strcmp(dep->name, ".") == 0)
402 else if (strcmp(dep->name, "..") == 0)
405 code = CM_ERROR_NOTEMPTY;
408 idx = ntohs(dep->next);
411 if (BeyondPage && HaveDot && HaveDotDot)
412 code = CM_ERROR_NOTEMPTY;
416 lock_ReleaseMutex(&bufferp->mx);
417 buf_Release(bufferp);
418 lock_ReleaseMutex(&scp->mx);
423 * Iterate through all entries in a directory.
424 * When the function funcp is called, the buffer is locked but the
425 * directory vnode is not.
427 long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
428 osi_hyper_t *startOffsetp, cm_user_t *userp, cm_req_t *reqp,
429 cm_scache_t **retscp)
436 osi_hyper_t dirLength;
437 osi_hyper_t bufferOffset;
438 osi_hyper_t curOffset;
442 cm_pageHeader_t *pageHeaderp;
444 long nextEntryCookie;
445 int numDirChunks; /* # of 32 byte dir chunks in this entry */
447 /* get the directory size */
448 lock_ObtainMutex(&scp->mx);
449 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_LOOKUP,
450 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
452 lock_ReleaseMutex(&scp->mx);
456 if (scp->fileType != CM_SCACHETYPE_DIRECTORY) {
457 lock_ReleaseMutex(&scp->mx);
458 return CM_ERROR_NOTDIR;
461 if ( retscp ) /* if this is a lookup call */
463 cm_lookupSearch_t* sp = parmp;
464 if ( *retscp = cm_dnlcLookup(scp, sp)) /* dnlc hit */
466 lock_ReleaseMutex(&scp->mx);
472 * XXX We only get the length once. It might change when we drop the
475 dirLength = scp->length;
477 lock_ReleaseMutex(&scp->mx);
480 bufferOffset.LowPart = bufferOffset.HighPart = 0;
482 curOffset = *startOffsetp;
484 curOffset.HighPart = 0;
485 curOffset.LowPart = 0;
489 /* make sure that curOffset.LowPart doesn't point to the first
490 * 32 bytes in the 2nd through last dir page, and that it
491 * doesn't point at the first 13 32-byte chunks in the first
492 * dir page, since those are dir and page headers, and don't
493 * contain useful information.
495 temp = curOffset.LowPart & (2048-1);
496 if (curOffset.HighPart == 0 && curOffset.LowPart < 2048) {
497 /* we're in the first page */
498 if (temp < 13*32) temp = 13*32;
501 /* we're in a later dir page */
502 if (temp < 32) temp = 32;
505 /* make sure the low order 5 bits are zero */
508 /* now put temp bits back ito curOffset.LowPart */
509 curOffset.LowPart &= ~(2048-1);
510 curOffset.LowPart |= temp;
512 /* check if we've passed the dir's EOF */
513 if (LargeIntegerGreaterThanOrEqualTo(curOffset, dirLength))
516 /* see if we can use the bufferp we have now; compute in which
517 * page the current offset would be, and check whether that's
518 * the offset of the buffer we have. If not, get the buffer.
520 thyper.HighPart = curOffset.HighPart;
521 thyper.LowPart = curOffset.LowPart & ~(buf_bufferSize-1);
522 if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
525 lock_ReleaseMutex(&bufferp->mx);
526 buf_Release(bufferp);
530 lock_ObtainRead(&scp->bufCreateLock);
531 code = buf_Get(scp, &thyper, &bufferp);
532 lock_ReleaseRead(&scp->bufCreateLock);
534 lock_ObtainMutex(&bufferp->mx);
536 bufferOffset = thyper;
538 /* now get the data in the cache */
540 lock_ObtainMutex(&scp->mx);
541 code = cm_SyncOp(scp, bufferp, userp, reqp,
543 CM_SCACHESYNC_NEEDCALLBACK
545 | CM_SCACHESYNC_BUFLOCKED);
547 lock_ReleaseMutex(&scp->mx);
551 if (cm_HaveBuffer(scp, bufferp, 1)) {
552 lock_ReleaseMutex(&scp->mx);
556 /* otherwise, load the buffer and try again */
557 lock_ReleaseMutex(&bufferp->mx);
558 code = cm_GetBuffer(scp, bufferp, NULL, userp,
560 lock_ReleaseMutex(&scp->mx);
561 lock_ObtainMutex(&bufferp->mx);
565 lock_ReleaseMutex(&bufferp->mx);
566 buf_Release(bufferp);
570 } /* if (wrong buffer) ... */
572 /* now we have the buffer containing the entry we're interested
573 * in; copy it out if it represents a non-deleted entry.
575 entryInDir = curOffset.LowPart & (2048-1);
576 entryInBuffer = curOffset.LowPart & (buf_bufferSize - 1);
578 /* page header will help tell us which entries are free. Page
579 * header can change more often than once per buffer, since
580 * AFS 3 dir page size may be less than (but not more than) a
581 * buffer package buffer.
583 /* only look intra-buffer */
584 temp = curOffset.LowPart & (buf_bufferSize - 1);
585 temp &= ~(2048 - 1); /* turn off intra-page bits */
586 pageHeaderp = (cm_pageHeader_t *) (bufferp->datap + temp);
588 /* now determine which entry we're looking at in the page. If
589 * it is free (there's a free bitmap at the start of the dir),
590 * we should skip these 32 bytes.
592 slotInPage = (entryInDir & 0x7e0) >> 5;
593 if (!(pageHeaderp->freeBitmap[slotInPage>>3]
594 & (1 << (slotInPage & 0x7)))) {
595 /* this entry is free */
596 numDirChunks = 1; /* only skip this guy */
600 tp = bufferp->datap + entryInBuffer;
601 dep = (cm_dirEntry_t *) tp; /* now points to AFS3 dir entry */
603 /* while we're here, compute the next entry's location, too,
604 * since we'll need it when writing out the cookie into the
605 * dir listing stream.
607 numDirChunks = cm_NameEntries(dep->name, NULL);
609 /* compute the offset of the cookie representing the next entry */
610 nextEntryCookie = curOffset.LowPart
611 + (CM_DIR_CHUNKSIZE * numDirChunks);
613 if (dep->fid.vnode != 0) {
614 /* this is one of the entries to use: it is not deleted */
615 code = (*funcp)(scp, dep, parmp, &curOffset);
617 } /* if we're including this name */
620 /* and adjust curOffset to be where the new cookie is */
622 thyper.LowPart = CM_DIR_CHUNKSIZE * numDirChunks;
623 curOffset = LargeIntegerAdd(thyper, curOffset);
624 } /* while copying data for dir listing */
626 /* release the mutex */
628 lock_ReleaseMutex(&bufferp->mx);
629 buf_Release(bufferp);
634 int cm_NoneUpper(char *s)
638 if (c >= 'A' && c <= 'Z')
643 int cm_NoneLower(char *s)
647 if (c >= 'a' && c <= 'z')
652 long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
655 cm_lookupSearch_t *sp;
662 matchName = dep->name;
664 match = cm_stricmp(matchName, sp->searchNamep);
666 match = strcmp(matchName, sp->searchNamep);
670 && !cm_Is8Dot3(dep->name)) {
671 matchName = shortName;
672 cm_Gen8Dot3Name(dep, shortName, NULL);
674 match = cm_stricmp(matchName, sp->searchNamep);
676 match = strcmp(matchName, sp->searchNamep);
684 if (!sp->caseFold || matchName == shortName) {
685 sp->fid.vnode = ntohl(dep->fid.vnode);
686 sp->fid.unique = ntohl(dep->fid.unique);
687 return CM_ERROR_STOPNOW;
691 * If we get here, we are doing a case-insensitive search, and we
692 * have found a match. Now we determine what kind of match it is:
693 * exact, lower-case, upper-case, or none of the above. This is done
694 * in order to choose among matches, if there are more than one.
697 /* Exact matches are the best. */
698 match = strcmp(matchName, sp->searchNamep);
700 sp->fid.vnode = ntohl(dep->fid.vnode);
701 sp->fid.unique = ntohl(dep->fid.unique);
702 return CM_ERROR_STOPNOW;
705 /* Lower-case matches are next. */
708 if (cm_NoneUpper(matchName)) {
713 /* Upper-case matches are next. */
716 if (cm_NoneLower(matchName)) {
721 /* General matches are last. */
727 sp->fid.vnode = ntohl(dep->fid.vnode);
728 sp->fid.unique = ntohl(dep->fid.unique);
732 /* read the contents of a mount point into the appropriate string.
733 * called with locked scp, and returns with locked scp.
735 long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
742 if (scp->mountPointStringp) return 0;
744 /* otherwise, we have to read it in */
745 lock_ReleaseMutex(&scp->mx);
747 lock_ObtainRead(&scp->bufCreateLock);
748 thyper.LowPart = thyper.HighPart = 0;
749 code = buf_Get(scp, &thyper, &bufp);
750 lock_ReleaseRead(&scp->bufCreateLock);
752 lock_ObtainMutex(&scp->mx);
757 code = cm_SyncOp(scp, bufp, userp, reqp, 0,
758 CM_SCACHESYNC_READ | CM_SCACHESYNC_NEEDCALLBACK);
763 if (cm_HaveBuffer(scp, bufp, 0)) break;
765 /* otherwise load buffer */
766 code = cm_GetBuffer(scp, bufp, NULL, userp, reqp);
771 /* locked, has callback, has valid data in buffer */
772 if ((tlen = scp->length.LowPart) > 1000) return CM_ERROR_TOOBIG;
774 code = CM_ERROR_INVAL;
778 /* someone else did the work while we were out */
779 if (scp->mountPointStringp) {
784 /* otherwise, copy out the link */
785 scp->mountPointStringp = malloc(tlen);
786 memcpy(scp->mountPointStringp, bufp->datap, tlen);
788 /* now make it null-terminated. Note that the original contents of a
789 * link that is a mount point is "#volname." where "." is there just to
790 * be turned into a null. That is, we can trash the last char of the
791 * link without damaging the vol name. This is a stupid convention,
792 * but that's the protocol.
794 scp->mountPointStringp[tlen-1] = 0;
798 if (bufp) buf_Release(bufp);
802 /* called with a locked scp and chases the mount point, yielding outScpp.
803 * scp remains locked, just for simplicity of describing the interface.
805 long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
806 cm_req_t *reqp, cm_scache_t **outScpp)
821 if (scp->mountRootFidp && scp->mountRootGen >= cm_mountRootGen) {
822 tfid = *scp->mountRootFidp;
823 lock_ReleaseMutex(&scp->mx);
824 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
825 lock_ObtainMutex(&scp->mx);
829 /* parse the volume name */
830 mpNamep = scp->mountPointStringp;
832 tlen = strlen(scp->mountPointStringp);
833 mtType = *scp->mountPointStringp;
834 cellNamep = malloc(tlen);
835 volNamep = malloc(tlen);
837 cp = strrchr(mpNamep, ':');
839 /* cellular mount point */
840 memset(cellNamep, 0, tlen);
841 strncpy(cellNamep, mpNamep+1, cp - mpNamep - 1);
842 strcpy(volNamep, cp+1);
843 /* now look up the cell */
844 cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE);
848 strcpy(volNamep, mpNamep+1);
850 cellp = cm_FindCellByID(scp->fid.cell);
854 code = CM_ERROR_NOSUCHCELL;
858 vnLength = strlen(volNamep);
859 if (vnLength >= 8 && strcmp(volNamep + vnLength - 7, ".backup") == 0)
861 else if (vnLength >= 10
862 && strcmp(volNamep + vnLength - 9, ".readonly") == 0)
867 /* check for backups within backups */
869 && (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
870 == CM_SCACHEFLAG_RO) {
871 code = CM_ERROR_NOSUCHVOLUME;
875 /* now we need to get the volume */
876 lock_ReleaseMutex(&scp->mx);
877 code = cm_GetVolumeByName(cellp, volNamep, userp, reqp, 0, &volp);
878 lock_ObtainMutex(&scp->mx);
881 /* save the parent of the volume root for this is the
882 * place where the volume is mounted and we must remember
883 * this in the volume structure rather than just in the
884 * scache entry lest the scache entry gets recycled
887 lock_ObtainMutex(&volp->mx);
888 if(volp->dotdotFidp == (cm_fid_t *) NULL)
889 volp->dotdotFidp = (cm_fid_t *) malloc(sizeof(cm_fid_t));
890 *(volp->dotdotFidp) = dscp->fid;
891 lock_ReleaseMutex(&volp->mx);
893 if (scp->mountRootFidp == 0) {
894 scp->mountRootFidp = malloc(sizeof(cm_fid_t));
896 scp->mountRootFidp->cell = cellp->cellID;
897 /* if the mt pt is in a read-only volume (not just a
898 * backup), and if there is a read-only volume for the
899 * target, and if this is a type '#' mount point, use
900 * the read-only, otherwise use the one specified.
902 if (mtType == '#' && (scp->flags & CM_SCACHEFLAG_PURERO)
903 && volp->roID != 0 && type == RWVOL)
906 scp->mountRootFidp->volume = volp->roID;
907 else if (type == BACKVOL)
908 scp->mountRootFidp->volume = volp->bkID;
910 scp->mountRootFidp->volume = volp->rwID;
912 /* the rest of the fid is a magic number */
913 scp->mountRootFidp->vnode = 1;
914 scp->mountRootFidp->unique = 1;
915 scp->mountRootGen = cm_mountRootGen;
917 tfid = *scp->mountRootFidp;
918 lock_ReleaseMutex(&scp->mx);
919 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
920 lock_ObtainMutex(&scp->mx);
929 int cm_ExpandSysName(char *inp, char *outp, long outSize)
934 tp = strrchr(inp, '@');
935 if (tp == NULL) return 0; /* no @sys */
937 if (strcmp(tp, "@sys") != 0) return 0; /* no @sys */
939 /* caller just wants to know if this is a valid @sys type of name */
940 if (outp == NULL) return 1;
942 /* otherwise generate the properly expanded @sys name */
943 prefixCount = tp - inp;
945 strncpy(outp, inp, prefixCount); /* copy out "a." from "a.@sys" */
946 outp[prefixCount] = 0; /* null terminate the "a." */
947 strcat(outp, cm_sysName); /* append i386_nt40 */
951 long cm_Lookup(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
952 cm_req_t *reqp, cm_scache_t **outpScpp)
955 int dnlcHit = 1; /* did we hit in the dnlc? yes, we did */
956 cm_scache_t *tscp = NULL;
957 cm_scache_t *mountedScp;
958 cm_lookupSearch_t rock;
961 if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
962 && strcmp(namep, "..") == 0) {
963 if (dscp->dotdotFidp == (cm_fid_t *)NULL
964 || dscp->dotdotFidp->volume == 0)
965 return CM_ERROR_NOSUCHVOLUME;
966 rock.fid = *dscp->dotdotFidp;
970 if (cm_ExpandSysName(namep, tname, sizeof(tname))) {
973 memset(&rock, 0, sizeof(rock));
974 rock.fid.cell = dscp->fid.cell;
975 rock.fid.volume = dscp->fid.volume;
976 rock.searchNamep = namep;
977 rock.caseFold = (flags & CM_FLAG_CASEFOLD);
978 rock.hasTilde = ((strchr(namep, '~') != NULL) ? 1 : 0);
980 /* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
981 code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
982 (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
984 /* code == 0 means we fell off the end of the dir, while stopnow means
985 * that we stopped early, probably because we found the entry we're
986 * looking for. Any other non-zero code is an error.
988 if (code && code != CM_ERROR_STOPNOW) return code;
991 if (flags & CM_FLAG_CHECKPATH)
992 return CM_ERROR_NOSUCHPATH;
994 return CM_ERROR_NOSUCHFILE;
997 if ( !tscp ) /* we did not find it in the dnlc */
1000 code = cm_GetSCache(&rock.fid, &tscp, userp, reqp);
1001 if (code) return code;
1003 /* tscp is now held */
1005 lock_ObtainMutex(&tscp->mx);
1006 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1007 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1009 lock_ReleaseMutex(&tscp->mx);
1010 cm_ReleaseSCache(tscp);
1013 /* tscp is now locked */
1015 if (!(flags & CM_FLAG_NOMOUNTCHASE)
1016 && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
1017 /* mount points are funny: they have a volume name to mount
1020 code = cm_ReadMountPoint(tscp, userp, reqp);
1022 code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
1024 lock_ReleaseMutex(&tscp->mx);
1025 cm_ReleaseSCache(tscp);
1032 lock_ReleaseMutex(&tscp->mx);
1035 /* copy back pointer */
1038 /* insert scache in dnlc */
1039 if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) ) {
1040 /* lock the directory entry to prevent racing callback revokes */
1041 lock_ObtainMutex(&dscp->mx);
1042 if ( dscp->cbServerp && dscp->cbExpires )
1043 cm_dnlcEnter(dscp, namep, tscp);
1044 lock_ReleaseMutex(&dscp->mx);
1051 long cm_Unlink(cm_scache_t *dscp, char *namep, cm_user_t *userp, cm_req_t *reqp)
1057 AFSFetchStatus newDirStatus;
1060 /* make sure we don't screw up the dir status during the merge */
1061 lock_ObtainMutex(&dscp->mx);
1062 sflags = CM_SCACHESYNC_STOREDATA;
1063 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1064 lock_ReleaseMutex(&dscp->mx);
1065 if (code) return code;
1068 afsFid.Volume = dscp->fid.volume;
1069 afsFid.Vnode = dscp->fid.vnode;
1070 afsFid.Unique = dscp->fid.unique;
1072 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1075 code = RXAFS_RemoveFile(connp->callp, &afsFid, namep,
1076 &newDirStatus, &volSync);
1078 } while (cm_Analyze(connp, userp, reqp,
1079 &dscp->fid, &volSync, NULL, code));
1080 code = cm_MapRPCError(code, reqp);
1082 lock_ObtainMutex(&dscp->mx);
1083 cm_dnlcRemove(dscp, namep);
1084 cm_SyncOpDone(dscp, NULL, sflags);
1085 if (code == 0) cm_MergeStatus(dscp, &newDirStatus, &volSync, userp, 0);
1086 lock_ReleaseMutex(&dscp->mx);
1091 /* called with a locked vnode, and fills in the link info.
1092 * returns this the vnode still locked.
1094 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1101 lock_AssertMutex(&linkScp->mx);
1102 if (!linkScp->mountPointStringp) {
1103 /* read the link data */
1104 lock_ReleaseMutex(&linkScp->mx);
1105 thyper.LowPart = thyper.HighPart = 0;
1106 code = buf_Get(linkScp, &thyper, &bufp);
1107 lock_ObtainMutex(&linkScp->mx);
1108 if (code) return code;
1110 code = cm_SyncOp(linkScp, bufp, userp, reqp, 0,
1111 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1116 if (cm_HaveBuffer(linkScp, bufp, 0)) break;
1118 code = cm_GetBuffer(linkScp, bufp, NULL, userp, reqp);
1123 } /* while loop to get the data */
1125 /* now if we still have no link read in,
1126 * copy the data from the buffer */
1127 if ((temp = linkScp->length.LowPart) >= 1024) {
1129 return CM_ERROR_TOOBIG;
1132 /* otherwise, it fits; make sure it is still null (could have
1133 * lost race with someone else referencing this link above),
1134 * and if so, copy in the data.
1136 if (linkScp->mountPointStringp == NULL) {
1137 linkScp->mountPointStringp = malloc(temp+1);
1138 strncpy(linkScp->mountPointStringp, bufp->datap, temp);
1139 linkScp->mountPointStringp[temp] = 0; /* null terminate */
1142 } /* don't have sym link contents cached */
1147 /* called with a held vnode and a path suffix, with the held vnode being a
1148 * symbolic link. Our goal is to generate a new path to interpret, and return
1149 * this new path in newSpaceBufferp. If the new vnode is relative to a dir
1150 * other than the directory containing the symbolic link, then the new root is
1151 * returned in *newRootScpp, otherwise a null is returned there.
1153 long cm_AssembleLink(cm_scache_t *linkScp, char *pathSuffixp,
1154 cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1155 cm_user_t *userp, cm_req_t *reqp)
1161 lock_ObtainMutex(&linkScp->mx);
1162 code = cm_HandleLink(linkScp, userp, reqp);
1163 if (code) goto done;
1165 /* if we may overflow the buffer, bail out; buffer is signficantly
1166 * bigger than max path length, so we don't really have to worry about
1167 * being a little conservative here.
1169 if (strlen(linkScp->mountPointStringp) + strlen(pathSuffixp) + 2
1170 >= CM_UTILS_SPACESIZE)
1171 return CM_ERROR_TOOBIG;
1173 tsp = cm_GetSpace();
1174 linkp = linkScp->mountPointStringp;
1175 if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1176 if (strlen(linkp) > cm_mountRootLen)
1177 strcpy(tsp->data, linkp+cm_mountRootLen+1);
1180 *newRootScpp = cm_rootSCachep;
1181 cm_HoldSCache(cm_rootSCachep);
1182 } else if (*linkp == '\\' || *linkp == '/') {
1183 strcpy(tsp->data, linkp+1);
1184 *newRootScpp = cm_rootSCachep;
1185 cm_HoldSCache(cm_rootSCachep);
1188 /* a relative link */
1189 strcpy(tsp->data, linkp);
1190 *newRootScpp = NULL;
1192 if (pathSuffixp[0] != 0) { /* if suffix string is non-null */
1193 strcat(tsp->data, "\\");
1194 strcat(tsp->data, pathSuffixp);
1196 *newSpaceBufferp = tsp;
1200 lock_ReleaseMutex(&linkScp->mx);
1204 long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
1205 cm_user_t *userp, char *tidPathp, cm_req_t *reqp, cm_scache_t **outScpp)
1208 char *tp; /* ptr moving through input buffer */
1209 char tc; /* temp char */
1210 int haveComponent; /* has new component started? */
1211 char component[256]; /* this is the new component */
1212 char *cp; /* component name being assembled */
1213 cm_scache_t *tscp; /* current location in the hierarchy */
1214 cm_scache_t *nscp; /* next dude down */
1215 cm_scache_t *dirScp; /* last dir we searched */
1216 cm_scache_t *linkScp; /* new root for the symlink we just
1218 cm_space_t *psp; /* space for current path, if we've hit
1220 cm_space_t *tempsp; /* temp vbl */
1221 char *restp; /* rest of the pathname to interpret */
1222 int symlinkCount; /* count of # of symlinks traversed */
1223 int extraFlag; /* avoid chasing mt pts for dir cmd */
1224 int phase = 1; /* 1 = tidPathp, 2 = pathp */
1234 cm_HoldSCache(tscp);
1239 /* map Unix slashes into DOS ones so we can interpret Unix
1242 if (tc == '/') tc = '\\';
1244 if (!haveComponent) {
1245 if (tc == '\\') continue;
1262 /* we have a component here */
1263 if (tc == 0 || tc == '\\') {
1264 /* end of the component; we're at the last
1265 * component if tc == 0. However, if the last
1266 * is a symlink, we have more to do.
1268 *cp++ = 0; /* add null termination */
1270 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
1271 extraFlag = CM_FLAG_NOMOUNTCHASE;
1272 code = cm_Lookup(tscp, component,
1274 userp, reqp, &nscp);
1276 cm_ReleaseSCache(tscp);
1277 if (psp) cm_FreeSpace(psp);
1280 haveComponent = 0; /* component done */
1281 dirScp = tscp; /* for some symlinks */
1282 tscp = nscp; /* already held */
1283 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
1285 cm_ReleaseSCache(dirScp);
1289 /* now, if tscp is a symlink, we should follow
1290 * it and assemble the path again.
1292 lock_ObtainMutex(&tscp->mx);
1293 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1294 CM_SCACHESYNC_GETSTATUS
1295 | CM_SCACHESYNC_NEEDCALLBACK);
1297 lock_ReleaseMutex(&tscp->mx);
1298 cm_ReleaseSCache(tscp);
1299 cm_ReleaseSCache(dirScp);
1302 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
1303 /* this is a symlink; assemble a new buffer */
1304 lock_ReleaseMutex(&tscp->mx);
1305 if (symlinkCount++ >= 16) {
1306 cm_ReleaseSCache(tscp);
1307 cm_ReleaseSCache(dirScp);
1308 if (psp) cm_FreeSpace(psp);
1309 return CM_ERROR_TOOBIG;
1311 if (tc == 0) restp = "";
1313 code = cm_AssembleLink(tscp, restp,
1314 &linkScp, &tempsp, userp, reqp);
1316 /* something went wrong */
1317 cm_ReleaseSCache(tscp);
1318 cm_ReleaseSCache(dirScp);
1322 /* otherwise, tempsp has the new path,
1323 * and linkScp is the new root from
1324 * which to interpret that path.
1325 * Continue with the namei processing,
1326 * also doing the bookkeeping for the
1327 * space allocation and tracking the
1328 * vnode reference counts.
1330 if (psp) cm_FreeSpace(psp);
1333 cm_ReleaseSCache(tscp);
1334 tscp = linkScp; /* already held
1335 * by AssembleLink */
1336 /* now, if linkScp is null, that's
1337 * AssembleLink's way of telling us that
1338 * the sym link is relative to the dir
1339 * containing the link. We have a ref
1340 * to it in dirScp, and we hold it now
1341 * and reuse it as the new spot in the
1345 cm_HoldSCache(dirScp);
1348 } /* if we have a sym link */
1350 /* not a symlink, we may be done */
1351 lock_ReleaseMutex(&tscp->mx);
1358 cm_ReleaseSCache(dirScp);
1363 cm_ReleaseSCache(dirScp);
1364 } /* end of a component */
1366 } /* we have a component */
1367 } /* big while loop over all components */
1370 if (psp) cm_FreeSpace(psp);
1371 if (code == 0) *outScpp = tscp;
1375 /* called with a dir, and a vnode within the dir that happens to be a symlink.
1376 * We chase the link, and return a held pointer to the target, if it exists,
1377 * in *outScpp. If we succeed, we return 0, otherwise we return an error code
1378 * and do not hold or return a target vnode.
1380 * This is very similar to calling cm_NameI with the last component of a name,
1381 * which happens to be a symlink, except that we've already passed by the name.
1383 * This function is typically called by the directory listing functions, which
1384 * encounter symlinks but need to return the proper file length so programs
1385 * like "more" work properly when they make use of the attributes retrieved from
1388 * The input vnode should not be locked when this function is called.
1390 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
1391 cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
1395 cm_scache_t *newRootScp;
1397 osi_Log1(afsd_logp, "Evaluating symlink vp %x", linkScp);
1399 code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
1400 if (code) return code;
1402 /* now, if newRootScp is NULL, we're really being told that the symlink
1403 * is relative to the current directory (dscp).
1405 if (newRootScp == NULL) {
1407 cm_HoldSCache(dscp);
1410 code = cm_NameI(newRootScp, spacep->data,
1411 CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
1412 userp, NULL, reqp, outScpp);
1414 /* this stuff is allocated no matter what happened on the namei call,
1416 cm_FreeSpace(spacep);
1417 cm_ReleaseSCache(newRootScp);
1422 /* make this big enough so that one buffer of dir pages won't overflow. We'll
1423 * check anyway, but we want to minimize the chance that we have to leave stuff
1426 #define CM_BULKMAX 128
1428 /* rock for bulk stat calls */
1429 typedef struct cm_bulkStat {
1430 osi_hyper_t bufOffset; /* only do it for things in this buffer page */
1432 /* info for the actual call */
1433 int counter; /* next free slot */
1434 AFSFid fids[CM_BULKMAX];
1435 AFSFetchStatus stats[CM_BULKMAX];
1436 AFSCallBack callbacks[CM_BULKMAX];
1439 /* for a given entry, make sure that it isn't in the stat cache, and then
1440 * add it to the list of file IDs to be obtained.
1442 * Don't bother adding it if we already have a vnode. Note that the dir
1443 * is locked, so we have to be careful checking the vnode we're thinking of
1444 * processing, to avoid deadlocks.
1446 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
1457 /* Don't overflow bsp. */
1458 if (bsp->counter >= CM_BULKMAX)
1459 return CM_ERROR_STOPNOW;
1461 thyper.LowPart = buf_bufferSize;
1462 thyper.HighPart = 0;
1463 thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
1465 /* thyper is now the first byte past the end of the record we're
1466 * interested in, and bsp->bufOffset is the first byte of the record
1467 * we're interested in.
1468 * Skip data in the others.
1471 if (LargeIntegerLessThan(*offp, bsp->bufOffset))
1473 if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
1474 return CM_ERROR_STOPNOW;
1475 if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
1478 tfid.cell = scp->fid.cell;
1479 tfid.volume = scp->fid.volume;
1480 tfid.vnode = ntohl(dep->fid.vnode);
1481 tfid.unique = ntohl(dep->fid.unique);
1482 tscp = cm_FindSCache(&tfid);
1484 if (lock_TryMutex(&tscp->mx)) {
1485 /* we have an entry that we can look at */
1486 if (cm_HaveCallback(tscp)) {
1487 /* we have a callback on it. Don't bother
1488 * fetching this stat entry, since we're happy
1489 * with the info we have.
1491 lock_ReleaseMutex(&tscp->mx);
1492 cm_ReleaseSCache(tscp);
1495 lock_ReleaseMutex(&tscp->mx);
1497 cm_ReleaseSCache(tscp);
1501 bsp->fids[i].Volume = scp->fid.volume;
1502 bsp->fids[i].Vnode = tfid.vnode;
1503 bsp->fids[i].Unique = tfid.unique;
1507 /* called with a locked scp and a pointer to a buffer. Make bulk stat
1508 * calls on all undeleted files in the page of the directory specified.
1510 void cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
1514 cm_bulkStat_t bb; /* this is *BIG*, probably 12K or so;
1515 * watch for stack problems */
1516 AFSCBFids fidStruct;
1517 AFSBulkStats statStruct;
1519 AFSCBs callbackStruct;
1522 cm_callbackRequest_t cbReq;
1529 osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%x", (long) dscp);
1531 /* should be on a buffer boundary */
1532 osi_assert((offsetp->LowPart & (buf_bufferSize - 1)) == 0);
1535 bb.bufOffset = *offsetp;
1537 /* first, assemble the file IDs we need to stat */
1538 code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) &bb, offsetp, userp,
1541 /* if we failed, bail out early */
1542 if (code && code != CM_ERROR_STOPNOW) return;
1544 /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
1545 * make the calls to create the entries. Handle AFSCBMAX files at a
1549 while(filex < bb.counter) {
1550 filesThisCall = bb.counter - filex;
1551 if (filesThisCall > AFSCBMAX) filesThisCall = AFSCBMAX;
1553 fidStruct.AFSCBFids_len = filesThisCall;
1554 fidStruct.AFSCBFids_val = &bb.fids[filex];
1555 statStruct.AFSBulkStats_len = filesThisCall;
1556 statStruct.AFSBulkStats_val = &bb.stats[filex];
1557 callbackStruct.AFSCBs_len = filesThisCall;
1558 callbackStruct.AFSCBs_val = &bb.callbacks[filex];
1559 cm_StartCallbackGrantingCall(NULL, &cbReq);
1560 osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
1562 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1565 code = RXAFS_BulkStatus(connp->callp, &fidStruct,
1566 &statStruct, &callbackStruct, &volSync);
1568 } while (cm_Analyze(connp, userp, reqp, &dscp->fid,
1569 &volSync, &cbReq, code));
1570 code = cm_MapRPCError(code, reqp);
1572 osi_Log0(afsd_logp, "CALL BulkStatus DONE");
1574 /* may as well quit on an error, since we're not going to do
1575 * much better on the next immediate call, either.
1579 /* otherwise, we should do the merges */
1580 for(i = 0; i<filesThisCall; i++) {
1582 tfid.cell = dscp->fid.cell;
1583 tfid.volume = bb.fids[j].Volume;
1584 tfid.vnode = bb.fids[j].Vnode;
1585 tfid.unique = bb.fids[j].Unique;
1586 code = cm_GetSCache(&tfid, &scp, userp, reqp);
1587 if (code != 0) continue;
1589 /* otherwise, if this entry has no callback info,
1592 lock_ObtainMutex(&scp->mx);
1593 /* now, we have to be extra paranoid on merging in this
1594 * information, since we didn't use cm_SyncOp before
1595 * starting the fetch to make sure that no bad races
1596 * were occurring. Specifically, we need to make sure
1597 * we don't obliterate any newer information in the
1598 * vnode than have here.
1600 * Right now, be pretty conservative: if there's a
1601 * callback or a pending call, skip it.
1603 if (scp->cbServerp == NULL
1605 (CM_SCACHEFLAG_FETCHING
1606 | CM_SCACHEFLAG_STORING
1607 | CM_SCACHEFLAG_SIZESTORING))) {
1608 cm_EndCallbackGrantingCall(scp, &cbReq,
1610 CM_CALLBACK_MAINTAINCOUNT);
1611 cm_MergeStatus(scp, &bb.stats[j], &volSync,
1614 lock_ReleaseMutex(&scp->mx);
1615 cm_ReleaseSCache(scp);
1616 } /* all files in the response */
1617 /* now tell it to drop the count,
1618 * after doing the vnode processing above */
1619 cm_EndCallbackGrantingCall(NULL, NULL, NULL, 0);
1621 filex += filesThisCall;
1622 } /* while there are still more files to process */
1623 osi_Log0(afsd_logp, "END cm_TryBulkStat");
1626 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
1630 /* initialize store back mask as inexpensive local variable */
1632 memset(statusp, 0, sizeof(AFSStoreStatus));
1634 /* copy out queued info from scache first, if scp passed in */
1636 if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
1637 statusp->ClientModTime = scp->clientModTime;
1638 mask |= AFS_SETMODTIME;
1639 scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
1644 /* now add in our locally generated request */
1645 if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
1646 statusp->ClientModTime = attrp->clientModTime;
1647 mask |= AFS_SETMODTIME;
1649 if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
1650 statusp->UnixModeBits = attrp->unixModeBits;
1651 mask |= AFS_SETMODE;
1653 if (attrp->mask & CM_ATTRMASK_OWNER) {
1654 statusp->Owner = attrp->owner;
1655 mask |= AFS_SETOWNER;
1657 if (attrp->mask & CM_ATTRMASK_GROUP) {
1658 statusp->Group = attrp->group;
1659 mask |= AFS_SETGROUP;
1662 statusp->Mask = mask;
1665 /* set the file size, and make sure that all relevant buffers have been
1666 * truncated. Ensure that any partially truncated buffers have been zeroed
1667 * to the end of the buffer.
1669 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
1675 /* start by locking out buffer creation */
1676 lock_ObtainWrite(&scp->bufCreateLock);
1678 /* verify that this is a file, not a dir or a symlink */
1679 lock_ObtainMutex(&scp->mx);
1680 code = cm_SyncOp(scp, NULL, userp, reqp, 0,
1681 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1682 if (code) goto done;
1684 if (scp->fileType != CM_SCACHETYPE_FILE) {
1685 code = CM_ERROR_ISDIR;
1690 if (LargeIntegerLessThan(*sizep, scp->length))
1695 lock_ReleaseMutex(&scp->mx);
1697 /* can't hold scp->mx lock here, since we may wait for a storeback to
1698 * finish if the buffer package is cleaning a buffer by storing it to
1702 buf_Truncate(scp, userp, reqp, sizep);
1704 /* now ensure that file length is short enough, and update truncPos */
1705 lock_ObtainMutex(&scp->mx);
1707 /* make sure we have a callback (so we have the right value for the
1708 * length), and wait for it to be safe to do a truncate.
1710 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
1711 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
1712 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
1713 if (code) goto done;
1715 if (LargeIntegerLessThan(*sizep, scp->length)) {
1716 /* a real truncation. If truncPos is not set yet, or is bigger
1717 * than where we're truncating the file, set truncPos to this
1722 if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
1723 || LargeIntegerLessThan(*sizep, scp->length)) {
1725 scp->truncPos = *sizep;
1726 scp->mask |= CM_SCACHEMASK_TRUNCPOS;
1728 /* in either case, the new file size has been changed */
1729 scp->length = *sizep;
1730 scp->mask |= CM_SCACHEMASK_LENGTH;
1732 else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
1733 /* really extending the file */
1734 scp->length = *sizep;
1735 scp->mask |= CM_SCACHEMASK_LENGTH;
1738 /* done successfully */
1742 lock_ReleaseMutex(&scp->mx);
1743 lock_ReleaseWrite(&scp->bufCreateLock);
1748 /* set the file size or other attributes (but not both at once) */
1749 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
1754 AFSFetchStatus afsOutStatus;
1758 AFSStoreStatus afsInStatus;
1760 /* handle file length setting */
1761 if (attrp->mask & CM_ATTRMASK_LENGTH)
1762 return cm_SetLength(scp, &attrp->length, userp, reqp);
1764 flags = CM_SCACHESYNC_STORESTATUS;
1766 lock_ObtainMutex(&scp->mx);
1767 /* otherwise, we have to make an RPC to get the status */
1768 code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
1770 /* make the attr structure */
1771 cm_StatusFromAttr(&afsInStatus, scp, attrp);
1773 lock_ReleaseMutex(&scp->mx);
1774 if (code) return code;
1776 /* now make the RPC */
1777 osi_Log1(afsd_logp, "CALL StoreStatus vp %x", (long) scp);
1778 tfid.Volume = scp->fid.volume;
1779 tfid.Vnode = scp->fid.vnode;
1780 tfid.Unique = scp->fid.unique;
1782 code = cm_Conn(&scp->fid, userp, reqp, &connp);
1785 code = RXAFS_StoreStatus(connp->callp, &tfid,
1786 &afsInStatus, &afsOutStatus, &volSync);
1788 } while (cm_Analyze(connp, userp, reqp,
1789 &scp->fid, &volSync, NULL, code));
1790 code = cm_MapRPCError(code, reqp);
1792 osi_Log1(afsd_logp, "CALL StoreStatus DONE, code %d", code);
1794 lock_ObtainMutex(&scp->mx);
1795 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
1797 cm_MergeStatus(scp, &afsOutStatus, &volSync, userp,
1798 CM_MERGEFLAG_FORCE);
1800 /* if we're changing the mode bits, discard the ACL cache,
1801 * since we changed the mode bits.
1803 if (afsInStatus.Mask & AFS_SETMODE) cm_FreeAllACLEnts(scp);
1804 lock_ReleaseMutex(&scp->mx);
1808 long cm_Create(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
1809 cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
1814 cm_callbackRequest_t cbReq;
1819 AFSStoreStatus inStatus;
1820 AFSFetchStatus updatedDirStatus;
1821 AFSFetchStatus newFileStatus;
1822 AFSCallBack newFileCallback;
1825 /* can't create names with @sys in them; must expand it manually first.
1826 * return "invalid request" if they try.
1828 if (cm_ExpandSysName(namep, NULL, 0)) {
1829 return CM_ERROR_ATSYS;
1832 /* before starting the RPC, mark that we're changing the file data, so
1833 * that someone who does a chmod will know to wait until our call
1836 lock_ObtainMutex(&dscp->mx);
1837 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
1839 cm_StartCallbackGrantingCall(NULL, &cbReq);
1841 lock_ReleaseMutex(&dscp->mx);
1847 cm_StatusFromAttr(&inStatus, NULL, attrp);
1849 /* try the RPC now */
1851 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1854 dirAFSFid.Volume = dscp->fid.volume;
1855 dirAFSFid.Vnode = dscp->fid.vnode;
1856 dirAFSFid.Unique = dscp->fid.unique;
1857 code = RXAFS_CreateFile(connp->callp, &dirAFSFid, namep,
1858 &inStatus, &newAFSFid, &newFileStatus,
1859 &updatedDirStatus, &newFileCallback,
1861 } while (cm_Analyze(connp, userp, reqp,
1862 &dscp->fid, &volSync, &cbReq, code));
1863 code = cm_MapRPCError(code, reqp);
1865 lock_ObtainMutex(&dscp->mx);
1866 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
1868 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
1870 lock_ReleaseMutex(&dscp->mx);
1872 /* now try to create the file's entry, too, but be careful to
1873 * make sure that we don't merge in old info. Since we weren't locking
1874 * out any requests during the file's creation, we may have pretty old
1878 newFid.cell = dscp->fid.cell;
1879 newFid.volume = dscp->fid.volume;
1880 newFid.vnode = newAFSFid.Vnode;
1881 newFid.unique = newAFSFid.Unique;
1882 code = cm_GetSCache(&newFid, &scp, userp, reqp);
1884 lock_ObtainMutex(&scp->mx);
1885 if (!cm_HaveCallback(scp)) {
1886 cm_MergeStatus(scp, &newFileStatus, &volSync,
1888 cm_EndCallbackGrantingCall(scp, &cbReq,
1889 &newFileCallback, 0);
1892 lock_ReleaseMutex(&scp->mx);
1897 /* make sure we end things properly */
1899 cm_EndCallbackGrantingCall(NULL, NULL, NULL, 0);
1904 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
1908 lock_ObtainWrite(&scp->bufCreateLock);
1909 code = buf_CleanVnode(scp, userp, reqp);
1910 lock_ReleaseWrite(&scp->bufCreateLock);
1912 lock_ObtainMutex(&scp->mx);
1913 scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA
1914 | CM_SCACHEFLAG_OUTOFSPACE);
1915 if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
1916 | CM_SCACHEMASK_CLIENTMODTIME
1917 | CM_SCACHEMASK_LENGTH))
1918 code = cm_StoreMini(scp, userp, reqp);
1919 lock_ReleaseMutex(&scp->mx);
1924 long cm_MakeDir(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
1925 cm_user_t *userp, cm_req_t *reqp)
1930 cm_callbackRequest_t cbReq;
1935 AFSStoreStatus inStatus;
1936 AFSFetchStatus updatedDirStatus;
1937 AFSFetchStatus newDirStatus;
1938 AFSCallBack newDirCallback;
1941 /* can't create names with @sys in them; must expand it manually first.
1942 * return "invalid request" if they try.
1944 if (cm_ExpandSysName(namep, NULL, 0)) {
1945 return CM_ERROR_ATSYS;
1948 /* before starting the RPC, mark that we're changing the directory
1949 * data, so that someone who does a chmod on the dir will wait until
1950 * our call completes.
1952 lock_ObtainMutex(&dscp->mx);
1953 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
1955 cm_StartCallbackGrantingCall(NULL, &cbReq);
1957 lock_ReleaseMutex(&dscp->mx);
1963 cm_StatusFromAttr(&inStatus, NULL, attrp);
1965 /* try the RPC now */
1967 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1970 dirAFSFid.Volume = dscp->fid.volume;
1971 dirAFSFid.Vnode = dscp->fid.vnode;
1972 dirAFSFid.Unique = dscp->fid.unique;
1973 code = RXAFS_MakeDir(connp->callp, &dirAFSFid, namep,
1974 &inStatus, &newAFSFid, &newDirStatus,
1975 &updatedDirStatus, &newDirCallback,
1977 } while (cm_Analyze(connp, userp, reqp,
1978 &dscp->fid, &volSync, &cbReq, code));
1979 code = cm_MapRPCError(code, reqp);
1981 lock_ObtainMutex(&dscp->mx);
1982 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
1984 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
1986 lock_ReleaseMutex(&dscp->mx);
1988 /* now try to create the new dir's entry, too, but be careful to
1989 * make sure that we don't merge in old info. Since we weren't locking
1990 * out any requests during the file's creation, we may have pretty old
1994 newFid.cell = dscp->fid.cell;
1995 newFid.volume = dscp->fid.volume;
1996 newFid.vnode = newAFSFid.Vnode;
1997 newFid.unique = newAFSFid.Unique;
1998 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2000 lock_ObtainMutex(&scp->mx);
2001 if (!cm_HaveCallback(scp)) {
2002 cm_MergeStatus(scp, &newDirStatus, &volSync,
2004 cm_EndCallbackGrantingCall(scp, &cbReq,
2005 &newDirCallback, 0);
2008 lock_ReleaseMutex(&scp->mx);
2009 cm_ReleaseSCache(scp);
2013 /* make sure we end things properly */
2015 cm_EndCallbackGrantingCall(NULL, NULL, NULL, 0);
2017 /* and return error code */
2021 long cm_SymLink(cm_scache_t *dscp, char *namep, char *contentsp, long flags,
2022 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
2030 AFSStoreStatus inStatus;
2031 AFSFetchStatus updatedDirStatus;
2032 AFSFetchStatus newLinkStatus;
2035 /* before starting the RPC, mark that we're changing the directory data,
2036 * so that someone who does a chmod on the dir will wait until our
2039 lock_ObtainMutex(&dscp->mx);
2040 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2041 lock_ReleaseMutex(&dscp->mx);
2046 cm_StatusFromAttr(&inStatus, NULL, attrp);
2048 /* try the RPC now */
2050 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2053 dirAFSFid.Volume = dscp->fid.volume;
2054 dirAFSFid.Vnode = dscp->fid.vnode;
2055 dirAFSFid.Unique = dscp->fid.unique;
2056 code = RXAFS_Symlink(connp->callp, &dirAFSFid, namep, contentsp,
2057 &inStatus, &newAFSFid, &newLinkStatus,
2058 &updatedDirStatus, &volSync);
2059 } while (cm_Analyze(connp, userp, reqp,
2060 &dscp->fid, &volSync, NULL, code));
2061 code = cm_MapRPCError(code, reqp);
2063 lock_ObtainMutex(&dscp->mx);
2064 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2066 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2068 lock_ReleaseMutex(&dscp->mx);
2070 /* now try to create the new dir's entry, too, but be careful to
2071 * make sure that we don't merge in old info. Since we weren't locking
2072 * out any requests during the file's creation, we may have pretty old
2076 newFid.cell = dscp->fid.cell;
2077 newFid.volume = dscp->fid.volume;
2078 newFid.vnode = newAFSFid.Vnode;
2079 newFid.unique = newAFSFid.Unique;
2080 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2082 lock_ObtainMutex(&scp->mx);
2083 if (!cm_HaveCallback(scp)) {
2084 cm_MergeStatus(scp, &newLinkStatus, &volSync,
2087 lock_ReleaseMutex(&scp->mx);
2088 cm_ReleaseSCache(scp);
2092 /* and return error code */
2096 long cm_RemoveDir(cm_scache_t *dscp, char *namep, cm_user_t *userp,
2103 AFSFetchStatus updatedDirStatus;
2106 /* before starting the RPC, mark that we're changing the directory data,
2107 * so that someone who does a chmod on the dir will wait until our
2110 lock_ObtainMutex(&dscp->mx);
2111 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2112 lock_ReleaseMutex(&dscp->mx);
2118 /* try the RPC now */
2120 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2123 dirAFSFid.Volume = dscp->fid.volume;
2124 dirAFSFid.Vnode = dscp->fid.vnode;
2125 dirAFSFid.Unique = dscp->fid.unique;
2126 code = RXAFS_RemoveDir(connp->callp, &dirAFSFid, namep,
2127 &updatedDirStatus, &volSync);
2128 } while (cm_Analyze(connp, userp, reqp,
2129 &dscp->fid, &volSync, NULL, code));
2130 code = cm_MapRPCErrorRmdir(code, reqp);
2132 lock_ObtainMutex(&dscp->mx);
2133 cm_dnlcRemove(dscp, namep);
2134 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2136 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2138 lock_ReleaseMutex(&dscp->mx);
2140 /* and return error code */
2144 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
2146 /* grab mutex on contents */
2147 lock_ObtainMutex(&scp->mx);
2149 /* reset the prefetch info */
2150 scp->prefetch.base.LowPart = 0; /* base */
2151 scp->prefetch.base.HighPart = 0;
2152 scp->prefetch.end.LowPart = 0; /* and end */
2153 scp->prefetch.end.HighPart = 0;
2155 /* release mutex on contents */
2156 lock_ReleaseMutex(&scp->mx);
2162 long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
2163 char *newNamep, cm_user_t *userp, cm_req_t *reqp)
2167 AFSFid oldDirAFSFid;
2168 AFSFid newDirAFSFid;
2170 AFSFetchStatus updatedOldDirStatus;
2171 AFSFetchStatus updatedNewDirStatus;
2175 /* before starting the RPC, mark that we're changing the directory data,
2176 * so that someone who does a chmod on the dir will wait until our call
2177 * completes. We do this in vnode order so that we don't deadlock,
2178 * which makes the code a little verbose.
2180 if (oldDscp == newDscp) {
2182 lock_ObtainMutex(&oldDscp->mx);
2183 cm_dnlcRemove(oldDscp, oldNamep);
2184 cm_dnlcRemove(oldDscp, newNamep);
2185 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2186 CM_SCACHESYNC_STOREDATA);
2187 lock_ReleaseMutex(&oldDscp->mx);
2190 /* two distinct dir vnodes */
2192 if (oldDscp->fid.cell != newDscp->fid.cell ||
2193 oldDscp->fid.volume != newDscp->fid.volume)
2194 return CM_ERROR_CROSSDEVLINK;
2196 /* shouldn't happen that we have distinct vnodes for two
2197 * different files, but could due to deliberate attack, or
2198 * stale info. Avoid deadlocks and quit now.
2200 if (oldDscp->fid.vnode == newDscp->fid.vnode)
2201 return CM_ERROR_CROSSDEVLINK;
2203 if (oldDscp->fid.vnode < newDscp->fid.vnode) {
2204 lock_ObtainMutex(&oldDscp->mx);
2205 cm_dnlcRemove(oldDscp, oldNamep);
2206 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2207 CM_SCACHESYNC_STOREDATA);
2208 lock_ReleaseMutex(&oldDscp->mx);
2210 lock_ObtainMutex(&newDscp->mx);
2211 cm_dnlcRemove(newDscp, newNamep);
2212 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2213 CM_SCACHESYNC_STOREDATA);
2214 lock_ReleaseMutex(&newDscp->mx);
2216 /* cleanup first one */
2217 cm_SyncOpDone(oldDscp, NULL,
2218 CM_SCACHESYNC_STOREDATA);
2223 /* lock the new vnode entry first */
2224 lock_ObtainMutex(&newDscp->mx);
2225 cm_dnlcRemove(newDscp, newNamep);
2226 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2227 CM_SCACHESYNC_STOREDATA);
2228 lock_ReleaseMutex(&newDscp->mx);
2230 lock_ObtainMutex(&oldDscp->mx);
2231 cm_dnlcRemove(oldDscp, oldNamep);
2232 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2233 CM_SCACHESYNC_STOREDATA);
2234 lock_ReleaseMutex(&oldDscp->mx);
2236 /* cleanup first one */
2237 cm_SyncOpDone(newDscp, NULL,
2238 CM_SCACHESYNC_STOREDATA);
2242 } /* two distinct vnodes */
2249 /* try the RPC now */
2251 code = cm_Conn(&oldDscp->fid, userp, reqp, &connp);
2254 oldDirAFSFid.Volume = oldDscp->fid.volume;
2255 oldDirAFSFid.Vnode = oldDscp->fid.vnode;
2256 oldDirAFSFid.Unique = oldDscp->fid.unique;
2257 newDirAFSFid.Volume = newDscp->fid.volume;
2258 newDirAFSFid.Vnode = newDscp->fid.vnode;
2259 newDirAFSFid.Unique = newDscp->fid.unique;
2260 code = RXAFS_Rename(connp->callp, &oldDirAFSFid, oldNamep,
2261 &newDirAFSFid, newNamep,
2262 &updatedOldDirStatus, &updatedNewDirStatus,
2264 } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
2265 &volSync, NULL, code));
2266 code = cm_MapRPCError(code, reqp);
2268 /* update the individual stat cache entries for the directories */
2269 lock_ObtainMutex(&oldDscp->mx);
2270 cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
2272 cm_MergeStatus(oldDscp, &updatedOldDirStatus, &volSync,
2275 lock_ReleaseMutex(&oldDscp->mx);
2277 /* and update it for the new one, too, if necessary */
2279 lock_ObtainMutex(&newDscp->mx);
2280 cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
2282 cm_MergeStatus(newDscp, &updatedNewDirStatus, &volSync,
2285 lock_ReleaseMutex(&newDscp->mx);
2288 /* and return error code */
2292 long cm_Lock(cm_scache_t *scp, unsigned char LockType,
2293 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
2294 u_long Timeout, cm_user_t *userp, cm_req_t *reqp,
2298 int Which = ((LockType & 0x1) ? LockRead : LockWrite);
2302 cm_file_lock_t *fileLock;
2306 /* Look for a conflict. Also, if we are asking for a shared lock,
2307 * look for another shared lock, so we don't have to do an RPC.
2311 fileLock = (cm_file_lock_t *)
2312 ((char *) q - offsetof(cm_file_lock_t, fileq));
2313 if ((fileLock->flags &
2314 (CM_FILELOCK_FLAG_INVALID | CM_FILELOCK_FLAG_WAITING))
2316 if ((LockType & 0x1) == 0
2317 || (fileLock->LockType & 0x1) == 0)
2318 return CM_ERROR_WOULDBLOCK;
2327 tfid.Volume = scp->fid.volume;
2328 tfid.Vnode = scp->fid.vnode;
2329 tfid.Unique = scp->fid.unique;
2330 lock_ReleaseMutex(&scp->mx);
2332 code = cm_Conn(&scp->fid, userp, reqp, &connp);
2334 code = RXAFS_SetLock(connp->callp, &tfid, Which,
2336 } while (cm_Analyze(connp, userp, reqp, &scp->fid, &volSync,
2338 lock_ObtainMutex(&scp->mx);
2339 code = cm_MapRPCError(code, reqp);
2342 if (code == 0 || Timeout != 0) {
2343 fileLock = malloc(sizeof(cm_file_lock_t));
2344 fileLock->LockType = LockType;
2345 fileLock->userp = userp;
2347 fileLock->fid = scp->fid;
2348 fileLock->LOffset = LOffset;
2349 fileLock->LLength = LLength;
2350 fileLock->flags = (code == 0 ? 0 : CM_FILELOCK_FLAG_WAITING);
2351 osi_QAdd(&scp->fileLocks, &fileLock->fileq);
2352 lock_ObtainWrite(&cm_scacheLock);
2353 osi_QAdd(&cm_allFileLocks, &fileLock->q);
2354 lock_ReleaseWrite(&cm_scacheLock);
2355 if (code != 0) *lockpp = fileLock;
2360 long cm_Unlock(cm_scache_t *scp, unsigned char LockType,
2361 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
2362 cm_user_t *userp, cm_req_t *reqp)
2365 int Which = ((LockType & 0x1) ? LockRead : LockWrite);
2369 cm_file_lock_t *fileLock, *ourLock;
2370 osi_queue_t *q, *qq;
2371 int anotherReader = 0;
2375 if (LargeIntegerLessThan(LLength, scp->length))
2378 /* Look for our own lock on the list, so as to remove it.
2379 * Also, determine if we're the last reader; if not, avoid an RPC.
2383 fileLock = (cm_file_lock_t *)
2384 ((char *) q - offsetof(cm_file_lock_t, fileq));
2386 && fileLock->userp == userp
2387 && LargeIntegerEqualTo(fileLock->LOffset, LOffset)
2388 && LargeIntegerEqualTo(fileLock->LLength, LLength)) {
2393 else if (fileLock->LockType & 0x1)
2398 /* ignore byte ranges */
2399 if (smallLock && !found)
2402 /* don't try to unlock other people's locks */
2404 return CM_ERROR_WOULDBLOCK;
2406 /* discard lock record */
2407 osi_QRemove(&scp->fileLocks, qq);
2409 * Don't delete it here; let the daemon delete it, to simplify
2410 * the daemon's traversal of the list.
2412 lock_ObtainWrite(&cm_scacheLock);
2413 ourLock->flags |= CM_FILELOCK_FLAG_INVALID;
2414 cm_ReleaseUser(ourLock->userp);
2415 lock_ReleaseWrite(&cm_scacheLock);
2417 if (!anotherReader) {
2418 tfid.Volume = scp->fid.volume;
2419 tfid.Vnode = scp->fid.vnode;
2420 tfid.Unique = scp->fid.unique;
2421 lock_ReleaseMutex(&scp->mx);
2423 code = cm_Conn(&scp->fid, userp, reqp, &connp);
2425 code = RXAFS_ReleaseLock(connp->callp, &tfid, &volSync);
2426 } while (cm_Analyze(connp, userp, reqp, &scp->fid, &volSync,
2428 code = cm_MapRPCError(code, reqp);
2429 lock_ObtainMutex(&scp->mx);
2435 void cm_CheckLocks()
2437 osi_queue_t *q, *nq;
2438 cm_file_lock_t *fileLock;
2447 lock_ObtainWrite(&cm_scacheLock);
2448 q = cm_allFileLocks;
2450 fileLock = (cm_file_lock_t *) q;
2452 if (fileLock->flags & CM_FILELOCK_FLAG_INVALID) {
2453 osi_QRemove(&cm_allFileLocks, q);
2456 else if (!(fileLock->flags & CM_FILELOCK_FLAG_WAITING)) {
2457 tfid.Volume = fileLock->fid.volume;
2458 tfid.Vnode = fileLock->fid.vnode;
2459 tfid.Unique = fileLock->fid.unique;
2460 lock_ReleaseWrite(&cm_scacheLock);
2462 code = cm_Conn(&fileLock->fid, fileLock->userp,
2465 code = RXAFS_ExtendLock(connp->callp, &tfid,
2467 } while (cm_Analyze(connp, fileLock->userp, &req,
2468 &fileLock->fid, &volSync, NULL,
2470 code = cm_MapRPCError(code, &req);
2471 lock_ObtainWrite(&cm_scacheLock);
2475 lock_ReleaseWrite(&cm_scacheLock);
2478 long cm_RetryLock(cm_file_lock_t *oldFileLock, int vcp_is_dead)
2481 int Which = ((oldFileLock->LockType & 0x1) ? LockRead : LockWrite);
2486 cm_file_lock_t *fileLock;
2492 code = CM_ERROR_TIMEDOUT;
2498 /* Look for a conflict. Also, if we are asking for a shared lock,
2499 * look for another shared lock, so we don't have to do an RPC.
2501 code = cm_GetSCache(&oldFileLock->fid, &scp, oldFileLock->userp, &req);
2507 fileLock = (cm_file_lock_t *)
2508 ((char *) q - offsetof(cm_file_lock_t, fileq));
2509 if ((fileLock->flags &
2510 (CM_FILELOCK_FLAG_INVALID | CM_FILELOCK_FLAG_WAITING))
2512 if ((oldFileLock->LockType & 0x1) == 0
2513 || (fileLock->LockType & 0x1) == 0) {
2514 cm_ReleaseSCache(scp);
2515 return CM_ERROR_WOULDBLOCK;
2525 tfid.Volume = oldFileLock->fid.volume;
2526 tfid.Vnode = oldFileLock->fid.vnode;
2527 tfid.Unique = oldFileLock->fid.unique;
2529 code = cm_Conn(&oldFileLock->fid, oldFileLock->userp,
2532 code = RXAFS_SetLock(connp->callp, &tfid, Which,
2534 } while (cm_Analyze(connp, oldFileLock->userp, &req,
2535 &oldFileLock->fid, &volSync,
2537 code = cm_MapRPCError(code, &req);
2541 if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
2542 lock_ObtainMutex(&scp->mx);
2543 osi_QRemove(&scp->fileLocks, &oldFileLock->fileq);
2544 lock_ReleaseMutex(&scp->mx);
2546 lock_ObtainWrite(&cm_scacheLock);
2548 oldFileLock->flags = 0;
2549 else if (code != CM_ERROR_WOULDBLOCK) {
2550 oldFileLock->flags |= CM_FILELOCK_FLAG_INVALID;
2551 cm_ReleaseUser(oldFileLock->userp);
2553 lock_ReleaseWrite(&cm_scacheLock);