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) return CM_ERROR_TOOBIG;
792 code = CM_ERROR_INVAL;
796 /* someone else did the work while we were out */
797 if (scp->mountPointStringp) {
802 /* otherwise, copy out the link */
803 scp->mountPointStringp = malloc(tlen);
804 memcpy(scp->mountPointStringp, bufp->datap, tlen);
806 /* now make it null-terminated. Note that the original contents of a
807 * link that is a mount point is "#volname." where "." is there just to
808 * be turned into a null. That is, we can trash the last char of the
809 * link without damaging the vol name. This is a stupid convention,
810 * but that's the protocol.
812 scp->mountPointStringp[tlen-1] = 0;
821 /* called with a locked scp and chases the mount point, yielding outScpp.
822 * scp remains locked, just for simplicity of describing the interface.
824 long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
825 cm_req_t *reqp, cm_scache_t **outScpp)
840 if (scp->mountRootFidp && scp->mountRootGen >= cm_mountRootGen) {
841 tfid = *scp->mountRootFidp;
842 lock_ReleaseMutex(&scp->mx);
843 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
844 lock_ObtainMutex(&scp->mx);
848 /* parse the volume name */
849 mpNamep = scp->mountPointStringp;
851 tlen = strlen(scp->mountPointStringp);
852 mtType = *scp->mountPointStringp;
853 cellNamep = malloc(tlen);
854 volNamep = malloc(tlen);
856 cp = strrchr(mpNamep, ':');
858 /* cellular mount point */
859 memset(cellNamep, 0, tlen);
860 strncpy(cellNamep, mpNamep+1, cp - mpNamep - 1);
861 strcpy(volNamep, cp+1);
862 /* now look up the cell */
863 cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE);
867 strcpy(volNamep, mpNamep+1);
869 cellp = cm_FindCellByID(scp->fid.cell);
873 code = CM_ERROR_NOSUCHCELL;
877 vnLength = strlen(volNamep);
878 if (vnLength >= 8 && strcmp(volNamep + vnLength - 7, ".backup") == 0)
880 else if (vnLength >= 10
881 && strcmp(volNamep + vnLength - 9, ".readonly") == 0)
886 /* check for backups within backups */
888 && (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
889 == CM_SCACHEFLAG_RO) {
890 code = CM_ERROR_NOSUCHVOLUME;
894 /* now we need to get the volume */
895 lock_ReleaseMutex(&scp->mx);
896 code = cm_GetVolumeByName(cellp, volNamep, userp, reqp, 0, &volp);
897 lock_ObtainMutex(&scp->mx);
900 /* save the parent of the volume root for this is the
901 * place where the volume is mounted and we must remember
902 * this in the volume structure rather than just in the
903 * scache entry lest the scache entry gets recycled
906 lock_ObtainMutex(&volp->mx);
907 if(volp->dotdotFidp == (cm_fid_t *) NULL)
908 volp->dotdotFidp = (cm_fid_t *) malloc(sizeof(cm_fid_t));
909 *(volp->dotdotFidp) = dscp->fid;
910 lock_ReleaseMutex(&volp->mx);
912 if (scp->mountRootFidp == 0) {
913 scp->mountRootFidp = malloc(sizeof(cm_fid_t));
915 scp->mountRootFidp->cell = cellp->cellID;
916 /* if the mt pt is in a read-only volume (not just a
917 * backup), and if there is a read-only volume for the
918 * target, and if this is a type '#' mount point, use
919 * the read-only, otherwise use the one specified.
921 if (mtType == '#' && (scp->flags & CM_SCACHEFLAG_PURERO)
922 && volp->roID != 0 && type == RWVOL)
925 scp->mountRootFidp->volume = volp->roID;
926 else if (type == BACKVOL)
927 scp->mountRootFidp->volume = volp->bkID;
929 scp->mountRootFidp->volume = volp->rwID;
931 /* the rest of the fid is a magic number */
932 scp->mountRootFidp->vnode = 1;
933 scp->mountRootFidp->unique = 1;
934 scp->mountRootGen = cm_mountRootGen;
936 tfid = *scp->mountRootFidp;
937 lock_ReleaseMutex(&scp->mx);
938 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
939 lock_ObtainMutex(&scp->mx);
948 long cm_LookupInternal(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
949 cm_req_t *reqp, cm_scache_t **outpScpp)
952 int dnlcHit = 1; /* did we hit in the dnlc? yes, we did */
953 cm_scache_t *tscp = NULL;
954 cm_scache_t *mountedScp;
955 cm_lookupSearch_t rock;
958 if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
959 && strcmp(namep, "..") == 0) {
960 if (dscp->dotdotFidp == (cm_fid_t *)NULL
961 || dscp->dotdotFidp->volume == 0)
962 return CM_ERROR_NOSUCHVOLUME;
963 rock.fid = *dscp->dotdotFidp;
967 memset(&rock, 0, sizeof(rock));
968 rock.fid.cell = dscp->fid.cell;
969 rock.fid.volume = dscp->fid.volume;
970 rock.searchNamep = namep;
971 rock.caseFold = (flags & CM_FLAG_CASEFOLD);
972 rock.hasTilde = ((strchr(namep, '~') != NULL) ? 1 : 0);
974 /* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
975 code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
976 (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
978 /* code == 0 means we fell off the end of the dir, while stopnow means
979 * that we stopped early, probably because we found the entry we're
980 * looking for. Any other non-zero code is an error.
982 if (code && code != CM_ERROR_STOPNOW)
985 getroot = (dscp==cm_rootSCachep) ;
987 if (!cm_freelanceEnabled || !getroot) {
988 if (flags & CM_FLAG_CHECKPATH)
989 return CM_ERROR_NOSUCHPATH;
991 return CM_ERROR_NOSUCHFILE;
993 else { /* nonexistent dir on freelance root, so add it */
994 osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %s",
995 osi_LogSaveString(afsd_logp,namep));
996 code = cm_FreelanceAddMount(namep, namep, "root.cell.", namep[0] == '.', &rock.fid);
997 if (code < 0) { /* add mount point failed, so give up */
998 if (flags & CM_FLAG_CHECKPATH)
999 return CM_ERROR_NOSUCHPATH;
1001 return CM_ERROR_NOSUCHFILE;
1003 tscp = NULL; /* to force call of cm_GetSCache */
1008 if ( !tscp ) /* we did not find it in the dnlc */
1011 code = cm_GetSCache(&rock.fid, &tscp, userp, reqp);
1015 /* tscp is now held */
1017 lock_ObtainMutex(&tscp->mx);
1018 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1019 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1021 lock_ReleaseMutex(&tscp->mx);
1022 cm_ReleaseSCache(tscp);
1025 /* tscp is now locked */
1027 if (!(flags & CM_FLAG_NOMOUNTCHASE)
1028 && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
1029 /* mount points are funny: they have a volume name to mount
1032 code = cm_ReadMountPoint(tscp, userp, reqp);
1034 code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
1036 lock_ReleaseMutex(&tscp->mx);
1037 cm_ReleaseSCache(tscp);
1044 lock_ReleaseMutex(&tscp->mx);
1047 /* copy back pointer */
1050 /* insert scache in dnlc */
1051 if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
1052 /* lock the directory entry to prevent racing callback revokes */
1053 lock_ObtainMutex(&dscp->mx);
1054 if ( dscp->cbServerp && dscp->cbExpires )
1055 cm_dnlcEnter(dscp, namep, tscp);
1056 lock_ReleaseMutex(&dscp->mx);
1063 int cm_ExpandSysName(char *inp, char *outp, long outSize, unsigned int index)
1068 tp = strrchr(inp, '@');
1070 return 0; /* no @sys */
1072 if (strcmp(tp, "@sys") != 0)
1073 return 0; /* no @sys */
1075 /* caller just wants to know if this is a valid @sys type of name */
1079 if (index >= MAXNUMSYSNAMES)
1082 /* otherwise generate the properly expanded @sys name */
1083 prefixCount = tp - inp;
1085 strncpy(outp, inp, prefixCount); /* copy out "a." from "a.@sys" */
1086 outp[prefixCount] = 0; /* null terminate the "a." */
1087 strcat(outp, cm_sysNameList[index]);/* append i386_nt40 */
1091 long cm_Lookup(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
1092 cm_req_t *reqp, cm_scache_t **outpScpp)
1096 int sysNameIndex = 0;
1097 cm_scache_t *scp = 0;
1099 for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
1100 code = cm_ExpandSysName(namep, tname, sizeof(tname), sysNameIndex);
1102 code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
1108 cm_ReleaseSCache(scp);
1112 return cm_LookupInternal(dscp, namep, flags, userp, reqp, outpScpp);
1116 /* None of the possible sysName expansions could be found */
1117 if (flags & CM_FLAG_CHECKPATH)
1118 return CM_ERROR_NOSUCHPATH;
1120 return CM_ERROR_NOSUCHFILE;
1123 long cm_Unlink(cm_scache_t *dscp, char *namep, cm_user_t *userp, cm_req_t *reqp)
1129 AFSFetchStatus newDirStatus;
1132 #ifdef AFS_FREELANCE_CLIENT
1133 if (cm_freelanceEnabled && dscp == cm_rootSCachep) {
1134 /* deleting a mount point from the root dir. */
1135 code = cm_FreelanceRemoveMount(namep);
1140 /* make sure we don't screw up the dir status during the merge */
1141 lock_ObtainMutex(&dscp->mx);
1142 sflags = CM_SCACHESYNC_STOREDATA;
1143 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1144 lock_ReleaseMutex(&dscp->mx);
1149 afsFid.Volume = dscp->fid.volume;
1150 afsFid.Vnode = dscp->fid.vnode;
1151 afsFid.Unique = dscp->fid.unique;
1153 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1157 lock_ObtainMutex(&connp->mx);
1158 code = RXAFS_RemoveFile(connp->callp, &afsFid, namep,
1159 &newDirStatus, &volSync);
1160 lock_ReleaseMutex(&connp->mx);
1161 } while (cm_Analyze(connp, userp, reqp, &dscp->fid, &volSync, NULL, NULL, code));
1162 code = cm_MapRPCError(code, reqp);
1164 lock_ObtainMutex(&dscp->mx);
1165 cm_dnlcRemove(dscp, namep);
1166 cm_SyncOpDone(dscp, NULL, sflags);
1168 cm_MergeStatus(dscp, &newDirStatus, &volSync, userp, 0);
1169 lock_ReleaseMutex(&dscp->mx);
1174 /* called with a locked vnode, and fills in the link info.
1175 * returns this the vnode still locked.
1177 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1184 lock_AssertMutex(&linkScp->mx);
1185 if (!linkScp->mountPointStringp) {
1186 /* read the link data */
1187 lock_ReleaseMutex(&linkScp->mx);
1188 thyper.LowPart = thyper.HighPart = 0;
1189 code = buf_Get(linkScp, &thyper, &bufp);
1190 lock_ObtainMutex(&linkScp->mx);
1194 code = cm_SyncOp(linkScp, bufp, userp, reqp, 0,
1195 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1200 if (cm_HaveBuffer(linkScp, bufp, 0))
1203 code = cm_GetBuffer(linkScp, bufp, NULL, userp, reqp);
1208 } /* while loop to get the data */
1210 /* now if we still have no link read in,
1211 * copy the data from the buffer */
1212 if ((temp = linkScp->length.LowPart) >= 1024) {
1214 return CM_ERROR_TOOBIG;
1217 /* otherwise, it fits; make sure it is still null (could have
1218 * lost race with someone else referencing this link above),
1219 * and if so, copy in the data.
1221 if (linkScp->mountPointStringp == NULL) {
1222 linkScp->mountPointStringp = malloc(temp+1);
1223 strncpy(linkScp->mountPointStringp, bufp->datap, temp);
1224 linkScp->mountPointStringp[temp] = 0; /* null terminate */
1227 } /* don't have sym link contents cached */
1232 /* called with a held vnode and a path suffix, with the held vnode being a
1233 * symbolic link. Our goal is to generate a new path to interpret, and return
1234 * this new path in newSpaceBufferp. If the new vnode is relative to a dir
1235 * other than the directory containing the symbolic link, then the new root is
1236 * returned in *newRootScpp, otherwise a null is returned there.
1238 long cm_AssembleLink(cm_scache_t *linkScp, char *pathSuffixp,
1239 cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1240 cm_user_t *userp, cm_req_t *reqp)
1246 lock_ObtainMutex(&linkScp->mx);
1247 code = cm_HandleLink(linkScp, userp, reqp);
1251 /* if we may overflow the buffer, bail out; buffer is signficantly
1252 * bigger than max path length, so we don't really have to worry about
1253 * being a little conservative here.
1255 if (strlen(linkScp->mountPointStringp) + strlen(pathSuffixp) + 2
1256 >= CM_UTILS_SPACESIZE)
1257 return CM_ERROR_TOOBIG;
1259 tsp = cm_GetSpace();
1260 linkp = linkScp->mountPointStringp;
1261 if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1262 if (strlen(linkp) > cm_mountRootLen)
1263 strcpy(tsp->data, linkp+cm_mountRootLen+1);
1266 *newRootScpp = cm_rootSCachep;
1267 cm_HoldSCache(cm_rootSCachep);
1268 } else if (*linkp == '\\' || *linkp == '/') {
1269 /* formerly, this was considered to be from the AFS root,
1270 * but this seems to create problems. instead, we will just
1271 * reject the link */
1273 strcpy(tsp->data, linkp+1);
1274 *newRootScpp = cm_rootSCachep;
1275 cm_HoldSCache(cm_rootSCachep);
1277 code = CM_ERROR_NOSUCHPATH;
1282 /* a relative link */
1283 strcpy(tsp->data, linkp);
1284 *newRootScpp = NULL;
1286 if (pathSuffixp[0] != 0) { /* if suffix string is non-null */
1287 strcat(tsp->data, "\\");
1288 strcat(tsp->data, pathSuffixp);
1290 *newSpaceBufferp = tsp;
1294 lock_ReleaseMutex(&linkScp->mx);
1298 long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
1299 cm_user_t *userp, char *tidPathp, cm_req_t *reqp, cm_scache_t **outScpp)
1302 char *tp; /* ptr moving through input buffer */
1303 char tc; /* temp char */
1304 int haveComponent; /* has new component started? */
1305 char component[256]; /* this is the new component */
1306 char *cp; /* component name being assembled */
1307 cm_scache_t *tscp; /* current location in the hierarchy */
1308 cm_scache_t *nscp; /* next dude down */
1309 cm_scache_t *dirScp; /* last dir we searched */
1310 cm_scache_t *linkScp; /* new root for the symlink we just
1312 cm_space_t *psp; /* space for current path, if we've hit
1314 cm_space_t *tempsp; /* temp vbl */
1315 char *restp; /* rest of the pathname to interpret */
1316 int symlinkCount; /* count of # of symlinks traversed */
1317 int extraFlag; /* avoid chasing mt pts for dir cmd */
1318 int phase = 1; /* 1 = tidPathp, 2 = pathp */
1331 cm_HoldSCache(tscp);
1336 /* map Unix slashes into DOS ones so we can interpret Unix
1342 if (!haveComponent) {
1361 /* we have a component here */
1362 if (tc == 0 || tc == '\\') {
1363 /* end of the component; we're at the last
1364 * component if tc == 0. However, if the last
1365 * is a symlink, we have more to do.
1367 *cp++ = 0; /* add null termination */
1369 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
1370 extraFlag = CM_FLAG_NOMOUNTCHASE;
1371 code = cm_Lookup(tscp, component,
1373 userp, reqp, &nscp);
1376 cm_ReleaseSCache(tscp);
1381 haveComponent = 0; /* component done */
1382 dirScp = tscp; /* for some symlinks */
1383 tscp = nscp; /* already held */
1384 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
1386 cm_ReleaseSCache(dirScp);
1390 /* now, if tscp is a symlink, we should follow
1391 * it and assemble the path again.
1393 lock_ObtainMutex(&tscp->mx);
1394 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1395 CM_SCACHESYNC_GETSTATUS
1396 | CM_SCACHESYNC_NEEDCALLBACK);
1398 lock_ReleaseMutex(&tscp->mx);
1399 cm_ReleaseSCache(tscp);
1400 cm_ReleaseSCache(dirScp);
1403 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
1404 /* this is a symlink; assemble a new buffer */
1405 lock_ReleaseMutex(&tscp->mx);
1406 if (symlinkCount++ >= 16) {
1407 cm_ReleaseSCache(tscp);
1408 cm_ReleaseSCache(dirScp);
1411 return CM_ERROR_TOOBIG;
1417 code = cm_AssembleLink(tscp, restp, &linkScp, &tempsp, userp, reqp);
1419 /* something went wrong */
1420 cm_ReleaseSCache(tscp);
1421 cm_ReleaseSCache(dirScp);
1425 /* otherwise, tempsp has the new path,
1426 * and linkScp is the new root from
1427 * which to interpret that path.
1428 * Continue with the namei processing,
1429 * also doing the bookkeeping for the
1430 * space allocation and tracking the
1431 * vnode reference counts.
1437 cm_ReleaseSCache(tscp);
1441 * now, if linkScp is null, that's
1442 * AssembleLink's way of telling us that
1443 * the sym link is relative to the dir
1444 * containing the link. We have a ref
1445 * to it in dirScp, and we hold it now
1446 * and reuse it as the new spot in the
1450 cm_HoldSCache(dirScp);
1453 } /* if we have a sym link */
1455 /* not a symlink, we may be done */
1456 lock_ReleaseMutex(&tscp->mx);
1463 cm_ReleaseSCache(dirScp);
1468 cm_ReleaseSCache(dirScp);
1469 } /* end of a component */
1471 } /* we have a component */
1472 } /* big while loop over all components */
1482 /* called with a dir, and a vnode within the dir that happens to be a symlink.
1483 * We chase the link, and return a held pointer to the target, if it exists,
1484 * in *outScpp. If we succeed, we return 0, otherwise we return an error code
1485 * and do not hold or return a target vnode.
1487 * This is very similar to calling cm_NameI with the last component of a name,
1488 * which happens to be a symlink, except that we've already passed by the name.
1490 * This function is typically called by the directory listing functions, which
1491 * encounter symlinks but need to return the proper file length so programs
1492 * like "more" work properly when they make use of the attributes retrieved from
1495 * The input vnode should not be locked when this function is called.
1497 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
1498 cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
1502 cm_scache_t *newRootScp;
1504 osi_Log1(afsd_logp, "Evaluating symlink vp %x", linkScp);
1506 code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
1510 /* now, if newRootScp is NULL, we're really being told that the symlink
1511 * is relative to the current directory (dscp).
1513 if (newRootScp == NULL) {
1515 cm_HoldSCache(dscp);
1518 code = cm_NameI(newRootScp, spacep->data,
1519 CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
1520 userp, NULL, reqp, outScpp);
1522 /* this stuff is allocated no matter what happened on the namei call,
1524 cm_FreeSpace(spacep);
1525 cm_ReleaseSCache(newRootScp);
1530 /* make this big enough so that one buffer of dir pages won't overflow. We'll
1531 * check anyway, but we want to minimize the chance that we have to leave stuff
1534 #define CM_BULKMAX 128
1536 /* rock for bulk stat calls */
1537 typedef struct cm_bulkStat {
1538 osi_hyper_t bufOffset; /* only do it for things in this buffer page */
1540 /* info for the actual call */
1541 int counter; /* next free slot */
1542 AFSFid fids[CM_BULKMAX];
1543 AFSFetchStatus stats[CM_BULKMAX];
1544 AFSCallBack callbacks[CM_BULKMAX];
1547 /* for a given entry, make sure that it isn't in the stat cache, and then
1548 * add it to the list of file IDs to be obtained.
1550 * Don't bother adding it if we already have a vnode. Note that the dir
1551 * is locked, so we have to be careful checking the vnode we're thinking of
1552 * processing, to avoid deadlocks.
1554 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
1565 /* Don't overflow bsp. */
1566 if (bsp->counter >= CM_BULKMAX)
1567 return CM_ERROR_STOPNOW;
1569 thyper.LowPart = buf_bufferSize;
1570 thyper.HighPart = 0;
1571 thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
1573 /* thyper is now the first byte past the end of the record we're
1574 * interested in, and bsp->bufOffset is the first byte of the record
1575 * we're interested in.
1576 * Skip data in the others.
1579 if (LargeIntegerLessThan(*offp, bsp->bufOffset))
1581 if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
1582 return CM_ERROR_STOPNOW;
1583 if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
1586 tfid.cell = scp->fid.cell;
1587 tfid.volume = scp->fid.volume;
1588 tfid.vnode = ntohl(dep->fid.vnode);
1589 tfid.unique = ntohl(dep->fid.unique);
1590 tscp = cm_FindSCache(&tfid);
1592 if (lock_TryMutex(&tscp->mx)) {
1593 /* we have an entry that we can look at */
1594 if (cm_HaveCallback(tscp)) {
1595 /* we have a callback on it. Don't bother
1596 * fetching this stat entry, since we're happy
1597 * with the info we have.
1599 lock_ReleaseMutex(&tscp->mx);
1600 cm_ReleaseSCache(tscp);
1603 lock_ReleaseMutex(&tscp->mx);
1605 cm_ReleaseSCache(tscp);
1608 #ifdef AFS_FREELANCE_CLIENT
1609 // yj: if this is a mountpoint under root.afs then we don't want it
1610 // to be bulkstat-ed, instead, we call getSCache directly and under
1611 // getSCache, it is handled specially.
1612 if ( cm_freelanceEnabled &&
1613 tfid.cell==AFS_FAKE_ROOT_CELL_ID &&
1614 tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
1615 !(tfid.vnode==0x1 && tfid.unique==0x1) )
1617 osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
1618 return cm_GetSCache(&tfid, &tscp, NULL, NULL);
1620 #endif /* AFS_FREELANCE_CLIENT */
1623 bsp->fids[i].Volume = scp->fid.volume;
1624 bsp->fids[i].Vnode = tfid.vnode;
1625 bsp->fids[i].Unique = tfid.unique;
1629 /* called with a locked scp and a pointer to a buffer. Make bulk stat
1630 * calls on all undeleted files in the page of the directory specified.
1632 void cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
1636 cm_bulkStat_t bb; /* this is *BIG*, probably 12K or so;
1637 * watch for stack problems */
1638 AFSCBFids fidStruct;
1639 AFSBulkStats statStruct;
1641 AFSCBs callbackStruct;
1644 cm_callbackRequest_t cbReq;
1651 osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%x", (long) dscp);
1653 /* should be on a buffer boundary */
1654 osi_assert((offsetp->LowPart & (buf_bufferSize - 1)) == 0);
1657 bb.bufOffset = *offsetp;
1659 /* first, assemble the file IDs we need to stat */
1660 code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) &bb, offsetp, userp,
1663 /* if we failed, bail out early */
1664 if (code && code != CM_ERROR_STOPNOW) return;
1666 /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
1667 * make the calls to create the entries. Handle AFSCBMAX files at a
1671 while(filex < bb.counter) {
1672 filesThisCall = bb.counter - filex;
1673 if (filesThisCall > AFSCBMAX) filesThisCall = AFSCBMAX;
1675 fidStruct.AFSCBFids_len = filesThisCall;
1676 fidStruct.AFSCBFids_val = &bb.fids[filex];
1677 statStruct.AFSBulkStats_len = filesThisCall;
1678 statStruct.AFSBulkStats_val = &bb.stats[filex];
1679 callbackStruct.AFSCBs_len = filesThisCall;
1680 callbackStruct.AFSCBs_val = &bb.callbacks[filex];
1681 cm_StartCallbackGrantingCall(NULL, &cbReq);
1682 osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
1684 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1688 lock_ObtainMutex(&connp->mx);
1689 code = RXAFS_BulkStatus(connp->callp, &fidStruct,
1690 &statStruct, &callbackStruct, &volSync);
1691 lock_ReleaseMutex(&connp->mx);
1692 } while (cm_Analyze(connp, userp, reqp, &dscp->fid,
1693 &volSync, NULL, &cbReq, code));
1694 code = cm_MapRPCError(code, reqp);
1696 osi_Log0(afsd_logp, "CALL BulkStatus DONE");
1698 /* may as well quit on an error, since we're not going to do
1699 * much better on the next immediate call, either.
1704 /* otherwise, we should do the merges */
1705 for(i = 0; i<filesThisCall; i++) {
1707 tfid.cell = dscp->fid.cell;
1708 tfid.volume = bb.fids[j].Volume;
1709 tfid.vnode = bb.fids[j].Vnode;
1710 tfid.unique = bb.fids[j].Unique;
1711 code = cm_GetSCache(&tfid, &scp, userp, reqp);
1715 /* otherwise, if this entry has no callback info,
1718 lock_ObtainMutex(&scp->mx);
1719 /* now, we have to be extra paranoid on merging in this
1720 * information, since we didn't use cm_SyncOp before
1721 * starting the fetch to make sure that no bad races
1722 * were occurring. Specifically, we need to make sure
1723 * we don't obliterate any newer information in the
1724 * vnode than have here.
1726 * Right now, be pretty conservative: if there's a
1727 * callback or a pending call, skip it.
1729 if (scp->cbServerp == NULL
1731 (CM_SCACHEFLAG_FETCHING
1732 | CM_SCACHEFLAG_STORING
1733 | CM_SCACHEFLAG_SIZESTORING))) {
1734 cm_EndCallbackGrantingCall(scp, &cbReq,
1736 CM_CALLBACK_MAINTAINCOUNT);
1737 cm_MergeStatus(scp, &bb.stats[j], &volSync,
1740 lock_ReleaseMutex(&scp->mx);
1741 cm_ReleaseSCache(scp);
1742 } /* all files in the response */
1743 /* now tell it to drop the count,
1744 * after doing the vnode processing above */
1745 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
1747 filex += filesThisCall;
1748 } /* while there are still more files to process */
1749 osi_Log0(afsd_logp, "END cm_TryBulkStat");
1752 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
1756 /* initialize store back mask as inexpensive local variable */
1758 memset(statusp, 0, sizeof(AFSStoreStatus));
1760 /* copy out queued info from scache first, if scp passed in */
1762 if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
1763 statusp->ClientModTime = scp->clientModTime;
1764 mask |= AFS_SETMODTIME;
1765 scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
1770 /* now add in our locally generated request */
1771 if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
1772 statusp->ClientModTime = attrp->clientModTime;
1773 mask |= AFS_SETMODTIME;
1775 if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
1776 statusp->UnixModeBits = attrp->unixModeBits;
1777 mask |= AFS_SETMODE;
1779 if (attrp->mask & CM_ATTRMASK_OWNER) {
1780 statusp->Owner = attrp->owner;
1781 mask |= AFS_SETOWNER;
1783 if (attrp->mask & CM_ATTRMASK_GROUP) {
1784 statusp->Group = attrp->group;
1785 mask |= AFS_SETGROUP;
1788 statusp->Mask = mask;
1791 /* set the file size, and make sure that all relevant buffers have been
1792 * truncated. Ensure that any partially truncated buffers have been zeroed
1793 * to the end of the buffer.
1795 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
1801 /* start by locking out buffer creation */
1802 lock_ObtainWrite(&scp->bufCreateLock);
1804 /* verify that this is a file, not a dir or a symlink */
1805 lock_ObtainMutex(&scp->mx);
1806 code = cm_SyncOp(scp, NULL, userp, reqp, 0,
1807 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1811 if (scp->fileType != CM_SCACHETYPE_FILE) {
1812 code = CM_ERROR_ISDIR;
1817 if (LargeIntegerLessThan(*sizep, scp->length))
1822 lock_ReleaseMutex(&scp->mx);
1824 /* can't hold scp->mx lock here, since we may wait for a storeback to
1825 * finish if the buffer package is cleaning a buffer by storing it to
1829 buf_Truncate(scp, userp, reqp, sizep);
1831 /* now ensure that file length is short enough, and update truncPos */
1832 lock_ObtainMutex(&scp->mx);
1834 /* make sure we have a callback (so we have the right value for the
1835 * length), and wait for it to be safe to do a truncate.
1837 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
1838 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
1839 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
1843 if (LargeIntegerLessThan(*sizep, scp->length)) {
1844 /* a real truncation. If truncPos is not set yet, or is bigger
1845 * than where we're truncating the file, set truncPos to this
1850 if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
1851 || LargeIntegerLessThan(*sizep, scp->length)) {
1853 scp->truncPos = *sizep;
1854 scp->mask |= CM_SCACHEMASK_TRUNCPOS;
1856 /* in either case, the new file size has been changed */
1857 scp->length = *sizep;
1858 scp->mask |= CM_SCACHEMASK_LENGTH;
1860 else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
1861 /* really extending the file */
1862 scp->length = *sizep;
1863 scp->mask |= CM_SCACHEMASK_LENGTH;
1866 /* done successfully */
1870 lock_ReleaseMutex(&scp->mx);
1871 lock_ReleaseWrite(&scp->bufCreateLock);
1876 /* set the file size or other attributes (but not both at once) */
1877 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
1882 AFSFetchStatus afsOutStatus;
1886 AFSStoreStatus afsInStatus;
1888 /* handle file length setting */
1889 if (attrp->mask & CM_ATTRMASK_LENGTH)
1890 return cm_SetLength(scp, &attrp->length, userp, reqp);
1892 flags = CM_SCACHESYNC_STORESTATUS;
1894 lock_ObtainMutex(&scp->mx);
1895 /* otherwise, we have to make an RPC to get the status */
1896 code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
1898 /* make the attr structure */
1899 cm_StatusFromAttr(&afsInStatus, scp, attrp);
1901 lock_ReleaseMutex(&scp->mx);
1905 /* now make the RPC */
1906 osi_Log1(afsd_logp, "CALL StoreStatus vp %x", (long) scp);
1907 tfid.Volume = scp->fid.volume;
1908 tfid.Vnode = scp->fid.vnode;
1909 tfid.Unique = scp->fid.unique;
1911 code = cm_Conn(&scp->fid, userp, reqp, &connp);
1915 lock_ObtainMutex(&connp->mx);
1916 code = RXAFS_StoreStatus(connp->callp, &tfid,
1917 &afsInStatus, &afsOutStatus, &volSync);
1918 lock_ReleaseMutex(&connp->mx);
1919 } while (cm_Analyze(connp, userp, reqp,
1920 &scp->fid, &volSync, NULL, NULL, code));
1921 code = cm_MapRPCError(code, reqp);
1923 osi_Log1(afsd_logp, "CALL StoreStatus DONE, code %d", code);
1925 lock_ObtainMutex(&scp->mx);
1926 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
1928 cm_MergeStatus(scp, &afsOutStatus, &volSync, userp,
1929 CM_MERGEFLAG_FORCE);
1931 /* if we're changing the mode bits, discard the ACL cache,
1932 * since we changed the mode bits.
1934 if (afsInStatus.Mask & AFS_SETMODE) cm_FreeAllACLEnts(scp);
1935 lock_ReleaseMutex(&scp->mx);
1939 long cm_Create(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
1940 cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
1945 cm_callbackRequest_t cbReq;
1950 AFSStoreStatus inStatus;
1951 AFSFetchStatus updatedDirStatus;
1952 AFSFetchStatus newFileStatus;
1953 AFSCallBack newFileCallback;
1956 /* can't create names with @sys in them; must expand it manually first.
1957 * return "invalid request" if they try.
1959 if (cm_ExpandSysName(namep, NULL, 0, 0)) {
1960 return CM_ERROR_ATSYS;
1963 /* before starting the RPC, mark that we're changing the file data, so
1964 * that someone who does a chmod will know to wait until our call
1967 lock_ObtainMutex(&dscp->mx);
1968 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
1970 cm_StartCallbackGrantingCall(NULL, &cbReq);
1972 lock_ReleaseMutex(&dscp->mx);
1978 cm_StatusFromAttr(&inStatus, NULL, attrp);
1980 /* try the RPC now */
1982 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1986 dirAFSFid.Volume = dscp->fid.volume;
1987 dirAFSFid.Vnode = dscp->fid.vnode;
1988 dirAFSFid.Unique = dscp->fid.unique;
1989 lock_ObtainMutex(&connp->mx);
1990 code = RXAFS_CreateFile(connp->callp, &dirAFSFid, namep,
1991 &inStatus, &newAFSFid, &newFileStatus,
1992 &updatedDirStatus, &newFileCallback,
1994 lock_ReleaseMutex(&connp->mx);
1995 } while (cm_Analyze(connp, userp, reqp,
1996 &dscp->fid, &volSync, NULL, &cbReq, code));
1997 code = cm_MapRPCError(code, reqp);
1999 lock_ObtainMutex(&dscp->mx);
2000 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2002 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2004 lock_ReleaseMutex(&dscp->mx);
2006 /* now try to create the file's entry, too, but be careful to
2007 * make sure that we don't merge in old info. Since we weren't locking
2008 * out any requests during the file's creation, we may have pretty old
2012 newFid.cell = dscp->fid.cell;
2013 newFid.volume = dscp->fid.volume;
2014 newFid.vnode = newAFSFid.Vnode;
2015 newFid.unique = newAFSFid.Unique;
2016 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2018 lock_ObtainMutex(&scp->mx);
2019 if (!cm_HaveCallback(scp)) {
2020 cm_MergeStatus(scp, &newFileStatus, &volSync,
2022 cm_EndCallbackGrantingCall(scp, &cbReq,
2023 &newFileCallback, 0);
2026 lock_ReleaseMutex(&scp->mx);
2031 /* make sure we end things properly */
2033 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2038 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
2042 lock_ObtainWrite(&scp->bufCreateLock);
2043 code = buf_CleanVnode(scp, userp, reqp);
2044 lock_ReleaseWrite(&scp->bufCreateLock);
2046 lock_ObtainMutex(&scp->mx);
2047 scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA
2048 | CM_SCACHEFLAG_OUTOFSPACE);
2049 if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
2050 | CM_SCACHEMASK_CLIENTMODTIME
2051 | CM_SCACHEMASK_LENGTH))
2052 code = cm_StoreMini(scp, userp, reqp);
2053 lock_ReleaseMutex(&scp->mx);
2058 long cm_MakeDir(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2059 cm_user_t *userp, cm_req_t *reqp)
2064 cm_callbackRequest_t cbReq;
2069 AFSStoreStatus inStatus;
2070 AFSFetchStatus updatedDirStatus;
2071 AFSFetchStatus newDirStatus;
2072 AFSCallBack newDirCallback;
2075 /* can't create names with @sys in them; must expand it manually first.
2076 * return "invalid request" if they try.
2078 if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2079 return CM_ERROR_ATSYS;
2082 /* before starting the RPC, mark that we're changing the directory
2083 * data, so that someone who does a chmod on the dir will wait until
2084 * our call completes.
2086 lock_ObtainMutex(&dscp->mx);
2087 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2089 cm_StartCallbackGrantingCall(NULL, &cbReq);
2091 lock_ReleaseMutex(&dscp->mx);
2097 cm_StatusFromAttr(&inStatus, NULL, attrp);
2099 /* try the RPC now */
2101 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2105 dirAFSFid.Volume = dscp->fid.volume;
2106 dirAFSFid.Vnode = dscp->fid.vnode;
2107 dirAFSFid.Unique = dscp->fid.unique;
2108 lock_ObtainMutex(&connp->mx);
2109 code = RXAFS_MakeDir(connp->callp, &dirAFSFid, namep,
2110 &inStatus, &newAFSFid, &newDirStatus,
2111 &updatedDirStatus, &newDirCallback,
2113 lock_ReleaseMutex(&connp->mx);
2114 } while (cm_Analyze(connp, userp, reqp,
2115 &dscp->fid, &volSync, NULL, &cbReq, code));
2116 code = cm_MapRPCError(code, reqp);
2118 lock_ObtainMutex(&dscp->mx);
2119 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2121 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2123 lock_ReleaseMutex(&dscp->mx);
2125 /* now try to create the new dir's entry, too, but be careful to
2126 * make sure that we don't merge in old info. Since we weren't locking
2127 * out any requests during the file's creation, we may have pretty old
2131 newFid.cell = dscp->fid.cell;
2132 newFid.volume = dscp->fid.volume;
2133 newFid.vnode = newAFSFid.Vnode;
2134 newFid.unique = newAFSFid.Unique;
2135 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2137 lock_ObtainMutex(&scp->mx);
2138 if (!cm_HaveCallback(scp)) {
2139 cm_MergeStatus(scp, &newDirStatus, &volSync,
2141 cm_EndCallbackGrantingCall(scp, &cbReq,
2142 &newDirCallback, 0);
2145 lock_ReleaseMutex(&scp->mx);
2146 cm_ReleaseSCache(scp);
2150 /* make sure we end things properly */
2152 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2154 /* and return error code */
2158 long cm_Link(cm_scache_t *dscp, char *namep, cm_scache_t *sscp, long flags,
2159 cm_user_t *userp, cm_req_t *reqp)
2164 AFSFid existingAFSFid;
2165 AFSFetchStatus updatedDirStatus;
2166 AFSFetchStatus newLinkStatus;
2169 if (dscp->fid.cell != sscp->fid.cell ||
2170 dscp->fid.volume != sscp->fid.volume) {
2171 return CM_ERROR_CROSSDEVLINK;
2174 lock_ObtainMutex(&dscp->mx);
2175 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2176 lock_ReleaseMutex(&dscp->mx);
2182 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2185 dirAFSFid.Volume = dscp->fid.volume;
2186 dirAFSFid.Vnode = dscp->fid.vnode;
2187 dirAFSFid.Unique = dscp->fid.unique;
2189 existingAFSFid.Volume = sscp->fid.volume;
2190 existingAFSFid.Vnode = sscp->fid.vnode;
2191 existingAFSFid.Unique = sscp->fid.unique;
2193 lock_ObtainMutex(&connp->mx);
2194 code = RXAFS_Link(connp->callp, &dirAFSFid, namep, &existingAFSFid,
2195 &newLinkStatus, &updatedDirStatus, &volSync);
2196 lock_ReleaseMutex(&connp->mx);
2198 osi_Log1(smb_logp," RXAFS_Link returns %d", code);
2199 } while (cm_Analyze(connp, userp, reqp,
2200 &dscp->fid, &volSync, NULL, NULL, code));
2202 code = cm_MapRPCError(code, reqp);
2204 lock_ObtainMutex(&dscp->mx);
2205 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2207 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2209 lock_ReleaseMutex(&dscp->mx);
2214 long cm_SymLink(cm_scache_t *dscp, char *namep, char *contentsp, long flags,
2215 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
2223 AFSStoreStatus inStatus;
2224 AFSFetchStatus updatedDirStatus;
2225 AFSFetchStatus newLinkStatus;
2228 /* before starting the RPC, mark that we're changing the directory data,
2229 * so that someone who does a chmod on the dir will wait until our
2232 lock_ObtainMutex(&dscp->mx);
2233 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2234 lock_ReleaseMutex(&dscp->mx);
2239 cm_StatusFromAttr(&inStatus, NULL, attrp);
2241 /* try the RPC now */
2243 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2247 dirAFSFid.Volume = dscp->fid.volume;
2248 dirAFSFid.Vnode = dscp->fid.vnode;
2249 dirAFSFid.Unique = dscp->fid.unique;
2250 lock_ObtainMutex(&connp->mx);
2251 code = RXAFS_Symlink(connp->callp, &dirAFSFid, namep, contentsp,
2252 &inStatus, &newAFSFid, &newLinkStatus,
2253 &updatedDirStatus, &volSync);
2254 lock_ReleaseMutex(&connp->mx);
2255 } while (cm_Analyze(connp, userp, reqp,
2256 &dscp->fid, &volSync, NULL, NULL, code));
2257 code = cm_MapRPCError(code, reqp);
2259 lock_ObtainMutex(&dscp->mx);
2260 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2262 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2264 lock_ReleaseMutex(&dscp->mx);
2266 /* now try to create the new dir's entry, too, but be careful to
2267 * make sure that we don't merge in old info. Since we weren't locking
2268 * out any requests during the file's creation, we may have pretty old
2272 newFid.cell = dscp->fid.cell;
2273 newFid.volume = dscp->fid.volume;
2274 newFid.vnode = newAFSFid.Vnode;
2275 newFid.unique = newAFSFid.Unique;
2276 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2278 lock_ObtainMutex(&scp->mx);
2279 if (!cm_HaveCallback(scp)) {
2280 cm_MergeStatus(scp, &newLinkStatus, &volSync,
2283 lock_ReleaseMutex(&scp->mx);
2284 cm_ReleaseSCache(scp);
2288 /* and return error code */
2292 long cm_RemoveDir(cm_scache_t *dscp, char *namep, cm_user_t *userp,
2299 AFSFetchStatus updatedDirStatus;
2302 /* before starting the RPC, mark that we're changing the directory data,
2303 * so that someone who does a chmod on the dir will wait until our
2306 lock_ObtainMutex(&dscp->mx);
2307 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2308 lock_ReleaseMutex(&dscp->mx);
2314 /* try the RPC now */
2316 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2320 dirAFSFid.Volume = dscp->fid.volume;
2321 dirAFSFid.Vnode = dscp->fid.vnode;
2322 dirAFSFid.Unique = dscp->fid.unique;
2323 lock_ObtainMutex(&connp->mx);
2324 code = RXAFS_RemoveDir(connp->callp, &dirAFSFid, namep,
2325 &updatedDirStatus, &volSync);
2326 lock_ReleaseMutex(&connp->mx);
2327 } while (cm_Analyze(connp, userp, reqp,
2328 &dscp->fid, &volSync, NULL, NULL, code));
2329 code = cm_MapRPCErrorRmdir(code, reqp);
2331 lock_ObtainMutex(&dscp->mx);
2332 cm_dnlcRemove(dscp, namep);
2333 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2335 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2337 lock_ReleaseMutex(&dscp->mx);
2339 /* and return error code */
2343 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
2345 /* grab mutex on contents */
2346 lock_ObtainMutex(&scp->mx);
2348 /* reset the prefetch info */
2349 scp->prefetch.base.LowPart = 0; /* base */
2350 scp->prefetch.base.HighPart = 0;
2351 scp->prefetch.end.LowPart = 0; /* and end */
2352 scp->prefetch.end.HighPart = 0;
2354 /* release mutex on contents */
2355 lock_ReleaseMutex(&scp->mx);
2361 long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
2362 char *newNamep, cm_user_t *userp, cm_req_t *reqp)
2366 AFSFid oldDirAFSFid;
2367 AFSFid newDirAFSFid;
2369 AFSFetchStatus updatedOldDirStatus;
2370 AFSFetchStatus updatedNewDirStatus;
2374 /* before starting the RPC, mark that we're changing the directory data,
2375 * so that someone who does a chmod on the dir will wait until our call
2376 * completes. We do this in vnode order so that we don't deadlock,
2377 * which makes the code a little verbose.
2379 if (oldDscp == newDscp) {
2380 /* check for identical names */
2381 if (strcmp(oldNamep, newNamep) == 0)
2382 return CM_ERROR_RENAME_IDENTICAL;
2385 lock_ObtainMutex(&oldDscp->mx);
2386 cm_dnlcRemove(oldDscp, oldNamep);
2387 cm_dnlcRemove(oldDscp, newNamep);
2388 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2389 CM_SCACHESYNC_STOREDATA);
2390 lock_ReleaseMutex(&oldDscp->mx);
2393 /* two distinct dir vnodes */
2395 if (oldDscp->fid.cell != newDscp->fid.cell ||
2396 oldDscp->fid.volume != newDscp->fid.volume)
2397 return CM_ERROR_CROSSDEVLINK;
2399 /* shouldn't happen that we have distinct vnodes for two
2400 * different files, but could due to deliberate attack, or
2401 * stale info. Avoid deadlocks and quit now.
2403 if (oldDscp->fid.vnode == newDscp->fid.vnode)
2404 return CM_ERROR_CROSSDEVLINK;
2406 if (oldDscp->fid.vnode < newDscp->fid.vnode) {
2407 lock_ObtainMutex(&oldDscp->mx);
2408 cm_dnlcRemove(oldDscp, oldNamep);
2409 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2410 CM_SCACHESYNC_STOREDATA);
2411 lock_ReleaseMutex(&oldDscp->mx);
2413 lock_ObtainMutex(&newDscp->mx);
2414 cm_dnlcRemove(newDscp, newNamep);
2415 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2416 CM_SCACHESYNC_STOREDATA);
2417 lock_ReleaseMutex(&newDscp->mx);
2419 /* cleanup first one */
2420 cm_SyncOpDone(oldDscp, NULL,
2421 CM_SCACHESYNC_STOREDATA);
2426 /* lock the new vnode entry first */
2427 lock_ObtainMutex(&newDscp->mx);
2428 cm_dnlcRemove(newDscp, newNamep);
2429 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2430 CM_SCACHESYNC_STOREDATA);
2431 lock_ReleaseMutex(&newDscp->mx);
2433 lock_ObtainMutex(&oldDscp->mx);
2434 cm_dnlcRemove(oldDscp, oldNamep);
2435 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2436 CM_SCACHESYNC_STOREDATA);
2437 lock_ReleaseMutex(&oldDscp->mx);
2439 /* cleanup first one */
2440 cm_SyncOpDone(newDscp, NULL,
2441 CM_SCACHESYNC_STOREDATA);
2445 } /* two distinct vnodes */
2452 /* try the RPC now */
2454 code = cm_Conn(&oldDscp->fid, userp, reqp, &connp);
2458 oldDirAFSFid.Volume = oldDscp->fid.volume;
2459 oldDirAFSFid.Vnode = oldDscp->fid.vnode;
2460 oldDirAFSFid.Unique = oldDscp->fid.unique;
2461 newDirAFSFid.Volume = newDscp->fid.volume;
2462 newDirAFSFid.Vnode = newDscp->fid.vnode;
2463 newDirAFSFid.Unique = newDscp->fid.unique;
2464 lock_ObtainMutex(&connp->mx);
2465 code = RXAFS_Rename(connp->callp, &oldDirAFSFid, oldNamep,
2466 &newDirAFSFid, newNamep,
2467 &updatedOldDirStatus, &updatedNewDirStatus,
2469 lock_ReleaseMutex(&connp->mx);
2470 } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
2471 &volSync, NULL, NULL, code));
2472 code = cm_MapRPCError(code, reqp);
2474 /* update the individual stat cache entries for the directories */
2475 lock_ObtainMutex(&oldDscp->mx);
2476 cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
2478 cm_MergeStatus(oldDscp, &updatedOldDirStatus, &volSync,
2481 lock_ReleaseMutex(&oldDscp->mx);
2483 /* and update it for the new one, too, if necessary */
2485 lock_ObtainMutex(&newDscp->mx);
2486 cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
2488 cm_MergeStatus(newDscp, &updatedNewDirStatus, &volSync,
2491 lock_ReleaseMutex(&newDscp->mx);
2494 /* and return error code */
2498 long cm_Lock(cm_scache_t *scp, unsigned char LockType,
2499 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
2500 u_long Timeout, cm_user_t *userp, cm_req_t *reqp,
2504 int Which = ((LockType & 0x1) ? LockRead : LockWrite);
2508 cm_file_lock_t *fileLock;
2512 /* Look for a conflict. Also, if we are asking for a shared lock,
2513 * look for another shared lock, so we don't have to do an RPC.
2517 fileLock = (cm_file_lock_t *)
2518 ((char *) q - offsetof(cm_file_lock_t, fileq));
2519 if ((fileLock->flags &
2520 (CM_FILELOCK_FLAG_INVALID | CM_FILELOCK_FLAG_WAITING))
2522 if ((LockType & 0x1) == 0
2523 || (fileLock->LockType & 0x1) == 0)
2524 return CM_ERROR_WOULDBLOCK;
2533 tfid.Volume = scp->fid.volume;
2534 tfid.Vnode = scp->fid.vnode;
2535 tfid.Unique = scp->fid.unique;
2536 lock_ReleaseMutex(&scp->mx);
2538 code = cm_Conn(&scp->fid, userp, reqp, &connp);
2541 lock_ObtainMutex(&connp->mx);
2542 code = RXAFS_SetLock(connp->callp, &tfid, Which,
2544 lock_ReleaseMutex(&connp->mx);
2545 } while (cm_Analyze(connp, userp, reqp, &scp->fid, &volSync,
2547 lock_ObtainMutex(&scp->mx);
2548 code = cm_MapRPCError(code, reqp);
2551 if (code == 0 || Timeout != 0) {
2552 fileLock = malloc(sizeof(cm_file_lock_t));
2553 fileLock->LockType = LockType;
2555 fileLock->userp = userp;
2556 fileLock->fid = scp->fid;
2557 fileLock->LOffset = LOffset;
2558 fileLock->LLength = LLength;
2559 fileLock->flags = (code == 0 ? 0 : CM_FILELOCK_FLAG_WAITING);
2560 osi_QAdd(&scp->fileLocks, &fileLock->fileq);
2561 lock_ObtainWrite(&cm_scacheLock);
2562 osi_QAdd(&cm_allFileLocks, &fileLock->q);
2563 lock_ReleaseWrite(&cm_scacheLock);
2570 long cm_Unlock(cm_scache_t *scp, unsigned char LockType,
2571 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
2572 cm_user_t *userp, cm_req_t *reqp)
2575 int Which = ((LockType & 0x1) ? LockRead : LockWrite);
2579 cm_file_lock_t *fileLock, *ourLock;
2580 osi_queue_t *q, *qq;
2581 int anotherReader = 0;
2585 if (LargeIntegerLessThan(LLength, scp->length))
2588 /* Look for our own lock on the list, so as to remove it.
2589 * Also, determine if we're the last reader; if not, avoid an RPC.
2593 fileLock = (cm_file_lock_t *)
2594 ((char *) q - offsetof(cm_file_lock_t, fileq));
2596 && fileLock->userp == userp
2597 && LargeIntegerEqualTo(fileLock->LOffset, LOffset)
2598 && LargeIntegerEqualTo(fileLock->LLength, LLength)) {
2603 else if (fileLock->LockType & 0x1)
2608 /* ignore byte ranges */
2609 if (smallLock && !found)
2612 /* don't try to unlock other people's locks */
2614 return CM_ERROR_WOULDBLOCK;
2616 /* discard lock record */
2617 osi_QRemove(&scp->fileLocks, qq);
2619 * Don't delete it here; let the daemon delete it, to simplify
2620 * the daemon's traversal of the list.
2622 lock_ObtainWrite(&cm_scacheLock);
2623 ourLock->flags |= CM_FILELOCK_FLAG_INVALID;
2624 cm_ReleaseUser(ourLock->userp);
2625 lock_ReleaseWrite(&cm_scacheLock);
2627 if (!anotherReader) {
2628 tfid.Volume = scp->fid.volume;
2629 tfid.Vnode = scp->fid.vnode;
2630 tfid.Unique = scp->fid.unique;
2631 lock_ReleaseMutex(&scp->mx);
2633 code = cm_Conn(&scp->fid, userp, reqp, &connp);
2636 lock_ObtainMutex(&connp->mx);
2637 code = RXAFS_ReleaseLock(connp->callp, &tfid, &volSync);
2638 lock_ReleaseMutex(&connp->mx);
2639 } while (cm_Analyze(connp, userp, reqp, &scp->fid, &volSync,
2641 code = cm_MapRPCError(code, reqp);
2642 lock_ObtainMutex(&scp->mx);
2648 void cm_CheckLocks()
2650 osi_queue_t *q, *nq;
2651 cm_file_lock_t *fileLock;
2660 lock_ObtainWrite(&cm_scacheLock);
2661 q = cm_allFileLocks;
2663 fileLock = (cm_file_lock_t *) q;
2665 if (fileLock->flags & CM_FILELOCK_FLAG_INVALID) {
2666 osi_QRemove(&cm_allFileLocks, q);
2669 else if (!(fileLock->flags & CM_FILELOCK_FLAG_WAITING)) {
2670 tfid.Volume = fileLock->fid.volume;
2671 tfid.Vnode = fileLock->fid.vnode;
2672 tfid.Unique = fileLock->fid.unique;
2673 lock_ReleaseWrite(&cm_scacheLock);
2675 code = cm_Conn(&fileLock->fid, fileLock->userp,
2679 lock_ObtainMutex(&connp->mx);
2680 code = RXAFS_ExtendLock(connp->callp, &tfid,
2682 lock_ReleaseMutex(&connp->mx);
2683 } while (cm_Analyze(connp, fileLock->userp, &req,
2684 &fileLock->fid, &volSync, NULL, NULL,
2686 code = cm_MapRPCError(code, &req);
2687 lock_ObtainWrite(&cm_scacheLock);
2691 lock_ReleaseWrite(&cm_scacheLock);
2694 long cm_RetryLock(cm_file_lock_t *oldFileLock, int vcp_is_dead)
2697 int Which = ((oldFileLock->LockType & 0x1) ? LockRead : LockWrite);
2702 cm_file_lock_t *fileLock;
2708 code = CM_ERROR_TIMEDOUT;
2714 /* Look for a conflict. Also, if we are asking for a shared lock,
2715 * look for another shared lock, so we don't have to do an RPC.
2717 code = cm_GetSCache(&oldFileLock->fid, &scp, oldFileLock->userp, &req);
2723 fileLock = (cm_file_lock_t *)
2724 ((char *) q - offsetof(cm_file_lock_t, fileq));
2725 if ((fileLock->flags &
2726 (CM_FILELOCK_FLAG_INVALID | CM_FILELOCK_FLAG_WAITING))
2728 if ((oldFileLock->LockType & 0x1) == 0
2729 || (fileLock->LockType & 0x1) == 0) {
2730 cm_ReleaseSCache(scp);
2731 return CM_ERROR_WOULDBLOCK;
2741 tfid.Volume = oldFileLock->fid.volume;
2742 tfid.Vnode = oldFileLock->fid.vnode;
2743 tfid.Unique = oldFileLock->fid.unique;
2745 code = cm_Conn(&oldFileLock->fid, oldFileLock->userp,
2749 lock_ObtainMutex(&connp->mx);
2750 code = RXAFS_SetLock(connp->callp, &tfid, Which,
2752 lock_ReleaseMutex(&connp->mx);
2753 } while (cm_Analyze(connp, oldFileLock->userp, &req,
2754 &oldFileLock->fid, &volSync,
2756 code = cm_MapRPCError(code, &req);
2760 if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
2761 lock_ObtainMutex(&scp->mx);
2762 osi_QRemove(&scp->fileLocks, &oldFileLock->fileq);
2763 lock_ReleaseMutex(&scp->mx);
2765 lock_ObtainWrite(&cm_scacheLock);
2767 oldFileLock->flags = 0;
2768 else if (code != CM_ERROR_WOULDBLOCK) {
2769 oldFileLock->flags |= CM_FILELOCK_FLAG_INVALID;
2770 cm_ReleaseUser(oldFileLock->userp);
2771 oldFileLock->userp = NULL;
2773 lock_ReleaseWrite(&cm_scacheLock);