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 * If the retscp parameter is not NULL, the parmp must be a
432 * cm_lookupSearch_t object.
434 long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
435 osi_hyper_t *startOffsetp, cm_user_t *userp, cm_req_t *reqp,
436 cm_scache_t **retscp)
443 osi_hyper_t dirLength;
444 osi_hyper_t bufferOffset;
445 osi_hyper_t curOffset;
449 cm_pageHeader_t *pageHeaderp;
451 long nextEntryCookie;
452 int numDirChunks; /* # of 32 byte dir chunks in this entry */
454 /* get the directory size */
455 lock_ObtainMutex(&scp->mx);
456 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_LOOKUP,
457 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
459 lock_ReleaseMutex(&scp->mx);
463 if (scp->fileType != CM_SCACHETYPE_DIRECTORY) {
464 lock_ReleaseMutex(&scp->mx);
465 return CM_ERROR_NOTDIR;
468 if (retscp) /* if this is a lookup call */
470 cm_lookupSearch_t* sp = parmp;
472 #ifdef AFS_FREELANCE_CLIENT
473 /* Freelance entries never end up in the DNLC because they
474 * do not have an associated cm_server_t
476 if ( !(cm_freelanceEnabled &&
477 sp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
478 sp->fid.volume==AFS_FAKE_ROOT_VOL_ID ) )
479 #endif /* AFS_FREELANCE_CLIENT */
481 int casefold = sp->caseFold;
482 sp->caseFold = 0; /* we have a strong preference for exact matches */
483 if ( *retscp = cm_dnlcLookup(scp, sp)) /* dnlc hit */
485 sp->caseFold = casefold;
486 lock_ReleaseMutex(&scp->mx);
489 sp->caseFold = casefold;
494 * XXX We only get the length once. It might change when we drop the
497 dirLength = scp->length;
499 lock_ReleaseMutex(&scp->mx);
502 bufferOffset.LowPart = bufferOffset.HighPart = 0;
504 curOffset = *startOffsetp;
506 curOffset.HighPart = 0;
507 curOffset.LowPart = 0;
511 /* make sure that curOffset.LowPart doesn't point to the first
512 * 32 bytes in the 2nd through last dir page, and that it
513 * doesn't point at the first 13 32-byte chunks in the first
514 * dir page, since those are dir and page headers, and don't
515 * contain useful information.
517 temp = curOffset.LowPart & (2048-1);
518 if (curOffset.HighPart == 0 && curOffset.LowPart < 2048) {
519 /* we're in the first page */
520 if (temp < 13*32) temp = 13*32;
523 /* we're in a later dir page */
524 if (temp < 32) temp = 32;
527 /* make sure the low order 5 bits are zero */
530 /* now put temp bits back ito curOffset.LowPart */
531 curOffset.LowPart &= ~(2048-1);
532 curOffset.LowPart |= temp;
534 /* check if we've passed the dir's EOF */
535 if (LargeIntegerGreaterThanOrEqualTo(curOffset, dirLength))
538 /* see if we can use the bufferp we have now; compute in which
539 * page the current offset would be, and check whether that's
540 * the offset of the buffer we have. If not, get the buffer.
542 thyper.HighPart = curOffset.HighPart;
543 thyper.LowPart = curOffset.LowPart & ~(buf_bufferSize-1);
544 if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
547 lock_ReleaseMutex(&bufferp->mx);
548 buf_Release(bufferp);
552 lock_ObtainRead(&scp->bufCreateLock);
553 code = buf_Get(scp, &thyper, &bufferp);
554 lock_ReleaseRead(&scp->bufCreateLock);
556 /* if buf_Get() fails we do not have a buffer object to lock */
561 lock_ObtainMutex(&bufferp->mx);
562 bufferOffset = thyper;
564 /* now get the data in the cache */
566 lock_ObtainMutex(&scp->mx);
567 code = cm_SyncOp(scp, bufferp, userp, reqp,
569 CM_SCACHESYNC_NEEDCALLBACK
571 | CM_SCACHESYNC_BUFLOCKED);
573 lock_ReleaseMutex(&scp->mx);
577 if (cm_HaveBuffer(scp, bufferp, 1)) {
578 lock_ReleaseMutex(&scp->mx);
582 /* otherwise, load the buffer and try again */
583 lock_ReleaseMutex(&bufferp->mx);
584 code = cm_GetBuffer(scp, bufferp, NULL, userp,
586 lock_ReleaseMutex(&scp->mx);
587 lock_ObtainMutex(&bufferp->mx);
592 lock_ReleaseMutex(&bufferp->mx);
593 buf_Release(bufferp);
597 } /* if (wrong buffer) ... */
599 /* now we have the buffer containing the entry we're interested
600 * in; copy it out if it represents a non-deleted entry.
602 entryInDir = curOffset.LowPart & (2048-1);
603 entryInBuffer = curOffset.LowPart & (buf_bufferSize - 1);
605 /* page header will help tell us which entries are free. Page
606 * header can change more often than once per buffer, since
607 * AFS 3 dir page size may be less than (but not more than) a
608 * buffer package buffer.
610 /* only look intra-buffer */
611 temp = curOffset.LowPart & (buf_bufferSize - 1);
612 temp &= ~(2048 - 1); /* turn off intra-page bits */
613 pageHeaderp = (cm_pageHeader_t *) (bufferp->datap + temp);
615 /* now determine which entry we're looking at in the page. If
616 * it is free (there's a free bitmap at the start of the dir),
617 * we should skip these 32 bytes.
619 slotInPage = (entryInDir & 0x7e0) >> 5;
620 if (!(pageHeaderp->freeBitmap[slotInPage>>3]
621 & (1 << (slotInPage & 0x7)))) {
622 /* this entry is free */
623 numDirChunks = 1; /* only skip this guy */
627 tp = bufferp->datap + entryInBuffer;
628 dep = (cm_dirEntry_t *) tp; /* now points to AFS3 dir entry */
630 /* while we're here, compute the next entry's location, too,
631 * since we'll need it when writing out the cookie into the
632 * dir listing stream.
634 numDirChunks = cm_NameEntries(dep->name, NULL);
636 /* compute the offset of the cookie representing the next entry */
637 nextEntryCookie = curOffset.LowPart
638 + (CM_DIR_CHUNKSIZE * numDirChunks);
640 if (dep->fid.vnode != 0) {
641 /* this is one of the entries to use: it is not deleted */
642 code = (*funcp)(scp, dep, parmp, &curOffset);
645 } /* if we're including this name */
648 /* and adjust curOffset to be where the new cookie is */
650 thyper.LowPart = CM_DIR_CHUNKSIZE * numDirChunks;
651 curOffset = LargeIntegerAdd(thyper, curOffset);
652 } /* while copying data for dir listing */
654 /* release the mutex */
656 lock_ReleaseMutex(&bufferp->mx);
657 buf_Release(bufferp);
662 int cm_NoneUpper(char *s)
666 if (c >= 'A' && c <= 'Z')
671 int cm_NoneLower(char *s)
675 if (c >= 'a' && c <= 'z')
680 long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
683 cm_lookupSearch_t *sp;
688 sp = (cm_lookupSearch_t *) rockp;
690 matchName = dep->name;
692 match = cm_stricmp(matchName, sp->searchNamep);
694 match = strcmp(matchName, sp->searchNamep);
698 && !cm_Is8Dot3(dep->name)) {
699 matchName = shortName;
700 cm_Gen8Dot3Name(dep, shortName, NULL);
702 match = cm_stricmp(matchName, sp->searchNamep);
704 match = strcmp(matchName, sp->searchNamep);
714 if (!sp->caseFold || matchName == shortName) {
715 sp->fid.vnode = ntohl(dep->fid.vnode);
716 sp->fid.unique = ntohl(dep->fid.unique);
717 return CM_ERROR_STOPNOW;
721 * If we get here, we are doing a case-insensitive search, and we
722 * have found a match. Now we determine what kind of match it is:
723 * exact, lower-case, upper-case, or none of the above. This is done
724 * in order to choose among matches, if there are more than one.
727 /* Exact matches are the best. */
728 match = strcmp(matchName, sp->searchNamep);
731 sp->fid.vnode = ntohl(dep->fid.vnode);
732 sp->fid.unique = ntohl(dep->fid.unique);
733 return CM_ERROR_STOPNOW;
736 /* Lower-case matches are next. */
739 if (cm_NoneUpper(matchName)) {
744 /* Upper-case matches are next. */
747 if (cm_NoneLower(matchName)) {
752 /* General matches are last. */
758 sp->fid.vnode = ntohl(dep->fid.vnode);
759 sp->fid.unique = ntohl(dep->fid.unique);
763 /* read the contents of a mount point into the appropriate string.
764 * called with locked scp, and returns with locked scp.
766 long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
773 if (scp->mountPointStringp)
776 /* otherwise, we have to read it in */
777 lock_ReleaseMutex(&scp->mx);
779 lock_ObtainRead(&scp->bufCreateLock);
780 thyper.LowPart = thyper.HighPart = 0;
781 code = buf_Get(scp, &thyper, &bufp);
782 lock_ReleaseRead(&scp->bufCreateLock);
784 lock_ObtainMutex(&scp->mx);
789 code = cm_SyncOp(scp, bufp, userp, reqp, 0,
790 CM_SCACHESYNC_READ | CM_SCACHESYNC_NEEDCALLBACK);
795 if (cm_HaveBuffer(scp, bufp, 0))
798 /* otherwise load buffer */
799 code = cm_GetBuffer(scp, bufp, NULL, userp, reqp);
804 /* locked, has callback, has valid data in buffer */
805 if ((tlen = scp->length.LowPart) > 1000)
806 return CM_ERROR_TOOBIG;
808 code = CM_ERROR_INVAL;
812 /* someone else did the work while we were out */
813 if (scp->mountPointStringp) {
818 /* otherwise, copy out the link */
819 scp->mountPointStringp = malloc(tlen);
820 memcpy(scp->mountPointStringp, bufp->datap, tlen);
822 /* now make it null-terminated. Note that the original contents of a
823 * link that is a mount point is "#volname." where "." is there just to
824 * be turned into a null. That is, we can trash the last char of the
825 * link without damaging the vol name. This is a stupid convention,
826 * but that's the protocol.
828 scp->mountPointStringp[tlen-1] = 0;
837 /* called with a locked scp and chases the mount point, yielding outScpp.
838 * scp remains locked, just for simplicity of describing the interface.
840 long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
841 cm_req_t *reqp, cm_scache_t **outScpp)
856 if (scp->mountRootFidp && scp->mountRootGen >= cm_mountRootGen) {
857 tfid = *scp->mountRootFidp;
858 lock_ReleaseMutex(&scp->mx);
859 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
860 lock_ObtainMutex(&scp->mx);
864 /* parse the volume name */
865 mpNamep = scp->mountPointStringp;
867 tlen = strlen(scp->mountPointStringp);
868 mtType = *scp->mountPointStringp;
869 cellNamep = malloc(tlen);
870 volNamep = malloc(tlen);
872 cp = strrchr(mpNamep, ':');
874 /* cellular mount point */
875 memset(cellNamep, 0, tlen);
876 strncpy(cellNamep, mpNamep+1, cp - mpNamep - 1);
877 strcpy(volNamep, cp+1);
878 /* now look up the cell */
879 cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE);
883 strcpy(volNamep, mpNamep+1);
885 cellp = cm_FindCellByID(scp->fid.cell);
889 code = CM_ERROR_NOSUCHCELL;
893 vnLength = strlen(volNamep);
894 if (vnLength >= 8 && strcmp(volNamep + vnLength - 7, ".backup") == 0)
896 else if (vnLength >= 10
897 && strcmp(volNamep + vnLength - 9, ".readonly") == 0)
902 /* check for backups within backups */
904 && (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
905 == CM_SCACHEFLAG_RO) {
906 code = CM_ERROR_NOSUCHVOLUME;
910 /* now we need to get the volume */
911 lock_ReleaseMutex(&scp->mx);
912 code = cm_GetVolumeByName(cellp, volNamep, userp, reqp, 0, &volp);
913 lock_ObtainMutex(&scp->mx);
916 /* save the parent of the volume root for this is the
917 * place where the volume is mounted and we must remember
918 * this in the volume structure rather than just in the
919 * scache entry lest the scache entry gets recycled
922 lock_ObtainMutex(&volp->mx);
923 if(volp->dotdotFidp == (cm_fid_t *) NULL)
924 volp->dotdotFidp = (cm_fid_t *) malloc(sizeof(cm_fid_t));
925 *(volp->dotdotFidp) = dscp->fid;
926 lock_ReleaseMutex(&volp->mx);
928 if (scp->mountRootFidp == 0) {
929 scp->mountRootFidp = malloc(sizeof(cm_fid_t));
931 scp->mountRootFidp->cell = cellp->cellID;
932 /* if the mt pt is in a read-only volume (not just a
933 * backup), and if there is a read-only volume for the
934 * target, and if this is a type '#' mount point, use
935 * the read-only, otherwise use the one specified.
937 if (mtType == '#' && (scp->flags & CM_SCACHEFLAG_PURERO)
938 && volp->roID != 0 && type == RWVOL)
941 scp->mountRootFidp->volume = volp->roID;
942 else if (type == BACKVOL)
943 scp->mountRootFidp->volume = volp->bkID;
945 scp->mountRootFidp->volume = volp->rwID;
947 /* the rest of the fid is a magic number */
948 scp->mountRootFidp->vnode = 1;
949 scp->mountRootFidp->unique = 1;
950 scp->mountRootGen = cm_mountRootGen;
952 tfid = *scp->mountRootFidp;
953 lock_ReleaseMutex(&scp->mx);
954 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
955 lock_ObtainMutex(&scp->mx);
964 long cm_LookupInternal(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
965 cm_req_t *reqp, cm_scache_t **outpScpp)
968 int dnlcHit = 1; /* did we hit in the dnlc? yes, we did */
969 cm_scache_t *tscp = NULL;
970 cm_scache_t *mountedScp;
971 cm_lookupSearch_t rock;
974 if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
975 && strcmp(namep, "..") == 0) {
976 if (dscp->dotdotFidp == (cm_fid_t *)NULL
977 || dscp->dotdotFidp->volume == 0)
978 return CM_ERROR_NOSUCHVOLUME;
979 rock.fid = *dscp->dotdotFidp;
983 memset(&rock, 0, sizeof(rock));
984 rock.fid.cell = dscp->fid.cell;
985 rock.fid.volume = dscp->fid.volume;
986 rock.searchNamep = namep;
987 rock.caseFold = (flags & CM_FLAG_CASEFOLD);
988 rock.hasTilde = ((strchr(namep, '~') != NULL) ? 1 : 0);
990 /* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
991 code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
992 (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
994 /* code == 0 means we fell off the end of the dir, while stopnow means
995 * that we stopped early, probably because we found the entry we're
996 * looking for. Any other non-zero code is an error.
998 if (code && code != CM_ERROR_STOPNOW)
1001 getroot = (dscp==cm_rootSCachep) ;
1003 if (!cm_freelanceEnabled || !getroot) {
1004 if (flags & CM_FLAG_CHECKPATH)
1005 return CM_ERROR_NOSUCHPATH;
1007 return CM_ERROR_NOSUCHFILE;
1009 else { /* nonexistent dir on freelance root, so add it */
1010 char fullname[200] = ".";
1013 osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %s",
1014 osi_LogSaveString(afsd_logp,namep));
1015 if (namep[0] == '.') {
1016 if (cm_GetCell_Gen(&namep[1], &fullname[1], CM_FLAG_CREATE)) {
1018 if ( stricmp(&namep[1], &fullname[1]) )
1019 code = cm_FreelanceAddSymlink(namep, fullname, &rock.fid);
1021 code = cm_FreelanceAddMount(namep, &fullname[1], "root.cell.", 1, &rock.fid);
1024 if (cm_GetCell_Gen(namep, fullname, CM_FLAG_CREATE)) {
1026 if ( stricmp(namep, fullname) )
1027 code = cm_FreelanceAddSymlink(namep, fullname, &rock.fid);
1029 code = cm_FreelanceAddMount(namep, fullname, "root.cell.", 0, &rock.fid);
1032 if (!found || code < 0) { /* add mount point failed, so give up */
1033 if (flags & CM_FLAG_CHECKPATH)
1034 return CM_ERROR_NOSUCHPATH;
1036 return CM_ERROR_NOSUCHFILE;
1038 tscp = NULL; /* to force call of cm_GetSCache */
1043 if ( !tscp ) /* we did not find it in the dnlc */
1046 code = cm_GetSCache(&rock.fid, &tscp, userp, reqp);
1050 /* tscp is now held */
1052 lock_ObtainMutex(&tscp->mx);
1053 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1054 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1056 lock_ReleaseMutex(&tscp->mx);
1057 cm_ReleaseSCache(tscp);
1060 /* tscp is now locked */
1062 if (!(flags & CM_FLAG_NOMOUNTCHASE)
1063 && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
1064 /* mount points are funny: they have a volume name to mount
1067 code = cm_ReadMountPoint(tscp, userp, reqp);
1069 code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
1071 lock_ReleaseMutex(&tscp->mx);
1072 cm_ReleaseSCache(tscp);
1079 lock_ReleaseMutex(&tscp->mx);
1082 /* copy back pointer */
1085 /* insert scache in dnlc */
1086 if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
1087 /* lock the directory entry to prevent racing callback revokes */
1088 lock_ObtainMutex(&dscp->mx);
1089 if ( dscp->cbServerp && dscp->cbExpires )
1090 cm_dnlcEnter(dscp, namep, tscp);
1091 lock_ReleaseMutex(&dscp->mx);
1098 int cm_ExpandSysName(char *inp, char *outp, long outSize, unsigned int index)
1103 tp = strrchr(inp, '@');
1105 return 0; /* no @sys */
1107 if (strcmp(tp, "@sys") != 0)
1108 return 0; /* no @sys */
1110 /* caller just wants to know if this is a valid @sys type of name */
1114 if (index >= MAXNUMSYSNAMES)
1117 /* otherwise generate the properly expanded @sys name */
1118 prefixCount = tp - inp;
1120 strncpy(outp, inp, prefixCount); /* copy out "a." from "a.@sys" */
1121 outp[prefixCount] = 0; /* null terminate the "a." */
1122 strcat(outp, cm_sysNameList[index]);/* append i386_nt40 */
1126 long cm_Lookup(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
1127 cm_req_t *reqp, cm_scache_t **outpScpp)
1131 int sysNameIndex = 0;
1132 cm_scache_t *scp = 0;
1134 if ( stricmp(namep,SMB_IOCTL_FILENAME_NOSLASH) == 0 ) {
1135 if (flags & CM_FLAG_CHECKPATH)
1136 return CM_ERROR_NOSUCHPATH;
1138 return CM_ERROR_NOSUCHFILE;
1141 for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
1142 code = cm_ExpandSysName(namep, tname, sizeof(tname), sysNameIndex);
1144 code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
1150 cm_ReleaseSCache(scp);
1154 return cm_LookupInternal(dscp, namep, flags, userp, reqp, outpScpp);
1158 /* None of the possible sysName expansions could be found */
1159 if (flags & CM_FLAG_CHECKPATH)
1160 return CM_ERROR_NOSUCHPATH;
1162 return CM_ERROR_NOSUCHFILE;
1165 long cm_Unlink(cm_scache_t *dscp, char *namep, cm_user_t *userp, cm_req_t *reqp)
1171 AFSFetchStatus newDirStatus;
1173 struct rx_connection * callp;
1175 #ifdef AFS_FREELANCE_CLIENT
1176 if (cm_freelanceEnabled && dscp == cm_rootSCachep) {
1177 /* deleting a mount point from the root dir. */
1178 code = cm_FreelanceRemoveMount(namep);
1183 /* make sure we don't screw up the dir status during the merge */
1184 lock_ObtainMutex(&dscp->mx);
1185 sflags = CM_SCACHESYNC_STOREDATA;
1186 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1187 lock_ReleaseMutex(&dscp->mx);
1192 afsFid.Volume = dscp->fid.volume;
1193 afsFid.Vnode = dscp->fid.vnode;
1194 afsFid.Unique = dscp->fid.unique;
1196 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1200 callp = cm_GetRxConn(connp);
1201 code = RXAFS_RemoveFile(callp, &afsFid, namep,
1202 &newDirStatus, &volSync);
1203 rx_PutConnection(callp);
1205 } while (cm_Analyze(connp, userp, reqp, &dscp->fid, &volSync, NULL, NULL, code));
1206 code = cm_MapRPCError(code, reqp);
1208 lock_ObtainMutex(&dscp->mx);
1209 cm_dnlcRemove(dscp, namep);
1210 cm_SyncOpDone(dscp, NULL, sflags);
1212 cm_MergeStatus(dscp, &newDirStatus, &volSync, userp, 0);
1213 lock_ReleaseMutex(&dscp->mx);
1218 /* called with a locked vnode, and fills in the link info.
1219 * returns this the vnode still locked.
1221 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1228 lock_AssertMutex(&linkScp->mx);
1229 if (!linkScp->mountPointStringp) {
1230 /* read the link data */
1231 lock_ReleaseMutex(&linkScp->mx);
1232 thyper.LowPart = thyper.HighPart = 0;
1233 code = buf_Get(linkScp, &thyper, &bufp);
1234 lock_ObtainMutex(&linkScp->mx);
1238 code = cm_SyncOp(linkScp, bufp, userp, reqp, 0,
1239 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1244 if (cm_HaveBuffer(linkScp, bufp, 0))
1247 code = cm_GetBuffer(linkScp, bufp, NULL, userp, reqp);
1252 } /* while loop to get the data */
1254 /* now if we still have no link read in,
1255 * copy the data from the buffer */
1256 if ((temp = linkScp->length.LowPart) >= 1024) {
1258 return CM_ERROR_TOOBIG;
1261 /* otherwise, it fits; make sure it is still null (could have
1262 * lost race with someone else referencing this link above),
1263 * and if so, copy in the data.
1265 if (linkScp->mountPointStringp == NULL) {
1266 linkScp->mountPointStringp = malloc(temp+1);
1267 strncpy(linkScp->mountPointStringp, bufp->datap, temp);
1268 linkScp->mountPointStringp[temp] = 0; /* null terminate */
1271 } /* don't have sym link contents cached */
1276 /* called with a held vnode and a path suffix, with the held vnode being a
1277 * symbolic link. Our goal is to generate a new path to interpret, and return
1278 * this new path in newSpaceBufferp. If the new vnode is relative to a dir
1279 * other than the directory containing the symbolic link, then the new root is
1280 * returned in *newRootScpp, otherwise a null is returned there.
1282 long cm_AssembleLink(cm_scache_t *linkScp, char *pathSuffixp,
1283 cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1284 cm_user_t *userp, cm_req_t *reqp)
1290 lock_ObtainMutex(&linkScp->mx);
1291 code = cm_HandleLink(linkScp, userp, reqp);
1295 /* if we may overflow the buffer, bail out; buffer is signficantly
1296 * bigger than max path length, so we don't really have to worry about
1297 * being a little conservative here.
1299 if (strlen(linkScp->mountPointStringp) + strlen(pathSuffixp) + 2
1300 >= CM_UTILS_SPACESIZE)
1301 return CM_ERROR_TOOBIG;
1303 tsp = cm_GetSpace();
1304 linkp = linkScp->mountPointStringp;
1305 if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1306 if (strlen(linkp) > cm_mountRootLen)
1307 strcpy(tsp->data, linkp+cm_mountRootLen+1);
1310 *newRootScpp = cm_rootSCachep;
1311 cm_HoldSCache(cm_rootSCachep);
1312 } else if (*linkp == '\\' || *linkp == '/') {
1313 /* formerly, this was considered to be from the AFS root,
1314 * but this seems to create problems. instead, we will just
1315 * reject the link */
1317 strcpy(tsp->data, linkp+1);
1318 *newRootScpp = cm_rootSCachep;
1319 cm_HoldSCache(cm_rootSCachep);
1321 code = CM_ERROR_NOSUCHPATH;
1326 /* a relative link */
1327 strcpy(tsp->data, linkp);
1328 *newRootScpp = NULL;
1330 if (pathSuffixp[0] != 0) { /* if suffix string is non-null */
1331 strcat(tsp->data, "\\");
1332 strcat(tsp->data, pathSuffixp);
1334 *newSpaceBufferp = tsp;
1338 lock_ReleaseMutex(&linkScp->mx);
1342 long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
1343 cm_user_t *userp, char *tidPathp, cm_req_t *reqp, cm_scache_t **outScpp)
1346 char *tp; /* ptr moving through input buffer */
1347 char tc; /* temp char */
1348 int haveComponent; /* has new component started? */
1349 char component[256]; /* this is the new component */
1350 char *cp; /* component name being assembled */
1351 cm_scache_t *tscp; /* current location in the hierarchy */
1352 cm_scache_t *nscp; /* next dude down */
1353 cm_scache_t *dirScp; /* last dir we searched */
1354 cm_scache_t *linkScp; /* new root for the symlink we just
1356 cm_space_t *psp; /* space for current path, if we've hit
1358 cm_space_t *tempsp; /* temp vbl */
1359 char *restp; /* rest of the pathname to interpret */
1360 int symlinkCount; /* count of # of symlinks traversed */
1361 int extraFlag; /* avoid chasing mt pts for dir cmd */
1362 int phase = 1; /* 1 = tidPathp, 2 = pathp */
1375 cm_HoldSCache(tscp);
1380 /* map Unix slashes into DOS ones so we can interpret Unix
1386 if (!haveComponent) {
1405 /* we have a component here */
1406 if (tc == 0 || tc == '\\') {
1407 /* end of the component; we're at the last
1408 * component if tc == 0. However, if the last
1409 * is a symlink, we have more to do.
1411 *cp++ = 0; /* add null termination */
1413 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
1414 extraFlag = CM_FLAG_NOMOUNTCHASE;
1415 code = cm_Lookup(tscp, component,
1417 userp, reqp, &nscp);
1420 cm_ReleaseSCache(tscp);
1425 haveComponent = 0; /* component done */
1426 dirScp = tscp; /* for some symlinks */
1427 tscp = nscp; /* already held */
1428 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
1430 cm_ReleaseSCache(dirScp);
1434 /* now, if tscp is a symlink, we should follow
1435 * it and assemble the path again.
1437 lock_ObtainMutex(&tscp->mx);
1438 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1439 CM_SCACHESYNC_GETSTATUS
1440 | CM_SCACHESYNC_NEEDCALLBACK);
1442 lock_ReleaseMutex(&tscp->mx);
1443 cm_ReleaseSCache(tscp);
1444 cm_ReleaseSCache(dirScp);
1447 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
1448 /* this is a symlink; assemble a new buffer */
1449 lock_ReleaseMutex(&tscp->mx);
1450 if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
1451 cm_ReleaseSCache(tscp);
1452 cm_ReleaseSCache(dirScp);
1455 return CM_ERROR_TOO_MANY_SYMLINKS;
1461 code = cm_AssembleLink(tscp, restp, &linkScp, &tempsp, userp, reqp);
1463 /* something went wrong */
1464 cm_ReleaseSCache(tscp);
1465 cm_ReleaseSCache(dirScp);
1469 /* otherwise, tempsp has the new path,
1470 * and linkScp is the new root from
1471 * which to interpret that path.
1472 * Continue with the namei processing,
1473 * also doing the bookkeeping for the
1474 * space allocation and tracking the
1475 * vnode reference counts.
1481 cm_ReleaseSCache(tscp);
1485 * now, if linkScp is null, that's
1486 * AssembleLink's way of telling us that
1487 * the sym link is relative to the dir
1488 * containing the link. We have a ref
1489 * to it in dirScp, and we hold it now
1490 * and reuse it as the new spot in the
1494 cm_HoldSCache(dirScp);
1497 } /* if we have a sym link */
1499 /* not a symlink, we may be done */
1500 lock_ReleaseMutex(&tscp->mx);
1507 cm_ReleaseSCache(dirScp);
1512 cm_ReleaseSCache(dirScp);
1513 } /* end of a component */
1515 } /* we have a component */
1516 } /* big while loop over all components */
1526 /* called with a dir, and a vnode within the dir that happens to be a symlink.
1527 * We chase the link, and return a held pointer to the target, if it exists,
1528 * in *outScpp. If we succeed, we return 0, otherwise we return an error code
1529 * and do not hold or return a target vnode.
1531 * This is very similar to calling cm_NameI with the last component of a name,
1532 * which happens to be a symlink, except that we've already passed by the name.
1534 * This function is typically called by the directory listing functions, which
1535 * encounter symlinks but need to return the proper file length so programs
1536 * like "more" work properly when they make use of the attributes retrieved from
1539 * The input vnode should not be locked when this function is called.
1541 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
1542 cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
1546 cm_scache_t *newRootScp;
1548 osi_Log1(afsd_logp, "Evaluating symlink vp %x", linkScp);
1550 code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
1554 /* now, if newRootScp is NULL, we're really being told that the symlink
1555 * is relative to the current directory (dscp).
1557 if (newRootScp == NULL) {
1559 cm_HoldSCache(dscp);
1562 code = cm_NameI(newRootScp, spacep->data,
1563 CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
1564 userp, NULL, reqp, outScpp);
1566 /* this stuff is allocated no matter what happened on the namei call,
1568 cm_FreeSpace(spacep);
1569 cm_ReleaseSCache(newRootScp);
1574 /* make this big enough so that one buffer of dir pages won't overflow. We'll
1575 * check anyway, but we want to minimize the chance that we have to leave stuff
1578 #define CM_BULKMAX 128
1580 /* rock for bulk stat calls */
1581 typedef struct cm_bulkStat {
1582 osi_hyper_t bufOffset; /* only do it for things in this buffer page */
1584 /* info for the actual call */
1585 int counter; /* next free slot */
1586 AFSFid fids[CM_BULKMAX];
1587 AFSFetchStatus stats[CM_BULKMAX];
1588 AFSCallBack callbacks[CM_BULKMAX];
1591 /* for a given entry, make sure that it isn't in the stat cache, and then
1592 * add it to the list of file IDs to be obtained.
1594 * Don't bother adding it if we already have a vnode. Note that the dir
1595 * is locked, so we have to be careful checking the vnode we're thinking of
1596 * processing, to avoid deadlocks.
1598 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
1609 /* Don't overflow bsp. */
1610 if (bsp->counter >= CM_BULKMAX)
1611 return CM_ERROR_STOPNOW;
1613 thyper.LowPart = buf_bufferSize;
1614 thyper.HighPart = 0;
1615 thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
1617 /* thyper is now the first byte past the end of the record we're
1618 * interested in, and bsp->bufOffset is the first byte of the record
1619 * we're interested in.
1620 * Skip data in the others.
1623 if (LargeIntegerLessThan(*offp, bsp->bufOffset))
1625 if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
1626 return CM_ERROR_STOPNOW;
1627 if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
1630 tfid.cell = scp->fid.cell;
1631 tfid.volume = scp->fid.volume;
1632 tfid.vnode = ntohl(dep->fid.vnode);
1633 tfid.unique = ntohl(dep->fid.unique);
1634 tscp = cm_FindSCache(&tfid);
1636 if (lock_TryMutex(&tscp->mx)) {
1637 /* we have an entry that we can look at */
1638 if (cm_HaveCallback(tscp)) {
1639 /* we have a callback on it. Don't bother
1640 * fetching this stat entry, since we're happy
1641 * with the info we have.
1643 lock_ReleaseMutex(&tscp->mx);
1644 cm_ReleaseSCache(tscp);
1647 lock_ReleaseMutex(&tscp->mx);
1649 cm_ReleaseSCache(tscp);
1652 #ifdef AFS_FREELANCE_CLIENT
1653 // yj: if this is a mountpoint under root.afs then we don't want it
1654 // to be bulkstat-ed, instead, we call getSCache directly and under
1655 // getSCache, it is handled specially.
1656 if ( cm_freelanceEnabled &&
1657 tfid.cell==AFS_FAKE_ROOT_CELL_ID &&
1658 tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
1659 !(tfid.vnode==0x1 && tfid.unique==0x1) )
1661 osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
1662 return cm_GetSCache(&tfid, &tscp, NULL, NULL);
1664 #endif /* AFS_FREELANCE_CLIENT */
1667 bsp->fids[i].Volume = scp->fid.volume;
1668 bsp->fids[i].Vnode = tfid.vnode;
1669 bsp->fids[i].Unique = tfid.unique;
1673 /* called with a locked scp and a pointer to a buffer. Make bulk stat
1674 * calls on all undeleted files in the page of the directory specified.
1676 void cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
1680 cm_bulkStat_t bb; /* this is *BIG*, probably 12K or so;
1681 * watch for stack problems */
1682 AFSCBFids fidStruct;
1683 AFSBulkStats statStruct;
1685 AFSCBs callbackStruct;
1688 cm_callbackRequest_t cbReq;
1694 struct rx_connection * callp;
1696 osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%x", (long) dscp);
1698 /* should be on a buffer boundary */
1699 osi_assert((offsetp->LowPart & (buf_bufferSize - 1)) == 0);
1702 bb.bufOffset = *offsetp;
1704 lock_ReleaseMutex(&dscp->mx);
1705 /* first, assemble the file IDs we need to stat */
1706 code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) &bb, offsetp, userp, reqp, NULL);
1707 lock_ObtainMutex(&dscp->mx);
1709 /* if we failed, bail out early */
1710 if (code && code != CM_ERROR_STOPNOW) return;
1712 /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
1713 * make the calls to create the entries. Handle AFSCBMAX files at a
1717 while (filex < bb.counter) {
1718 filesThisCall = bb.counter - filex;
1719 if (filesThisCall > AFSCBMAX) filesThisCall = AFSCBMAX;
1721 fidStruct.AFSCBFids_len = filesThisCall;
1722 fidStruct.AFSCBFids_val = &bb.fids[filex];
1723 statStruct.AFSBulkStats_len = filesThisCall;
1724 statStruct.AFSBulkStats_val = &bb.stats[filex];
1725 callbackStruct.AFSCBs_len = filesThisCall;
1726 callbackStruct.AFSCBs_val = &bb.callbacks[filex];
1727 cm_StartCallbackGrantingCall(NULL, &cbReq);
1728 osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
1730 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1734 callp = cm_GetRxConn(connp);
1735 code = RXAFS_BulkStatus(callp, &fidStruct,
1736 &statStruct, &callbackStruct, &volSync);
1737 rx_PutConnection(callp);
1739 } while (cm_Analyze(connp, userp, reqp, &dscp->fid,
1740 &volSync, NULL, &cbReq, code));
1741 code = cm_MapRPCError(code, reqp);
1743 osi_Log0(afsd_logp, "CALL BulkStatus DONE");
1745 /* may as well quit on an error, since we're not going to do
1746 * much better on the next immediate call, either.
1751 /* otherwise, we should do the merges */
1752 for(i = 0; i<filesThisCall; i++) {
1754 tfid.cell = dscp->fid.cell;
1755 tfid.volume = bb.fids[j].Volume;
1756 tfid.vnode = bb.fids[j].Vnode;
1757 tfid.unique = bb.fids[j].Unique;
1758 code = cm_GetSCache(&tfid, &scp, userp, reqp);
1762 /* otherwise, if this entry has no callback info,
1765 lock_ObtainMutex(&scp->mx);
1766 /* now, we have to be extra paranoid on merging in this
1767 * information, since we didn't use cm_SyncOp before
1768 * starting the fetch to make sure that no bad races
1769 * were occurring. Specifically, we need to make sure
1770 * we don't obliterate any newer information in the
1771 * vnode than have here.
1773 * Right now, be pretty conservative: if there's a
1774 * callback or a pending call, skip it.
1776 if (scp->cbServerp == NULL
1778 (CM_SCACHEFLAG_FETCHING
1779 | CM_SCACHEFLAG_STORING
1780 | CM_SCACHEFLAG_SIZESTORING))) {
1781 cm_EndCallbackGrantingCall(scp, &cbReq,
1783 CM_CALLBACK_MAINTAINCOUNT);
1784 cm_MergeStatus(scp, &bb.stats[j], &volSync,
1787 lock_ReleaseMutex(&scp->mx);
1788 cm_ReleaseSCache(scp);
1789 } /* all files in the response */
1790 /* now tell it to drop the count,
1791 * after doing the vnode processing above */
1792 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
1794 filex += filesThisCall;
1795 } /* while there are still more files to process */
1796 osi_Log0(afsd_logp, "END cm_TryBulkStat");
1799 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
1803 /* initialize store back mask as inexpensive local variable */
1805 memset(statusp, 0, sizeof(AFSStoreStatus));
1807 /* copy out queued info from scache first, if scp passed in */
1809 if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
1810 statusp->ClientModTime = scp->clientModTime;
1811 mask |= AFS_SETMODTIME;
1812 scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
1817 /* now add in our locally generated request */
1818 if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
1819 statusp->ClientModTime = attrp->clientModTime;
1820 mask |= AFS_SETMODTIME;
1822 if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
1823 statusp->UnixModeBits = attrp->unixModeBits;
1824 mask |= AFS_SETMODE;
1826 if (attrp->mask & CM_ATTRMASK_OWNER) {
1827 statusp->Owner = attrp->owner;
1828 mask |= AFS_SETOWNER;
1830 if (attrp->mask & CM_ATTRMASK_GROUP) {
1831 statusp->Group = attrp->group;
1832 mask |= AFS_SETGROUP;
1835 statusp->Mask = mask;
1838 /* set the file size, and make sure that all relevant buffers have been
1839 * truncated. Ensure that any partially truncated buffers have been zeroed
1840 * to the end of the buffer.
1842 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
1848 /* start by locking out buffer creation */
1849 lock_ObtainWrite(&scp->bufCreateLock);
1851 /* verify that this is a file, not a dir or a symlink */
1852 lock_ObtainMutex(&scp->mx);
1853 code = cm_SyncOp(scp, NULL, userp, reqp, 0,
1854 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1858 if (scp->fileType != CM_SCACHETYPE_FILE) {
1859 code = CM_ERROR_ISDIR;
1864 if (LargeIntegerLessThan(*sizep, scp->length))
1869 lock_ReleaseMutex(&scp->mx);
1871 /* can't hold scp->mx lock here, since we may wait for a storeback to
1872 * finish if the buffer package is cleaning a buffer by storing it to
1876 buf_Truncate(scp, userp, reqp, sizep);
1878 /* now ensure that file length is short enough, and update truncPos */
1879 lock_ObtainMutex(&scp->mx);
1881 /* make sure we have a callback (so we have the right value for the
1882 * length), and wait for it to be safe to do a truncate.
1884 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
1885 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
1886 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
1890 if (LargeIntegerLessThan(*sizep, scp->length)) {
1891 /* a real truncation. If truncPos is not set yet, or is bigger
1892 * than where we're truncating the file, set truncPos to this
1897 if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
1898 || LargeIntegerLessThan(*sizep, scp->length)) {
1900 scp->truncPos = *sizep;
1901 scp->mask |= CM_SCACHEMASK_TRUNCPOS;
1903 /* in either case, the new file size has been changed */
1904 scp->length = *sizep;
1905 scp->mask |= CM_SCACHEMASK_LENGTH;
1907 else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
1908 /* really extending the file */
1909 scp->length = *sizep;
1910 scp->mask |= CM_SCACHEMASK_LENGTH;
1913 /* done successfully */
1917 lock_ReleaseMutex(&scp->mx);
1918 lock_ReleaseWrite(&scp->bufCreateLock);
1923 /* set the file size or other attributes (but not both at once) */
1924 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
1929 AFSFetchStatus afsOutStatus;
1933 AFSStoreStatus afsInStatus;
1934 struct rx_connection * callp;
1936 /* handle file length setting */
1937 if (attrp->mask & CM_ATTRMASK_LENGTH)
1938 return cm_SetLength(scp, &attrp->length, userp, reqp);
1940 flags = CM_SCACHESYNC_STORESTATUS;
1942 lock_ObtainMutex(&scp->mx);
1943 /* otherwise, we have to make an RPC to get the status */
1944 code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
1946 /* make the attr structure */
1947 cm_StatusFromAttr(&afsInStatus, scp, attrp);
1949 lock_ReleaseMutex(&scp->mx);
1953 /* now make the RPC */
1954 osi_Log1(afsd_logp, "CALL StoreStatus vp %x", (long) scp);
1955 tfid.Volume = scp->fid.volume;
1956 tfid.Vnode = scp->fid.vnode;
1957 tfid.Unique = scp->fid.unique;
1959 code = cm_Conn(&scp->fid, userp, reqp, &connp);
1963 callp = cm_GetRxConn(connp);
1964 code = RXAFS_StoreStatus(callp, &tfid,
1965 &afsInStatus, &afsOutStatus, &volSync);
1966 rx_PutConnection(callp);
1968 } while (cm_Analyze(connp, userp, reqp,
1969 &scp->fid, &volSync, NULL, NULL, code));
1970 code = cm_MapRPCError(code, reqp);
1972 osi_Log1(afsd_logp, "CALL StoreStatus DONE, code %d", code);
1974 lock_ObtainMutex(&scp->mx);
1975 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
1977 cm_MergeStatus(scp, &afsOutStatus, &volSync, userp,
1978 CM_MERGEFLAG_FORCE);
1980 /* if we're changing the mode bits, discard the ACL cache,
1981 * since we changed the mode bits.
1983 if (afsInStatus.Mask & AFS_SETMODE) cm_FreeAllACLEnts(scp);
1984 lock_ReleaseMutex(&scp->mx);
1988 long cm_Create(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
1989 cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
1994 cm_callbackRequest_t cbReq;
1999 AFSStoreStatus inStatus;
2000 AFSFetchStatus updatedDirStatus;
2001 AFSFetchStatus newFileStatus;
2002 AFSCallBack newFileCallback;
2004 struct rx_connection * callp;
2006 /* can't create names with @sys in them; must expand it manually first.
2007 * return "invalid request" if they try.
2009 if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2010 return CM_ERROR_ATSYS;
2013 /* before starting the RPC, mark that we're changing the file data, so
2014 * that someone who does a chmod will know to wait until our call
2017 lock_ObtainMutex(&dscp->mx);
2018 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2020 cm_StartCallbackGrantingCall(NULL, &cbReq);
2022 lock_ReleaseMutex(&dscp->mx);
2028 cm_StatusFromAttr(&inStatus, NULL, attrp);
2030 /* try the RPC now */
2032 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2036 dirAFSFid.Volume = dscp->fid.volume;
2037 dirAFSFid.Vnode = dscp->fid.vnode;
2038 dirAFSFid.Unique = dscp->fid.unique;
2040 callp = cm_GetRxConn(connp);
2041 code = RXAFS_CreateFile(connp->callp, &dirAFSFid, namep,
2042 &inStatus, &newAFSFid, &newFileStatus,
2043 &updatedDirStatus, &newFileCallback,
2045 rx_PutConnection(callp);
2047 } while (cm_Analyze(connp, userp, reqp,
2048 &dscp->fid, &volSync, NULL, &cbReq, code));
2049 code = cm_MapRPCError(code, reqp);
2051 lock_ObtainMutex(&dscp->mx);
2052 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2054 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2056 lock_ReleaseMutex(&dscp->mx);
2058 /* now try to create the file's entry, too, but be careful to
2059 * make sure that we don't merge in old info. Since we weren't locking
2060 * out any requests during the file's creation, we may have pretty old
2064 newFid.cell = dscp->fid.cell;
2065 newFid.volume = dscp->fid.volume;
2066 newFid.vnode = newAFSFid.Vnode;
2067 newFid.unique = newAFSFid.Unique;
2068 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2070 lock_ObtainMutex(&scp->mx);
2071 if (!cm_HaveCallback(scp)) {
2072 cm_MergeStatus(scp, &newFileStatus, &volSync,
2074 cm_EndCallbackGrantingCall(scp, &cbReq,
2075 &newFileCallback, 0);
2078 lock_ReleaseMutex(&scp->mx);
2083 /* make sure we end things properly */
2085 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2090 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
2094 lock_ObtainWrite(&scp->bufCreateLock);
2095 code = buf_CleanVnode(scp, userp, reqp);
2096 lock_ReleaseWrite(&scp->bufCreateLock);
2098 lock_ObtainMutex(&scp->mx);
2099 scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA
2100 | CM_SCACHEFLAG_OUTOFSPACE);
2101 if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
2102 | CM_SCACHEMASK_CLIENTMODTIME
2103 | CM_SCACHEMASK_LENGTH))
2104 code = cm_StoreMini(scp, userp, reqp);
2105 lock_ReleaseMutex(&scp->mx);
2110 long cm_MakeDir(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2111 cm_user_t *userp, cm_req_t *reqp)
2116 cm_callbackRequest_t cbReq;
2121 AFSStoreStatus inStatus;
2122 AFSFetchStatus updatedDirStatus;
2123 AFSFetchStatus newDirStatus;
2124 AFSCallBack newDirCallback;
2126 struct rx_connection * callp;
2128 /* can't create names with @sys in them; must expand it manually first.
2129 * return "invalid request" if they try.
2131 if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2132 return CM_ERROR_ATSYS;
2135 /* before starting the RPC, mark that we're changing the directory
2136 * data, so that someone who does a chmod on the dir will wait until
2137 * our call completes.
2139 lock_ObtainMutex(&dscp->mx);
2140 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2142 cm_StartCallbackGrantingCall(NULL, &cbReq);
2144 lock_ReleaseMutex(&dscp->mx);
2150 cm_StatusFromAttr(&inStatus, NULL, attrp);
2152 /* try the RPC now */
2154 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2158 dirAFSFid.Volume = dscp->fid.volume;
2159 dirAFSFid.Vnode = dscp->fid.vnode;
2160 dirAFSFid.Unique = dscp->fid.unique;
2162 callp = cm_GetRxConn(connp);
2163 code = RXAFS_MakeDir(connp->callp, &dirAFSFid, namep,
2164 &inStatus, &newAFSFid, &newDirStatus,
2165 &updatedDirStatus, &newDirCallback,
2167 rx_PutConnection(callp);
2169 } while (cm_Analyze(connp, userp, reqp,
2170 &dscp->fid, &volSync, NULL, &cbReq, code));
2171 code = cm_MapRPCError(code, reqp);
2173 lock_ObtainMutex(&dscp->mx);
2174 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2176 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2178 lock_ReleaseMutex(&dscp->mx);
2180 /* now try to create the new dir's entry, too, but be careful to
2181 * make sure that we don't merge in old info. Since we weren't locking
2182 * out any requests during the file's creation, we may have pretty old
2186 newFid.cell = dscp->fid.cell;
2187 newFid.volume = dscp->fid.volume;
2188 newFid.vnode = newAFSFid.Vnode;
2189 newFid.unique = newAFSFid.Unique;
2190 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2192 lock_ObtainMutex(&scp->mx);
2193 if (!cm_HaveCallback(scp)) {
2194 cm_MergeStatus(scp, &newDirStatus, &volSync,
2196 cm_EndCallbackGrantingCall(scp, &cbReq,
2197 &newDirCallback, 0);
2200 lock_ReleaseMutex(&scp->mx);
2201 cm_ReleaseSCache(scp);
2205 /* make sure we end things properly */
2207 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2209 /* and return error code */
2213 long cm_Link(cm_scache_t *dscp, char *namep, cm_scache_t *sscp, long flags,
2214 cm_user_t *userp, cm_req_t *reqp)
2219 AFSFid existingAFSFid;
2220 AFSFetchStatus updatedDirStatus;
2221 AFSFetchStatus newLinkStatus;
2223 struct rx_connection * callp;
2225 if (dscp->fid.cell != sscp->fid.cell ||
2226 dscp->fid.volume != sscp->fid.volume) {
2227 return CM_ERROR_CROSSDEVLINK;
2230 lock_ObtainMutex(&dscp->mx);
2231 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2232 lock_ReleaseMutex(&dscp->mx);
2238 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2241 dirAFSFid.Volume = dscp->fid.volume;
2242 dirAFSFid.Vnode = dscp->fid.vnode;
2243 dirAFSFid.Unique = dscp->fid.unique;
2245 existingAFSFid.Volume = sscp->fid.volume;
2246 existingAFSFid.Vnode = sscp->fid.vnode;
2247 existingAFSFid.Unique = sscp->fid.unique;
2249 callp = cm_GetRxConn(connp);
2250 code = RXAFS_Link(callp, &dirAFSFid, namep, &existingAFSFid,
2251 &newLinkStatus, &updatedDirStatus, &volSync);
2252 rx_PutConnection(callp);
2253 osi_Log1(smb_logp," RXAFS_Link returns %d", code);
2255 } while (cm_Analyze(connp, userp, reqp,
2256 &dscp->fid, &volSync, NULL, NULL, code));
2258 code = cm_MapRPCError(code, reqp);
2260 lock_ObtainMutex(&dscp->mx);
2261 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2263 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2265 lock_ReleaseMutex(&dscp->mx);
2270 long cm_SymLink(cm_scache_t *dscp, char *namep, char *contentsp, long flags,
2271 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
2279 AFSStoreStatus inStatus;
2280 AFSFetchStatus updatedDirStatus;
2281 AFSFetchStatus newLinkStatus;
2283 struct rx_connection * callp;
2285 /* before starting the RPC, mark that we're changing the directory data,
2286 * so that someone who does a chmod on the dir will wait until our
2289 lock_ObtainMutex(&dscp->mx);
2290 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2291 lock_ReleaseMutex(&dscp->mx);
2296 cm_StatusFromAttr(&inStatus, NULL, attrp);
2298 /* try the RPC now */
2300 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2304 dirAFSFid.Volume = dscp->fid.volume;
2305 dirAFSFid.Vnode = dscp->fid.vnode;
2306 dirAFSFid.Unique = dscp->fid.unique;
2308 callp = cm_GetRxConn(connp);
2309 code = RXAFS_Symlink(callp, &dirAFSFid, namep, contentsp,
2310 &inStatus, &newAFSFid, &newLinkStatus,
2311 &updatedDirStatus, &volSync);
2312 rx_PutConnection(callp);
2314 } while (cm_Analyze(connp, userp, reqp,
2315 &dscp->fid, &volSync, NULL, NULL, code));
2316 code = cm_MapRPCError(code, reqp);
2318 lock_ObtainMutex(&dscp->mx);
2319 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2321 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2323 lock_ReleaseMutex(&dscp->mx);
2325 /* now try to create the new dir's entry, too, but be careful to
2326 * make sure that we don't merge in old info. Since we weren't locking
2327 * out any requests during the file's creation, we may have pretty old
2331 newFid.cell = dscp->fid.cell;
2332 newFid.volume = dscp->fid.volume;
2333 newFid.vnode = newAFSFid.Vnode;
2334 newFid.unique = newAFSFid.Unique;
2335 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2337 lock_ObtainMutex(&scp->mx);
2338 if (!cm_HaveCallback(scp)) {
2339 cm_MergeStatus(scp, &newLinkStatus, &volSync,
2342 lock_ReleaseMutex(&scp->mx);
2343 cm_ReleaseSCache(scp);
2347 /* and return error code */
2351 long cm_RemoveDir(cm_scache_t *dscp, char *namep, cm_user_t *userp,
2358 AFSFetchStatus updatedDirStatus;
2360 struct rx_connection * callp;
2362 /* before starting the RPC, mark that we're changing the directory data,
2363 * so that someone who does a chmod on the dir will wait until our
2366 lock_ObtainMutex(&dscp->mx);
2367 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2368 lock_ReleaseMutex(&dscp->mx);
2374 /* try the RPC now */
2376 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2380 dirAFSFid.Volume = dscp->fid.volume;
2381 dirAFSFid.Vnode = dscp->fid.vnode;
2382 dirAFSFid.Unique = dscp->fid.unique;
2384 callp = cm_GetRxConn(connp);
2385 code = RXAFS_RemoveDir(callp, &dirAFSFid, namep,
2386 &updatedDirStatus, &volSync);
2387 rx_PutConnection(callp);
2389 } while (cm_Analyze(connp, userp, reqp,
2390 &dscp->fid, &volSync, NULL, NULL, code));
2391 code = cm_MapRPCErrorRmdir(code, reqp);
2393 lock_ObtainMutex(&dscp->mx);
2394 cm_dnlcRemove(dscp, namep);
2395 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2397 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2399 lock_ReleaseMutex(&dscp->mx);
2401 /* and return error code */
2405 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
2407 /* grab mutex on contents */
2408 lock_ObtainMutex(&scp->mx);
2410 /* reset the prefetch info */
2411 scp->prefetch.base.LowPart = 0; /* base */
2412 scp->prefetch.base.HighPart = 0;
2413 scp->prefetch.end.LowPart = 0; /* and end */
2414 scp->prefetch.end.HighPart = 0;
2416 /* release mutex on contents */
2417 lock_ReleaseMutex(&scp->mx);
2423 long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
2424 char *newNamep, cm_user_t *userp, cm_req_t *reqp)
2428 AFSFid oldDirAFSFid;
2429 AFSFid newDirAFSFid;
2431 AFSFetchStatus updatedOldDirStatus;
2432 AFSFetchStatus updatedNewDirStatus;
2435 struct rx_connection * callp;
2437 /* before starting the RPC, mark that we're changing the directory data,
2438 * so that someone who does a chmod on the dir will wait until our call
2439 * completes. We do this in vnode order so that we don't deadlock,
2440 * which makes the code a little verbose.
2442 if (oldDscp == newDscp) {
2443 /* check for identical names */
2444 if (strcmp(oldNamep, newNamep) == 0)
2445 return CM_ERROR_RENAME_IDENTICAL;
2448 lock_ObtainMutex(&oldDscp->mx);
2449 cm_dnlcRemove(oldDscp, oldNamep);
2450 cm_dnlcRemove(oldDscp, newNamep);
2451 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2452 CM_SCACHESYNC_STOREDATA);
2453 lock_ReleaseMutex(&oldDscp->mx);
2456 /* two distinct dir vnodes */
2458 if (oldDscp->fid.cell != newDscp->fid.cell ||
2459 oldDscp->fid.volume != newDscp->fid.volume)
2460 return CM_ERROR_CROSSDEVLINK;
2462 /* shouldn't happen that we have distinct vnodes for two
2463 * different files, but could due to deliberate attack, or
2464 * stale info. Avoid deadlocks and quit now.
2466 if (oldDscp->fid.vnode == newDscp->fid.vnode)
2467 return CM_ERROR_CROSSDEVLINK;
2469 if (oldDscp->fid.vnode < newDscp->fid.vnode) {
2470 lock_ObtainMutex(&oldDscp->mx);
2471 cm_dnlcRemove(oldDscp, oldNamep);
2472 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2473 CM_SCACHESYNC_STOREDATA);
2474 lock_ReleaseMutex(&oldDscp->mx);
2476 lock_ObtainMutex(&newDscp->mx);
2477 cm_dnlcRemove(newDscp, newNamep);
2478 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2479 CM_SCACHESYNC_STOREDATA);
2480 lock_ReleaseMutex(&newDscp->mx);
2482 /* cleanup first one */
2483 cm_SyncOpDone(oldDscp, NULL,
2484 CM_SCACHESYNC_STOREDATA);
2489 /* lock the new vnode entry first */
2490 lock_ObtainMutex(&newDscp->mx);
2491 cm_dnlcRemove(newDscp, newNamep);
2492 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2493 CM_SCACHESYNC_STOREDATA);
2494 lock_ReleaseMutex(&newDscp->mx);
2496 lock_ObtainMutex(&oldDscp->mx);
2497 cm_dnlcRemove(oldDscp, oldNamep);
2498 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2499 CM_SCACHESYNC_STOREDATA);
2500 lock_ReleaseMutex(&oldDscp->mx);
2502 /* cleanup first one */
2503 cm_SyncOpDone(newDscp, NULL,
2504 CM_SCACHESYNC_STOREDATA);
2508 } /* two distinct vnodes */
2515 /* try the RPC now */
2517 code = cm_Conn(&oldDscp->fid, userp, reqp, &connp);
2521 oldDirAFSFid.Volume = oldDscp->fid.volume;
2522 oldDirAFSFid.Vnode = oldDscp->fid.vnode;
2523 oldDirAFSFid.Unique = oldDscp->fid.unique;
2524 newDirAFSFid.Volume = newDscp->fid.volume;
2525 newDirAFSFid.Vnode = newDscp->fid.vnode;
2526 newDirAFSFid.Unique = newDscp->fid.unique;
2528 callp = cm_GetRxConn(connp);
2529 code = RXAFS_Rename(callp, &oldDirAFSFid, oldNamep,
2530 &newDirAFSFid, newNamep,
2531 &updatedOldDirStatus, &updatedNewDirStatus,
2533 rx_PutConnection(callp);
2535 } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
2536 &volSync, NULL, NULL, code));
2537 code = cm_MapRPCError(code, reqp);
2539 /* update the individual stat cache entries for the directories */
2540 lock_ObtainMutex(&oldDscp->mx);
2541 cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
2543 cm_MergeStatus(oldDscp, &updatedOldDirStatus, &volSync,
2546 lock_ReleaseMutex(&oldDscp->mx);
2548 /* and update it for the new one, too, if necessary */
2550 lock_ObtainMutex(&newDscp->mx);
2551 cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
2553 cm_MergeStatus(newDscp, &updatedNewDirStatus, &volSync,
2556 lock_ReleaseMutex(&newDscp->mx);
2559 /* and return error code */
2563 long cm_Lock(cm_scache_t *scp, unsigned char LockType,
2564 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
2565 u_long Timeout, cm_user_t *userp, cm_req_t *reqp,
2569 int Which = ((LockType & 0x1) ? LockRead : LockWrite);
2573 cm_file_lock_t *fileLock;
2576 struct rx_connection * callp;
2578 /* Look for a conflict. Also, if we are asking for a shared lock,
2579 * look for another shared lock, so we don't have to do an RPC.
2583 fileLock = (cm_file_lock_t *)
2584 ((char *) q - offsetof(cm_file_lock_t, fileq));
2585 if ((fileLock->flags &
2586 (CM_FILELOCK_FLAG_INVALID | CM_FILELOCK_FLAG_WAITING))
2588 if ((LockType & 0x1) == 0
2589 || (fileLock->LockType & 0x1) == 0)
2590 return CM_ERROR_WOULDBLOCK;
2599 tfid.Volume = scp->fid.volume;
2600 tfid.Vnode = scp->fid.vnode;
2601 tfid.Unique = scp->fid.unique;
2602 lock_ReleaseMutex(&scp->mx);
2604 code = cm_Conn(&scp->fid, userp, reqp, &connp);
2608 callp = cm_GetRxConn(connp);
2609 code = RXAFS_SetLock(callp, &tfid, Which,
2611 rx_PutConnection(callp);
2613 } while (cm_Analyze(connp, userp, reqp, &scp->fid, &volSync,
2615 lock_ObtainMutex(&scp->mx);
2616 code = cm_MapRPCError(code, reqp);
2619 if (code == 0 || Timeout != 0) {
2620 fileLock = malloc(sizeof(cm_file_lock_t));
2621 fileLock->LockType = LockType;
2623 fileLock->userp = userp;
2624 fileLock->fid = scp->fid;
2625 fileLock->LOffset = LOffset;
2626 fileLock->LLength = LLength;
2627 fileLock->flags = (code == 0 ? 0 : CM_FILELOCK_FLAG_WAITING);
2628 osi_QAdd(&scp->fileLocks, &fileLock->fileq);
2629 lock_ObtainWrite(&cm_scacheLock);
2630 osi_QAdd(&cm_allFileLocks, &fileLock->q);
2631 lock_ReleaseWrite(&cm_scacheLock);
2638 long cm_Unlock(cm_scache_t *scp, unsigned char LockType,
2639 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
2640 cm_user_t *userp, cm_req_t *reqp)
2643 int Which = ((LockType & 0x1) ? LockRead : LockWrite);
2647 cm_file_lock_t *fileLock, *ourLock;
2648 osi_queue_t *q, *qq;
2649 int anotherReader = 0;
2652 struct rx_connection * callp;
2654 if (LargeIntegerLessThan(LLength, scp->length))
2657 /* Look for our own lock on the list, so as to remove it.
2658 * Also, determine if we're the last reader; if not, avoid an RPC.
2662 fileLock = (cm_file_lock_t *)
2663 ((char *) q - offsetof(cm_file_lock_t, fileq));
2665 && fileLock->userp == userp
2666 && LargeIntegerEqualTo(fileLock->LOffset, LOffset)
2667 && LargeIntegerEqualTo(fileLock->LLength, LLength)) {
2672 else if (fileLock->LockType & 0x1)
2677 /* ignore byte ranges */
2678 if (smallLock && !found)
2681 /* don't try to unlock other people's locks */
2683 return CM_ERROR_WOULDBLOCK;
2685 /* discard lock record */
2686 osi_QRemove(&scp->fileLocks, qq);
2688 * Don't delete it here; let the daemon delete it, to simplify
2689 * the daemon's traversal of the list.
2691 lock_ObtainWrite(&cm_scacheLock);
2692 ourLock->flags |= CM_FILELOCK_FLAG_INVALID;
2693 cm_ReleaseUser(ourLock->userp);
2694 lock_ReleaseWrite(&cm_scacheLock);
2696 if (!anotherReader) {
2697 tfid.Volume = scp->fid.volume;
2698 tfid.Vnode = scp->fid.vnode;
2699 tfid.Unique = scp->fid.unique;
2700 lock_ReleaseMutex(&scp->mx);
2702 code = cm_Conn(&scp->fid, userp, reqp, &connp);
2706 callp = cm_GetRxConn(connp);
2707 code = RXAFS_ReleaseLock(callp, &tfid, &volSync);
2708 rx_PutConnection(callp);
2710 } while (cm_Analyze(connp, userp, reqp, &scp->fid, &volSync,
2712 code = cm_MapRPCError(code, reqp);
2713 lock_ObtainMutex(&scp->mx);
2719 void cm_CheckLocks()
2721 osi_queue_t *q, *nq;
2722 cm_file_lock_t *fileLock;
2728 struct rx_connection * callp;
2732 lock_ObtainWrite(&cm_scacheLock);
2733 q = cm_allFileLocks;
2735 fileLock = (cm_file_lock_t *) q;
2737 if (fileLock->flags & CM_FILELOCK_FLAG_INVALID) {
2738 osi_QRemove(&cm_allFileLocks, q);
2741 else if (!(fileLock->flags & CM_FILELOCK_FLAG_WAITING)) {
2742 tfid.Volume = fileLock->fid.volume;
2743 tfid.Vnode = fileLock->fid.vnode;
2744 tfid.Unique = fileLock->fid.unique;
2745 lock_ReleaseWrite(&cm_scacheLock);
2747 code = cm_Conn(&fileLock->fid, fileLock->userp,
2752 callp = cm_GetRxConn(connp);
2753 code = RXAFS_ExtendLock(callp, &tfid,
2755 rx_PutConnection(callp);
2757 } while (cm_Analyze(connp, fileLock->userp, &req,
2758 &fileLock->fid, &volSync, NULL, NULL,
2760 code = cm_MapRPCError(code, &req);
2761 lock_ObtainWrite(&cm_scacheLock);
2765 lock_ReleaseWrite(&cm_scacheLock);
2768 long cm_RetryLock(cm_file_lock_t *oldFileLock, int vcp_is_dead)
2771 int Which = ((oldFileLock->LockType & 0x1) ? LockRead : LockWrite);
2776 cm_file_lock_t *fileLock;
2780 struct rx_connection * callp;
2783 code = CM_ERROR_TIMEDOUT;
2789 /* Look for a conflict. Also, if we are asking for a shared lock,
2790 * look for another shared lock, so we don't have to do an RPC.
2792 code = cm_GetSCache(&oldFileLock->fid, &scp, oldFileLock->userp, &req);
2798 fileLock = (cm_file_lock_t *)
2799 ((char *) q - offsetof(cm_file_lock_t, fileq));
2800 if ((fileLock->flags &
2801 (CM_FILELOCK_FLAG_INVALID | CM_FILELOCK_FLAG_WAITING))
2803 if ((oldFileLock->LockType & 0x1) == 0
2804 || (fileLock->LockType & 0x1) == 0) {
2805 cm_ReleaseSCache(scp);
2806 return CM_ERROR_WOULDBLOCK;
2816 tfid.Volume = oldFileLock->fid.volume;
2817 tfid.Vnode = oldFileLock->fid.vnode;
2818 tfid.Unique = oldFileLock->fid.unique;
2820 code = cm_Conn(&oldFileLock->fid, oldFileLock->userp,
2825 callp = cm_GetRxConn(connp);
2826 code = RXAFS_SetLock(callp, &tfid, Which,
2828 rx_PutConnection(callp);
2830 } while (cm_Analyze(connp, oldFileLock->userp, &req,
2831 &oldFileLock->fid, &volSync,
2833 code = cm_MapRPCError(code, &req);
2837 if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
2838 lock_ObtainMutex(&scp->mx);
2839 osi_QRemove(&scp->fileLocks, &oldFileLock->fileq);
2840 lock_ReleaseMutex(&scp->mx);
2842 lock_ObtainWrite(&cm_scacheLock);
2844 oldFileLock->flags = 0;
2845 else if (code != CM_ERROR_WOULDBLOCK) {
2846 oldFileLock->flags |= CM_FILELOCK_FLAG_INVALID;
2847 cm_ReleaseUser(oldFileLock->userp);
2848 oldFileLock->userp = NULL;
2850 lock_ReleaseWrite(&cm_scacheLock);