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>
27 extern void afsi_log(char *pattern, ...);
30 int cm_enableServerLocks = 1;
32 int cm_followBackupPath = 0;
35 * Case-folding array. This was constructed by inspecting of SMBtrace output.
36 * I do not know anything more about it.
38 unsigned char cm_foldUpper[256] = {
39 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
40 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
41 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
42 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
43 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
44 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
45 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
46 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
47 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
48 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
49 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
50 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
51 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
52 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
53 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
54 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
55 0x80, 0x9a, 0x90, 0x41, 0x8e, 0x41, 0x8f, 0x80,
56 0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x8e, 0x8f,
57 0x90, 0x92, 0x92, 0x4f, 0x99, 0x4f, 0x55, 0x55,
58 0x59, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
59 0x41, 0x49, 0x4f, 0x55, 0xa5, 0xa5, 0x56, 0xa7,
60 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
61 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
62 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
63 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
64 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
65 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
66 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
67 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
68 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
69 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
70 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
74 * Case-insensitive string comparison. We used to use stricmp, but it doesn't
75 * know about 8-bit characters (e.g. 129 is lowercase u-umlaut, 154 is
76 * upper-case u-umlaut).
78 int cm_stricmp(const char *str1, const char *str2)
90 c1 = (char) cm_foldUpper[(unsigned char)(*str1++)];
91 c2 = (char) cm_foldUpper[(unsigned char)(*str2++)];
99 /* characters that are legal in an 8.3 name */
101 * We used to have 1's for all characters from 128 to 254. But
102 * the NT client behaves better if we create an 8.3 name for any
103 * name that has a character with the high bit on, and if we
104 * delete those characters from 8.3 names. In particular, see
105 * Sybase defect 10859.
107 char cm_LegalChars[256] = {
108 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
109 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
110 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0,
111 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
112 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
113 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1,
114 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
115 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1,
116 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
117 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
118 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
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
126 /* return true iff component is a valid 8.3 name */
127 int cm_Is8Dot3(char *namep)
134 * can't have a leading dot;
135 * special case for . and ..
137 if (namep[0] == '.') {
140 if (namep[1] == '.' && namep[2] == 0)
144 while (tc = *namep++) {
146 /* saw another dot */
147 if (sawDot) return 0; /* second dot */
152 if (cm_LegalChars[tc] == 0)
155 if (!sawDot && charCount > 8)
156 /* more than 8 chars in name */
158 if (sawDot && charCount > 3)
159 /* more than 3 chars in extension */
166 * Number unparsing map for generating 8.3 names;
167 * The version taken from DFS was on drugs.
168 * You can't include '&' and '@' in a file name.
170 char cm_8Dot3Mapping[42] =
171 {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
172 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K',
173 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U',
174 'V', 'W', 'X', 'Y', 'Z', '_', '-', '$', '#', '!', '+', '='
176 int cm_8Dot3MapSize = sizeof(cm_8Dot3Mapping);
178 void cm_Gen8Dot3NameInt(const char * longname, cm_dirFid_t * pfid,
179 char *shortName, char **shortNameEndp)
183 int vnode = ntohl(pfid->vnode);
185 int validExtension = 0;
189 /* Unparse the file's vnode number to get a "uniquifier" */
191 number[nsize] = cm_8Dot3Mapping[vnode % cm_8Dot3MapSize];
193 vnode /= cm_8Dot3MapSize;
197 * Look for valid extension. There has to be a dot, and
198 * at least one of the characters following has to be legal.
200 lastDot = strrchr(longname, '.');
202 temp = lastDot; temp++;
204 if (cm_LegalChars[tc])
210 /* Copy name characters */
211 for (i = 0, name = longname;
212 i < (7 - nsize) && name != lastDot; ) {
217 if (!cm_LegalChars[tc])
220 *shortName++ = toupper(tc);
226 /* Copy uniquifier characters */
227 memcpy(shortName, number, nsize);
230 if (validExtension) {
231 /* Copy extension characters */
232 *shortName++ = *lastDot++; /* copy dot */
233 for (i = 0, tc = *lastDot++;
236 if (cm_LegalChars[tc]) {
238 *shortName++ = toupper(tc);
247 *shortNameEndp = shortName;
250 /* return success if we can open this file in this mode */
251 long cm_CheckOpen(cm_scache_t *scp, int openMode, int trunc, cm_user_t *userp,
259 rights |= PRSFS_READ;
260 if (openMode == 1 || openMode == 2 || trunc)
261 rights |= PRSFS_WRITE;
263 lock_ObtainMutex(&scp->mx);
265 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
266 CM_SCACHESYNC_GETSTATUS
267 | CM_SCACHESYNC_NEEDCALLBACK
268 | CM_SCACHESYNC_LOCK);
271 ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
272 scp->fileType == CM_SCACHETYPE_FILE) {
275 unsigned int sLockType;
276 LARGE_INTEGER LOffset, LLength;
278 /* Check if there's some sort of lock on the file at the
281 key = cm_GenerateKey(CM_SESSION_CMINT,0,0);
283 if (rights & PRSFS_WRITE)
286 sLockType = LOCKING_ANDX_SHARED_LOCK;
288 LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
289 LOffset.LowPart = CM_FLSHARE_OFFSET_LOW;
290 LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
291 LLength.LowPart = CM_FLSHARE_LENGTH_LOW;
293 code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
296 cm_Unlock(scp, sLockType, LOffset, LLength, key, userp, reqp);
298 /* In this case, we allow the file open to go through even
299 though we can't enforce mandatory locking on the
301 if (code == CM_ERROR_NOACCESS &&
302 !(rights & PRSFS_WRITE))
306 case CM_ERROR_ALLOFFLINE:
307 case CM_ERROR_ALLDOWN:
308 case CM_ERROR_ALLBUSY:
309 case CM_ERROR_TIMEDOUT:
311 case CM_ERROR_WOULDBLOCK:
314 code = CM_ERROR_SHARING_VIOLATION;
319 } else if (code != 0) {
323 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
327 lock_ReleaseMutex(&scp->mx);
332 /* return success if we can open this file in this mode */
333 long cm_CheckNTOpen(cm_scache_t *scp, unsigned int desiredAccess,
334 unsigned int createDisp, cm_user_t *userp, cm_req_t *reqp,
335 cm_lock_data_t **ldpp)
340 osi_assertx(ldpp != NULL, "null cm_lock_data_t");
343 /* Always allow delete; the RPC will tell us if it's OK */
344 if (desiredAccess == DELETE)
349 if (desiredAccess & AFS_ACCESS_READ)
350 rights |= (scp->fileType == CM_SCACHETYPE_DIRECTORY ? PRSFS_LOOKUP : PRSFS_READ);
352 /* We used to require PRSFS_WRITE if createDisp was 4
353 (OPEN_ALWAYS) even if AFS_ACCESS_WRITE was not requested.
354 However, we don't need to do that since the existence of the
355 scp implies that we don't need to create it. */
356 if (desiredAccess & AFS_ACCESS_WRITE)
357 rights |= PRSFS_WRITE;
359 lock_ObtainMutex(&scp->mx);
361 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
362 CM_SCACHESYNC_GETSTATUS
363 | CM_SCACHESYNC_NEEDCALLBACK
364 | CM_SCACHESYNC_LOCK);
367 * If the open will fail because the volume is readonly, then we will
368 * return an access denied error instead. This is to help brain-dead
369 * apps run correctly on replicated volumes.
370 * See defect 10007 for more information.
372 if (code == CM_ERROR_READONLY)
373 code = CM_ERROR_NOACCESS;
376 ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
377 scp->fileType == CM_SCACHETYPE_FILE) {
379 unsigned int sLockType;
380 LARGE_INTEGER LOffset, LLength;
382 /* Check if there's some sort of lock on the file at the
385 key = cm_GenerateKey(CM_SESSION_CMINT,0,0);
386 if (rights & PRSFS_WRITE)
389 sLockType = LOCKING_ANDX_SHARED_LOCK;
391 /* single byte lock at offset 0x0100 0000 0000 0000 */
392 LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
393 LOffset.LowPart = CM_FLSHARE_OFFSET_LOW;
394 LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
395 LLength.LowPart = CM_FLSHARE_LENGTH_LOW;
397 code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
400 (*ldpp) = (cm_lock_data_t *)malloc(sizeof(cm_lock_data_t));
407 (*ldpp)->sLockType = sLockType;
408 (*ldpp)->LOffset.HighPart = LOffset.HighPart;
409 (*ldpp)->LOffset.LowPart = LOffset.LowPart;
410 (*ldpp)->LLength.HighPart = LLength.HighPart;
411 (*ldpp)->LLength.LowPart = LLength.LowPart;
413 /* In this case, we allow the file open to go through even
414 though we can't enforce mandatory locking on the
416 if (code == CM_ERROR_NOACCESS &&
417 !(rights & PRSFS_WRITE))
421 case CM_ERROR_ALLOFFLINE:
422 case CM_ERROR_ALLDOWN:
423 case CM_ERROR_ALLBUSY:
424 case CM_ERROR_TIMEDOUT:
426 case CM_ERROR_WOULDBLOCK:
429 code = CM_ERROR_SHARING_VIOLATION;
433 } else if (code != 0) {
437 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
440 lock_ReleaseMutex(&scp->mx);
445 extern long cm_CheckNTOpenDone(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp,
446 cm_lock_data_t ** ldpp)
449 lock_ObtainMutex(&scp->mx);
450 cm_Unlock(scp, (*ldpp)->sLockType, (*ldpp)->LOffset, (*ldpp)->LLength,
451 (*ldpp)->key, userp, reqp);
452 lock_ReleaseMutex(&scp->mx);
459 * When CAP_NT_SMBS has been negotiated, deletion (of files or directories) is
460 * done in three steps:
461 * (1) open for deletion (NT_CREATE_AND_X)
462 * (2) set for deletion on close (NT_TRANSACTION2, SET_FILE_INFO)
464 * We must not do the RPC until step 3. But if we are going to return an error
465 * code (e.g. directory not empty), we must return it by step 2, otherwise most
466 * clients will not notice it. So we do a preliminary check. For deleting
467 * files, this is almost free, since we have already done the RPC to get the
468 * parent directory's status bits. But for deleting directories, we must do an
469 * additional RPC to get the directory's data to check if it is empty. Sigh.
471 long cm_CheckNTDelete(cm_scache_t *dscp, cm_scache_t *scp, cm_user_t *userp,
478 unsigned short *hashTable;
480 int BeyondPage = 0, HaveDot = 0, HaveDotDot = 0;
482 /* First check permissions */
483 lock_ObtainMutex(&scp->mx);
484 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_DELETE,
485 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
486 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
487 lock_ReleaseMutex(&scp->mx);
491 /* If deleting directory, must be empty */
493 if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
496 thyper.HighPart = 0; thyper.LowPart = 0;
497 lock_ObtainRead(&scp->bufCreateLock);
498 code = buf_Get(scp, &thyper, &bufferp);
499 lock_ReleaseRead(&scp->bufCreateLock);
503 lock_ObtainMutex(&bufferp->mx);
504 lock_ObtainMutex(&scp->mx);
506 code = cm_SyncOp(scp, bufferp, userp, reqp, 0,
507 CM_SCACHESYNC_NEEDCALLBACK
509 | CM_SCACHESYNC_BUFLOCKED);
513 if (cm_HaveBuffer(scp, bufferp, 1))
516 /* otherwise, load the buffer and try again */
517 lock_ReleaseMutex(&bufferp->mx);
518 code = cm_GetBuffer(scp, bufferp, NULL, userp, reqp);
519 lock_ReleaseMutex(&scp->mx);
520 lock_ObtainMutex(&bufferp->mx);
521 lock_ObtainMutex(&scp->mx);
522 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
527 /* We try to determine emptiness without looking beyond the first page,
528 * and without assuming "." and ".." are present and are on the first
529 * page (though these assumptions might, after all, be reasonable).
531 hashTable = (unsigned short *)(bufferp->datap + (32 * 5));
532 for (i=0; i<128; i++) {
533 idx = ntohs(hashTable[i]);
539 dep = (cm_dirEntry_t *)(bufferp->datap + (32 * idx));
540 if (strcmp(dep->name, ".") == 0)
542 else if (strcmp(dep->name, "..") == 0)
545 code = CM_ERROR_NOTEMPTY;
548 idx = ntohs(dep->next);
551 if (BeyondPage && HaveDot && HaveDotDot)
552 code = CM_ERROR_NOTEMPTY;
556 lock_ReleaseMutex(&bufferp->mx);
557 buf_Release(bufferp);
558 lock_ReleaseMutex(&scp->mx);
563 * Iterate through all entries in a directory.
564 * When the function funcp is called, the buffer is locked but the
565 * directory vnode is not.
567 * If the retscp parameter is not NULL, the parmp must be a
568 * cm_lookupSearch_t object.
570 long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
571 osi_hyper_t *startOffsetp, cm_user_t *userp, cm_req_t *reqp,
572 cm_scache_t **retscp)
579 osi_hyper_t dirLength;
580 osi_hyper_t bufferOffset;
581 osi_hyper_t curOffset;
585 cm_pageHeader_t *pageHeaderp;
587 long nextEntryCookie;
588 int numDirChunks; /* # of 32 byte dir chunks in this entry */
590 /* get the directory size */
591 lock_ObtainMutex(&scp->mx);
592 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_LOOKUP,
593 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
594 lock_ReleaseMutex(&scp->mx);
598 if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
599 return CM_ERROR_NOTDIR;
601 if (retscp) /* if this is a lookup call */
603 cm_lookupSearch_t* sp = parmp;
606 #ifdef AFS_FREELANCE_CLIENT
607 /* Freelance entries never end up in the DNLC because they
608 * do not have an associated cm_server_t
610 !(cm_freelanceEnabled &&
611 sp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
612 sp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
613 #else /* !AFS_FREELANCE_CLIENT */
618 int casefold = sp->caseFold;
619 sp->caseFold = 0; /* we have a strong preference for exact matches */
620 if ( *retscp = cm_dnlcLookup(scp, sp)) /* dnlc hit */
622 sp->caseFold = casefold;
625 sp->caseFold = casefold;
627 /* see if we can find it using the directory hash tables.
628 we can only do exact matches, since the hash is case
638 code = cm_BeginDirOp(scp, userp, reqp, CM_DIRLOCK_READ, &dirop);
642 code = cm_BPlusDirLookup(&dirop, sp->searchNamep, &sp->fid);
647 code = cm_DirLookup(&dirop, sp->searchNamep, &sp->fid);
655 sp->ExactFound = TRUE;
656 *retscp = NULL; /* force caller to call cm_GetSCache() */
661 if (sp->caseFold && code == CM_ERROR_INEXACT_MATCH) {
664 sp->ExactFound = FALSE;
665 *retscp = NULL; /* force caller to call cm_GetSCache() */
669 return CM_ERROR_BPLUS_NOMATCH;
677 * XXX We only get the length once. It might change when we drop the
680 dirLength = scp->length;
683 bufferOffset.LowPart = bufferOffset.HighPart = 0;
685 curOffset = *startOffsetp;
687 curOffset.HighPart = 0;
688 curOffset.LowPart = 0;
692 /* make sure that curOffset.LowPart doesn't point to the first
693 * 32 bytes in the 2nd through last dir page, and that it
694 * doesn't point at the first 13 32-byte chunks in the first
695 * dir page, since those are dir and page headers, and don't
696 * contain useful information.
698 temp = curOffset.LowPart & (2048-1);
699 if (curOffset.HighPart == 0 && curOffset.LowPart < 2048) {
700 /* we're in the first page */
701 if (temp < 13*32) temp = 13*32;
704 /* we're in a later dir page */
705 if (temp < 32) temp = 32;
708 /* make sure the low order 5 bits are zero */
711 /* now put temp bits back ito curOffset.LowPart */
712 curOffset.LowPart &= ~(2048-1);
713 curOffset.LowPart |= temp;
715 /* check if we've passed the dir's EOF */
716 if (LargeIntegerGreaterThanOrEqualTo(curOffset, dirLength))
719 /* see if we can use the bufferp we have now; compute in which
720 * page the current offset would be, and check whether that's
721 * the offset of the buffer we have. If not, get the buffer.
723 thyper.HighPart = curOffset.HighPart;
724 thyper.LowPart = curOffset.LowPart & ~(cm_data.buf_blockSize-1);
725 if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
728 lock_ReleaseMutex(&bufferp->mx);
729 buf_Release(bufferp);
733 lock_ObtainRead(&scp->bufCreateLock);
734 code = buf_Get(scp, &thyper, &bufferp);
735 lock_ReleaseRead(&scp->bufCreateLock);
737 /* if buf_Get() fails we do not have a buffer object to lock */
743 /* for the IFS version, we bulkstat the dirents because this
744 routine is used in place of smb_ReceiveCoreSearchDir. our
745 other option is to modify smb_ReceiveCoreSearchDir itself,
746 but this seems to be the proper use for cm_ApplyDir. */
747 lock_ObtainMutex(&scp->mx);
748 if ((scp->flags & CM_SCACHEFLAG_BULKSTATTING) == 0
749 && (scp->bulkStatProgress.QuadPart <= thyper.QuadPart))
751 scp->flags |= CM_SCACHEFLAG_BULKSTATTING;
752 code = cm_TryBulkStat(scp, &thyper, userp, reqp);
753 scp->flags &= ~CM_SCACHEFLAG_BULKSTATTING;
754 scp->bulkStatProgress = thyper;
756 lock_ReleaseMutex(&scp->mx);
759 lock_ObtainMutex(&bufferp->mx);
760 bufferOffset = thyper;
762 /* now get the data in the cache */
764 lock_ObtainMutex(&scp->mx);
765 code = cm_SyncOp(scp, bufferp, userp, reqp,
767 CM_SCACHESYNC_NEEDCALLBACK
769 | CM_SCACHESYNC_BUFLOCKED);
771 lock_ReleaseMutex(&scp->mx);
774 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
776 if (cm_HaveBuffer(scp, bufferp, 1)) {
777 lock_ReleaseMutex(&scp->mx);
781 /* otherwise, load the buffer and try again */
782 lock_ReleaseMutex(&bufferp->mx);
783 code = cm_GetBuffer(scp, bufferp, NULL, userp,
785 lock_ReleaseMutex(&scp->mx);
786 lock_ObtainMutex(&bufferp->mx);
791 lock_ReleaseMutex(&bufferp->mx);
792 buf_Release(bufferp);
796 } /* if (wrong buffer) ... */
798 /* now we have the buffer containing the entry we're interested
799 * in; copy it out if it represents a non-deleted entry.
801 entryInDir = curOffset.LowPart & (2048-1);
802 entryInBuffer = curOffset.LowPart & (cm_data.buf_blockSize - 1);
804 /* page header will help tell us which entries are free. Page
805 * header can change more often than once per buffer, since
806 * AFS 3 dir page size may be less than (but not more than) a
807 * buffer package buffer.
809 /* only look intra-buffer */
810 temp = curOffset.LowPart & (cm_data.buf_blockSize - 1);
811 temp &= ~(2048 - 1); /* turn off intra-page bits */
812 pageHeaderp = (cm_pageHeader_t *) (bufferp->datap + temp);
814 /* now determine which entry we're looking at in the page. If
815 * it is free (there's a free bitmap at the start of the dir),
816 * we should skip these 32 bytes.
818 slotInPage = (entryInDir & 0x7e0) >> 5;
819 if (!(pageHeaderp->freeBitmap[slotInPage>>3]
820 & (1 << (slotInPage & 0x7)))) {
821 /* this entry is free */
822 numDirChunks = 1; /* only skip this guy */
826 tp = bufferp->datap + entryInBuffer;
827 dep = (cm_dirEntry_t *) tp; /* now points to AFS3 dir entry */
829 /* while we're here, compute the next entry's location, too,
830 * since we'll need it when writing out the cookie into the
831 * dir listing stream.
833 numDirChunks = cm_NameEntries(dep->name, NULL);
835 /* compute the offset of the cookie representing the next entry */
836 nextEntryCookie = curOffset.LowPart
837 + (CM_DIR_CHUNKSIZE * numDirChunks);
839 if (dep->fid.vnode != 0) {
840 /* this is one of the entries to use: it is not deleted */
841 code = (*funcp)(scp, dep, parmp, &curOffset);
844 } /* if we're including this name */
847 /* and adjust curOffset to be where the new cookie is */
849 thyper.LowPart = CM_DIR_CHUNKSIZE * numDirChunks;
850 curOffset = LargeIntegerAdd(thyper, curOffset);
851 } /* while copying data for dir listing */
853 /* release the mutex */
855 lock_ReleaseMutex(&bufferp->mx);
856 buf_Release(bufferp);
861 int cm_NoneUpper(char *s)
865 if (c >= 'A' && c <= 'Z')
870 int cm_NoneLower(char *s)
874 if (c >= 'a' && c <= 'z')
879 long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
882 cm_lookupSearch_t *sp;
887 sp = (cm_lookupSearch_t *) rockp;
889 matchName = dep->name;
891 match = cm_stricmp(matchName, sp->searchNamep);
893 match = strcmp(matchName, sp->searchNamep);
897 && !cm_Is8Dot3(dep->name)) {
898 matchName = shortName;
899 cm_Gen8Dot3Name(dep, shortName, NULL);
901 match = cm_stricmp(matchName, sp->searchNamep);
903 match = strcmp(matchName, sp->searchNamep);
913 if (!sp->caseFold || matchName == shortName) {
914 sp->fid.vnode = ntohl(dep->fid.vnode);
915 sp->fid.unique = ntohl(dep->fid.unique);
916 return CM_ERROR_STOPNOW;
920 * If we get here, we are doing a case-insensitive search, and we
921 * have found a match. Now we determine what kind of match it is:
922 * exact, lower-case, upper-case, or none of the above. This is done
923 * in order to choose among matches, if there are more than one.
926 /* Exact matches are the best. */
927 match = strcmp(matchName, sp->searchNamep);
930 sp->fid.vnode = ntohl(dep->fid.vnode);
931 sp->fid.unique = ntohl(dep->fid.unique);
932 return CM_ERROR_STOPNOW;
935 /* Lower-case matches are next. */
938 if (cm_NoneUpper(matchName)) {
943 /* Upper-case matches are next. */
946 if (cm_NoneLower(matchName)) {
951 /* General matches are last. */
957 sp->fid.vnode = ntohl(dep->fid.vnode);
958 sp->fid.unique = ntohl(dep->fid.unique);
962 /* read the contents of a mount point into the appropriate string.
963 * called with locked scp, and returns with locked scp.
965 long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
972 if (scp->mountPointStringp[0])
975 /* otherwise, we have to read it in */
976 lock_ReleaseMutex(&scp->mx);
978 lock_ObtainRead(&scp->bufCreateLock);
979 thyper.LowPart = thyper.HighPart = 0;
980 code = buf_Get(scp, &thyper, &bufp);
981 lock_ReleaseRead(&scp->bufCreateLock);
983 lock_ObtainMutex(&scp->mx);
988 code = cm_SyncOp(scp, bufp, userp, reqp, 0,
989 CM_SCACHESYNC_READ | CM_SCACHESYNC_NEEDCALLBACK);
993 cm_SyncOpDone(scp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
995 if (cm_HaveBuffer(scp, bufp, 0))
998 /* otherwise load buffer */
999 code = cm_GetBuffer(scp, bufp, NULL, userp, reqp);
1003 /* locked, has callback, has valid data in buffer */
1004 if ((tlen = scp->length.LowPart) > MOUNTPOINTLEN - 1)
1005 return CM_ERROR_TOOBIG;
1007 code = CM_ERROR_INVAL;
1011 /* someone else did the work while we were out */
1012 if (scp->mountPointStringp[0]) {
1017 /* otherwise, copy out the link */
1018 memcpy(scp->mountPointStringp, bufp->datap, tlen);
1020 /* now make it null-terminated. Note that the original contents of a
1021 * link that is a mount point is "#volname." where "." is there just to
1022 * be turned into a null. That is, we can trash the last char of the
1023 * link without damaging the vol name. This is a stupid convention,
1024 * but that's the protocol.
1026 scp->mountPointStringp[tlen-1] = 0;
1036 /* called with a locked scp and chases the mount point, yielding outScpp.
1037 * scp remains locked, just for simplicity of describing the interface.
1039 long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
1040 cm_req_t *reqp, cm_scache_t **outScpp)
1048 cm_volume_t *volp = NULL;
1055 if (scp->mountRootFid.cell != 0 && scp->mountRootGen >= cm_data.mountRootGen) {
1056 tfid = scp->mountRootFid;
1057 lock_ReleaseMutex(&scp->mx);
1058 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
1059 lock_ObtainMutex(&scp->mx);
1063 /* parse the volume name */
1064 mpNamep = scp->mountPointStringp;
1066 return CM_ERROR_NOSUCHPATH;
1067 tlen = (int)strlen(scp->mountPointStringp);
1068 mtType = *scp->mountPointStringp;
1069 cellNamep = malloc(tlen);
1070 volNamep = malloc(tlen);
1072 cp = strrchr(mpNamep, ':');
1074 /* cellular mount point */
1075 memset(cellNamep, 0, tlen);
1076 strncpy(cellNamep, mpNamep+1, cp - mpNamep - 1);
1077 strcpy(volNamep, cp+1);
1078 /* now look up the cell */
1079 lock_ReleaseMutex(&scp->mx);
1080 cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE);
1081 lock_ObtainMutex(&scp->mx);
1085 strcpy(volNamep, mpNamep+1);
1087 cellp = cm_FindCellByID(scp->fid.cell);
1091 code = CM_ERROR_NOSUCHCELL;
1095 vnLength = strlen(volNamep);
1096 if (vnLength >= 8 && strcmp(volNamep + vnLength - 7, ".backup") == 0)
1097 targetType = BACKVOL;
1098 else if (vnLength >= 10
1099 && strcmp(volNamep + vnLength - 9, ".readonly") == 0)
1104 /* check for backups within backups */
1105 if (targetType == BACKVOL
1106 && (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
1107 == CM_SCACHEFLAG_RO) {
1108 code = CM_ERROR_NOSUCHVOLUME;
1112 /* now we need to get the volume */
1113 lock_ReleaseMutex(&scp->mx);
1114 if (cm_VolNameIsID(volNamep)) {
1115 code = cm_GetVolumeByID(cellp, atoi(volNamep), userp, reqp,
1116 CM_GETVOL_FLAG_CREATE, &volp);
1118 code = cm_GetVolumeByName(cellp, volNamep, userp, reqp,
1119 CM_GETVOL_FLAG_CREATE, &volp);
1121 lock_ObtainMutex(&scp->mx);
1124 /* save the parent of the volume root for this is the
1125 * place where the volume is mounted and we must remember
1126 * this in the volume structure rather than just in the
1127 * scache entry lest the scache entry gets recycled
1130 lock_ObtainMutex(&volp->mx);
1131 volp->dotdotFid = dscp->fid;
1132 lock_ReleaseMutex(&volp->mx);
1134 scp->mountRootFid.cell = cellp->cellID;
1136 /* if the mt pt originates in a .backup volume (not a .readonly)
1137 * and FollowBackupPath is active, and if there is a .backup
1138 * volume for the target, then use the .backup of the target
1139 * instead of the read-write.
1141 if (cm_followBackupPath && targetType == RWVOL &&
1142 (scp->flags & CM_SCACHEFLAG_RO|CM_SCACHEFLAG_PURERO) == CM_SCACHEFLAG_RO &&
1144 targetType = BACKVOL;
1146 /* if the mt pt is in a read-only volume (not just a
1147 * backup), and if there is a read-only volume for the
1148 * target, and if this is a targetType '#' mount point, use
1149 * the read-only, otherwise use the one specified.
1151 else if (mtType == '#' && targetType == RWVOL &&
1152 (scp->flags & CM_SCACHEFLAG_PURERO) &&
1156 if (targetType == ROVOL)
1157 scp->mountRootFid.volume = volp->ro.ID;
1158 else if (targetType == BACKVOL)
1159 scp->mountRootFid.volume = volp->bk.ID;
1161 scp->mountRootFid.volume = volp->rw.ID;
1163 /* the rest of the fid is a magic number */
1164 scp->mountRootFid.vnode = 1;
1165 scp->mountRootFid.unique = 1;
1166 scp->mountRootGen = cm_data.mountRootGen;
1168 tfid = scp->mountRootFid;
1169 lock_ReleaseMutex(&scp->mx);
1170 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
1171 lock_ObtainMutex(&scp->mx);
1182 long cm_LookupInternal(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
1183 cm_req_t *reqp, cm_scache_t **outpScpp)
1186 int dnlcHit = 1; /* did we hit in the dnlc? yes, we did */
1187 cm_scache_t *tscp = NULL;
1188 cm_scache_t *mountedScp;
1189 cm_lookupSearch_t rock;
1192 memset(&rock, 0, sizeof(rock));
1194 if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
1195 && strcmp(namep, "..") == 0) {
1196 if (dscp->dotdotFid.volume == 0)
1197 return CM_ERROR_NOSUCHVOLUME;
1198 rock.fid = dscp->dotdotFid;
1200 } else if (strcmp(namep, ".") == 0) {
1201 rock.fid = dscp->fid;
1205 if (flags & CM_FLAG_NOMOUNTCHASE) {
1206 /* In this case, we should go and call cm_Dir* functions
1207 directly since the following cm_ApplyDir() function will
1215 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ, &dirop);
1218 code = cm_BPlusDirLookup(&dirop, namep, &rock.fid);
1223 code = cm_DirLookup(&dirop, namep, &rock.fid);
1225 cm_EndDirOp(&dirop);
1235 if (code == CM_ERROR_INEXACT_MATCH && (flags & CM_FLAG_CASEFOLD)) {
1242 return CM_ERROR_BPLUS_NOMATCH;
1247 rock.fid.cell = dscp->fid.cell;
1248 rock.fid.volume = dscp->fid.volume;
1249 rock.searchNamep = namep;
1250 rock.caseFold = (flags & CM_FLAG_CASEFOLD);
1251 rock.hasTilde = ((strchr(namep, '~') != NULL) ? 1 : 0);
1253 /* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
1254 code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
1255 (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
1257 /* code == 0 means we fell off the end of the dir, while stopnow means
1258 * that we stopped early, probably because we found the entry we're
1259 * looking for. Any other non-zero code is an error.
1261 if (code && code != CM_ERROR_STOPNOW) {
1262 /* if the cm_scache_t we are searching in is not a directory
1263 * we must return path not found because the error
1264 * is to describe the final component not an intermediary
1266 if (code == CM_ERROR_NOTDIR) {
1267 if (flags & CM_FLAG_CHECKPATH)
1268 return CM_ERROR_NOSUCHPATH;
1270 return CM_ERROR_NOSUCHFILE;
1275 getroot = (dscp==cm_data.rootSCachep) ;
1277 if (!cm_freelanceEnabled || !getroot) {
1278 if (flags & CM_FLAG_CHECKPATH)
1279 return CM_ERROR_NOSUCHPATH;
1281 return CM_ERROR_NOSUCHFILE;
1283 else { /* nonexistent dir on freelance root, so add it */
1284 char fullname[200] = ".";
1287 osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %s",
1288 osi_LogSaveString(afsd_logp,namep));
1289 if (namep[0] == '.') {
1290 if (cm_GetCell_Gen(&namep[1], &fullname[1], CM_FLAG_CREATE)) {
1292 if ( stricmp(&namep[1], &fullname[1]) )
1293 code = cm_FreelanceAddSymlink(namep, fullname, &rock.fid);
1295 code = cm_FreelanceAddMount(namep, &fullname[1], "root.cell.", 1, &rock.fid);
1298 if (cm_GetCell_Gen(namep, fullname, CM_FLAG_CREATE)) {
1300 if ( stricmp(namep, fullname) )
1301 code = cm_FreelanceAddSymlink(namep, fullname, &rock.fid);
1303 code = cm_FreelanceAddMount(namep, fullname, "root.cell.", 0, &rock.fid);
1306 if (!found || code < 0) { /* add mount point failed, so give up */
1307 if (flags & CM_FLAG_CHECKPATH)
1308 return CM_ERROR_NOSUCHPATH;
1310 return CM_ERROR_NOSUCHFILE;
1312 tscp = NULL; /* to force call of cm_GetSCache */
1317 if ( !tscp ) /* we did not find it in the dnlc */
1320 code = cm_GetSCache(&rock.fid, &tscp, userp, reqp);
1324 /* tscp is now held */
1326 lock_ObtainMutex(&tscp->mx);
1327 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1328 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1330 lock_ReleaseMutex(&tscp->mx);
1331 cm_ReleaseSCache(tscp);
1334 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1335 /* tscp is now locked */
1337 if (!(flags & CM_FLAG_NOMOUNTCHASE)
1338 && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
1339 /* mount points are funny: they have a volume name to mount
1342 code = cm_ReadMountPoint(tscp, userp, reqp);
1344 code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
1346 lock_ReleaseMutex(&tscp->mx);
1347 cm_ReleaseSCache(tscp);
1354 lock_ReleaseMutex(&tscp->mx);
1357 /* copy back pointer */
1360 /* insert scache in dnlc */
1361 if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
1362 /* lock the directory entry to prevent racing callback revokes */
1363 lock_ObtainMutex(&dscp->mx);
1364 if ( dscp->cbServerp != NULL && dscp->cbExpires > 0 )
1365 cm_dnlcEnter(dscp, namep, tscp);
1366 lock_ReleaseMutex(&dscp->mx);
1373 int cm_ExpandSysName(char *inp, char *outp, long outSize, unsigned int index)
1378 tp = strrchr(inp, '@');
1380 return 0; /* no @sys */
1382 if (strcmp(tp, "@sys") != 0)
1383 return 0; /* no @sys */
1385 /* caller just wants to know if this is a valid @sys type of name */
1389 if (index >= MAXNUMSYSNAMES)
1392 /* otherwise generate the properly expanded @sys name */
1393 prefixCount = (int)(tp - inp);
1395 strncpy(outp, inp, prefixCount); /* copy out "a." from "a.@sys" */
1396 outp[prefixCount] = 0; /* null terminate the "a." */
1397 strcat(outp, cm_sysNameList[index]);/* append i386_nt40 */
1401 long cm_EvaluateVolumeReference(char * namep, long flags, cm_user_t * userp,
1402 cm_req_t *reqp, cm_scache_t ** outpScpp)
1405 char cellName[CELL_MAXNAMELEN];
1406 char volumeName[VL_MAXNAMELEN];
1411 cm_cell_t * cellp = NULL;
1412 cm_volume_t * volp = NULL;
1415 int mountType = RWVOL;
1417 osi_Log1(afsd_logp, "cm_EvaluateVolumeReference for string [%s]",
1418 osi_LogSaveString(afsd_logp, namep));
1420 if (strnicmp(namep, CM_PREFIX_VOL, CM_PREFIX_VOL_CCH) != 0) {
1421 goto _exit_invalid_path;
1424 /* namep is assumed to look like the following:
1426 @vol:<cellname>%<volume>\0
1428 @vol:<cellname>#<volume>\0
1432 cp = namep + CM_PREFIX_VOL_CCH; /* cp points to cell name, hopefully */
1433 tp = strchr(cp, '%');
1435 tp = strchr(cp, '#');
1437 (len = tp - cp) == 0 ||
1438 len > CELL_MAXNAMELEN)
1439 goto _exit_invalid_path;
1440 strncpy(cellName, cp, len);
1441 cellName[len] = '\0';
1446 cp = tp+1; /* cp now points to volume, supposedly */
1447 strncpy(volumeName, cp, VL_MAXNAMELEN-1);
1448 volumeName[VL_MAXNAMELEN - 1] = 0;
1450 /* OK, now we have the cell and the volume */
1451 osi_Log2(afsd_logp, " Found cell [%s] and volume [%s]",
1452 osi_LogSaveString(afsd_logp, cellName),
1453 osi_LogSaveString(afsd_logp, volumeName));
1455 cellp = cm_GetCell(cellName, CM_FLAG_CREATE);
1456 if (cellp == NULL) {
1457 goto _exit_invalid_path;
1460 len = strlen(volumeName);
1461 if (len >= 8 && strcmp(volumeName + len - 7, ".backup") == 0)
1463 else if (len >= 10 &&
1464 strcmp(volumeName + len - 9, ".readonly") == 0)
1469 if (cm_VolNameIsID(volumeName)) {
1470 code = cm_GetVolumeByID(cellp, atoi(volumeName), userp, reqp,
1471 CM_GETVOL_FLAG_CREATE, &volp);
1473 code = cm_GetVolumeByName(cellp, volumeName, userp, reqp,
1474 CM_GETVOL_FLAG_CREATE, &volp);
1480 fid.cell = cellp->cellID;
1482 if (volType == BACKVOL)
1483 fid.volume = volp->bk.ID;
1484 else if (volType == ROVOL ||
1485 (volType == RWVOL && mountType == ROVOL && volp->ro.ID != 0))
1486 fid.volume = volp->ro.ID;
1488 fid.volume = volp->rw.ID;
1493 code = cm_GetSCache(&fid, outpScpp, userp, reqp);
1503 if (flags & CM_FLAG_CHECKPATH)
1504 return CM_ERROR_NOSUCHPATH;
1506 return CM_ERROR_NOSUCHFILE;
1509 #ifdef DEBUG_REFCOUNT
1510 long cm_LookupDbg(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
1511 cm_req_t *reqp, cm_scache_t **outpScpp, char * file, long line)
1513 long cm_Lookup(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
1514 cm_req_t *reqp, cm_scache_t **outpScpp)
1518 char tname[AFSPATHMAX];
1519 int sysNameIndex = 0;
1520 cm_scache_t *scp = NULL;
1522 #ifdef DEBUG_REFCOUNT
1523 afsi_log("%s:%d cm_Lookup dscp 0x%p ref %d", file, line, dscp, dscp->refCount, file, line);
1524 osi_Log2(afsd_logp, "cm_Lookup dscp 0x%p ref %d", dscp, dscp->refCount);
1527 if ( stricmp(namep,SMB_IOCTL_FILENAME_NOSLASH) == 0 ) {
1528 if (flags & CM_FLAG_CHECKPATH)
1529 return CM_ERROR_NOSUCHPATH;
1531 return CM_ERROR_NOSUCHFILE;
1534 if (dscp == cm_data.rootSCachep &&
1535 strnicmp(namep, CM_PREFIX_VOL, CM_PREFIX_VOL_CCH) == 0) {
1536 return cm_EvaluateVolumeReference(namep, flags, userp, reqp, outpScpp);
1539 if (cm_ExpandSysName(namep, NULL, 0, 0) > 0) {
1540 for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
1541 code = cm_ExpandSysName(namep, tname, sizeof(tname), sysNameIndex);
1543 code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
1544 #ifdef DEBUG_REFCOUNT
1545 afsi_log("%s:%d cm_LookupInternal (1) code 0x%x dscp 0x%p ref %d scp 0x%p ref %d", file, line, code, dscp, dscp->refCount, scp, scp ? scp->refCount : 0);
1546 osi_Log3(afsd_logp, "cm_LookupInternal (1) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1554 cm_ReleaseSCache(scp);
1558 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1559 #ifdef DEBUG_REFCOUNT
1560 afsi_log("%s:%d cm_LookupInternal (2) code 0x%x dscp 0x%p ref %d scp 0x%p ref %d", file, line, code, dscp, dscp->refCount, scp, scp ? scp->refCount : 0);
1561 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1568 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1569 #ifdef DEBUG_REFCOUNT
1570 afsi_log("%s:%d cm_LookupInternal (2) code 0x%x dscp 0x%p ref %d scp 0x%p ref %d", file, line, code, dscp, dscp->refCount, scp, scp ? scp->refCount : 0);
1571 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1577 /* None of the possible sysName expansions could be found */
1578 if (flags & CM_FLAG_CHECKPATH)
1579 return CM_ERROR_NOSUCHPATH;
1581 return CM_ERROR_NOSUCHFILE;
1584 long cm_Unlink(cm_scache_t *dscp, char *namep, cm_user_t *userp, cm_req_t *reqp)
1590 AFSFetchStatus newDirStatus;
1592 struct rx_connection * callp;
1595 #ifdef AFS_FREELANCE_CLIENT
1596 if (cm_freelanceEnabled && dscp == cm_data.rootSCachep) {
1597 /* deleting a mount point from the root dir. */
1598 code = cm_FreelanceRemoveMount(namep);
1603 /* make sure we don't screw up the dir status during the merge */
1604 code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
1606 lock_ObtainMutex(&dscp->mx);
1607 sflags = CM_SCACHESYNC_STOREDATA;
1608 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1609 lock_ReleaseMutex(&dscp->mx);
1611 cm_EndDirOp(&dirop);
1616 afsFid.Volume = dscp->fid.volume;
1617 afsFid.Vnode = dscp->fid.vnode;
1618 afsFid.Unique = dscp->fid.unique;
1620 osi_Log1(afsd_logp, "CALL RemoveFile scp 0x%p", dscp);
1622 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
1626 callp = cm_GetRxConn(connp);
1627 code = RXAFS_RemoveFile(callp, &afsFid, namep,
1628 &newDirStatus, &volSync);
1629 rx_PutConnection(callp);
1631 } while (cm_Analyze(connp, userp, reqp, &dscp->fid, &volSync, NULL, NULL, code));
1632 code = cm_MapRPCError(code, reqp);
1635 osi_Log1(afsd_logp, "CALL RemoveFile FAILURE, code 0x%x", code);
1637 osi_Log0(afsd_logp, "CALL RemoveFile SUCCESS");
1640 lock_ObtainWrite(&dirop.scp->dirlock);
1641 dirop.lockType = CM_DIRLOCK_WRITE;
1643 lock_ObtainMutex(&dscp->mx);
1644 cm_dnlcRemove(dscp, namep);
1645 cm_SyncOpDone(dscp, NULL, sflags);
1647 cm_MergeStatus(NULL, dscp, &newDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
1648 } else if (code == CM_ERROR_NOSUCHFILE) {
1649 /* windows would not have allowed the request to delete the file
1650 * if it did not believe the file existed. therefore, we must
1651 * have an inconsistent view of the world.
1653 dscp->cbServerp = NULL;
1655 lock_ReleaseMutex(&dscp->mx);
1657 if (code == 0 && cm_CheckDirOpForSingleChange(&dirop)) {
1658 cm_DirDeleteEntry(&dirop, namep);
1660 cm_BPlusDirDeleteEntry(&dirop, namep);
1663 cm_EndDirOp(&dirop);
1668 /* called with a locked vnode, and fills in the link info.
1669 * returns this the vnode still locked.
1671 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1678 lock_AssertMutex(&linkScp->mx);
1679 if (!linkScp->mountPointStringp[0]) {
1680 /* read the link data */
1681 lock_ReleaseMutex(&linkScp->mx);
1682 thyper.LowPart = thyper.HighPart = 0;
1683 code = buf_Get(linkScp, &thyper, &bufp);
1684 lock_ObtainMutex(&linkScp->mx);
1688 code = cm_SyncOp(linkScp, bufp, userp, reqp, 0,
1689 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1694 cm_SyncOpDone(linkScp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1696 if (cm_HaveBuffer(linkScp, bufp, 0))
1699 code = cm_GetBuffer(linkScp, bufp, NULL, userp, reqp);
1704 } /* while loop to get the data */
1706 /* now if we still have no link read in,
1707 * copy the data from the buffer */
1708 if ((temp = linkScp->length.LowPart) >= MOUNTPOINTLEN) {
1710 return CM_ERROR_TOOBIG;
1713 /* otherwise, it fits; make sure it is still null (could have
1714 * lost race with someone else referencing this link above),
1715 * and if so, copy in the data.
1717 if (!linkScp->mountPointStringp[0]) {
1718 strncpy(linkScp->mountPointStringp, bufp->datap, temp);
1719 linkScp->mountPointStringp[temp] = 0; /* null terminate */
1722 } /* don't have sym link contents cached */
1727 /* called with a held vnode and a path suffix, with the held vnode being a
1728 * symbolic link. Our goal is to generate a new path to interpret, and return
1729 * this new path in newSpaceBufferp. If the new vnode is relative to a dir
1730 * other than the directory containing the symbolic link, then the new root is
1731 * returned in *newRootScpp, otherwise a null is returned there.
1733 long cm_AssembleLink(cm_scache_t *linkScp, char *pathSuffixp,
1734 cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1735 cm_user_t *userp, cm_req_t *reqp)
1742 lock_ObtainMutex(&linkScp->mx);
1743 code = cm_HandleLink(linkScp, userp, reqp);
1747 /* if we may overflow the buffer, bail out; buffer is signficantly
1748 * bigger than max path length, so we don't really have to worry about
1749 * being a little conservative here.
1751 if (strlen(linkScp->mountPointStringp) + strlen(pathSuffixp) + 2
1752 >= CM_UTILS_SPACESIZE)
1753 return CM_ERROR_TOOBIG;
1755 tsp = cm_GetSpace();
1756 linkp = linkScp->mountPointStringp;
1757 if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1758 if (strlen(linkp) > cm_mountRootLen)
1759 strcpy(tsp->data, linkp+cm_mountRootLen+1);
1762 *newRootScpp = cm_data.rootSCachep;
1763 cm_HoldSCache(cm_data.rootSCachep);
1764 } else if (linkp[0] == '\\' && linkp[1] == '\\') {
1765 if (!strnicmp(&linkp[2], cm_NetbiosName, (len = (long)strlen(cm_NetbiosName))))
1767 char * p = &linkp[len + 3];
1768 if (strnicmp(p, "all", 3) == 0)
1771 strcpy(tsp->data, p);
1772 for (p = tsp->data; *p; p++) {
1776 *newRootScpp = cm_data.rootSCachep;
1777 cm_HoldSCache(cm_data.rootSCachep);
1779 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1780 strcpy(tsp->data, linkp);
1781 *newRootScpp = NULL;
1782 code = CM_ERROR_PATH_NOT_COVERED;
1784 } else if ( !strnicmp(linkp, "msdfs:", (len = (long)strlen("msdfs:"))) ) {
1785 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1786 strcpy(tsp->data, linkp);
1787 *newRootScpp = NULL;
1788 code = CM_ERROR_PATH_NOT_COVERED;
1789 } else if (*linkp == '\\' || *linkp == '/') {
1791 /* formerly, this was considered to be from the AFS root,
1792 * but this seems to create problems. instead, we will just
1793 * reject the link */
1794 strcpy(tsp->data, linkp+1);
1795 *newRootScpp = cm_data.rootSCachep;
1796 cm_HoldSCache(cm_data.rootSCachep);
1798 /* we still copy the link data into the response so that
1799 * the user can see what the link points to
1801 linkScp->fileType = CM_SCACHETYPE_INVALID;
1802 strcpy(tsp->data, linkp);
1803 *newRootScpp = NULL;
1804 code = CM_ERROR_NOSUCHPATH;
1807 /* a relative link */
1808 strcpy(tsp->data, linkp);
1809 *newRootScpp = NULL;
1811 if (pathSuffixp[0] != 0) { /* if suffix string is non-null */
1812 strcat(tsp->data, "\\");
1813 strcat(tsp->data, pathSuffixp);
1815 *newSpaceBufferp = tsp;
1818 lock_ReleaseMutex(&linkScp->mx);
1821 #ifdef DEBUG_REFCOUNT
1822 long cm_NameIDbg(cm_scache_t *rootSCachep, char *pathp, long flags,
1823 cm_user_t *userp, char *tidPathp, cm_req_t *reqp, cm_scache_t **outScpp,
1824 char * file, long line)
1826 long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
1827 cm_user_t *userp, char *tidPathp, cm_req_t *reqp, cm_scache_t **outScpp)
1831 char *tp; /* ptr moving through input buffer */
1832 char tc; /* temp char */
1833 int haveComponent; /* has new component started? */
1834 char component[AFSPATHMAX]; /* this is the new component */
1835 char *cp; /* component name being assembled */
1836 cm_scache_t *tscp; /* current location in the hierarchy */
1837 cm_scache_t *nscp; /* next dude down */
1838 cm_scache_t *dirScp; /* last dir we searched */
1839 cm_scache_t *linkScp; /* new root for the symlink we just
1841 cm_space_t *psp; /* space for current path, if we've hit
1843 cm_space_t *tempsp; /* temp vbl */
1844 char *restp; /* rest of the pathname to interpret */
1845 int symlinkCount; /* count of # of symlinks traversed */
1846 int extraFlag; /* avoid chasing mt pts for dir cmd */
1847 int phase = 1; /* 1 = tidPathp, 2 = pathp */
1848 #define MAX_FID_COUNT 512
1849 cm_fid_t fids[MAX_FID_COUNT]; /* array of fids processed in this path walk */
1850 int fid_count = 0; /* number of fids processed in this path walk */
1853 #ifdef DEBUG_REFCOUNT
1854 afsi_log("%s:%d cm_NameI rootscp 0x%p ref %d", file, line, rootSCachep, rootSCachep->refCount);
1855 osi_Log4(afsd_logp,"cm_NameI rootscp 0x%p path %s tidpath %s flags 0x%x",
1856 rootSCachep, pathp ? pathp : "<NULL>", tidPathp ? tidPathp : "<NULL>",
1871 cm_HoldSCache(tscp);
1879 /* map Unix slashes into DOS ones so we can interpret Unix
1885 if (!haveComponent) {
1888 } else if (tc == 0) {
1902 /* we have a component here */
1903 if (tc == 0 || tc == '\\') {
1904 /* end of the component; we're at the last
1905 * component if tc == 0. However, if the last
1906 * is a symlink, we have more to do.
1908 *cp++ = 0; /* add null termination */
1910 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
1911 extraFlag = CM_FLAG_NOMOUNTCHASE;
1912 code = cm_Lookup(tscp, component,
1914 userp, reqp, &nscp);
1917 if (!strcmp(component,"..") || !strcmp(component,".")) {
1919 * roll back the fid list until we find the fid
1920 * that matches where we are now. Its not necessarily
1921 * one or two fids because they might have been
1922 * symlinks or mount points or both that were crossed.
1924 for ( i=fid_count-1; i>=0; i--) {
1925 if (!cm_FidCmp(&nscp->fid, &fids[i]))
1929 /* add the new fid to the list */
1930 for ( i=0; i<fid_count; i++) {
1931 if ( !cm_FidCmp(&nscp->fid, &fids[i]) ) {
1932 code = CM_ERROR_TOO_MANY_SYMLINKS;
1933 cm_ReleaseSCache(nscp);
1938 if (i == fid_count && fid_count < MAX_FID_COUNT) {
1939 fids[fid_count++] = nscp->fid;
1945 cm_ReleaseSCache(tscp);
1947 cm_ReleaseSCache(dirScp);
1950 if ((code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH) &&
1951 tscp->fileType == CM_SCACHETYPE_SYMLINK)
1953 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_NOSUCHPATH");
1954 return CM_ERROR_NOSUCHPATH;
1956 osi_Log1(afsd_logp,"cm_NameI code 0x%x", code);
1961 haveComponent = 0; /* component done */
1963 cm_ReleaseSCache(dirScp);
1964 dirScp = tscp; /* for some symlinks */
1965 tscp = nscp; /* already held */
1967 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
1970 cm_ReleaseSCache(dirScp);
1976 /* now, if tscp is a symlink, we should follow
1977 * it and assemble the path again.
1979 lock_ObtainMutex(&tscp->mx);
1980 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1981 CM_SCACHESYNC_GETSTATUS
1982 | CM_SCACHESYNC_NEEDCALLBACK);
1984 lock_ReleaseMutex(&tscp->mx);
1985 cm_ReleaseSCache(tscp);
1988 cm_ReleaseSCache(dirScp);
1993 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1995 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
1996 /* this is a symlink; assemble a new buffer */
1997 lock_ReleaseMutex(&tscp->mx);
1998 if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
1999 cm_ReleaseSCache(tscp);
2002 cm_ReleaseSCache(dirScp);
2007 osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_TOO_MANY_SYMLINKS");
2008 return CM_ERROR_TOO_MANY_SYMLINKS;
2014 code = cm_AssembleLink(tscp, restp, &linkScp, &tempsp, userp, reqp);
2016 if (code == 0 && linkScp != NULL) {
2017 if (linkScp == cm_data.rootSCachep)
2020 for ( i=0; i<fid_count; i++) {
2021 if ( !cm_FidCmp(&linkScp->fid, &fids[i]) ) {
2022 code = CM_ERROR_TOO_MANY_SYMLINKS;
2023 cm_ReleaseSCache(linkScp);
2029 if (i == fid_count && fid_count < MAX_FID_COUNT) {
2030 fids[fid_count++] = linkScp->fid;
2035 /* something went wrong */
2036 cm_ReleaseSCache(tscp);
2039 cm_ReleaseSCache(dirScp);
2045 /* otherwise, tempsp has the new path,
2046 * and linkScp is the new root from
2047 * which to interpret that path.
2048 * Continue with the namei processing,
2049 * also doing the bookkeeping for the
2050 * space allocation and tracking the
2051 * vnode reference counts.
2057 cm_ReleaseSCache(tscp);
2062 * now, if linkScp is null, that's
2063 * AssembleLink's way of telling us that
2064 * the sym link is relative to the dir
2065 * containing the link. We have a ref
2066 * to it in dirScp, and we hold it now
2067 * and reuse it as the new spot in the
2075 /* not a symlink, we may be done */
2076 lock_ReleaseMutex(&tscp->mx);
2084 cm_ReleaseSCache(dirScp);
2092 cm_ReleaseSCache(dirScp);
2095 } /* end of a component */
2098 } /* we have a component */
2099 } /* big while loop over all components */
2103 cm_ReleaseSCache(dirScp);
2109 cm_ReleaseSCache(tscp);
2111 #ifdef DEBUG_REFCOUNT
2112 afsi_log("%s:%d cm_NameI code 0x%x outScpp 0x%p ref %d", file, line, code, *outScpp, (*outScpp)->refCount);
2114 osi_Log2(afsd_logp,"cm_NameI code 0x%x outScpp 0x%p", code, *outScpp);
2118 /* called with a dir, and a vnode within the dir that happens to be a symlink.
2119 * We chase the link, and return a held pointer to the target, if it exists,
2120 * in *outScpp. If we succeed, we return 0, otherwise we return an error code
2121 * and do not hold or return a target vnode.
2123 * This is very similar to calling cm_NameI with the last component of a name,
2124 * which happens to be a symlink, except that we've already passed by the name.
2126 * This function is typically called by the directory listing functions, which
2127 * encounter symlinks but need to return the proper file length so programs
2128 * like "more" work properly when they make use of the attributes retrieved from
2131 * The input vnode should not be locked when this function is called.
2133 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
2134 cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
2138 cm_scache_t *newRootScp;
2140 osi_Log1(afsd_logp, "Evaluating symlink scp 0x%p", linkScp);
2142 code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
2146 /* now, if newRootScp is NULL, we're really being told that the symlink
2147 * is relative to the current directory (dscp).
2149 if (newRootScp == NULL) {
2151 cm_HoldSCache(dscp);
2154 code = cm_NameI(newRootScp, spacep->data,
2155 CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
2156 userp, NULL, reqp, outScpp);
2158 if (code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH)
2159 code = CM_ERROR_NOSUCHPATH;
2161 /* this stuff is allocated no matter what happened on the namei call,
2163 cm_FreeSpace(spacep);
2164 cm_ReleaseSCache(newRootScp);
2166 if (linkScp == *outScpp) {
2167 cm_ReleaseSCache(*outScpp);
2169 code = CM_ERROR_NOSUCHPATH;
2175 /* make this big enough so that one buffer of dir pages won't overflow. We'll
2176 * check anyway, but we want to minimize the chance that we have to leave stuff
2179 #define CM_BULKMAX (3 * AFSCBMAX)
2181 /* rock for bulk stat calls */
2182 typedef struct cm_bulkStat {
2183 osi_hyper_t bufOffset; /* only do it for things in this buffer page */
2185 /* info for the actual call */
2186 int counter; /* next free slot */
2187 AFSFid fids[CM_BULKMAX];
2188 AFSFetchStatus stats[CM_BULKMAX];
2189 AFSCallBack callbacks[CM_BULKMAX];
2192 /* for a given entry, make sure that it isn't in the stat cache, and then
2193 * add it to the list of file IDs to be obtained.
2195 * Don't bother adding it if we already have a vnode. Note that the dir
2196 * is locked, so we have to be careful checking the vnode we're thinking of
2197 * processing, to avoid deadlocks.
2199 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
2210 /* Don't overflow bsp. */
2211 if (bsp->counter >= CM_BULKMAX)
2212 return CM_ERROR_STOPNOW;
2214 thyper.LowPart = cm_data.buf_blockSize;
2215 thyper.HighPart = 0;
2216 thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
2218 /* thyper is now the first byte past the end of the record we're
2219 * interested in, and bsp->bufOffset is the first byte of the record
2220 * we're interested in.
2221 * Skip data in the others.
2224 if (LargeIntegerLessThan(*offp, bsp->bufOffset))
2226 if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
2227 return CM_ERROR_STOPNOW;
2228 if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
2231 tfid.cell = scp->fid.cell;
2232 tfid.volume = scp->fid.volume;
2233 tfid.vnode = ntohl(dep->fid.vnode);
2234 tfid.unique = ntohl(dep->fid.unique);
2235 tscp = cm_FindSCache(&tfid);
2237 if (lock_TryMutex(&tscp->mx)) {
2238 /* we have an entry that we can look at */
2239 if (!(tscp->flags & CM_SCACHEFLAG_EACCESS) && cm_HaveCallback(tscp)) {
2240 /* we have a callback on it. Don't bother
2241 * fetching this stat entry, since we're happy
2242 * with the info we have.
2244 lock_ReleaseMutex(&tscp->mx);
2245 cm_ReleaseSCache(tscp);
2248 lock_ReleaseMutex(&tscp->mx);
2250 cm_ReleaseSCache(tscp);
2253 #ifdef AFS_FREELANCE_CLIENT
2254 // yj: if this is a mountpoint under root.afs then we don't want it
2255 // to be bulkstat-ed, instead, we call getSCache directly and under
2256 // getSCache, it is handled specially.
2257 if ( cm_freelanceEnabled &&
2258 tfid.cell==AFS_FAKE_ROOT_CELL_ID &&
2259 tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
2260 !(tfid.vnode==0x1 && tfid.unique==0x1) )
2262 osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
2263 return cm_GetSCache(&tfid, &tscp, NULL, NULL);
2265 #endif /* AFS_FREELANCE_CLIENT */
2268 bsp->fids[i].Volume = scp->fid.volume;
2269 bsp->fids[i].Vnode = tfid.vnode;
2270 bsp->fids[i].Unique = tfid.unique;
2274 /* called with a locked scp and a pointer to a buffer. Make bulk stat
2275 * calls on all undeleted files in the page of the directory specified.
2278 cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
2282 cm_bulkStat_t bb; /* this is *BIG*, probably 16K or so;
2283 * watch for stack problems */
2284 AFSCBFids fidStruct;
2285 AFSBulkStats statStruct;
2287 AFSCBs callbackStruct;
2290 cm_callbackRequest_t cbReq;
2296 struct rx_connection * callp;
2297 int inlinebulk = 0; /* Did we use InlineBulkStatus RPC or not? */
2299 osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%p", dscp);
2301 /* should be on a buffer boundary */
2302 osi_assertx((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0, "invalid offset");
2304 memset(&bb, 0, sizeof(bb));
2305 bb.bufOffset = *offsetp;
2307 lock_ReleaseMutex(&dscp->mx);
2308 /* first, assemble the file IDs we need to stat */
2309 code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) &bb, offsetp, userp, reqp, NULL);
2311 /* if we failed, bail out early */
2312 if (code && code != CM_ERROR_STOPNOW) {
2313 lock_ObtainMutex(&dscp->mx);
2317 /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
2318 * make the calls to create the entries. Handle AFSCBMAX files at a
2322 while (filex < bb.counter) {
2323 filesThisCall = bb.counter - filex;
2324 if (filesThisCall > AFSCBMAX)
2325 filesThisCall = AFSCBMAX;
2327 fidStruct.AFSCBFids_len = filesThisCall;
2328 fidStruct.AFSCBFids_val = &bb.fids[filex];
2329 statStruct.AFSBulkStats_len = filesThisCall;
2330 statStruct.AFSBulkStats_val = &bb.stats[filex];
2331 callbackStruct.AFSCBs_len = filesThisCall;
2332 callbackStruct.AFSCBs_val = &bb.callbacks[filex];
2333 cm_StartCallbackGrantingCall(NULL, &cbReq);
2334 osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
2336 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2340 callp = cm_GetRxConn(connp);
2341 if (!(connp->serverp->flags & CM_SERVERFLAG_NOINLINEBULK)) {
2342 code = RXAFS_InlineBulkStatus(callp, &fidStruct,
2343 &statStruct, &callbackStruct, &volSync);
2344 if (code == RXGEN_OPCODE) {
2345 cm_SetServerNoInlineBulk(connp->serverp, 0);
2351 code = RXAFS_BulkStatus(callp, &fidStruct,
2352 &statStruct, &callbackStruct, &volSync);
2354 rx_PutConnection(callp);
2356 } while (cm_Analyze(connp, userp, reqp, &dscp->fid,
2357 &volSync, NULL, &cbReq, code));
2358 code = cm_MapRPCError(code, reqp);
2360 osi_Log2(afsd_logp, "CALL %sBulkStatus FAILURE code 0x%x",
2361 inlinebulk ? "Inline" : "", code);
2363 osi_Log1(afsd_logp, "CALL %sBulkStatus SUCCESS", inlinebulk ? "Inline" : "");
2365 /* may as well quit on an error, since we're not going to do
2366 * much better on the next immediate call, either.
2369 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2373 /* otherwise, we should do the merges */
2374 for (i = 0; i<filesThisCall; i++) {
2376 tfid.cell = dscp->fid.cell;
2377 tfid.volume = bb.fids[j].Volume;
2378 tfid.vnode = bb.fids[j].Vnode;
2379 tfid.unique = bb.fids[j].Unique;
2380 code = cm_GetSCache(&tfid, &scp, userp, reqp);
2384 /* otherwise, if this entry has no callback info,
2387 lock_ObtainMutex(&scp->mx);
2388 /* now, we have to be extra paranoid on merging in this
2389 * information, since we didn't use cm_SyncOp before
2390 * starting the fetch to make sure that no bad races
2391 * were occurring. Specifically, we need to make sure
2392 * we don't obliterate any newer information in the
2393 * vnode than have here.
2395 * Right now, be pretty conservative: if there's a
2396 * callback or a pending call, skip it.
2398 if ((scp->cbServerp == NULL || (scp->flags & CM_SCACHEFLAG_EACCESS))
2400 (CM_SCACHEFLAG_FETCHING
2401 | CM_SCACHEFLAG_STORING
2402 | CM_SCACHEFLAG_SIZESTORING))) {
2403 cm_EndCallbackGrantingCall(scp, &cbReq,
2405 CM_CALLBACK_MAINTAINCOUNT);
2406 cm_MergeStatus(dscp, scp, &bb.stats[j], &volSync, userp, 0);
2408 lock_ReleaseMutex(&scp->mx);
2409 cm_ReleaseSCache(scp);
2410 } /* all files in the response */
2411 /* now tell it to drop the count,
2412 * after doing the vnode processing above */
2413 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2415 filex += filesThisCall;
2416 } /* while there are still more files to process */
2417 lock_ObtainMutex(&dscp->mx);
2419 /* If we did the InlineBulk RPC pull out the return code and log it */
2421 if ((&bb.stats[0])->errorCode) {
2422 osi_Log1(afsd_logp, "cm_TryBulkStat bulk stat error: %d",
2423 (&bb.stats[0])->errorCode);
2427 osi_Log0(afsd_logp, "END cm_TryBulkStat");
2431 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
2435 /* initialize store back mask as inexpensive local variable */
2437 memset(statusp, 0, sizeof(AFSStoreStatus));
2439 /* copy out queued info from scache first, if scp passed in */
2441 if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
2442 statusp->ClientModTime = scp->clientModTime;
2443 mask |= AFS_SETMODTIME;
2444 scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
2449 /* now add in our locally generated request */
2450 if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
2451 statusp->ClientModTime = attrp->clientModTime;
2452 mask |= AFS_SETMODTIME;
2454 if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
2455 statusp->UnixModeBits = attrp->unixModeBits;
2456 mask |= AFS_SETMODE;
2458 if (attrp->mask & CM_ATTRMASK_OWNER) {
2459 statusp->Owner = attrp->owner;
2460 mask |= AFS_SETOWNER;
2462 if (attrp->mask & CM_ATTRMASK_GROUP) {
2463 statusp->Group = attrp->group;
2464 mask |= AFS_SETGROUP;
2467 statusp->Mask = mask;
2470 /* set the file size, and make sure that all relevant buffers have been
2471 * truncated. Ensure that any partially truncated buffers have been zeroed
2472 * to the end of the buffer.
2474 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
2480 /* start by locking out buffer creation */
2481 lock_ObtainWrite(&scp->bufCreateLock);
2483 /* verify that this is a file, not a dir or a symlink */
2484 lock_ObtainMutex(&scp->mx);
2485 code = cm_SyncOp(scp, NULL, userp, reqp, 0,
2486 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2489 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2491 if (scp->fileType != CM_SCACHETYPE_FILE) {
2492 code = CM_ERROR_ISDIR;
2497 if (LargeIntegerLessThan(*sizep, scp->length))
2502 lock_ReleaseMutex(&scp->mx);
2504 /* can't hold scp->mx lock here, since we may wait for a storeback to
2505 * finish if the buffer package is cleaning a buffer by storing it to
2509 buf_Truncate(scp, userp, reqp, sizep);
2511 /* now ensure that file length is short enough, and update truncPos */
2512 lock_ObtainMutex(&scp->mx);
2514 /* make sure we have a callback (so we have the right value for the
2515 * length), and wait for it to be safe to do a truncate.
2517 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
2518 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2519 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2521 /* If we only have 'i' bits, then we should still be able to set
2522 the size of a file we created. */
2523 if (code == CM_ERROR_NOACCESS && scp->creator == userp) {
2524 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
2525 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2526 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2532 if (LargeIntegerLessThan(*sizep, scp->length)) {
2533 /* a real truncation. If truncPos is not set yet, or is bigger
2534 * than where we're truncating the file, set truncPos to this
2539 if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
2540 || LargeIntegerLessThan(*sizep, scp->length)) {
2542 scp->truncPos = *sizep;
2543 scp->mask |= CM_SCACHEMASK_TRUNCPOS;
2545 /* in either case, the new file size has been changed */
2546 scp->length = *sizep;
2547 scp->mask |= CM_SCACHEMASK_LENGTH;
2549 else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
2550 /* really extending the file */
2551 scp->length = *sizep;
2552 scp->mask |= CM_SCACHEMASK_LENGTH;
2555 /* done successfully */
2558 cm_SyncOpDone(scp, NULL,
2559 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2560 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2563 lock_ReleaseMutex(&scp->mx);
2564 lock_ReleaseWrite(&scp->bufCreateLock);
2569 /* set the file size or other attributes (but not both at once) */
2570 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
2574 AFSFetchStatus afsOutStatus;
2578 AFSStoreStatus afsInStatus;
2579 struct rx_connection * callp;
2581 /* handle file length setting */
2582 if (attrp->mask & CM_ATTRMASK_LENGTH)
2583 return cm_SetLength(scp, &attrp->length, userp, reqp);
2585 lock_ObtainMutex(&scp->mx);
2586 /* otherwise, we have to make an RPC to get the status */
2587 code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
2589 lock_ReleaseMutex(&scp->mx);
2593 /* make the attr structure */
2594 cm_StatusFromAttr(&afsInStatus, scp, attrp);
2596 tfid.Volume = scp->fid.volume;
2597 tfid.Vnode = scp->fid.vnode;
2598 tfid.Unique = scp->fid.unique;
2599 lock_ReleaseMutex(&scp->mx);
2601 /* now make the RPC */
2602 osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%p", scp);
2604 code = cm_ConnFromFID(&scp->fid, userp, reqp, &connp);
2608 callp = cm_GetRxConn(connp);
2609 code = RXAFS_StoreStatus(callp, &tfid,
2610 &afsInStatus, &afsOutStatus, &volSync);
2611 rx_PutConnection(callp);
2613 } while (cm_Analyze(connp, userp, reqp,
2614 &scp->fid, &volSync, NULL, NULL, code));
2615 code = cm_MapRPCError(code, reqp);
2618 osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
2620 osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
2622 lock_ObtainMutex(&scp->mx);
2623 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
2625 cm_MergeStatus(NULL, scp, &afsOutStatus, &volSync, userp,
2626 CM_MERGEFLAG_FORCE|CM_MERGEFLAG_STOREDATA);
2628 /* if we're changing the mode bits, discard the ACL cache,
2629 * since we changed the mode bits.
2631 if (afsInStatus.Mask & AFS_SETMODE)
2632 cm_FreeAllACLEnts(scp);
2633 lock_ReleaseMutex(&scp->mx);
2637 long cm_Create(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2638 cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
2643 cm_callbackRequest_t cbReq;
2646 cm_scache_t *scp = NULL;
2648 AFSStoreStatus inStatus;
2649 AFSFetchStatus updatedDirStatus;
2650 AFSFetchStatus newFileStatus;
2651 AFSCallBack newFileCallback;
2653 struct rx_connection * callp;
2656 /* can't create names with @sys in them; must expand it manually first.
2657 * return "invalid request" if they try.
2659 if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2660 return CM_ERROR_ATSYS;
2663 /* before starting the RPC, mark that we're changing the file data, so
2664 * that someone who does a chmod will know to wait until our call
2667 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2668 lock_ObtainMutex(&dscp->mx);
2669 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2670 lock_ReleaseMutex(&dscp->mx);
2672 cm_StartCallbackGrantingCall(NULL, &cbReq);
2674 cm_EndDirOp(&dirop);
2681 cm_StatusFromAttr(&inStatus, NULL, attrp);
2683 /* try the RPC now */
2684 osi_Log1(afsd_logp, "CALL CreateFile scp 0x%p", dscp);
2686 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2690 dirAFSFid.Volume = dscp->fid.volume;
2691 dirAFSFid.Vnode = dscp->fid.vnode;
2692 dirAFSFid.Unique = dscp->fid.unique;
2694 callp = cm_GetRxConn(connp);
2695 code = RXAFS_CreateFile(connp->callp, &dirAFSFid, namep,
2696 &inStatus, &newAFSFid, &newFileStatus,
2697 &updatedDirStatus, &newFileCallback,
2699 rx_PutConnection(callp);
2701 } while (cm_Analyze(connp, userp, reqp,
2702 &dscp->fid, &volSync, NULL, &cbReq, code));
2703 code = cm_MapRPCError(code, reqp);
2706 osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
2708 osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
2711 lock_ObtainWrite(&dirop.scp->dirlock);
2712 dirop.lockType = CM_DIRLOCK_WRITE;
2714 lock_ObtainMutex(&dscp->mx);
2715 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2717 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
2719 lock_ReleaseMutex(&dscp->mx);
2721 /* now try to create the file's entry, too, but be careful to
2722 * make sure that we don't merge in old info. Since we weren't locking
2723 * out any requests during the file's creation, we may have pretty old
2727 newFid.cell = dscp->fid.cell;
2728 newFid.volume = dscp->fid.volume;
2729 newFid.vnode = newAFSFid.Vnode;
2730 newFid.unique = newAFSFid.Unique;
2731 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2733 lock_ObtainMutex(&scp->mx);
2734 scp->creator = userp; /* remember who created it */
2735 if (!cm_HaveCallback(scp)) {
2736 cm_MergeStatus(dscp, scp, &newFileStatus, &volSync,
2738 cm_EndCallbackGrantingCall(scp, &cbReq,
2739 &newFileCallback, 0);
2742 lock_ReleaseMutex(&scp->mx);
2747 /* make sure we end things properly */
2749 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2751 if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
2752 cm_DirCreateEntry(&dirop, namep, &newFid);
2754 cm_BPlusDirCreateEntry(&dirop, namep, &newFid);
2757 cm_EndDirOp(&dirop);
2762 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
2766 lock_ObtainWrite(&scp->bufCreateLock);
2767 code = buf_CleanVnode(scp, userp, reqp);
2768 lock_ReleaseWrite(&scp->bufCreateLock);
2770 lock_ObtainMutex(&scp->mx);
2772 if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
2773 | CM_SCACHEMASK_CLIENTMODTIME
2774 | CM_SCACHEMASK_LENGTH))
2775 code = cm_StoreMini(scp, userp, reqp);
2777 if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) {
2778 code = (scp->flags & CM_SCACHEFLAG_OVERQUOTA) ? CM_ERROR_QUOTA : CM_ERROR_SPACE;
2779 scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE);
2782 lock_ReleaseMutex(&scp->mx);
2787 long cm_MakeDir(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2788 cm_user_t *userp, cm_req_t *reqp)
2793 cm_callbackRequest_t cbReq;
2796 cm_scache_t *scp = NULL;
2798 AFSStoreStatus inStatus;
2799 AFSFetchStatus updatedDirStatus;
2800 AFSFetchStatus newDirStatus;
2801 AFSCallBack newDirCallback;
2803 struct rx_connection * callp;
2806 /* can't create names with @sys in them; must expand it manually first.
2807 * return "invalid request" if they try.
2809 if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2810 return CM_ERROR_ATSYS;
2813 /* before starting the RPC, mark that we're changing the directory
2814 * data, so that someone who does a chmod on the dir will wait until
2815 * our call completes.
2817 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2818 lock_ObtainMutex(&dscp->mx);
2819 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2820 lock_ReleaseMutex(&dscp->mx);
2822 cm_StartCallbackGrantingCall(NULL, &cbReq);
2824 cm_EndDirOp(&dirop);
2831 cm_StatusFromAttr(&inStatus, NULL, attrp);
2833 /* try the RPC now */
2834 osi_Log1(afsd_logp, "CALL MakeDir scp 0x%p", dscp);
2836 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2840 dirAFSFid.Volume = dscp->fid.volume;
2841 dirAFSFid.Vnode = dscp->fid.vnode;
2842 dirAFSFid.Unique = dscp->fid.unique;
2844 callp = cm_GetRxConn(connp);
2845 code = RXAFS_MakeDir(connp->callp, &dirAFSFid, namep,
2846 &inStatus, &newAFSFid, &newDirStatus,
2847 &updatedDirStatus, &newDirCallback,
2849 rx_PutConnection(callp);
2851 } while (cm_Analyze(connp, userp, reqp,
2852 &dscp->fid, &volSync, NULL, &cbReq, code));
2853 code = cm_MapRPCError(code, reqp);
2856 osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
2858 osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
2861 lock_ObtainWrite(&dirop.scp->dirlock);
2862 dirop.lockType = CM_DIRLOCK_WRITE;
2864 lock_ObtainMutex(&dscp->mx);
2865 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2867 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
2869 lock_ReleaseMutex(&dscp->mx);
2871 /* now try to create the new dir's entry, too, but be careful to
2872 * make sure that we don't merge in old info. Since we weren't locking
2873 * out any requests during the file's creation, we may have pretty old
2877 newFid.cell = dscp->fid.cell;
2878 newFid.volume = dscp->fid.volume;
2879 newFid.vnode = newAFSFid.Vnode;
2880 newFid.unique = newAFSFid.Unique;
2881 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2883 lock_ObtainMutex(&scp->mx);
2884 if (!cm_HaveCallback(scp)) {
2885 cm_MergeStatus(dscp, scp, &newDirStatus, &volSync,
2887 cm_EndCallbackGrantingCall(scp, &cbReq,
2888 &newDirCallback, 0);
2891 lock_ReleaseMutex(&scp->mx);
2892 cm_ReleaseSCache(scp);
2896 /* make sure we end things properly */
2898 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2900 if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
2901 cm_DirCreateEntry(&dirop, namep, &newFid);
2903 cm_BPlusDirCreateEntry(&dirop, namep, &newFid);
2906 cm_EndDirOp(&dirop);
2908 /* and return error code */
2912 long cm_Link(cm_scache_t *dscp, char *namep, cm_scache_t *sscp, long flags,
2913 cm_user_t *userp, cm_req_t *reqp)
2918 AFSFid existingAFSFid;
2919 AFSFetchStatus updatedDirStatus;
2920 AFSFetchStatus newLinkStatus;
2922 struct rx_connection * callp;
2925 if (dscp->fid.cell != sscp->fid.cell ||
2926 dscp->fid.volume != sscp->fid.volume) {
2927 return CM_ERROR_CROSSDEVLINK;
2930 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2931 lock_ObtainMutex(&dscp->mx);
2932 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2933 lock_ReleaseMutex(&dscp->mx);
2935 cm_EndDirOp(&dirop);
2940 /* try the RPC now */
2941 osi_Log1(afsd_logp, "CALL Link scp 0x%p", dscp);
2943 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2946 dirAFSFid.Volume = dscp->fid.volume;
2947 dirAFSFid.Vnode = dscp->fid.vnode;
2948 dirAFSFid.Unique = dscp->fid.unique;
2950 existingAFSFid.Volume = sscp->fid.volume;
2951 existingAFSFid.Vnode = sscp->fid.vnode;
2952 existingAFSFid.Unique = sscp->fid.unique;
2954 callp = cm_GetRxConn(connp);
2955 code = RXAFS_Link(callp, &dirAFSFid, namep, &existingAFSFid,
2956 &newLinkStatus, &updatedDirStatus, &volSync);
2957 rx_PutConnection(callp);
2958 osi_Log1(smb_logp," RXAFS_Link returns 0x%x", code);
2960 } while (cm_Analyze(connp, userp, reqp,
2961 &dscp->fid, &volSync, NULL, NULL, code));
2963 code = cm_MapRPCError(code, reqp);
2966 osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
2968 osi_Log0(afsd_logp, "CALL Link SUCCESS");
2971 lock_ObtainWrite(&dirop.scp->dirlock);
2972 dirop.lockType = CM_DIRLOCK_WRITE;
2974 lock_ObtainMutex(&dscp->mx);
2975 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2977 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
2979 lock_ReleaseMutex(&dscp->mx);
2982 if (cm_CheckDirOpForSingleChange(&dirop)) {
2983 cm_DirCreateEntry(&dirop, namep, &sscp->fid);
2985 cm_BPlusDirCreateEntry(&dirop, namep, &sscp->fid);
2989 cm_EndDirOp(&dirop);
2994 long cm_SymLink(cm_scache_t *dscp, char *namep, char *contentsp, long flags,
2995 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
3003 AFSStoreStatus inStatus;
3004 AFSFetchStatus updatedDirStatus;
3005 AFSFetchStatus newLinkStatus;
3007 struct rx_connection * callp;
3010 /* before starting the RPC, mark that we're changing the directory data,
3011 * so that someone who does a chmod on the dir will wait until our
3014 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
3015 lock_ObtainMutex(&dscp->mx);
3016 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3017 lock_ReleaseMutex(&dscp->mx);
3019 cm_EndDirOp(&dirop);
3024 cm_StatusFromAttr(&inStatus, NULL, attrp);
3026 /* try the RPC now */
3027 osi_Log1(afsd_logp, "CALL Symlink scp 0x%p", dscp);
3029 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3033 dirAFSFid.Volume = dscp->fid.volume;
3034 dirAFSFid.Vnode = dscp->fid.vnode;
3035 dirAFSFid.Unique = dscp->fid.unique;
3037 callp = cm_GetRxConn(connp);
3038 code = RXAFS_Symlink(callp, &dirAFSFid, namep, contentsp,
3039 &inStatus, &newAFSFid, &newLinkStatus,
3040 &updatedDirStatus, &volSync);
3041 rx_PutConnection(callp);
3043 } while (cm_Analyze(connp, userp, reqp,
3044 &dscp->fid, &volSync, NULL, NULL, code));
3045 code = cm_MapRPCError(code, reqp);
3048 osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
3050 osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
3053 lock_ObtainWrite(&dirop.scp->dirlock);
3054 dirop.lockType = CM_DIRLOCK_WRITE;
3056 lock_ObtainMutex(&dscp->mx);
3057 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3059 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
3061 lock_ReleaseMutex(&dscp->mx);
3064 if (cm_CheckDirOpForSingleChange(&dirop)) {
3065 newFid.cell = dscp->fid.cell;
3066 newFid.volume = dscp->fid.volume;
3067 newFid.vnode = newAFSFid.Vnode;
3068 newFid.unique = newAFSFid.Unique;
3070 cm_DirCreateEntry(&dirop, namep, &newFid);
3072 cm_BPlusDirCreateEntry(&dirop, namep, &newFid);
3076 cm_EndDirOp(&dirop);
3078 /* now try to create the new dir's entry, too, but be careful to
3079 * make sure that we don't merge in old info. Since we weren't locking
3080 * out any requests during the file's creation, we may have pretty old
3084 newFid.cell = dscp->fid.cell;
3085 newFid.volume = dscp->fid.volume;
3086 newFid.vnode = newAFSFid.Vnode;
3087 newFid.unique = newAFSFid.Unique;
3088 code = cm_GetSCache(&newFid, &scp, userp, reqp);
3090 lock_ObtainMutex(&scp->mx);
3091 if (!cm_HaveCallback(scp)) {
3092 cm_MergeStatus(dscp, scp, &newLinkStatus, &volSync,
3095 lock_ReleaseMutex(&scp->mx);
3096 cm_ReleaseSCache(scp);
3100 /* and return error code */
3104 long cm_RemoveDir(cm_scache_t *dscp, char *namep, cm_user_t *userp,
3111 AFSFetchStatus updatedDirStatus;
3113 struct rx_connection * callp;
3116 /* before starting the RPC, mark that we're changing the directory data,
3117 * so that someone who does a chmod on the dir will wait until our
3120 cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
3121 lock_ObtainMutex(&dscp->mx);
3122 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3123 lock_ReleaseMutex(&dscp->mx);
3125 cm_EndDirOp(&dirop);
3130 /* try the RPC now */
3131 osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%p", dscp);
3133 code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3137 dirAFSFid.Volume = dscp->fid.volume;
3138 dirAFSFid.Vnode = dscp->fid.vnode;
3139 dirAFSFid.Unique = dscp->fid.unique;
3141 callp = cm_GetRxConn(connp);
3142 code = RXAFS_RemoveDir(callp, &dirAFSFid, namep,
3143 &updatedDirStatus, &volSync);
3144 rx_PutConnection(callp);
3146 } while (cm_Analyze(connp, userp, reqp,
3147 &dscp->fid, &volSync, NULL, NULL, code));
3148 code = cm_MapRPCErrorRmdir(code, reqp);
3151 osi_Log1(afsd_logp, "CALL RemoveDir FAILURE, code 0x%x", code);
3153 osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
3156 lock_ObtainWrite(&dirop.scp->dirlock);
3157 dirop.lockType = CM_DIRLOCK_WRITE;
3159 lock_ObtainMutex(&dscp->mx);
3160 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3162 cm_dnlcRemove(dscp, namep);
3163 cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
3165 lock_ReleaseMutex(&dscp->mx);
3168 if (cm_CheckDirOpForSingleChange(&dirop)) {
3169 cm_DirDeleteEntry(&dirop, namep);
3171 cm_BPlusDirDeleteEntry(&dirop, namep);
3175 cm_EndDirOp(&dirop);
3177 /* and return error code */
3181 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
3183 /* grab mutex on contents */
3184 lock_ObtainMutex(&scp->mx);
3186 /* reset the prefetch info */
3187 scp->prefetch.base.LowPart = 0; /* base */
3188 scp->prefetch.base.HighPart = 0;
3189 scp->prefetch.end.LowPart = 0; /* and end */
3190 scp->prefetch.end.HighPart = 0;
3192 /* release mutex on contents */
3193 lock_ReleaseMutex(&scp->mx);
3199 long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
3200 char *newNamep, cm_user_t *userp, cm_req_t *reqp)
3204 AFSFid oldDirAFSFid;
3205 AFSFid newDirAFSFid;
3207 AFSFetchStatus updatedOldDirStatus;
3208 AFSFetchStatus updatedNewDirStatus;
3211 struct rx_connection * callp;
3212 cm_dirOp_t oldDirOp;
3215 cm_dirOp_t newDirOp;
3217 /* before starting the RPC, mark that we're changing the directory data,
3218 * so that someone who does a chmod on the dir will wait until our call
3219 * completes. We do this in vnode order so that we don't deadlock,
3220 * which makes the code a little verbose.
3222 if (oldDscp == newDscp) {
3223 /* check for identical names */
3224 if (strcmp(oldNamep, newNamep) == 0)
3225 return CM_ERROR_RENAME_IDENTICAL;
3228 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3229 lock_ObtainMutex(&oldDscp->mx);
3230 cm_dnlcRemove(oldDscp, oldNamep);
3231 cm_dnlcRemove(oldDscp, newNamep);
3232 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3233 CM_SCACHESYNC_STOREDATA);
3234 lock_ReleaseMutex(&oldDscp->mx);
3236 cm_EndDirOp(&oldDirOp);
3240 /* two distinct dir vnodes */
3242 if (oldDscp->fid.cell != newDscp->fid.cell ||
3243 oldDscp->fid.volume != newDscp->fid.volume)
3244 return CM_ERROR_CROSSDEVLINK;
3246 /* shouldn't happen that we have distinct vnodes for two
3247 * different files, but could due to deliberate attack, or
3248 * stale info. Avoid deadlocks and quit now.
3250 if (oldDscp->fid.vnode == newDscp->fid.vnode)
3251 return CM_ERROR_CROSSDEVLINK;
3253 if (oldDscp->fid.vnode < newDscp->fid.vnode) {
3254 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3255 lock_ObtainMutex(&oldDscp->mx);
3256 cm_dnlcRemove(oldDscp, oldNamep);
3257 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3258 CM_SCACHESYNC_STOREDATA);
3259 lock_ReleaseMutex(&oldDscp->mx);
3261 cm_EndDirOp(&oldDirOp);
3263 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE, &newDirOp);
3264 lock_ObtainMutex(&newDscp->mx);
3265 cm_dnlcRemove(newDscp, newNamep);
3266 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3267 CM_SCACHESYNC_STOREDATA);
3268 lock_ReleaseMutex(&newDscp->mx);
3270 cm_EndDirOp(&newDirOp);
3272 /* cleanup first one */
3273 lock_ObtainMutex(&oldDscp->mx);
3274 cm_SyncOpDone(oldDscp, NULL,
3275 CM_SCACHESYNC_STOREDATA);
3276 lock_ReleaseMutex(&oldDscp->mx);
3277 cm_EndDirOp(&oldDirOp);
3282 /* lock the new vnode entry first */
3283 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE, &newDirOp);
3284 lock_ObtainMutex(&newDscp->mx);
3285 cm_dnlcRemove(newDscp, newNamep);
3286 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3287 CM_SCACHESYNC_STOREDATA);
3288 lock_ReleaseMutex(&newDscp->mx);
3290 cm_EndDirOp(&newDirOp);
3292 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3293 lock_ObtainMutex(&oldDscp->mx);
3294 cm_dnlcRemove(oldDscp, oldNamep);
3295 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3296 CM_SCACHESYNC_STOREDATA);
3297 lock_ReleaseMutex(&oldDscp->mx);
3299 cm_EndDirOp(&oldDirOp);
3301 /* cleanup first one */
3302 lock_ObtainMutex(&newDscp->mx);
3303 cm_SyncOpDone(newDscp, NULL,
3304 CM_SCACHESYNC_STOREDATA);
3305 lock_ReleaseMutex(&newDscp->mx);
3306 cm_EndDirOp(&newDirOp);
3310 } /* two distinct vnodes */
3317 /* try the RPC now */
3318 osi_Log2(afsd_logp, "CALL Rename old scp 0x%p new scp 0x%p",
3321 code = cm_ConnFromFID(&oldDscp->fid, userp, reqp, &connp);
3325 oldDirAFSFid.Volume = oldDscp->fid.volume;
3326 oldDirAFSFid.Vnode = oldDscp->fid.vnode;
3327 oldDirAFSFid.Unique = oldDscp->fid.unique;
3328 newDirAFSFid.Volume = newDscp->fid.volume;
3329 newDirAFSFid.Vnode = newDscp->fid.vnode;
3330 newDirAFSFid.Unique = newDscp->fid.unique;
3332 callp = cm_GetRxConn(connp);
3333 code = RXAFS_Rename(callp, &oldDirAFSFid, oldNamep,
3334 &newDirAFSFid, newNamep,
3335 &updatedOldDirStatus, &updatedNewDirStatus,
3337 rx_PutConnection(callp);
3339 } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
3340 &volSync, NULL, NULL, code));
3341 code = cm_MapRPCError(code, reqp);
3344 osi_Log1(afsd_logp, "CALL Rename FAILURE, code 0x%x", code);
3346 osi_Log0(afsd_logp, "CALL Rename SUCCESS");
3348 /* update the individual stat cache entries for the directories */
3350 lock_ObtainWrite(&oldDirOp.scp->dirlock);
3351 oldDirOp.lockType = CM_DIRLOCK_WRITE;
3353 lock_ObtainMutex(&oldDscp->mx);
3354 cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
3357 cm_MergeStatus(NULL, oldDscp, &updatedOldDirStatus, &volSync,
3358 userp, CM_MERGEFLAG_DIROP);
3359 lock_ReleaseMutex(&oldDscp->mx);
3362 if (cm_CheckDirOpForSingleChange(&oldDirOp)) {
3365 diropCode = cm_BPlusDirLookup(&oldDirOp, oldNamep, &fileFid);
3366 if (diropCode == CM_ERROR_INEXACT_MATCH)
3368 else if (diropCode == EINVAL)
3370 diropCode = cm_DirLookup(&oldDirOp, oldNamep, &fileFid);
3372 if (diropCode == 0) {
3374 diropCode = cm_DirCreateEntry(&oldDirOp, newNamep, &fileFid);
3376 cm_BPlusDirCreateEntry(&oldDirOp, newNamep, &fileFid);
3380 if (diropCode == 0) {
3381 diropCode = cm_DirDeleteEntry(&oldDirOp, oldNamep);
3383 cm_BPlusDirDeleteEntry(&oldDirOp, oldNamep);
3389 cm_EndDirOp(&oldDirOp);
3391 /* and update it for the new one, too, if necessary */
3394 lock_ObtainWrite(&newDirOp.scp->dirlock);
3395 newDirOp.lockType = CM_DIRLOCK_WRITE;
3397 lock_ObtainMutex(&newDscp->mx);
3398 cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
3400 cm_MergeStatus(NULL, newDscp, &updatedNewDirStatus, &volSync,
3401 userp, CM_MERGEFLAG_DIROP);
3402 lock_ReleaseMutex(&newDscp->mx);
3405 /* we only make the local change if we successfully made
3406 the change in the old directory AND there was only one
3407 change in the new directory */
3408 if (diropCode == 0 && cm_CheckDirOpForSingleChange(&newDirOp)) {
3409 cm_DirCreateEntry(&newDirOp, newNamep, &fileFid);
3411 cm_BPlusDirCreateEntry(&newDirOp, newNamep, &fileFid);
3415 cm_EndDirOp(&newDirOp);
3418 /* and return error code */
3422 /* Byte range locks:
3424 The OpenAFS Windows client has to fake byte range locks given no
3425 server side support for such locks. This is implemented as keyed
3426 byte range locks on the cache manager.
3428 Keyed byte range locks:
3430 Each cm_scache_t structure keeps track of a list of keyed locks.
3431 The key for a lock identifies an owner of a set of locks (referred
3432 to as a client). Each key is represented by a value. The set of
3433 key values used within a specific cm_scache_t structure form a
3434 namespace that has a scope of just that cm_scache_t structure. The
3435 same key value can be used with another cm_scache_t structure and
3436 correspond to a completely different client. However it is
3437 advantageous for the SMB or IFS layer to make sure that there is a
3438 1-1 mapping between client and keys over all cm_scache_t objects.
3440 Assume a client C has key Key(C) (although, since the scope of the
3441 key is a cm_scache_t, the key can be Key(C,S), where S is the
3442 cm_scache_t. But assume a 1-1 relation between keys and clients).
3443 A byte range (O,+L) denotes byte addresses (O) through (O+L-1)
3444 inclusive (a.k.a. [O,O+L-1]). The function Key(x) is implemented
3445 through cm_generateKey() function for both SMB and IFS.
3447 The list of locks for a cm_scache_t object S is maintained in
3448 S->fileLocks. The cache manager will set a lock on the AFS file
3449 server in order to assert the locks in S->fileLocks. If only
3450 shared locks are in place for S, then the cache manager will obtain
3451 a LockRead lock, while if there are any exclusive locks, it will
3452 obtain a LockWrite lock. If the exclusive locks are all released
3453 while the shared locks remain, then the cache manager will
3454 downgrade the lock from LockWrite to LockRead. Similarly, if an
3455 exclusive lock is obtained when only shared locks exist, then the
3456 cache manager will try to upgrade the lock from LockRead to
3459 Each lock L owned by client C maintains a key L->key such that
3460 L->key == Key(C), the effective range defined by L->LOffset and
3461 L->LLength such that the range of bytes affected by the lock is
3462 (L->LOffset, +L->LLength), a type maintained in L->LockType which
3463 is either exclusive or shared.
3467 A lock exists iff it is in S->fileLocks for some cm_scache_t
3468 S. Existing locks are in one of the following states: ACTIVE,
3469 WAITLOCK, WAITUNLOCK, LOST, DELETED.
3471 The following sections describe each lock and the associated
3474 1. ACTIVE: A lock L is ACTIVE iff the cache manager has asserted
3475 the lock with the AFS file server. This type of lock can be
3476 exercised by a client to read or write to the locked region (as
3479 1.1 ACTIVE->LOST: When the AFS file server fails to extend a
3480 server lock that was required to assert the lock. Before
3481 marking the lock as lost, the cache manager checks if the file
3482 has changed on the server. If the file has not changed, then
3483 the cache manager will attempt to obtain a new server lock
3484 that is sufficient to assert the client side locks for the
3485 file. If any of these fail, the lock is marked as LOST.
3486 Otherwise, it is left as ACTIVE.
3488 1.2 ACTIVE->DELETED: Lock is released.
3490 2. WAITLOCK: A lock is in a WAITLOCK state if the cache manager
3491 grants the lock but the lock is yet to be asserted with the AFS
3492 file server. Once the file server grants the lock, the state
3493 will transition to an ACTIVE lock.
3495 2.1 WAITLOCK->ACTIVE: The server granted the lock.
3497 2.2 WAITLOCK->DELETED: Lock is abandoned, or timed out during
3500 2.3 WAITLOCK->LOST: One or more locks from this client were
3501 marked as LOST. No further locks will be granted to this
3502 client until all lost locks are removed.
3504 3. WAITUNLOCK: A lock is in a WAITUNLOCK state if the cache manager
3505 receives a request for a lock that conflicts with an existing
3506 ACTIVE or WAITLOCK lock. The lock will be placed in the queue
3507 and will be granted at such time the conflicting locks are
3508 removed, at which point the state will transition to either
3511 3.1 WAITUNLOCK->ACTIVE: The conflicting lock was removed. The
3512 current serverLock is sufficient to assert this lock, or a
3513 sufficient serverLock is obtained.
3515 3.2 WAITUNLOCK->WAITLOCK: The conflicting lock was removed,
3516 however the required serverLock is yet to be asserted with the
3519 3.3 WAITUNLOCK->DELETED: The lock is abandoned, timed out or
3522 3.5 WAITUNLOCK->LOST: One or more locks from this client were
3523 marked as LOST. No further locks will be granted to this
3524 client until all lost locks are removed.
3526 4. LOST: A lock L is LOST if the server lock that was required to
3527 assert the lock could not be obtained or if it could not be
3528 extended, or if other locks by the same client were LOST.
3529 Essentially, once a lock is LOST, the contract between the cache
3530 manager and that specific client is no longer valid.
3532 The cache manager rechecks the server lock once every minute and
3533 extends it as appropriate. If this is not done for 5 minutes,
3534 the AFS file server will release the lock (the 5 minute timeout
3535 is based on current file server code and is fairly arbitrary).
3536 Once released, the lock cannot be re-obtained without verifying
3537 that the contents of the file hasn't been modified since the
3538 time the lock was released. Re-obtaining the lock without
3539 verifying this may lead to data corruption. If the lock can not
3540 be obtained safely, then all active locks for the cm_scache_t
3543 4.1 LOST->DELETED: The lock is released.
3545 5. DELETED: The lock is no longer relevant. Eventually, it will
3546 get removed from the cm_scache_t. In the meantime, it will be
3547 treated as if it does not exist.
3549 5.1 DELETED->not exist: The lock is removed from the
3552 The following are classifications of locks based on their state.
3554 6* A lock L is ACCEPTED if it is ACTIVE or WAITLOCK. These locks
3555 have been accepted by the cache manager, but may or may not have
3556 been granted back to the client.
3558 7* A lock L is QUEUED if it is ACTIVE, WAITLOCK or WAITUNLOCK.
3560 8* A lock L is WAITING if it is WAITLOCK or WAITUNLOCK.
3564 A client C can READ range (Offset,+Length) of a file represented by
3565 cm_scache_t S iff (1):
3567 1. for all _a_ in (Offset,+Length), all of the following is true:
3569 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3570 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3573 1.2 For each LOST lock L in S->fileLocks such that _a_ in
3574 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3577 (When locks are lost on an cm_scache_t, all locks are lost. By
3578 4.2 (below), if there is an exclusive LOST lock, then there
3579 can't be any overlapping ACTIVE locks.)
3581 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
3583 2. for all _a_ in (Offset,+Length), one of the following is true:
3585 2.1 Byte _a_ of S is unowned (as specified in 1.1) AND there
3586 does not exist a LOST lock L such that _a_ in
3587 (L->LOffset,+L->LLength).
3589 2.2 Byte _a_ of S is owned by C under lock L (as specified in
3590 1.2) AND L->LockType is exclusive.
3592 A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
3594 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
3597 3.1 If L->LockType is exclusive then there does NOT exist a
3598 ACCEPTED lock M in S->fileLocks such that _a_ in
3599 (M->LOffset,+M->LLength).
3601 (If we count all QUEUED locks then we hit cases such as
3602 cascading waiting locks where the locks later on in the queue
3603 can be granted without compromising file integrity. On the
3604 other hand if only ACCEPTED locks are considered, then locks
3605 that were received earlier may end up waiting for locks that
3606 were received later to be unlocked. The choice of ACCEPTED
3607 locks was made to mimic the Windows byte range lock
3610 3.2 If L->LockType is shared then for each ACCEPTED lock M in
3611 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
3612 M->LockType is shared.
3614 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
3616 4.1 M->key != Key(C)
3618 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
3619 and (M->LOffset,+M->LLength) do not intersect.
3621 (Note: If a client loses a lock, it loses all locks.
3622 Subsequently, it will not be allowed to obtain any more locks
3623 until all existing LOST locks that belong to the client are
3624 released. Once all locks are released by a single client,
3625 there exists no further contract between the client and AFS
3626 about the contents of the file, hence the client can then
3627 proceed to obtain new locks and establish a new contract.
3629 This doesn't quite work as you think it should, because most
3630 applications aren't built to deal with losing locks they
3631 thought they once had. For now, we don't have a good
3632 solution to lost locks.
3634 Also, for consistency reasons, we have to hold off on
3635 granting locks that overlap exclusive LOST locks.)
3637 A client C can only unlock locks L in S->fileLocks which have
3640 The representation and invariants are as follows:
3642 - Each cm_scache_t structure keeps:
3644 - A queue of byte-range locks (cm_scache_t::fileLocks) which
3645 are of type cm_file_lock_t.
3647 - A record of the highest server-side lock that has been
3648 obtained for this object (cm_scache_t::serverLock), which is
3649 one of (-1), LockRead, LockWrite.
3651 - A count of ACCEPTED exclusive and shared locks that are in the
3652 queue (cm_scache_t::sharedLocks and
3653 cm_scache_t::exclusiveLocks)
3655 - Each cm_file_lock_t structure keeps:
3657 - The type of lock (cm_file_lock_t::LockType)
3659 - The key associated with the lock (cm_file_lock_t::key)
3661 - The offset and length of the lock (cm_file_lock_t::LOffset
3662 and cm_file_lock_t::LLength)
3664 - The state of the lock.
3666 - Time of issuance or last successful extension
3668 Semantic invariants:
3670 I1. The number of ACCEPTED locks in S->fileLocks are
3671 (S->sharedLocks + S->exclusiveLocks)
3673 External invariants:
3675 I3. S->serverLock is the lock that we have asserted with the
3676 AFS file server for this cm_scache_t.
3678 I4. S->serverLock == LockRead iff there is at least one ACTIVE
3679 shared lock, but no ACTIVE exclusive locks.
3681 I5. S->serverLock == LockWrite iff there is at least one ACTIVE
3684 I6. If L is a LOST lock, then for each lock M in S->fileLocks,
3685 M->key == L->key IMPLIES M is LOST or DELETED.
3690 #define IS_LOCK_ACTIVE(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == 0)
3692 #define IS_LOCK_WAITLOCK(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_WAITLOCK)
3694 #define IS_LOCK_WAITUNLOCK(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_WAITUNLOCK)
3696 #define IS_LOCK_LOST(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_LOST)
3698 #define IS_LOCK_DELETED(lockp) (((lockp)->flags & CM_FILELOCK_FLAG_DELETED) == CM_FILELOCK_FLAG_DELETED)
3701 #define IS_LOCK_ACCEPTED(lockp) (IS_LOCK_ACTIVE(lockp) || IS_LOCK_WAITLOCK(lockp))
3704 #define IS_LOCK_CLIENTONLY(lockp) ((((lockp)->scp->flags & CM_SCACHEFLAG_RO) == CM_SCACHEFLAG_RO) || (((lockp)->flags & CM_FILELOCK_FLAG_CLIENTONLY) == CM_FILELOCK_FLAG_CLIENTONLY))
3707 #define INTERSECT_RANGE(r1,r2) (((r2).offset+(r2).length) > (r1).offset && ((r1).offset +(r1).length) > (r2).offset)
3710 #define CONTAINS_RANGE(r1,r2) (((r2).offset+(r2).length) <= ((r1).offset+(r1).length) && (r1).offset <= (r2).offset)
3712 #if defined(VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS) && !defined(LOCK_TESTING)
3713 #define SCP_SUPPORTS_BRLOCKS(scp) ((scp)->cbServerp && ((scp)->cbServerp->capabilities & VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS))
3715 #define SCP_SUPPORTS_BRLOCKS(scp) (1)
3718 #define SERVERLOCKS_ENABLED(scp) (!((scp)->flags & CM_SCACHEFLAG_RO) && cm_enableServerLocks && SCP_SUPPORTS_BRLOCKS(scp))
3720 #if defined(VICED_CAPABILITY_WRITELOCKACL)
3721 #define SCP_SUPPORTS_WRITELOCKACL(scp) ((scp)->cbServerp && ((scp->cbServerp->capabilities & VICED_CAPABILITY_WRITELOCKACL)))
3723 #define SCP_SUPPORTS_WRITELOCKACL(scp) (0)
3725 /* This should really be defined in any build that this code is being
3727 #error VICED_CAPABILITY_WRITELOCKACL not defined.
3730 static void cm_LockRangeSubtract(cm_range_t * pos, const cm_range_t * neg)
3732 afs_int64 int_begin;
3735 int_begin = MAX(pos->offset, neg->offset);
3736 int_end = MIN(pos->offset+pos->length, neg->offset+neg->length);
3738 if (int_begin < int_end) {
3739 if (int_begin == pos->offset) {
3740 pos->length = pos->offset + pos->length - int_end;
3741 pos->offset = int_end;
3742 } else if (int_end == pos->offset + pos->length) {
3743 pos->length = int_begin - pos->offset;
3746 /* We only subtract ranges if the resulting range is
3747 contiguous. If we try to support non-contigous ranges, we
3748 aren't actually improving performance. */
3752 /* Called with scp->mx held. Returns 0 if all is clear to read the
3753 specified range by the client identified by key.
3755 long cm_LockCheckRead(cm_scache_t *scp,
3756 LARGE_INTEGER LOffset,
3757 LARGE_INTEGER LLength,
3760 #ifndef ADVISORY_LOCKS
3762 cm_file_lock_t *fileLock;
3766 int substract_ranges = FALSE;
3768 range.offset = LOffset.QuadPart;
3769 range.length = LLength.QuadPart;
3773 1. for all _a_ in (Offset,+Length), all of the following is true:
3775 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3776 (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3779 1.2 For each LOST lock L in S->fileLocks such that _a_ in
3780 (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3785 lock_ObtainRead(&cm_scacheLock);
3787 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
3789 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
3791 if (INTERSECT_RANGE(range, fileLock->range)) {
3792 if (IS_LOCK_ACTIVE(fileLock)) {
3793 if (fileLock->key == key) {
3795 /* If there is an active lock for this client, it
3796 is safe to substract ranges.*/
3797 cm_LockRangeSubtract(&range, &fileLock->range);
3798 substract_ranges = TRUE;
3800 if (fileLock->lockType != LockRead) {
3801 code = CM_ERROR_LOCK_CONFLICT;
3805 /* even if the entire range is locked for reading,
3806 we still can't grant the lock at this point
3807 because the client may have lost locks. That
3808 is, unless we have already seen an active lock
3809 belonging to the client, in which case there
3810 can't be any lost locks for this client. */
3811 if (substract_ranges)
3812 cm_LockRangeSubtract(&range, &fileLock->range);
3814 } else if (IS_LOCK_LOST(fileLock) &&
3815 (fileLock->key == key || fileLock->lockType == LockWrite)) {
3816 code = CM_ERROR_BADFD;
3822 lock_ReleaseRead(&cm_scacheLock);
3824 osi_Log4(afsd_logp, "cm_LockCheckRead scp 0x%x offset %d length %d code 0x%x",
3825 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
3836 /* Called with scp->mx held. Returns 0 if all is clear to write the
3837 specified range by the client identified by key.
3839 long cm_LockCheckWrite(cm_scache_t *scp,
3840 LARGE_INTEGER LOffset,
3841 LARGE_INTEGER LLength,
3844 #ifndef ADVISORY_LOCKS
3846 cm_file_lock_t *fileLock;
3851 range.offset = LOffset.QuadPart;
3852 range.length = LLength.QuadPart;
3855 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
3857 2. for all _a_ in (Offset,+Length), one of the following is true:
3859 2.1 Byte _a_ of S is unowned AND there does not exist a LOST
3860 lock L such that _a_ in (L->LOffset,+L->LLength).
3862 2.2 Byte _a_ of S is owned by C under lock L AND L->LockType is
3866 lock_ObtainRead(&cm_scacheLock);
3868 for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
3870 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
3872 if (INTERSECT_RANGE(range, fileLock->range)) {
3873 if (IS_LOCK_ACTIVE(fileLock)) {
3874 if (fileLock->key == key) {
3875 if (fileLock->lockType == LockWrite) {
3877 /* if there is an active lock for this client, it
3878 is safe to substract ranges */
3879 cm_LockRangeSubtract(&range, &fileLock->range);
3881 code = CM_ERROR_LOCK_CONFLICT;
3885 code = CM_ERROR_LOCK_CONFLICT;
3888 } else if (IS_LOCK_LOST(fileLock)) {
3889 code = CM_ERROR_BADFD;
3895 lock_ReleaseRead(&cm_scacheLock);
3897 osi_Log4(afsd_logp, "cm_LockCheckWrite scp 0x%x offset %d length %d code 0x%x",
3898 scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
3910 static void cm_LockMarkSCacheLost(cm_scache_t * scp);
3912 /* Called with cm_scacheLock write locked */
3913 static cm_file_lock_t * cm_GetFileLock(void) {
3916 l = (cm_file_lock_t *) cm_freeFileLocks;
3918 osi_QRemove(&cm_freeFileLocks, &l->q);
3920 l = malloc(sizeof(cm_file_lock_t));
3921 osi_assertx(l, "null cm_file_lock_t");
3924 memset(l, 0, sizeof(cm_file_lock_t));
3929 /* Called with cm_scacheLock write locked */
3930 static void cm_PutFileLock(cm_file_lock_t *l) {
3931 osi_QAdd(&cm_freeFileLocks, &l->q);
3934 /* called with scp->mx held. May release it during processing, but
3935 leaves it held on exit. */
3936 long cm_IntSetLock(cm_scache_t * scp, cm_user_t * userp, int lockType,
3942 struct rx_connection * callp;
3945 tfid.Volume = scp->fid.volume;
3946 tfid.Vnode = scp->fid.vnode;
3947 tfid.Unique = scp->fid.unique;
3950 osi_Log2(afsd_logp, "CALL SetLock scp 0x%p for lock %d", scp, lockType);
3952 lock_ReleaseMutex(&scp->mx);
3955 code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
3959 callp = cm_GetRxConn(connp);
3960 code = RXAFS_SetLock(callp, &tfid, lockType,
3962 rx_PutConnection(callp);
3964 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
3967 code = cm_MapRPCError(code, reqp);
3969 osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
3971 osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
3974 lock_ObtainMutex(&scp->mx);
3979 /* called with scp->mx held. Releases it during processing */
3980 long cm_IntReleaseLock(cm_scache_t * scp, cm_user_t * userp,
3986 struct rx_connection * callp;
3989 tfid.Volume = scp->fid.volume;
3990 tfid.Vnode = scp->fid.vnode;
3991 tfid.Unique = scp->fid.unique;
3994 lock_ReleaseMutex(&scp->mx);
3996 osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
3999 code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4003 callp = cm_GetRxConn(connp);
4004 code = RXAFS_ReleaseLock(callp, &tfid, &volSync);
4005 rx_PutConnection(callp);
4007 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
4009 code = cm_MapRPCError(code, reqp);
4012 "CALL ReleaseLock FAILURE, code 0x%x", code);
4015 "CALL ReleaseLock SUCCESS");
4017 lock_ObtainMutex(&scp->mx);
4022 /* called with scp->mx held. May release it during processing, but
4023 will exit with lock held.
4027 - 0 if the user has permission to get the specified lock for the scp
4029 - CM_ERROR_NOACCESS if not
4031 Any other error from cm_SyncOp will be sent down untranslated.
4033 If CM_ERROR_NOACCESS is returned and lock_type is LockRead, then
4034 phas_insert (if non-NULL) will receive a boolean value indicating
4035 whether the user has INSERT permission or not.
4037 long cm_LockCheckPerms(cm_scache_t * scp,
4044 long code = 0, code2 = 0;
4046 /* lock permissions are slightly tricky because of the 'i' bit.
4047 If the user has PRSFS_LOCK, she can read-lock the file. If the
4048 user has PRSFS_WRITE, she can write-lock the file. However, if
4049 the user has PRSFS_INSERT, then she can write-lock new files,
4050 but not old ones. Since we don't have information about
4051 whether a file is new or not, we assume that if the user owns
4052 the scp, then she has the permissions that are granted by
4055 osi_Log3(afsd_logp, "cm_LockCheckPerms for scp[0x%p] type[%d] user[0x%p]",
4056 scp, lock_type, userp);
4058 if (lock_type == LockRead)
4059 rights |= PRSFS_LOCK;
4060 else if (lock_type == LockWrite)