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);
547 bufferOffset = thyper;
549 /* now get the data in the cache */
551 lock_ObtainMutex(&scp->mx);
552 code = cm_SyncOp(scp, bufferp, userp, reqp,
554 CM_SCACHESYNC_NEEDCALLBACK
556 | CM_SCACHESYNC_BUFLOCKED);
558 lock_ReleaseMutex(&scp->mx);
562 if (cm_HaveBuffer(scp, bufferp, 1)) {
563 lock_ReleaseMutex(&scp->mx);
567 /* otherwise, load the buffer and try again */
568 lock_ReleaseMutex(&bufferp->mx);
569 code = cm_GetBuffer(scp, bufferp, NULL, userp,
571 lock_ReleaseMutex(&scp->mx);
572 lock_ObtainMutex(&bufferp->mx);
577 lock_ReleaseMutex(&bufferp->mx);
578 buf_Release(bufferp);
582 } /* if (wrong buffer) ... */
584 /* now we have the buffer containing the entry we're interested
585 * in; copy it out if it represents a non-deleted entry.
587 entryInDir = curOffset.LowPart & (2048-1);
588 entryInBuffer = curOffset.LowPart & (buf_bufferSize - 1);
590 /* page header will help tell us which entries are free. Page
591 * header can change more often than once per buffer, since
592 * AFS 3 dir page size may be less than (but not more than) a
593 * buffer package buffer.
595 /* only look intra-buffer */
596 temp = curOffset.LowPart & (buf_bufferSize - 1);
597 temp &= ~(2048 - 1); /* turn off intra-page bits */
598 pageHeaderp = (cm_pageHeader_t *) (bufferp->datap + temp);
600 /* now determine which entry we're looking at in the page. If
601 * it is free (there's a free bitmap at the start of the dir),
602 * we should skip these 32 bytes.
604 slotInPage = (entryInDir & 0x7e0) >> 5;
605 if (!(pageHeaderp->freeBitmap[slotInPage>>3]
606 & (1 << (slotInPage & 0x7)))) {
607 /* this entry is free */
608 numDirChunks = 1; /* only skip this guy */
612 tp = bufferp->datap + entryInBuffer;
613 dep = (cm_dirEntry_t *) tp; /* now points to AFS3 dir entry */
615 /* while we're here, compute the next entry's location, too,
616 * since we'll need it when writing out the cookie into the
617 * dir listing stream.
619 numDirChunks = cm_NameEntries(dep->name, NULL);
621 /* compute the offset of the cookie representing the next entry */
622 nextEntryCookie = curOffset.LowPart
623 + (CM_DIR_CHUNKSIZE * numDirChunks);
625 if (dep->fid.vnode != 0) {
626 /* this is one of the entries to use: it is not deleted */
627 code = (*funcp)(scp, dep, parmp, &curOffset);
630 } /* if we're including this name */
633 /* and adjust curOffset to be where the new cookie is */
635 thyper.LowPart = CM_DIR_CHUNKSIZE * numDirChunks;
636 curOffset = LargeIntegerAdd(thyper, curOffset);
637 } /* while copying data for dir listing */
639 /* release the mutex */
641 lock_ReleaseMutex(&bufferp->mx);
642 buf_Release(bufferp);
647 int cm_NoneUpper(char *s)
651 if (c >= 'A' && c <= 'Z')
656 int cm_NoneLower(char *s)
660 if (c >= 'a' && c <= 'z')
665 long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
668 cm_lookupSearch_t *sp;
673 sp = (cm_lookupSearch_t *) rockp;
675 matchName = dep->name;
677 match = cm_stricmp(matchName, sp->searchNamep);
679 match = strcmp(matchName, sp->searchNamep);
683 && !cm_Is8Dot3(dep->name)) {
684 matchName = shortName;
685 cm_Gen8Dot3Name(dep, shortName, NULL);
687 match = cm_stricmp(matchName, sp->searchNamep);
689 match = strcmp(matchName, sp->searchNamep);
699 if (!sp->caseFold || matchName == shortName) {
700 sp->fid.vnode = ntohl(dep->fid.vnode);
701 sp->fid.unique = ntohl(dep->fid.unique);
702 return CM_ERROR_STOPNOW;
706 * If we get here, we are doing a case-insensitive search, and we
707 * have found a match. Now we determine what kind of match it is:
708 * exact, lower-case, upper-case, or none of the above. This is done
709 * in order to choose among matches, if there are more than one.
712 /* Exact matches are the best. */
713 match = strcmp(matchName, sp->searchNamep);
716 sp->fid.vnode = ntohl(dep->fid.vnode);
717 sp->fid.unique = ntohl(dep->fid.unique);
718 return CM_ERROR_STOPNOW;
721 /* Lower-case matches are next. */
724 if (cm_NoneUpper(matchName)) {
729 /* Upper-case matches are next. */
732 if (cm_NoneLower(matchName)) {
737 /* General matches are last. */
743 sp->fid.vnode = ntohl(dep->fid.vnode);
744 sp->fid.unique = ntohl(dep->fid.unique);
748 /* read the contents of a mount point into the appropriate string.
749 * called with locked scp, and returns with locked scp.
751 long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
758 if (scp->mountPointStringp)
761 /* otherwise, we have to read it in */
762 lock_ReleaseMutex(&scp->mx);
764 lock_ObtainRead(&scp->bufCreateLock);
765 thyper.LowPart = thyper.HighPart = 0;
766 code = buf_Get(scp, &thyper, &bufp);
767 lock_ReleaseRead(&scp->bufCreateLock);
769 lock_ObtainMutex(&scp->mx);
774 code = cm_SyncOp(scp, bufp, userp, reqp, 0,
775 CM_SCACHESYNC_READ | CM_SCACHESYNC_NEEDCALLBACK);
780 if (cm_HaveBuffer(scp, bufp, 0))
783 /* otherwise load buffer */
784 code = cm_GetBuffer(scp, bufp, NULL, userp, reqp);
789 /* locked, has callback, has valid data in buffer */
790 if ((tlen = scp->length.LowPart) > 1000)
791 return CM_ERROR_TOOBIG;
793 code = CM_ERROR_INVAL;
797 /* someone else did the work while we were out */
798 if (scp->mountPointStringp) {
803 /* otherwise, copy out the link */
804 scp->mountPointStringp = malloc(tlen);
805 memcpy(scp->mountPointStringp, bufp->datap, tlen);
807 /* now make it null-terminated. Note that the original contents of a
808 * link that is a mount point is "#volname." where "." is there just to
809 * be turned into a null. That is, we can trash the last char of the
810 * link without damaging the vol name. This is a stupid convention,
811 * but that's the protocol.
813 scp->mountPointStringp[tlen-1] = 0;
822 /* called with a locked scp and chases the mount point, yielding outScpp.
823 * scp remains locked, just for simplicity of describing the interface.
825 long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
826 cm_req_t *reqp, cm_scache_t **outScpp)
841 if (scp->mountRootFidp && scp->mountRootGen >= cm_mountRootGen) {
842 tfid = *scp->mountRootFidp;
843 lock_ReleaseMutex(&scp->mx);
844 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
845 lock_ObtainMutex(&scp->mx);
849 /* parse the volume name */
850 mpNamep = scp->mountPointStringp;
852 tlen = strlen(scp->mountPointStringp);
853 mtType = *scp->mountPointStringp;
854 cellNamep = malloc(tlen);
855 volNamep = malloc(tlen);
857 cp = strrchr(mpNamep, ':');
859 /* cellular mount point */
860 memset(cellNamep, 0, tlen);
861 strncpy(cellNamep, mpNamep+1, cp - mpNamep - 1);
862 strcpy(volNamep, cp+1);
863 /* now look up the cell */
864 cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE);
868 strcpy(volNamep, mpNamep+1);
870 cellp = cm_FindCellByID(scp->fid.cell);
874 code = CM_ERROR_NOSUCHCELL;
878 vnLength = strlen(volNamep);
879 if (vnLength >= 8 && strcmp(volNamep + vnLength - 7, ".backup") == 0)
881 else if (vnLength >= 10
882 && strcmp(volNamep + vnLength - 9, ".readonly") == 0)
887 /* check for backups within backups */
889 && (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
890 == CM_SCACHEFLAG_RO) {
891 code = CM_ERROR_NOSUCHVOLUME;
895 /* now we need to get the volume */
896 lock_ReleaseMutex(&scp->mx);
897 code = cm_GetVolumeByName(cellp, volNamep, userp, reqp, 0, &volp);
898 lock_ObtainMutex(&scp->mx);
901 /* save the parent of the volume root for this is the
902 * place where the volume is mounted and we must remember
903 * this in the volume structure rather than just in the
904 * scache entry lest the scache entry gets recycled
907 lock_ObtainMutex(&volp->mx);
908 if(volp->dotdotFidp == (cm_fid_t *) NULL)
909 volp->dotdotFidp = (cm_fid_t *) malloc(sizeof(cm_fid_t));
910 *(volp->dotdotFidp) = dscp->fid;
911 lock_ReleaseMutex(&volp->mx);
913 if (scp->mountRootFidp == 0) {
914 scp->mountRootFidp = malloc(sizeof(cm_fid_t));
916 scp->mountRootFidp->cell = cellp->cellID;
917 /* if the mt pt is in a read-only volume (not just a
918 * backup), and if there is a read-only volume for the
919 * target, and if this is a type '#' mount point, use
920 * the read-only, otherwise use the one specified.
922 if (mtType == '#' && (scp->flags & CM_SCACHEFLAG_PURERO)
923 && volp->roID != 0 && type == RWVOL)
926 scp->mountRootFidp->volume = volp->roID;
927 else if (type == BACKVOL)
928 scp->mountRootFidp->volume = volp->bkID;
930 scp->mountRootFidp->volume = volp->rwID;
932 /* the rest of the fid is a magic number */
933 scp->mountRootFidp->vnode = 1;
934 scp->mountRootFidp->unique = 1;
935 scp->mountRootGen = cm_mountRootGen;
937 tfid = *scp->mountRootFidp;
938 lock_ReleaseMutex(&scp->mx);
939 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
940 lock_ObtainMutex(&scp->mx);
949 long cm_LookupInternal(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
950 cm_req_t *reqp, cm_scache_t **outpScpp)
953 int dnlcHit = 1; /* did we hit in the dnlc? yes, we did */
954 cm_scache_t *tscp = NULL;
955 cm_scache_t *mountedScp;
956 cm_lookupSearch_t rock;
959 if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
960 && strcmp(namep, "..") == 0) {
961 if (dscp->dotdotFidp == (cm_fid_t *)NULL
962 || dscp->dotdotFidp->volume == 0)
963 return CM_ERROR_NOSUCHVOLUME;
964 rock.fid = *dscp->dotdotFidp;
968 memset(&rock, 0, sizeof(rock));
969 rock.fid.cell = dscp->fid.cell;
970 rock.fid.volume = dscp->fid.volume;
971 rock.searchNamep = namep;
972 rock.caseFold = (flags & CM_FLAG_CASEFOLD);
973 rock.hasTilde = ((strchr(namep, '~') != NULL) ? 1 : 0);
975 /* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
976 code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
977 (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
979 /* code == 0 means we fell off the end of the dir, while stopnow means
980 * that we stopped early, probably because we found the entry we're
981 * looking for. Any other non-zero code is an error.
983 if (code && code != CM_ERROR_STOPNOW)
986 getroot = (dscp==cm_rootSCachep) ;
988 if (!cm_freelanceEnabled || !getroot) {
989 if (flags & CM_FLAG_CHECKPATH)
990 return CM_ERROR_NOSUCHPATH;
992 return CM_ERROR_NOSUCHFILE;
994 else { /* nonexistent dir on freelance root, so add it */
995 osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %s",
996 osi_LogSaveString(afsd_logp,namep));
997 code = cm_FreelanceAddMount(namep, namep, "root.cell.", namep[0] == '.', &rock.fid);
998 if (code < 0) { /* add mount point failed, so give up */
999 if (flags & CM_FLAG_CHECKPATH)
1000 return CM_ERROR_NOSUCHPATH;
1002 return CM_ERROR_NOSUCHFILE;
1004 tscp = NULL; /* to force call of cm_GetSCache */
1009 if ( !tscp ) /* we did not find it in the dnlc */
1012 code = cm_GetSCache(&rock.fid, &tscp, userp, reqp);
1016 /* tscp is now held */
1018 lock_ObtainMutex(&tscp->mx);
1019 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1020 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1022 lock_ReleaseMutex(&tscp->mx);
1023 cm_ReleaseSCache(tscp);
1026 /* tscp is now locked */
1028 if (!(flags & CM_FLAG_NOMOUNTCHASE)
1029 && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
1030 /* mount points are funny: they have a volume name to mount
1033 code = cm_ReadMountPoint(tscp, userp, reqp);
1035 code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
1037 lock_ReleaseMutex(&tscp->mx);
1038 cm_ReleaseSCache(tscp);
1045 lock_ReleaseMutex(&tscp->mx);
1048 /* copy back pointer */
1051 /* insert scache in dnlc */
1052 if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
1053 /* lock the directory entry to prevent racing callback revokes */
1054 lock_ObtainMutex(&dscp->mx);
1055 if ( dscp->cbServerp && dscp->cbExpires )
1056 cm_dnlcEnter(dscp, namep, tscp);
1057 lock_ReleaseMutex(&dscp->mx);
1064 int cm_ExpandSysName(char *inp, char *outp, long outSize, unsigned int index)
1069 tp = strrchr(inp, '@');
1071 return 0; /* no @sys */
1073 if (strcmp(tp, "@sys") != 0)
1074 return 0; /* no @sys */
1076 /* caller just wants to know if this is a valid @sys type of name */
1080 if (index >= MAXNUMSYSNAMES)
1083 /* otherwise generate the properly expanded @sys name */
1084 prefixCount = tp - inp;
1086 strncpy(outp, inp, prefixCount); /* copy out "a." from "a.@sys" */
1087 outp[prefixCount] = 0; /* null terminate the "a." */
1088 strcat(outp, cm_sysNameList[index]);/* append i386_nt40 */
1092 long cm_Lookup(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
1093 cm_req_t *reqp, cm_scache_t **outpScpp)
1097 int sysNameIndex = 0;
1098 cm_scache_t *scp = 0;
1100 for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
1101 code = cm_ExpandSysName(namep, tname, sizeof(tname), sysNameIndex);
1103 code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
1109 cm_ReleaseSCache(scp);
1113 return cm_LookupInternal(dscp, namep, flags, userp, reqp, outpScpp);
1117 /* None of the possible sysName expansions could be found */
1118 if (flags & CM_FLAG_CHECKPATH)
1119 return CM_ERROR_NOSUCHPATH;
1121 return CM_ERROR_NOSUCHFILE;
1124 long cm_Unlink(cm_scache_t *dscp, char *namep, cm_user_t *userp, cm_req_t *reqp)
1130 AFSFetchStatus newDirStatus;
1132 struct rx_connection * callp;
1134 #ifdef AFS_FREELANCE_CLIENT
1135 if (cm_freelanceEnabled && dscp == cm_rootSCachep) {
1136 /* deleting a mount point from the root dir. */
1137 code = cm_FreelanceRemoveMount(namep);
1142 /* make sure we don't screw up the dir status during the merge */
1143 lock_ObtainMutex(&dscp->mx);
1144 sflags = CM_SCACHESYNC_STOREDATA;
1145 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1146 lock_ReleaseMutex(&dscp->mx);
1151 afsFid.Volume = dscp->fid.volume;
1152 afsFid.Vnode = dscp->fid.vnode;
1153 afsFid.Unique = dscp->fid.unique;
1155 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1159 callp = cm_GetRxConn(connp);
1160 code = RXAFS_RemoveFile(callp, &afsFid, namep,
1161 &newDirStatus, &volSync);
1162 rx_PutConnection(callp);
1164 } while (cm_Analyze(connp, userp, reqp, &dscp->fid, &volSync, NULL, NULL, code));
1165 code = cm_MapRPCError(code, reqp);
1167 lock_ObtainMutex(&dscp->mx);
1168 cm_dnlcRemove(dscp, namep);
1169 cm_SyncOpDone(dscp, NULL, sflags);
1171 cm_MergeStatus(dscp, &newDirStatus, &volSync, userp, 0);
1172 lock_ReleaseMutex(&dscp->mx);
1177 /* called with a locked vnode, and fills in the link info.
1178 * returns this the vnode still locked.
1180 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1187 lock_AssertMutex(&linkScp->mx);
1188 if (!linkScp->mountPointStringp) {
1189 /* read the link data */
1190 lock_ReleaseMutex(&linkScp->mx);
1191 thyper.LowPart = thyper.HighPart = 0;
1192 code = buf_Get(linkScp, &thyper, &bufp);
1193 lock_ObtainMutex(&linkScp->mx);
1197 code = cm_SyncOp(linkScp, bufp, userp, reqp, 0,
1198 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1203 if (cm_HaveBuffer(linkScp, bufp, 0))
1206 code = cm_GetBuffer(linkScp, bufp, NULL, userp, reqp);
1211 } /* while loop to get the data */
1213 /* now if we still have no link read in,
1214 * copy the data from the buffer */
1215 if ((temp = linkScp->length.LowPart) >= 1024) {
1217 return CM_ERROR_TOOBIG;
1220 /* otherwise, it fits; make sure it is still null (could have
1221 * lost race with someone else referencing this link above),
1222 * and if so, copy in the data.
1224 if (linkScp->mountPointStringp == NULL) {
1225 linkScp->mountPointStringp = malloc(temp+1);
1226 strncpy(linkScp->mountPointStringp, bufp->datap, temp);
1227 linkScp->mountPointStringp[temp] = 0; /* null terminate */
1230 } /* don't have sym link contents cached */
1235 /* called with a held vnode and a path suffix, with the held vnode being a
1236 * symbolic link. Our goal is to generate a new path to interpret, and return
1237 * this new path in newSpaceBufferp. If the new vnode is relative to a dir
1238 * other than the directory containing the symbolic link, then the new root is
1239 * returned in *newRootScpp, otherwise a null is returned there.
1241 long cm_AssembleLink(cm_scache_t *linkScp, char *pathSuffixp,
1242 cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1243 cm_user_t *userp, cm_req_t *reqp)
1249 lock_ObtainMutex(&linkScp->mx);
1250 code = cm_HandleLink(linkScp, userp, reqp);
1254 /* if we may overflow the buffer, bail out; buffer is signficantly
1255 * bigger than max path length, so we don't really have to worry about
1256 * being a little conservative here.
1258 if (strlen(linkScp->mountPointStringp) + strlen(pathSuffixp) + 2
1259 >= CM_UTILS_SPACESIZE)
1260 return CM_ERROR_TOOBIG;
1262 tsp = cm_GetSpace();
1263 linkp = linkScp->mountPointStringp;
1264 if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1265 if (strlen(linkp) > cm_mountRootLen)
1266 strcpy(tsp->data, linkp+cm_mountRootLen+1);
1269 *newRootScpp = cm_rootSCachep;
1270 cm_HoldSCache(cm_rootSCachep);
1271 } else if (*linkp == '\\' || *linkp == '/') {
1272 /* formerly, this was considered to be from the AFS root,
1273 * but this seems to create problems. instead, we will just
1274 * reject the link */
1276 strcpy(tsp->data, linkp+1);
1277 *newRootScpp = cm_rootSCachep;
1278 cm_HoldSCache(cm_rootSCachep);
1280 code = CM_ERROR_NOSUCHPATH;
1285 /* a relative link */
1286 strcpy(tsp->data, linkp);
1287 *newRootScpp = NULL;
1289 if (pathSuffixp[0] != 0) { /* if suffix string is non-null */
1290 strcat(tsp->data, "\\");
1291 strcat(tsp->data, pathSuffixp);
1293 *newSpaceBufferp = tsp;
1297 lock_ReleaseMutex(&linkScp->mx);
1301 long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
1302 cm_user_t *userp, char *tidPathp, cm_req_t *reqp, cm_scache_t **outScpp)
1305 char *tp; /* ptr moving through input buffer */
1306 char tc; /* temp char */
1307 int haveComponent; /* has new component started? */
1308 char component[256]; /* this is the new component */
1309 char *cp; /* component name being assembled */
1310 cm_scache_t *tscp; /* current location in the hierarchy */
1311 cm_scache_t *nscp; /* next dude down */
1312 cm_scache_t *dirScp; /* last dir we searched */
1313 cm_scache_t *linkScp; /* new root for the symlink we just
1315 cm_space_t *psp; /* space for current path, if we've hit
1317 cm_space_t *tempsp; /* temp vbl */
1318 char *restp; /* rest of the pathname to interpret */
1319 int symlinkCount; /* count of # of symlinks traversed */
1320 int extraFlag; /* avoid chasing mt pts for dir cmd */
1321 int phase = 1; /* 1 = tidPathp, 2 = pathp */
1334 cm_HoldSCache(tscp);
1339 /* map Unix slashes into DOS ones so we can interpret Unix
1345 if (!haveComponent) {
1364 /* we have a component here */
1365 if (tc == 0 || tc == '\\') {
1366 /* end of the component; we're at the last
1367 * component if tc == 0. However, if the last
1368 * is a symlink, we have more to do.
1370 *cp++ = 0; /* add null termination */
1372 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
1373 extraFlag = CM_FLAG_NOMOUNTCHASE;
1374 code = cm_Lookup(tscp, component,
1376 userp, reqp, &nscp);
1379 cm_ReleaseSCache(tscp);
1384 haveComponent = 0; /* component done */
1385 dirScp = tscp; /* for some symlinks */
1386 tscp = nscp; /* already held */
1387 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
1389 cm_ReleaseSCache(dirScp);
1393 /* now, if tscp is a symlink, we should follow
1394 * it and assemble the path again.
1396 lock_ObtainMutex(&tscp->mx);
1397 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1398 CM_SCACHESYNC_GETSTATUS
1399 | CM_SCACHESYNC_NEEDCALLBACK);
1401 lock_ReleaseMutex(&tscp->mx);
1402 cm_ReleaseSCache(tscp);
1403 cm_ReleaseSCache(dirScp);
1406 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
1407 /* this is a symlink; assemble a new buffer */
1408 lock_ReleaseMutex(&tscp->mx);
1409 if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
1410 cm_ReleaseSCache(tscp);
1411 cm_ReleaseSCache(dirScp);
1414 return CM_ERROR_TOO_MANY_SYMLINKS;
1420 code = cm_AssembleLink(tscp, restp, &linkScp, &tempsp, userp, reqp);
1422 /* something went wrong */
1423 cm_ReleaseSCache(tscp);
1424 cm_ReleaseSCache(dirScp);
1428 /* otherwise, tempsp has the new path,
1429 * and linkScp is the new root from
1430 * which to interpret that path.
1431 * Continue with the namei processing,
1432 * also doing the bookkeeping for the
1433 * space allocation and tracking the
1434 * vnode reference counts.
1440 cm_ReleaseSCache(tscp);
1444 * now, if linkScp is null, that's
1445 * AssembleLink's way of telling us that
1446 * the sym link is relative to the dir
1447 * containing the link. We have a ref
1448 * to it in dirScp, and we hold it now
1449 * and reuse it as the new spot in the
1453 cm_HoldSCache(dirScp);
1456 } /* if we have a sym link */
1458 /* not a symlink, we may be done */
1459 lock_ReleaseMutex(&tscp->mx);
1466 cm_ReleaseSCache(dirScp);
1471 cm_ReleaseSCache(dirScp);
1472 } /* end of a component */
1474 } /* we have a component */
1475 } /* big while loop over all components */
1485 /* called with a dir, and a vnode within the dir that happens to be a symlink.
1486 * We chase the link, and return a held pointer to the target, if it exists,
1487 * in *outScpp. If we succeed, we return 0, otherwise we return an error code
1488 * and do not hold or return a target vnode.
1490 * This is very similar to calling cm_NameI with the last component of a name,
1491 * which happens to be a symlink, except that we've already passed by the name.
1493 * This function is typically called by the directory listing functions, which
1494 * encounter symlinks but need to return the proper file length so programs
1495 * like "more" work properly when they make use of the attributes retrieved from
1498 * The input vnode should not be locked when this function is called.
1500 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
1501 cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
1505 cm_scache_t *newRootScp;
1507 osi_Log1(afsd_logp, "Evaluating symlink vp %x", linkScp);
1509 code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
1513 /* now, if newRootScp is NULL, we're really being told that the symlink
1514 * is relative to the current directory (dscp).
1516 if (newRootScp == NULL) {
1518 cm_HoldSCache(dscp);
1521 code = cm_NameI(newRootScp, spacep->data,
1522 CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
1523 userp, NULL, reqp, outScpp);
1525 /* this stuff is allocated no matter what happened on the namei call,
1527 cm_FreeSpace(spacep);
1528 cm_ReleaseSCache(newRootScp);
1533 /* make this big enough so that one buffer of dir pages won't overflow. We'll
1534 * check anyway, but we want to minimize the chance that we have to leave stuff
1537 #define CM_BULKMAX 128
1539 /* rock for bulk stat calls */
1540 typedef struct cm_bulkStat {
1541 osi_hyper_t bufOffset; /* only do it for things in this buffer page */
1543 /* info for the actual call */
1544 int counter; /* next free slot */
1545 AFSFid fids[CM_BULKMAX];
1546 AFSFetchStatus stats[CM_BULKMAX];
1547 AFSCallBack callbacks[CM_BULKMAX];
1550 /* for a given entry, make sure that it isn't in the stat cache, and then
1551 * add it to the list of file IDs to be obtained.
1553 * Don't bother adding it if we already have a vnode. Note that the dir
1554 * is locked, so we have to be careful checking the vnode we're thinking of
1555 * processing, to avoid deadlocks.
1557 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
1568 /* Don't overflow bsp. */
1569 if (bsp->counter >= CM_BULKMAX)
1570 return CM_ERROR_STOPNOW;
1572 thyper.LowPart = buf_bufferSize;
1573 thyper.HighPart = 0;
1574 thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
1576 /* thyper is now the first byte past the end of the record we're
1577 * interested in, and bsp->bufOffset is the first byte of the record
1578 * we're interested in.
1579 * Skip data in the others.
1582 if (LargeIntegerLessThan(*offp, bsp->bufOffset))
1584 if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
1585 return CM_ERROR_STOPNOW;
1586 if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
1589 tfid.cell = scp->fid.cell;
1590 tfid.volume = scp->fid.volume;
1591 tfid.vnode = ntohl(dep->fid.vnode);
1592 tfid.unique = ntohl(dep->fid.unique);
1593 tscp = cm_FindSCache(&tfid);
1595 if (lock_TryMutex(&tscp->mx)) {
1596 /* we have an entry that we can look at */
1597 if (cm_HaveCallback(tscp)) {
1598 /* we have a callback on it. Don't bother
1599 * fetching this stat entry, since we're happy
1600 * with the info we have.
1602 lock_ReleaseMutex(&tscp->mx);
1603 cm_ReleaseSCache(tscp);
1606 lock_ReleaseMutex(&tscp->mx);
1608 cm_ReleaseSCache(tscp);
1611 #ifdef AFS_FREELANCE_CLIENT
1612 // yj: if this is a mountpoint under root.afs then we don't want it
1613 // to be bulkstat-ed, instead, we call getSCache directly and under
1614 // getSCache, it is handled specially.
1615 if ( cm_freelanceEnabled &&
1616 tfid.cell==AFS_FAKE_ROOT_CELL_ID &&
1617 tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
1618 !(tfid.vnode==0x1 && tfid.unique==0x1) )
1620 osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
1621 return cm_GetSCache(&tfid, &tscp, NULL, NULL);
1623 #endif /* AFS_FREELANCE_CLIENT */
1626 bsp->fids[i].Volume = scp->fid.volume;
1627 bsp->fids[i].Vnode = tfid.vnode;
1628 bsp->fids[i].Unique = tfid.unique;
1632 /* called with a locked scp and a pointer to a buffer. Make bulk stat
1633 * calls on all undeleted files in the page of the directory specified.
1635 void cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
1639 cm_bulkStat_t bb; /* this is *BIG*, probably 12K or so;
1640 * watch for stack problems */
1641 AFSCBFids fidStruct;
1642 AFSBulkStats statStruct;
1644 AFSCBs callbackStruct;
1647 cm_callbackRequest_t cbReq;
1653 struct rx_connection * callp;
1655 osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%x", (long) dscp);
1657 /* should be on a buffer boundary */
1658 osi_assert((offsetp->LowPart & (buf_bufferSize - 1)) == 0);
1661 bb.bufOffset = *offsetp;
1663 lock_ReleaseMutex(&dscp->mx);
1664 /* first, assemble the file IDs we need to stat */
1665 code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) &bb, offsetp, userp,
1667 lock_ObtainMutex(&dscp->mx);
1669 /* if we failed, bail out early */
1670 if (code && code != CM_ERROR_STOPNOW) return;
1672 /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
1673 * make the calls to create the entries. Handle AFSCBMAX files at a
1677 while (filex < bb.counter) {
1678 filesThisCall = bb.counter - filex;
1679 if (filesThisCall > AFSCBMAX) filesThisCall = AFSCBMAX;
1681 fidStruct.AFSCBFids_len = filesThisCall;
1682 fidStruct.AFSCBFids_val = &bb.fids[filex];
1683 statStruct.AFSBulkStats_len = filesThisCall;
1684 statStruct.AFSBulkStats_val = &bb.stats[filex];
1685 callbackStruct.AFSCBs_len = filesThisCall;
1686 callbackStruct.AFSCBs_val = &bb.callbacks[filex];
1687 cm_StartCallbackGrantingCall(NULL, &cbReq);
1688 osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
1690 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1694 callp = cm_GetRxConn(connp);
1695 code = RXAFS_BulkStatus(callp, &fidStruct,
1696 &statStruct, &callbackStruct, &volSync);
1697 rx_PutConnection(callp);
1699 } while (cm_Analyze(connp, userp, reqp, &dscp->fid,
1700 &volSync, NULL, &cbReq, code));
1701 code = cm_MapRPCError(code, reqp);
1703 osi_Log0(afsd_logp, "CALL BulkStatus DONE");
1705 /* may as well quit on an error, since we're not going to do
1706 * much better on the next immediate call, either.
1711 /* otherwise, we should do the merges */
1712 for(i = 0; i<filesThisCall; i++) {
1714 tfid.cell = dscp->fid.cell;
1715 tfid.volume = bb.fids[j].Volume;
1716 tfid.vnode = bb.fids[j].Vnode;
1717 tfid.unique = bb.fids[j].Unique;
1718 code = cm_GetSCache(&tfid, &scp, userp, reqp);
1722 /* otherwise, if this entry has no callback info,
1725 lock_ObtainMutex(&scp->mx);
1726 /* now, we have to be extra paranoid on merging in this
1727 * information, since we didn't use cm_SyncOp before
1728 * starting the fetch to make sure that no bad races
1729 * were occurring. Specifically, we need to make sure
1730 * we don't obliterate any newer information in the
1731 * vnode than have here.
1733 * Right now, be pretty conservative: if there's a
1734 * callback or a pending call, skip it.
1736 if (scp->cbServerp == NULL
1738 (CM_SCACHEFLAG_FETCHING
1739 | CM_SCACHEFLAG_STORING
1740 | CM_SCACHEFLAG_SIZESTORING))) {
1741 cm_EndCallbackGrantingCall(scp, &cbReq,
1743 CM_CALLBACK_MAINTAINCOUNT);
1744 cm_MergeStatus(scp, &bb.stats[j], &volSync,
1747 lock_ReleaseMutex(&scp->mx);
1748 cm_ReleaseSCache(scp);
1749 } /* all files in the response */
1750 /* now tell it to drop the count,
1751 * after doing the vnode processing above */
1752 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
1754 filex += filesThisCall;
1755 } /* while there are still more files to process */
1756 osi_Log0(afsd_logp, "END cm_TryBulkStat");
1759 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
1763 /* initialize store back mask as inexpensive local variable */
1765 memset(statusp, 0, sizeof(AFSStoreStatus));
1767 /* copy out queued info from scache first, if scp passed in */
1769 if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
1770 statusp->ClientModTime = scp->clientModTime;
1771 mask |= AFS_SETMODTIME;
1772 scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
1777 /* now add in our locally generated request */
1778 if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
1779 statusp->ClientModTime = attrp->clientModTime;
1780 mask |= AFS_SETMODTIME;
1782 if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
1783 statusp->UnixModeBits = attrp->unixModeBits;
1784 mask |= AFS_SETMODE;
1786 if (attrp->mask & CM_ATTRMASK_OWNER) {
1787 statusp->Owner = attrp->owner;
1788 mask |= AFS_SETOWNER;
1790 if (attrp->mask & CM_ATTRMASK_GROUP) {
1791 statusp->Group = attrp->group;
1792 mask |= AFS_SETGROUP;
1795 statusp->Mask = mask;
1798 /* set the file size, and make sure that all relevant buffers have been
1799 * truncated. Ensure that any partially truncated buffers have been zeroed
1800 * to the end of the buffer.
1802 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
1808 /* start by locking out buffer creation */
1809 lock_ObtainWrite(&scp->bufCreateLock);
1811 /* verify that this is a file, not a dir or a symlink */
1812 lock_ObtainMutex(&scp->mx);
1813 code = cm_SyncOp(scp, NULL, userp, reqp, 0,
1814 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1818 if (scp->fileType != CM_SCACHETYPE_FILE) {
1819 code = CM_ERROR_ISDIR;
1824 if (LargeIntegerLessThan(*sizep, scp->length))
1829 lock_ReleaseMutex(&scp->mx);
1831 /* can't hold scp->mx lock here, since we may wait for a storeback to
1832 * finish if the buffer package is cleaning a buffer by storing it to
1836 buf_Truncate(scp, userp, reqp, sizep);
1838 /* now ensure that file length is short enough, and update truncPos */
1839 lock_ObtainMutex(&scp->mx);
1841 /* make sure we have a callback (so we have the right value for the
1842 * length), and wait for it to be safe to do a truncate.
1844 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
1845 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
1846 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
1850 if (LargeIntegerLessThan(*sizep, scp->length)) {
1851 /* a real truncation. If truncPos is not set yet, or is bigger
1852 * than where we're truncating the file, set truncPos to this
1857 if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
1858 || LargeIntegerLessThan(*sizep, scp->length)) {
1860 scp->truncPos = *sizep;
1861 scp->mask |= CM_SCACHEMASK_TRUNCPOS;
1863 /* in either case, the new file size has been changed */
1864 scp->length = *sizep;
1865 scp->mask |= CM_SCACHEMASK_LENGTH;
1867 else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
1868 /* really extending the file */
1869 scp->length = *sizep;
1870 scp->mask |= CM_SCACHEMASK_LENGTH;
1873 /* done successfully */
1877 lock_ReleaseMutex(&scp->mx);
1878 lock_ReleaseWrite(&scp->bufCreateLock);
1883 /* set the file size or other attributes (but not both at once) */
1884 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
1889 AFSFetchStatus afsOutStatus;
1893 AFSStoreStatus afsInStatus;
1894 struct rx_connection * callp;
1896 /* handle file length setting */
1897 if (attrp->mask & CM_ATTRMASK_LENGTH)
1898 return cm_SetLength(scp, &attrp->length, userp, reqp);
1900 flags = CM_SCACHESYNC_STORESTATUS;
1902 lock_ObtainMutex(&scp->mx);
1903 /* otherwise, we have to make an RPC to get the status */
1904 code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
1906 /* make the attr structure */
1907 cm_StatusFromAttr(&afsInStatus, scp, attrp);
1909 lock_ReleaseMutex(&scp->mx);
1913 /* now make the RPC */
1914 osi_Log1(afsd_logp, "CALL StoreStatus vp %x", (long) scp);
1915 tfid.Volume = scp->fid.volume;
1916 tfid.Vnode = scp->fid.vnode;
1917 tfid.Unique = scp->fid.unique;
1919 code = cm_Conn(&scp->fid, userp, reqp, &connp);
1923 callp = cm_GetRxConn(connp);
1924 code = RXAFS_StoreStatus(callp, &tfid,
1925 &afsInStatus, &afsOutStatus, &volSync);
1926 rx_PutConnection(callp);
1928 } while (cm_Analyze(connp, userp, reqp,
1929 &scp->fid, &volSync, NULL, NULL, code));
1930 code = cm_MapRPCError(code, reqp);
1932 osi_Log1(afsd_logp, "CALL StoreStatus DONE, code %d", code);
1934 lock_ObtainMutex(&scp->mx);
1935 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
1937 cm_MergeStatus(scp, &afsOutStatus, &volSync, userp,
1938 CM_MERGEFLAG_FORCE);
1940 /* if we're changing the mode bits, discard the ACL cache,
1941 * since we changed the mode bits.
1943 if (afsInStatus.Mask & AFS_SETMODE) cm_FreeAllACLEnts(scp);
1944 lock_ReleaseMutex(&scp->mx);
1948 long cm_Create(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
1949 cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
1954 cm_callbackRequest_t cbReq;
1959 AFSStoreStatus inStatus;
1960 AFSFetchStatus updatedDirStatus;
1961 AFSFetchStatus newFileStatus;
1962 AFSCallBack newFileCallback;
1964 struct rx_connection * callp;
1966 /* can't create names with @sys in them; must expand it manually first.
1967 * return "invalid request" if they try.
1969 if (cm_ExpandSysName(namep, NULL, 0, 0)) {
1970 return CM_ERROR_ATSYS;
1973 /* before starting the RPC, mark that we're changing the file data, so
1974 * that someone who does a chmod will know to wait until our call
1977 lock_ObtainMutex(&dscp->mx);
1978 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
1980 cm_StartCallbackGrantingCall(NULL, &cbReq);
1982 lock_ReleaseMutex(&dscp->mx);
1988 cm_StatusFromAttr(&inStatus, NULL, attrp);
1990 /* try the RPC now */
1992 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1996 dirAFSFid.Volume = dscp->fid.volume;
1997 dirAFSFid.Vnode = dscp->fid.vnode;
1998 dirAFSFid.Unique = dscp->fid.unique;
2000 callp = cm_GetRxConn(connp);
2001 code = RXAFS_CreateFile(connp->callp, &dirAFSFid, namep,
2002 &inStatus, &newAFSFid, &newFileStatus,
2003 &updatedDirStatus, &newFileCallback,
2005 rx_PutConnection(callp);
2007 } while (cm_Analyze(connp, userp, reqp,
2008 &dscp->fid, &volSync, NULL, &cbReq, code));
2009 code = cm_MapRPCError(code, reqp);
2011 lock_ObtainMutex(&dscp->mx);
2012 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2014 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2016 lock_ReleaseMutex(&dscp->mx);
2018 /* now try to create the file's entry, too, but be careful to
2019 * make sure that we don't merge in old info. Since we weren't locking
2020 * out any requests during the file's creation, we may have pretty old
2024 newFid.cell = dscp->fid.cell;
2025 newFid.volume = dscp->fid.volume;
2026 newFid.vnode = newAFSFid.Vnode;
2027 newFid.unique = newAFSFid.Unique;
2028 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2030 lock_ObtainMutex(&scp->mx);
2031 if (!cm_HaveCallback(scp)) {
2032 cm_MergeStatus(scp, &newFileStatus, &volSync,
2034 cm_EndCallbackGrantingCall(scp, &cbReq,
2035 &newFileCallback, 0);
2038 lock_ReleaseMutex(&scp->mx);
2043 /* make sure we end things properly */
2045 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2050 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
2054 lock_ObtainWrite(&scp->bufCreateLock);
2055 code = buf_CleanVnode(scp, userp, reqp);
2056 lock_ReleaseWrite(&scp->bufCreateLock);
2058 lock_ObtainMutex(&scp->mx);
2059 scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA
2060 | CM_SCACHEFLAG_OUTOFSPACE);
2061 if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
2062 | CM_SCACHEMASK_CLIENTMODTIME
2063 | CM_SCACHEMASK_LENGTH))
2064 code = cm_StoreMini(scp, userp, reqp);
2065 lock_ReleaseMutex(&scp->mx);
2070 long cm_MakeDir(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2071 cm_user_t *userp, cm_req_t *reqp)
2076 cm_callbackRequest_t cbReq;
2081 AFSStoreStatus inStatus;
2082 AFSFetchStatus updatedDirStatus;
2083 AFSFetchStatus newDirStatus;
2084 AFSCallBack newDirCallback;
2086 struct rx_connection * callp;
2088 /* can't create names with @sys in them; must expand it manually first.
2089 * return "invalid request" if they try.
2091 if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2092 return CM_ERROR_ATSYS;
2095 /* before starting the RPC, mark that we're changing the directory
2096 * data, so that someone who does a chmod on the dir will wait until
2097 * our call completes.
2099 lock_ObtainMutex(&dscp->mx);
2100 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2102 cm_StartCallbackGrantingCall(NULL, &cbReq);
2104 lock_ReleaseMutex(&dscp->mx);
2110 cm_StatusFromAttr(&inStatus, NULL, attrp);
2112 /* try the RPC now */
2114 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2118 dirAFSFid.Volume = dscp->fid.volume;
2119 dirAFSFid.Vnode = dscp->fid.vnode;
2120 dirAFSFid.Unique = dscp->fid.unique;
2122 callp = cm_GetRxConn(connp);
2123 code = RXAFS_MakeDir(connp->callp, &dirAFSFid, namep,
2124 &inStatus, &newAFSFid, &newDirStatus,
2125 &updatedDirStatus, &newDirCallback,
2127 rx_PutConnection(callp);
2129 } while (cm_Analyze(connp, userp, reqp,
2130 &dscp->fid, &volSync, NULL, &cbReq, code));
2131 code = cm_MapRPCError(code, reqp);
2133 lock_ObtainMutex(&dscp->mx);
2134 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2136 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2138 lock_ReleaseMutex(&dscp->mx);
2140 /* now try to create the new dir's entry, too, but be careful to
2141 * make sure that we don't merge in old info. Since we weren't locking
2142 * out any requests during the file's creation, we may have pretty old
2146 newFid.cell = dscp->fid.cell;
2147 newFid.volume = dscp->fid.volume;
2148 newFid.vnode = newAFSFid.Vnode;
2149 newFid.unique = newAFSFid.Unique;
2150 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2152 lock_ObtainMutex(&scp->mx);
2153 if (!cm_HaveCallback(scp)) {
2154 cm_MergeStatus(scp, &newDirStatus, &volSync,
2156 cm_EndCallbackGrantingCall(scp, &cbReq,
2157 &newDirCallback, 0);
2160 lock_ReleaseMutex(&scp->mx);
2161 cm_ReleaseSCache(scp);
2165 /* make sure we end things properly */
2167 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2169 /* and return error code */
2173 long cm_Link(cm_scache_t *dscp, char *namep, cm_scache_t *sscp, long flags,
2174 cm_user_t *userp, cm_req_t *reqp)
2179 AFSFid existingAFSFid;
2180 AFSFetchStatus updatedDirStatus;
2181 AFSFetchStatus newLinkStatus;
2183 struct rx_connection * callp;
2185 if (dscp->fid.cell != sscp->fid.cell ||
2186 dscp->fid.volume != sscp->fid.volume) {
2187 return CM_ERROR_CROSSDEVLINK;
2190 lock_ObtainMutex(&dscp->mx);
2191 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2192 lock_ReleaseMutex(&dscp->mx);
2198 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2201 dirAFSFid.Volume = dscp->fid.volume;
2202 dirAFSFid.Vnode = dscp->fid.vnode;
2203 dirAFSFid.Unique = dscp->fid.unique;
2205 existingAFSFid.Volume = sscp->fid.volume;
2206 existingAFSFid.Vnode = sscp->fid.vnode;
2207 existingAFSFid.Unique = sscp->fid.unique;
2209 callp = cm_GetRxConn(connp);
2210 code = RXAFS_Link(callp, &dirAFSFid, namep, &existingAFSFid,
2211 &newLinkStatus, &updatedDirStatus, &volSync);
2212 rx_PutConnection(callp);
2213 osi_Log1(smb_logp," RXAFS_Link returns %d", code);
2215 } while (cm_Analyze(connp, userp, reqp,
2216 &dscp->fid, &volSync, NULL, NULL, code));
2218 code = cm_MapRPCError(code, reqp);
2220 lock_ObtainMutex(&dscp->mx);
2221 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2223 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2225 lock_ReleaseMutex(&dscp->mx);
2230 long cm_SymLink(cm_scache_t *dscp, char *namep, char *contentsp, long flags,
2231 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
2239 AFSStoreStatus inStatus;
2240 AFSFetchStatus updatedDirStatus;
2241 AFSFetchStatus newLinkStatus;
2243 struct rx_connection * callp;
2245 /* before starting the RPC, mark that we're changing the directory data,
2246 * so that someone who does a chmod on the dir will wait until our
2249 lock_ObtainMutex(&dscp->mx);
2250 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2251 lock_ReleaseMutex(&dscp->mx);
2256 cm_StatusFromAttr(&inStatus, NULL, attrp);
2258 /* try the RPC now */
2260 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2264 dirAFSFid.Volume = dscp->fid.volume;
2265 dirAFSFid.Vnode = dscp->fid.vnode;
2266 dirAFSFid.Unique = dscp->fid.unique;
2268 callp = cm_GetRxConn(connp);
2269 code = RXAFS_Symlink(callp, &dirAFSFid, namep, contentsp,
2270 &inStatus, &newAFSFid, &newLinkStatus,
2271 &updatedDirStatus, &volSync);
2272 rx_PutConnection(callp);
2274 } while (cm_Analyze(connp, userp, reqp,
2275 &dscp->fid, &volSync, NULL, NULL, code));
2276 code = cm_MapRPCError(code, reqp);
2278 lock_ObtainMutex(&dscp->mx);
2279 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2281 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2283 lock_ReleaseMutex(&dscp->mx);
2285 /* now try to create the new dir's entry, too, but be careful to
2286 * make sure that we don't merge in old info. Since we weren't locking
2287 * out any requests during the file's creation, we may have pretty old
2291 newFid.cell = dscp->fid.cell;
2292 newFid.volume = dscp->fid.volume;
2293 newFid.vnode = newAFSFid.Vnode;
2294 newFid.unique = newAFSFid.Unique;
2295 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2297 lock_ObtainMutex(&scp->mx);
2298 if (!cm_HaveCallback(scp)) {
2299 cm_MergeStatus(scp, &newLinkStatus, &volSync,
2302 lock_ReleaseMutex(&scp->mx);
2303 cm_ReleaseSCache(scp);
2307 /* and return error code */
2311 long cm_RemoveDir(cm_scache_t *dscp, char *namep, cm_user_t *userp,
2318 AFSFetchStatus updatedDirStatus;
2320 struct rx_connection * callp;
2322 /* before starting the RPC, mark that we're changing the directory data,
2323 * so that someone who does a chmod on the dir will wait until our
2326 lock_ObtainMutex(&dscp->mx);
2327 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2328 lock_ReleaseMutex(&dscp->mx);
2334 /* try the RPC now */
2336 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2340 dirAFSFid.Volume = dscp->fid.volume;
2341 dirAFSFid.Vnode = dscp->fid.vnode;
2342 dirAFSFid.Unique = dscp->fid.unique;
2344 callp = cm_GetRxConn(connp);
2345 code = RXAFS_RemoveDir(callp, &dirAFSFid, namep,
2346 &updatedDirStatus, &volSync);
2347 rx_PutConnection(callp);
2349 } while (cm_Analyze(connp, userp, reqp,
2350 &dscp->fid, &volSync, NULL, NULL, code));
2351 code = cm_MapRPCErrorRmdir(code, reqp);
2353 lock_ObtainMutex(&dscp->mx);
2354 cm_dnlcRemove(dscp, namep);
2355 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2357 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2359 lock_ReleaseMutex(&dscp->mx);
2361 /* and return error code */
2365 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
2367 /* grab mutex on contents */
2368 lock_ObtainMutex(&scp->mx);
2370 /* reset the prefetch info */
2371 scp->prefetch.base.LowPart = 0; /* base */
2372 scp->prefetch.base.HighPart = 0;
2373 scp->prefetch.end.LowPart = 0; /* and end */
2374 scp->prefetch.end.HighPart = 0;
2376 /* release mutex on contents */
2377 lock_ReleaseMutex(&scp->mx);
2383 long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
2384 char *newNamep, cm_user_t *userp, cm_req_t *reqp)
2388 AFSFid oldDirAFSFid;
2389 AFSFid newDirAFSFid;
2391 AFSFetchStatus updatedOldDirStatus;
2392 AFSFetchStatus updatedNewDirStatus;
2395 struct rx_connection * callp;
2397 /* before starting the RPC, mark that we're changing the directory data,
2398 * so that someone who does a chmod on the dir will wait until our call
2399 * completes. We do this in vnode order so that we don't deadlock,
2400 * which makes the code a little verbose.
2402 if (oldDscp == newDscp) {
2403 /* check for identical names */
2404 if (strcmp(oldNamep, newNamep) == 0)
2405 return CM_ERROR_RENAME_IDENTICAL;
2408 lock_ObtainMutex(&oldDscp->mx);
2409 cm_dnlcRemove(oldDscp, oldNamep);
2410 cm_dnlcRemove(oldDscp, newNamep);
2411 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2412 CM_SCACHESYNC_STOREDATA);
2413 lock_ReleaseMutex(&oldDscp->mx);
2416 /* two distinct dir vnodes */
2418 if (oldDscp->fid.cell != newDscp->fid.cell ||
2419 oldDscp->fid.volume != newDscp->fid.volume)
2420 return CM_ERROR_CROSSDEVLINK;
2422 /* shouldn't happen that we have distinct vnodes for two
2423 * different files, but could due to deliberate attack, or
2424 * stale info. Avoid deadlocks and quit now.
2426 if (oldDscp->fid.vnode == newDscp->fid.vnode)
2427 return CM_ERROR_CROSSDEVLINK;
2429 if (oldDscp->fid.vnode < newDscp->fid.vnode) {
2430 lock_ObtainMutex(&oldDscp->mx);
2431 cm_dnlcRemove(oldDscp, oldNamep);
2432 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2433 CM_SCACHESYNC_STOREDATA);
2434 lock_ReleaseMutex(&oldDscp->mx);
2436 lock_ObtainMutex(&newDscp->mx);
2437 cm_dnlcRemove(newDscp, newNamep);
2438 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2439 CM_SCACHESYNC_STOREDATA);
2440 lock_ReleaseMutex(&newDscp->mx);
2442 /* cleanup first one */
2443 cm_SyncOpDone(oldDscp, NULL,
2444 CM_SCACHESYNC_STOREDATA);
2449 /* lock the new vnode entry first */
2450 lock_ObtainMutex(&newDscp->mx);
2451 cm_dnlcRemove(newDscp, newNamep);
2452 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2453 CM_SCACHESYNC_STOREDATA);
2454 lock_ReleaseMutex(&newDscp->mx);
2456 lock_ObtainMutex(&oldDscp->mx);
2457 cm_dnlcRemove(oldDscp, oldNamep);
2458 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2459 CM_SCACHESYNC_STOREDATA);
2460 lock_ReleaseMutex(&oldDscp->mx);
2462 /* cleanup first one */
2463 cm_SyncOpDone(newDscp, NULL,
2464 CM_SCACHESYNC_STOREDATA);
2468 } /* two distinct vnodes */
2475 /* try the RPC now */
2477 code = cm_Conn(&oldDscp->fid, userp, reqp, &connp);
2481 oldDirAFSFid.Volume = oldDscp->fid.volume;
2482 oldDirAFSFid.Vnode = oldDscp->fid.vnode;
2483 oldDirAFSFid.Unique = oldDscp->fid.unique;
2484 newDirAFSFid.Volume = newDscp->fid.volume;
2485 newDirAFSFid.Vnode = newDscp->fid.vnode;
2486 newDirAFSFid.Unique = newDscp->fid.unique;
2488 callp = cm_GetRxConn(connp);
2489 code = RXAFS_Rename(callp, &oldDirAFSFid, oldNamep,
2490 &newDirAFSFid, newNamep,
2491 &updatedOldDirStatus, &updatedNewDirStatus,
2493 rx_PutConnection(callp);
2495 } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
2496 &volSync, NULL, NULL, code));
2497 code = cm_MapRPCError(code, reqp);
2499 /* update the individual stat cache entries for the directories */
2500 lock_ObtainMutex(&oldDscp->mx);
2501 cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
2503 cm_MergeStatus(oldDscp, &updatedOldDirStatus, &volSync,
2506 lock_ReleaseMutex(&oldDscp->mx);
2508 /* and update it for the new one, too, if necessary */
2510 lock_ObtainMutex(&newDscp->mx);
2511 cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
2513 cm_MergeStatus(newDscp, &updatedNewDirStatus, &volSync,
2516 lock_ReleaseMutex(&newDscp->mx);
2519 /* and return error code */
2523 long cm_Lock(cm_scache_t *scp, unsigned char LockType,
2524 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
2525 u_long Timeout, cm_user_t *userp, cm_req_t *reqp,
2529 int Which = ((LockType & 0x1) ? LockRead : LockWrite);
2533 cm_file_lock_t *fileLock;
2536 struct rx_connection * callp;
2538 /* Look for a conflict. Also, if we are asking for a shared lock,
2539 * look for another shared lock, so we don't have to do an RPC.
2543 fileLock = (cm_file_lock_t *)
2544 ((char *) q - offsetof(cm_file_lock_t, fileq));
2545 if ((fileLock->flags &
2546 (CM_FILELOCK_FLAG_INVALID | CM_FILELOCK_FLAG_WAITING))
2548 if ((LockType & 0x1) == 0
2549 || (fileLock->LockType & 0x1) == 0)
2550 return CM_ERROR_WOULDBLOCK;
2559 tfid.Volume = scp->fid.volume;
2560 tfid.Vnode = scp->fid.vnode;
2561 tfid.Unique = scp->fid.unique;
2562 lock_ReleaseMutex(&scp->mx);
2564 code = cm_Conn(&scp->fid, userp, reqp, &connp);
2568 callp = cm_GetRxConn(connp);
2569 code = RXAFS_SetLock(callp, &tfid, Which,
2571 rx_PutConnection(callp);
2573 } while (cm_Analyze(connp, userp, reqp, &scp->fid, &volSync,
2575 lock_ObtainMutex(&scp->mx);
2576 code = cm_MapRPCError(code, reqp);
2579 if (code == 0 || Timeout != 0) {
2580 fileLock = malloc(sizeof(cm_file_lock_t));
2581 fileLock->LockType = LockType;
2583 fileLock->userp = userp;
2584 fileLock->fid = scp->fid;
2585 fileLock->LOffset = LOffset;
2586 fileLock->LLength = LLength;
2587 fileLock->flags = (code == 0 ? 0 : CM_FILELOCK_FLAG_WAITING);
2588 osi_QAdd(&scp->fileLocks, &fileLock->fileq);
2589 lock_ObtainWrite(&cm_scacheLock);
2590 osi_QAdd(&cm_allFileLocks, &fileLock->q);
2591 lock_ReleaseWrite(&cm_scacheLock);
2598 long cm_Unlock(cm_scache_t *scp, unsigned char LockType,
2599 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
2600 cm_user_t *userp, cm_req_t *reqp)
2603 int Which = ((LockType & 0x1) ? LockRead : LockWrite);
2607 cm_file_lock_t *fileLock, *ourLock;
2608 osi_queue_t *q, *qq;
2609 int anotherReader = 0;
2612 struct rx_connection * callp;
2614 if (LargeIntegerLessThan(LLength, scp->length))
2617 /* Look for our own lock on the list, so as to remove it.
2618 * Also, determine if we're the last reader; if not, avoid an RPC.
2622 fileLock = (cm_file_lock_t *)
2623 ((char *) q - offsetof(cm_file_lock_t, fileq));
2625 && fileLock->userp == userp
2626 && LargeIntegerEqualTo(fileLock->LOffset, LOffset)
2627 && LargeIntegerEqualTo(fileLock->LLength, LLength)) {
2632 else if (fileLock->LockType & 0x1)
2637 /* ignore byte ranges */
2638 if (smallLock && !found)
2641 /* don't try to unlock other people's locks */
2643 return CM_ERROR_WOULDBLOCK;
2645 /* discard lock record */
2646 osi_QRemove(&scp->fileLocks, qq);
2648 * Don't delete it here; let the daemon delete it, to simplify
2649 * the daemon's traversal of the list.
2651 lock_ObtainWrite(&cm_scacheLock);
2652 ourLock->flags |= CM_FILELOCK_FLAG_INVALID;
2653 cm_ReleaseUser(ourLock->userp);
2654 lock_ReleaseWrite(&cm_scacheLock);
2656 if (!anotherReader) {
2657 tfid.Volume = scp->fid.volume;
2658 tfid.Vnode = scp->fid.vnode;
2659 tfid.Unique = scp->fid.unique;
2660 lock_ReleaseMutex(&scp->mx);
2662 code = cm_Conn(&scp->fid, userp, reqp, &connp);
2666 callp = cm_GetRxConn(connp);
2667 code = RXAFS_ReleaseLock(callp, &tfid, &volSync);
2668 rx_PutConnection(callp);
2670 } while (cm_Analyze(connp, userp, reqp, &scp->fid, &volSync,
2672 code = cm_MapRPCError(code, reqp);
2673 lock_ObtainMutex(&scp->mx);
2679 void cm_CheckLocks()
2681 osi_queue_t *q, *nq;
2682 cm_file_lock_t *fileLock;
2688 struct rx_connection * callp;
2692 lock_ObtainWrite(&cm_scacheLock);
2693 q = cm_allFileLocks;
2695 fileLock = (cm_file_lock_t *) q;
2697 if (fileLock->flags & CM_FILELOCK_FLAG_INVALID) {
2698 osi_QRemove(&cm_allFileLocks, q);
2701 else if (!(fileLock->flags & CM_FILELOCK_FLAG_WAITING)) {
2702 tfid.Volume = fileLock->fid.volume;
2703 tfid.Vnode = fileLock->fid.vnode;
2704 tfid.Unique = fileLock->fid.unique;
2705 lock_ReleaseWrite(&cm_scacheLock);
2707 code = cm_Conn(&fileLock->fid, fileLock->userp,
2712 callp = cm_GetRxConn(connp);
2713 code = RXAFS_ExtendLock(callp, &tfid,
2715 rx_PutConnection(callp);
2717 } while (cm_Analyze(connp, fileLock->userp, &req,
2718 &fileLock->fid, &volSync, NULL, NULL,
2720 code = cm_MapRPCError(code, &req);
2721 lock_ObtainWrite(&cm_scacheLock);
2725 lock_ReleaseWrite(&cm_scacheLock);
2728 long cm_RetryLock(cm_file_lock_t *oldFileLock, int vcp_is_dead)
2731 int Which = ((oldFileLock->LockType & 0x1) ? LockRead : LockWrite);
2736 cm_file_lock_t *fileLock;
2740 struct rx_connection * callp;
2743 code = CM_ERROR_TIMEDOUT;
2749 /* Look for a conflict. Also, if we are asking for a shared lock,
2750 * look for another shared lock, so we don't have to do an RPC.
2752 code = cm_GetSCache(&oldFileLock->fid, &scp, oldFileLock->userp, &req);
2758 fileLock = (cm_file_lock_t *)
2759 ((char *) q - offsetof(cm_file_lock_t, fileq));
2760 if ((fileLock->flags &
2761 (CM_FILELOCK_FLAG_INVALID | CM_FILELOCK_FLAG_WAITING))
2763 if ((oldFileLock->LockType & 0x1) == 0
2764 || (fileLock->LockType & 0x1) == 0) {
2765 cm_ReleaseSCache(scp);
2766 return CM_ERROR_WOULDBLOCK;
2776 tfid.Volume = oldFileLock->fid.volume;
2777 tfid.Vnode = oldFileLock->fid.vnode;
2778 tfid.Unique = oldFileLock->fid.unique;
2780 code = cm_Conn(&oldFileLock->fid, oldFileLock->userp,
2785 callp = cm_GetRxConn(connp);
2786 code = RXAFS_SetLock(callp, &tfid, Which,
2788 rx_PutConnection(callp);
2790 } while (cm_Analyze(connp, oldFileLock->userp, &req,
2791 &oldFileLock->fid, &volSync,
2793 code = cm_MapRPCError(code, &req);
2797 if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
2798 lock_ObtainMutex(&scp->mx);
2799 osi_QRemove(&scp->fileLocks, &oldFileLock->fileq);
2800 lock_ReleaseMutex(&scp->mx);
2802 lock_ObtainWrite(&cm_scacheLock);
2804 oldFileLock->flags = 0;
2805 else if (code != CM_ERROR_WOULDBLOCK) {
2806 oldFileLock->flags |= CM_FILELOCK_FLAG_INVALID;
2807 cm_ReleaseUser(oldFileLock->userp);
2808 oldFileLock->userp = NULL;
2810 lock_ReleaseWrite(&cm_scacheLock);