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;
38 * Case-folding array. This was constructed by inspecting of SMBtrace output.
39 * I do not know anything more about it.
41 unsigned char cm_foldUpper[256] = {
42 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
43 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
44 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
45 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
46 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
47 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
48 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
49 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
50 0x40, 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, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
54 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
55 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
56 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
57 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
58 0x80, 0x9a, 0x90, 0x41, 0x8e, 0x41, 0x8f, 0x80,
59 0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x8e, 0x8f,
60 0x90, 0x92, 0x92, 0x4f, 0x99, 0x4f, 0x55, 0x55,
61 0x59, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
62 0x41, 0x49, 0x4f, 0x55, 0xa5, 0xa5, 0x56, 0xa7,
63 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
64 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
65 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
66 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
67 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
68 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
69 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
70 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
71 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
72 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
73 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
77 * Case-insensitive string comparison. We used to use stricmp, but it doesn't
78 * know about 8-bit characters (e.g. 129 is lowercase u-umlaut, 154 is
79 * upper-case u-umlaut).
81 int cm_stricmp(const char *str1, const char *str2)
93 c1 = (char) cm_foldUpper[(unsigned char)(*str1++)];
94 c2 = (char) cm_foldUpper[(unsigned char)(*str2++)];
102 /* characters that are legal in an 8.3 name */
104 * We used to have 1's for all characters from 128 to 254. But
105 * the NT client behaves better if we create an 8.3 name for any
106 * name that has a character with the high bit on, and if we
107 * delete those characters from 8.3 names. In particular, see
108 * Sybase defect 10859.
110 char cm_LegalChars[256] = {
111 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
112 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
113 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0,
114 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
115 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
116 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1,
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, 1, 0, 1, 1, 1,
119 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
120 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
121 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
122 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
123 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
124 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
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
129 /* return true iff component is a valid 8.3 name */
130 int cm_Is8Dot3(char *namep)
133 int sawUpper = 0, sawLower = 0;
138 * can't have a leading dot;
139 * special case for . and ..
141 if (namep[0] == '.') {
144 if (namep[1] == '.' && namep[2] == 0)
148 while (tc = *namep++) {
150 /* saw another dot */
151 if (sawDot) return 0; /* second dot */
156 if (cm_LegalChars[tc] == 0)
158 if (tc >= 'A' && tc <= 'Z')
160 else if (tc >= 'a' && tc <= 'z')
163 if (!sawDot && charCount > 8)
164 /* more than 8 chars in name */
166 if (sawDot && charCount > 3)
167 /* more than 3 chars in extension */
171 * Used to check that all characters were the same case.
172 * This doesn't help 16-bit apps, and meanwhile it causes the
173 * MS-DOS Command Prompt to misbehave; see Sybase defect 10709.
175 if (sawUpper && sawLower)
182 * Number unparsing map for generating 8.3 names;
185 char cm_8Dot3Mapping[41] =
186 {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
187 'B', 'C', 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S',
188 'T', 'V', 'W', 'X', 'Y', 'Z', '_', '-', '$', '#', '@', '%', '!', '&', 'E', 'O'
190 int cm_8Dot3MapSize = sizeof(cm_8Dot3Mapping);
192 void cm_Gen8Dot3Name(cm_dirEntry_t *dep, char *shortName, char **shortNameEndp)
196 int vnode = ntohl(dep->fid.vnode);
198 int validExtension = 0;
199 char tc, *temp, *name;
201 /* Unparse the file's vnode number to get a "uniquifier" */
203 number[nsize] = cm_8Dot3Mapping[vnode % cm_8Dot3MapSize];
205 vnode /= cm_8Dot3MapSize;
209 * Look for valid extension. There has to be a dot, and
210 * at least one of the characters following has to be legal.
212 lastDot = strrchr(dep->name, '.');
214 temp = lastDot; temp++;
216 if (cm_LegalChars[tc])
222 /* Copy name characters */
224 for (i = 0, name = dep->name;
225 i < (7 - nsize) && name != lastDot; ) {
230 if (!cm_LegalChars[tc])
233 *shortName++ = toupper(tc);
239 /* Copy uniquifier characters */
240 memcpy(shortName, number, nsize);
243 if (validExtension) {
244 /* Copy extension characters */
245 *shortName++ = *lastDot++; /* copy dot */
246 for (i = 0, tc = *lastDot++;
249 if (cm_LegalChars[tc]) {
251 *shortName++ = toupper(tc);
260 *shortNameEndp = shortName;
263 /* return success if we can open this file in this mode */
264 long cm_CheckOpen(cm_scache_t *scp, int openMode, int trunc, cm_user_t *userp,
271 if (openMode != 1) rights |= PRSFS_READ;
272 if (openMode == 1 || openMode == 2 || trunc) rights |= PRSFS_WRITE;
274 lock_ObtainMutex(&scp->mx);
276 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
277 CM_SCACHESYNC_GETSTATUS
278 | CM_SCACHESYNC_NEEDCALLBACK);
279 lock_ReleaseMutex(&scp->mx);
284 /* return success if we can open this file in this mode */
285 long cm_CheckNTOpen(cm_scache_t *scp, unsigned int desiredAccess,
286 unsigned int createDisp, cm_user_t *userp, cm_req_t *reqp)
291 /* Always allow delete; the RPC will tell us if it's OK */
292 if (desiredAccess == DELETE)
297 if (desiredAccess & AFS_ACCESS_READ)
298 rights |= PRSFS_READ;
300 if ((desiredAccess & AFS_ACCESS_WRITE)
302 rights |= PRSFS_WRITE;
304 lock_ObtainMutex(&scp->mx);
306 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
307 CM_SCACHESYNC_GETSTATUS
308 | CM_SCACHESYNC_NEEDCALLBACK);
309 lock_ReleaseMutex(&scp->mx);
312 * If the open will fail because the volume is readonly, then we will
313 * return an access denied error instead. This is to help brain-dead
314 * apps run correctly on replicated volumes.
315 * See defect 10007 for more information.
317 if (code == CM_ERROR_READONLY)
318 code = CM_ERROR_NOACCESS;
324 * When CAP_NT_SMBS has been negotiated, deletion (of files or directories) is
325 * done in three steps:
326 * (1) open for deletion (NT_CREATE_AND_X)
327 * (2) set for deletion on close (NTWTRANSACTION2, SET_FILE_INFO)
329 * We must not do the RPC until step 3. But if we are going to return an error
330 * code (e.g. directory not empty), we must return it by step 2, otherwise most
331 * clients will not notice it. So we do a preliminary check. For deleting
332 * files, this is almost free, since we have already done the RPC to get the
333 * parent directory's status bits. But for deleting directories, we must do an
334 * additional RPC to get the directory's data to check if it is empty. Sigh.
336 long cm_CheckNTDelete(cm_scache_t *dscp, cm_scache_t *scp, cm_user_t *userp,
343 unsigned short *hashTable;
345 int BeyondPage = 0, HaveDot = 0, HaveDotDot = 0;
347 /* First check permissions */
348 lock_ObtainMutex(&dscp->mx);
349 code = cm_SyncOp(dscp, NULL, userp, reqp, PRSFS_DELETE,
350 CM_SCACHESYNC_GETSTATUS
351 | CM_SCACHESYNC_NEEDCALLBACK);
352 lock_ReleaseMutex(&dscp->mx);
356 /* If deleting directory, must be empty */
358 if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
361 thyper.HighPart = 0; thyper.LowPart = 0;
362 lock_ObtainRead(&scp->bufCreateLock);
363 code = buf_Get(scp, &thyper, &bufferp);
364 lock_ReleaseRead(&scp->bufCreateLock);
368 lock_ObtainMutex(&bufferp->mx);
369 lock_ObtainMutex(&scp->mx);
371 code = cm_SyncOp(scp, bufferp, userp, reqp, 0,
372 CM_SCACHESYNC_NEEDCALLBACK
374 | CM_SCACHESYNC_BUFLOCKED);
378 if (cm_HaveBuffer(scp, bufferp, 1))
381 /* otherwise, load the buffer and try again */
382 lock_ReleaseMutex(&bufferp->mx);
383 code = cm_GetBuffer(scp, bufferp, NULL, userp, reqp);
384 lock_ReleaseMutex(&scp->mx);
385 lock_ObtainMutex(&bufferp->mx);
386 lock_ObtainMutex(&scp->mx);
391 /* We try to determine emptiness without looking beyond the first page,
392 * and without assuming "." and ".." are present and are on the first
393 * page (though these assumptions might, after all, be reasonable).
395 hashTable = (unsigned short *)(bufferp->datap + (32 * 5));
396 for (i=0; i<128; i++) {
397 idx = ntohs(hashTable[i]);
403 dep = (cm_dirEntry_t *)(bufferp->datap + (32 * idx));
404 if (strcmp(dep->name, ".") == 0)
406 else if (strcmp(dep->name, "..") == 0)
409 code = CM_ERROR_NOTEMPTY;
412 idx = ntohs(dep->next);
415 if (BeyondPage && HaveDot && HaveDotDot)
416 code = CM_ERROR_NOTEMPTY;
420 lock_ReleaseMutex(&bufferp->mx);
421 buf_Release(bufferp);
422 lock_ReleaseMutex(&scp->mx);
427 * Iterate through all entries in a directory.
428 * When the function funcp is called, the buffer is locked but the
429 * directory vnode is not.
431 long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
432 osi_hyper_t *startOffsetp, cm_user_t *userp, cm_req_t *reqp,
433 cm_scache_t **retscp)
440 osi_hyper_t dirLength;
441 osi_hyper_t bufferOffset;
442 osi_hyper_t curOffset;
446 cm_pageHeader_t *pageHeaderp;
448 long nextEntryCookie;
449 int numDirChunks; /* # of 32 byte dir chunks in this entry */
451 /* get the directory size */
452 lock_ObtainMutex(&scp->mx);
453 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_LOOKUP,
454 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
456 lock_ReleaseMutex(&scp->mx);
460 if (scp->fileType != CM_SCACHETYPE_DIRECTORY) {
461 lock_ReleaseMutex(&scp->mx);
462 return CM_ERROR_NOTDIR;
465 if (retscp) /* if this is a lookup call */
467 cm_lookupSearch_t* sp = parmp;
468 int casefold = sp->caseFold;
470 sp->caseFold = 0; /* we have a strong preference for exact matches */
471 if ( *retscp = cm_dnlcLookup(scp, sp)) /* dnlc hit */
473 sp->caseFold = casefold;
474 lock_ReleaseMutex(&scp->mx);
478 sp->caseFold = casefold;
482 * XXX We only get the length once. It might change when we drop the
485 dirLength = scp->length;
487 lock_ReleaseMutex(&scp->mx);
490 bufferOffset.LowPart = bufferOffset.HighPart = 0;
492 curOffset = *startOffsetp;
494 curOffset.HighPart = 0;
495 curOffset.LowPart = 0;
499 /* make sure that curOffset.LowPart doesn't point to the first
500 * 32 bytes in the 2nd through last dir page, and that it
501 * doesn't point at the first 13 32-byte chunks in the first
502 * dir page, since those are dir and page headers, and don't
503 * contain useful information.
505 temp = curOffset.LowPart & (2048-1);
506 if (curOffset.HighPart == 0 && curOffset.LowPart < 2048) {
507 /* we're in the first page */
508 if (temp < 13*32) temp = 13*32;
511 /* we're in a later dir page */
512 if (temp < 32) temp = 32;
515 /* make sure the low order 5 bits are zero */
518 /* now put temp bits back ito curOffset.LowPart */
519 curOffset.LowPart &= ~(2048-1);
520 curOffset.LowPart |= temp;
522 /* check if we've passed the dir's EOF */
523 if (LargeIntegerGreaterThanOrEqualTo(curOffset, dirLength))
526 /* see if we can use the bufferp we have now; compute in which
527 * page the current offset would be, and check whether that's
528 * the offset of the buffer we have. If not, get the buffer.
530 thyper.HighPart = curOffset.HighPart;
531 thyper.LowPart = curOffset.LowPart & ~(buf_bufferSize-1);
532 if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
535 lock_ReleaseMutex(&bufferp->mx);
536 buf_Release(bufferp);
540 lock_ObtainRead(&scp->bufCreateLock);
541 code = buf_Get(scp, &thyper, &bufferp);
542 lock_ReleaseRead(&scp->bufCreateLock);
544 lock_ObtainMutex(&bufferp->mx);
546 bufferOffset = thyper;
548 /* now get the data in the cache */
550 lock_ObtainMutex(&scp->mx);
551 code = cm_SyncOp(scp, bufferp, userp, reqp,
553 CM_SCACHESYNC_NEEDCALLBACK
555 | CM_SCACHESYNC_BUFLOCKED);
557 lock_ReleaseMutex(&scp->mx);
561 if (cm_HaveBuffer(scp, bufferp, 1)) {
562 lock_ReleaseMutex(&scp->mx);
566 /* otherwise, load the buffer and try again */
567 lock_ReleaseMutex(&bufferp->mx);
568 code = cm_GetBuffer(scp, bufferp, NULL, userp,
570 lock_ReleaseMutex(&scp->mx);
571 lock_ObtainMutex(&bufferp->mx);
575 lock_ReleaseMutex(&bufferp->mx);
576 buf_Release(bufferp);
580 } /* if (wrong buffer) ... */
582 /* now we have the buffer containing the entry we're interested
583 * in; copy it out if it represents a non-deleted entry.
585 entryInDir = curOffset.LowPart & (2048-1);
586 entryInBuffer = curOffset.LowPart & (buf_bufferSize - 1);
588 /* page header will help tell us which entries are free. Page
589 * header can change more often than once per buffer, since
590 * AFS 3 dir page size may be less than (but not more than) a
591 * buffer package buffer.
593 /* only look intra-buffer */
594 temp = curOffset.LowPart & (buf_bufferSize - 1);
595 temp &= ~(2048 - 1); /* turn off intra-page bits */
596 pageHeaderp = (cm_pageHeader_t *) (bufferp->datap + temp);
598 /* now determine which entry we're looking at in the page. If
599 * it is free (there's a free bitmap at the start of the dir),
600 * we should skip these 32 bytes.
602 slotInPage = (entryInDir & 0x7e0) >> 5;
603 if (!(pageHeaderp->freeBitmap[slotInPage>>3]
604 & (1 << (slotInPage & 0x7)))) {
605 /* this entry is free */
606 numDirChunks = 1; /* only skip this guy */
610 tp = bufferp->datap + entryInBuffer;
611 dep = (cm_dirEntry_t *) tp; /* now points to AFS3 dir entry */
613 /* while we're here, compute the next entry's location, too,
614 * since we'll need it when writing out the cookie into the
615 * dir listing stream.
617 numDirChunks = cm_NameEntries(dep->name, NULL);
619 /* compute the offset of the cookie representing the next entry */
620 nextEntryCookie = curOffset.LowPart
621 + (CM_DIR_CHUNKSIZE * numDirChunks);
623 if (dep->fid.vnode != 0) {
624 /* this is one of the entries to use: it is not deleted */
625 code = (*funcp)(scp, dep, parmp, &curOffset);
627 } /* if we're including this name */
630 /* and adjust curOffset to be where the new cookie is */
632 thyper.LowPart = CM_DIR_CHUNKSIZE * numDirChunks;
633 curOffset = LargeIntegerAdd(thyper, curOffset);
634 } /* while copying data for dir listing */
636 /* release the mutex */
638 lock_ReleaseMutex(&bufferp->mx);
639 buf_Release(bufferp);
644 int cm_NoneUpper(char *s)
648 if (c >= 'A' && c <= 'Z')
653 int cm_NoneLower(char *s)
657 if (c >= 'a' && c <= 'z')
662 long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
665 cm_lookupSearch_t *sp;
670 sp = (cm_lookupSearch_t *) rockp;
672 matchName = dep->name;
674 match = cm_stricmp(matchName, sp->searchNamep);
676 match = strcmp(matchName, sp->searchNamep);
680 && !cm_Is8Dot3(dep->name)) {
681 matchName = shortName;
682 cm_Gen8Dot3Name(dep, shortName, NULL);
684 match = cm_stricmp(matchName, sp->searchNamep);
686 match = strcmp(matchName, sp->searchNamep);
693 if(!sp->caseFold) sp->ExactFound = 1;
695 if (!sp->caseFold || matchName == shortName) {
696 sp->fid.vnode = ntohl(dep->fid.vnode);
697 sp->fid.unique = ntohl(dep->fid.unique);
698 return CM_ERROR_STOPNOW;
702 * If we get here, we are doing a case-insensitive search, and we
703 * have found a match. Now we determine what kind of match it is:
704 * exact, lower-case, upper-case, or none of the above. This is done
705 * in order to choose among matches, if there are more than one.
708 /* Exact matches are the best. */
709 match = strcmp(matchName, sp->searchNamep);
712 sp->fid.vnode = ntohl(dep->fid.vnode);
713 sp->fid.unique = ntohl(dep->fid.unique);
714 return CM_ERROR_STOPNOW;
717 /* Lower-case matches are next. */
720 if (cm_NoneUpper(matchName)) {
725 /* Upper-case matches are next. */
728 if (cm_NoneLower(matchName)) {
733 /* General matches are last. */
739 sp->fid.vnode = ntohl(dep->fid.vnode);
740 sp->fid.unique = ntohl(dep->fid.unique);
744 /* read the contents of a mount point into the appropriate string.
745 * called with locked scp, and returns with locked scp.
747 long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
754 if (scp->mountPointStringp) return 0;
756 /* otherwise, we have to read it in */
757 lock_ReleaseMutex(&scp->mx);
759 lock_ObtainRead(&scp->bufCreateLock);
760 thyper.LowPart = thyper.HighPart = 0;
761 code = buf_Get(scp, &thyper, &bufp);
762 lock_ReleaseRead(&scp->bufCreateLock);
764 lock_ObtainMutex(&scp->mx);
769 code = cm_SyncOp(scp, bufp, userp, reqp, 0,
770 CM_SCACHESYNC_READ | CM_SCACHESYNC_NEEDCALLBACK);
775 if (cm_HaveBuffer(scp, bufp, 0)) break;
777 /* otherwise load buffer */
778 code = cm_GetBuffer(scp, bufp, NULL, userp, reqp);
783 /* locked, has callback, has valid data in buffer */
784 if ((tlen = scp->length.LowPart) > 1000) return CM_ERROR_TOOBIG;
786 code = CM_ERROR_INVAL;
790 /* someone else did the work while we were out */
791 if (scp->mountPointStringp) {
796 /* otherwise, copy out the link */
797 scp->mountPointStringp = malloc(tlen);
798 memcpy(scp->mountPointStringp, bufp->datap, tlen);
800 /* now make it null-terminated. Note that the original contents of a
801 * link that is a mount point is "#volname." where "." is there just to
802 * be turned into a null. That is, we can trash the last char of the
803 * link without damaging the vol name. This is a stupid convention,
804 * but that's the protocol.
806 scp->mountPointStringp[tlen-1] = 0;
810 if (bufp) buf_Release(bufp);
814 /* called with a locked scp and chases the mount point, yielding outScpp.
815 * scp remains locked, just for simplicity of describing the interface.
817 long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
818 cm_req_t *reqp, cm_scache_t **outScpp)
833 if (scp->mountRootFidp && scp->mountRootGen >= cm_mountRootGen) {
834 tfid = *scp->mountRootFidp;
835 lock_ReleaseMutex(&scp->mx);
836 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
837 lock_ObtainMutex(&scp->mx);
841 /* parse the volume name */
842 mpNamep = scp->mountPointStringp;
844 tlen = strlen(scp->mountPointStringp);
845 mtType = *scp->mountPointStringp;
846 cellNamep = malloc(tlen);
847 volNamep = malloc(tlen);
849 cp = strrchr(mpNamep, ':');
851 /* cellular mount point */
852 memset(cellNamep, 0, tlen);
853 strncpy(cellNamep, mpNamep+1, cp - mpNamep - 1);
854 strcpy(volNamep, cp+1);
855 /* now look up the cell */
856 cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE);
860 strcpy(volNamep, mpNamep+1);
862 cellp = cm_FindCellByID(scp->fid.cell);
866 code = CM_ERROR_NOSUCHCELL;
870 vnLength = strlen(volNamep);
871 if (vnLength >= 8 && strcmp(volNamep + vnLength - 7, ".backup") == 0)
873 else if (vnLength >= 10
874 && strcmp(volNamep + vnLength - 9, ".readonly") == 0)
879 /* check for backups within backups */
881 && (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
882 == CM_SCACHEFLAG_RO) {
883 code = CM_ERROR_NOSUCHVOLUME;
887 /* now we need to get the volume */
888 lock_ReleaseMutex(&scp->mx);
889 code = cm_GetVolumeByName(cellp, volNamep, userp, reqp, 0, &volp);
890 lock_ObtainMutex(&scp->mx);
893 /* save the parent of the volume root for this is the
894 * place where the volume is mounted and we must remember
895 * this in the volume structure rather than just in the
896 * scache entry lest the scache entry gets recycled
899 lock_ObtainMutex(&volp->mx);
900 if(volp->dotdotFidp == (cm_fid_t *) NULL)
901 volp->dotdotFidp = (cm_fid_t *) malloc(sizeof(cm_fid_t));
902 *(volp->dotdotFidp) = dscp->fid;
903 lock_ReleaseMutex(&volp->mx);
905 if (scp->mountRootFidp == 0) {
906 scp->mountRootFidp = malloc(sizeof(cm_fid_t));
908 scp->mountRootFidp->cell = cellp->cellID;
909 /* if the mt pt is in a read-only volume (not just a
910 * backup), and if there is a read-only volume for the
911 * target, and if this is a type '#' mount point, use
912 * the read-only, otherwise use the one specified.
914 if (mtType == '#' && (scp->flags & CM_SCACHEFLAG_PURERO)
915 && volp->roID != 0 && type == RWVOL)
918 scp->mountRootFidp->volume = volp->roID;
919 else if (type == BACKVOL)
920 scp->mountRootFidp->volume = volp->bkID;
922 scp->mountRootFidp->volume = volp->rwID;
924 /* the rest of the fid is a magic number */
925 scp->mountRootFidp->vnode = 1;
926 scp->mountRootFidp->unique = 1;
927 scp->mountRootGen = cm_mountRootGen;
929 tfid = *scp->mountRootFidp;
930 lock_ReleaseMutex(&scp->mx);
931 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
932 lock_ObtainMutex(&scp->mx);
941 int cm_ExpandSysName(char *inp, char *outp, long outSize)
946 tp = strrchr(inp, '@');
947 if (tp == NULL) return 0; /* no @sys */
949 if (strcmp(tp, "@sys") != 0) return 0; /* no @sys */
951 /* caller just wants to know if this is a valid @sys type of name */
952 if (outp == NULL) return 1;
954 /* otherwise generate the properly expanded @sys name */
955 prefixCount = tp - inp;
957 strncpy(outp, inp, prefixCount); /* copy out "a." from "a.@sys" */
958 outp[prefixCount] = 0; /* null terminate the "a." */
959 strcat(outp, cm_sysName); /* append i386_nt40 */
963 long cm_Lookup(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
964 cm_req_t *reqp, cm_scache_t **outpScpp)
967 int dnlcHit = 1; /* did we hit in the dnlc? yes, we did */
968 cm_scache_t *tscp = NULL;
969 cm_scache_t *mountedScp;
970 cm_lookupSearch_t rock;
974 if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
975 && strcmp(namep, "..") == 0) {
976 if (dscp->dotdotFidp == (cm_fid_t *)NULL
977 || dscp->dotdotFidp->volume == 0)
978 return CM_ERROR_NOSUCHVOLUME;
979 rock.fid = *dscp->dotdotFidp;
983 if (cm_ExpandSysName(namep, tname, sizeof(tname))) {
986 memset(&rock, 0, sizeof(rock));
987 rock.fid.cell = dscp->fid.cell;
988 rock.fid.volume = dscp->fid.volume;
989 rock.searchNamep = namep;
990 rock.caseFold = (flags & CM_FLAG_CASEFOLD);
991 rock.hasTilde = ((strchr(namep, '~') != NULL) ? 1 : 0);
993 /* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
994 code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
995 (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
997 /* code == 0 means we fell off the end of the dir, while stopnow means
998 * that we stopped early, probably because we found the entry we're
999 * looking for. Any other non-zero code is an error.
1001 if (code && code != CM_ERROR_STOPNOW)
1004 getroot = (dscp==cm_rootSCachep) ;
1006 if (!cm_freelanceEnabled || !getroot) {
1007 if (flags & CM_FLAG_CHECKPATH)
1008 return CM_ERROR_NOSUCHPATH;
1010 return CM_ERROR_NOSUCHFILE;
1012 else { /* nonexistent dir on freelance root, so add it */
1013 osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %s",
1014 osi_LogSaveString(afsd_logp,namep));
1015 code = cm_FreelanceAddMount(namep, namep, "root.cell.", namep[0] == '.', &rock.fid);
1016 if (code < 0) { /* add mount point failed, so give up */
1017 if (flags & CM_FLAG_CHECKPATH)
1018 return CM_ERROR_NOSUCHPATH;
1020 return CM_ERROR_NOSUCHFILE;
1022 tscp = NULL; /* to force call of cm_GetSCache */
1027 if ( !tscp ) /* we did not find it in the dnlc */
1030 code = cm_GetSCache(&rock.fid, &tscp, userp, reqp);
1034 /* tscp is now held */
1036 lock_ObtainMutex(&tscp->mx);
1037 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1038 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1040 lock_ReleaseMutex(&tscp->mx);
1041 cm_ReleaseSCache(tscp);
1044 /* tscp is now locked */
1046 if (!(flags & CM_FLAG_NOMOUNTCHASE)
1047 && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
1048 /* mount points are funny: they have a volume name to mount
1051 code = cm_ReadMountPoint(tscp, userp, reqp);
1053 code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
1055 lock_ReleaseMutex(&tscp->mx);
1056 cm_ReleaseSCache(tscp);
1063 lock_ReleaseMutex(&tscp->mx);
1066 /* copy back pointer */
1069 /* insert scache in dnlc */
1070 if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
1071 /* lock the directory entry to prevent racing callback revokes */
1072 lock_ObtainMutex(&dscp->mx);
1073 if ( dscp->cbServerp && dscp->cbExpires )
1074 cm_dnlcEnter(dscp, namep, tscp);
1075 lock_ReleaseMutex(&dscp->mx);
1082 long cm_Unlink(cm_scache_t *dscp, char *namep, cm_user_t *userp, cm_req_t *reqp)
1088 AFSFetchStatus newDirStatus;
1091 #ifdef AFS_FREELANCE_CLIENT
1092 if (cm_freelanceEnabled && dscp == cm_rootSCachep) {
1093 /* deleting a mount point from the root dir. */
1094 code = cm_FreelanceRemoveMount(namep);
1099 /* make sure we don't screw up the dir status during the merge */
1100 lock_ObtainMutex(&dscp->mx);
1101 sflags = CM_SCACHESYNC_STOREDATA;
1102 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1103 lock_ReleaseMutex(&dscp->mx);
1104 if (code) return code;
1107 afsFid.Volume = dscp->fid.volume;
1108 afsFid.Vnode = dscp->fid.vnode;
1109 afsFid.Unique = dscp->fid.unique;
1111 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1114 code = RXAFS_RemoveFile(connp->callp, &afsFid, namep,
1115 &newDirStatus, &volSync);
1117 } while (cm_Analyze(connp, userp, reqp, &dscp->fid, &volSync, NULL, NULL, code));
1118 code = cm_MapRPCError(code, reqp);
1120 lock_ObtainMutex(&dscp->mx);
1121 cm_dnlcRemove(dscp, namep);
1122 cm_SyncOpDone(dscp, NULL, sflags);
1123 if (code == 0) cm_MergeStatus(dscp, &newDirStatus, &volSync, userp, 0);
1124 lock_ReleaseMutex(&dscp->mx);
1129 /* called with a locked vnode, and fills in the link info.
1130 * returns this the vnode still locked.
1132 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1139 lock_AssertMutex(&linkScp->mx);
1140 if (!linkScp->mountPointStringp) {
1141 /* read the link data */
1142 lock_ReleaseMutex(&linkScp->mx);
1143 thyper.LowPart = thyper.HighPart = 0;
1144 code = buf_Get(linkScp, &thyper, &bufp);
1145 lock_ObtainMutex(&linkScp->mx);
1146 if (code) return code;
1148 code = cm_SyncOp(linkScp, bufp, userp, reqp, 0,
1149 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1154 if (cm_HaveBuffer(linkScp, bufp, 0)) break;
1156 code = cm_GetBuffer(linkScp, bufp, NULL, userp, reqp);
1161 } /* while loop to get the data */
1163 /* now if we still have no link read in,
1164 * copy the data from the buffer */
1165 if ((temp = linkScp->length.LowPart) >= 1024) {
1167 return CM_ERROR_TOOBIG;
1170 /* otherwise, it fits; make sure it is still null (could have
1171 * lost race with someone else referencing this link above),
1172 * and if so, copy in the data.
1174 if (linkScp->mountPointStringp == NULL) {
1175 linkScp->mountPointStringp = malloc(temp+1);
1176 strncpy(linkScp->mountPointStringp, bufp->datap, temp);
1177 linkScp->mountPointStringp[temp] = 0; /* null terminate */
1180 } /* don't have sym link contents cached */
1185 /* called with a held vnode and a path suffix, with the held vnode being a
1186 * symbolic link. Our goal is to generate a new path to interpret, and return
1187 * this new path in newSpaceBufferp. If the new vnode is relative to a dir
1188 * other than the directory containing the symbolic link, then the new root is
1189 * returned in *newRootScpp, otherwise a null is returned there.
1191 long cm_AssembleLink(cm_scache_t *linkScp, char *pathSuffixp,
1192 cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1193 cm_user_t *userp, cm_req_t *reqp)
1199 lock_ObtainMutex(&linkScp->mx);
1200 code = cm_HandleLink(linkScp, userp, reqp);
1201 if (code) goto done;
1203 /* if we may overflow the buffer, bail out; buffer is signficantly
1204 * bigger than max path length, so we don't really have to worry about
1205 * being a little conservative here.
1207 if (strlen(linkScp->mountPointStringp) + strlen(pathSuffixp) + 2
1208 >= CM_UTILS_SPACESIZE)
1209 return CM_ERROR_TOOBIG;
1211 tsp = cm_GetSpace();
1212 linkp = linkScp->mountPointStringp;
1213 if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1214 if (strlen(linkp) > cm_mountRootLen)
1215 strcpy(tsp->data, linkp+cm_mountRootLen+1);
1218 *newRootScpp = cm_rootSCachep;
1219 cm_HoldSCache(cm_rootSCachep);
1220 } else if (*linkp == '\\' || *linkp == '/') {
1221 /* formerly, this was considered to be from the AFS root,
1222 but this seems to create problems. instead, we will just
1225 strcpy(tsp->data, linkp+1);
1226 *newRootScpp = cm_rootSCachep;
1227 cm_HoldSCache(cm_rootSCachep);
1229 code = CM_ERROR_NOSUCHPATH;
1234 /* a relative link */
1235 strcpy(tsp->data, linkp);
1236 *newRootScpp = NULL;
1238 if (pathSuffixp[0] != 0) { /* if suffix string is non-null */
1239 strcat(tsp->data, "\\");
1240 strcat(tsp->data, pathSuffixp);
1242 *newSpaceBufferp = tsp;
1246 lock_ReleaseMutex(&linkScp->mx);
1250 long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
1251 cm_user_t *userp, char *tidPathp, cm_req_t *reqp, cm_scache_t **outScpp)
1254 char *tp; /* ptr moving through input buffer */
1255 char tc; /* temp char */
1256 int haveComponent; /* has new component started? */
1257 char component[256]; /* this is the new component */
1258 char *cp; /* component name being assembled */
1259 cm_scache_t *tscp; /* current location in the hierarchy */
1260 cm_scache_t *nscp; /* next dude down */
1261 cm_scache_t *dirScp; /* last dir we searched */
1262 cm_scache_t *linkScp; /* new root for the symlink we just
1264 cm_space_t *psp; /* space for current path, if we've hit
1266 cm_space_t *tempsp; /* temp vbl */
1267 char *restp; /* rest of the pathname to interpret */
1268 int symlinkCount; /* count of # of symlinks traversed */
1269 int extraFlag; /* avoid chasing mt pts for dir cmd */
1270 int phase = 1; /* 1 = tidPathp, 2 = pathp */
1283 cm_HoldSCache(tscp);
1288 /* map Unix slashes into DOS ones so we can interpret Unix
1291 if (tc == '/') tc = '\\';
1293 if (!haveComponent) {
1294 if (tc == '\\') continue;
1311 /* we have a component here */
1312 if (tc == 0 || tc == '\\') {
1313 /* end of the component; we're at the last
1314 * component if tc == 0. However, if the last
1315 * is a symlink, we have more to do.
1317 *cp++ = 0; /* add null termination */
1319 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
1320 extraFlag = CM_FLAG_NOMOUNTCHASE;
1321 code = cm_Lookup(tscp, component,
1323 userp, reqp, &nscp);
1326 cm_ReleaseSCache(tscp);
1327 if (psp) cm_FreeSpace(psp);
1330 haveComponent = 0; /* component done */
1331 dirScp = tscp; /* for some symlinks */
1332 tscp = nscp; /* already held */
1333 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
1335 cm_ReleaseSCache(dirScp);
1339 /* now, if tscp is a symlink, we should follow
1340 * it and assemble the path again.
1342 lock_ObtainMutex(&tscp->mx);
1343 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1344 CM_SCACHESYNC_GETSTATUS
1345 | CM_SCACHESYNC_NEEDCALLBACK);
1347 lock_ReleaseMutex(&tscp->mx);
1348 cm_ReleaseSCache(tscp);
1349 cm_ReleaseSCache(dirScp);
1352 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
1353 /* this is a symlink; assemble a new buffer */
1354 lock_ReleaseMutex(&tscp->mx);
1355 if (symlinkCount++ >= 16) {
1356 cm_ReleaseSCache(tscp);
1357 cm_ReleaseSCache(dirScp);
1358 if (psp) cm_FreeSpace(psp);
1359 return CM_ERROR_TOOBIG;
1361 if (tc == 0) restp = "";
1363 code = cm_AssembleLink(tscp, restp, &linkScp, &tempsp, userp, reqp);
1365 /* something went wrong */
1366 cm_ReleaseSCache(tscp);
1367 cm_ReleaseSCache(dirScp);
1371 /* otherwise, tempsp has the new path,
1372 * and linkScp is the new root from
1373 * which to interpret that path.
1374 * Continue with the namei processing,
1375 * also doing the bookkeeping for the
1376 * space allocation and tracking the
1377 * vnode reference counts.
1379 if (psp) cm_FreeSpace(psp);
1382 cm_ReleaseSCache(tscp);
1385 * by AssembleLink */
1386 /* now, if linkScp is null, that's
1387 * AssembleLink's way of telling us that
1388 * the sym link is relative to the dir
1389 * containing the link. We have a ref
1390 * to it in dirScp, and we hold it now
1391 * and reuse it as the new spot in the
1395 cm_HoldSCache(dirScp);
1398 } /* if we have a sym link */
1400 /* not a symlink, we may be done */
1401 lock_ReleaseMutex(&tscp->mx);
1408 cm_ReleaseSCache(dirScp);
1413 cm_ReleaseSCache(dirScp);
1414 } /* end of a component */
1416 } /* we have a component */
1417 } /* big while loop over all components */
1420 if (psp) cm_FreeSpace(psp);
1421 if (code == 0) *outScpp = tscp;
1425 /* called with a dir, and a vnode within the dir that happens to be a symlink.
1426 * We chase the link, and return a held pointer to the target, if it exists,
1427 * in *outScpp. If we succeed, we return 0, otherwise we return an error code
1428 * and do not hold or return a target vnode.
1430 * This is very similar to calling cm_NameI with the last component of a name,
1431 * which happens to be a symlink, except that we've already passed by the name.
1433 * This function is typically called by the directory listing functions, which
1434 * encounter symlinks but need to return the proper file length so programs
1435 * like "more" work properly when they make use of the attributes retrieved from
1438 * The input vnode should not be locked when this function is called.
1440 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
1441 cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
1445 cm_scache_t *newRootScp;
1447 osi_Log1(afsd_logp, "Evaluating symlink vp %x", linkScp);
1449 code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
1450 if (code) return code;
1452 /* now, if newRootScp is NULL, we're really being told that the symlink
1453 * is relative to the current directory (dscp).
1455 if (newRootScp == NULL) {
1457 cm_HoldSCache(dscp);
1460 code = cm_NameI(newRootScp, spacep->data,
1461 CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
1462 userp, NULL, reqp, outScpp);
1464 /* this stuff is allocated no matter what happened on the namei call,
1466 cm_FreeSpace(spacep);
1467 cm_ReleaseSCache(newRootScp);
1472 /* make this big enough so that one buffer of dir pages won't overflow. We'll
1473 * check anyway, but we want to minimize the chance that we have to leave stuff
1476 #define CM_BULKMAX 128
1478 /* rock for bulk stat calls */
1479 typedef struct cm_bulkStat {
1480 osi_hyper_t bufOffset; /* only do it for things in this buffer page */
1482 /* info for the actual call */
1483 int counter; /* next free slot */
1484 AFSFid fids[CM_BULKMAX];
1485 AFSFetchStatus stats[CM_BULKMAX];
1486 AFSCallBack callbacks[CM_BULKMAX];
1489 /* for a given entry, make sure that it isn't in the stat cache, and then
1490 * add it to the list of file IDs to be obtained.
1492 * Don't bother adding it if we already have a vnode. Note that the dir
1493 * is locked, so we have to be careful checking the vnode we're thinking of
1494 * processing, to avoid deadlocks.
1496 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
1507 /* Don't overflow bsp. */
1508 if (bsp->counter >= CM_BULKMAX)
1509 return CM_ERROR_STOPNOW;
1511 thyper.LowPart = buf_bufferSize;
1512 thyper.HighPart = 0;
1513 thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
1515 /* thyper is now the first byte past the end of the record we're
1516 * interested in, and bsp->bufOffset is the first byte of the record
1517 * we're interested in.
1518 * Skip data in the others.
1521 if (LargeIntegerLessThan(*offp, bsp->bufOffset))
1523 if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
1524 return CM_ERROR_STOPNOW;
1525 if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
1528 tfid.cell = scp->fid.cell;
1529 tfid.volume = scp->fid.volume;
1530 tfid.vnode = ntohl(dep->fid.vnode);
1531 tfid.unique = ntohl(dep->fid.unique);
1532 tscp = cm_FindSCache(&tfid);
1534 if (lock_TryMutex(&tscp->mx)) {
1535 /* we have an entry that we can look at */
1536 if (cm_HaveCallback(tscp)) {
1537 /* we have a callback on it. Don't bother
1538 * fetching this stat entry, since we're happy
1539 * with the info we have.
1541 lock_ReleaseMutex(&tscp->mx);
1542 cm_ReleaseSCache(tscp);
1545 lock_ReleaseMutex(&tscp->mx);
1547 cm_ReleaseSCache(tscp);
1550 #ifdef AFS_FREELANCE_CLIENT
1551 // yj: if this is a mountpoint under root.afs then we don't want it
1552 // to be bulkstat-ed, instead, we call getSCache directly and under
1553 // getSCache, it is handled specially.
1554 if ( cm_freelanceEnabled &&
1555 tfid.cell==AFS_FAKE_ROOT_CELL_ID &&
1556 tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
1557 !(tfid.vnode==0x1 && tfid.unique==0x1) )
1559 osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
1560 return cm_GetSCache(&tfid, &tscp, NULL, NULL);
1562 #endif /* AFS_FREELANCE_CLIENT */
1565 bsp->fids[i].Volume = scp->fid.volume;
1566 bsp->fids[i].Vnode = tfid.vnode;
1567 bsp->fids[i].Unique = tfid.unique;
1571 /* called with a locked scp and a pointer to a buffer. Make bulk stat
1572 * calls on all undeleted files in the page of the directory specified.
1574 void cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
1578 cm_bulkStat_t bb; /* this is *BIG*, probably 12K or so;
1579 * watch for stack problems */
1580 AFSCBFids fidStruct;
1581 AFSBulkStats statStruct;
1583 AFSCBs callbackStruct;
1586 cm_callbackRequest_t cbReq;
1593 osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%x", (long) dscp);
1595 /* should be on a buffer boundary */
1596 osi_assert((offsetp->LowPart & (buf_bufferSize - 1)) == 0);
1599 bb.bufOffset = *offsetp;
1601 /* first, assemble the file IDs we need to stat */
1602 code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) &bb, offsetp, userp,
1605 /* if we failed, bail out early */
1606 if (code && code != CM_ERROR_STOPNOW) return;
1608 /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
1609 * make the calls to create the entries. Handle AFSCBMAX files at a
1613 while(filex < bb.counter) {
1614 filesThisCall = bb.counter - filex;
1615 if (filesThisCall > AFSCBMAX) filesThisCall = AFSCBMAX;
1617 fidStruct.AFSCBFids_len = filesThisCall;
1618 fidStruct.AFSCBFids_val = &bb.fids[filex];
1619 statStruct.AFSBulkStats_len = filesThisCall;
1620 statStruct.AFSBulkStats_val = &bb.stats[filex];
1621 callbackStruct.AFSCBs_len = filesThisCall;
1622 callbackStruct.AFSCBs_val = &bb.callbacks[filex];
1623 cm_StartCallbackGrantingCall(NULL, &cbReq);
1624 osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
1626 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1629 code = RXAFS_BulkStatus(connp->callp, &fidStruct,
1630 &statStruct, &callbackStruct, &volSync);
1632 } while (cm_Analyze(connp, userp, reqp, &dscp->fid,
1633 &volSync, NULL, &cbReq, code));
1634 code = cm_MapRPCError(code, reqp);
1636 osi_Log0(afsd_logp, "CALL BulkStatus DONE");
1638 /* may as well quit on an error, since we're not going to do
1639 * much better on the next immediate call, either.
1643 /* otherwise, we should do the merges */
1644 for(i = 0; i<filesThisCall; i++) {
1646 tfid.cell = dscp->fid.cell;
1647 tfid.volume = bb.fids[j].Volume;
1648 tfid.vnode = bb.fids[j].Vnode;
1649 tfid.unique = bb.fids[j].Unique;
1650 code = cm_GetSCache(&tfid, &scp, userp, reqp);
1651 if (code != 0) continue;
1653 /* otherwise, if this entry has no callback info,
1656 lock_ObtainMutex(&scp->mx);
1657 /* now, we have to be extra paranoid on merging in this
1658 * information, since we didn't use cm_SyncOp before
1659 * starting the fetch to make sure that no bad races
1660 * were occurring. Specifically, we need to make sure
1661 * we don't obliterate any newer information in the
1662 * vnode than have here.
1664 * Right now, be pretty conservative: if there's a
1665 * callback or a pending call, skip it.
1667 if (scp->cbServerp == NULL
1669 (CM_SCACHEFLAG_FETCHING
1670 | CM_SCACHEFLAG_STORING
1671 | CM_SCACHEFLAG_SIZESTORING))) {
1672 cm_EndCallbackGrantingCall(scp, &cbReq,
1674 CM_CALLBACK_MAINTAINCOUNT);
1675 cm_MergeStatus(scp, &bb.stats[j], &volSync,
1678 lock_ReleaseMutex(&scp->mx);
1679 cm_ReleaseSCache(scp);
1680 } /* all files in the response */
1681 /* now tell it to drop the count,
1682 * after doing the vnode processing above */
1683 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
1685 filex += filesThisCall;
1686 } /* while there are still more files to process */
1687 osi_Log0(afsd_logp, "END cm_TryBulkStat");
1690 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
1694 /* initialize store back mask as inexpensive local variable */
1696 memset(statusp, 0, sizeof(AFSStoreStatus));
1698 /* copy out queued info from scache first, if scp passed in */
1700 if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
1701 statusp->ClientModTime = scp->clientModTime;
1702 mask |= AFS_SETMODTIME;
1703 scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
1708 /* now add in our locally generated request */
1709 if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
1710 statusp->ClientModTime = attrp->clientModTime;
1711 mask |= AFS_SETMODTIME;
1713 if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
1714 statusp->UnixModeBits = attrp->unixModeBits;
1715 mask |= AFS_SETMODE;
1717 if (attrp->mask & CM_ATTRMASK_OWNER) {
1718 statusp->Owner = attrp->owner;
1719 mask |= AFS_SETOWNER;
1721 if (attrp->mask & CM_ATTRMASK_GROUP) {
1722 statusp->Group = attrp->group;
1723 mask |= AFS_SETGROUP;
1726 statusp->Mask = mask;
1729 /* set the file size, and make sure that all relevant buffers have been
1730 * truncated. Ensure that any partially truncated buffers have been zeroed
1731 * to the end of the buffer.
1733 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
1739 /* start by locking out buffer creation */
1740 lock_ObtainWrite(&scp->bufCreateLock);
1742 /* verify that this is a file, not a dir or a symlink */
1743 lock_ObtainMutex(&scp->mx);
1744 code = cm_SyncOp(scp, NULL, userp, reqp, 0,
1745 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1746 if (code) goto done;
1748 if (scp->fileType != CM_SCACHETYPE_FILE) {
1749 code = CM_ERROR_ISDIR;
1754 if (LargeIntegerLessThan(*sizep, scp->length))
1759 lock_ReleaseMutex(&scp->mx);
1761 /* can't hold scp->mx lock here, since we may wait for a storeback to
1762 * finish if the buffer package is cleaning a buffer by storing it to
1766 buf_Truncate(scp, userp, reqp, sizep);
1768 /* now ensure that file length is short enough, and update truncPos */
1769 lock_ObtainMutex(&scp->mx);
1771 /* make sure we have a callback (so we have the right value for the
1772 * length), and wait for it to be safe to do a truncate.
1774 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
1775 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
1776 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
1777 if (code) goto done;
1779 if (LargeIntegerLessThan(*sizep, scp->length)) {
1780 /* a real truncation. If truncPos is not set yet, or is bigger
1781 * than where we're truncating the file, set truncPos to this
1786 if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
1787 || LargeIntegerLessThan(*sizep, scp->length)) {
1789 scp->truncPos = *sizep;
1790 scp->mask |= CM_SCACHEMASK_TRUNCPOS;
1792 /* in either case, the new file size has been changed */
1793 scp->length = *sizep;
1794 scp->mask |= CM_SCACHEMASK_LENGTH;
1796 else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
1797 /* really extending the file */
1798 scp->length = *sizep;
1799 scp->mask |= CM_SCACHEMASK_LENGTH;
1802 /* done successfully */
1806 lock_ReleaseMutex(&scp->mx);
1807 lock_ReleaseWrite(&scp->bufCreateLock);
1812 /* set the file size or other attributes (but not both at once) */
1813 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
1818 AFSFetchStatus afsOutStatus;
1822 AFSStoreStatus afsInStatus;
1824 /* handle file length setting */
1825 if (attrp->mask & CM_ATTRMASK_LENGTH)
1826 return cm_SetLength(scp, &attrp->length, userp, reqp);
1828 flags = CM_SCACHESYNC_STORESTATUS;
1830 lock_ObtainMutex(&scp->mx);
1831 /* otherwise, we have to make an RPC to get the status */
1832 code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
1834 /* make the attr structure */
1835 cm_StatusFromAttr(&afsInStatus, scp, attrp);
1837 lock_ReleaseMutex(&scp->mx);
1838 if (code) return code;
1840 /* now make the RPC */
1841 osi_Log1(afsd_logp, "CALL StoreStatus vp %x", (long) scp);
1842 tfid.Volume = scp->fid.volume;
1843 tfid.Vnode = scp->fid.vnode;
1844 tfid.Unique = scp->fid.unique;
1846 code = cm_Conn(&scp->fid, userp, reqp, &connp);
1849 code = RXAFS_StoreStatus(connp->callp, &tfid,
1850 &afsInStatus, &afsOutStatus, &volSync);
1852 } while (cm_Analyze(connp, userp, reqp,
1853 &scp->fid, &volSync, NULL, NULL, code));
1854 code = cm_MapRPCError(code, reqp);
1856 osi_Log1(afsd_logp, "CALL StoreStatus DONE, code %d", code);
1858 lock_ObtainMutex(&scp->mx);
1859 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
1861 cm_MergeStatus(scp, &afsOutStatus, &volSync, userp,
1862 CM_MERGEFLAG_FORCE);
1864 /* if we're changing the mode bits, discard the ACL cache,
1865 * since we changed the mode bits.
1867 if (afsInStatus.Mask & AFS_SETMODE) cm_FreeAllACLEnts(scp);
1868 lock_ReleaseMutex(&scp->mx);
1872 long cm_Create(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
1873 cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
1878 cm_callbackRequest_t cbReq;
1883 AFSStoreStatus inStatus;
1884 AFSFetchStatus updatedDirStatus;
1885 AFSFetchStatus newFileStatus;
1886 AFSCallBack newFileCallback;
1889 /* can't create names with @sys in them; must expand it manually first.
1890 * return "invalid request" if they try.
1892 if (cm_ExpandSysName(namep, NULL, 0)) {
1893 return CM_ERROR_ATSYS;
1896 /* before starting the RPC, mark that we're changing the file data, so
1897 * that someone who does a chmod will know to wait until our call
1900 lock_ObtainMutex(&dscp->mx);
1901 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
1903 cm_StartCallbackGrantingCall(NULL, &cbReq);
1905 lock_ReleaseMutex(&dscp->mx);
1911 cm_StatusFromAttr(&inStatus, NULL, attrp);
1913 /* try the RPC now */
1915 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1918 dirAFSFid.Volume = dscp->fid.volume;
1919 dirAFSFid.Vnode = dscp->fid.vnode;
1920 dirAFSFid.Unique = dscp->fid.unique;
1921 code = RXAFS_CreateFile(connp->callp, &dirAFSFid, namep,
1922 &inStatus, &newAFSFid, &newFileStatus,
1923 &updatedDirStatus, &newFileCallback,
1925 } while (cm_Analyze(connp, userp, reqp,
1926 &dscp->fid, &volSync, NULL, &cbReq, code));
1927 code = cm_MapRPCError(code, reqp);
1929 lock_ObtainMutex(&dscp->mx);
1930 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
1932 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
1934 lock_ReleaseMutex(&dscp->mx);
1936 /* now try to create the file's entry, too, but be careful to
1937 * make sure that we don't merge in old info. Since we weren't locking
1938 * out any requests during the file's creation, we may have pretty old
1942 newFid.cell = dscp->fid.cell;
1943 newFid.volume = dscp->fid.volume;
1944 newFid.vnode = newAFSFid.Vnode;
1945 newFid.unique = newAFSFid.Unique;
1946 code = cm_GetSCache(&newFid, &scp, userp, reqp);
1948 lock_ObtainMutex(&scp->mx);
1949 if (!cm_HaveCallback(scp)) {
1950 cm_MergeStatus(scp, &newFileStatus, &volSync,
1952 cm_EndCallbackGrantingCall(scp, &cbReq,
1953 &newFileCallback, 0);
1956 lock_ReleaseMutex(&scp->mx);
1961 /* make sure we end things properly */
1963 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
1968 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
1972 lock_ObtainWrite(&scp->bufCreateLock);
1973 code = buf_CleanVnode(scp, userp, reqp);
1974 lock_ReleaseWrite(&scp->bufCreateLock);
1976 lock_ObtainMutex(&scp->mx);
1977 scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA
1978 | CM_SCACHEFLAG_OUTOFSPACE);
1979 if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
1980 | CM_SCACHEMASK_CLIENTMODTIME
1981 | CM_SCACHEMASK_LENGTH))
1982 code = cm_StoreMini(scp, userp, reqp);
1983 lock_ReleaseMutex(&scp->mx);
1988 long cm_MakeDir(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
1989 cm_user_t *userp, cm_req_t *reqp)
1994 cm_callbackRequest_t cbReq;
1999 AFSStoreStatus inStatus;
2000 AFSFetchStatus updatedDirStatus;
2001 AFSFetchStatus newDirStatus;
2002 AFSCallBack newDirCallback;
2005 /* can't create names with @sys in them; must expand it manually first.
2006 * return "invalid request" if they try.
2008 if (cm_ExpandSysName(namep, NULL, 0)) {
2009 return CM_ERROR_ATSYS;
2012 /* before starting the RPC, mark that we're changing the directory
2013 * data, so that someone who does a chmod on the dir will wait until
2014 * our call completes.
2016 lock_ObtainMutex(&dscp->mx);
2017 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2019 cm_StartCallbackGrantingCall(NULL, &cbReq);
2021 lock_ReleaseMutex(&dscp->mx);
2027 cm_StatusFromAttr(&inStatus, NULL, attrp);
2029 /* try the RPC now */
2031 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2034 dirAFSFid.Volume = dscp->fid.volume;
2035 dirAFSFid.Vnode = dscp->fid.vnode;
2036 dirAFSFid.Unique = dscp->fid.unique;
2037 code = RXAFS_MakeDir(connp->callp, &dirAFSFid, namep,
2038 &inStatus, &newAFSFid, &newDirStatus,
2039 &updatedDirStatus, &newDirCallback,
2041 } while (cm_Analyze(connp, userp, reqp,
2042 &dscp->fid, &volSync, NULL, &cbReq, code));
2043 code = cm_MapRPCError(code, reqp);
2045 lock_ObtainMutex(&dscp->mx);
2046 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2048 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2050 lock_ReleaseMutex(&dscp->mx);
2052 /* now try to create the new dir's entry, too, but be careful to
2053 * make sure that we don't merge in old info. Since we weren't locking
2054 * out any requests during the file's creation, we may have pretty old
2058 newFid.cell = dscp->fid.cell;
2059 newFid.volume = dscp->fid.volume;
2060 newFid.vnode = newAFSFid.Vnode;
2061 newFid.unique = newAFSFid.Unique;
2062 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2064 lock_ObtainMutex(&scp->mx);
2065 if (!cm_HaveCallback(scp)) {
2066 cm_MergeStatus(scp, &newDirStatus, &volSync,
2068 cm_EndCallbackGrantingCall(scp, &cbReq,
2069 &newDirCallback, 0);
2072 lock_ReleaseMutex(&scp->mx);
2073 cm_ReleaseSCache(scp);
2077 /* make sure we end things properly */
2079 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2081 /* and return error code */
2085 long cm_Link(cm_scache_t *dscp, char *namep, cm_scache_t *sscp, long flags,
2086 cm_user_t *userp, cm_req_t *reqp)
2091 AFSFid existingAFSFid;
2092 AFSFetchStatus updatedDirStatus;
2093 AFSFetchStatus newLinkStatus;
2096 if (dscp->fid.cell != sscp->fid.cell ||
2097 dscp->fid.volume != sscp->fid.volume) {
2098 return CM_ERROR_CROSSDEVLINK;
2101 lock_ObtainMutex(&dscp->mx);
2102 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2103 lock_ReleaseMutex(&dscp->mx);
2109 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2112 dirAFSFid.Volume = dscp->fid.volume;
2113 dirAFSFid.Vnode = dscp->fid.vnode;
2114 dirAFSFid.Unique = dscp->fid.unique;
2116 existingAFSFid.Volume = sscp->fid.volume;
2117 existingAFSFid.Vnode = sscp->fid.vnode;
2118 existingAFSFid.Unique = sscp->fid.unique;
2120 code = RXAFS_Link(connp->callp, &dirAFSFid, namep, &existingAFSFid,
2121 &newLinkStatus, &updatedDirStatus, &volSync);
2123 osi_Log1(smb_logp," RXAFS_Link returns %d", code);
2124 } while (cm_Analyze(connp, userp, reqp,
2125 &dscp->fid, &volSync, NULL, NULL, code));
2127 code = cm_MapRPCError(code, reqp);
2129 lock_ObtainMutex(&dscp->mx);
2130 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2132 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2134 lock_ReleaseMutex(&dscp->mx);
2139 long cm_SymLink(cm_scache_t *dscp, char *namep, char *contentsp, long flags,
2140 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
2148 AFSStoreStatus inStatus;
2149 AFSFetchStatus updatedDirStatus;
2150 AFSFetchStatus newLinkStatus;
2153 /* before starting the RPC, mark that we're changing the directory data,
2154 * so that someone who does a chmod on the dir will wait until our
2157 lock_ObtainMutex(&dscp->mx);
2158 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2159 lock_ReleaseMutex(&dscp->mx);
2164 cm_StatusFromAttr(&inStatus, NULL, attrp);
2166 /* try the RPC now */
2168 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2171 dirAFSFid.Volume = dscp->fid.volume;
2172 dirAFSFid.Vnode = dscp->fid.vnode;
2173 dirAFSFid.Unique = dscp->fid.unique;
2174 code = RXAFS_Symlink(connp->callp, &dirAFSFid, namep, contentsp,
2175 &inStatus, &newAFSFid, &newLinkStatus,
2176 &updatedDirStatus, &volSync);
2177 } while (cm_Analyze(connp, userp, reqp,
2178 &dscp->fid, &volSync, NULL, NULL, code));
2179 code = cm_MapRPCError(code, reqp);
2181 lock_ObtainMutex(&dscp->mx);
2182 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2184 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2186 lock_ReleaseMutex(&dscp->mx);
2188 /* now try to create the new dir's entry, too, but be careful to
2189 * make sure that we don't merge in old info. Since we weren't locking
2190 * out any requests during the file's creation, we may have pretty old
2194 newFid.cell = dscp->fid.cell;
2195 newFid.volume = dscp->fid.volume;
2196 newFid.vnode = newAFSFid.Vnode;
2197 newFid.unique = newAFSFid.Unique;
2198 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2200 lock_ObtainMutex(&scp->mx);
2201 if (!cm_HaveCallback(scp)) {
2202 cm_MergeStatus(scp, &newLinkStatus, &volSync,
2205 lock_ReleaseMutex(&scp->mx);
2206 cm_ReleaseSCache(scp);
2210 /* and return error code */
2214 long cm_RemoveDir(cm_scache_t *dscp, char *namep, cm_user_t *userp,
2221 AFSFetchStatus updatedDirStatus;
2224 /* before starting the RPC, mark that we're changing the directory data,
2225 * so that someone who does a chmod on the dir will wait until our
2228 lock_ObtainMutex(&dscp->mx);
2229 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2230 lock_ReleaseMutex(&dscp->mx);
2236 /* try the RPC now */
2238 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2241 dirAFSFid.Volume = dscp->fid.volume;
2242 dirAFSFid.Vnode = dscp->fid.vnode;
2243 dirAFSFid.Unique = dscp->fid.unique;
2244 code = RXAFS_RemoveDir(connp->callp, &dirAFSFid, namep,
2245 &updatedDirStatus, &volSync);
2246 } while (cm_Analyze(connp, userp, reqp,
2247 &dscp->fid, &volSync, NULL, NULL, code));
2248 code = cm_MapRPCErrorRmdir(code, reqp);
2250 lock_ObtainMutex(&dscp->mx);
2251 cm_dnlcRemove(dscp, namep);
2252 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2254 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2256 lock_ReleaseMutex(&dscp->mx);
2258 /* and return error code */
2262 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
2264 /* grab mutex on contents */
2265 lock_ObtainMutex(&scp->mx);
2267 /* reset the prefetch info */
2268 scp->prefetch.base.LowPart = 0; /* base */
2269 scp->prefetch.base.HighPart = 0;
2270 scp->prefetch.end.LowPart = 0; /* and end */
2271 scp->prefetch.end.HighPart = 0;
2273 /* release mutex on contents */
2274 lock_ReleaseMutex(&scp->mx);
2280 long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
2281 char *newNamep, cm_user_t *userp, cm_req_t *reqp)
2285 AFSFid oldDirAFSFid;
2286 AFSFid newDirAFSFid;
2288 AFSFetchStatus updatedOldDirStatus;
2289 AFSFetchStatus updatedNewDirStatus;
2293 /* before starting the RPC, mark that we're changing the directory data,
2294 * so that someone who does a chmod on the dir will wait until our call
2295 * completes. We do this in vnode order so that we don't deadlock,
2296 * which makes the code a little verbose.
2298 if (oldDscp == newDscp) {
2299 /* check for identical names */
2300 if (strcmp(oldNamep, newNamep) == 0)
2301 return CM_ERROR_RENAME_IDENTICAL;
2304 lock_ObtainMutex(&oldDscp->mx);
2305 cm_dnlcRemove(oldDscp, oldNamep);
2306 cm_dnlcRemove(oldDscp, newNamep);
2307 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2308 CM_SCACHESYNC_STOREDATA);
2309 lock_ReleaseMutex(&oldDscp->mx);
2312 /* two distinct dir vnodes */
2314 if (oldDscp->fid.cell != newDscp->fid.cell ||
2315 oldDscp->fid.volume != newDscp->fid.volume)
2316 return CM_ERROR_CROSSDEVLINK;
2318 /* shouldn't happen that we have distinct vnodes for two
2319 * different files, but could due to deliberate attack, or
2320 * stale info. Avoid deadlocks and quit now.
2322 if (oldDscp->fid.vnode == newDscp->fid.vnode)
2323 return CM_ERROR_CROSSDEVLINK;
2325 if (oldDscp->fid.vnode < newDscp->fid.vnode) {
2326 lock_ObtainMutex(&oldDscp->mx);
2327 cm_dnlcRemove(oldDscp, oldNamep);
2328 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2329 CM_SCACHESYNC_STOREDATA);
2330 lock_ReleaseMutex(&oldDscp->mx);
2332 lock_ObtainMutex(&newDscp->mx);
2333 cm_dnlcRemove(newDscp, newNamep);
2334 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2335 CM_SCACHESYNC_STOREDATA);
2336 lock_ReleaseMutex(&newDscp->mx);
2338 /* cleanup first one */
2339 cm_SyncOpDone(oldDscp, NULL,
2340 CM_SCACHESYNC_STOREDATA);
2345 /* lock the new vnode entry first */
2346 lock_ObtainMutex(&newDscp->mx);
2347 cm_dnlcRemove(newDscp, newNamep);
2348 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2349 CM_SCACHESYNC_STOREDATA);
2350 lock_ReleaseMutex(&newDscp->mx);
2352 lock_ObtainMutex(&oldDscp->mx);
2353 cm_dnlcRemove(oldDscp, oldNamep);
2354 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2355 CM_SCACHESYNC_STOREDATA);
2356 lock_ReleaseMutex(&oldDscp->mx);
2358 /* cleanup first one */
2359 cm_SyncOpDone(newDscp, NULL,
2360 CM_SCACHESYNC_STOREDATA);
2364 } /* two distinct vnodes */
2371 /* try the RPC now */
2373 code = cm_Conn(&oldDscp->fid, userp, reqp, &connp);
2376 oldDirAFSFid.Volume = oldDscp->fid.volume;
2377 oldDirAFSFid.Vnode = oldDscp->fid.vnode;
2378 oldDirAFSFid.Unique = oldDscp->fid.unique;
2379 newDirAFSFid.Volume = newDscp->fid.volume;
2380 newDirAFSFid.Vnode = newDscp->fid.vnode;
2381 newDirAFSFid.Unique = newDscp->fid.unique;
2382 code = RXAFS_Rename(connp->callp, &oldDirAFSFid, oldNamep,
2383 &newDirAFSFid, newNamep,
2384 &updatedOldDirStatus, &updatedNewDirStatus,
2386 } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
2387 &volSync, NULL, NULL, code));
2388 code = cm_MapRPCError(code, reqp);
2390 /* update the individual stat cache entries for the directories */
2391 lock_ObtainMutex(&oldDscp->mx);
2392 cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
2394 cm_MergeStatus(oldDscp, &updatedOldDirStatus, &volSync,
2397 lock_ReleaseMutex(&oldDscp->mx);
2399 /* and update it for the new one, too, if necessary */
2401 lock_ObtainMutex(&newDscp->mx);
2402 cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
2404 cm_MergeStatus(newDscp, &updatedNewDirStatus, &volSync,
2407 lock_ReleaseMutex(&newDscp->mx);
2410 /* and return error code */
2414 long cm_Lock(cm_scache_t *scp, unsigned char LockType,
2415 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
2416 u_long Timeout, cm_user_t *userp, cm_req_t *reqp,
2420 int Which = ((LockType & 0x1) ? LockRead : LockWrite);
2424 cm_file_lock_t *fileLock;
2428 /* Look for a conflict. Also, if we are asking for a shared lock,
2429 * look for another shared lock, so we don't have to do an RPC.
2433 fileLock = (cm_file_lock_t *)
2434 ((char *) q - offsetof(cm_file_lock_t, fileq));
2435 if ((fileLock->flags &
2436 (CM_FILELOCK_FLAG_INVALID | CM_FILELOCK_FLAG_WAITING))
2438 if ((LockType & 0x1) == 0
2439 || (fileLock->LockType & 0x1) == 0)
2440 return CM_ERROR_WOULDBLOCK;
2449 tfid.Volume = scp->fid.volume;
2450 tfid.Vnode = scp->fid.vnode;
2451 tfid.Unique = scp->fid.unique;
2452 lock_ReleaseMutex(&scp->mx);
2454 code = cm_Conn(&scp->fid, userp, reqp, &connp);
2456 code = RXAFS_SetLock(connp->callp, &tfid, Which,
2458 } while (cm_Analyze(connp, userp, reqp, &scp->fid, &volSync,
2460 lock_ObtainMutex(&scp->mx);
2461 code = cm_MapRPCError(code, reqp);
2464 if (code == 0 || Timeout != 0) {
2465 fileLock = malloc(sizeof(cm_file_lock_t));
2466 fileLock->LockType = LockType;
2468 fileLock->userp = userp;
2469 fileLock->fid = scp->fid;
2470 fileLock->LOffset = LOffset;
2471 fileLock->LLength = LLength;
2472 fileLock->flags = (code == 0 ? 0 : CM_FILELOCK_FLAG_WAITING);
2473 osi_QAdd(&scp->fileLocks, &fileLock->fileq);
2474 lock_ObtainWrite(&cm_scacheLock);
2475 osi_QAdd(&cm_allFileLocks, &fileLock->q);
2476 lock_ReleaseWrite(&cm_scacheLock);
2477 if (code != 0) *lockpp = fileLock;
2482 long cm_Unlock(cm_scache_t *scp, unsigned char LockType,
2483 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
2484 cm_user_t *userp, cm_req_t *reqp)
2487 int Which = ((LockType & 0x1) ? LockRead : LockWrite);
2491 cm_file_lock_t *fileLock, *ourLock;
2492 osi_queue_t *q, *qq;
2493 int anotherReader = 0;
2497 if (LargeIntegerLessThan(LLength, scp->length))
2500 /* Look for our own lock on the list, so as to remove it.
2501 * Also, determine if we're the last reader; if not, avoid an RPC.
2505 fileLock = (cm_file_lock_t *)
2506 ((char *) q - offsetof(cm_file_lock_t, fileq));
2508 && fileLock->userp == userp
2509 && LargeIntegerEqualTo(fileLock->LOffset, LOffset)
2510 && LargeIntegerEqualTo(fileLock->LLength, LLength)) {
2515 else if (fileLock->LockType & 0x1)
2520 /* ignore byte ranges */
2521 if (smallLock && !found)
2524 /* don't try to unlock other people's locks */
2526 return CM_ERROR_WOULDBLOCK;
2528 /* discard lock record */
2529 osi_QRemove(&scp->fileLocks, qq);
2531 * Don't delete it here; let the daemon delete it, to simplify
2532 * the daemon's traversal of the list.
2534 lock_ObtainWrite(&cm_scacheLock);
2535 ourLock->flags |= CM_FILELOCK_FLAG_INVALID;
2536 cm_ReleaseUser(ourLock->userp);
2537 lock_ReleaseWrite(&cm_scacheLock);
2539 if (!anotherReader) {
2540 tfid.Volume = scp->fid.volume;
2541 tfid.Vnode = scp->fid.vnode;
2542 tfid.Unique = scp->fid.unique;
2543 lock_ReleaseMutex(&scp->mx);
2545 code = cm_Conn(&scp->fid, userp, reqp, &connp);
2548 code = RXAFS_ReleaseLock(connp->callp, &tfid, &volSync);
2549 } while (cm_Analyze(connp, userp, reqp, &scp->fid, &volSync,
2551 code = cm_MapRPCError(code, reqp);
2552 lock_ObtainMutex(&scp->mx);
2558 void cm_CheckLocks()
2560 osi_queue_t *q, *nq;
2561 cm_file_lock_t *fileLock;
2570 lock_ObtainWrite(&cm_scacheLock);
2571 q = cm_allFileLocks;
2573 fileLock = (cm_file_lock_t *) q;
2575 if (fileLock->flags & CM_FILELOCK_FLAG_INVALID) {
2576 osi_QRemove(&cm_allFileLocks, q);
2579 else if (!(fileLock->flags & CM_FILELOCK_FLAG_WAITING)) {
2580 tfid.Volume = fileLock->fid.volume;
2581 tfid.Vnode = fileLock->fid.vnode;
2582 tfid.Unique = fileLock->fid.unique;
2583 lock_ReleaseWrite(&cm_scacheLock);
2585 code = cm_Conn(&fileLock->fid, fileLock->userp,
2588 code = RXAFS_ExtendLock(connp->callp, &tfid,
2590 } while (cm_Analyze(connp, fileLock->userp, &req,
2591 &fileLock->fid, &volSync, NULL, NULL,
2593 code = cm_MapRPCError(code, &req);
2594 lock_ObtainWrite(&cm_scacheLock);
2598 lock_ReleaseWrite(&cm_scacheLock);
2601 long cm_RetryLock(cm_file_lock_t *oldFileLock, int vcp_is_dead)
2604 int Which = ((oldFileLock->LockType & 0x1) ? LockRead : LockWrite);
2609 cm_file_lock_t *fileLock;
2615 code = CM_ERROR_TIMEDOUT;
2621 /* Look for a conflict. Also, if we are asking for a shared lock,
2622 * look for another shared lock, so we don't have to do an RPC.
2624 code = cm_GetSCache(&oldFileLock->fid, &scp, oldFileLock->userp, &req);
2630 fileLock = (cm_file_lock_t *)
2631 ((char *) q - offsetof(cm_file_lock_t, fileq));
2632 if ((fileLock->flags &
2633 (CM_FILELOCK_FLAG_INVALID | CM_FILELOCK_FLAG_WAITING))
2635 if ((oldFileLock->LockType & 0x1) == 0
2636 || (fileLock->LockType & 0x1) == 0) {
2637 cm_ReleaseSCache(scp);
2638 return CM_ERROR_WOULDBLOCK;
2648 tfid.Volume = oldFileLock->fid.volume;
2649 tfid.Vnode = oldFileLock->fid.vnode;
2650 tfid.Unique = oldFileLock->fid.unique;
2652 code = cm_Conn(&oldFileLock->fid, oldFileLock->userp,
2655 code = RXAFS_SetLock(connp->callp, &tfid, Which,
2657 } while (cm_Analyze(connp, oldFileLock->userp, &req,
2658 &oldFileLock->fid, &volSync,
2660 code = cm_MapRPCError(code, &req);
2664 if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
2665 lock_ObtainMutex(&scp->mx);
2666 osi_QRemove(&scp->fileLocks, &oldFileLock->fileq);
2667 lock_ReleaseMutex(&scp->mx);
2669 lock_ObtainWrite(&cm_scacheLock);
2671 oldFileLock->flags = 0;
2672 else if (code != CM_ERROR_WOULDBLOCK) {
2673 oldFileLock->flags |= CM_FILELOCK_FLAG_INVALID;
2674 cm_ReleaseUser(oldFileLock->userp);
2675 oldFileLock->userp = NULL;
2677 lock_ReleaseWrite(&cm_scacheLock);