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;
1131 struct rx_connection * callp;
1133 #ifdef AFS_FREELANCE_CLIENT
1134 if (cm_freelanceEnabled && dscp == cm_rootSCachep) {
1135 /* deleting a mount point from the root dir. */
1136 code = cm_FreelanceRemoveMount(namep);
1141 /* make sure we don't screw up the dir status during the merge */
1142 lock_ObtainMutex(&dscp->mx);
1143 sflags = CM_SCACHESYNC_STOREDATA;
1144 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1145 lock_ReleaseMutex(&dscp->mx);
1150 afsFid.Volume = dscp->fid.volume;
1151 afsFid.Vnode = dscp->fid.vnode;
1152 afsFid.Unique = dscp->fid.unique;
1154 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1158 callp = cm_GetRxConn(connp);
1159 code = RXAFS_RemoveFile(callp, &afsFid, namep,
1160 &newDirStatus, &volSync);
1161 rx_PutConnection(callp);
1163 } while (cm_Analyze(connp, userp, reqp, &dscp->fid, &volSync, NULL, NULL, code));
1164 code = cm_MapRPCError(code, reqp);
1166 lock_ObtainMutex(&dscp->mx);
1167 cm_dnlcRemove(dscp, namep);
1168 cm_SyncOpDone(dscp, NULL, sflags);
1170 cm_MergeStatus(dscp, &newDirStatus, &volSync, userp, 0);
1171 lock_ReleaseMutex(&dscp->mx);
1176 /* called with a locked vnode, and fills in the link info.
1177 * returns this the vnode still locked.
1179 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1186 lock_AssertMutex(&linkScp->mx);
1187 if (!linkScp->mountPointStringp) {
1188 /* read the link data */
1189 lock_ReleaseMutex(&linkScp->mx);
1190 thyper.LowPart = thyper.HighPart = 0;
1191 code = buf_Get(linkScp, &thyper, &bufp);
1192 lock_ObtainMutex(&linkScp->mx);
1196 code = cm_SyncOp(linkScp, bufp, userp, reqp, 0,
1197 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1202 if (cm_HaveBuffer(linkScp, bufp, 0))
1205 code = cm_GetBuffer(linkScp, bufp, NULL, userp, reqp);
1210 } /* while loop to get the data */
1212 /* now if we still have no link read in,
1213 * copy the data from the buffer */
1214 if ((temp = linkScp->length.LowPart) >= 1024) {
1216 return CM_ERROR_TOOBIG;
1219 /* otherwise, it fits; make sure it is still null (could have
1220 * lost race with someone else referencing this link above),
1221 * and if so, copy in the data.
1223 if (linkScp->mountPointStringp == NULL) {
1224 linkScp->mountPointStringp = malloc(temp+1);
1225 strncpy(linkScp->mountPointStringp, bufp->datap, temp);
1226 linkScp->mountPointStringp[temp] = 0; /* null terminate */
1229 } /* don't have sym link contents cached */
1234 /* called with a held vnode and a path suffix, with the held vnode being a
1235 * symbolic link. Our goal is to generate a new path to interpret, and return
1236 * this new path in newSpaceBufferp. If the new vnode is relative to a dir
1237 * other than the directory containing the symbolic link, then the new root is
1238 * returned in *newRootScpp, otherwise a null is returned there.
1240 long cm_AssembleLink(cm_scache_t *linkScp, char *pathSuffixp,
1241 cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1242 cm_user_t *userp, cm_req_t *reqp)
1248 lock_ObtainMutex(&linkScp->mx);
1249 code = cm_HandleLink(linkScp, userp, reqp);
1253 /* if we may overflow the buffer, bail out; buffer is signficantly
1254 * bigger than max path length, so we don't really have to worry about
1255 * being a little conservative here.
1257 if (strlen(linkScp->mountPointStringp) + strlen(pathSuffixp) + 2
1258 >= CM_UTILS_SPACESIZE)
1259 return CM_ERROR_TOOBIG;
1261 tsp = cm_GetSpace();
1262 linkp = linkScp->mountPointStringp;
1263 if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1264 if (strlen(linkp) > cm_mountRootLen)
1265 strcpy(tsp->data, linkp+cm_mountRootLen+1);
1268 *newRootScpp = cm_rootSCachep;
1269 cm_HoldSCache(cm_rootSCachep);
1270 } else if (*linkp == '\\' || *linkp == '/') {
1271 /* formerly, this was considered to be from the AFS root,
1272 * but this seems to create problems. instead, we will just
1273 * reject the link */
1275 strcpy(tsp->data, linkp+1);
1276 *newRootScpp = cm_rootSCachep;
1277 cm_HoldSCache(cm_rootSCachep);
1279 code = CM_ERROR_NOSUCHPATH;
1284 /* a relative link */
1285 strcpy(tsp->data, linkp);
1286 *newRootScpp = NULL;
1288 if (pathSuffixp[0] != 0) { /* if suffix string is non-null */
1289 strcat(tsp->data, "\\");
1290 strcat(tsp->data, pathSuffixp);
1292 *newSpaceBufferp = tsp;
1296 lock_ReleaseMutex(&linkScp->mx);
1300 long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
1301 cm_user_t *userp, char *tidPathp, cm_req_t *reqp, cm_scache_t **outScpp)
1304 char *tp; /* ptr moving through input buffer */
1305 char tc; /* temp char */
1306 int haveComponent; /* has new component started? */
1307 char component[256]; /* this is the new component */
1308 char *cp; /* component name being assembled */
1309 cm_scache_t *tscp; /* current location in the hierarchy */
1310 cm_scache_t *nscp; /* next dude down */
1311 cm_scache_t *dirScp; /* last dir we searched */
1312 cm_scache_t *linkScp; /* new root for the symlink we just
1314 cm_space_t *psp; /* space for current path, if we've hit
1316 cm_space_t *tempsp; /* temp vbl */
1317 char *restp; /* rest of the pathname to interpret */
1318 int symlinkCount; /* count of # of symlinks traversed */
1319 int extraFlag; /* avoid chasing mt pts for dir cmd */
1320 int phase = 1; /* 1 = tidPathp, 2 = pathp */
1333 cm_HoldSCache(tscp);
1338 /* map Unix slashes into DOS ones so we can interpret Unix
1344 if (!haveComponent) {
1363 /* we have a component here */
1364 if (tc == 0 || tc == '\\') {
1365 /* end of the component; we're at the last
1366 * component if tc == 0. However, if the last
1367 * is a symlink, we have more to do.
1369 *cp++ = 0; /* add null termination */
1371 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
1372 extraFlag = CM_FLAG_NOMOUNTCHASE;
1373 code = cm_Lookup(tscp, component,
1375 userp, reqp, &nscp);
1378 cm_ReleaseSCache(tscp);
1383 haveComponent = 0; /* component done */
1384 dirScp = tscp; /* for some symlinks */
1385 tscp = nscp; /* already held */
1386 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
1388 cm_ReleaseSCache(dirScp);
1392 /* now, if tscp is a symlink, we should follow
1393 * it and assemble the path again.
1395 lock_ObtainMutex(&tscp->mx);
1396 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1397 CM_SCACHESYNC_GETSTATUS
1398 | CM_SCACHESYNC_NEEDCALLBACK);
1400 lock_ReleaseMutex(&tscp->mx);
1401 cm_ReleaseSCache(tscp);
1402 cm_ReleaseSCache(dirScp);
1405 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
1406 /* this is a symlink; assemble a new buffer */
1407 lock_ReleaseMutex(&tscp->mx);
1408 if (symlinkCount++ >= 16) {
1409 cm_ReleaseSCache(tscp);
1410 cm_ReleaseSCache(dirScp);
1413 return CM_ERROR_TOOBIG;
1419 code = cm_AssembleLink(tscp, restp, &linkScp, &tempsp, userp, reqp);
1421 /* something went wrong */
1422 cm_ReleaseSCache(tscp);
1423 cm_ReleaseSCache(dirScp);
1427 /* otherwise, tempsp has the new path,
1428 * and linkScp is the new root from
1429 * which to interpret that path.
1430 * Continue with the namei processing,
1431 * also doing the bookkeeping for the
1432 * space allocation and tracking the
1433 * vnode reference counts.
1439 cm_ReleaseSCache(tscp);
1443 * now, if linkScp is null, that's
1444 * AssembleLink's way of telling us that
1445 * the sym link is relative to the dir
1446 * containing the link. We have a ref
1447 * to it in dirScp, and we hold it now
1448 * and reuse it as the new spot in the
1452 cm_HoldSCache(dirScp);
1455 } /* if we have a sym link */
1457 /* not a symlink, we may be done */
1458 lock_ReleaseMutex(&tscp->mx);
1465 cm_ReleaseSCache(dirScp);
1470 cm_ReleaseSCache(dirScp);
1471 } /* end of a component */
1473 } /* we have a component */
1474 } /* big while loop over all components */
1484 /* called with a dir, and a vnode within the dir that happens to be a symlink.
1485 * We chase the link, and return a held pointer to the target, if it exists,
1486 * in *outScpp. If we succeed, we return 0, otherwise we return an error code
1487 * and do not hold or return a target vnode.
1489 * This is very similar to calling cm_NameI with the last component of a name,
1490 * which happens to be a symlink, except that we've already passed by the name.
1492 * This function is typically called by the directory listing functions, which
1493 * encounter symlinks but need to return the proper file length so programs
1494 * like "more" work properly when they make use of the attributes retrieved from
1497 * The input vnode should not be locked when this function is called.
1499 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
1500 cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
1504 cm_scache_t *newRootScp;
1506 osi_Log1(afsd_logp, "Evaluating symlink vp %x", linkScp);
1508 code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
1512 /* now, if newRootScp is NULL, we're really being told that the symlink
1513 * is relative to the current directory (dscp).
1515 if (newRootScp == NULL) {
1517 cm_HoldSCache(dscp);
1520 code = cm_NameI(newRootScp, spacep->data,
1521 CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
1522 userp, NULL, reqp, outScpp);
1524 /* this stuff is allocated no matter what happened on the namei call,
1526 cm_FreeSpace(spacep);
1527 cm_ReleaseSCache(newRootScp);
1532 /* make this big enough so that one buffer of dir pages won't overflow. We'll
1533 * check anyway, but we want to minimize the chance that we have to leave stuff
1536 #define CM_BULKMAX 128
1538 /* rock for bulk stat calls */
1539 typedef struct cm_bulkStat {
1540 osi_hyper_t bufOffset; /* only do it for things in this buffer page */
1542 /* info for the actual call */
1543 int counter; /* next free slot */
1544 AFSFid fids[CM_BULKMAX];
1545 AFSFetchStatus stats[CM_BULKMAX];
1546 AFSCallBack callbacks[CM_BULKMAX];
1549 /* for a given entry, make sure that it isn't in the stat cache, and then
1550 * add it to the list of file IDs to be obtained.
1552 * Don't bother adding it if we already have a vnode. Note that the dir
1553 * is locked, so we have to be careful checking the vnode we're thinking of
1554 * processing, to avoid deadlocks.
1556 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
1567 /* Don't overflow bsp. */
1568 if (bsp->counter >= CM_BULKMAX)
1569 return CM_ERROR_STOPNOW;
1571 thyper.LowPart = buf_bufferSize;
1572 thyper.HighPart = 0;
1573 thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
1575 /* thyper is now the first byte past the end of the record we're
1576 * interested in, and bsp->bufOffset is the first byte of the record
1577 * we're interested in.
1578 * Skip data in the others.
1581 if (LargeIntegerLessThan(*offp, bsp->bufOffset))
1583 if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
1584 return CM_ERROR_STOPNOW;
1585 if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
1588 tfid.cell = scp->fid.cell;
1589 tfid.volume = scp->fid.volume;
1590 tfid.vnode = ntohl(dep->fid.vnode);
1591 tfid.unique = ntohl(dep->fid.unique);
1592 tscp = cm_FindSCache(&tfid);
1594 if (lock_TryMutex(&tscp->mx)) {
1595 /* we have an entry that we can look at */
1596 if (cm_HaveCallback(tscp)) {
1597 /* we have a callback on it. Don't bother
1598 * fetching this stat entry, since we're happy
1599 * with the info we have.
1601 lock_ReleaseMutex(&tscp->mx);
1602 cm_ReleaseSCache(tscp);
1605 lock_ReleaseMutex(&tscp->mx);
1607 cm_ReleaseSCache(tscp);
1610 #ifdef AFS_FREELANCE_CLIENT
1611 // yj: if this is a mountpoint under root.afs then we don't want it
1612 // to be bulkstat-ed, instead, we call getSCache directly and under
1613 // getSCache, it is handled specially.
1614 if ( cm_freelanceEnabled &&
1615 tfid.cell==AFS_FAKE_ROOT_CELL_ID &&
1616 tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
1617 !(tfid.vnode==0x1 && tfid.unique==0x1) )
1619 osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
1620 return cm_GetSCache(&tfid, &tscp, NULL, NULL);
1622 #endif /* AFS_FREELANCE_CLIENT */
1625 bsp->fids[i].Volume = scp->fid.volume;
1626 bsp->fids[i].Vnode = tfid.vnode;
1627 bsp->fids[i].Unique = tfid.unique;
1631 /* called with a locked scp and a pointer to a buffer. Make bulk stat
1632 * calls on all undeleted files in the page of the directory specified.
1634 void cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
1638 cm_bulkStat_t bb; /* this is *BIG*, probably 12K or so;
1639 * watch for stack problems */
1640 AFSCBFids fidStruct;
1641 AFSBulkStats statStruct;
1643 AFSCBs callbackStruct;
1646 cm_callbackRequest_t cbReq;
1652 struct rx_connection * callp;
1654 osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%x", (long) dscp);
1656 /* should be on a buffer boundary */
1657 osi_assert((offsetp->LowPart & (buf_bufferSize - 1)) == 0);
1660 bb.bufOffset = *offsetp;
1662 /* first, assemble the file IDs we need to stat */
1663 code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) &bb, offsetp, userp,
1666 /* if we failed, bail out early */
1667 if (code && code != CM_ERROR_STOPNOW) return;
1669 /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
1670 * make the calls to create the entries. Handle AFSCBMAX files at a
1674 while(filex < bb.counter) {
1675 filesThisCall = bb.counter - filex;
1676 if (filesThisCall > AFSCBMAX) filesThisCall = AFSCBMAX;
1678 fidStruct.AFSCBFids_len = filesThisCall;
1679 fidStruct.AFSCBFids_val = &bb.fids[filex];
1680 statStruct.AFSBulkStats_len = filesThisCall;
1681 statStruct.AFSBulkStats_val = &bb.stats[filex];
1682 callbackStruct.AFSCBs_len = filesThisCall;
1683 callbackStruct.AFSCBs_val = &bb.callbacks[filex];
1684 cm_StartCallbackGrantingCall(NULL, &cbReq);
1685 osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
1687 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1691 callp = cm_GetRxConn(connp);
1692 code = RXAFS_BulkStatus(callp, &fidStruct,
1693 &statStruct, &callbackStruct, &volSync);
1694 rx_PutConnection(callp);
1696 } while (cm_Analyze(connp, userp, reqp, &dscp->fid,
1697 &volSync, NULL, &cbReq, code));
1698 code = cm_MapRPCError(code, reqp);
1700 osi_Log0(afsd_logp, "CALL BulkStatus DONE");
1702 /* may as well quit on an error, since we're not going to do
1703 * much better on the next immediate call, either.
1708 /* otherwise, we should do the merges */
1709 for(i = 0; i<filesThisCall; i++) {
1711 tfid.cell = dscp->fid.cell;
1712 tfid.volume = bb.fids[j].Volume;
1713 tfid.vnode = bb.fids[j].Vnode;
1714 tfid.unique = bb.fids[j].Unique;
1715 code = cm_GetSCache(&tfid, &scp, userp, reqp);
1719 /* otherwise, if this entry has no callback info,
1722 lock_ObtainMutex(&scp->mx);
1723 /* now, we have to be extra paranoid on merging in this
1724 * information, since we didn't use cm_SyncOp before
1725 * starting the fetch to make sure that no bad races
1726 * were occurring. Specifically, we need to make sure
1727 * we don't obliterate any newer information in the
1728 * vnode than have here.
1730 * Right now, be pretty conservative: if there's a
1731 * callback or a pending call, skip it.
1733 if (scp->cbServerp == NULL
1735 (CM_SCACHEFLAG_FETCHING
1736 | CM_SCACHEFLAG_STORING
1737 | CM_SCACHEFLAG_SIZESTORING))) {
1738 cm_EndCallbackGrantingCall(scp, &cbReq,
1740 CM_CALLBACK_MAINTAINCOUNT);
1741 cm_MergeStatus(scp, &bb.stats[j], &volSync,
1744 lock_ReleaseMutex(&scp->mx);
1745 cm_ReleaseSCache(scp);
1746 } /* all files in the response */
1747 /* now tell it to drop the count,
1748 * after doing the vnode processing above */
1749 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
1751 filex += filesThisCall;
1752 } /* while there are still more files to process */
1753 osi_Log0(afsd_logp, "END cm_TryBulkStat");
1756 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
1760 /* initialize store back mask as inexpensive local variable */
1762 memset(statusp, 0, sizeof(AFSStoreStatus));
1764 /* copy out queued info from scache first, if scp passed in */
1766 if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
1767 statusp->ClientModTime = scp->clientModTime;
1768 mask |= AFS_SETMODTIME;
1769 scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
1774 /* now add in our locally generated request */
1775 if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
1776 statusp->ClientModTime = attrp->clientModTime;
1777 mask |= AFS_SETMODTIME;
1779 if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
1780 statusp->UnixModeBits = attrp->unixModeBits;
1781 mask |= AFS_SETMODE;
1783 if (attrp->mask & CM_ATTRMASK_OWNER) {
1784 statusp->Owner = attrp->owner;
1785 mask |= AFS_SETOWNER;
1787 if (attrp->mask & CM_ATTRMASK_GROUP) {
1788 statusp->Group = attrp->group;
1789 mask |= AFS_SETGROUP;
1792 statusp->Mask = mask;
1795 /* set the file size, and make sure that all relevant buffers have been
1796 * truncated. Ensure that any partially truncated buffers have been zeroed
1797 * to the end of the buffer.
1799 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
1805 /* start by locking out buffer creation */
1806 lock_ObtainWrite(&scp->bufCreateLock);
1808 /* verify that this is a file, not a dir or a symlink */
1809 lock_ObtainMutex(&scp->mx);
1810 code = cm_SyncOp(scp, NULL, userp, reqp, 0,
1811 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1815 if (scp->fileType != CM_SCACHETYPE_FILE) {
1816 code = CM_ERROR_ISDIR;
1821 if (LargeIntegerLessThan(*sizep, scp->length))
1826 lock_ReleaseMutex(&scp->mx);
1828 /* can't hold scp->mx lock here, since we may wait for a storeback to
1829 * finish if the buffer package is cleaning a buffer by storing it to
1833 buf_Truncate(scp, userp, reqp, sizep);
1835 /* now ensure that file length is short enough, and update truncPos */
1836 lock_ObtainMutex(&scp->mx);
1838 /* make sure we have a callback (so we have the right value for the
1839 * length), and wait for it to be safe to do a truncate.
1841 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
1842 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
1843 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
1847 if (LargeIntegerLessThan(*sizep, scp->length)) {
1848 /* a real truncation. If truncPos is not set yet, or is bigger
1849 * than where we're truncating the file, set truncPos to this
1854 if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
1855 || LargeIntegerLessThan(*sizep, scp->length)) {
1857 scp->truncPos = *sizep;
1858 scp->mask |= CM_SCACHEMASK_TRUNCPOS;
1860 /* in either case, the new file size has been changed */
1861 scp->length = *sizep;
1862 scp->mask |= CM_SCACHEMASK_LENGTH;
1864 else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
1865 /* really extending the file */
1866 scp->length = *sizep;
1867 scp->mask |= CM_SCACHEMASK_LENGTH;
1870 /* done successfully */
1874 lock_ReleaseMutex(&scp->mx);
1875 lock_ReleaseWrite(&scp->bufCreateLock);
1880 /* set the file size or other attributes (but not both at once) */
1881 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
1886 AFSFetchStatus afsOutStatus;
1890 AFSStoreStatus afsInStatus;
1891 struct rx_connection * callp;
1893 /* handle file length setting */
1894 if (attrp->mask & CM_ATTRMASK_LENGTH)
1895 return cm_SetLength(scp, &attrp->length, userp, reqp);
1897 flags = CM_SCACHESYNC_STORESTATUS;
1899 lock_ObtainMutex(&scp->mx);
1900 /* otherwise, we have to make an RPC to get the status */
1901 code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
1903 /* make the attr structure */
1904 cm_StatusFromAttr(&afsInStatus, scp, attrp);
1906 lock_ReleaseMutex(&scp->mx);
1910 /* now make the RPC */
1911 osi_Log1(afsd_logp, "CALL StoreStatus vp %x", (long) scp);
1912 tfid.Volume = scp->fid.volume;
1913 tfid.Vnode = scp->fid.vnode;
1914 tfid.Unique = scp->fid.unique;
1916 code = cm_Conn(&scp->fid, userp, reqp, &connp);
1920 callp = cm_GetRxConn(connp);
1921 code = RXAFS_StoreStatus(callp, &tfid,
1922 &afsInStatus, &afsOutStatus, &volSync);
1923 rx_PutConnection(callp);
1925 } while (cm_Analyze(connp, userp, reqp,
1926 &scp->fid, &volSync, NULL, NULL, code));
1927 code = cm_MapRPCError(code, reqp);
1929 osi_Log1(afsd_logp, "CALL StoreStatus DONE, code %d", code);
1931 lock_ObtainMutex(&scp->mx);
1932 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
1934 cm_MergeStatus(scp, &afsOutStatus, &volSync, userp,
1935 CM_MERGEFLAG_FORCE);
1937 /* if we're changing the mode bits, discard the ACL cache,
1938 * since we changed the mode bits.
1940 if (afsInStatus.Mask & AFS_SETMODE) cm_FreeAllACLEnts(scp);
1941 lock_ReleaseMutex(&scp->mx);
1945 long cm_Create(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
1946 cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
1951 cm_callbackRequest_t cbReq;
1956 AFSStoreStatus inStatus;
1957 AFSFetchStatus updatedDirStatus;
1958 AFSFetchStatus newFileStatus;
1959 AFSCallBack newFileCallback;
1961 struct rx_connection * callp;
1963 /* can't create names with @sys in them; must expand it manually first.
1964 * return "invalid request" if they try.
1966 if (cm_ExpandSysName(namep, NULL, 0, 0)) {
1967 return CM_ERROR_ATSYS;
1970 /* before starting the RPC, mark that we're changing the file data, so
1971 * that someone who does a chmod will know to wait until our call
1974 lock_ObtainMutex(&dscp->mx);
1975 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
1977 cm_StartCallbackGrantingCall(NULL, &cbReq);
1979 lock_ReleaseMutex(&dscp->mx);
1985 cm_StatusFromAttr(&inStatus, NULL, attrp);
1987 /* try the RPC now */
1989 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1993 dirAFSFid.Volume = dscp->fid.volume;
1994 dirAFSFid.Vnode = dscp->fid.vnode;
1995 dirAFSFid.Unique = dscp->fid.unique;
1997 callp = cm_GetRxConn(connp);
1998 code = RXAFS_CreateFile(connp->callp, &dirAFSFid, namep,
1999 &inStatus, &newAFSFid, &newFileStatus,
2000 &updatedDirStatus, &newFileCallback,
2002 rx_PutConnection(callp);
2004 } while (cm_Analyze(connp, userp, reqp,
2005 &dscp->fid, &volSync, NULL, &cbReq, code));
2006 code = cm_MapRPCError(code, reqp);
2008 lock_ObtainMutex(&dscp->mx);
2009 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2011 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2013 lock_ReleaseMutex(&dscp->mx);
2015 /* now try to create the file's entry, too, but be careful to
2016 * make sure that we don't merge in old info. Since we weren't locking
2017 * out any requests during the file's creation, we may have pretty old
2021 newFid.cell = dscp->fid.cell;
2022 newFid.volume = dscp->fid.volume;
2023 newFid.vnode = newAFSFid.Vnode;
2024 newFid.unique = newAFSFid.Unique;
2025 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2027 lock_ObtainMutex(&scp->mx);
2028 if (!cm_HaveCallback(scp)) {
2029 cm_MergeStatus(scp, &newFileStatus, &volSync,
2031 cm_EndCallbackGrantingCall(scp, &cbReq,
2032 &newFileCallback, 0);
2035 lock_ReleaseMutex(&scp->mx);
2040 /* make sure we end things properly */
2042 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2047 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
2051 lock_ObtainWrite(&scp->bufCreateLock);
2052 code = buf_CleanVnode(scp, userp, reqp);
2053 lock_ReleaseWrite(&scp->bufCreateLock);
2055 lock_ObtainMutex(&scp->mx);
2056 scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA
2057 | CM_SCACHEFLAG_OUTOFSPACE);
2058 if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
2059 | CM_SCACHEMASK_CLIENTMODTIME
2060 | CM_SCACHEMASK_LENGTH))
2061 code = cm_StoreMini(scp, userp, reqp);
2062 lock_ReleaseMutex(&scp->mx);
2067 long cm_MakeDir(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2068 cm_user_t *userp, cm_req_t *reqp)
2073 cm_callbackRequest_t cbReq;
2078 AFSStoreStatus inStatus;
2079 AFSFetchStatus updatedDirStatus;
2080 AFSFetchStatus newDirStatus;
2081 AFSCallBack newDirCallback;
2083 struct rx_connection * callp;
2085 /* can't create names with @sys in them; must expand it manually first.
2086 * return "invalid request" if they try.
2088 if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2089 return CM_ERROR_ATSYS;
2092 /* before starting the RPC, mark that we're changing the directory
2093 * data, so that someone who does a chmod on the dir will wait until
2094 * our call completes.
2096 lock_ObtainMutex(&dscp->mx);
2097 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2099 cm_StartCallbackGrantingCall(NULL, &cbReq);
2101 lock_ReleaseMutex(&dscp->mx);
2107 cm_StatusFromAttr(&inStatus, NULL, attrp);
2109 /* try the RPC now */
2111 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2115 dirAFSFid.Volume = dscp->fid.volume;
2116 dirAFSFid.Vnode = dscp->fid.vnode;
2117 dirAFSFid.Unique = dscp->fid.unique;
2119 callp = cm_GetRxConn(connp);
2120 code = RXAFS_MakeDir(connp->callp, &dirAFSFid, namep,
2121 &inStatus, &newAFSFid, &newDirStatus,
2122 &updatedDirStatus, &newDirCallback,
2124 rx_PutConnection(callp);
2126 } while (cm_Analyze(connp, userp, reqp,
2127 &dscp->fid, &volSync, NULL, &cbReq, code));
2128 code = cm_MapRPCError(code, reqp);
2130 lock_ObtainMutex(&dscp->mx);
2131 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2133 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2135 lock_ReleaseMutex(&dscp->mx);
2137 /* now try to create the new dir's entry, too, but be careful to
2138 * make sure that we don't merge in old info. Since we weren't locking
2139 * out any requests during the file's creation, we may have pretty old
2143 newFid.cell = dscp->fid.cell;
2144 newFid.volume = dscp->fid.volume;
2145 newFid.vnode = newAFSFid.Vnode;
2146 newFid.unique = newAFSFid.Unique;
2147 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2149 lock_ObtainMutex(&scp->mx);
2150 if (!cm_HaveCallback(scp)) {
2151 cm_MergeStatus(scp, &newDirStatus, &volSync,
2153 cm_EndCallbackGrantingCall(scp, &cbReq,
2154 &newDirCallback, 0);
2157 lock_ReleaseMutex(&scp->mx);
2158 cm_ReleaseSCache(scp);
2162 /* make sure we end things properly */
2164 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2166 /* and return error code */
2170 long cm_Link(cm_scache_t *dscp, char *namep, cm_scache_t *sscp, long flags,
2171 cm_user_t *userp, cm_req_t *reqp)
2176 AFSFid existingAFSFid;
2177 AFSFetchStatus updatedDirStatus;
2178 AFSFetchStatus newLinkStatus;
2180 struct rx_connection * callp;
2182 if (dscp->fid.cell != sscp->fid.cell ||
2183 dscp->fid.volume != sscp->fid.volume) {
2184 return CM_ERROR_CROSSDEVLINK;
2187 lock_ObtainMutex(&dscp->mx);
2188 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2189 lock_ReleaseMutex(&dscp->mx);
2195 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2198 dirAFSFid.Volume = dscp->fid.volume;
2199 dirAFSFid.Vnode = dscp->fid.vnode;
2200 dirAFSFid.Unique = dscp->fid.unique;
2202 existingAFSFid.Volume = sscp->fid.volume;
2203 existingAFSFid.Vnode = sscp->fid.vnode;
2204 existingAFSFid.Unique = sscp->fid.unique;
2206 callp = cm_GetRxConn(connp);
2207 code = RXAFS_Link(callp, &dirAFSFid, namep, &existingAFSFid,
2208 &newLinkStatus, &updatedDirStatus, &volSync);
2209 rx_PutConnection(callp);
2210 osi_Log1(smb_logp," RXAFS_Link returns %d", code);
2212 } while (cm_Analyze(connp, userp, reqp,
2213 &dscp->fid, &volSync, NULL, NULL, code));
2215 code = cm_MapRPCError(code, reqp);
2217 lock_ObtainMutex(&dscp->mx);
2218 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2220 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2222 lock_ReleaseMutex(&dscp->mx);
2227 long cm_SymLink(cm_scache_t *dscp, char *namep, char *contentsp, long flags,
2228 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
2236 AFSStoreStatus inStatus;
2237 AFSFetchStatus updatedDirStatus;
2238 AFSFetchStatus newLinkStatus;
2240 struct rx_connection * callp;
2242 /* before starting the RPC, mark that we're changing the directory data,
2243 * so that someone who does a chmod on the dir will wait until our
2246 lock_ObtainMutex(&dscp->mx);
2247 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2248 lock_ReleaseMutex(&dscp->mx);
2253 cm_StatusFromAttr(&inStatus, NULL, attrp);
2255 /* try the RPC now */
2257 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2261 dirAFSFid.Volume = dscp->fid.volume;
2262 dirAFSFid.Vnode = dscp->fid.vnode;
2263 dirAFSFid.Unique = dscp->fid.unique;
2265 callp = cm_GetRxConn(connp);
2266 code = RXAFS_Symlink(callp, &dirAFSFid, namep, contentsp,
2267 &inStatus, &newAFSFid, &newLinkStatus,
2268 &updatedDirStatus, &volSync);
2269 rx_PutConnection(callp);
2271 } while (cm_Analyze(connp, userp, reqp,
2272 &dscp->fid, &volSync, NULL, NULL, code));
2273 code = cm_MapRPCError(code, reqp);
2275 lock_ObtainMutex(&dscp->mx);
2276 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2278 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2280 lock_ReleaseMutex(&dscp->mx);
2282 /* now try to create the new dir's entry, too, but be careful to
2283 * make sure that we don't merge in old info. Since we weren't locking
2284 * out any requests during the file's creation, we may have pretty old
2288 newFid.cell = dscp->fid.cell;
2289 newFid.volume = dscp->fid.volume;
2290 newFid.vnode = newAFSFid.Vnode;
2291 newFid.unique = newAFSFid.Unique;
2292 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2294 lock_ObtainMutex(&scp->mx);
2295 if (!cm_HaveCallback(scp)) {
2296 cm_MergeStatus(scp, &newLinkStatus, &volSync,
2299 lock_ReleaseMutex(&scp->mx);
2300 cm_ReleaseSCache(scp);
2304 /* and return error code */
2308 long cm_RemoveDir(cm_scache_t *dscp, char *namep, cm_user_t *userp,
2315 AFSFetchStatus updatedDirStatus;
2317 struct rx_connection * callp;
2319 /* before starting the RPC, mark that we're changing the directory data,
2320 * so that someone who does a chmod on the dir will wait until our
2323 lock_ObtainMutex(&dscp->mx);
2324 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2325 lock_ReleaseMutex(&dscp->mx);
2331 /* try the RPC now */
2333 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2337 dirAFSFid.Volume = dscp->fid.volume;
2338 dirAFSFid.Vnode = dscp->fid.vnode;
2339 dirAFSFid.Unique = dscp->fid.unique;
2341 callp = cm_GetRxConn(connp);
2342 code = RXAFS_RemoveDir(callp, &dirAFSFid, namep,
2343 &updatedDirStatus, &volSync);
2344 rx_PutConnection(callp);
2346 } while (cm_Analyze(connp, userp, reqp,
2347 &dscp->fid, &volSync, NULL, NULL, code));
2348 code = cm_MapRPCErrorRmdir(code, reqp);
2350 lock_ObtainMutex(&dscp->mx);
2351 cm_dnlcRemove(dscp, namep);
2352 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2354 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2356 lock_ReleaseMutex(&dscp->mx);
2358 /* and return error code */
2362 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
2364 /* grab mutex on contents */
2365 lock_ObtainMutex(&scp->mx);
2367 /* reset the prefetch info */
2368 scp->prefetch.base.LowPart = 0; /* base */
2369 scp->prefetch.base.HighPart = 0;
2370 scp->prefetch.end.LowPart = 0; /* and end */
2371 scp->prefetch.end.HighPart = 0;
2373 /* release mutex on contents */
2374 lock_ReleaseMutex(&scp->mx);
2380 long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
2381 char *newNamep, cm_user_t *userp, cm_req_t *reqp)
2385 AFSFid oldDirAFSFid;
2386 AFSFid newDirAFSFid;
2388 AFSFetchStatus updatedOldDirStatus;
2389 AFSFetchStatus updatedNewDirStatus;
2392 struct rx_connection * callp;
2394 /* before starting the RPC, mark that we're changing the directory data,
2395 * so that someone who does a chmod on the dir will wait until our call
2396 * completes. We do this in vnode order so that we don't deadlock,
2397 * which makes the code a little verbose.
2399 if (oldDscp == newDscp) {
2400 /* check for identical names */
2401 if (strcmp(oldNamep, newNamep) == 0)
2402 return CM_ERROR_RENAME_IDENTICAL;
2405 lock_ObtainMutex(&oldDscp->mx);
2406 cm_dnlcRemove(oldDscp, oldNamep);
2407 cm_dnlcRemove(oldDscp, newNamep);
2408 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2409 CM_SCACHESYNC_STOREDATA);
2410 lock_ReleaseMutex(&oldDscp->mx);
2413 /* two distinct dir vnodes */
2415 if (oldDscp->fid.cell != newDscp->fid.cell ||
2416 oldDscp->fid.volume != newDscp->fid.volume)
2417 return CM_ERROR_CROSSDEVLINK;
2419 /* shouldn't happen that we have distinct vnodes for two
2420 * different files, but could due to deliberate attack, or
2421 * stale info. Avoid deadlocks and quit now.
2423 if (oldDscp->fid.vnode == newDscp->fid.vnode)
2424 return CM_ERROR_CROSSDEVLINK;
2426 if (oldDscp->fid.vnode < newDscp->fid.vnode) {
2427 lock_ObtainMutex(&oldDscp->mx);
2428 cm_dnlcRemove(oldDscp, oldNamep);
2429 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2430 CM_SCACHESYNC_STOREDATA);
2431 lock_ReleaseMutex(&oldDscp->mx);
2433 lock_ObtainMutex(&newDscp->mx);
2434 cm_dnlcRemove(newDscp, newNamep);
2435 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2436 CM_SCACHESYNC_STOREDATA);
2437 lock_ReleaseMutex(&newDscp->mx);
2439 /* cleanup first one */
2440 cm_SyncOpDone(oldDscp, NULL,
2441 CM_SCACHESYNC_STOREDATA);
2446 /* lock the new vnode entry first */
2447 lock_ObtainMutex(&newDscp->mx);
2448 cm_dnlcRemove(newDscp, newNamep);
2449 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2450 CM_SCACHESYNC_STOREDATA);
2451 lock_ReleaseMutex(&newDscp->mx);
2453 lock_ObtainMutex(&oldDscp->mx);
2454 cm_dnlcRemove(oldDscp, oldNamep);
2455 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2456 CM_SCACHESYNC_STOREDATA);
2457 lock_ReleaseMutex(&oldDscp->mx);
2459 /* cleanup first one */
2460 cm_SyncOpDone(newDscp, NULL,
2461 CM_SCACHESYNC_STOREDATA);
2465 } /* two distinct vnodes */
2472 /* try the RPC now */
2474 code = cm_Conn(&oldDscp->fid, userp, reqp, &connp);
2478 oldDirAFSFid.Volume = oldDscp->fid.volume;
2479 oldDirAFSFid.Vnode = oldDscp->fid.vnode;
2480 oldDirAFSFid.Unique = oldDscp->fid.unique;
2481 newDirAFSFid.Volume = newDscp->fid.volume;
2482 newDirAFSFid.Vnode = newDscp->fid.vnode;
2483 newDirAFSFid.Unique = newDscp->fid.unique;
2485 callp = cm_GetRxConn(connp);
2486 code = RXAFS_Rename(callp, &oldDirAFSFid, oldNamep,
2487 &newDirAFSFid, newNamep,
2488 &updatedOldDirStatus, &updatedNewDirStatus,
2490 rx_PutConnection(callp);
2492 } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
2493 &volSync, NULL, NULL, code));
2494 code = cm_MapRPCError(code, reqp);
2496 /* update the individual stat cache entries for the directories */
2497 lock_ObtainMutex(&oldDscp->mx);
2498 cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
2500 cm_MergeStatus(oldDscp, &updatedOldDirStatus, &volSync,
2503 lock_ReleaseMutex(&oldDscp->mx);
2505 /* and update it for the new one, too, if necessary */
2507 lock_ObtainMutex(&newDscp->mx);
2508 cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
2510 cm_MergeStatus(newDscp, &updatedNewDirStatus, &volSync,
2513 lock_ReleaseMutex(&newDscp->mx);
2516 /* and return error code */
2520 long cm_Lock(cm_scache_t *scp, unsigned char LockType,
2521 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
2522 u_long Timeout, cm_user_t *userp, cm_req_t *reqp,
2526 int Which = ((LockType & 0x1) ? LockRead : LockWrite);
2530 cm_file_lock_t *fileLock;
2533 struct rx_connection * callp;
2535 /* Look for a conflict. Also, if we are asking for a shared lock,
2536 * look for another shared lock, so we don't have to do an RPC.
2540 fileLock = (cm_file_lock_t *)
2541 ((char *) q - offsetof(cm_file_lock_t, fileq));
2542 if ((fileLock->flags &
2543 (CM_FILELOCK_FLAG_INVALID | CM_FILELOCK_FLAG_WAITING))
2545 if ((LockType & 0x1) == 0
2546 || (fileLock->LockType & 0x1) == 0)
2547 return CM_ERROR_WOULDBLOCK;
2556 tfid.Volume = scp->fid.volume;
2557 tfid.Vnode = scp->fid.vnode;
2558 tfid.Unique = scp->fid.unique;
2559 lock_ReleaseMutex(&scp->mx);
2561 code = cm_Conn(&scp->fid, userp, reqp, &connp);
2565 callp = cm_GetRxConn(connp);
2566 code = RXAFS_SetLock(callp, &tfid, Which,
2568 rx_PutConnection(callp);
2570 } while (cm_Analyze(connp, userp, reqp, &scp->fid, &volSync,
2572 lock_ObtainMutex(&scp->mx);
2573 code = cm_MapRPCError(code, reqp);
2576 if (code == 0 || Timeout != 0) {
2577 fileLock = malloc(sizeof(cm_file_lock_t));
2578 fileLock->LockType = LockType;
2580 fileLock->userp = userp;
2581 fileLock->fid = scp->fid;
2582 fileLock->LOffset = LOffset;
2583 fileLock->LLength = LLength;
2584 fileLock->flags = (code == 0 ? 0 : CM_FILELOCK_FLAG_WAITING);
2585 osi_QAdd(&scp->fileLocks, &fileLock->fileq);
2586 lock_ObtainWrite(&cm_scacheLock);
2587 osi_QAdd(&cm_allFileLocks, &fileLock->q);
2588 lock_ReleaseWrite(&cm_scacheLock);
2595 long cm_Unlock(cm_scache_t *scp, unsigned char LockType,
2596 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
2597 cm_user_t *userp, cm_req_t *reqp)
2600 int Which = ((LockType & 0x1) ? LockRead : LockWrite);
2604 cm_file_lock_t *fileLock, *ourLock;
2605 osi_queue_t *q, *qq;
2606 int anotherReader = 0;
2609 struct rx_connection * callp;
2611 if (LargeIntegerLessThan(LLength, scp->length))
2614 /* Look for our own lock on the list, so as to remove it.
2615 * Also, determine if we're the last reader; if not, avoid an RPC.
2619 fileLock = (cm_file_lock_t *)
2620 ((char *) q - offsetof(cm_file_lock_t, fileq));
2622 && fileLock->userp == userp
2623 && LargeIntegerEqualTo(fileLock->LOffset, LOffset)
2624 && LargeIntegerEqualTo(fileLock->LLength, LLength)) {
2629 else if (fileLock->LockType & 0x1)
2634 /* ignore byte ranges */
2635 if (smallLock && !found)
2638 /* don't try to unlock other people's locks */
2640 return CM_ERROR_WOULDBLOCK;
2642 /* discard lock record */
2643 osi_QRemove(&scp->fileLocks, qq);
2645 * Don't delete it here; let the daemon delete it, to simplify
2646 * the daemon's traversal of the list.
2648 lock_ObtainWrite(&cm_scacheLock);
2649 ourLock->flags |= CM_FILELOCK_FLAG_INVALID;
2650 cm_ReleaseUser(ourLock->userp);
2651 lock_ReleaseWrite(&cm_scacheLock);
2653 if (!anotherReader) {
2654 tfid.Volume = scp->fid.volume;
2655 tfid.Vnode = scp->fid.vnode;
2656 tfid.Unique = scp->fid.unique;
2657 lock_ReleaseMutex(&scp->mx);
2659 code = cm_Conn(&scp->fid, userp, reqp, &connp);
2663 callp = cm_GetRxConn(connp);
2664 code = RXAFS_ReleaseLock(callp, &tfid, &volSync);
2665 rx_PutConnection(callp);
2667 } while (cm_Analyze(connp, userp, reqp, &scp->fid, &volSync,
2669 code = cm_MapRPCError(code, reqp);
2670 lock_ObtainMutex(&scp->mx);
2676 void cm_CheckLocks()
2678 osi_queue_t *q, *nq;
2679 cm_file_lock_t *fileLock;
2685 struct rx_connection * callp;
2689 lock_ObtainWrite(&cm_scacheLock);
2690 q = cm_allFileLocks;
2692 fileLock = (cm_file_lock_t *) q;
2694 if (fileLock->flags & CM_FILELOCK_FLAG_INVALID) {
2695 osi_QRemove(&cm_allFileLocks, q);
2698 else if (!(fileLock->flags & CM_FILELOCK_FLAG_WAITING)) {
2699 tfid.Volume = fileLock->fid.volume;
2700 tfid.Vnode = fileLock->fid.vnode;
2701 tfid.Unique = fileLock->fid.unique;
2702 lock_ReleaseWrite(&cm_scacheLock);
2704 code = cm_Conn(&fileLock->fid, fileLock->userp,
2709 callp = cm_GetRxConn(connp);
2710 code = RXAFS_ExtendLock(callp, &tfid,
2712 rx_PutConnection(callp);
2714 } while (cm_Analyze(connp, fileLock->userp, &req,
2715 &fileLock->fid, &volSync, NULL, NULL,
2717 code = cm_MapRPCError(code, &req);
2718 lock_ObtainWrite(&cm_scacheLock);
2722 lock_ReleaseWrite(&cm_scacheLock);
2725 long cm_RetryLock(cm_file_lock_t *oldFileLock, int vcp_is_dead)
2728 int Which = ((oldFileLock->LockType & 0x1) ? LockRead : LockWrite);
2733 cm_file_lock_t *fileLock;
2737 struct rx_connection * callp;
2740 code = CM_ERROR_TIMEDOUT;
2746 /* Look for a conflict. Also, if we are asking for a shared lock,
2747 * look for another shared lock, so we don't have to do an RPC.
2749 code = cm_GetSCache(&oldFileLock->fid, &scp, oldFileLock->userp, &req);
2755 fileLock = (cm_file_lock_t *)
2756 ((char *) q - offsetof(cm_file_lock_t, fileq));
2757 if ((fileLock->flags &
2758 (CM_FILELOCK_FLAG_INVALID | CM_FILELOCK_FLAG_WAITING))
2760 if ((oldFileLock->LockType & 0x1) == 0
2761 || (fileLock->LockType & 0x1) == 0) {
2762 cm_ReleaseSCache(scp);
2763 return CM_ERROR_WOULDBLOCK;
2773 tfid.Volume = oldFileLock->fid.volume;
2774 tfid.Vnode = oldFileLock->fid.vnode;
2775 tfid.Unique = oldFileLock->fid.unique;
2777 code = cm_Conn(&oldFileLock->fid, oldFileLock->userp,
2782 callp = cm_GetRxConn(connp);
2783 code = RXAFS_SetLock(callp, &tfid, Which,
2785 rx_PutConnection(callp);
2787 } while (cm_Analyze(connp, oldFileLock->userp, &req,
2788 &oldFileLock->fid, &volSync,
2790 code = cm_MapRPCError(code, &req);
2794 if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
2795 lock_ObtainMutex(&scp->mx);
2796 osi_QRemove(&scp->fileLocks, &oldFileLock->fileq);
2797 lock_ReleaseMutex(&scp->mx);
2799 lock_ObtainWrite(&cm_scacheLock);
2801 oldFileLock->flags = 0;
2802 else if (code != CM_ERROR_WOULDBLOCK) {
2803 oldFileLock->flags |= CM_FILELOCK_FLAG_INVALID;
2804 cm_ReleaseUser(oldFileLock->userp);
2805 oldFileLock->userp = NULL;
2807 lock_ReleaseWrite(&cm_scacheLock);