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 lock_ObtainMutex(&bufferp->mx);
559 bufferOffset = thyper;
561 /* now get the data in the cache */
563 lock_ObtainMutex(&scp->mx);
564 code = cm_SyncOp(scp, bufferp, userp, reqp,
566 CM_SCACHESYNC_NEEDCALLBACK
568 | CM_SCACHESYNC_BUFLOCKED);
570 lock_ReleaseMutex(&scp->mx);
574 if (cm_HaveBuffer(scp, bufferp, 1)) {
575 lock_ReleaseMutex(&scp->mx);
579 /* otherwise, load the buffer and try again */
580 lock_ReleaseMutex(&bufferp->mx);
581 code = cm_GetBuffer(scp, bufferp, NULL, userp,
583 lock_ReleaseMutex(&scp->mx);
584 lock_ObtainMutex(&bufferp->mx);
589 lock_ReleaseMutex(&bufferp->mx);
590 buf_Release(bufferp);
594 } /* if (wrong buffer) ... */
596 /* now we have the buffer containing the entry we're interested
597 * in; copy it out if it represents a non-deleted entry.
599 entryInDir = curOffset.LowPart & (2048-1);
600 entryInBuffer = curOffset.LowPart & (buf_bufferSize - 1);
602 /* page header will help tell us which entries are free. Page
603 * header can change more often than once per buffer, since
604 * AFS 3 dir page size may be less than (but not more than) a
605 * buffer package buffer.
607 /* only look intra-buffer */
608 temp = curOffset.LowPart & (buf_bufferSize - 1);
609 temp &= ~(2048 - 1); /* turn off intra-page bits */
610 pageHeaderp = (cm_pageHeader_t *) (bufferp->datap + temp);
612 /* now determine which entry we're looking at in the page. If
613 * it is free (there's a free bitmap at the start of the dir),
614 * we should skip these 32 bytes.
616 slotInPage = (entryInDir & 0x7e0) >> 5;
617 if (!(pageHeaderp->freeBitmap[slotInPage>>3]
618 & (1 << (slotInPage & 0x7)))) {
619 /* this entry is free */
620 numDirChunks = 1; /* only skip this guy */
624 tp = bufferp->datap + entryInBuffer;
625 dep = (cm_dirEntry_t *) tp; /* now points to AFS3 dir entry */
627 /* while we're here, compute the next entry's location, too,
628 * since we'll need it when writing out the cookie into the
629 * dir listing stream.
631 numDirChunks = cm_NameEntries(dep->name, NULL);
633 /* compute the offset of the cookie representing the next entry */
634 nextEntryCookie = curOffset.LowPart
635 + (CM_DIR_CHUNKSIZE * numDirChunks);
637 if (dep->fid.vnode != 0) {
638 /* this is one of the entries to use: it is not deleted */
639 code = (*funcp)(scp, dep, parmp, &curOffset);
642 } /* if we're including this name */
645 /* and adjust curOffset to be where the new cookie is */
647 thyper.LowPart = CM_DIR_CHUNKSIZE * numDirChunks;
648 curOffset = LargeIntegerAdd(thyper, curOffset);
649 } /* while copying data for dir listing */
651 /* release the mutex */
653 lock_ReleaseMutex(&bufferp->mx);
654 buf_Release(bufferp);
659 int cm_NoneUpper(char *s)
663 if (c >= 'A' && c <= 'Z')
668 int cm_NoneLower(char *s)
672 if (c >= 'a' && c <= 'z')
677 long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
680 cm_lookupSearch_t *sp;
685 sp = (cm_lookupSearch_t *) rockp;
687 matchName = dep->name;
689 match = cm_stricmp(matchName, sp->searchNamep);
691 match = strcmp(matchName, sp->searchNamep);
695 && !cm_Is8Dot3(dep->name)) {
696 matchName = shortName;
697 cm_Gen8Dot3Name(dep, shortName, NULL);
699 match = cm_stricmp(matchName, sp->searchNamep);
701 match = strcmp(matchName, sp->searchNamep);
711 if (!sp->caseFold || matchName == shortName) {
712 sp->fid.vnode = ntohl(dep->fid.vnode);
713 sp->fid.unique = ntohl(dep->fid.unique);
714 return CM_ERROR_STOPNOW;
718 * If we get here, we are doing a case-insensitive search, and we
719 * have found a match. Now we determine what kind of match it is:
720 * exact, lower-case, upper-case, or none of the above. This is done
721 * in order to choose among matches, if there are more than one.
724 /* Exact matches are the best. */
725 match = strcmp(matchName, sp->searchNamep);
728 sp->fid.vnode = ntohl(dep->fid.vnode);
729 sp->fid.unique = ntohl(dep->fid.unique);
730 return CM_ERROR_STOPNOW;
733 /* Lower-case matches are next. */
736 if (cm_NoneUpper(matchName)) {
741 /* Upper-case matches are next. */
744 if (cm_NoneLower(matchName)) {
749 /* General matches are last. */
755 sp->fid.vnode = ntohl(dep->fid.vnode);
756 sp->fid.unique = ntohl(dep->fid.unique);
760 /* read the contents of a mount point into the appropriate string.
761 * called with locked scp, and returns with locked scp.
763 long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
770 if (scp->mountPointStringp)
773 /* otherwise, we have to read it in */
774 lock_ReleaseMutex(&scp->mx);
776 lock_ObtainRead(&scp->bufCreateLock);
777 thyper.LowPart = thyper.HighPart = 0;
778 code = buf_Get(scp, &thyper, &bufp);
779 lock_ReleaseRead(&scp->bufCreateLock);
781 lock_ObtainMutex(&scp->mx);
786 code = cm_SyncOp(scp, bufp, userp, reqp, 0,
787 CM_SCACHESYNC_READ | CM_SCACHESYNC_NEEDCALLBACK);
792 if (cm_HaveBuffer(scp, bufp, 0))
795 /* otherwise load buffer */
796 code = cm_GetBuffer(scp, bufp, NULL, userp, reqp);
801 /* locked, has callback, has valid data in buffer */
802 if ((tlen = scp->length.LowPart) > 1000)
803 return CM_ERROR_TOOBIG;
805 code = CM_ERROR_INVAL;
809 /* someone else did the work while we were out */
810 if (scp->mountPointStringp) {
815 /* otherwise, copy out the link */
816 scp->mountPointStringp = malloc(tlen);
817 memcpy(scp->mountPointStringp, bufp->datap, tlen);
819 /* now make it null-terminated. Note that the original contents of a
820 * link that is a mount point is "#volname." where "." is there just to
821 * be turned into a null. That is, we can trash the last char of the
822 * link without damaging the vol name. This is a stupid convention,
823 * but that's the protocol.
825 scp->mountPointStringp[tlen-1] = 0;
834 /* called with a locked scp and chases the mount point, yielding outScpp.
835 * scp remains locked, just for simplicity of describing the interface.
837 long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
838 cm_req_t *reqp, cm_scache_t **outScpp)
853 if (scp->mountRootFidp && scp->mountRootGen >= cm_mountRootGen) {
854 tfid = *scp->mountRootFidp;
855 lock_ReleaseMutex(&scp->mx);
856 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
857 lock_ObtainMutex(&scp->mx);
861 /* parse the volume name */
862 mpNamep = scp->mountPointStringp;
864 tlen = strlen(scp->mountPointStringp);
865 mtType = *scp->mountPointStringp;
866 cellNamep = malloc(tlen);
867 volNamep = malloc(tlen);
869 cp = strrchr(mpNamep, ':');
871 /* cellular mount point */
872 memset(cellNamep, 0, tlen);
873 strncpy(cellNamep, mpNamep+1, cp - mpNamep - 1);
874 strcpy(volNamep, cp+1);
875 /* now look up the cell */
876 cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE);
880 strcpy(volNamep, mpNamep+1);
882 cellp = cm_FindCellByID(scp->fid.cell);
886 code = CM_ERROR_NOSUCHCELL;
890 vnLength = strlen(volNamep);
891 if (vnLength >= 8 && strcmp(volNamep + vnLength - 7, ".backup") == 0)
893 else if (vnLength >= 10
894 && strcmp(volNamep + vnLength - 9, ".readonly") == 0)
899 /* check for backups within backups */
901 && (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
902 == CM_SCACHEFLAG_RO) {
903 code = CM_ERROR_NOSUCHVOLUME;
907 /* now we need to get the volume */
908 lock_ReleaseMutex(&scp->mx);
909 code = cm_GetVolumeByName(cellp, volNamep, userp, reqp, 0, &volp);
910 lock_ObtainMutex(&scp->mx);
913 /* save the parent of the volume root for this is the
914 * place where the volume is mounted and we must remember
915 * this in the volume structure rather than just in the
916 * scache entry lest the scache entry gets recycled
919 lock_ObtainMutex(&volp->mx);
920 if(volp->dotdotFidp == (cm_fid_t *) NULL)
921 volp->dotdotFidp = (cm_fid_t *) malloc(sizeof(cm_fid_t));
922 *(volp->dotdotFidp) = dscp->fid;
923 lock_ReleaseMutex(&volp->mx);
925 if (scp->mountRootFidp == 0) {
926 scp->mountRootFidp = malloc(sizeof(cm_fid_t));
928 scp->mountRootFidp->cell = cellp->cellID;
929 /* if the mt pt is in a read-only volume (not just a
930 * backup), and if there is a read-only volume for the
931 * target, and if this is a type '#' mount point, use
932 * the read-only, otherwise use the one specified.
934 if (mtType == '#' && (scp->flags & CM_SCACHEFLAG_PURERO)
935 && volp->roID != 0 && type == RWVOL)
938 scp->mountRootFidp->volume = volp->roID;
939 else if (type == BACKVOL)
940 scp->mountRootFidp->volume = volp->bkID;
942 scp->mountRootFidp->volume = volp->rwID;
944 /* the rest of the fid is a magic number */
945 scp->mountRootFidp->vnode = 1;
946 scp->mountRootFidp->unique = 1;
947 scp->mountRootGen = cm_mountRootGen;
949 tfid = *scp->mountRootFidp;
950 lock_ReleaseMutex(&scp->mx);
951 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
952 lock_ObtainMutex(&scp->mx);
961 long cm_LookupInternal(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
962 cm_req_t *reqp, cm_scache_t **outpScpp)
965 int dnlcHit = 1; /* did we hit in the dnlc? yes, we did */
966 cm_scache_t *tscp = NULL;
967 cm_scache_t *mountedScp;
968 cm_lookupSearch_t rock;
971 if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
972 && strcmp(namep, "..") == 0) {
973 if (dscp->dotdotFidp == (cm_fid_t *)NULL
974 || dscp->dotdotFidp->volume == 0)
975 return CM_ERROR_NOSUCHVOLUME;
976 rock.fid = *dscp->dotdotFidp;
980 memset(&rock, 0, sizeof(rock));
981 rock.fid.cell = dscp->fid.cell;
982 rock.fid.volume = dscp->fid.volume;
983 rock.searchNamep = namep;
984 rock.caseFold = (flags & CM_FLAG_CASEFOLD);
985 rock.hasTilde = ((strchr(namep, '~') != NULL) ? 1 : 0);
987 /* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
988 code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
989 (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
991 /* code == 0 means we fell off the end of the dir, while stopnow means
992 * that we stopped early, probably because we found the entry we're
993 * looking for. Any other non-zero code is an error.
995 if (code && code != CM_ERROR_STOPNOW)
998 getroot = (dscp==cm_rootSCachep) ;
1000 if (!cm_freelanceEnabled || !getroot) {
1001 if (flags & CM_FLAG_CHECKPATH)
1002 return CM_ERROR_NOSUCHPATH;
1004 return CM_ERROR_NOSUCHFILE;
1006 else { /* nonexistent dir on freelance root, so add it */
1007 osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %s",
1008 osi_LogSaveString(afsd_logp,namep));
1009 code = cm_FreelanceAddMount(namep, namep, "root.cell.", namep[0] == '.', &rock.fid);
1010 if (code < 0) { /* add mount point failed, so give up */
1011 if (flags & CM_FLAG_CHECKPATH)
1012 return CM_ERROR_NOSUCHPATH;
1014 return CM_ERROR_NOSUCHFILE;
1016 tscp = NULL; /* to force call of cm_GetSCache */
1021 if ( !tscp ) /* we did not find it in the dnlc */
1024 code = cm_GetSCache(&rock.fid, &tscp, userp, reqp);
1028 /* tscp is now held */
1030 lock_ObtainMutex(&tscp->mx);
1031 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1032 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1034 lock_ReleaseMutex(&tscp->mx);
1035 cm_ReleaseSCache(tscp);
1038 /* tscp is now locked */
1040 if (!(flags & CM_FLAG_NOMOUNTCHASE)
1041 && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
1042 /* mount points are funny: they have a volume name to mount
1045 code = cm_ReadMountPoint(tscp, userp, reqp);
1047 code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
1049 lock_ReleaseMutex(&tscp->mx);
1050 cm_ReleaseSCache(tscp);
1057 lock_ReleaseMutex(&tscp->mx);
1060 /* copy back pointer */
1063 /* insert scache in dnlc */
1064 if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
1065 /* lock the directory entry to prevent racing callback revokes */
1066 lock_ObtainMutex(&dscp->mx);
1067 if ( dscp->cbServerp && dscp->cbExpires )
1068 cm_dnlcEnter(dscp, namep, tscp);
1069 lock_ReleaseMutex(&dscp->mx);
1076 int cm_ExpandSysName(char *inp, char *outp, long outSize, unsigned int index)
1081 tp = strrchr(inp, '@');
1083 return 0; /* no @sys */
1085 if (strcmp(tp, "@sys") != 0)
1086 return 0; /* no @sys */
1088 /* caller just wants to know if this is a valid @sys type of name */
1092 if (index >= MAXNUMSYSNAMES)
1095 /* otherwise generate the properly expanded @sys name */
1096 prefixCount = tp - inp;
1098 strncpy(outp, inp, prefixCount); /* copy out "a." from "a.@sys" */
1099 outp[prefixCount] = 0; /* null terminate the "a." */
1100 strcat(outp, cm_sysNameList[index]);/* append i386_nt40 */
1104 long cm_Lookup(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
1105 cm_req_t *reqp, cm_scache_t **outpScpp)
1109 int sysNameIndex = 0;
1110 cm_scache_t *scp = 0;
1112 if ( stricmp(namep,SMB_IOCTL_FILENAME_NOSLASH) == 0 ) {
1113 if (flags & CM_FLAG_CHECKPATH)
1114 return CM_ERROR_NOSUCHPATH;
1116 return CM_ERROR_NOSUCHFILE;
1119 for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
1120 code = cm_ExpandSysName(namep, tname, sizeof(tname), sysNameIndex);
1122 code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
1128 cm_ReleaseSCache(scp);
1132 return cm_LookupInternal(dscp, namep, flags, userp, reqp, outpScpp);
1136 /* None of the possible sysName expansions could be found */
1137 if (flags & CM_FLAG_CHECKPATH)
1138 return CM_ERROR_NOSUCHPATH;
1140 return CM_ERROR_NOSUCHFILE;
1143 long cm_Unlink(cm_scache_t *dscp, char *namep, cm_user_t *userp, cm_req_t *reqp)
1149 AFSFetchStatus newDirStatus;
1151 struct rx_connection * callp;
1153 #ifdef AFS_FREELANCE_CLIENT
1154 if (cm_freelanceEnabled && dscp == cm_rootSCachep) {
1155 /* deleting a mount point from the root dir. */
1156 code = cm_FreelanceRemoveMount(namep);
1161 /* make sure we don't screw up the dir status during the merge */
1162 lock_ObtainMutex(&dscp->mx);
1163 sflags = CM_SCACHESYNC_STOREDATA;
1164 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1165 lock_ReleaseMutex(&dscp->mx);
1170 afsFid.Volume = dscp->fid.volume;
1171 afsFid.Vnode = dscp->fid.vnode;
1172 afsFid.Unique = dscp->fid.unique;
1174 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1178 callp = cm_GetRxConn(connp);
1179 code = RXAFS_RemoveFile(callp, &afsFid, namep,
1180 &newDirStatus, &volSync);
1181 rx_PutConnection(callp);
1183 } while (cm_Analyze(connp, userp, reqp, &dscp->fid, &volSync, NULL, NULL, code));
1184 code = cm_MapRPCError(code, reqp);
1186 lock_ObtainMutex(&dscp->mx);
1187 cm_dnlcRemove(dscp, namep);
1188 cm_SyncOpDone(dscp, NULL, sflags);
1190 cm_MergeStatus(dscp, &newDirStatus, &volSync, userp, 0);
1191 lock_ReleaseMutex(&dscp->mx);
1196 /* called with a locked vnode, and fills in the link info.
1197 * returns this the vnode still locked.
1199 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1206 lock_AssertMutex(&linkScp->mx);
1207 if (!linkScp->mountPointStringp) {
1208 /* read the link data */
1209 lock_ReleaseMutex(&linkScp->mx);
1210 thyper.LowPart = thyper.HighPart = 0;
1211 code = buf_Get(linkScp, &thyper, &bufp);
1212 lock_ObtainMutex(&linkScp->mx);
1216 code = cm_SyncOp(linkScp, bufp, userp, reqp, 0,
1217 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1222 if (cm_HaveBuffer(linkScp, bufp, 0))
1225 code = cm_GetBuffer(linkScp, bufp, NULL, userp, reqp);
1230 } /* while loop to get the data */
1232 /* now if we still have no link read in,
1233 * copy the data from the buffer */
1234 if ((temp = linkScp->length.LowPart) >= 1024) {
1236 return CM_ERROR_TOOBIG;
1239 /* otherwise, it fits; make sure it is still null (could have
1240 * lost race with someone else referencing this link above),
1241 * and if so, copy in the data.
1243 if (linkScp->mountPointStringp == NULL) {
1244 linkScp->mountPointStringp = malloc(temp+1);
1245 strncpy(linkScp->mountPointStringp, bufp->datap, temp);
1246 linkScp->mountPointStringp[temp] = 0; /* null terminate */
1249 } /* don't have sym link contents cached */
1254 /* called with a held vnode and a path suffix, with the held vnode being a
1255 * symbolic link. Our goal is to generate a new path to interpret, and return
1256 * this new path in newSpaceBufferp. If the new vnode is relative to a dir
1257 * other than the directory containing the symbolic link, then the new root is
1258 * returned in *newRootScpp, otherwise a null is returned there.
1260 long cm_AssembleLink(cm_scache_t *linkScp, char *pathSuffixp,
1261 cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1262 cm_user_t *userp, cm_req_t *reqp)
1268 lock_ObtainMutex(&linkScp->mx);
1269 code = cm_HandleLink(linkScp, userp, reqp);
1273 /* if we may overflow the buffer, bail out; buffer is signficantly
1274 * bigger than max path length, so we don't really have to worry about
1275 * being a little conservative here.
1277 if (strlen(linkScp->mountPointStringp) + strlen(pathSuffixp) + 2
1278 >= CM_UTILS_SPACESIZE)
1279 return CM_ERROR_TOOBIG;
1281 tsp = cm_GetSpace();
1282 linkp = linkScp->mountPointStringp;
1283 if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1284 if (strlen(linkp) > cm_mountRootLen)
1285 strcpy(tsp->data, linkp+cm_mountRootLen+1);
1288 *newRootScpp = cm_rootSCachep;
1289 cm_HoldSCache(cm_rootSCachep);
1290 } else if (*linkp == '\\' || *linkp == '/') {
1291 /* formerly, this was considered to be from the AFS root,
1292 * but this seems to create problems. instead, we will just
1293 * reject the link */
1295 strcpy(tsp->data, linkp+1);
1296 *newRootScpp = cm_rootSCachep;
1297 cm_HoldSCache(cm_rootSCachep);
1299 code = CM_ERROR_NOSUCHPATH;
1304 /* a relative link */
1305 strcpy(tsp->data, linkp);
1306 *newRootScpp = NULL;
1308 if (pathSuffixp[0] != 0) { /* if suffix string is non-null */
1309 strcat(tsp->data, "\\");
1310 strcat(tsp->data, pathSuffixp);
1312 *newSpaceBufferp = tsp;
1316 lock_ReleaseMutex(&linkScp->mx);
1320 long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
1321 cm_user_t *userp, char *tidPathp, cm_req_t *reqp, cm_scache_t **outScpp)
1324 char *tp; /* ptr moving through input buffer */
1325 char tc; /* temp char */
1326 int haveComponent; /* has new component started? */
1327 char component[256]; /* this is the new component */
1328 char *cp; /* component name being assembled */
1329 cm_scache_t *tscp; /* current location in the hierarchy */
1330 cm_scache_t *nscp; /* next dude down */
1331 cm_scache_t *dirScp; /* last dir we searched */
1332 cm_scache_t *linkScp; /* new root for the symlink we just
1334 cm_space_t *psp; /* space for current path, if we've hit
1336 cm_space_t *tempsp; /* temp vbl */
1337 char *restp; /* rest of the pathname to interpret */
1338 int symlinkCount; /* count of # of symlinks traversed */
1339 int extraFlag; /* avoid chasing mt pts for dir cmd */
1340 int phase = 1; /* 1 = tidPathp, 2 = pathp */
1353 cm_HoldSCache(tscp);
1358 /* map Unix slashes into DOS ones so we can interpret Unix
1364 if (!haveComponent) {
1383 /* we have a component here */
1384 if (tc == 0 || tc == '\\') {
1385 /* end of the component; we're at the last
1386 * component if tc == 0. However, if the last
1387 * is a symlink, we have more to do.
1389 *cp++ = 0; /* add null termination */
1391 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
1392 extraFlag = CM_FLAG_NOMOUNTCHASE;
1393 code = cm_Lookup(tscp, component,
1395 userp, reqp, &nscp);
1398 cm_ReleaseSCache(tscp);
1403 haveComponent = 0; /* component done */
1404 dirScp = tscp; /* for some symlinks */
1405 tscp = nscp; /* already held */
1406 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
1408 cm_ReleaseSCache(dirScp);
1412 /* now, if tscp is a symlink, we should follow
1413 * it and assemble the path again.
1415 lock_ObtainMutex(&tscp->mx);
1416 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1417 CM_SCACHESYNC_GETSTATUS
1418 | CM_SCACHESYNC_NEEDCALLBACK);
1420 lock_ReleaseMutex(&tscp->mx);
1421 cm_ReleaseSCache(tscp);
1422 cm_ReleaseSCache(dirScp);
1425 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
1426 /* this is a symlink; assemble a new buffer */
1427 lock_ReleaseMutex(&tscp->mx);
1428 if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
1429 cm_ReleaseSCache(tscp);
1430 cm_ReleaseSCache(dirScp);
1433 return CM_ERROR_TOO_MANY_SYMLINKS;
1439 code = cm_AssembleLink(tscp, restp, &linkScp, &tempsp, userp, reqp);
1441 /* something went wrong */
1442 cm_ReleaseSCache(tscp);
1443 cm_ReleaseSCache(dirScp);
1447 /* otherwise, tempsp has the new path,
1448 * and linkScp is the new root from
1449 * which to interpret that path.
1450 * Continue with the namei processing,
1451 * also doing the bookkeeping for the
1452 * space allocation and tracking the
1453 * vnode reference counts.
1459 cm_ReleaseSCache(tscp);
1463 * now, if linkScp is null, that's
1464 * AssembleLink's way of telling us that
1465 * the sym link is relative to the dir
1466 * containing the link. We have a ref
1467 * to it in dirScp, and we hold it now
1468 * and reuse it as the new spot in the
1472 cm_HoldSCache(dirScp);
1475 } /* if we have a sym link */
1477 /* not a symlink, we may be done */
1478 lock_ReleaseMutex(&tscp->mx);
1485 cm_ReleaseSCache(dirScp);
1490 cm_ReleaseSCache(dirScp);
1491 } /* end of a component */
1493 } /* we have a component */
1494 } /* big while loop over all components */
1504 /* called with a dir, and a vnode within the dir that happens to be a symlink.
1505 * We chase the link, and return a held pointer to the target, if it exists,
1506 * in *outScpp. If we succeed, we return 0, otherwise we return an error code
1507 * and do not hold or return a target vnode.
1509 * This is very similar to calling cm_NameI with the last component of a name,
1510 * which happens to be a symlink, except that we've already passed by the name.
1512 * This function is typically called by the directory listing functions, which
1513 * encounter symlinks but need to return the proper file length so programs
1514 * like "more" work properly when they make use of the attributes retrieved from
1517 * The input vnode should not be locked when this function is called.
1519 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
1520 cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
1524 cm_scache_t *newRootScp;
1526 osi_Log1(afsd_logp, "Evaluating symlink vp %x", linkScp);
1528 code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
1532 /* now, if newRootScp is NULL, we're really being told that the symlink
1533 * is relative to the current directory (dscp).
1535 if (newRootScp == NULL) {
1537 cm_HoldSCache(dscp);
1540 code = cm_NameI(newRootScp, spacep->data,
1541 CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
1542 userp, NULL, reqp, outScpp);
1544 /* this stuff is allocated no matter what happened on the namei call,
1546 cm_FreeSpace(spacep);
1547 cm_ReleaseSCache(newRootScp);
1552 /* make this big enough so that one buffer of dir pages won't overflow. We'll
1553 * check anyway, but we want to minimize the chance that we have to leave stuff
1556 #define CM_BULKMAX 128
1558 /* rock for bulk stat calls */
1559 typedef struct cm_bulkStat {
1560 osi_hyper_t bufOffset; /* only do it for things in this buffer page */
1562 /* info for the actual call */
1563 int counter; /* next free slot */
1564 AFSFid fids[CM_BULKMAX];
1565 AFSFetchStatus stats[CM_BULKMAX];
1566 AFSCallBack callbacks[CM_BULKMAX];
1569 /* for a given entry, make sure that it isn't in the stat cache, and then
1570 * add it to the list of file IDs to be obtained.
1572 * Don't bother adding it if we already have a vnode. Note that the dir
1573 * is locked, so we have to be careful checking the vnode we're thinking of
1574 * processing, to avoid deadlocks.
1576 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
1587 /* Don't overflow bsp. */
1588 if (bsp->counter >= CM_BULKMAX)
1589 return CM_ERROR_STOPNOW;
1591 thyper.LowPart = buf_bufferSize;
1592 thyper.HighPart = 0;
1593 thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
1595 /* thyper is now the first byte past the end of the record we're
1596 * interested in, and bsp->bufOffset is the first byte of the record
1597 * we're interested in.
1598 * Skip data in the others.
1601 if (LargeIntegerLessThan(*offp, bsp->bufOffset))
1603 if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
1604 return CM_ERROR_STOPNOW;
1605 if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
1608 tfid.cell = scp->fid.cell;
1609 tfid.volume = scp->fid.volume;
1610 tfid.vnode = ntohl(dep->fid.vnode);
1611 tfid.unique = ntohl(dep->fid.unique);
1612 tscp = cm_FindSCache(&tfid);
1614 if (lock_TryMutex(&tscp->mx)) {
1615 /* we have an entry that we can look at */
1616 if (cm_HaveCallback(tscp)) {
1617 /* we have a callback on it. Don't bother
1618 * fetching this stat entry, since we're happy
1619 * with the info we have.
1621 lock_ReleaseMutex(&tscp->mx);
1622 cm_ReleaseSCache(tscp);
1625 lock_ReleaseMutex(&tscp->mx);
1627 cm_ReleaseSCache(tscp);
1630 #ifdef AFS_FREELANCE_CLIENT
1631 // yj: if this is a mountpoint under root.afs then we don't want it
1632 // to be bulkstat-ed, instead, we call getSCache directly and under
1633 // getSCache, it is handled specially.
1634 if ( cm_freelanceEnabled &&
1635 tfid.cell==AFS_FAKE_ROOT_CELL_ID &&
1636 tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
1637 !(tfid.vnode==0x1 && tfid.unique==0x1) )
1639 osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
1640 return cm_GetSCache(&tfid, &tscp, NULL, NULL);
1642 #endif /* AFS_FREELANCE_CLIENT */
1645 bsp->fids[i].Volume = scp->fid.volume;
1646 bsp->fids[i].Vnode = tfid.vnode;
1647 bsp->fids[i].Unique = tfid.unique;
1651 /* called with a locked scp and a pointer to a buffer. Make bulk stat
1652 * calls on all undeleted files in the page of the directory specified.
1654 void cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
1658 cm_bulkStat_t bb; /* this is *BIG*, probably 12K or so;
1659 * watch for stack problems */
1660 AFSCBFids fidStruct;
1661 AFSBulkStats statStruct;
1663 AFSCBs callbackStruct;
1666 cm_callbackRequest_t cbReq;
1672 struct rx_connection * callp;
1674 osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%x", (long) dscp);
1676 /* should be on a buffer boundary */
1677 osi_assert((offsetp->LowPart & (buf_bufferSize - 1)) == 0);
1680 bb.bufOffset = *offsetp;
1682 lock_ReleaseMutex(&dscp->mx);
1683 /* first, assemble the file IDs we need to stat */
1684 code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) &bb, offsetp, userp, reqp, NULL);
1685 lock_ObtainMutex(&dscp->mx);
1687 /* if we failed, bail out early */
1688 if (code && code != CM_ERROR_STOPNOW) return;
1690 /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
1691 * make the calls to create the entries. Handle AFSCBMAX files at a
1695 while (filex < bb.counter) {
1696 filesThisCall = bb.counter - filex;
1697 if (filesThisCall > AFSCBMAX) filesThisCall = AFSCBMAX;
1699 fidStruct.AFSCBFids_len = filesThisCall;
1700 fidStruct.AFSCBFids_val = &bb.fids[filex];
1701 statStruct.AFSBulkStats_len = filesThisCall;
1702 statStruct.AFSBulkStats_val = &bb.stats[filex];
1703 callbackStruct.AFSCBs_len = filesThisCall;
1704 callbackStruct.AFSCBs_val = &bb.callbacks[filex];
1705 cm_StartCallbackGrantingCall(NULL, &cbReq);
1706 osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
1708 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1712 callp = cm_GetRxConn(connp);
1713 code = RXAFS_BulkStatus(callp, &fidStruct,
1714 &statStruct, &callbackStruct, &volSync);
1715 rx_PutConnection(callp);
1717 } while (cm_Analyze(connp, userp, reqp, &dscp->fid,
1718 &volSync, NULL, &cbReq, code));
1719 code = cm_MapRPCError(code, reqp);
1721 osi_Log0(afsd_logp, "CALL BulkStatus DONE");
1723 /* may as well quit on an error, since we're not going to do
1724 * much better on the next immediate call, either.
1729 /* otherwise, we should do the merges */
1730 for(i = 0; i<filesThisCall; i++) {
1732 tfid.cell = dscp->fid.cell;
1733 tfid.volume = bb.fids[j].Volume;
1734 tfid.vnode = bb.fids[j].Vnode;
1735 tfid.unique = bb.fids[j].Unique;
1736 code = cm_GetSCache(&tfid, &scp, userp, reqp);
1740 /* otherwise, if this entry has no callback info,
1743 lock_ObtainMutex(&scp->mx);
1744 /* now, we have to be extra paranoid on merging in this
1745 * information, since we didn't use cm_SyncOp before
1746 * starting the fetch to make sure that no bad races
1747 * were occurring. Specifically, we need to make sure
1748 * we don't obliterate any newer information in the
1749 * vnode than have here.
1751 * Right now, be pretty conservative: if there's a
1752 * callback or a pending call, skip it.
1754 if (scp->cbServerp == NULL
1756 (CM_SCACHEFLAG_FETCHING
1757 | CM_SCACHEFLAG_STORING
1758 | CM_SCACHEFLAG_SIZESTORING))) {
1759 cm_EndCallbackGrantingCall(scp, &cbReq,
1761 CM_CALLBACK_MAINTAINCOUNT);
1762 cm_MergeStatus(scp, &bb.stats[j], &volSync,
1765 lock_ReleaseMutex(&scp->mx);
1766 cm_ReleaseSCache(scp);
1767 } /* all files in the response */
1768 /* now tell it to drop the count,
1769 * after doing the vnode processing above */
1770 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
1772 filex += filesThisCall;
1773 } /* while there are still more files to process */
1774 osi_Log0(afsd_logp, "END cm_TryBulkStat");
1777 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
1781 /* initialize store back mask as inexpensive local variable */
1783 memset(statusp, 0, sizeof(AFSStoreStatus));
1785 /* copy out queued info from scache first, if scp passed in */
1787 if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
1788 statusp->ClientModTime = scp->clientModTime;
1789 mask |= AFS_SETMODTIME;
1790 scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
1795 /* now add in our locally generated request */
1796 if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
1797 statusp->ClientModTime = attrp->clientModTime;
1798 mask |= AFS_SETMODTIME;
1800 if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
1801 statusp->UnixModeBits = attrp->unixModeBits;
1802 mask |= AFS_SETMODE;
1804 if (attrp->mask & CM_ATTRMASK_OWNER) {
1805 statusp->Owner = attrp->owner;
1806 mask |= AFS_SETOWNER;
1808 if (attrp->mask & CM_ATTRMASK_GROUP) {
1809 statusp->Group = attrp->group;
1810 mask |= AFS_SETGROUP;
1813 statusp->Mask = mask;
1816 /* set the file size, and make sure that all relevant buffers have been
1817 * truncated. Ensure that any partially truncated buffers have been zeroed
1818 * to the end of the buffer.
1820 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
1826 /* start by locking out buffer creation */
1827 lock_ObtainWrite(&scp->bufCreateLock);
1829 /* verify that this is a file, not a dir or a symlink */
1830 lock_ObtainMutex(&scp->mx);
1831 code = cm_SyncOp(scp, NULL, userp, reqp, 0,
1832 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1836 if (scp->fileType != CM_SCACHETYPE_FILE) {
1837 code = CM_ERROR_ISDIR;
1842 if (LargeIntegerLessThan(*sizep, scp->length))
1847 lock_ReleaseMutex(&scp->mx);
1849 /* can't hold scp->mx lock here, since we may wait for a storeback to
1850 * finish if the buffer package is cleaning a buffer by storing it to
1854 buf_Truncate(scp, userp, reqp, sizep);
1856 /* now ensure that file length is short enough, and update truncPos */
1857 lock_ObtainMutex(&scp->mx);
1859 /* make sure we have a callback (so we have the right value for the
1860 * length), and wait for it to be safe to do a truncate.
1862 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
1863 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
1864 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
1868 if (LargeIntegerLessThan(*sizep, scp->length)) {
1869 /* a real truncation. If truncPos is not set yet, or is bigger
1870 * than where we're truncating the file, set truncPos to this
1875 if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
1876 || LargeIntegerLessThan(*sizep, scp->length)) {
1878 scp->truncPos = *sizep;
1879 scp->mask |= CM_SCACHEMASK_TRUNCPOS;
1881 /* in either case, the new file size has been changed */
1882 scp->length = *sizep;
1883 scp->mask |= CM_SCACHEMASK_LENGTH;
1885 else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
1886 /* really extending the file */
1887 scp->length = *sizep;
1888 scp->mask |= CM_SCACHEMASK_LENGTH;
1891 /* done successfully */
1895 lock_ReleaseMutex(&scp->mx);
1896 lock_ReleaseWrite(&scp->bufCreateLock);
1901 /* set the file size or other attributes (but not both at once) */
1902 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
1907 AFSFetchStatus afsOutStatus;
1911 AFSStoreStatus afsInStatus;
1912 struct rx_connection * callp;
1914 /* handle file length setting */
1915 if (attrp->mask & CM_ATTRMASK_LENGTH)
1916 return cm_SetLength(scp, &attrp->length, userp, reqp);
1918 flags = CM_SCACHESYNC_STORESTATUS;
1920 lock_ObtainMutex(&scp->mx);
1921 /* otherwise, we have to make an RPC to get the status */
1922 code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
1924 /* make the attr structure */
1925 cm_StatusFromAttr(&afsInStatus, scp, attrp);
1927 lock_ReleaseMutex(&scp->mx);
1931 /* now make the RPC */
1932 osi_Log1(afsd_logp, "CALL StoreStatus vp %x", (long) scp);
1933 tfid.Volume = scp->fid.volume;
1934 tfid.Vnode = scp->fid.vnode;
1935 tfid.Unique = scp->fid.unique;
1937 code = cm_Conn(&scp->fid, userp, reqp, &connp);
1941 callp = cm_GetRxConn(connp);
1942 code = RXAFS_StoreStatus(callp, &tfid,
1943 &afsInStatus, &afsOutStatus, &volSync);
1944 rx_PutConnection(callp);
1946 } while (cm_Analyze(connp, userp, reqp,
1947 &scp->fid, &volSync, NULL, NULL, code));
1948 code = cm_MapRPCError(code, reqp);
1950 osi_Log1(afsd_logp, "CALL StoreStatus DONE, code %d", code);
1952 lock_ObtainMutex(&scp->mx);
1953 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
1955 cm_MergeStatus(scp, &afsOutStatus, &volSync, userp,
1956 CM_MERGEFLAG_FORCE);
1958 /* if we're changing the mode bits, discard the ACL cache,
1959 * since we changed the mode bits.
1961 if (afsInStatus.Mask & AFS_SETMODE) cm_FreeAllACLEnts(scp);
1962 lock_ReleaseMutex(&scp->mx);
1966 long cm_Create(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
1967 cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
1972 cm_callbackRequest_t cbReq;
1977 AFSStoreStatus inStatus;
1978 AFSFetchStatus updatedDirStatus;
1979 AFSFetchStatus newFileStatus;
1980 AFSCallBack newFileCallback;
1982 struct rx_connection * callp;
1984 /* can't create names with @sys in them; must expand it manually first.
1985 * return "invalid request" if they try.
1987 if (cm_ExpandSysName(namep, NULL, 0, 0)) {
1988 return CM_ERROR_ATSYS;
1991 /* before starting the RPC, mark that we're changing the file data, so
1992 * that someone who does a chmod will know to wait until our call
1995 lock_ObtainMutex(&dscp->mx);
1996 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
1998 cm_StartCallbackGrantingCall(NULL, &cbReq);
2000 lock_ReleaseMutex(&dscp->mx);
2006 cm_StatusFromAttr(&inStatus, NULL, attrp);
2008 /* try the RPC now */
2010 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2014 dirAFSFid.Volume = dscp->fid.volume;
2015 dirAFSFid.Vnode = dscp->fid.vnode;
2016 dirAFSFid.Unique = dscp->fid.unique;
2018 callp = cm_GetRxConn(connp);
2019 code = RXAFS_CreateFile(connp->callp, &dirAFSFid, namep,
2020 &inStatus, &newAFSFid, &newFileStatus,
2021 &updatedDirStatus, &newFileCallback,
2023 rx_PutConnection(callp);
2025 } while (cm_Analyze(connp, userp, reqp,
2026 &dscp->fid, &volSync, NULL, &cbReq, code));
2027 code = cm_MapRPCError(code, reqp);
2029 lock_ObtainMutex(&dscp->mx);
2030 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2032 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2034 lock_ReleaseMutex(&dscp->mx);
2036 /* now try to create the file's entry, too, but be careful to
2037 * make sure that we don't merge in old info. Since we weren't locking
2038 * out any requests during the file's creation, we may have pretty old
2042 newFid.cell = dscp->fid.cell;
2043 newFid.volume = dscp->fid.volume;
2044 newFid.vnode = newAFSFid.Vnode;
2045 newFid.unique = newAFSFid.Unique;
2046 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2048 lock_ObtainMutex(&scp->mx);
2049 if (!cm_HaveCallback(scp)) {
2050 cm_MergeStatus(scp, &newFileStatus, &volSync,
2052 cm_EndCallbackGrantingCall(scp, &cbReq,
2053 &newFileCallback, 0);
2056 lock_ReleaseMutex(&scp->mx);
2061 /* make sure we end things properly */
2063 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2068 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
2072 lock_ObtainWrite(&scp->bufCreateLock);
2073 code = buf_CleanVnode(scp, userp, reqp);
2074 lock_ReleaseWrite(&scp->bufCreateLock);
2076 lock_ObtainMutex(&scp->mx);
2077 scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA
2078 | CM_SCACHEFLAG_OUTOFSPACE);
2079 if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
2080 | CM_SCACHEMASK_CLIENTMODTIME
2081 | CM_SCACHEMASK_LENGTH))
2082 code = cm_StoreMini(scp, userp, reqp);
2083 lock_ReleaseMutex(&scp->mx);
2088 long cm_MakeDir(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2089 cm_user_t *userp, cm_req_t *reqp)
2094 cm_callbackRequest_t cbReq;
2099 AFSStoreStatus inStatus;
2100 AFSFetchStatus updatedDirStatus;
2101 AFSFetchStatus newDirStatus;
2102 AFSCallBack newDirCallback;
2104 struct rx_connection * callp;
2106 /* can't create names with @sys in them; must expand it manually first.
2107 * return "invalid request" if they try.
2109 if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2110 return CM_ERROR_ATSYS;
2113 /* before starting the RPC, mark that we're changing the directory
2114 * data, so that someone who does a chmod on the dir will wait until
2115 * our call completes.
2117 lock_ObtainMutex(&dscp->mx);
2118 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2120 cm_StartCallbackGrantingCall(NULL, &cbReq);
2122 lock_ReleaseMutex(&dscp->mx);
2128 cm_StatusFromAttr(&inStatus, NULL, attrp);
2130 /* try the RPC now */
2132 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2136 dirAFSFid.Volume = dscp->fid.volume;
2137 dirAFSFid.Vnode = dscp->fid.vnode;
2138 dirAFSFid.Unique = dscp->fid.unique;
2140 callp = cm_GetRxConn(connp);
2141 code = RXAFS_MakeDir(connp->callp, &dirAFSFid, namep,
2142 &inStatus, &newAFSFid, &newDirStatus,
2143 &updatedDirStatus, &newDirCallback,
2145 rx_PutConnection(callp);
2147 } while (cm_Analyze(connp, userp, reqp,
2148 &dscp->fid, &volSync, NULL, &cbReq, code));
2149 code = cm_MapRPCError(code, reqp);
2151 lock_ObtainMutex(&dscp->mx);
2152 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2154 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2156 lock_ReleaseMutex(&dscp->mx);
2158 /* now try to create the new dir's entry, too, but be careful to
2159 * make sure that we don't merge in old info. Since we weren't locking
2160 * out any requests during the file's creation, we may have pretty old
2164 newFid.cell = dscp->fid.cell;
2165 newFid.volume = dscp->fid.volume;
2166 newFid.vnode = newAFSFid.Vnode;
2167 newFid.unique = newAFSFid.Unique;
2168 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2170 lock_ObtainMutex(&scp->mx);
2171 if (!cm_HaveCallback(scp)) {
2172 cm_MergeStatus(scp, &newDirStatus, &volSync,
2174 cm_EndCallbackGrantingCall(scp, &cbReq,
2175 &newDirCallback, 0);
2178 lock_ReleaseMutex(&scp->mx);
2179 cm_ReleaseSCache(scp);
2183 /* make sure we end things properly */
2185 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2187 /* and return error code */
2191 long cm_Link(cm_scache_t *dscp, char *namep, cm_scache_t *sscp, long flags,
2192 cm_user_t *userp, cm_req_t *reqp)
2197 AFSFid existingAFSFid;
2198 AFSFetchStatus updatedDirStatus;
2199 AFSFetchStatus newLinkStatus;
2201 struct rx_connection * callp;
2203 if (dscp->fid.cell != sscp->fid.cell ||
2204 dscp->fid.volume != sscp->fid.volume) {
2205 return CM_ERROR_CROSSDEVLINK;
2208 lock_ObtainMutex(&dscp->mx);
2209 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2210 lock_ReleaseMutex(&dscp->mx);
2216 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2219 dirAFSFid.Volume = dscp->fid.volume;
2220 dirAFSFid.Vnode = dscp->fid.vnode;
2221 dirAFSFid.Unique = dscp->fid.unique;
2223 existingAFSFid.Volume = sscp->fid.volume;
2224 existingAFSFid.Vnode = sscp->fid.vnode;
2225 existingAFSFid.Unique = sscp->fid.unique;
2227 callp = cm_GetRxConn(connp);
2228 code = RXAFS_Link(callp, &dirAFSFid, namep, &existingAFSFid,
2229 &newLinkStatus, &updatedDirStatus, &volSync);
2230 rx_PutConnection(callp);
2231 osi_Log1(smb_logp," RXAFS_Link returns %d", code);
2233 } while (cm_Analyze(connp, userp, reqp,
2234 &dscp->fid, &volSync, NULL, NULL, code));
2236 code = cm_MapRPCError(code, reqp);
2238 lock_ObtainMutex(&dscp->mx);
2239 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2241 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2243 lock_ReleaseMutex(&dscp->mx);
2248 long cm_SymLink(cm_scache_t *dscp, char *namep, char *contentsp, long flags,
2249 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
2257 AFSStoreStatus inStatus;
2258 AFSFetchStatus updatedDirStatus;
2259 AFSFetchStatus newLinkStatus;
2261 struct rx_connection * callp;
2263 /* before starting the RPC, mark that we're changing the directory data,
2264 * so that someone who does a chmod on the dir will wait until our
2267 lock_ObtainMutex(&dscp->mx);
2268 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2269 lock_ReleaseMutex(&dscp->mx);
2274 cm_StatusFromAttr(&inStatus, NULL, attrp);
2276 /* try the RPC now */
2278 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2282 dirAFSFid.Volume = dscp->fid.volume;
2283 dirAFSFid.Vnode = dscp->fid.vnode;
2284 dirAFSFid.Unique = dscp->fid.unique;
2286 callp = cm_GetRxConn(connp);
2287 code = RXAFS_Symlink(callp, &dirAFSFid, namep, contentsp,
2288 &inStatus, &newAFSFid, &newLinkStatus,
2289 &updatedDirStatus, &volSync);
2290 rx_PutConnection(callp);
2292 } while (cm_Analyze(connp, userp, reqp,
2293 &dscp->fid, &volSync, NULL, NULL, code));
2294 code = cm_MapRPCError(code, reqp);
2296 lock_ObtainMutex(&dscp->mx);
2297 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2299 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2301 lock_ReleaseMutex(&dscp->mx);
2303 /* now try to create the new dir's entry, too, but be careful to
2304 * make sure that we don't merge in old info. Since we weren't locking
2305 * out any requests during the file's creation, we may have pretty old
2309 newFid.cell = dscp->fid.cell;
2310 newFid.volume = dscp->fid.volume;
2311 newFid.vnode = newAFSFid.Vnode;
2312 newFid.unique = newAFSFid.Unique;
2313 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2315 lock_ObtainMutex(&scp->mx);
2316 if (!cm_HaveCallback(scp)) {
2317 cm_MergeStatus(scp, &newLinkStatus, &volSync,
2320 lock_ReleaseMutex(&scp->mx);
2321 cm_ReleaseSCache(scp);
2325 /* and return error code */
2329 long cm_RemoveDir(cm_scache_t *dscp, char *namep, cm_user_t *userp,
2336 AFSFetchStatus updatedDirStatus;
2338 struct rx_connection * callp;
2340 /* before starting the RPC, mark that we're changing the directory data,
2341 * so that someone who does a chmod on the dir will wait until our
2344 lock_ObtainMutex(&dscp->mx);
2345 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2346 lock_ReleaseMutex(&dscp->mx);
2352 /* try the RPC now */
2354 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2358 dirAFSFid.Volume = dscp->fid.volume;
2359 dirAFSFid.Vnode = dscp->fid.vnode;
2360 dirAFSFid.Unique = dscp->fid.unique;
2362 callp = cm_GetRxConn(connp);
2363 code = RXAFS_RemoveDir(callp, &dirAFSFid, namep,
2364 &updatedDirStatus, &volSync);
2365 rx_PutConnection(callp);
2367 } while (cm_Analyze(connp, userp, reqp,
2368 &dscp->fid, &volSync, NULL, NULL, code));
2369 code = cm_MapRPCErrorRmdir(code, reqp);
2371 lock_ObtainMutex(&dscp->mx);
2372 cm_dnlcRemove(dscp, namep);
2373 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2375 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2377 lock_ReleaseMutex(&dscp->mx);
2379 /* and return error code */
2383 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
2385 /* grab mutex on contents */
2386 lock_ObtainMutex(&scp->mx);
2388 /* reset the prefetch info */
2389 scp->prefetch.base.LowPart = 0; /* base */
2390 scp->prefetch.base.HighPart = 0;
2391 scp->prefetch.end.LowPart = 0; /* and end */
2392 scp->prefetch.end.HighPart = 0;
2394 /* release mutex on contents */
2395 lock_ReleaseMutex(&scp->mx);
2401 long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
2402 char *newNamep, cm_user_t *userp, cm_req_t *reqp)
2406 AFSFid oldDirAFSFid;
2407 AFSFid newDirAFSFid;
2409 AFSFetchStatus updatedOldDirStatus;
2410 AFSFetchStatus updatedNewDirStatus;
2413 struct rx_connection * callp;
2415 /* before starting the RPC, mark that we're changing the directory data,
2416 * so that someone who does a chmod on the dir will wait until our call
2417 * completes. We do this in vnode order so that we don't deadlock,
2418 * which makes the code a little verbose.
2420 if (oldDscp == newDscp) {
2421 /* check for identical names */
2422 if (strcmp(oldNamep, newNamep) == 0)
2423 return CM_ERROR_RENAME_IDENTICAL;
2426 lock_ObtainMutex(&oldDscp->mx);
2427 cm_dnlcRemove(oldDscp, oldNamep);
2428 cm_dnlcRemove(oldDscp, newNamep);
2429 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2430 CM_SCACHESYNC_STOREDATA);
2431 lock_ReleaseMutex(&oldDscp->mx);
2434 /* two distinct dir vnodes */
2436 if (oldDscp->fid.cell != newDscp->fid.cell ||
2437 oldDscp->fid.volume != newDscp->fid.volume)
2438 return CM_ERROR_CROSSDEVLINK;
2440 /* shouldn't happen that we have distinct vnodes for two
2441 * different files, but could due to deliberate attack, or
2442 * stale info. Avoid deadlocks and quit now.
2444 if (oldDscp->fid.vnode == newDscp->fid.vnode)
2445 return CM_ERROR_CROSSDEVLINK;
2447 if (oldDscp->fid.vnode < newDscp->fid.vnode) {
2448 lock_ObtainMutex(&oldDscp->mx);
2449 cm_dnlcRemove(oldDscp, oldNamep);
2450 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2451 CM_SCACHESYNC_STOREDATA);
2452 lock_ReleaseMutex(&oldDscp->mx);
2454 lock_ObtainMutex(&newDscp->mx);
2455 cm_dnlcRemove(newDscp, newNamep);
2456 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2457 CM_SCACHESYNC_STOREDATA);
2458 lock_ReleaseMutex(&newDscp->mx);
2460 /* cleanup first one */
2461 cm_SyncOpDone(oldDscp, NULL,
2462 CM_SCACHESYNC_STOREDATA);
2467 /* lock the new vnode entry first */
2468 lock_ObtainMutex(&newDscp->mx);
2469 cm_dnlcRemove(newDscp, newNamep);
2470 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2471 CM_SCACHESYNC_STOREDATA);
2472 lock_ReleaseMutex(&newDscp->mx);
2474 lock_ObtainMutex(&oldDscp->mx);
2475 cm_dnlcRemove(oldDscp, oldNamep);
2476 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2477 CM_SCACHESYNC_STOREDATA);
2478 lock_ReleaseMutex(&oldDscp->mx);
2480 /* cleanup first one */
2481 cm_SyncOpDone(newDscp, NULL,
2482 CM_SCACHESYNC_STOREDATA);
2486 } /* two distinct vnodes */
2493 /* try the RPC now */
2495 code = cm_Conn(&oldDscp->fid, userp, reqp, &connp);
2499 oldDirAFSFid.Volume = oldDscp->fid.volume;
2500 oldDirAFSFid.Vnode = oldDscp->fid.vnode;
2501 oldDirAFSFid.Unique = oldDscp->fid.unique;
2502 newDirAFSFid.Volume = newDscp->fid.volume;
2503 newDirAFSFid.Vnode = newDscp->fid.vnode;
2504 newDirAFSFid.Unique = newDscp->fid.unique;
2506 callp = cm_GetRxConn(connp);
2507 code = RXAFS_Rename(callp, &oldDirAFSFid, oldNamep,
2508 &newDirAFSFid, newNamep,
2509 &updatedOldDirStatus, &updatedNewDirStatus,
2511 rx_PutConnection(callp);
2513 } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
2514 &volSync, NULL, NULL, code));
2515 code = cm_MapRPCError(code, reqp);
2517 /* update the individual stat cache entries for the directories */
2518 lock_ObtainMutex(&oldDscp->mx);
2519 cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
2521 cm_MergeStatus(oldDscp, &updatedOldDirStatus, &volSync,
2524 lock_ReleaseMutex(&oldDscp->mx);
2526 /* and update it for the new one, too, if necessary */
2528 lock_ObtainMutex(&newDscp->mx);
2529 cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
2531 cm_MergeStatus(newDscp, &updatedNewDirStatus, &volSync,
2534 lock_ReleaseMutex(&newDscp->mx);
2537 /* and return error code */
2541 long cm_Lock(cm_scache_t *scp, unsigned char LockType,
2542 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
2543 u_long Timeout, cm_user_t *userp, cm_req_t *reqp,
2547 int Which = ((LockType & 0x1) ? LockRead : LockWrite);
2551 cm_file_lock_t *fileLock;
2554 struct rx_connection * callp;
2556 /* Look for a conflict. Also, if we are asking for a shared lock,
2557 * look for another shared lock, so we don't have to do an RPC.
2561 fileLock = (cm_file_lock_t *)
2562 ((char *) q - offsetof(cm_file_lock_t, fileq));
2563 if ((fileLock->flags &
2564 (CM_FILELOCK_FLAG_INVALID | CM_FILELOCK_FLAG_WAITING))
2566 if ((LockType & 0x1) == 0
2567 || (fileLock->LockType & 0x1) == 0)
2568 return CM_ERROR_WOULDBLOCK;
2577 tfid.Volume = scp->fid.volume;
2578 tfid.Vnode = scp->fid.vnode;
2579 tfid.Unique = scp->fid.unique;
2580 lock_ReleaseMutex(&scp->mx);
2582 code = cm_Conn(&scp->fid, userp, reqp, &connp);
2586 callp = cm_GetRxConn(connp);
2587 code = RXAFS_SetLock(callp, &tfid, Which,
2589 rx_PutConnection(callp);
2591 } while (cm_Analyze(connp, userp, reqp, &scp->fid, &volSync,
2593 lock_ObtainMutex(&scp->mx);
2594 code = cm_MapRPCError(code, reqp);
2597 if (code == 0 || Timeout != 0) {
2598 fileLock = malloc(sizeof(cm_file_lock_t));
2599 fileLock->LockType = LockType;
2601 fileLock->userp = userp;
2602 fileLock->fid = scp->fid;
2603 fileLock->LOffset = LOffset;
2604 fileLock->LLength = LLength;
2605 fileLock->flags = (code == 0 ? 0 : CM_FILELOCK_FLAG_WAITING);
2606 osi_QAdd(&scp->fileLocks, &fileLock->fileq);
2607 lock_ObtainWrite(&cm_scacheLock);
2608 osi_QAdd(&cm_allFileLocks, &fileLock->q);
2609 lock_ReleaseWrite(&cm_scacheLock);
2616 long cm_Unlock(cm_scache_t *scp, unsigned char LockType,
2617 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
2618 cm_user_t *userp, cm_req_t *reqp)
2621 int Which = ((LockType & 0x1) ? LockRead : LockWrite);
2625 cm_file_lock_t *fileLock, *ourLock;
2626 osi_queue_t *q, *qq;
2627 int anotherReader = 0;
2630 struct rx_connection * callp;
2632 if (LargeIntegerLessThan(LLength, scp->length))
2635 /* Look for our own lock on the list, so as to remove it.
2636 * Also, determine if we're the last reader; if not, avoid an RPC.
2640 fileLock = (cm_file_lock_t *)
2641 ((char *) q - offsetof(cm_file_lock_t, fileq));
2643 && fileLock->userp == userp
2644 && LargeIntegerEqualTo(fileLock->LOffset, LOffset)
2645 && LargeIntegerEqualTo(fileLock->LLength, LLength)) {
2650 else if (fileLock->LockType & 0x1)
2655 /* ignore byte ranges */
2656 if (smallLock && !found)
2659 /* don't try to unlock other people's locks */
2661 return CM_ERROR_WOULDBLOCK;
2663 /* discard lock record */
2664 osi_QRemove(&scp->fileLocks, qq);
2666 * Don't delete it here; let the daemon delete it, to simplify
2667 * the daemon's traversal of the list.
2669 lock_ObtainWrite(&cm_scacheLock);
2670 ourLock->flags |= CM_FILELOCK_FLAG_INVALID;
2671 cm_ReleaseUser(ourLock->userp);
2672 lock_ReleaseWrite(&cm_scacheLock);
2674 if (!anotherReader) {
2675 tfid.Volume = scp->fid.volume;
2676 tfid.Vnode = scp->fid.vnode;
2677 tfid.Unique = scp->fid.unique;
2678 lock_ReleaseMutex(&scp->mx);
2680 code = cm_Conn(&scp->fid, userp, reqp, &connp);
2684 callp = cm_GetRxConn(connp);
2685 code = RXAFS_ReleaseLock(callp, &tfid, &volSync);
2686 rx_PutConnection(callp);
2688 } while (cm_Analyze(connp, userp, reqp, &scp->fid, &volSync,
2690 code = cm_MapRPCError(code, reqp);
2691 lock_ObtainMutex(&scp->mx);
2697 void cm_CheckLocks()
2699 osi_queue_t *q, *nq;
2700 cm_file_lock_t *fileLock;
2706 struct rx_connection * callp;
2710 lock_ObtainWrite(&cm_scacheLock);
2711 q = cm_allFileLocks;
2713 fileLock = (cm_file_lock_t *) q;
2715 if (fileLock->flags & CM_FILELOCK_FLAG_INVALID) {
2716 osi_QRemove(&cm_allFileLocks, q);
2719 else if (!(fileLock->flags & CM_FILELOCK_FLAG_WAITING)) {
2720 tfid.Volume = fileLock->fid.volume;
2721 tfid.Vnode = fileLock->fid.vnode;
2722 tfid.Unique = fileLock->fid.unique;
2723 lock_ReleaseWrite(&cm_scacheLock);
2725 code = cm_Conn(&fileLock->fid, fileLock->userp,
2730 callp = cm_GetRxConn(connp);
2731 code = RXAFS_ExtendLock(callp, &tfid,
2733 rx_PutConnection(callp);
2735 } while (cm_Analyze(connp, fileLock->userp, &req,
2736 &fileLock->fid, &volSync, NULL, NULL,
2738 code = cm_MapRPCError(code, &req);
2739 lock_ObtainWrite(&cm_scacheLock);
2743 lock_ReleaseWrite(&cm_scacheLock);
2746 long cm_RetryLock(cm_file_lock_t *oldFileLock, int vcp_is_dead)
2749 int Which = ((oldFileLock->LockType & 0x1) ? LockRead : LockWrite);
2754 cm_file_lock_t *fileLock;
2758 struct rx_connection * callp;
2761 code = CM_ERROR_TIMEDOUT;
2767 /* Look for a conflict. Also, if we are asking for a shared lock,
2768 * look for another shared lock, so we don't have to do an RPC.
2770 code = cm_GetSCache(&oldFileLock->fid, &scp, oldFileLock->userp, &req);
2776 fileLock = (cm_file_lock_t *)
2777 ((char *) q - offsetof(cm_file_lock_t, fileq));
2778 if ((fileLock->flags &
2779 (CM_FILELOCK_FLAG_INVALID | CM_FILELOCK_FLAG_WAITING))
2781 if ((oldFileLock->LockType & 0x1) == 0
2782 || (fileLock->LockType & 0x1) == 0) {
2783 cm_ReleaseSCache(scp);
2784 return CM_ERROR_WOULDBLOCK;
2794 tfid.Volume = oldFileLock->fid.volume;
2795 tfid.Vnode = oldFileLock->fid.vnode;
2796 tfid.Unique = oldFileLock->fid.unique;
2798 code = cm_Conn(&oldFileLock->fid, oldFileLock->userp,
2803 callp = cm_GetRxConn(connp);
2804 code = RXAFS_SetLock(callp, &tfid, Which,
2806 rx_PutConnection(callp);
2808 } while (cm_Analyze(connp, oldFileLock->userp, &req,
2809 &oldFileLock->fid, &volSync,
2811 code = cm_MapRPCError(code, &req);
2815 if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
2816 lock_ObtainMutex(&scp->mx);
2817 osi_QRemove(&scp->fileLocks, &oldFileLock->fileq);
2818 lock_ReleaseMutex(&scp->mx);
2820 lock_ObtainWrite(&cm_scacheLock);
2822 oldFileLock->flags = 0;
2823 else if (code != CM_ERROR_WOULDBLOCK) {
2824 oldFileLock->flags |= CM_FILELOCK_FLAG_INVALID;
2825 cm_ReleaseUser(oldFileLock->userp);
2826 oldFileLock->userp = NULL;
2828 lock_ReleaseWrite(&cm_scacheLock);